]>
Commit | Line | Data |
---|---|---|
4fb35c3c | 1 | /* |
1f7b830e | 2 | * Copyright (C) 1996-2025 The Squid Software Foundation and contributors |
4fb35c3c | 3 | * |
bbc27441 AJ |
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. | |
4fb35c3c | 7 | */ |
8 | ||
ff9d9458 FC |
9 | #ifndef SQUID_SRC_ACL_CHECKLIST_H |
10 | #define SQUID_SRC_ACL_CHECKLIST_H | |
4fb35c3c | 11 | |
922513e5 | 12 | #include "acl/Acl.h" |
6f58d7d7 | 13 | #include "acl/InnerNode.h" |
922513e5 | 14 | #include "cbdata.h" |
25aa6c9a AR |
15 | |
16 | #include <optional> | |
6f58d7d7 | 17 | #include <stack> |
640fe8fb | 18 | #include <vector> |
4fb35c3c | 19 | |
cb365059 EB |
20 | class HttpRequest; |
21 | ||
2efeb0b7 | 22 | /// ACL checklist callback |
329c128c | 23 | typedef void ACLCB(Acl::Answer, void *); |
2efeb0b7 | 24 | |
351fe86d AR |
25 | /** \ingroup ACLAPI |
26 | Base class for maintaining Squid and transaction state for access checks. | |
f53969cc SM |
27 | Provides basic ACL checking methods. Its only child, ACLFilledChecklist, |
28 | keeps the actual state data. The split is necessary to avoid exposing | |
351fe86d | 29 | all ACL-related code to virtually Squid data types. */ |
62e76326 | 30 | class ACLChecklist |
31 | { | |
32 | ||
33 | public: | |
8000a965 | 34 | |
5fb6afec | 35 | /// a function that initiates asynchronous ACL checks; see goAsync() |
922513e5 | 36 | using AsyncStarter = void (ACLFilledChecklist &, const Acl::Node &); |
62e76326 | 37 | |
351fe86d | 38 | public: |
4fb35c3c | 39 | ACLChecklist(); |
351fe86d | 40 | virtual ~ACLChecklist(); |
b50e327b | 41 | |
b50e327b | 42 | /** |
e0f7153c AR |
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). | |
af6a12ee | 48 | * |
e0f7153c AR |
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. | |
b50e327b | 51 | * |
e0f7153c AR |
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. | |
af6a12ee | 55 | * |
e0f7153c AR |
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. | |
b50e327b | 61 | */ |
329c128c | 62 | Acl::Answer const & fastCheck(); |
b50e327b AJ |
63 | |
64 | /** | |
e0f7153c AR |
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. | |
af6a12ee | 72 | * |
e0f7153c AR |
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. | |
b50e327b | 84 | */ |
0d645414 | 85 | const Acl::Answer &fastCheck(const ACLList *); |
6f58d7d7 AR |
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. | |
922513e5 | 89 | bool goAsync(AsyncStarter, const Acl::Node &); |
6f58d7d7 | 90 | |
0d645414 | 91 | /// Matches (or resumes matching of) a child node at pos while maintaining |
6f58d7d7 | 92 | /// resumption breadcrumbs if a [grand]child node goes async. |
0d645414 | 93 | bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos); |
b50e327b | 94 | |
6f58d7d7 AR |
95 | /// Whether we should continue to match tree nodes or stop/pause. |
96 | bool keepMatching() const { return !finished() && !asyncInProgress(); } | |
b50e327b | 97 | |
e0f7153c | 98 | /// whether markFinished() was called |
6f58d7d7 AR |
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; } | |
e0f7153c AR |
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 | |
329c128c | 104 | void markFinished(const Acl::Answer &newAnswer, const char *reason); |
b50e327b | 105 | |
329c128c | 106 | const Acl::Answer ¤tAnswer() const { return answer_; } |
b50e327b | 107 | |
640fe8fb | 108 | /// whether the action is banned or not |
329c128c | 109 | bool bannedAction(const Acl::Answer &action) const; |
640fe8fb | 110 | /// add action to the list of banned actions |
329c128c | 111 | void banAction(const Acl::Answer &action); |
640fe8fb | 112 | |
af6a12ee AJ |
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 | |
351fe86d | 115 | // for ACL::checklistMatches to use |
af6a12ee AJ |
116 | virtual bool hasRequest() const = 0; |
117 | virtual bool hasReply() const = 0; | |
4ff6370b | 118 | virtual bool hasAle() const = 0; |
cb365059 EB |
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; | |
b50e327b | 123 | |
3d29e126 | 124 | /// change the current ACL list |
0d645414 | 125 | void changeAcl(const acl_access *); |
3d29e126 | 126 | |
25aa6c9a AR |
127 | /// remember the name of the last ACL being evaluated |
128 | void setLastCheckedName(const SBuf &name) { lastCheckedName_ = name; } | |
129 | ||
c56edb4a EB |
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 | ||
eac759e1 | 153 | private: |
d5cbce97 | 154 | /// Calls non-blocking check callback with the answer and destroys self. |
96ab90cc EB |
155 | /// If abortReason is provided, sets the final answer to ACCESS_DUNNO. |
156 | void checkCallback(const char *abortReason); | |
d5cbce97 | 157 | |
6f58d7d7 AR |
158 | void matchAndFinish(); |
159 | ||
0d645414 AR |
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 | ||
3d29e126 | 166 | public: |
62e76326 | 167 | |
2efeb0b7 | 168 | ACLCB *callback; |
4fb35c3c | 169 | void *callback_data; |
62e76326 | 170 | |
6f58d7d7 AR |
171 | /// Resumes non-blocking check started by nonBlockingCheck() and |
172 | /// suspended until some async operation updated Squid state. | |
5fb6afec | 173 | void resumeNonBlockingCheck(); |
2efeb0b7 | 174 | |
b50e327b | 175 | private: /* internal methods */ |
922513e5 | 176 | /// Position of a child node within an Acl::Node tree. |
e936c41c AR |
177 | class Breadcrumb |
178 | { | |
6f58d7d7 | 179 | public: |
aee3523a | 180 | Breadcrumb(): parent(nullptr) {} |
6f58d7d7 AR |
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); } | |
aee3523a | 184 | void clear() { parent = nullptr; } |
0d645414 | 185 | RefCount<const Acl::InnerNode> parent; ///< intermediate node in the ACL tree |
6f58d7d7 AR |
186 | Acl::Nodes::const_iterator position; ///< child position inside parent |
187 | }; | |
188 | ||
e0f7153c AR |
189 | /// possible outcomes when trying to match a single ACL node in a list |
190 | typedef enum { nmrMatch, nmrMismatch, nmrFinished, nmrNeedsAsync } | |
c0f81932 | 191 | NodeMatchingResult; |
e0f7153c AR |
192 | |
193 | /// prepare for checking ACLs; called once per check | |
194 | void preCheck(const char *what); | |
6f58d7d7 AR |
195 | bool prepNonBlocking(); |
196 | void completeNonBlocking(); | |
197 | void calcImplicitAnswer(); | |
b50e327b | 198 | |
6f58d7d7 | 199 | bool asyncCaller_; ///< whether the caller supports async/slow ACLs |
7c469a68 | 200 | bool occupied_; ///< whether a check (fast or non-blocking) is in progress |
8000a965 | 201 | bool finished_; |
329c128c | 202 | Acl::Answer answer_; |
351fe86d | 203 | |
6f58d7d7 AR |
204 | enum AsyncStage { asyncNone, asyncStarting, asyncRunning, asyncFailed }; |
205 | AsyncStage asyncStage_; | |
6f58d7d7 AR |
206 | Breadcrumb matchLoc_; ///< location of the node running matches() now |
207 | Breadcrumb asyncLoc_; ///< currentNode_ that called goAsync() | |
1707b9ad | 208 | unsigned asyncLoopDepth_; ///< how many times the current async state has resumed |
ab321f4b | 209 | |
315b856d | 210 | bool callerGone(); |
6f58d7d7 AR |
211 | |
212 | /// suspended (due to an async lookup) matches() in the ACL tree | |
213 | std::stack<Breadcrumb> matchPath; | |
640fe8fb | 214 | /// the list of actions which must ignored during acl checks |
329c128c | 215 | std::vector<Acl::Answer> bannedActions_; |
25aa6c9a AR |
216 | |
217 | /// the name of the last evaluated ACL (if any ACLs were evaluated) | |
218 | std::optional<SBuf> lastCheckedName_; | |
4fb35c3c | 219 | }; |
220 | ||
ff9d9458 | 221 | #endif /* SQUID_SRC_ACL_CHECKLIST_H */ |
f53969cc | 222 |