https://issues.shibboleth.net/jira/browse/SSPCPP-425
[shibboleth/cpp-sp.git] / apache / mod_apache.cpp
index 3bc0770..6ecb324 100644 (file)
@@ -1,17 +1,21 @@
-/*
- *  Copyright 2001-2011 Internet2
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
  */
 
 /**
 
 #undef _XPG4_2
 
+#include <set>
+#include <memory>
+#include <fstream>
+#include <stdexcept>
+#include <boost/lexical_cast.hpp>
+
 // Apache specific header files
 #include <httpd.h>
 #include <http_config.h>
 #include <apr_pools.h>
 #endif
 
-#include <set>
-#include <memory>
-#include <fstream>
-#include <sstream>
-#include <stdexcept>
-
+#include <cstddef>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>            // for getpid()
 #endif
 
 using namespace shibsp;
 using namespace xmltooling;
+using namespace boost;
 using namespace std;
 using xercesc::RegularExpression;
 using xercesc::XMLException;
@@ -168,6 +174,7 @@ struct shib_dir_config
     int bExportAssertion;   // export SAML assertion to the environment?
     int bUseEnvVars;        // use environment?
     int bUseHeaders;        // use headers?
+    int bExpireRedirects;   // expire redirects?
 };
 
 // creates per-directory config structure
@@ -188,6 +195,7 @@ extern "C" void* create_shib_dir_config (SH_AP_POOL* p, char* d)
     dc->bExportAssertion = -1;
     dc->bUseEnvVars = -1;
     dc->bUseHeaders = -1;
+    dc->bExpireRedirects = -1;
     return dc;
 }
 
@@ -252,6 +260,7 @@ extern "C" void* merge_shib_dir_config (SH_AP_POOL* p, void* base, void* sub)
     dc->bAuthoritative=((child->bAuthoritative==-1) ? parent->bAuthoritative : child->bAuthoritative);
     dc->bUseEnvVars=((child->bUseEnvVars==-1) ? parent->bUseEnvVars : child->bUseEnvVars);
     dc->bUseHeaders=((child->bUseHeaders==-1) ? parent->bUseHeaders : child->bUseHeaders);
+    dc->bExpireRedirects=((child->bExpireRedirects==-1) ? parent->bExpireRedirects : child->bExpireRedirects);
     return dc;
 }
 
@@ -309,6 +318,30 @@ extern "C" const char* shib_table_set(cmd_parms* parms, shib_dir_config* dc, con
     return nullptr;
 }
 
+extern "C" const char* shib_set_acl_slot(cmd_parms* params, shib_dir_config* dc, char* arg)
+{
+    bool absolute;
+    switch (*arg) {
+        case 0:
+            absolute = false;
+            break;
+        case '/':
+        case '\\':
+            absolute = true;
+            break;
+        case '.':
+            absolute = (*(arg+1) == '.' || *(arg+1) == '/' || *(arg+1) == '\\');
+            break;
+        default:
+            absolute = *(arg+1) == ':';
+    }
+
+    if (absolute || !params->path)
+        dc->szAccessControl = ap_pstrdup(params->pool, arg);
+    else
+        dc->szAccessControl = ap_pstrcat(params->pool, params->path, arg);
+    return nullptr;
+}
 
 class ShibTargetApache : public AbstractSPRequest
 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)
@@ -582,6 +615,10 @@ public:
   long sendRedirect(const char* url) {
     HTTPResponse::sendRedirect(url);
     ap_table_set(m_req->headers_out, "Location", url);
+    if (m_dc->bExpireRedirects != 0) {
+        ap_table_set(m_req->err_headers_out, "Expires", "Wed, 01 Jan 1997 12:00:00 GMT");
+        ap_table_set(m_req->err_headers_out, "Cache-Control", "private,no-store,no-cache,max-age=0");
+    }
     return REDIRECT;
   }
   const vector<string>& getClientCertificates() const {
@@ -620,9 +657,9 @@ 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", (int)getpid());
 
-  ostringstream threadid;
-  threadid << "[" << getpid() << "] shib_check_user" << '\0';
-  xmltooling::NDC ndc(threadid.str().c_str());
+  string threadid("[");
+  threadid += lexical_cast<string>(getpid()) + "] shib_check_user";
+  xmltooling::NDC ndc(threadid.c_str());
 
   try {
     ShibTargetApache sta(r,false,true);
@@ -642,7 +679,7 @@ extern "C" int shib_check_user(request_rec* r)
     // export happened successfully..  this user is ok.
     return OK;
   }
-  catch (exception& e) {
+  catch (std::exception& e) {
     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user threw an exception: %s", e.what());
     return SERVER_ERROR;
   }
@@ -660,9 +697,9 @@ extern "C" int shib_handler(request_rec* r)
   if (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bOff==1)
     return DECLINED;
 
-  ostringstream threadid;
-  threadid << "[" << getpid() << "] shib_handler" << '\0';
-  xmltooling::NDC ndc(threadid.str().c_str());
+  string threadid("[");
+  threadid += lexical_cast<string>(getpid()) + "] shib_handler";
+  xmltooling::NDC ndc(threadid.c_str());
 
 #ifndef SHIB_APACHE_13
   // With 2.x, this handler always runs, though last.
@@ -687,7 +724,7 @@ extern "C" int shib_handler(request_rec* r)
     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "doHandler() did not do anything.");
     return SERVER_ERROR;
   }
-  catch (exception& e) {
+  catch (std::exception& e) {
     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_handler threw an exception: %s", e.what());
     return SERVER_ERROR;
   }
@@ -711,9 +748,9 @@ 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';
-  xmltooling::NDC ndc(threadid.str().c_str());
+  string threadid("[");
+  threadid += lexical_cast<string>(getpid()) + "] shib_auth_checker";
+  xmltooling::NDC ndc(threadid.c_str());
 
   try {
     ShibTargetApache sta(r,false,false);
@@ -725,7 +762,7 @@ extern "C" int shib_auth_checker(request_rec* r)
     // Just let Apache (or some other module) decide what to do.
     return DECLINED;
   }
-  catch (exception& e) {
+  catch (std::exception& e) {
     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker threw an exception: %s", e.what());
     return SERVER_ERROR;
   }
@@ -759,7 +796,7 @@ class ApacheRequestMapper : public virtual RequestMapper, public virtual Propert
 {
 public:
     ApacheRequestMapper(const xercesc::DOMElement* e);
-    ~ApacheRequestMapper() { delete m_mapper; delete m_htaccess; delete m_staKey; delete m_propsKey; }
+    ~ApacheRequestMapper() {}
     Lockable* lock() { return m_mapper->lock(); }
     void unlock() { m_staKey->setData(nullptr); m_propsKey->setData(nullptr); m_mapper->unlock(); }
     Settings getSettings(const HTTPRequest& request) const;
@@ -776,10 +813,9 @@ public:
     const xercesc::DOMElement* getElement() const;
 
 private:
-    RequestMapper* m_mapper;
-    ThreadKey* m_staKey;
-    ThreadKey* m_propsKey;
-    AccessControl* m_htaccess;
+    scoped_ptr<RequestMapper> m_mapper;
+    scoped_ptr<ThreadKey> m_staKey,m_propsKey;
+    mutable htAccessControl m_htaccess;
 };
 
 RequestMapper* ApacheRequestMapFactory(const xercesc::DOMElement* const & e)
@@ -787,20 +823,18 @@ RequestMapper* ApacheRequestMapFactory(const xercesc::DOMElement* const & e)
     return new ApacheRequestMapper(e);
 }
 
-ApacheRequestMapper::ApacheRequestMapper(const xercesc::DOMElement* e) : m_mapper(nullptr), m_staKey(nullptr), m_propsKey(nullptr), m_htaccess(nullptr)
+ApacheRequestMapper::ApacheRequestMapper(const xercesc::DOMElement* e)
+    : m_mapper(SPConfig::getConfig().RequestMapperManager.newPlugin(XML_REQUEST_MAPPER,e)),
+        m_staKey(ThreadKey::create(nullptr)), m_propsKey(ThreadKey::create(nullptr))
 {
-    m_mapper=SPConfig::getConfig().RequestMapperManager.newPlugin(XML_REQUEST_MAPPER,e);
-    m_htaccess=new htAccessControl();
-    m_staKey=ThreadKey::create(nullptr);
-    m_propsKey=ThreadKey::create(nullptr);
 }
 
 RequestMapper::Settings ApacheRequestMapper::getSettings(const HTTPRequest& request) const
 {
-    Settings s=m_mapper->getSettings(request);
+    Settings s = m_mapper->getSettings(request);
     m_staKey->setData((void*)dynamic_cast<const ShibTargetApache*>(&request));
     m_propsKey->setData((void*)s.first);
-    return pair<const PropertySet*,AccessControl*>(this,s.second ? s.second : m_htaccess);
+    return pair<const PropertySet*,AccessControl*>(this, s.second ? s.second : &m_htaccess);
 }
 
 pair<bool,bool> ApacheRequestMapper::getBool(const char* name, const char* ns) const
@@ -928,7 +962,7 @@ void ApacheRequestMapper::getAll(map<string,const char*>& properties) const
         properties["exportAssertion"] = (sta->m_dc->bExportAssertion==1) ? "true" : "false";
 
     if (sta->m_dc->tSettings)
-        ap_table_do(_rm_get_all_table_walk, &properties, sta->m_dc->tSettings, nullptr);
+        ap_table_do(_rm_get_all_table_walk, &properties, sta->m_dc->tSettings, NULL);
 }
 
 const PropertySet* ApacheRequestMapper::getPropertySet(const char* name, const char* ns) const
@@ -995,7 +1029,7 @@ bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute*
 {
     bool caseSensitive = attr->isCaseSensitive();
     const vector<string>& vals = attr->getSerializedValues();
-    for (vector<string>::const_iterator v=vals.begin(); v!=vals.end(); ++v) {
+    for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
         if (re) {
             auto_arrayptr<XMLCh> trans(fromUTF8(v->c_str()));
             if (re->matches(trans.get())) {
@@ -1036,17 +1070,19 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request,
                aclresult_t result = shib_acl_false;
                try {
             ifstream aclfile(sta->m_dc->szAccessControl);
+            if (!aclfile)
+                throw ConfigurationException("Unable to open access control file ($1).", params(1, sta->m_dc->szAccessControl));
             xercesc::DOMDocument* acldoc = XMLToolingConfig::getConfig().getParser().parse(aclfile);
                    XercesJanitor<xercesc::DOMDocument> docjanitor(acldoc);
                    static XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
             string t(XMLHelper::getAttrString(acldoc ? acldoc->getDocumentElement() : nullptr, nullptr, _type));
             if (t.empty())
                 throw ConfigurationException("Missing type attribute in AccessControl plugin configuration.");
-            auto_ptr<AccessControl> aclplugin(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acldoc->getDocumentElement()));
+            scoped_ptr<AccessControl> aclplugin(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acldoc->getDocumentElement()));
                        Locker acllock(aclplugin.get());
                        result = aclplugin->authorized(request, session);
                }
-               catch (exception& ex) {
+               catch (std::exception& ex) {
                        request.log(SPRequest::SPError, ex.what());
                }
 
@@ -1242,12 +1278,10 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request,
                 }
 
                 try {
-                    auto_ptr<RegularExpression> re;
+                    scoped_ptr<RegularExpression> re;
                     if (regexp) {
-                        delete re.release();
                         auto_arrayptr<XMLCh> trans(fromUTF8(w));
-                        auto_ptr<xercesc::RegularExpression> temp(new xercesc::RegularExpression(trans.get()));
-                        re=temp;
+                        re.reset(new xercesc::RegularExpression(trans.get()));
                     }
                     
                     pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs2(attrs);
@@ -1408,7 +1442,7 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
         if (!g_Config->instantiate(g_szSHIBConfig, true))
             throw runtime_error("unknown error");
     }
-    catch (exception& ex) {
+    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");
         exit(1);
@@ -1465,7 +1499,7 @@ static apr_status_t do_output_filter(ap_filter_t *f, apr_bucket_brigade *in)
         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);
         // 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,nullptr);
+        apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL);
     }
 
     /* remove ourselves from the filter chain */
@@ -1484,7 +1518,7 @@ static apr_status_t do_error_filter(ap_filter_t *f, apr_bucket_brigade *in)
         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);
         // 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,nullptr);
+        apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL);
     }
 
     /* remove ourselves from the filter chain */
@@ -1516,8 +1550,7 @@ static command_rec shire_cmds[] = {
   {"ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,
    OR_AUTHCFG, TAKE2, "Set arbitrary Shibboleth request property for content"},
 
-  {"ShibAccessControl", (config_fn_t)ap_set_string_slot,
-   (void *) XtOffsetOf (shib_dir_config, szAccessControl),
+  {"ShibAccessControl", (config_fn_t)shib_set_acl_slot, nullptr,
    OR_AUTHCFG, TAKE1, "Set arbitrary Shibboleth access control plugin for content"},
 
   {"ShibDisable", (config_fn_t)ap_set_flag_slot,
@@ -1556,6 +1589,9 @@ static command_rec shire_cmds[] = {
   {"ShibUseHeaders", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bUseHeaders),
    OR_AUTHCFG, FLAG, "Export attributes using custom HTTP headers"},
+  {"ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,
+   (void *) XtOffsetOf (shib_dir_config, bExpireRedirects),
+   OR_AUTHCFG, FLAG, "Expire SP-generated redirects"},
 
   {nullptr}
 };
@@ -1635,8 +1671,7 @@ static command_rec shib_cmds[] = {
     AP_INIT_TAKE2("ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,
         OR_AUTHCFG, "Set arbitrary Shibboleth request property for content"),
 
-    AP_INIT_TAKE1("ShibAccessControl", (config_fn_t)ap_set_string_slot,
-        (void *) offsetof (shib_dir_config, szAccessControl),
+    AP_INIT_TAKE1("ShibAccessControl", (config_fn_t)shib_set_acl_slot, nullptr,
         OR_AUTHCFG, "Set arbitrary Shibboleth access control plugin for content"),
 
     AP_INIT_FLAG("ShibDisable", (config_fn_t)ap_set_flag_slot,
@@ -1675,6 +1710,9 @@ static command_rec shib_cmds[] = {
     AP_INIT_FLAG("ShibUseHeaders", (config_fn_t)ap_set_flag_slot,
         (void *) offsetof (shib_dir_config, bUseHeaders),
         OR_AUTHCFG, "Export attributes using custom HTTP headers"),
+    AP_INIT_FLAG("ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,
+        (void *) offsetof (shib_dir_config, bExpireRedirects),
+        OR_AUTHCFG, "Expire SP-generated redirects"),
 
     {nullptr}
 };