Redesigned target around URL->application mapping
[shibboleth/sp.git] / mod_shire / mod_shire.cpp
index 8b63d12..615f1f6 100644 (file)
@@ -6,6 +6,12 @@
  * $Id$
  */
 
+// SAML Runtime
+#include <saml/saml.h>
+#include <shib/shib.h>
+#include <shib/shib-threads.h>
+#include <shib-target/shib-target.h>
+
 // Apache specific header files
 #include "httpd.h"
 #include "http_config.h"
 #include "http_core.h"
 #include "http_log.h"
 
-// For POST processing from Apache
-#include <libapreq/apache_request.h>
-
-// SAML Runtime
-#include <saml/saml.h>
-#include <shib/shib.h>
-#include <shib-target/shib-target.h>
-
 #include <fstream>
-#include <strstream>
+#include <sstream>
 #include <stdexcept>
 
+// For POST processing from Apache
+#undef _XOPEN_SOURCE           // bombs on solaris
+#include <libapreq/apache_request.h>
+
 using namespace std;
 using namespace saml;
 using namespace shibboleth;
@@ -36,39 +38,10 @@ using namespace shibtarget;
 extern "C" module MODULE_VAR_EXPORT shire_module;
 
 namespace {
+    char* g_szSHIREURL = NULL;
     char* g_szSHIREConfig = NULL;
-    RPCHandle *rpc_handle = NULL;
-    ShibTargetConfig * g_szConfig = NULL;
-}
-
-// per-server configuration structure
-struct shire_server_config
-{
-    char* serverName;          // Name of this server
-};
-
-// creates the per-server configuration
-extern "C" void* create_shire_server_config (pool * p, server_rec * s)
-{
-    shire_server_config* sc=(shire_server_config*)ap_pcalloc(p,sizeof(shire_server_config));
-    return sc;
-}
-
-// overrides server configuration in virtual servers
-extern "C" void* merge_shire_server_config (pool* p, void* base, void* sub)
-{
-    shire_server_config* sc=(shire_server_config*)ap_pcalloc(p,sizeof(shire_server_config));
-    shire_server_config* parent=(shire_server_config*)base;
-    shire_server_config* child=(shire_server_config*)sub;
-
-    if (child->serverName)
-        sc->serverName=ap_pstrdup(p,child->serverName);
-    else if (parent->serverName)
-        sc->serverName=ap_pstrdup(p,parent->serverName);
-    else
-        sc->serverName=NULL;
-
-    return sc;
+    ThreadKey* rpc_handle_key = NULL;
+    ShibTargetConfig* g_Config = NULL;
 }
 
 // per-dir module configuration structure
@@ -111,15 +84,6 @@ extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*, const
     return NULL;
 }
 
-// generic per-server slot handlers
-extern "C" const char* ap_set_server_string_slot(cmd_parms* parms, void*, const char* arg)
-{
-    char* base=(char*)ap_get_module_config(parms->server->module_config,&shire_module);
-    int offset=(int)parms->info;
-    *((char**)(base + offset))=ap_pstrdup(parms->pool,arg);
-    return NULL;
-}
-
 // some shortcuts for directory config slots
 extern "C" const char* set_lifetime(cmd_parms* parms, shire_dir_config* dc, const char* arg)
 {
@@ -133,9 +97,6 @@ extern "C" const char* set_timeout(cmd_parms* parms, shire_dir_config* dc, const
     return NULL;
 }
 
-#ifdef SOLARIS
-extern "C"
-#endif
 typedef const char* (*config_fn_t)(void);
 
 // SHIRE Module commands
@@ -143,6 +104,8 @@ typedef const char* (*config_fn_t)(void);
 static command_rec shire_cmds[] = {
   {"SHIREConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIREConfig,
    RSRC_CONF, TAKE1, "Path to SHIRE ini file."},
+  {"SHIREURL", (config_fn_t)ap_set_global_string_slot, &g_szSHIREURL,
+   RSRC_CONF, TAKE1, "SHIRE POST processor URL."},
 
   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shire_dir_config, bBasicHijack),
@@ -158,6 +121,12 @@ static command_rec shire_cmds[] = {
   {NULL}
 };
 
+namespace {
+    void destroy_handle(void* data)
+    {
+        delete (RPCHandle*)data;
+    }
+}
 
 /* 
  * shire_child_init()
@@ -167,23 +136,25 @@ extern "C" void shire_child_init(server_rec* s, pool* p)
 {
     // Initialize runtime components.
 
-    if (g_szConfig) {
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,
+                "shire_child_init() starting");
+
+    if (g_Config) {
       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,s,
                   "shire_child_init(): already initialized!");
       exit (1);
     }
 
     try {
-      g_szConfig = &(ShibTargetConfig::init(SHIBTARGET_SHIRE, g_szSHIREConfig));
-    } catch (runtime_error& e) {
+      g_Config = &(ShibTargetConfig::init(SHIBTARGET_SHIRE, g_szSHIREConfig));
+    } catch (...) {
       ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,
                   "shire_child_init() failed to initialize SHIB Target");
       exit (1);
     }
 
-    // Create the RPC Handle..  Note: this should be per _thread_
-    // if there is some way to do that reasonably..
-    rpc_handle = new RPCHandle(SHIB_SHAR_SOCKET, SHIBRPC_PROG, SHIBRPC_VERS_1);
+    // Create the RPC Handle TLS key.
+    rpc_handle_key=ThreadKey::create(destroy_handle);
 
     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_init() done");
 }
@@ -195,9 +166,9 @@ extern "C" void shire_child_init(server_rec* s, pool* p)
  */
 extern "C" void shire_child_exit(server_rec* s, pool* p)
 {
-    delete rpc_handle;
-    g_szConfig->shutdown();
-    g_szConfig = NULL;
+    delete rpc_handle_key;
+    g_Config->shutdown();
+    g_Config = NULL;
     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,s,"shire_child_exit() done");
 }
 
@@ -227,83 +198,35 @@ static char* url_encode(request_rec* r, const char* s)
     return ret;
 }
 
-// Return the "name" of this server to look up configuration options
-static const char* get_service_name(request_rec* r)
+static const char* get_application_id(request_rec* r)
 {
-  shire_server_config* sc =
-    (shire_server_config*) ap_get_module_config(r->server->module_config,
-                                               &shire_module);
-
-  if (sc->serverName)
-    return sc->serverName;
-
-  return ap_get_server_name(r);
+    ApplicationMapper mapper;
+    return ap_pstrdup(r->pool,
+        mapper->getApplicationFromParsedURL(
+            ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
+            )
+       );
 }
 
-// return the "normalized" target URL
-static const char* get_target(request_rec* r, const char* target)
+static const char* get_shire_location(request_rec* r, const char* target, const char* application_id)
 {
-  const char* serverName = get_service_name(r);
-  string tag;
-  if ((g_szConfig->getINI()).get_tag (serverName, "normalizeRequest", true, &tag))
-  {
-    if (ShibINI::boolean (tag))
-    {
-        const char* colon=strchr(target,':');
-        const char* slash=strchr(colon+3,'/');
-        const char* second_colon=strchr(colon+3,':');
-        return ap_pstrcat(r->pool,ap_pstrndup(r->pool,target,colon+3-target),
-                         ap_get_server_name(r),
-                         (second_colon && second_colon < slash) ?
-                         second_colon : slash,
-                         NULL);
-    }
-  }
-  return target;
-}
-
-static const char* get_shire_location(request_rec* r, const char* target, bool encode)
-{
-  ShibINI& ini = g_szConfig->getINI();
-  const char* serverName = get_service_name(r);
   string shire_location;
+  ShibINI& ini = g_Config->getINI();
 
-  if (! ini.get_tag (serverName, "shireURL", true, &shire_location)) {
+  if (!ini.get_tag (application_id, "shireURL", true, &shire_location)) {
     ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
-                 "shire_get_location() no shireURL configuration for %s",
-                 serverName);
+                 "shire_get_location() no shireURL configuration for %s", application_id);
     return NULL;
   }
 
   const char* shire = shire_location.c_str();
 
-  if (*shire != '/') {
-    if (encode)
-      return url_encode(r,shire);
-    else
+  if (*shire != '/')
       return ap_pstrdup(r->pool,shire);
-    }    
-    const char* colon=strchr(target,':');
-    const char* slash=strchr(colon+3,'/');
-    if (encode)
-      return url_encode(r,ap_pstrcat(r->pool,
-                                    ap_pstrndup(r->pool,target,slash-target),
-                                    shire,NULL));
-    else
-      return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target),
-                       shire, NULL);
-}
-
-static bool is_shire_location(request_rec* r, const char* target)
-{
-  const char* shire = get_shire_location(r, target, false);
 
-  if (!shire) return false;
-
-  if (!strstr(target, shire))
-    return false;
-
-  return (!strcmp(target,shire));
+  const char* colon=strchr(target,':');
+  const char* slash=strchr(colon+3,'/');
+  return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target), shire, NULL);
 }
 
 static int shire_error_page(request_rec* r, const char* filename, ShibMLP& mlp)
@@ -324,348 +247,400 @@ static int shire_error_page(request_rec* r, const char* filename, ShibMLP& mlp)
 
 extern "C" int shire_check_user(request_rec* r)
 {
-    ostrstream threadid;
-    threadid << "[" << getpid() << "] shire" << '\0';
-    saml::NDC ndc(threadid.str());
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user: ENTER");
+    shire_dir_config* dc=(shire_dir_config*)ap_get_module_config(r->per_dir_config,&shire_module);
+
+    // This will always be normalized, because Apache uses ap_get_server_name in this API call.
+    const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r);
+    
+    // Map request to application ID, which is the key for config lookup.
+    const char* application_id=get_application_id(r);
+    
+    // Get unescaped location of this application's assertion consumer service.
+    const char* unescaped_shire = get_shire_location(r, targeturl, application_id);
+    
+    if (strstr(targeturl,unescaped_shire)) {
+      ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
+           "shire_check_user: REQUEST FOR SHIRE!  Maybe you did not configure the SHIRE Handler?");
+      return SERVER_ERROR;
+    }
+    else {
+      // Regular access to arbitrary resource...check AuthType
+      const char *auth_type=ap_auth_type (r);
+      if (!auth_type)
+        return DECLINED;
 
-    ShibINI& ini = g_szConfig->getINI();
-    ShibMLP markupProcessor;
+      if (strcasecmp(auth_type,"shibboleth"))
+      {
+        if (!strcasecmp(auth_type,"basic") && dc->bBasicHijack==1)
+        {
+            core_dir_config* conf=
+                (core_dir_config*)ap_get_module_config(r->per_dir_config,
+                    ap_find_linked_module("http_core.c"));
+            conf->ap_auth_type="shibboleth";
+        }
+        else
+            return DECLINED;
+      }
 
-    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                 "shire_check_user: ENTER");
+      // set the connection authtype
+      if (r->connection)
+        r->connection->ap_auth_type = "shibboleth";
+
+      // SSL check.
+      if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
+      {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,"shire_check_user() blocked non-SSL access");
+        return SERVER_ERROR;
+      }
+    }
+
+    ostringstream threadid;
+    threadid << "[" << getpid() << "] shire" << '\0';
+    saml::NDC ndc(threadid.str().c_str());
 
-    shire_dir_config* dc=
-        (shire_dir_config*)ap_get_module_config(r->per_dir_config,&shire_module);
+    ShibINI& ini = g_Config->getINI();
 
-    const char* targeturl=get_target(r,ap_construct_url(r->pool,r->unparsed_uri,r));
-    const char * shire_location = get_shire_location(r,targeturl,true);
-    if (!shire_location) return SERVER_ERROR;
-    string shire_url = get_shire_location(r,targeturl,false);
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                    "shire_check_user() Shib check for %s", targeturl);
 
-    const char* serverName = get_service_name (r);
+    
     string tag;
-    bool has_tag = ini.get_tag (serverName, "checkIPAddress", true, &tag);
+    bool has_tag = ini.get_tag (application_id, "checkIPAddress", true, &tag);
     dc->config.checkIPAddress = (has_tag ? ShibINI::boolean (tag) : false);
 
     string shib_cookie;
-    if (! ini.get_tag (serverName, "cookieName", true, &shib_cookie)) {
+    if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) {
       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
-                   "shire_check_user: no cookieName configuration for %s",
-                   serverName);
+                   "shire_check_user: no cookieName configuration for %s", application_id);
       return SERVER_ERROR;
     }
 
     string wayfLocation;
-    if (! ini.get_tag (serverName, "wayfURL", true, &wayfLocation)) {
+    if (!ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) {
       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
-                   "shire_check_user: no wayfURL configuration for %s",
-                   serverName);
+                   "shire_check_user: no wayfURL configuration for %s", application_id);
       return SERVER_ERROR;
     }
 
-    string wayfError;
-    if (! ini.get_tag (serverName, "wayfError", true, &wayfError)) {
+    string shireError;
+    if (!ini.get_tag(application_id, "shireError", true, &shireError)) {
       ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
-                   "shire_check_user: no wayfError configuration for %s",
-                   serverName);
+                   "shire_check_user: no shireError configuration for %s", application_id);
       return SERVER_ERROR;
     }
+    
+    // Get an RPC handle and build the SHIRE object.
+    RPCHandle* rpc_handle = (RPCHandle*)rpc_handle_key->getData();
+    if (!rpc_handle)
+    {
+        rpc_handle = new RPCHandle(shib_target_sockname(), SHIBRPC_PROG, SHIBRPC_VERS_1);
+        rpc_handle_key->setData(rpc_handle);
+    }
+    SHIRE shire(rpc_handle, dc->config, unescaped_shire);
+
+    // We're in charge, so check for cookie.
+    const char* session_id=NULL;
+    const char* cookies=ap_table_get(r->headers_in,"Cookie");
 
-    ini.get_tag (serverName, "supportContact", true, &tag);
-    markupProcessor.insert ("supportContact", has_tag ? tag : "");
-    has_tag = ini.get_tag (serverName, "logoLocation", true, &tag);
-    markupProcessor.insert ("logoLocation", has_tag ? tag : "");
-    markupProcessor.insert ("requestURL", targeturl);
+    if (cookies)
+    {
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                        "shire_check_user() cookies found: %s",cookies);
+        if (session_id=strstr(cookies,shib_cookie.c_str()))
+        {
+            // Yep, we found a cookie -- pull it out (our session_id)
+            session_id+=strlen(shib_cookie.c_str()) + 1; /* Skip over the '=' */
+            char* cookiebuf = ap_pstrdup(r->pool,session_id);
+            char* cookieend = strchr(cookiebuf,';');
+            if (cookieend)
+                *cookieend = '\0';    /* Ignore anyting after a ; */
+            session_id=cookiebuf;
+        }
+    }
 
-    SHIRE shire(rpc_handle, dc->config, shire_url);
+    if (!session_id || !*session_id)
+    {
+        // No acceptable cookie.  Redirect to WAYF.
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                     "shire_check_user() no cookie found -- redirecting to WAYF");
+        char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
+                             "?shire=",url_encode(r,unescaped_shire),
+                             "&target=",url_encode(r,targeturl),NULL);
+        ap_table_setn(r->headers_out,"Location",wayf);
+        return REDIRECT;
+    }
 
-    if (is_shire_location (r, targeturl)) {
-      // Process SHIRE POST
+    // Make sure this session is still valid
+    RPCError* status = NULL;
+    ShibMLP markupProcessor;
+    has_tag = ini.get_tag(application_id, "supportContact", true, &tag);
+    markupProcessor.insert("supportContact", has_tag ? tag : "");
+    has_tag = ini.get_tag(application_id, "logoLocation", true, &tag);
+    markupProcessor.insert("logoLocation", has_tag ? tag : "");
+    markupProcessor.insert("requestURL", targeturl);
 
-      ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                   "shire_check_user() Beginning SHIRE POST processing");
-      
+    try {
+        status = shire.sessionIsValid(session_id, r->connection->remote_ip,application_id);
+    }
+    catch (ShibTargetException &e) {
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user(): %s", e.what());
+        markupProcessor.insert ("errorType", "SHIRE Processing Error");
+        markupProcessor.insert ("errorText", e.what());
+        markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
+        return shire_error_page (r, shireError.c_str(), markupProcessor);
+    }
+    catch (...) {
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user(): caught unexpected error");
+        markupProcessor.insert ("errorType", "SHIRE Processing Error");
+        markupProcessor.insert ("errorText", "Unexpected Exception");
+        markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
+        return shire_error_page (r, shireError.c_str(), markupProcessor);
+    }
 
-      try {
+    // Check the status
+    if (status->isError()) {
+        ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
+                     "shire_check_user() session invalid: %s",
+                     status->getText());
+
+        if (status->isRetryable()) {
+            // Oops, session is invalid.  Redirect to WAYF.
+            char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
+                               "?shire=",url_encode(r,unescaped_shire),
+                               "&target=",url_encode(r,targeturl),NULL);
+            ap_table_setn(r->headers_out,"Location",wayf);
+
+            delete status;
+            return REDIRECT;
+        }
+        else {
+            // return the error page to the user
+            markupProcessor.insert (*status);
+            delete status;
+            return shire_error_page (r, shireError.c_str(), markupProcessor);
+        }
+    }
+    else {
+        delete status;
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user() success");
+        return OK;
+    }
 
-       string sslonly;
-       if (! ini.get_tag (serverName, "shireSSLOnly", true, &sslonly))
-         ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
-                       "shire_check_user: no shireSSLOnly configuration");
-       
-       // Make sure this is SSL, if it should be
-       if (ShibINI::boolean(sslonly) && strcmp(ap_http_method(r),"https"))
-         throw ShibTargetException (SHIBRPC_OK,
-                                    "blocked non-SSL access to SHIRE POST processor");
-
-       // Make sure this is a POST
-       if (strcasecmp (r->method, "POST"))
-         throw ShibTargetException (SHIBRPC_OK,
-                                    "blocked non-POST to SHIRE POST processor");
-
-       // Sure sure this POST is an appropriate content type
-       const char *ct = ap_table_get (r->headers_in, "Content-type");
-       if (!ct || strcasecmp (ct, "application/x-www-form-urlencoded"))
-         throw ShibTargetException (SHIBRPC_OK,
-                                    ap_psprintf(r->pool,
-                                    "blocked bad content-type to SHIRE POST processor: %s",
-                                                (ct ? ct : "")));
-       
-       // Make sure the "bytes sent" is a reasonable number
-       if (r->bytes_sent > 1024*1024) // 1MB?
-         throw ShibTargetException (SHIBRPC_OK,
-                                    "blocked too-large a post to SHIRE POST processor");
-
-       // Read the posted data
-       ApacheRequest *ap_req = ApacheRequest_new(r);
-       int err = ApacheRequest_parse(ap_req);
-       if (err != OK)
-         throw ShibTargetException (SHIBRPC_OK,
-                                    ap_psprintf(r->pool,
-                                    "ApacheRequest_parse() failed with %d.", err));
-
-
-       // Make sure the target parameter exists
-       const char *target = ApacheRequest_param(ap_req, "TARGET");
-       if (!target || *target == '\0')
-         // invalid post
-         throw ShibTargetException (SHIBRPC_OK,
-                                    "SHIRE POST failed to find TARGET");
-
-       // Make sure the SAML Response parameter exists
-       const char *post = ApacheRequest_param(ap_req, "SAMLResponse");
-       if (!post || *post == '\0')
-         // invalid post
-         throw ShibTargetException (SHIBRPC_OK,
-                                    "SHIRE POST failed to find SAMLResponse");
-
-       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-             "shire_check_user() Processing POST for target: %s", target);
-
-#if 0 // 2002-09-19
-       post = 
-         "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
-         "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
-         "c3VlSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
-         "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
-         "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
-         "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
-         "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
-         "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
-         "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTE5VDA1OjMwOjAwWiIgSXNzdWVy"
-         "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
-         "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
-         "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
-         "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
-         "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
-         "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
-         "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0xOVQwNTozMDowMFoiIEF1dGhl"
-         "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
-         "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
-         "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
-         "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
-         "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
-         "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
-         "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
-         "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
-         "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
-         "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
-         "c3NlcnRpb24+PC9SZXNwb25zZT4K";
-#endif
-#if 0 // 2002-09-20
-       post = 
-         "PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wi"
-         "IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjA6cHJvdG9jb2wiIElz"
-         "c3VlSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIE1ham9yVmVyc2lvbj0iMSIgTWlu"
-         "b3JWZXJzaW9uPSIwIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2hpYmJvbGV0aC9T"
-         "SElSRSIgUmVzcG9uc2VJRD0iYmI3ZjZmYjQtMmU0YS00YzY1LTgzY2QtYjIyMjQ0OWQwYmY4"
-         "Ij48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJzYW1scDpTdWNjZXNzIj48L1N0YXR1c0Nv"
-         "ZGU+PC9TdGF0dXM+PEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6"
-         "MS4wOmFzc2VydGlvbiIgQXNzZXJ0aW9uSUQ9IjZhYzUxYTg2LTJhNTgtNDM2My1hZjlkLTQy"
-         "YjQzYTRhMGNiZSIgSXNzdWVJbnN0YW50PSIyMDAyLTA5LTIwVDIzOjAwOjAwWiIgSXNzdWVy"
-         "PSJzaGlicHJvZDAuaW50ZXJuZXQyLmVkdSIgTWFqb3JWZXJzaW9uPSIxIiBNaW5vclZlcnNp"
-         "b249IjAiPjxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAwMi0wOS0xN1QwMjo1MDowMFoiIE5v"
-         "dE9uT3JBZnRlcj0iMjAxMC0wOS0xOVQwNjozMDowMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9u"
-         "Q29uZGl0aW9uPjxBdWRpZW5jZT5odHRwOi8vbWlkZGxld2FyZS5pbnRlcm5ldDIuZWR1L3No"
-         "aWJib2xldGgvY2x1YnMvY2x1YnNoaWIvMjAwMi8wNS88L0F1ZGllbmNlPjwvQXVkaWVuY2VS"
-         "ZXN0cmljdGlvbkNvbmRpdGlvbj48L0NvbmRpdGlvbnM+PEF1dGhlbnRpY2F0aW9uU3RhdGVt"
-         "ZW50IEF1dGhlbnRpY2F0aW9uSW5zdGFudD0iMjAwMi0wOS0yMFQyMzowMDowMFoiIEF1dGhl"
-         "bnRpY2F0aW9uTWV0aG9kPSJCYXNpYyI+PFN1YmplY3Q+PE5hbWVJZGVudGlmaWVyIE5hbWVR"
-         "dWFsaWZpZXI9ImV4YW1wbGUuZWR1Ij40YzBmYjg2Yi01NjQwLTQ1ZTUtOTM3Ny1mNTJkNjhh"
-         "ZDNiNjQ8L05hbWVJZGVudGlmaWVyPjxTdWJqZWN0Q29uZmlybWF0aW9uPjxDb25maXJtYXRp"
-         "b25NZXRob2Q+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmNtOkJlYXJlcjwvQ29uZmly"
-         "bWF0aW9uTWV0aG9kPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PFN1YmplY3RM"
-         "b2NhbGl0eSBJUEFkZHJlc3M9IjE4LjEwMS4xLjEyIj48L1N1YmplY3RMb2NhbGl0eT48QXV0"
-         "aG9yaXR5QmluZGluZyBBdXRob3JpdHlLaW5kPSJzYW1scDpBdHRyaWJ1dGVRdWVyeSIgQmlu"
-         "ZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4wOmJpbmRpbmdzOlNPQVAtYmluZGlu"
-         "ZyIgTG9jYXRpb249Imh0dHBzOi8vc2hpYnByb2QwLmludGVybmV0Mi5lZHUvc2hpYmJvbGV0"
-         "aC9BQSI+PC9BdXRob3JpdHlCaW5kaW5nPjwvQXV0aGVudGljYXRpb25TdGF0ZW1lbnQ+PC9B"
-         "c3NlcnRpb24+PC9SZXNwb25zZT4K";
-#endif
-       
-       // process the post
-       string cookie;
-       RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, cookie);
-
-       if (status->isError()) {
-         ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
-                       "shire_check_user() POST process failed (%d): %s",
-                       status->status, status->error_msg.c_str());
-
-         if (status->isRetryable()) {
-           ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
-                         "shire_check_user() Retrying POST by redirecting to WAYF");
-
-           char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
-                                 "?shire=",shire_location,
-                                 "&target=",url_encode(r,target),NULL);
-           ap_table_setn(r->headers_out,"Location",wayf);
-           delete status;
-           return REDIRECT;
-         }
-
-         // return this error to the user.
-         markupProcessor.insert (*status);
-         delete status;
-         return shire_error_page (r, wayfError.c_str(), markupProcessor);
-       }
-       delete status;
-
-       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user() POST process succeeded.  New cookie: %s",
-                     cookie.c_str());
-
-       // We've got a good session, set the cookie...
-       char * domain = NULL;
-       char * new_cookie = ap_psprintf(r->pool, "%s=%s; path=/%s%s",
-                                       shib_cookie.c_str(),
-                                       cookie.c_str(),
-                                       (domain ? "; domain=" : ""),
-                                       (domain ? domain : ""));
-
-       ap_table_setn(r->err_headers_out, "Set-Cookie", new_cookie);
-       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user() Set cookie: %s", new_cookie);
-                   
-       // ... and redirect to the target
-       char* redir=ap_pstrcat(r->pool,url_encode(r,target),NULL);
-       ap_table_setn(r->headers_out, "Location", target);
-       return REDIRECT;
-
-      } catch (ShibTargetException &e) {
-       ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user(): %s", e.what());
-       
-       markupProcessor.insert ("errorType", "SHIRE Processing Error");
-       markupProcessor.insert ("errorText", e.what());
-       return shire_error_page (r, wayfError.c_str(), markupProcessor);
-      }
+    ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,"shire_check_user() server error");
+    return SERVER_ERROR;
+}
 
-      /**************************************************************************/
+extern "C" int shire_post_handler (request_rec* r)
+{
+  ostringstream threadid;
+  threadid << "[" << getpid() << "] shire" << '\0';
+  saml::NDC ndc(threadid.str().c_str());
 
-    } else {
-      // Regular access to arbitrary resource...check AuthType
+  ShibINI& ini = g_Config->getINI();
+  ShibMLP markupProcessor;
 
-      const char *auth_type=ap_auth_type (r);
-      if (!auth_type)
-        return DECLINED;
+  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_post_handler() ENTER");
 
-      if (strcasecmp(auth_type,"shibboleth"))
-      {
-        if (!strcasecmp(auth_type,"basic") && dc->bBasicHijack==1)
-       {
-         core_dir_config* conf=
-           (core_dir_config*)ap_get_module_config(r->per_dir_config,
-                                                  ap_find_linked_module("http_core.c"));
-         conf->ap_auth_type="shibboleth";
-       }
-       else
-         return DECLINED;
-      }
+  // This will always be normalized, because Apache uses ap_get_server_name in this API call.
+  const char* targeturl = ap_construct_url(r->pool,r->unparsed_uri,r);
+   
+  // Map request to application ID, which is the key for config lookup.
+  const char* application_id = get_application_id(r);
+    
+  // The SHIRE URL is the current request URL, by definition...
+  const char* unescaped_shire = targeturl;
 
-      ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                   "shire_check_user() Shib check for %s", targeturl);
+  string tag;
+  bool has_tag = ini.get_tag(application_id, "checkIPAddress", true, &tag);
+  SHIREConfig config;
+  config.checkIPAddress = (has_tag ? ShibINI::boolean(tag) : false);
+
+  string shib_cookie;
+  if (! ini.get_tag(application_id, "cookieName", true, &shib_cookie)) {
+    ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
+                 "shire_check_user: no cookieName configuration for %s", application_id);
+    return SERVER_ERROR;
+  }
 
-      // SSL check.
-      if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https"))
-      {
-        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
-                     "shire_check_user() blocked non-SSL access");
-        return SERVER_ERROR;
-      }
+  string wayfLocation;
+  if (! ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) {
+    ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
+                 "shire_check_user: no wayfURL configuration for %s", application_id);
+    return SERVER_ERROR;
+  }
 
-      // We're in charge, so check for cookie.
-      const char* session_id=NULL;
-      const char* cookies=ap_table_get(r->headers_in,"Cookie");
+  string shireError;
+  if (! ini.get_tag(application_id, "shireError", true, &shireError)) {
+    ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
+                 "shire_check_user: no shireError configuration for %s", application_id);
+    return SERVER_ERROR;
+  }
 
-      if (cookies)
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user() cookies found: %s",
-                     cookies);               
+  has_tag = ini.get_tag(application_id, "supportContact", true, &tag);
+  markupProcessor.insert("supportContact", has_tag ? tag : "");
+  has_tag = ini.get_tag(application_id, "logoLocation", true, &tag);
+  markupProcessor.insert("logoLocation", has_tag ? tag : "");
+  markupProcessor.insert("requestURL", targeturl);
+  
+    // Get an RPC handle and build the SHIRE object.
+    RPCHandle* rpc_handle = (RPCHandle*)rpc_handle_key->getData();
+    if (!rpc_handle)
+    {
+        rpc_handle = new RPCHandle(shib_target_sockname(), SHIBRPC_PROG, SHIBRPC_VERS_1);
+        rpc_handle_key->setData(rpc_handle);
+    }
+    SHIRE shire(rpc_handle, config, unescaped_shire);
 
-      if (!cookies || !(session_id=strstr(cookies,shib_cookie.c_str())))
-      {
-        // No cookie.  Redirect to WAYF.
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user() no cookie found -- redirecting to WAYF");
-        char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
-                             "?shire=",shire_location,
-                             "&target=",url_encode(r,targeturl),NULL);
-       ap_table_setn(r->headers_out,"Location",wayf);
-       return REDIRECT;
-      }
+  // Process SHIRE POST
 
-      // Yep, we found a cookie -- pull it out (our session_id)
-      session_id+=strlen(shib_cookie.c_str()) + 1;     /* Skip over the '=' */
-      char* cookiebuf = ap_pstrdup(r->pool,session_id);
-      char* cookieend = strchr(cookiebuf,';');
-      if (cookieend)
-       *cookieend = '\0';      /* Ignore anyting after a ; */
-      session_id=cookiebuf;
+  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+               "shire_post_handler() Beginning SHIRE POST processing");
+      
+  try {
+    string sslonly;
+    if (!ini.get_tag(application_id, "shireSSLOnly", true, &sslonly))
+      ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r,
+                   "shire_post_handler: no shireSSLOnly configuration");
+    
+    // Make sure this is SSL, if it should be
+    if (ShibINI::boolean(sslonly) && strcmp(ap_http_method(r),"https"))
+      throw ShibTargetException(SHIBRPC_OK, "blocked non-SSL access to SHIRE POST processor");
+
+    // Make sure this is a POST
+    if (strcasecmp (r->method, "POST"))
+      throw ShibTargetException(SHIBRPC_OK, "blocked non-POST to SHIRE POST processor");
+
+    // Sure sure this POST is an appropriate content type
+    const char *ct = ap_table_get (r->headers_in, "Content-type");
+    if (!ct || strcasecmp (ct, "application/x-www-form-urlencoded"))
+      throw ShibTargetException (SHIBRPC_OK,
+                                ap_psprintf(r->pool,
+                            "blocked bad content-type to SHIRE POST processor: %s",
+                                            (ct ? ct : "")));
+       
+    // Make sure the "bytes sent" is a reasonable number
+    if (r->bytes_sent > 1024*1024) // 1MB?
+      throw ShibTargetException (SHIBRPC_OK,
+                                "blocked too-large a post to SHIRE POST processor");
+
+    // Read the posted data
+    ApacheRequest *ap_req = ApacheRequest_new(r);
+    int err = ApacheRequest_parse(ap_req);
+    if (err != OK)
+      throw ShibTargetException (SHIBRPC_OK,
+                                ap_psprintf(r->pool,
+                            "ApacheRequest_parse() failed with %d.", err));
+
+    
+    // Make sure the target parameter exists
+    const char *target = ApacheRequest_param(ap_req, "TARGET");
+    if (!target || *target == '\0')
+      // invalid post
+      throw ShibTargetException (SHIBRPC_OK,
+                                "SHIRE POST failed to find TARGET");
+
+    // Make sure the SAML Response parameter exists
+    const char *post = ApacheRequest_param(ap_req, "SAMLResponse");
+    if (!post || *post == '\0')
+      // invalid post
+      throw ShibTargetException (SHIBRPC_OK,
+                                "SHIRE POST failed to find SAMLResponse");
 
-      // Make sure this session is still valid
-      RPCError* status = shire.sessionIsValid(session_id, r->connection->remote_ip);
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                 "shire_post_handler() Processing POST for target: %s", target);
 
-      // Check the status
-      if (status->isError()) {
+    // process the post
+    string cookie;
+    RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, application_id, cookie);
 
-       ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
-                     "shire_check_user() session invalid: %s",
-                     status->error_msg.c_str());
+    if (status->isError()) {
+      ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
+                   "shire_post_handler() POST process failed (%d): %s",
+                   status->getCode(), status->getText());
 
-        // Oops, session is invalid.  Redirect to WAYF.
+    if (status->isRetryable()) {
+         ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r,
+               "shire_post_handler() Retrying POST by redirecting to WAYF");
+       
         char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(),
-                             "?shire=",shire_location,
-                             "&target=",url_encode(r,targeturl),NULL);
-       ap_table_setn(r->headers_out,"Location",wayf);
-
-       delete status;
-       return REDIRECT;
+                             "?shire=",url_encode(r,unescaped_shire),
+                             "&target=",url_encode(r,target),NULL);
+        ap_table_setn(r->headers_out,"Location",wayf);
+        delete status;
+        return REDIRECT;
+    }
 
-      } else {
-       delete status;
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
-                     "shire_check_user() success");
-       return OK;
-      }
+      // return this error to the user.
+      markupProcessor.insert (*status);
+      delete status;
+      return shire_error_page (r, shireError.c_str(), markupProcessor);
     }
+    delete status;
 
-    ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,
-                 "shire_check_user() server error");
-    return SERVER_ERROR;
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                 "shire_post_handler() POST process succeeded.  New cookie: %s",
+                 cookie.c_str());
+
+    // We've got a good session, set the cookie...
+    char * domain = NULL;
+    char * new_cookie = ap_psprintf(r->pool, "%s=%s; path=/%s%s",
+                                   shib_cookie.c_str(),
+                                   cookie.c_str(),
+                                   (domain ? "; domain=" : ""),
+                                   (domain ? domain : ""));
+    
+    ap_table_setn(r->err_headers_out, "Set-Cookie", new_cookie);
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                 "shire_post_handler() Set cookie: %s", new_cookie);
+                   
+    // ... and redirect to the target
+    char* redir=ap_pstrcat(r->pool,url_encode(r,target),NULL);
+    ap_table_setn(r->headers_out, "Location", target);
+    return REDIRECT;
+
+  } catch (ShibTargetException &e) {
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,
+                 "shire_post_handler(): %s", e.what());
+       
+    markupProcessor.insert ("errorType", "SHIRE Processing Error");
+    markupProcessor.insert ("errorText", e.what());
+    markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
+    return shire_error_page (r, shireError.c_str(), markupProcessor);
+  }
+  catch (...) {
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_post_handler(): unexpected exception");
+  
+    markupProcessor.insert ("errorType", "SHIRE Processing Error");
+    markupProcessor.insert ("errorText", "Unexpected Exception");
+    markupProcessor.insert ("errorDesc", "An error occurred while processing your request.");
+    return shire_error_page (r, shireError.c_str(), markupProcessor);
+  }
+
+  ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,"shire_post_handler() server error");
+  return SERVER_ERROR;
 }
 
 extern "C"{
+handler_rec shire_handlers[] = {
+  { "shib-shire-post", shire_post_handler },
+  { NULL }
+};
+
+extern "C" void mod_shire_init (server_rec*r, pool* p)
+{
+  ShibTargetConfig::preinit();
+}
+
 module MODULE_VAR_EXPORT shire_module = {
     STANDARD_MODULE_STUFF,
-    NULL,                      /* initializer */
+    mod_shire_init,            /* initializer */
     create_shire_dir_config,   /* dir config creater */
     merge_shire_dir_config,    /* dir merger --- default is to override */
-    create_shire_server_config,        /* server config */
-    merge_shire_server_config, /* merge server config */
+    NULL,                      /* server config */
+    NULL,                      /* merge server config */
     shire_cmds,                        /* command table */
-    NULL,                      /* handlers */
+    shire_handlers,            /* handlers */
     NULL,                      /* filename translation */
     shire_check_user,          /* check_user_id */
     NULL,                      /* check auth */