[cpp-sp] branch main updated: Implement option for partial regex matching.
Scott Cantor
cantor.2 at osu.edu
Wed Jun 25 13:13:12 UTC 2025
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=7656339c6d679218e7390a6da21b0063aa920599
The following commit(s) were added to refs/heads/main by this push:
new 7656339c Implement option for partial regex matching.
7656339c is described below
commit 7656339c6d679218e7390a6da21b0063aa920599
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jun 25 09:13:04 2025 -0400
Implement option for partial regex matching.
---
apache/mod_shib_24.cpp | 92 ++++++----------------
configs/shibboleth.ini | 3 +-
shibsp/Agent.cpp | 8 +-
shibsp/Agent.h | 2 +
.../impl/DefaultAttributeConfiguration.cpp | 13 ++-
shibsp/impl/XMLAccessControl.cpp | 29 ++++---
shibsp/impl/XMLRequestMapper.cpp | 22 ++++--
7 files changed, 75 insertions(+), 94 deletions(-)
diff --git a/apache/mod_shib_24.cpp b/apache/mod_shib_24.cpp
index 0ec33634..f92b56d8 100644
--- a/apache/mod_shib_24.cpp
+++ b/apache/mod_shib_24.cpp
@@ -814,13 +814,18 @@ extern "C" int shib_fixups(request_rec* r)
class htAccessControl : virtual public AccessControl, public NoOpSharedLockable
{
public:
- htAccessControl() {}
+ htAccessControl() {
+ m_partialRegexMatching = AgentConfig::getConfig().getAgent().getBool(
+ Agent::PARTIAL_REGEX_MATCHING_PROP_NAME, Agent::PARTIAL_REGEX_MATCHING_PROP_DEFAULT);
+ }
~htAccessControl() {}
- aclresult_t authorized(const SPRequest& request, const Session* session) const;
+ aclresult_t authorized(const SPRequest& request, const Session* session) const {
+ // We should never be invoked in Apache 2.4+ as an SP plugin.
+ throw ConfigurationException("Save my walrus!");
+ }
aclresult_t doAccessControl(const ShibTargetApache& sta, const Session* session, const char* plugin) const;
aclresult_t doUser(const ShibTargetApache& sta, const char* params) const;
- aclresult_t doAuthnContext(const ShibTargetApache& sta, const char* acRef, const char* params) const;
aclresult_t doShibAttr(const ShibTargetApache& sta, const Session* session, const char* rule, const char* params) const;
private:
@@ -832,6 +837,8 @@ private:
const char* toMatch,
bool isRegex=false
) const;
+
+ bool m_partialRegexMatching;
};
AccessControl* htAccessFactory(const ptree&, bool)
@@ -881,17 +888,19 @@ AccessControl::aclresult_t htAccessControl::doUser(const ShibTargetApache& sta,
continue;
}
+ static regexp::match_flag_type match_flags = regexp::regex_constants::match_any | regexp::regex_constants::match_not_null;
+
// Figure out if there's a match.
bool match = false;
if (regexp) {
try {
- // TODO: support regex options?
regexp::regex re(w, regexp::regex_constants::extended);
- match = regexp::regex_match(sta.getRemoteUser(), re, regexp::regex_constants::match_any | regexp::regex_constants::match_not_null);
+ match = m_partialRegexMatching ?
+ regexp::regex_search(sta.getRemoteUser(), re, match_flags) :
+ regexp::regex_match(sta.getRemoteUser(), re, match_flags);
}
catch (const regexp::regex_error& e) {
- sta.log(Priority::SHIB_ERROR,
- string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
+ sta.error(string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
}
}
else if (sta.getRemoteUser() == w) {
@@ -899,65 +908,15 @@ AccessControl::aclresult_t htAccessControl::doUser(const ShibTargetApache& sta,
}
if (match) {
- if (sta.isPriorityEnabled(Priority::SHIB_DEBUG))
- sta.log(Priority::SHIB_DEBUG,
- string("htaccess: require user ") + (negated ? "rejecting (" : "accepting (") + sta.getRemoteUser() + ")");
+ if (sta.isPriorityEnabled(Priority::SHIB_DEBUG)) {
+ sta.debug(string("htaccess: require user ") + (negated ? "rejecting (" : "accepting (") + sta.getRemoteUser() + ")");
+ }
return (negated ? shib_acl_false : shib_acl_true);
}
}
return (negated ? shib_acl_true : shib_acl_false);
}
-AccessControl::aclresult_t htAccessControl::doAuthnContext(const ShibTargetApache& sta, const char* ref, const char* params) const
-{
- if (ref && *ref) {
- bool regexp = false;
- bool negated = false;
- while (ref && *params) {
- const char* w = ap_getword_conf(sta.m_req->pool, ¶ms);
- if (*w == '~') {
- regexp = true;
- continue;
- }
- else if (*w == '!') {
- // A negated rule presumes success unless a match is found.
- negated = true;
- if (*(w+1) == '~')
- regexp = true;
- continue;
- }
-
- // Figure out if there's a match.
- bool match = false;
- if (regexp) {
- try {
- regexp::regex re(w, regexp::regex_constants::extended);
- match = regexp::regex_match(ref, re, regexp::regex_constants::match_any | regexp::regex_constants::match_not_null);
- }
- catch (const regexp::regex_error& e) {
- sta.log(Priority::SHIB_ERROR,
- string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
- }
- }
- else if (!strcmp(w, ref)) {
- match = true;
- }
-
- if (match) {
- if (sta.isPriorityEnabled(Priority::SHIB_DEBUG))
- sta.log(Priority::SHIB_DEBUG,
- string("htaccess: require authnContext ") + (negated ? "rejecting (" : "accepting (") + ref + ")");
- return (negated ? shib_acl_false : shib_acl_true);
- }
- }
- return (negated ? shib_acl_true : shib_acl_false);
- }
-
- if (sta.isPriorityEnabled(Priority::SHIB_DEBUG))
- sta.debug("htaccess: require authnContext rejecting session with no context associated");
- return shib_acl_false;
-}
-
bool htAccessControl::checkAttribute(
const SPRequest& request,
const Session& session,
@@ -982,8 +941,7 @@ bool htAccessControl::checkAttribute(
return true;
}
} catch (const regexp::regex_error& e) {
- request.log(Priority::SHIB_ERROR,
- string("htaccess plugin caught exception while parsing regular expression (") + toMatch + "): " + e.what());
+ request.error(string("htaccess plugin caught exception while parsing regular expression (") + toMatch + "): " + e.what());
}
}
else if (attrConfig.hasMatchingValue(session, attributeID, toMatch)) {
@@ -1026,12 +984,6 @@ AccessControl::aclresult_t htAccessControl::doShibAttr(
return shib_acl_false;
}
-AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, const Session* session) const
-{
- // We should never be invoked in 2.4+ as an SP plugin.
- throw ConfigurationException("Save my walrus!");
-}
-
class ApacheRequestMapper : public virtual RequestMapper, public virtual PropertySet
{
public:
@@ -1300,7 +1252,9 @@ extern "C" authz_status shib_acclass_check_authz(request_rec* r, const char* req
try {
unique_lock<Session> session = sta.first->getSession(false, true);
if (session && hta.doShibAttr(*sta.first, session.mutex(),
- sta.first->getAgent().getString("legacy-classref-attribute", "Shib-AuthnContext-Class"),
+ sta.first->getAgent().getString(
+ AttributeConfiguration::LEGACY_CLASSREF_ATTRIBUTE_PROP_NAME,
+ AttributeConfiguration::LEGACY_CLASSREF_ATTRIBUTE_PROP_DEFAULT),
require_line) == AccessControl::shib_acl_true)
return AUTHZ_GRANTED;
return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;
diff --git a/configs/shibboleth.ini b/configs/shibboleth.ini
index 63ca0238..461e4dfc 100644
--- a/configs/shibboleth.ini
+++ b/configs/shibboleth.ini
@@ -2,7 +2,7 @@
agentID = sp.example.org
[logging]
-
+defaultLevel = INFO
[logging-categories]
;Shibboleth.RequestMapper = DEBUG
@@ -24,4 +24,5 @@ type = filesystem
[request-mapper]
path = request-map.xml
reloadChanges = true
+#partialRegexMatching = false
diff --git a/shibsp/Agent.cpp b/shibsp/Agent.cpp
index edd56a6e..22d493c5 100644
--- a/shibsp/Agent.cpp
+++ b/shibsp/Agent.cpp
@@ -49,14 +49,14 @@ using namespace shibsp;
using namespace std;
const char Agent::UNSET_HEADER_VALUE_PROP_NAME[] = "unsetHeaderValue";
-
const char Agent::CHECK_SPOOFING_PROP_NAME[] = "checkSpoofing";
-bool Agent::CHECK_SPOOFING_PROP_DEFAULT = true;
-
const char Agent::SPOOF_KEY_PROP_NAME[] = "spoofKey";
-
const char Agent::CATCH_ALL_PROP_NAME[] = "catchAll";
+const char Agent::PARTIAL_REGEX_MATCHING_PROP_NAME[] = "partialRegexMatching";
+
+bool Agent::CHECK_SPOOFING_PROP_DEFAULT = true;
bool Agent::CATCH_ALL_PROP_DEFAULT = false;
+bool Agent::PARTIAL_REGEX_MATCHING_PROP_DEFAULT = false;
Agent::Agent()
{
diff --git a/shibsp/Agent.h b/shibsp/Agent.h
index 53e37d09..00d5f5d8 100644
--- a/shibsp/Agent.h
+++ b/shibsp/Agent.h
@@ -181,9 +181,11 @@ namespace shibsp {
static const char CHECK_SPOOFING_PROP_NAME[];
static const char SPOOF_KEY_PROP_NAME[];
static const char CATCH_ALL_PROP_NAME[];
+ static const char PARTIAL_REGEX_MATCHING_PROP_NAME[];
static bool CHECK_SPOOFING_PROP_DEFAULT;
static bool CATCH_ALL_PROP_DEFAULT;
+ static bool PARTIAL_REGEX_MATCHING_PROP_DEFAULT;
protected:
/** The AuthTypes to "recognize" (defaults to "shibboleth"). */
diff --git a/shibsp/attribute/impl/DefaultAttributeConfiguration.cpp b/shibsp/attribute/impl/DefaultAttributeConfiguration.cpp
index 7011f780..658008b0 100644
--- a/shibsp/attribute/impl/DefaultAttributeConfiguration.cpp
+++ b/shibsp/attribute/impl/DefaultAttributeConfiguration.cpp
@@ -21,6 +21,7 @@
#include "internal.h"
#include "exceptions.h"
+#include "Agent.h"
#include "AgentConfig.h"
#include "RequestMapper.h"
#include "SPRequest.h"
@@ -73,7 +74,7 @@ namespace {
Category& m_log;
ptree m_pt;
- bool m_urlEncoding,m_exportDuplicates;
+ bool m_urlEncoding,m_exportDuplicates,m_partialRegexMatching;
map<string,string> m_mappings;
set<string> m_caseSensitiveIds;
};
@@ -113,6 +114,9 @@ DefaultAttributeConfiguration::DefaultAttributeConfiguration(const char* pathnam
m_urlEncoding = !strcmp(getString("encoding", ""), "URL");
m_exportDuplicates = getBool("exportDuplicateValues", true);
+ m_partialRegexMatching = AgentConfig::getConfig().getAgent().getBool(
+ Agent::PARTIAL_REGEX_MATCHING_PROP_NAME, Agent::PARTIAL_REGEX_MATCHING_PROP_DEFAULT);
+
boost::optional<ptree&> mappings = m_pt.get_child_optional("mappings");
if (!mappings) {
return;
@@ -385,7 +389,9 @@ bool DefaultAttributeConfiguration::hasMatchingValue(
return false;
}
-bool DefaultAttributeConfiguration::hasMatchingValue(const Session& session, const char* attributeId, const char* value) const
+bool DefaultAttributeConfiguration::hasMatchingValue(
+ const Session& session, const char* attributeId, const char* value
+ ) const
{
const auto& attr = session.getAttributes().find(attributeId);
if (attr == session.getAttributes().end()) {
@@ -420,7 +426,8 @@ bool DefaultAttributeConfiguration::hasMatchingValue(
DDF val = const_cast<DDF&>(attr->second).first();
while (!val.isnull()) {
- if (regexp::regex_match(val.string(), expression, match_flags)) {
+ if (m_partialRegexMatching ? regexp::regex_search(val.string(), expression, match_flags) :
+ regexp::regex_match(val.string(), expression, match_flags)) {
return true;
}
val = const_cast<DDF&>(attr->second).next();
diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp
index 444542bf..a3e51d31 100644
--- a/shibsp/impl/XMLAccessControl.cpp
+++ b/shibsp/impl/XMLAccessControl.cpp
@@ -172,20 +172,20 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
// Map alias in rule to the attribute.
if (!session) {
- request.log(Priority::SHIB_WARN, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
+ request.warn("AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
return shib_acl_false;
}
if (m_alias == "valid-user") {
if (session) {
- request.log(Priority::SHIB_DEBUG," AccessControl rule accepting valid-user based on active session");
+ request.debug("AccessControl rule accepting valid-user based on active session");
return shib_acl_true;
}
return shib_acl_false;
}
else if (m_alias == "user") {
if (m_vals.find(request.getRemoteUser()) != m_vals.end()) {
- request.log(Priority::SHIB_DEBUG, string("AccessControl rule expecting REMOTE_USER (") + request.getRemoteUser() + "), authz granted");
+ request.debug(string("AccessControl rule expecting REMOTE_USER (") + request.getRemoteUser() + "), authz granted");
return shib_acl_true;
}
return shib_acl_false;
@@ -206,7 +206,7 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
// Empty case is historical and not terribly smart, but we'll brute force it.
if (m_vals.empty()) {
if (session->getAttributes().find(actual_alias.c_str()) != session->getAttributes().end()) {
- request.log(Priority::SHIB_DEBUG, string("AccessControl rule requires presence of attribute (") + actual_alias + "), authz granted");
+ request.debug(string("AccessControl rule requires presence of attribute (") + actual_alias + "), authz granted");
return shib_acl_true;
}
return shib_acl_false;
@@ -214,7 +214,7 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
// Otherwise call into the helper logic to handle matching process..
if (attributeConfig.hasMatchingValue(*session, actual_alias.c_str(), m_vals)) {
- request.log(Priority::SHIB_DEBUG, string("AccessControl rule satisfied for attribute (") + actual_alias + "), authz granted");
+ request.debug(string("AccessControl rule satisfied for attribute (") + actual_alias + "), authz granted");
return shib_acl_true;
}
@@ -244,26 +244,31 @@ RuleRegex::RuleRegex(const ptree& pt)
AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const Session* session) const
{
-
static regexp::regex_constants::match_flag_type match_flags = regexp::regex_constants::match_any | regexp::regex_constants::match_not_null;
string actual_alias(m_alias);
if (!session) {
- request.log(Priority::SHIB_WARN, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
+ request.warn("AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
return shib_acl_false;
}
if (m_alias == "valid-user") {
if (session) {
- request.log(Priority::SHIB_DEBUG,"AccessControl rule accepting valid-user based on active session");
+ request.debug("AccessControl rule accepting valid-user based on active session");
return shib_acl_true;
}
return shib_acl_false;
}
else if (m_alias == "user") {
- if (regexp::regex_match(request.getRemoteUser(), m_re, match_flags)) {
- request.log(Priority::SHIB_DEBUG, string("AccessControl rule expecting REMOTE_USER regex (") + m_exp + "), authz granted");
+
+ bool partial = request.getAgent().getBool(
+ Agent::PARTIAL_REGEX_MATCHING_PROP_NAME, Agent::PARTIAL_REGEX_MATCHING_PROP_DEFAULT);
+
+ bool result = partial ? regexp::regex_search(request.getRemoteUser(), m_re, match_flags) :
+ regexp::regex_match(request.getRemoteUser(), m_re, match_flags);
+ if (result) {
+ request.debug(string("AccessControl rule expecting REMOTE_USER regex (") + m_exp + "), authz granted");
return shib_acl_true;
}
return shib_acl_false;
@@ -283,7 +288,7 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const
// Call into the helper logic to handle matching process..
if (attributeConfig.hasMatchingValue(*session, actual_alias.c_str(), m_re)) {
- request.log(Priority::SHIB_DEBUG,
+ request.debug(
string("AccessControl rule for attribute (") + actual_alias + ") expecting regex (" + m_exp + ", authz granted");
return shib_acl_true;
}
@@ -356,7 +361,7 @@ AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const
return shib_acl_false;
}
}
- request.log(Priority::SHIB_WARN,"unknown operation in access control policy, denying access");
+ request.warn("unknown operation in access control policy, denying access");
return shib_acl_false;
}
diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp
index 69edf4b7..d287f436 100644
--- a/shibsp/impl/XMLRequestMapper.cpp
+++ b/shibsp/impl/XMLRequestMapper.cpp
@@ -20,6 +20,7 @@
#include "internal.h"
#include "exceptions.h"
#include "AccessControl.h"
+#include "Agent.h"
#include "AgentConfig.h"
#include "RequestMapper.h"
#include "SPRequest.h"
@@ -144,8 +145,19 @@ namespace {
return new XMLRequestMapper(pt);
}
- static regexp::regex_constants::match_flag_type match_flags =
- regexp::regex_constants::match_any | regexp::regex_constants::match_not_null;
+ static bool doRegex(const regexp::regex& exp, const char* input) {
+ static regexp::regex_constants::match_flag_type match_flags =
+ regexp::regex_constants::match_any | regexp::regex_constants::match_not_null;
+
+ bool partial = AgentConfig::getConfig().getAgent().getBool(
+ Agent::PARTIAL_REGEX_MATCHING_PROP_NAME, Agent::PARTIAL_REGEX_MATCHING_PROP_DEFAULT);
+ if (partial) {
+ return regexp::regex_search(input, exp, match_flags);
+ }
+ else {
+ return regexp::regex_match(input, exp, match_flags);
+ }
+ }
}
void SHIBSP_API shibsp::registerRequestMappers()
@@ -453,7 +465,7 @@ const Override* Override::locate(const HTTPRequest& request) const
// If there's anything left, we try for a regex match on the rest of the path minus the query string.
if (*path) {
for (const auto& re : m_regexps) {
- if (regex_match(path, re.first, match_flags)) {
+ if (doRegex(re.first, path)) {
o = re.second.get();
break;
}
@@ -473,7 +485,7 @@ const Override* Override::locate(const HTTPRequest& request) const
if (get<1>(*q)) {
// We have to match one of the values.
while (vals.first != vals.second) {
- if (regexp::regex_match(vals.first->second, get<1>(*q).get(), match_flags)) {
+ if (doRegex(get<1>(*q).get(), vals.first->second)) {
o = get<2>(*q).get();
descended = true;
break;
@@ -654,7 +666,7 @@ const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTP
}
else {
for (const auto& re : m_regexps) {
- if (regexp::regex_match(vhost, re.first, match_flags)) {
+ if (doRegex(re.first, vhost)) {
o = re.second.get();
}
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list