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