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