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