]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/acl/Gadgets.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / acl / Gadgets.cc
index d65f6e8abda943738abf8b822dca1753bfc38aed..605d409d7a8d8603cc5aa401cc7547d98d5bdb07 100644 (file)
@@ -1,74 +1,66 @@
+/*
+ * 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
- * AUTHOR: Duane Wessels
  *
  * This file contains ACL routines that are not part of the
  * ACL class, nor any other class yet, and that need to be
  * factored into appropriate places. They are here to reduce
  * unneeded dependencies between the ACL class and the rest
  * of squid.
- *
- * 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.
- *
  */
 
 #include "squid.h"
 #include "acl/Acl.h"
+#include "acl/AclDenyInfoList.h"
 #include "acl/Checklist.h"
-#include "acl/Strategised.h"
 #include "acl/Gadgets.h"
+#include "acl/Strategised.h"
+#include "acl/Tree.h"
+#include "cache_cf.h"
 #include "ConfigParser.h"
 #include "errorpage.h"
 #include "globals.h"
 #include "HttpRequest.h"
-#include "Mem.h"
+#include "src/sbuf/Stream.h"
+
+#include <set>
+#include <algorithm>
+
+typedef std::set<ACL*> AclSet;
+/// Accumulates all ACLs to facilitate their clean deletion despite reuse.
+static AclSet *RegisteredAcls; // TODO: Remove when ACLs are refcounted
 
 /* does name lookup, returns page_id */
 err_type
-aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name, int redirect_allowed)
+aclGetDenyInfoPage(AclDenyInfoList ** head, const char *name, int redirect_allowed)
 {
-    acl_deny_info_list *A = NULL;
+    if (!name) {
+        debugs(28, 3, "ERR_NONE due to a NULL name");
+        return ERR_NONE;
+    }
 
-    debugs(28, 8, HERE << "got called for " << name);
+    AclDenyInfoList *A = nullptr;
 
-    for (A = *head; A; A = A->next) {
-        acl_name_list *L = NULL;
+    debugs(28, 8, "got called for " << name);
 
+    for (A = *head; A; A = A->next) {
         if (!redirect_allowed && strchr(A->err_page_name, ':') ) {
-            debugs(28, 8, HERE << "Skip '" << A->err_page_name << "' 30x redirects not allowed as response here.");
+            debugs(28, 8, "Skip '" << A->err_page_name << "' 30x redirects not allowed as response here.");
             continue;
         }
 
-        for (L = A->acl_list; L; L = L->next) {
-            if (!strcmp(name, L->name)) {
-                debugs(28, 8, HERE << "match on " << name);
+        for (const auto &aclName: A->acl_list) {
+            if (aclName.cmp(name) == 0) {
+                debugs(28, 8, "match on " << name);
                 return A->err_page_id;
             }
-
         }
     }
 
@@ -80,10 +72,12 @@ aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name, int redirect_al
 int
 aclIsProxyAuth(const char *name)
 {
-    debugs(28, 5, "aclIsProxyAuth: called for " << name);
-
-    if (NULL == name)
+    if (!name) {
+        debugs(28, 3, "false due to a NULL name");
         return false;
+    }
+
+    debugs(28, 5, "aclIsProxyAuth: called for " << name);
 
     ACL *a;
 
@@ -106,128 +100,146 @@ aclIsProxyAuth(const char *name)
  */
 
 void
-aclParseDenyInfoLine(acl_deny_info_list ** head)
+aclParseDenyInfoLine(AclDenyInfoList ** head)
 {
-    char *t = NULL;
-    acl_deny_info_list *A = NULL;
-    acl_deny_info_list *B = NULL;
-    acl_deny_info_list **T = NULL;
-    acl_name_list *L = NULL;
-    acl_name_list **Tail = NULL;
+    char *t = nullptr;
+    AclDenyInfoList *B;
+    AclDenyInfoList **T;
 
     /* first expect a page name */
 
-    if ((t = strtok(NULL, w_space)) == NULL) {
+    if ((t = ConfigParser::NextToken()) == nullptr) {
         debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
-        debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: missing 'error page' parameter.");
+        debugs(28, DBG_CRITICAL, "ERROR: aclParseDenyInfoLine: missing 'error page' parameter.");
         return;
     }
 
-    A = (acl_deny_info_list *)memAllocate(MEM_ACL_DENY_INFO_LIST);
-    A->err_page_id = errorReservePageId(t);
-    A->err_page_name = xstrdup(t);
-    A->next = (acl_deny_info_list *) NULL;
-    /* next expect a list of ACL names */
-    Tail = &A->acl_list;
+    const auto A = new AclDenyInfoList(t, ConfigParser::CurrentLocation());
 
-    while ((t = strtok(NULL, w_space))) {
-        L = (acl_name_list *)memAllocate(MEM_ACL_NAME_LIST);
-        xstrncpy(L->name, t, ACL_NAME_SZ);
-        *Tail = L;
-        Tail = &L->next;
+    /* next expect a list of ACL names */
+    while ((t = ConfigParser::NextToken())) {
+        A->acl_list.emplace_back(t);
     }
 
-    if (A->acl_list == NULL) {
+    if (A->acl_list.empty()) {
         debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: deny_info line contains no ACL's, skipping");
-        memFree(A, MEM_ACL_DENY_INFO_LIST);
+        delete A;
         return;
     }
 
     for (B = *head, T = head; B; T = &B->next, B = B->next)
 
-        ;      /* find the tail */
+        ;   /* find the tail */
     *T = A;
 }
 
 void
-aclParseAccessLine(ConfigParser &parser, acl_access ** head)
+aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
 {
-    char *t = NULL;
-    acl_access *A = NULL;
-    acl_access *B = NULL;
-    acl_access **T = NULL;
-
     /* first expect either 'allow' or 'deny' */
+    const char *t = ConfigParser::NextToken();
 
-    if ((t = strtok(NULL, w_space)) == NULL) {
+    if (!t) {
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
-        debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'.");
+        debugs(28, DBG_CRITICAL, "ERROR: aclParseAccessLine: missing 'allow' or 'deny'.");
         return;
     }
 
-    A = new acl_access;
-
+    auto action = Acl::Answer(ACCESS_DUNNO);
     if (!strcmp(t, "allow"))
-        A->allow = ACCESS_ALLOWED;
+        action = Acl::Answer(ACCESS_ALLOWED);
     else if (!strcmp(t, "deny"))
-        A->allow = ACCESS_DENIED;
+        action = Acl::Answer(ACCESS_DENIED);
     else {
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'.");
-        delete A;
         return;
     }
 
-    aclParseAclList(parser, &A->aclList);
+    const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1;
+    MemBuf ctxBuf;
+    ctxBuf.init();
+    ctxBuf.appendf("%s#%d", directive, ruleId);
+    ctxBuf.terminate();
 
-    if (A->aclList == NULL) {
-        debugs(28, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
+    Acl::AndNode *rule = new Acl::AndNode;
+    rule->context(ctxBuf.content(), config_input_line);
+    rule->lineParse();
+    if (rule->empty()) {
+        debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping");
-        delete A;
+        delete rule;
         return;
     }
 
-    A->cfgline = xstrdup(config_input_line);
     /* Append to the end of this list */
 
-    for (B = *head, T = head; B; T = &B->next, B = B->next);
-    *T = A;
+    assert(treep);
+    if (!*treep) {
+        *treep = new Acl::Tree;
+        (*treep)->context(directive, config_input_line);
+    }
+
+    (*treep)->add(rule, action);
 
     /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */
 }
 
-void
-aclParseAclList(ConfigParser &parser, ACLList ** head)
+// aclParseAclList does not expect or set actions (cf. aclParseAccessLine)
+size_t
+aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label)
 {
-    ACLList **Tail = head;     /* sane name in the use below */
-    ACL *a = NULL;
-    char *t;
-
-    /* next expect a list of ACL names, possibly preceeded
-     * by '!' for negation */
-
-    while ((t = strtok(NULL, w_space))) {
-        ACLList *L = new ACLList;
-
-        if (*t == '!') {
-            L->negated (true);
-            ++t;
-        }
-
-        debugs(28, 3, "aclParseAclList: looking for ACL name '" << t << "'");
-        a = ACL::FindByName(t);
+    // accommodate callers unable to convert their ACL list context to string
+    if (!label)
+        label = "...";
+
+    MemBuf ctxLine;
+    ctxLine.init();
+    ctxLine.appendf("(%s %s line)", cfg_directive, label);
+    ctxLine.terminate();
+
+    Acl::AndNode *rule = new Acl::AndNode;
+    rule->context(ctxLine.content(), config_input_line);
+    const auto aclCount = rule->lineParse();
+
+    MemBuf ctxTree;
+    ctxTree.init();
+    ctxTree.appendf("%s %s", cfg_directive, label);
+    ctxTree.terminate();
+
+    // We want a cbdata-protected Tree (despite giving it only one child node).
+    Acl::Tree *tree = new Acl::Tree;
+    tree->add(rule);
+    tree->context(ctxTree.content(), config_input_line);
+
+    assert(treep);
+    assert(!*treep);
+    *treep = tree;
+
+    return aclCount;
+}
 
-        if (a == NULL) {
-            debugs(28, DBG_CRITICAL, "aclParseAclList: ACL name '" << t << "' not found.");
-            delete L;
-            parser.destruct();
-            continue;
-        }
+void
+aclRegister(ACL *acl)
+{
+    if (!acl->registered) {
+        if (!RegisteredAcls)
+            RegisteredAcls = new AclSet;
+        RegisteredAcls->insert(acl);
+        acl->registered = true;
+    }
+}
 
-        L->_acl = a;
-        *Tail = L;
-        Tail = &L->next;
+/// remove registered acl from the centralized deletion set
+static
+void
+aclDeregister(ACL *acl)
+{
+    if (acl->registered) {
+        if (RegisteredAcls)
+            RegisteredAcls->erase(acl);
+        acl->registered = false;
     }
 }
 
@@ -235,73 +247,60 @@ aclParseAclList(ConfigParser &parser, ACLList ** head)
 /* Destroy functions */
 /*********************/
 
+/// called to delete ALL Acls.
 void
 aclDestroyAcls(ACL ** head)
 {
-    ACL *next = NULL;
-
-    debugs(28, 8, "aclDestroyACLs: invoked");
-
-    for (ACL *a = *head; a; a = next) {
-        next = a->next;
-        delete a;
+    *head = nullptr; // Config.aclList
+    if (AclSet *acls = RegisteredAcls) {
+        debugs(28, 8, "deleting all " << acls->size() << " ACLs");
+        while (!acls->empty()) {
+            ACL *acl = *acls->begin();
+            // We use centralized deletion (this function) so ~ACL should not
+            // delete other ACLs, but we still deregister first to prevent any
+            // accesses to the being-deleted ACL via RegisteredAcls.
+            assert(acl->registered); // make sure we are making progress
+            aclDeregister(acl);
+            delete acl;
+        }
     }
-
-    *head = NULL;
 }
 
 void
-aclDestroyAclList(ACLList ** head)
+aclDestroyAclList(ACLList **list)
 {
-    ACLList *l;
     debugs(28, 8, "aclDestroyAclList: invoked");
-
-    for (l = *head; l; l = *head) {
-        *head = l->next;
-        delete l;
-    }
+    assert(list);
+    delete *list;
+    *list = nullptr;
 }
 
 void
 aclDestroyAccessList(acl_access ** list)
 {
-    acl_access *l = NULL;
-    acl_access *next = NULL;
-
-    for (l = *list; l; l = next) {
-        debugs(28, 3, "aclDestroyAccessList: '" << l->cfgline << "'");
-        next = l->next;
-        aclDestroyAclList(&l->aclList);
-        safe_free(l->cfgline);
-        cbdataFree(l);
-    }
-
-    *list = NULL;
+    assert(list);
+    if (*list)
+        debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name);
+    delete *list;
+    *list = nullptr;
 }
 
 /* maex@space.net (06.09.1996)
- *    destroy an acl_deny_info_list */
+ *    destroy an AclDenyInfoList */
 
 void
-aclDestroyDenyInfoList(acl_deny_info_list ** list)
+aclDestroyDenyInfoList(AclDenyInfoList ** list)
 {
-    acl_deny_info_list *a = NULL;
-    acl_deny_info_list *a_next = NULL;
-    acl_name_list *l = NULL;
-    acl_name_list *l_next = NULL;
+    AclDenyInfoList *a = nullptr;
+    AclDenyInfoList *a_next = nullptr;
 
     debugs(28, 8, "aclDestroyDenyInfoList: invoked");
 
     for (a = *list; a; a = a_next) {
-        for (l = a->acl_list; l; l = l_next) {
-            l_next = l->next;
-            safe_free(l);
-        }
-
         a_next = a->next;
-        xfree(a->err_page_name);
-        memFree(a, MEM_ACL_DENY_INFO_LIST);
+        delete a;
     }
 
-    *list = NULL;
+    *list = nullptr;
 }
+