1 /* zxidwsp.c  -  Handwritten nitty-gritty functions for Liberty ID-WSF Web Services Provider
2  * Copyright (c) 2013-2015 Synergetics NV (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2009-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: zxidwsc.c,v 1.16 2009-11-20 20:27:13 sampo Exp $
11  *
12  * 22.11.2009, created --Sampo
13  * 7.1.2010,   added WSP signing --Sampo
14  * 31.5.2010,  reworked PEPs extensively --Sampo
15  * 25.1.2011,  tweaked RelatesTo header --Sampo
16  * 26.10.2013, improved error reporting on credential expired case --Sampo
17  * 12.3.2014,  added partial mime multipart support --Sampo
18  * 19.2.2015,  fixed Action header detection in the non-XML body case --Sampo
19  */
20 
21 #include "platform.h"  /* needed on Win32 for pthread_mutex_lock() et al. */
22 #include <string.h>
23 #include "errmac.h"
24 #include "zx.h"
25 #include "zxid.h"
26 #include "zxidpriv.h"
27 #include "zxidutil.h"
28 #include "zxidconf.h"
29 #include "saml2.h"
30 #include "wsf.h"
31 #include "c/zx-const.h"
32 #include "c/zx-ns.h"
33 #include "c/zx-data.h"
34 
35 #define BOOL_STR_TEST(x) ((x) && (x) != '0')
36 
37 /*() Create SOAP header Action (which is distinct from HTTP headers SOAPaction).
38  * This is driven by the configuration option WSC_ACTION_HDR */
39 
zxid_add_action_hdr(zxid_conf * cf,zxid_ses * ses,struct zx_e_Envelope_s * env)40 static void zxid_add_action_hdr(zxid_conf* cf, zxid_ses* ses, struct zx_e_Envelope_s* env)
41 {
42   struct zx_e_Header_s* hdr = env->Header;
43   struct zx_elem_s* first;
44   struct zx_el_tok* el_tok;
45   struct zx_str* ss;
46   char* p;
47 
48   if (!strcmp(cf->wsc_action_hdr, "#ses")) {
49     ERR("***NOT IMPLEMENTED %d", 0);  /* *** TBD */
50     return;
51   } else if (!strcmp(cf->wsc_action_hdr, "#body1st")) {
52     if (env->Body && (first = env->Body->gg.kids)) {
53       if (first->g.s && first->g.tok != ZX_TOK_DATA) {
54 	if (!(p = memchr(first->g.s, ':', first->g.len))) {
55 	  ss = &first->g;
56 	} else {
57 	  ++p;
58 	  ss = zx_ref_len_str(cf->ctx, first->g.len - (p - first->g.s), p);
59 	}
60       } else {
61 	if (el_tok = zx_get_el_tok(first)) {
62 	  ss = zx_ref_str(cf->ctx, el_tok->name);
63 	} else {
64 	  ERR("First child element of <e:Body> does not have tag string and is not known token %x", first->g.tok);
65 	  return;
66 	}
67       }
68     } else {
69       ERR("Tried to set <a:Action> SOAP header from first child of <e:Body>, but the body does not exist or does not have child element (e.g. JSON or other non-XML body) %p", env->Body);
70       return;
71     }
72   } else if (!strcmp(cf->wsc_action_hdr, "#body1stns")) {
73     if (env->Body && (first = env->Body->gg.kids)) {
74       if (first->g.s && first->g.tok != ZX_TOK_DATA) {
75 	if (!(p = memchr(first->g.s, ':', first->g.len))) {
76 	  ss = zx_strf(cf->ctx, "%s:%.*s", first->ns&&first->ns->url?first->ns->url:"", first->g.len, first->g.s);
77 	} else {
78 	  ++p;
79 	  ss = zx_strf(cf->ctx, "%s:%.*s", first->ns&&first->ns->url?first->ns->url:"", first->g.len - (p - first->g.s), p);
80 	}
81       } else {
82 	if (el_tok = zx_get_el_tok(first)) {
83 	  ss = zx_strf(cf->ctx, "%s:%s", first->ns&&first->ns->url?first->ns->url:"", el_tok->name);
84 	} else {
85 	  ERR("First child element of <e:Body> does not have tag string and is not known token %x", first->g.tok);
86 	  return;
87 	}
88       }
89     } else {
90       ERR("Tried to set <a:Action> SOAP header from first child of <e:Body>, but the Body does not exist or does not have child element %p", env->Body);
91       return;
92     }
93   } else {
94     ss = zx_ref_str(cf->ctx, cf->wsc_action_hdr);
95   }
96   hdr->Action = zx_NEW_a_Action(cf->ctx, &hdr->gg);
97   hdr->Action->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->Action->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
98   hdr->Action->actor = zx_ref_attr(cf->ctx, &hdr->Action->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
99   zx_add_content(cf->ctx, &hdr->Action->gg, ss);
100 }
101 
102 /* Possible child elements of e:Header and their order (see c/zx-elem.c, generated from sg)
103 paos_Request
104 paos_Response
105 ecp_Request
106 ecp_Response
107 ecp_RelayState
108 1.    sbf_Framework
109 2.    b_Sender
110 3.    a_MessageID
111 4.    wsse_Security
112 5.    tas3_Status
113 6.rs  a_RelatesTo
114 6.rq  a_ReplyTo      Often omitted, defaults to anonymous, i.e. other end of TCP conn.
115 a_From               Not used in ID-WSF2
116 a_FaultTo            Omitted, defaults to ReplyTo
117 7.rq  a_To
118 8.rq  a_Action
119 a_ReferenceParameters
120 b_Framework
121 9.rq  b_TargetIdentity
122 b_CredentialsContext
123 b_EndpointUpdate
124 b_Timeout
125 b_ProcessingContext
126 b_Consent
127 10.   b_UsageDirective
128 b_ApplicationEPR
129 b_UserInteraction
130 b_RedirectRequest
131 b12_Correlation
132 b12_Provider
133 b12_ProcessingContext
134 b12_Consent
135 b12_UsageDirective
136 mm7_TransactionID
137 tas3_Credentials
138 tas3_ESLPolicies
139  */
140 
141 /*(i) zxid_wsf_decor() implements the main low level ID-WSF web service call logic, including
142  * preparation of SOAP headers, use of sec mech (e.g. preparation of wsse:Security
143  * header and signing of appropriate compoments of the message), and sequencing
144  * of the call. In particular, it is possible that WSP requests user interaction
145  * and thus the caller web application will need to perform a redirect and then
146  * later call this function again to continue the web service call after interaction.
147  *
148  * env (rather than Body) is taken as argument so that caller can prepare
149  * additional SOAP headers at will before calling this function. This function
150  * will add Liberty ID-WSF specific SOAP headers.
151  * The returned lists are in reverse order, remember to call zx_reverse_elem_lists(),
152  * unless is_resp is set in which case the list is in forward order.
153  * epr must be set for request and can be null for response. */
154 
155 /* Called by:  covimp_test x2, zxid_soap_cgi_resp_body, zxid_wsc_prep, zxid_wsp_decorate x2 */
zxid_wsf_decor(zxid_conf * cf,zxid_ses * ses,struct zx_e_Envelope_s * env,int is_resp,zxid_epr * epr)156 int zxid_wsf_decor(zxid_conf* cf, zxid_ses* ses, struct zx_e_Envelope_s* env, int is_resp, zxid_epr* epr)
157 {
158   struct zx_wsse_Security_s* sec;
159   struct zx_e_Header_s* hdr;
160 
161   if (!env || !env->Body) {
162     ERR("NULL SOAP envelope or body %p", env);
163     return 0;
164   }
165 
166   if (!env->Header)
167     env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
168   hdr = env->Header;
169 
170   /* 1. Populate SOAP headers. */
171 
172   hdr->Framework = zx_NEW_sbf_Framework(cf->ctx, &hdr->gg);
173   hdr->Framework->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->Framework->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
174   hdr->Framework->actor = zx_ref_attr(cf->ctx, &hdr->Framework->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
175   hdr->Framework->version = zx_ref_attr(cf->ctx, &hdr->Framework->gg, zx_version_ATTR, "2.0");
176 
177 #if 1
178   /* 2. *** Conor claims Sender is not mandatory */
179   if (!hdr->Sender || !hdr->Sender->providerID) {
180     hdr->Sender = zx_NEW_b_Sender(cf->ctx, &hdr->gg);
181     hdr->Sender->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->Sender->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
182     hdr->Sender->actor = zx_ref_attr(cf->ctx, &hdr->Sender->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
183     hdr->Sender->providerID = zxid_my_ent_id_attr(cf, &hdr->Sender->gg, zx_providerID_ATTR);
184     if (cf->affiliation)
185       hdr->Sender->affiliationID = zx_ref_attr(cf->ctx, &hdr->Sender->gg, zx_affiliationID_ATTR, cf->affiliation);
186   } else {
187     D("Using caller supplied Sender(%.*s)", hdr->Sender->providerID->g.len, hdr->Sender->providerID->g.s);
188   }
189 #endif
190   /* 3. MessageID */
191   if (!hdr->MessageID) {
192     hdr->MessageID = zx_NEW_a_MessageID(cf->ctx, &hdr->gg);
193     hdr->MessageID->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->MessageID->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
194     hdr->MessageID->actor = zx_ref_attr(cf->ctx, &hdr->MessageID->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
195   } else {
196     D("Using caller supplied MessageID(%.*s)", ZX_GET_CONTENT_LEN(hdr->MessageID), ZX_GET_CONTENT_S(hdr->MessageID));
197   }
198 
199   /* 4. Security */
200 
201   sec = hdr->Security = zx_NEW_wsse_Security(cf->ctx, &hdr->gg);
202   sec->actor = zx_ref_attr(cf->ctx, &sec->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
203   sec->mustUnderstand = zx_ref_attr(cf->ctx, &sec->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
204   sec->Timestamp = zx_NEW_wsu_Timestamp(cf->ctx, &sec->gg);
205   sec->Timestamp->Created = zx_NEW_wsu_Created(cf->ctx, &sec->Timestamp->gg);
206   zx_reverse_elem_lists(&sec->gg);
207 
208   /* 5. Status */
209 
210   if (ses && ses->curstatus) {
211     ZX_ADD_KID(hdr, Status, ses->curstatus);
212   }
213 
214   if (is_resp) {
215 
216     /* 6.rs: RelatesTo and other WSA headers... */
217 
218     if (ses && ses->wsp_msgid && ses->wsp_msgid->len) {
219       D("wsp_msgid(%.*s) %p %d %p", ses->wsp_msgid->len, ses->wsp_msgid->s, ses->wsp_msgid, ses->wsp_msgid->len, ses->wsp_msgid->s);
220       hdr->RelatesTo = zx_NEW_a_RelatesTo(cf->ctx, &hdr->gg);
221       zx_add_content(cf->ctx, &hdr->RelatesTo->gg, ses->wsp_msgid);
222       hdr->RelatesTo->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->RelatesTo->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
223       hdr->RelatesTo->actor = zx_ref_attr(cf->ctx, &hdr->RelatesTo->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
224     } else {
225       ERR("RelatesTo header not created due to missing wsp_msgid. Are you passing same session to zxid_wsp_validate() and zxid_wsp_decorate()? %p", ses);
226     }
227   }
228 
229 #if 0
230   /* <a:From> is not used by ID-WSF2 as it is redundant with <b:Sender> */
231   hdr->From = zx_NEW_a_From(cf->ctx, &hdr->gg);
232   hdr->From->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->From->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
233   hdr->From->actor = zx_ref_attr(cf->ctx, &hdr->From->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
234   hdr->From->Address = zxid_mk_addr(cf, zx_strf(cf->ctx, "%s?o=P", cf->burl));
235 #endif
236 
237 
238   /* 7.rq a:To */
239 
240   if (!is_resp && cf->wsc_to_hdr && strcmp(cf->wsc_to_hdr, "#inhibit")) {
241     hdr->To = zx_NEW_a_To(cf->ctx, &hdr->gg);
242     if (!strcmp(cf->wsc_to_hdr, "#url")) {
243       if (epr && epr->Address) {
244 	zx_add_content(cf->ctx, &hdr->To->gg, ZX_GET_CONTENT(epr->Address));
245       } else {
246 	ERR("WSC_TO_HDR specified as #url, but no epr supplied %p (programmer error)", epr);
247       }
248     } else {
249       zx_add_content(cf->ctx, &hdr->To->gg, zx_dup_str(cf->ctx, cf->wsc_to_hdr));
250     }
251     hdr->To->mustUnderstand = zx_ref_attr(cf->ctx,&hdr->To->gg,zx_e_mustUnderstand_ATTR,XML_TRUE);
252     hdr->To->actor = zx_ref_attr(cf->ctx, &hdr->To->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
253   }
254 
255   /* 8. a:Action */
256 
257   if (!is_resp && !hdr->Action && cf->wsc_action_hdr)
258     zxid_add_action_hdr(cf, ses, env);
259 
260 #if 0
261   hdr->ReferenceParameters = zx_NEW_a_ReferenceParameters(cf->ctx, &hdr->gg);
262   hdr->ReferenceParameters->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->ReferenceParameters->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
263   hdr->ReferenceParameters->actor = zx_ref_attr(cf->ctx, &hdr->ReferenceParameters->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
264 #endif
265 
266 #if 0
267   hdr->Credentials = zx_NEW_tas3_Credentials(cf->ctx, &hdr->gg);
268   hdr->Credentials->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->Credentials->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
269   hdr->Credentials->actor = zx_ref_attr(cf->ctx, &hdr->Credentials->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
270 #endif
271 
272 #if 0
273   /* If you want this header, you should
274    * create it prior to calling zxid_wsc_call() */
275   hdr->UsageDirective = zx_NEW_b_UsageDirective(cf->ctx, &hdr->gg);
276   hdr->UsageDirective->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->UsageDirective->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
277   hdr->UsageDirective->actor = zx_ref_attr(cf->ctx, &hdr->UsageDirective->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
278 #endif
279 
280 #if 0
281   /* Interaction or redirection. If you want this header, you should
282    * create it prior to calling zxid_wsc_call() */
283   hdr->UserInteraction = zx_NEW_b_UserInteraction(cf->ctx, &hdr->gg);
284   hdr->UserInteraction->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->UserInteraction->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
285   hdr->UserInteraction->actor = zx_ref_attr(cf->ctx, &hdr->UserInteraction->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
286 #endif
287 
288   if (is_resp) {
289     zx_add_content(cf->ctx, &sec->Timestamp->Created->gg, zxid_date_time(cf, time(0)));
290     if (!ZX_GET_CONTENT(hdr->MessageID))
291       zx_add_content(cf->ctx, &hdr->MessageID->gg, zxid_mk_id(cf, "urn:M", ZXID_ID_BITS));
292     /* Clear away any credentials from previous iteration. *** clear kids list, too */
293     sec->Signature = 0;
294     sec->BinarySecurityToken = 0;
295     sec->SecurityTokenReference = 0;
296     sec->Assertion = 0;
297     sec->sa11_Assertion = 0;
298     sec->ff12_Assertion = 0;
299 
300     zxid_attach_sol1_usage_directive(cf, ses, env, TAS3_REQUIRE, cf->wsp_localpdp_obl_emit);
301     zx_reverse_elem_lists(&hdr->gg);
302     zxid_wsf_sign(cf, cf->wsp_sign, sec, 0, hdr, env->Body);
303   }
304   return 1;
305 }
306 
307 /* ----------------------------------------
308  * Simplify writing WSPs */
309 
310 /*(i) Add ID-WSF (and TAS3) specific headers and signatures to
311  * web service response. Simple and intuitive specification of
312  * XML as string: no need to build complex data structures.
313  *
314  * If the string starts by "<e:Envelope", then string should be
315  * a complete SOAP envelope including <e:Header> and <e:Body> parts. This
316  * allows caller to specify custom SOAP headers, in addition to the ones
317  * that the underlying zxid_wsc_call() will add. Usually the payload service
318  * will be passed as the contents of the body. If the string starts by
319  * "<e:Body", then the <e:Envelope> and <e:Header> are automatically added. If
320  * the string starts by neither of the above (be careful to use the "e:" as
321  * namespace prefix), the it is assumed to be the payload content to be
322  * wrapped in the <e:Body> and the rest of the SOAP envelope.
323  *
324  * cf:: ZXID configuration object, see zxid_new_conf()
325  * ses:: Session object that contains the EPR cache
326  * az_cred:: (Optional) Additional authorization credentials or
327  *     attributes, query string format. These credentials will be populated
328  *     to the attribute pool in addition to the ones obtained from token and
329  *     other sources. Then a PDP is called to get an authorization
330  *     decision (generating obligations). See also PEPMAP_RSOUT configuration
331  *     option. This implements generalized (application independent)
332  *     Responder Out PEP. To implement application dependent PEP features
333  *     you should call zxid_az() directly.
334  * enve:: XML payload as a string
335  * return:: SOAP Envelope of the response, as a string, ready to be
336  *     sent as HTTP response. */
337 
338 /* Called by:  covimp_test, main x9, ws_validations, zxid_mini_httpd_wsp_response, zxid_wsp_decoratef, zxidwspcgi_parent */
zxid_wsp_decorate(zxid_conf * cf,zxid_ses * ses,const char * az_cred,const char * enve)339 struct zx_str* zxid_wsp_decorate(zxid_conf* cf, zxid_ses* ses, const char* az_cred, const char* enve)
340 {
341   struct zx_str* ss;
342   struct zx_e_Envelope_s* env;
343 
344   if (!cf || !ses || !enve) {
345     ERR("Missing config, session, or envelope argument %p %p %p (programmer error)", cf,ses,enve);
346     return 0;
347   }
348   D_INDENT("decor: ");
349 
350   env = zxid_add_env_if_needed(cf, enve);
351   if (!env) {
352     D_DEDENT("decor: ");
353     return 0;
354   }
355 
356   //*** Needs thought and development
357 
358   /* Call Rs-Out PDP */
359 
360   if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RS_OUT,"e:Server", cf->pepmap_rsout)) {
361     /* Fall through, letting zxid_wsf_decor() pick up the fault and package it as response. */
362   }
363 
364   if (ses->curflt) {
365     D("Detected curflt, abandoning previous Body content. %d", 0);
366     /* *** LEAK: Should free previous body content */
367     env->Body = (struct zx_e_Body_s*)zx_replace_kid(&env->gg, (struct zx_elem_s*)zx_NEW_e_Body(cf->ctx, 0));
368     ZX_ADD_KID(env->Body, Fault, ses->curflt);
369   }
370 
371   if (!zxid_wsf_decor(cf, ses, env, 1, 0)) {
372     ERR("Response decoration failed %p", env);
373     D_DEDENT("decor: ");
374     return 0;
375   }
376   //zx_reverse_elem_lists(&env->Header->gg); //*** Again?!? Already done in zxid_wsf_decor(is_resp)
377 
378   ss = zx_easy_enc_elem_opt(cf, &env->gg);
379   DD("DECOR len=%d envelope(%.*s)", ss->len, ss->len, ss->s);
380   D_XML_BLOB(cf, "WSP_DECOR", ss->len, ss->s);
381   D_DEDENT("decor: ");
382   return ss;
383 }
384 
385 /*() Create web service response, printf style. See zxid_wsp_decorate() for more documentation. */
386 
387 /* Called by:  main */
zxid_wsp_decoratef(zxid_conf * cf,zxid_ses * ses,const char * az_cred,const char * env_f,...)388 struct zx_str* zxid_wsp_decoratef(zxid_conf* cf, zxid_ses* ses, const char* az_cred, const char* env_f, ...)
389 {
390   char* s;
391   va_list ap;
392   va_start(ap, env_f);
393   s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
394   va_end(ap);
395   return zxid_wsp_decorate(cf, ses, az_cred, s);
396 }
397 
398 /*() Perform necessary validation steps to check either requester or target identity
399  * assertion. Also log the assertion and extract from assertion relevant information
400  * into the session. The two types of assertion are distinguished by lk == "req" or "tgt".
401  * returns 0 on failure and 1 on success.
402  * See zxid_sp_sso_finalize() for similar code.  *** consider factoring out commonality */
403 
404 /* Called by:  zxid_wsp_validate_env x2 */
zxid_wsf_validate_a7n(zxid_conf * cf,zxid_ses * ses,zxid_a7n * a7n,const char * lk,struct timeval * srcts)405 static int zxid_wsf_validate_a7n(zxid_conf* cf, zxid_ses* ses, zxid_a7n* a7n, const char* lk, struct timeval* srcts)
406 {
407   struct zx_str* logpath;
408   struct zx_str* a7nss;
409   struct zxsig_ref refs;
410   zxid_nid* nameid;
411   int fmt;
412   struct zx_str* issuer;
413   zxid_entity* idp_meta;
414   zxid_cgi cgi;
415 
416   if (!a7n || !a7n->Subject) {
417     ERR("%s: Assertion lacking or does not have Subject. %p", lk, a7n);
418     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion does not have Subject.", "IDStarMsgNotUnderstood", 0, lk, 0));
419     return 0;
420   }
421 
422   issuer = ZX_GET_CONTENT(a7n->Issuer);
423   nameid = zxid_decrypt_nameid(cf, a7n->Subject->NameID, a7n->Subject->EncryptedID);
424   if (!ZX_GET_CONTENT(nameid)) {
425     ERR("%s: Assertion does not have Subject->NameID. %p", lk, ses->nameid);
426     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion does not have Subject->NameID.", "IDStarMsgNotUnderstood", 0, lk, 0));
427     return 0;
428   }
429 
430   if (nameid->Format && !memcmp(nameid->Format->g.s, SAML2_TRANSIENT_NID_FMT, nameid->Format->g.len)) {
431     fmt = 0;
432   } else {
433     fmt = 1;  /* anything nontransient may be a federation */
434   }
435 
436   D("A7N received. NID(%s) FMT(%d) SESIX(%s)", STRNULLCHKQ(ses->nid), ses->nidfmt, STRNULLCHK(ses->sesix));
437   if (!strcmp(lk, "tgt")) {
438     ses->tgtnameid = nameid;
439     ses->tgt = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(nameid));
440     ses->tgtfmt = fmt;
441   } else {
442     ses->nameid = nameid;
443     ses->nid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(nameid));
444     ses->nidfmt = fmt;
445   }
446 
447   /* Validate signature (*** add Issuer trusted check, CA validation, etc.) */
448 
449   idp_meta = zxid_get_ent_ss(cf, issuer);
450   if (!idp_meta) {
451     ses->sigres = ZXSIG_NO_SIG;
452     if (!cf->nosig_fatal) {
453       ERR("%s: Unable to find metadata for Assertion Issuer(%.*s).", lk, issuer->len, issuer->s);
454       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No unable to find SAML metadata for Assertion Issuer.", "ProviderIDNotValid", 0, lk, 0));
455       return 0;
456     } else {
457       INFO("%s: Unable to find metadata for Assertion Issuer(%.*s), but configured to ignore this problem (NOSIG_FATAL=0).", lk, issuer->len, issuer->s);
458     }
459   } else {
460     if (a7n->Signature && a7n->Signature->SignedInfo && a7n->Signature->SignedInfo->Reference) {
461       ZERO(&refs, sizeof(refs));
462       refs.sref = a7n->Signature->SignedInfo->Reference;
463       refs.blob = (struct zx_elem_s*)a7n;
464       ses->sigres = zxsig_validate(cf->ctx, idp_meta->sign_cert, a7n->Signature, 1, &refs);
465       zxid_sigres_map(ses->sigres, &cgi.sigval, &cgi.sigmsg);
466     } else {
467       if (cf->msg_sig_ok && !ses->sigres) {
468 	INFO("Assertion without signature accepted due to message level signature (SimpleSign) %d", 0);
469       } else {
470 	ses->sigres = ZXSIG_NO_SIG;
471 	if (!cf->nosig_fatal) {
472 	  ERR("Assertion not signed. Sigval(%s) %p", STRNULLCHKNULL(cgi.sigval), a7n->Signature);
473 	  zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion not signed.", TAS3_STATUS_NOSIG, 0, lk, 0));
474 	  return 0;
475 	} else {
476 	  INFO("SSO warn: assertion not signed, but configured to ignore this problem (NOSIG_FATAL=0). Sigval(%s) %p", STRNULLCHKNULL(cgi.sigval), a7n->Signature);
477 	}
478       }
479     }
480   }
481   if (cf->sig_fatal && ses->sigres) {
482     ERR("Fail due to failed assertion signature sigres=%d", ses->sigres);
483     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion signature did not validate.", TAS3_STATUS_BADSIG, 0, lk, 0));
484     return 0;
485   }
486 
487   if (zxid_validate_cond(cf, &cgi, ses, a7n, zxid_my_ent_id(cf), 0, 0)) {
488     /* Fault (ses->curflt) already set in zxid_validate_cond() */
489     return 0;
490   }
491 
492   if (cf->log_rely_a7n) {
493     DD("Logging... %d", 0);
494     logpath = zxlog_path(cf, issuer, &a7n->ID->g, ZXLOG_RELY_DIR, ZXLOG_A7N_KIND, 1);
495     if (logpath) {
496       ses->sso_a7n_path = ses->tgt_a7n_path = zx_str_to_c(cf->ctx, logpath);
497       a7nss = zx_easy_enc_elem_sig(cf, &a7n->gg);
498       if (zxlog_dup_check(cf, logpath, "SSO assertion")) {
499 	if (cf->dup_a7n_fatal) {
500 	  zxlog_blob(cf, cf->log_rely_a7n, logpath, a7nss, "wsp_validade dup err");
501 	  zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Duplicate use of credential (assertion). Replay attack?", TAS3_STATUS_REPLAY, 0, lk, 0));
502 	  return 0;
503 	}
504       }
505       zxlog_blob(cf, cf->log_rely_a7n, logpath, a7nss, "wsp_validate");
506       zxlog(cf, 0, srcts, ses->ipport, issuer, ses->wsp_msgid, &a7n->ID->g, ZX_GET_CONTENT(nameid), "N", "K", "A7N VALID", logpath->s, lk);
507       zx_str_free(cf->ctx, a7nss);
508     }
509   }
510   return 1;
511 }
512 
513 /*() Validate SOAP request envelope, specified as data structure
514  *
515  * cf:: ZXID configuration object, see zxid_new_conf()
516  * ses:: Session object that contains the EPR cache
517  * az_cred:: (Optional) Additional authorization credentials or
518  *     attributes, query string format. These credentials will be populated
519  *     to the attribute pool in addition to the ones obtained from token and
520  *     other sources. Then a PDP is called to get an authorization
521  *     decision (matching obligations we support to those in the request,
522  *     and obligations pleged by caller to those we insist on). See
523  *     also PEPMAP configuration option. This implements generalized
524  *     (application independent) Responder In PEP. To implement
525  *     application dependent PEP features you should call zxid_az() directly.
526  * env:: SOAP envelope as data structure
527  * return:: idpnid of target identity of the request (rest of the information
528  *     is populated to the session object, from where it can be retrieved).
529  *     NULL if the validation fails. The target identity is still retrievable
530  *     from the session, should there be desire to process the message despite
531  *     the validation failure.
532  *
533  * See also: zxid_wsc_validate_resp_env() */
534 
535 /* Called by:  zxid_sp_soap_dispatch, zxid_wsp_validate */
zxid_wsp_validate_env(zxid_conf * cf,zxid_ses * ses,const char * az_cred,struct zx_e_Envelope_s * env)536 char* zxid_wsp_validate_env(zxid_conf* cf, zxid_ses* ses, const char* az_cred, struct zx_e_Envelope_s* env)
537 {
538   int n_refs = 0;
539   struct zxsig_ref refs[ZXID_N_WSF_SIGNED_HEADERS];
540   struct timeval ourts;
541   zxid_entity* wsc_meta;
542   struct zx_e_Header_s* hdr;
543   struct zx_wsse_Security_s* sec;
544   zxid_cgi cgi;
545   /*struct zx_b_UsageDirective_s* ud;*/
546   struct zx_xa_Obligation_s* obl;
547 
548   D_INDENT("valid: ");
549   GETTIMEOFDAY(&ourts, 0);
550   zxid_set_fault(cf, ses, 0);
551   zxid_set_tas3_status(cf, ses, 0);
552 
553   if (!env) {
554     ERR("No <e:Envelope> found. %d", 0);
555     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No SOAP Envelope found.", "IDStarMsgNotUnderstood", 0, 0, 0));
556     D_DEDENT("valid: ");
557     return 0;
558   }
559 
560   hdr = env->Header;
561   if (!hdr) {
562     ERR("No <e:Header> found. %d", 0);
563     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No SOAP Header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
564     D_DEDENT("valid: ");
565     return 0;
566   }
567   if (!ZX_SIMPLE_ELEM_CHK(hdr->MessageID)) {
568     ERR("No <a:MessageID> found. %d", 0);
569     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No MessageID header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
570     D_DEDENT("valid: ");
571     return 0;
572   }
573   /* Remember MessageID for generating RelatesTo in Response */
574   ses->wsp_msgid = zx_dup_zx_str(cf->ctx, ZX_GET_CONTENT(hdr->MessageID));
575   DD("wsp_msgid(%.*s) %p %d %p", ses->wsp_msgid->len, ses->wsp_msgid->s, ses->wsp_msgid, ses->wsp_msgid->len, ses->wsp_msgid->s);
576 
577   if (!hdr->Sender || !hdr->Sender->providerID && !hdr->Sender->affiliationID) {
578     ERR("No <b:Sender> found (or missing providerID or affiliationID). %p", hdr->Sender);
579     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No b:Sender header found (or missing providerID or affiliationID).", "IDStarMsgNotUnderstood", 0, 0, 0));
580     D_DEDENT("valid: ");
581     return 0;
582   }
583   ses->issuer = zx_dup_zx_str(cf->ctx, hdr->Sender->providerID?
584 			      &hdr->Sender->providerID->g : &hdr->Sender->affiliationID->g);
585 
586   /* Validate message signature (*** add Issuer trusted check, CA validation, etc.) */
587 
588   if (!(sec = hdr->Security)) {
589     ERR("No <wsse:Security> found. %d", 0);
590     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No wsse:Security header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
591     D_DEDENT("valid: ");
592     return 0;
593   }
594 
595   if (!sec->Signature || !sec->Signature->SignedInfo || !sec->Signature->SignedInfo->Reference) {
596     ses->sigres = ZXSIG_NO_SIG;
597     if (cf->wsp_nosig_fatal) {
598       ERR("No Security/Signature found. %p", sec->Signature);
599       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No wsse:Security/ds:Signature found.", TAS3_STATUS_NOSIG, 0, 0, 0));
600       D_DEDENT("valid: ");
601       return 0;
602     } else {
603       INFO("No Security/Signature found, but configured to ignore this problem (NOSIG_FATAL=0). %p", sec->Signature);
604     }
605   }
606 
607   wsc_meta = zxid_get_ent_ss(cf, ses->issuer);
608   if (wsc_meta) {
609     ZERO(refs, sizeof(refs));
610     n_refs = zxid_hunt_sig_parts(cf, n_refs, refs, sec->Signature->SignedInfo->Reference, hdr, env->Body);
611     /* *** Consider adding BDY and STR */
612     ses->sigres = zxsig_validate(cf->ctx, wsc_meta->sign_cert, sec->Signature, n_refs, refs);
613     zxid_sigres_map(ses->sigres, &cgi.sigval, &cgi.sigmsg);
614     if (cf->sig_fatal && ses->sigres) {
615       ERR("Fail due to failed message signature sigres=%d", ses->sigres);
616       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Message signature did not validate.", TAS3_STATUS_BADSIG, 0, 0, 0));
617       D_DEDENT("valid: ");
618       return 0;
619     }
620   } else {
621     ses->sigres = ZXSIG_NO_SIG;
622     if (cf->nosig_fatal) {
623       INFO("Unable to find SAML metadata for Sender(%.*s), but configured to ignore this problem (NOSIG_FATAL=0).", ses->issuer->len, ses->issuer->s);
624     } else {
625       ERR("Unable to find SAML metadata for Sender(%.*s).", ses->issuer->len, ses->issuer->s);
626       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No unable to find SAML metadata for sender.", "ProviderIDNotValid", 0, 0, 0));
627       D_DEDENT("valid: ");
628       return 0;
629     }
630   }
631 
632   if (!zxid_timestamp_chk(cf, ses, sec->Timestamp, &ourts, &ses->srcts, TAS3_PEP_RQ_IN,"e:Client"))
633     return 0;
634 
635   /* Check Requester Identity */
636 
637   ses->a7n = zxid_dec_a7n(cf, sec->Assertion, sec->EncryptedAssertion);
638   if (ses->a7n && ses->a7n->Subject) {
639     if (!zxid_wsf_validate_a7n(cf, ses, ses->a7n, "req", &ses->srcts)) {
640       D_DEDENT("valid: ");
641       return 0;
642     }
643   } else {
644     if (sec->EncryptedAssertion && !ses->a7n) {
645       ERR("<sa:EncryptedAssertion> could not be decrypted. Perhaps the certificate used to encrypt does not match your private key. This could be due to IdP/Discovery service having wrong copy of your metadata. %d", 0);
646       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "EncryptedAssertion could not be decrypted. (your metadata at the IdP/Discovery has problem?)", TAS3_STATUS_BADCOND, 0, 0, 0));
647     } else {
648       /* *** should there be absolute requirement for a requester assertion to exist? */
649       ERR("No Requester <sa:Assertion> found or assertion missing Subject. %p", ses->a7n);
650       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No assertion found.", TAS3_STATUS_BADCOND, 0, 0, 0));
651     }
652     D_DEDENT("valid: ");
653     return 0;
654   }
655 
656   /* Check Target Identity */
657 
658   if (hdr->TargetIdentity) {
659     ses->tgta7n = zxid_dec_a7n(cf, hdr->TargetIdentity->Assertion, hdr->TargetIdentity->EncryptedAssertion);
660     if (ses->tgta7n && ses->tgta7n->Subject) {
661       if (!zxid_wsf_validate_a7n(cf, ses, ses->a7n, "tgt", &ses->srcts)) {
662 	D_DEDENT("valid: ");
663 	return 0;
664       }
665     } else {
666       ERR("No TargetIdentity <sa:Assertion> found. %p", ses->tgta7n);
667       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "No TargetIdentity Assertion found.", TAS3_STATUS_BADCOND, 0, 0, 0));
668       D_DEDENT("valid: ");
669       return 0;
670     }
671 
672   } else {
673     INFO("No explicit TargetIdentity, using requester identity(%s) as target identity.", ses->nid);
674     ses->tgta7n = ses->a7n;
675     ses->tgtnameid = ses->nameid;
676     ses->tgt = ses->nid;
677     ses->tgtfmt = ses->nidfmt;
678     ses->tgt_a7n_path = ses->sso_a7n_path;
679   }
680 
681   if (hdr->UsageDirective) {
682     if (obl = hdr->UsageDirective->Obligation) {
683       if (ZX_GET_CONTENT(obl->AttributeAssignment)) {
684 	ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(obl->AttributeAssignment));
685 	D("Found TAS3 UsageDirective with obligation(%s)", ses->rcvd_usagedir);
686       }
687       if (obl->ObligationId
688 	  && ZX_STR_EQ(&obl->ObligationId->g, TAS3_SOL1_ENGINE)) {
689 	if (ses->rcvd_usagedir
690 	     && obl->AttributeAssignment->AttributeId) {
691 	  if (ZX_STR_EQ(&obl->AttributeAssignment->AttributeId->g, TAS3_PLEDGE)) {
692 	    if (!zxid_eval_sol1(cf, ses, ses->rcvd_usagedir, cf->wsp_localpdp_obl_req)) {
693 	      return 0;
694 	    }
695 	  } else if (ZX_STR_EQ(&obl->AttributeAssignment->AttributeId->g, TAS3_REQUIRE)) {
696 	    /* *** extract inbound sticky policies */
697 	    INFO("*** Extraction of inbound sticky policies at WSP not implemented yet %d", 0);
698 	  } else {
699 	    ERR("UsageDirective/Obligation/AttributeAssignment@AttributeId(%.*s) not understood", obl->AttributeAssignment->AttributeId->g.len, obl->AttributeAssignment->AttributeId->g.s);
700 	  }
701 	} else {
702 	  ERR("UsageDirective/Obligation/AttributeAssignment missing %p",obl->AttributeAssignment);
703 	}
704       }
705     } else if (ZX_GET_CONTENT(hdr->UsageDirective)) {
706       ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective));
707       D("Found unknown UsageDirective(%s)", ses->rcvd_usagedir);
708     } else {
709       ERR("UsageDirective empty or not understood. %p", hdr->UsageDirective->Dict);
710     }
711   }
712 
713   zxid_put_ses(cf, ses);
714   zxid_ses_to_pool(cf, ses);
715   zxid_snarf_eprs_from_ses(cf, ses);  /* Harvest attributes and bootstrap(s) */
716   zxid_put_user(cf, &ses->nameid->Format->g, &ses->nameid->NameQualifier->g, &ses->nameid->SPNameQualifier->g, ZX_GET_CONTENT(ses->nameid), 0);
717   zxlogwsp(cf, ses, "K", "PNEWSES", ses->sid, 0);
718 
719   /* Call Rq-In PDP */
720 
721   if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RQ_IN, "e:Server", cf->pepmap_rqin)) {
722     return 0;
723   }
724 
725   D_DEDENT("valid: ");
726   return ses->tgt;
727 }
728 
729 /*(i) Validate SOAP request (envelope), specified by the string.
730  *
731  * The string should start by "<e:Envelope" (namespace prefix may vary)
732  * and should be a complete SOAP envelope including <e:Header> (and <e:Body>)
733  * parts.
734  *
735  * cf:: ZXID configuration object, see zxid_new_conf()
736  * ses:: Session object that contains the EPR cache. New data is extracted from request to session.
737  * az_cred:: (Optional) Additional authorization credentials or
738  *     attributes, query string format. These credentials will be populated
739  *     to the attribute pool in addition to the ones obtained from token and
740  *     other sources. Then a PDP is called to get an authorization
741  *     decision (matching obligations we support to those in the request,
742  *     and obligations pleged by caller to those we insist on). See
743  *     also PEPMAP configuration option. This implements generalized
744  *     (application independent) Responder-In PEP. To implement
745  *     application dependent PEP features you should call zxid_az() directly.
746  * env:: Entire SOAP envelope as a string
747  * return:: idpnid of target identity of the request (rest of the information
748  *     is populated to the session object, from where it can be retrieved).
749  *     NULL if the validation fails. The target identity is still retrievable
750  *     from the session, should there be desire to process the message despite
751  *     the validation failure.
752  *
753  * See also: zxid_wsc_validate_resp_env() */
754 
755 /* Called by:  chkuid, main, ws_validations, zxid_mini_httpd_wsp, zxidwspcgi_main */
zxid_wsp_validate(zxid_conf * cf,zxid_ses * ses,const char * az_cred,const char * enve)756 char* zxid_wsp_validate(zxid_conf* cf, zxid_ses* ses, const char* az_cred, const char* enve)
757 {
758   struct zx_str  ss;
759   const char* enve_start;
760   char* p;
761   char msg[256];
762   struct zx_str* logpath;
763   struct zx_root_s* r;
764 
765   if (!cf || !ses || !enve) {
766     ERR("Missing config, session, or envelope argument %p %p %p (programmer error)", cf,ses,enve);
767     return 0;
768   }
769 
770   enve_start = zxid_locate_soap_Envelope(enve);
771   if (!enve_start) {
772     ERR("SOAP request does not have Envelope element %d", 0);
773     D_XML_BLOB(cf, "NO ENVELOPE SOAP request", -2, enve);
774     return 0;
775   }
776 
777   ss.s = (char*)enve_start;
778   ss.len = strlen(enve_start);
779   D_XML_BLOB(cf, "WSP_VALIDATE", ss.len, ss.s);
780   r = zx_dec_zx_root(cf->ctx, ss.len, enve, "valid");
781   if (!r) {
782     zx_format_parse_error(cf->ctx, msg, sizeof(msg), "valid");
783     ERR("Malformed XML: %s", msg);
784     /* Squash " to ' because the message will appear in XML attribute value delimited by " */
785     for (p = msg; *p; ++p)
786       if (*p == '"')
787 	*p = '\'';
788     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Malformed XML", "IDStarMsgNotUnderstood", 0, msg, 0));
789     return 0;
790   }
791   p = zxid_wsp_validate_env(cf, ses, az_cred, r->Envelope);
792   ZX_FREE(cf->ctx, r);
793 
794   logpath = zxlog_path(cf, ses->issuer, ses->wsp_msgid, ZXLOG_RELY_DIR, ZXLOG_MSG_KIND, 1);
795   if (!logpath) {
796     ERR("Log path not valid, empty issuer? %p %p", ses->issuer, ses->wsp_msgid);
797     return 0;
798   }
799   if (zxlog_dup_check(cf, logpath, "validate request")) {
800     if (cf->dup_msg_fatal) {
801       zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate request dup err");
802       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Client", "Duplicate Message.", "DuplicateMsg", 0, 0, 0));
803       return 0;
804     } else {
805       INFO("Duplicate message detected, but configured to ignore this (DUP_MSG_FATAL=0). %d",0);
806     }
807   }
808   zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate request");
809   zxlogwsp(cf, ses, "K", "VALID", logpath->s, 0);
810   return p;
811 }
812 
813 /* EOF  --  zxidwsp.c */
814