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