1 /* 2 * Copyright (C) 1996-2021 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(allow_t, 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; ~AsyncState()51 virtual ~AsyncState() {} 52 }; 53 54 class NullState : public AsyncState 55 { 56 57 public: 58 static NullState *Instance(); 59 virtual void checkForAsync(ACLChecklist *) const; ~NullState()60 virtual ~NullState() {} 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 allow_t 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 allow_t 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. keepMatching()146 bool keepMatching() const { return !finished() && !asyncInProgress(); } 147 148 /// whether markFinished() was called finished()149 bool finished() const { return finished_; } 150 /// async call has been started and has not finished (or failed) yet asyncInProgress()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 allow_t &newAnswer, const char *reason); 155 currentAnswer()156 const allow_t ¤tAnswer() const { return allow_; } 157 158 /// whether the action is banned or not 159 bool bannedAction(const allow_t &action) const; 160 /// add action to the list of banned actions 161 void banAction(const allow_t &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) changeAcl(const Acl::Tree * t)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(allow_t 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: Breadcrumb()209 Breadcrumb(): parent(NULL) {} Breadcrumb(const Acl::InnerNode * aParent,Acl::Nodes::const_iterator aPos)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); } clear()213 void clear() { parent = NULL; } 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 allow_t allow_; 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<allow_t> bannedActions_; 246 }; 247 248 #endif /* SQUID_ACLCHECKLIST_H */ 249 250