1 /* zxidsso.c - Handwritten functions for implementing Single Sign-On logic for SP
2 * Copyright (c) 2013-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) 2006-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: zxidsso.c,v 1.64 2010-01-08 02:10:09 sampo Exp $
11 *
12 * 12.8.2006, created --Sampo
13 * 30.9.2006, added signature verification --Sampo
14 * 9.10.2007, added signing SOAP requests, Destination for redirects --Sampo
15 * 22.3.2008, permitted passing RelayState for SSO --Sampo
16 * 7.10.2008, added documentation --Sampo
17 * 1.2.2010, added authentication service client --Sampo
18 * 9.3.2011, added Proxy IdP processing --Sampo
19 * 26.10.2013, improved error reporting on credential expired case --Sampo
20 *
21 * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
22 */
23
24 #include "platform.h" /* needed on Win32 for snprintf() et al. */
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33
34 #include "errmac.h"
35 #include "zx.h"
36 #include "zxid.h"
37 #include "zxidpriv.h"
38 #include "zxidutil.h"
39 #include "zxidconf.h"
40 #include "saml2.h"
41 #include "wsf.h"
42 #include "c/zx-const.h"
43 #include "c/zx-ns.h"
44 #include "c/zx-data.h"
45
46 /* ============== Generating and sending AuthnReq ============== */
47
48 /*() This function makes the policy decision about which profile to
49 * use. It is only used if there was no explicit specification in the
50 * CGI form (e.g. "Login (P)" button). Currently it is a stub that
51 * always picks the SAML artifact profile. Eventually configuration options
52 * or cgi input can be used to determine the profile in a more
53 * sophisticated way. Often zxid_mk_authn_req() will override the
54 * return value of this function by its own inspection of the CGI
55 * variables. */
56
57 /* Called by: zxid_start_sso_url */
zxid_pick_sso_profile(zxid_conf * cf,zxid_cgi * cgi,zxid_entity * idp_meta)58 int zxid_pick_sso_profile(zxid_conf* cf, zxid_cgi* cgi, zxid_entity* idp_meta)
59 {
60 switch (cgi->pr_ix) {
61 case ZXID_OIDC1_CODE: return ZXID_OIDC1_CODE;
62 case ZXID_OIDC1_ID_TOK_TOK: return ZXID_OIDC1_ID_TOK_TOK;
63 }
64 /* More sophisticated policy may eventually go here. */
65 return ZXID_SAML2_ART;
66 }
67
68 /*() Map name id format form field to SAML specified URN string. */
69 /* Called by: covimp_test x10, zxid_map_identity_token, zxid_mk_authn_req, zxid_nidmap_identity_token */
zxid_saml2_map_nid_fmt(const char * f)70 const char* zxid_saml2_map_nid_fmt(const char* f)
71 {
72 if (!f || !f[0]) {
73 ERR("NULL argument %p", f);
74 return "trnsnt";
75 }
76 #if 0
77 switch (f[0]) {
78 case 'n' /*'none'*/: return "";
79 case 'p' /*'prstnt'*/: return SAML2_PERSISTENT_NID_FMT;
80 case 't' /*'trnsnt'*/: return SAML2_TRANSIENT_NID_FMT;
81 case 'u' /*'unspfd'*/: return SAML2_UNSPECIFIED_NID_FMT;
82 case 'e' /*'emladr'*/: return SAML2_EMAILADDR_NID_FMT;
83 case 'x' /*'x509sn'*/: return SAML2_X509_NID_FMT;
84 case 'w' /*'windmn'*/: return SAML2_WINDOMAINQN_NID_FMT;
85 case 'k' /*'kerbrs'*/: return SAML2_KERBEROS_NID_FMT;
86 case 's' /*'saml'*/: return SAML2_ENTITY_NID_FMT;
87 }
88 #else
89 if (!strcmp("prstnt", f)) return SAML2_PERSISTENT_NID_FMT;
90 if (!strcmp("trnsnt", f)) return SAML2_TRANSIENT_NID_FMT;
91 if (!strcmp("none", f)) return "";
92 if (!strcmp("unspfd", f)) return SAML2_UNSPECIFIED_NID_FMT;
93 if (!strcmp("emladr", f)) return SAML2_EMAILADDR_NID_FMT;
94 if (!strcmp("x509sn", f)) return SAML2_X509_NID_FMT;
95 if (!strcmp("windmn", f)) return SAML2_WINDOMAINQN_NID_FMT;
96 if (!strcmp("kerbrs", f)) return SAML2_KERBEROS_NID_FMT;
97 if (!strcmp("saml", f)) return SAML2_ENTITY_NID_FMT;
98 #endif
99 return f;
100 }
101
102 /*() Map protocol binding form field to SAML specified URN string. */
103 /* Called by: covimp_test x7 */
zxid_saml2_map_protocol_binding(const char * b)104 const char* zxid_saml2_map_protocol_binding(const char* b)
105 {
106 switch (b[0]) {
107 case 'r' /*'redir'*/: return SAML2_REDIR;
108 case 'a' /*'art'*/: return SAML2_ART;
109 case 'p' /*'post'*/: return SAML2_POST;
110 case 'q' /*'qsimplesig'*/: return SAML2_POST_SIMPLE_SIGN;
111 case 's' /*'soap'*/: return SAML2_SOAP;
112 case 'e' /*'ecp'*/:
113 /*case 'paos':*/ return SAML2_PAOS;
114 default: return b;
115 }
116 }
117
118 /*() Map SAML protocol binding URN to form field. */
119 /* Called by: covimp_test x8, zxid_idp_sso x3, zxid_sp_loc_by_index_raw */
zxid_protocol_binding_map_saml2(struct zx_str * b)120 int zxid_protocol_binding_map_saml2(struct zx_str* b)
121 {
122 if (!b || !b->len || !b->s) {
123 D("No binding supplied, assume redir %d", 0);
124 return 'r';
125 }
126 if (b->len == sizeof(SAML2_REDIR)-1 && !memcmp(b->s, SAML2_REDIR, b->len)) return 'r';
127 if (b->len == sizeof(SAML2_ART)-1 && !memcmp(b->s, SAML2_ART, b->len)) return 'a';
128 if (b->len == sizeof(SAML2_POST)-1 && !memcmp(b->s, SAML2_POST, b->len)) return 'p';
129 if (b->len == sizeof(SAML2_POST_SIMPLE_SIGN)-1 && !memcmp(b->s, SAML2_POST_SIMPLE_SIGN, b->len)) return 'q';
130 if (b->len == sizeof(SAML2_SOAP)-1 && !memcmp(b->s, SAML2_SOAP, b->len)) return 's';
131 if (b->len == sizeof(SAML2_PAOS)-1 && !memcmp(b->s, SAML2_PAOS, b->len)) return 'e';
132 D("Unknown binding(%.*s) supplied, assume redir.", b->len, b->s);
133 return 'r';
134 }
135
136 /*() Map authentication contest class ref form field to SAML specified URN string. */
137 /* Called by: covimp_test x8, zxid_mk_authn_req */
zxid_saml2_map_authn_ctx(char * c)138 char* zxid_saml2_map_authn_ctx(char* c)
139 {
140 switch (c[0]) {
141 case 'n' /*'none'*/: return "";
142 case 'p':
143 switch (c[2]) {
144 case 'p' /*'pwp'*/: return SAML_AUTHCTX_PASSWORDPROTECTED;
145 case 0 /*'pw'*/: return SAML_AUTHCTX_PASSWORD;
146 case 'v' /*'prvses'*/: return SAML_AUTHCTX_PREVSESS;
147 }
148 break;
149 case 'c' /*'clicert'*/: return SAML_AUTHCTX_SSL_TLS_CERT;
150 case 'u' /*'unspcf'*/: return SAML_AUTHCTX_UNSPCFD;
151 case 'i' /*'ip'*/: return SAML_AUTHCTX_INPROT;
152 }
153 return c;
154 }
155
156 /*() cgi->rs will be copied to ses->rs and from there in ab_pep to resource-id.
157 * We compress and safe_base64 encode it to protect any URL special characters. */
158 /* Called by: zxid_start_sso_url, zxid_simple_show_idp_sel */
zxid_sso_set_relay_state_to_return_to_this_url(zxid_conf * cf,zxid_cgi * cgi)159 void zxid_sso_set_relay_state_to_return_to_this_url(zxid_conf* cf, zxid_cgi* cgi)
160 {
161 struct zx_str* ss;
162 D("Previous rs(%s)", STRNULLCHKD(cgi->rs));
163 // *** absolute URI consideration
164 if (!cgi->rs || !cgi->rs[0]) {
165 if (!cgi->uri_path) {
166 ERR("null or empty cgi->uri_path=%p qs(%s) programming error", cgi->uri_path, STRNULLCHK(cgi->qs));
167 if (!cgi->uri_path)
168 cgi->uri_path = "";
169 }
170 ss = zx_strf(cf->ctx, "%s%c%s", cgi->uri_path, cgi->qs&&cgi->qs[0]?'?':0, STRNULLCHK(cgi->qs));
171 cgi->rs = zxid_deflate_safe_b64_raw(cf->ctx, -2, ss->s);
172 D("rs(%s) from(%s) uri_path(%s) qs(%s)",cgi->rs,ss->s,cgi->uri_path,STRNULLCHKD(cgi->qs));
173 zx_str_free(cf->ctx, ss);
174 }
175 }
176
177 /*(i) Generate an authentication request and make a URL out of it.
178 *
179 * cf:: Used for many configuration options and memory allocation
180 * cgi:: Used to pick the desired SSO profile based on hidden fields or user
181 * input. The cgi->rs field specifies the URL to redirect to after the SSO.
182 * The cgi->eid specifies the IdP entity ID.
183 * return:: Redirect URL as zx_str. Caller should eventually free this memory.
184 */
185 /* Called by: zxid_start_sso_location */
zxid_start_sso_url(zxid_conf * cf,zxid_cgi * cgi)186 struct zx_str* zxid_start_sso_url(zxid_conf* cf, zxid_cgi* cgi)
187 {
188 struct zx_md_SingleSignOnService_s* sso_svc;
189 struct zx_sp_AuthnRequest_s* ar;
190 struct zx_attr_s* dest;
191 struct zx_str* ars;
192 int sso_profile_ix;
193 zxid_entity* idp_meta;
194 D_INDENT("start_sso: ");
195 D("cgi=%p cgi->eid=%p eid(%s) pr_ix=%d", cgi, cgi->eid, STRNULLCHKD(cgi->eid), cgi->pr_ix);
196 zxid_sso_set_relay_state_to_return_to_this_url(cf, cgi);
197 if (!cgi->eid || !cgi->eid[0]) {
198 D("Entity ID missing %p", cgi->eid);
199 cgi->err = "IdP URL Missing or incorrect";
200 D_DEDENT("start_sso: ");
201 return 0;
202 }
203 idp_meta = zxid_get_ent(cf, cgi->eid);
204 if (!idp_meta) {
205 cgi->err = "IdP URL incorrect or IdP does not support fetching metadata from that URL.";
206 D_DEDENT("start_sso: ");
207 return 0;
208 }
209 switch (sso_profile_ix = zxid_pick_sso_profile(cf, cgi, idp_meta)) {
210 case ZXID_SAML2_ART:
211 case ZXID_SAML2_POST:
212 case ZXID_SAML2_POST_SIMPLE_SIGN:
213 /* All of the above use redir binding for sending AnReq */
214 if (!idp_meta->ed->IDPSSODescriptor) {
215 ERR("Entity(%s) does not have IdP SSO Descriptor (metadata problem)", cgi->eid);
216 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", cgi->eid, "No IDPSSODescriptor");
217 cgi->err = "Bad IdP metadata. Try different IdP.";
218 D_DEDENT("start_sso: ");
219 return 0;
220 }
221 for (sso_svc = idp_meta->ed->IDPSSODescriptor->SingleSignOnService;
222 sso_svc;
223 sso_svc = (struct zx_md_SingleSignOnService_s*)sso_svc->gg.g.n) {
224 if (sso_svc->gg.g.tok != zx_md_SingleSignOnService_ELEM)
225 continue;
226 if (sso_svc->Binding && !memcmp(SAML2_REDIR, sso_svc->Binding->g.s, sso_svc->Binding->g.len))
227 break;
228 }
229 if (!sso_svc) {
230 ERR("IdP Entity(%s) does not have any IdP SSO Service with " SAML2_REDIR " binding (metadata problem)", cgi->eid);
231 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", cgi->eid, "No redir binding");
232 cgi->err = "Bad IdP metadata. Try different IdP.";
233 D_DEDENT("start_sso: ");
234 return 0;
235 }
236 DD("HERE3 len=%d (%.*s)", sso_svc?sso_svc->Location->g.len:0, sso_svc->Location->g.len, sso_svc->Location->g.s);
237 ar = zxid_mk_authn_req(cf, cgi);
238 dest = zx_dup_len_attr(cf->ctx, 0, zx_Destination_ATTR, sso_svc->Location->g.len, sso_svc->Location->g.s);
239 ZX_ORD_INS_ATTR(ar, Destination, dest);
240 ars = zx_easy_enc_elem_opt(cf, &ar->gg);
241 D("AuthnReq(%.*s) %p", ars->len, ars->s, dest);
242 break;
243 case ZXID_OIDC1_CODE:
244 case ZXID_OIDC1_ID_TOK_TOK:
245 if (!idp_meta->ed->IDPSSODescriptor) {
246 ERR("Entity(%s) does not have IdP SSO Descriptor (OAUTH2) (metadata problem)", cgi->eid);
247 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", cgi->eid, "No IDPSSODescriptor (OAUTH2)");
248 cgi->err = "Bad IdP metadata (OAUTH). Try different IdP.";
249 D_DEDENT("start_sso: ");
250 return 0;
251 }
252 for (sso_svc = idp_meta->ed->IDPSSODescriptor->SingleSignOnService;
253 sso_svc;
254 sso_svc = (struct zx_md_SingleSignOnService_s*)sso_svc->gg.g.n) {
255 if (sso_svc->gg.g.tok != zx_md_SingleSignOnService_ELEM)
256 continue;
257 if (sso_svc->Binding && !memcmp(OAUTH2_REDIR,sso_svc->Binding->g.s,sso_svc->Binding->g.len))
258 break;
259 }
260 if (!sso_svc) {
261 ERR("IdP Entity(%s) does not have any IdP SSO Service with " OAUTH2_REDIR " binding (metadata problem)", cgi->eid);
262 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", cgi->eid, "No OAUTH2 redir binding");
263 cgi->err = "Bad IdP metadata. Try different IdP.";
264 D_DEDENT("start_sso: ");
265 return 0;
266 }
267 DD("HERE3 len=%d (%.*s)", sso_svc?sso_svc->Location->g.len:0, sso_svc->Location->g.len, sso_svc->Location->g.s);
268 if (cf->log_level>0)
269 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "OANREDIR", cgi->eid, 0);
270 ars = zxid_mk_oauth_az_req(cf, cgi, &sso_svc->Location->g, cgi->rs);
271
272 D_DEDENT("start_sso: ");
273 return ars;
274 default:
275 NEVER("Inappropriate SSO profile: %d", sso_profile_ix);
276 cgi->err = "Inappropriate SSO profile. Bad metadata?";
277 D_DEDENT("start_sso: ");
278 return 0;
279 }
280
281 if (cf->idp_ena) { /* (PXY) Middle IdP of Proxy IdP scenario */
282 if (cgi->rs) {
283 ERR("Attempt to supply RelayState(%s) in middle IdP of Proxy IdP flow. Ignored.", cgi->rs);
284 }
285 cgi->rs = cgi->ssoreq; /* Carry the original authn req in RelayState */
286 D("Middle IdP of Proxy IdP flow RelayState(%s)", STRNULLCHK(cgi->rs));
287 }
288
289 if (cf->log_level>0)
290 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "ANREDIR", cgi->eid, 0);
291 ars = zxid_saml2_redir_url(cf, &sso_svc->Location->g, ars, cgi->rs);
292 D_DEDENT("start_sso: ");
293 return ars;
294 }
295
296 /*() Wrapper for zxid_start_sso_url(), used when Location header needs to be passed outside.
297 * return:: Location header as zx_str. Caller should eventually free this memory. */
298
299 /* Called by: main x2, zxid_simple_no_ses_cf */
zxid_start_sso_location(zxid_conf * cf,zxid_cgi * cgi)300 struct zx_str* zxid_start_sso_location(zxid_conf* cf, zxid_cgi* cgi)
301 {
302 struct zx_str* ss;
303 struct zx_str* url = zxid_start_sso_url(cf, cgi);
304 if (!url)
305 return 0;
306 ss = zx_strf(cf->ctx, "Location: %.*s" CRLF2, url->len, url->s);
307 zx_str_free(cf->ctx, url);
308 return ss;
309 }
310
311 /* ============== Process Response and SSO Assertion ============== */
312
313 /*(i) Dereference an artifact to obtain an assertion. This is the last
314 * step in artifact SSO profile and involved making a SOAP call to the
315 * IdP. The artifact is received in saml_art CGI field, <<see:
316 * zxid_parse_cgi()>> where SAMLart query string argument is parsed. */
317
318 /* Called by: main x2, zxid_simple_no_ses_cf */
zxid_sp_deref_art(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)319 int zxid_sp_deref_art(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
320 {
321 struct zx_md_ArtifactResolutionService_s* ar_svc;
322 struct zx_e_Body_s* body;
323 struct zx_root_s* r;
324 zxid_entity* idp_meta;
325 int len;
326 char end_pt_ix[16];
327 char* raw_succinct_id;
328 /*char* msg_handle;*/
329 char* p;
330 char buf[64];
331 D_INDENT("deref: ");
332
333 if (!cgi || !cgi->saml_art || !*cgi->saml_art) {
334 ERR("SAMLart missing or empty string. %p %p", cgi, cgi?cgi->saml_art:0);
335 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "ERR", cgi?cgi->saml_art:0, "Artifact missing");
336 D_DEDENT("deref: ");
337 return 0;
338 }
339
340 len = strlen(cgi->saml_art);
341 if (cf->log_level > 0)
342 zxlog(cf, 0, 0, 0, 0, 0, 0, ZX_GET_CONTENT(ses->nameid), "N", "W", "ART", cgi->saml_art, 0);
343 if (len-7 >= sizeof(buf)*4/3) {
344 ERR("SAMLart(%s), %d chars, too long. Max(%d) chars.", cgi->saml_art, len, (int)sizeof(buf)*4/3+6);
345 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "ERR", cgi->saml_art, "Artifact too long");
346 D_DEDENT("deref: ");
347 return 0;
348 }
349 p = unbase64_raw(cgi->saml_art, cgi->saml_art + len, buf, zx_std_index_64);
350 *p = 0;
351 if (buf[0])
352 goto badart;
353 switch (buf[1]) {
354 case 0x03: /* SAML 1.1 */
355 end_pt_ix[0] = 0;
356 raw_succinct_id = buf + 2;
357 /*msg_handle = buf + 22;*/
358 break;
359 case 0x04: /* SAML 2.0 */
360 sprintf(end_pt_ix, "%d", (unsigned)buf[2] << 8 | (unsigned)buf[3]);
361 raw_succinct_id = buf + 4;
362 /*msg_handle = buf + 24;*/
363 break;
364 default: goto badart;
365 }
366
367 idp_meta = zxid_get_ent_by_succinct_id(cf, raw_succinct_id);
368 if (!idp_meta || !idp_meta->eid) {
369 ERR("Unable to dereference SAMLart(%s). Can not find metadata for IdP. %p", cgi->saml_art, idp_meta);
370 D_DEDENT("deref: ");
371 return 0;
372 }
373
374 switch (buf[1]) {
375 case 0x03: /* SAML 1.1 */
376 /* *** ff12_resolve_art() */
377 break;
378 case 0x04: /* SAML 2.0 */
379 if (!idp_meta->ed->IDPSSODescriptor) {
380 ERR("Entity(%s) does not have IdP SSO Descriptor (metadata problem)", idp_meta->eid);
381 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", 0, "No IDPSSODescriptor eid(%s)", idp_meta->eid);
382 D_DEDENT("deref: ");
383 return 0;
384 }
385 for (ar_svc = idp_meta->ed->IDPSSODescriptor->ArtifactResolutionService;
386 ar_svc;
387 ar_svc = (struct zx_md_ArtifactResolutionService_s*)ar_svc->gg.g.n) {
388 if (ar_svc->gg.g.tok != zx_md_ArtifactResolutionService_ELEM)
389 continue;
390 if (ar_svc->Binding && !memcmp(SAML2_SOAP, ar_svc->Binding->g.s, ar_svc->Binding->g.len)
391 && ar_svc->index && !memcmp(end_pt_ix, ar_svc->index->g.s, ar_svc->index->g.len)
392 && ar_svc->Location)
393 break;
394 }
395 if (!ar_svc) {
396 ERR("Entity(%s) does not have any IdP Artifact Resolution Service with " SAML2_SOAP " binding and index(%s) (metadata problem)", idp_meta->eid, end_pt_ix);
397 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "B", "ERR", 0, "No Artifact Resolution Svc eid(%s) ep_ix(%s)", idp_meta->eid, end_pt_ix);
398 D_DEDENT("deref: ");
399 return 0;
400 }
401
402 body = zx_NEW_e_Body(cf->ctx,0);
403 body->ArtifactResolve = zxid_mk_art_deref(cf, &body->gg, idp_meta, cgi->saml_art);
404 r = zxid_soap_call_hdr_body(cf, &ar_svc->Location->g, 0, body);
405 len = zxid_sp_soap_dispatch(cf, cgi, ses, r);
406 D_DEDENT("deref: ");
407 return len;
408 default: goto badart;
409 }
410
411 badart:
412 ERR("Bad SAMLart type code 0x%02x 0x%02x", buf[0], buf[1]);
413 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "ERR", 0, "Bad SAMLart type code 0x%02x 0x%02x", buf[0], buf[1]);
414 D_DEDENT("deref: ");
415 return 0;
416 }
417
418 /*() Map ZXSIG constant to letter for log and string message. */
419
420 /* Called by: covimp_test x11, zxid_chk_sig, zxid_decode_redir_or_post, zxid_sp_sso_finalize, zxid_wsc_valid_re_env, zxid_wsf_validate_a7n, zxid_wsp_validate_env */
zxid_sigres_map(int sigres,char ** sigval,char ** sigmsg)421 void zxid_sigres_map(int sigres, char** sigval, char** sigmsg)
422 {
423 switch (sigres) {
424 case ZXSIG_OK:
425 D("Signature validated. %d", 1);
426 *sigval = "O";
427 *sigmsg = "Signature validated.";
428 break;
429 case ZXSIG_BAD_DALGO: /* 1 Unsupported digest algorithm. */
430 ERR("Bad digest algo. %d", sigres);
431 *sigval = "A";
432 *sigmsg = "Unsupported digest algorithm. Signature can not be validated.";
433 break;
434 case ZXSIG_DIGEST_LEN: /* 2 Wrong digest length. */
435 ERR("Bad digest length. %d", sigres);
436 *sigval = "G";
437 *sigmsg = "Wrong digest length. Signature can not be validated.";
438 break;
439 case ZXSIG_BAD_DIGEST: /* 3 Digest value does not match. */
440 ERR("Bad digest. Canon problem? %d", sigres);
441 *sigval = "G";
442 *sigmsg = "Message digest does not match signed content. Canonicalization problem? Or falsified or altered or substituted content. Signature can not be validated.";
443 break;
444 case ZXSIG_BAD_SALGO: /* 4 Unsupported signature algorithm. */
445 ERR("Bad sig algo. %d", sigres);
446 *sigval = "A";
447 *sigmsg = "Unsupported signature algorithm. Signature can not be validated.";
448 break;
449 case ZXSIG_BAD_CERT: /* 5 Extraction of public key from certificate failed. */
450 ERR("Bad cert. %d", sigres);
451 *sigval = "I";
452 *sigmsg = "Bad IdP certificate or bad IdP metadata or unknown IdP. Signature can not be validated.";
453 break;
454 case ZXSIG_VFY_FAIL: /* 6 Verification of signature failed. */
455 ERR("Bad sig. %d", sigres);
456 *sigval = "R";
457 *sigmsg = "Signature does not match signed content (but content checksum matches). Content may have been falsified, altered, or substituted; or IdP metadata does not match the keys actually used by the IdP.";
458 break;
459 case ZXSIG_NO_SIG:
460 ERR("Not signed. %d", sigres);
461 *sigval = "N";
462 *sigmsg = "No signature found.";
463 break;
464 case ZXSIG_TIMEOUT:
465 ERR("Out of validity period. %d", sigres);
466 *sigval = "V";
467 *sigmsg = "Assertion is not in its validity period.";
468 break;
469 case ZXSIG_AUDIENCE:
470 ERR("Wrong audience. %d", sigres);
471 *sigval = "V";
472 *sigmsg = "Assertion has wrong audience.";
473 break;
474 default:
475 ERR("Other sig err(%d)", sigres);
476 *sigval = "E";
477 *sigmsg = "Broken or unvalidatable signature.";
478 }
479 }
480
481 /*(i) Validates conditions required by Liberty Alliance SAML2 conformance testing.
482 *
483 * May eventually validate additional conditions as well (this is the right place
484 * to add them). N.B. It is not an error if a condition is missing, or there
485 * is no Conditions element at all.
486 *
487 * cf:: Configuration object, used to determine time slops. Potentially
488 * used for memory allocation via cf->ctx.
489 * cgi:: Optional CGI object. If non-NULL, sigval and sigmsg will be set.
490 * ses:: Optional session object. If non-NULL, then sigres code will be set.
491 * a7n:: Assertion whose conditions are checked.
492 * myentid:: Entity ID used for checking audience restriction. Typically from zxid_my_ent_id(cf)
493 * ourts:: Timestamp for validating NotOnOrAfter and NotBefore.
494 * err:: Result argument: Error letter (as may appear in audit log entry). The returned
495 * string will be a constant and MUST NOT be freed by the caller.
496 * return:: 0 (ZXSIG_OK) if validation was successful, otherwise a ZXSIG error code. */
497
498 /* Called by: zxid_sp_sso_finalize, zxid_wsf_validate_a7n */
zxid_validate_cond(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_a7n * a7n,struct zx_str * myentid,struct timeval * ourts,char ** err)499 int zxid_validate_cond(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, zxid_a7n* a7n, struct zx_str* myentid, struct timeval* ourts, char** err)
500 {
501 struct timeval tsbuf;
502 struct zx_sa_AudienceRestriction_s* audr;
503 struct zx_elem_s* aud;
504 struct zx_str* ss;
505 int secs;
506
507 if (!a7n || !a7n->Conditions) {
508 INFO("Assertion does not have Conditions. %p", a7n);
509 return ZXSIG_OK;
510 }
511 if (!myentid || !myentid->len) {
512 ERR("My entity ID missing %p", myentid);
513 return ZXSIG_OK;
514 }
515
516 if (!ourts) {
517 GETTIMEOFDAY(&tsbuf, 0);
518 ourts = &tsbuf;
519 }
520
521 if (a7n->Conditions->AudienceRestriction) {
522 for (audr = a7n->Conditions->AudienceRestriction;
523 audr;
524 audr = (struct zx_sa_AudienceRestriction_s*)audr->gg.g.n) {
525 if (audr->gg.g.tok != zx_sa_AudienceRestriction_ELEM)
526 continue;
527 for (aud = audr->Audience;
528 aud;
529 aud = (struct zx_elem_s*)aud->g.n) {
530 if (aud->g.tok != zx_sa_Audience_ELEM)
531 continue;
532 ss = ZX_GET_CONTENT(aud);
533 if (ss?ss->len:0 == myentid->len && !memcmp(ss->s, myentid->s, ss->len)) {
534 D("Found audience. %d", 1);
535 goto found_audience;
536 }
537 }
538 }
539 if (cgi) {
540 cgi->sigval = "V";
541 cgi->sigmsg = "This SP not included in the Assertion Audience.";
542 }
543 if (ses)
544 ses->sigres = ZXSIG_AUDIENCE;
545 if (cf->audience_fatal) {
546 ERR("SSO error: AudienceRestriction wrong. My entityID(%.*s)", myentid->len, myentid->s);
547 if (err)
548 *err = "P";
549 if (ses) {
550 zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Audience Restriction is wrong. Configuration or implementation error.", TAS3_STATUS_BADCOND, 0, "a7n", 0));
551 }
552 return ZXSIG_AUDIENCE;
553 } else {
554 INFO("SSO warn: AudienceRestriction wrong. My entityID(%.*s). Configured to ignore this (AUDIENCE_FATAL=0).", myentid->len, myentid->s);
555 }
556 } else {
557 INFO("Assertion does not have AudienceRestriction. %d", 0);
558 }
559 found_audience:
560
561 if (a7n->Conditions->NotOnOrAfter && a7n->Conditions->NotOnOrAfter->g.len > 18) {
562 secs = zx_date_time_to_secs(a7n->Conditions->NotOnOrAfter->g.s);
563 if (secs <= ourts->tv_sec) {
564 if (secs + cf->after_slop <= ourts->tv_sec) {
565 ERR("NotOnOrAfter rejected with slop of %d. Time to expiry %ld secs. Our gettimeofday: %ld secs, remote: %d secs. Relogin to refresh the session?", cf->after_slop, secs - ourts->tv_sec, ourts->tv_sec, secs);
566 if (cgi) {
567 cgi->sigval = "V";
568 cgi->sigmsg = "Assertion has expired. Relogin to refresh the session?";
569 }
570 if (ses)
571 ses->sigres = ZXSIG_TIMEOUT;
572 if (cf->timeout_fatal) {
573 if (err)
574 *err = "P";
575 if (ses) {
576 /* This is the only problem fixable by the user so emit a special
577 * informative message with distinctive error code. It may even be
578 * possible for the client end to automatically refresh the credential. */
579 zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion has expired (or clock synchrony problem between servers). Perhaps relogin to refresh the session will fix the problem?", TAS3_STATUS_EXPIRED, 0, "a7n", 0));
580 }
581 return ZXSIG_TIMEOUT;
582 }
583 } else {
584 D("NotOnOrAfter accepted with slop of %d. Time to expiry %ld secs. Our gettimeofday: %ld secs, remote: %d secs", cf->after_slop, secs - ourts->tv_sec, ourts->tv_sec, secs);
585 }
586 } else {
587 D("NotOnOrAfter ok. Time to expiry %ld secs. Our gettimeofday: %ld secs, remote: %d secs", secs - ourts->tv_sec, ourts->tv_sec, secs);
588 }
589 } else {
590 INFO("Assertion does not have NotOnOrAfter. %d", 0);
591 }
592
593 if (a7n->Conditions->NotBefore && a7n->Conditions->NotBefore->g.len > 18) {
594 secs = zx_date_time_to_secs(a7n->Conditions->NotBefore->g.s);
595 if (secs > ourts->tv_sec) {
596 if (secs - cf->before_slop > ourts->tv_sec) {
597 ERR("NotBefore rejected with slop of %d. Time to validity %ld secs. Our gettimeofday: %ld secs, remote: %d secs", cf->before_slop, secs - ourts->tv_sec, ourts->tv_sec, secs);
598 if (cgi) {
599 cgi->sigval = "V";
600 cgi->sigmsg = "Assertion is not valid yet (too soon). Clock synchrony problem between servers?";
601 }
602 if (ses)
603 ses->sigres = ZXSIG_TIMEOUT;
604 if (cf->timeout_fatal) {
605 if (err)
606 *err = "P";
607 if (ses) {
608 zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Assertion is not valid yet (too soon). Clock synchrony problem between servers?", TAS3_STATUS_BADCOND, 0, "a7n", 0));
609 }
610 return ZXSIG_TIMEOUT;
611 }
612 } else {
613 D("NotBefore accepted with slop of %d. Time to validity %ld secs. Our gettimeofday: %ld secs, remote: %d secs", cf->before_slop, secs - ourts->tv_sec, ourts->tv_sec, secs);
614 }
615 } else {
616 D("NotBefore ok. Time from validity %ld secs. Our gettimeofday: %ld secs, remote: %d secs", ourts->tv_sec - secs, ourts->tv_sec, secs);
617 }
618 } else {
619 INFO("Assertion does not have NotBefore. %d", 0);
620 }
621 return ZXSIG_OK;
622 }
623
624 struct zx_str unknown_str = {0,0,1,"??"}; /* Static string used as dummy value. */
625
626 /*(i) zxid_sp_sso_finalize() gets called irrespective of binding (POST, Artifact)
627 * and validates the SSO a7n, including the authentication statement.
628 * Then, it creates session and optionally user entry.
629 *
630 * cf:: Configuration object, used to determine time slops, potentially memalloc via cf->ctx
631 * cgi:: CGI object. sigval and sigmsg may be set.
632 * ses:: Session object. Will be modified according to new session created from the SSO assertion.
633 * a7n:: Single Sign-On assertion
634 * return:: 0 for failure, otherwise some success code such as ZXID_SSO_OK
635 *
636 * See also: zxid_sp_sso_finalize_jwt() in zxidoauth.c
637 */
638
639 /* Called by: main, sig_validate, zxid_sp_dig_oauth_sso_a7n, zxid_sp_dig_sso_a7n */
zxid_sp_sso_finalize(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_a7n * a7n,struct zx_ns_s * pop_seen)640 int zxid_sp_sso_finalize(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, zxid_a7n* a7n, struct zx_ns_s* pop_seen)
641 {
642 char* err = "S"; /* See: RES in zxid-log.pd, section "ZXID Log Format" */
643 struct timeval ourts;
644 struct timeval srcts = {0,501000};
645 struct zx_str* logpath;
646 struct zx_str* issuer = &unknown_str;
647 struct zx_str* subj = &unknown_str;
648 struct zx_str* ss;
649 struct zxsig_ref refs;
650 zxid_entity* idp_meta;
651 /*ses->sigres = ZXSIG_NO_SIG; set earlier, do not overwrite */
652 ses->a7n = a7n;
653 ses->rs = cgi->rs;
654 ses->ssores = 1;
655 GETTIMEOFDAY(&ourts, 0);
656
657 D_INDENT("ssof: ");
658
659 if (!a7n || !a7n->AuthnStatement) {
660 ERR("SSO failed: no assertion supplied, or assertion didn't contain AuthnStatement. %p", a7n);
661 goto erro;
662 }
663 if (!a7n->IssueInstant || !a7n->IssueInstant->g.len || !a7n->IssueInstant->g.s || !a7n->IssueInstant->g.s[0]) {
664 ERR("SSO failed: assertion does not have IssueInstant or it is empty. %p", a7n->IssueInstant);
665 goto erro;
666 }
667 srcts.tv_sec = zx_date_time_to_secs(a7n->IssueInstant->g.s);
668 if (!(issuer = ZX_GET_CONTENT(a7n->Issuer))) {
669 ERR("SSO failed: assertion does not have Issuer. %p", a7n->Issuer);
670 goto erro;
671 }
672
673 /* See zxid_wsp_validate() for similar code. *** consider factoring out commonality */
674
675 if (!a7n->Subject) {
676 ERR("SSO failed: assertion does not have Subject. %p", a7n);
677 goto erro;
678 }
679
680 ses->nameid = zxid_decrypt_nameid(cf, a7n->Subject->NameID, a7n->Subject->EncryptedID);
681 if (!(subj = ZX_GET_CONTENT(ses->nameid))) {
682 ERR("SSO failed: assertion does not have Subject->NameID. %p", ses->nameid);
683 goto erro;
684 }
685
686 ses->nid = zx_str_to_c(cf->ctx, subj);
687 if (ses->nameid->Format && !memcmp(ses->nameid->Format->g.s, SAML2_TRANSIENT_NID_FMT, ses->nameid->Format->g.len)) {
688 ses->nidfmt = 0;
689 } else {
690 ses->nidfmt = 1; /* anything nontransient may be a federation */
691 }
692
693 /* In SSO the acting identity and the target identity are the same */
694 ses->tgta7n = ses->a7n;
695 ses->tgtnameid = ses->nameid;
696 ses->tgt = ses->nid;
697 ses->tgtfmt = ses->nidfmt;
698
699 if (a7n->AuthnStatement->SessionIndex)
700 ses->sesix = zx_str_to_c(cf->ctx, &a7n->AuthnStatement->SessionIndex->g);
701
702 D("SSOA7N received. NID(%s) FMT(%d) SESIX(%s)", ses->nid, ses->nidfmt, STRNULLCHK(ses->sesix));
703
704 /* Validate signature (*** add Issuer trusted check, CA validation, etc.) */
705
706 idp_meta = zxid_get_ent_ss(cf, issuer);
707 if (!idp_meta) {
708 ERR("Unable to find metadata for Issuer(%.*s).", issuer->len, issuer->s);
709 cgi->sigval = "I";
710 cgi->sigmsg = "Issuer of Assertion unknown.";
711 ses->sigres = ZXSIG_NO_SIG;
712 if (cf->nosig_fatal) {
713 err = "P";
714 goto erro;
715 }
716 } else {
717 if (a7n->Signature && a7n->Signature->SignedInfo && a7n->Signature->SignedInfo->Reference) {
718 zx_reset_ns_ctx(cf->ctx);
719 ZERO(&refs, sizeof(refs));
720 refs.sref = a7n->Signature->SignedInfo->Reference;
721 refs.blob = &a7n->gg;
722 refs.pop_seen = pop_seen;
723 zx_see_elem_ns(cf->ctx, &refs.pop_seen, &a7n->gg);
724 ses->sigres = zxsig_validate(cf->ctx, idp_meta->sign_cert, a7n->Signature, 1, &refs);
725 zxid_sigres_map(ses->sigres, &cgi->sigval, &cgi->sigmsg);
726 } else {
727 if (cf->msg_sig_ok && !ses->sigres) {
728 INFO("Assertion without signature accepted due to message level signature (SimpleSign) %d", 0);
729 } else {
730 ERR("SSO warn: assertion not signed. Sigval(%s) %p", STRNULLCHKNULL(cgi->sigval), a7n->Signature);
731 cgi->sigval = "N";
732 cgi->sigmsg = "Assertion was not signed.";
733 ses->sigres = ZXSIG_NO_SIG;
734 if (cf->nosig_fatal) {
735 err = "P";
736 goto erro;
737 }
738 }
739 }
740 }
741 if (cf->sig_fatal && ses->sigres) {
742 ERR("Fail SSO due to failed signature sigres=%d", ses->sigres);
743 err = "P";
744 goto erro;
745 }
746
747 if (zxid_validate_cond(cf, cgi, ses, a7n, zxid_my_ent_id(cf), &ourts, &err))
748 goto erro;
749
750 if (cf->log_rely_a7n) {
751 DD("Logging... %d", 0);
752 logpath = zxlog_path(cf, issuer, &a7n->ID->g, ZXLOG_RELY_DIR, ZXLOG_A7N_KIND, 1);
753 if (logpath) {
754 ses->sso_a7n_path = ses->tgt_a7n_path = zx_str_to_c(cf->ctx, logpath);
755 ss = zx_easy_enc_elem_sig(cf, &a7n->gg);
756 if (zxlog_dup_check(cf, logpath, "SSO assertion")) {
757 if (cf->dup_a7n_fatal) {
758 err = "C";
759 zxlog_blob(cf, cf->log_rely_a7n, logpath, ss, "sp_sso_finalize dup err");
760 goto erro;
761 }
762 }
763 zxlog_blob(cf, cf->log_rely_a7n, logpath, ss, "sp_sso_finalize");
764 zx_str_free(cf->ctx, ss);
765 }
766 }
767 DD("Creating session... %d", 0);
768 ses->ssores = 0;
769 zxid_put_ses(cf, ses);
770 zxid_snarf_eprs_from_ses(cf, ses); /* Harvest bootstrap(s) from attribute statements */
771 cgi->msg = "SSO completed and session created.";
772 cgi->op = '-'; /* Make sure management screen does not try to redispatch. */
773 zxid_put_user(cf, &ses->nameid->Format->g, &ses->nameid->NameQualifier->g, &ses->nameid->SPNameQualifier->g, ZX_GET_CONTENT(ses->nameid), 0);
774 DD("Logging... %d", 0);
775 zxlog(cf, &ourts, &srcts, 0, issuer, 0, &a7n->ID->g, subj,
776 cgi->sigval, "K", "NEWSES", ses->sid, "sesix(%s)", STRNULLCHKD(ses->sesix));
777 zxlog(cf, &ourts, &srcts, 0, issuer, 0, &a7n->ID->g, subj,
778 cgi->sigval, "K", ses->nidfmt?"FEDSSO":"TMPSSO", STRNULLCHKD(ses->sesix), 0);
779
780 if (cf->idp_ena) { /* (PXY) Middle IdP of Proxy IdP flow */
781 if (cgi->rs && cgi->rs[0]) {
782 D("ProxyIdP got RelayState(%s) ar(%s)", cgi->rs, STRNULLCHK(cgi->ssoreq));
783 cgi->saml_resp = 0; /* Clear Response to prevent re-interpretation. We want Request. */
784 cgi->ssoreq = cgi->rs;
785 zxid_decode_ssoreq(cf, cgi);
786 cgi->op = 'V';
787 D_DEDENT("ssof: ");
788 return ZXID_IDP_REQ; /* Cause zxid_simple_idp_an_ok_do_rest() to be called from zxid_sp_dispatch(); */
789 } else {
790 INFO("Middle IdP of Proxy IdP flow did not receive RelayState from upstream IdP %p", cgi->rs);
791 }
792 }
793 D_DEDENT("ssof: ");
794 return ZXID_SSO_OK;
795
796 erro:
797 ERR("SSO fail (%s)", err);
798 cgi->msg = "SSO failed. This could be due to signature, timeout, etc., technical failures, or by policy.";
799 zxlog(cf, &ourts, &srcts, 0, issuer, 0, a7n?&a7n->ID->g:0, subj,
800 cgi->sigval, err, ses->nidfmt?"FEDSSO":"TMPSSO", STRNULLCHKD(ses->sesix), "Error.");
801 D_DEDENT("ssof: ");
802 return 0;
803 }
804
805 /*() Fake a login and generate a session. Used if SSO failure is configured to result
806 * anonymous session.
807 *
808 * cf:: Configuration object, used to determine time slops, potentially memalloc via cf->ctx
809 * cgi:: CGI object. sigval and sigmsg may be set.
810 * ses:: Session object. Will be modified according to new session created from the SSO assertion.
811 * return:: 0 for failure, otherwise some success code such as ZXID_SSO_OK */
812
813 /* Called by: covimp_test, zxid_sp_dig_oauth_sso_a7n, zxid_sp_dig_sso_a7n */
zxid_sp_anon_finalize(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)814 int zxid_sp_anon_finalize(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
815 {
816 D_INDENT("anon_ssof: ");
817 cgi->sigval = "N";
818 cgi->sigmsg = "Anonymous login. No signature.";
819 ses->sigres = ZXSIG_NO_SIG;
820 ses->a7n = 0;
821 ses->rs = cgi->rs;
822 ses->nameid = 0;
823 ses->nid = "-";
824 ses->nidfmt = 0;
825 ses->sesix = 0;
826
827 D("SSO FAIL: ANON_OK. Creating session... %p", ses);
828
829 zxid_put_ses(cf, ses);
830 zxid_snarf_eprs_from_ses(cf, ses); /* Harvest attributes and bootstrap(s) */
831 cgi->msg = "SSO Failure treated as anonymous login and session created.";
832 cgi->op = '-'; /* Make sure management screen does not try to redispatch. */
833 /*zxid_put_user(cf, ses->nameid->Format, ses->nameid->NameQualifier, ses->nameid->SPNameQualifier, ZX_GET_CONTENT(ses->nameid), 0);*/
834 zxlog(cf, 0, 0, 0, 0, 0, 0, 0, cgi->sigval, "K", "TMPSSO", "-", 0);
835 D_DEDENT("anon_ssof: ");
836 return ZXID_SSO_OK;
837 }
838
839 /*() Authentication Service Client
840 * cgi->uid and cgi->pw contain the credentials
841 * See also: zxid_idp_as_do()
842 */
843
844 /* Called by: zxid_as_call */
zxid_as_call_ses(zxid_conf * cf,zxid_entity * idp_meta,zxid_cgi * cgi,zxid_ses * ses)845 int zxid_as_call_ses(zxid_conf* cf, zxid_entity* idp_meta, zxid_cgi* cgi, zxid_ses* ses)
846 {
847 int len;
848 struct zx_root_s* r;
849 struct zx_e_Body_s* body;
850 #if 0
851 struct zx_md_ArtifactResolutionService_s* ar_svc;
852 #else
853 struct zx_md_SingleLogoutService_s* ar_svc;
854 #endif
855 struct zx_as_SASLResponse_s* res;
856 char* buf;
857 char* b64;
858 char* p;
859 D_INDENT("as_call: ");
860
861 if (!cf || !cgi || !ses || !cgi->uid || !cgi->pw) {
862 ERR("Missing user, password, or mandatory argument cgi=%p (caller programming error)", cgi);
863 D_DEDENT("as_call: ");
864 return 0;
865 }
866
867 if (!idp_meta || !idp_meta->eid || !idp_meta->ed->IDPSSODescriptor) {
868 ERR("Entity(%s) does not have IdP SSO Descriptor (metadata problem)", idp_meta?STRNULLCHKQ(idp_meta->eid):"-");
869 zxlog(cf, 0,0,0,0,0,0,0, "N", "B", "ERR", 0, "No IDPSSODescriptor eid(%*s)", idp_meta?STRNULLCHKQ(idp_meta->eid):"-");
870 D_DEDENT("as_call: ");
871 return 0;
872 }
873
874 #if 0
875 for (ar_svc = idp_meta->ed->IDPSSODescriptor->ArtifactResolutionService;
876 ar_svc;
877 ar_svc = (struct zx_md_ArtifactResolutionService_s*)ar_svc->gg.g.n) {
878 if (ar_svc->gg.g.tok != zx_md_ArtifactResolutionService_ELEM)
879 continue;
880 if (ar_svc->Binding && !memcmp(SAML2_SOAP, ar_svc->Binding->s, ar_svc->Binding->len)
881 /*&& ar_svc->index && !memcmp(end_pt_ix, ar_svc->index->s, ar_svc->index->len)*/
882 && ar_svc->Location)
883 break;
884 }
885 #else
886 /* *** Kludge: We use the SLO SOAP endpoint for AS. ArtifactResolution might be more natural. */
887 for (ar_svc = idp_meta->ed->IDPSSODescriptor->SingleLogoutService;
888 ar_svc;
889 ar_svc = (struct zx_md_SingleLogoutService_s*)ar_svc->gg.g.n) {
890 if (ar_svc->gg.g.tok != zx_md_SingleLogoutService_ELEM)
891 continue;
892 if (ar_svc->Binding && !memcmp(SAML2_SOAP, ar_svc->Binding->g.s, ar_svc->Binding->g.len)
893 /*&& ar_svc->index && !memcmp(end_pt_ix, ar_svc->index->s, ar_svc->index->len)*/
894 && ar_svc->Location)
895 break;
896 }
897 #endif
898 if (!ar_svc) {
899 ERR("Entity(%s) does not have any IdP Artifact Resolution Service with " SAML2_SOAP " binding (metadata problem)", idp_meta->eid);
900 zxlog(cf, 0,0,0,0,0,0,0,"N","B","ERR",0,"No Artifact Resolution Svc eid(%s)", idp_meta->eid);
901 D_DEDENT("as_call: ");
902 return 0;
903 }
904
905 len = 1+strlen(cgi->uid)+1+strlen(cgi->pw)+1;
906 p = buf = ZX_ALLOC(cf->ctx, len);
907 *p++ = 0;
908 strcpy(p, cgi->uid);
909 p += strlen(cgi->uid) + 1;
910 strcpy(p, cgi->pw);
911
912 b64 = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_LEN(len)+1);
913 p = base64_fancy_raw(buf, len, b64, std_basis_64, 1<<31, 0, 0, '=');
914 *p = 0;
915 ZX_FREE(cf->ctx, buf);
916
917 body = zx_NEW_e_Body(cf->ctx,0);
918 body->SASLRequest = zx_NEW_as_SASLRequest(cf->ctx, &body->gg);
919 body->SASLRequest->mechanism = zx_dup_attr(cf->ctx, &body->SASLRequest->gg, zx_mechanism_ATTR, "PLAIN");
920 body->SASLRequest->Data = zx_ref_len_elem(cf->ctx, &body->SASLRequest->gg, zx_as_Data_ELEM, p-b64, b64);
921 r = zxid_soap_call_hdr_body(cf, &ar_svc->Location->g, 0, body);
922 /* *** free the body */
923
924 if (!r || !r->Envelope || !r->Envelope->Body || !(res = r->Envelope->Body->SASLResponse)) {
925 ERR("Autentication Service call failed idp(%s). Missing response.", idp_meta->eid);
926 zxlog(cf, 0,0,0,0,0,0,0, "N", "B", "ERR", 0, "Missing response eid(%s)", idp_meta->eid);
927 D_DEDENT("as_call: ");
928 return 0;
929 }
930
931 if (!res->Status || !res->Status->code || !res->Status->code->g.len || !res->Status->code->g.s) {
932 ERR("Autentication Service call failed idp(%s). Missing Status code.", idp_meta->eid);
933 zxlog(cf, 0,0,0,0,0,0,0, "N", "B", "ERR", 0, "Missing Status code eid(%s)", idp_meta->eid);
934 D_DEDENT("as_call: ");
935 return 0;
936 }
937
938 if (res->Status->code->g.len != 2
939 || res->Status->code->g.s[0]!='O' || res->Status->code->g.s[1]!='K') { /* "OK" */
940 ERR("Autentication Service call failed idp(%s). Status code(%.*s).", idp_meta->eid, res->Status->code->g.len, res->Status->code->g.s);
941 zxlog(cf, 0,0,0,0,0,0,0, "N", "B", "ERR", 0, "Missing Status code(%.*s) eid(%s)", res->Status->code->g.len, res->Status->code->g.s, idp_meta->eid);
942 D_DEDENT("as_call: ");
943 return 0;
944 }
945
946 ses->sigres = ZXSIG_NO_SIG;
947 ses->a7n = 0;
948 ses->nameid = 0;
949 ses->nid = "-";
950 ses->nidfmt = 0;
951 ses->sesix = 0;
952
953 D("AuthenSvc OK. Creating session... %p", ses);
954
955 zxid_put_ses(cf, ses);
956 zxid_ses_to_pool(cf, ses); /* Process SSO a7n, applying NEED, WANT, and INMAP */
957 zxid_snarf_eprs(cf, ses, res->EndpointReference);
958
959 /* *** free r */
960 D_DEDENT("as_call: ");
961 return ZXID_SSO_OK;
962 }
963
964 /* Called by: zxcall_main */
zxid_as_call(zxid_conf * cf,zxid_entity * idp_meta,const char * user,const char * pw)965 zxid_ses* zxid_as_call(zxid_conf* cf, zxid_entity* idp_meta, const char* user, const char* pw)
966 {
967 zxid_ses* ses = zxid_alloc_ses(cf);
968 zxid_cgi cgi;
969 ZERO(&cgi, sizeof(cgi));
970 cgi.uid = (char*)user;
971 cgi.pw = (char*)pw;
972
973 if (!zxid_as_call_ses(cf, idp_meta, &cgi, ses)) {
974 ZX_FREE(cf->ctx, ses);
975 return 0;
976 }
977 return ses;
978 }
979
980 /* EOF -- zxidsso.c */
981