1 /* zxidwsc.c  -  Handwritten nitty-gritty functions for Liberty ID-WSF Web Services Client
2  * Copyright (c) 2014 Synergetics NV (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2009-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2007-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.19 2010-01-08 02:10:09 sampo Exp $
11  *
12  * 7.1.2007,  created --Sampo
13  * 7.10.2008, added documentation --Sampo
14  * 7.1.2010,  added WSC signing --Sampo
15  * 31.5.2010, added WSC sig validation and PDP calls --Sampo
16  * 16.2.2011, added disable security option VALID_OPT --Sampo
17  * 12.3.2014, added partial mime multipart support --Sampo
18  */
19 
20 #include "platform.h"  /* needed on Win32 for pthread_mutex_lock() et al. */
21 
22 #include "errmac.h"
23 #include "zxid.h"
24 #include "zxidpriv.h"
25 #include "zxidutil.h"
26 #include "zxidconf.h"
27 #include "saml2.h"
28 #include "wsf.h"
29 #include "c/zx-const.h"
30 #include "c/zx-ns.h"
31 #include "c/zx-data.h"
32 #include "c/zx-e-data.h"
33 
34 /*() WSC response validation work horse.
35  * Checks the ID-WSF [SOAPbind2] specified criteria, as well
36  * as additional criteria and calls PDP, if configured.
37  *
38  * cf:: ZXID configuration object, see zxid_new_conf()
39  * ses:: Session object, used for attributes passed to az, and for recording errors
40  * az_cred:: (Optional) Additional authorization credentials or
41  *     attributes, query string format. These credentials will be populated
42  *     to the attribute pool in addition to the ones obtained from token and
43  *     other sources. Then a PDP is called to get an authorization
44  *     decision (matching obligations we support to those in the request,
45  *     and obligations pleged by caller to those we insist on). See
46  *     also PEPMAP configuration option. This implements generalized
47  *     (application independent) Responder In PEP. To implement
48  *     application dependent PEP features you should call zxid_az() directly.
49  * env:: Entire SOAP envelope as a data structure
50  * enve:: SOAP envelope as string
51  * return:: 1 on success, 0 on validation failure. Exact reason of the failure is
52  *     available from ses->curflt and ses->curstatus.
53  *
54  * See also: zxid_wsp_validate() */
55 
56 /* Called by:  covimp_test x3, zxid_call_epr, zxid_wsc_valid_resp */
zxid_wsc_valid_re_env(zxid_conf * cf,zxid_ses * ses,const char * az_cred,struct zx_e_Envelope_s * env,const char * enve)57 int zxid_wsc_valid_re_env(zxid_conf* cf, zxid_ses* ses, const char* az_cred, struct zx_e_Envelope_s* env, const char* enve)
58 {
59   int n_refs = 0;
60   struct zxsig_ref refs[ZXID_N_WSF_SIGNED_HEADERS];
61   struct timeval ourts;
62   struct timeval srcts = {0,501000};
63   zxid_entity* wsc_meta;
64   struct zx_wsse_Security_s* sec;
65   struct zx_e_Header_s* hdr;
66   struct zx_str* issuer;
67   struct zx_str* logpath;
68   struct zx_str* relto;
69   struct zx_str ss;
70   zxid_cgi cgi;
71 
72   GETTIMEOFDAY(&ourts, 0);
73   zxid_set_fault(cf, ses, 0);
74   zxid_set_tas3_status(cf, ses, 0);
75 
76   if (cf->valid_opt & ZXID_VALID_OPT_SKIP_RESP_HDR) {
77     ERR("WARNING! Important response security validations disabled by VALID_OPT=0x%x", cf->valid_opt);
78     return 1;
79   }
80 
81   if (!env) {
82     ERR("No <e:Envelope> found. enve(%s)", STRNULLCHK(enve));
83     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Envelope found.", "IDStarMsgNotUnderstood", 0, 0, 0));
84     return 0;
85   }
86   hdr = env->Header;
87   if (!hdr) {
88     ERR("No <e:Header> found. %d", 0);
89     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
90     return 0;
91   }
92   if (!ZX_SIMPLE_ELEM_CHK(hdr->MessageID)) {
93     ERR("No <a:MessageID> found. %d", 0);
94     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No MessageID header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
95     return 0;
96   }
97   relto = ZX_GET_CONTENT(hdr->RelatesTo);
98   if (relto && relto->len) {
99     if (ses->wsc_msgid) {
100       if (strlen(ses->wsc_msgid) == relto->len
101 	  && !memcmp(ses->wsc_msgid, relto->s, relto->len)) {
102 	D("RelatesTo check OK %d",1);
103       } else {
104 	/* N.B. [SOAPBinding2] p.27, ll.818-822 indicates RelatesTo checking as SHOULD. */
105 	if (cf->relto_fatal) {
106 	  ERR("<a:RelatesTo> (%.*s) does not match request msgid(%s).", relto->len, relto->s, ses->wsc_msgid);
107 	  zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "RelatesTo in response does not match request MessageID.", "InvalidRefToMsgID", 0, 0, 0));
108 	  return 0;
109 	} else {
110 	  INFO("<a:RelatesTo> (%.*s) does not match request msgid(%s), but configured to ignore this error (RELTO_FATAL=0).", relto->len, relto->s, ses->wsc_msgid);
111 	}
112       }
113     } else {
114       INFO("Session does not have wsc_msgid. Skipping <a:RelatesTo> check. %d",0);
115     }
116   } else {
117     if (cf->relto_fatal) {
118       ERR("No <a:RelatesTo> found. %d", 0);
119       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No RelatesTo header found in reply.", "IDStarMsgNotUnderstood", 0, 0, 0));
120       return 0;
121     } else {
122       INFO("No <a:RelatesTo> found, but configured to ignore this (RELTO_FATAL=0). %d", 0);
123       D("No RelTo OK enve(%s)", STRNULLCHK(enve));
124     }
125   }
126 
127   if (!hdr->Sender || !hdr->Sender->providerID && !hdr->Sender->affiliationID) {
128     ERR("No <b:Sender> found (or missing providerID or affiliationID). %p", hdr->Sender);
129     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No b:Sender header found (or missing providerID or affiliationID).", "IDStarMsgNotUnderstood", 0, 0, 0));
130     return 0;
131   }
132   issuer = &hdr->Sender->providerID->g;
133 
134   /* Validate message signature (*** add Issuer trusted check, CA validation, etc.) */
135 
136   if (!(sec = hdr->Security)) {
137     ERR("No <wsse:Security> found. %d", 0);
138     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
139     return 0;
140   }
141 
142   wsc_meta = zxid_get_ent_ss(cf, issuer);
143   if (!wsc_meta) {
144     ses->sigres = ZXSIG_NO_SIG;
145     if (cf->nosig_fatal) {
146       INFO("Unable to find SAML metadata for Sender(%.*s), but configured to ignore this problem (NOSIG_FATAL=0).", issuer->len, issuer->s);
147     } else {
148       ERR("Unable to find SAML metadata for Sender(%.*s).", issuer->len, issuer->s);
149       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No unable to find SAML metadata for sender.", "ProviderIDNotValid", 0, 0, 0));
150       return 0;
151     }
152   }
153 
154   if (!sec->Signature || !sec->Signature->SignedInfo || !sec->Signature->SignedInfo->Reference) {
155     ses->sigres = ZXSIG_NO_SIG;
156     if (cf->wsp_nosig_fatal) {
157       ERR("No Security/Signature found. %p", sec->Signature);
158       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security/ds:Signature found.", TAS3_STATUS_NOSIG, 0, 0, 0));
159       return 0;
160     } else {
161       INFO("No Security/Signature found, but configured to ignore this problem (WSP_NOSIG_FATAL=0). %p", sec->Signature);
162     }
163   } else {
164     ZERO(refs, sizeof(refs));
165     n_refs = zxid_hunt_sig_parts(cf, n_refs, refs, sec->Signature->SignedInfo->Reference, hdr, env->Body);
166     /* *** Consider adding BDY and STR */
167     ses->sigres = zxsig_validate(cf->ctx, wsc_meta?wsc_meta->sign_cert:0, sec->Signature, n_refs, refs);
168     zxid_sigres_map(ses->sigres, &cgi.sigval, &cgi.sigmsg);
169     if (cf->sig_fatal && ses->sigres) {
170       ERR("Fail due to failed message signature sigres=%d", ses->sigres);
171       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Message signature did not validate.", TAS3_STATUS_BADSIG, 0, 0, 0));
172       return 0;
173     }
174   }
175   if (!zxid_timestamp_chk(cf, ses, sec->Timestamp, &ourts, &srcts, TAS3_PEP_RS_IN, "e:Server"))
176     return 0;
177 
178   if (hdr->UsageDirective) {
179     if (hdr->UsageDirective->Obligation && ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment)) {
180       ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment));
181       D("Found TAS3 UsageDirective with obligation(%s)", ses->rcvd_usagedir);
182     } else if (ZX_GET_CONTENT(hdr->UsageDirective)) {
183       ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective));
184       D("Found unknown UsageDirective(%s)", ses->rcvd_usagedir);
185     } else {
186       ERR("UsageDirective empty or not understood. %p", hdr->UsageDirective->Dict);
187     }
188   }
189 
190   zxid_ses_to_pool(cf, ses);
191   zxid_snarf_eprs_from_ses(cf, ses);  /* Harvest attributes and bootstrap(s) */
192 
193   if (hdr->Status && hdr->Status->code
194       && (hdr->Status->code->g.len != 2
195 	  || hdr->Status->code->g.s[0] != 'O'
196 	  || hdr->Status->code->g.s[1] != 'K')) {
197     ERR("TAS3 or app level error code(%.*s)", hdr->Status->code->g.len, hdr->Status->code->g.s);
198     return 0;
199   }
200 
201   /* Call Rs-In PDP */
202 
203   if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RS_IN, "e:Client", cf->pepmap_rsin)) {
204     return 0;
205   }
206 
207   /* *** execute (or store for future execution) the obligations. */
208 
209   ss.s = (char*)enve;
210   ss.len = strlen(enve);
211   logpath = zxlog_path(cf, issuer, ZX_GET_CONTENT(hdr->MessageID),
212 		       ZXLOG_RELY_DIR, ZXLOG_MSG_KIND, 1);
213   if (zxlog_dup_check(cf, logpath, "validate response")) {
214     if (cf->dup_msg_fatal) {
215       zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response dup err");
216       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Duplicate Message.", "DuplicateMsg", 0, 0, 0));
217       return 0;
218     } else {
219       INFO("Duplicate message detected, but configured to ignore this (DUP_MSG_FATAL=0). %d",0);
220     }
221   }
222   zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response");
223   zxlog(cf, &ourts, &srcts, 0, issuer, 0, ses->a7n?&ses->a7n->ID->g:0, ZX_GET_CONTENT(ses->nameid), "N", "K", "VALID", logpath->s, 0);
224   return 1;
225 }
226 
227 /*() Prepare some headers for WSC call. Some of the headers, such
228  * as MessageID and Security, will receive their final content
229  * in zxid_wsc_pres_secmech(). All signing also happens later.
230  * The header list is kept in forward order. */
231 
232 /* Called by:  zxid_wsc_call, zxid_wsc_prepare_call */
zxid_wsc_prep(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,struct zx_e_Envelope_s * env)233 static int zxid_wsc_prep(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env)
234 {
235   zxid_tok* tok;
236   struct zx_e_Header_s* hdr;
237   if (!zxid_wsf_decor(cf, ses, env, 0, epr))
238     return 0;
239   hdr = env->Header;
240 
241   /* 6.rq: ReplyTo (optional) */
242 
243   if (cf->wsc_replyto_hdr && strcmp(cf->wsc_replyto_hdr, "#inhibit")) {
244     /* Mandatory for a request (says who? - apparenly AXIS2 or WSO2 has a bug of
245      * requiring this and not understanding to default it to anon).
246      * liberty-idwsf-soap-binding-2.0-errata-v1.0.pdf
247      * p.21 ll.591-595 seem to imply that ReplyTo can be omitted if value would be A_ANON. */
248     hdr->ReplyTo = zx_NEW_a_ReplyTo(cf->ctx, &hdr->gg);
249     /*hdr->ReplyTo->Address = zxid_mk_addr(cf, zx_strf(cf->ctx, "%s?o=P", cf->burl));*/
250     if (!strcmp(cf->wsc_replyto_hdr, "#anon")) {
251       hdr->ReplyTo->Address = zxid_mk_addr(cf, &hdr->ReplyTo->gg, zx_dup_str(cf->ctx, A_ANON));
252     } else if (!strcmp(cf->wsc_replyto_hdr, "#anon_2005_03")) {
253       hdr->ReplyTo->Address = zxid_mk_addr(cf, &hdr->ReplyTo->gg, zx_dup_str(cf->ctx, A_ANON_2005_03));
254     } else {
255       hdr->ReplyTo->Address = zxid_mk_addr(cf, &hdr->ReplyTo->gg, zx_dup_str(cf->ctx, cf->wsc_replyto_hdr));
256     }
257     hdr->ReplyTo->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->ReplyTo->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
258     hdr->ReplyTo->actor = zx_ref_attr(cf->ctx, &hdr->ReplyTo->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
259   }
260 
261 #if 0
262   /* Omission means to use same address as ReplyTo */
263   hdr->FaultTo = zx_NEW_a_FaultTo(cf->ctx, &hdr->gg);
264   hdr->FaultTo->Address = zx_mk_addr(cf->ctx, &hdr->FaultTo->gg, zx_strf(cf->ctx, "%s?o=P", cf->burl));
265   hdr->FaultTo->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->FaultTo->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
266   hdr->FaultTo->actor = zx_ref_attr(cf->ctx, &hdr->FaultTo->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
267 #endif
268 
269   if (ses->call_tgttok || ses->call_invoktok && epr && epr->Metadata && epr->Metadata->SecurityContext && epr->Metadata->SecurityContext->Token) {
270 
271     /* 9.rq: Target Identity */
272 
273     if (ses->call_tgttok) {
274       D("TargetIdentity: Explicit specification of ses->call_tgttok %d",0);
275       tok = ses->call_tgttok;
276     } else {
277       D("TargetIdentity: Using token from EPR due to specification of ses->call_invoktok %d",0);
278       tok = epr->Metadata->SecurityContext->Token;
279     }
280     hdr->TargetIdentity = zx_NEW_b_TargetIdentity(cf->ctx, &hdr->gg);
281     hdr->TargetIdentity->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->TargetIdentity->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
282     hdr->TargetIdentity->actor = zx_ref_attr(cf->ctx, &hdr->TargetIdentity->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
283     if (tok->EncryptedAssertion) {
284       ZX_ADD_KID(hdr->TargetIdentity, EncryptedAssertion, tok->EncryptedAssertion);
285     } else if (tok->Assertion) {
286       ZX_ADD_KID(hdr->TargetIdentity, Assertion, tok->Assertion);
287     } else {
288       ERR("No <sa:EncryptedAssertion> or <sa:Assertion> found in <sec:Token> %p", tok);
289     }
290   } /* else this is just implied by the sec mech */
291 
292   /* 10. UsageDirective */
293 
294   zxid_attach_sol1_usage_directive(cf, ses, env, TAS3_PLEDGE, cf->wsc_localpdp_obl_pledge);
295 
296   zx_reverse_elem_lists(&hdr->gg);
297   return 1;
298 }
299 
300 /*(-) Use EncryptedAssertion if available, otherwise plain Assertion.
301  * ses->call_invoktok allows other token to be specified, as an override. */
302 
303 /* Called by:  zxid_wsc_prep_secmech x2 */
zxid_choose_sectok(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,struct zx_wsse_Security_s * sec)304 static void zxid_choose_sectok(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_wsse_Security_s* sec)
305 {
306   zxid_tok* tok;
307   if (ses->call_invoktok) {
308     D("Security Token: Explicit specification of ses->call_invoktok %d",0);
309     tok = ses->call_invoktok;
310   } else {
311     if (epr && epr->Metadata && epr->Metadata->SecurityContext && epr->Metadata->SecurityContext->Token) {
312       D("Security Token: Using token from EPR Metadata %d",0);
313       tok = epr->Metadata->SecurityContext->Token;
314     } else {
315       ERR("Security Token: No SecurityContext in EPR Metadata %p",epr);
316       return;
317     }
318   }
319   if (tok->EncryptedAssertion) {
320     sec->EncryptedAssertion = tok->EncryptedAssertion;
321     zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &sec->EncryptedAssertion->gg);
322   } else if (tok->Assertion) {
323     sec->Assertion = tok->Assertion;
324     zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &sec->Assertion->gg);
325   } else
326     ERR("No <sa:EncryptedAssertion> or <sa:Assertion> found in <sec:Token> %p", tok);
327 }
328 
329 /*() Perform security mechanism related processing for a WSC call.
330  * This function will add Liberty ID-WSF specific content to already
331  * existing SOAP headers, namely in Security and MessageID. This
332  * header content varies from SOAP call retry to retry. Other
333  * headers are always the same and handled in zxid_wsc_prep(). */
334 
335 /* Called by:  zxid_wsc_call, zxid_wsc_prepare_call */
zxid_wsc_prep_secmech(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,struct zx_e_Envelope_s * env)336 static int zxid_wsc_prep_secmech(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env)
337 {
338   int secmech;
339   struct zx_wsse_Security_s* sec;
340   struct zx_wsse_SecurityTokenReference_s* str;
341   struct zx_e_Header_s* hdr;
342 
343   if (!epr || !env) {
344     ERR("MUST supply epr %p and envelope as arguments", epr);
345     return 0;
346   }
347 
348   hdr = env->Header;
349   zx_add_content(cf->ctx, &hdr->MessageID->gg, zxid_mk_id(cf, "urn:M", ZXID_ID_BITS));
350   sec = hdr->Security;
351   if (!sec || !sec->Timestamp || !sec->Timestamp->Created) {
352     ERR("MUST supply wsse:Security and Timestamp %p", sec);
353     return 0;
354   }
355   zx_add_content(cf->ctx, &sec->Timestamp->Created->gg, zxid_date_time(cf, time(0)));
356 
357   /* Clear away any credentials from previous iteration, if any. *** clear kids list, too */
358   sec->Signature = 0;
359   sec->BinarySecurityToken = 0;
360   sec->SecurityTokenReference = 0;
361   sec->Assertion = 0;
362   sec->EncryptedAssertion = 0;
363   sec->sa11_Assertion = 0;
364   sec->ff12_Assertion = 0;
365 
366   /* Sign all Headers that have Id set. See wsc_sign_sec_mech() */
367   secmech = zxid_map_sec_mech(epr);
368   switch (secmech) {
369   case ZXID_SEC_MECH_NULL:
370     D("secmech null %d", secmech);
371     break;
372   case ZXID_SEC_MECH_BEARER:
373     zxid_choose_sectok(cf, ses, epr, sec);
374     str = sec->SecurityTokenReference = zx_NEW_wsse_SecurityTokenReference(cf->ctx, 0);
375     zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &str->gg);
376     str->KeyIdentifier = zx_NEW_wsse_KeyIdentifier(cf->ctx, &str->gg);
377     str->KeyIdentifier->ValueType = zx_ref_attr(cf->ctx, &str->KeyIdentifier->gg, zx_ValueType_ATTR, SAMLID_TOK_PROFILE);
378     if (sec->Assertion)
379       zx_add_content(cf->ctx, &str->KeyIdentifier->gg, &sec->Assertion->ID->g);
380     /* *** In case of encrypted assertion, how is the KeyIdentifier populated? */
381 
382     zxid_wsf_sign(cf, cf->wsc_sign, sec, str, hdr, env->Body);
383     D("secmech bearer %d", secmech);
384     break;
385   case ZXID_SEC_MECH_SAML:
386     zxid_choose_sectok(cf, ses, epr, sec);
387     /* *** Sign SEC, MID, TO, ACT (if any) */
388     zxid_wsf_sign(cf, cf->wsc_sign, sec, 0, hdr, env->Body);
389     D("secmech saml hok %d", secmech);
390     break;
391   case ZXID_SEC_MECH_X509:
392     /* *** Sign SEC, MID, TO, ACT (if any) */
393     zxid_wsf_sign(cf, cf->wsc_sign, sec, 0, hdr, env->Body);
394     D("secmech x509 %d", secmech);
395     break;
396   case ZXID_SEC_MECH_PEERS:
397     /* *** ? */
398     D("secmech peers %d", secmech);
399     break;
400   default:
401     ERR("Unknown secmech %d", secmech);
402     return 0;
403   }
404   return 1;
405 }
406 
407 /*(i) zxid_wsc_call() implements the main low level ID-WSF web service call
408  * logic, including preparation of SOAP headers, use of sec mech (e.g.
409  * preparation of wsse:Security header and signing of appropriate components
410  * of the message), and sequencing of the call. In particular, it is
411  * possible that WSP requests user interaction and thus the caller web
412  * application will need to perform a redirect and then later call this
413  * function again to continue the web service call after interaction.
414  *
415  * env (rather than Body) is taken as argument so that caller can prepare
416  * additional SOAP headers at will before calling this function. */
417 
418 /* Called by:  main x9, zxid_call_epr, zxid_discover_epr, zxid_map_identity_token, zxid_nidmap_identity_token */
zxid_wsc_call(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,struct zx_e_Envelope_s * env,char ** ret_enve)419 struct zx_e_Envelope_s* zxid_wsc_call(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env, char** ret_enve)
420 {
421   int i, res;
422   struct zx_str* code;
423   struct zx_str* str;
424   struct zx_str* actor;
425   struct zx_root_s* root;
426   struct zx_e_Fault_s* flt;
427 
428   D_INDENT("wsc_call rq: ");
429 
430   if (!zxid_wsc_prep(cf, ses, epr, env)) {
431     D_DEDENT("wsc_call rq: ");
432     return 0;
433   }
434 
435   for (i=0; i < cf->max_soap_retry; ++i) {
436     if (!zxid_wsc_prep_secmech(cf, ses, epr, env)) {
437       D_DEDENT("wsc_call rq: ");
438       return 0;
439     }
440     ses->wsc_msgid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(env->Header->MessageID));
441 
442     root = zxid_soap_call_raw(cf, ZX_GET_CONTENT(epr->Address), env, ret_enve);
443     D_DEDENT("wsc_call rq: ");
444     D_INDENT("wsc_call rs: ");
445     if (!root || !root->Envelope || !root->Envelope->Body) {
446       ERR("soap call returned empty or seriously flawed response %p", root);
447       zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_PARSE, "e:Server", "Server sent empty or invalid reply. SOAP Envelope or Body can not be found.", 0, 0, 0, 0));
448       D_DEDENT("wsc_call rs: ");
449       return 0;
450     }
451     flt = root->Envelope->Body->Fault;
452     if (flt) {
453       code = ZX_GET_CONTENT(flt->faultcode);
454       str = ZX_GET_CONTENT(flt->faultstring);
455       actor = ZX_GET_CONTENT(flt->faultactor);
456       D("SOAP Fault(%.*s) string(%.*s) actor(%.*s)", code?code->len:1, code?code->s:"?", str?str->len:1, str?str->s:"?", actor?actor->len:1, actor?actor->s:"?");
457       zxid_set_fault(cf, ses, zxid_mk_fault_zx_str(cf, 0, zx_dup_str(cf->ctx,TAS3_PEP_RS_VAL), code?code:zx_dup_str(cf->ctx,"e:Server"), str));
458 
459       D_DEDENT("wsc_call rs: ");
460       return 0;
461     }
462 
463     //res = zxid_wsf_analyze_result_headers(cf, ret); // detect, e.g., redirect
464     res = ZXID_OK;
465     switch (res) {
466     case ZXID_OK:
467       D_DEDENT("wsc_call rs: ");
468       return root->Envelope;      /* Success case */
469 #if 0
470     case ZXID_NEW_CRED:
471       break;
472     case ZXID_EP_MOVE:  // ***
473       break;
474     case ZXID_EP_UPDATE:
475       break;
476 #endif
477     case ZXID_REDIR_OK:
478       D("Redirection requested (e.g. Interaction Service) %d", 0);
479       D_DEDENT("wsc_call rs: ");
480       return (void*)ZXID_REDIR_OK;
481     default:
482       ERR("Unknown result code: %d", res);
483       D_DEDENT("wsc_call rs: ");
484       return 0;
485     }
486   }
487   ERR("Number of soap call retries exhausted max_soap_retry=%d", cf->max_soap_retry);
488   D_DEDENT("wsc_call rs: ");
489   return 0;
490 }
491 
492 static char zx_env_body_open[]  = "<e:Envelope xmlns:e=\""zx_xmlns_e"\"><e:Header></e:Header><e:Body>";
493 static char zx_env_body_close[] = "</e:Body></e:Envelope>";
494 #if 0
495 static char zx_env_open[]  = "<e:Envelope xmlns:e=\""zx_xmlns_e"\"><e:Header></e:Header>";
496 static char zx_env_close[] = "</e:Envelope>";
497 #endif
498 
499 /*() Convenience helper function to parse SOAP Envelope input string.
500  * If the specified envelope is incomplete, it is completed.
501  *
502  * If the string starts by "<e:Envelope", then string
503  * should be a complete SOAP envelope including <e:Header> and <e:Body> parts.
504  * If the string starts by "<e:Body", then the <e:Envelope> and <e:Header> are
505  * automatically added. If the string starts by neither of the above (be
506  * careful to use the "e:" as namespace prefix), then it is assumed to be the
507  * payload content of the <e:Body> and the rest of the SOAP envelope is added.
508  * Supplying <e:Header>, but not <e:Body>, is not supported.
509  * N.B. The lists are returned in forward order.
510  */
511 
512 /* Called by:  zxid_call_epr, zxid_wsc_prepare_call, zxid_wsc_valid_resp, zxid_wsp_decorate */
zxid_add_env_if_needed(zxid_conf * cf,const char * enve)513 struct zx_e_Envelope_s* zxid_add_env_if_needed(zxid_conf* cf, const char* enve)
514 {
515   struct zx_e_Envelope_s* env;
516   struct zx_root_s* r;
517   struct zx_str* ret;
518   r = zx_dec_zx_root(cf->ctx, strlen(enve), enve, "add_env");
519   if (!r) {
520     ERR("Malformed XML enve(%s)", enve);
521     return 0;
522   }
523   /* N.B. The lists are in reverse order after the parse. */
524   env = r->Envelope;
525   if (env) {
526     /* N.B. Maintain the forward order, Header is 1st element of Envelope->kids. */
527     if (!env->Header) {
528       D("ENV EXISTS, no Header %p %p", env, env->Body);
529       if (!env->Body)
530 	env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
531       env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
532     } else {
533       D("ENV EXISTS w/Header %p %p", env, env->Body);
534       if (!env->Body)
535 	env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
536     }
537   } else if (r->Body) {
538     D("HERE2 BODY EXISTS %p %p", env, r->Header);
539     env = zx_NEW_e_Envelope(cf->ctx,0);
540     ZX_ADD_KID(env, Body, r->Body);
541     if (r->Header)
542       ZX_ADD_KID(env, Header, r->Header);
543     else
544       env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
545     /* N.B. Maintain the Forward order: Header is now first element of Envelope->kids. */
546   } else { /* Resort to stringwise attempt to add envelope. */
547     ZX_FREE(cf->ctx, r);
548     if (!memcmp(enve, "<?xml ", sizeof("<?xml ")-1)) {  /* Ignore common, but unnecessary decl. */
549       for (enve += sizeof("<?xml "); *enve && !(enve[0] == '?' && enve[1] == '>'); ++enve) ;
550       if (*enve)
551 	enve += 2;
552     }
553     /* Must be just payload */
554     enve = zx_alloc_sprintf(cf->ctx, 0, "%s%s%s", zx_env_body_open, enve, zx_env_body_close);
555     D("HERE3 ADD ENV(%s)", enve);
556     r = zx_dec_zx_root(cf->ctx, strlen(enve), enve, "add_env2");
557     if (!r) {
558       ERR("Malformed XML enve(%s)", enve);
559       return 0;
560     }
561     env = r->Envelope;
562 #if 0
563     ret=zx_easy_enc_elem_opt(cf,&env->gg); INFO("ser(%.*s) enve(%s)",ret->len,ret->s,enve); // ***
564     /* The lists are in reverse order after the parse. But since this is a text parse,
565      * wireorder is maintained, thus giving forward order, afterall. */
566     zx_reverse_elem_lists(&env->gg);
567 #endif
568   }
569   ZX_FREE(cf->ctx, r);
570   if (env->gg.kids != &env->Header->gg) {
571     D("ENV Fixing Header-Body ordering %p", env);
572     env->gg.kids = &env->Header->gg;
573     env->Header->gg.g.n = &env->Body->gg.g;
574     env->Body->gg.g.n = 0;
575   }
576   ret = zx_easy_enc_elem_opt(cf,&env->gg); INFO("ser(%.*s) enve(%s)",ret->len,ret->s,enve); // ***
577   if (!env)
578     ERR("No <e:Envelope> found in input argument. enve(%s)", enve);
579   /* DO NOT: zx_reverse_elem_lists(&env->gg);  * ensure forward order for external use */
580   return env;
581 }
582 
583 /* ----------------------------------------
584  * Simplify writing WSCs */
585 
586 /*() Make a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
587  * specified by the string. Assumes the EPR has already been discovered.
588  * This is sometimes useful in prediscovered or delegated use cases, but
589  * normally you should be using zxid_call() and let the discovery
590  * take its course. */
591 
592 /* Called by:  zxid_call, zxid_callf_epr */
zxid_call_epr(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,const char * az_cred,const char * enve)593 struct zx_str* zxid_call_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* enve)
594 {
595   char* ret_enve;
596   struct zx_str* ret;
597   struct zx_e_Envelope_s* env;
598 
599   if (!cf || !ses || !enve) {
600     ERR("Missing mandatory arguments ses=%p enve=%p (programmer error)", ses, enve);
601     return 0;
602   }
603 
604   D_INDENT("call: ");
605   env = zxid_add_env_if_needed(cf, enve);
606   if (!env) {
607     D_DEDENT("call: ");
608     return 0;
609   }
610   if (errmac_debug > 1) { ret = zx_easy_enc_elem_opt(cf, &env->gg);  D("sending(%.*s) enve(%s)", ret->len, ret->s, enve); }
611 
612   /* Call Rq-Out PDP */
613 
614   if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RQ_OUT,"e:Client", cf->pepmap_rqout)) {
615     D_DEDENT("call: ");
616     return 0;
617   }
618 
619   /* *** add usage directives */
620 
621   env = zxid_wsc_call(cf, ses, epr, env, &ret_enve);
622   if (!env || env == (void*)ZXID_REDIR_OK || !env->Body) {
623     ERR("Parsing return value failed %p", env);
624     INFO("ret_enve(%s) len=%d", ret_enve, (int)strlen(ret_enve));
625     D_DEDENT("call: ");
626     if (cf->valid_opt & ZXID_VALID_OPT_SKIP_RESP_HDR) {
627       ERR("WARNING! Important response security validations disabled by VALID_OPT=0x%x AND Fault occured or parsing return value failed. Pretending success anyway.", cf->valid_opt);
628       return zx_dup_str(cf->ctx, ret_enve);
629     }
630     return 0;
631   }
632   if (zxid_wsc_valid_re_env(cf, ses, az_cred, env, ret_enve) != 1) {
633     D_DEDENT("call: ");
634     return 0;
635   }
636 
637 #if 1
638   ret = zx_ref_str(cf->ctx, ret_enve);
639 #else
640   ret = zx_easy_enc_elem_opt(cf, &env->gg);
641 #endif
642   D_DEDENT("call: ");
643   return ret;
644 }
645 
646 /*() Call web service, printf style. See zxid_call() for more documentation.
647  * Normally you should be calling zxid_callf() instead. */
648 
649 /* Called by: */
zxid_callf_epr(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,const char * az_cred,const char * env_f,...)650 struct zx_str* zxid_callf_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* env_f, ...)
651 {
652   char* s;
653   va_list ap;
654   va_start(ap, env_f);
655   s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
656   va_end(ap);
657   return zxid_call_epr(cf, ses, epr, az_cred, s);
658 }
659 
660 /*(i) Make a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
661  * specified by the string. This is your WSC work horse for calling almost any kind
662  * of web service. Simple and intuitive specification of XML as string: no need
663  * to build complex data structures.
664  *
665  * If the string starts by "<e:Envelope", then string
666  * should be a complete SOAP envelope including <e:Header> and <e:Body> parts. This
667  * allows caller to specify custom SOAP headers, in addition to the ones
668  * that the underlying zxid_wsc_call() will add. Usually the payload service
669  * will be passed as the contents of the body. If the string starts by
670  * "<e:Body", then the <e:Envelope> and <e:Header> are automatically added. If
671  * the string starts by neither of the above (be careful to use the "e:" as
672  * namespace prefix), then it is assumed to be the payload content of the <e:Body>
673  * and the rest of the SOAP envelope is added.
674  *
675  * cf:: ZXID configuration object, see zxid_new_conf()
676  * ses:: Session object that contains the EPR cache
677  * svctype:: URI (often the namespace URI) specifying the kind of service we
678  *     wish to call. Used for EPR lookup or discovery.
679  * url:: (Optional) If provided, this argument has to match either
680  *     the ProviderID, EntityID, or actual service endpoint URL.
681  * di_opt:: (Optional) Additional discovery options for selecting the
682  *     service, query string format
683  * az_cred:: (Optional) Additional authorization credentials or
684  *     attributes, query string format. These credentials will be populated
685  *     to the attribute pool in addition to the ones obtained from SSO and
686  *     other sources. Then a PDP is called to get an authorization decision
687  *     (as well as obligations we pledge to support). See also PEPMAP
688  *     configuration option. This implementes generalized (application
689  *     independent) Requestor Out and Requestor In PEPs. To implement
690  *     application dependent PEP features you should call zxid_az() directly.
691  * enve:: Request XML payload as string
692  * return:: SOAP Envelope of the response, as a string. You can parse this
693  *     string to obtain all returned SOAP headers as well as the Body and its
694  *     content. NULL on failure. ses->curflt and/or ses->curstatus contain
695  *     more detailed error information. */
696 
697 /* Called by:  zxcall_main, zxid_callf */
zxid_call(zxid_conf * cf,zxid_ses * ses,const char * svctype,const char * url,const char * di_opt,const char * az_cred,const char * enve)698 struct zx_str* zxid_call(zxid_conf* cf, zxid_ses* ses, const char* svctype, const char* url, const char* di_opt, const char* az_cred, const char* enve)
699 {
700   zxid_epr* epr;
701 
702   if (!cf || !ses) {
703     ERR("Missing mandatory arguments ses=%p (programmer error)", ses);
704     return 0;
705   }
706 
707   epr = zxid_get_epr(cf, ses, svctype, url, di_opt, 0 /*Action*/, 1);
708   if (!epr) {
709     ERR("EPR could not be discovered for svctype(%s) (missing registration?)", svctype);
710     zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_WSC_RQ_OUT, "e:Client", "End Point for the service type could not be found. No end point has been registered? Too strict criteria for id_opt or az_cred? Permission denied? No discovery bootstrap is available?", TAS3_STATUS_EPR_NOT_FOUND, 0, url, svctype));
711     return 0;
712   }
713 
714   return zxid_call_epr(cf, ses, epr, az_cred, enve);
715 }
716 
717 /*() Call web service, printf style. See zxid_call() for more documentation. */
718 
719 /* Called by:  main, zxid_di_query */
zxid_callf(zxid_conf * cf,zxid_ses * ses,const char * svctype,const char * url,const char * di_opt,const char * az_cred,const char * env_f,...)720 struct zx_str* zxid_callf(zxid_conf* cf, zxid_ses* ses, const char* svctype, const char* url, const char* di_opt, const char* az_cred, const char* env_f, ...)
721 {
722   char* s;
723   va_list ap;
724   va_start(ap, env_f);
725   s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
726   va_end(ap);
727   return zxid_call(cf, ses, svctype, url, di_opt, az_cred, s);
728 }
729 
730 /*(i) Prepare a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
731  * specified by the string. Usually you should use zxid_call(), but if you want
732  * to control the steps yourself or use your own http client, this function
733  * may be useful.
734  *
735  * If the string starts by "<e:Envelope", then string
736  * should be a complete SOAP envelope including <e:Header> and <e:Body> parts. This
737  * allows caller to specify custom SOAP headers, in addition to the ones
738  * that the underlying zxid_wsc_call() will add. Usually the payload service
739  * will be passed as the contents of the body. If the string starts by
740  * "<e:Body", then the <e:Envelope> and <e:Header> are automatically added. If
741  * the string starts by neither of the above (be careful to use the "e:" as
742  * namespace prefix), then it is assumed to be the payload content of the <e:Body>
743  * and the rest of the SOAP envelope is added.
744  *
745  * cf:: ZXID configuration object, see zxid_new_conf()
746  * ses:: Session object that contains the EPR cache
747  * epr:: End point to call. From zxid_get_epr().
748  * az_cred:: (Optional) Additional authorization credentials or
749  *     attributes, query string format. These credentials will be populated
750  *     to the attribute pool in addition to the ones obtained from SSO and
751  *     other sources. Then a PDP is called to get an authorization decision
752  *     (as well as obligations we pledge to support). See also PEPMAP
753  *     configuration option. This implementes generalized (application
754  *     independent) Requestor Out and Requestor In PEPs. To implement
755  *     application dependent PEP features you should call zxid_az() directly.
756  * env:: XML payload (or SOAP Envelope) as a string
757  * return:: SOAP Envelope ready to be sent to the WSP. You can pass this to HTTP client.
758  *
759  * N.B. If the ID-WSF call for some reason needs to be retried, this function
760  * should be called for each retry. */
761 
762 /* Called by:  ws_validations, zxid_wsc_prepare_callf */
zxid_wsc_prepare_call(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,const char * az_cred,const char * enve)763 struct zx_str* zxid_wsc_prepare_call(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* enve)
764 {
765   struct zx_str* ret;
766   struct zx_e_Envelope_s* env;
767 
768   if (!cf || !ses || !enve) {
769     ERR("Missing mandatory arguments ses=%p (programmer error)", ses);
770     return 0;
771   }
772   D_INDENT("prep: ");
773   env = zxid_add_env_if_needed(cf, enve);
774   if (!env) {
775     D_DEDENT("prep: ");
776     return 0;
777   }
778 
779   /* Call Rq-Out PDP */
780 
781   if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RQ_OUT,"e:Client", cf->pepmap_rqout)) {
782     D_DEDENT("prep: ");
783     return 0;
784   }
785 
786   /* *** add usage directives */
787 
788   if (!zxid_wsc_prep(cf, ses, epr, env)) {
789     D_DEDENT("prep: ");
790     return 0;
791   }
792   if (!zxid_wsc_prep_secmech(cf, ses, epr, env)) {
793     D_DEDENT("prep: ");
794     return 0;
795   }
796   ses->wsc_msgid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(env->Header->MessageID));
797 
798   ret = zx_easy_enc_elem_opt(cf, &env->gg);
799   D_DEDENT("prep: ");
800   return ret;
801 }
802 
803 /*() Prepare a web service call, printf style.
804  * See zxid_wsc_prepare_call() for more documentation. */
805 
806 /* Called by: */
zxid_wsc_prepare_callf(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,const char * az_cred,const char * env_f,...)807 struct zx_str* zxid_wsc_prepare_callf(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* env_f, ...)
808 {
809   char* s;
810   va_list ap;
811   va_start(ap, env_f);
812   s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
813   va_end(ap);
814   return zxid_wsc_prepare_call(cf, ses, epr, az_cred, s);
815 }
816 
817 /*(i) Validate a response to web service call. Return: 1=valid. */
818 
819 /* Called by:  ws_validations */
zxid_wsc_valid_resp(zxid_conf * cf,zxid_ses * ses,const char * az_cred,const char * enve)820 int zxid_wsc_valid_resp(zxid_conf* cf, zxid_ses* ses, const char* az_cred, const char* enve)
821 {
822   int ret;
823   struct zx_e_Envelope_s* env;
824 
825   if (!cf || !ses || !enve) {
826     ERR("Missing mandatory arguments ses=%p enve=%p (programmer error)", ses, enve);
827     return 0;
828   }
829 
830   D_INDENT("valid: ");
831   env = zxid_add_env_if_needed(cf, enve);  /* *** why would envelope be missing? */
832   ret = zxid_wsc_valid_re_env(cf, ses, az_cred, env, enve);
833   D_DEDENT("valid: ");
834   return ret;
835 }
836 
837 /* EOF  --  zxidwsc.c */
838