Branch data Line data Source code
1 : : /* zxidlib.c - Handwritten functions for implementing common application logic for SP
2 : : * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 : : * Author: Sampo Kellomaki (sampo@iki.fi)
5 : : * This is confidential unpublished proprietary source code of the author.
6 : : * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 : : * Distribution prohibited unless authorized in writing.
8 : : * Licensed under Apache License 2.0, see file COPYING.
9 : : * $Id: zxidlib.c,v 1.73 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 12.8.2006, created --Sampo
12 : : * 16.1.2007, factored out ses, conf, cgi, ecp, cdc, and loc --Sampo
13 : : * 7.10.2008, added documentation --Sampo
14 : : * 4.9.2009, added zxid_map_val() --Sampo
15 : : */
16 : :
17 : : #include "platform.h"
18 : : #include <string.h>
19 : : #include <stdio.h>
20 : : #include <sys/stat.h> /* umask(2) */
21 : :
22 : : #include "errmac.h"
23 : : #include "zxid.h"
24 : : #include "zxidpriv.h"
25 : : #include "zxidutil.h"
26 : : #include "zxidconf.h"
27 : : #include "saml2.h"
28 : : #include "c/zxidvers.h"
29 : : #include "c/zx-const.h"
30 : : #include "c/zx-ns.h"
31 : : #include "c/zx-data.h"
32 : :
33 : : int zx_debug = 0; /* declared in errmac.h */
34 : : char zx_indent[256] = ""; /* declared in errmac.h */
35 : : char zx_instance[64] = "\tzx"; /* declared in errmac.h */
36 : : int assert_nonfatal = 0;
37 : : char* assert_msg = "%s: Internal error caused an ASSERT to fire. Deliberately trying to dump core.\nSorry for the inconvenience. If no core appears, try `ulimit -c unlimited'\n";
38 : : int trace = 0;
39 : :
40 : : /*() Obtain the hex encoded version integer describing the libzxid. This can be
41 : : * used to effectuate a runtime version number check. For compile time you
42 : : * should check the value of the ~ZXID_VERSION~ macro. */
43 : :
44 : : /* Called by: covimp_test, opt x2 */
45 : : int zxid_version()
46 : 1 : {
47 : 1 : return ZXID_VERSION;
48 : : }
49 : :
50 : : /*() Obtain the version string describing the libzxid. This can be
51 : : * used for runtime version display. For compile time you
52 : : * should check the value of the ~ZXID_VERSION~ macro. */
53 : :
54 : : /* Called by: main x7, opt x2, zxid_fed_mgmt_cf, zxid_idp_select_zxstr_cf_cgi, zxid_map_bangbang, zxid_mgmt */
55 : : char* zxid_version_str()
56 : 39 : {
57 : 39 : return ZXID_REL " " ZXID_COMPILE_DATE " libzxid (zxid.org)";
58 : : }
59 : :
60 : : /*(i) Render any element with some options, controlled by
61 : : * config option ENC_TAIL_OPT. Often used to generate slightly optimized
62 : : * version for wire transfer. Not suitable for generating canonicalization. */
63 : :
64 : : /* Called by: main x3, so_enc_dec, zxid_addmd, zxid_anoint_sso_resp, zxid_cache_epr, zxid_call_epr, zxid_idp_sso, zxid_lecp_check, zxid_map_val_ss, zxid_mk_enc_a7n, zxid_mk_enc_id, zxid_mk_mni, zxid_mni_do_ss, zxid_pep_az_base_soap_pepmap x3, zxid_pep_az_soap_pepmap x3, zxid_reg_svc, zxid_ses_to_pool x2, zxid_slo_resp_redir, zxid_snarf_eprs_from_ses, zxid_soap_call_raw, zxid_soap_cgi_resp_body, zxid_sp_meta, zxid_sp_mni_redir, zxid_sp_slo_redir, zxid_start_sso_url, zxid_write_ent_to_cache, zxid_wsc_prepare_call, zxid_wsp_decorate */
65 : : struct zx_str* zx_easy_enc_elem_opt(zxid_conf* cf, struct zx_elem_s* x)
66 : 2298 : {
67 : : struct zx_str* ss;
68 : 2298 : cf->ctx->enc_tail_opt = cf->enc_tail_opt;
69 : 2298 : ss = zx_EASY_ENC_elem(cf->ctx, x);
70 : 2298 : cf->ctx->enc_tail_opt = 0;
71 : 2298 : return ss;
72 : : }
73 : :
74 : : /* Called by: attribute_sort_test, zxid_a7n2str, zxid_anoint_a7n x2, zxid_anoint_sso_resp, zxid_az_soap x2, zxid_idp_sso, zxid_mk_art_deref, zxid_nid2str, zxid_sp_mni_soap, zxid_sp_slo_soap, zxid_sp_soap_dispatch x5, zxid_sp_sso_finalize, zxid_ssos_anreq, zxid_token2str, zxid_wsf_validate_a7n */
75 : : struct zx_str* zx_easy_enc_elem_sig(zxid_conf* cf, struct zx_elem_s* x)
76 : 3163 : {
77 : 3163 : cf->ctx->enc_tail_opt = 0;
78 : 3163 : return zx_EASY_ENC_elem(cf->ctx, x);
79 : : }
80 : :
81 : : /*() Generate pseudorandom or statistically unique identifier of given length. The
82 : : * unique identifier will be safe base64 encoded.
83 : : *
84 : : * cf:: Configuration object, used for memory allocation.
85 : : * prefix:: A prefix string, usually used to distinguish classes of unique ids.
86 : : * bits:: Number of pseudorandom bits in the unique ID. For best results,
87 : : * bits should be multiple of 24 (3 bytes expands to 4 safe base64 chars)
88 : : * return:: The identifier as zx_str. Caller should eventually free this memory.
89 : : */
90 : : /* Called by: zxid_check_fed, zxid_mk_subj, zxid_mk_transient_nid, zxid_ps_addent_invite x3, zxid_ps_resolv_id, zxid_put_ses, zxid_pw_authn, zxid_ssos_anreq, zxid_wsc_prep_secmech, zxid_wsf_decor */
91 : : struct zx_str* zxid_mk_id(zxid_conf* cf, char* prefix, int bits)
92 : 268 : {
93 : : char bit_buf[ZXID_ID_MAX_BITS/8];
94 : : char base64_buf[ZXID_ID_MAX_BITS/6 + 1];
95 : : char* p;
96 [ + - - + ]: 268 : if (bits > ZXID_ID_MAX_BITS || bits & 0x07) {
97 : 0 : ERR("Requested bits(%d) more than internal limit(%d), or bits not divisible by 8.", bits, ZXID_ID_MAX_BITS);
98 : 0 : return 0;
99 : : }
100 : 268 : zx_rand(bit_buf, bits >> 3);
101 : 268 : p = base64_fancy_raw(bit_buf, bits >> 3, base64_buf, safe_basis_64, 1<<31, 0, 0, '.');
102 [ + - ]: 268 : return zx_strf(cf->ctx, "%s%.*s", prefix?prefix:"", p-base64_buf, base64_buf);
103 : : }
104 : :
105 : : /* Called by: zxid_mk_a7n, zxid_mk_art_deref, zxid_mk_authn_req, zxid_mk_az, zxid_mk_az_cd1, zxid_mk_dap_query_item, zxid_mk_dap_resquery, zxid_mk_dap_subscription, zxid_mk_dap_test_item, zxid_mk_logout, zxid_mk_logout_resp, zxid_mk_mni, zxid_mk_mni_resp, zxid_mk_saml_resp */
106 : : struct zx_attr_s* zxid_mk_id_attr(zxid_conf* cf, struct zx_elem_s* father, int tok, char* prefix, int bits)
107 : 1134 : {
108 : : char bit_buf[ZXID_ID_MAX_BITS/8];
109 : : char base64_buf[ZXID_ID_MAX_BITS/6 + 1];
110 : : char* p;
111 [ + - - + ]: 1134 : if (bits > ZXID_ID_MAX_BITS || bits & 0x07) {
112 : 0 : ERR("Requested bits(%d) more than internal limit(%d), or bits not divisible by 8.", bits, ZXID_ID_MAX_BITS);
113 : 0 : return 0;
114 : : }
115 : 1134 : zx_rand(bit_buf, bits >> 3);
116 : 1134 : p = base64_fancy_raw(bit_buf, bits >> 3, base64_buf, safe_basis_64, 1<<31, 0, 0, '.');
117 [ + - ]: 1134 : return zx_attrf(cf->ctx, father, tok, "%s%.*s", prefix?prefix:"", p-base64_buf, base64_buf);
118 : : }
119 : :
120 : : /*() Format a date-time string as usually used in XML, SAML, and Liberty. Apparently
121 : : * there are two ways to format this: with or with-out milliseconds. ZXID accepts
122 : : * either form as input, as they are both legal, but will only generate the
123 : : * without milliseconds form. Some other softwares are buggy and fail to
124 : : * accept the without milliseconds form. You can change the format at compile time
125 : : * by editing zxidlib.c:94.
126 : : */
127 : : /* Called by: zxid_put_invite x2, zxid_put_psobj x2, zxid_wsc_prep_secmech, zxid_wsf_decor */
128 : : struct zx_str* zxid_date_time(zxid_conf* cf, time_t secs)
129 : 191 : {
130 : : struct tm t;
131 : 191 : secs += cf->timeskew;
132 : 191 : GMTIME_R(secs, t);
133 : : #if 0
134 : : /* "2002-10-31T21:42:14.002Z" */
135 : : return zx_strf(cf->ctx, "%04d-%02d-%02dT%02d:%02d:%02d.002Z",
136 : : t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
137 : : #else
138 : : /* "2002-10-31T21:42:14Z" */
139 : 191 : return zx_strf(cf->ctx, "%04d-%02d-%02dT%02d:%02d:%02dZ",
140 : : t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
141 : : #endif
142 : : }
143 : :
144 : : /* Called by: zxid_mk_a7n x3, zxid_mk_an_stmt, zxid_mk_art_deref, zxid_mk_authn_req, zxid_mk_az, zxid_mk_az_cd1, zxid_mk_logout, zxid_mk_logout_resp, zxid_mk_mni, zxid_mk_mni_resp, zxid_mk_saml_resp, zxid_ps_addent_invite x2 */
145 : : struct zx_attr_s* zxid_date_time_attr(zxid_conf* cf, struct zx_elem_s* father, int tok, time_t secs)
146 : 3099 : {
147 : : struct tm t;
148 : 3099 : secs += cf->timeskew;
149 : 3099 : GMTIME_R(secs, t);
150 : : #if 0
151 : : /* "2002-10-31T21:42:14.002Z" */
152 : : return zx_attrf(cf->ctx, father, tok, "%04d-%02d-%02dT%02d:%02d:%02d.002Z",
153 : : t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
154 : : #else
155 : : /* "2002-10-31T21:42:14Z" */
156 : 3099 : return zx_attrf(cf->ctx, father, tok, "%04d-%02d-%02dT%02d:%02d:%02dZ",
157 : : t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
158 : : #endif
159 : : }
160 : :
161 : : /* ============== Redirect Encodings ============= */
162 : :
163 : : #define SIG_ALGO_RSA_SHA1 "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
164 : : #define SIG_ALGO_RSA_SHA1_URLENC "http://www.w3.org/2000/09/xmldsig%23rsa-sha1"
165 : : #define SIG_ALGO SIG_ALGO_RSA_SHA1
166 : : #define SIG_ALGO_URLENC SIG_ALGO_RSA_SHA1_URLENC
167 : : #define ETSIGNATURE_EQ "&Signature="
168 : : #define SIG_SIZE 256 /* Maximum size of the base64 encoded signature, for buffer allocation */
169 : :
170 : : /*(i) Encode (and sign if Simple Sign) a form according to SAML2 POST binding.
171 : : * zxid_decode_redir_or_post() performs the opposite operation.
172 : : *
173 : : * cf:: ZXID configuration object, also used for memory allocation
174 : : * field:: The name of the CGI variable, e.g. "SAMLRequest" or "SAMLResponse"
175 : : * payload:: What should be encoded in the redirect URL. Effectively becomes the query string
176 : : * relay_state:: Optional relay state argument. Ends up being encoded in the query string
177 : : * sign:: Whether binding layer signature is to be applied: 0=no, 1=POST-Simple-Sign
178 : : * action_url:: URL where the form should be posted
179 : : * return:: Query string encoding of the request. The memory should be freed by the caller.
180 : : * 0 on failure. */
181 : :
182 : : /* Called by: zxid_idp_sso x3 */
183 : : struct zx_str* zxid_saml2_post_enc(zxid_conf* cf, char* field, struct zx_str* payload, char* relay_state, int sign, struct zx_str* action_url)
184 : 5 : {
185 : : EVP_PKEY* sign_pkey;
186 : : struct zx_str id_str;
187 : : struct zx_str* logpath;
188 : : char* sigbuf[SIG_SIZE];
189 : : char* zbuf;
190 : : char* url;
191 : : char* sig;
192 : : char* p;
193 : : int alloc_len, zlen, slen, field_len, rs_len;
194 : : zxid_cgi cgi;
195 : 5 : field_len = strlen(field);
196 [ + + ]: 5 : rs_len = relay_state?strlen(relay_state):0;
197 [ + + ]: 5 : if (rs_len) {
198 : : /* Inplace decode. Decode is needed because the browser will encode again when
199 : : * submitting the form hidden field. */
200 : 3 : p = url = relay_state;
201 [ - + # # : 3 : URL_DECODE(p, url, relay_state + rs_len);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # - + +
+ ]
202 : 3 : *p = 0;
203 : 3 : rs_len = p - relay_state;
204 [ - + ]: 6 : while (p = strchr(relay_state,'"')) {
205 : 0 : ERR("RelayState(%s) MUST NOT contain double quote character because it would interfere with HTML form hidden field double quotes. Bad character squashed at position %d.", relay_state, p-relay_state);
206 : 0 : *p = '_';
207 : : }
208 : : }
209 : :
210 : : /* The url buf is allocated large enough to be used for both signing and/or base64 encoding. */
211 : 5 : alloc_len = MAX((field_len + 1 + payload->len
212 : : + sizeof("&RelayState=")-1 + rs_len
213 : : + sizeof("&SigAlg=" SIG_ALGO)-1 + sizeof(ETSIGNATURE_EQ)-1 + SIG_SIZE),
214 : : SIMPLE_BASE64_LEN(payload->len));
215 : 5 : url = p = ZX_ALLOC(cf->ctx, alloc_len + 1); /* +1 for nul term */
216 : :
217 [ - + ]: 5 : if (sign) { /* Additional POST-Simple-Sign signing (sign payload prior to base64 & URL enc) */
218 : 0 : memcpy(p, field, field_len);
219 : 0 : p += field_len;
220 : 0 : *p++ = '=';
221 : 0 : memcpy(p, payload->s, payload->len);
222 : 0 : p += payload->len;
223 : :
224 [ # # ]: 0 : if (rs_len) {
225 : 0 : memcpy(p, "&RelayState=", sizeof("&RelayState=")-1);
226 : 0 : p += sizeof("&RelayState=")-1;
227 : 0 : memcpy(p, relay_state, rs_len);
228 : 0 : p += rs_len;
229 : : }
230 : :
231 : 0 : memcpy(p, "&SigAlg=" SIG_ALGO, sizeof("&SigAlg=" SIG_ALGO)-1);
232 : 0 : p += sizeof("&SigAlg=" SIG_ALGO)-1;
233 : :
234 [ # # ]: 0 : if (zxid_lazy_load_sign_cert_and_pkey(cf, 0, &sign_pkey, "SAML2 post"))
235 : 0 : zlen = zxsig_data(cf->ctx, p-url, url, &zbuf, sign_pkey, "SAML2 post");
236 [ # # ]: 0 : if (zlen == -1)
237 : 0 : return 0;
238 : :
239 : 0 : memcpy(p, ETSIGNATURE_EQ, sizeof(ETSIGNATURE_EQ)-1);
240 : 0 : p += sizeof(ETSIGNATURE_EQ)-1;
241 : 0 : sig = p;
242 : 0 : p = base64_fancy_raw(zbuf, zlen, p, std_basis_64, 1<<31, 0, 0, '=');
243 [ # # # # ]: 0 : ASSERTOP(p-url, <, alloc_len); /* Check sig did not overrun its fixed size alloc SIG_SIZE */
244 : 0 : slen = p-sig;
245 : 0 : ZX_FREE(cf->ctx, zbuf);
246 : :
247 [ # # ]: 0 : if (cf->log_issue_msg) {
248 : 0 : id_str.len = p-url;
249 : 0 : id_str.s = url;
250 : 0 : logpath = zxlog_path(cf, action_url, &id_str, ZXLOG_ISSUE_DIR, ZXLOG_WIR_KIND, 1);
251 [ # # ]: 0 : if (logpath) {
252 [ # # ]: 0 : if (zxlog_dup_check(cf, logpath, "IdP POST SimpleSign")) {
253 : 0 : ERR("Duplicate wire msg(%.*s) (Simple Sign)", p-url, url);
254 [ # # ]: 0 : if (cf->dup_msg_fatal) {
255 : 0 : ERR("FATAL (by configuration): Duplicate wire msg(%.*s) (Simple Sign)", p-url, url);
256 : 0 : zxlog_blob(cf, 1, logpath, &id_str, "POST SimpleSign dup");
257 : 0 : zx_str_free(cf->ctx, logpath);
258 : 0 : ZX_FREE(cf->ctx, url);
259 : 0 : return 0;
260 : : }
261 : : }
262 : 0 : zxlog_blob(cf, 1, logpath, &id_str, "POST SimpleSign");
263 : 0 : zx_str_free(cf->ctx, logpath);
264 : : }
265 : : }
266 [ # # # # ]: 0 : ASSERTOP(slen, <, SIG_SIZE-1);
267 : 0 : memcpy(sigbuf, sig, slen);
268 : 0 : sigbuf[slen] = 0;
269 : : } else {
270 : 5 : sigbuf[0] = 0;
271 : : }
272 : :
273 : 5 : p = base64_fancy_raw(payload->s, payload->len, url, std_basis_64, 1<<31, 0, 0, '=');
274 : 5 : *p = 0;
275 [ - + # # ]: 5 : ASSERTOP(p-url, <=, alloc_len); /* Check sig did not overrun its fixed size alloc SIG_SIZE */
276 : :
277 : : #if 1
278 : : /* Template based POST page, see post.html */
279 : 5 : ZERO(&cgi, sizeof(cgi));
280 : 5 : cgi.action_url = zx_str_to_c(cf->ctx, action_url);
281 : 5 : cgi.saml_art = field;
282 : 5 : cgi.saml_resp = url;
283 [ + + ]: 5 : if (rs_len) {
284 : 3 : logpath = zx_strf(cf->ctx, "<input type=hidden name=RelayState value=\"%s\">", relay_state);
285 : 3 : cgi.rs = logpath->s;
286 : 3 : ZX_FREE(cf->ctx, logpath);
287 : : }
288 [ - + ]: 5 : if (sign) {
289 : 0 : logpath = zx_strf(cf->ctx, "<input type=hidden name=SigAlg value=\"" SIG_ALGO "\"><input type=hidden name=Signature value=\"%s\">", sigbuf);
290 : 0 : cgi.sig = logpath->s;
291 : 0 : ZX_FREE(cf->ctx, logpath);
292 : : }
293 : 5 : payload = zxid_template_page_cf(cf, &cgi, cf->post_templ_file, cf->post_templ, 64*1024, 0);
294 : : #else
295 : : payload = zx_strf(cf->ctx, "<title>ZXID POST Profile</title>"
296 : : "<body bgcolor=white OnLoad=\"document.forms[0].submit()\">"
297 : : "<h1>ZXID POST Profile POST</h1>"
298 : : "<form method=post action=\"%.*s\">\n"
299 : : "<input type=hidden name=%s value=\"%s\"><br>\n"
300 : : "%s%s%s" /* rs */
301 : : "%s%s%s" /* sigalg & sig */
302 : : "<input type=submit name=ok value=\" If JavaScript is not on, please click here to complete the transaction \">"
303 : : "</form>",
304 : : action_url->len, action_url->s,
305 : : field, url,
306 : : rs_len?"<input type=hidden name=RelayState value=\"":"",
307 : : rs_len?relay_state:"",
308 : : rs_len?"\">":"",
309 : : sign?"<input type=hidden name=SigAlg value=\"" SIG_ALGO "\"><input type=hidden name=Signature value=\"":"",
310 : : sigbuf,
311 : : sign?"\">":"");
312 : : #endif
313 : 5 : ZX_FREE(cf->ctx, url);
314 : 5 : return payload;
315 : : }
316 : :
317 : : struct zx_str zxstr_unknown = {0,0,sizeof("UNKNOWN")-1, "UNKNOWN"};
318 : :
319 : : /*(i) Encode and sign a URL according to SAML2 redirect binding.
320 : : * zxid_decode_redir_or_post() performs the opposite operation.
321 : : *
322 : : * 1. Compress payload
323 : : * 2. Base64 encode payload
324 : : * 3. URL encode and concatenate RelayState (if any)
325 : : * 4. Sign the URL encoded form (SimpleSign signs message prior to base64 and URL encodings)
326 : : * 5. Base64 encode the sig and concatenate to the URL
327 : : *
328 : : * cf:: ZXID configuration object, also used for memory allocation
329 : : * field:: The name of the CGI variable, e.g. "SAMLRequest=" or "SAMLResponse="
330 : : * payload:: What should be encoded in the redirect URL. Effectively becomes the query string
331 : : * relay_state:: Optional relay state argument. Ends up being encoded in the query string
332 : : * return:: Query string encoding of the request. The memory should be freed by the caller. */
333 : :
334 : : /* Called by: zxid_saml2_redir, zxid_saml2_redir_url, zxid_saml2_resp_redir */
335 : : struct zx_str* zxid_saml2_redir_enc(zxid_conf* cf, char* field, struct zx_str* pay_load, char* relay_state)
336 : 9 : {
337 : : EVP_PKEY* sign_pkey;
338 : : struct zx_str* logpath;
339 : : struct zx_str* ss;
340 : : char* zbuf;
341 : : char* b64;
342 : : char* url;
343 : : char* sig;
344 : : char* p;
345 : : int zlen, len, slen, field_len, rs_len;
346 : 9 : field_len = strlen(field);
347 [ + + ]: 9 : rs_len = relay_state?strlen(relay_state):0;
348 : :
349 : : /* RFC1951 per SAML2 binding line 576 (p.17), i.e. NOT gzip or ordinary zlib */
350 : 9 : zbuf = zx_zlib_raw_deflate(cf->ctx, pay_load->len, pay_load->s, &zlen);
351 [ - + ]: 9 : if (!zbuf)
352 : 0 : return 0;
353 : :
354 : 9 : len = SIMPLE_BASE64_LEN(zlen);
355 : 9 : b64 = ZX_ALLOC(cf->ctx, len);
356 : 9 : p = base64_fancy_raw(zbuf, zlen, b64, std_basis_64, 1<<31, 0, 0, '=');
357 : :
358 : 9 : len = field_len + zx_url_encode_len(p-b64, b64) - 1 /* zap nul termination */;
359 [ + + ]: 9 : url = ZX_ALLOC(cf->ctx, len + sizeof("&SigAlg=" SIG_ALGO_URLENC)
360 : : + (rs_len?(sizeof("&RelayState=")-1+rs_len):0));
361 : 9 : memcpy(url, field, field_len);
362 : :
363 : 9 : zx_url_encode_raw(p-b64, b64, url+field_len);
364 : 9 : ZX_FREE(cf->ctx, b64);
365 : :
366 [ + + ]: 9 : if (rs_len) {
367 : 3 : memcpy(url + len, "&RelayState=", sizeof("&RelayState=")-1);
368 : 3 : memcpy(url + len + sizeof("&RelayState=")-1, relay_state, rs_len);
369 : 3 : len += sizeof("&RelayState=")-1+rs_len;
370 : : }
371 : :
372 [ - + ]: 9 : if (!cf->authn_req_sign) { /* Simple nonsigned case. */
373 : 0 : url[len] = 0; /* Reservation for ETSIG_ALGO_RSA_SHA1_URLENC provides space for nul term. */
374 : 0 : return zx_ref_len_str(cf->ctx, len, url);
375 : : }
376 : :
377 : : /* Additional URL signing */
378 : :
379 : 9 : memcpy(url+len, "&SigAlg=" SIG_ALGO_URLENC, sizeof("&SigAlg=" SIG_ALGO_URLENC)-1);
380 : 9 : len += sizeof("&SigAlg=" SIG_ALGO_URLENC)-1;
381 [ + - ]: 9 : if (zxid_lazy_load_sign_cert_and_pkey(cf, 0, &sign_pkey, "SAML2 redir"))
382 : 9 : zlen = zxsig_data(cf->ctx, len, url, &zbuf, sign_pkey, "SAML2 redir");
383 [ - + ]: 9 : if (zlen == -1)
384 : 0 : return 0;
385 : :
386 : : /* Base64 and URL encode the sig. Had SAML2 specified safe base64, world would be simpler! */
387 : :
388 : 9 : b64 = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_LEN(zlen));
389 : 9 : p = base64_fancy_raw(zbuf, zlen, b64, std_basis_64, 1<<31, 0, 0, '=');
390 : :
391 : 9 : slen = zx_url_encode_len(p-b64, b64) - 1;
392 : 9 : sig = ZX_ALLOC(cf->ctx, len + sizeof(ETSIGNATURE_EQ)-1 + slen + 1);
393 : 9 : memcpy(sig, url, len);
394 : 9 : memcpy(sig + len, ETSIGNATURE_EQ, sizeof(ETSIGNATURE_EQ)-1);
395 : 9 : len += sizeof(ETSIGNATURE_EQ)-1;
396 : 9 : zx_url_encode_raw(p-b64, b64, sig + len);
397 : 9 : ZX_FREE(cf->ctx, b64);
398 : 9 : ZX_FREE(cf->ctx, url);
399 : 9 : sig[len + slen] = 0;
400 : :
401 : 9 : ss = zx_ref_len_str(cf->ctx, len + slen, sig);
402 : :
403 [ + - ]: 9 : if (cf->log_issue_msg) {
404 : 9 : logpath = zxlog_path(cf, &zxstr_unknown, ss, ZXLOG_ISSUE_DIR, ZXLOG_WIR_KIND, 1);
405 [ + - ]: 9 : if (logpath) {
406 [ - + ]: 9 : if (zxlog_dup_check(cf, logpath, "Redir")) {
407 : 0 : ERR("Duplicate wire msg(%.*s) (Redir)", ss->len, ss->s);
408 [ # # ]: 0 : if (cf->dup_msg_fatal) {
409 : 0 : ERR("FATAL (by configuration): Duplicate wire msg(%.*s) (Redir)", ss->len, ss->s);
410 : 0 : zxlog_blob(cf, 1, logpath, ss, "Redir dup");
411 : 0 : zx_str_free(cf->ctx, logpath);
412 : 0 : ZX_FREE(cf->ctx, ss);
413 : 0 : return 0;
414 : : }
415 : : }
416 : 9 : zxlog_blob(cf, 1, logpath, ss, "Redir");
417 : 9 : zx_str_free(cf->ctx, logpath);
418 : : }
419 : : }
420 : :
421 : 9 : return ss;
422 : : }
423 : :
424 : : /*() SAMLRequest. Return the URL needed for redirect. You need to pass this to
425 : : * some application layer facility to effectuate the actual redirect.
426 : : * Wrapper for zxid_saml2_redir_enc(). This function is different from
427 : : * zxid_saml2_redir() in that only the URL is returned, not the complete
428 : : * Location header.
429 : : *
430 : : * cf:: ZXID configuration object, also used for memory allocation
431 : : * loc:: The URL up to query string
432 : : * pay_load:: What should be encoded in the redirect URL. Effectively becomes the query string
433 : : * relay_state:: Optional relay state argument. Ends up being encoded in the query string
434 : : * return:: URL suitable for redirection as ~zx_str~. The memory should be freed by the caller. */
435 : :
436 : : /* Called by: zxid_start_sso_url */
437 : : struct zx_str* zxid_saml2_redir_url(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
438 : 5 : {
439 : : struct zx_str* ss;
440 : 5 : struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLRequest=", pay_load, relay_state);
441 [ + - - + ]: 5 : if (!loc || !rse) {
442 [ # # # # : 0 : ERR("Redirection location URL missing. rse(%.*s) %p", rse?rse->len:0, rse?STRNULLCHK(rse->s):"", rse);
# # ]
443 : 0 : return 0;
444 : : }
445 [ + - ]: 5 : ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
446 : : ? "%.*s&%.*s" CRLF2
447 : : : "%.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
448 [ - + ]: 5 : if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len, ss->s);
449 : 5 : zx_str_free(cf->ctx, rse);
450 : 5 : return ss;
451 : : }
452 : :
453 : : /*() SAMLRequest. Return the HTTP 302 redirect LOCATION header + CRLF2. You need to pass this to
454 : : * some application layer facility to effectuate the actual redirect.
455 : : * Wrapper for zxid_saml2_redir_enc(). This is different from zxid_saml2_redir_url()
456 : : * in that the entire Location header is returned, rather than just the url.
457 : : *
458 : : * cf:: ZXID configuration object, also used for memory allocation
459 : : * loc:: The URL up to query string
460 : : * pay_load:: What should be encoded in the redirect URL. Effectively becomes the query string
461 : : * relay_state:: Optional relay state argument. Ends up being encoded in the query string
462 : : * return:: HTTP Location header as ~zx_str~. The memory should be freed by the caller. */
463 : :
464 : : /* Called by: zxid_sp_mni_redir, zxid_sp_slo_redir */
465 : : struct zx_str* zxid_saml2_redir(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
466 : 2 : {
467 : : struct zx_str* ss;
468 : 2 : struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLRequest=", pay_load, relay_state);
469 [ + - - + ]: 2 : if (!loc || !rse) {
470 [ # # # # : 0 : ERR("Redirection location URL missing. rse(%.*s) %p", rse?rse->len:0, rse?STRNULLCHK(rse->s):"", rse);
# # ]
471 : 0 : return zx_dup_str(cf->ctx, "* ERR");
472 : : }
473 [ + - ]: 2 : ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
474 : : ? "Location: %.*s&%.*s" CRLF2
475 : : : "Location: %.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
476 [ - + ]: 2 : if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len - sizeof(CRLF2) + 1, ss->s);
477 : 2 : zx_str_free(cf->ctx, rse);
478 : 2 : return ss;
479 : : }
480 : :
481 : : /*() SAMLResponse. Return the HTTP 302 redirect LOCATION header + CRLF2. You
482 : : * need to pass this to some application layer facility to effectuate the actual redirect.
483 : : * Wrapper for zxid_saml2_redir_enc().
484 : : *
485 : : * cf:: ZXID configuration object, also used for memory allocation
486 : : * loc:: The URL up to query string
487 : : * pay_load:: What should be encoded in the redirect URL. Effectively becomes the query string
488 : : * relay_state:: Optional relay state argument. Ends up being encoded in the query string
489 : : * return:: HTTP Location header as ~zx_str~. The memory should be freed by the caller. */
490 : :
491 : : /* Called by: zxid_idp_dispatch, zxid_slo_resp_redir, zxid_sp_dispatch */
492 : : struct zx_str* zxid_saml2_resp_redir(zxid_conf* cf, struct zx_str* loc, struct zx_str* pay_load, char* relay_state)
493 : 2 : {
494 : : struct zx_str* ss;
495 : 2 : struct zx_str* rse = zxid_saml2_redir_enc(cf, "SAMLResponse=", pay_load, relay_state);
496 [ + - - + ]: 2 : if (!loc || !rse) {
497 [ # # # # : 0 : ERR("Redirection location(%.*s) URL missing or redirect encoding(%.*s) failed.", loc?loc->len:0, loc?loc->s:"", rse?rse->len:0, rse?rse->s:"");
# # # # ]
498 : 0 : return zx_dup_str(cf->ctx, "* ERR");
499 : : }
500 [ + - ]: 2 : ss = zx_strf(cf->ctx, (memchr(loc->s, '?', loc->len)
501 : : ? "Location: %.*s&%.*s" CRLF2
502 : : : "Location: %.*s?%.*s" CRLF2), loc->len, loc->s, rse->len, rse->s);
503 [ - + ]: 2 : if (zx_debug & ZXID_INOUT) INFO("%.*s", ss->len - sizeof(CRLF2) + 1, ss->s);
504 : 2 : zx_str_free(cf->ctx, rse);
505 : 2 : return ss;
506 : : }
507 : :
508 : : /*() Check status codes in SAML response to verify that request was completed OK.
509 : : *
510 : : * cf:: ZXID configuration object, also used for memory allocation
511 : : * cgi:: CGI variables decoded from the query string. ~err~ field of
512 : : * the CGI object will be set upon failure.
513 : : * st:: The SAML <Status> element from the response, as XML data structure
514 : : * what:: Explanatory string used in error and log messages
515 : : * return:: 1 of SAML message is OK, 0 if message is not OK. */
516 : :
517 : : /* Called by: zxid_az_soap, zxid_idp_dispatch x2, zxid_idp_soap_dispatch, zxid_sp_dispatch x3, zxid_sp_mni_soap, zxid_sp_slo_soap, zxid_sp_soap_dispatch x3 */
518 : : int zxid_saml_ok(zxid_conf* cf, zxid_cgi* cgi, struct zx_sp_Status_s* st, char* what)
519 : 95 : {
520 : : struct zx_str* ss;
521 : 95 : struct zx_str* m = 0;
522 : 95 : struct zx_str* sc1 = 0;
523 : 95 : struct zx_str* sc2 = 0;
524 : 95 : struct zx_sp_StatusCode_s* sc = st->StatusCode;
525 [ + - ]: 95 : if (!memcmp(SAML2_SC_SUCCESS, sc->Value->g.s, sc->Value->g.len)) {
526 [ + + - + ]: 95 : D("SAML ok what(%s)", what);
527 [ + - ]: 95 : if (cf->log_level>0)
528 : 95 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "K", "SAMLOK", what, 0);
529 : 95 : return 1;
530 : : }
531 [ # # # # : 0 : if (st->StatusMessage && (m = ZX_GET_CONTENT(st->StatusMessage)))
# # # # #
# ]
532 : 0 : ERR("SAML Fail what(%s) msg(%.*s)", what, m->len, m->s);
533 [ # # ]: 0 : if (sc1 = &sc->Value->g)
534 : 0 : ERR("SAML Fail what(%s) SC1(%.*s)", what, sc1->len, sc1->s);
535 [ # # ]: 0 : if (sc->StatusCode)
536 : 0 : sc2 = &sc->StatusCode->Value->g;
537 [ # # ]: 0 : for (sc = sc->StatusCode; sc; sc = sc->StatusCode)
538 : 0 : ERR("SAML Fail what(%s) subcode(%.*s)", what, sc->Value->g.len, sc->Value->g.s);
539 : :
540 [ # # # # : 0 : ss = zx_strf(cf->ctx, "SAML Fail what(%s) msg(%.*s) SC1(%.*s) subcode(%.*s)", what,
# # # # #
# # # ]
541 : : m?m->len:0, m?m->s:"",
542 : : sc1?sc1->len:0, sc1?sc1->s:"",
543 : : sc2?sc2->len:0, sc2?sc2->s:"");
544 : :
545 [ # # ]: 0 : if (cf->log_level>0)
546 : 0 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "F", "SAMLFAIL", what, ss->s);
547 : :
548 [ # # # # ]: 0 : D("SAML Response NOT OK what(%s)", what);
549 [ # # ]: 0 : if (!cgi)
550 : 0 : return 0;
551 : 0 : cgi->err = ss->s;
552 : 0 : return 0;
553 : : }
554 : :
555 : : /*() Given NameID or <EncryptedID>, return Name ID. Typically used by SSO and SLO.
556 : : * If unencrypted NameID is available, then decryption will not be attempted.
557 : : * This facilitates code that handles either encrypted or non-encrypted
558 : : * case in one line:
559 : : *
560 : : * req->NameID = zxid_decrypt_nameid(cf, req->NameID, req->EncryptedID);
561 : : *
562 : : * cf:: ZXID configuration object, also used for memory allocation
563 : : * nid:: XML data structure for Name ID. Possibly 0 (NULL). In that case ~encid~
564 : : * should be specified.
565 : : * encid:: XML Data Structure for Encrypted Name ID. If no ~nid~ is specified, this
566 : : * structure is decrypted and its contents returned as the Name ID
567 : : * return:: XML data structure corresponding to (possibly decrypted) Name ID */
568 : :
569 : : /* Called by: test_ibm_cert_problem, test_ibm_cert_problem_enc_dec, zxid_idp_slo_do, zxid_imreq, zxid_mni_do, zxid_nidmap_do, zxid_sp_slo_do, zxid_sp_sso_finalize, zxid_wsf_validate_a7n */
570 : : zxid_nid* zxid_decrypt_nameid(zxid_conf* cf, zxid_nid* nid, struct zx_sa_EncryptedID_s* encid)
571 : 49 : {
572 : : struct zx_str* ss;
573 : : struct zx_root_s* r;
574 [ + + ]: 49 : if (nid)
575 : 5 : return nid;
576 [ + - ]: 44 : if (encid) {
577 : 44 : ss = zxenc_privkey_dec(cf, encid->EncryptedData, encid->EncryptedKey);
578 [ - + ]: 44 : if (!ss) {
579 : 0 : ERR("Failed to decrypt NameID. Most probably certificate-private key mismatch or metadata problem. Could also be corrupt message. %d", 0);
580 : 0 : return 0;
581 : : }
582 : 44 : r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "dec nid");
583 [ - + ]: 44 : if (!r) {
584 : 0 : ERR("Failed to parse EncryptedID buf(%.*s)", ss->len, ss->s);
585 : 0 : return 0;
586 : : }
587 : 44 : return r->NameID;
588 : : }
589 : 0 : ERR("Neither NameID nor EncryptedID available %d", 0);
590 : 0 : return 0;
591 : : }
592 : :
593 : : /*() Given new nym or <NewEncryptedID>, return Name ID. Typically used by Name ID Management
594 : : *
595 : : * cf:: ZXID configuration object, also used for memory allocation
596 : : * newnym:: XML data structure for new Name ID. Possibly 0 (NULL). In that case ~encid~
597 : : * should be specified.
598 : : * encid:: XML Data Structure for Encrypted Name ID. If no ~newnym~ is specified, this
599 : : * structure is decrypted and its contents returned as the Name ID
600 : : * return:: XML data structure corresponding to (possibly decrypted) new Name ID */
601 : :
602 : : /* Called by: zxid_mni_do */
603 : : struct zx_str* zxid_decrypt_newnym(zxid_conf* cf, struct zx_str* newnym, struct zx_sp_NewEncryptedID_s* encid)
604 : 1 : {
605 : : struct zx_str* ss;
606 : : struct zx_root_s* r;
607 [ - + ]: 1 : if (newnym)
608 : 0 : return newnym;
609 [ + - ]: 1 : if (encid) {
610 : 1 : ss = zxenc_privkey_dec(cf, encid->EncryptedData, encid->EncryptedKey);
611 : 1 : r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "dec newnym");
612 [ - + ]: 1 : if (!r) {
613 : 0 : ERR("Failed to parse NewEncryptedID buf(%.*s)", ss->len, ss->s);
614 : 0 : return 0;
615 : : }
616 [ + - + - : 1 : return ZX_GET_CONTENT(r->NewID);
+ - ]
617 : : }
618 : 0 : ERR("Neither NewNameID nor NewEncryptedID available %d", 0);
619 : 0 : return 0;
620 : : }
621 : :
622 : : /*(i) Check single item signature on given Request, Response, or Assertion. Typical usage
623 : : *
624 : : * if (!zxid_chk_sig(cf, cgi, ses, (struct zx_elem_s*)req,
625 : : * req->Signature, req->Issuer, "LogoutRequest"))
626 : : * return 0;
627 : : *
628 : : * cf:: ZXID configuration and context object, used for settings and memory allocation
629 : : * cgi:: cgi or invocation variables object. cgi->sigval and cgi->sigmsg
630 : : * will be altered, if there is any signature.
631 : : * ses:: Session object. The ses->sigres will be altered to reflect result
632 : : * of verification, if there is signature.
633 : : * elem:: Element that was signed, usually needs type cast.
634 : : * sig:: Signature element within elem
635 : : * issue_ent:: The EntityID zx_str of the signer (Issuer)
636 : : * pop_seen:: Namespaces collected from outer layers
637 : : * lk:: Log key
638 : : * return:: 0 if sig check could not be made due to error, 1 if there was
639 : : * no signature to check, 2 if check was made, in which case the result is
640 : : * in ses->sigres, 3 if check was not possible (due to error), but sig was not
641 : : * configured to be required (NOSIG_FATAL option).
642 : : *
643 : : * See also: Signature validation codes VVV in zxid-log.pd, section "ZXID Log Format".
644 : : * N.B: If the signature is over multiple references, you need to do many processing steps
645 : : * manually and then call zxsig_validate() with correctly populate refs array.
646 : : */
647 : :
648 : : /* Called by: sig_validate, zxid_idp_slo_do, zxid_mni_do, zxid_sp_dig_sso_a7n, zxid_sp_slo_do, zxid_xacml_az_cd1_do, zxid_xacml_az_do */
649 : : int zxid_chk_sig(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_elem_s* elem, struct zx_ds_Signature_s* sig, struct zx_sa_Issuer_s* issue_ent, struct zx_ns_s* pop_seen, const char* lk)
650 : 112 : {
651 : 112 : struct zx_str* issuer = 0;
652 : : struct zxsig_ref refs;
653 : : zxid_entity* idp_meta;
654 : 112 : char* err = "S"; /* See: RES in zxid-log.pd, section "ZXID Log Format" */
655 : :
656 [ + + + + : 112 : if (!sig) { D("No signature in %s", lk); return 1; /* Not an error */ }
- + ]
657 [ + - - + ]: 101 : if (!sig->SignedInfo || !sig->SignedInfo->Reference) {
658 : 0 : ERR("Malformed signature in %s, missing mandatory SignedInfo(%p) or Reference", lk, sig->SignedInfo);
659 : 0 : cgi->sigval = "M";
660 : 0 : cgi->sigmsg = "Malformed signature.";
661 : 0 : ses->sigres = ZXSIG_NO_SIG;
662 : 0 : err = "C";
663 : 0 : goto erro;
664 : : }
665 : :
666 [ + - + - : 101 : if (!issue_ent || !(issuer = ZX_GET_CONTENT(issue_ent)) || !issuer->len || !issuer->s[0]) {
+ - + - +
- + - -
+ ]
667 : 0 : ERR("Issuer of %s is empty although %s was signed. %p", lk, lk, issuer);
668 : 0 : cgi->sigval = "I";
669 : 0 : cgi->sigmsg = "Issuer of signed Response missing.";
670 : 0 : ses->sigres = ZXSIG_NO_SIG;
671 [ # # ]: 0 : if (!cf->nosig_fatal)
672 : 0 : goto nosig_allow;
673 : 0 : err = "C";
674 : 0 : goto erro;
675 : : }
676 : :
677 : 101 : idp_meta = zxid_get_ent_ss(cf, issuer);
678 [ - + ]: 101 : if (!idp_meta) {
679 : 0 : ERR("Unable to find metadata for Issuer(%.*s).", issuer->len, issuer->s);
680 : 0 : cgi->sigval = "I";
681 : 0 : cgi->sigmsg = "Issuer of signed Response unknown.";
682 : 0 : ses->sigres = ZXSIG_NO_SIG;
683 [ # # ]: 0 : if (!cf->nosig_fatal)
684 : 0 : goto nosig_allow;
685 : 0 : err = "P"; /* Policy issue */
686 : 0 : goto erro;
687 : : }
688 : :
689 : 101 : ZERO(&refs, sizeof(refs));
690 : 101 : refs.sref = sig->SignedInfo->Reference;
691 : 101 : refs.blob = elem;
692 : 101 : refs.pop_seen = pop_seen;
693 : 101 : zx_see_elem_ns(cf->ctx, &refs.pop_seen, elem);
694 : 101 : ses->sigres = zxsig_validate(cf->ctx, idp_meta->sign_cert, sig, 1, &refs);
695 : 101 : zxid_sigres_map(ses->sigres, &cgi->sigval, &cgi->sigmsg);
696 [ + + - + ]: 101 : D("Response sigres(%d)", ses->sigres);
697 : 101 : return 2;
698 : :
699 : 0 : nosig_allow:
700 : 0 : return 3;
701 : :
702 : 0 : erro:
703 : 0 : cgi->msg = "SSO failed due to Response that was signed, but badly (or did not have Issuer).";
704 [ # # # # ]: 0 : zxlog(cf, 0, 0, 0, issuer, 0, 0, 0,
705 : : cgi->sigval, err, ses->nidfmt?"FEDSSO":"TMPSSO", ses->sesix?ses->sesix:"-", "Error.");
706 : 0 : return 0;
707 : : }
708 : :
709 : : /*() Figure out sp_name_buf corresponding to affiliation */
710 : :
711 : : /* Called by: zxid_add_fed_tok2epr, zxid_map_val_ss x3 */
712 : : struct zx_str* zxid_get_affil_and_sp_name_buf(zxid_conf* cf, zxid_entity* meta, char* sp_name_buf)
713 : 1762 : {
714 : : struct zx_str* affil;
715 [ + - + - : 1762 : if (meta && meta->ed && meta->ed->AffiliationDescriptor
- + # # #
# # # ]
716 : : && (affil = &meta->ed->AffiliationDescriptor->affiliationOwnerID->g)
717 : : && affil->s && affil->len)
718 : : ; /* affil is good */
719 : : else
720 [ + - + - ]: 1762 : affil = meta && meta->ed ? &meta->ed->entityID->g : 0;
721 [ - + ]: 1762 : if (!affil) {
722 [ # # ]: 0 : ERR("Unable to determine affiliation ID or provider ID. Metadata missing? %p %p", meta, meta?meta->ed:0);
723 : 0 : *sp_name_buf = 0;
724 : 0 : return 0;
725 : : }
726 : 1762 : zxid_nice_sha1(cf, sp_name_buf, ZXID_MAX_SP_NAME_BUF, affil, affil, 7);
727 : 1762 : return affil;
728 : : }
729 : :
730 : : /* Called by: zxid_add_fed_tok2epr, zxid_map_val_ss x2, zxid_sso_issue_a7n */
731 : : zxid_nid* zxid_get_fed_nameid(zxid_conf* cf, struct zx_str* prvid, struct zx_str* affil, const char* uid, const char* sp_name_buf, int allow_create, int want_transient, struct timeval* srcts, struct zx_str* id, char* logop)
732 : 1225 : {
733 : 1225 : zxid_nid* nameid = zxid_check_fed(cf, affil, uid, allow_create, srcts, prvid, id, sp_name_buf);
734 [ + + ]: 1225 : if (nameid) {
735 [ - + ]: 1224 : if (want_transient) {
736 [ # # # # ]: 0 : D("Despite old fed, using transient due to want_transient=%d", want_transient);
737 : 0 : zxid_mk_transient_nid(cf, nameid, sp_name_buf, uid);
738 [ # # ]: 0 : if (logop) strcpy(logop, "TMPDI");
739 : : } else
740 [ + + ]: 1224 : if (logop) strcpy(logop, "FEDDI");
741 : : } else {
742 [ - + # # ]: 1 : D("No nameid (because of no federation), using transient %d", 0);
743 : 1 : nameid = zx_NEW_sa_NameID(cf->ctx,0);
744 : 1 : zxid_mk_transient_nid(cf, nameid, sp_name_buf, uid);
745 [ - + ]: 1 : if (logop) strcpy(logop, "TMPDI");
746 : : }
747 : 1225 : return nameid;
748 : : }
749 : :
750 : : /*() Transform content according to map. The returned zx_str will be nul terminated.
751 : : * The list ends up being built in reverse order, which at runtime
752 : : * causes last stanzas to be evaluated first and first match is used.
753 : : * Thus you should place most specific rules last and most generic rules first.
754 : : * See also: zxid_load_map() and zxid_find_map() */
755 : :
756 : : /* Called by: zxid_add_at_values, zxid_map_val */
757 : : struct zx_str* zxid_map_val_ss(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zxid_map* map, const char* atname, struct zx_str* val)
758 : 8163 : {
759 : : zxid_a7n* a7n;
760 : : zxid_nid* nameid;
761 : : struct zx_sa_AttributeStatement_s* at_stmt;
762 : : struct zx_str* prvid;
763 : : struct zx_str* affil;
764 : : struct zx_str* ss;
765 : : char buf[MIN(ZXID_MAX_SP_NAME_BUF,4096)];
766 : : char* bin;
767 : : char* p;
768 : : int len;
769 [ - + ]: 8163 : if (!val) {
770 : 0 : ERR("NULL ponter as val %p", map);
771 : 0 : val = zx_dup_str(cf->ctx, "");
772 : : }
773 [ + + ]: 8163 : if (!map)
774 : 1062 : return val;
775 : :
776 [ + + + + : 7101 : switch (map->rule & ZXID_MAP_RULE_WRAP_MASK) {
- ]
777 : 5469 : case 0: break; /* No wrap */
778 : : case ZXID_MAP_RULE_WRAP_A7N: /* 0x10 Wrap the attribute in SAML2 assertion */
779 [ + - + - : 680 : if (!meta || !ses || !ses->uid) {
- + ]
780 : 0 : ERR("MAP_RULE_WRAP_A7N requires SP metadata and session to be specified. %p %p", meta, ses);
781 : 0 : break;
782 : : }
783 : 680 : affil = zxid_get_affil_and_sp_name_buf(cf, meta, buf);
784 [ + - ]: 680 : prvid = meta->ed ? &meta->ed->entityID->g : 0;
785 : 680 : nameid = zxid_get_fed_nameid(cf, prvid, affil, ses->uid, buf, cf->di_allow_create,
786 : : (cf->di_nid_fmt == 't'), 0, 0, 0);
787 : :
788 : 680 : at_stmt = zx_NEW_sa_AttributeStatement(cf->ctx, 0);
789 [ + - + + : 680 : if (map->dst && *map->dst && map->src && map->src[0] != '*')
+ - + - ]
790 : 136 : atname = map->dst;
791 : 680 : at_stmt->Attribute = zxid_mk_sa_attribute_ss(cf, &at_stmt->gg, atname, 0, val);
792 : :
793 : 680 : a7n = zxid_mk_a7n(cf, prvid, zxid_mk_subj(cf, 0, meta, nameid), 0, at_stmt);
794 : 680 : zxid_anoint_a7n(cf, 1, a7n, prvid, "map_val", ses->uid);
795 : 680 : val = zx_easy_enc_elem_opt(cf, &a7n->gg);
796 : 680 : break;
797 : : case ZXID_MAP_RULE_WRAP_X509: /* 0x20 Wrap the attribute in X509 attribute certificate */
798 [ + - + - : 408 : if (!meta || !ses || !ses->uid) {
- + ]
799 : 0 : ERR("MAP_RULE_WRAP_X509 requires SP metadata and session to be specified. %p %p", meta, ses);
800 : 0 : break;
801 : : }
802 : 408 : affil = zxid_get_affil_and_sp_name_buf(cf, meta, buf);
803 [ + - ]: 408 : prvid = meta->ed ? &meta->ed->entityID->g : 0;
804 : 408 : nameid = zxid_get_fed_nameid(cf, prvid, affil, ses->uid, buf, cf->di_allow_create,
805 : : (cf->di_nid_fmt == 't'), 0, 0, 0);
806 : :
807 [ + - - + : 408 : if (map->dst && *map->dst && map->src && map->src[0] != '*')
# # # # ]
808 : 0 : atname = map->dst;
809 : 408 : zxid_mk_at_cert(cf, sizeof(buf), buf, "map_val", nameid, atname, val);
810 : 408 : val = zx_dup_str(cf->ctx, buf);
811 : 408 : break;
812 : : case ZXID_MAP_RULE_WRAP_FILE: /* 0x30 Get attribute value from file specified in ext */
813 [ + - + - : 544 : if (!meta || !ses || !ses->uid) {
- + ]
814 : 0 : ERR("MAP_RULE_WRAP_FILE requires SP metadata and session to be specified. %p %p", meta, ses);
815 : 0 : break;
816 : : }
817 : 544 : zxid_get_affil_and_sp_name_buf(cf, meta, buf);
818 [ + - - + ]: 544 : if (!map->ext || !*map->ext) {
819 : 0 : ERR("WRAP_FILE rule without file name in ext field of stanza %p", map->ext);
820 : 0 : break;
821 [ - + ]: 544 : } else if (!strcmp(map->ext, "_VAL_FROM_FILE")) {
822 [ # # # # ]: 0 : D("_VAL_FROM_FILE specified, taking filename from attribute value(%.*s)", val->len, val->s);
823 : 0 : p = zx_str_to_c(cf->ctx, val);
824 : : } else {
825 : 544 : p = map->ext;
826 : : }
827 : 544 : bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
828 : : "%s" ZXID_UID_DIR "%s/%s/%s", cf->path, ses->uid, buf, p);
829 [ + + ]: 544 : if (!bin)
830 : 538 : bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
831 : : "%s" ZXID_UID_DIR "%s/.bs/%s", cf->path, ses->uid, p);
832 [ + + ]: 544 : if (!bin)
833 : 402 : bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
834 : : "%s" ZXID_UID_DIR ".all/%s/%s", cf->path, buf, p);
835 [ + + ]: 544 : if (!bin)
836 : 396 : bin = read_all_alloc(cf->ctx, "map_val from file", 0, &len,
837 : : "%s" ZXID_UID_DIR ".all/.bs/%s", cf->path, p);
838 [ + + ]: 544 : if (bin) {
839 : 284 : val = zx_ref_len_str(cf->ctx, len, bin);
840 [ + - - + ]: 284 : D("FILE RULE uid(%s) sp_name_buf(%s) file(%s) GOT(%.*s)",ses->uid,buf,p,val->len,val->s);
841 : : } else {
842 [ + - ]: 260 : INFO("Attribute(%s) value not found in any file(%s" ZXID_UID_DIR "%s/%s/%s)", STRNULLCHKQ(atname), cf->path, ses->uid, buf, p);
843 : 260 : val = zx_ref_str(cf->ctx, "");
844 : : }
845 : 544 : break;
846 : : default:
847 [ # # # # ]: 0 : NEVER("unknow map_val rule=%x", map->rule);
848 : : }
849 : :
850 [ + + + + : 7101 : switch (map->rule & ZXID_MAP_RULE_ENC_MASK) {
+ + + - ]
851 : 5596 : case ZXID_MAP_RULE_RENAME: ss = val; break;
852 : : case ZXID_MAP_RULE_FEIDEDEC: /* Norway */
853 : : /* "feide": FEIDE currently (2008) stores several values in a single
854 : : * AttributeValue element. The values are base64 encoded
855 : : * and separated by an underscore. This decoder reverses that encoding. */
856 : : DD("*** FEIDEDEC only base64 decodes one attribute: it does not handle the concatenatenation with _ of several attributes. val_before(%.*s)", val->len, val->s);
857 : 3 : ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
858 : 3 : p = unbase64_raw(val->s, val->s + val->len, ss->s, zx_std_index_64);
859 : 3 : *p = 0;
860 : 3 : ss->len = p - ss->s;
861 : 3 : break;
862 : : case ZXID_MAP_RULE_FEIDEENC: /* Norway */
863 : : DD("*** FEIDEENC only base64 encodes one attribute: it does not concatenate with _ several attributes. %d", 0);
864 : 544 : ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(val->len));
865 : 544 : base64_fancy_raw(val->s, val->len, ss->s, std_basis_64, 1<<31, 0, 0, '=');
866 : 544 : break;
867 : : case ZXID_MAP_RULE_UNSB64_INF: /* Decode safebase64-inflate ([RFC3548], [RFC1951]) */
868 : 3 : bin = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
869 : 3 : p = unbase64_raw(val->s, val->s + val->len, bin, zx_std_index_64);
870 : 3 : ss = ZX_ZALLOC(cf->ctx, struct zx_str);
871 : 3 : ss->s = zx_zlib_raw_inflate(cf->ctx, p-bin, bin, &ss->len);
872 : 3 : ZX_FREE(cf->ctx, bin);
873 [ - + ]: 3 : if (!ss->s) {
874 : 0 : ss->len = 0;
875 : 0 : ss->s = "";
876 : 0 : return ss; /* should return 0, but caller may be assuming this can not fail */
877 : : }
878 : 3 : ss->s[ss->len] = 0;
879 : 3 : break;
880 : : case ZXID_MAP_RULE_DEF_SB64: /* Encode gzip-safebase64 ([RFC1951], [RFC3548]) */
881 : 544 : bin = zx_zlib_raw_deflate(cf->ctx, val->len, val->s, &len);
882 [ - + ]: 544 : if (!bin) {
883 : 0 : return zx_dup_str(cf->ctx, "");
884 : : }
885 : 544 : ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(len));
886 : 544 : base64_fancy_raw(bin, len, ss->s, safe_basis_64, 1<<31, 0, 0, '=');
887 : 544 : ZX_FREE(cf->ctx, bin);
888 : 544 : break;
889 : : case ZXID_MAP_RULE_UNSB64: /* NZ: Decode safebase64 ([RFC3548]) */
890 : 3 : ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(val->len));
891 : 3 : p = unbase64_raw(val->s, val->s + val->len, ss->s, zx_std_index_64);
892 : 3 : *p = 0;
893 : 3 : ss->len = p - ss->s;
894 : 3 : break;
895 : : case ZXID_MAP_RULE_SB64: /* NZ: Encode safebase64 ([RFC3548]) */
896 : 408 : ss = zx_new_len_str(cf->ctx, SIMPLE_BASE64_LEN(val->len));
897 : 408 : base64_fancy_raw(val->s, val->len, ss->s, safe_basis_64, 1<<31, 0, 0, '=');
898 : 408 : break;
899 : : default:
900 [ # # # # ]: 0 : NEVER("unknow map_val rule=%x", map->rule);
901 : : }
902 : 7101 : return ss;
903 : : }
904 : :
905 : : /* Called by: pool2apache x2, zxid_add_mapped_attr, zxid_pepmap_extract x2, zxid_pool_to_json x2, zxid_pool_to_ldif x2, zxid_pool_to_qs x2 */
906 : 7101 : struct zx_str* zxid_map_val(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zxid_map* map, const char* atname, const char* val) {
907 [ + + ]: 7101 : return zxid_map_val_ss(cf, ses, meta, map, atname, zx_dup_str(cf->ctx, STRNULLCHK(val)));
908 : : }
909 : :
910 : : /*() Extract from a string representing SOAP envelope, the payload part in the body. */
911 : :
912 : : /* Called by: zxcall_main */
913 : : char* zxid_extract_body(zxid_conf* cf, char* enve)
914 : 16 : {
915 : : char* p;
916 : : char* q;
917 : :
918 [ - + ]: 16 : if (!p)
919 : 0 : goto nobody;
920 [ + - ]: 16 : for (p = enve; p; p+=4) {
921 : 16 : p = strstr(p, "Body");
922 [ - + ]: 16 : if (!p) {
923 : 0 : nobody:
924 [ # # ]: 0 : ERR("Response does not contain <Body> res(%s)", STRNULLCHKD(enve));
925 : 0 : return 0;
926 : : }
927 [ + - + - : 16 : if (p > enve && ONE_OF_2(p[-1], '<', ':') && ONE_OF_5(p[4], '>', ' ', '\t', '\r', '\n'))
+ - + - -
+ # # # #
# # ]
928 : : break; /* Opening <Body> detected. */
929 : : }
930 [ - + ]: 16 : if (!p)
931 : 0 : goto nobody;
932 : :
933 : 16 : p = strchr(p+4, '>'); /* Scan for close of opening <Body */
934 [ - + ]: 16 : if (!p)
935 : 0 : goto nobody;
936 : :
937 [ + - ]: 16 : for (q = ++p; q; q+=5) {
938 : 16 : q = strstr(q, "Body>");
939 [ - + ]: 16 : if (!q)
940 : 0 : goto nobody; /* Missing closing </Body> tag */
941 [ + - - + ]: 16 : if (ONE_OF_2(q[-1], '<', ':'))
942 : : break;
943 : : }
944 [ + + ]: 16 : for (--q; *q != '<'; --q) ; /* Scan for the start of </Body>, skipping any namespace prefix */
945 : :
946 : 16 : enve = ZX_ALLOC(cf->ctx, q-p+1);
947 : 16 : memcpy(enve, p, q-p);
948 : 16 : enve[q-p] = 0;
949 : 16 : return enve;
950 : : }
951 : :
952 : : /*() Get symmetric key, generating it if necessary. */
953 : :
954 : : /* Called by: zxid_psobj_key_setup, zxlog_write_line */
955 : : char* zx_get_symkey(zxid_conf* cf, const char* keyname, char* symkey)
956 : 24 : {
957 : : char buf[1024];
958 : 24 : int um, gotall = read_all(sizeof(buf), buf, "symkey", 1, "%s" ZXID_PEM_DIR "%s", cf->path, keyname);
959 [ + - + - ]: 24 : if (!gotall && cf->auto_cert) {
960 : 24 : INFO("AUTO_CERT: generating symmetric encryption key in %s" ZXID_PEM_DIR "%s", cf->path, keyname);
961 : 24 : gotall = 128 >> 3;
962 : 24 : zx_rand(buf, gotall);
963 : 24 : um = umask(0077); /* Key material should be readable only by owner */
964 : 24 : write_all_path_fmt("auto_cert", sizeof(buf), buf,
965 : : "%s" ZXID_PEM_DIR "%s", cf->path, keyname, "%.*s", gotall, buf);
966 : 24 : umask(um);
967 : : }
968 : 24 : SHA1((unsigned char*)buf, gotall, (unsigned char*)symkey);
969 : 24 : return symkey;
970 : : }
971 : :
972 : : /* EOF -- zxidlib.c */
|