]>
Commit | Line | Data |
---|---|---|
1d620765 | 1 | |
2 | /* | |
190154cf | 3 | * $Id: authenticate.cc,v 1.63 2003/08/10 11:00:40 robertc Exp $ |
1d620765 | 4 | * |
5 | * DEBUG: section 29 Authenticator | |
e6ccf245 | 6 | * AUTHOR: Robert Collins |
1d620765 | 7 | * |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
1d620765 | 9 | * ---------------------------------------------------------- |
10 | * | |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
1d620765 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
94439e4e | 36 | /* The functions in this file handle authentication. |
37 | * They DO NOT perform access control or auditing. | |
38 | * See acl.c for access control and client_side.c for auditing */ | |
1d620765 | 39 | |
94439e4e | 40 | #include "squid.h" |
e6ccf245 | 41 | #include "authenticate.h" |
8000a965 | 42 | #include "ACL.h" |
a46d2c0e | 43 | #include "client_side.h" |
924f73bc | 44 | #include "HttpReply.h" |
a2ac85d9 | 45 | #include "HttpRequest.h" |
1d620765 | 46 | |
60d096f4 | 47 | CBDATA_TYPE(auth_user_ip_t); |
48 | ||
94439e4e | 49 | /* |
50 | * | |
51 | * Private Data | |
52 | * | |
53 | */ | |
54 | ||
e6ccf245 | 55 | MemPool *AuthUserRequest::pool = NULL; |
56 | MemPool *AuthUserHashPointer::pool = NULL; | |
57 | MemPool *AuthUser::pool = NULL; | |
58 | /* | |
59 | * memDataInit(MEM_AUTH_USER_T, "auth_user_t", | |
60 | * sizeof(auth_user_t), 0); | |
61 | */ | |
94439e4e | 62 | |
63 | /* Generic Functions */ | |
64 | ||
65 | ||
2d72d4fd | 66 | static int |
94439e4e | 67 | authenticateAuthSchemeConfigured(const char *proxy_auth) |
1d620765 | 68 | { |
94439e4e | 69 | authScheme *scheme; |
70 | int i; | |
62e76326 | 71 | |
e6ccf245 | 72 | for (i = 0; i < Config.authConfiguration.n_configured; i++) { |
62e76326 | 73 | scheme = Config.authConfiguration.schemes + i; |
74 | ||
75 | if ((strncasecmp(proxy_auth, scheme->typestr, strlen(scheme->typestr)) == 0) && | |
76 | (authscheme_list[scheme->Id].Active())) | |
77 | return 1; | |
c68e9c6b | 78 | } |
62e76326 | 79 | |
94439e4e | 80 | return 0; |
1d620765 | 81 | } |
82 | ||
94439e4e | 83 | int |
84 | authenticateAuthSchemeId(const char *typestr) | |
1d620765 | 85 | { |
94439e4e | 86 | int i = 0; |
62e76326 | 87 | |
94439e4e | 88 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { |
62e76326 | 89 | if (strncasecmp(typestr, authscheme_list[i].typestr, strlen(authscheme_list[i].typestr)) == 0) { |
90 | return i; | |
91 | } | |
94439e4e | 92 | } |
62e76326 | 93 | |
94439e4e | 94 | return -1; |
1d620765 | 95 | } |
96 | ||
94439e4e | 97 | void |
e6ccf245 | 98 | AuthUserRequest::decodeAuth(const char *proxy_auth) |
1d620765 | 99 | { |
94439e4e | 100 | int i = 0; |
101 | assert(proxy_auth != NULL); | |
94439e4e | 102 | debug(29, 9) ("authenticateDecodeAuth: header = '%s'\n", proxy_auth); |
62e76326 | 103 | |
104 | if (!authenticateAuthSchemeConfigured(proxy_auth) || | |
105 | (i = authenticateAuthSchemeId(proxy_auth)) == -1) { | |
106 | debug(29, 1) ("AuthUserRequest::decodeAuth: Unsupported or unconfigured proxy-auth scheme, '%s'\n", proxy_auth); | |
107 | return; | |
94439e4e | 108 | } |
62e76326 | 109 | |
e6ccf245 | 110 | assert (i >= 0); |
111 | authscheme_list[i].decodeauth(this, proxy_auth); | |
112 | auth_user->auth_module = i + 1; | |
113 | } | |
114 | ||
115 | size_t | |
116 | AuthUserRequest::refCount () const | |
117 | { | |
118 | return references; | |
119 | } | |
120 | ||
121 | char const * | |
122 | AuthUserRequest::username() const | |
123 | { | |
124 | if (auth_user) | |
62e76326 | 125 | return auth_user->username(); |
e6ccf245 | 126 | else |
62e76326 | 127 | return NULL; |
e6ccf245 | 128 | } |
129 | ||
130 | size_t | |
131 | authenticateRequestRefCount (auth_user_request_t *aRequest) | |
132 | { | |
133 | return aRequest->refCount(); | |
1d620765 | 134 | } |
135 | ||
94439e4e | 136 | /* clear any connection related authentication details */ |
137 | void | |
138 | authenticateOnCloseConnection(ConnStateData * conn) | |
139 | { | |
140 | auth_user_request_t *auth_user_request; | |
141 | assert(conn != NULL); | |
62e76326 | 142 | |
94439e4e | 143 | if (conn->auth_user_request != NULL) { |
62e76326 | 144 | auth_user_request = conn->auth_user_request; |
145 | /* if the auth type gets reset, the connection shouldn't | |
146 | * remain linked to it - the next type might not be conn based | |
147 | */ | |
148 | assert(auth_user_request->auth_user->auth_module); | |
149 | ||
150 | if (authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection) { | |
151 | authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection(conn); | |
152 | } | |
94439e4e | 153 | } |
154 | } | |
1d620765 | 155 | |
94439e4e | 156 | /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/ |
1d620765 | 157 | |
94439e4e | 158 | /* send the initial data to an authenticator module */ |
1d620765 | 159 | void |
e6ccf245 | 160 | AuthUserRequest::start(RH * handler, void *data) |
1d620765 | 161 | { |
74addf6c | 162 | assert(handler); |
e6ccf245 | 163 | debug(29, 9) ("authenticateStart: auth_user_request '%p'\n", this); |
62e76326 | 164 | |
e6ccf245 | 165 | if (auth_user->auth_module > 0) |
62e76326 | 166 | authscheme_list[auth_user->auth_module - 1].authStart(this, handler, data); |
94439e4e | 167 | else |
62e76326 | 168 | handler(data, NULL); |
94439e4e | 169 | } |
170 | ||
e6ccf245 | 171 | void |
172 | authenticateStart(auth_user_request_t * auth_user_request, RH * handler, void *data) | |
173 | { | |
174 | assert(auth_user_request); | |
175 | auth_user_request->start (handler, data); | |
176 | } | |
177 | ||
94439e4e | 178 | /* |
179 | * Check a auth_user pointer for validity. Does not check passwords, just data | |
180 | * sensability. Broken or Unknown auth_types are not valid for use... | |
181 | */ | |
182 | ||
183 | int | |
184 | authenticateValidateUser(auth_user_request_t * auth_user_request) | |
185 | { | |
5dae8514 | 186 | debug(29, 9) ("authenticateValidateUser: Validating Auth_user request '%p'.\n", auth_user_request); |
62e76326 | 187 | |
94439e4e | 188 | if (auth_user_request == NULL) { |
62e76326 | 189 | debug(29, 4) ("authenticateValidateUser: Auth_user_request was NULL!\n"); |
190 | return 0; | |
1d620765 | 191 | } |
62e76326 | 192 | |
94439e4e | 193 | if (auth_user_request->auth_user == NULL) { |
62e76326 | 194 | debug(29, 4) ("authenticateValidateUser: No associated auth_user structure\n"); |
195 | return 0; | |
94439e4e | 196 | } |
62e76326 | 197 | |
94439e4e | 198 | if (auth_user_request->auth_user->auth_type == AUTH_UNKNOWN) { |
62e76326 | 199 | debug(29, 4) ("authenticateValidateUser: Auth_user '%p' uses unknown scheme.\n", auth_user_request->auth_user); |
200 | return 0; | |
94439e4e | 201 | } |
62e76326 | 202 | |
94439e4e | 203 | if (auth_user_request->auth_user->auth_type == AUTH_BROKEN) { |
62e76326 | 204 | debug(29, 4) ("authenticateValidateUser: Auth_user '%p' is broken for it's scheme.\n", auth_user_request->auth_user); |
205 | return 0; | |
94439e4e | 206 | } |
62e76326 | 207 | |
721b0310 | 208 | if (!auth_user_request->auth_user->scheme_data) { |
62e76326 | 209 | debug(29, 4) ("authenticateValidateUser: auth_user '%p' has no scheme data\n", auth_user_request->auth_user); |
210 | return 0; | |
721b0310 | 211 | } |
62e76326 | 212 | |
94439e4e | 213 | /* any other sanity checks that we need in the future */ |
214 | ||
215 | /* Thus should a module call to something like authValidate */ | |
216 | ||
217 | /* finally return ok */ | |
721b0310 | 218 | debug(29, 5) ("authenticateValidateUser: Validated Auth_user request '%p'.\n", auth_user_request); |
62e76326 | 219 | |
94439e4e | 220 | return 1; |
221 | ||
222 | } | |
223 | ||
e6ccf245 | 224 | void * |
768cb287 | 225 | AuthUser::operator new (size_t byteCount) |
e6ccf245 | 226 | { |
227 | /* derived classes with different sizes must implement their own new */ | |
228 | assert (byteCount == sizeof (AuthUser)); | |
62e76326 | 229 | |
e6ccf245 | 230 | if (!pool) |
62e76326 | 231 | pool = memPoolCreate("Authenticate User Data", sizeof (auth_user_t)); |
232 | ||
d3b3ab85 | 233 | return memPoolAlloc(pool); |
e6ccf245 | 234 | } |
235 | ||
236 | AuthUser::AuthUser (const char *scheme) : | |
62e76326 | 237 | auth_type (AUTH_UNKNOWN), auth_module (authenticateAuthSchemeId(scheme) + 1), |
238 | usernamehash (NULL), ipcount (0), expiretime (0), references (0), scheme_data (NULL) | |
e6ccf245 | 239 | { |
240 | proxy_auth_list.head = proxy_auth_list.tail = NULL; | |
241 | proxy_match_cache.head = proxy_match_cache.tail = NULL; | |
242 | ip_list.head = ip_list.tail = NULL; | |
243 | requests.head = requests.tail = NULL; | |
244 | } | |
245 | ||
246 | char const * | |
247 | AuthUser::username () const | |
248 | { | |
249 | if (auth_module <= 0) | |
62e76326 | 250 | return NULL; |
251 | ||
e6ccf245 | 252 | return authscheme_list[auth_module - 1].authUserUsername(this); |
253 | } | |
254 | ||
94439e4e | 255 | auth_user_t * |
256 | authenticateAuthUserNew(const char *scheme) | |
257 | { | |
e6ccf245 | 258 | return new AuthUser (scheme); |
94439e4e | 259 | } |
260 | ||
e6ccf245 | 261 | void * |
768cb287 | 262 | AuthUserRequest::operator new (size_t byteCount) |
94439e4e | 263 | { |
e6ccf245 | 264 | /* derived classes with different sizes must implement their own new */ |
265 | assert (byteCount == sizeof (AuthUserRequest)); | |
62e76326 | 266 | |
e6ccf245 | 267 | if (!pool) |
62e76326 | 268 | pool = memPoolCreate("Authenticate Request Data", sizeof(auth_user_request_t)); |
269 | ||
e6ccf245 | 270 | return static_cast<auth_user_request_t *>(memPoolAlloc(pool)); |
1d620765 | 271 | } |
272 | ||
e6ccf245 | 273 | void |
274 | AuthUserRequest::operator delete (void *address) | |
275 | { | |
276 | memPoolFree(pool, address); | |
277 | } | |
278 | ||
82b045dc | 279 | AuthUserRequest::AuthUserRequest():auth_user(NULL), message(NULL), |
280 | references (0), lastReply (AUTH_ACL_CANNOT_AUTHENTICATE), state_ (NULL) | |
62e76326 | 281 | {} |
282 | ||
e6ccf245 | 283 | AuthUserRequest::~AuthUserRequest() |
1d620765 | 284 | { |
94439e4e | 285 | dlink_node *link; |
e6ccf245 | 286 | debug(29, 5) ("AuthUserRequest::~AuthUserRequest: freeing request %p\n", this); |
287 | assert(references == 0); | |
62e76326 | 288 | |
e6ccf245 | 289 | if (auth_user) { |
82b045dc | 290 | if (state() != NULL) { |
62e76326 | 291 | /* we MUST know the module */ |
292 | assert(auth_user->auth_module > 0); | |
82b045dc | 293 | |
294 | if (authscheme_list[auth_user->auth_module - 1].requestFree != NULL) | |
295 | authscheme_list[auth_user->auth_module - 1].requestFree(this); | |
296 | else { | |
00d77d6b | 297 | delete state(); |
82b045dc | 298 | state(NULL); |
299 | } | |
62e76326 | 300 | } |
301 | ||
302 | /* unlink from the auth_user struct */ | |
303 | link = auth_user->requests.head; | |
304 | ||
305 | while (link && (link->data != this)) | |
306 | link = link->next; | |
307 | ||
308 | assert(link != NULL); | |
309 | ||
310 | dlinkDelete(link, &auth_user->requests); | |
311 | ||
312 | dlinkNodeDelete(link); | |
313 | ||
314 | /* unlock the request structure's lock */ | |
315 | authenticateAuthUserUnlock(auth_user); | |
316 | ||
317 | auth_user = NULL; | |
94439e4e | 318 | } else |
82b045dc | 319 | assert(state() == NULL); |
62e76326 | 320 | |
e6ccf245 | 321 | safe_free (message); |
94439e4e | 322 | } |
323 | ||
e6ccf245 | 324 | void |
325 | AuthUserRequest::setDenyMessage (char const *aString) | |
326 | { | |
327 | safe_free (message); | |
328 | message = xstrdup (aString); | |
329 | } | |
330 | ||
331 | char const * | |
332 | AuthUserRequest::getDenyMessage () | |
333 | { | |
334 | return message; | |
335 | } | |
336 | ||
337 | char const * | |
94439e4e | 338 | authenticateAuthUserRequestMessage(auth_user_request_t * auth_user_request) |
339 | { | |
340 | if (auth_user_request) | |
62e76326 | 341 | return auth_user_request->getDenyMessage(); |
342 | ||
94439e4e | 343 | return NULL; |
344 | } | |
345 | ||
e6ccf245 | 346 | void |
347 | authenticateSetDenyMessage (auth_user_request_t * auth_user_request, char const *message) | |
348 | { | |
349 | auth_user_request->setDenyMessage (message); | |
350 | } | |
351 | ||
64f904ee | 352 | static void |
62e76326 | 353 | |
94439e4e | 354 | authenticateAuthUserRequestSetIp(auth_user_request_t * auth_user_request, struct in_addr ipaddr) |
355 | { | |
60d096f4 | 356 | auth_user_ip_t *ipdata, *tempnode; |
357 | auth_user_t *auth_user; | |
358 | char *ip1; | |
d57a7893 | 359 | int found = 0; |
60d096f4 | 360 | CBDATA_INIT_TYPE(auth_user_ip_t); |
62e76326 | 361 | |
60d096f4 | 362 | if (!auth_user_request->auth_user) |
62e76326 | 363 | return; |
364 | ||
60d096f4 | 365 | auth_user = auth_user_request->auth_user; |
62e76326 | 366 | |
60d096f4 | 367 | ipdata = (auth_user_ip_t *) auth_user->ip_list.head; |
62e76326 | 368 | |
d57a7893 | 369 | /* |
370 | * we walk the entire list to prevent the first item in the list | |
371 | * preventing old entries being flushed and locking a user out after | |
372 | * a timeout+reconfigure | |
60d096f4 | 373 | */ |
62e76326 | 374 | while (ipdata) |
375 | { | |
376 | tempnode = (auth_user_ip_t *) ipdata->node.next; | |
377 | /* walk the ip list */ | |
378 | ||
379 | if (ipdata->ipaddr.s_addr == ipaddr.s_addr) { | |
380 | /* This ip has alreadu been seen. */ | |
381 | found = 1; | |
382 | /* update IP ttl */ | |
383 | ipdata->ip_expiretime = squid_curtime; | |
384 | } else if (ipdata->ip_expiretime + Config.authenticateIpTTL < squid_curtime) { | |
385 | /* This IP has expired - remove from the seen list */ | |
386 | dlinkDelete(&ipdata->node, &auth_user->ip_list); | |
387 | cbdataFree(ipdata); | |
388 | /* catch incipient underflow */ | |
389 | assert(auth_user->ipcount); | |
390 | auth_user->ipcount--; | |
391 | } | |
392 | ||
393 | ipdata = tempnode; | |
60d096f4 | 394 | } |
395 | ||
a51989a0 | 396 | if (found) |
62e76326 | 397 | return; |
d57a7893 | 398 | |
60d096f4 | 399 | /* This ip is not in the seen list */ |
400 | ipdata = cbdataAlloc(auth_user_ip_t); | |
62e76326 | 401 | |
60d096f4 | 402 | ipdata->ip_expiretime = squid_curtime; |
62e76326 | 403 | |
60d096f4 | 404 | ipdata->ipaddr = ipaddr; |
62e76326 | 405 | |
60d096f4 | 406 | dlinkAddTail(ipdata, &ipdata->node, &auth_user->ip_list); |
62e76326 | 407 | |
60d096f4 | 408 | auth_user->ipcount++; |
409 | ||
410 | ip1 = xstrdup(inet_ntoa(ipaddr)); | |
62e76326 | 411 | |
d09176e1 | 412 | debug(29, 2) ("authenticateAuthUserRequestSetIp: user '%s' has been seen at a new IP address (%s)\n", auth_user->username(), ip1); |
62e76326 | 413 | |
60d096f4 | 414 | safe_free(ip1); |
415 | } | |
416 | ||
417 | void | |
62e76326 | 418 | |
60d096f4 | 419 | authenticateAuthUserRequestRemoveIp(auth_user_request_t * auth_user_request, struct in_addr ipaddr) |
420 | { | |
421 | auth_user_ip_t *ipdata; | |
422 | auth_user_t *auth_user; | |
62e76326 | 423 | |
60d096f4 | 424 | if (!auth_user_request->auth_user) |
62e76326 | 425 | return; |
426 | ||
60d096f4 | 427 | auth_user = auth_user_request->auth_user; |
62e76326 | 428 | |
60d096f4 | 429 | ipdata = (auth_user_ip_t *) auth_user->ip_list.head; |
62e76326 | 430 | |
431 | while (ipdata) | |
432 | { | |
433 | /* walk the ip list */ | |
434 | ||
435 | if (ipdata->ipaddr.s_addr == ipaddr.s_addr) { | |
436 | /* remove the node */ | |
437 | dlinkDelete(&ipdata->node, &auth_user->ip_list); | |
438 | cbdataFree(ipdata); | |
439 | /* catch incipient underflow */ | |
440 | assert(auth_user->ipcount); | |
441 | auth_user->ipcount--; | |
442 | return; | |
443 | } | |
444 | ||
445 | ipdata = (auth_user_ip_t *) ipdata->node.next; | |
60d096f4 | 446 | } |
447 | ||
94439e4e | 448 | } |
449 | ||
60d096f4 | 450 | static void |
451 | authenticateAuthUserClearIp(auth_user_t * auth_user) | |
452 | { | |
453 | auth_user_ip_t *ipdata, *tempnode; | |
62e76326 | 454 | |
60d096f4 | 455 | if (!auth_user) |
62e76326 | 456 | return; |
457 | ||
60d096f4 | 458 | ipdata = (auth_user_ip_t *) auth_user->ip_list.head; |
62e76326 | 459 | |
60d096f4 | 460 | while (ipdata) { |
62e76326 | 461 | tempnode = (auth_user_ip_t *) ipdata->node.next; |
462 | /* walk the ip list */ | |
463 | dlinkDelete(&ipdata->node, &auth_user->ip_list); | |
464 | cbdataFree(ipdata); | |
465 | /* catch incipient underflow */ | |
466 | assert(auth_user->ipcount); | |
467 | auth_user->ipcount--; | |
468 | ipdata = tempnode; | |
60d096f4 | 469 | } |
62e76326 | 470 | |
60d096f4 | 471 | /* integrity check */ |
472 | assert(auth_user->ipcount == 0); | |
473 | } | |
474 | ||
475 | ||
476 | void | |
477 | authenticateAuthUserRequestClearIp(auth_user_request_t * auth_user_request) | |
478 | { | |
479 | if (auth_user_request) | |
62e76326 | 480 | authenticateAuthUserClearIp(auth_user_request->auth_user); |
60d096f4 | 481 | } |
482 | ||
483 | size_t | |
484 | authenticateAuthUserRequestIPCount(auth_user_request_t * auth_user_request) | |
485 | { | |
486 | assert(auth_user_request); | |
487 | assert(auth_user_request->auth_user); | |
488 | return auth_user_request->auth_user->ipcount; | |
489 | } | |
490 | ||
491 | ||
94439e4e | 492 | /* Get Auth User: Return a filled out auth_user structure for the given |
493 | * Proxy Auth (or Auth) header. It may be a cached Auth User or a new | |
494 | * Unauthenticated structure. The structure is given an inital lock here. | |
495 | */ | |
e6ccf245 | 496 | auth_user_request_t * |
497 | AuthUserRequest::createAuthUser(const char *proxy_auth) | |
94439e4e | 498 | { |
e6ccf245 | 499 | auth_user_request_t *result = new auth_user_request_t; |
94439e4e | 500 | /* and lock for the callers instance */ |
62e76326 | 501 | |
502 | result->lock() | |
503 | ||
504 | ; | |
60d096f4 | 505 | /* The scheme is allowed to provide a cached auth_user or a new one */ |
e6ccf245 | 506 | result->decodeAuth(proxy_auth); |
62e76326 | 507 | |
e6ccf245 | 508 | return result; |
94439e4e | 509 | } |
510 | ||
511 | /* | |
512 | * authenticateUserAuthenticated: is this auth_user structure logged in ? | |
513 | */ | |
514 | int | |
515 | authenticateUserAuthenticated(auth_user_request_t * auth_user_request) | |
516 | { | |
6437ac71 | 517 | if (!authenticateValidateUser(auth_user_request)) |
62e76326 | 518 | return 0; |
519 | ||
82b045dc | 520 | if (auth_user_request->auth_user->auth_module > 0) { |
521 | /* legacy interface */ | |
522 | ||
523 | if (authscheme_list[auth_user_request->auth_user->auth_module - 1].authenticated) | |
524 | return authscheme_list[auth_user_request->auth_user->auth_module - 1].authenticated(auth_user_request); | |
525 | else { | |
526 | /* state interface */ | |
527 | assert (auth_user_request->state()); | |
528 | return auth_user_request->state()->authenticated(); | |
529 | } | |
530 | } else | |
62e76326 | 531 | return 0; |
94439e4e | 532 | } |
533 | ||
534 | /* | |
60d096f4 | 535 | * authenticateAuthenticateUser: call the module specific code to |
536 | * log this user request in. | |
94439e4e | 537 | * Cache hits may change the auth_user pointer in the structure if needed. |
538 | * This is basically a handle approach. | |
539 | */ | |
60d096f4 | 540 | static void |
190154cf | 541 | authenticateAuthenticateUser(auth_user_request_t * auth_user_request, HttpRequest * request, ConnStateData::Pointer &conn, http_hdr_type type) |
94439e4e | 542 | { |
543 | assert(auth_user_request != NULL); | |
62e76326 | 544 | |
82b045dc | 545 | if (auth_user_request->auth_user->auth_module > 0) { |
546 | if (authscheme_list[auth_user_request->auth_user->auth_module - 1].authAuthenticate) | |
547 | authscheme_list[auth_user_request->auth_user->auth_module - 1].authAuthenticate(auth_user_request, request, conn, type); | |
548 | else { | |
549 | assert (auth_user_request->state()); | |
550 | auth_user_request->state()->authenticate(request, conn, type); | |
551 | } | |
552 | } | |
94439e4e | 553 | } |
554 | ||
b4504bc8 | 555 | static auth_user_request_t * |
a2ac85d9 | 556 | authTryGetUser (auth_user_request_t **auth_user_request, ConnStateData::Pointer & conn) |
b4504bc8 | 557 | { |
558 | if (*auth_user_request) | |
62e76326 | 559 | return *auth_user_request; |
a2ac85d9 | 560 | else if (conn.getRaw() != NULL) |
62e76326 | 561 | return conn->auth_user_request; |
b4504bc8 | 562 | else |
62e76326 | 563 | return NULL; |
b4504bc8 | 564 | } |
62e76326 | 565 | |
60d096f4 | 566 | /* returns one of |
567 | * AUTH_ACL_CHALLENGE, | |
568 | * AUTH_ACL_HELPER, | |
569 | * AUTH_ACL_CANNOT_AUTHENTICATE, | |
570 | * AUTH_AUTHENTICATED | |
571 | * | |
572 | * How to use: In your proxy-auth dependent acl code, use the following | |
573 | * construct: | |
574 | * int rv; | |
575 | * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED) | |
576 | * return rv; | |
577 | * | |
578 | * when this code is reached, the request/connection is authenticated. | |
579 | * | |
580 | * if you have non-acl code, but want to force authentication, you need a | |
581 | * callback mechanism like the acl testing routines that will send a 40[1|7] to | |
582 | * the client when rv==AUTH_ACL_CHALLENGE, and will communicate with | |
583 | * the authenticateStart routine for rv==AUTH_ACL_HELPER | |
584 | */ | |
585 | auth_acl_t | |
62e76326 | 586 | |
190154cf | 587 | AuthUserRequest::authenticate(auth_user_request_t ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData::Pointer conn, struct in_addr src_addr) |
60d096f4 | 588 | { |
589 | const char *proxy_auth; | |
590 | assert(headertype != 0); | |
0e3be1ea | 591 | |
60d096f4 | 592 | proxy_auth = httpHeaderGetStr(&request->header, headertype); |
593 | ||
60d096f4 | 594 | /* |
595 | * a note on proxy_auth logix here: | |
aa387023 | 596 | * proxy_auth==NULL -> unauthenticated request || already |
597 | * authenticated connection so we test for an authenticated | |
598 | * connection when we recieve no authentication header. | |
60d096f4 | 599 | */ |
62e76326 | 600 | |
b4504bc8 | 601 | if (((proxy_auth == NULL) && (!authenticateUserAuthenticated(authTryGetUser(auth_user_request,conn)))) |
a2ac85d9 | 602 | || (conn.getRaw() != NULL && conn->auth_type == AUTH_BROKEN)) |
62e76326 | 603 | { |
604 | /* no header or authentication failed/got corrupted - restart */ | |
605 | debug(28, 4) ("authenticateAuthenticate: broken auth or no proxy_auth header. Requesting auth header.\n"); | |
606 | /* something wrong with the AUTH credentials. Force a new attempt */ | |
607 | ||
a2ac85d9 | 608 | if (conn.getRaw() != NULL) { |
62e76326 | 609 | conn->auth_type = AUTH_UNKNOWN; |
610 | ||
611 | if (conn->auth_user_request) | |
612 | conn->auth_user_request->unlock(); | |
613 | ||
614 | conn->auth_user_request = NULL; | |
615 | } | |
616 | ||
617 | if (*auth_user_request) { | |
618 | /* unlock the ACL lock */ | |
619 | (*auth_user_request)->unlock(); | |
620 | auth_user_request = NULL; | |
621 | } | |
622 | ||
623 | return AUTH_ACL_CHALLENGE; | |
60d096f4 | 624 | } |
62e76326 | 625 | |
626 | /* | |
60d096f4 | 627 | * Is this an already authenticated connection with a new auth header? |
628 | * No check for function required in the if: its compulsory for conn based | |
629 | * auth modules | |
630 | */ | |
a2ac85d9 | 631 | if (proxy_auth && conn.getRaw() != NULL && conn->auth_user_request && |
62e76326 | 632 | authenticateUserAuthenticated(conn->auth_user_request) && |
633 | strcmp(proxy_auth, authscheme_list[conn->auth_user_request->auth_user->auth_module - 1].authConnLastHeader(conn->auth_user_request))) | |
634 | { | |
635 | debug(28, 2) ("authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU %p, Current user '%s' proxy_auth %s\n", conn->auth_user_request, conn->auth_user_request->username(), proxy_auth); | |
636 | /* remove this request struct - the link is already authed and it can't be to | |
637 | * reauth. | |
638 | */ | |
639 | ||
640 | /* This should _only_ ever occur on the first pass through | |
641 | * authenticateAuthenticate | |
642 | */ | |
643 | assert(*auth_user_request == NULL); | |
644 | /* unlock the conn lock on the auth_user_request */ | |
645 | conn->auth_user_request->unlock(); | |
646 | /* mark the conn as non-authed. */ | |
647 | conn->auth_user_request = NULL; | |
648 | /* Set the connection auth type */ | |
649 | conn->auth_type = AUTH_UNKNOWN; | |
60d096f4 | 650 | } |
62e76326 | 651 | |
60d096f4 | 652 | /* we have a proxy auth header and as far as we know this connection has |
653 | * not had bungled connection oriented authentication happen on it. */ | |
2c3de963 | 654 | debug(28, 9) ("authenticateAuthenticate: header %s.\n", proxy_auth ? proxy_auth : "-"); |
62e76326 | 655 | |
656 | if (*auth_user_request == NULL) | |
657 | { | |
658 | debug(28, 9) ("authenticateAuthenticate: This is a new checklist test on FD:%d\n", | |
a2ac85d9 | 659 | conn.getRaw() != NULL ? conn->fd : -1); |
62e76326 | 660 | |
661 | if ((!request->auth_user_request) | |
a2ac85d9 | 662 | && (conn.getRaw() == NULL || conn->auth_type == AUTH_UNKNOWN)) { |
62e76326 | 663 | /* beginning of a new request check */ |
664 | debug(28, 4) ("authenticateAuthenticate: no connection authentication type\n"); | |
665 | ||
666 | if (!authenticateValidateUser(*auth_user_request = | |
667 | createAuthUser(proxy_auth))) { | |
668 | /* the decode might have left a username for logging, or a message to | |
669 | * the user */ | |
670 | ||
671 | if ((*auth_user_request)->username()) { | |
672 | /* lock the user for the request structure link */ | |
673 | ||
674 | (*auth_user_request)->lock() | |
675 | ||
676 | ; | |
677 | request->auth_user_request = *auth_user_request; | |
678 | } | |
679 | ||
680 | /* unlock the ACL reference granted by ...createAuthUser. */ | |
681 | (*auth_user_request)->unlock(); | |
682 | ||
683 | *auth_user_request = NULL; | |
684 | ||
685 | return AUTH_ACL_CHALLENGE; | |
686 | } | |
687 | ||
688 | /* the user_request comes prelocked for the caller to createAuthUser (us) */ | |
689 | } else if (request->auth_user_request) { | |
690 | *auth_user_request = request->auth_user_request; | |
691 | /* lock the user request for this ACL processing */ | |
692 | ||
693 | (*auth_user_request)->lock() | |
694 | ||
695 | ; | |
696 | } else { | |
a2ac85d9 | 697 | assert (conn.getRaw() != NULL); |
62e76326 | 698 | |
699 | if (conn->auth_user_request != NULL) { | |
700 | *auth_user_request = conn->auth_user_request; | |
701 | /* lock the user request for this ACL processing */ | |
702 | ||
703 | (*auth_user_request)->lock() | |
704 | ||
705 | ; | |
706 | } else { | |
707 | /* failed connection based authentication */ | |
708 | debug(28, 4) ("authenticateAuthenticate: Auth user request %p conn-auth user request %p conn type %d authentication failed.\n", | |
709 | *auth_user_request, conn->auth_user_request, conn->auth_type); | |
710 | (*auth_user_request)->unlock(); | |
711 | *auth_user_request = NULL; | |
712 | return AUTH_ACL_CHALLENGE; | |
713 | } | |
714 | } | |
60d096f4 | 715 | } |
62e76326 | 716 | |
717 | if (!authenticateUserAuthenticated(*auth_user_request)) | |
718 | { | |
719 | /* User not logged in. Log them in */ | |
720 | authenticateAuthenticateUser(*auth_user_request, request, | |
721 | conn, headertype); | |
722 | ||
723 | switch (authenticateDirection(*auth_user_request)) { | |
724 | ||
725 | case 1: | |
726 | ||
f5292c64 | 727 | if (!request->auth_user_request) { |
728 | ||
729 | (*auth_user_request)->lock() | |
730 | ||
731 | ; | |
732 | request->auth_user_request = *auth_user_request; | |
733 | } | |
734 | ||
735 | /* fallthrough to -2 */ | |
736 | ||
62e76326 | 737 | case -2: |
738 | /* this ACL check is finished. Unlock. */ | |
739 | (*auth_user_request)->unlock(); | |
f5292c64 | 740 | |
62e76326 | 741 | *auth_user_request = NULL; |
f5292c64 | 742 | |
62e76326 | 743 | return AUTH_ACL_CHALLENGE; |
744 | ||
745 | case -1: | |
746 | /* we are partway through authentication within squid, | |
747 | * the *auth_user_request variables stores the auth_user_request | |
748 | * for the callback to here - Do not Unlock */ | |
749 | return AUTH_ACL_HELPER; | |
750 | } | |
751 | ||
752 | /* on 0 the authentication is finished - fallthrough */ | |
753 | /* See if user authentication failed for some reason */ | |
754 | if (!authenticateUserAuthenticated(*auth_user_request)) { | |
755 | if ((*auth_user_request)->username()) { | |
756 | if (!request->auth_user_request) { | |
757 | /* lock the user for the request structure link */ | |
758 | ||
759 | (*auth_user_request)->lock() | |
760 | ||
761 | ; | |
762 | request->auth_user_request = *auth_user_request; | |
763 | } | |
764 | } | |
765 | ||
766 | /* this ACL check is finished. Unlock. */ | |
767 | (*auth_user_request)->unlock(); | |
768 | ||
769 | *auth_user_request = NULL; | |
770 | ||
771 | return AUTH_ACL_CHALLENGE; | |
772 | } | |
60d096f4 | 773 | } |
62e76326 | 774 | |
60d096f4 | 775 | /* copy username to request for logging on client-side */ |
776 | /* the credentials are correct at this point */ | |
62e76326 | 777 | if (!request->auth_user_request) |
778 | { | |
779 | /* lock the user for the request structure link */ | |
780 | ||
781 | (*auth_user_request)->lock() | |
782 | ||
783 | ; | |
784 | request->auth_user_request = *auth_user_request; | |
785 | ||
786 | authenticateAuthUserRequestSetIp(*auth_user_request, src_addr); | |
60d096f4 | 787 | } |
62e76326 | 788 | |
60d096f4 | 789 | /* Unlock the request - we've authenticated it */ |
e6ccf245 | 790 | (*auth_user_request)->unlock(); |
62e76326 | 791 | |
60d096f4 | 792 | return AUTH_AUTHENTICATED; |
793 | } | |
794 | ||
0e3be1ea | 795 | auth_acl_t |
62e76326 | 796 | |
190154cf | 797 | AuthUserRequest::tryToAuthenticateAndSetAuthUser(auth_user_request_t ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData::Pointer conn, struct in_addr src_addr) |
0e3be1ea | 798 | { |
799 | /* If we have already been called, return the cached value */ | |
b4504bc8 | 800 | auth_user_request_t *t = authTryGetUser (auth_user_request, conn); |
62e76326 | 801 | |
0e3be1ea | 802 | if (t && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE |
62e76326 | 803 | && t->lastReply != AUTH_ACL_HELPER) |
804 | { | |
805 | if (!*auth_user_request) | |
806 | *auth_user_request = t; | |
807 | ||
808 | return t->lastReply; | |
0e3be1ea | 809 | } |
62e76326 | 810 | |
0e3be1ea | 811 | /* ok, call the actual authenticator routine. */ |
b4504bc8 | 812 | auth_acl_t result = authenticate(auth_user_request, headertype, request, conn, src_addr); |
62e76326 | 813 | |
b4504bc8 | 814 | t = authTryGetUser (auth_user_request, conn); |
62e76326 | 815 | |
0e3be1ea | 816 | if (t && result != AUTH_ACL_CANNOT_AUTHENTICATE && |
62e76326 | 817 | result != AUTH_ACL_HELPER) |
818 | t->lastReply = result; | |
819 | ||
0e3be1ea | 820 | return result; |
821 | } | |
822 | ||
e6ccf245 | 823 | auth_acl_t |
62e76326 | 824 | |
190154cf | 825 | authenticateTryToAuthenticateAndSetAuthUser(auth_user_request_t ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData::Pointer conn, struct in_addr src_addr) |
94439e4e | 826 | { |
e6ccf245 | 827 | return AuthUserRequest::tryToAuthenticateAndSetAuthUser (auth_user_request, headertype,request, conn, src_addr); |
94439e4e | 828 | } |
829 | ||
830 | /* authenticateUserRequestUsername: return a pointer to the username in the */ | |
e6ccf245 | 831 | char const * |
94439e4e | 832 | authenticateUserRequestUsername(auth_user_request_t * auth_user_request) |
833 | { | |
834 | assert(auth_user_request != NULL); | |
e6ccf245 | 835 | return auth_user_request->username(); |
94439e4e | 836 | } |
837 | ||
838 | /* returns | |
839 | * 0: no output needed | |
840 | * 1: send to client | |
841 | * -1: send to helper | |
842 | * -2: authenticate broken in some fashion | |
843 | */ | |
844 | int | |
845 | authenticateDirection(auth_user_request_t * auth_user_request) | |
846 | { | |
847 | if (!auth_user_request) | |
62e76326 | 848 | return -2; |
849 | ||
94439e4e | 850 | if (authenticateUserAuthenticated(auth_user_request)) |
62e76326 | 851 | return 0; |
852 | ||
82b045dc | 853 | if (auth_user_request->auth_user->auth_module > 0) { |
854 | if (authscheme_list[auth_user_request->auth_user->auth_module - 1].getdirection) | |
855 | return authscheme_list[auth_user_request->auth_user->auth_module - 1].getdirection(auth_user_request); | |
856 | else { | |
857 | assert (auth_user_request->state()); | |
858 | return auth_user_request->state()->direction(); | |
859 | } | |
860 | } | |
62e76326 | 861 | |
94439e4e | 862 | return -2; |
863 | } | |
864 | ||
865 | int | |
2d72d4fd | 866 | authenticateActiveSchemeCount(void) |
94439e4e | 867 | { |
868 | int i = 0, rv = 0; | |
62e76326 | 869 | |
94439e4e | 870 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) |
62e76326 | 871 | if (authscheme_list[i].configured()) |
872 | rv++; | |
873 | ||
94439e4e | 874 | debug(29, 9) ("authenticateActiveSchemeCount: %d active.\n", rv); |
62e76326 | 875 | |
94439e4e | 876 | return rv; |
877 | } | |
878 | ||
879 | int | |
2d72d4fd | 880 | authenticateSchemeCount(void) |
94439e4e | 881 | { |
882 | int i = 0, rv = 0; | |
62e76326 | 883 | |
94439e4e | 884 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) |
62e76326 | 885 | rv++; |
886 | ||
94439e4e | 887 | debug(29, 9) ("authenticateSchemeCount: %d active.\n", rv); |
62e76326 | 888 | |
94439e4e | 889 | return rv; |
890 | } | |
891 | ||
892 | void | |
893 | authenticateSchemeInit(void) | |
894 | { | |
895 | authSchemeSetup(); | |
896 | } | |
897 | ||
898 | void | |
899 | authenticateInit(authConfig * config) | |
900 | { | |
901 | int i; | |
902 | authScheme *scheme; | |
62e76326 | 903 | |
94439e4e | 904 | for (i = 0; i < config->n_configured; i++) { |
62e76326 | 905 | scheme = config->schemes + i; |
906 | ||
907 | if (authscheme_list[scheme->Id].init && authscheme_list[scheme->Id].configured()) { | |
908 | authscheme_list[scheme->Id].init(scheme); | |
909 | } | |
1d620765 | 910 | } |
62e76326 | 911 | |
94439e4e | 912 | if (!proxy_auth_username_cache) |
62e76326 | 913 | AuthUser::cacheInit(); |
914 | else | |
915 | AuthUser::CachedACLsReset(); | |
c623f072 | 916 | } |
917 | ||
1d620765 | 918 | void |
74addf6c | 919 | authenticateShutdown(void) |
1d620765 | 920 | { |
94439e4e | 921 | int i; |
922 | debug(29, 2) ("authenticateShutdown: shutting down auth schemes\n"); | |
c623f072 | 923 | /* free the cache if we are shutting down */ |
62e76326 | 924 | |
c623f072 | 925 | if (shutting_down) |
62e76326 | 926 | hashFreeItems(proxy_auth_username_cache, AuthUserHashPointer::removeFromCache); |
c623f072 | 927 | |
94439e4e | 928 | /* find the currently known authscheme types */ |
929 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { | |
62e76326 | 930 | if (authscheme_list[i].donefunc != NULL) |
931 | authscheme_list[i].donefunc(); | |
932 | else | |
933 | debug(29, 2) ("authenticateShutdown: scheme %s has not registered a shutdown function.\n", authscheme_list[i].typestr); | |
934 | ||
935 | if (shutting_down) | |
936 | authscheme_list[i].typestr = NULL; | |
94439e4e | 937 | } |
938 | } | |
939 | ||
940 | void | |
190154cf | 941 | AuthUserRequest::addReplyAuthHeader(HttpReply * rep, auth_user_request_t * auth_user_request, HttpRequest * request, int accelerated, int internal) |
94439e4e | 942 | /* send the auth types we are configured to support (and have compiled in!) */ |
943 | { | |
e6ccf245 | 944 | http_hdr_type type; |
62e76326 | 945 | |
946 | switch (rep->sline.status) | |
947 | { | |
948 | ||
94439e4e | 949 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: |
62e76326 | 950 | /* Proxy authorisation needed */ |
951 | type = HDR_PROXY_AUTHENTICATE; | |
952 | break; | |
953 | ||
94439e4e | 954 | case HTTP_UNAUTHORIZED: |
62e76326 | 955 | /* WWW Authorisation needed */ |
956 | type = HDR_WWW_AUTHENTICATE; | |
957 | break; | |
958 | ||
94439e4e | 959 | default: |
62e76326 | 960 | /* Keep GCC happy */ |
961 | /* some other HTTP status */ | |
962 | type = HDR_ENUM_END; | |
963 | break; | |
94439e4e | 964 | } |
62e76326 | 965 | |
5dae8514 | 966 | debug(29, 9) ("authenticateFixHeader: headertype:%d authuser:%p\n", type, auth_user_request); |
62e76326 | 967 | |
721b0310 | 968 | if (((rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) |
62e76326 | 969 | || (rep->sline.status == HTTP_UNAUTHORIZED)) && internal) |
970 | /* this is a authenticate-needed response */ | |
94439e4e | 971 | { |
62e76326 | 972 | |
973 | if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0) & !authenticateUserAuthenticated(auth_user_request)) | |
974 | authscheme_list[auth_user_request->auth_user->auth_module - 1].authFixHeader(auth_user_request, rep, type, request); | |
975 | else | |
976 | { | |
977 | int i; | |
978 | authScheme *scheme; | |
979 | /* call each configured & running authscheme */ | |
980 | ||
981 | for (i = 0; i < Config.authConfiguration.n_configured; i++) { | |
982 | scheme = Config.authConfiguration.schemes + i; | |
983 | ||
984 | if (authscheme_list[scheme->Id].Active()) | |
985 | authscheme_list[scheme->Id].authFixHeader(NULL, rep, type, | |
986 | request); | |
987 | else | |
988 | debug(29, 4) ("authenticateFixHeader: Configured scheme %s not Active\n", scheme->typestr); | |
989 | } | |
990 | } | |
991 | ||
94439e4e | 992 | } |
62e76326 | 993 | /* |
aa387023 | 994 | * allow protocol specific headers to be _added_ to the existing |
995 | * response - ie digest auth | |
721b0310 | 996 | */ |
62e76326 | 997 | |
82b045dc | 998 | if (auth_user_request != NULL && auth_user_request->state()) |
999 | auth_user_request->state()->addHeader (rep, accelerated); | |
62e76326 | 1000 | |
0e3be1ea | 1001 | if (auth_user_request != NULL) |
62e76326 | 1002 | auth_user_request->lastReply = AUTH_ACL_CANNOT_AUTHENTICATE; |
94439e4e | 1003 | } |
1004 | ||
e6ccf245 | 1005 | void |
190154cf | 1006 | authenticateFixHeader(HttpReply * rep, auth_user_request_t * auth_user_request, HttpRequest * request, int accelerated, int internal) |
e6ccf245 | 1007 | { |
1008 | AuthUserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal); | |
1009 | } | |
62e76326 | 1010 | |
e6ccf245 | 1011 | |
94439e4e | 1012 | /* call the active auth module and allow it to add a trailer to the request */ |
1013 | void | |
190154cf | 1014 | authenticateAddTrailer(HttpReply * rep, auth_user_request_t * auth_user_request, HttpRequest * request, int accelerated) |
94439e4e | 1015 | { |
1016 | if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0) | |
62e76326 | 1017 | && (authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer)) |
1018 | authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer(auth_user_request, rep, accelerated); | |
94439e4e | 1019 | } |
1020 | ||
1021 | void | |
1022 | authenticateAuthUserLock(auth_user_t * auth_user) | |
1023 | { | |
5dae8514 | 1024 | debug(29, 9) ("authenticateAuthUserLock auth_user '%p'.\n", auth_user); |
94439e4e | 1025 | assert(auth_user != NULL); |
1026 | auth_user->references++; | |
32754419 | 1027 | debug(29, 9) ("authenticateAuthUserLock auth_user '%p' now at '%ld'.\n", auth_user, (long int) auth_user->references); |
94439e4e | 1028 | } |
1029 | ||
1030 | void | |
1031 | authenticateAuthUserUnlock(auth_user_t * auth_user) | |
1032 | { | |
5dae8514 | 1033 | debug(29, 9) ("authenticateAuthUserUnlock auth_user '%p'.\n", auth_user); |
94439e4e | 1034 | assert(auth_user != NULL); |
62e76326 | 1035 | |
94439e4e | 1036 | if (auth_user->references > 0) { |
62e76326 | 1037 | auth_user->references--; |
94439e4e | 1038 | } else { |
62e76326 | 1039 | debug(29, 1) ("Attempt to lower Auth User %p refcount below 0!\n", auth_user); |
94439e4e | 1040 | } |
62e76326 | 1041 | |
32754419 | 1042 | debug(29, 9) ("authenticateAuthUserUnlock auth_user '%p' now at '%ld'.\n", auth_user, (long int) auth_user->references); |
62e76326 | 1043 | |
94439e4e | 1044 | if (auth_user->references == 0) |
62e76326 | 1045 | delete auth_user; |
94439e4e | 1046 | } |
1047 | ||
1048 | void | |
62e76326 | 1049 | |
e6ccf245 | 1050 | AuthUserRequest::lock() |
94439e4e | 1051 | { |
e6ccf245 | 1052 | debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p'.\n", this); |
1053 | assert(this != NULL); | |
1054 | ++references; | |
1055 | debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p' now at '%ld'.\n", this, (long int) references); | |
94439e4e | 1056 | } |
1057 | ||
1058 | void | |
e6ccf245 | 1059 | AuthUserRequest::unlock() |
94439e4e | 1060 | { |
e6ccf245 | 1061 | debug(29, 9) ("AuthUserRequest::unlock: auth_user request '%p'.\n", this); |
1062 | assert(this != NULL); | |
62e76326 | 1063 | |
e6ccf245 | 1064 | if (references > 0) { |
62e76326 | 1065 | --references; |
94439e4e | 1066 | } else { |
62e76326 | 1067 | debug(29, 1) ("Attempt to lower Auth User request %p refcount below 0!\n", this); |
94439e4e | 1068 | } |
62e76326 | 1069 | |
e6ccf245 | 1070 | debug(29, 9) ("AuthUserRequest::unlock: auth_user_request '%p' now at '%ld'.\n", this, (long int) references); |
62e76326 | 1071 | |
e6ccf245 | 1072 | if (references == 0) |
62e76326 | 1073 | /* not locked anymore */ |
1074 | delete this; | |
e6ccf245 | 1075 | } |
1076 | ||
1077 | void | |
1078 | authenticateAuthUserRequestLock(auth_user_request_t * auth_user_request) | |
1079 | { | |
62e76326 | 1080 | |
1081 | auth_user_request->lock() | |
1082 | ||
1083 | ; | |
94439e4e | 1084 | } |
1085 | ||
e6ccf245 | 1086 | void |
1087 | authenticateAuthUserRequestUnlock(auth_user_request_t * auth_user_request) | |
1088 | { | |
1089 | auth_user_request->unlock(); | |
1090 | } | |
62e76326 | 1091 | |
e6ccf245 | 1092 | |
94439e4e | 1093 | int |
1094 | authenticateAuthUserInuse(auth_user_t * auth_user) | |
1095 | /* returns 0 for not in use */ | |
1096 | { | |
1097 | assert(auth_user != NULL); | |
1098 | return auth_user->references; | |
1099 | } | |
1100 | ||
e6ccf245 | 1101 | /* Combine two user structs. ONLY to be called from within a scheme |
1102 | * module. The scheme module is responsible for ensuring that the | |
aa387023 | 1103 | * two users _can_ be merged without invalidating all the request |
e6ccf245 | 1104 | * scheme data. The scheme is also responsible for merging any user |
aa387023 | 1105 | * related scheme data itself. |
1106 | */ | |
94439e4e | 1107 | void |
e6ccf245 | 1108 | AuthUser::absorb (AuthUser *from) |
94439e4e | 1109 | { |
94439e4e | 1110 | auth_user_request_t *auth_user_request; |
aa387023 | 1111 | /* |
1112 | * XXX combine two authuser structs. Incomplete: it should merge | |
1113 | * in hash references too and ask the module to merge in scheme | |
1114 | * data | |
1115 | */ | |
e6ccf245 | 1116 | debug(29, 5) ("authenticateAuthUserMerge auth_user '%p' into auth_user '%p'.\n", from, this); |
1117 | dlink_node *link = from->requests.head; | |
62e76326 | 1118 | |
94439e4e | 1119 | while (link) { |
62e76326 | 1120 | auth_user_request = static_cast<auth_user_request_t *>(link->data); |
1121 | dlink_node *tmplink = link; | |
1122 | link = link->next; | |
1123 | dlinkDelete(tmplink, &from->requests); | |
1124 | dlinkAddTail(auth_user_request, tmplink, &requests); | |
1125 | auth_user_request->auth_user = this; | |
94439e4e | 1126 | } |
62e76326 | 1127 | |
e6ccf245 | 1128 | references += from->references; |
94439e4e | 1129 | from->references = 0; |
e6ccf245 | 1130 | delete from; |
1131 | } | |
1132 | ||
1133 | void | |
1134 | authenticateAuthUserMerge(auth_user_t * from, auth_user_t * to) | |
62e76326 | 1135 | { |
e6ccf245 | 1136 | to->absorb (from); |
94439e4e | 1137 | } |
1138 | ||
1139 | void | |
e6ccf245 | 1140 | AuthUser::operator delete (void *address) |
1141 | { | |
1142 | memPoolFree(pool, address); | |
1143 | } | |
1144 | ||
1145 | AuthUser::~AuthUser() | |
94439e4e | 1146 | { |
94439e4e | 1147 | auth_user_request_t *auth_user_request; |
94439e4e | 1148 | dlink_node *link, *tmplink; |
e6ccf245 | 1149 | debug(29, 5) ("AuthUser::~AuthUser: Freeing auth_user '%p' with refcount '%ld'.\n", this, (long int) references); |
1150 | assert(references == 0); | |
94439e4e | 1151 | /* were they linked in by username ? */ |
62e76326 | 1152 | |
e6ccf245 | 1153 | if (usernamehash) { |
62e76326 | 1154 | assert(usernamehash->user() == this); |
1155 | debug(29, 5) ("AuthUser::~AuthUser: removing usernamehash entry '%p'\n", usernamehash); | |
1156 | hash_remove_link(proxy_auth_username_cache, | |
1157 | (hash_link *) usernamehash); | |
1158 | /* don't free the key as we use the same user string as the auth_user | |
1159 | * structure */ | |
1160 | delete usernamehash; | |
94439e4e | 1161 | } |
62e76326 | 1162 | |
94439e4e | 1163 | /* remove any outstanding requests */ |
e6ccf245 | 1164 | link = requests.head; |
62e76326 | 1165 | |
94439e4e | 1166 | while (link) { |
62e76326 | 1167 | debug(29, 5) ("AuthUser::~AuthUser: removing request entry '%p'\n", link->data); |
1168 | auth_user_request = static_cast<auth_user_request_t *>(link->data); | |
1169 | tmplink = link; | |
1170 | link = link->next; | |
1171 | dlinkDelete(tmplink, &requests); | |
1172 | dlinkNodeDelete(tmplink); | |
1173 | delete auth_user_request; | |
94439e4e | 1174 | } |
62e76326 | 1175 | |
94439e4e | 1176 | /* free cached acl results */ |
e6ccf245 | 1177 | aclCacheMatchFlush(&proxy_match_cache); |
62e76326 | 1178 | |
60d096f4 | 1179 | /* free seen ip address's */ |
e6ccf245 | 1180 | authenticateAuthUserClearIp(this); |
62e76326 | 1181 | |
e6ccf245 | 1182 | if (scheme_data && auth_module > 0) |
62e76326 | 1183 | authscheme_list[auth_module - 1].FreeUser(this); |
1184 | ||
94439e4e | 1185 | /* prevent accidental reuse */ |
e6ccf245 | 1186 | auth_type = AUTH_UNKNOWN; |
94439e4e | 1187 | } |
1188 | ||
1189 | void | |
e6ccf245 | 1190 | AuthUser::cacheInit(void) |
94439e4e | 1191 | { |
1192 | if (!proxy_auth_username_cache) { | |
62e76326 | 1193 | /* First time around, 7921 should be big enough */ |
1194 | proxy_auth_username_cache = | |
1195 | hash_create((HASHCMP *) strcmp, 7921, hash_string); | |
1196 | assert(proxy_auth_username_cache); | |
1197 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1); | |
94439e4e | 1198 | } |
1199 | } | |
1200 | ||
658d5a21 | 1201 | void |
1202 | AuthUser::CachedACLsReset() | |
1203 | { | |
1204 | /* | |
1205 | * We walk the hash by username as that is the unique key we use. | |
1206 | * This must complete all at once, because we are ensuring correctness. | |
1207 | */ | |
1208 | AuthUserHashPointer *usernamehash; | |
1209 | auth_user_t *auth_user; | |
1210 | char const *username = NULL; | |
1211 | debug(29, 3) ("AuthUser::CachedACLsReset: Flushing the ACL caches for all users.\n"); | |
1212 | hash_first(proxy_auth_username_cache); | |
62e76326 | 1213 | |
658d5a21 | 1214 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { |
62e76326 | 1215 | auth_user = usernamehash->user(); |
1216 | username = auth_user->username(); | |
1217 | /* free cached acl results */ | |
1218 | aclCacheMatchFlush(&auth_user->proxy_match_cache); | |
658d5a21 | 1219 | |
1220 | } | |
62e76326 | 1221 | |
658d5a21 | 1222 | debug(29, 3) ("AuthUser::CachedACLsReset: Finished.\n"); |
1223 | } | |
1224 | ||
94439e4e | 1225 | void |
e6ccf245 | 1226 | AuthUser::cacheCleanup(void *datanotused) |
94439e4e | 1227 | { |
1228 | /* | |
1229 | * We walk the hash by username as that is the unique key we use. | |
1230 | * For big hashs we could consider stepping through the cache, 100/200 | |
1231 | * entries at a time. Lets see how it flys first. | |
1232 | */ | |
e6ccf245 | 1233 | AuthUserHashPointer *usernamehash; |
94439e4e | 1234 | auth_user_t *auth_user; |
e6ccf245 | 1235 | char const *username = NULL; |
1236 | debug(29, 3) ("AuthUser::cacheCleanup: Cleaning the user cache now\n"); | |
1237 | debug(29, 3) ("AuthUser::cacheCleanup: Current time: %ld\n", (long int) current_time.tv_sec); | |
94439e4e | 1238 | hash_first(proxy_auth_username_cache); |
62e76326 | 1239 | |
e6ccf245 | 1240 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { |
62e76326 | 1241 | auth_user = usernamehash->user(); |
1242 | username = auth_user->username(); | |
1243 | ||
1244 | /* if we need to have inpedendent expiry clauses, insert a module call | |
1245 | * here */ | |
1246 | debug(29, 4) ("AuthUser::cacheCleanup: Cache entry:\n\tType: %d\n\tUsername: %s\n\texpires: %ld\n\treferences: %ld\n", auth_user->auth_type, username, (long int) (auth_user->expiretime + Config.authenticateTTL), (long int) auth_user->references); | |
1247 | ||
1248 | if (auth_user->expiretime + Config.authenticateTTL <= current_time.tv_sec) { | |
1249 | debug(29, 5) ("AuthUser::cacheCleanup: Removing user %s from cache due to timeout.\n", username); | |
1250 | /* the minus 1 accounts for the cache lock */ | |
1251 | ||
1252 | if (!(authenticateAuthUserInuse(auth_user) - 1)) | |
1253 | /* we don't warn if we leave the user in the cache, | |
1254 | * because other modules (ie delay pools) may keep | |
1255 | * locks on users, and thats legitimate | |
1256 | */ | |
1257 | authenticateAuthUserUnlock(auth_user); | |
1258 | } | |
94439e4e | 1259 | } |
62e76326 | 1260 | |
e6ccf245 | 1261 | debug(29, 3) ("AuthUser::cacheCleanup: Finished cleaning the user cache.\n"); |
1262 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1); | |
94439e4e | 1263 | } |
1264 | ||
1265 | /* | |
1266 | * authenticateUserCacheRestart() cleans all config-dependent data from the | |
1267 | * auth_user cache. It DOES NOT Flush the user cache. | |
1268 | */ | |
1269 | ||
1270 | void | |
2d72d4fd | 1271 | authenticateUserCacheRestart(void) |
94439e4e | 1272 | { |
e6ccf245 | 1273 | AuthUserHashPointer *usernamehash; |
94439e4e | 1274 | auth_user_t *auth_user; |
94439e4e | 1275 | debug(29, 3) ("authenticateUserCacheRestart: Clearing config dependent cache data.\n"); |
1276 | hash_first(proxy_auth_username_cache); | |
62e76326 | 1277 | |
e6ccf245 | 1278 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { |
62e76326 | 1279 | auth_user = usernamehash->user(); |
1280 | debug(29, 5) ("authenticateUserCacheRestat: Clearing cache ACL results for user: %s\n", auth_user->username()); | |
94439e4e | 1281 | } |
1282 | ||
1283 | } | |
1284 | ||
1285 | /* | |
1286 | * called to add another auth scheme module | |
1287 | */ | |
1288 | void | |
a2c963ae | 1289 | authSchemeAdd(const char *type, AUTHSSETUP * setup) |
94439e4e | 1290 | { |
1291 | int i; | |
1d1cc61b | 1292 | debug(29, 4) ("authSchemeAdd: adding %s\n", type); |
94439e4e | 1293 | /* find the number of currently known authscheme types */ |
62e76326 | 1294 | |
94439e4e | 1295 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { |
62e76326 | 1296 | assert(strcmp(authscheme_list[i].typestr, type) != 0); |
94439e4e | 1297 | } |
62e76326 | 1298 | |
94439e4e | 1299 | /* add the new type */ |
e6ccf245 | 1300 | authscheme_list = static_cast<authscheme_entry_t *>(xrealloc(authscheme_list, (i + 2) * sizeof(authscheme_entry_t))); |
62e76326 | 1301 | |
8d25251a | 1302 | memset(&authscheme_list[i], 0, sizeof(authscheme_entry_t)); |
62e76326 | 1303 | |
94439e4e | 1304 | memset(&authscheme_list[i + 1], 0, sizeof(authscheme_entry_t)); |
62e76326 | 1305 | |
94439e4e | 1306 | authscheme_list[i].typestr = type; |
62e76326 | 1307 | |
94439e4e | 1308 | /* Call the scheme module to set up capabilities and initialize any global data */ |
1309 | setup(&authscheme_list[i]); | |
1310 | } | |
1311 | ||
e6ccf245 | 1312 | /* _auth_user_hash_pointe */ |
1313 | ||
1314 | void | |
1315 | AuthUserHashPointer::removeFromCache(void *usernamehash_p) | |
1316 | { | |
1317 | AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(usernamehash_p); | |
1318 | auth_user_t *auth_user = usernamehash->auth_user; | |
62e76326 | 1319 | |
e6ccf245 | 1320 | if ((authenticateAuthUserInuse(auth_user) - 1)) |
62e76326 | 1321 | debug(29, 1) ("AuthUserHashPointer::removeFromCache: entry in use - not freeing\n"); |
1322 | ||
e6ccf245 | 1323 | authenticateAuthUserUnlock(auth_user); |
62e76326 | 1324 | |
e6ccf245 | 1325 | /* TODO: change behaviour - we remove from the auth user list here, and then unlock, and the |
1326 | * delete ourselves. | |
1327 | */ | |
1328 | } | |
94439e4e | 1329 | |
e6ccf245 | 1330 | void * |
768cb287 | 1331 | AuthUserHashPointer::operator new (size_t byteCount) |
e6ccf245 | 1332 | { |
1333 | assert (byteCount == sizeof (AuthUserHashPointer)); | |
62e76326 | 1334 | |
e6ccf245 | 1335 | if (!pool) |
62e76326 | 1336 | pool = memPoolCreate("Auth user hash link", sizeof(AuthUserHashPointer)); |
1337 | ||
e6ccf245 | 1338 | return static_cast<AuthUserHashPointer *>(memPoolAlloc(pool)); |
1339 | } | |
94439e4e | 1340 | |
94439e4e | 1341 | void |
e6ccf245 | 1342 | AuthUserHashPointer::operator delete (void *address) |
94439e4e | 1343 | { |
e6ccf245 | 1344 | memPoolFree(pool, address); |
1345 | } | |
1346 | ||
1347 | AuthUserHashPointer::AuthUserHashPointer (auth_user_t * anAuth_user): | |
62e76326 | 1348 | auth_user (anAuth_user) |
e6ccf245 | 1349 | { |
4a8b20e8 | 1350 | key = (void *)anAuth_user->username(); |
1351 | next = NULL; | |
e6ccf245 | 1352 | hash_join(proxy_auth_username_cache, (hash_link *) this); |
94439e4e | 1353 | /* lock for presence in the cache */ |
1354 | authenticateAuthUserLock(auth_user); | |
1355 | } | |
e6ccf245 | 1356 | |
1357 | AuthUser * | |
1358 | AuthUserHashPointer::user() const | |
1359 | { | |
1360 | return auth_user; | |
1361 | } | |
1362 | ||
1363 | /* C bindings */ | |
1364 | /* UserNameCacheAdd: add a auth_user structure to the username cache */ | |
1365 | void | |
1366 | authenticateUserNameCacheAdd(auth_user_t * auth_user) | |
1367 | { | |
1368 | auth_user->usernamehash = new AuthUserHashPointer (auth_user); | |
1369 | } | |
1370 | ||
1371 | auth_user_t* | |
1372 | authUserHashPointerUser (auth_user_hash_pointer *aHashEntry) | |
1373 | { | |
1374 | return aHashEntry->user(); | |
1375 | } | |
1376 | ||
82b045dc | 1377 | void * |
1378 | AuthUserRequestState::operator new (size_t) | |
1379 | { | |
1380 | fatal ("unusable\n"); | |
a46d2c0e | 1381 | return (void *)1; |
82b045dc | 1382 | } |
1383 | ||
1384 | void | |
1385 | AuthUserRequestState::operator delete (void *) | |
1386 | { | |
1387 | fatal ("unusable\n"); | |
1388 | } |