Branch data Line data Source code
1 : : /* zxidsimp.c - Handwritten zxid_simple() API
2 : : * Copyright (c) 2009-2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 : : * Author: Sampo Kellomaki (sampo@iki.fi)
5 : : * This is confidential unpublished proprietary source code of the author.
6 : : * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 : : * Distribution prohibited unless authorized in writing.
8 : : * Licensed under Apache License 2.0, see file COPYING.
9 : : * $Id: zxidsimp.c,v 1.64 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 17.1.2007, created --Sampo
12 : : * 2.2.2007, improved the LDIF return --Sampo
13 : : * 9.3.2008, refactored the logged in and need login cases to subroutines --Sampo
14 : : * 7.10.2008, added documentation --Sampo
15 : : * 4.9.2009, added attribute broker and PEP functionality --Sampo
16 : : * 31.5.2010, moved local PEP and attribute broker functionality to zxidpep.c --Sampo
17 : : * 7.9.2010, tweaked the az requests to separate ses az from resource az --Sampo
18 : : * 22.9.2010, added People Service invitation resolution --Sampo
19 : : *
20 : : * Login button abbreviations
21 : : * A2 = SAML 2.0 Artifact Profile
22 : : * P2 = SAML 2.0 POST Profile
23 : : * S2 = SAML 2.0 POST Simple Sign
24 : : * A12 = Liberty ID-FF 1.2 Artifact Profile
25 : : * P12 = Liberty ID-FF 1.2 POST Profile
26 : : * A1 = Bare SAML 1.x Artifact Profile
27 : : * P1 = Base SAML 1.x POST Profile
28 : : * A0 = WS-Federation Artifact Profile
29 : : * P0 = WS-Federation POST Profile
30 : : */
31 : :
32 : : #include "platform.h" /* needed on Win32 for pthread_mutex_lock() et al. */
33 : :
34 : : #include <memory.h>
35 : : #include <string.h>
36 : :
37 : : #include "errmac.h"
38 : : #include "zx.h"
39 : : #include "zxid.h"
40 : : #include "zxidpriv.h"
41 : : #include "zxidutil.h"
42 : : #include "zxidconf.h"
43 : : #include "c/zxidvers.h"
44 : : #include "c/zx-md-data.h"
45 : :
46 : : /*() Convert configuration string ~conf~ to configuration object ~cf~. See zxid_conf_to_cf() */
47 : :
48 : : /* Called by: dirconf, main x2, zxid_az, zxid_az_base, zxid_fed_mgmt_len, zxid_idp_list_len, zxid_idp_select_len, zxid_new_conf_to_cf, zxid_simple_len */
49 : : int zxid_conf_to_cf_len(zxid_conf* cf, int conf_len, const char* conf)
50 : 262 : {
51 : : #if 1
52 [ + + ]: 262 : if (!cf->ctx) {
53 : 71 : cf->ctx = zx_init_ctx();
54 [ - + ]: 71 : if (!cf->ctx) {
55 : 0 : ERR("Failed to alloc zx_ctx %d",0);
56 : 0 : exit(2);
57 : : }
58 : : }
59 : 262 : zxid_init_conf(cf, ZXID_PATH);
60 : : #ifdef USE_CURL
61 [ - + # # ]: 262 : LOCK(cf->curl_mx, "curl init");
62 : 262 : cf->curl = curl_easy_init();
63 [ - + ]: 262 : if (!cf->curl) {
64 : 0 : ERR("Failed to initialize libcurl %d",0);
65 [ # # # # ]: 0 : UNLOCK(cf->curl_mx, "curl init");
66 : 0 : exit(2);
67 : : }
68 [ - + # # ]: 262 : UNLOCK(cf->curl_mx, "curl init");
69 : : #endif
70 : : #else
71 : : zxid_init_conf_ctx(cf, ZXID_PATH /* N.B. Often this is overridden. */);
72 : : #endif
73 : : #if defined(ZXID_CONF_FILE) || defined(ZXID_CONF_FLAG)
74 : : {
75 : : char* buf;
76 : : char* cc;
77 [ + - + + ]: 262 : int len, clen = conf_len == -1 && conf ? strlen(conf) : conf_len;
78 : :
79 [ + + - + : 262 : if (!conf || conf_len < 5 || memcmp(conf, "PATH=", 5)) {
# # ]
80 : : /* No conf, or conf does not start by PATH: read from file default values */
81 : 262 : buf = read_all_alloc(cf->ctx, "-conf_to_cf", 1, &len, "%szxid.conf", cf->path);
82 [ + - + - ]: 262 : if (buf && len)
83 : 262 : zxid_parse_conf_raw(cf, len, buf);
84 : : }
85 : :
86 [ + + + - ]: 262 : if (conf && conf_len) {
87 : : /* Copy the conf string because we are going to modify it in place. */
88 : 197 : cc = ZX_ALLOC(cf->ctx, clen+1);
89 : 197 : memcpy(cc, conf, clen);
90 : 197 : cc[clen] = 0;
91 : 197 : zxid_parse_conf_raw(cf, clen, cc);
92 : : }
93 : : }
94 : : #endif
95 : 262 : return 0;
96 : : }
97 : :
98 : : /*() Create new ZXID configuration object given configuration string and
99 : : * possibly configuration file.
100 : : *
101 : : * zxid_new_conf_to_cf() parses first the default config file, then the string (i.e. string
102 : : * can override config file). However, if the string contains PATH specification,
103 : : * then the config file is reread from (presumably new) location and overrides
104 : : * eariler config.
105 : : *
106 : : * conf:: Configuration string
107 : : * return:: Configuration object */
108 : :
109 : : /* Called by: main x6, opt x2, zxcall_main, zxcot_main, zxidwspcgi_main x2 */
110 : : zxid_conf* zxid_new_conf_to_cf(const char* conf)
111 : 71 : {
112 : 71 : zxid_conf* cf = malloc(sizeof(zxid_conf)); /* *** direct use of malloc */
113 [ + + - + ]: 71 : D("malloc %p size=%d", cf, sizeof(zxid_conf));
114 [ - + ]: 71 : if (!cf) {
115 : 0 : ERR("out-of-memory %d", sizeof(zxid_conf));
116 : 0 : exit(1); /* *** perhaps too severe! */
117 : : }
118 : 71 : cf = ZERO(cf, sizeof(zxid_conf));
119 : 71 : zxid_conf_to_cf_len(cf, -1, conf);
120 : 71 : return cf;
121 : : }
122 : :
123 : : /* ------------ zxid_fed_mgmt() ------------ */
124 : :
125 : : /*(i) Generate Single Logout button and possibly other federation management
126 : : * buttons for use in logged in state of the app HTML GUI.
127 : : *
128 : : * Either outputs the management screen to stdout or returns string of HTML (at specified
129 : : * automation level). If res_len is supplied, the string length is returned in res_len.
130 : : * Otherwise you can just run strlen() on return value.
131 : : *
132 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
133 : :
134 : : /* Called by: zxid_fed_mgmt_len, zxid_simple_ses_active_cf */
135 : : char* zxid_fed_mgmt_cf(zxid_conf* cf, int* res_len, int sid_len, char* sid, int auto_flags)
136 : 3 : {
137 : : char* res;
138 : : struct zx_str* ss;
139 : : struct zx_str* ss2;
140 [ + - + - ]: 3 : int slen = sid_len == -1 && sid ? strlen(sid) : sid_len;
141 [ + - ]: 3 : if (auto_flags & ZXID_AUTO_DEBUG) zxid_set_opt(cf, 1, 1);
142 : :
143 [ + - ]: 3 : if (cf->log_level>1)
144 [ + - ]: 3 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "MGMT", 0, "sid(%.*s)", sid_len, STRNULLCHK(sid));
145 : :
146 [ + - + - ]: 6 : if ((auto_flags & ZXID_AUTO_FORMT) && (auto_flags & ZXID_AUTO_FORMF))
147 [ - + + - ]: 3 : ss = zx_strf(cf->ctx,
148 : : "%s"
149 : : #ifdef ZXID_USE_POST
150 : : "<form method=post action=\"%s?o=P\">\n"
151 : : #else
152 : : "<form method=get action=\"%s\">\n"
153 : : #endif
154 : : "<input type=hidden name=s value=\"%.*s\">\n"
155 : : "%s%s\n"
156 : : "</form>%s%s%s%s",
157 : : cf->mgmt_start,
158 : : cf->url,
159 : : slen, STRNULLCHK(sid),
160 : : cf->mgmt_logout, cf->mgmt_defed,
161 : : cf->mgmt_footer, zxid_version_str(), STRNULLCHK(cf->dbg), cf->mgmt_end);
162 [ # # ]: 0 : else if (auto_flags & ZXID_AUTO_FORMT)
163 [ # # ]: 0 : ss = zx_strf(cf->ctx,
164 : : #ifdef ZXID_USE_POST
165 : : "<form method=post action=\"%s?o=P\">\n"
166 : : #else
167 : : "<form method=get action=\"%s\">\n"
168 : : #endif
169 : : "<input type=hidden name=s value=\"%.*s\">"
170 : : "%s%s\n"
171 : : "</form>",
172 : : cf->url,
173 : : slen, STRNULLCHK(sid),
174 : : cf->mgmt_logout, cf->mgmt_defed);
175 [ # # ]: 0 : else if (auto_flags & ZXID_AUTO_FORMF)
176 [ # # ]: 0 : ss = zx_strf(cf->ctx,
177 : : "<input type=hidden name=s value=\"%.*s\">"
178 : : "%s%s\n",
179 : : slen, STRNULLCHK(sid),
180 : : cf->mgmt_logout, cf->mgmt_defed);
181 : : else
182 : 0 : ss = zx_dup_str(cf->ctx, "");
183 : :
184 : : #if 0
185 : : printf("COOKIE: foo\r\n");
186 : : if (qs) printf("QS(%s)\n", qs);
187 : : if (got>0) printf("GOT(%.*s)\n", got, buf);
188 : : if (cgi->err) printf("<p><font color=red><i>%s</i></font></p>\n", cgi->err);
189 : : if (cgi->msg) printf("<p><i>%s</i></p>\n", cgi->msg);
190 : : printf("User:<input name=user> PW:<input name=pw type=password>");
191 : : printf("<input name=login value=\" Login \" type=submit>");
192 : : printf("<h3>Technical options (typically hidden fields on production site)</h3>\n");
193 : : printf("sid(%s) nid(%s) <a href=\"zxid?s=%s\">Reload</a>", ses->sid, ses->nid, ses->sid);
194 : : if (cgi->dbg) printf("<p><form><textarea cols=100 row=10>%s</textarea></form>\n", cgi->dbg);
195 : : #endif
196 : :
197 [ + - - + ]: 3 : if (auto_flags & ZXID_AUTO_MGMTC && auto_flags & ZXID_AUTO_MGMTH) { /* Both H&C: CGI */
198 : 0 : printf("Content-Type: text/html" CRLF "Content-Length: %d" CRLF2 "%.*s",
199 : : ss->len, ss->len, ss->s);
200 : 0 : zx_str_free(cf->ctx, ss);
201 : 0 : return 0;
202 : : }
203 : :
204 [ + - ]: 3 : if (auto_flags & (ZXID_AUTO_MGMTC | ZXID_AUTO_MGMTH)) {
205 [ - + ]: 3 : if (auto_flags & ZXID_AUTO_MGMTH) { /* H only: return both H and C */
206 [ # # # # ]: 0 : D("With headers 0x%x", auto_flags);
207 : 0 : ss2 = zx_strf(cf->ctx, "Content-Type: text/html" CRLF "Content-Length: %d" CRLF2 "%.*s",
208 : : ss->len, ss->len, ss->s);
209 : 0 : zx_str_free(cf->ctx, ss);
210 : : } else {
211 [ + - - + ]: 3 : D("No headers 0x%x", auto_flags);
212 : 3 : ss2 = ss; /* C only */
213 : : }
214 : 3 : res = ss2->s;
215 : : DD("res(%s)", res);
216 [ - + ]: 3 : if (res_len)
217 : 0 : *res_len = ss2->len;
218 : 3 : ZX_FREE(cf->ctx, ss2);
219 : 3 : return res;
220 : : }
221 [ # # # # ]: 0 : D("m(%.*s)", ss->len, ss->s);
222 : 0 : zx_str_free(cf->ctx, ss);
223 [ # # ]: 0 : if (res_len)
224 : 0 : *res_len = 1;
225 : 0 : return zx_dup_cstr(cf->ctx, "m"); /* Neither H nor C */
226 : : }
227 : :
228 : : /* Called by: zxid_fed_mgmt */
229 : 0 : char* zxid_fed_mgmt_len(int conf_len, char* conf, int* res_len, char* sid, int auto_flags) {
230 : : zxid_conf cf;
231 : 0 : zxid_conf_to_cf_len(&cf, conf_len, conf);
232 : 0 : return zxid_fed_mgmt_cf(&cf, 0, -1, sid, auto_flags);
233 : : }
234 : :
235 : : /* Called by: */
236 : 0 : char* zxid_fed_mgmt(char* conf, char* sid, int auto_flags) {
237 : 0 : return zxid_fed_mgmt_len(-1, conf, 0, sid, auto_flags);
238 : : }
239 : :
240 : : /* ------------ zxid_an_page() ------------ */
241 : :
242 : : #define BBMATCH(k, key, lim) (sizeof(k)-1 == (lim)-(key) && !memcmp((k), (key), sizeof(k)-1))
243 : :
244 : : /*() Bang-bang expansions (!!VAR) understood in the templates. */
245 : :
246 : : /* Called by: zxid_template_page_cf */
247 : : static char* zxid_map_bangbang(zxid_conf* cf, zxid_cgi* cgi, const char* key, const char* lim, int auto_flags)
248 : 381 : {
249 : : char* s;
250 : : struct zx_str* ss;
251 : :
252 [ + + + + : 381 : switch (*key) {
+ + + + +
+ - ]
253 : : case 'A':
254 [ + - + - ]: 20 : if (BBMATCH("ACTION_URL", key, lim)) return cgi->action_url;
255 : 0 : break;
256 : : case 'D':
257 [ + - + - ]: 46 : if (BBMATCH("DBG", key, lim)) return cgi->dbg;
258 : 0 : break;
259 : : case 'E':
260 [ + - + + ]: 96 : if (BBMATCH("EID", key, lim)) {
261 : 50 : ss = zxid_my_ent_id(cf);
262 : 50 : s = ss->s; ZX_FREE(cf->ctx, ss);
263 : 50 : return s;
264 : : }
265 [ + - + - ]: 46 : if (BBMATCH("ERR", key, lim)) return cgi->err;
266 : 0 : break;
267 : : case 'I':
268 [ + - + - ]: 20 : if (BBMATCH("IDP_LIST", key, lim)) return zxid_idp_list_cf_cgi(cf, cgi, 0, auto_flags);
269 : 0 : break;
270 : : case 'M':
271 [ + - + - ]: 46 : if (BBMATCH("MSG", key, lim)) return cgi->msg;
272 : 0 : break;
273 : : case 'U':
274 [ + - + - ]: 26 : if (BBMATCH("URL", key, lim)) return cf->url;
275 : 0 : break;
276 : : case 'R':
277 [ + - + - ]: 25 : if (BBMATCH("RS", key, lim)) return cgi->rs;
278 : 0 : break;
279 : : case 'S':
280 [ + + + - ]: 65 : if (BBMATCH("SIG", key, lim)) return cgi->sig;
281 [ + + + + ]: 60 : if (BBMATCH("SP_EID", key, lim)) return cgi->sp_eid;
282 [ + + + - ]: 50 : if (BBMATCH("SP_DPY_NAME", key, lim)) return cgi->sp_dpy_name;
283 [ + + + - ]: 45 : if (BBMATCH("SSOREQ", key, lim)) return cgi->ssoreq;
284 [ + + + - ]: 40 : if (BBMATCH("SAML_ART", key, lim)) return cgi->saml_art;
285 [ + - + - ]: 20 : if (BBMATCH("SAML_RESP", key, lim)) return cgi->saml_resp;
286 : 0 : break;
287 : : case 'V':
288 [ + - + - ]: 31 : if (BBMATCH("VERSION", key, lim)) return zxid_version_str();
289 : 0 : break;
290 : : case 'Z':
291 [ + - + - ]: 6 : if (BBMATCH("ZXAPP", key, lim)) return cgi->zxapp;
292 : : break;
293 : : }
294 [ # # # # ]: 0 : D("Unmatched bangbang key(%.*s), taken as empty.", lim-key, key);
295 : 0 : return 0;
296 : : }
297 : :
298 : : /*() Expand a template. Only selected !!VAR expansions supported. No IFs or loops. */
299 : :
300 : : /* Called by: zxid_idp_select_zxstr_cf_cgi, zxid_saml2_post_enc, zxid_simple_idp_show_an, zxid_simple_show_err */
301 : : struct zx_str* zxid_template_page_cf(zxid_conf* cf, zxid_cgi* cgi, const char* templ_path, const char* default_templ, int size_hint, int auto_flags)
302 : 31 : {
303 : : char buf[8192];
304 : : const char* templ;
305 : : const char* tp;
306 : : const char* tq;
307 : : char* pp;
308 : : struct zx_str* ss;
309 : 31 : int len, got = read_all(sizeof(buf)-1, buf, "templ", 1, "%s", templ_path);
310 [ - + ]: 31 : if (got <= 0) {
311 [ # # # # ]: 0 : D("Template at path(%s) not found. Using default template.", templ_path);
312 : 0 : templ = default_templ;
313 [ - + ]: 31 : } else if (got == sizeof(buf)-1) {
314 : 0 : ERR("Template at path(%s) does not fit in buffer of %d. Using default template.", templ_path, sizeof(buf)-1);
315 : 0 : templ = default_templ;
316 : : } else
317 : 31 : templ = buf;
318 : : while (1) { /* Try rendering, iterate if expansion is needed. */
319 : 46 : tp = templ;
320 : 46 : ss = zx_new_len_str(cf->ctx, strlen(tp) + size_hint);
321 [ + + + - ]: 45508 : for (pp = ss->s; *tp && pp < ss->s + ss->len; ) {
322 [ + + + + : 45431 : if (tp[0] == '!' && tp[1] == '!' && AZaz_(tp[2])) {
+ - + - -
+ # # #
# ]
323 [ + + + + : 381 : for (tq = tp+=2; AZaz_(*tp); ++tp) ;
+ - - + #
# ]
324 : 381 : tq = zxid_map_bangbang(cf, cgi, tq, tp, auto_flags);
325 [ + + + + ]: 381 : if (!tq || !*tq)
326 : : continue;
327 : 212 : len = strlen(tq);
328 [ + + ]: 212 : if (pp + len >= ss->s + ss->len) {
329 : 15 : pp += len;
330 : 15 : break;
331 : : }
332 : 197 : memcpy(pp, tq, len);
333 : 197 : pp += len;
334 : 197 : continue;
335 : : }
336 : 45050 : *pp++ = *tp++;
337 : : }
338 [ + + ]: 46 : if (pp >= ss->s + ss->len) {
339 : 15 : INFO("Expansion of template too big. Does not fit in %d. Expanding.", ss->len);
340 : 15 : size_hint += size_hint; /* Double it */
341 : : continue;
342 : : }
343 : : break;
344 : 15 : }
345 : 31 : *pp = 0;
346 : 31 : ss->len = pp - ss->s;
347 : 31 : return ss;
348 : : }
349 : :
350 : : /* ------------ zxid_idp_list() ------------ */
351 : :
352 : : /*(i) Generate IdP selection buttons (Login buttons) for the IdPs that are
353 : : * members of our Circle of Trust (CoT). This can be used as component for
354 : : * developing your application specific (HTML) login screen.
355 : : *
356 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
357 : :
358 : : /* Called by: zxid_idp_list_cf, zxid_idp_select_zxstr_cf_cgi, zxid_map_bangbang */
359 : : char* zxid_idp_list_cf_cgi(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
360 : 20 : {
361 : : int i;
362 : : char* s;
363 : : char mark[32];
364 : : struct zx_str* ss;
365 : : struct zx_str* dd;
366 : : zxid_entity* idp;
367 : : zxid_entity* idp_cdc;
368 [ + - ]: 20 : if (auto_flags & ZXID_AUTO_DEBUG) zxid_set_opt(cf, 1, 1);
369 : 20 : idp = zxid_load_cot_cache(cf);
370 [ - + ]: 20 : if (!idp) {
371 [ # # # # ]: 0 : D("No IdP's found %p", res_len);
372 [ # # ]: 0 : if (res_len)
373 : 0 : *res_len = 0;
374 : 0 : return "";
375 : : }
376 : :
377 [ + - + - ]: 40 : if ((auto_flags & ZXID_AUTO_FORMT) && (auto_flags & ZXID_AUTO_FORMF))
378 : 20 : ss = zx_dup_str(cf->ctx, "<h3>Login Using Known IdP</h3>\n");
379 : : else
380 : 0 : ss = zx_dup_str(cf->ctx, "");
381 : :
382 [ - + ]: 20 : if (cf->idp_list_meth == ZXID_IDP_LIST_POPUP) {
383 : 0 : dd = zx_strf(cf->ctx, "%.*s<select name=d>\n", ss->len, ss->s);
384 : 0 : zx_str_free(cf->ctx, ss);
385 : 0 : ss = dd;
386 : : }
387 : :
388 [ + - - + ]: 20 : D("Starting IdP list processing... %p", idp);
389 [ + + ]: 409 : for (; idp; idp = idp->n) {
390 [ + + ]: 389 : if (!idp->ed->IDPSSODescriptor)
391 : 125 : continue;
392 : :
393 : 264 : mark[0] = 0;
394 [ + - ]: 264 : if (cgi) { /* Was IdP recommended in IdP list supplied via CDC? See zxid_cdc_check() */
395 : 264 : for (idp_cdc = cgi->idp_list, i=1;
396 [ - + # # ]: 528 : idp_cdc && idp_cdc != idp;
397 : 0 : idp_cdc = idp_cdc->n_cdc, ++i);
398 [ - + # # : 264 : if (cf->cdc_choice == ZXID_CDC_CHOICE_UI_ONLY_CDC && cgi->idp_list && !idp_cdc)
# # ]
399 : 0 : continue;
400 [ - + ]: 264 : if (idp_cdc) {
401 : 0 : snprintf(mark, sizeof(mark), " CDC %d", i);
402 : 0 : mark[sizeof(mark)-1] = 0;
403 : : }
404 : : }
405 : :
406 [ - - + ]: 264 : switch (cf->idp_list_meth) {
407 : : default:
408 : 0 : ERR("Unsupported IDP_LIST_METH=%d, reverting to popup.", cf->idp_list_meth);
409 : 0 : cf->idp_list_meth = ZXID_IDP_LIST_POPUP;
410 : : /* fall thru */
411 : : case ZXID_IDP_LIST_POPUP:
412 [ # # ]: 0 : dd = zx_strf(cf->ctx, "%.*s"
413 : : "<option value=\"%s\"> %s (%s) %s\n",
414 : : ss->len, ss->s, idp->eid, STRNULLCHK(idp->dpy_name), idp->eid, mark);
415 : 0 : break;
416 : : case ZXID_IDP_LIST_BUTTON:
417 [ - + ]: 264 : if (cf->show_tech) {
418 [ # # # # : 0 : dd = zx_strf(cf->ctx, "%.*s"
# # # # ]
419 : : "<input type=submit name=\"l0%s\" value=\" Login with %s (%s)\">\n"
420 : : "<input type=submit name=\"l1%s\" value=\" Login with %s (%s) (A2) \">\n"
421 : : "<input type=submit name=\"l2%s\" value=\" Login with %s (%s) (P2) \">\n"
422 : : "<input type=submit name=\"l5%s\" value=\" Login with %s (%s) (S2) \">%s<br>\n",
423 : : ss->len, ss->s,
424 : : idp->eid, STRNULLCHK(idp->dpy_name), idp->eid,
425 : : idp->eid, STRNULLCHK(idp->dpy_name), idp->eid,
426 : : idp->eid, STRNULLCHK(idp->dpy_name), idp->eid,
427 : : idp->eid, STRNULLCHK(idp->dpy_name), idp->eid,
428 : : mark);
429 : : } else {
430 [ + + ]: 264 : dd = zx_strf(cf->ctx, "%.*s"
431 : : "<input type=submit name=\"l0%s\" value=\" Login with %s (%s) \">%s<br>\n",
432 : : ss->len, ss->s, idp->eid, STRNULLCHK(idp->dpy_name), idp->eid, mark);
433 : : }
434 : : break;
435 : : }
436 : 264 : zx_str_free(cf->ctx, ss);
437 : 264 : ss = dd;
438 : : }
439 [ - + ]: 20 : if (cf->idp_list_meth == ZXID_IDP_LIST_POPUP) {
440 [ # # ]: 0 : if (cf->show_tech) {
441 : 0 : dd = zx_strf(cf->ctx, "%.*s</select>"
442 : : "<input type=submit name=\"l0\" value=\" Login \">\n"
443 : : "<input type=submit name=\"l1\" value=\" Login (A2) \">\n"
444 : : "<input type=submit name=\"l2\" value=\" Login (P2) \">\n"
445 : : "<input type=submit name=\"l5\" value=\" Login (S2) \"><br>\n",
446 : : ss->len, ss->s);
447 : : } else {
448 : 0 : dd = zx_strf(cf->ctx, "%.*s</select>"
449 : : "<input type=submit name=\"l0\" value=\" Login \"><br>\n",
450 : : ss->len, ss->s);
451 : : }
452 : 0 : zx_str_free(cf->ctx, ss);
453 : 0 : ss = dd;
454 : : }
455 : :
456 : 20 : s = ss->s;
457 [ + - - + ]: 20 : D("IdP list(%s)", s);
458 [ - + ]: 20 : if (res_len)
459 : 0 : *res_len = ss->len;
460 : 20 : ZX_FREE(cf->ctx, ss);
461 : 20 : return s;
462 : : }
463 : :
464 : : /* Called by: zxid_idp_list_len */
465 : 0 : char* zxid_idp_list_cf(zxid_conf* cf, int* res_len, int auto_flags) {
466 : 0 : return zxid_idp_list_cf_cgi(cf, 0, res_len, auto_flags);
467 : : }
468 : :
469 : : /* Called by: zxid_idp_list */
470 : 0 : char* zxid_idp_list_len(int conf_len, char* conf, int* res_len, int auto_flags) {
471 : : zxid_conf cf;
472 : 0 : zxid_conf_to_cf_len(&cf, conf_len, conf);
473 : 0 : return zxid_idp_list_cf(&cf, 0, auto_flags);
474 : : }
475 : :
476 : : /* Called by: */
477 : 0 : char* zxid_idp_list(char* conf, int auto_flags) {
478 : 0 : return zxid_idp_list_len(-1, conf, 0, auto_flags);
479 : : }
480 : :
481 : : #define FLDCHK(x,y) (x && x->y ? x->y : "")
482 : :
483 : : /*(i) Render entire IdP selection screen. You may use this code, possibly adjusted
484 : : * by some configuration options (see zxidconf.h), or you may choose to develop
485 : : * your own IdP selection screen from scratch.
486 : : *
487 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
488 : :
489 : : /* Called by: zxid_idp_select_zxstr_cf, zxid_simple_show_idp_sel */
490 : : struct zx_str* zxid_idp_select_zxstr_cf_cgi(zxid_conf* cf, zxid_cgi* cgi, int auto_flags)
491 : 20 : {
492 : 20 : struct zx_str* eid=0;
493 : : struct zx_str* ss;
494 : :
495 [ + - + - : 20 : D("HERE %p e(%s) m(%s) d(%s)", eid, FLDCHK(cgi, err), FLDCHK(cgi, msg), FLDCHK(cgi, dbg));
- + + - +
+ + - + +
- + ]
496 [ + - ]: 20 : if (cf->log_level>1)
497 : 20 : zxlog(cf, 0,0,0,0,0,0,0, "N", "W", "IDPSEL", 0, 0);
498 : :
499 : : #if 1
500 : 20 : ss = zxid_template_page_cf(cf, cgi, cf->idp_sel_templ_file, cf->idp_sel_templ, 4096, auto_flags);
501 : : #else
502 : : if (cf->idp_sel_our_eid && cf->idp_sel_our_eid[0])
503 : : eid = zxid_my_ent_id(cf);
504 : : char* idp_list = zxid_idp_list_cf_cgi(cf, cgi, 0, auto_flags);
505 : : if ((auto_flags & ZXID_AUTO_FORMT) && (auto_flags & ZXID_AUTO_FORMF)) {
506 : : DD("HERE %p", cgi->idp_list);
507 : : ss = zx_strf(cf->ctx,
508 : : "%s"
509 : : #ifdef ZXID_USE_POST
510 : : "<form method=post action=\"%s?o=P\">\n"
511 : : #else
512 : : "<form method=get action=\"%s\">\n"
513 : : #endif
514 : : "<font color=red>%s</font><font color=green>%s</font><font color=white>%s</font>"
515 : : "%s"
516 : : "%s<a href=\"%.*s\">%.*s</a><br>"
517 : : "%s" /* IdP List */
518 : : "%s%s"
519 : : "<input type=hidden name=fr value=\"%s\">\n"
520 : : "</form>%s%s%s",
521 : : cf->idp_sel_start,
522 : : cf->url,
523 : : FLDCHK(cgi, err), FLDCHK(cgi, msg), FLDCHK(cgi, dbg),
524 : : cf->idp_sel_new_idp,
525 : : cf->idp_sel_our_eid, eid?eid->len:0, eid?eid->s:"", eid?eid->len:0, eid?eid->s:"",
526 : : idp_list,
527 : : cf->idp_sel_tech_user, cf->idp_sel_tech_site,
528 : : FLDCHK(cgi, rs),
529 : : cf->idp_sel_footer, zxid_version_str(), cf->idp_sel_end);
530 : : DD("HERE(%d) ss(%.*s)", ss->len, ss->len, ss->s);
531 : : } else if (auto_flags & ZXID_AUTO_FORMT) {
532 : : ss = zx_strf(cf->ctx,
533 : : #ifdef ZXID_USE_POST
534 : : "<form method=post action=\"%s?o=P\">\n"
535 : : #else
536 : : "<form method=get action=\"%s\">\n"
537 : : #endif
538 : : "<font color=red>%s</font><font color=green>%s</font><font color=white>%s</font>"
539 : : "%s"
540 : : "%s<a href=\"%.*s\">%.*s</a><br>"
541 : : "%s" /* IdP List */
542 : : "%s%s"
543 : : "<input type=hidden name=fr value=\"%s\">\n"
544 : : "</form>",
545 : : cf->url,
546 : : FLDCHK(cgi, err), FLDCHK(cgi, msg), FLDCHK(cgi, dbg),
547 : : cf->idp_sel_new_idp,
548 : : cf->idp_sel_our_eid, eid?eid->len:0, eid?eid->s:"", eid?eid->len:0, eid?eid->s:"",
549 : : idp_list,
550 : : cf->idp_sel_tech_user, cf->idp_sel_tech_site,
551 : : FLDCHK(cgi, rs));
552 : : } else if (auto_flags & ZXID_AUTO_FORMF) {
553 : : ss = zx_strf(cf->ctx,
554 : : "<font color=red>%s</font><font color=green>%s</font><font color=white>%s</font>"
555 : : "%s"
556 : : "%s<a href=\"%.*s\">%.*s</a><br>"
557 : : "%s" /* IdP List */
558 : : "%s%s"
559 : : "<input type=hidden name=fr value=\"%s\">\n",
560 : : FLDCHK(cgi, err), FLDCHK(cgi, msg), FLDCHK(cgi, dbg),
561 : : cf->idp_sel_new_idp,
562 : : cf->idp_sel_our_eid, eid?eid->len:0, eid?eid->s:"", eid?eid->len:0, eid?eid->s:"",
563 : : idp_list,
564 : : cf->idp_sel_tech_user, cf->idp_sel_tech_site,
565 : : FLDCHK(cgi, rs));
566 : : } else
567 : : ss = zx_dup_str(cf->ctx, "");
568 : : #endif
569 : : #if 0
570 : : if (cgi.err) printf("<p><font color=red><i>%s</i></font></p>\n", cgi.err);
571 : : if (cgi.msg) printf("<p><i>%s</i></p>\n", cgi.msg);
572 : : printf("User:<input name=user> PW:<input name=pw type=password>");
573 : : printf("<input name=login value=\" Login \" type=submit>");
574 : : printf("<h3>Login Using IdP Discovered from Common Domain Cookie (CDC)</h3>\n");
575 : : printf("RelayState: <input name=fr value=\"rs123\"><br>\n");
576 : : if (cgi.dbg) printf("<p><form><textarea cols=100 row=10>%s</textarea></form>\n", cgi.dbg);
577 : : #endif
578 : 20 : return ss;
579 : : }
580 : :
581 : : /* Called by: zxid_idp_select_cf */
582 : 0 : struct zx_str* zxid_idp_select_zxstr_cf(zxid_conf* cf, int auto_flags) {
583 : 0 : return zxid_idp_select_zxstr_cf_cgi(cf, 0, auto_flags);
584 : : }
585 : :
586 : : /* Called by: zxid_idp_select_len */
587 : 0 : char* zxid_idp_select_cf(zxid_conf* cf, int* res_len, int auto_flags) {
588 : : char* s;
589 : 0 : struct zx_str* ss = zxid_idp_select_zxstr_cf(cf, auto_flags);
590 : 0 : s = ss->s;
591 [ # # ]: 0 : if (res_len)
592 : 0 : *res_len = ss->len;
593 : 0 : ZX_FREE(cf->ctx, ss);
594 : 0 : return s;
595 : : }
596 : :
597 : : /* Called by: zxid_idp_select */
598 : 0 : char* zxid_idp_select_len(int conf_len, char* conf, int* res_len, int auto_flags) {
599 : : zxid_conf cf;
600 : 0 : zxid_conf_to_cf_len(&cf, conf_len, conf);
601 : 0 : return zxid_idp_select_cf(&cf, 0, auto_flags);
602 : : }
603 : :
604 : : /* Called by: */
605 : 0 : char* zxid_idp_select(char* conf, int auto_flags) {
606 : 0 : return zxid_idp_select_len(-1, conf, 0, auto_flags);
607 : : }
608 : :
609 : : /* ------------ zxid_simple() ------------ */
610 : :
611 : : /*() Deal with the various methods of shipping the page, including CGI stdout, or
612 : : * as string with or without headers, as indicated by the auto_flag. The
613 : : * page is in ss. */
614 : :
615 : : /* Called by: zxid_simple_idp_show_an, zxid_simple_show_carml, zxid_simple_show_conf, zxid_simple_show_err, zxid_simple_show_idp_sel, zxid_simple_show_meta */
616 : : static char* zxid_simple_show_page(zxid_conf* cf, struct zx_str* ss, int c_mask, int h_mask, char* rets, char* cont_type, int* res_len, int auto_flags)
617 : 32 : {
618 : : char* res;
619 : : struct zx_str* ss2;
620 [ + - + + ]: 32 : if (auto_flags & c_mask && auto_flags & h_mask) { /* Both H&C: CGI */
621 [ + - - + ]: 27 : D("CGI %x", auto_flags);
622 : 27 : printf("Content-Type: %s" CRLF "Content-Length: %d" CRLF2 "%.*s",
623 : : cont_type, ss->len, ss->len, ss->s);
624 [ + - ]: 27 : if (auto_flags & ZXID_AUTO_EXIT)
625 : 27 : exit(0);
626 : 0 : zx_str_free(cf->ctx, ss);
627 [ # # ]: 0 : if (res_len)
628 : 0 : *res_len = 1;
629 : 0 : return zx_dup_cstr(cf->ctx, "n");
630 : : }
631 : :
632 [ + - ]: 5 : if (auto_flags & (c_mask | h_mask)) {
633 [ - + ]: 5 : if (auto_flags & h_mask) { /* H only: return both H and C */
634 [ # # # # ]: 0 : D("With headers %x", auto_flags);
635 : 0 : ss2 = zx_strf(cf->ctx, "Content-Type: %s" CRLF "Content-Length: %d" CRLF2 "%.*s",
636 : : cont_type, ss->len, ss->len, ss->s);
637 : 0 : zx_str_free(cf->ctx, ss);
638 : : } else {
639 [ + - - + ]: 5 : D("No headers %x", auto_flags);
640 : 5 : ss2 = ss; /* C only */
641 : : }
642 : 5 : res = ss2->s;
643 : : DD("res(%s)", res);
644 [ - + ]: 5 : if (res_len)
645 : 0 : *res_len = ss2->len;
646 : 5 : ZX_FREE(cf->ctx, ss2);
647 : 5 : return res;
648 : : }
649 : : /* Do not output anything (both c and h 0). Effectively the generated page is thrown away. */
650 [ # # # # : 0 : D("e(%.*s) cm=%x hm=%x af=%x rets(%s)", ss?ss->len:-1, ss?ss->s:"", c_mask, h_mask, auto_flags, rets);
# # # # ]
651 [ # # ]: 0 : if (ss)
652 : 0 : zx_str_free(cf->ctx, ss);
653 [ # # ]: 0 : if (res_len)
654 : 0 : *res_len = 1;
655 : 0 : return zx_dup_cstr(cf->ctx, rets); /* Neither H nor C */
656 : : }
657 : :
658 : : /*() Helper function to redirect according to auto flags. */
659 : :
660 : : /* Called by: zxid_simple_idp_an_ok_do_rest, zxid_simple_idp_new_user, zxid_simple_idp_recover_password, zxid_simple_idp_show_an, zxid_simple_show_err, zxid_simple_show_idp_sel */
661 : : static char* zxid_simple_redir_page(zxid_conf* cf, char* redir, char* rs, int* res_len, int auto_flags)
662 : 2 : {
663 : : char* res;
664 : : struct zx_str* ss;
665 [ + - - + ]: 2 : D("cf=%p redir(%s)", cf, redir);
666 [ + - ]: 2 : if (auto_flags & ZXID_AUTO_REDIR) {
667 [ + - ]: 2 : printf("Location: %s?%s" CRLF2, redir, STRNULLCHK(rs));
668 [ + - ]: 2 : if (auto_flags & ZXID_AUTO_EXIT)
669 : 2 : exit(0);
670 [ # # ]: 0 : if (res_len)
671 : 0 : *res_len = 1;
672 : 0 : return zx_dup_cstr(cf->ctx, "n");
673 : : }
674 [ # # ]: 0 : ss = zx_strf(cf->ctx, "Location: %s?%s" CRLF2, redir, STRNULLCHK(rs));
675 [ # # ]: 0 : if (res_len)
676 : 0 : *res_len = ss->len;
677 : 0 : res = ss->s;
678 : 0 : ZX_FREE(cf->ctx, ss);
679 : 0 : return res;
680 : : }
681 : :
682 : : /*() Show IdP selection or login screen.
683 : : *
684 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
685 : :
686 : : /* Called by: zxid_ps_accept_invite, zxid_ps_finalize_invite, zxid_simple_no_ses_cf, zxid_simple_ses_active_cf x5 */
687 : : char* zxid_simple_show_idp_sel(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
688 : 20 : {
689 : : struct zx_str* ss;
690 [ + - - + ]: 20 : D("cf=%p cgi=%p", cf, cgi);
691 [ - + # # ]: 20 : if (cf->idp_sel_page && cf->idp_sel_page[0]) {
692 [ # # # # : 0 : D("idp_sel_page(%s) rs(%s)", cf->idp_sel_page, STRNULLCHK(cgi->rs));
# # ]
693 : 0 : return zxid_simple_redir_page(cf, cf->idp_sel_page, cgi->rs, res_len, auto_flags);
694 : : }
695 [ + - ]: 20 : ss = auto_flags & (ZXID_AUTO_LOGINC | ZXID_AUTO_LOGINH)
696 : : ? zxid_idp_select_zxstr_cf_cgi(cf, cgi, auto_flags)
697 : : : 0;
698 : : DD("idp_select: ret(%s)", ss?ss->len:1, ss?ss->s:"?");
699 : 20 : return zxid_simple_show_page(cf, ss, ZXID_AUTO_LOGINC, ZXID_AUTO_LOGINH,
700 : : "e", "text/html", res_len, auto_flags);
701 : : }
702 : :
703 : :
704 : : /*() Emit metadata. Corresponds to "o=B" query string.
705 : : *
706 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
707 : :
708 : : /* Called by: zxid_simple_no_ses_cf x2, zxid_simple_ses_active_cf x2 */
709 : : static char* zxid_simple_show_meta(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
710 : 4 : {
711 : 4 : struct zx_str* meta = zxid_sp_meta(cf, cgi);
712 : 4 : return zxid_simple_show_page(cf, meta, ZXID_AUTO_METAC, ZXID_AUTO_METAH,
713 : : "b", "text/xml", res_len, auto_flags);
714 : : }
715 : :
716 : : /*() Emit CARML declaration for SP. Corresponds to "o=c" query string. */
717 : :
718 : : /* Called by: zxid_simple_no_ses_cf, zxid_simple_ses_active_cf */
719 : : static char* zxid_simple_show_carml(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
720 : 1 : {
721 : 1 : struct zx_str* carml = zxid_sp_carml(cf);
722 : 1 : return zxid_simple_show_page(cf, carml, ZXID_AUTO_METAC, ZXID_AUTO_METAH,
723 : : "c", "text/xml", res_len, auto_flags);
724 : : }
725 : :
726 : : /*() Dump internal info and configuration. Corresponds to "o=d" query string. */
727 : :
728 : : /* Called by: zxid_simple_no_ses_cf, zxid_simple_ses_active_cf */
729 : : static char* zxid_simple_show_conf(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
730 : 1 : {
731 : 1 : struct zx_str* ss = zxid_show_conf(cf);
732 : 1 : return zxid_simple_show_page(cf, ss, ZXID_AUTO_METAC, ZXID_AUTO_METAH,
733 : : "d", "text/html", res_len, auto_flags);
734 : : }
735 : :
736 : : /*() Show Error screen. */
737 : :
738 : : /* Called by: zxid_ps_accept_invite x4, zxid_ps_finalize_invite x4 */
739 : : char* zxid_simple_show_err(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
740 : 1 : {
741 : : char* p;
742 : : struct zx_str* ss;
743 : :
744 [ + - ]: 1 : if (cf->log_level>1)
745 : 1 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "ERR", 0, "");
746 : :
747 [ - + # # ]: 1 : if (cf->err_page && cf->err_page[0]) {
748 [ # # # # : 0 : ss = zx_strf(cf->ctx, "zxrfr=F%s%s%s%s&zxidpurl=%s",
# # # # #
# # # ]
749 : : cgi->zxapp && cgi->zxapp[0] ? "&zxapp=" : "", cgi->zxapp ? cgi->zxapp : "",
750 : : cgi->err && cgi->err[0] ? "&err=" : "", cgi->err ? cgi->err : "",
751 : : cf->url);
752 : 0 : p = ss->s;
753 : 0 : ZX_FREE(cf->ctx, ss);
754 [ # # # # ]: 0 : D("err_page(%s) p(%s)", cf->err_page, p);
755 : 0 : return zxid_simple_redir_page(cf, cf->err_page, p, res_len, auto_flags);
756 : : }
757 : :
758 : 1 : ss = zxid_template_page_cf(cf, cgi, cf->err_templ_file, cf->err_templ, 4096, auto_flags);
759 : 1 : return zxid_simple_show_page(cf, ss, ZXID_AUTO_LOGINC, ZXID_AUTO_LOGINH,
760 : : "g", "text/html", res_len, auto_flags);
761 : : }
762 : :
763 : : /* ----------- IdP Screens ----------- */
764 : :
765 : : /*() Decode ssoreq (ar=), i.e. the preserved original AuthnReq */
766 : :
767 : : /* Called by: zxid_simple_idp_pw_authn, zxid_simple_idp_show_an */
768 : : static int zxid_decode_ssoreq(zxid_conf* cf, zxid_cgi* cgi)
769 : 2 : {
770 : : int len;
771 : : char* buf;
772 : : char* p;
773 [ + - + + ]: 2 : if (!cgi->ssoreq || !cgi->ssoreq[0])
774 : 1 : return 1;
775 [ + - - + ]: 1 : D("ssoreq(%s)", cgi->ssoreq);
776 : 1 : len = strlen(cgi->ssoreq);
777 : 1 : buf = ZX_ALLOC(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(len));
778 : 1 : p = unbase64_raw(cgi->ssoreq, cgi->ssoreq + len, buf, zx_std_index_64);
779 : 1 : p = zx_zlib_raw_inflate(0, p-buf, buf, &len);
780 : 1 : ZX_FREE(cf->ctx, buf);
781 [ + - ]: 1 : if (!p)
782 : 1 : return 0;
783 : 0 : p[len] = 0;
784 : 0 : cgi->op = 0;
785 [ # # # # ]: 0 : D("ar/ssoreq decoded(%s)", p);
786 : 0 : zxid_parse_cgi(cgi, p); /* cgi->op will be Q due to SAMLRequest inside ssoreq */
787 : 0 : cgi->op = 'F';
788 : 0 : return 1;
789 : : }
790 : :
791 : : /*() Process IdP side after successful authentication. If IdP was
792 : : * invoked with AuthnReq (in SAMLRequest) then op=='F' as set
793 : : * in zxid_simple_idp_pw_authn() which will trigger the rest of the
794 : : * SSO protocol in zxid_simple_ses_active_cf(). Otherwise just
795 : : * show the IdP management screen. */
796 : :
797 : : /* Called by: zxid_simple_idp_pw_authn, zxid_simple_idp_show_an */
798 : : static char* zxid_simple_idp_an_ok_do_rest(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int* res_len, int auto_flags)
799 : 2 : {
800 : : struct zx_str* ss;
801 : : char* p;
802 : : DD("idp do_rest %p", ses);
803 [ + - - + ]: 2 : if (cf->atsel_page && cgi->atselafter) { /* *** More sophisticated criteria needed. */
804 [ # # # # : 0 : ss = zx_strf(cf->ctx, "ar=%s&s=%s&zxrfr=F%s%s%s%s&zxidpurl=%s",
# # # # #
# # # ]
805 : : cgi->ssoreq, cgi->sid,
806 : : cgi->zxapp && cgi->zxapp[0] ? "&zxapp=" : "", cgi->zxapp ? cgi->zxapp : "",
807 : : cgi->err && cgi->err[0] ? "&err=" : "", cgi->err ? cgi->err : "",
808 : : cf->url);
809 : 0 : p = ss->s;
810 : 0 : ZX_FREE(cf->ctx, ss);
811 [ # # # # ]: 0 : D("atsel_page(%s) redir(%s)", cf->atsel_page, p);
812 : 0 : return zxid_simple_redir_page(cf, cf->atsel_page, p, res_len, auto_flags);
813 : : }
814 : 2 : return zxid_simple_ses_active_cf(cf, cgi, ses, res_len, auto_flags);
815 : : }
816 : :
817 : : /*() Show Authentication screen. Generally this will be in response to
818 : : * the SP having sent user via redirect to o=F carrying AuthnRequest encoded
819 : : * in SAMLRequest query string parameter, per SAML redirect binding
820 : : * [SAML2bind]. We must preserve SAMLRequest as hidden field, ar, in the
821 : : * page for later processing once the authentication step has been
822 : : * taken care of. It will also be passed on the query string to
823 : : * external authentication page if any was configured with AN_PAGE
824 : : * directive.
825 : : *
826 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
827 : :
828 : : /* Called by: zxid_simple_idp_new_user, zxid_simple_idp_pw_authn, zxid_simple_idp_recover_password, zxid_simple_no_ses_cf */
829 : : static char* zxid_simple_idp_show_an(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
830 : 6 : {
831 : : int zlen, len;
832 : : char* zbuf;
833 : 6 : char* b64 = 0;
834 : : char* p;
835 : : char* ar;
836 : : struct zx_sa_Issuer_s* issuer;
837 : : zxid_entity* meta;
838 : : struct zx_root_s* r;
839 : : struct zx_str* ss;
840 : : zxid_ses sess;
841 : 6 : ZERO(&sess, sizeof(sess));
842 [ + - - + ]: 6 : D("cf=%p cgi=%p", cf, cgi);
843 : :
844 : : DD("z saml_req(%s) rs(%s) sigalg(%s) sig(%s)", cgi->saml_req, cgi->rs, cgi->sigalg, cgi->sig);
845 [ + + + - ]: 6 : if (cgi->uid && zxid_pw_authn(cf, cgi, &sess)) { /* Try login, just in case. */
846 : 1 : return zxid_simple_idp_an_ok_do_rest(cf, cgi, &sess, res_len, auto_flags);
847 : : }
848 [ + + ]: 5 : if (cgi->saml_req) {
849 : : DD("zz saml_req(%s) rs(%s) sigalg(%s) sig(%s)", cgi->saml_req, cgi->rs, cgi->sigalg, cgi->sig);
850 [ + - + - : 1 : ss = zx_strf(cf->ctx, "SAMLRequest=%s%s%s&SigAlg=%s&Signature=%s",
- + - + #
# + - ]
851 : : STRNULLCHK(cgi->saml_req),
852 : : cgi->rs && cgi->rs[0] ? "&RelayState=" : "", cgi->rs ? cgi->rs : "",
853 : : STRNULLCHK(cgi->sigalg),
854 : : STRNULLCHK(cgi->sig));
855 [ + - - + ]: 1 : D("z input(%.*s) len=%d", ss->len, ss->s, ss->len);
856 : 1 : zbuf = zx_zlib_raw_deflate(cf->ctx, ss->len, ss->s, &zlen);
857 [ - + ]: 1 : if (!zbuf)
858 : 0 : return 0;
859 : :
860 : 1 : len = SIMPLE_BASE64_LEN(zlen);
861 : : DD("zbuf(%.*s) zlen=%d len=%d", zlen, zbuf, zlen, len);
862 : 1 : b64 = ZX_ALLOC(cf->ctx, len+1);
863 : 1 : p = base64_fancy_raw(zbuf, zlen, b64, safe_basis_64, 1<<31, 0, 0, '=');
864 : 1 : *p = 0;
865 : 1 : zx_str_free(cf->ctx, ss);
866 : 1 : cgi->ssoreq = b64;
867 : : }
868 : :
869 [ - + # # ]: 5 : if (cf->an_page && cf->an_page[0]) {
870 [ # # # # : 0 : ss = zx_strf(cf->ctx, "ar=%s&zxrfr=F%s%s%s%s&zxidpurl=%s",
# # # # #
# # # ]
871 : : cgi->ssoreq,
872 : : cgi->zxapp && cgi->zxapp[0] ? "&zxapp=" : "", cgi->zxapp ? cgi->zxapp : "",
873 : : cgi->err && cgi->err[0] ? "&err=" : "", cgi->err ? cgi->err : "",
874 : : cf->url);
875 [ # # ]: 0 : if (b64)
876 : 0 : ZX_FREE(cf->ctx, b64);
877 : 0 : ar = ss->s;
878 : 0 : ZX_FREE(cf->ctx, ss);
879 [ # # # # ]: 0 : D("an_page(%s) ar(%s)", cf->an_page, ar);
880 : 0 : return zxid_simple_redir_page(cf, cf->an_page, ar, res_len, auto_flags);
881 : : }
882 : :
883 [ + - ]: 5 : if (cf->log_level>1)
884 : 5 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "AUTHN", 0, "");
885 : :
886 : : /* Attempt to provisorily decode the request and fetch metadata of the SP so we
887 : : * can detect trouble early on and provide some assuring knowledge to the user. */
888 : :
889 [ + + - + ]: 5 : if (!cgi->saml_req && cgi->ssoreq) {
890 : 0 : zxid_decode_ssoreq(cf, cgi);
891 : : }
892 : :
893 : 5 : r = zxid_decode_redir_or_post(cf, cgi, &sess, 0x2);
894 [ + + ]: 5 : if (r) {
895 : 1 : issuer = zxid_extract_issuer(cf, cgi, &sess, r);
896 [ + - + - : 2 : if (ZX_SIMPLE_ELEM_CHK(issuer)) {
+ - + - +
- + - ]
897 [ + - + - : 1 : meta = zxid_get_ent_ss(cf, ZX_GET_CONTENT(issuer));
+ - ]
898 [ + - ]: 1 : if (meta) {
899 : 1 : cgi->sp_eid = meta->eid;
900 : 1 : cgi->sp_dpy_name = meta->dpy_name;
901 : : } else {
902 [ # # # # : 0 : ERR("Unable to find metadata for Issuer(%.*s) in AnReq Redir", ZX_GET_CONTENT_LEN(issuer), ZX_GET_CONTENT_S(issuer));
# # # # #
# # # ]
903 : 0 : cgi->err = "Issuer unknown - metadata exchange may be needed (AnReq).";
904 : 0 : cgi->sp_dpy_name = "--SP description unavailable--";
905 [ # # # # : 0 : cgi->sp_eid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(issuer));
# # ]
906 : : }
907 : : } else {
908 : 0 : cgi->err = "Issuer could not be determined from Authentication Request.";
909 : 0 : cgi->sp_eid = "";
910 : 0 : cgi->sp_dpy_name = "--No SP could be determined--";
911 : : }
912 : : } else {
913 : 4 : cgi->err = "Malformed or nonexistant Authentication Request";
914 : 4 : cgi->sp_eid = "";
915 : 4 : cgi->sp_dpy_name = "--No SP could be determined--";
916 : : }
917 : :
918 : 5 : ss = zxid_template_page_cf(cf, cgi, cf->an_templ_file, cf->an_templ, 4096, auto_flags);
919 [ + + ]: 5 : if (b64)
920 : 1 : ZX_FREE(cf->ctx, b64);
921 : : DD("an_page: ret(%s)", ss?ss->len:1, ss?ss->s:"?");
922 : 5 : return zxid_simple_show_page(cf, ss, ZXID_AUTO_LOGINC, ZXID_AUTO_LOGINH,
923 : : "a", "text/html", res_len, auto_flags);
924 : : }
925 : :
926 : : /*() Process password authentication form and, if ssoreq (ar=) is present
927 : : * (see zxid_simple_idp_show_an() for how it is embedded to hidden
928 : : * form field), proceed to federated SSO. If login fails, redisplay
929 : : * the authentication page.
930 : : *
931 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
932 : :
933 : : /* Called by: zxid_simple_no_ses_cf */
934 : : static char* zxid_simple_idp_pw_authn(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
935 : 2 : {
936 : : zxid_ses sess;
937 [ + - - + ]: 2 : D("cf=%p cgi=%p", cf, cgi);
938 : :
939 [ + + ]: 2 : if (!zxid_decode_ssoreq(cf, cgi))
940 : 1 : goto err;
941 : :
942 : 1 : ZERO(&sess, sizeof(sess));
943 [ + - ]: 1 : if (zxid_pw_authn(cf, cgi, &sess))
944 : 1 : return zxid_simple_idp_an_ok_do_rest(cf, cgi, &sess, res_len, auto_flags);
945 : :
946 [ # # # # : 0 : D("PW Login failed uid(%s) pw(%s) err(%s)", STRNULLCHK(cgi->uid), STRNULLCHK(cgi->pw), STRNULLCHK(cgi->err));
# # # # #
# ]
947 : 1 : err:
948 : 1 : return zxid_simple_idp_show_an(cf, cgi, res_len, auto_flags);
949 : : }
950 : :
951 : : /*() Redirect user to new user creation page. */
952 : :
953 : : /* Called by: zxid_simple_no_ses_cf */
954 : : static char* zxid_simple_idp_new_user(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
955 : 1 : {
956 : : char* p;
957 : : struct zx_str* ss;
958 [ + - - + ]: 1 : D("cf=%p cgi=%p", cf, cgi);
959 : :
960 : : // ***
961 : :
962 [ + - + - ]: 1 : if (cf->new_user_page && cf->new_user_page[0]) {
963 [ - + - + : 1 : ss = zx_strf(cf->ctx, "ar=%s&zxrfr=F%s%s%s%s&zxidpurl=%s",
# # - + -
+ # # ]
964 : : cgi->ssoreq,
965 : : cgi->zxapp && cgi->zxapp[0] ? "&zxapp=" : "", cgi->zxapp ? cgi->zxapp : "",
966 : : cgi->err && cgi->err[0] ? "&err=" : "", cgi->err ? cgi->err : "",
967 : : cf->url);
968 : 1 : p = ss->s;
969 : 1 : ZX_FREE(cf->ctx, ss);
970 [ + - - + ]: 1 : D("new_user_page(%s) redir(%s)", cf->new_user_page, p);
971 : 1 : return zxid_simple_redir_page(cf, cf->new_user_page, p, res_len, auto_flags);
972 : : }
973 : :
974 : 0 : ERR("No new user page URL defined. (IdP config problem, or IdP intentionally does not support online new user creation. See NEW_USER_PAGE config option.) %d", 0);
975 : 0 : cgi->err = "No new user page URL defined. (IdP config problem, or IdP intentionally does not support online new user creation.)";
976 : :
977 : 0 : return zxid_simple_idp_show_an(cf, cgi, res_len, auto_flags);
978 : : }
979 : :
980 : : /*() Redirect user to recover password page. */
981 : :
982 : : /* Called by: zxid_simple_no_ses_cf */
983 : : static char* zxid_simple_idp_recover_password(zxid_conf* cf, zxid_cgi* cgi, int* res_len, int auto_flags)
984 : 1 : {
985 : : char* p;
986 : : struct zx_str* ss;
987 [ + - - + ]: 1 : D("cf=%p cgi=%p", cf, cgi);
988 : :
989 : : // ***
990 : :
991 [ + - + - ]: 1 : if (cf->recover_passwd && cf->recover_passwd[0]) {
992 [ - + - + : 1 : ss = zx_strf(cf->ctx, "ar=%s&zxrfr=F%s%s%s%s&zxidpurl=%s",
# # - + -
+ # # ]
993 : : cgi->ssoreq,
994 : : cgi->zxapp && cgi->zxapp[0] ? "&zxapp=" : "", cgi->zxapp ? cgi->zxapp : "",
995 : : cgi->err && cgi->err[0] ? "&err=" : "", cgi->err ? cgi->err : "",
996 : : cf->url);
997 : 1 : p = ss->s;
998 : 1 : ZX_FREE(cf->ctx, ss);
999 [ + - - + ]: 1 : D("recover_passwd(%s) redir(%s)", cf->recover_passwd, p);
1000 : 1 : return zxid_simple_redir_page(cf, cf->recover_passwd, p, res_len, auto_flags);
1001 : : }
1002 : :
1003 : 0 : ERR("No password recover page URL defined. (IdP config problem, or IdP intentionally does not support online password recovery. See RECOVER_PASSWD config option.) %d", 0);
1004 : 0 : cgi->err = "No password recover page URL defined. (IdP config problem, or IdP intentionally does not support online password recovery.)";
1005 : :
1006 : 0 : return zxid_simple_idp_show_an(cf, cgi, res_len, auto_flags);
1007 : : }
1008 : :
1009 : : /* ===== Main Control Logic for Session Active and Session Inactive Cases ===== */
1010 : :
1011 : : /*() Subroutine of zxid_simple_cf() for the session active case.
1012 : : *
1013 : : * NULL return means the "not logged in" processing is needed, see zxid_simple_no_ses_cf()
1014 : : *
1015 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1016 : :
1017 : : /* Called by: chkuid x2, zxid_simple_cf_ses, zxid_simple_idp_an_ok_do_rest */
1018 : : char* zxid_simple_ses_active_cf(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int* res_len, int auto_flags)
1019 : 16 : {
1020 : : struct zx_str* accr;
1021 : : char* p;
1022 : 16 : char* res = 0;
1023 : : struct zx_str* ss;
1024 : :
1025 [ + - + - : 16 : if (!cf || !cgi || !ses) {
- + ]
1026 : 0 : ERR("FATAL: NULL pointer. You MUST supply configuration(%p), cgi(%p), and session(%p) objects (programming error)", cf, cgi, ses);
1027 [ # # ]: 0 : NEVERNEVER("Bad args %p", cf);
1028 : : }
1029 : :
1030 : : /* OPs (the o= CGI field. Not to be confused with first letter of zxid_simple() return value)
1031 : : * l = local logout (form gl)
1032 : : * r = SLO redir (form gr)
1033 : : * s = SLO soap (form gs)
1034 : : * t = nireg redir (form gt)
1035 : : * u = nireg soap (form gu)
1036 : : * v = Az soap (form gv)
1037 : : * c = CARML for the SP
1038 : : * d = Dump internal data, including config; debug screen
1039 : : * m = Show management screen
1040 : : * n = Just check session (used for checking session for protected content pages)
1041 : : * p = Password Login (IdP form submit alp= with au= and ap=)
1042 : : * P = POST response. HTTP POST in general
1043 : : * Q = POST request
1044 : : * R = POST request to IdP
1045 : : * S = SOAP (POST) request
1046 : : * B = Metadata
1047 : : *
1048 : : * M = CDC redirect and LECP detect
1049 : : * C = CDC reader
1050 : : * E = Normal "Entry" page (e.g. after CDC read)
1051 : : * L = Start SSO (submit of E)
1052 : : * A = Artifact processing
1053 : : * N = New User, during IdP Login (form an)
1054 : : * W = Recover password, during IdP Login (form aw)
1055 : : * D = Delegation / Invitation acceptance user interface, the idp selection
1056 : : * G = Delegation / Invitation finalization after SSO (via RelayState)
1057 : : *
1058 : : * I = used for IdP ???
1059 : : * K = used?
1060 : : * F = IdP: Return SSO A7N after successful An; no ses case, generate IdP ui
1061 : : *
1062 : : * Still available: GHJORTUVWXYZabcdefghijkoqwxyz
1063 : : */
1064 : :
1065 [ - + ]: 16 : if (cgi->enc_hint)
1066 : 0 : cf->nameid_enc = cgi->enc_hint != '0';
1067 [ + - - + ]: 16 : D("op(%c) session(%s) active", cgi->op, cgi->sid);
1068 : : DD("session(%s) active op(%c) saml_req(%s)", cgi->sid, cgi->op, STRNULLCHK(cgi->saml_req));
1069 [ + + - - : 16 : switch (cgi->op) {
- + - + -
- - + - -
- - + + ]
1070 : : case 'l':
1071 [ + - ]: 3 : if (cf->log_level>0)
1072 [ - + # # : 3 : zxlog(cf, 0,0,0,0,0,0, ZX_GET_CONTENT(ses->nameid), "N", "W", "LOCLO", ses->sid,0);
# # ]
1073 : 3 : zxid_del_ses(cf, ses);
1074 : 3 : cgi->msg = "Local logout Ok. Session terminated.";
1075 : 3 : return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1076 : : case 'r':
1077 : 2 : ss = zxid_sp_slo_redir(cf, cgi, ses);
1078 : 2 : zxid_del_ses(cf, ses);
1079 : 2 : goto redir_ok;
1080 : : case 's':
1081 : 0 : zxid_sp_slo_soap(cf, cgi, ses);
1082 : 0 : zxid_del_ses(cf, ses);
1083 : 0 : cgi->msg = "SP Initiated logout (SOAP). Session terminated.";
1084 : 0 : return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1085 : : case 't':
1086 : 0 : ss = zxid_sp_mni_redir(cf, cgi, ses, zx_ref_str(cf->ctx, cgi->newnym));
1087 : 0 : goto redir_ok;
1088 : : case 'u':
1089 : 0 : zxid_sp_mni_soap(cf, cgi, ses, zx_ref_str(cf->ctx, cgi->newnym));
1090 : 0 : cgi->msg = "SP Initiated defederation (SOAP).";
1091 : 0 : break; /* Defederation does not have to mean SLO */
1092 : : case 'v': /* N.B. This is just testing facility. The result is ignored. */
1093 [ - + ]: 1 : zxid_pep_az_soap_pepmap(cf, cgi, ses, cf->pdp_call_url?cf->pdp_call_url:cf->pdp_url, cf->pepmap);
1094 : 1 : cgi->msg = "PEP-to-PDP Authorization call (SOAP).";
1095 : 1 : break; /* Defederation does not have to mean SLO */
1096 : : case 'm':
1097 : 0 : res = zxid_fed_mgmt_cf(cf, res_len, -1, cgi->sid, auto_flags);
1098 [ # # ]: 0 : if (auto_flags & ZXID_AUTO_EXIT)
1099 : 0 : exit(0);
1100 : 0 : return res;
1101 : : case 'P': /* POST Profile Responses */
1102 : : case 'I':
1103 : : case 'K':
1104 : : case 'Q': /* POST Profile Requests */
1105 [ + - + - : 2 : D("saml_req(%s) rs(%s) sigalg(%s) sig(%s)", STRNULLCHK(cgi->saml_req), STRNULLCHK(cgi->rs), STRNULLCHK(cgi->sigalg), STRNULLCHK(cgi->sig));
+ - - + +
- - + ]
1106 : 2 : ss = zxid_sp_dispatch(cf, cgi, ses);
1107 [ - + - - ]: 2 : switch (ss->s[0]) {
1108 : 0 : case 'K': return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1109 : 2 : case 'L': goto redir_ok;
1110 : 0 : case 'I': goto idp;
1111 : : }
1112 [ # # # # ]: 0 : D("Q ss(%.*s) (fall thru)", ss->len, ss->s);
1113 : 0 : break;
1114 : :
1115 : : /* Delegation / Invitation URL clicked. */
1116 : 0 : case 'D': return zxid_ps_accept_invite(cf, cgi, ses, res_len, auto_flags);
1117 : 0 : case 'G': return zxid_ps_finalize_invite(cf, cgi, ses, res_len, auto_flags);
1118 : :
1119 : : case 'R':
1120 : 0 : cgi->op = 'F';
1121 : : /* Fall thru */
1122 : : case 'F': /* IdP: Return SSO A7N after successful An; no ses case, generate IdP ui */
1123 : 5 : idp:
1124 : 5 : ss = zxid_idp_dispatch(cf, cgi, ses, 1); /* N.B. The original request is in cgi->saml_req */
1125 [ - + - ]: 5 : switch (ss->s[0]) {
1126 : 0 : case 'K': return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1127 : : case 'C': /* Content-type: -- i.e. ship page or XML out */
1128 : : case 'L':
1129 : 9 : redir_ok:
1130 [ + + ]: 9 : if (auto_flags & ZXID_AUTO_REDIR) {
1131 : 8 : printf("%.*s", ss->len, ss->s);
1132 : 8 : zx_str_free(cf->ctx, ss);
1133 : 8 : fflush(stdout);
1134 : 8 : goto cgi_exit;
1135 : : } else
1136 : 1 : goto res_zx_str;
1137 : : }
1138 [ # # # # ]: 0 : D("idp err(%.*s) (fall thru)", ss->len, ss->s);
1139 : : /* *** */
1140 : 0 : break;
1141 : 0 : case 'c': return zxid_simple_show_carml(cf, cgi, res_len, auto_flags);
1142 : 0 : case 'd': return zxid_simple_show_conf(cf, cgi, res_len, auto_flags);
1143 : 0 : case 'B': return zxid_simple_show_meta(cf, cgi, res_len, auto_flags);
1144 : 0 : case 'n': break;
1145 : 2 : case 'p': break;
1146 : : default:
1147 [ - + ]: 1 : if (cf->bare_url_entityid)
1148 : 0 : return zxid_simple_show_meta(cf, cgi, res_len, auto_flags);
1149 : : }
1150 [ - + ]: 4 : if (cf->required_authnctx) {
1151 : 0 : zxid_get_ses_sso_a7n(cf, ses);
1152 [ # # # # : 0 : accr = ses->a7n&&ses->a7n->AuthnStatement&&ses->a7n->AuthnStatement->AuthnContext
# # # # #
# # # ]
1153 : : ?ZX_GET_CONTENT(ses->a7n->AuthnStatement->AuthnContext->AuthnContextClassRef):0;
1154 : :
1155 [ # # ]: 0 : if (accr)
1156 [ # # ]: 0 : for (p = cf->required_authnctx[0]; p; ++p)
1157 [ # # # # ]: 0 : if (!memcmp(accr->s, p, accr->len) && !p[accr->len])
1158 : 0 : goto ok;
1159 : :
1160 : : /* *** arrange same session to be used after step-up authentication. */
1161 : :
1162 [ # # # # : 0 : D("Required AuthnCtx not satisfied by (%.*s). Step-up authentication needed.", accr&&accr->len?accr->len:1, accr&&accr->len?accr->s:"-");
# # # # #
# # # ]
1163 : 0 : cgi->msg = "Step-up authentication requested.";
1164 : 0 : return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1165 : 0 : ok:
1166 [ # # # # ]: 0 : D("Required AuthnCtx satisfied(%s)", p);
1167 : : }
1168 : :
1169 : : /* Already successful Single Sign-On case starts here */
1170 : 4 : ses->rs = cgi->rs;
1171 : 4 : return zxid_simple_ab_pep(cf, ses, res_len, auto_flags);
1172 : :
1173 : 8 : cgi_exit:
1174 [ + - ]: 8 : if (auto_flags & ZXID_AUTO_EXIT)
1175 : 8 : exit(0);
1176 : 0 : res = zx_dup_cstr(cf->ctx, "n");
1177 [ # # ]: 0 : if (res_len)
1178 : 0 : *res_len = 1;
1179 : 0 : return res;
1180 : :
1181 : 1 : res_zx_str:
1182 : 1 : res = ss->s;
1183 [ - + ]: 1 : if (res_len)
1184 : 0 : *res_len = ss->len;
1185 : 1 : ZX_FREE(cf->ctx, ss);
1186 : 1 : return res;
1187 : : }
1188 : :
1189 : : /*() Subroutine of zxid_simple_cf() for the no session detected/active case.
1190 : : *
1191 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1192 : :
1193 : : /* Called by: chkuid, zxid_simple_cf_ses */
1194 : : char* zxid_simple_no_ses_cf(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int* res_len, int auto_flags)
1195 : 45 : {
1196 : 45 : char* res = 0;
1197 : : struct zx_str* ss;
1198 : :
1199 [ + - + - : 45 : if (!cf || !cgi || !ses) {
- + ]
1200 : 0 : ERR("FATAL: NULL pointer. You MUST supply configuration(%p), cgi(%p), and session(%p) objects (programming error)", cf, cgi, ses);
1201 : 0 : exit(1);
1202 : : }
1203 : :
1204 [ + + + + : 45 : switch (cgi->op) {
+ + + + +
+ + + + +
+ + ]
1205 : : case 'M': /* Invoke LECP or redirect to CDC reader. */
1206 : 2 : ss = zxid_lecp_check(cf, cgi);
1207 [ + - - + : 2 : D("LECP check: ss(%.*s)", ss?ss->len:1, ss?ss->s:"?");
- + - + ]
1208 [ - + ]: 2 : if (ss) {
1209 [ # # ]: 0 : if (auto_flags & ZXID_AUTO_REDIR) {
1210 : 0 : printf("%.*s", ss->len, ss->s);
1211 : 0 : zx_str_free(cf->ctx, ss);
1212 : 0 : goto cgi_exit;
1213 : : } else
1214 : 0 : goto res_zx_str;
1215 : : } else {
1216 [ + - ]: 2 : if (auto_flags & ZXID_AUTO_REDIR) {
1217 : 2 : printf("Location: %s?o=C" CRLF2, cf->cdc_url);
1218 : 2 : goto cgi_exit;
1219 : : } else {
1220 : 0 : ss = zx_strf(cf->ctx, "Location: %s?o=C" CRLF2, cf->cdc_url);
1221 : 0 : goto res_zx_str;
1222 : : }
1223 : : }
1224 : : case 'C': /* CDC Read: Common Domain Cookie Reader */
1225 : 1 : zxid_cdc_read(cf, cgi);
1226 : 1 : goto cgi_exit;
1227 : : case 'E': /* Return from CDC read, or start here to by-pass CDC read. */
1228 : 5 : ss = zxid_lecp_check(cf, cgi); /* use o=E&fc=1&fn=p to set allow create true */
1229 [ + - - + : 5 : D("LECP check: ss(%.*s)", ss?ss->len:1, ss?ss->s:"?");
- + - + ]
1230 [ - + ]: 5 : if (ss) {
1231 [ # # ]: 0 : if (auto_flags & ZXID_AUTO_REDIR) {
1232 : 0 : printf("%.*s", ss->len, ss->s);
1233 : 0 : zx_str_free(cf->ctx, ss);
1234 : 0 : goto cgi_exit;
1235 : : } else
1236 : 0 : goto res_zx_str;
1237 : : }
1238 [ - + ]: 5 : if (zxid_cdc_check(cf, cgi))
1239 : 0 : return 0;
1240 [ + - - + ]: 5 : D("NOT CDC %d", 0);
1241 : 5 : break;
1242 : : case 'L':
1243 [ + + ]: 6 : if (auto_flags & ZXID_AUTO_REDIR) {
1244 [ + + ]: 3 : if (zxid_start_sso(cf, cgi))
1245 : 2 : goto cgi_exit;
1246 : : } else {
1247 [ + - ]: 3 : if ((ss = zxid_start_sso_location(cf, cgi)))
1248 : 3 : goto res_zx_str;
1249 : : }
1250 : 1 : break;
1251 : : case 'A':
1252 [ + - - + ]: 1 : D("Process artifact(%s) pid=%d", cgi->saml_art, getpid());
1253 [ - - + ]: 1 : switch (zxid_sp_deref_art(cf, cgi, ses)) {
1254 : 0 : case ZXID_REDIR_OK: ERR("*** Odd, redirect on artifact deref. %d", 0); break;
1255 : : case ZXID_SSO_OK:
1256 : 4 : show_protected_content_setcookie:
1257 [ + - - + ]: 4 : D("show_protected_content_setcookie: (%s)", cf->ses_cookie_name);
1258 [ + - + - ]: 4 : if (cf->ses_cookie_name && *cf->ses_cookie_name) {
1259 [ + - - + ]: 4 : ses->setcookie = zx_alloc_sprintf(cf->ctx, 0, "%s=%s; path=/%s",
1260 : : cf->ses_cookie_name, ses->sid,
1261 : : ONE_OF_2(cf->url[4], 's', 'S')?"; secure":"");
1262 : 4 : ses->cookie = zx_alloc_sprintf(cf->ctx, 0, "$Version=1; %s=%s",
1263 : : cf->ses_cookie_name, ses->sid);
1264 : : }
1265 : 4 : ses->rs = cgi->rs;
1266 : 4 : return zxid_simple_ab_pep(cf, ses, res_len, auto_flags);
1267 : : }
1268 : 1 : break;
1269 : : case 'P': /* POST Profile Responses */
1270 : : case 'I':
1271 : : case 'K':
1272 : : case 'Q': /* POST Profile Requests */
1273 : : DD("PRE saml_req(%s) saml_resp(%s) rs(%s) sigalg(%s) sig(%s)", STRNULLCHK(cgi->saml_req), STRNULLCHK(cgi->saml_resp), cgi->rs, cgi->sigalg, cgi->sig);
1274 : 8 : ss = zxid_sp_dispatch(cf, cgi, ses);
1275 [ + - - + ]: 8 : D("POST dispatch_loc(%s)", ss->s);
1276 [ + - - - : 8 : switch (ss->s[0]) {
+ ]
1277 : 4 : case 'O': goto show_protected_content_setcookie;
1278 : 0 : case 'M': return zxid_simple_ab_pep(cf, ses, res_len, auto_flags); /* Mgmt screen case */
1279 : : case 'L': /* Location */
1280 [ # # ]: 0 : if (auto_flags & ZXID_AUTO_REDIR) {
1281 : 0 : printf("%.*s", ss->len, ss->s);
1282 : 0 : zx_str_free(cf->ctx, ss);
1283 : 0 : fflush(stdout);
1284 : 0 : goto cgi_exit;
1285 : : } else
1286 : 0 : goto res_zx_str;
1287 : 0 : case 'I': goto idp;
1288 : : }
1289 [ + - - + ]: 4 : D("Q err (fall thru) %d", 0);
1290 : 4 : break;
1291 : 1 : case 'c': return zxid_simple_show_carml(cf, cgi, res_len, auto_flags);
1292 : 1 : case 'd': return zxid_simple_show_conf(cf, cgi, res_len, auto_flags);
1293 : 4 : case 'B': return zxid_simple_show_meta(cf, cgi, res_len, auto_flags);
1294 : : case 'D': /* Delegation / Inviatation URL clicked. */
1295 : 1 : return zxid_ps_accept_invite(cf, cgi, ses, res_len, auto_flags);
1296 : : case 'R':
1297 : 1 : cgi->op = 'F';
1298 : : /* Fall thru */
1299 : : case 'F':
1300 : 5 : idp: return zxid_simple_idp_show_an(cf, cgi, res_len, auto_flags);
1301 : 2 : case 'p': return zxid_simple_idp_pw_authn(cf, cgi, res_len, auto_flags);
1302 : 1 : case 'N': return zxid_simple_idp_new_user(cf, cgi, res_len, auto_flags);
1303 : 1 : case 'W': return zxid_simple_idp_recover_password(cf, cgi, res_len, auto_flags);
1304 : : default:
1305 [ - + ]: 6 : if (cf->bare_url_entityid)
1306 : 0 : return zxid_simple_show_meta(cf, cgi, res_len, auto_flags);
1307 [ + - - + ]: 6 : D("unknown op(%c)", cgi->op);
1308 : : }
1309 : 17 : return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
1310 : :
1311 : 5 : cgi_exit:
1312 [ + - ]: 5 : if (auto_flags & ZXID_AUTO_EXIT)
1313 : 5 : exit(0);
1314 : 0 : res = zx_dup_cstr(cf->ctx, "n");
1315 [ # # ]: 0 : if (res_len)
1316 : 0 : *res_len = 1;
1317 : 0 : return res;
1318 : :
1319 : 3 : res_zx_str:
1320 : 3 : res = ss->s;
1321 [ - + ]: 3 : if (res_len)
1322 : 0 : *res_len = ss->len;
1323 : 3 : ZX_FREE(cf->ctx, ss);
1324 : 3 : return res;
1325 : : }
1326 : :
1327 : : /*(i) Simple handler that assumes the configuration has already been read in.
1328 : : * The memory for result is grabbed from ZX_ALLOC(), usually malloc(3)
1329 : : * and is "given" away to the caller, i.e. caller must free it. The
1330 : : * return value is LDIF (or JSON or query string, if configured)
1331 : : * of attributes in success case.
1332 : : * res_len, if non-null, will receive the length of the response.
1333 : : *
1334 : : * The major advantage of zxid_simple_cf_ses() is that the session stays
1335 : : * as binary object and does not need to be recreated / reparsed from
1336 : : * filesystem representation. The object can be directly used for PEP
1337 : : * calls (but see inline PEP call enabled by PDPURL) and WSC.
1338 : : *
1339 : : * cf:: Configuration object
1340 : : * qs_len:: Length of the query string. -1 = use strlen()
1341 : : * qs:: Query string (or POST content)
1342 : : * ses:: Session object
1343 : : * res_len:: Result parameter. If non-null, will be set to the length of the returned string
1344 : : * auto_flags:: Automation flags, see zxid-simple.pd for documentation
1345 : : * return:: String representing protocol action or SSO attributes
1346 : : *
1347 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1348 : :
1349 : : /* Called by: zxid_simple_cf */
1350 : : char* zxid_simple_cf_ses(zxid_conf* cf, int qs_len, char* qs, zxid_ses* ses, int* res_len, int auto_flags)
1351 : 196 : {
1352 : : int got, ret;
1353 : : char* remote_addr;
1354 : : char* cont_len;
1355 : 196 : char* buf = 0;
1356 : 196 : char* res = 0;
1357 : : zxid_cgi cgi;
1358 : 196 : ZERO(&cgi, sizeof(cgi));
1359 : :
1360 [ + - - + ]: 196 : if (!cf || !ses) {
1361 : 0 : ERR("NULL pointer. You MUST supply configuration object %p and session object %p (programming error). auto_flags=%x", cf, ses, auto_flags);
1362 : 0 : exit(1);
1363 : : }
1364 : :
1365 : : /*fprintf(stderr, "qs(%s) arg, autoflags=%x\n", qs, auto_flags);*/
1366 [ + - ]: 196 : if (auto_flags & ZXID_AUTO_DEBUG) zxid_set_opt(cf, 1, 1);
1367 [ - + # # ]: 196 : LOCK(cf->mx, "simple ipport");
1368 [ + - ]: 196 : if (!cf->ipport) {
1369 : 196 : remote_addr = getenv("REMOTE_ADDR");
1370 [ + + ]: 196 : if (remote_addr) {
1371 : 166 : ses->ipport = ZX_ALLOC(cf->ctx, strlen(remote_addr) + 6 + 1); /* :12345 */
1372 : 166 : sprintf(ses->ipport, "%s:-", remote_addr);
1373 : 166 : cf->ipport = ses->ipport;
1374 : : }
1375 : : }
1376 [ - + # # ]: 196 : UNLOCK(cf->mx, "simple ipport");
1377 : :
1378 [ + + ]: 196 : if (!qs) {
1379 : 183 : qs = getenv("QUERY_STRING");
1380 [ + - ]: 183 : if (qs) {
1381 [ + - + - : 183 : D("QUERY_STRING(%s) %s", STRNULLCHK(qs), ZXID_REL);
- + ]
1382 : 183 : zxid_parse_cgi(&cgi, qs);
1383 [ + + + + : 192 : if (ONE_OF_3(cgi.op, 'P', 'R', 'S')) {
+ + ]
1384 : 146 : cont_len = getenv("CONTENT_LENGTH");
1385 [ + + ]: 146 : if (cont_len) {
1386 : 143 : sscanf(cont_len, "%d", &got);
1387 [ + - - + ]: 143 : D("o=%c cont_len=%s got=%d rel=%s", cgi.op, cont_len, got, ZXID_REL);
1388 : 143 : buf = ZX_ALLOC(cf->ctx, got);
1389 [ - + ]: 143 : if (!buf) {
1390 : 0 : ERR("out of memory len=%d", got);
1391 : 0 : exit(1);
1392 : : }
1393 [ - + ]: 143 : if (read_all_fd(0, buf, got, &got) == -1) {
1394 : 0 : perror("Trouble reading post content.");
1395 : : } else {
1396 : 143 : buf[got] = 0;
1397 : : DD("POST(%s) got=%d cont_len(%s)", buf, got, cont_len);
1398 : 143 : D_XML_BLOB(cf, "POST", got, buf);
1399 [ + + ]: 143 : if (buf[0] == '<') goto sp_soap; /* No BOM and looks like XML */
1400 [ - + ]: 6 : if (buf[2] == '<') { /* UTF-16 BOM and looks like XML */
1401 : 0 : got-=2; buf+=2;
1402 : 0 : ERR("UTF-16 NOT SUPPORTED %x%x", buf[0], buf[1]);
1403 : 0 : goto sp_soap;
1404 : : }
1405 [ - + ]: 6 : if (buf[3] == '<') { /* UTF-8 BOM and looks XML */
1406 : 0 : got-=3; buf+=3;
1407 : 137 : sp_soap:
1408 : : /* *** TODO: SOAP response should not be sent internally unless there is auto */
1409 : 137 : ret = zxid_sp_soap_parse(cf, &cgi, ses, got, buf);
1410 [ + - - + ]: 137 : D("POST soap parse returned %d (0=fail, 1=ok, 2=redir, 3=sso ok)", ret);
1411 [ - + ]: 137 : if (ret == ZXID_SSO_OK)
1412 : 0 : return zxid_simple_ab_pep(cf, ses, res_len, auto_flags);
1413 [ - + # # ]: 137 : if (auto_flags & ZXID_AUTO_SOAPC || auto_flags & ZXID_AUTO_SOAPH) {
1414 [ + - ]: 137 : if (auto_flags & ZXID_AUTO_EXIT)
1415 : 137 : exit(0);
1416 : 0 : res = zx_dup_cstr(cf->ctx, "n");
1417 [ # # ]: 0 : if (res_len)
1418 : 0 : *res_len = 1;
1419 : 0 : goto done;
1420 : : }
1421 [ # # ]: 0 : res = zx_dup_cstr(cf->ctx, ret ? "n" : "*** SOAP error (enable debug if you want to see why)");
1422 [ # # ]: 0 : if (res_len)
1423 : 0 : *res_len = strlen(res);
1424 : 0 : goto done;
1425 : : }
1426 : 6 : zxid_parse_cgi(&cgi, buf);
1427 : : }
1428 : : } else {
1429 [ + - - + ]: 3 : D("o=%c post, but no CONTENT_LENGTH rel=%s", cgi.op, ZXID_REL);
1430 : : }
1431 : : } else {
1432 [ + - - + ]: 37 : D("o=%c other rel=%s", cgi.op, ZXID_REL);
1433 : : }
1434 : : }
1435 : : } else {
1436 [ + + ]: 13 : if (qs_len == -1)
1437 : 12 : qs_len = strlen(qs);
1438 [ - + ]: 13 : if (qs[qs_len]) { /* *** reads one past end of buffer */
1439 : 0 : ERR("IMPLEMENTATION LIMIT: Query String MUST be nul terminated len=%d", qs_len);
1440 : 0 : exit(1);
1441 : : }
1442 [ + - + - : 13 : D("QUERY_STRING(%s) %s", STRNULLCHK(qs), ZXID_REL);
- + ]
1443 : 13 : zxid_parse_cgi(&cgi, qs);
1444 : : }
1445 [ + + + - ]: 59 : if (!cgi.op && !cf->bare_url_entityid)
1446 : 1 : cgi.op = 'M'; /* By default, if no ses, check CDC and offer SSO */
1447 : :
1448 [ + + + - : 59 : if (!cgi.sid && cf->ses_cookie_name && *cf->ses_cookie_name)
+ - ]
1449 : 52 : zxid_get_sid_from_cookie(cf, &cgi, getenv("HTTP_COOKIE"));
1450 : :
1451 [ + + ]: 59 : if (cgi.sid) {
1452 [ + + ]: 21 : if (!zxid_get_ses(cf, ses, cgi.sid)) {
1453 [ + - - + ]: 7 : D("No session(%s) active op(%c)", cgi.sid, cgi.op);
1454 : : } else
1455 [ + - ]: 14 : if (res = zxid_simple_ses_active_cf(cf, &cgi, ses, res_len, auto_flags))
1456 : 4 : goto done;
1457 : : }
1458 : :
1459 : 45 : ZERO(ses, sizeof(zxid_ses)); /* No session yet! Process login form */
1460 : 45 : res = zxid_simple_no_ses_cf(cf, &cgi, ses, res_len, auto_flags);
1461 : :
1462 : 17 : done:
1463 [ + + ]: 17 : if (buf)
1464 : 1 : ZX_FREE(cf->ctx, buf);
1465 : 17 : return res;
1466 : : }
1467 : :
1468 : : /*() Allocate simple session and then call simple handler. Strings
1469 : : * are length + pointer (no C string nul termination needed).
1470 : : * A wrapper for zxid_simple_cf().
1471 : : *
1472 : : * cf:: Configuration object
1473 : : * qs_len:: Length of the query string. -1 = use strlen()
1474 : : * qs:: Query string (or POST content)
1475 : : * res_len:: Result parameter. If non-null, will be set to the length of the returned string
1476 : : * auto_flags:: Automation flags, see zxid-simple.pd for documentation
1477 : : * return:: String representing protocol action or SSO attributes
1478 : : *
1479 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1480 : :
1481 : : /* Called by: main x3, zxid_simple_len, zxidwspcgi_main */
1482 : : char* zxid_simple_cf(zxid_conf* cf, int qs_len, char* qs, int* res_len, int auto_flags)
1483 : 196 : {
1484 : : zxid_ses ses;
1485 : 196 : ZERO(&ses, sizeof(ses));
1486 : 196 : return zxid_simple_cf_ses(cf, qs_len, qs, &ses, res_len, auto_flags);
1487 : : }
1488 : :
1489 : : /*() Process simple configuration and then call simple handler. Strings
1490 : : * are length + pointer (no C string nul termination needed).
1491 : : * a wrapper for zxid_simple_cf().
1492 : : *
1493 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1494 : :
1495 : : /* Called by: zxid_simple */
1496 : : char* zxid_simple_len(int conf_len, char* conf, int qs_len, char* qs, int* res_len, int auto_flags)
1497 : 183 : {
1498 : : struct zx_ctx ctx;
1499 : : zxid_conf cf;
1500 : 183 : zx_reset_ctx(&ctx);
1501 : 183 : ZERO(&cf, sizeof(cf));
1502 : 183 : cf.ctx = &ctx;
1503 : 183 : zxid_conf_to_cf_len(&cf, conf_len, conf);
1504 : 183 : return zxid_simple_cf(&cf, qs_len, qs, res_len, auto_flags);
1505 : : }
1506 : :
1507 : : /*() Main simple interface. C string nul termination is assumed. Really just
1508 : : * a wrapper for zxid_simple_cf().
1509 : : *
1510 : : * N.B. More complete documentation is available in <<link: zxid-simple.pd>> (*** fixme) */
1511 : :
1512 : : /* Called by: main x4 */
1513 : : char* zxid_simple(char* conf, char* qs, int auto_flags)
1514 : 183 : {
1515 : 183 : return zxid_simple_len(-1, conf, -1, qs, 0, auto_flags);
1516 : : }
1517 : :
1518 : : /* EOF -- zxidsimp.c */
|