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