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