1 /* mod_auth_saml.c - Handwritten functions for Apache mod_auth_saml module
2 * Copyright (c) 2012-2015 Synergetics NV (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 * 17.11.2013, move redir_to_content feature to zxid_simple() --Sampo
21 * 8.2.2014, added OPTIONAL_LOGIN_PAT feature --Sampo
22 * 5.3.2015, improved Apache httpd-2.4 compatibility --Sampo
23 * 9.3.2015, refactored to isolate httpd version dependencies to httpdglue.c --Sampo
24 * 20151218, added special placeholder user "-anon-" for the 2.4 optional_login_pat case --Sampo
25 *
26 * To configure this module add to httpd.conf something like
27 *
28 * LoadModule auth_saml_module modules/mod_auth_saml.so
29 * <Location /secret>
30 * Require valid-user
31 * AuthType "saml"
32 * ZXIDConf "URL=https://sp1.zxidsp.org:8443/secret/saml"
33 * </Location>
34 *
35 * http://httpd.apache.org/docs/2.2/developer/
36 * http://modules.apache.org/doc/API.html
37 *
38 * Apache 2.4 Quirks
39 *
40 * [Mon Apr 13 23:04:07.291360 2015] [core:error] [pid 4841:tid 139900761208576] [client 127.0.0.1:60629] AH00027: No authentication done but request not allowed without authentication for /protected/index.html. Authentication not configured?
41 *
42 * See: httpd-2.4.12/server/request.c lines 250 and 287 (basically r->user needs to be set)
43 */
44
45 #define _LARGEFILE64_SOURCE /* So off64_t is found, see: man 3 lseek64 */
46
47 #include <zx/platform.h>
48 #include <zx/errmac.h>
49 #include <zx/zxid.h>
50 #include <zx/zxidpriv.h>
51 #include <zx/zxidconf.h>
52 #include <zx/zxidutil.h>
53 #include <zx/c/zxidvers.h>
54
55 #ifdef MINGW
56 /* apr.h defines these */
57 #undef uid_t
58 #undef gid_t
59 #endif
60
61 #include "ap_config.h"
62 #include "ap_compat.h"
63 #include "apr_strings.h"
64 #include "httpd.h" /* request_rec et al. */
65 #include "http_config.h"
66 #include "http_core.h"
67 #include "http_log.h"
68 #include "http_protocol.h"
69 #include "http_request.h" /* accessor methods for request_rec */
70
71 #include "HRR.h" /* httpd glue */
72
73 /*#define srv_cf(s) (struct zxid_srv_cf*)ap_get_module_config((s)->module_config, &auth_saml_module)*/
74 #define dir_cf(r) (zxid_conf*)ap_get_module_config(HRR_per_dir_config(r), &auth_saml_module)
75
76 /* This extern variable is used as first argument to LoadModule in httpd.conf,
77 * E.g: LoadModule auth_saml_module modules/mod_auth_saml.so */
78
79 extern module AP_MODULE_DECLARE_DATA auth_saml_module;
80
81 #if 0
82 /*(-) This function is run when each child process of apache starts. It does
83 * initializations that do not survive fork(2). */
84 /* Called by: */
85 static void chldinit(apr_pool_t* p, server_rec* s)
86 {
87 CURLcode res;
88 D("server_rec=%p", m, s);
89 res = curl_global_init(CURL_GLOBAL_ALL); /* vs. _SSL. Also OpenSSL vs. gnuTLS. */
90 if(res != CURLE_OK) {
91 ERR("Failed to initialize curl library: %u", res);
92 }
93 }
94 #endif
95
96 /*(-) Set cookies apache style. Internal. */
97
set_cookies(zxid_conf * cf,request_rec * r,const char * setcookie,const char * setptmcookie)98 static void set_cookies(zxid_conf* cf, request_rec* r, const char* setcookie, const char* setptmcookie)
99 {
100 if (setcookie && setcookie[0] && setcookie[0] != '-') {
101 /* http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-19.html */
102 D("Set-Cookie(%s)", setcookie);
103 apr_table_addn(HRR_headers_out(r), "Set-Cookie", setcookie);
104 apr_table_addn(HRR_err_headers_out(r), "Set-Cookie", setcookie); /* Only way to get redir to set header */
105 apr_table_addn(HRR_headers_in(r), "Set-Cookie", setcookie); /* So subrequest can pick them up! */
106 }
107 if (setptmcookie && setptmcookie[0] && setptmcookie[0] != '-') {
108 /* http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-19.html */
109 D("PTM Set-Cookie(%s)", setptmcookie);
110 apr_table_addn(HRR_headers_out(r), "Set-Cookie", setptmcookie);
111 apr_table_addn(HRR_err_headers_out(r), "Set-Cookie", setptmcookie); /* Only way to get redir to set header */
112 apr_table_addn(HRR_headers_in(r), "Set-Cookie", setptmcookie); /* So subrequest can pick them up! */
113 }
114 }
115
116 /* ------------------------ Run time action -------------------------- */
117
118 /*() Convert session attribute pool into Apache execution environment
119 * that will be passed to CGI, mod_php, mod_perl, and other Apache modules.
120 *
121 * OUTMAP will be applied to decide which attributes to pass to the environment
122 * and to rename them.
123 *
124 * This is considered internal function to mod_auth_saml, called by chkuid().
125 * You should not call this directly, unless you know what you are doing.
126 *
127 * return:: Apache error code, typically OK, which allows Apache continue
128 * processing the request. */
129
130 /* Called by: chkuid x2 */
pool2apache(zxid_conf * cf,request_rec * r,struct zxid_attr * pool)131 static int pool2apache(zxid_conf* cf, request_rec* r, struct zxid_attr* pool)
132 {
133 int ret = OK;
134 char* name;
135 //char* rs = 0;
136 //char* rs_qs;
137 char* setcookie = 0;
138 char* setptmcookie = 0;
139 char* cookie = 0;
140 char* idpnid = 0;
141 struct zxid_map* map;
142 struct zxid_attr* at;
143 struct zxid_attr* av;
144 void* r_pool = HRR_pool(r);
145 void* sbe = HRR_subprocess_env(r);
146
147 /* Length computation pass */
148
149 for (at = pool; at; at = at->n) {
150 DD("HERE name(%s)", at->name);
151 map = zxid_find_map(cf->outmap, at->name);
152 if (map) {
153 if (map->rule == ZXID_MAP_RULE_DEL) {
154 D("attribute(%s) filtered out by del rule in OUTMAP", at->name);
155 continue;
156 }
157 at->map_val = zxid_map_val(cf, 0, 0, map, at->name, at->val);
158 if (map->dst && *map->dst && map->src && map->src[0] != '*') {
159 name = map->dst;
160 } else {
161 name = at->name;
162 }
163
164 name = apr_psprintf(r_pool, "%s%s", cf->mod_saml_attr_prefix, name);
165 apr_table_set(sbe, name, at->val);
166 for (av = at->nv; av; av = av->n) {
167 av->map_val = zxid_map_val(cf, 0, 0, map, at->name, av->val);
168 apr_table_set(sbe, name, av->map_val->s);
169 }
170 } else {
171 if ((errmac_debug & ERRMAC_DEBUG_MASK)>1)
172 D("ATTR(%s)=VAL(%s)", at->name, STRNULLCHKNULL(at->val));
173 else
174 D("ATTR(%s)=VAL(%.*s)", at->name, at->val?(int)MIN(35,strlen(at->val)):6, at->val?at->val:"(null)");
175 /* *** handling of multivalued attributes (right now only last is preserved) */
176 name = apr_psprintf(r_pool, "%s%s", cf->mod_saml_attr_prefix, at->name);
177 apr_table_set(sbe, name, at->val);
178 for (av = at->nv; av; av = av->n)
179 apr_table_set(sbe, name, av->val);
180 }
181 if (!strcmp(at->name, "idpnid")) idpnid = at->val; /* Capture special */
182 else if (!strcmp(at->name, "setcookie")) setcookie = at->val;
183 else if (!strcmp(at->name, "setptmcookie")) setptmcookie = at->val;
184 else if (!strcmp(at->name, "cookie")) cookie = at->val;
185 //else if (!strcmp(at->name, "rs")) rs = at->val;
186 }
187 #if 0
188 /* This code moved to zxidsimp.c: zxid_show_protected_content_setcookie() */
189 if (rs && rs[0] && rs[0] != '-') {
190 /* N.B. RelayState was set by chkuid() "some other page" section by setting cgi.rs
191 * to deflated and safe base64 encoded value which was then sent to IdP as RelayState.
192 * It then came back from IdP and was decoded as one of the SSO attributes.
193 * The decoding is controlled by <<tt: rsrc$rs$unsb64-inf$$ >> rule in OUTMAP. */
194 rs = zxid_unbase64_inflate(cf->ctx, -2, rs, 0);
195 if (!rs) {
196 ERR("Bad relaystate. Error in inflate. %d", 0);
197 return HTTP_BAD_REQUEST;
198 }
199 rs_qs = strchr(rs, '?');
200 if (rs_qs
201 ?(memcmp(HRR_uri(r), rs, rs_qs-rs)||strcmp(HRR_args(r)?HRR_args(r):"",rs_qs+1))
202 :strcmp(HRR_uri(r), rs)) { /* Different, need external or internal redirect */
203 D("redirect(%s) redir_to_content=%d", rs, cf->redir_to_content);
204 //r->uri = apr_pstrdup(r->pool, val);
205 if (cf->redir_to_content) {
206 apr_table_setn(HRR_headers_out(r), "Location", rs);
207 ret = HTTP_SEE_OTHER;
208 } else {
209 /* *** any attributes after this point may not appear in subrequest */
210 ap_internal_redirect_handler(rs, r);
211 }
212 }
213 }
214 #endif
215
216 set_cookies(cf, r, setcookie, setptmcookie);
217 if (cookie && cookie[0] != '-') {
218 D("Cookie(%s) 2", cookie);
219 apr_table_addn(HRR_headers_in(r), "Cookie", cookie); /* so internal redirect sees it */
220 }
221 if (idpnid && idpnid[0] != '-') {
222 D("REMOTE_USER(%s)", idpnid);
223 apr_table_set(sbe, "REMOTE_USER", idpnid);
224 HRR_set_user(r, idpnid); /* httpd-2.4 anz framework requires this, 2.2 does not care */
225 }
226
227 //apr_table_setn(r->subprocess_env,
228 // apr_psprintf(r->pool, "%sLDIF", cf->mod_saml_attr_prefix), ldif);
229 D("SSO OK ret(%d) uri(%s) filename(%s) path_info(%s) user(%s)=%p", ret, (char*)HRR_uri(r), (char*)HRR_filename(r), (char*)HRR_path_info(r), STRNULLCHKD((char*)HRR_user(r)), HRR_user(r));
230 return ret;
231 }
232
233 /*() Send Apache response.
234 *
235 * This is considered internal function to mod_auth_saml, called by chkuid().
236 * You should not call this directly, unless you know what you are doing. */
237
238 /* Called by: chkuid */
send_res(zxid_conf * cf,request_rec * r,char * res)239 static int send_res(zxid_conf* cf, request_rec* r, char* res)
240 {
241 int len;
242 char* p;
243 #if 0
244 apr_table_setn(HRR_headers_out(r), "Cache-Control", "no-cache");
245 apr_table_setn(HRR_err_headers_out(r), "Cache-Control", "no-cache");
246 apr_table_setn(HRR_headers_out(r), "Pragma", "no-cache");
247 apr_table_setn(HRR_err_headers_out(r), "Pragma", "no-cache");
248 #endif
249 res += 14; /* skip "Content-Type:" (14 chars) */
250 DD("RES(%s)", res);
251 p = strchr(res, '\r');
252 *p = 0;
253 //apr_table_setn(HRR_headers_out(r), "Content-Type", res);
254 DD("CONTENT-TYPE(%s)", res);
255 ap_set_content_type(r, res);
256 res = p+2 + 16; /* skip "Content-Length:" (16 chars) */
257 sscanf(res, "%d", &len);
258 res = strchr(res, '\r') + 4; /* skip CRFL pair before body */
259 DD("CONTENT-LENGTH(%d)", len);
260 ap_set_content_length(r, len);
261
262 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("LEN(%d) strlen(%d) RES(%s)", len, (int)strlen(res), res);
263
264 //register_timeout("send", r);
265 ap_send_http_header(r);
266 if (!HRR_header_only(r))
267 ap_rprintf(r, "%s", res); //send_fd(f, r); rprintf(); ap_rwrite()
268 return DONE; /* Prevent further hooks from processing the request. */
269 }
270
271 /*(-) Read POST input, Apache style
272 *
273 * This is considered internal function to mod_auth_saml, called by chkuid().
274 * You should not call this directly, unless you know what you are doing. */
275
276 /* Called by: chkuid x2 */
read_post(zxid_conf * cf,request_rec * r)277 static char* read_post(zxid_conf* cf, request_rec* r)
278 {
279 int len, ret;
280 char* res;
281 char* p;
282 /*len = apr_table_get(r->headers_in, "Content-Length");*/
283
284 /* Ask Apache to dechunk data if it is chunked. */
285 ret = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
286 if (ret != OK) {
287 ERR("ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK): %d", ret);
288 return 0;
289 }
290
291 /* This function will send a 100 Continue response if the client is
292 * waiting for that. If the client isn't going to send data, then this
293 * function will return 0. */
294 if (!ap_should_client_block(r)) {
295 len = 0;
296 } else {
297 len = HRR_remaining(r);
298 }
299 res = p = apr_palloc(HRR_pool(r), len + 1);
300 res[len] = 0;
301 D("remaining=%d", len);
302
303 while (len > 0) {
304 /* Read data from the client. Returns 0 on EOF or error, the
305 * number of bytes otherwise. */
306 ret = ap_get_client_block(r, p, len);
307 if (!ret) {
308 ERR("Failed to read POST data from client. len=%d",len);
309 return 0; /* HTTP_INTERNAL_SERVER_ERROR */
310 }
311
312 p += ret;
313 len -= ret;
314 }
315 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("POST(%s)", res);
316 return res;
317 }
318
319 /* 0x6000 outf QS + JSON = no output on successful sso, the attrubutes are in session
320 * 0x1000 debug
321 * 0x0e00 11 + 10 = Generate all HTML + Mgmt w/headers as string
322 * 0x00a0 10 + 10 = Login w/headers as string + Meta w/headers as string
323 * 0x0008 10 + 00 = SOAP w/headers as string + no auto redir, no exit(2) */
324 #define AUTO_FLAGS 0x6ea8
325
326 /*() Apache hook. Internal function of mod_auth_saml. Do not try to call.
327 *
328 * Called from httpd-2.2.8/server/request.c: ap_process_request_internal()
329 * ap_run_check_user_id(). Return value is processed in modules/http/http_request.c
330 * and redirect is in ap_die(), http_protocol.c: ap_send_error_response()
331 *
332 * It seems this function will in effect be called twice by Apache internals: once
333 * to see if it would succeed and second time to actually do the work. This is rather
334 * wasteful, but we do not know any easy way to avoid it.
335 *
336 * Originally this was just for SSO, but nowdays we also support WSP mode. */
337
338 /* Called by: */
chkuid(request_rec * r)339 static int chkuid(request_rec* r)
340 {
341 int ret, len, uri_len, url_len, args_len;
342 char* cp;
343 char* res;
344 char buf[256];
345 const char* cookie_hdr=0;
346 const char* set_cookie_hdr;
347 const char* cur_auth;
348 request_rec* main_req;
349 char* uri = r?HRR_uri(r):0;
350 zxid_conf* cf = dir_cf(r);
351 zxid_cgi cgi;
352 zxid_ses ses;
353 ZERO(&cgi, sizeof(zxid_cgi));
354 ZERO(&ses, sizeof(zxid_ses));
355 cgi.uri_path = uri;
356 cgi.qs = r?HRR_args(r):0;
357
358 //D("request_rec sizeof=%d offset(r->uri)=%d offset(r->user)=%d", sizeof(request_rec), (void*)(uri)-(void*)r, (void*)(&(r->user))-(void*)r);
359 D("===== START %s req=%p uri(%s) args(%s) pid=%d cwd(%s)", ZXID_REL, r, r?STRNULLCHKNULL(uri):"(r null)", r?STRNULLCHKNULL(HRR_args(r)):"(r null)", getpid(), getcwd(buf,sizeof(buf)));
360 if (cf->wd && *cf->wd)
361 chdir(cf->wd); /* Ensure the working dir is not / (sometimes Apache httpd changes dir) */
362 D_INDENT("chkuid: ");
363
364 if (main_req = HRR_main(r)) { /* subreq can't come from net: always auth OK. */
365 D("sub ok user(%s)=%p", STRNULLCHKD((char*)HRR_user(r)), HRR_user(r));
366 HRR_set_user(r, HRR_user(main_req));
367 D("sub from main user(%s)=%p", STRNULLCHKD((char*)HRR_user(r)), HRR_user(r));
368 D_DEDENT("chkuid: ");
369 return OK;
370 }
371
372 cur_auth = ap_auth_type(r); /* From directive: AuthType "saml" */
373 if (!cur_auth || strcasecmp(cur_auth, "saml")) {
374 D("not saml auth (%s) %d", STRNULLCHKD(cur_auth), DECLINED);
375 D_DEDENT("chkuid: ");
376 return DECLINED;
377 }
378 //r->ap_auth_type = "saml"; *** This is already verified to be the case?!?
379
380 /* Probe for Session ID in cookie. Also propagate the cookie to subrequests. */
381
382 if (cf->ses_cookie_name && *cf->ses_cookie_name) {
383 cookie_hdr = apr_table_get(HRR_headers_in(r), "Cookie");
384 if (cookie_hdr) {
385 D("found cookie(%s) 3", STRNULLCHK(cookie_hdr));
386 zxid_get_sid_from_cookie(cf, &cgi, cookie_hdr);
387 apr_table_addn(HRR_headers_out(r), "Cookie", cookie_hdr); /* Pass cookies to subreq */
388 DD("found cookie(%s) 5", STRNULLCHK(cookie_hdr));
389 /* Kludge to get subrequest to set-cookie, i.e. on return path */
390 set_cookie_hdr = apr_table_get(HRR_headers_in(r), "Set-Cookie");
391 if (set_cookie_hdr) {
392 D("subrequest set-cookie(%s) 2", set_cookie_hdr);
393 apr_table_addn(HRR_headers_out(r), "Set-Cookie", set_cookie_hdr);
394 }
395 }
396 }
397
398 /* Redirect hack: deal with externally imposed ACS url that does not follow zxid convention. */
399
400 args_len = HRR_args(r)?strlen(HRR_args(r)):0;
401 if (cf->redirect_hack_imposed_url && !strcmp(uri, cf->redirect_hack_imposed_url)) {
402 D("Redirect hack: mapping(%s) imposed to zxid(%s)", uri, cf->redirect_hack_zxid_url);
403 HRR_set_uri(r, cf->redirect_hack_zxid_url);
404 uri = cf->redirect_hack_zxid_url;
405 if (cf->redirect_hack_zxid_qs && *cf->redirect_hack_zxid_qs) {
406 if (args_len) {
407 /* concatenate redirect_hack_zxid_qs with existing qs */
408 len = strlen(cf->redirect_hack_zxid_qs);
409 cp = apr_palloc(HRR_pool(r), len+1+args_len+1);
410 strcpy(cp, cf->redirect_hack_zxid_qs);
411 cp[len] = '&';
412 strcpy(cp+len+1, HRR_args(r));
413 cgi.qs = cp;
414 HRR_set_args(r, cp);
415 } else {
416 cgi.qs = cf->redirect_hack_zxid_qs;
417 HRR_set_args(r, cgi.qs);
418 }
419 args_len = strlen(HRR_args(r));
420 }
421 D("After hack uri(%s) args(%s)", STRNULLCHK(uri), STRNULLCHK(HRR_args(r)));
422 }
423
424 DD("HERE1 args_len=%d cgi=%p k(%s) args(%s)", args_len, &cgi, STRNULLCHKNULL(cgi.skin), STRNULLCHKNULL(HRR_args(r)));
425 if (args_len) {
426 /* leak the dup str: the cgi structure will take references to this and change &s to nuls */
427 cp = apr_palloc(HRR_pool(r), args_len + 1);
428 strcpy(cp, HRR_args(r));
429 zxid_parse_cgi(cf, &cgi, cp);
430 DD("HERE2 args_len=%d cgi=%p k(%s) args(%s)", args_len, &cgi, STRNULLCHKNULL(cgi.skin), STRNULLCHKNULL(HRR_args(r)));
431 }
432 /* Check if we are supposed to enter zxid due to URL suffix - to
433 * process protocol messages rather than ordinary pages. To do this
434 * correctly we need to ignore the query string part. We are looking
435 * here at exact match, like /protected/saml, rather than any of
436 * the other documents under /protected/ (which are handled in the
437 * else clause). Both then and else -clause URLs are defined as requiring
438 * SSO by virtue of the web server configuration. */
439
440 uri_len = strlen(uri);
441 url_len = strlen(cf->burl);
442 for (cp = cf->burl + url_len - 1; cp > cf->burl; --cp)
443 if (*cp == '?')
444 break;
445 if (cp == cf->burl)
446 cp = cf->burl + url_len;
447
448 if (url_len >= uri_len && !memcmp(cp - uri_len, uri, uri_len)) { /* Suffix match */
449 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("matched uri(%s) cf->burl(%s) qs(%s) rs(%s) op(%c)", uri, cf->burl, STRNULLCHKNULL(HRR_args(r)), STRNULLCHKNULL(cgi.rs), cgi.op);
450 if (HRR_method_number(r) == M_POST) {
451 res = read_post(cf, r); /* Will print some debug output */
452 if (res) {
453 if (cgi.op == 'S') {
454 ret = zxid_sp_soap_parse(cf, &cgi, &ses, strlen(res), res);
455 D("POST soap parse returned %d", ret);
456 #if 0
457 /* *** TODO: SOAP response should not be sent internally unless there is auto */
458 if (ret == ZXID_SSO_OK) {
459 ret = zxid_simple_ab_pep(cf, &ses, res_len, auto_flags);
460 D_DEDENT("chkuid: ");
461 return ret;
462 }
463 if (auto_flags & ZXID_AUTO_SOAPC || auto_flags & ZXID_AUTO_SOAPH) {
464 res = zx_dup_cstr(cf->ctx, "n");
465 if (res_len)
466 *res_len = 1;
467 goto done;
468 }
469 res = zx_dup_cstr(cf->ctx, ret ? "n" : "*** SOAP error (enable debug to see why)");
470 if (res_len)
471 *res_len = strlen(res);
472 goto done;
473 #endif
474 } else {
475 zxid_parse_cgi(cf, &cgi, res);
476 D("POST CGI parsed. rs(%s)", STRNULLCHKQ(cgi.rs));
477 }
478 }
479 }
480 if (ONE_OF_2(cgi.op, 'L', 'A')) /* SSO (Login, Artifact) activity overrides current session. */
481 goto step_up;
482 if (!cgi.sid || !zxid_get_ses(cf, &ses, cgi.sid)) {
483 D("No session(%s) active op(%c)", STRNULLCHK(cgi.sid), cgi.op);
484 } else {
485 res = zxid_simple_ses_active_cf(cf, &cgi, &ses, 0, AUTO_FLAGS);
486 if (res)
487 goto process_zxid_simple_outcome;
488 }
489 /* not logged in, fall thru */
490 } else if (zx_match(cf->wsp_pat, uri)) {
491 /* WSP case */
492 if (HRR_method_number(r) == M_POST) {
493 res = read_post(cf, r); /* Will print some debug output */
494 if (zxid_wsp_validate(cf, &ses, 0, res)) {
495 D("WSP(%s) request valid", uri);
496 D("WSP CALL uri(%s) filename(%s) path_info(%s)", uri, (char*)HRR_filename(r), (char*)HRR_path_info(r));
497 ret = pool2apache(cf, r, ses.at);
498 D_DEDENT("chkuid: ");
499 return ret;
500 /* Essentially we fall through and let CGI processing happen.
501 * *** how to decorate the CGI return value?!? New hook needed? --Sampo */
502 } else {
503 ERR("WSP(%s) request validation failed", uri);
504 D_DEDENT("chkuid: ");
505 return HTTP_FORBIDDEN;
506 }
507 } else {
508 ERR("WSP(%s) must be called with POST method %d", uri, HRR_method_number(r));
509 D_DEDENT("chkuid: ");
510 return HTTP_METHOD_NOT_ALLOWED;
511 }
512 } else if (zx_match(cf->uma_pat, uri)) {
513 /* UMA case */
514 if (HRR_method_number(r) == M_POST) {
515 res = read_post(cf, r); /* Will print some debug output */
516 #if 0
517 // *** add UMA Resource Server stuff here
518 if (zxid_wsp_validate(cf, &ses, 0, res)) {
519 D("WSP(%s) request valid", uri);
520 D("WSP CALL uri(%s) filename(%s) path_info(%s)", uri, HRR_filename(r), HRR_path_info(r));
521 ret = pool2apache(cf, r, ses.at);
522 D_DEDENT("chkuid: ");
523 return ret;
524 /* Essentially we fall through and let CGI processing happen.
525 * *** how to decorate the CGI return value?!? New hook needed? --Sampo */
526 } else {
527 ERR("WSP(%s) request validation failed", uri);
528 D_DEDENT("chkuid: ");
529 return HTTP_FORBIDDEN;
530 }
531 #endif
532 } else {
533 ERR("WSP(%s) must be called with POST method %d", uri, HRR_method_number(r));
534 D_DEDENT("chkuid: ");
535 return HTTP_METHOD_NOT_ALLOWED;
536 }
537 } else {
538 /* Some other page. Just check for session. */
539 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("other page uri(%s) qs(%s) cf->burl(%s) uri_len=%d url_len=%d", uri, STRNULLCHKNULL(HRR_args(r)), cf->burl, uri_len, url_len);
540 if (cgi.sid && cgi.sid[0] && zxid_get_ses(cf, &ses, cgi.sid)) {
541 res = zxid_simple_ses_active_cf(cf, &cgi, &ses, 0, AUTO_FLAGS);
542 if (res)
543 goto process_zxid_simple_outcome;
544 } else {
545 D("No active session(%s) op(%c)", STRNULLCHK(cgi.sid), cgi.op?cgi.op:'-');
546 if (cf->optional_login_pat && zx_match(cf->optional_login_pat, uri)) {
547 D("optional_login_pat matches %d", OK);
548 HRR_set_user(r, "-anon-"); /* httpd-2.4 anz framework requires this, 2.2 does not care */
549 D_DEDENT("chkuid: ");
550 return OK;
551 }
552 }
553 if (HRR_args(r) && ((char*)HRR_args(r))[0] == 'l') {
554 D("Detect login(%s)", (char*)HRR_args(r));
555 } else
556 cgi.op = 'E'; /* Trigger IdP selection screen */
557 D("other page: no_ses uri(%s) templ(%s) tf(%s) k(%s)", uri, STRNULLCHKNULL(cgi.templ), STRNULLCHKNULL(cf->idp_sel_templ_file), STRNULLCHKNULL(cgi.skin));
558 }
559 step_up:
560 res = zxid_simple_no_ses_cf(cf, &cgi, &ses, 0, AUTO_FLAGS);
561
562 process_zxid_simple_outcome:
563 if (cookie_hdr && cookie_hdr[0]) {
564 D("Passing previous cookie(%s) to environment", cookie_hdr);
565 zxid_add_attr_to_ses(cf, &ses, "cookie", zx_dup_str(cf->ctx, cookie_hdr));
566 }
567
568 switch (res[0]) {
569 case 'L':
570 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("REDIR(%s)", res);
571 apr_table_setn(HRR_headers_out(r), "Location", res+10);
572 set_cookies(cf, r, ses.setcookie, ses.setptmcookie);
573 D_DEDENT("chkuid: ");
574 return HTTP_SEE_OTHER;
575 case 'C':
576 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("CONTENT(%s)", res);
577 set_cookies(cf, r, ses.setcookie, ses.setptmcookie);
578 ret = send_res(cf, r, res);
579 D_DEDENT("chkuid: ");
580 return ret;
581 case 'z':
582 INFO("User not authorized %d", 0);
583 D_DEDENT("chkuid: ");
584 return HTTP_FORBIDDEN;
585 case 0: /* Logged in case */
586 D("SSO OK pre uri(%s) filename(%s) path_info(%s)", uri, (char*)HRR_filename(r), (char*)HRR_path_info(r));
587 ret = pool2apache(cf, r, ses.at);
588 D_DEDENT("chkuid: ");
589 return ret;
590 #if 0
591 case 'd': /* Logged in case */
592 if (errmac_debug & MOD_AUTH_SAML_INOUT) INFO("SSO OK LDIF(%s)", res);
593 D("SSO OK pre uri(%s) filename(%s) path_info(%s)", uri, (char*)HRR_filename(r), (char*)HRR_path_info(r));
594 ret = ldif2apache(cf, r, res);
595 D_DEDENT("chkuid: ");
596 return ret;
597 #endif
598 default:
599 ERR("Unknown zxid_simple response(%s)", res);
600 D_DEDENT("chkuid: ");
601 return HTTP_INTERNAL_SERVER_ERROR;
602 }
603
604 D("final ok %d", OK);
605 D_DEDENT("chkuid: ");
606 return OK;
607 }
608
609 /* ------------------------ CONF -------------------------- */
610
611 /*(-) Process ZXIDDebug directive in Apache configuration file.
612 *
613 * This is considered internal function to mod_auth_saml. Do not call directly. */
614
615 /* Called by: */
set_debug(cmd_parms * cmd,void * st,const char * arg)616 static const char* set_debug(cmd_parms* cmd, void* st, const char* arg) {
617 char buf[256];
618 D("old debug=%x, new debug(%s)", errmac_debug, arg);
619 sscanf(arg, "%i", &errmac_debug);
620 INFO("debug=0x%x now arg(%s) cwd(%s)", errmac_debug, arg, getcwd(buf, sizeof(buf)));
621 {
622 struct rlimit rlim;
623 getrlimit(RLIMIT_CORE, &rlim);
624 D("MALLOC_CHECK_(%s) core_rlimit=%d,%d", getenv("MALLOC_CHECK_"), (int)rlim.rlim_cur, (int)rlim.rlim_max);
625 }
626 return 0;
627 }
628
629 /*(-) Process ZXIDConf directive in Apache configuration file.
630 * Can be called any number of times to set additional parameters.
631 *
632 * This is considered internal function to mod_auth_saml. Do not call directly. */
633
634 /* Called by: */
set_zxid_conf(cmd_parms * cmd,void * st,const char * arg)635 static const char* set_zxid_conf(cmd_parms* cmd, void* st, const char* arg) {
636 int len;
637 char* buf;
638 zxid_conf* cf = (zxid_conf*)st;
639 D("arg(%s) cf=%p", arg, cf);
640 len = strlen(arg);
641 buf = ZX_ALLOC(cf->ctx, len+1);
642 memcpy(buf, arg, len+1);
643 zxid_parse_conf(cf, buf);
644 return 0;
645 }
646
647 const command_rec zxid_apache_commands[] = {
648 AP_INIT_TAKE1("ZXIDDebug", set_debug, 0, OR_AUTHCFG,
649 "Enable debugging output to stderr. 0 to disable."),
650 AP_INIT_TAKE1("ZXIDConf", set_zxid_conf, 0, OR_AUTHCFG,
651 "Supply ZXID CONF string. May be supplied multiple times."),
652 {0}
653 };
654
655
656 #define ZXID_APACHE_DEFAULT_CONF "" /* defaults will reign, including cpath /var/zxid */
657
658 /*(-) Create default configuration in response for Apache <Location> or <Directory>
659 * directives. This is then augmented by ZXIDConf directives.
660 * This code may run twice: once for syntax check, and then again for
661 * production use. Currently we just redo the work. Apache stores the
662 * return value of this function and it can be read in chkuid() hook using
663 * ap_get_module_config((r)->per_dir_config, &auth_saml_module)
664 *
665 * This is considered internal function to mod_auth_saml. Do not call directly. */
666
667 /* Called by: */
dirconf(apr_pool_t * p,char * d)668 static void* dirconf(apr_pool_t* p, char* d)
669 {
670 zxid_conf* cf;
671 strncpy(errmac_instance, "\tmas", sizeof(errmac_instance));
672 cf = apr_palloc(p, sizeof(zxid_conf));
673 ZERO(cf, sizeof(zxid_conf));
674 cf->ctx = apr_palloc(p, sizeof(struct zx_ctx));
675 zx_reset_ctx(cf->ctx);
676 D("cf=%p ctx=%p d(%s)", cf, cf->ctx, STRNULLCHKD(d));
677 /* *** set malloc func ptr in ctx to use apr_palloc() */
678 zxid_conf_to_cf_len(cf, -1, ZXID_APACHE_DEFAULT_CONF);
679 cf->cpath_supplied = 0;
680 return cf;
681 }
682
683 /* ------------------------ Hooks -------------------------- */
684
685 /*(-) Register Apache hook for mod_auth_saml
686 *
687 * This is considered internal function to mod_auth_saml. Do not call directly. */
688
689 /* Called by: */
reghk(apr_pool_t * p)690 static void reghk(apr_pool_t* p) {
691 D("pool=%p", p);
692 //ap_hook_access_checker(authusr, 0, 0, APR_HOOK_MIDDLE);
693 ap_hook_check_user_id( chkuid, 0, 0, APR_HOOK_MIDDLE);
694 //ap_hook_post_config( postconf, 0, 0, APR_HOOK_MIDDLE);
695 /*ap_hook_child_init( chldinit, 0, 0, APR_HOOK_MIDDLE);*/
696 }
697
698 /* This extern variable is used as first argument to LoadModule in httpd.conf,
699 * E.g: LoadModule auth_saml_module modules/mod_auth_saml.so
700 * See httpd-2.2/include/http_config.h for module_struct.
701 * Lucky for 2.2 vs. 2.4 compat, m->module_index and other fields up to
702 * m->magic are on sample places on both. */
703
704 module AP_MODULE_DECLARE_DATA auth_saml_module = {
705 STANDARD20_MODULE_STUFF,
706 dirconf,
707 0, //dirmerge,
708 0, //srvconf,
709 0, //srvmerge,
710 zxid_apache_commands,
711 reghk
712 };
713
714 /* EOF - mod_auth_saml.c */
715