efa95c6cdec6219ff405d93e8ccdf179729e76fe
[shibboleth/sp.git] / apache-2.0 / mod_shib.cpp
1 /*
2  * mod_shib.cpp -- Shibboleth module for Apache-2.0
3  *
4  * Created by:  Derek Atkins <derek@ihtfp.com>
5  *
6  * $Id$
7  */
8
9 // SAML Runtime
10 #include <saml/saml.h>
11 #include <shib/shib.h>
12 #include <shib/shib-threads.h>
13 #include <shib-target/shib-target.h>
14
15 // Apache specific header files
16 #include <httpd.h>
17 #include <http_config.h>
18 #include <http_protocol.h>
19 #include <http_main.h>
20 #include <http_request.h>
21 #include <util_script.h>
22 #include <apr_strings.h>
23 #define CORE_PRIVATE
24 #include <http_core.h>
25 #include <http_log.h>
26 #include <apr_pools.h>
27
28 #include <xercesc/util/regx/RegularExpression.hpp>
29
30 #include <fstream>
31 #include <sstream>
32 #include <stdexcept>
33
34 // For POST processing from Apache
35 //-- do we still need this? #undef _XOPEN_SOURCE                // bombs on solaris
36 #include <apreq_params.h>
37
38 #include "cgiparse.h"
39
40 #include <unistd.h>             // for getpid()
41
42 using namespace std;
43 using namespace saml;
44 using namespace shibboleth;
45 using namespace shibtarget;
46
47 extern "C" AP_MODULE_DECLARE_DATA module mod_shib;
48
49 namespace {
50     char* g_szSHIBConfig = NULL;
51     ShibTargetConfig* g_Config = NULL;
52 }
53
54 // per-dir module configuration structure
55 struct shib_dir_config
56 {
57     // RM Configuration
58     char* szAuthGrpFile;        // Auth GroupFile name
59     int bExportAssertion;       // export SAML assertion to the environment?
60     int bRequireAll;            // all require directives must match, otherwise OR logic
61     int bDisableRM;             // disable the RM functionality?
62
63     // SHIRE Configuration
64     int bBasicHijack;           // activate for AuthType Basic?
65     int bSSLOnly;               // only over SSL?
66     SHIREConfig config;         // SHIB Configuration
67     RMConfig rm_config;         // RM Configuration
68 };
69
70 // creates per-directory config structure
71 extern "C" void* create_shib_dir_config (apr_pool_t* p, char* d)
72 {
73     shib_dir_config* dc=(shib_dir_config*)apr_pcalloc(p,sizeof(shib_dir_config));
74     dc->szAuthGrpFile = NULL;
75     dc->bExportAssertion = -1;
76     dc->bRequireAll = -1;
77     dc->bDisableRM = -1;
78
79     dc->bBasicHijack = -1;
80     dc->bSSLOnly = -1;
81     dc->config.lifetime = -1;
82     dc->config.timeout = -1;
83     return dc;
84 }
85
86 // overrides server configuration in directories
87 extern "C" void* merge_shib_dir_config (apr_pool_t* p, void* base, void* sub)
88 {
89     shib_dir_config* dc=(shib_dir_config*)apr_pcalloc(p,sizeof(shib_dir_config));
90     shib_dir_config* parent=(shib_dir_config*)base;
91     shib_dir_config* child=(shib_dir_config*)sub;
92
93     if (child->szAuthGrpFile)
94         dc->szAuthGrpFile=apr_pstrdup(p,child->szAuthGrpFile);
95     else if (parent->szAuthGrpFile)
96         dc->szAuthGrpFile=apr_pstrdup(p,parent->szAuthGrpFile);
97     else
98         dc->szAuthGrpFile=NULL;
99
100     dc->bExportAssertion=((child->bExportAssertion==-1) ?
101                           parent->bExportAssertion : child->bExportAssertion);
102     dc->bRequireAll=((child->bRequireAll==-1) ?
103                           parent->bRequireAll : child->bRequireAll);
104     dc->bDisableRM=((child->bDisableRM==-1) ?
105                     parent->bDisableRM : child->bDisableRM);
106
107     dc->bBasicHijack=((child->bBasicHijack==-1) ?
108                       parent->bBasicHijack : child->bBasicHijack);
109     dc->bSSLOnly=((child->bSSLOnly==-1) ? parent->bSSLOnly : child->bSSLOnly);
110     dc->config.lifetime=((child->config.lifetime==-1) ?
111                          parent->config.lifetime : child->config.lifetime);
112     dc->config.timeout=((child->config.timeout==-1) ?
113                         parent->config.timeout : child->config.timeout);
114     return dc;
115 }
116
117 // generic global slot handlers
118 extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*,
119                                                  const char* arg)
120 {
121     *((char**)(parms->info))=apr_pstrdup(parms->pool,arg);
122     return NULL;
123 }
124
125 // some shortcuts for directory config slots
126 extern "C" const char* set_lifetime(cmd_parms* parms, shib_dir_config* dc,
127                                     const char* arg)
128 {
129     dc->config.lifetime=atoi(arg);
130     return NULL;
131 }
132
133 extern "C" const char* set_timeout(cmd_parms* parms, shib_dir_config* dc,
134                                    const char* arg)
135 {
136     dc->config.timeout=atoi(arg);
137     return NULL;
138 }
139
140 typedef const char* (*config_fn_t)(void);
141
142 static char* url_encode(request_rec* r, const char* s)
143 {
144     int len=strlen(s);
145     char* ret=(char*)apr_palloc(r->pool,sizeof(char)*3*len+1);
146
147     // apreq_decode takes a string and url-encodes it.  Don't ask why
148     // the name is backwards.
149     apreq_decode(ret, s, len);
150     return ret;
151 }
152
153 static const char* get_shire_location(request_rec* r, const char* target,
154                                       const char* application_id)
155 {
156   ShibINI& ini = g_Config->getINI();
157   string shire_location;
158   bool shire_ssl_only = false;
159
160   // Determine if this is supposed to be ssl-only (default == false)
161   if (ini.get_tag (application_id, "shireSSLOnly", true, &shire_location))
162     shire_ssl_only = ShibINI::boolean(shire_location);
163
164   // Grab the specified shire-location from the config file
165   if (! ini.get_tag (application_id, "shireURL", true, &shire_location)) {
166     ap_log_rerror(APLOG_MARK,APLOG_CRIT,0,r,
167                   "shire_get_location() no shireURL configuration for %s",
168                   application_id);
169     return NULL;
170   }
171
172   //
173   // The "shireURL" can be one of three formats:
174   //
175   // 1) a full URI:             http://host/foo/bar
176   // 2) a partial URI:          http:///foo/bar
177   // 3) a relative path:        /foo/bar
178   //
179   // #  Protocol  Host    Path
180   // 1  shire     shire   shire
181   // 2  shire     target  shire
182   // 3  target    target  shire
183   //
184   // note: if shire_ssl_only is true, make sure the protocol is https
185   //
186
187   const char* shire = shire_location.c_str();
188   const char* path = NULL;
189
190   // Decide whether to use the shire or the target for the "protocol"
191   const char* prot;
192   if (*shire != '/') {
193     prot = shire;
194   } else {
195     prot = target;
196     path = shire;
197   }
198
199   //  ap_log_rerror(APLOG_MARK,APLOG_DEBUG,0,r,
200   //            "get_shire_location: prot=%s, path=%s", prot,
201   //            path ? path : "(null)");
202
203   // break apart the "protocol" string into protocol, host, and "the rest"
204   const char* colon=strchr(prot,':');
205   colon += 3;
206   const char* slash=strchr(colon,'/');
207   if (!path)
208     path = slash;
209
210   // Compute the actual protocol
211   const char* proto;
212   if (shire_ssl_only)
213     proto = "https://";
214   else
215     proto = apr_pstrndup(r->pool, prot, colon-prot);
216
217   // create the "host" from either the colon/slash or from the target string
218   // If prot == shire then we're in either #1 or #2, else #3.
219   // If slash == colon then we're in #2
220   if (prot != shire || slash == colon) {
221     colon = strchr(target, ':');
222     colon += 3;         // Get past the ://
223     slash = strchr(colon, '/');
224   }
225   const char *host = apr_pstrndup(r->pool, colon, slash-colon);
226
227   // Build the shire URL
228   return apr_pstrcat(r->pool, proto, host, path, NULL);
229 }
230
231 static int shib_error_page(request_rec* r, const char* filename, ShibMLP& mlp)
232 {
233   ifstream infile (filename);
234   if (!infile) {
235       ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,
236                     "shib_error_page() cannot open %s", filename);
237       return HTTP_INTERNAL_SERVER_ERROR;
238   }
239
240   string res = mlp.run(infile);
241   r->content_type = apr_psprintf(r->pool, "text/html");
242   ap_rprintf(r, res.c_str());
243   return DONE;
244 }
245
246 static const char* get_application_id(request_rec* r)
247 {
248   ApplicationMapper mapper;
249   return apr_pstrdup(r->pool,
250                     mapper->getApplicationFromParsedURL(
251                         ap_http_method(r), ap_get_server_name(r),
252                         ap_get_server_port(r), r->unparsed_uri
253                         )
254                     );
255 }
256
257 static apr_table_t* groups_for_user(request_rec* r, const char* user, char* grpfile)
258 {
259     ap_configfile_t* f;
260     apr_table_t* grps=apr_table_make(r->pool,15);
261     char l[MAX_STRING_LEN];
262     const char *group_name, *ll, *w;
263
264     if (ap_pcfg_openfile(&f,r->pool,grpfile) != APR_SUCCESS)
265     {
266         ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,
267                       "groups_for_user() could not open group file: %s\n", grpfile);
268         return NULL;
269     }
270
271     apr_pool_t* sp;
272     if (apr_pool_create(&sp,r->pool) != APR_SUCCESS)
273     {
274         ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,
275                       "groups_for_user() could not create a subpool");
276         return NULL;
277     }
278
279     while (!(ap_cfg_getline(l,MAX_STRING_LEN,f)))
280     {
281         if ((*l=='#') || (!*l))
282             continue;
283         ll = l;
284         apr_pool_clear(sp);
285
286         group_name=ap_getword(sp,&ll,':');
287
288         while (*ll)
289         {
290             w=ap_getword_conf(sp,&ll);
291             if (!strcmp(w,user))
292             {
293                 apr_table_setn(grps,apr_pstrdup(r->pool,group_name),"in");
294                 break;
295             }
296         }
297     }
298     ap_cfg_closefile(f);
299     apr_pool_destroy(sp);
300     return grps;
301 }
302
303 extern "C" int shib_check_user(request_rec* r)
304 {
305     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,"shib_check_user: ENTER");
306     shib_dir_config* dc=(shib_dir_config*)ap_get_module_config(r->per_dir_config,&mod_shib);
307
308     // This will always be normalized, because Apache uses ap_get_server_name in this API call.
309     const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r);
310
311     // Map request to application ID, which is the key for config lookup.
312     const char* application_id=get_application_id(r);
313     
314     // Get unescaped location of this application's assertion consumer service.
315     const char* unescaped_shire = get_shire_location(r, targeturl, application_id);
316     
317     if (strstr(targeturl,unescaped_shire)) {
318       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
319            "shib_check_user: REQUEST FOR SHIRE!  Maybe you did not configure the SHIRE Handler?");
320       return HTTP_INTERNAL_SERVER_ERROR;
321     }
322     else {
323       // Regular access to arbitrary resource...check AuthType
324       const char *auth_type=ap_auth_type (r);
325       if (!auth_type)
326         return DECLINED;
327
328       if (strcasecmp(auth_type,"shibboleth"))
329       {
330         if (!strcasecmp(auth_type,"basic") && dc->bBasicHijack==1)
331         {
332             core_dir_config* conf=
333                 (core_dir_config*)ap_get_module_config(r->per_dir_config,
334                     ap_find_linked_module("http_core.c"));
335             conf->ap_auth_type="shibboleth";
336         }
337         else
338             return DECLINED;
339       }
340
341       // set the connection authtype
342       r->ap_auth_type = "shibboleth";
343
344       // SSL check.
345       if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
346       {
347         ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
348            "shib_check_user() blocked non-SSL access");
349         return HTTP_INTERNAL_SERVER_ERROR;
350       }
351     }
352
353     ostringstream threadid;
354     threadid << "[" << getpid() << "] shib" << '\0';
355     saml::NDC ndc(threadid.str().c_str());
356
357     ShibINI& ini = g_Config->getINI();
358
359     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
360                     "shib_check_user() Shib check for %s", targeturl);
361
362     string tag;
363     bool has_tag = ini.get_tag (application_id, "checkIPAddress", true, &tag);
364     dc->config.checkIPAddress = (has_tag ? ShibINI::boolean (tag) : false);
365
366     string shib_cookie;
367     if (! ini.get_tag(application_id, "cookieName", true, &shib_cookie)) {
368       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
369                     "shib_check_user: no cookieName configuration for %s",
370                     application_id);
371       return HTTP_INTERNAL_SERVER_ERROR;
372     }
373
374     string wayfLocation;
375     if (! ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) {
376       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
377                     "shib_check_user: no wayfURL configuration for %s",
378                     application_id);
379       return HTTP_INTERNAL_SERVER_ERROR;
380     }
381
382     string shireError;
383     if (! ini.get_tag(application_id, "shireError", true, &shireError)) {
384       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
385                     "shib_check_user: no shireError configuration for %s",
386                     application_id);
387       return HTTP_INTERNAL_SERVER_ERROR;
388     }
389     
390     SHIRE shire(dc->config, unescaped_shire);
391
392     // We're in charge, so check for cookie.
393     const char* session_id=NULL;
394     const char* cookies=apr_table_get(r->headers_in,"Cookie");
395
396     if (cookies)
397     {
398         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
399                         "shire_check_user() cookies found: %s",cookies);
400         if (session_id=strstr(cookies,shib_cookie.c_str()))
401         {
402             // Yep, we found a cookie -- pull it out (our session_id)
403             session_id+=strlen(shib_cookie.c_str()) + 1; /* Skip over the '=' */
404             char* cookiebuf = apr_pstrdup(r->pool,session_id);
405             char* cookieend = strchr(cookiebuf,';');
406             if (cookieend)
407                 *cookieend = '\0';    /* Ignore anyting after a ; */
408             session_id=cookiebuf;
409         }
410     }
411
412     if (!session_id || !*session_id)
413     {
414         // No cookie.  Redirect to WAYF.
415         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
416                       "shib_check_user() no cookie found -- redirecting to WAYF");
417         char timebuf[16];
418         sprintf(timebuf,"%u",time(NULL));
419         char* wayf=apr_pstrcat(r->pool,wayfLocation.c_str(),
420                               "?shire=",url_encode(r,unescaped_shire),
421                               "&target=",url_encode(r,targeturl),
422                                "&time=",timebuf,
423                                "&providerId=",application_id,
424                                NULL);
425         apr_table_setn(r->headers_out,"Location",wayf);
426         return HTTP_MOVED_TEMPORARILY;
427     }
428
429     // Make sure this session is still valid
430     RPCError* status = NULL;
431     ShibMLP markupProcessor;
432     has_tag = ini.get_tag(application_id, "supportContact", true, &tag);
433     markupProcessor.insert("supportContact", has_tag ? tag : "");
434     has_tag = ini.get_tag(application_id, "logoLocation", true, &tag);
435     markupProcessor.insert("logoLocation", has_tag ? tag : "");
436     markupProcessor.insert("requestURL", targeturl);
437
438     try {
439         status = shire.sessionIsValid(session_id, r->connection->remote_ip,application_id);
440     }
441     catch (ShibTargetException &e) {
442         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,"shib_check_user(): %s", e.what());
443         markupProcessor.insert ("errorType", "SHIRE Processing Error");
444         markupProcessor.insert ("errorText", e.what());
445         markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
446         return shib_error_page (r, shireError.c_str(), markupProcessor);
447     }
448     catch (...) {
449         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,"shib_check_user(): caught unexpected error");
450         markupProcessor.insert ("errorType", "SHIRE Processing Error");
451         markupProcessor.insert ("errorText", "Unexpected Exception");
452         markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
453         return shib_error_page (r, shireError.c_str(), markupProcessor);
454     }
455
456     // Check the status
457     if (status->isError()) {
458         ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,0,r,
459                       "shib_check_user() session invalid: %s",
460                       status->getText());
461
462         if (status->isRetryable()) {
463             // Oops, session is invalid.  Redirect to WAYF.
464             char timebuf[16];
465             sprintf(timebuf,"%u",time(NULL));
466             char* wayf=apr_pstrcat(r->pool,wayfLocation.c_str(),
467                                    "?shire=",url_encode(r,unescaped_shire),
468                                    "&target=",url_encode(r,targeturl),
469                                    "&time=",timebuf,
470                                    "&providerId=",application_id,
471                                    NULL);
472             apr_table_setn(r->headers_out,"Location",wayf);
473
474             delete status;
475             return HTTP_MOVED_TEMPORARILY;
476         }
477         else {
478             // return the error page to the user
479             markupProcessor.insert (*status);
480             delete status;
481             return shib_error_page (r, shireError.c_str(), markupProcessor);
482         }
483     }
484     else {
485         delete status;
486         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
487                       "shib_check_user() success");
488         return OK;
489     }
490
491     ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,"shib_check_user() server error");
492     return HTTP_INTERNAL_SERVER_ERROR;
493 }
494
495 extern "C" int shib_shire_handler (request_rec* r)
496 {
497   ostringstream threadid;
498   threadid << "[" << getpid() << "] shire" << '\0';
499   saml::NDC ndc(threadid.str().c_str());
500
501   // This will always be normalized, because Apache uses
502   // ap_get_server_name in this API call.
503   const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r);
504
505   // Map request to application ID, which is the key for config lookup.
506   const char* application_id = get_application_id(r);
507     
508   // Find out what SHOULD be the SHIRE URL...
509   const char* unescaped_shire = get_shire_location(r, targeturl, application_id);
510
511   // Make sure we only process the SHIRE posts.
512   if (!strstr(targeturl,unescaped_shire))
513     return DECLINED;
514
515   ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
516                 "shire_post_handler() ENTER");
517
518   ShibINI& ini = g_Config->getINI();
519   ShibMLP markupProcessor;
520  
521   string tag;
522   bool has_tag = ini.get_tag(application_id, "checkIPAddress", true, &tag);
523   SHIREConfig config;
524   config.checkIPAddress = (has_tag ? ShibINI::boolean(tag) : false);
525
526   string shib_cookie;
527   if (! ini.get_tag(application_id, "cookieName", true, &shib_cookie)) {
528     ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
529                   "shire_post_handler: no cookieName configuration for %s",
530                   application_id);
531     return HTTP_INTERNAL_SERVER_ERROR;
532   }
533
534   string wayfLocation;
535   if (! ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) {
536     ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
537                   "shire_post_handler: no wayfURL configuration for %s",
538                   application_id);
539     return HTTP_INTERNAL_SERVER_ERROR;
540   }
541
542   string shireError;
543   if (! ini.get_tag(application_id, "shireError", true, &shireError)) {
544     ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
545                   "shire_post_handler: no shireError configuration for %s",
546                   application_id);
547     return HTTP_INTERNAL_SERVER_ERROR;
548   }
549
550   has_tag = ini.get_tag(application_id, "supportContact", true, &tag);
551   markupProcessor.insert("supportContact", has_tag ? tag : "");
552   has_tag = ini.get_tag(application_id, "logoLocation", true, &tag);
553   markupProcessor.insert("logoLocation", has_tag ? tag : "");
554   markupProcessor.insert("requestURL", targeturl);
555   
556   SHIRE shire(config, unescaped_shire);
557
558   // Process SHIRE POST
559
560   ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
561                 "shire_post_handler() Beginning SHIRE POST processing");
562       
563   CgiParse* cgi = NULL;
564
565   try {
566     string sslonly;
567     if (!ini.get_tag(application_id, "shireSSLOnly", true, &sslonly))
568       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
569                     "shire_post_handler: no shireSSLOnly configuration");
570     
571     // Make sure this is SSL, if it should be
572     if (ShibINI::boolean(sslonly) && strcmp(ap_http_method(r),"https"))
573       throw ShibTargetException (SHIBRPC_OK,
574                                  "blocked non-SSL access to SHIRE POST processor");
575
576     // Make sure this is a POST
577     if (strcasecmp (r->method, "POST"))
578       throw ShibTargetException (SHIBRPC_OK,
579                                  "blocked non-POST to SHIRE POST processor");
580
581     // Sure sure this POST is an appropriate content type
582     const char *ct = apr_table_get (r->headers_in, "Content-type");
583     if (!ct || strcasecmp (ct, "application/x-www-form-urlencoded"))
584       throw ShibTargetException (SHIBRPC_OK,
585                                  apr_psprintf(r->pool,
586                              "blocked bad content-type to SHIRE POST processor: %s",
587                                              (ct ? ct : "")));
588         
589     // Make sure the "bytes sent" is a reasonable number
590     if (r->bytes_sent > 1024*1024) // 1MB?
591       throw ShibTargetException (SHIBRPC_OK,
592                                  "blocked too-large a post to SHIRE POST processor");
593
594     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
595                   "shire_post_handler() about to run setup_client_block");
596
597     // Read the posted data
598     if (ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))
599       throw ShibTargetException (SHIBRPC_OK, "CGI setup_client_block failed");
600
601     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
602                   "shire_post_handler() about to run should_client_block");
603
604     if (!ap_should_client_block(r))
605       throw ShibTargetException (SHIBRPC_OK, "CGI should_client_block failed");
606
607     long length = r->remaining;
608     if (length > 1024*1024)
609       throw ShibTargetException (SHIBRPC_OK, "CGI length too long...");
610
611     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
612                   "shire_post_handler() about to read using get_client_block");
613     string cgistr = "";
614     char buff[BUFSIZ];
615     //ap_hard_timeout("[mod_shib] CGI Parser", r);
616
617     memset(buff, 0, sizeof(buff));
618     while (ap_get_client_block(r, buff, sizeof(buff)) > 0) {
619       cgistr += buff;
620       memset(buff, 0, sizeof(buff));
621     }
622
623     //ap_kill_timeout(r);
624
625     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
626                   "shire_post_handler() about to parse cgi...");
627
628     cgi = CgiParse::ParseCGI(cgistr);
629
630     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
631                   "shire_post_handler() CGI parsed... (%p)", cgi);
632
633     if (!cgi)
634       throw ShibTargetException (SHIBRPC_OK, "CgiParse failed");
635     
636     // Make sure the target parameter exists
637     const char *target = cgi->get_value("TARGET");
638
639     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
640                   "shire_post_handler() obtained target...");
641
642     if (!target || *target == '\0')
643       // invalid post
644       throw ShibTargetException (SHIBRPC_OK,
645                                  "SHIRE POST failed to find TARGET");
646
647     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
648                   "shire_post_handler() obtained target...");
649
650     // Make sure the SAML Response parameter exists
651     const char *post = cgi->get_value("SAMLResponse");
652     if (!post || *post == '\0')
653       // invalid post
654       throw ShibTargetException (SHIBRPC_OK,
655                                  "SHIRE POST failed to find SAMLResponse");
656
657     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
658                   "shire_post_handler() Processing POST for target: %s", target);
659
660     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
661                   "shire_post_handler() POST contents: %s", post);
662
663     // process the post
664     string cookie;
665     RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, application_id, cookie);
666
667     if (status->isError()) {
668       ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
669                     "shire_post_handler() POST process failed (%d): %s",
670                     status->getCode(), status->getText());
671
672       if (status->isRetryable()) {
673         ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,0,r,
674                       "shire_post_handler() Retrying POST by redirecting to WAYF");
675         
676         char timebuf[16];
677         sprintf(timebuf,"%u",time(NULL));
678         char* wayf=apr_pstrcat(r->pool,wayfLocation.c_str(),
679                                "?shire=",url_encode(r,unescaped_shire),
680                                "&target=",url_encode(r,target),
681                                "&time=",timebuf,
682                                "&providerId=",application_id,
683                                NULL);
684         apr_table_setn(r->headers_out,"Location",wayf);
685         delete status;
686         return HTTP_MOVED_TEMPORARILY;
687       }
688
689       // return this error to the user.
690       markupProcessor.insert (*status);
691       delete status;
692       return shib_error_page (r, shireError.c_str(), markupProcessor);
693     }
694     delete status;
695
696     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
697                   "shire_post_handler() POST process succeeded.  New cookie: %s",
698                   cookie.c_str());
699
700     // We've got a good session, set the cookie...
701     char * domain = NULL;
702     char * new_cookie = apr_psprintf(r->pool, "%s=%s; path=/%s%s",
703                                     shib_cookie.c_str(),
704                                     cookie.c_str(),
705                                     (domain ? "; domain=" : ""),
706                                     (domain ? domain : ""));
707     
708     apr_table_setn(r->err_headers_out, "Set-Cookie", new_cookie);
709     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
710                   "shire_post_handler() Set cookie: %s", new_cookie);
711                     
712     // ... and redirect to the target
713     char* redir=apr_pstrcat(r->pool,url_encode(r,target),NULL);
714     apr_table_setn(r->headers_out, "Location", target);
715     delete cgi;
716     return HTTP_MOVED_TEMPORARILY;
717
718   } catch (ShibTargetException &e) {
719     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
720                   "shire_post_handler(): %s", e.what());
721         
722     markupProcessor.insert ("errorType", "SHIRE Processing Error");
723     markupProcessor.insert ("errorText", e.what());
724     markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
725     if (cgi) delete cgi;
726     return shib_error_page (r, shireError.c_str(), markupProcessor);
727   }
728   catch (...) {
729     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,"shire_post_handler(): unexpected exception");
730   
731     markupProcessor.insert ("errorType", "SHIRE Processing Error");
732     markupProcessor.insert ("errorText", "Unexpected Exception");
733     markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
734     if (cgi) delete cgi;
735     return shib_error_page (r, shireError.c_str(), markupProcessor);
736   }
737
738   ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,"shire_post_handler() server error");
739   return HTTP_INTERNAL_SERVER_ERROR;
740 }
741
742 /*
743  * shib_auth_checker() -- a simple resource manager to
744  * process the .htaccess settings and copy attributes
745  * into the HTTP headers.
746  */
747 extern "C" int shib_auth_checker(request_rec *r)
748 {
749     shib_dir_config* dc=
750         (shib_dir_config*)ap_get_module_config(r->per_dir_config,&mod_shib);
751
752     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
753                   "shib_auth_checker() executing");
754
755     // Regular access to arbitrary resource...check AuthType
756     const char* auth_type=ap_auth_type(r);
757     if (!auth_type || strcasecmp(auth_type,"shibboleth"))
758         return DECLINED;
759
760     ostringstream threadid;
761     threadid << "[" << getpid() << "] shib" << '\0';
762     saml::NDC ndc(threadid.str().c_str());
763
764     ShibINI& ini = g_Config->getINI();
765
766     // This will always be normalized, because Apache uses ap_get_server_name
767     // in this API call.
768     const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r);
769     
770     // Map request to application ID, which is the key for config lookup.
771     const char* application_id=get_application_id(r);
772
773     // Ok, this is a SHIB target; grab the cookie
774     string shib_cookie;
775     if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) {
776       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
777                     "shib_check_user: no cookieName configuration for %s",
778                     application_id);
779       return HTTP_INTERNAL_SERVER_ERROR;
780     }
781
782     const char* session_id=NULL;
783     const char* cookies=apr_table_get(r->headers_in,"Cookie");
784     if (!cookies || !(session_id=strstr(cookies,shib_cookie.c_str())))
785     {
786       // No cookie???  Must be a server error!
787       ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
788                     "shib_auth_checker() no cookie found");
789
790       return HTTP_INTERNAL_SERVER_ERROR;
791     }
792
793     // Yep, we found a cookie -- pull it out (our session_id)
794     session_id+=strlen(shib_cookie.c_str()) + 1;        /* Skip over the '=' */
795     char* cookiebuf = apr_pstrdup(r->pool,session_id);
796     char* cookieend = strchr(cookiebuf,';');
797     if (cookieend)
798       *cookieend = '\0';        /* Ignore anyting after a ; */
799     session_id=cookiebuf;
800
801     ShibMLP markupProcessor;
802     string tag;
803     bool has_tag = ini.get_tag(application_id, "supportContact", true, &tag);
804     markupProcessor.insert("supportContact", has_tag ? tag : "");
805     has_tag = ini.get_tag(application_id, "logoLocation", true, &tag);
806     markupProcessor.insert("logoLocation", has_tag ? tag : "");
807     markupProcessor.insert("requestURL", targeturl);
808
809     // Now grab the attributes...
810     has_tag = ini.get_tag (application_id, "checkIPAddress", true, &tag);
811     dc->rm_config.checkIPAddress = (has_tag ? ShibINI::boolean (tag) : false);
812
813     RM rm(dc->rm_config);
814
815     vector<SAMLAssertion*> assertions;
816     SAMLAuthenticationStatement* sso_statement=NULL;
817     RPCError* status = rm.getAssertions(session_id, r->connection->remote_ip, application_id, assertions, &sso_statement);
818
819     if (status->isError()) {
820       ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
821                     "shib_auth_checker() getAssertions failed: %s",
822                     status->getText());
823
824       string rmError;
825       if (!ini.get_tag(application_id, "rmError", true, &rmError)) {
826         ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
827                       "shib_auth_checker: no rmError configuration for %s",
828                       application_id);
829         delete status;
830         return HTTP_INTERNAL_SERVER_ERROR;      
831       }
832       markupProcessor.insert(*status);
833       delete status;
834       return shib_error_page (r, rmError.c_str(), markupProcessor);
835     }
836     delete status;
837
838     string rmError;
839     if (!ini.get_tag(application_id, "accessError", true, &rmError)) {
840         ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,0,r,
841            "shib_auth_checker: no accessError configuration for %s",
842             application_id);
843
844         delete status;
845         for (int k = 0; k < assertions.size(); k++)
846           delete assertions[k];
847         delete sso_statement;
848         return HTTP_INTERNAL_SERVER_ERROR;  
849     }
850
851     // Get the AAP providers, which contain the attribute policy info.
852     Iterator<IAAP*> provs=g_Config->getAAPProviders();
853
854     // Clear out the list of mapped attributes
855     while (provs.hasNext())
856     {
857         IAAP* aap=provs.next();
858         aap->lock();
859         try
860         {
861             Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
862             while (rules.hasNext())
863             {
864                 const char* header=rules.next()->getHeader();
865                 if (header)
866                     apr_table_unset(r->headers_in,header);
867             }
868         }
869         catch(...)
870         {
871             aap->unlock();
872             for (int k = 0; k < assertions.size(); k++)
873               delete assertions[k];
874             delete sso_statement;
875             throw;
876         }
877         aap->unlock();
878     }
879     provs.reset();
880     
881     // Maybe export the first assertion.
882     apr_table_unset(r->headers_in,"Shib-Attributes");
883     if (dc->bExportAssertion==1 && assertions.size()) {
884         string assertion;
885         RM::serialize(*(assertions[0]), assertion);
886         apr_table_set(r->headers_in,"Shib-Attributes", assertion.c_str());
887     }
888
889     // Export the SAML AuthnMethod and the origin site name.
890     apr_table_unset(r->headers_in,"Shib-Origin-Site");
891     apr_table_unset(r->headers_in,"Shib-Authentication-Method");
892     if (sso_statement)
893     {
894         auto_ptr_char os(sso_statement->getSubject()->getNameQualifier());
895         auto_ptr_char am(sso_statement->getAuthMethod());
896         apr_table_set(r->headers_in,"Shib-Origin-Site", os.get());
897         apr_table_set(r->headers_in,"Shib-Authentication-Method", am.get());
898     }
899
900     apr_table_unset(r->headers_in,"Shib-Application-ID");
901     apr_table_set(r->headers_in,"Shib-Application-ID",application_id);
902
903     // Export the attributes.
904     Iterator<SAMLAssertion*> a_iter(assertions);
905     while (a_iter.hasNext()) {
906       SAMLAssertion* assert=a_iter.next();
907       Iterator<SAMLStatement*> statements=assert->getStatements();
908       while (statements.hasNext()) {
909         SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());
910         if (!astate)
911           continue;
912         Iterator<SAMLAttribute*> attrs=astate->getAttributes();
913         while (attrs.hasNext()) {
914           SAMLAttribute* attr=attrs.next();
915           
916           // Are we supposed to export it?
917           AAP wrapper(provs,attr->getName(),attr->getNamespace());
918           if (wrapper.fail())
919             continue;
920           
921           Iterator<string> vals=attr->getSingleByteValues();
922           if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext())
923             r->user=apr_pstrdup(r->connection->pool,vals.next().c_str());
924           else {
925             char* header = apr_pstrdup(r->pool, "");
926             for (int it = 0; vals.hasNext(); it++) {
927               string value = vals.next();
928               for (string::size_type pos = value.find_first_of(";", string::size_type(0)); pos != string::npos; pos = value.find_first_of(";", pos)) {
929                 value.insert(pos, "\\");
930                 pos += 2;
931               }
932               header=apr_pstrcat(r->pool, header, (it ? ";" : ""), value.c_str(), NULL);
933             }
934             apr_table_setn(r->headers_in, wrapper->getHeader(), header);
935           }
936         }
937       }
938     }
939
940     // clean up memory
941     for (int k = 0; k < assertions.size(); k++)
942       delete assertions[k];
943     delete sso_statement;
944
945     // mod_auth clone
946
947     int m=r->method_number;
948     bool method_restricted=false;
949     const char *t, *w;
950     
951     const apr_array_header_t* reqs_arr=ap_requires(r);
952     if (!reqs_arr)
953         return OK;
954
955     require_line* reqs=(require_line*)reqs_arr->elts;
956
957     //XXX
958     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
959                  "REQUIRE nelts: %d", reqs_arr->nelts);
960     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
961                  "REQUIRE all: %d", dc->bRequireAll);
962
963     bool auth_OK[reqs_arr->nelts];
964
965 #define SHIB_AP_CHECK_IS_OK {           \
966             if (dc->bRequireAll < 1)    \
967                 return OK;              \
968             auth_OK[x] = true;          \
969             continue;                   \
970 }
971
972     for (int x=0; x<reqs_arr->nelts; x++)
973     {
974         auth_OK[x] = false;
975
976         if (!(reqs[x].method_mask & (1 << m)))
977             continue;
978         method_restricted=true;
979
980         t = reqs[x].requirement;
981         w = ap_getword_white(r->pool, &t);
982
983         if (!strcmp(w,"valid-user"))
984         {
985             ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
986                           "shib_auth_checker() accepting valid-user");
987             SHIB_AP_CHECK_IS_OK;
988         }
989         else if (!strcmp(w,"user") && r->user)
990         {
991             bool regexp=false;
992             while (*t)
993             {
994                 w=ap_getword_conf(r->pool,&t);
995                 if (*w=='~')
996                 {
997                     regexp=true;
998                     continue;
999                 }
1000                 
1001                 if (regexp)
1002                 {
1003                     try
1004                     {
1005                         // To do regex matching, we have to convert from UTF-8.
1006                         auto_ptr<XMLCh> trans(fromUTF8(w));
1007                         RegularExpression re(trans.get());
1008                         auto_ptr<XMLCh> trans2(fromUTF8(r->user));
1009                         if (re.matches(trans2.get())) {
1010                             ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1011                                           "shib_auth_checker() accepting user: %s",w);
1012                             SHIB_AP_CHECK_IS_OK;
1013                         }
1014                     }
1015                     catch (XMLException& ex)
1016                     {
1017                         auto_ptr_char tmp(ex.getMessage());
1018                         ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
1019         "shib_auth_checker caught exception while parsing regular expression (%s): %s",w,tmp.get());
1020                     }
1021                 }
1022                 else if (!strcmp(r->user,w))
1023                 {
1024                     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1025                                   "shib_auth_checker() accepting user: %s",w);
1026                     SHIB_AP_CHECK_IS_OK;
1027                 }
1028             }
1029         }
1030         else if (!strcmp(w,"group"))
1031         {
1032             apr_table_t* grpstatus=NULL;
1033             if (dc->szAuthGrpFile && r->user)
1034             {
1035                 ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1036                               "shib_auth_checker() using groups file: %s\n",
1037                               dc->szAuthGrpFile);
1038                 grpstatus=groups_for_user(r,r->user,dc->szAuthGrpFile);
1039             }
1040             if (!grpstatus)
1041                 return DECLINED;
1042     
1043             while (*t)
1044             {
1045                 w=ap_getword_conf(r->pool,&t);
1046                 if (apr_table_get(grpstatus,w))
1047                 {
1048                     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1049                                   "shib_auth_checker() accepting group: %s",w);
1050                     SHIB_AP_CHECK_IS_OK;
1051                 }
1052             }
1053         }
1054         else
1055         {
1056             AAP wrapper(provs,w);
1057             if (wrapper.fail()) {
1058                 ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,
1059                                 "shib_auth_checker() didn't recognize require rule: %s\n",w);
1060                 continue;
1061             }
1062
1063             bool regexp=false;
1064             const char* vals=apr_table_get(r->headers_in,wrapper->getHeader());
1065             while (*t && vals)
1066             {
1067               w=ap_getword_conf(r->pool,&t);
1068               if (*w=='~')
1069               {
1070                 regexp=true;
1071                 continue;
1072               }
1073
1074               try
1075               {
1076                 auto_ptr<RegularExpression> re;
1077                 if (regexp)
1078                 {
1079                   delete re.release();
1080                   auto_ptr<XMLCh> trans(fromUTF8(w));
1081                   auto_ptr<RegularExpression> temp(new RegularExpression(trans.get()));
1082                   re=temp;
1083                 }
1084                         
1085                 string vals_str(vals);
1086                 int j = 0;
1087                 for (int i = 0;  i < vals_str.length();  i++)
1088                 {
1089                   if (vals_str.at(i) == ';') 
1090                   {
1091                     if (i == 0) {
1092                       ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,
1093                                     "shib_auth_checker() invalid header encoding %s: starts with semicolon", vals);
1094                       return HTTP_INTERNAL_SERVER_ERROR;
1095                     }
1096         
1097                     if (vals_str.at(i-1) == '\\') {
1098                       vals_str.erase(i-1, 1);
1099                       i--;
1100                       continue;
1101                     }
1102         
1103                     string val = vals_str.substr(j, i-j);
1104                     j = i+1;
1105                     if (regexp) {
1106                       auto_ptr<XMLCh> trans(fromUTF8(val.c_str()));
1107                       if (re->matches(trans.get())) {
1108                         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1109                                       "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
1110                         SHIB_AP_CHECK_IS_OK;
1111                       }
1112                     }
1113                     else if (val==w) {
1114                       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1115                                     "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
1116                       SHIB_AP_CHECK_IS_OK;
1117                     }
1118                     else {
1119                       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1120                                     "shib_auth_checker() expecting %s, got %s: authorization not granted", w, val.c_str());
1121                     }
1122                   }
1123                 }
1124         
1125                 string val = vals_str.substr(j, vals_str.length()-j);
1126                 if (regexp) {
1127                   auto_ptr<XMLCh> trans(fromUTF8(val.c_str()));
1128                   if (re->matches(trans.get())) {
1129                     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1130                                   "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
1131                     SHIB_AP_CHECK_IS_OK;
1132                   }
1133                 }
1134                 else if (val==w) {
1135                   ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1136                                 "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
1137                   SHIB_AP_CHECK_IS_OK;
1138                 }
1139                 else {
1140                   ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,r,
1141                                 "shib_auth_checker() expecting %s, got %s: authorization not granted", w, val.c_str());
1142                 }
1143               }
1144               catch (XMLException& ex)
1145               {
1146                 auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
1147                 ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,r,
1148                               "shib_auth_checker caught exception while parsing regular expression (%s): %s",w,tmp.get());
1149               }
1150             }
1151         }
1152     }
1153
1154     // check if all require directives are true
1155     bool auth_all_OK = true;
1156     for (int i= 0; i<reqs_arr->nelts; i++) {
1157         auth_all_OK &= auth_OK[i];
1158     } 
1159     if (auth_all_OK)
1160         return OK;
1161
1162     if (!method_restricted)
1163         return OK;
1164
1165     return shib_error_page(r, rmError.c_str(), markupProcessor);
1166 }
1167
1168 /*
1169  * shib_exit()
1170  *  Cleanup the (per-process) pool info.
1171  */
1172 extern "C" apr_status_t shib_exit(void* data)
1173 {
1174     g_Config->shutdown();
1175     g_Config = NULL;
1176     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,NULL,"shib_exit() done");
1177     return OK;
1178 }
1179
1180 /*
1181  * shib_post_config()
1182  *  Things to do at process startup after the configs are read
1183  */
1184 extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
1185                                 apr_pool_t* ptemp, server_rec* s)
1186 {
1187     // Initialize runtime components.
1188
1189     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,s,
1190                  "shib_post_config() starting");
1191
1192     ShibTargetConfig::preinit();
1193
1194     if (g_Config) {
1195       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,0,s,
1196                    "shib_post_config(): already initialized!");
1197       exit (1);
1198     }
1199
1200     try {
1201       g_Config = &(ShibTargetConfig::init(SHIBTARGET_SHIRE, g_szSHIBConfig));
1202     } catch (...) {
1203       ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,s,
1204                    "shib_post_config() failed to initialize SHIB Target");
1205       exit (1);
1206     }
1207
1208     // Set the cleanup handler
1209     apr_pool_cleanup_register(pconf, NULL, shib_exit, NULL);
1210
1211     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,s,"shib_post_config() done");
1212
1213     return OK;
1214 }
1215
1216 extern "C" void shib_register_hooks (apr_pool_t *p)
1217 {
1218   ap_hook_post_config(shib_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1219   ap_hook_check_user_id(shib_check_user, NULL, NULL, APR_HOOK_MIDDLE);
1220   ap_hook_auth_checker(shib_auth_checker, NULL, NULL, APR_HOOK_FIRST);
1221   ap_hook_handler(shib_shire_handler, NULL, NULL, APR_HOOK_MIDDLE);
1222 }
1223
1224 // SHIB Module commands
1225
1226 extern "C" {
1227 static command_rec shib_cmds[] = {
1228   AP_INIT_TAKE1("SHIBConfig",
1229                 (config_fn_t)ap_set_global_string_slot, &g_szSHIBConfig,
1230                 RSRC_CONF, "Path to SHIB ini file."),
1231
1232   AP_INIT_FLAG("ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
1233                (void *) offsetof (shib_dir_config, bBasicHijack),
1234                OR_AUTHCFG, "Respond to AuthType Basic and convert to shib?"),
1235   AP_INIT_FLAG("ShibSSLOnly", (config_fn_t)ap_set_flag_slot,
1236                (void *) offsetof (shib_dir_config, bSSLOnly),
1237                OR_AUTHCFG, "Require SSL when accessing a secured directory?"),
1238   AP_INIT_TAKE1("ShibAuthLifetime", (config_fn_t)set_lifetime, NULL,
1239                 OR_AUTHCFG, "Lifetime of session in seconds."),
1240   AP_INIT_TAKE1("ShibAuthTimeout", (config_fn_t)set_timeout, NULL,
1241                 OR_AUTHCFG, "Timeout for session in seconds."),
1242
1243   AP_INIT_TAKE1("AuthGroupFile", (config_fn_t)ap_set_file_slot,
1244                 (void *) offsetof (shib_dir_config, szAuthGrpFile),
1245                 OR_AUTHCFG, "text file containing group names and member user IDs"),
1246   AP_INIT_FLAG("ShibExportAssertion", (config_fn_t)ap_set_flag_slot,
1247                (void *) offsetof (shib_dir_config, bExportAssertion),
1248                OR_AUTHCFG, "Export SAML assertion to Shibboleth-defined header?"),
1249   AP_INIT_FLAG("ShibRequireAll", (config_fn_t)ap_set_flag_slot,
1250                (void *) offsetof (shib_dir_config, bRequireAll),
1251                OR_AUTHCFG, "All require directives must match!"),
1252   AP_INIT_FLAG("DisableRM", (config_fn_t)ap_set_flag_slot,
1253                (void *) offsetof (shib_dir_config, bDisableRM),
1254                OR_AUTHCFG, "Disable the Shibboleth Resource Manager?"),
1255
1256   {NULL}
1257 };
1258
1259 module AP_MODULE_DECLARE_DATA mod_shib = {
1260     STANDARD20_MODULE_STUFF,
1261     create_shib_dir_config,     /* create dir config */
1262     merge_shib_dir_config,      /* merge dir config --- default is to override */
1263     NULL,                       /* create server config */
1264     NULL,                       /* merge server config */
1265     shib_cmds,                  /* command table */
1266     shib_register_hooks         /* register hooks */
1267 };
1268 } // extern "C"