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