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