SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / apache / mod_shib.cpp
index fe351d0..5652713 100644 (file)
@@ -136,6 +136,7 @@ namespace {
 struct shib_server_config
 {
     char* szScheme;
+    int bCompatValidUser;
 };
 
 // creates the per-server configuration
@@ -143,6 +144,7 @@ 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 = nullptr;
+    sc->bCompatValidUser = -1;
     return sc;
 }
 
@@ -160,6 +162,8 @@ extern "C" void* merge_shib_server_config (SH_AP_POOL* p, void* base, void* sub)
     else
         sc->szScheme=nullptr;
 
+    sc->bCompatValidUser = ((child->bCompatValidUser==-1) ? parent->bCompatValidUser : child->bCompatValidUser);
+
     return sc;
 }
 
@@ -305,13 +309,19 @@ struct shib_request_config
 #endif
 };
 
-// create a request record
-static shib_request_config* init_request_config(request_rec *r)
+// create or return a request record
+static shib_request_config* get_request_config(request_rec *r)
 {
-    shib_request_config* rc = (shib_request_config*)ap_pcalloc(r->pool,sizeof(shib_request_config));
-    memset(rc, 0, sizeof(shib_request_config));
-    ap_set_module_config(r->request_config, &mod_shib, rc);
-    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_init_rc");
+    shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
+    if (rc) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "get_request_config called redundantly");
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "get_request_config created per-request structure");
+        rc = (shib_request_config*)ap_pcalloc(r->pool,sizeof(shib_request_config));
+        memset(rc, 0, sizeof(shib_request_config));
+        ap_set_module_config(r->request_config, &mod_shib, rc);
+    }
     return rc;
 }
 
@@ -335,7 +345,7 @@ public:
   shib_server_config* m_sc;
   shib_request_config* m_rc;
 
-  ShibTargetApache(request_rec* req) : AbstractSPRequest(SHIBSP_LOGCAT".Apache"),
+  ShibTargetApache(request_rec* req) : AbstractSPRequest(SHIBSP_LOGCAT ".Apache"),
         m_gotBody(false),m_firsttime(true),
 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)
         m_gssname(GSS_C_NO_NAME),
@@ -405,7 +415,11 @@ public:
     return type ? type : "";
   }
   long getContentLength() const {
-      return m_gotBody ? m_body.length() : m_req->remaining;
+      // Apache won't expose content length until the body's read.
+      if (!m_gotBody) {
+          getRequestBody();
+      }
+      return m_body.length();
   }
   string getRemoteAddr() const {
     string ret = AbstractSPRequest::getRemoteAddr();
@@ -536,7 +550,7 @@ public:
        if (!m_rc) {
           // this happens on subrequests
           // ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_setheader: no_m_rc\n");
-          m_rc = init_request_config(m_req);
+          m_rc = get_request_config(m_req);
        }
        if (!m_rc->env)
            m_rc->env = ap_make_table(m_req->pool, 10);
@@ -588,19 +602,23 @@ public:
       m_req->content_type = ap_psprintf(m_req->pool, "%s", type);
   }
   void setResponseHeader(const char* name, const char* value) {
-   HTTPResponse::setResponseHeader(name, value);
+    HTTPResponse::setResponseHeader(name, value);
+    if (name) {
 #ifdef SHIB_DEFERRED_HEADERS
-   if (!m_rc)
-      // this happens on subrequests
-      m_rc = init_request_config(m_req);
-    if (m_handler) {
-        if (!m_rc->hdr_out)
-            m_rc->hdr_out = ap_make_table(m_req->pool, 5);
-        ap_table_add(m_rc->hdr_out, name, value);
-    }
-    else
+        if (!m_rc) {
+            // this happens on subrequests
+            m_rc = get_request_config(m_req);
+        }
+        if (m_handler) {
+            if (!m_rc->hdr_out) {
+                m_rc->hdr_out = ap_make_table(m_req->pool, 5);
+            }
+            ap_table_add(m_rc->hdr_out, name, value);
+        }
+        else
 #endif
-    ap_table_add(m_req->err_headers_out, name, value);
+            ap_table_add(m_req->err_headers_out, name, value);
+    }
   }
   long sendResponse(istream& in, long status) {
     if (status != XMLTOOLING_HTTP_STATUS_OK)
@@ -667,21 +685,25 @@ public:
 // Apache hooks
 
 #ifndef SHIB_APACHE_13
-extern "C" apr_status_t shib_request_cleanup(void* r)
+extern "C" apr_status_t shib_request_cleanup(void* rc)
 {
-    if (r)
-        delete reinterpret_cast<ShibTargetApache*>(r);
+    if (rc && reinterpret_cast<shib_request_config*>(rc)->sta) {
+        delete reinterpret_cast<ShibTargetApache*>(reinterpret_cast<shib_request_config*>(rc)->sta);
+        reinterpret_cast<shib_request_config*>(rc)->sta = nullptr;
+    }
     return APR_SUCCESS;
 }
 #endif
 
-// Initial look at a request - create the per-request structure
+// Initial look at a request - create the per-request structure if need be
 static int shib_post_read(request_rec *r)
 {
-    shib_request_config* rc = init_request_config(r);
+    shib_request_config* rc = get_request_config(r);
 #ifdef SHIB_APACHE_24
-    rc->sta = new ShibTargetApache(r);
-    apr_pool_cleanup_register(r->pool, rc->sta, shib_request_cleanup, apr_pool_cleanup_null);
+    if (!rc->sta) {
+        rc->sta = new ShibTargetApache(r);
+        apr_pool_cleanup_register(r->pool, rc, shib_request_cleanup, apr_pool_cleanup_null);
+    }
 #endif
     return DECLINED;
 }
@@ -695,7 +717,7 @@ extern "C" int shib_check_user(request_rec* r)
     if (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bOff == 1)
         return DECLINED;
 
-    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user(%d): ENTER", (int)getpid());
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user entered in pid (%d)", (int)getpid());
 
     string threadid("[");
     threadid += lexical_cast<string>(getpid()) + "] shib_check_user";
@@ -708,8 +730,9 @@ extern "C" int shib_check_user(request_rec* r)
 #else
         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         if (!rc || !rc->sta) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user found no per-request structure");
-            return SERVER_ERROR;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user found no per-request structure");
+            shib_post_read(r);  // ensures objects are created if post_read hook didn't run
+            rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         }
         ShibTargetApache* psta = rc->sta;
 #endif
@@ -722,7 +745,7 @@ extern "C" int shib_check_user(request_rec* r)
         pair<bool,long> res = psta->getServiceProvider().doAuthentication(*psta, true);
         apr_pool_userdata_setn((const void*)42,g_UserDataKey,nullptr,r->pool);
         // If directed, install a spoof key to recognize when we've already cleared headers.
-        if (!g_spoofKey.empty() && (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bUseHeaders==1))
+        if (!g_spoofKey.empty() && (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bUseHeaders == 1))
             ap_table_set(r->headers_in, "Shib-Spoof-Check", g_spoofKey.c_str());
         if (res.first) {
 #ifdef SHIB_APACHE_24
@@ -784,12 +807,12 @@ extern "C" int shib_handler(request_rec* r)
     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_handler skipped since check_user ran");
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler skipped since check_user ran");
         return DECLINED;
     }
 #endif
 
-    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_handler(%d): ENTER: %s", (int)getpid(), r->handler);
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler entered in pid (%d): %s", (int)getpid(), r->handler);
 
     try {
 #ifndef SHIB_APACHE_24
@@ -798,8 +821,9 @@ extern "C" int shib_handler(request_rec* r)
 #else
         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         if (!rc || !rc->sta) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_handler found no per-request structure");
-            return SERVER_ERROR;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler found no per-request structure");
+            shib_post_read(r);  // ensures objects are created if post_read hook didn't run
+            rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         }
         ShibTargetApache* psta = rc->sta;
 #endif
@@ -811,7 +835,7 @@ extern "C" int shib_handler(request_rec* r)
         pair<bool,long> res = psta->getServiceProvider().doHandler(*psta);
         if (res.first) return res.second;
 
-        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "doHandler() did not do anything.");
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "doHandler() did not handle the request");
         return SERVER_ERROR;
     }
     catch (std::exception& e) {
@@ -844,7 +868,7 @@ extern "C" int shib_auth_checker(request_rec* r)
         return DECLINED;
     }
 
-    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker(%d): ENTER", (int)getpid());
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker entered in pid (%d)", (int)getpid());
 
     string threadid("[");
     threadid += lexical_cast<string>(getpid()) + "] shib_auth_checker";
@@ -857,8 +881,9 @@ extern "C" int shib_auth_checker(request_rec* r)
 #else
         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         if (!rc || !rc->sta) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker found no per-request structure");
-            return SERVER_ERROR;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker found no per-request structure");
+            shib_post_read(r);  // ensures objects are created if post_read hook didn't run
+            rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
         }
         ShibTargetApache* psta = rc->sta;
 #endif
@@ -889,20 +914,20 @@ extern "C" int shib_auth_checker(request_rec* r)
 // Overlays environment variables on top of subprocess table.
 extern "C" int shib_fixups(request_rec* r)
 {
-  shib_dir_config *dc = (shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib);
-  if (dc->bOff==1 || dc->bUseEnvVars==0)
-    return DECLINED;
+    shib_dir_config *dc = (shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib);
+    if (dc->bOff==1 || dc->bUseEnvVars==0)
+        return DECLINED;
 
-  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r), "shib_fixup(%d): ENTER", (int)getpid());
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_fixups entered in pid (%d)", (int)getpid());
 
-  shib_request_config *rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
-  if (rc==nullptr || rc->env==nullptr || ap_is_empty_table(rc->env))
+    shib_request_config *rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
+    if (rc==nullptr || rc->env==nullptr || ap_is_empty_table(rc->env))
         return DECLINED;
 
-  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r), "shib_fixup adding %d vars", ap_table_elts(rc->env)->nelts);
-  r->subprocess_env = ap_overlay_tables(r->pool, r->subprocess_env, rc->env);
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_fixups adding %d vars", ap_table_elts(rc->env)->nelts);
+    r->subprocess_env = ap_overlay_tables(r->pool, r->subprocess_env, rc->env);
 
-  return OK;
+    return OK;
 }
 
 
@@ -1016,11 +1041,11 @@ static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpf
     const char *group_name, *ll, *w;
 
 #ifdef SHIB_APACHE_13
-    if (!(f=ap_pcfg_openfile(r->pool,grpfile))) {
+    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);
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, SH_AP_R(r), "groups_for_user: could not open group file: %s\n", grpfile);
         return nullptr;
     }
 
@@ -1030,7 +1055,7 @@ static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpf
 #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");
+            "groups_for_user: could not create a subpool");
         return nullptr;
     }
 #endif
@@ -1279,9 +1304,16 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request,
             request.log(SPRequest::SPDebug, "htaccess: accepting valid-user based on active session");
             status = true;
         }
+        else if (sta->m_dc->bCompatWith24 == 1 && !strcmp(w,"shib-session") && session) {
+            request.log(SPRequest::SPDebug, "htaccess: accepting shib-session based on active session");
+            status = true;
+        }
         else if (!strcmp(w,"user") && !remote_user.empty()) {
             status = (doUser(*sta, t) == shib_acl_true);
         }
+        else if (sta->m_dc->bCompatWith24 == 1 && !strcmp(w,"shib-user") && !remote_user.empty()) {
+            status = (doUser(*sta, t) == shib_acl_true);
+        }
         else if (!strcmp(w,"group")  && !remote_user.empty()) {
             status = (doGroup(*sta, t) == shib_acl_true);
         }
@@ -1539,15 +1571,21 @@ const xercesc::DOMElement* ApacheRequestMapper::getElement() const
 }
 
 // Authz callbacks for Apache 2.4
+// For some reason, these get run twice for each request, once before hooks like check_user, etc.
+// and once after. The first time through, the request object exists, but isn't initialized.
+// The other case is subrequests of some kinds: then post_read doesn't run, and the objects
+// themselves don't exist. We do deferred creation of the objects in check_user to fix that case.
+// In each screwed up case, we return "denied" so that nothing bad happens.
 #ifdef SHIB_APACHE_24
 pair<ShibTargetApache*,authz_status> shib_base_check_authz(request_rec* r)
 {
     shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);
     if (!rc || !rc->sta) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_base_check_authz found no per-request structure");
-        return make_pair((ShibTargetApache*)nullptr, AUTHZ_GENERAL_ERROR);
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_base_check_authz found no per-request structure");
+        return make_pair((ShibTargetApache*)nullptr, AUTHZ_DENIED_NO_USER);
     }
     else if (!rc->sta->isInitialized()) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_base_check_authz found uninitialized request object");
         return make_pair((ShibTargetApache*)nullptr, AUTHZ_DENIED_NO_USER);
     }
     return make_pair(rc->sta, AUTHZ_GRANTED);
@@ -1561,16 +1599,17 @@ extern "C" authz_status shib_shibboleth_check_authz(request_rec* r, const char*
     return AUTHZ_GRANTED;
 }
 
-extern "C" authz_status shib_validuser_check_authz(request_rec* r, const char* require_line, const void*)
+extern "C" authz_status shib_session_check_authz(request_rec* r, const char* require_line, const void*)
 {
     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);
     if (!sta.first)
         return sta.second;
 
     try {
-        const Session* session = sta.first->getSession(false);
+        Session* session = sta.first->getSession(false, true, false);
+        Locker slocker(session, false);
         if (session) {
-            sta.first->log(SPRequest::SPDebug, "htaccess: accepting valid-user based on active session");
+            sta.first->log(SPRequest::SPDebug, "htaccess: accepting shib-session/valid-user based on active session");
             return AUTHZ_GRANTED;
         }
     }
@@ -1578,13 +1617,36 @@ extern "C" authz_status shib_validuser_check_authz(request_rec* r, const char* r
         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());
     }
 
+    sta.first->log(SPRequest::SPDebug, "htaccess: denying shib-access/valid-user rule, no active session");
     return AUTHZ_DENIED_NO_USER;
 }
 
-extern "C" authz_status shib_user_check_authz(request_rec* r, const char* require_line, const void*)
+extern "C" authz_status shib_validuser_check_authz(request_rec* r, const char* require_line, const void*)
 {
-    if (!r->user || !*(r->user))
+    // Shouldn't have actually ever hooked this, and now we're in conflict with mod_authz_user over the meaning.
+    // For now, added a command to restore "normal" semantics for valid-user so that combined deployments can
+    // use valid-user for non-Shibboleth cases and shib-session for the Shibboleth semantic.
+
+    // In future, we may want to expose the AuthType set to honor down at this level so we can differentiate
+    // based on AuthType. Unfortunately we allow overriding the AuthType to honor and we don't have access to
+    // that setting from the ServiceProvider class..
+
+    shib_server_config* sc = (shib_server_config*)ap_get_module_config(r->server->module_config, &mod_shib);
+    if (sc->bCompatValidUser != 1) {
+        return shib_session_check_authz(r, require_line, nullptr);
+    }
+
+    // Reproduce mod_authz_user version...
+
+    if (!r->user) {
         return AUTHZ_DENIED_NO_USER;
+    }
+
+    return AUTHZ_GRANTED;
+}
+
+extern "C" authz_status shib_ext_user_check_authz(request_rec* r, const char* require_line, const void*)
+{
     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);
     if (!sta.first)
         return sta.second;
@@ -1595,6 +1657,43 @@ extern "C" authz_status shib_user_check_authz(request_rec* r, const char* requir
     return AUTHZ_DENIED;
 }
 
+extern "C" authz_status shib_user_check_authz(request_rec* r, const char* require_line, const void*)
+{
+    // Shouldn't have actually ever hooked this, and now we're in conflict with mod_authz_user over the meaning.
+    // For now, added a command to restore "normal" semantics for user rules so that combined deployments can
+    // use user for non-Shibboleth cases and shib-user for the Shibboleth semantic.
+
+    // In future, we may want to expose the AuthType set to honor down at this level so we can differentiate
+    // based on AuthType. Unfortunately we allow overriding the AuthType to honor and we don't have access to
+    // that setting from the ServiceProvider class..
+
+    shib_server_config* sc = (shib_server_config*)ap_get_module_config(r->server->module_config, &mod_shib);
+    if (sc->bCompatValidUser != 1) {
+        return shib_ext_user_check_authz(r, require_line, nullptr);
+    }
+
+    // Reproduce mod_authz_user version...
+
+    if (!r->user) {
+        return AUTHZ_DENIED_NO_USER;
+    }
+       
+    const char* t = require_line;
+    const char *w;
+    while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
+        if (!strcmp(r->user, w)) {
+            return AUTHZ_GRANTED;
+        }
+    }
+       
+    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01663)
+        "access to %s failed, reason: user '%s' does not meet "
+        "'require'ments for user to be allowed access",
+        r->uri, r->user);
+       
+    return AUTHZ_DENIED;
+}
+
 extern "C" authz_status shib_acclass_check_authz(request_rec* r, const char* require_line, const void*)
 {
     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);
@@ -1604,7 +1703,8 @@ extern "C" authz_status shib_acclass_check_authz(request_rec* r, const char* req
     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();
 
     try {
-        const Session* session = sta.first->getSession(false);
+        Session* session = sta.first->getSession(false, true, false);
+        Locker slocker(session, false);
         if (session && hta.doAuthnContext(*sta.first, session->getAuthnContextClassRef(), require_line) == AccessControl::shib_acl_true)
             return AUTHZ_GRANTED;
         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;
@@ -1625,7 +1725,8 @@ extern "C" authz_status shib_acdecl_check_authz(request_rec* r, const char* requ
     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();
 
     try {
-        const Session* session = sta.first->getSession(false);
+        Session* session = sta.first->getSession(false, true, false);
+        Locker slocker(session, false);
         if (session && hta.doAuthnContext(*sta.first, session->getAuthnContextDeclRef(), require_line) == AccessControl::shib_acl_true)
             return AUTHZ_GRANTED;
         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;
@@ -1646,7 +1747,8 @@ extern "C" authz_status shib_attr_check_authz(request_rec* r, const char* requir
     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();
 
     try {
-        const Session* session = sta.first->getSession(false);
+        Session* session = sta.first->getSession(false, true, false);
+        Locker slocker(session, false);
         if (session) {
             const char* rule = ap_getword_conf(r->pool, &require_line);
             if (rule && hta.doShibAttr(*sta.first, session, rule, require_line) == AccessControl::shib_acl_true)
@@ -1670,7 +1772,8 @@ extern "C" authz_status shib_plugin_check_authz(request_rec* r, const char* requ
     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();
 
     try {
-        const Session* session = sta.first->getSession(false);
+        Session* session = sta.first->getSession(false, true, false);
+        Locker slocker(session, false);
         if (session) {
             const char* config = ap_getword_conf(r->pool, &require_line);
             if (config && hta.doAccessControl(*sta.first, session, config) == AccessControl::shib_acl_true)
@@ -1702,6 +1805,14 @@ extern "C" const char* shib_set_server_string_slot(cmd_parms* parms, void*, cons
     return nullptr;
 }
 
+extern "C" const char* shib_set_server_flag_slot(cmd_parms* parms, void*, int arg)
+{
+    char* base=(char*)ap_get_module_config(parms->server->module_config,&mod_shib);
+    size_t offset=(size_t)parms->info;
+    *((int*)(base + offset)) = arg;
+    return nullptr;
+}
+
 extern "C" const char* shib_ap_set_file_slot(cmd_parms* parms,
 #ifdef SHIB_APACHE_13
                                             char* arg1, char* arg2
@@ -1758,11 +1869,10 @@ extern "C" const char* shib_set_acl_slot(cmd_parms* params, shib_dir_config* dc,
 extern "C" void shib_child_exit(server_rec* s, SH_AP_POOL* p)
 {
     if (g_Config) {
-        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->term();
         g_Config = nullptr;
-        ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit() done");
     }
+    ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s), "child_exit: mod_shib shutdown in pid (%d)", (int)getpid());
 }
 #else
 /*
@@ -1775,15 +1885,57 @@ extern "C" apr_status_t shib_exit(void* data)
         g_Config->term();
         g_Config = nullptr;
     }
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,nullptr,"shib_exit() done");
+    server_rec* s = reinterpret_cast<server_rec*>(data);
+    ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s), "shib_exit: mod_shib shutdown in pid (%d)", (int)getpid());
     return OK;
 }
+
+/*
+ * shib_post_config()
+ *  We do the library init/term work here for 2.x to reduce overhead and
+ *  get default logging established before the fork happens.
+ */
+apr_status_t shib_post_config(apr_pool_t* p, apr_pool_t*, apr_pool_t*, server_rec* s)
+{
+    // Initialize runtime components.
+    ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s),"post_config: mod_shib initializing in pid (%d)", (int)getpid());
+
+    if (g_Config) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(s), "post_config: mod_shib already initialized");
+        return !OK;
+    }
+
+    g_Config = &SPConfig::getConfig();
+    g_Config->setFeatures(
+        SPConfig::Listener |
+        SPConfig::Caching |
+        SPConfig::RequestMapping |
+        SPConfig::InProcess |
+        SPConfig::Logging |
+        SPConfig::Handlers
+        );
+    if (!g_Config->init(g_szSchemaDir, g_szPrefix)) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "post_config: mod_shib failed to initialize libraries");
+        return !OK;
+    }
+#ifndef SHIB_APACHE_24
+    g_Config->AccessControlManager.registerFactory(HT_ACCESS_CONTROL, &htAccessFactory);
+#endif
+    g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);
+
+    // Set the cleanup handler, passing in the server_rec for logging.
+    apr_pool_cleanup_register(p, s, &shib_exit, apr_pool_cleanup_null);
+
+    return OK;
+}
+
 #endif
 
 /*
- * shire_child_init()
+ * shib_child_init()
  *  Things to do when the child process is initialized.
- *  (or after the configs are read in apache-2)
+ *  We can't use post-config for all of it on 2.x because only the forking thread shows
+ *  up in the child, losing the internal threads spun up by plugins in the SP.
  */
 #ifdef SHIB_APACHE_13
 extern "C" void shib_child_init(server_rec* s, SH_AP_POOL* p)
@@ -1793,14 +1945,16 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
 {
     // Initialize runtime components.
 
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init(%d) starting", (int)getpid());
+    ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s),"child_init: mod_shib initializing in pid (%d)", (int)getpid());
 
+    // 2.x versions have already initialized the libraries.
+#ifdef SHIB_APACHE_13
     if (g_Config) {
-        ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() already initialized!");
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib already initialized, exiting");
         exit(1);
     }
 
-    g_Config=&SPConfig::getConfig();
+    g_Config = &SPConfig::getConfig();
     g_Config->setFeatures(
         SPConfig::Listener |
         SPConfig::Caching |
@@ -1810,21 +1964,21 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
         SPConfig::Handlers
         );
     if (!g_Config->init(g_szSchemaDir, g_szPrefix)) {
-        ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to initialize libraries");
+        ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib failed to initialize libraries");
         exit(1);
     }
-#ifndef SHIB_APACHE_24
     g_Config->AccessControlManager.registerFactory(HT_ACCESS_CONTROL, &htAccessFactory);
-#endif
     g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);
+#endif
 
+    // The config gets installed for all versions here due to the background thread/fork issues.
     try {
         if (!g_Config->instantiate(g_szSHIBConfig, true))
             throw runtime_error("unknown error");
     }
     catch (std::exception& ex) {
-        ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"%s",ex.what());
-        ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to load configuration");
+        ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib failed to load configuration: %s", ex.what());
+        g_Config->term();
         exit(1);
     }
 
@@ -1846,10 +2000,10 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
         g_catchAll = flag.first && flag.second;
     }
 
-    // Set the cleanup handler
-    apr_pool_cleanup_register(p, nullptr, &shib_exit, apr_pool_cleanup_null);
+    // Set the cleanup handler, passing in the server_rec for logging.
+    apr_pool_cleanup_register(p, s, &shib_exit, apr_pool_cleanup_null);
 
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(s), "shib_child_init() done");
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib config initialized");
 }
 
 // Output filters
@@ -1876,7 +2030,7 @@ static apr_status_t do_output_filter(ap_filter_t *f, apr_bucket_brigade *in)
     shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib);
 
     if (rc && rc->hdr_out) {
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_out_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "output_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
         // can't use overlap call because it will collapse Set-Cookie headers
         //apr_table_overlap(r->headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);
         apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL);
@@ -1895,7 +2049,7 @@ static apr_status_t do_error_filter(ap_filter_t *f, apr_bucket_brigade *in)
     shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib);
 
     if (rc && rc->hdr_out) {
-        ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_err_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "error_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
         // can't use overlap call because it will collapse Set-Cookie headers
         //apr_table_overlap(r->err_headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);
         apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL);
@@ -2012,7 +2166,9 @@ module MODULE_VAR_EXPORT mod_shib = {
 #ifdef SHIB_APACHE_24
 extern "C" const authz_provider shib_authz_shibboleth_provider = { &shib_shibboleth_check_authz, nullptr };
 extern "C" const authz_provider shib_authz_validuser_provider = { &shib_validuser_check_authz, nullptr };
+extern "C" const authz_provider shib_authz_session_provider = { &shib_session_check_authz, nullptr };
 extern "C" const authz_provider shib_authz_user_provider = { &shib_user_check_authz, nullptr };
+extern "C" const authz_provider shib_authz_ext_user_provider = { &shib_ext_user_check_authz, nullptr };
 extern "C" const authz_provider shib_authz_acclass_provider = { &shib_acclass_check_authz, nullptr };
 extern "C" const authz_provider shib_authz_acdecl_provider = { &shib_acdecl_check_authz, nullptr };
 extern "C" const authz_provider shib_authz_attr_provider = { &shib_attr_check_authz, nullptr };
@@ -2028,6 +2184,7 @@ extern "C" void shib_register_hooks (apr_pool_t *p)
     ap_hook_insert_error_filter(set_error_filter, nullptr, nullptr, APR_HOOK_LAST);
     ap_hook_post_read_request(shib_post_read, nullptr, nullptr, APR_HOOK_MIDDLE);
 #endif
+    ap_hook_post_config(shib_post_config, nullptr, nullptr, APR_HOOK_MIDDLE);
     ap_hook_child_init(shib_child_init, nullptr, nullptr, APR_HOOK_MIDDLE);
     const char* prereq = getenv("SHIBSP_APACHE_PREREQ");
 #ifdef SHIB_APACHE_24
@@ -2055,7 +2212,9 @@ extern "C" void shib_register_hooks (apr_pool_t *p)
 #ifdef SHIB_APACHE_24
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shibboleth", AUTHZ_PROVIDER_VERSION, &shib_authz_shibboleth_provider, AP_AUTH_INTERNAL_PER_CONF);
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "valid-user", AUTHZ_PROVIDER_VERSION, &shib_authz_validuser_provider, AP_AUTH_INTERNAL_PER_CONF);
+    ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-session", AUTHZ_PROVIDER_VERSION, &shib_authz_session_provider, AP_AUTH_INTERNAL_PER_CONF);
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "user", AUTHZ_PROVIDER_VERSION, &shib_authz_user_provider, AP_AUTH_INTERNAL_PER_CONF);
+    ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-user", AUTHZ_PROVIDER_VERSION, &shib_authz_ext_user_provider, AP_AUTH_INTERNAL_PER_CONF);
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "authnContextClassRef", AUTHZ_PROVIDER_VERSION, &shib_authz_acclass_provider, AP_AUTH_INTERNAL_PER_CONF);
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "authnContextDeclRef", AUTHZ_PROVIDER_VERSION, &shib_authz_acdecl_provider, AP_AUTH_INTERNAL_PER_CONF);
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-attr", AUTHZ_PROVIDER_VERSION, &shib_authz_attr_provider, AP_AUTH_INTERNAL_PER_CONF);
@@ -2108,6 +2267,9 @@ static command_rec shib_cmds[] = {
     AP_INIT_FLAG("ShibRequestMapperAuthz", (config_fn_t)ap_set_flag_slot,
         (void *) offsetof (shib_dir_config, bRequestMapperAuthz),
         OR_AUTHCFG, "Support access control via shibboleth2.xml / RequestMapper"),
+    AP_INIT_FLAG("ShibCompatValidUser", (config_fn_t)shib_set_server_flag_slot,
+        (void *) offsetof (shib_server_config, bCompatValidUser),
+        RSRC_CONF, "Handle 'require valid-user' in mod_authz_user-compatible fashion (requiring username)"),
 #else
     AP_INIT_TAKE1("AuthGroupFile", (config_fn_t)shib_ap_set_file_slot,
         (void *) offsetof (shib_dir_config, szAuthGrpFile),