Convert more strings to references.
[shibboleth/sp.git] / apache / mod_apache.cpp
index 39ecdf5..d98a066 100644 (file)
@@ -6,6 +6,10 @@
  * $Id$
  */
 
+#ifdef SOLARIS2
+#undef _XOPEN_SOURCE    // causes gethostname conflict in unistd.h
+#endif
+
 // SAML Runtime
 #include <saml/saml.h>
 #include <shib/shib.h>
@@ -43,7 +47,9 @@ using namespace shibboleth;
 using namespace shibtarget;
 
 extern "C" module MODULE_VAR_EXPORT mod_shib;
+#if 0
 int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire);
+#endif
 
 namespace {
     char* g_szSHIBConfig = NULL;
@@ -52,6 +58,41 @@ namespace {
     static const char* g_UserDataKey = "_shib_check_user_";
 }
 
+/********************************************************************************/
+// Basic Apache Configuration code.
+//
+
+// per-server module configuration structure
+struct shib_server_config
+{
+    char* szScheme;
+};
+
+// creates the per-server configuration
+extern "C" void* create_shib_server_config(SH_AP_POOL* p, server_rec* s)
+{
+    shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));
+    sc->szScheme = NULL;
+    return sc;
+}
+
+// overrides server configuration in virtual servers
+extern "C" void* merge_shib_server_config (SH_AP_POOL* p, void* base, void* sub)
+{
+    shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));
+    shib_server_config* parent=(shib_server_config*)base;
+    shib_server_config* child=(shib_server_config*)sub;
+
+    if (child->szScheme)
+        sc->szScheme=ap_pstrdup(p,child->szScheme);
+    else if (parent->szScheme)
+        sc->szScheme=ap_pstrdup(p,parent->szScheme);
+    else
+        sc->szScheme=NULL;
+
+    return sc;
+}
+
 // per-dir module configuration structure
 struct shib_dir_config
 {
@@ -105,43 +146,407 @@ extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*, const
     return NULL;
 }
 
-typedef const char* (*config_fn_t)(void);
+extern "C" const char* shib_set_server_string_slot(cmd_parms* parms, void*, const char* arg)
+{
+    char* base=(char*)ap_get_module_config(parms->server->module_config,&mod_shib);
+    int offset=(int)parms->info;
+    *((char**)(base + offset))=ap_pstrdup(parms->pool,arg);
+    return NULL;
+}
 
-static int shib_error_page(request_rec* r, const IApplication* app, const char* page, ShibMLP& mlp)
+extern "C" const char* shib_ap_set_file_slot(cmd_parms* parms,
+#ifdef SHIB_APACHE_13
+                                            char* arg1, char* arg2
+#else
+                                            void* arg1, const char* arg2
+#endif
+                                            )
 {
-    const IPropertySet* props=app->getPropertySet("Errors");
-    if (props) {
-        pair<bool,const char*> p=props->getString(page);
-        if (p.first) {
-            ifstream infile(p.second);
-            if (!infile.fail()) {
-                const char* res = mlp.run(infile,props);
-                if (res) {
-                    r->content_type = ap_psprintf(r->pool, "text/html");
-                    ap_send_http_header(r);
-                    ap_rprintf(r, res);
-                    return DONE;
-                }
+  ap_set_file_slot(parms, arg1, arg2);
+  return DECLINE_CMD;
+}
+
+/********************************************************************************/
+// Some other useful helper function(s)
+
+static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpfile)
+{
+    SH_AP_CONFIGFILE* f;
+    SH_AP_TABLE* grps=ap_make_table(r->pool,15);
+    char l[MAX_STRING_LEN];
+    const char *group_name, *ll, *w;
+
+#ifdef SHIB_APACHE_13
+    if (!(f=ap_pcfg_openfile(r->pool,grpfile))) {
+#else
+    if (ap_pcfg_openfile(&f,r->pool,grpfile) != APR_SUCCESS) {
+#endif
+        ap_log_rerror(APLOG_MARK,APLOG_DEBUG,SH_AP_R(r),"groups_for_user() could not open group file: %s\n",grpfile);
+        return NULL;
+    }
+
+    SH_AP_POOL* sp;
+#ifdef SHIB_APACHE_13
+    sp=ap_make_sub_pool(r->pool);
+#else
+    if (apr_pool_create(&sp,r->pool) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,
+            "groups_for_user() could not create a subpool");
+        return NULL;
+    }
+#endif
+
+    while (!(ap_cfg_getline(l,MAX_STRING_LEN,f))) {
+        if ((*l=='#') || (!*l))
+            continue;
+        ll = l;
+        ap_clear_pool(sp);
+
+        group_name=ap_getword(sp,&ll,':');
+
+        while (*ll) {
+            w=ap_getword_conf(sp,&ll);
+            if (!strcmp(w,user)) {
+                ap_table_setn(grps,ap_pstrdup(r->pool,group_name),"in");
+                break;
             }
         }
     }
+    ap_cfg_closefile(f);
+    ap_destroy_pool(sp);
+    return grps;
+}
 
-    ap_log_rerror(APLOG_MARK,APLOG_ERR,SH_AP_R(r),
-        "shib_error_page() could not process shire error template for application %s",app->getId());
+/********************************************************************************/
+// Apache ShibTarget subclass(es) here.
+
+class HTGroupTableApache : public HTGroupTable
+{
+public:
+  HTGroupTableApache(request_rec* r, const char *user, char *grpfile) {
+    groups = groups_for_user(r, user, grpfile);
+    if (!groups)
+      throw ResourceAccessException("Unable to access group file ($1) for user ($2)",params(2,grpfile,user));
+  }
+  ~HTGroupTableApache() {}
+  bool lookup(const char *entry) { return (ap_table_get(groups, entry)!=NULL); }
+  SH_AP_TABLE* groups;
+};
+
+class ShibTargetApache : public ShibTarget
+{
+public:
+  ShibTargetApache(request_rec* req) {
+    m_sc = (shib_server_config*)
+      ap_get_module_config(req->server->module_config, &mod_shib);
+
+    m_dc = (shib_dir_config*)ap_get_module_config(req->per_dir_config, &mod_shib);
+
+    init(
+        g_Config,
+        m_sc->szScheme ? m_sc->szScheme : ap_http_method(req),
+           ap_get_server_name(req),
+        (int)ap_get_server_port(req),
+           req->unparsed_uri,
+        ap_table_get(req->headers_in, "Content-type"),
+           req->connection->remote_ip,
+        req->method
+        );
+
+    m_req = req;
+  }
+  ~ShibTargetApache() { }
+
+  virtual void log(ShibLogLevel level, const string &msg) {
+    ap_log_rerror(APLOG_MARK,
+                 (level == LogLevelDebug ? APLOG_DEBUG :
+                  (level == LogLevelInfo ? APLOG_INFO :
+                   (level == LogLevelWarn ? APLOG_WARNING :
+                    APLOG_ERR)))|APLOG_NOERRNO, SH_AP_R(m_req),
+                 msg.c_str());
+  }
+  virtual string getCookies(void) {
+    const char *c = ap_table_get(m_req->headers_in, "Cookie");
+    return string(c ? c : "");
+  }
+  virtual void setCookie(const string &name, const string &value) {
+    char* val = ap_psprintf(m_req->pool, "%s=%s", name.c_str(), value.c_str());
+    ap_table_setn(m_req->err_headers_out, "Set-Cookie", val);
+  }
+  virtual string getArgs(void) { return string(m_req->args ? m_req->args : ""); }
+  virtual string getPostData(void) {
+    // Read the posted data
+    if (ap_setup_client_block(m_req, REQUEST_CHUNKED_ERROR))
+        throw FatalProfileException("Apache function (setup_client_block) failed while reading profile submission.");
+    if (!ap_should_client_block(m_req))
+        throw FatalProfileException("Apache function (should_client_block) failed while reading profile submission.");
+    if (m_req->remaining > 1024*1024)
+        throw FatalProfileException("Blocked too-large a submission to profile endpoint.");
+    string cgistr;
+    char buff[HUGE_STRING_LEN];
+    ap_hard_timeout("[mod_shib] getPostData", m_req);
+    memset(buff, 0, sizeof(buff));
+    while (ap_get_client_block(m_req, buff, sizeof(buff)-1) > 0) {
+      ap_reset_timeout(m_req);
+      cgistr += buff;
+      memset(buff, 0, sizeof(buff));
+    }
+    ap_kill_timeout(m_req);
+
+    return cgistr;
+  }
+  virtual void clearHeader(const string &name) {
+    ap_table_unset(m_req->headers_in, name.c_str());
+  }
+  virtual void setHeader(const string &name, const string &value) {
+    ap_table_set(m_req->headers_in, name.c_str(), value.c_str());
+  }
+  virtual string getHeader(const string &name) {
+    const char *hdr = ap_table_get(m_req->headers_in, name.c_str());
+    return string(hdr ? hdr : "");
+  }
+  virtual void setRemoteUser(const string &user) {
+    SH_AP_USER(m_req) = ap_pstrdup(m_req->pool, user.c_str());
+  }
+  virtual string getRemoteUser(void) {
+    return string(SH_AP_USER(m_req) ? SH_AP_USER(m_req) : "");
+  }
+  // override so we can look at the actual auth type and maybe override it.
+  virtual string getAuthType(void) {
+    const char *auth_type=ap_auth_type(m_req);
+    if (!auth_type)
+        return string("");
+    if (strcasecmp(auth_type, "shibboleth")) {
+      if (!strcasecmp(auth_type, "basic") && m_dc->bBasicHijack == 1) {
+       core_dir_config* conf= (core_dir_config*)
+         ap_get_module_config(m_req->per_dir_config,
+                              ap_find_linked_module("http_core.c"));
+       auth_type = conf->ap_auth_type = "shibboleth";
+      }
+    }
+    return string(auth_type);
+  }
+  // Override this function because we want to add the Apache Directory override
+  virtual pair<bool,bool> getRequireSession(IRequestMapper::Settings &settings) {
+    pair<bool,bool> requireSession = settings.first->getBool("requireSession");
+    if (!requireSession.first || !requireSession.second)
+      if (m_dc->bRequireSession == 1)
+       requireSession.second=true;
+
+    return requireSession;
+  }
+
+  virtual HTAccessInfo* getAccessInfo(void) { 
+    int m = m_req->method_number;
+    const array_header* reqs_arr = ap_requires(m_req);
+    if (!reqs_arr)
+      return NULL;
+
+    require_line* reqs = (require_line*) reqs_arr->elts;
+
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req),
+                 "REQUIRE nelts: %d", reqs_arr->nelts);
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req),
+                 "REQUIRE all: %d", m_dc->bRequireAll);
+
+    HTAccessInfo* ht = new HTAccessInfo();
+    ht->requireAll = (m_dc->bRequireAll == 1);
+    ht->elements.reserve(reqs_arr->nelts);
+    for (int x = 0; x < reqs_arr->nelts; x++) {
+      HTAccessInfo::RequireLine* rline = new HTAccessInfo::RequireLine();
+      rline->use_line = (reqs[x].method_mask & (1 << m));
+      rline->tokens.reserve(6);        // No reason to reserve specifically 6 tokens
+      const char* t = reqs[x].requirement;
+      const char* w = ap_getword_white(m_req->pool, &t);
+      rline->tokens.push_back(w);
+      while (*t) {
+       w = ap_getword_conf(m_req->pool, &t);
+       rline->tokens.push_back(w);
+      }
+      ht->elements.push_back(rline);
+    }
+    return ht;
+  }
+  virtual HTGroupTable* getGroupTable(string &user) {
+    if (m_dc->szAuthGrpFile && !user.empty()) {
+      ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req),
+                   "getGroupTable() using groups file: %s\n",
+                   m_dc->szAuthGrpFile);
+      try {
+       HTGroupTableApache *gt = new HTGroupTableApache(m_req, user.c_str(),
+                                                       m_dc->szAuthGrpFile);
+       return gt;
+      } catch (...) { }
+    }
+    return NULL;
+  }
+
+  virtual void* sendPage(
+    const string& msg,
+    const string& content_type,
+       const saml::Iterator<header_t>& headers=EMPTY(header_t),
+    int code=200
+    ) {
+    m_req->content_type = ap_psprintf(m_req->pool, content_type.c_str());
+    while (headers.hasNext()) {
+        const header_t& h=headers.next();
+        ap_table_setn(m_req->headers_out, h.first.c_str(), h.second.c_str());
+    }
+    ap_send_http_header(m_req);
+    ap_rprintf(m_req, msg.c_str());
+    return (void*)DONE;
+  }
+  virtual void* sendRedirect(const string& url) {
+    ap_table_set(m_req->headers_out, "Location", url.c_str());
+    return (void*)REDIRECT;
+  }
+  virtual void* returnDecline(void) { return (void*)DECLINED; }
+  virtual void* returnOK(void) { return (void*)OK; }
+
+  request_rec* m_req;
+  shib_dir_config* m_dc;
+  shib_server_config* m_sc;
+};
+
+/********************************************************************************/
+// Apache handlers
+
+extern "C" int shib_check_user(request_rec* r)
+{
+  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
+               "shib_check_user(%d): ENTER\n", (int)getpid());
+
+  ostringstream threadid;
+  threadid << "[" << getpid() << "] shib_check_user" << '\0';
+  saml::NDC ndc(threadid.str().c_str());
+
+#ifndef _DEBUG
+  try {
+#endif
+    ShibTargetApache sta(r);
+
+    // Check user authentication, the set the post handler bypass
+    pair<bool,void*> res = sta.doCheckAuthN((sta.m_dc->bRequireSession == 1), true);
+    apr_pool_userdata_setn((const void*)42,g_UserDataKey,NULL,r->pool);
+    if (res.first) return (int)res.second;
+
+    // user auth was okay -- export the assertions now
+    res = sta.doExportAssertions((sta.m_dc->bExportAssertion == 1));
+    if (res.first) return (int)res.second;
+
+    // export happened successfully..  this user is ok.
+    return OK;
+
+#ifndef _DEBUG
+  } catch (...) {
+    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r),
+                 "shib_check_user threw an uncaught exception!");
+    return SERVER_ERROR;
+  }
+#endif
+}
+
+extern "C" int shib_post_handler(request_rec* r)
+{
+  ostringstream threadid;
+  threadid << "[" << getpid() << "] shib_post_handler" << '\0';
+  saml::NDC ndc(threadid.str().c_str());
+
+#ifndef SHIB_APACHE_13
+  // With 2.x, this handler always runs, though last.
+  // We check if shib_check_user ran, because it will detect a SHIRE request
+  // and dispatch it directly.
+  void* data;
+  apr_pool_userdata_get(&data,g_UserDataKey,r->pool);
+  if (data==(const void*)42) {
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_post_handler skipped since check_user ran");
+    return DECLINED;
+  }
+#endif
+
+  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
+               "shib_post_handler(%d): ENTER", (int)getpid());
+
+#ifndef _DEBUG
+  try {
+#endif
+    ShibTargetApache sta(r);
+
+    pair<bool,void*> res = sta.doHandleProfile();
+    if (res.first) return (int)res.second;
+
+    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r),
+                 "doHandleProfile() did not do anything.");
+    return SERVER_ERROR;
+
+#ifndef _DEBUG
+  } catch (...) {
+    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r),
+                 "shib_post_handler threw an uncaught exception!");
+    return SERVER_ERROR;
+  }
+#endif
+}
+
+/*
+ * shib_auth_checker() -- a simple resource manager to
+ * process the .htaccess settings
+ */
+extern "C" int shib_auth_checker(request_rec* r)
+{
+  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
+               "shib_auth_checker(%d): ENTER", (int)getpid());
+
+  ostringstream threadid;
+  threadid << "[" << getpid() << "] shib_auth_checker" << '\0';
+  saml::NDC ndc(threadid.str().c_str());
+
+#ifndef _DEBUG
+  try {
+#endif
+    ShibTargetApache sta(r);
+
+    pair<bool,void*> res = sta.doCheckAuthZ();
+    if (res.first) return (int)res.second;
+
+    // We're all okay.
+    return OK;
+
+#ifndef _DEBUG
+  } catch (...) {
+    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r),
+                 "shib_auth_checker threw an uncaught exception!");
     return SERVER_ERROR;
+  }
+#endif
+}
+
+#if 0
+static char* shib_get_targeturl(request_rec* r, const char* scheme=NULL)
+{
+    // On 1.3, this is always canonical, but on 2.0, UseCanonicalName comes into play.
+    // However, we also have a setting to forcibly replace the scheme for esoteric cases.
+    if (scheme) {
+        unsigned port = ap_get_server_port(r);
+        if ((!strcmp(scheme,"http") && port==80) || (!strcmp(scheme,"https") && port==443)) {
+            return ap_pstrcat(r->pool, scheme, "://", ap_get_server_name(r), r->unparsed_uri, NULL);
+        }
+        return ap_psprintf(r->pool, "%s://%s:%u%s", scheme, ap_get_server_name(r), port, r->unparsed_uri);
+    }
+    return ap_construct_url(r->pool,r->unparsed_uri,r);
 }
 
 extern "C" int shib_check_user(request_rec* r)
 {
     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_check_user: ENTER");
     shib_dir_config* dc=(shib_dir_config*)ap_get_module_config(r->per_dir_config,&mod_shib);
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
 
     ostringstream threadid;
     threadid << "[" << getpid() << "] shib_check_user" << '\0';
     saml::NDC ndc(threadid.str().c_str());
 
-    // 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);
+    const char* targeturl=shib_get_targeturl(r,sc->szScheme);
 
     // We lock the configuration system for the duration.
     IConfig* conf=g_Config->getINI();
@@ -151,7 +556,7 @@ extern "C" int shib_check_user(request_rec* r)
     IRequestMapper* mapper=conf->getRequestMapper();
     Locker locker2(mapper);
     IRequestMapper::Settings settings=mapper->getSettingsFromParsedURL(
-        ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
+        (sc-> szScheme ? sc-> szScheme : ap_http_method(r)), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
         );
     pair<bool,const char*> application_id=settings.first->getString("applicationId");
     const IApplication* application=conf->getApplication(application_id.second);
@@ -164,8 +569,15 @@ extern "C" int shib_check_user(request_rec* r)
     // Declare SHIRE object for this request.
     SHIRE shire(application);
     
+    const char* shireURL=shire.getShireURL(targeturl);
+    if (!shireURL) {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(r),
+           "shib_check_user: unable to map request to proper shireURL setting, check configuration");
+        return SERVER_ERROR;
+    }
+    
     // Get location of this application's assertion consumer service and see if this is it.
-    if (strstr(targeturl,shire.getShireURL(targeturl))) {
+    if (strstr(targeturl,shireURL)) {
         return shib_handler(r,application,shire);
     }
 
@@ -325,7 +737,7 @@ extern "C" int shib_check_user(request_rec* r)
     // Do we have an access control plugin?
     if (settings.second) {
         Locker acllock(settings.second);
-        if (!settings.second->authorized(assertions)) {
+        if (!settings.second->authorized(*sso_statement,assertions)) {
             for (int k = 0; k < assertions.size(); k++)
                 delete assertions[k];
             delete sso_statement;
@@ -381,23 +793,21 @@ extern "C" int shib_check_user(request_rec* r)
     ap_table_unset(r->headers_in,"Shib-Origin-Site");
     ap_table_unset(r->headers_in,"Shib-Authentication-Method");
     ap_table_unset(r->headers_in,"Shib-NameIdentifier-Format");
-    if (sso_statement) {
-        auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
-        auto_ptr_char am(sso_statement->getAuthMethod());
-        ap_table_set(r->headers_in,"Shib-Origin-Site", os.get());
-        ap_table_set(r->headers_in,"Shib-Authentication-Method", am.get());
-        
-        // Export NameID?
-        AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
-        if (!wrapper.fail() && wrapper->getHeader()) {
-            auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
-            auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
-            ap_table_set(r->headers_in,"Shib-NameIdentifier-Format",form.get());
-            if (!strcmp(wrapper->getHeader(),"REMOTE_USER"))
-                SH_AP_USER(r)=ap_pstrdup(r->pool,nameid.get());
-            else
-                ap_table_set(r->headers_in,wrapper->getHeader(),nameid.get());
-        }
+    auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
+    auto_ptr_char am(sso_statement->getAuthMethod());
+    ap_table_set(r->headers_in,"Shib-Origin-Site", os.get());
+    ap_table_set(r->headers_in,"Shib-Authentication-Method", am.get());
+    
+    // Export NameID?
+    AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+    if (!wrapper.fail() && wrapper->getHeader()) {
+        auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
+        auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
+        ap_table_set(r->headers_in,"Shib-NameIdentifier-Format",form.get());
+        if (!strcmp(wrapper->getHeader(),"REMOTE_USER"))
+            SH_AP_USER(r)=ap_pstrdup(r->pool,nameid.get());
+        else
+            ap_table_set(r->headers_in,wrapper->getHeader(),nameid.get());
     }
     
     ap_table_unset(r->headers_in,"Shib-Application-ID");
@@ -459,8 +869,9 @@ extern "C" int shib_check_user(request_rec* r)
 
 extern "C" int shib_post_handler(request_rec* r)
 {
-  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
                "shib_post_handler(%d): ENTER", (int)getpid());
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
 
 #ifndef SHIB_APACHE_13
     // With 2.x, this handler always runs, though last.
@@ -486,7 +897,7 @@ extern "C" int shib_post_handler(request_rec* r)
     IRequestMapper* mapper=conf->getRequestMapper();
     Locker locker2(mapper);
     IRequestMapper::Settings settings=mapper->getSettingsFromParsedURL(
-        ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
+        (sc->szScheme ? sc->szScheme : ap_http_method(r)), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
         );
     pair<bool,const char*> application_id=settings.first->getString("applicationId");
     const IApplication* application=conf->getApplication(application_id.second);
@@ -504,11 +915,19 @@ extern "C" int shib_post_handler(request_rec* r)
 
 int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire)
 {
-    // Prime the pump...
-    const char* targeturl = ap_construct_url(r->pool,r->unparsed_uri,r);
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
+
+    const char* targeturl=shib_get_targeturl(r,sc->szScheme);
+
+    const char* shireURL=shire.getShireURL(targeturl);
+    if (!shireURL) {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(r),
+           "shib_post_handler: unable to map request to proper shireURL setting, check configuration");
+        return SERVER_ERROR;
+    }
 
     // Make sure we only process the SHIRE requests.
-    if (!strstr(targeturl,shire.getShireURL(targeturl)))
+    if (!strstr(targeturl,shireURL))
         return DECLINED;
 
     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_handler() running");
@@ -566,6 +985,7 @@ int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire)
         ap_hard_timeout("[mod_shib] CGI Parser", r);
         memset(buff, 0, sizeof(buff));
         while (ap_get_client_block(r, buff, sizeof(buff)-1) > 0) {
+            ap_reset_timeout(r);
             cgistr += buff;
             memset(buff, 0, sizeof(buff));
         }
@@ -641,52 +1061,28 @@ int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire)
     return SERVER_ERROR;
 }
 
-static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpfile)
+static int shib_error_page(request_rec* r, const IApplication* app, const char* page, ShibMLP& mlp)
 {
-    SH_AP_CONFIGFILE* f;
-    SH_AP_TABLE* grps=ap_make_table(r->pool,15);
-    char l[MAX_STRING_LEN];
-    const char *group_name, *ll, *w;
-
-#ifdef SHIB_APACHE_13
-    if (!(f=ap_pcfg_openfile(r->pool,grpfile))) {
-#else
-    if (ap_pcfg_openfile(&f,r->pool,grpfile) != APR_SUCCESS) {
-#endif
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG,SH_AP_R(r),"groups_for_user() could not open group file: %s\n",grpfile);
-        return NULL;
-    }
-
-    SH_AP_POOL* sp;
-#ifdef SHIB_APACHE_13
-    sp=ap_make_sub_pool(r->pool);
-#else
-    if (apr_pool_create(&sp,r->pool) != APR_SUCCESS) {
-        ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,
-            "groups_for_user() could not create a subpool");
-        return NULL;
-    }
-#endif
-
-    while (!(ap_cfg_getline(l,MAX_STRING_LEN,f))) {
-        if ((*l=='#') || (!*l))
-            continue;
-        ll = l;
-        ap_clear_pool(sp);
-
-        group_name=ap_getword(sp,&ll,':');
-
-        while (*ll) {
-            w=ap_getword_conf(sp,&ll);
-            if (!strcmp(w,user)) {
-                ap_table_setn(grps,ap_pstrdup(r->pool,group_name),"in");
-                break;
+    const IPropertySet* props=app->getPropertySet("Errors");
+    if (props) {
+        pair<bool,const char*> p=props->getString(page);
+        if (p.first) {
+            ifstream infile(p.second);
+            if (!infile.fail()) {
+                const char* res = mlp.run(infile,props);
+                if (res) {
+                    r->content_type = ap_psprintf(r->pool, "text/html");
+                    ap_send_http_header(r);
+                    ap_rprintf(r, res);
+                    return DONE;
+                }
             }
         }
     }
-    ap_cfg_closefile(f);
-    ap_destroy_pool(sp);
-    return grps;
+
+    ap_log_rerror(APLOG_MARK,APLOG_ERR,SH_AP_R(r),
+        "shib_error_page() could not process shire error template for application %s",app->getId());
+    return SERVER_ERROR;
 }
 
 /*
@@ -863,7 +1259,7 @@ extern "C" int shib_auth_checker(request_rec* r)
                                     SHIB_AP_CHECK_IS_OK;
                                 }
                             }
-                            else if (val==w) {
+                            else if ((wrapper->getCaseSensitive() && val==w) || (!wrapper->getCaseSensitive() && !strcasecmp(val.c_str(),w))) {
                                 ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
                                                 "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
                                 SHIB_AP_CHECK_IS_OK;
@@ -884,7 +1280,7 @@ extern "C" int shib_auth_checker(request_rec* r)
                             SHIB_AP_CHECK_IS_OK;
                         }
                     }
-                    else if (val==w) {
+                    else if ((wrapper->getCaseSensitive() && val==w) || (!wrapper->getCaseSensitive() && !strcasecmp(val.c_str(),w))) {
                         ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
                                         "shib_auth_checker() expecting %s, got %s: authorization granted", w, val.c_str());
                         SHIB_AP_CHECK_IS_OK;
@@ -929,42 +1325,38 @@ extern "C" int shib_auth_checker(request_rec* r)
     markupProcessor.insert("requestURL", ap_construct_url(r->pool,r->unparsed_uri,r));
     return shib_error_page(r, application, "access", markupProcessor);
 }
+#endif /* 0 */
 
+#ifndef SHIB_APACHE_13
 /*
  * shib_exit()
- *  Cleanup the (per-process) pool info.
+ *  Empty cleanup hook, Apache 2.x doesn't check NULL very well...
  */
-#ifdef SHIB_APACHE_13
-extern "C" void shib_exit(server_rec* s, SH_AP_POOL* p)
-{
-#else
 extern "C" apr_status_t shib_exit(void* data)
 {
-    server_rec* s = NULL;
-#endif
-
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_exit(%d) dealing with g_Config..", (int)getpid());
-
-    g_Config->shutdown();
-    g_Config = NULL;
-
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_exit() done\n");
-#ifndef SHIB_APACHE_13
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,NULL,"shib_exit() done\n");
     return OK;
-#endif
 }
+#endif
 
 
+/*
+ * shib_child_exit()
+ *  Cleanup the (per-process) pool info.
+ */
 #ifdef SHIB_APACHE_13
 extern "C" void shib_child_exit(server_rec* s, SH_AP_POOL* p)
+{
 #else
 extern "C" apr_status_t shib_child_exit(void* data)
-#endif
 {
   server_rec* s = NULL;
+#endif
 
-  ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit(%d)",
-              (int)getpid());
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit(%d) dealing with g_Config..", (int)getpid());
+    g_Config->shutdown();
+    g_Config = NULL;
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit() done\n");
 
 #ifndef SHIB_APACHE_13
     return OK;
@@ -979,8 +1371,7 @@ extern "C" apr_status_t shib_child_exit(void* data)
 #ifdef SHIB_APACHE_13
 extern "C" void shib_child_init(server_rec* s, SH_AP_POOL* p)
 #else
-extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
-                               apr_pool_t* ptemp, server_rec* s)
+extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
 #endif
 {
     // Initialize runtime components.
@@ -989,11 +1380,7 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
 
     if (g_Config) {
         ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() already initialized!");
-#ifdef SHIB_APACHE_13
         exit(1);
-#else
-       return OK;
-#endif
     }
 
     try {
@@ -1003,7 +1390,8 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
             ShibTargetConfig::Metadata |
             ShibTargetConfig::AAP |
             ShibTargetConfig::RequestMapper |
-            ShibTargetConfig::SHIREExtensions
+            ShibTargetConfig::LocalExtensions |
+            ShibTargetConfig::Logging
             );
         if (!g_Config->init(g_szSchemaDir,g_szSHIBConfig)) {
             ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to initialize SHIB Target");
@@ -1016,15 +1404,13 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
     }
 
     // Set the cleanup handler
-    apr_pool_cleanup_register(pconf, NULL, &shib_exit, &shib_child_exit);
+    apr_pool_cleanup_register(p, NULL, &shib_exit, &shib_child_exit);
 
     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() done");
-
-#ifndef SHIB_APACHE_13
-    return OK;
-#endif
 }
 
+typedef const char* (*config_fn_t)(void);
+
 #ifdef SHIB_APACHE_13
 
 // SHIB Module commands
@@ -1037,6 +1423,10 @@ static command_rec shire_cmds[] = {
   {"ShibSchemaDir", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,
    RSRC_CONF, TAKE1, "Path to Shibboleth XML schema directory."},
 
+  {"ShibURLScheme", (config_fn_t)shib_set_server_string_slot,
+   (void *) XtOffsetOf (shib_server_config, szScheme),
+   RSRC_CONF, TAKE1, "URL scheme to force into generated URLs for a vhost."},
+   
   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bBasicHijack),
    OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shib?"},
@@ -1046,7 +1436,7 @@ static command_rec shire_cmds[] = {
   {"ShibExportAssertion", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bExportAssertion),
    OR_AUTHCFG, FLAG, "Export SAML assertion to Shibboleth-defined header?"},
-  {"AuthGroupFile", (config_fn_t)ap_set_file_slot,
+  {"AuthGroupFile", (config_fn_t)shib_ap_set_file_slot,
    (void *) XtOffsetOf (shib_dir_config, szAuthGrpFile),
    OR_AUTHCFG, TAKE1, "text file containing group names and member user IDs"},
   {"ShibRequireAll", (config_fn_t)ap_set_flag_slot,
@@ -1067,8 +1457,8 @@ module MODULE_VAR_EXPORT mod_shib = {
     NULL,                        /* initializer */
     create_shib_dir_config,    /* dir config creater */
     merge_shib_dir_config,     /* dir merger --- default is to override */
-    NULL,                      /* server config */
-    NULL,                      /* merge server config */
+    create_shib_server_config, /* server config */
+    merge_shib_server_config,   /* merge server config */
     shire_cmds,                        /* command table */
     shib_handlers,             /* handlers */
     NULL,                      /* filename translation */
@@ -1080,7 +1470,7 @@ module MODULE_VAR_EXPORT mod_shib = {
     NULL,                      /* logger */
     NULL,                      /* header parser */
     shib_child_init,           /* child_init */
-    shib_exit,                 /* child_exit */
+    shib_child_exit,           /* child_exit */
     NULL                       /* post read-request */
 };
 
@@ -1088,7 +1478,7 @@ module MODULE_VAR_EXPORT mod_shib = {
 
 extern "C" void shib_register_hooks (apr_pool_t *p)
 {
-  ap_hook_post_config(shib_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+  ap_hook_child_init(shib_child_init, NULL, NULL, APR_HOOK_MIDDLE);
   ap_hook_check_user_id(shib_check_user, NULL, NULL, APR_HOOK_MIDDLE);
   ap_hook_auth_checker(shib_auth_checker, NULL, NULL, APR_HOOK_FIRST);
   ap_hook_handler(shib_post_handler, NULL, NULL, APR_HOOK_LAST);
@@ -1105,6 +1495,11 @@ static command_rec shib_cmds[] = {
      (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,
       RSRC_CONF, "Path to Shibboleth XML schema directory."),
 
+  AP_INIT_TAKE1("ShibURLScheme",
+     (config_fn_t)shib_set_server_string_slot,
+     (void *) offsetof (shib_server_config, szScheme),
+      RSRC_CONF, "URL scheme to force into generated URLs for a vhost."),
+
   AP_INIT_FLAG("ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
               (void *) offsetof (shib_dir_config, bBasicHijack),
               OR_AUTHCFG, "Respond to AuthType Basic and convert to shib?"),
@@ -1114,7 +1509,7 @@ static command_rec shib_cmds[] = {
   AP_INIT_FLAG("ShibExportAssertion", (config_fn_t)ap_set_flag_slot,
          (void *) offsetof (shib_dir_config, bExportAssertion),
         OR_AUTHCFG, "Export SAML assertion to Shibboleth-defined header?"),
-  AP_INIT_TAKE1("AuthGroupFile", (config_fn_t)ap_set_file_slot,
+  AP_INIT_TAKE1("AuthGroupFile", (config_fn_t)shib_ap_set_file_slot,
                (void *) offsetof (shib_dir_config, szAuthGrpFile),
                OR_AUTHCFG, "text file containing group names and member user IDs"),
   AP_INIT_FLAG("ShibRequireAll", (config_fn_t)ap_set_flag_slot,
@@ -1126,12 +1521,12 @@ static command_rec shib_cmds[] = {
 
 module AP_MODULE_DECLARE_DATA mod_shib = {
     STANDARD20_MODULE_STUFF,
-    create_shib_dir_config,    /* create dir config */
-    merge_shib_dir_config,     /* merge dir config --- default is to override */
-    NULL,                      /* create server config */
-    NULL,                      /* merge server config */
-    shib_cmds,                 /* command table */
-    shib_register_hooks                /* register hooks */
+    create_shib_dir_config,     /* create dir config */
+    merge_shib_dir_config,      /* merge dir config --- default is to override */
+    create_shib_server_config,  /* create server config */
+    merge_shib_server_config,   /* merge server config */
+    shib_cmds,                  /* command table */
+    shib_register_hooks         /* register hooks */
 };
 
 #else