]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/AccessCheck.cc
SourceFormat: enforcement
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
1 #include "squid.h"
2 #include "structs.h"
3
4 #include "ConfigParser.h"
5 #include "HttpRequest.h"
6 #include "HttpReply.h"
7 #include "acl/FilledChecklist.h"
8 #include "adaptation/Service.h"
9 #include "adaptation/ServiceGroups.h"
10 #include "adaptation/AccessRule.h"
11 #include "adaptation/Config.h"
12 #include "adaptation/AccessCheck.h"
13
14
15 /** \cond AUTODOCS-IGNORE */
16 cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
17 /** \endcond */
18
19 bool
20 Adaptation::AccessCheck::Start(Method method, VectPoint vp,
21 HttpRequest *req, HttpReply *rep, AccessCheckCallback *cb, void *cbdata)
22 {
23
24 if (Config::Enabled) {
25 // the new check will call the callback and delete self, eventually
26 return AsyncStart(new AccessCheck(
27 ServiceFilter(method, vp, req, rep), cb, cbdata));
28 }
29
30 debugs(83, 3, HERE << "adaptation off, skipping");
31 return false;
32 }
33
34 Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter,
35 AccessCheckCallback *aCallback,
36 void *aCallbackData):
37 AsyncJob("AccessCheck"), filter(aFilter),
38 callback(aCallback),
39 callback_data(cbdataReference(aCallbackData)),
40 acl_checklist(NULL)
41 {
42 #if ICAP_CLIENT
43 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
44 if (h != NULL)
45 h->start("ACL");
46 #endif
47
48 debugs(93, 5, HERE << "AccessCheck constructed for " <<
49 methodStr(filter.method) << " " << vectPointStr(filter.point));
50 }
51
52 Adaptation::AccessCheck::~AccessCheck()
53 {
54 #if ICAP_CLIENT
55 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
56 if (h != NULL)
57 h->stop("ACL");
58 #endif
59 if (callback_data)
60 cbdataReferenceDone(callback_data);
61 }
62
63 void
64 Adaptation::AccessCheck::start()
65 {
66 AsyncJob::start();
67 check();
68 }
69
70 /// Walk the access rules list to find rules with applicable service groups
71 void
72 Adaptation::AccessCheck::check()
73 {
74 debugs(93, 4, HERE << "start checking");
75
76 typedef AccessRules::iterator ARI;
77 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
78 AccessRule *r = *i;
79 if (isCandidate(*r)) {
80 debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate");
81 candidates += r->id;
82 }
83 }
84
85 checkCandidates();
86 }
87
88 // XXX: Here and everywhere we call FindRule(topCandidate()):
89 // Once we identified the candidate, we should not just ignore it
90 // if reconfigure changes rules. We should either lock the rule to
91 // prevent reconfigure from stealing it or restart the check with
92 // new rules. Throwing an exception may also be appropriate.
93 void
94 Adaptation::AccessCheck::checkCandidates()
95 {
96 debugs(93, 4, HERE << "has " << candidates.size() << " rules");
97
98 while (!candidates.empty()) {
99 if (AccessRule *r = FindRule(topCandidate())) {
100 /* BUG 2526: what to do when r->acl is empty?? */
101 // XXX: we do not have access to conn->rfc931 here.
102 acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str);
103 acl_checklist->reply = filter.reply ? HTTPMSGLOCK(filter.reply) : NULL;
104 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
105 return;
106 }
107
108 candidates.shift(); // the rule apparently went away (reconfigure)
109 }
110
111 debugs(93, 4, HERE << "NO candidates left");
112 callBack(NULL);
113 Must(done());
114 }
115
116 void
117 Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
118 {
119 debugs(93, 8, HERE << "callback answer=" << answer);
120 AccessCheck *ac = (AccessCheck*)data;
121
122 /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH
123 * we should be kicking off an authentication before continuing
124 * with this request. see bug 2400 for details.
125 */
126 ac->noteAnswer(answer==ACCESS_ALLOWED);
127 }
128
129 /// process the results of the ACL check
130 void
131 Adaptation::AccessCheck::noteAnswer(int answer)
132 {
133 Must(!candidates.empty()); // the candidate we were checking must be there
134 debugs(93,5, HERE << topCandidate() << " answer=" << answer);
135
136 if (answer) { // the rule matched
137 ServiceGroupPointer g = topGroup();
138 if (g != NULL) { // the corresponding group found
139 callBack(g);
140 Must(done());
141 return;
142 }
143 }
144
145 // no match or the group disappeared during reconfiguration
146 candidates.shift();
147 checkCandidates();
148 }
149
150 /// call back with a possibly nil group; the job ends here because all failures
151 /// at this point are fatal to the access check process
152 void
153 Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
154 {
155 debugs(93,3, HERE << g);
156
157 void *validated_cbdata;
158 if (cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
159 callback(g, validated_cbdata);
160 }
161 mustStop("done"); // called back or will never be able to call back
162 }
163
164 Adaptation::ServiceGroupPointer
165 Adaptation::AccessCheck::topGroup() const
166 {
167 ServiceGroupPointer g;
168 if (candidates.size()) {
169 if (AccessRule *r = FindRule(topCandidate())) {
170 g = FindGroup(r->groupId);
171 debugs(93,5, HERE << "top group for " << r->id << " is " << g);
172 } else {
173 debugs(93,5, HERE << "no rule for " << topCandidate());
174 }
175 } else {
176 debugs(93,5, HERE << "no candidates"); // should not happen
177 }
178
179 return g;
180 }
181
182 /** Returns true iff the rule's service group will be used after ACL matches.
183 Used to detect rules worth ACl-checking. */
184 bool
185 Adaptation::AccessCheck::isCandidate(AccessRule &r)
186 {
187 debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " <<
188 r.groupId);
189
190 ServiceGroupPointer g = FindGroup(r.groupId);
191
192 if (!g) {
193 debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id);
194 return false;
195 }
196
197 const bool wants = g->wants(filter);
198 debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores"));
199 return wants;
200 }