]>
Commit | Line | Data |
---|---|---|
225b7b10 | 1 | /* |
66e818e8 | 2 | * $Id: ACLChecklist.cc,v 1.2 2003/02/14 11:56:51 robertc Exp $ |
225b7b10 | 3 | * |
4 | * DEBUG: section 28 Access Control | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
8 | * ---------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from | |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License | |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
32 | * | |
33 | * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org> | |
34 | */ | |
35 | ||
36 | #include "squid.h" | |
37 | #include "ACLChecklist.h" | |
38 | /* TODO: trim this ! */ | |
39 | #include "splay.h" | |
40 | #include "HttpRequest.h" | |
41 | #include "authenticate.h" | |
42 | #include "fde.h" | |
43 | #include "ACLProxyAuth.h" | |
44 | #if USE_IDENT | |
45 | #include "ACLIdent.h" | |
46 | #endif | |
47 | #include "ACLUserData.h" | |
48 | ||
49 | int | |
50 | ACLChecklist::authenticated() | |
51 | { | |
52 | http_hdr_type headertype; | |
53 | if (NULL == request) { | |
54 | fatal ("requiresRequest SHOULD have been true for this ACL!!"); | |
66e818e8 | 55 | return 0; |
225b7b10 | 56 | } else if (!request->flags.accelerated) { |
57 | /* Proxy authorization on proxy requests */ | |
58 | headertype = HDR_PROXY_AUTHORIZATION; | |
59 | } else if (request->flags.internal) { | |
60 | /* WWW authorization on accelerated internal requests */ | |
61 | headertype = HDR_AUTHORIZATION; | |
62 | } else { | |
63 | #if AUTH_ON_ACCELERATION | |
64 | /* WWW authorization on accelerated requests */ | |
65 | headertype = HDR_AUTHORIZATION; | |
66 | #else | |
67 | debug(28, 1) ("ACHChecklist::authenticated: authentication not applicable on accelerated requests.\n"); | |
68 | return -1; | |
69 | #endif | |
70 | } | |
71 | /* get authed here */ | |
72 | /* Note: this fills in auth_user_request when applicable */ | |
73 | switch (authenticateTryToAuthenticateAndSetAuthUser(&auth_user_request, headertype, request, conn(), src_addr)) { | |
74 | case AUTH_ACL_CANNOT_AUTHENTICATE: | |
75 | debug(28, 4) ("aclMatchAcl: returning 0 user authenticated but not authorised.\n"); | |
76 | return 0; | |
77 | case AUTH_AUTHENTICATED: | |
78 | return 1; | |
79 | break; | |
80 | case AUTH_ACL_HELPER: | |
81 | debug(28, 4) ("aclMatchAcl: returning 0 sending credentials to helper.\n"); | |
82 | changeState (ProxyAuthLookup::Instance()); | |
83 | return 0; | |
84 | case AUTH_ACL_CHALLENGE: | |
85 | debug(28, 4) ("aclMatchAcl: returning 0 sending authentication challenge.\n"); | |
86 | changeState (ProxyAuthNeeded::Instance()); | |
87 | return 0; | |
88 | default: | |
89 | fatal("unexpected authenticateAuthenticate reply\n"); | |
90 | return 0; | |
91 | } | |
92 | } | |
93 | ||
94 | allow_t const & | |
95 | ACLChecklist::currentAnswer() const | |
96 | { | |
97 | return allow_; | |
98 | } | |
99 | ||
100 | void | |
101 | ACLChecklist::currentAnswer(allow_t const newAnswer) | |
102 | { | |
103 | allow_ = newAnswer; | |
104 | } | |
105 | ||
106 | void | |
107 | ACLChecklist::check() | |
108 | { | |
109 | /* deny if no rules present */ | |
110 | currentAnswer(ACCESS_DENIED); | |
111 | /* NOTE: This holds a cbdata reference to the current access_list | |
112 | * entry, not the whole list. | |
113 | */ | |
114 | while (accessList != NULL) { | |
115 | /* | |
116 | * If the _acl_access is no longer valid (i.e. its been | |
117 | * freed because of a reconfigure), then bail on this | |
118 | * access check. For now, return ACCESS_DENIED. | |
119 | */ | |
120 | if (!cbdataReferenceValid(accessList)) { | |
121 | cbdataReferenceDone(accessList); | |
122 | break; | |
123 | } | |
124 | ||
125 | checkAccessList(); | |
126 | if (asyncInProgress()) | |
127 | return; | |
128 | ||
129 | if (finished()) { | |
130 | /* | |
131 | * We are done. Either the request | |
132 | * is allowed, denied, requires authentication. | |
133 | */ | |
134 | debug(28, 3) ("ACLChecklist::check: match found, returning %d\n", currentAnswer()); | |
135 | cbdataReferenceDone(accessList); /* A */ | |
136 | checkCallback(currentAnswer()); | |
137 | /* From here on in, this may be invalid */ | |
138 | return; | |
139 | } | |
140 | /* | |
141 | * Reference the next access entry | |
142 | */ | |
143 | const acl_access *A = accessList; | |
144 | accessList = cbdataReference(accessList->next); | |
145 | cbdataReferenceDone(A); | |
146 | } | |
147 | /* dropped off the end of the list */ | |
148 | debug(28, 3) ("ACLChecklist::check: NO match found, returning %d\n", | |
149 | currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED); | |
150 | checkCallback(currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED); | |
151 | } | |
152 | ||
153 | bool | |
154 | ACLChecklist::asyncInProgress() const | |
155 | { | |
156 | return async_; | |
157 | } | |
158 | ||
159 | void | |
160 | ACLChecklist::asyncInProgress(bool const newAsync) | |
161 | { | |
162 | assert (!finished()); | |
163 | async_ = newAsync; | |
164 | debug (28,3)("ACLChecklist::asyncInProgress: async set to %d\n",async_); | |
165 | } | |
166 | ||
167 | bool | |
168 | ACLChecklist::finished() const | |
169 | { | |
170 | return finished_; | |
171 | } | |
172 | ||
173 | void | |
174 | ACLChecklist::markFinished() | |
175 | { | |
176 | assert (!finished()); | |
177 | finished_ = true; | |
178 | debug (28,3)("checklist processing finished\n"); | |
179 | } | |
180 | ||
181 | void | |
182 | ACLChecklist::checkAccessList() | |
183 | { | |
184 | debug(28, 3) ("ACLChecklist::checkAccessList: checking '%s'\n", accessList->cfgline); | |
185 | /* what is our result on a match? */ | |
186 | currentAnswer(accessList->allow); | |
187 | /* does the current AND clause match */ | |
188 | bool match = matchAclList(accessList->aclList); | |
189 | if (match) | |
190 | markFinished(); | |
191 | /* Should be else, but keep the exact same flow as before */ | |
192 | checkForAsync(); | |
193 | } | |
194 | ||
195 | void | |
196 | ACLChecklist::checkForAsync() | |
197 | { | |
198 | /* check for async lookups needed. */ | |
199 | if (asyncState() != NullState::Instance()) { | |
200 | /* If a state object is here, use it. | |
201 | * When all cases are converted, the if goes away and it | |
202 | * becomes unconditional. | |
203 | * RBC 02 2003 | |
204 | */ | |
205 | asyncState()->checkForAsync(this); | |
206 | } else if (state[ACL_DST_ASN] == ACL_LOOKUP_NEEDED) { | |
207 | state[ACL_DST_ASN] = ACL_LOOKUP_PENDING; | |
208 | ipcache_nbgethostbyname(request->host, | |
209 | aclLookupDstIPforASNDone, this); | |
210 | asyncInProgress(true); | |
211 | } else if (state[ACL_SRC_DOMAIN] == ACL_LOOKUP_NEEDED) { | |
212 | state[ACL_SRC_DOMAIN] = ACL_LOOKUP_PENDING; | |
213 | fqdncache_nbgethostbyaddr(src_addr, | |
214 | aclLookupSrcFQDNDone, this); | |
215 | asyncInProgress(true); | |
216 | } else if (state[ACL_DST_DOMAIN] == ACL_LOOKUP_NEEDED) { | |
217 | ipcache_addrs *ia; | |
218 | ia = ipcacheCheckNumeric(request->host); | |
219 | if (ia == NULL) { | |
220 | state[ACL_DST_DOMAIN] = ACL_LOOKUP_DONE; | |
221 | } else { | |
222 | dst_addr = ia->in_addrs[0]; | |
223 | state[ACL_DST_DOMAIN] = ACL_LOOKUP_PENDING; | |
224 | fqdncache_nbgethostbyaddr(dst_addr, | |
225 | aclLookupDstFQDNDone, this); | |
226 | } | |
227 | asyncInProgress(true); | |
228 | #if USE_IDENT | |
229 | } else if (state[ACL_IDENT] == ACL_LOOKUP_NEEDED) { | |
230 | debug(28, 3) ("ACLChecklist::checkForAsync: Doing ident lookup\n"); | |
231 | if (conn() && cbdataReferenceValid(conn())) { | |
232 | identStart(&conn()->me, &conn()->peer, | |
233 | aclLookupIdentDone, this); | |
234 | state[ACL_IDENT] = ACL_LOOKUP_PENDING; | |
235 | } else { | |
236 | debug(28, 1) ("ACLChecklist::checkForAsync: Can't start ident lookup. No client connection\n"); | |
237 | currentAnswer(ACCESS_DENIED); | |
238 | markFinished(); | |
239 | return; | |
240 | } | |
241 | asyncInProgress(true); | |
242 | #endif | |
243 | } | |
244 | } | |
245 | ||
246 | void | |
247 | ACLChecklist::checkCallback(allow_t answer) | |
248 | { | |
249 | PF *callback_; | |
250 | void *cbdata_; | |
251 | debug(28, 3) ("ACLChecklist::checkCallback: answer=%d\n", answer); | |
252 | /* During reconfigure, we can end up not finishing call | |
253 | * sequences into the auth code */ | |
254 | if (auth_user_request) { | |
255 | /* the checklist lock */ | |
256 | authenticateAuthUserRequestUnlock(auth_user_request); | |
257 | /* it might have been connection based */ | |
258 | assert(conn()); | |
259 | conn()->auth_user_request = NULL; | |
260 | conn()->auth_type = AUTH_BROKEN; | |
261 | auth_user_request = NULL; | |
262 | } | |
263 | callback_ = callback; | |
264 | callback = NULL; | |
265 | if (cbdataReferenceValidDone(callback_data, &cbdata_)) | |
266 | callback_(answer, cbdata_); | |
267 | delete this; | |
268 | } | |
269 | bool | |
270 | ACLChecklist::matchAclList(const acl_list * head) | |
271 | { | |
272 | PROF_start(aclMatchAclList); | |
273 | const acl_list *node = head; | |
274 | while (node) { | |
275 | if (!node->matches(this)) { | |
276 | debug(28, 3) ("aclmatchAclList: returning false (AND list entry failed to match)\n"); | |
277 | PROF_stop(aclMatchAclList); | |
278 | return false; | |
279 | } | |
280 | node = node->next; | |
281 | } | |
282 | debug(28, 3) ("aclmatchAclList: returning true (AND list satisfied)\n"); | |
283 | PROF_stop(aclMatchAclList); | |
284 | return true; | |
285 | } | |
286 | ||
287 | CBDATA_CLASS_INIT(ACLChecklist); | |
288 | ||
289 | void * | |
290 | ACLChecklist::operator new (size_t size) | |
291 | { | |
292 | assert (size == sizeof(ACLChecklist)); | |
293 | CBDATA_INIT_TYPE(ACLChecklist); | |
294 | ACLChecklist *result = cbdataAlloc(ACLChecklist); | |
295 | /* Mark result as being owned - we want the refcounter to do the delete | |
296 | * call */ | |
297 | cbdataReference(result); | |
298 | return result; | |
299 | } | |
300 | ||
301 | void | |
302 | ACLChecklist::operator delete (void *address) | |
303 | { | |
304 | ACLChecklist *t = static_cast<ACLChecklist *>(address); | |
305 | cbdataFree(address); | |
306 | /* And allow the memory to be freed */ | |
307 | cbdataReferenceDone (t); | |
308 | } | |
309 | ||
310 | void | |
311 | ACLChecklist::deleteSelf() const | |
312 | { | |
313 | delete this; | |
314 | } | |
315 | ||
316 | ACLChecklist::ACLChecklist() : accessList (NULL), my_port (0), request (NULL), | |
317 | reply (NULL), | |
318 | auth_user_request (NULL) | |
319 | #if SQUID_SNMP | |
320 | ,snmp_community(NULL) | |
321 | #endif | |
322 | , callback (NULL), | |
323 | callback_data (NULL), | |
324 | extacl_entry (NULL), | |
325 | conn_(NULL), | |
326 | async_(false), | |
327 | finished_(false), | |
328 | allow_(ACCESS_DENIED), | |
329 | state_(NullState::Instance()) | |
330 | { | |
331 | memset (&src_addr, '\0', sizeof (struct in_addr)); | |
332 | memset (&dst_addr, '\0', sizeof (struct in_addr)); | |
333 | memset (&my_addr, '\0', sizeof (struct in_addr)); | |
334 | rfc931[0] = '\0'; | |
335 | memset (&state, '\0', sizeof (state)); | |
336 | } | |
337 | ||
338 | ACLChecklist::~ACLChecklist() | |
339 | { | |
340 | assert (!asyncInProgress()); | |
341 | if (extacl_entry) | |
342 | cbdataReferenceDone(extacl_entry); | |
343 | if (request) | |
344 | requestUnlink(request); | |
345 | request = NULL; | |
346 | cbdataReferenceDone(conn_); | |
347 | cbdataReferenceDone(accessList); | |
348 | } | |
349 | ||
350 | ||
351 | ConnStateData * | |
352 | ACLChecklist::conn() | |
353 | { | |
354 | return conn_; | |
355 | } | |
356 | ||
357 | void | |
358 | ACLChecklist::conn(ConnStateData *aConn) | |
359 | { | |
360 | assert (conn() == NULL); | |
361 | conn_ = aConn; | |
362 | } | |
363 | ||
364 | void | |
365 | ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const | |
366 | { | |
367 | checklist->changeState(newState); | |
368 | } | |
369 | ||
370 | ACLChecklist::NullState * | |
371 | ACLChecklist::NullState::Instance() | |
372 | { | |
373 | return &_instance; | |
374 | } | |
375 | ||
376 | void | |
377 | ACLChecklist::NullState::checkForAsync(ACLChecklist *) const | |
378 | { | |
379 | } | |
380 | ||
381 | ACLChecklist::NullState ACLChecklist::NullState::_instance; | |
382 | ||
383 | void | |
384 | ACLChecklist::changeState (AsyncState *newState) | |
385 | { | |
386 | /* only change from null to active and back again, | |
387 | * not active to active. | |
388 | * relax this once conversion to states is complete | |
389 | * RBC 02 2003 | |
390 | */ | |
391 | assert (state_ == NullState::Instance() || newState == NullState::Instance()); | |
392 | state_ = newState; | |
393 | } | |
394 | ||
395 | ACLChecklist::AsyncState * | |
396 | ACLChecklist::asyncState() const | |
397 | { | |
398 | return state_; | |
399 | } | |
400 | ||
401 | void | |
402 | ACLChecklist::nonBlockingCheck(PF * callback_, void *callback_data_) | |
403 | { | |
404 | callback = callback_; | |
405 | callback_data = cbdataReference(callback_data_); | |
406 | check(); | |
407 | } | |
408 |