Squid Web Cache master
Loading...
Searching...
No Matches
Gadgets.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/*
10 * DEBUG: section 28 Access Control
11 *
12 * This file contains ACL routines that are not part of the
13 * Acl::Node class, nor any other class yet, and that need to be
14 * factored into appropriate places. They are here to reduce
15 * unneeded dependencies between the Acl::Node class and the rest
16 * of squid.
17 */
18
19#include "squid.h"
20#include "acl/AclDenyInfoList.h"
21#include "acl/Gadgets.h"
22#include "acl/Tree.h"
23#include "cache_cf.h"
24#include "ConfigParser.h"
25#include "errorpage.h"
26#include "globals.h"
27#include "HttpRequest.h"
28#include "SquidConfig.h"
29#include "src/sbuf/Stream.h"
30
31#include <algorithm>
32
34FindDenyInfoPage(const Acl::Answer &answer, const bool redirect_allowed)
35{
36 if (!answer.lastCheckedName) {
37 debugs(28, 3, "ERR_NONE because access was denied without evaluating ACLs");
38 return ERR_NONE;
39 }
40
41 const auto &name = *answer.lastCheckedName;
42
43 for (auto A = Config.denyInfoList; A; A = A->next) {
44 if (!redirect_allowed && strchr(A->err_page_name, ':') ) {
45 debugs(28, 8, "Skip '" << A->err_page_name << "' 30x redirects not allowed as response here.");
46 continue;
47 }
48
49 for (const auto &aclName: A->acl_list) {
50 if (aclName.cmp(name) == 0) {
51 debugs(28, 8, "matched " << name << "; returning " << A->err_page_id << ' ' << A->err_page_name);
52 return A->err_page_id;
53 }
54 }
55 }
56
57 debugs(28, 8, "no match for " << name << (Config.denyInfoList ? "" : "; no deny_info rules"));
58 return ERR_NONE;
59}
60
61bool
62aclIsProxyAuth(const std::optional<SBuf> &name)
63{
64 if (!name) {
65 debugs(28, 3, "no; caller did not supply an ACL name");
66 return false;
67 }
68
69 if (const auto a = Acl::Node::FindByName(*name)) {
70 debugs(28, 5, "returning " << a->isProxyAuth() << " for ACL " << *name);
71 return a->isProxyAuth();
72 }
73
74 debugs(28, 3, "WARNING: Called for nonexistent ACL " << *name);
75 return false;
76}
77
78/* maex@space.net (05.09.96)
79 * get the info for redirecting "access denied" to info pages
80 * TODO (probably ;-)
81 * currently there is no optimization for
82 * - more than one deny_info line with the same url
83 * - a check, whether the given acl really is defined
84 * - a check, whether an acl is added more than once for the same url
85 */
86
87void
89{
90 char *t = nullptr;
93
94 /* first expect a page name */
95
96 if ((t = ConfigParser::NextToken()) == nullptr) {
97 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
98 debugs(28, DBG_CRITICAL, "ERROR: aclParseDenyInfoLine: missing 'error page' parameter.");
99 return;
100 }
101
102 const auto A = new AclDenyInfoList(t, ConfigParser::CurrentLocation());
103
104 /* next expect a list of ACL names */
105 while ((t = ConfigParser::NextToken())) {
106 A->acl_list.emplace_back(t);
107 }
108
109 if (A->acl_list.empty()) {
110 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
111 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: deny_info line contains no ACL's, skipping");
112 delete A;
113 return;
114 }
115
116 for (B = *head, T = head; B; T = &B->next, B = B->next)
117
118 ; /* find the tail */
119 *T = A;
120}
121
122const Acl::Tree &
123Acl::ToTree(const TreePointer * const config)
124{
125 Assure(config);
126 const auto &treePtr = *config;
127 Assure(treePtr);
128 return *treePtr;
129}
130
131void
132aclParseAccessLine(const char *directive, ConfigParser &, acl_access **config)
133{
134 /* first expect either 'allow' or 'deny' */
135 const char *t = ConfigParser::NextToken();
136
137 if (!t) {
138 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
139 debugs(28, DBG_CRITICAL, "ERROR: aclParseAccessLine: missing 'allow' or 'deny'.");
140 return;
141 }
142
143 auto action = Acl::Answer(ACCESS_DUNNO);
144 if (!strcmp(t, "allow"))
145 action = Acl::Answer(ACCESS_ALLOWED);
146 else if (!strcmp(t, "deny"))
147 action = Acl::Answer(ACCESS_DENIED);
148 else {
149 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
150 debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'.");
151 return;
152 }
153
154 const int ruleId = ((config && *config) ? ToTree(*config).childrenCount() : 0) + 1;
155
156 Acl::AndNode *rule = new Acl::AndNode;
157 rule->context(ToSBuf(directive, '#', ruleId), config_input_line);
158 rule->lineParse();
159 if (rule->empty()) {
160 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
161 debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping");
162 delete rule;
163 return;
164 }
165
166 /* Append to the end of this list */
167
168 assert(config);
169 if (!*config)
170 *config = new acl_access();
171 const auto treep = *config;
172
173 assert(treep);
174 if (!*treep) {
175 *treep = new Acl::Tree;
176 (*treep)->context(SBuf(directive), config_input_line);
177 }
178
179 (*treep)->add(rule, action);
180}
181
182// aclParseAclList does not expect or set actions (cf. aclParseAccessLine)
183size_t
184aclParseAclList(ConfigParser &, ACLList **config, const char *label)
185{
186 // accommodate callers unable to convert their ACL list context to string
187 if (!label)
188 label = "...";
189
190 Acl::AndNode *rule = new Acl::AndNode;
191 rule->context(ToSBuf('(', cfg_directive, ' ', label, " line)"), config_input_line);
192 const auto aclCount = rule->lineParse();
193
194 // XXX: We have created only one node, and our callers do not support
195 // actions, but we now have to create an action-supporting Acl::Tree because
196 // Checklist needs actions support. TODO: Add actions methods to Acl::Node,
197 // so that Checklist can be satisfied with Acl::AndNode created above.
198 Acl::Tree *tree = new Acl::Tree;
199 tree->add(rule);
200 tree->context(ToSBuf(cfg_directive, ' ', label), config_input_line);
201
202 assert(config);
203 assert(!*config);
204 *config = new acl_access(tree);
205
206 return aclCount;
207}
208
209/*********************/
210/* Destroy functions */
211/*********************/
212
213void
215{
216 debugs(28, 8, "aclDestroyAclList: invoked");
217 assert(list);
218 delete *list;
219 *list = nullptr;
220}
221
222void
224{
225 assert(list);
226 if (*list)
227 debugs(28, 3, "destroying: " << *list << ' ' << ToTree(*list).name);
228 delete *list;
229 *list = nullptr;
230}
231
232/* maex@space.net (06.09.1996)
233 * destroy an AclDenyInfoList */
234
235void
237{
238 AclDenyInfoList *a = nullptr;
239 AclDenyInfoList *a_next = nullptr;
240
241 debugs(28, 8, "aclDestroyDenyInfoList: invoked");
242
243 for (a = *list; a; a = a_next) {
244 a_next = a->next;
245 delete a;
246 }
247
248 *list = nullptr;
249}
250
#define Assure(condition)
Definition Assure.h:35
class SquidConfig Config
err_type FindDenyInfoPage(const Acl::Answer &answer, const bool redirect_allowed)
Definition Gadgets.cc:34
bool aclIsProxyAuth(const std::optional< SBuf > &name)
Definition Gadgets.cc:62
void aclParseAccessLine(const char *directive, ConfigParser &, acl_access **config)
Parses a single line of a "action followed by acls" directive (e.g., http_access).
Definition Gadgets.cc:132
size_t aclParseAclList(ConfigParser &, ACLList **config, const char *label)
Definition Gadgets.cc:184
Acl::TreePointer acl_access
Definition forward.h:46
squidaio_request_t * head
Definition aiops.cc:129
#define assert(EX)
Definition assert.h:17
const char * cfg_directive
During parsing, the name of the current squid.conf directive being parsed.
Definition cache_cf.cc:269
char config_input_line[BUFSIZ]
Definition cache_cf.cc:272
const char * cfg_filename
Definition cache_cf.cc:270
int config_lineno
Definition cache_cf.cc:271
deny_info representation. Currently a POD.
AclDenyInfoList * next
std::optional< SBuf > lastCheckedName
the name of the ACL (if any) that was evaluated last while obtaining this answer
Definition Acl.h:105
Nodes::size_type childrenCount() const
the number of children nodes
Definition InnerNode.h:29
bool empty() const override
Definition InnerNode.cc:30
size_t lineParse()
Definition InnerNode.cc:44
SBuf name
Definition Node.h:81
void context(const SBuf &aName, const char *configuration)
sets user-specified ACL name and squid.conf context
Definition Acl.cc:220
static Acl::Node * FindByName(const SBuf &)
A configured ACL with a given name or nil.
Definition Acl.cc:159
void add(Acl::Node *rule, const Answer &action)
appends and takes control over the rule with a given action
Definition Tree.cc:42
static SBuf CurrentLocation()
static char * NextToken()
Definition SBuf.h:94
AclDenyInfoList * denyInfoList
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
err_type
Definition forward.h:14
@ ERR_NONE
Definition forward.h:15
void aclDestroyAccessList(acl_access **list)
Definition Gadgets.cc:223
void aclDestroyDenyInfoList(AclDenyInfoList **list)
Definition Gadgets.cc:236
void aclDestroyAclList(ACLList **list)
Definition Gadgets.cc:214
void aclParseDenyInfoLine(AclDenyInfoList **head)
Definition Gadgets.cc:88
@ ACCESS_DENIED
Definition Acl.h:41
@ ACCESS_ALLOWED
Definition Acl.h:42
@ ACCESS_DUNNO
Definition Acl.h:43
const Tree & ToTree(const TreePointer *cfg)
Definition Gadgets.cc:123
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63