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