1 /* mini_httpd_filter.c  -  Emulate mod_auth_saml for mini_httpd
2  * Copyright (c) 2012-2015 Synergetics SA (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2009-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2008-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing or as licensed below.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: mod_auth_saml.c,v 1.17 2010-01-08 02:10:09 sampo Exp $
11  *
12  * 1.2.2008,  created --Sampo
13  * 22.2.2008, distilled to much more compact version --Sampo
14  * 25.8.2009, add attribute passing and pep call --Sampo
15  * 11.1.2010, refactoring and review --Sampo
16  * 15.7.2010, consider passing to simple layer more data about the request --Sampo
17  * 28.9.2012, changed zx_instance string to "mas", fixed parsing CGI for other page --Sampo
18  * 13.2.2013, added WD option --Sampo
19  * 21.6.2013, added SOAP WSP capability --Sampo
20  * 22.6.2013, created, based on mod_auth_saml.c and zxidwspcgi.c --Sampo
21  * 10.11.2013, many bugs fixed, much improved --Sampo
22  *
23  * See also: zxidwspcgi.c, mod_auth_saml.c
24  */
25 
26 #define _LARGEFILE64_SOURCE   /* So off64_t is found, see: man 3 lseek64 */
27 
28 #include <zx/platform.h>
29 #include <zx/errmac.h>
30 #include <zx/zx.h>
31 #include <zx/zxid.h>
32 #include <zx/zxidpriv.h>
33 #include <zx/zxidconf.h>
34 #include <zx/zxidutil.h>
35 #include <zx/c/zxidvers.h>
36 
37 #include <errno.h>
38 #include <unistd.h>
39 
40 /* declare stuff from mini_httpd.c */
41 void send_error_and_exit(int s, char* title, char* extra_header, char* text);
42 ssize_t conn_read(char* buf, size_t size);
43 ssize_t conn_write(char* buf, size_t size);
44 void add_to_buf(char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len);
45 void add_to_request(char* str, size_t len);
46 void add_headers(int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod);
47 void add_to_response(char* str, size_t len);
48 void send_response(void);
49 extern char* remoteuser;
50 extern char* path;
51 extern char* request;
52 extern size_t request_size, request_len, request_idx;
53 extern size_t content_length;
54 extern int zxid_is_proto;             /* Flag to indicate protocol URL like /protected/saml. */
55 extern int zxid_is_wsp;               /* Flag to trigger WSP response decoration. */
56 extern zxid_ses* zxid_session;
57 
58 /*() Convert session attribute pool into mini_httpd CGI execution environment.
59  *
60  * OUTMAP will be applied to decide which attributes to pass to the environment
61  * and to rename them.
62  *
63  * This is considered internal function to mini_httpd_zxid, called by make_envp() in do_cgi().
64  * You should not call this directly, unless you know what you are doing. */
65 
66 /* Called by:  make_envp */
zxid_pool2env(zxid_conf * cf,zxid_ses * ses,char ** envp,int envn,int max_envn,const char * uri_path,const char * qs)67 int zxid_pool2env(zxid_conf* cf, zxid_ses* ses, char** envp, int envn, int max_envn, const char* uri_path, const char* qs)
68 {
69   char* name;
70   struct zxid_map* map;
71   struct zxid_attr* at;
72   struct zxid_attr* av;
73 
74   for (at = ses->at; at; at = at->n) {
75     DD("HERE name(%s)", at->name);
76     map = zxid_find_map(cf->outmap, at->name);
77     if (map) {
78       if (map->rule == ZXID_MAP_RULE_DEL) {
79 	D("attribute(%s) filtered out by del rule in OUTMAP", at->name);
80 	continue;
81       }
82       at->map_val = zxid_map_val(cf, 0, 0, map, at->name, at->val);
83       if (map->dst && *map->dst && map->src && map->src[0] != '*') {
84 	name = map->dst;
85       } else {
86 	name = at->name;
87       }
88 
89       if (envn >= max_envn) goto enverr;
90       envp[envn++] = zx_alloc_sprintf(cf->ctx, 0, "%s%s=%s",
91 				      cf->mod_saml_attr_prefix, name, at->map_val->s);
92       for (av = at->nv; av; av = av->n) {
93 	/* Multivalued */
94 	av->map_val = zxid_map_val(cf, 0, 0, map, at->name, av->val);
95 	if (envn >= max_envn) goto enverr;
96 	envp[envn++] = zx_alloc_sprintf(cf->ctx, 0, "%s%s=%s",
97 					cf->mod_saml_attr_prefix, name, av->map_val->s);
98       }
99     } else {
100       if ((errmac_debug & ERRMAC_DEBUG_MASK)>1)
101 	D("ATTR(%s)=VAL(%s)", at->name, STRNULLCHKNULL(at->val));
102       else
103 	D("ATTR(%s)=VAL(%.*s)", at->name, at->val?(int)MIN(35,strlen(at->val)):6, at->val?at->val:"(null)");
104 
105       if (envn >= max_envn) goto enverr;
106       envp[envn++] = zx_alloc_sprintf(cf->ctx, 0, "%s%s=%s",
107 				      cf->mod_saml_attr_prefix, at->name, at->val);
108       for (av = at->nv; av; av = av->n) {
109 	/* Multivalued */
110 	av->map_val = zxid_map_val(cf, 0, 0, map, at->name, av->val);
111 	if (envn >= max_envn) goto enverr;
112 	envp[envn++] = zx_alloc_sprintf(cf->ctx, 0, "%s%s=%s",
113 					cf->mod_saml_attr_prefix, at->name, av->val);
114       }
115     }
116     if (!strcmp(at->name, "idpnid") && at->val && at->val[0] != '-') {
117       D("REMOTE_USER(%s)", at->val);
118       remoteuser = at->val;
119     }
120   }
121 
122   D("CGI SSO OK uri(%s)", uri_path);
123   return envn;
124  enverr:
125   ERR("Statically allocated CGI environment array too small. max_envn=%d", max_envn);
126   return envn;
127 }
128 
129 /*() Read POST input
130  *
131  * This is considered internal function to mini_httpd_filter().
132  * It works by accessing certain request related global variables from mini_httpd.
133  * You should not call this directly, unless you know what you are doing. */
134 
135 /* Called by:  zxid_mini_httpd_sso, zxid_mini_httpd_wsp */
zxid_mini_httpd_read_post(zxid_conf * cf)136 static char* zxid_mini_httpd_read_post(zxid_conf* cf)
137 {
138   char* res;
139 
140   for (;;) {
141     char buf[32*1024];
142     int already_read = request_len-request_idx;
143     int len = MIN(sizeof(buf), content_length - already_read);
144     D("Read post already_read=%d/%d buf_siz=%d len=%d", already_read, (int)content_length, (int)sizeof(buf), len);
145     DD("uri(%s)=%p buf=%p request(%.*s)=%p request_size=%d request_len=%d", path, path, buf, (int)request_size, request, request, (int)request_size, (int)request_len);
146     if (!len)
147       break;  /* nothing further to read */
148     len = conn_read(buf, len);
149     if (len < 0 && ONE_OF_2(errno, EINTR, EAGAIN))
150       continue;
151     if (len <= 0)
152       break;
153     DD("uri(%s)=%p buf=%p request(%.*s)=%p request_size=%d request_len=%d", path, path, buf, (int)request_size, request, request, (int)request_size, (int)request_len);
154     add_to_request(buf, len);
155     DD("uri(%s)=%p buf=%p request(%.*s)=%p request_size=%d request_len=%d", path, path, buf, (int)request_size, request, request, (int)request_size, (int)request_len);
156   }
157   res = request + request_idx;
158   if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("POST(%s)", res);
159   return res;
160 }
161 
162 #if 0
163 static void zxid_mini_httpd_metadata_get_special_case(zxid_conf* cf, const char* uri_path)
164 {
165   struct zx_str* ss;
166   char* eid;
167   eid = zxid_my_ent_id_cstr(cf);
168   D("metadata for eid(%s)?", eid);
169   if (!strcmp(uri_path, eid)) {
170     ss = zxid_sp_meta(cf, 0);
171     if (!ss)
172       send_error_and_exit(500, "Internal Server Error", "", "Generating SP metadata failed.");
173 
174     add_headers(200, "OK", "", "", "text/xml; charset=%s", ss->len, (time_t)-1);
175     add_to_response(ss->s, ss->len);
176     send_response();
177     exit(0);  /* This function is called in mini_httpd handle_request() subprocess. */
178   }
179   ZX_FREE(cf->ctx, eid);
180 }
181 #endif
182 
183 /* Called by:  zxid_mini_httpd_filter */
zxid_mini_httpd_wsp(zxid_conf * cf,zxid_ses * ses,const char * method,const char * uri_path,const char * qs)184 static zxid_ses* zxid_mini_httpd_wsp(zxid_conf* cf, zxid_ses* ses, const char* method, const char* uri_path, const char* qs)
185 {
186   char* res;
187 
188   if (*method == 'P') {
189     res = zxid_mini_httpd_read_post(cf);
190     if (zxid_wsp_validate(cf, ses, 0, res)) {
191       D("WSP(%s) request valid", uri_path);
192       /* Essentially we fall through and let CGI processing happen.
193        * zxid_wsp_decorate() will be called in cgi_interpose_output() */
194     } else {
195       INFO("WSP(%s) call not authorized", uri_path);
196       send_error_and_exit(403, "Forbidden", "", "Authorization denied.");
197     }
198   } else {
199     //zxid_mini_httpd_metadata_get_special_case(cf, uri_path);
200     ERR("WSP(%s) must be called with POST method (%s)", uri_path, method);
201     send_error_and_exit(405, "Method Not Allowed", "", "WSP only accepts POST method.");
202   }
203   return ses;
204 }
205 
206 /*() Handle the WSP case of cgi_interpose_output(). Read in entire response,
207  * apply decoration, and send it on its way. */
208 
209 /* Called by:  cgi_interpose_output */
zxid_mini_httpd_wsp_response(zxid_conf * cf,zxid_ses * ses,int rfd,char ** response,size_t * response_size,size_t * response_len,int br_ix)210 void zxid_mini_httpd_wsp_response(zxid_conf* cf, zxid_ses* ses, int rfd, char** response, size_t* response_size, size_t* response_len, int br_ix)
211 {
212   struct zx_str* res;
213 
214   D_INDENT("wsp_resp");
215   D("DECOR START response_size=%d response_len=%d br_ix=%d response(%.*s)", (int)*response_size, (int)*response_len, br_ix, (int)*response_len, *response);
216 
217   /* Read until EOF */
218   for (;;) {
219     char buf[10*1024];
220     int len = read(rfd, buf, sizeof(buf));
221     if (len < 0 && ONE_OF_2(errno, EINTR, EAGAIN)) {
222       sleep(1);
223       continue;
224     }
225     if (len <= 0)
226       break;
227     add_to_buf(response, response_size, response_len, buf, len);
228   }
229 
230   D("DECOR2 response_size=%d response_len=%d br_ix=%d response(%.*s)", (int)*response_size, (int)*response_len, br_ix, (int)*response_len, *response);
231 
232   /* Write the saved headers (and any beginning of payload). */
233   if ((*response)[br_ix] == '\015') ++br_ix;
234   if ((*response)[br_ix] == '\012') ++br_ix;
235   if ((*response)[br_ix] == '\015') ++br_ix;
236   if ((*response)[br_ix] == '\012') ++br_ix;
237 
238   D("DECOR3 response_len=%d br_ix=%d header(%.*s)", (int)*response_len, br_ix, br_ix, *response);
239   (void) conn_write(*response, br_ix);
240 
241   res = zxid_wsp_decorate(cf, ses, 0, *response+br_ix);
242   (void) conn_write(res->s, res->len);
243   D_DEDENT("wsp_resp");
244 }
245 
246 extern char* authorization;
247 
248 /* Called by:  zxid_mini_httpd_filter */
zxid_mini_httpd_uma(zxid_conf * cf,zxid_ses * ses,const char * method,const char * uri_path,const char * qs)249 static zxid_ses* zxid_mini_httpd_uma(zxid_conf* cf, zxid_ses* ses, const char* method, const char* uri_path, const char* qs)
250 {
251   char* res;
252 
253   if (!authorization || memcmp(authorization, "Bearer ", sizeof("Bearer ")-1)) {
254       INFO("UMA(%s) Missing Authorization header", uri_path);
255       send_error_and_exit(401, "Unauthorized", "WWW-Authenticate: UMA realm=\"uma testing\" host_id=\"https://zxidp.org/rs.uma\" as_uri=\"https://zxidp.org/idpuma\"", "Authorization header with UMA Bearer token required");
256   }
257 
258   // *** POST to token interospection endpoint on AS
259   // *** add UMA Resource Server stuff here
260 
261   if (*method == 'P') {
262     res = zxid_mini_httpd_read_post(cf);
263     if (zxid_wsp_validate(cf, ses, 0, res)) {
264       D("WSP(%s) request valid", uri_path);
265       /* Essentially we fall through and let CGI processing happen.
266        * zxid_wsp_decorate() will be called in cgi_interpose_output() */
267     } else {
268       INFO("WSP(%s) call not authorized", uri_path);
269       send_error_and_exit(403, "Forbidden", "", "Authorization denied.");
270     }
271   } else {
272     //zxid_mini_httpd_metadata_get_special_case(cf, uri_path);
273     ERR("WSP(%s) must be called with POST method (%s)", uri_path, method);
274     send_error_and_exit(405, "Method Not Allowed", "", "WSP only accepts POST method.");
275   }
276   return ses;
277 }
278 
279 /* 0x6000 outf QS + JSON = no output on successful sso, the attrubutes are in session
280  * 0x1000 debug
281  * 0x0e00 11 + 10 = Generate all HTML + Mgmt w/headers as string
282  * 0x00a0 10 + 10 = Login w/headers as string + Meta w/headers as string
283  * 0x0008 10 + 00 = SOAP w/headers as string + no auto redir, no exit(2) */
284 #define AUTO_FLAGS 0x6ea8
285 
zxid_mini_httpd_process_zxid_simple_outcome(zxid_conf * cf,zxid_ses * ses,const char * uri_path,const char * cookie_hdr,char * res)286 static zxid_ses* zxid_mini_httpd_process_zxid_simple_outcome(zxid_conf* cf, zxid_ses* ses, const char* uri_path, const char* cookie_hdr, char* res)
287 {
288   int len;
289   char* p;
290   char* mt;
291 
292   if (cookie_hdr && cookie_hdr[0]) {
293     D("Passing previous cookie(%s) to environment", cookie_hdr);
294     zxid_add_attr_to_ses(cf, ses, "cookie", zx_dup_str(cf->ctx, cookie_hdr));
295   }
296   D("res(%s) uri(%s)",res,uri_path);
297   switch (res[0]) {
298   case 'L':
299     if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("REDIR(%s)", res);
300     zxid_session = ses; /* Set the session so that the mini_httpd add_headers() can set cookies */
301     send_error_and_exit(302, "Found", res, "SAML Redirect");
302   case 'C':
303     if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("CONTENT(%s)", res);
304     res += 14;  /* skip "Content-Type:" (14 chars) */
305     DD("RES(%s)", res);
306     p = strchr(res, '\r');
307     *p = 0;
308     mt = res;
309     DD("CONTENT-TYPE(%s)", res);
310     res = p+2 + 16;  /* skip "Content-Length:" (16 chars) */
311     sscanf(res, "%d", &len);
312     res = strchr(res, '\r') + 4; /* skip CRFL pair before body */
313     DD("CONTENT-LENGTH(%d)", len);
314     zxid_session = ses; /* Set the session so that the mini_httpd add_headers() can set cookies */
315     add_headers(200, "OK", "", "", mt?mt:"text/html; charset=%s", len, (time_t)-1);
316     add_to_response(res, len);
317     send_response();
318     exit(0);  /* This function is called in mini_httpd handle_request() subprocess. */
319   case 'z':
320     INFO("User not authorized %d", 0);
321     send_error_and_exit(403, "Forbidden", "", "Authorization denied.");
322   case 0: /* Logged in case */
323     D("SSO OK uri(%s)", uri_path);
324     /*ret = pool2apache(cf, r, ses.at); // will be done in do_cgi() */
325     break;
326 #if 0
327   case 'd': /* Logged in case */
328     if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("SSO OK LDIF(%s)", res);
329     D("SSO OK uri(%s)", uri_path);
330     ret = ldif2apache(cf, r, res);
331     return ret;
332 #endif
333   default:
334     ERR("Unknown zxid_simple response(%s)", res);
335     send_error_and_exit(501, "Internal Server Error", "", "Unknown zxid_simple response." );
336   }
337   return ses;
338 }
339 
zxid_mini_httpd_step_up(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * uri_path,const char * cookie_hdr)340 zxid_ses* zxid_mini_httpd_step_up(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* uri_path, const char* cookie_hdr)
341 {
342   char* res;
343   DD("before uri(%s)=%p", uri_path, uri_path);
344   if (!ses)
345     ses = zxid_alloc_ses(cf);
346   res = zxid_simple_no_ses_cf(cf, cgi, ses, 0, AUTO_FLAGS);
347   DD("after uri(%s)", uri_path);
348   return zxid_mini_httpd_process_zxid_simple_outcome(cf, ses, uri_path, cookie_hdr, res);
349 }
350 
351 /* Called by:  zxid_mini_httpd_filter */
zxid_mini_httpd_sso(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * method,const char * uri_path,const char * qs,const char * cookie_hdr)352 static zxid_ses* zxid_mini_httpd_sso(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* method, const char* uri_path, const char* qs, const char* cookie_hdr)
353 {
354   int uri_len;
355   char* res;
356   //const char* set_cookie_hdr;
357   //const char* cur_auth;
358 
359   uri_len = strlen(uri_path);
360   if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("other page uri(%s) qs(%s) cf->burl(%s) uri_len=%d", uri_path, STRNULLCHKNULL(qs), cf->burl, uri_len);
361   if (qs && qs[0] == 'l') {
362     D("Detect login(%s)", qs);
363   } else
364     cgi->op = 'E';   /* Trigger IdP selection screen */
365 #if 0
366   p = ZX_ALLOC(cf->ctx, uri_len+1+qs_len+1);
367   strcpy(p, uri_path);
368   if (qs_len) {
369     p[uri_len] = '?';
370     strcpy(p+uri_len+1, qs);
371   }
372   D("HERE3 qs_len=%d cgi=%p k(%s) uri(%s) qs(%s) rs(%s)", qs_len, cgi, STRNULLCHKNULL(cgi->skin), uri_path, STRNULLCHKNULL(qs), p);
373   // *** p never used. Should there be cgi->rs =p; ?
374 #endif
375   if (cgi->sid && cgi->sid[0] && zxid_get_ses(cf, ses, cgi->sid)) {
376     res = zxid_simple_ses_active_cf(cf, cgi, ses, 0, AUTO_FLAGS);
377     if (res)
378       return zxid_mini_httpd_process_zxid_simple_outcome(cf, ses, uri_path, cookie_hdr, res);
379   } else {
380     D("No session(%s) active op(%c)", STRNULLCHK(cgi->sid), cgi->op);
381   }
382   D("other page: no_ses uri(%s) templ(%s) tf(%s) k(%s) cgi=%p rs(%s)", uri_path, STRNULLCHKNULL(cgi->templ), STRNULLCHKNULL(cf->idp_sel_templ_file), cgi->skin, cgi, cgi->rs);
383   if (cf->optional_login_pat && zx_match(cf->optional_login_pat, uri_path)) {
384     D("optional_login_pat matches ok %s", cf->optional_login_pat);
385     return ses;
386   }
387   return zxid_mini_httpd_step_up(cf, cgi, ses, uri_path, cookie_hdr);
388 }
389 
390 /*() Special case handling for protocol URLs like /protected/saml (configurable)
391  * This special case is checked before any other processing. Thus the protocol
392  * URL does not have to match SSO_PAT to be effective.
393  * Any exceptional outcome is handled internally and terminates in exit(2),
394  * hence the void return. */
395 
zxid_mini_httpd_check_protocol_url(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * method,const char * uri_path,const char * cookie_hdr)396 static void zxid_mini_httpd_check_protocol_url(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* method, const char* uri_path, const char* cookie_hdr)
397 {
398   int ret, uri_len, url_len;
399   char* local_url;
400   char* p;
401   char* res;
402 
403   uri_len = strlen(uri_path);
404   for (local_url = cf->burl; *local_url && *local_url != ':' && *local_url != '/'; ++local_url);
405   if (local_url[0] == ':' && local_url[1] == '/' && local_url[2] == '/') {
406     for (local_url += 3; *local_url && *local_url != '/'; ++local_url);
407   }
408 
409   url_len = strlen(local_url);
410   for (p = local_url + url_len - 1; p > local_url; --p)
411     if (*p == '?')
412       break;
413   if (p == local_url)
414     p = local_url + url_len;
415   url_len = p-local_url;
416 
417   /* Check if we are supposed to enter zxid due to URL suffix - to
418    * process protocol messages rather than ordinary pages. To do this
419    * correctly we need to ignore the query string part. We are looking
420    * here at an exact match, like /protected/saml, rather than any of
421    * the other documents under /protected/ (which are handled in the
422    * else clause). Both then and else -clause URLs are defined as requiring
423    * SSO by virtue of the web server configuration (SSO_PAT in mini_httpd_zxid). */
424 
425   D("match? uri(%s)=%p cf->burl(%s) qs(%s) rs(%s) op(%c)", uri_path, uri_path, cf->burl, STRNULLCHKNULL(cgi->qs), STRNULLCHKNULL(cgi->rs), cgi->op);
426 
427   if (url_len != uri_len || memcmp(local_url, uri_path, uri_len))
428     return; /* Not an Exact match */
429 
430   if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("matched uri(%s)=%p cf->burl(%s) qs(%s) rs(%s) op(%c)", uri_path, uri_path, cf->burl, STRNULLCHKNULL(cgi->qs), STRNULLCHKNULL(cgi->rs), cgi->op);
431   if (*method == 'P') {
432     res = zxid_mini_httpd_read_post(cf);   /* Will print some debug output */  // ***
433     if (res) {
434       DD("uri(%s)=%p", uri_path, uri_path);
435       if (cgi->op == 'S') {
436 	ret = zxid_sp_soap_parse(cf, cgi, ses, strlen(res), res);
437 	D("POST soap parse returned %d", ret);
438 #if 0
439 	/* *** TODO: SOAP response should not be sent internally unless there is auto */
440 	if (ret == ZXID_SSO_OK) {
441 	  ret = zxid_simple_ab_pep(cf, ses, res_len, auto_flags);
442 	  D_DEDENT("minizx: ");
443 	  return ret;
444 	}
445 	if (auto_flags & ZXID_AUTO_SOAPC || auto_flags & ZXID_AUTO_SOAPH) {
446 	  res = zx_dup_cstr(cf->ctx, "n");
447 	  if (res_len)
448 	    *res_len = 1;
449 	  goto done;
450 	}
451 	res = zx_dup_cstr(cf->ctx, ret ? "n" : "*** SOAP error (enable debug to see why)");
452 	if (res_len)
453 	  *res_len = strlen(res);
454 	goto done;
455 #endif
456       } else {
457 	zxid_parse_cgi(cf, cgi, res);
458 	D("POST CGI parsed. rs(%s)", STRNULLCHKQ(cgi->rs));
459 	DD("uri(%s)=%p", uri_path, uri_path);
460       }
461     }
462   }
463   D("HERE2.1 urls_len=%d local_url(%.*s) url(%s)", url_len, url_len, local_url, cf->burl);
464   if (ONE_OF_2(cgi->op, 'L', 'A')) /* SSO (Login, Artifact) activity overrides current session. */
465     goto step_up;
466   if (!cgi->sid || !zxid_get_ses(cf, ses, cgi->sid)) {
467     D("No session(%s) active op(%c) uri(%s)=%p", STRNULLCHK(cgi->sid), cgi->op, uri_path,uri_path);
468   } else {
469     D("HERE2.2 %d",0);
470     res = zxid_simple_ses_active_cf(cf, cgi, ses, 0, AUTO_FLAGS);
471     if (res) {
472       zxid_mini_httpd_process_zxid_simple_outcome(cf, ses, uri_path, cookie_hdr, res);
473       exit(0);  /* This function is called in mini_httpd handle_request() subprocess. */
474     }
475   }
476   /* not logged in, fall thru to step_up */
477 step_up:
478   zxid_mini_httpd_step_up(cf, cgi, ses, uri_path, cookie_hdr);
479   exit(0);  /* This function is called in mini_httpd handle_request() subprocess. */
480 }
481 
482 /*(-)  Redirect hack: deal with externally imposed ACS url that does not follow zxid convention.
483  * If the hack is active, returns the new qs and via pointer the new uri_path. */
484 
zxid_mini_httpd_check_redirect_hack(zxid_conf * cf,zxid_cgi * cgi,char ** uri_path,const char * qs)485 static char* zxid_mini_httpd_check_redirect_hack(zxid_conf* cf, zxid_cgi* cgi, char** uri_path, const char* qs)
486 {
487   int len, qs_len = qs?strlen(qs):0;
488   char* p;
489   cgi->uri_path = (char*)*uri_path;
490   cgi->qs = (char*)qs;
491 
492   if (cf->redirect_hack_imposed_url && !strcmp(*uri_path, cf->redirect_hack_imposed_url)) {
493     D("mapping(%s) imposed to zxid(%s)", *uri_path, cf->redirect_hack_zxid_url);
494     *uri_path = cf->redirect_hack_zxid_url;
495     cgi->uri_path = *uri_path;
496     if (cf->redirect_hack_zxid_qs && *cf->redirect_hack_zxid_qs) {
497       if (qs_len) {
498 	/* concatenate redirect_hack_zxid_qs with existing qs */
499 	len = strlen(cf->redirect_hack_zxid_qs);
500 	p = ZX_ALLOC(cf->ctx, len+1+qs_len+1);
501 	strcpy(p, cf->redirect_hack_zxid_qs);
502 	p[len] = '&';
503 	strcpy(p+len+1, qs);
504 	cgi->qs = p;
505       } else {
506 	cgi->qs = cf->redirect_hack_zxid_qs;
507       }
508     }
509   }
510   return cgi->qs;
511 }
512 
513 /*() Handle SSO or ID-WSF SSO
514  * Called from mini_httpd handle_request() if zxid is configured.
515  * In many cases entire situation is handled in this function
516  * and exit is called. In successful SSO or WSP call function
517  * may return and the regular mini_httpd processing continues.
518  * In that case docgi() contains further zxid related steps to
519  * pass the SSO attributes to the CGI environment. */
520 
521 /* Called by:  handle_request */
zxid_mini_httpd_filter(zxid_conf * cf,const char * method,const char * uri_path,const char * qs,const char * cookie_hdr)522 zxid_ses* zxid_mini_httpd_filter(zxid_conf* cf, const char* method, const char* uri_path, const char* qs, const char* cookie_hdr)
523 {
524   zxid_ses* ses = zxid_alloc_ses(cf);
525   char buf[256];
526   char* p;
527   zxid_cgi cgi;
528   ZERO(&cgi, sizeof(zxid_cgi));
529 
530   D(CC_GREENY("===== START %s uri(%s) qs(%s) uid=%d pid=%d gid=%d cwd(%s)"), ZXID_REL, uri_path, STRNULLCHKNULL(qs), getpid(), geteuid(), getegid(), getcwd(buf,sizeof(buf)));
531   if (cf->wd && *cf->wd)
532     chdir(cf->wd);
533 
534   qs = zxid_mini_httpd_check_redirect_hack(cf, &cgi, (char**)&uri_path, qs);
535   if (qs && *qs) {
536     /* leak the dup str: the cgi structure will take references to this and change &s to nuls */
537     p = zx_dup_cstr(cf->ctx, qs);
538     zxid_parse_cgi(cf, &cgi, p);
539   }
540 
541   /* Probe for Session ID in cookie. */
542 
543   if (cf->ses_cookie_name && *cf->ses_cookie_name) {
544     if (cookie_hdr) {
545       D("found cookie(%s) 3", STRNULLCHK(cookie_hdr));
546       zxid_get_sid_from_cookie(cf, &cgi, cookie_hdr);
547     }
548   }
549 
550   zxid_mini_httpd_check_protocol_url(cf, &cgi, ses, method, uri_path, cookie_hdr);
551 
552   zxid_is_wsp = 0;
553   if (zx_match(cf->wsp_pat, uri_path)) {
554     zxid_is_wsp = 1;
555     ses = zxid_mini_httpd_wsp(cf, ses, method, uri_path, qs);
556     return ses;
557   } else if (zx_match(cf->uma_pat, uri_path)) {
558     zxid_is_wsp = 1;
559     ses = zxid_mini_httpd_uma(cf, ses, method, uri_path, qs);
560     return ses;
561   } else if (zx_match(cf->sso_pat, uri_path)) {
562     ses = zxid_mini_httpd_sso(cf, &cgi, ses, method, uri_path, qs, cookie_hdr);
563     return ses;
564   } else {
565     D("No SSO or WSP match(%s) wsp_pat(%s) uma_pat(%s) sso_pat(%s)", uri_path, STRNULLCHK(cf->wsp_pat), STRNULLCHK(cf->uma_pat), STRNULLCHK(cf->sso_pat));
566     return 0;
567   }
568 }
569 
570 /* EOF - mini_httpd_filter.c */
571