Add Handler feature for config.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 7 Sep 2007 19:51:46 +0000 (19:51 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 7 Sep 2007 19:51:46 +0000 (19:51 +0000)
Allow access control logic to return indeterminate results, mapping to Apache DECLINE.
Refine htaccess plugin to match behavior of other Apache modules.

git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2456 cb58f699-b61c-0410-a6fe-9272a202ed29

apache/mod_apache.cpp
shibsp/AccessControl.h
shibsp/ServiceProvider.cpp
shibsp/impl/XMLAccessControl.cpp
shibsp/impl/XMLRequestMapper.cpp

index bfa823f..b7a00d0 100644 (file)
@@ -145,7 +145,8 @@ struct shib_dir_config
 
     // RM Configuration
     char* szAuthGrpFile;    // Auth GroupFile name
-    int bRequireAll;        // all require directives must match, otherwise OR logic
+    int bRequireAll;        // all "known" require directives must match, otherwise OR logic
+    int bAuthoritative;     // allow htaccess plugin to DECLINE when authz fails
 
     // Content Configuration
     char* szApplicationId;  // Shib applicationId value
@@ -166,6 +167,7 @@ extern "C" void* create_shib_dir_config (SH_AP_POOL* p, char* d)
     dc->tSettings = NULL;
     dc->szAuthGrpFile = NULL;
     dc->bRequireAll = -1;
+    dc->bAuthoritative = -1;
     dc->szApplicationId = NULL;
     dc->szRequireWith = NULL;
     dc->szRedirectToSSL = NULL;
@@ -229,6 +231,7 @@ extern "C" void* merge_shib_dir_config (SH_AP_POOL* p, void* base, void* sub)
     dc->bRequireSession=((child->bRequireSession==-1) ? parent->bRequireSession : child->bRequireSession);
     dc->bExportAssertion=((child->bExportAssertion==-1) ? parent->bExportAssertion : child->bExportAssertion);
     dc->bRequireAll=((child->bRequireAll==-1) ? parent->bRequireAll : child->bRequireAll);
+    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);
     return dc;
@@ -637,8 +640,9 @@ extern "C" int shib_auth_checker(request_rec* r)
     pair<bool,long> res = sta.getServiceProvider().doAuthorization(sta);
     if (res.first) return res.second;
 
-    // We're all okay.
-    return OK;
+    // The SP method should always return true, so if we get this far, something unusual happened.
+    // Just let Apache (or some other module) decide what to do.
+    return DECLINED;
   }
   catch (exception& e) {
     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker threw an exception: %s", e.what());
@@ -660,7 +664,7 @@ public:
     ~htAccessControl() {}
     Lockable* lock() {return this;}
     void unlock() {}
-    bool authorized(const SPRequest& request, const Session* session) const;
+    aclresult_t authorized(const SPRequest& request, const Session* session) const;
 private:
     bool checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const;
 };
@@ -895,7 +899,7 @@ bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute*
     return false;
 }
 
-bool htAccessControl::authorized(const SPRequest& request, const Session* session) const
+AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, const Session* session) const
 {
     // Make sure the object is our type.
     const ShibTargetApache* sta=dynamic_cast<const ShibTargetApache*>(&request);
@@ -910,7 +914,7 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
     
     const array_header* reqs_arr=ap_requires(sta->m_req);
     if (!reqs_arr)
-        return true;
+        return shib_acl_indeterminate;  // should never happen
 
     require_line* reqs=(require_line*)reqs_arr->elts;
     
@@ -921,7 +925,7 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
 
 #define SHIB_AP_CHECK_IS_OK {           \
      if (sta->m_dc->bRequireAll < 1)    \
-         return true;                   \
+         return shib_acl_true;          \
      auth_OK[x] = true;                 \
      continue;                          \
 }
@@ -946,8 +950,9 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                 request.log(SPRequest::SPDebug,"htAccessControl plugin accepting valid-user based on active session");
                 SHIB_AP_CHECK_IS_OK;
             }
-            else
+            else {
                 request.log(SPRequest::SPError,"htAccessControl plugin rejecting access for valid-user rule, no session is active");
+            }
         }
         else if (!strcmp(w,"user") && !remote_user.empty()) {
             bool regexp=false;
@@ -1047,15 +1052,15 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
         }
     }
 
-    // check if all require directives are true
+    // If we get here, we either "failed" or we're in require all mode.
     bool auth_all_OK = true;
     for (int i= 0; i<reqs_arr->nelts; i++) {
         auth_all_OK &= auth_OK[i];
     }
     if (auth_all_OK || !method_restricted)
-        return true;
+        return shib_acl_true;
 
-    return false;
+    return (sta->m_dc->bAuthoritative != 0) ? shib_acl_false : shib_acl_indeterminate;
 }
 
 
@@ -1148,7 +1153,8 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
         SPConfig::Caching |
         SPConfig::RequestMapping |
         SPConfig::InProcess |
-        SPConfig::Logging
+        SPConfig::Logging |
+        SPConfig::Handlers
         );
     if (!g_Config->init(g_szSchemaDir)) {
         ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to initialize libraries");
@@ -1297,6 +1303,9 @@ static command_rec shire_cmds[] = {
   {"ShibRequireAll", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bRequireAll),
    OR_AUTHCFG, FLAG, "All require directives must match"},
+  {"AuthzShibAuthoritative", (config_fn_t)ap_set_flag_slot,
+   (void *) XtOffsetOf (shib_dir_config, bAuthoritative),
+   OR_AUTHCFG, FLAG, "Allow failed mod_shib htaccess authorization to fall through to other modules"},
   {"ShibUseEnvironment", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bUseEnvVars),
    OR_AUTHCFG, FLAG, "Export attributes using environment variables (default)"},
@@ -1398,6 +1407,9 @@ static command_rec shib_cmds[] = {
     AP_INIT_FLAG("ShibRequireAll", (config_fn_t)ap_set_flag_slot,
         (void *) offsetof (shib_dir_config, bRequireAll),
         OR_AUTHCFG, "All require directives must match"),
+    AP_INIT_FLAG("AuthzShibAuthoritative", (config_fn_t)ap_set_flag_slot,
+        (void *) offsetof (shib_dir_config, bAuthoritative),
+        OR_AUTHCFG, "Allow failed mod_shib htaccess authorization to fall through to other modules"),
     AP_INIT_FLAG("ShibUseEnvironment", (config_fn_t)ap_set_flag_slot,
         (void *) offsetof (shib_dir_config, bUseEnvVars),
         OR_AUTHCFG, "Export attributes using environment variables (default)"),
index 7d3ea73..77774b6 100644 (file)
@@ -47,13 +47,22 @@ namespace shibsp {
         virtual ~AccessControl() {}
 
         /**
+         * Possible results from an access control decision.
+         */
+        enum aclresult_t {
+            shib_acl_true,
+            shib_acl_false,
+            shib_acl_indeterminate
+        };
+        
+        /**
          * Perform an authorization check.
          * 
          * @param request   SP request information
          * @param session   active user session, if any
          * @return true iff access should be granted
          */
-        virtual bool authorized(const SPRequest& request, const Session* session) const=0;
+        virtual aclresult_t authorized(const SPRequest& request, const Session* session) const=0;
     };
 
     /**
index 9ee1ecf..dd8ce3b 100644 (file)
@@ -283,21 +283,27 @@ pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
             }
        
             Locker acllock(settings.second);
-            if (settings.second->authorized(request,session)) {
-                // Let the caller decide how to proceed.
-                request.log(SPRequest::SPDebug, "access control provider granted access");
-                return make_pair(false,0);
-            }
-            else {
-                request.log(SPRequest::SPWarn, "access control provider denied access");
-                TemplateParameters tp;
-                tp.m_map["requestURL"] = targetURL;
-                return make_pair(true,sendError(request, app, "access", tp));
+            switch (settings.second->authorized(request,session)) {
+                case AccessControl::shib_acl_true:
+                    request.log(SPRequest::SPDebug, "access control provider granted access");
+                    return make_pair(true,request.returnOK());
+
+                case AccessControl::shib_acl_false:
+                {
+                    request.log(SPRequest::SPWarn, "access control provider denied access");
+                    TemplateParameters tp;
+                    tp.m_map["requestURL"] = targetURL;
+                    return make_pair(true,sendError(request, app, "access", tp));
+                }
+
+                default:
+                    // Use the "DECLINE" interface to signal we don't know what to do.
+                    return make_pair(true,request.returnDecline());
             }
-            return make_pair(false,0);
         }
-        else
+        else {
             return make_pair(true,request.returnDecline());
+        }
     }
     catch (exception& e) {
         TemplateParameters tp(&e);
index 9f223c9..afb6bb9 100644 (file)
@@ -50,7 +50,7 @@ namespace {
         Lockable* lock() {return this;}\r
         void unlock() {}\r
 \r
-        bool authorized(const SPRequest& request, const Session* session) const;\r
+        aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
     \r
     private:\r
         string m_alias;\r
@@ -66,7 +66,7 @@ namespace {
         Lockable* lock() {return this;}\r
         void unlock() {}\r
 \r
-        bool authorized(const SPRequest& request, const Session* session) const;\r
+        aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
         \r
     private:\r
         enum operator_t { OP_NOT, OP_AND, OP_OR } m_op;\r
@@ -90,7 +90,7 @@ namespace {
             delete m_rootAuthz;\r
         }\r
 \r
-        bool authorized(const SPRequest& request, const Session* session) const;\r
+        aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
 \r
     protected:\r
         pair<bool,DOMElement*> load();\r
@@ -147,7 +147,7 @@ Rule::Rule(const DOMElement* e)
     }\r
 }\r
 \r
-bool Rule::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Session* session) const\r
 {\r
     // We can make this more complex later using pluggable comparison functions,\r
     // but for now, just a straight port to the new Attribute API.\r
@@ -155,7 +155,7 @@ bool Rule::authorized(const SPRequest& request, const Session* session) const
     // Map alias in rule to the attribute.\r
     if (!session) {\r
         request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
-        return false;\r
+        return shib_acl_false;\r
     }\r
     \r
     // Find the attribute(s) matching the require rule.\r
@@ -163,7 +163,7 @@ bool Rule::authorized(const SPRequest& request, const Session* session) const
         session->getIndexedAttributes().equal_range(m_alias);\r
     if (attrs.first == attrs.second) {\r
         request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
-        return false;\r
+        return shib_acl_false;\r
     }\r
 \r
     for (; attrs.first != attrs.second; ++attrs.first) {\r
@@ -175,13 +175,13 @@ bool Rule::authorized(const SPRequest& request, const Session* session) const
             for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
                 if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {\r
                     request.log(SPRequest::SPDebug, string("AccessControl plugin expecting ") + *j + ", authz granted");\r
-                    return true;\r
+                    return shib_acl_true;\r
                 }\r
             }\r
         }\r
     }\r
 \r
-    return false;\r
+    return shib_acl_false;\r
 }\r
 \r
 Operator::Operator(const DOMElement* e)\r
@@ -225,32 +225,39 @@ Operator::~Operator()
     for_each(m_operands.begin(),m_operands.end(),xmltooling::cleanup<AccessControl>());\r
 }\r
 \r
-bool Operator::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const Session* session) const\r
 {\r
     switch (m_op) {\r
         case OP_NOT:\r
-            return !m_operands[0]->authorized(request,session);\r
+            switch (m_operands[0]->authorized(request,session)) {\r
+                case shib_acl_true:\r
+                    return shib_acl_false;\r
+                case shib_acl_false:\r
+                    return shib_acl_true;\r
+                default:\r
+                    return shib_acl_indeterminate;\r
+            }\r
         \r
         case OP_AND:\r
         {\r
             for (vector<AccessControl*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {\r
                 if (!(*i)->authorized(request,session))\r
-                    return false;\r
+                    return shib_acl_false;\r
             }\r
-            return true;\r
+            return shib_acl_true;\r
         }\r
         \r
         case OP_OR:\r
         {\r
             for (vector<AccessControl*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {\r
                 if ((*i)->authorized(request,session))\r
-                    return true;\r
+                    return shib_acl_true;\r
             }\r
-            return false;\r
+            return shib_acl_false;\r
         }\r
     }\r
     request.log(SPRequest::SPWarn,"unknown operation in access control policy, denying access");\r
-    return false;\r
+    return shib_acl_false;\r
 }\r
 \r
 pair<bool,DOMElement*> XMLAccessControl::load()\r
@@ -276,7 +283,7 @@ pair<bool,DOMElement*> XMLAccessControl::load()
     return make_pair(false,(DOMElement*)NULL);\r
 }\r
 \r
-bool XMLAccessControl::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t XMLAccessControl::authorized(const SPRequest& request, const Session* session) const\r
 {\r
-    return m_rootAuthz ? m_rootAuthz->authorized(request,session) : false;\r
+    return m_rootAuthz ? m_rootAuthz->authorized(request,session) : shib_acl_false;\r
 }\r
index 72c9efe..62da247 100644 (file)
@@ -49,8 +49,8 @@ namespace shibsp {
         \r
         void unlock() {}\r
     \r
-        bool authorized(const SPRequest& request, const Session* session) const {\r
-            return false;\r
+        aclresult_t authorized(const SPRequest& request, const Session* session) const {\r
+            return shib_acl_false;\r
         }\r
     };\r
 \r