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 &currentAnswer() 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