]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Checklist.h
Maintenance: Consistent use of C++11 "override" specifier (#1224)
[thirdparty/squid.git] / src / acl / Checklist.h
1 /*
2 * Copyright (C) 1996-2022 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 #ifndef SQUID_ACLCHECKLIST_H
10 #define SQUID_ACLCHECKLIST_H
11
12 #include "acl/InnerNode.h"
13 #include <stack>
14 #include <vector>
15
16 class HttpRequest;
17
18 /// ACL checklist callback
19 typedef void ACLCB(Acl::Answer, void *);
20
21 /** \ingroup ACLAPI
22 Base class for maintaining Squid and transaction state for access checks.
23 Provides basic ACL checking methods. Its only child, ACLFilledChecklist,
24 keeps the actual state data. The split is necessary to avoid exposing
25 all ACL-related code to virtually Squid data types. */
26 class ACLChecklist
27 {
28
29 public:
30
31 /**
32 * State class.
33 * This abstract class defines the behaviour of
34 * async lookups - which can vary for different ACL types.
35 * Today, every state object must be a singleton.
36 * See NULLState for an example.
37 *
38 \note *no* state should be stored in the state object,
39 * they are used to change the behaviour of the checklist, not
40 * to hold information. If you need to store information in the
41 * state object, consider subclassing ACLChecklist, converting it
42 * to a composite, or changing the state objects from singletons to
43 * refcounted objects.
44 */
45
46 class AsyncState
47 {
48
49 public:
50 virtual void checkForAsync(ACLChecklist *) const = 0;
51 virtual ~AsyncState() {}
52 };
53
54 class NullState : public AsyncState
55 {
56
57 public:
58 static NullState *Instance();
59 void checkForAsync(ACLChecklist *) const override;
60 ~NullState() override {}
61
62 private:
63 static NullState _instance;
64 };
65
66 public:
67 ACLChecklist();
68 virtual ~ACLChecklist();
69
70 /**
71 * Start a non-blocking (async) check for a list of allow/deny rules.
72 * Each rule comes with a list of ACLs.
73 *
74 * The callback specified will be called with the result of the check.
75 *
76 * The first rule where all ACLs match wins. If there is such a rule,
77 * the result becomes that rule keyword (ACCESS_ALLOWED or ACCESS_DENIED).
78 *
79 * If there are rules but all ACL lists mismatch, an implicit rule is used.
80 * Its result is the negation of the keyword of the last seen rule.
81 *
82 * Some ACLs may stop the check prematurely by setting an exceptional
83 * check result (e.g., ACCESS_AUTH_REQUIRED) instead of declaring a
84 * match or mismatch.
85 *
86 * If there are no rules to check at all, the result becomes ACCESS_DUNNO.
87 * Calling this method with no rules to check wastes a lot of CPU cycles
88 * and will result in a DBG_CRITICAL debugging message.
89 */
90 void nonBlockingCheck(ACLCB * callback, void *callback_data);
91
92 /**
93 * Perform a blocking (immediate) check for a list of allow/deny rules.
94 * Each rule comes with a list of ACLs.
95 *
96 * The first rule where all ACLs match wins. If there is such a rule,
97 * the result becomes that rule keyword (ACCESS_ALLOWED or ACCESS_DENIED).
98 *
99 * If there are rules but all ACL lists mismatch, an implicit rule is used
100 * Its result is the negation of the keyword of the last seen rule.
101 *
102 * Some ACLs may stop the check prematurely by setting an exceptional
103 * check result (e.g., ACCESS_AUTH_REQUIRED) instead of declaring a
104 * match or mismatch.
105 *
106 * Some ACLs may require an async lookup which is prohibited by this
107 * method. In this case, the exceptional check result of ACCESS_DUNNO is
108 * immediately returned.
109 *
110 * If there are no rules to check at all, the result becomes ACCESS_DUNNO.
111 */
112 Acl::Answer const & fastCheck();
113
114 /**
115 * Perform a blocking (immediate) check whether a list of ACLs matches.
116 * This method is meant to be used with squid.conf ACL-driven options that
117 * lack allow/deny keywords and are tested one ACL list at a time. Whether
118 * the checks for other occurrences of the same option continue after this
119 * call is up to the caller and option semantics.
120 *
121 * If all ACLs match, the result becomes ACCESS_ALLOWED.
122 *
123 * If all ACLs mismatch, the result becomes ACCESS_DENIED.
124 *
125 * Some ACLs may stop the check prematurely by setting an exceptional
126 * check result (e.g., ACCESS_AUTH_REQUIRED) instead of declaring a
127 * match or mismatch.
128 *
129 * Some ACLs may require an async lookup which is prohibited by this
130 * method. In this case, the exceptional check result of ACCESS_DUNNO is
131 * immediately returned.
132 *
133 * If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED.
134 */
135 Acl::Answer const & fastCheck(const Acl::Tree *list);
136
137 /// If slow lookups are allowed, switches into "async in progress" state.
138 /// Otherwise, returns false; the caller is expected to handle the failure.
139 bool goAsync(AsyncState *);
140
141 /// Matches (or resumes matching of) a child node while maintaning
142 /// resumption breadcrumbs if a [grand]child node goes async.
143 bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child);
144
145 /// Whether we should continue to match tree nodes or stop/pause.
146 bool keepMatching() const { return !finished() && !asyncInProgress(); }
147
148 /// whether markFinished() was called
149 bool finished() const { return finished_; }
150 /// async call has been started and has not finished (or failed) yet
151 bool asyncInProgress() const { return asyncStage_ != asyncNone; }
152 /// called when no more ACLs should be checked; sets the final answer and
153 /// prints a debugging message explaining the reason for that answer
154 void markFinished(const Acl::Answer &newAnswer, const char *reason);
155
156 const Acl::Answer &currentAnswer() const { return answer_; }
157
158 /// whether the action is banned or not
159 bool bannedAction(const Acl::Answer &action) const;
160 /// add action to the list of banned actions
161 void banAction(const Acl::Answer &action);
162
163 // XXX: ACLs that need request or reply have to use ACLFilledChecklist and
164 // should do their own checks so that we do not have to povide these two
165 // for ACL::checklistMatches to use
166 virtual bool hasRequest() const = 0;
167 virtual bool hasReply() const = 0;
168 virtual bool hasAle() const = 0;
169 /// assigns uninitialized adapted_request and url ALE components
170 virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const = 0;
171 /// warns if there are uninitialized ALE components and fills them
172 virtual void verifyAle() const = 0;
173
174 /// change the current ACL list
175 /// \return a pointer to the old list value (may be nullptr)
176 const Acl::Tree *changeAcl(const Acl::Tree *t) {
177 const Acl::Tree *old = accessList;
178 if (t != accessList) {
179 cbdataReferenceDone(accessList);
180 accessList = cbdataReference(t);
181 }
182 return old;
183 }
184
185 private:
186 /// Calls non-blocking check callback with the answer and destroys self.
187 void checkCallback(Acl::Answer answer);
188
189 void matchAndFinish();
190
191 void changeState(AsyncState *);
192 AsyncState *asyncState() const;
193
194 const Acl::Tree *accessList;
195 public:
196
197 ACLCB *callback;
198 void *callback_data;
199
200 /// Resumes non-blocking check started by nonBlockingCheck() and
201 /// suspended until some async operation updated Squid state.
202 void resumeNonBlockingCheck(AsyncState *state);
203
204 private: /* internal methods */
205 /// Position of a child node within an ACL tree.
206 class Breadcrumb
207 {
208 public:
209 Breadcrumb(): parent(nullptr) {}
210 Breadcrumb(const Acl::InnerNode *aParent, Acl::Nodes::const_iterator aPos): parent(aParent), position(aPos) {}
211 bool operator ==(const Breadcrumb &b) const { return parent == b.parent && (!parent || position == b.position); }
212 bool operator !=(const Breadcrumb &b) const { return !this->operator ==(b); }
213 void clear() { parent = nullptr; }
214 const Acl::InnerNode *parent; ///< intermediate node in the ACL tree
215 Acl::Nodes::const_iterator position; ///< child position inside parent
216 };
217
218 /// possible outcomes when trying to match a single ACL node in a list
219 typedef enum { nmrMatch, nmrMismatch, nmrFinished, nmrNeedsAsync }
220 NodeMatchingResult;
221
222 /// prepare for checking ACLs; called once per check
223 void preCheck(const char *what);
224 bool prepNonBlocking();
225 void completeNonBlocking();
226 void calcImplicitAnswer();
227
228 bool asyncCaller_; ///< whether the caller supports async/slow ACLs
229 bool occupied_; ///< whether a check (fast or non-blocking) is in progress
230 bool finished_;
231 Acl::Answer answer_;
232
233 enum AsyncStage { asyncNone, asyncStarting, asyncRunning, asyncFailed };
234 AsyncStage asyncStage_;
235 AsyncState *state_;
236 Breadcrumb matchLoc_; ///< location of the node running matches() now
237 Breadcrumb asyncLoc_; ///< currentNode_ that called goAsync()
238 unsigned asyncLoopDepth_; ///< how many times the current async state has resumed
239
240 bool callerGone();
241
242 /// suspended (due to an async lookup) matches() in the ACL tree
243 std::stack<Breadcrumb> matchPath;
244 /// the list of actions which must ignored during acl checks
245 std::vector<Acl::Answer> bannedActions_;
246 };
247
248 #endif /* SQUID_ACLCHECKLIST_H */
249