]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
bbc27441 AJ |
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 | ||
582c2af2 | 9 | #include "squid.h" |
af0ded40 | 10 | #include "AccessLogEntry.h" |
127dce76 | 11 | #include "acl/FilledChecklist.h" |
582c2af2 FC |
12 | #include "adaptation/AccessCheck.h" |
13 | #include "adaptation/AccessRule.h" | |
14 | #include "adaptation/Config.h" | |
79628299 | 15 | #include "adaptation/Initiator.h" |
62c7f90e AR |
16 | #include "adaptation/Service.h" |
17 | #include "adaptation/ServiceGroups.h" | |
582c2af2 | 18 | #include "base/AsyncJobCalls.h" |
3d93a84d | 19 | #include "base/TextException.h" |
582c2af2 FC |
20 | #include "ConfigParser.h" |
21 | #include "globals.h" | |
22 | #include "HttpReply.h" | |
23 | #include "HttpRequest.h" | |
62c7f90e | 24 | |
d6d0eb11 | 25 | /** \cond AUTODOCS_IGNORE */ |
62c7f90e | 26 | cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN; |
d85b8894 | 27 | /** \endcond */ |
62c7f90e | 28 | |
abd4b611 AR |
29 | bool |
30 | Adaptation::AccessCheck::Start(Method method, VectPoint vp, | |
af0ded40 CT |
31 | HttpRequest *req, HttpReply *rep, |
32 | AccessLogEntry::Pointer &al, Adaptation::Initiator *initiator) | |
26ac0430 | 33 | { |
abd4b611 AR |
34 | |
35 | if (Config::Enabled) { | |
36 | // the new check will call the callback and delete self, eventually | |
4299f876 | 37 | AsyncJob::Start(new AccessCheck( // we do not store so not a CbcPointer |
af0ded40 | 38 | ServiceFilter(method, vp, req, rep, al), initiator)); |
4299f876 | 39 | return true; |
26ac0430 | 40 | } |
abd4b611 AR |
41 | |
42 | debugs(83, 3, HERE << "adaptation off, skipping"); | |
43 | return false; | |
44 | } | |
45 | ||
a22e6cd3 | 46 | Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter, |
79628299 | 47 | Adaptation::Initiator *initiator): |
f53969cc SM |
48 | AsyncJob("AccessCheck"), filter(aFilter), |
49 | theInitiator(initiator), | |
50 | acl_checklist(NULL) | |
62c7f90e | 51 | { |
3ff65596 | 52 | #if ICAP_CLIENT |
a22e6cd3 | 53 | Adaptation::Icap::History::Pointer h = filter.request->icapHistory(); |
3ff65596 AR |
54 | if (h != NULL) |
55 | h->start("ACL"); | |
56 | #endif | |
57 | ||
a22e6cd3 | 58 | debugs(93, 5, HERE << "AccessCheck constructed for " << |
e1381638 | 59 | methodStr(filter.method) << " " << vectPointStr(filter.point)); |
62c7f90e AR |
60 | } |
61 | ||
62 | Adaptation::AccessCheck::~AccessCheck() | |
63 | { | |
3ff65596 | 64 | #if ICAP_CLIENT |
a22e6cd3 | 65 | Adaptation::Icap::History::Pointer h = filter.request->icapHistory(); |
3ff65596 AR |
66 | if (h != NULL) |
67 | h->stop("ACL"); | |
68 | #endif | |
62c7f90e AR |
69 | } |
70 | ||
dd5d7172 | 71 | void |
e1381638 AJ |
72 | Adaptation::AccessCheck::start() |
73 | { | |
74 | AsyncJob::start(); | |
53340485 AR |
75 | |
76 | if (!usedDynamicRules()) | |
77 | check(); | |
78 | } | |
79 | ||
80 | /// returns true if previous services configured dynamic chaining "rules" | |
81 | bool | |
82 | Adaptation::AccessCheck::usedDynamicRules() | |
83 | { | |
84 | Adaptation::History::Pointer ah = filter.request->adaptHistory(); | |
85 | if (!ah) | |
86 | return false; // dynamic rules not enabled or not triggered | |
87 | ||
88 | DynamicGroupCfg services; | |
89 | if (!ah->extractFutureServices(services)) { // clears history | |
90 | debugs(85,9, HERE << "no service-proposed rules stored"); | |
91 | return false; // earlier service did not plan for the future | |
92 | } | |
93 | ||
94 | debugs(85,3, HERE << "using stored service-proposed rules: " << services); | |
95 | ||
96 | ServiceGroupPointer g = new DynamicServiceChain(services, filter); | |
97 | callBack(g); | |
98 | Must(done()); | |
99 | return true; | |
dd5d7172 AR |
100 | } |
101 | ||
a22e6cd3 | 102 | /// Walk the access rules list to find rules with applicable service groups |
62c7f90e AR |
103 | void |
104 | Adaptation::AccessCheck::check() | |
105 | { | |
192378eb | 106 | debugs(93, 4, HERE << "start checking"); |
62c7f90e AR |
107 | |
108 | typedef AccessRules::iterator ARI; | |
109 | for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) { | |
62c7f90e | 110 | AccessRule *r = *i; |
a22e6cd3 AR |
111 | if (isCandidate(*r)) { |
112 | debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate"); | |
4c9eadc2 | 113 | candidates.push_back(r->id); |
62c7f90e AR |
114 | } |
115 | } | |
116 | ||
117 | checkCandidates(); | |
118 | } | |
119 | ||
120 | // XXX: Here and everywhere we call FindRule(topCandidate()): | |
121 | // Once we identified the candidate, we should not just ignore it | |
122 | // if reconfigure changes rules. We should either lock the rule to | |
123 | // prevent reconfigure from stealing it or restart the check with | |
124 | // new rules. Throwing an exception may also be appropriate. | |
125 | void | |
126 | Adaptation::AccessCheck::checkCandidates() | |
127 | { | |
192378eb | 128 | debugs(93, 4, HERE << "has " << candidates.size() << " rules"); |
62c7f90e AR |
129 | |
130 | while (!candidates.empty()) { | |
131 | if (AccessRule *r = FindRule(topCandidate())) { | |
b50e327b | 132 | /* BUG 2526: what to do when r->acl is empty?? */ |
62c7f90e | 133 | // XXX: we do not have access to conn->rfc931 here. |
a22e6cd3 | 134 | acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str); |
eced1234 CT |
135 | if ((acl_checklist->reply = filter.reply)) |
136 | HTTPMSGLOCK(acl_checklist->reply); | |
d4806c91 | 137 | acl_checklist->al = filter.al; |
62c7f90e AR |
138 | acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this); |
139 | return; | |
140 | } | |
141 | ||
c8ea3cc0 | 142 | candidates.erase(candidates.begin()); // the rule apparently went away (reconfigure) |
62c7f90e AR |
143 | } |
144 | ||
192378eb | 145 | debugs(93, 4, HERE << "NO candidates left"); |
a22e6cd3 AR |
146 | callBack(NULL); |
147 | Must(done()); | |
62c7f90e AR |
148 | } |
149 | ||
150 | void | |
2efeb0b7 | 151 | Adaptation::AccessCheck::AccessCheckCallbackWrapper(allow_t answer, void *data) |
62c7f90e | 152 | { |
192378eb | 153 | debugs(93, 8, HERE << "callback answer=" << answer); |
62c7f90e | 154 | AccessCheck *ac = (AccessCheck*)data; |
f9cf4ba9 | 155 | |
7dfddb79 | 156 | /** \todo AYJ 2008-06-12: If answer == ACCESS_AUTH_REQUIRED |
f9cf4ba9 AJ |
157 | * we should be kicking off an authentication before continuing |
158 | * with this request. see bug 2400 for details. | |
159 | */ | |
7866ecc9 AR |
160 | |
161 | // convert to async call to get async call protections and features | |
2efeb0b7 | 162 | typedef UnaryMemFunT<AccessCheck, allow_t> MyDialer; |
7866ecc9 AR |
163 | AsyncCall::Pointer call = |
164 | asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer", | |
2efeb0b7 | 165 | MyDialer(ac, &Adaptation::AccessCheck::noteAnswer, answer)); |
7866ecc9 AR |
166 | ScheduleCallHere(call); |
167 | ||
62c7f90e AR |
168 | } |
169 | ||
a22e6cd3 | 170 | /// process the results of the ACL check |
62c7f90e | 171 | void |
2efeb0b7 | 172 | Adaptation::AccessCheck::noteAnswer(allow_t answer) |
62c7f90e | 173 | { |
a22e6cd3 AR |
174 | Must(!candidates.empty()); // the candidate we were checking must be there |
175 | debugs(93,5, HERE << topCandidate() << " answer=" << answer); | |
176 | ||
2efeb0b7 | 177 | if (answer == ACCESS_ALLOWED) { // the rule matched |
a22e6cd3 AR |
178 | ServiceGroupPointer g = topGroup(); |
179 | if (g != NULL) { // the corresponding group found | |
180 | callBack(g); | |
181 | Must(done()); | |
182 | return; | |
183 | } | |
62c7f90e AR |
184 | } |
185 | ||
a22e6cd3 | 186 | // no match or the group disappeared during reconfiguration |
c8ea3cc0 | 187 | candidates.erase(candidates.begin()); |
a22e6cd3 | 188 | checkCandidates(); |
62c7f90e AR |
189 | } |
190 | ||
a22e6cd3 AR |
191 | /// call back with a possibly nil group; the job ends here because all failures |
192 | /// at this point are fatal to the access check process | |
62c7f90e | 193 | void |
a22e6cd3 | 194 | Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g) |
62c7f90e | 195 | { |
a22e6cd3 | 196 | debugs(93,3, HERE << g); |
79628299 CT |
197 | CallJobHere1(93, 5, theInitiator, Adaptation::Initiator, |
198 | noteAdaptationAclCheckDone, g); | |
a22e6cd3 AR |
199 | mustStop("done"); // called back or will never be able to call back |
200 | } | |
62c7f90e | 201 | |
a22e6cd3 AR |
202 | Adaptation::ServiceGroupPointer |
203 | Adaptation::AccessCheck::topGroup() const | |
204 | { | |
205 | ServiceGroupPointer g; | |
62c7f90e AR |
206 | if (candidates.size()) { |
207 | if (AccessRule *r = FindRule(topCandidate())) { | |
a22e6cd3 AR |
208 | g = FindGroup(r->groupId); |
209 | debugs(93,5, HERE << "top group for " << r->id << " is " << g); | |
62c7f90e | 210 | } else { |
a22e6cd3 | 211 | debugs(93,5, HERE << "no rule for " << topCandidate()); |
62c7f90e | 212 | } |
26ac0430 | 213 | } else { |
a22e6cd3 | 214 | debugs(93,5, HERE << "no candidates"); // should not happen |
26ac0430 | 215 | } |
62c7f90e | 216 | |
a22e6cd3 | 217 | return g; |
62c7f90e AR |
218 | } |
219 | ||
a22e6cd3 AR |
220 | /** Returns true iff the rule's service group will be used after ACL matches. |
221 | Used to detect rules worth ACl-checking. */ | |
222 | bool | |
223 | Adaptation::AccessCheck::isCandidate(AccessRule &r) | |
26ac0430 | 224 | { |
a22e6cd3 | 225 | debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " << |
e1381638 | 226 | r.groupId); |
62c7f90e | 227 | |
a22e6cd3 | 228 | ServiceGroupPointer g = FindGroup(r.groupId); |
62c7f90e AR |
229 | |
230 | if (!g) { | |
a22e6cd3 AR |
231 | debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id); |
232 | return false; | |
62c7f90e AR |
233 | } |
234 | ||
a22e6cd3 AR |
235 | const bool wants = g->wants(filter); |
236 | debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores")); | |
237 | return wants; | |
62c7f90e | 238 | } |
f53969cc | 239 |