/*
- * DEBUG: section 28 Access Control
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
*
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+
+/* DEBUG: section 28 Access Control */
+
#include "squid.h"
#include "acl/Acl.h"
#include "acl/Checklist.h"
#include "acl/Gadgets.h"
+#include "acl/Options.h"
#include "anyp/PortCfg.h"
#include "cache_cf.h"
#include "ConfigParser.h"
-#include "Debug.h"
-#include "dlink.h"
+#include "debug/Stream.h"
+#include "fatal.h"
#include "globals.h"
-#include "profiler/Profiler.h"
+#include "sbuf/List.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
-const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
+#include <algorithm>
+#include <map>
+
+const char *AclMatchedName = nullptr;
+
+namespace Acl {
+
+/// ACL type name comparison functor
+class TypeNameCmp {
+public:
+ bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
+};
-const char *AclMatchedName = NULL;
+/// ACL makers indexed by ACL type name
+typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
-bool ACLFlags::supported(const ACLFlag f) const
+/// registered ACL Makers
+static Makers &
+TheMakers()
{
- if (f == ACL_F_REGEX_CASE)
- return true;
- return (supported_.find(f) != std::string::npos);
+ static Makers Registry;
+ return Registry;
}
-void
-ACLFlags::parseFlags()
+/// creates an ACL object of the named (and already registered) ACL child type
+static
+ACL *
+Make(TypeName typeName)
{
- char *nextToken;
- while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') {
- (void)ConfigParser::NextToken(); //Get token from cfg line
- //if token is the "--" break flag
- if (strcmp(nextToken, "--") == 0)
- break;
-
- for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
- if (supported(*flg)) {
- makeSet(*flg);
- } else {
- debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
- self_destruct();
- }
- }
+ const auto pos = TheMakers().find(typeName);
+ if (pos == TheMakers().end()) {
+ debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << typeName << "'");
+ self_destruct();
+ assert(false); // not reached
}
- /*Regex code needs to parse -i file*/
- if ( isSet(ACL_F_REGEX_CASE))
- ConfigParser::TokenPutBack("-i");
+ ACL *result = (pos->second)(pos->first);
+ debugs(28, 4, typeName << '=' << result);
+ assert(result);
+ return result;
}
-const char *
-ACLFlags::flagsStr() const
+} // namespace Acl
+
+void
+Acl::RegisterMaker(TypeName typeName, Maker maker)
{
- static char buf[64];
- if (flags_ == 0)
- return "";
-
- char *s = buf;
- *s++ = '-';
- for (ACLFlag f = 'A'; f <= 'z'; f++) {
- // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
- if (isSet(f) && f != ACL_F_REGEX_CASE)
- *s++ = f;
- }
- *s = '\0';
- return buf;
+ assert(typeName);
+ assert(*typeName);
+ TheMakers().emplace(typeName, maker);
}
void *
-ACL::operator new (size_t byteCount)
+ACL::operator new (size_t)
{
fatal ("unusable ACL::new");
return (void *)1;
}
void
-ACL::operator delete (void *address)
+ACL::operator delete (void *)
{
fatal ("unusable ACL::delete");
}
debugs(28, 9, "ACL::FindByName found no match");
- return NULL;
-}
-
-ACL *
-ACL::Factory (char const *type)
-{
- ACL *result = Prototype::Factory (type);
-
- if (!result)
- fatal ("Unknown acl type in ACL::Factory");
-
- return result;
+ return nullptr;
}
ACL::ACL() :
- cfgline(NULL),
- next(NULL),
- registered(false)
+ cfgline(nullptr),
+ next(nullptr),
+ registered(false)
{
*name = 0;
}
bool
ACL::matches(ACLChecklist *checklist) const
{
- PROF_start(ACL_matches);
debugs(28, 5, "checking " << name);
// XXX: AclMatchedName does not contain a matched ACL name when the acl
AclMatchedName = name;
int result = 0;
- if (!checklist->hasRequest() && requiresRequest()) {
+ if (!checklist->hasAle() && requiresAle()) {
+ debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
+ "context without an ALE state. Assuming mismatch.");
+ } else if (!checklist->hasRequest() && requiresRequest()) {
debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
"context without an HTTP request. Assuming mismatch.");
} else if (!checklist->hasReply() && requiresReply()) {
debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
"context without an HTTP response. Assuming mismatch.");
} else {
+ // make sure the ALE has as much data as possible
+ if (requiresAle())
+ checklist->verifyAle();
+
// have to cast because old match() API is missing const
result = const_cast<ACL*>(this)->match(checklist);
}
const char *extra = checklist->asyncInProgress() ? " async" : "";
debugs(28, 3, "checked: " << name << " = " << result << extra);
- PROF_stop(ACL_matches);
return result == 1; // true for match; false for everything else
}
ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
{
/* we're already using strtok() to grok the line */
- char *t = NULL;
- ACL *A = NULL;
+ char *t = nullptr;
+ ACL *A = nullptr;
LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
int new_acl = 0;
/* snarf the ACL name */
- if ((t = ConfigParser::NextToken()) == NULL) {
- debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
+ if ((t = ConfigParser::NextToken()) == nullptr) {
+ debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL name.");
parser.destruct();
return;
}
/* snarf the ACL type */
const char *theType;
- if ((theType = ConfigParser::NextToken()) == NULL) {
- debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
+ if ((theType = ConfigParser::NextToken()) == nullptr) {
+ debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL type.");
parser.destruct();
return;
}
// Is this ACL going to work?
if (strcmp(theType, "myip") == 0) {
- AnyP::PortCfg *p = Config.Sockaddr.http;
- while (p) {
+ AnyP::PortCfgPointer p = HttpPortList;
+ while (p != nullptr) {
// Bug 3239: not reliable when there is interception traffic coming
if (p->flags.natIntercept)
debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
p = p->next;
}
- debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
+ debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myip' type has been renamed to 'localip' and matches the IP the client connected to.");
theType = "localip";
} else if (strcmp(theType, "myport") == 0) {
- AnyP::PortCfg *p = Config.Sockaddr.http;
- while (p) {
+ AnyP::PortCfgPointer p = HttpPortList;
+ while (p != nullptr) {
// Bug 3239: not reliable when there is interception traffic coming
// Bug 3239: myport - not reliable (yet) when there is interception traffic coming
if (p->flags.natIntercept)
p = p->next;
}
theType = "localport";
- debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
- }
-
- if (!Prototype::Registered(theType)) {
- debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
- // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
- parser.destruct();
- return;
+ debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myport' type has been renamed to 'localport' and matches the port the client connected to.");
+ } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
+ // ACL manager is now a built-in and has a different type.
+ debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
+ return; // ignore the line
+ } else if (strcmp(theType, "clientside_mark") == 0) {
+ debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'clientside_mark' type has been renamed to 'client_connection_mark'.");
+ theType = "client_connection_mark";
}
- if ((A = FindByName(aclname)) == NULL) {
+ if ((A = FindByName(aclname)) == nullptr) {
debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
- A = ACL::Factory(theType);
+ A = Acl::Make(theType);
A->context(aclname, config_input_line);
new_acl = 1;
} else {
* Here we set AclMatchedName in case we need to use it in a
* warning message in aclDomainCompare().
*/
- AclMatchedName = A->name; /* ugly */
+ AclMatchedName = A->name; /* ugly */
- A->flags.parseFlags();
+ A->parseFlags();
/*split the function here */
A->parse();
/*
* Clear AclMatchedName from our temporary hack
*/
- AclMatchedName = NULL; /* ugly */
+ AclMatchedName = nullptr; /* ugly */
if (!new_acl)
return;
if (A->empty()) {
- debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
+ debugs(28, DBG_CRITICAL, "WARNING: empty ACL: " << A->cfgline);
}
if (!A->valid()) {
return false;
}
+void
+ACL::parseFlags()
+{
+ Acl::Options allOptions = options();
+ for (const auto lineOption: lineOptions()) {
+ lineOption->unconfigure(); // forget any previous "acl ..." line effects
+ allOptions.push_back(lineOption);
+ }
+ Acl::ParseFlags(allOptions);
+}
+
+SBufList
+ACL::dumpOptions()
+{
+ SBufList result;
+
+ const auto &myOptions = options();
+ // XXX: No lineOptions() call here because we do not remember ACL "line"
+ // boundaries and associated "line" options; we cannot report them.
+
+ // optimization: most ACLs do not have myOptions
+ // this check also works around dump_SBufList() adding ' ' after empty items
+ if (!myOptions.empty()) {
+ SBufStream stream;
+ stream << myOptions;
+ const SBuf optionsImage = stream.buf();
+ if (!optionsImage.isEmpty())
+ result.push_back(optionsImage);
+ }
+ return result;
+}
+
/* ACL result caching routines */
int
-ACL::matchForCache(ACLChecklist *checklist)
+ACL::matchForCache(ACLChecklist *)
{
/* This is a fatal to ensure that cacheMatchAcl calls are _only_
* made for supported acl types */
fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
- return 0; /* NOTREACHED */
+ return 0; /* NOTREACHED */
}
/*
link = link->next;
}
- auth_match = new acl_proxy_auth_match_cache();
- auth_match->matchrv = matchForCache (checklist);
- auth_match->acl_data = this;
+ auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
dlinkAddTail(auth_match, &auth_match->link, cache);
debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
return auth_match->matchrv;
}
}
+bool
+ACL::requiresAle() const
+{
+ return false;
+}
+
bool
ACL::requiresReply() const
{
ACL::~ACL()
{
- debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
+ debugs(28, 3, "freeing ACL " << name);
safe_free(cfgline);
- AclMatchedName = NULL; // in case it was pointing to our name
-}
-
-ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
-
-ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
-{
- registerMe ();
-}
-
-Vector<ACL::Prototype const *> * ACL::Prototype::Registry;
-void *ACL::Prototype::Initialized;
-
-bool
-ACL::Prototype::Registered(char const *aType)
-{
- debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
-
- for (iterator i = Registry->begin(); i != Registry->end(); ++i)
- if (!strcmp (aType, (*i)->typeString)) {
- debugs(28, 7, "ACL::Prototype::Registered: yes");
- return true;
- }
-
- debugs(28, 7, "ACL::Prototype::Registered: no");
- return false;
-}
-
-void
-ACL::Prototype::registerMe ()
-{
- if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
- /* TODO: extract this */
- /* Not initialised */
- Registry = new Vector <ACL::Prototype const *>;
- Initialized = (char *)Registry - 5;
- }
-
- if (Registered (typeString))
- fatalf ("Attempt to register %s twice", typeString);
-
- Registry->push_back (this);
-}
-
-ACL::Prototype::~Prototype()
-{
- // TODO: unregister me
-}
-
-ACL *
-ACL::Prototype::Factory (char const *typeToClone)
-{
- debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
-
- for (iterator i = Registry->begin(); i != Registry->end(); ++i)
- if (!strcmp (typeToClone, (*i)->typeString)) {
- ACL *A = (*i)->prototype->clone();
- A->flags = (*i)->prototype->flags;
- return A;
- }
-
- debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
-
- return NULL;
+ AclMatchedName = nullptr; // in case it was pointing to our name
}
void
a = a->next;
}
}
+