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