update sample apache config with a few more comments
[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) || !strcasecmp(arg, "on"));
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 #endif
192
193   {"ShibNormalizeRequest", (config_fn_t)set_normalize, NULL,
194    RSRC_CONF, TAKE1, "Normalize/convert browser requests using server name when redirecting."},
195
196   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
197    (void *) XtOffsetOf (shire_dir_config, bBasicHijack),
198    OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shib?"},
199   {"ShibSSLOnly", (config_fn_t)ap_set_flag_slot,
200    (void *) XtOffsetOf (shire_dir_config, bSSLOnly),
201    OR_AUTHCFG, FLAG, "Require SSL when accessing a secured directory?"},
202   {"ShibCheckAddress", (config_fn_t)ap_set_flag_slot,
203    (void *) XtOffsetOf (shire_dir_config, checkIPAddress),
204    OR_AUTHCFG, FLAG, "Verify IP address of requester matches token?"},
205   {"ShibAuthLifetime", (config_fn_t)set_lifetime, NULL,
206    OR_AUTHCFG, TAKE1, "Lifetime of session in seconds."},
207   {"ShibAuthTimeout", (config_fn_t)set_timeout, NULL,
208    OR_AUTHCFG, TAKE1, "Timeout for session in seconds."},
209
210   {NULL}
211 };
212
213
214
215 /* 
216  * shire_child_init()
217  *  Things to do when the child process is initialized.
218  */
219 extern "C" void shire_child_init(server_rec* s, pool* p)
220 {
221     // Initialize runtime components.
222
223     if (g_szConfig) {
224       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,s,
225                    "shire_child_init(): already initialized!");
226       exit (1);
227     }
228
229     try {
230       g_szConfig = &(ShibTargetConfig::init(SHIBTARGET_SHIRE, g_szSHIREConfig));
231     } catch (runtime_error& e) {
232       ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,
233                    "shire_child_init() failed to initialize SHIB Target");
234       exit (1);
235     }
236
237     // Create the RPC Handle..  Note: this should be per _thread_
238     // if there is some way to do that reasonably..
239     rpc_handle = new RPCHandle(SHIB_SHAR_SOCKET, SHIBRPC_PROG, SHIBRPC_VERS_1);
240
241     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_init() done");
242 }
243
244
245 /*
246  * shire_child_exit()
247  *  Cleanup.
248  */
249 extern "C" void shire_child_exit(server_rec* s, pool* p)
250 {
251     delete rpc_handle;
252     g_szConfig->shutdown();
253     g_szConfig = NULL;
254     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_exit() done");
255 }
256
257 inline char hexchar(unsigned short s)
258 {
259     return (s<=9) ? ('0' + s) : ('A' + s - 10);
260 }
261
262 static char* url_encode(request_rec* r, const char* s)
263 {
264     static char badchars[]="\"\\+<>#%{}|^~[]`;/?:@=&";
265     char* ret=(char*)ap_palloc(r->pool,sizeof(char)*3*strlen(s)+1);
266
267     unsigned long count=0;
268     for (; *s; s++)
269     {
270         if (strchr(badchars,*s)!=NULL || *s<=0x1F || *s>=0x7F)
271         {
272             ret[count++]='%';
273             ret[count++]=hexchar(*s >> 4);
274             ret[count++]=hexchar(*s & 0x0F);
275         }
276         else
277             ret[count++]=*s;
278     }
279     ret[count++]=*s;
280     return ret;
281 }
282
283 static const char* get_target(request_rec* r, const char* target)
284 {
285     shire_server_config* sc=
286         (shire_server_config*)ap_get_module_config(r->server->module_config,&shire_module);
287     if (sc->bNormalizeRequest)
288     {
289         const char* colon=strchr(target,':');
290         const char* slash=strchr(colon+3,'/');
291         const char* second_colon=strchr(colon+3,':');
292         return ap_pstrcat(r->pool,ap_pstrndup(r->pool,target,colon+3-target),ap_get_server_name(r),
293                           (second_colon && second_colon < slash) ? second_colon : slash,NULL);
294     }
295     return target;
296 }
297
298 static const char* get_shire_location(request_rec* r, const char* target, bool encode)
299 {
300   ShibINI& ini = g_szConfig->getINI();
301   const string& shire_location = ini.get (SHIBTARGET_HTTP, "shire");
302
303   const char* shire = shire_location.c_str();
304
305   if (*shire != '/') {
306     if (encode)
307       return url_encode(r,shire);
308     else
309       return ap_pstrdup(r->pool,shire);
310     }    
311     const char* colon=strchr(target,':');
312     const char* slash=strchr(colon+3,'/');
313     if (encode)
314       return url_encode(r,ap_pstrcat(r->pool,
315                                      ap_pstrndup(r->pool,target,slash-target),
316                                      shire,NULL));
317     else
318       return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target),
319                         shire, NULL);
320 }
321
322 static bool is_shire_location(request_rec* r, const char* target)
323 {
324   const char* shire = get_shire_location(r, target, false);
325
326   if (!strstr(target, shire))
327     return false;
328
329   return (!strcmp(target,shire));
330 }
331
332 static int shire_error_page(request_rec* r, const char* filename, ShibMLP& mlp)
333 {
334   ifstream infile (filename);
335   if (!infile) {
336       ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
337                     "shire_error_page() cannot open %s", filename);
338       return SERVER_ERROR;
339   }
340
341   string res = mlp.run(infile);
342   r->content_type = ap_psprintf(r->pool, "text/html");
343   ap_send_http_header(r);
344   ap_rprintf(r, res.c_str());
345   return DONE;
346 }
347
348 extern "C" int shire_check_user(request_rec* r)
349 {
350     ostrstream threadid;
351     threadid << "[" << getpid() << "] shire" << '\0';
352     saml::NDC ndc(threadid.str());
353
354     ShibINI& ini = g_szConfig->getINI();
355     ShibMLP markupProcessor;
356
357     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
358                   "shire_check_user: ENTER");
359
360     shire_server_config* sc=
361         (shire_server_config*)ap_get_module_config(r->server->module_config,&shire_module);
362     shire_dir_config* dc=
363         (shire_dir_config*)ap_get_module_config(r->per_dir_config,&shire_module);
364
365     const char* targeturl=get_target(r,ap_construct_url(r->pool,r->unparsed_uri,r));
366  
367     const char * shire_location = get_shire_location(r,targeturl,true);
368     string shire_url = get_shire_location(r,targeturl,false);
369     dc->config.checkIPAddress = (dc->checkIPAddress == 1 ? true : false);
370     SHIRE shire(rpc_handle, dc->config, shire_url);
371
372     const string& shib_cookie = ini.get (SHIBTARGET_HTTP, "cookie");
373     const string& wayfLocation = ini.get (SHIBTARGET_HTTP, "wayfLocation");
374     const string& wayfError = ini.get (SHIBTARGET_HTTP, "wayfError");
375
376     string tag;
377     bool has_tag = ini.get_tag (SHIBTARGET_HTTP, "supportContact", true, &tag);
378     markupProcessor.insert ("supportContact", has_tag ? tag : "");
379     has_tag = ini.get_tag (SHIBTARGET_HTTP, "logoLocation", true, &tag);
380     markupProcessor.insert ("logoLocation", has_tag ? tag : "");
381     markupProcessor.insert ("requestURL", targeturl);
382
383     if (is_shire_location (r, targeturl)) {
384       // Process SHIRE POST
385
386       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
387                     "shire_check_user() Beginning SHIRE POST processing");
388       
389
390       try {
391
392         const string& sslonly = ini.get (SHIBTARGET_HTTP, "shireSSLOnly");
393         const char* sslonlyc = sslonly.c_str();
394         
395         // Make sure this is SSL, if it should be
396         if ((*sslonlyc == 't' || *sslonlyc == 'T') &&
397             strcmp(ap_http_method(r),"https"))
398           throw ShibTargetException (SHIBRPC_OK,
399                                      "blocked non-SSL access to SHIRE POST processor");
400
401         // Make sure this is a POST
402         if (strcasecmp (r->method, "POST"))
403           throw ShibTargetException (SHIBRPC_OK,
404                                      "blocked non-POST to SHIRE POST processor");
405
406         // Sure sure this POST is an appropriate content type
407         const char *ct = ap_table_get (r->headers_in, "Content-type");
408         if (!ct || strcasecmp (ct, "application/x-www-form-urlencoded"))
409           throw ShibTargetException (SHIBRPC_OK,
410                                      ap_psprintf(r->pool,
411                                      "blocked bad content-type to SHIRE POST processor: %s",
412                                                  (ct ? ct : "")));
413         
414         // Make sure the "bytes sent" is a reasonable number
415         if (r->bytes_sent > 1024*1024) // 1MB?
416           throw ShibTargetException (SHIBRPC_OK,
417                                      "blocked too-large a post to SHIRE POST processor");
418
419         // Read the posted data
420         ApacheRequest *ap_req = ApacheRequest_new(r);
421         int err = ApacheRequest_parse(ap_req);
422         if (err != OK)
423           throw ShibTargetException (SHIBRPC_OK,
424                                      ap_psprintf(r->pool,
425                                      "ApacheRequest_parse() failed with %d.", err));
426
427
428         // Make sure the target parameter exists
429         const char *target = ApacheRequest_param(ap_req, "TARGET");
430         if (!target || *target == '\0')
431           // invalid post
432           throw ShibTargetException (SHIBRPC_OK,
433                                      "SHIRE POST failed to find TARGET");
434
435         // Make sure the SAML Response parameter exists
436         const char *post = ApacheRequest_param(ap_req, "SAMLResponse");
437         if (!post || *post == '\0')
438           // invalid post
439           throw ShibTargetException (SHIBRPC_OK,
440                                      "SHIRE POST failed to find SAMLResponse");
441
442         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
443               "shire_check_user() Processing POST for target: %s", target);
444
445 #if 0 // 2002-09-19
446         post = 
447           "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
448           "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
449           "c3VlSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
450           "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
451           "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
452           "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
453           "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
454           "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
455           "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTE5VDA1OjMwOjAwWiIgSXNzdWVy"
456           "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
457           "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
458           "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
459           "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
460           "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
461           "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
462           "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIEF1dGhl"
463           "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
464           "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
465           "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
466           "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
467           "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
468           "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
469           "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
470           "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
471           "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
472           "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
473           "c3NlcnRpb24+PC9SZXNwb25zZT4K";
474 #endif
475 #if 0 // 2002-09-20
476         post = 
477           "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
478           "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
479           "c3VlSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
480           "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
481           "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
482           "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
483           "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
484           "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
485           "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTIwVDIzOjAwOjAwWiIgSXNzdWVy"
486           "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
487           "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
488           "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
489           "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
490           "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
491           "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
492           "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIEF1dGhl"
493           "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
494           "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
495           "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
496           "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
497           "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
498           "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
499           "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
500           "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
501           "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
502           "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
503           "c3NlcnRpb24+PC9SZXNwb25zZT4K";
504 #endif
505         
506         // process the post
507         string cookie;
508         RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, cookie);
509
510         if (status->isError()) {
511           ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
512                         "shire_check_user() POST process failed (%d): %s",
513                         status->status, status->error_msg.c_str());
514
515           if (status->isRetryable()) {
516             ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
517                           "shire_check_user() Retrying POST by redirecting to WAYF");
518
519             char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
520                                   "?shire=",shire_location,
521                                   "&target=",url_encode(r,target),NULL);
522             ap_table_setn(r->headers_out,"Location",wayf);
523             delete status;
524             return REDIRECT;
525           }
526
527           // return this error to the user.
528           markupProcessor.insert (*status);
529           delete status;
530           return shire_error_page (r, wayfError.c_str(), markupProcessor);
531         }
532         delete status;
533
534         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
535                       "shire_check_user() POST process succeeded.  New cookie: %s",
536                       cookie.c_str());
537
538         // We've got a good session, set the cookie...
539         char * domain = NULL;
540         char * new_cookie = ap_psprintf(r->pool, "%s=%s; path=/%s%s",
541                                         shib_cookie.c_str(),
542                                         cookie.c_str(),
543                                         (domain ? "; domain=" : ""),
544                                         (domain ? domain : ""));
545
546         ap_table_setn(r->err_headers_out, "Set-Cookie", new_cookie);
547         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
548                       "shire_check_user() Set cookie: %s", new_cookie);
549                     
550         // ... and redirect to the target
551         char* redir=ap_pstrcat(r->pool,url_encode(r,target),NULL);
552         ap_table_setn(r->headers_out, "Location", target);
553         return REDIRECT;
554
555       } catch (ShibTargetException &e) {
556         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
557                       "shire_check_user(): %s", e.what());
558         
559         markupProcessor.insert ("errorType", "SHIRE Processing Error");
560         markupProcessor.insert ("errorText", e.what());
561         return shire_error_page (r, wayfError.c_str(), markupProcessor);
562       }
563
564       /**************************************************************************/
565
566     } else {
567       // Regular access to arbitrary resource...check AuthType
568
569       const char *auth_type=ap_auth_type (r);
570       if (!auth_type)
571         return DECLINED;
572
573       if (strcasecmp(auth_type,"shibboleth"))
574       {
575         if (!strcasecmp(auth_type,"basic") && dc->bBasicHijack==1)
576         {
577           core_dir_config* conf=
578             (core_dir_config*)ap_get_module_config(r->per_dir_config,
579                                                    ap_find_linked_module("http_core.c"));
580           conf->ap_auth_type="shibboleth";
581         }
582         else
583           return DECLINED;
584       }
585
586       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
587                     "shire_check_user() Shib check for %s", targeturl);
588
589       // SSL check.
590       if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
591       {
592         ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
593                       "shire_check_user() blocked non-SSL access");
594         return SERVER_ERROR;
595       }
596
597       // We're in charge, so check for cookie.
598       const char* session_id=NULL;
599       const char* cookies=ap_table_get(r->headers_in,"Cookie");
600
601       if (cookies)
602         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
603                       "shire_check_user() cookies found: %s",
604                       cookies);               
605
606       if (!cookies || !(session_id=strstr(cookies,shib_cookie.c_str())))
607       {
608         // No cookie.  Redirect to WAYF.
609         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
610                       "shire_check_user() no cookie found -- redirecting to WAYF");
611         char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
612                               "?shire=",shire_location,
613                               "&target=",url_encode(r,targeturl),NULL);
614         ap_table_setn(r->headers_out,"Location",wayf);
615         return REDIRECT;
616       }
617
618       // Yep, we found a cookie -- pull it out (our session_id)
619       session_id+=strlen(shib_cookie.c_str()) + 1;      /* Skip over the '=' */
620       char* cookiebuf = ap_pstrdup(r->pool,session_id);
621       char* cookieend = strchr(cookiebuf,';');
622       if (cookieend)
623         *cookieend = '\0';      /* Ignore anyting after a ; */
624       session_id=cookiebuf;
625
626       // Make sure this session is still valid
627       RPCError* status = shire.sessionIsValid(session_id, r->connection->remote_ip);
628
629       // Check the status
630       if (status->isError()) {
631
632         ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
633                       "shire_check_user() session invalid: %s",
634                       status->error_msg.c_str());
635
636         // Oops, session is invalid.  Redirect to WAYF.
637         char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
638                               "?shire=",shire_location,
639                               "&target=",url_encode(r,targeturl),NULL);
640         ap_table_setn(r->headers_out,"Location",wayf);
641
642         delete status;
643         return REDIRECT;
644
645       } else {
646         delete status;
647         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
648                       "shire_check_user() success");
649         return OK;
650       }
651     }
652
653     ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
654                   "shire_check_user() server error");
655     return SERVER_ERROR;
656 }
657
658 extern "C"{
659 module MODULE_VAR_EXPORT shire_module = {
660     STANDARD_MODULE_STUFF,
661     NULL,                       /* initializer */
662     create_shire_dir_config,    /* dir config creater */
663     merge_shire_dir_config,     /* dir merger --- default is to override */
664     create_shire_server_config, /* server config */
665     merge_shire_server_config,  /* merge server config */
666     shire_cmds,                 /* command table */
667     NULL,                       /* handlers */
668     NULL,                       /* filename translation */
669     shire_check_user,           /* check_user_id */
670     NULL,                       /* check auth */
671     NULL,                       /* check access */
672     NULL,                       /* type_checker */
673     NULL,                       /* fixups */
674     NULL,                       /* logger */
675     NULL,                       /* header parser */
676     shire_child_init,           /* child_init */
677     shire_child_exit,           /* child_exit */
678     NULL                        /* post read-request */
679 };
680 }