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