1 /* zxidpsso.c  -  Handwritten functions for implementing Single Sign-On logic on IdP
2  * Copyright (c) 2009-2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * Copyright (c) 2008-2009 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * This is confidential unpublished proprietary source code of the author.
5  * NO WARRANTY, not even implied warranties. Contains trade secrets.
6  * Distribution prohibited unless authorized in writing.
7  * Licensed under Apache License 2.0, see file COPYING.
8  * $Id: zxidpsso.c,v 1.16 2010-01-08 02:10:09 sampo Exp $
9  *
10  * 14.11.2008, created --Sampo
11  * 4.9.2009,   added persistent nameid support --Sampo
12  * 24.11.2009, fixed handling of transient nameid --Sampo
13  * 12.2.2010,  added locking to lazy loading --Sampo
14  *
15  * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
16  */
17 
18 #include "platform.h"  /* for dirent.h */
19 
20 #include <sys/stat.h>
21 #include <errno.h>
22 
23 #include "errmac.h"
24 #include "zxid.h"
25 #include "zxidpriv.h"
26 #include "zxidutil.h"
27 #include "zxidconf.h"
28 #include "saml2.h"
29 #include "wsf.h"
30 #include "c/zxidvers.h"
31 #include "c/zx-const.h"
32 #include "c/zx-ns.h"
33 #include "c/zx-data.h"
34 
35 /*() Helper function to sign, if needed, and log the issued assertion.
36  * Checks for Assertion ID duplicate and returns 0 on
37  * failure (i.e. duplicate), 1 on success. The ret_logpath argument,
38  * if not NULL, allows returnign the logpath to caller, e.g. to use
39  * as an artifact (caller frees). */
40 
41 /* Called by:  zxid_add_fed_tok2epr, zxid_idp_sso x3, zxid_imreq, zxid_map_val_ss */
zxid_anoint_a7n(zxid_conf * cf,int sign,zxid_a7n * a7n,struct zx_str * issued_to,const char * lk,const char * uid,struct zx_str ** ret_logpath)42 int zxid_anoint_a7n(zxid_conf* cf, int sign, zxid_a7n* a7n, struct zx_str* issued_to, const char* lk, const char* uid, struct zx_str** ret_logpath)
43 {
44   X509* sign_cert;
45   EVP_PKEY*  sign_pkey;
46   struct zxsig_ref refs;
47   struct zx_str* ss;
48   struct zx_str* logpath;
49   struct timeval ourts;
50   GETTIMEOFDAY(&ourts, 0);
51 
52   if (sign) {
53     ZERO(&refs, sizeof(refs));
54     refs.id = &a7n->ID->g;
55     refs.canon = zx_easy_enc_elem_sig(cf, &a7n->gg);
56     if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey,"use sign cert anoint a7n")) {
57       a7n->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
58       zx_add_kid_after_sa_Issuer(&a7n->gg, &a7n->Signature->gg);
59     }
60     zx_str_free(cf->ctx, refs.canon);
61   }
62 
63   /* Log the issued a7n */
64 
65   if (cf->loguser)
66     zxlogusr(cf, uid, &ourts, &ourts, 0, issued_to, 0, &a7n->ID->g,
67 	     (ZX_GET_CONTENT(a7n->Subject->NameID)
68 	      ?ZX_GET_CONTENT(a7n->Subject->NameID)
69 	      :(zx_dup_str(cf->ctx, (a7n->Subject->EncryptedID?"ENC":"-")))),
70 	     sign?"U":"N", "K", lk, "-", 0);
71 
72   zxlog(cf, &ourts, &ourts, 0, issued_to, 0, &a7n->ID->g,
73 	(ZX_GET_CONTENT(a7n->Subject->NameID)
74 	 ?ZX_GET_CONTENT(a7n->Subject->NameID)
75 	 :(zx_dup_str(cf->ctx, (a7n->Subject->EncryptedID?"ENC":"-")))),
76 	sign?"U":"N", "K", lk, "-", 0);
77 
78   if (cf->log_issue_a7n) {
79     logpath = zxlog_path(cf, issued_to, &a7n->ID->g, ZXLOG_ISSUE_DIR, ZXLOG_A7N_KIND, 1);
80     if (logpath) {
81       ss = zx_easy_enc_elem_sig(cf, &a7n->gg);
82       if (zxlog_dup_check(cf, logpath, "IdP POST Assertion")) {
83 	ERR("Duplicate Assertion ID(%.*s)", a7n->ID->g.len, a7n->ID->g.s);
84 	if (cf->dup_a7n_fatal) {
85 	  ERR("FATAL (by configuration): Duplicate Assertion ID(%.*s)", a7n->ID->g.len, a7n->ID->g.s);
86 	  zxlog_blob(cf, 1, logpath, ss, "anoint_a7n dup");
87 	  zx_str_free(cf->ctx, ss);
88 	  zx_str_free(cf->ctx, logpath);
89 	  return 0;
90 	}
91       }
92       zxlog_blob(cf, 1, logpath, ss, "anoint_a7n");
93       if (ret_logpath)
94 	*ret_logpath = logpath;
95       else
96 	zx_str_free(cf->ctx, logpath);
97       zx_str_free(cf->ctx, ss);
98     }
99   }
100   return 1;
101 }
102 
103 /*() Helper function to sign, if needed, and log the issued response.
104  * Checks for message ID duplicate and returns 0 on failure (i.e. duplicate),
105  * or the canonicalized response message string on success. This string
106  * may be useful for caller to send further and should be freed by the caller. */
107 
108 /* Called by:  zxid_idp_sso x4, zxid_ssos_anreq */
zxid_anoint_sso_resp(zxid_conf * cf,int sign,struct zx_sp_Response_s * resp,struct zx_sp_AuthnRequest_s * ar)109 struct zx_str* zxid_anoint_sso_resp(zxid_conf* cf, int sign, struct zx_sp_Response_s* resp, struct zx_sp_AuthnRequest_s* ar)
110 {
111   X509* sign_cert;
112   EVP_PKEY* sign_pkey;
113   zxid_a7n* a7n;
114   struct zxsig_ref refs;
115   struct zx_str* ss;
116   struct zx_str* logpath;
117   struct timeval ourts;
118   GETTIMEOFDAY(&ourts, 0);
119 
120   if (sign) {
121     ZERO(&refs, sizeof(refs));
122     refs.id = &resp->ID->g;
123     refs.canon = zx_easy_enc_elem_sig(cf, &resp->gg);
124     if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert,&sign_pkey,"use sign cert anoint resp")) {
125       resp->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
126       zx_add_kid_after_sa_Issuer(&resp->gg, &resp->Signature->gg);
127     }
128     zx_str_free(cf->ctx, refs.canon);
129   }
130 
131   /* Log the issued Response */
132 
133   a7n = resp->Assertion;
134   zxlog(cf, &ourts, &ourts, 0, ZX_GET_CONTENT(ar->Issuer), &resp->ID->g,
135 	a7n&&a7n->ID?&a7n->ID->g:zx_dup_str(cf->ctx, "-"),
136 	(a7n
137 	 ?(ZX_GET_CONTENT(a7n->Subject->NameID)
138 	   ?ZX_GET_CONTENT(a7n->Subject->NameID)
139 	   :(zx_dup_str(cf->ctx, (a7n->Subject->EncryptedID?"ENC":"-"))))
140 	 :zx_dup_str(cf->ctx,"-")),
141 	sign?"U":"N", "K", "SSORESP", "-", 0);
142 
143   ss = zx_easy_enc_elem_opt(cf, &resp->gg);
144 
145   if (cf->log_issue_msg) {
146     logpath = zxlog_path(cf, ZX_GET_CONTENT(ar->Issuer), &resp->ID->g, ZXLOG_ISSUE_DIR, ZXLOG_MSG_KIND,1);
147     if (logpath) {
148       if (zxlog_dup_check(cf, logpath, "IdP POST Response")) {
149 	ERR("Duplicate Response ID(%.*s)", resp->ID->g.len, resp->ID->g.s);
150 	if (cf->dup_msg_fatal) {
151 	  ERR("FATAL (by configuration): Duplicate Response ID(%.*s)", resp->ID->g.len, resp->ID->g.s);
152 	  zxlog_blob(cf, 1, logpath, ss, "anoint_sso_resp dup");
153 	  zx_str_free(cf->ctx, ss);
154 	  zx_str_free(cf->ctx, logpath);
155 	  return 0;
156 	}
157       }
158       zxlog_blob(cf, 1, logpath, ss, "anoint_sso_resp");
159       zx_str_free(cf->ctx, logpath);
160     }
161   }
162   return ss;
163 }
164 
165 #define ZXID_ADD_BS_LVL_LIM 2  /* 2=only add full bootstraps on SSO. Only add di there after. */
166 
167 /*() Process .bs directory. See also zxid_di_query() */
168 
169 /* Called by:  zxid_idp_as_do x2, zxid_mk_usr_a7n_to_sp x2 */
zxid_gen_boots(zxid_conf * cf,zxid_ses * ses,struct zx_sa_AttributeStatement_s * father,char * path,int bs_lvl)170 void zxid_gen_boots(zxid_conf* cf, zxid_ses* ses, struct zx_sa_AttributeStatement_s* father, char* path, int bs_lvl)
171 {
172   struct timeval srcts = {0,501000};
173   struct zx_sa_Attribute_s* at;
174   zxid_epr* epr;
175   struct zx_root_s* r;
176   struct zx_str* ss;
177   DIR* dir;
178   struct dirent * de;
179   char mdpath[ZXID_MAX_BUF];
180   char logop[8];
181   char* epr_buf;
182   int epr_len, is_di, ret;
183   strcpy(logop, "xxxBSyy");
184   D_INDENT("gen_bs: ");
185 
186   if (!bs_lvl) {
187     D("bs_lvl=%d: nothing to add", bs_lvl);
188     D_DEDENT("gen_bs: ");
189     return;  /* Discovery EPRs do not need any bootstraps. */
190   }
191 
192   name_from_path(mdpath, sizeof(mdpath), "%s" ZXID_DIMD_DIR, cf->cpath);
193   D("Looking for service metadata in dir(%s) bs_lvl=%d", mdpath, bs_lvl);
194 
195   dir = opendir(path);
196   if (!dir) {
197     perror("opendir to find bootstraps");
198     ERR("Opening bootstrap directory failed path(%s)", path);
199     D_DEDENT("gen_bs: ");
200     return;
201   }
202 
203   while (de = readdir(dir)) {
204     D("Consider bs(%s%s)", path, de->d_name);
205 
206     if (de->d_name[strlen(de->d_name)-1] == '~')  /* Ignore backups from hand edited EPRs. */
207       continue;
208     if (de->d_name[0] == '.')  /* Ignore hidden files. */
209       continue;
210 
211     /* Probable enough, read and parse EPR so we can continue examination. */
212 
213     epr_buf = read_all_alloc(cf->ctx, "find_bs_svcmd", 1, &epr_len, "%s/%s", mdpath, de->d_name);
214     if (!epr_buf) {
215       ERR("User's (%s) bootstrap(%s) lacks service metadata registration. Reject. Consider using zxcot -e ... | zxcot -bs. See zxid-idp.pd for further information.", ses->uid, de->d_name);
216       ZX_FREE(cf->ctx, epr_buf);
217       continue;
218     }
219     r = zx_dec_zx_root(cf->ctx, epr_len, epr_buf, "gen boots");
220     if (!r) {
221       ERR("Failed to XML parse epr_buf(%.*s) file(%s)", epr_len, epr_buf, de->d_name);
222       ZX_FREE(cf->ctx, epr_buf);
223       continue;
224     }
225     /* *** add ID-WSF 1.1 handling */
226     epr = r->EndpointReference;
227     ZX_FREE(cf->ctx, r);
228 
229     if (!epr || !epr->Metadata || !ZX_SIMPLE_ELEM_CHK(epr->Metadata->ServiceType)) {
230       ERR("No EPR, corrupt EPR, or missing <Metadata> %p or <ServiceType>. epr_buf(%.*s) file(%s)", epr->Metadata, epr_len, epr_buf, de->d_name);
231       ZX_FREE(cf->ctx, epr_buf);
232       continue;
233     }
234     ss = ZX_GET_CONTENT(epr->Metadata->ServiceType);
235     is_di = ss? !memcmp(ss->s, XMLNS_DISCO_2_0, ss->len) : 0;
236     D("FOUND BOOTSTRAP url(%.*s) is_di=%d", ZX_GET_CONTENT_LEN(epr->Address), ZX_GET_CONTENT_S(epr->Address), is_di);
237 
238     if (is_di) {
239       ret = zxid_add_fed_tok2epr(cf, ses, epr, 0, logop); /* recurse, di tail */
240     } else if (bs_lvl > cf->bootstrap_level) {
241       D("No further bootstraps generated due to boostrap_level=%d (except di boostraps)", bs_lvl);
242       ZX_FREE(cf->ctx, epr_buf);
243       continue;
244     } else
245       ret = zxid_add_fed_tok2epr(cf, ses, epr, bs_lvl+1, logop); /* recurse */
246     D("bs_lvl=%d: adding logop(%s)", bs_lvl, logop);
247     if (!ret)
248       goto next_file;
249 
250     D("ADD BOOTSTRAP url(%.*s) is_di=%d", ZX_GET_CONTENT_LEN(epr->Address), ZX_GET_CONTENT_S(epr->Address), is_di);
251     father->Attribute = at = zxid_mk_sa_attribute(cf, &father->gg, WSF20_DI_RO, 0, 0);
252     ZX_ADD_KID(at->AttributeValue, EndpointReference, epr);
253 
254     zxlog(cf, 0, &srcts, 0, 0, 0, 0 /*a7n->ID*/, 0 /*nameid->gg.content*/,"N","K", logop, ses->uid, "gen_bs");
255 
256   next_file:
257     continue;
258   }
259 
260   closedir(dir);
261   D_DEDENT("gen_bs: ");
262 }
263 
264 /* Called by:  zxid_add_ldif_attrs, zxid_mk_usr_a7n_to_sp x3 */
zxid_add_mapped_attr(zxid_conf * cf,zxid_ses * ses,zxid_entity * meta,struct zx_elem_s * father,char * lk,struct zxid_map * sp_aamap,const char * name,const char * val)265 static void zxid_add_mapped_attr(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zx_elem_s* father, char* lk, struct zxid_map* sp_aamap, const char* name, const char* val)
266 {
267   struct zxid_map* map;
268   map = zxid_find_map(sp_aamap, name);
269   if (!map)
270     map = zxid_find_map(cf->aamap, name);
271   if (map && map->rule != ZXID_MAP_RULE_DEL) {
272     D("%s: ATTR(%s)=VAL(%s)", lk, name, val);
273     if (map->dst && *map->dst && map->src && map->src[0] != '*')
274       name = map->dst;
275     zxid_mk_sa_attribute_ss(cf, father, name, 0,
276 			    zxid_map_val(cf, ses, meta, map, name, val));
277   } else {
278     D("%s: Attribute(%s) filtered out either by del rule in aamap, or does not match aamap %p", lk, name, map);
279   }
280 }
281 
282 /*() Parse LDIF format and insert attributes to linked list. Return new head of the list.
283  * The input is temporarily modified and then restored. Do not pass const string.
284  * Multiple attribute lines by same name (meaning multivalued attribute) generate
285  * multiple <sa:Attribute> elements. At least zxid sp code will corretly interpret
286  * this as single multivalued attribute. */
287 
288 /* Called by:  zxid_read_ldif_attrs */
zxid_add_ldif_attrs(zxid_conf * cf,zxid_ses * ses,zxid_entity * meta,struct zx_elem_s * father,char * p,char * lk,struct zxid_map * sp_aamap)289 static void zxid_add_ldif_attrs(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, struct zx_elem_s* father, char* p, char* lk, struct zxid_map* sp_aamap)
290 {
291   char* name;
292   char* val;
293 
294   for (; p; ++p) {
295     name = p;
296     p = strstr(p, ": ");
297     if (!p)
298       break;
299     *p = 0;
300     val = p+2;
301     p = strchr(val, '\n');  /* *** parsing LDIF is fragile if values are multiline */
302     if (p)
303       *p = 0;
304 
305     zxid_add_mapped_attr(cf, ses, meta, father, lk, sp_aamap, name, val);
306 
307     val[-2] = ':'; /* restore */
308     if (p)
309       *p = '\n';
310     else
311       break;
312   }
313 }
314 
315 /*() Read Attribute Authority Map */
316 
317 /* Called by:  zxid_mk_usr_a7n_to_sp x2 */
zxid_read_map(zxid_conf * cf,const char * sp_name_buf,const char * mapname)318 static struct zxid_map* zxid_read_map(zxid_conf* cf, const char* sp_name_buf, const char* mapname)
319 {
320   char* p;
321   char* buf = read_all_alloc(cf->ctx, "read_aamap", 0, 0, "%s" ZXID_UID_DIR ".all/%s/.cf", cf->cpath,sp_name_buf);
322   if (!buf)
323     return 0;
324   p = strstr(buf, mapname);
325   if (!p) {
326     ERR(".cf file does not contain AAMAP directive buf(%s)", buf);
327     return 0;
328   }
329   if (p > buf && p[-1] == '#') {
330     INFO(".cf file contains commented out AAMAP directive buf(%s)", buf);
331     return 0;
332   }
333   p += strlen(mapname);
334   return zxid_load_map(cf, 0, p);
335 }
336 
337 /* Called by:  zxid_mk_usr_a7n_to_sp x4 */
zxid_read_ldif_attrs(zxid_conf * cf,zxid_ses * ses,zxid_entity * meta,const char * sp_name_buf,const char * uid,struct zxid_map * sp_aamap,struct zx_sa_AttributeStatement_s * at_stmt)338 static void zxid_read_ldif_attrs(zxid_conf* cf, zxid_ses* ses, zxid_entity* meta, const char* sp_name_buf, const char* uid, struct zxid_map* sp_aamap, struct zx_sa_AttributeStatement_s* at_stmt)
339 {
340   char* buf = read_all_alloc(cf->ctx, "read_ldif_attrs", 0, 0,
341 			     "%s" ZXID_UID_DIR "%s/%s/.at", cf->cpath, uid, sp_name_buf);
342   if (buf)
343     zxid_add_ldif_attrs(cf, ses, meta, &at_stmt->gg, buf, "read_ldif_attrs", sp_aamap);
344 }
345 
346 /*(i) Construct an assertion given user's attribute and bootstrap configuration.
347  * This involves adding attributes in user's .bs/.at, SP specific .at, as well as
348  * .all/.bs/.at and .all's SP specific attributes. The attributes are filtered
349  * and converted according to global and SP specific AAMAP.
350  * Finally the bootstrap EPRs are added.
351  *
352  * bs_lvl:: 0: DI (do not add any bs), 1: add all bootstraps at sso level,
353  *     <= cf->bootstrap_level: add all boostraps, > cf->bootstrap_level: only add di BS. */
354 
355 /* Called by:  a7n_test, zxid_add_fed_tok2epr, zxid_imreq, zxid_sso_issue_a7n */
zxid_mk_usr_a7n_to_sp(zxid_conf * cf,zxid_ses * ses,zxid_nid * nameid,zxid_entity * sp_meta,const char * sp_name_buf,int bs_lvl)356 zxid_a7n* zxid_mk_usr_a7n_to_sp(zxid_conf* cf, zxid_ses* ses, zxid_nid* nameid, zxid_entity* sp_meta, const char* sp_name_buf, int bs_lvl)
357 {
358   struct zxid_map* sp_aamap;
359   zxid_a7n* a7n;
360   struct zx_sa_AttributeStatement_s* at_stmt;
361   char dir[ZXID_MAX_DIR];
362 
363   D_INDENT("mka7n: ");
364   D("sp_eid(%s)", sp_meta->eid);
365 
366   if (!cf->aamap)
367     cf->aamap = zxid_read_map(cf, ".bs", "AAMAP=");
368   if (!cf->aamap)
369     cf->aamap = zxid_load_map(cf, 0, ZXID_DEFAULT_IDP_AAMAP);
370   sp_aamap = zxid_read_map(cf, sp_name_buf, "AAMAP=");
371 
372   at_stmt = zx_NEW_sa_AttributeStatement(cf->ctx, 0);
373   at_stmt->Attribute = zxid_mk_sa_attribute(cf, &at_stmt->gg, "zxididp", 0, ZXID_REL " " ZXID_COMPILE_DATE);
374 
375   a7n = zxid_mk_a7n(cf,
376 		    zx_dup_str(cf->ctx, sp_meta->eid),
377 		    zxid_mk_subj(cf, 0, sp_meta, nameid),
378 		    ses ? zxid_mk_an_stmt(cf, ses, 0, sp_meta->eid) : 0,
379 		    at_stmt);
380 
381   if (cf->fedusername_suffix && cf->fedusername_suffix[0]) {
382     snprintf(dir, sizeof(dir), "%.*s@%s", ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid), cf->fedusername_suffix);
383     dir[sizeof(dir)-1] = 0; /* must terminate manually as on win32 nul is not guaranteed */
384     zxid_add_mapped_attr(cf, ses, sp_meta, &at_stmt->gg, "mk_usr_a7n_to_sp", sp_aamap, "fedusername", dir);
385     if (cf->idpatopt & 0x01)
386       zxid_add_mapped_attr(cf, ses, sp_meta, &at_stmt->gg, "mk_usr_a7n_to_sp", sp_aamap, "urn:oid:1.3.6.1.4.1.5923.1.1.1.6" /* eduPersonPrincipalName */, dir);
387     //zxid_mk_sa_attribute(cf, &at_stmt->gg, "urn:oid:1.3.6.1.4.1.5923.1.1.1.6" /* eduPersonPrincipalName */, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", zx_dup_cstr(cf->ctx, dir));
388   }
389 
390   /* Following idpsesid attribute risks exposing federation-wide temporary unique ID.
391    * This is dangerous as it provides a correlation handle to participants of the
392    * session that would otherwise just have had pairwise pseudonyms. Even the regular
393    * session index is pariwise safe.
394    * As this is dangerous to privacy, it is disabled in the default AAMAP. You need to
395    * enable it explicitly in deployment specific AAMAP (in .all/.bs/.cf file) if you
396    * want it. There you can also specify whether it will be wrapped in assertion. */
397   if (ses && ses->sid && *ses->sid)
398     zxid_add_mapped_attr(cf, ses, sp_meta, &at_stmt->gg, "mk_usr_a7n_to_sp", sp_aamap, "idpsesid", ses->sid);
399 
400   zxid_read_ldif_attrs(cf, ses, sp_meta, ".bs",       ses->uid, sp_aamap, at_stmt);
401   zxid_read_ldif_attrs(cf, ses, sp_meta, sp_name_buf, ses->uid, sp_aamap, at_stmt);
402   zxid_read_ldif_attrs(cf, ses, sp_meta, ".bs",       ".all",   sp_aamap, at_stmt);
403   zxid_read_ldif_attrs(cf, ses, sp_meta, sp_name_buf, ".all",   sp_aamap, at_stmt);
404   D("sp_eid(%s) bs_lvl=%d", sp_meta->eid, bs_lvl);
405 
406   /* Process bootstraps */
407 
408   name_from_path(dir, sizeof(dir), "%s" ZXID_UID_DIR "%s/.bs/", cf->cpath, ses->uid);
409   zxid_gen_boots(cf, ses, at_stmt, dir, bs_lvl);
410 
411   name_from_path(dir, sizeof(dir), "%s" ZXID_UID_DIR ".all/.bs/", cf->cpath);
412   zxid_gen_boots(cf, ses, at_stmt, dir, bs_lvl);
413 
414   D_DEDENT("mka7n: ");
415   return a7n;
416 }
417 
418 /*(i) Check federation, create federation if appropriate. */
419 
420 /* Called by:  zxid_get_fed_nameid, zxid_imreq, zxid_nidmap_do */
zxid_check_fed(zxid_conf * cf,struct zx_str * affil,const char * uid,char allow_create,struct timeval * srcts,struct zx_str * issuer,struct zx_str * req_id,const char * sp_name_buf)421 zxid_nid* zxid_check_fed(zxid_conf* cf, struct zx_str* affil, const char* uid, char allow_create, struct timeval* srcts, struct zx_str* issuer, struct zx_str* req_id, const char* sp_name_buf)
422 {
423   int got;
424   char buf[ZXID_MAX_USER];
425   char dir[ZXID_MAX_DIR];
426   zxid_nid* nameid;
427   struct zx_str* nid;
428   struct zx_attr_s* idp_eid;
429 
430   got = read_all(sizeof(buf)-1, buf, "idpsso", 0, "%s" ZXID_UID_DIR "%s/%s/.mni" , cf->cpath, uid, sp_name_buf);
431 
432   if (!got) {
433     if (allow_create == '1') {
434 
435       D_INDENT("allowcreate: ");
436 
437       name_from_path(dir, sizeof(dir), "%s" ZXID_UID_DIR "%s/%s", cf->cpath, uid, sp_name_buf);
438       if (MKDIR(dir, 0777) && errno != EEXIST) {
439 	perror("mkdir for uid/sp fed");
440 	ERR("Creating uid/sp federation directory(%s) failed", dir);
441 	zxlog(cf, 0, srcts, 0, issuer, req_id, 0, 0, "N", "S", "EFILE", dir, "mkdir fail, permissions?");
442 	D_DEDENT("allowcreate: ");
443 	return 0;
444       }
445 
446       nid = zxid_mk_id(cf, "F", ZXID_ID_BITS);
447       nameid = zx_NEW_sa_NameID(cf->ctx,0);
448       nameid->SPNameQualifier = zx_ref_len_attr(cf->ctx, &nameid->gg, zx_SPNameQualifier_ATTR, affil->len, affil->s);
449       nameid->NameQualifier = idp_eid = zxid_my_ent_id_attr(cf,&nameid->gg,zx_NameQualifier_ATTR);
450       nameid->Format = zx_ref_attr(cf->ctx, &nameid->gg, zx_Format_ATTR, SAML2_PERSISTENT_NID_FMT);
451       zx_add_content(cf->ctx, &nameid->gg, nid);
452 
453       if (!write_all_path_fmt("put_fed", ZXID_MAX_USER, buf,
454 			      "%s%s", dir, "/.mni",
455 			      "%.*s|%.*s|%.*s|%.*s|",
456 			      sizeof(SAML2_PERSISTENT_NID_FMT), SAML2_PERSISTENT_NID_FMT,
457 			      idp_eid->g.len, idp_eid->g.s,
458 			      affil->len, affil->s,
459 			      nid->len, nid->s)) {
460 	zxlog(cf, 0, srcts, 0, issuer, req_id, 0, nid, "N", "S", "EFILE", uid, "put_fed fail, permissions?");
461 	D_DEDENT("allowcreate: ");
462 	return 0;
463       }
464 
465       /* Create entry for reverse mapping from pseudonym nid to uid */
466 
467       name_from_path(dir, sizeof(dir), "%s" ZXID_NID_DIR "%s", cf->cpath, sp_name_buf);
468       if (MKDIR(dir, 0777) && errno != EEXIST) {
469 	perror("mkdir for nid fed");
470 	ERR("Creating nid index directory(%s) failed", dir);
471 	zxlog(cf, 0, srcts, 0, issuer, req_id, 0, nid, "N", "S", "EFILE", dir, "mkdir fail, permissions?");
472 	D_DEDENT("allowcreate: ");
473 	return 0;
474       }
475 
476       name_from_path(dir, sizeof(dir), "%s" ZXID_NID_DIR "%s/%.*s", cf->cpath, sp_name_buf, nid->len, nid->s);
477       if (!write_all_path("put_nidmap", "%s", dir, 0, -1, uid)) {
478 	zxlog(cf, 0, srcts, 0, issuer, req_id, 0, nid, "N", "S", "EFILE", uid, "put_nidmap fail, permissions?");
479 	D_DEDENT("allowcreate: ");
480 	return 0;
481       }
482 
483       zxlog(cf, 0, srcts, 0, issuer, req_id, 0, nid, "N", "K", "FEDNEW", uid, 0);
484       D_DEDENT("allowcreate: ");
485 
486     } else {
487       ERR("No federation for uid(%s) in affil(%.*s) and AllowCreate false %d", uid, affil->len, affil->s, allow_create);
488       return 0;
489     }
490   } else {
491     buf[got] = 0;
492     nameid = zxid_parse_mni(cf, buf, 0);
493     D("Old fed uid(%s) affil(%.*s) nid(%.*s)", uid, affil->len, affil->s, ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid));
494   }
495 
496   if (!nameid) {
497     ERR("No federation for affil(%.*s) and AllowCreate false %d", affil->len, affil->s, allow_create);
498     return 0;
499   }
500   return nameid;
501 }
502 
503 /*() Change NameID to be transient and record corresponding mapping. */
504 
505 /* Called by:  zxid_get_fed_nameid x2, zxid_imreq x2, zxid_nidmap_do x2 */
zxid_mk_transient_nid(zxid_conf * cf,zxid_nid * nameid,const char * sp_name_buf,const char * uid)506 void zxid_mk_transient_nid(zxid_conf* cf, zxid_nid* nameid, const char* sp_name_buf, const char* uid)
507 {
508   struct zx_str* nid;
509   char dir[ZXID_MAX_DIR];
510 
511   D_INDENT("mk_trans: ");
512   nameid->Format = zx_ref_attr(cf->ctx, &nameid->gg, zx_Format_ATTR, SAML2_TRANSIENT_NID_FMT);
513   zx_add_content(cf->ctx, &nameid->gg, (nid = zxid_mk_id(cf, "T", ZXID_ID_BITS)));
514 
515   /* Create entry for reverse mapping from pseudonym nid to uid */
516 
517   name_from_path(dir, sizeof(dir), "%s" ZXID_NID_DIR "%s", cf->cpath, sp_name_buf);
518   if (MKDIR(dir, 0777) && errno != EEXIST) {
519     perror("mkdir for nid tmp");
520     ERR("Creating nid index directory(%s) failed", dir);
521     zxlog(cf, 0, 0, 0, 0, 0, 0, nid, "N", "S", "EFILE", dir, "mkdir fail, permissions?");
522     D_DEDENT("mk_trans: ");
523     return;
524   }
525 
526   name_from_path(dir, sizeof(dir), "%s" ZXID_NID_DIR "%s/%.*s", cf->cpath, sp_name_buf, nid->len, nid->s);
527   if (!write_all_path("put_nidmap_tmp", "%s", dir, 0, -1, uid)) {
528     zxlog(cf, 0, 0, 0, 0, 0, 0, nid, "N", "S", "EFILE", uid, "put_nidmap fail, permissions?");
529     D_DEDENT("mk_trans: ");
530     return;
531   }
532 
533   /*zxlog(cf, 0, srcts, 0, issuer, req_id, 0, nid, "N", "K", "TMPNEW", uid, 0);*/
534   D_DEDENT("mk_trans: ");
535 }
536 
537 /*() Consider an EPR and user and generate the necessary access credential (SAML a7n).
538  * The EPR, which the caller obtained by parsing XML, is modified in place by adding
539  * the SecurityContext to the end of the kids list.
540  * Returns 1 on success, 0 on failure. */
541 
542 /* Called by:  zxid_di_query, zxid_gen_boots x2 */
zxid_add_fed_tok2epr(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,int bs_lvl,char * logop)543 int zxid_add_fed_tok2epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, int bs_lvl, char* logop)
544 {
545   struct timeval srcts = {0,501000};
546   zxid_nid* nameid;
547   zxid_a7n* a7n;
548   zxid_entity* sp_meta;
549   struct zx_di_SecurityContext_s* sc;
550   struct zx_str* prvid;
551   struct zx_str* affil;
552   char sp_name_buf[ZXID_MAX_SP_NAME_BUF];
553 
554   if (prvid = ZX_GET_CONTENT(epr->Metadata->ProviderID)) {
555     sp_meta = zxid_get_ent_ss(cf, prvid);
556     if (!sp_meta) {
557       ERR("The metadata for provider could not be found or fetched. Reject. %d", 0);
558       return 0;
559     }
560   } else {
561     ERR("The EPR does not have ProviderID element. Reject. %d", 0);
562     return 0;
563   }
564 
565   affil = zxid_get_affil_and_sp_name_buf(cf, sp_meta, sp_name_buf);
566   D("sp_name_buf(%s) ProviderID(%.*s) di_allow_create=%d", sp_name_buf, prvid->len, prvid->s, cf->di_allow_create);
567 
568   nameid = zxid_get_fed_nameid(cf, prvid, affil, ses->uid, sp_name_buf, cf->di_allow_create,
569 			       (cf->di_nid_fmt == 't'), &srcts, 0, logop);
570 
571   /* Generate access credential */
572 
573   a7n = zxid_mk_usr_a7n_to_sp(cf, ses, nameid, sp_meta, sp_name_buf, bs_lvl);
574 
575   if (!zxid_anoint_a7n(cf, cf->sso_sign & ZXID_SSO_SIGN_A7N, a7n, prvid, "DIA7N", ses->uid, 0)) {
576     ERR("Failed to sign the assertion %d", 0);
577     return 0;
578   }
579 
580   if (!(sc = epr->Metadata->SecurityContext)) {
581     epr->Metadata->SecurityContext = sc = zx_NEW_di_SecurityContext(cf->ctx, 0);
582     zx_add_kid_before(&epr->Metadata->gg, ZX_TOK_NOT_FOUND, &sc->gg);
583   }
584 
585   if (!sc->SecurityMechID) {
586     sc->SecurityMechID = zx_dup_elem(cf->ctx, &sc->gg, zx_di_SecurityMechID_ELEM, WSF20_SEC_MECH_TLS_BEARER);
587   }
588 
589   if (!sc->Token)
590     sc->Token = zx_NEW_sec_Token(cf->ctx, &sc->gg);
591 
592   if (cf->di_a7n_enc) {
593     sc->Token->EncryptedAssertion = zxid_mk_enc_a7n(cf, &sc->Token->gg, a7n, sp_meta);
594   } else {
595     sc->Token->Assertion = a7n;
596     zx_add_kid(&sc->Token->gg, &a7n->gg);
597   }
598   zx_reverse_elem_lists(&sc->gg);
599   return 1;
600 }
601 
602 /*() Internal function, just to factor out some commonality between SSO and SSOS. */
603 
604 /* Called by:  a7n_test, x509_test, zxid_idp_sso, zxid_ssos_anreq */
zxid_sso_issue_a7n(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct timeval * srcts,zxid_entity * sp_meta,struct zx_str * acsurl,zxid_nid ** nameid,char * logop,struct zx_sp_AuthnRequest_s * ar)605 zxid_a7n* zxid_sso_issue_a7n(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct timeval* srcts, zxid_entity* sp_meta, struct zx_str* acsurl, zxid_nid** nameid, char* logop, struct zx_sp_AuthnRequest_s* ar)
606 {
607   zxid_a7n* a7n;
608   struct zx_sp_NameIDPolicy_s* nidpol;
609   struct zx_sa_SubjectConfirmation_s* sc;
610   struct zx_str* issuer;
611   struct zx_str* affil;
612   zxid_nid* tmpnameid;
613   char sp_name_buf[ZXID_MAX_SP_NAME_BUF];
614   D("sp_eid(%s)", sp_meta->eid);
615   if (!nameid)
616     nameid = &tmpnameid;
617 
618   if (ar && ar->IssueInstant && ar->IssueInstant->g.len && ar->IssueInstant->g.s)
619     srcts->tv_sec = zx_date_time_to_secs(ar->IssueInstant->g.s);
620 
621   nidpol = ar ? ar->NameIDPolicy : 0;
622   if (!cgi->allow_create && nidpol && nidpol->AllowCreate && nidpol->AllowCreate->g.s) {
623     D("No allow_create from form, extract from SAMLRequest (%.*s) len=%d", nidpol->AllowCreate->g.len, nidpol->AllowCreate->g.s, nidpol->AllowCreate->g.len);
624     cgi->allow_create = XML_TRUE_TEST(&nidpol->AllowCreate->g) ? '1':'0';
625   }
626 
627   if ((!cgi->nid_fmt || !cgi->nid_fmt[0]) && nidpol && nidpol->Format && nidpol->Format->g.s) {
628     D("No Name ID Format from form, extract from SAMLRequest (%.*s) len=%d", nidpol->Format->g.len, nidpol->Format->g.s, nidpol->Format->g.len);
629     cgi->nid_fmt = nidpol->Format->g.len == sizeof(SAML2_TRANSIENT_NID_FMT)-1
630       && !memcmp(nidpol->Format->g.s, SAML2_TRANSIENT_NID_FMT, sizeof(SAML2_TRANSIENT_NID_FMT)-1)
631       ? "trnsnt" : "prstnt";
632   }
633 
634   /* Check for federation. */
635 
636   issuer = ar ? ZX_GET_CONTENT(ar->Issuer) : 0;  /* *** must arrange AR issuer somehow */
637   affil = nidpol && nidpol->SPNameQualifier ? &nidpol->SPNameQualifier->g : issuer;
638   zxid_nice_sha1(cf, sp_name_buf, sizeof(sp_name_buf), affil, affil, 7);
639   D("sp_name_buf(%s)  allow_create=%d", sp_name_buf, cgi->allow_create);
640 
641   *nameid = zxid_get_fed_nameid(cf, issuer, affil, ses->uid, sp_name_buf, cgi->allow_create,
642 				(cgi->nid_fmt && !strcmp(cgi->nid_fmt, "trnsnt")),
643 				srcts, ar?&ar->ID->g:0, logop);
644   if (logop) { logop[3]='S';  logop[4]='S';  logop[5]='O';  logop[6]=0;  /* Patch in SSO */ }
645 
646   a7n = zxid_mk_usr_a7n_to_sp(cf, ses, *nameid, sp_meta, sp_name_buf, 1);  /* SSO a7n */
647 
648   /* saml-profiles-2.0-os.pdf ll.549-551 requires SubjectConfirmation even though
649    * saml-core-2.0-os.pdf ll.653-657 says <SubjectConfirmation> [Zero or More]. The
650    * profile seems to make it mandatory. See profiles ll.554-560. */
651 
652   a7n->Subject->SubjectConfirmation = sc = zx_NEW_sa_SubjectConfirmation(cf->ctx, 0);
653   zx_add_kid_before(&a7n->Subject->gg, ZX_TOK_NOT_FOUND, &sc->gg);
654   sc->Method = zx_ref_attr(cf->ctx, &sc->gg, zx_Method_ATTR, SAML2_BEARER);
655   sc->SubjectConfirmationData = zx_NEW_sa_SubjectConfirmationData(cf->ctx, &sc->gg);
656   if (acsurl)
657     sc->SubjectConfirmationData->Recipient = zx_ref_len_attr(cf->ctx, &sc->SubjectConfirmationData->gg, zx_Recipient_ATTR, acsurl->len, acsurl->s);
658   sc->SubjectConfirmationData->NotOnOrAfter
659     = zx_ref_len_attr(cf->ctx, &sc->SubjectConfirmationData->gg, zx_NotOnOrAfter_ATTR, a7n->Conditions->NotOnOrAfter->g.len, a7n->Conditions->NotOnOrAfter->g.s);
660 
661   return a7n;
662 }
663 
664 /*() Given uid, look up the idpnid (pairwise pseudonym) as seen by given SP (eid) */
665 
zxid_get_idpnid_at_eid(zxid_conf * cf,const char * uid,const char * eid,int allow_create)666 char* zxid_get_idpnid_at_eid(zxid_conf* cf, const char* uid, const char* eid, int allow_create)
667 {
668   zxid_nid* nameid;
669   struct zx_str* affil;
670   char sp_name_buf[ZXID_MAX_SP_NAME_BUF];
671   affil = zx_dup_str(cf->ctx, eid);
672   zxid_nice_sha1(cf, sp_name_buf, sizeof(sp_name_buf), affil, affil, 7);
673   nameid = zxid_check_fed(cf, affil, uid, allow_create, 0, 0, 0, sp_name_buf);
674   if (!nameid || !nameid->gg.g.len || !nameid->gg.g.s) {
675     D("No nameid for uid(%s) eid(%s) allow_create(%d) %p", STRNULLCHK(uid), STRNULLCHK(eid), allow_create, nameid);
676     return 0;
677   }
678   return zx_str_to_c(cf->ctx, &nameid->gg.g);
679 }
680 
681 /*(i) Generate SSO assertion and ship it to SP by chosen binding. User has already
682  * logged in by the time this is called. See also zxid_ssos_anreq()
683  * and zxid_oauth2_az_server_sso() */
684 
685 /* Called by:  zxid_idp_dispatch */
zxid_idp_sso(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zx_sp_AuthnRequest_s * ar)686 struct zx_str* zxid_idp_sso(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zx_sp_AuthnRequest_s* ar)
687 {
688   X509* sign_cert;
689   EVP_PKEY* sign_pkey;
690   int binding = 0;
691   struct zxsig_ref refs;
692   zxid_entity* sp_meta;
693   struct zx_str* acsurl = 0;
694   struct zx_str tmpss;
695   struct zx_str* ss;
696   struct zx_str* payload;
697   struct zx_str* logpath;
698   struct timeval srcts = {0,501000};
699   zxid_nid* nameid;
700   zxid_a7n* a7n;
701   struct zx_sp_Response_s* resp;
702   struct zx_e_Envelope_s* e;
703   char* p;
704   char logop[8];
705   strcpy(logop, "IDPxxxx");
706 
707   if (!ar || !ZX_GET_CONTENT(ar->Issuer)) {
708     ERR("No Issuer found in AuthnRequest %p", ar);
709     return zx_dup_str(cf->ctx, "* ERR");
710   }
711 
712   sp_meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(ar->Issuer));
713   if (!sp_meta) {
714     ERR("The metadata for Issuer of the AuthnRequest could not be found or fetched %d", 0);
715     return zx_dup_str(cf->ctx, "* ERR");
716   }
717   D("sp_eid(%s)", sp_meta->eid);
718 
719   /* Figure out the binding and url */
720 
721   if (ar->AssertionConsumerServiceIndex) {
722     if (ar->ProtocolBinding || ar->AssertionConsumerServiceURL) {
723       ERR("When SP specifies AssertionConsumerServiceIndex in AuthnRequest, it SHOULD NOT specify ProtocolBinding(%p) or AssertionConsumerServiceURL(%p). They are ignored. AssertionConsumerServiceIndex approach is the preferred approach.", ar->ProtocolBinding, ar->AssertionConsumerServiceURL);
724     }
725     acsurl = zxid_sp_loc_by_index_raw(cf, cgi, sp_meta, ZXID_ACS_SVC, &ar->AssertionConsumerServiceIndex->g, &binding);
726   } else if (ar->ProtocolBinding) {
727     p = zx_str_to_c(cf->ctx, &ar->ProtocolBinding->g);
728     acsurl = zxid_sp_loc_raw(cf, cgi, sp_meta, ZXID_ACS_SVC, p, 0);
729     ZX_FREE(cf->ctx, p);
730     if (acsurl && ar->AssertionConsumerServiceURL) {
731       if (acsurl->len != ar->AssertionConsumerServiceURL->g.len
732 	  || memcmp(acsurl->s, ar->AssertionConsumerServiceURL->g.s, acsurl->len)) {
733 	ERR("SECURITY/SPOOFING: SP specified in AuthnRequest an AssertionConsumerServiceURL(%.*s) but this does not agree with the metadata specified url(%.*s) for Binding(%.*s). SP would be better off using AssertionConsumerServiceIndex approach. The metadata is relied on and the AssertionConsumerServiceURL is ignored.", ar->AssertionConsumerServiceURL->g.len, ar->AssertionConsumerServiceURL->g.s, acsurl->len, acsurl->s, ar->ProtocolBinding->g.len, ar->ProtocolBinding->g.s);
734       }
735       binding = zxid_protocol_binding_map_saml2(&ar->ProtocolBinding->g);
736     }
737   }
738   if (!acsurl) {
739     D("AuthnRequest did not specify any ACS or binding. Using idp_pref_acs_binding(%s)", cf->idp_pref_acs_binding);
740     acsurl = zxid_sp_loc_raw(cf, cgi, sp_meta, ZXID_ACS_SVC, cf->idp_pref_acs_binding, 0);
741     if (acsurl) {
742       tmpss.len = strlen(cf->idp_pref_acs_binding);
743       tmpss.s = cf->idp_pref_acs_binding;
744       binding = zxid_protocol_binding_map_saml2(&tmpss);
745     } else {
746       D("Preferred binding not supported by SP metadata, using first ACS entry from metadata %d", 0);
747       if (!sp_meta->ed || !sp_meta->ed->SPSSODescriptor || !sp_meta->ed->SPSSODescriptor->AssertionConsumerService || !sp_meta->ed->SPSSODescriptor->AssertionConsumerService->Location) {
748 	ERR("SP metadata does not contain any AssertionConsumerService. Can not complete SSO (SP metadata problem) %d", 0);
749 	return zx_dup_str(cf->ctx, "* ERR");
750       }
751       acsurl = &sp_meta->ed->SPSSODescriptor->AssertionConsumerService->Location->g;
752       binding = zxid_protocol_binding_map_saml2(&sp_meta->ed->SPSSODescriptor->AssertionConsumerService->Binding->g);
753     }
754   }
755 
756   /* User ses->uid is already logged in, now check for federation with sp */
757 
758   a7n = zxid_sso_issue_a7n(cf, cgi, ses, &srcts, sp_meta, acsurl, &nameid, logop, ar);
759 
760   /* Sign, encrypt, and ship the assertion according to the binding. */
761 
762   switch (binding) {
763   case 'e':
764     D("SAML2 PAOS ep(%.*s)", acsurl->len, acsurl->s);
765 
766     if (cf->sso_sign & ZXID_SSO_SIGN_A7N) {
767       ZERO(&refs, sizeof(refs));
768       refs.id = &a7n->ID->g;
769       refs.canon = zx_easy_enc_elem_sig(cf, &a7n->gg);
770       if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert paos")) {
771 	a7n->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
772 	zx_add_kid_after_sa_Issuer(&a7n->gg, &a7n->Signature->gg);
773       }
774     }
775     resp = zxid_mk_saml_resp(cf, a7n, cf->post_a7n_enc?sp_meta:0);
776     payload = zxid_anoint_sso_resp(cf, cf->sso_sign & ZXID_SSO_SIGN_RESP, resp, ar);
777     if (!payload)
778       return zx_dup_str(cf->ctx, "* ERR");
779     zx_str_free(cf->ctx, payload);
780 
781     /* Generate SOAP envelope with ECP header */
782 
783     e = zx_NEW_e_Envelope(cf->ctx,0);
784 
785     e->Header = zx_NEW_e_Header(cf->ctx, &e->gg);
786     e->Header->ecp_Response = zx_NEW_ecp_Response(cf->ctx, &e->Header->gg);
787     e->Header->ecp_Response->mustUnderstand = zx_dup_attr(cf->ctx, &e->Header->ecp_Response->gg, zx_e_mustUnderstand_ATTR, "1");
788     e->Header->ecp_Response->actor = zx_ref_attr(cf->ctx, &e->Header->ecp_Response->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
789     e->Header->ecp_Response->AssertionConsumerServiceURL = zx_ref_len_attr(cf->ctx, &e->Header->ecp_Response->gg, zx_AssertionConsumerServiceURL_ATTR, acsurl->len, acsurl->s);
790 
791     e->Body = zx_NEW_e_Body(cf->ctx, &e->gg);
792     e->Body->Response = resp;
793 
794     ss = zx_easy_enc_elem_opt(cf, &e->gg);
795 
796     zxlog(cf, 0, &srcts, 0, ZX_GET_CONTENT(ar->Issuer), 0, &a7n->ID->g, ZX_GET_CONTENT(nameid), "N", "K", logop, ses->uid, "PAOS2");
797 
798 
799     /* *** Check what HTTP level headers PAOS needs */
800     return zx_strf(cf->ctx, "Content-type: text/xml\r\nContent-Length: %d\r\n%s%s%s\r\n%.*s",
801 		   ss->len,
802 		   ses->setcookie?"Set-Cookie: ":"", ses->setcookie?ses->setcookie:"", ses->setcookie?"\r\n":"",
803 		   ss->len, ss->s);
804 
805   case 'q':
806     D("SAML2 BRWS-POST-SIMPLE-SIGN ep(%.*s)", acsurl->len, acsurl->s);
807 
808     if (!zxid_anoint_a7n(cf, cf->sso_sign & ZXID_SSO_SIGN_A7N_SIMPLE, a7n, ZX_GET_CONTENT(ar->Issuer), "SSOA7N", ses->uid, 0))
809       return zx_dup_str(cf->ctx, "* ERR");
810     resp = zxid_mk_saml_resp(cf, a7n, cf->post_a7n_enc?sp_meta:0);
811     payload = zxid_anoint_sso_resp(cf, 0, resp, ar);
812     if (!payload)
813       return zx_dup_str(cf->ctx, "* ERR");
814     ss = zxid_saml2_post_enc(cf, "SAMLResponse", payload, cgi->rs, 1, acsurl);
815     zx_str_free(cf->ctx, payload);
816     if (!ss)
817       return zx_dup_str(cf->ctx, "* ERR");
818 
819     zxlog(cf, 0, &srcts, 0, ZX_GET_CONTENT(ar->Issuer), 0, &a7n->ID->g, ZX_GET_CONTENT(nameid), "N", "K", logop, ses->uid, "SIMPSIG");
820 
821     return zx_strf(cf->ctx, "Content-type: text/html\r\nContent-Length: %d\r\n%s%s%s\r\n%.*s",
822 		   ss->len,
823 		   ses->setcookie?"Set-Cookie: ":"", ses->setcookie?ses->setcookie:"", ses->setcookie?"\r\n":"",
824 		   ss->len, ss->s);
825 
826   case 'p':
827     D("SAML2 BRWS-POST ep(%.*s)", acsurl->len, acsurl->s);
828 
829     if (!zxid_anoint_a7n(cf, cf->sso_sign & ZXID_SSO_SIGN_A7N, a7n, ZX_GET_CONTENT(ar->Issuer), "SSOA7N", ses->uid, 0))
830       return zx_dup_str(cf->ctx, "* ERR");
831     resp = zxid_mk_saml_resp(cf, a7n, cf->post_a7n_enc?sp_meta:0);
832     payload = zxid_anoint_sso_resp(cf, cf->sso_sign & ZXID_SSO_SIGN_RESP, resp, ar);
833     if (!payload)
834       return zx_dup_str(cf->ctx, "* ERR");
835 
836     ss = zxid_saml2_post_enc(cf, "SAMLResponse", payload, cgi->rs, 0, acsurl);
837     zx_str_free(cf->ctx, payload);
838     if (!ss)
839       return zx_dup_str(cf->ctx, "* ERR");
840 
841     zxlog(cf, 0, &srcts, 0, ZX_GET_CONTENT(ar->Issuer), 0, &a7n->ID->g, ZX_GET_CONTENT(nameid), "N", "K", logop, ses->uid, "BRWS-POST");
842 
843     return zx_strf(cf->ctx, "Content-type: text/html\r\nContent-Length: %d\r\n%s%s%s\r\n%.*s",
844 		   ss->len,
845 		   ses->setcookie?"Set-Cookie: ":"", ses->setcookie?ses->setcookie:"", ses->setcookie?"\r\n":"",
846 		   ss->len, ss->s);
847 
848   case 'a':
849     D("SAML2 BRWS-ART ep(%.*s)", acsurl->len, acsurl->s);
850 
851     if (!cf->log_issue_a7n) {
852       INFO("LOG_ISSUE_A7N must be turned on in IdP configuration for artifact profile to work. Turning on now automatically. %d", 0);
853       cf->log_issue_a7n = 1;
854     }
855     if (!zxid_anoint_a7n(cf, cf->sso_sign & ZXID_SSO_SIGN_A7N, a7n, ZX_GET_CONTENT(ar->Issuer), "SSOA7N", ses->uid, &logpath))
856       return zx_dup_str(cf->ctx, "* ERR");
857     resp = zxid_mk_saml_resp(cf, a7n, 0);
858     payload = zxid_anoint_sso_resp(cf, cf->sso_sign & ZXID_SSO_SIGN_RESP, resp, ar);
859     if (!payload)
860       return zx_dup_str(cf->ctx, "* ERR");
861 
862     //ss = zxid_saml2_post_enc(cf, "SAMLResponse", pay_load, ar->RelayState);  *** redirect
863     /* *** Do artifact processing: artifact can be the file name in /var/zxid/idplog/issue/SPEID/art/ */
864 
865     ERR("Trying to use SAML2 Artifact Binding, but code not fully implemented. %d", 0);
866 
867     zxlog(cf, 0, &srcts, 0, ZX_GET_CONTENT(ar->Issuer), 0, &a7n->ID->g, ZX_GET_CONTENT(nameid), "N", "K", logop, ses->uid, "BRWS-ART");
868 
869     ss = zx_strf(cf->ctx, "Location: %.*s%c"
870 		 "SAMLResponse=%.*s" CRLF
871 		 "%s%s%s",   /* Set-Cookie */
872 		 acsurl->len, acsurl->s, (memchr(acsurl->s, '?', acsurl->len) ? '&' : '?'),
873 		 payload->len, payload->s,
874 		 (ses->setcookie?"Set-Cookie: ":""), (ses->setcookie?ses->setcookie:""), (ses->setcookie?CRLF:""));
875     zx_str_free(cf->ctx, payload);
876     return ss;
877 
878   default:
879     NEVER("Unknown or unsupported binding %d", binding);
880   }
881 
882   return zx_dup_str(cf->ctx, "* ERR");
883 }
884 
885 /*() ID-WSF Authentication Service: check password and emit bootstrap(s)
886  * To generate the data, use:
887  *   perl -MMIME::Base64 -e 'print encode_base64("\0user\0pw\0")'
888  *   perl -MMIME::Base64 -e 'print encode_base64("\0tastest\0tas123\0")'
889  * See also: zxid_as_call_ses()
890  */
891 
892 /* Called by:  zxid_sp_soap_dispatch */
zxid_idp_as_do(zxid_conf * cf,struct zx_as_SASLRequest_s * req)893 struct zx_as_SASLResponse_s* zxid_idp_as_do(zxid_conf* cf, struct zx_as_SASLRequest_s* req)
894 {
895   zxid_cgi cgi;
896   zxid_ses sess;
897   struct zx_as_SASLResponse_s* res = zx_NEW_as_SASLResponse(cf->ctx,0);
898   struct zx_sa_AttributeStatement_s* at_stmt;
899   struct zx_sa_Attribute_s* at;
900   struct zx_sa_Attribute_s* at_next;
901   char* q;
902   char* u;
903   char* p;
904   char buf[1024];
905   char path[ZXID_MAX_BUF];
906 
907   ZERO(&cgi, sizeof(zxid_cgi));
908   ZERO(&sess, sizeof(zxid_ses));
909 
910   if (SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(ZX_GET_CONTENT_LEN(req->Data)) >= sizeof(buf)-1) {
911     ERR("Too long username and password %p. limit=%d", ZX_GET_CONTENT(req->Data), (int)sizeof(buf)-1);
912     res->Status = zxid_mk_lu_Status(cf, &res->gg, "ERR", 0, 0, 0);
913     return res;
914   }
915   q = unbase64_raw(ZX_GET_CONTENT_S(req->Data), ZX_GET_CONTENT_S(req->Data) + ZX_GET_CONTENT_LEN(req->Data), buf, zx_std_index_64);
916   *q = 0;
917   for (u = buf; *u && u < q; ++u) ;  /* skip initial */
918   for (p = ++u; *p && p < q; ++p) ;
919   ++p;
920   cgi.uid = u;
921   cgi.pw = p;
922 
923   if (zxid_pw_authn(cf, &cgi, &sess)) {
924     D_INDENT("as: ");
925     at_stmt = zx_NEW_sa_AttributeStatement(cf->ctx, 0 /* Do not attach */);
926     name_from_path(path, sizeof(path), "%s" ZXID_UID_DIR "%s/.bs/", cf->cpath, cgi.uid);
927     zxid_gen_boots(cf, &sess, at_stmt, path, 1);
928     name_from_path(path, sizeof(path), "%s" ZXID_UID_DIR ".all/.bs/", cf->cpath);
929     zxid_gen_boots(cf, &sess, at_stmt, path, 1);
930 
931     /* Kludgy extraction of the EPRs from the attributes. */
932 
933     at = at_stmt->Attribute;
934     if (at) {
935       res->EndpointReference = at->AttributeValue->EndpointReference;
936       D("TRANSMIT EPR to res %p %p", res->EndpointReference, res->EndpointReference->gg.g.n);
937       for (; at; at = at_next) {
938 	if (at->AttributeValue->EndpointReference) {
939 	  D("TRANSMIT ANOTHER EPR to res %p %p", at->AttributeValue->EndpointReference, at->AttributeValue->EndpointReference->gg.g.n);
940 	  zx_add_kid(&res->gg, &at->AttributeValue->EndpointReference->gg);
941 	} else {
942 	  D("NO EPR %p", at->AttributeValue->EndpointReference);
943 	}
944 	at_next = (struct zx_sa_Attribute_s*)at->gg.g.n;
945 	ZX_FREE(cf->ctx, at);
946       }
947     }
948     ZX_FREE(cf->ctx, at_stmt);
949     res->Status = zxid_mk_lu_Status(cf, &res->gg, "OK", 0, 0, 0);
950     /*zx_reverse_elem_lists(&res->gg); already built right */
951     D_DEDENT("as: ");
952   } else {
953     ERR("Authentication failed uid(%s) pw(%s)", cgi.uid, cgi.pw);
954     res->Status = zxid_mk_lu_Status(cf, &res->gg, "ERR", 0, 0, 0);
955   }
956   return res;
957 }
958 
959 /* EOF  --  zxidpsso.c */
960