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