- Update to Xerces-2.1 support
[shibboleth/sp.git] / mod_shire / mod_shire.cpp
1 /*
2  * mod_shire.cpp -- the SHIRE Apache Module
3  *
4  * Created by:  Derek Atkins <derek@ihtfp.com>
5  *
6  * $Id$
7  */
8
9 // Apache specific header files
10 #include "httpd.h"
11 #include "http_config.h"
12 #include "http_protocol.h"
13 #include "http_main.h"
14 #include "util_script.h"
15 #define CORE_PRIVATE
16 #include "http_core.h"
17 #include "http_log.h"
18
19 // For POST processing from Apache
20 #include <libapreq/apache_request.h>
21
22 #include <unistd.h>
23
24 // SAML Runtime
25 #include <saml.h>
26 #include <shib.h>
27 #include <shib-target.h>
28
29 #include <fstream>
30 #include <strstream>
31 #include <stdexcept>
32
33 using namespace std;
34 using namespace saml;
35 using namespace shibboleth;
36 using namespace shibtarget;
37
38 extern "C" module MODULE_VAR_EXPORT shire_module;
39
40 static char* g_szSHIREConfig = NULL;
41 static RPCHandle *rpc_handle = NULL;
42 static ShibTargetConfig * g_szConfig = NULL;
43
44 // per-server configuration structure
45 struct shire_server_config
46 {
47     char* szCookieName;         // name of session token
48     char* szWAYFLocation;       // URL of WAYF service
49     char* szSHIRELocation;      // URL of SHIRE acceptance point
50     int bSSLOnly;               // SSL only for this SHIRE?
51     int bNormalizeRequest;      // normalize requested URL based on server name?
52 };
53
54 // creates the per-server configuration
55 extern "C" void* create_shire_server_config (pool * p, server_rec * s)
56 {
57     shire_server_config* sc=(shire_server_config*)ap_pcalloc(p,sizeof(shire_server_config));
58     sc->bSSLOnly = -1;
59     sc->bNormalizeRequest = -1;
60     return sc;
61 }
62
63 // overrides server configuration in virtual servers
64 extern "C" void* merge_shire_server_config (pool* p, void* base, void* sub)
65 {
66     shire_server_config* sc=(shire_server_config*)ap_pcalloc(p,sizeof(shire_server_config));
67     shire_server_config* parent=(shire_server_config*)base;
68     shire_server_config* child=(shire_server_config*)sub;
69
70     if (child->szCookieName)
71         sc->szCookieName=ap_pstrdup(p,child->szCookieName);
72     else if (parent->szCookieName)
73         sc->szCookieName=ap_pstrdup(p,parent->szCookieName);
74     else
75         sc->szCookieName=NULL;
76
77     if (child->szWAYFLocation)
78         sc->szWAYFLocation=ap_pstrdup(p,child->szWAYFLocation);
79     else if (parent->szWAYFLocation)
80         sc->szWAYFLocation=ap_pstrdup(p,parent->szWAYFLocation);
81     else
82         sc->szWAYFLocation=NULL;
83
84     if (child->szSHIRELocation)
85         sc->szSHIRELocation=ap_pstrdup(p,child->szSHIRELocation);
86     else if (parent->szSHIRELocation)
87         sc->szSHIRELocation=ap_pstrdup(p,parent->szSHIRELocation);
88     else
89         sc->szSHIRELocation=NULL;
90
91     sc->bSSLOnly=((child->bSSLOnly==-1) ? parent->bSSLOnly : child->bSSLOnly);
92     sc->bNormalizeRequest=((child->bNormalizeRequest==-1) ? parent->bNormalizeRequest : child->bNormalizeRequest);
93     return sc;
94 }
95
96 // per-dir module configuration structure
97 struct shire_dir_config
98 {
99     int bBasicHijack;           // activate for AuthType Basic?
100     int bSSLOnly;               // only over SSL?
101     int checkIPAddress;         // placeholder for check
102     SHIREConfig config;         // SHIRE Configuration
103 };
104
105 // creates per-directory config structure
106 extern "C" void* create_shire_dir_config (pool* p, char* d)
107 {
108     shire_dir_config* dc=(shire_dir_config*)ap_pcalloc(p,sizeof(shire_dir_config));
109     dc->bBasicHijack = -1;
110     dc->bSSLOnly = -1;
111     dc->checkIPAddress = -1;
112     dc->config.lifetime = -1;
113     dc->config.timeout = -1;
114     return dc;
115 }
116
117 // overrides server configuration in directories
118 extern "C" void* merge_shire_dir_config (pool* p, void* base, void* sub)
119 {
120     shire_dir_config* dc=(shire_dir_config*)ap_pcalloc(p,sizeof(shire_dir_config));
121     shire_dir_config* parent=(shire_dir_config*)base;
122     shire_dir_config* child=(shire_dir_config*)sub;
123
124     dc->bSSLOnly=((child->bSSLOnly==-1) ? parent->bSSLOnly : child->bSSLOnly);
125     dc->bBasicHijack=((child->bBasicHijack==-1) ? parent->bBasicHijack : child->bBasicHijack);
126     dc->checkIPAddress=((child->checkIPAddress==-1) ? parent->checkIPAddress : child->checkIPAddress);
127     dc->config.lifetime=((child->config.lifetime==-1) ? parent->config.lifetime : child->config.lifetime);
128     dc->config.timeout=((child->config.timeout==-1) ? parent->config.timeout : child->config.timeout);
129     return dc;
130 }
131
132 // generic global slot handlers
133 extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*, const char* arg)
134 {
135     *((char**)(parms->info))=ap_pstrdup(parms->pool,arg);
136     return NULL;
137 }
138
139 // generic per-server slot handlers
140 extern "C" const char* ap_set_server_string_slot(cmd_parms* parms, void*, const char* arg)
141 {
142     char* base=(char*)ap_get_module_config(parms->server->module_config,&shire_module);
143     int offset=(int)parms->info;
144     *((char**)(base + offset))=ap_pstrdup(parms->pool,arg);
145     return NULL;
146 }
147
148 extern "C" const char* set_normalize(cmd_parms* parms, shire_server_config* sc, const char* arg)
149 {
150     sc->bNormalizeRequest=atoi(arg);
151     return NULL;
152 }
153
154 // some shortcuts for directory config slots
155 extern "C" const char* set_lifetime(cmd_parms* parms, shire_dir_config* dc, const char* arg)
156 {
157     dc->config.lifetime=atoi(arg);
158     return NULL;
159 }
160
161 extern "C" const char* set_timeout(cmd_parms* parms, shire_dir_config* dc, const char* arg)
162 {
163     dc->config.timeout=atoi(arg);
164     return NULL;
165 }
166
167 #ifdef SOLARIS
168 extern "C"
169 #endif
170 typedef const char* (*config_fn_t)(void);
171
172 // SHIRE Module commands
173
174 static command_rec shire_cmds[] = {
175   {"SHIREConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIREConfig,
176    RSRC_CONF, TAKE1, "Path to SHIRE ini file."},
177
178 #if 0
179   {"SHIRELocation", (config_fn_t)ap_set_server_string_slot,
180    (void *) XtOffsetOf (shire_server_config, szSHIRELocation),
181    RSRC_CONF, TAKE1, "URL of SHIRE handle acceptance point."},
182   {"SHIRESSLOnly", (config_fn_t)ap_set_flag_slot,
183    (void *) XtOffsetOf (shire_server_config, bSSLOnly),
184    RSRC_CONF, FLAG, "Require SSL when POSTING to the SHIRE?"},
185   {"WAYFLocation", (config_fn_t)ap_set_server_string_slot,
186    (void *) XtOffsetOf (shire_server_config, szWAYFLocation),
187    RSRC_CONF, TAKE1, "URL of WAYF service."},
188   {"ShibCookieName", (config_fn_t)ap_set_server_string_slot,
189    (void *) XtOffsetOf (shire_server_config, szCookieName),
190    RSRC_CONF, TAKE1, "Name of cookie to use as session token."},
191   {"ShibNormalizeRequest", (config_fn_t)set_normalize, NULL,
192    RSRC_CONF, TAKE1, "Normalize/convert browser requests using server name when redirecting."},
193 #endif
194
195   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
196    (void *) XtOffsetOf (shire_dir_config, bBasicHijack),
197    OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shib?"},
198   {"ShibSSLOnly", (config_fn_t)ap_set_flag_slot,
199    (void *) XtOffsetOf (shire_dir_config, bSSLOnly),
200    OR_AUTHCFG, FLAG, "Require SSL when accessing a secured directory?"},
201   {"ShibCheckAddress", (config_fn_t)ap_set_flag_slot,
202    (void *) XtOffsetOf (shire_dir_config, checkIPAddress),
203    OR_AUTHCFG, FLAG, "Verify IP address of requester matches token?"},
204   {"ShibAuthLifetime", (config_fn_t)set_lifetime, NULL,
205    OR_AUTHCFG, TAKE1, "Lifetime of session in seconds."},
206   {"ShibAuthTimeout", (config_fn_t)set_timeout, NULL,
207    OR_AUTHCFG, TAKE1, "Timeout for session in seconds."},
208
209   {NULL}
210 };
211
212
213
214 /* 
215  * shire_child_init()
216  *  Things to do when the child process is initialized.
217  */
218 extern "C" void shire_child_init(server_rec* s, pool* p)
219 {
220     // Initialize runtime components.
221
222     if (g_szConfig) {
223       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,s,
224                    "shire_child_init(): already initialized!");
225       exit (1);
226     }
227
228     try {
229       g_szConfig = &(ShibTargetConfig::init(SHIBTARGET_SHIRE, g_szSHIREConfig));
230     } catch (runtime_error& e) {
231       ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,
232                    "shire_child_init() failed to initialize SHIB Target");
233       exit (1);
234     }
235
236     // Create the RPC Handle..  Note: this should be per _thread_
237     // if there is some way to do that reasonably..
238     rpc_handle = new RPCHandle(SHIB_SHAR_SOCKET, SHIBRPC_PROG, SHIBRPC_VERS_1);
239
240     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_init() done");
241 }
242
243
244 /*
245  * shire_child_exit()
246  *  Cleanup.
247  */
248 extern "C" void shire_child_exit(server_rec* s, pool* p)
249 {
250     delete rpc_handle;
251     g_szConfig->shutdown();
252     g_szConfig = NULL;
253     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_exit() done");
254 }
255
256 inline char hexchar(unsigned short s)
257 {
258     return (s<=9) ? ('0' + s) : ('A' + s - 10);
259 }
260
261 static char* url_encode(request_rec* r, const char* s)
262 {
263     static char badchars[]="\"\\+<>#%{}|^~[]`;/?:@=&";
264     char* ret=(char*)ap_palloc(r->pool,sizeof(char)*3*strlen(s)+1);
265
266     unsigned long count=0;
267     for (; *s; s++)
268     {
269         if (strchr(badchars,*s)!=NULL || *s<=0x1F || *s>=0x7F)
270         {
271             ret[count++]='%';
272             ret[count++]=hexchar(*s >> 4);
273             ret[count++]=hexchar(*s & 0x0F);
274         }
275         else
276             ret[count++]=*s;
277     }
278     ret[count++]=*s;
279     return ret;
280 }
281
282 static const char* get_target(request_rec* r, const char* target)
283 {
284     shire_server_config* sc=
285         (shire_server_config*)ap_get_module_config(r->server->module_config,&shire_module);
286     if (sc->bNormalizeRequest)
287     {
288         const char* colon=strchr(target,':');
289         const char* slash=strchr(colon+3,'/');
290         const char* second_colon=strchr(colon+3,':');
291         return ap_pstrcat(r->pool,ap_pstrndup(r->pool,target,colon+3-target),ap_get_server_name(r),
292                           (second_colon && second_colon < slash) ? second_colon : slash,NULL);
293     }
294     return target;
295 }
296
297 static const char* get_shire_location(request_rec* r, const char* target, bool encode)
298 {
299   ShibINI& ini = g_szConfig->getINI();
300   const string& shire_location = ini.get (SHIBTARGET_HTTP, "shire");
301
302   const char* shire = shire_location.c_str();
303
304   if (*shire != '/') {
305     if (encode)
306       return url_encode(r,shire);
307     else
308       return ap_pstrdup(r->pool,shire);
309     }    
310     const char* colon=strchr(target,':');
311     const char* slash=strchr(colon+3,'/');
312     if (encode)
313       return url_encode(r,ap_pstrcat(r->pool,
314                                      ap_pstrndup(r->pool,target,slash-target),
315                                      shire,NULL));
316     else
317       return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target),
318                         shire, NULL);
319 }
320
321 static bool is_shire_location(request_rec* r, const char* target)
322 {
323   const char* shire = get_shire_location(r, target, false);
324
325   if (!strstr(target, shire))
326     return false;
327
328   return (!strcmp(target,shire));
329 }
330
331 static int shire_error_page(request_rec* r, const char* filename, ShibMLP& mlp)
332 {
333   ifstream infile (filename);
334   if (!infile) {
335       ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
336                     "shire_error_page() cannot open %s", filename);
337       return SERVER_ERROR;
338   }
339
340   string res = mlp.run(infile);
341   r->content_type = ap_psprintf(r->pool, "text/html");
342   ap_send_http_header(r);
343   ap_rprintf(r, res.c_str());
344   return DONE;
345 }
346
347 extern "C" int shire_check_user(request_rec* r)
348 {
349     ostrstream threadid;
350     threadid << "[" << getpid() << "] shire" << '\0';
351     saml::NDC ndc(threadid.str());
352
353     ShibINI& ini = g_szConfig->getINI();
354     ShibMLP markupProcessor;
355
356     ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
357                   "shire_check_user: ENTER");
358
359     shire_server_config* sc=
360         (shire_server_config*)ap_get_module_config(r->server->module_config,&shire_module);
361     shire_dir_config* dc=
362         (shire_dir_config*)ap_get_module_config(r->per_dir_config,&shire_module);
363
364     const char* targeturl=get_target(r,ap_construct_url(r->pool,r->unparsed_uri,r));
365  
366     const char * shire_location = get_shire_location(r,targeturl,true);
367     string shire_url = get_shire_location(r,targeturl,false);
368     dc->config.checkIPAddress = (dc->checkIPAddress == 1 ? true : false);
369     SHIRE shire(rpc_handle, dc->config, shire_url);
370
371     const string& shib_cookie = ini.get (SHIBTARGET_HTTP, "cookie");
372     const string& wayfLocation = ini.get (SHIBTARGET_HTTP, "wayfLocation");
373     const string& wayfError = ini.get (SHIBTARGET_HTTP, "wayfError");
374
375     string tag;
376     bool has_tag = ini.get_tag (SHIBTARGET_HTTP, "supportContact", true, &tag);
377     markupProcessor.insert ("supportContact", has_tag ? tag : "");
378     has_tag = ini.get_tag (SHIBTARGET_HTTP, "logoLocation", true, &tag);
379     markupProcessor.insert ("logoLocation", has_tag ? tag : "");
380     markupProcessor.insert ("requestURL", targeturl);
381
382     if (is_shire_location (r, targeturl)) {
383       // Process SHIRE POST
384
385       ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
386                     "shire_check_user() Beginning SHIRE POST processing");
387       
388
389       try {
390
391         // Make sure this is SSL, if it should be
392         if (sc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
393           throw ShibTargetException (SHIBRPC_OK,
394                                      "blocked non-SSL access to SHIRE POST processor");
395
396         // Make sure this is a POST
397         if (strcasecmp (r->method, "POST"))
398           throw ShibTargetException (SHIBRPC_OK,
399                                      "blocked non-POST to SHIRE POST processor");
400
401         // Sure sure this POST is an appropriate content type
402         const char *ct = ap_table_get (r->headers_in, "Content-type");
403         if (strcasecmp (ct, "application/x-www-form-urlencoded"))
404           throw ShibTargetException (SHIBRPC_OK,
405                                      ap_psprintf(r->pool,
406                                      "blocked bad content-type to SHIRE POST processor: %s",
407                                                  ct));
408         
409         // Make sure the "bytes sent" is a reasonable number
410         if (r->bytes_sent > 1024*1024) // 1MB?
411           throw ShibTargetException (SHIBRPC_OK,
412                                      "blocked too-large a post to SHIRE POST processor");
413
414         // Read the posted data
415         ApacheRequest *ap_req = ApacheRequest_new(r);
416         int err = ApacheRequest_parse(ap_req);
417         if (err != OK)
418           throw ShibTargetException (SHIBRPC_OK,
419                                      ap_psprintf(r->pool,
420                                      "ApacheRequest_parse() failed with %d.", err));
421
422
423         // Make sure the target parameter exists
424         const char *target = ApacheRequest_param(ap_req, "TARGET");
425         if (!target || *target == '\0')
426           // invalid post
427           throw ShibTargetException (SHIBRPC_OK,
428                                      "SHIRE POST failed to find TARGET");
429
430         // Make sure the SAML Response parameter exists
431         const char *post = ApacheRequest_param(ap_req, "SAMLResponse");
432         if (!post || *post == '\0')
433           // invalid post
434           throw ShibTargetException (SHIBRPC_OK,
435                                      "SHIRE POST failed to find SAMLResponse");
436
437         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
438               "shire_check_user() Processing POST for target: %s", target);
439
440 #if 0 // 2002-09-19
441         post = 
442           "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
443           "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
444           "c3VlSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
445           "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
446           "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
447           "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
448           "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
449           "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
450           "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTE5VDA1OjMwOjAwWiIgSXNzdWVy"
451           "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
452           "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
453           "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
454           "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
455           "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
456           "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
457           "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIEF1dGhl"
458           "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
459           "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
460           "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
461           "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
462           "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
463           "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
464           "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
465           "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
466           "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
467           "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
468           "c3NlcnRpb24+PC9SZXNwb25zZT4K";
469 #endif
470 #if 0 // 2002-09-20
471         post = 
472           "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
473           "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
474           "c3VlSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
475           "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
476           "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
477           "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
478           "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
479           "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
480           "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTIwVDIzOjAwOjAwWiIgSXNzdWVy"
481           "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
482           "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
483           "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
484           "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
485           "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
486           "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
487           "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIEF1dGhl"
488           "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
489           "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
490           "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
491           "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
492           "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
493           "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
494           "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
495           "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
496           "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
497           "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
498           "c3NlcnRpb24+PC9SZXNwb25zZT4K";
499 #endif
500         
501         // process the post
502         string cookie;
503         RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, cookie);
504
505         if (status->isError()) {
506           ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
507                         "shire_check_user() POST process failed (%d): %s",
508                         status->status, status->error_msg.c_str());
509
510           if (status->isRetryable()) {
511             ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
512                           "shire_check_user() Retrying POST by redirecting to WAYF");
513
514             char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
515                                   "?shire=",shire_location,
516                                   "&target=",url_encode(r,target),NULL);
517             ap_table_setn(r->headers_out,"Location",wayf);
518             delete status;
519             return REDIRECT;
520           }
521
522           // return this error to the user.
523           markupProcessor.insert (*status);
524           delete status;
525           return shire_error_page (r, wayfError.c_str(), markupProcessor);
526         }
527         delete status;
528
529         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
530                       "shire_check_user() POST process succeeded.  New cookie: %s",
531                       cookie.c_str());
532
533         // We've got a good session, set the cookie...
534         char * domain = NULL;
535         char * new_cookie = ap_psprintf(r->pool, "%s=%s; path=/%s%s",
536                                         shib_cookie.c_str(),
537                                         cookie.c_str(),
538                                         (domain ? "; domain=" : ""),
539                                         (domain ? domain : ""));
540
541         ap_table_setn(r->err_headers_out, "Set-Cookie", new_cookie);
542         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
543                       "shire_check_user() Set cookie: %s", new_cookie);
544                     
545         // ... and redirect to the target
546         char* redir=ap_pstrcat(r->pool,url_encode(r,target),NULL);
547         ap_table_setn(r->headers_out, "Location", target);
548         return REDIRECT;
549
550       } catch (ShibTargetException &e) {
551         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
552                       "shire_check_user(): %s", e.what());
553         
554         markupProcessor.insert ("errorType", "SHIRE Processing Error");
555         markupProcessor.insert ("errorText", e.what());
556         return shire_error_page (r, wayfError.c_str(), markupProcessor);
557       }
558
559       /**************************************************************************/
560
561     } else {
562       // Regular access to arbitrary resource...check AuthType
563
564       const char *auth_type=ap_auth_type (r);
565       if (!auth_type)
566         return DECLINED;
567
568       if (strcasecmp(auth_type,"shibboleth"))
569       {
570         if (!strcasecmp(auth_type,"basic") && dc->bBasicHijack==1)
571         {
572           core_dir_config* conf=
573             (core_dir_config*)ap_get_module_config(r->per_dir_config,
574                                                    ap_find_linked_module("http_core.c"));
575           conf->ap_auth_type="shibboleth";
576         }
577         else
578           return DECLINED;
579       }
580
581       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
582                     "shire_check_user() Shib check for %s", targeturl);
583
584       // SSL check.
585       if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
586       {
587         ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
588                       "shire_check_user() blocked non-SSL access");
589         return SERVER_ERROR;
590       }
591
592       // We're in charge, so check for cookie.
593       const char* session_id=NULL;
594       const char* cookies=ap_table_get(r->headers_in,"Cookie");
595
596       if (cookies)
597         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
598                       "shire_check_user() cookies found: %s",
599                       cookies);               
600
601       if (!cookies || !(session_id=strstr(cookies,shib_cookie.c_str())))
602       {
603         // No cookie.  Redirect to WAYF.
604         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
605                       "shire_check_user() no cookie found -- redirecting to WAYF");
606         char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
607                               "?shire=",shire_location,
608                               "&target=",url_encode(r,targeturl),NULL);
609         ap_table_setn(r->headers_out,"Location",wayf);
610         return REDIRECT;
611       }
612
613       // Yep, we found a cookie -- pull it out (our session_id)
614       session_id+=strlen(shib_cookie.c_str()) + 1;      /* Skip over the '=' */
615       char* cookiebuf = ap_pstrdup(r->pool,session_id);
616       char* cookieend = strchr(cookiebuf,';');
617       if (cookieend)
618         *cookieend = '\0';      /* Ignore anyting after a ; */
619       session_id=cookiebuf;
620
621       // Make sure this session is still valid
622       RPCError* status = shire.sessionIsValid(session_id, r->connection->remote_ip);
623
624       // Check the status
625       if (status->isError()) {
626
627         ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
628                       "shire_check_user() session invalid: %s",
629                       status->error_msg.c_str());
630
631         // Oops, session is invalid.  Redirect to WAYF.
632         char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
633                               "?shire=",shire_location,
634                               "&target=",url_encode(r,targeturl),NULL);
635         ap_table_setn(r->headers_out,"Location",wayf);
636
637         delete status;
638         return REDIRECT;
639
640       } else {
641         delete status;
642         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
643                       "shire_check_user() success");
644         return OK;
645       }
646     }
647
648     ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
649                   "shire_check_user() server error");
650     return SERVER_ERROR;
651 }
652
653 extern "C"{
654 module MODULE_VAR_EXPORT shire_module = {
655     STANDARD_MODULE_STUFF,
656     NULL,                       /* initializer */
657     create_shire_dir_config,    /* dir config creater */
658     merge_shire_dir_config,     /* dir merger --- default is to override */
659     create_shire_server_config, /* server config */
660     merge_shire_server_config,  /* merge server config */
661     shire_cmds,                 /* command table */
662     NULL,                       /* handlers */
663     NULL,                       /* filename translation */
664     shire_check_user,           /* check_user_id */
665     NULL,                       /* check auth */
666     NULL,                       /* check access */
667     NULL,                       /* type_checker */
668     NULL,                       /* fixups */
669     NULL,                       /* logger */
670     NULL,                       /* header parser */
671     shire_child_init,           /* child_init */
672     shire_child_exit,           /* child_exit */
673     NULL                        /* post read-request */
674 };
675 }