]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | /* | |
3 | * $Id: authenticate.cc,v 1.15 2001/01/07 23:36:37 hno Exp $ | |
4 | * | |
5 | * DEBUG: section 29 Authenticator | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by the | |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * the Regents of the University of California. Please see the | |
16 | * COPYRIGHT file for full details. Squid incorporates software | |
17 | * developed and/or copyrighted by other sources. Please see the | |
18 | * CREDITS file for full details. | |
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 | ||
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 */ | |
39 | ||
40 | ||
41 | #include "squid.h" | |
42 | ||
43 | static void | |
44 | authenticateDecodeAuth(const char *proxy_auth, auth_user_request_t * auth_user_request); | |
45 | ||
46 | /* | |
47 | * | |
48 | * Private Data | |
49 | * | |
50 | */ | |
51 | ||
52 | MemPool *auth_user_request_pool = NULL; | |
53 | ||
54 | /* Generic Functions */ | |
55 | ||
56 | ||
57 | int | |
58 | authenticateAuthSchemeConfigured(const char *proxy_auth) | |
59 | { | |
60 | authScheme *scheme; | |
61 | int i; | |
62 | for (i = 0; i < Config.authConfig.n_configured; i++) { | |
63 | scheme = Config.authConfig.schemes + i; | |
64 | if (strncasecmp(proxy_auth, scheme->typestr, strlen(scheme->typestr)) == 0) | |
65 | return 1; | |
66 | } | |
67 | return 0; | |
68 | } | |
69 | ||
70 | int | |
71 | authenticateAuthSchemeId(const char *typestr) | |
72 | { | |
73 | int i = 0; | |
74 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { | |
75 | if (strncasecmp(typestr, authscheme_list[i].typestr, strlen(authscheme_list[i].typestr)) == 0) { | |
76 | return i; | |
77 | } | |
78 | } | |
79 | return -1; | |
80 | } | |
81 | ||
82 | void | |
83 | authenticateDecodeAuth(const char *proxy_auth, auth_user_request_t * auth_user_request) | |
84 | { | |
85 | int i = 0; | |
86 | assert(proxy_auth != NULL); | |
87 | assert(auth_user_request != NULL); /* we need this created for us. */ | |
88 | debug(29, 9) ("authenticateDecodeAuth: header = '%s'\n", proxy_auth); | |
89 | if (authenticateAuthSchemeConfigured(proxy_auth)) { | |
90 | /* we're configured to use this scheme - but is it active ? */ | |
91 | if ((i = authenticateAuthSchemeId(proxy_auth)) != -1) { | |
92 | authscheme_list[i].decodeauth(auth_user_request, proxy_auth); | |
93 | auth_user_request->auth_user->auth_module = i + 1; | |
94 | return; | |
95 | } | |
96 | } | |
97 | debug(29, 1) | |
98 | ("authenticateDecodeAuth: Unsupported or unconfigured proxy-auth scheme, '%s'\n", | |
99 | proxy_auth); | |
100 | return; | |
101 | } | |
102 | ||
103 | /* clear any connection related authentication details */ | |
104 | void | |
105 | authenticateOnCloseConnection(ConnStateData * conn) | |
106 | { | |
107 | auth_user_request_t *auth_user_request; | |
108 | assert(conn != NULL); | |
109 | if (conn->auth_user_request != NULL) { | |
110 | auth_user_request = conn->auth_user_request; | |
111 | if (authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection) { | |
112 | authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection(conn); | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/ | |
118 | ||
119 | /* send the initial data to an authenticator module */ | |
120 | void | |
121 | authenticateStart(auth_user_request_t * auth_user_request, RH * handler, void *data) | |
122 | { | |
123 | assert(auth_user_request); | |
124 | assert(handler); | |
125 | debug(29, 9) ("authenticateStart: auth_user_request '%d'\n", auth_user_request); | |
126 | if (auth_user_request->auth_user->auth_module > 0) | |
127 | authscheme_list[auth_user_request->auth_user->auth_module - 1].authStart(auth_user_request, handler, data); | |
128 | else | |
129 | handler(data, NULL); | |
130 | } | |
131 | ||
132 | /* | |
133 | * Check a auth_user pointer for validity. Does not check passwords, just data | |
134 | * sensability. Broken or Unknown auth_types are not valid for use... | |
135 | */ | |
136 | ||
137 | int | |
138 | authenticateValidateUser(auth_user_request_t * auth_user_request) | |
139 | { | |
140 | debug(29, 9) ("authenticateValidateUser: Validating Auth_user request '%d'.\n", auth_user_request); | |
141 | if (auth_user_request == NULL) { | |
142 | debug(29, 4) ("authenticateValidateUser: Auth_user_request was NULL!\n"); | |
143 | return 0; | |
144 | } | |
145 | if (auth_user_request->auth_user == NULL) { | |
146 | debug(29, 4) ("authenticateValidateUser: No associated auth_user structure\n"); | |
147 | return 0; | |
148 | } | |
149 | if (auth_user_request->auth_user->auth_type == AUTH_UNKNOWN) { | |
150 | debug(29, 4) ("authenticateValidateUser: Auth_user '%d' uses unknown scheme.\n", auth_user_request->auth_user); | |
151 | return 0; | |
152 | } | |
153 | if (auth_user_request->auth_user->auth_type == AUTH_BROKEN) { | |
154 | debug(29, 4) ("authenticateValidateUser: Auth_user '%d' is broken for it's scheme.\n", auth_user_request->auth_user); | |
155 | return 0; | |
156 | } | |
157 | /* any other sanity checks that we need in the future */ | |
158 | ||
159 | /* Thus should a module call to something like authValidate */ | |
160 | ||
161 | /* finally return ok */ | |
162 | debug(29, 4) ("authenticateValidateUser: Validated Auth_user request '%d'.\n", auth_user_request); | |
163 | return 1; | |
164 | ||
165 | } | |
166 | ||
167 | auth_user_t * | |
168 | authenticateAuthUserNew(const char *scheme) | |
169 | { | |
170 | auth_user_t *temp_auth; | |
171 | temp_auth = memAllocate(MEM_AUTH_USER_T); | |
172 | assert(temp_auth != NULL); | |
173 | temp_auth->auth_type = AUTH_UNKNOWN; | |
174 | temp_auth->references = 0; | |
175 | temp_auth->auth_module = authenticateAuthSchemeId(scheme) + 1; | |
176 | return temp_auth; | |
177 | } | |
178 | ||
179 | auth_user_request_t * | |
180 | authenticateAuthUserRequestNew() | |
181 | { | |
182 | auth_user_request_t *temp_request; | |
183 | if (!auth_user_request_pool) | |
184 | auth_user_request_pool = memPoolCreate("Authenticate Request Data", sizeof(auth_user_request_t)); | |
185 | temp_request = memPoolAlloc(auth_user_request_pool); | |
186 | assert(temp_request != NULL); | |
187 | temp_request->auth_user = NULL; | |
188 | temp_request->message = NULL; | |
189 | temp_request->scheme_data = NULL; | |
190 | temp_request->references = 0; | |
191 | return temp_request; | |
192 | } | |
193 | ||
194 | void | |
195 | authenticateAuthUserRequestFree(auth_user_request_t * auth_user_request) | |
196 | { | |
197 | dlink_node *link; | |
198 | debug(29, 5) ("authenticateAuthUserRequestFree: freeing request %d\n", auth_user_request); | |
199 | if (!auth_user_request) | |
200 | return; | |
201 | assert(auth_user_request->references == 0); | |
202 | if (auth_user_request->auth_user) { | |
203 | if (auth_user_request->scheme_data != NULL) { | |
204 | /* we MUST know the module */ | |
205 | assert((auth_user_request->auth_user->auth_module > 0)); | |
206 | /* and the module MUST support requestFree if it has created scheme data */ | |
207 | assert(authscheme_list[auth_user_request->auth_user->auth_module - 1].requestFree != NULL); | |
208 | authscheme_list[auth_user_request->auth_user->auth_module - 1].requestFree(auth_user_request); | |
209 | } | |
210 | /* unlink from the auth_user struct */ | |
211 | link = auth_user_request->auth_user->requests.head; | |
212 | while (link && (link->data != auth_user_request)) | |
213 | link = link->next; | |
214 | assert(link != NULL); | |
215 | dlinkDelete(link, &auth_user_request->auth_user->requests); | |
216 | dlinkNodeDelete(link); | |
217 | ||
218 | /* unlock the request structure's lock */ | |
219 | authenticateAuthUserUnlock(auth_user_request->auth_user); | |
220 | auth_user_request->auth_user = NULL; | |
221 | } else | |
222 | assert(auth_user_request->scheme_data == NULL); | |
223 | if (auth_user_request->message) | |
224 | xfree(auth_user_request->message); | |
225 | } | |
226 | ||
227 | char * | |
228 | authenticateAuthUserRequestMessage(auth_user_request_t * auth_user_request) | |
229 | { | |
230 | if (auth_user_request) | |
231 | return auth_user_request->message; | |
232 | return NULL; | |
233 | } | |
234 | ||
235 | void | |
236 | authenticateAuthUserRequestSetIp(auth_user_request_t * auth_user_request, struct in_addr ipaddr) | |
237 | { | |
238 | if (auth_user_request->auth_user) | |
239 | if (!auth_user_request->auth_user->ipaddr.s_addr) | |
240 | auth_user_request->auth_user->ipaddr = ipaddr; | |
241 | } | |
242 | ||
243 | /* Get Auth User: Return a filled out auth_user structure for the given | |
244 | * Proxy Auth (or Auth) header. It may be a cached Auth User or a new | |
245 | * Unauthenticated structure. The structure is given an inital lock here. | |
246 | */ | |
247 | auth_user_request_t * | |
248 | authenticateGetAuthUser(const char *proxy_auth) | |
249 | { | |
250 | auth_user_request_t *auth_user_request = authenticateAuthUserRequestNew(); | |
251 | /* and lock for the callers instance */ | |
252 | authenticateAuthUserRequestLock(auth_user_request); | |
253 | authenticateDecodeAuth(proxy_auth, auth_user_request); | |
254 | return auth_user_request; | |
255 | } | |
256 | ||
257 | /* | |
258 | * authenticateUserAuthenticated: is this auth_user structure logged in ? | |
259 | */ | |
260 | int | |
261 | authenticateUserAuthenticated(auth_user_request_t * auth_user_request) | |
262 | { | |
263 | assert(authenticateValidateUser(auth_user_request)); | |
264 | if (auth_user_request->auth_user->auth_module > 0) | |
265 | return authscheme_list[auth_user_request->auth_user->auth_module - 1].authenticated(auth_user_request); | |
266 | else | |
267 | return 0; | |
268 | } | |
269 | ||
270 | /* | |
271 | * authenticateAuthenticateUser: log this user request in. | |
272 | * Cache hits may change the auth_user pointer in the structure if needed. | |
273 | * This is basically a handle approach. | |
274 | */ | |
275 | void | |
276 | authenticateAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type) | |
277 | { | |
278 | assert(auth_user_request != NULL); | |
279 | if (auth_user_request->auth_user->auth_module > 0) | |
280 | authscheme_list[auth_user_request->auth_user->auth_module - 1].authAuthenticate(auth_user_request, request, conn, type); | |
281 | } | |
282 | ||
283 | /* authenticateUserUsername: return a pointer to the username in the */ | |
284 | char * | |
285 | authenticateUserUsername(auth_user_t * auth_user) | |
286 | { | |
287 | if (!auth_user) | |
288 | return NULL; | |
289 | if (auth_user->auth_module > 0) | |
290 | return authscheme_list[auth_user->auth_module - 1].authUserUsername(auth_user); | |
291 | return NULL; | |
292 | } | |
293 | ||
294 | /* authenticateUserRequestUsername: return a pointer to the username in the */ | |
295 | char * | |
296 | authenticateUserRequestUsername(auth_user_request_t * auth_user_request) | |
297 | { | |
298 | assert(auth_user_request != NULL); | |
299 | return authenticateUserUsername(auth_user_request->auth_user); | |
300 | } | |
301 | ||
302 | /* returns | |
303 | * 0: no output needed | |
304 | * 1: send to client | |
305 | * -1: send to helper | |
306 | * -2: authenticate broken in some fashion | |
307 | */ | |
308 | int | |
309 | authenticateDirection(auth_user_request_t * auth_user_request) | |
310 | { | |
311 | if (!auth_user_request) | |
312 | return -2; | |
313 | if (authenticateUserAuthenticated(auth_user_request)) | |
314 | return 0; | |
315 | if (auth_user_request->auth_user->auth_module > 0) | |
316 | return authscheme_list[auth_user_request->auth_user->auth_module - 1].getdirection(auth_user_request); | |
317 | return -2; | |
318 | } | |
319 | ||
320 | int | |
321 | authenticateActiveSchemeCount() | |
322 | { | |
323 | int i = 0, rv = 0; | |
324 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) | |
325 | if (authscheme_list[i].Active()) | |
326 | rv++; | |
327 | debug(29, 9) ("authenticateActiveSchemeCount: %d active.\n", rv); | |
328 | return rv; | |
329 | } | |
330 | ||
331 | int | |
332 | authenticateSchemeCount() | |
333 | { | |
334 | int i = 0, rv = 0; | |
335 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) | |
336 | rv++; | |
337 | debug(29, 9) ("authenticateSchemeCount: %d active.\n", rv); | |
338 | return rv; | |
339 | } | |
340 | ||
341 | void | |
342 | authenticateSchemeInit(void) | |
343 | { | |
344 | authSchemeSetup(); | |
345 | } | |
346 | ||
347 | void | |
348 | authenticateInit(authConfig * config) | |
349 | { | |
350 | int i; | |
351 | authScheme *scheme; | |
352 | for (i = 0; i < config->n_configured; i++) { | |
353 | if (authscheme_list[i].init) { | |
354 | scheme = config->schemes + i; | |
355 | authscheme_list[i].init(scheme); | |
356 | } | |
357 | } | |
358 | if (!proxy_auth_username_cache) | |
359 | authenticateInitUserCache(); | |
360 | } | |
361 | ||
362 | void | |
363 | authenticateShutdown(void) | |
364 | { | |
365 | int i; | |
366 | debug(29, 2) ("authenticateShutdown: shutting down auth schemes\n"); | |
367 | /* find the currently known authscheme types */ | |
368 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { | |
369 | if (authscheme_list[i].donefunc != NULL) | |
370 | authscheme_list[i].donefunc(); | |
371 | else | |
372 | debug(29, 2) ("authenticateShutdown: scheme %s has not registered a shutdown function.\n", authscheme_list[i].typestr); | |
373 | authscheme_list[i].typestr = NULL; | |
374 | } | |
375 | } | |
376 | ||
377 | void | |
378 | authenticateFixHeader(HttpReply * rep, auth_user_request_t * auth_user_request, request_t * request, int accelerated) | |
379 | /* send the auth types we are configured to support (and have compiled in!) */ | |
380 | { | |
381 | /* auth_type_t auth_type=err->auth_type; | |
382 | * auth_state_t auth_state=err->auth_state; | |
383 | * char *authchallenge=err->authchallenge; | |
384 | * auth_user_request_t *auth_user_request=err->auth_user_request; | |
385 | */ | |
386 | int type = 0; | |
387 | switch (rep->sline.status) { | |
388 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: | |
389 | /* Proxy authorisation needed */ | |
390 | type = HDR_PROXY_AUTHENTICATE; | |
391 | break; | |
392 | case HTTP_UNAUTHORIZED: | |
393 | /* WWW Authorisation needed */ | |
394 | type = HDR_WWW_AUTHENTICATE; | |
395 | break; | |
396 | default: | |
397 | /* Keep GCC happy */ | |
398 | /* some other HTTP status */ | |
399 | break; | |
400 | } | |
401 | debug(29, 9) ("authenticateFixHeader: headertype:%d authuser:%d\n", type, auth_user_request); | |
402 | if ((rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) | |
403 | || (rep->sline.status == HTTP_UNAUTHORIZED)) | |
404 | /* this is a authenticate-needed response */ | |
405 | { | |
406 | if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0)) | |
407 | authscheme_list[auth_user_request->auth_user->auth_module - 1].authFixHeader(auth_user_request, rep, type, request); | |
408 | else { | |
409 | int i; | |
410 | authScheme *scheme; | |
411 | /* call each configured authscheme */ | |
412 | for (i = 0; i < Config.authConfig.n_configured; i++) { | |
413 | scheme = Config.authConfig.schemes + i; | |
414 | if (authscheme_list[scheme->Id].Active()) | |
415 | authscheme_list[scheme->Id].authFixHeader(auth_user_request, rep, type, | |
416 | request); | |
417 | else | |
418 | debug(29, 4) ("authenticateFixHeader: Configured scheme %s not Active\n", scheme->typestr); | |
419 | } | |
420 | } | |
421 | } | |
422 | if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0) | |
423 | && (authscheme_list[auth_user_request->auth_user->auth_module - 1].AddHeader)) | |
424 | authscheme_list[auth_user_request->auth_user->auth_module - 1].AddHeader(auth_user_request, rep, accelerated); | |
425 | } | |
426 | ||
427 | /* call the active auth module and allow it to add a trailer to the request */ | |
428 | void | |
429 | authenticateAddTrailer(HttpReply * rep, auth_user_request_t * auth_user_request, request_t * request, int accelerated) | |
430 | { | |
431 | if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0) | |
432 | && (authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer)) | |
433 | authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer(auth_user_request, rep, accelerated); | |
434 | } | |
435 | ||
436 | void | |
437 | authenticateAuthUserLock(auth_user_t * auth_user) | |
438 | { | |
439 | debug(29, 9) ("authenticateAuthUserLock auth_user '%d'.\n", auth_user); | |
440 | assert(auth_user != NULL); | |
441 | auth_user->references++; | |
442 | debug(29, 9) ("authenticateAuthUserLock auth_user '%d' now at '%d'.\n", auth_user, auth_user->references); | |
443 | } | |
444 | ||
445 | void | |
446 | authenticateAuthUserUnlock(auth_user_t * auth_user) | |
447 | { | |
448 | debug(29, 9) ("authenticateAuthUserUnlock auth_user '%d'.\n", auth_user); | |
449 | assert(auth_user != NULL); | |
450 | if (auth_user->references > 0) { | |
451 | auth_user->references--; | |
452 | } else { | |
453 | debug(29, 1) ("Attempt to lower Auth User %d refcount below 0!\n", auth_user); | |
454 | } | |
455 | debug(29, 9) ("authenticateAuthUserUnlock auth_user '%d' now at '%d'.\n", auth_user, auth_user->references); | |
456 | if (auth_user->references == 0) | |
457 | authenticateFreeProxyAuthUser(auth_user); | |
458 | } | |
459 | ||
460 | void | |
461 | authenticateAuthUserRequestLock(auth_user_request_t * auth_user_request) | |
462 | { | |
463 | debug(29, 9) ("authenticateAuthUserRequestLock auth_user request '%d'.\n", auth_user_request); | |
464 | assert(auth_user_request != NULL); | |
465 | auth_user_request->references++; | |
466 | debug(29, 9) ("authenticateAuthUserRequestLock auth_user request '%d' now at '%d'.\n", auth_user_request, auth_user_request->references); | |
467 | } | |
468 | ||
469 | void | |
470 | authenticateAuthUserRequestUnlock(auth_user_request_t * auth_user_request) | |
471 | { | |
472 | debug(29, 9) ("authenticateAuthUserRequestUnlock auth_user request '%d'.\n", auth_user_request); | |
473 | assert(auth_user_request != NULL); | |
474 | if (auth_user_request->references > 0) { | |
475 | auth_user_request->references--; | |
476 | } else { | |
477 | debug(29, 1) ("Attempt to lower Auth User request %d refcount below 0!\n", auth_user_request); | |
478 | } | |
479 | debug(29, 9) ("authenticateAuthUserRequestUnlock auth_user_request '%d' now at '%d'.\n", auth_user_request, auth_user_request->references); | |
480 | if (auth_user_request->references == 0) { | |
481 | /* not locked anymore */ | |
482 | authenticateAuthUserRequestFree(auth_user_request); | |
483 | } | |
484 | } | |
485 | ||
486 | int | |
487 | authenticateAuthUserInuse(auth_user_t * auth_user) | |
488 | /* returns 0 for not in use */ | |
489 | { | |
490 | assert(auth_user != NULL); | |
491 | return auth_user->references; | |
492 | } | |
493 | ||
494 | /* Combine two user structs. ONLY to be called from within a scheme module. | |
495 | * The scheme module is responsible for ensuring that the two users _can_ be merged | |
496 | * without invalidating all the request scheme data. | |
497 | * the scheme is also responsible for merging any user related scheme data itself. */ | |
498 | void | |
499 | authenticateAuthUserMerge(auth_user_t * from, auth_user_t * to) | |
500 | { | |
501 | dlink_node *link, *tmplink; | |
502 | auth_user_request_t *auth_user_request; | |
503 | /* XXX combine two authuser structs. Incomplete: it should merge in hash references | |
504 | * too and ask the module to merge in scheme data */ | |
505 | debug(29, 5) ("authenticateAuthUserMerge auth_user '%d' into auth_user '%d'.\n", from, to); | |
506 | link = from->requests.head; | |
507 | while (link) { | |
508 | auth_user_request = link->data; | |
509 | tmplink = link; | |
510 | link = link->next; | |
511 | dlinkDelete(tmplink, &from->requests); | |
512 | dlinkAddTail(auth_user_request, tmplink, &to->requests); | |
513 | auth_user_request->auth_user = to; | |
514 | } | |
515 | to->references += from->references; | |
516 | from->references = 0; | |
517 | authenticateFreeProxyAuthUser(from); | |
518 | } | |
519 | ||
520 | void | |
521 | authenticateFreeProxyAuthUser(void *data) | |
522 | { | |
523 | auth_user_t *u = data; | |
524 | auth_user_request_t *auth_user_request; | |
525 | #if 0 | |
526 | auth_user_hash_pointer *proxy_auth_hash; | |
527 | #endif | |
528 | dlink_node *link, *tmplink; | |
529 | assert(data != NULL); | |
530 | debug(29, 5) ("authenticateFreeProxyAuthUser: Freeing auth_user '%d' with refcount '%d'.\n", u, u->references); | |
531 | assert(u->references == 0); | |
532 | /* were they linked in by username ? */ | |
533 | if (u->usernamehash) { | |
534 | assert(u->usernamehash->auth_user == u); | |
535 | debug(29, 5) ("authenticateFreeProxyAuthUser: removing usernamehash entry '%d'\n", u->usernamehash); | |
536 | hash_remove_link(proxy_auth_username_cache, | |
537 | (hash_link *) u->usernamehash); | |
538 | /* don't free the key as we use the same user string as the auth_user | |
539 | * structure */ | |
540 | memFree(u->usernamehash, MEM_AUTH_USER_HASH); | |
541 | } | |
542 | /* remove any outstanding requests */ | |
543 | link = u->requests.head; | |
544 | while (link) { | |
545 | debug(29, 5) ("authenticateFreeProxyAuthUser: removing request entry '%d'\n", link->data); | |
546 | auth_user_request = link->data; | |
547 | tmplink = link; | |
548 | link = link->next; | |
549 | dlinkDelete(tmplink, &u->requests); | |
550 | dlinkNodeDelete(tmplink); | |
551 | authenticateAuthUserRequestFree(auth_user_request); | |
552 | } | |
553 | /* free cached acl results */ | |
554 | aclCacheMatchFlush(&u->proxy_match_cache); | |
555 | if (u->scheme_data && u->auth_module > 0) | |
556 | authscheme_list[u->auth_module - 1].FreeUser(u); | |
557 | /* prevent accidental reuse */ | |
558 | u->auth_type = AUTH_UNKNOWN; | |
559 | memFree(u, MEM_AUTH_USER_T); | |
560 | } | |
561 | ||
562 | void | |
563 | authenticateInitUserCache() | |
564 | { | |
565 | if (!proxy_auth_username_cache) { | |
566 | /* First time around, 7921 should be big enough */ | |
567 | proxy_auth_username_cache = | |
568 | hash_create((HASHCMP *) strcmp, 7921, hash_string); | |
569 | assert(proxy_auth_username_cache); | |
570 | eventAdd("User Cache Maintenance", authenticateProxyUserCacheCleanup, NULL, Config.authenticateGCInterval, 1); | |
571 | } | |
572 | } | |
573 | ||
574 | void | |
575 | authenticateProxyUserCacheCleanup(void *datanotused) | |
576 | { | |
577 | /* | |
578 | * We walk the hash by username as that is the unique key we use. | |
579 | * For big hashs we could consider stepping through the cache, 100/200 | |
580 | * entries at a time. Lets see how it flys first. | |
581 | */ | |
582 | auth_user_hash_pointer *usernamehash; | |
583 | auth_user_t *auth_user; | |
584 | char *username = NULL; | |
585 | debug(29, 3) ("authenticateProxyUserCacheCleanup: Cleaning the user cache now\n"); | |
586 | debug(29, 3) ("authenticateProxyUserCacheCleanup: Current time: %d\n", current_time.tv_sec); | |
587 | hash_first(proxy_auth_username_cache); | |
588 | while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) { | |
589 | auth_user = usernamehash->auth_user; | |
590 | username = authenticateUserUsername(auth_user); | |
591 | ||
592 | /* if we need to have inpedendent expiry clauses, insert a module call | |
593 | * here */ | |
594 | debug(29, 4) ("authenticateProxyUserCacheCleanup: Cache entry:\n\tType: %d\n\tUsername: %s\n\texpires: %d\n\treferences: %d\n", auth_user->auth_type, username, auth_user->expiretime + Config.authenticateTTL, auth_user->references); | |
595 | if (auth_user->expiretime + Config.authenticateTTL <= current_time.tv_sec) { | |
596 | debug(29, 5) ("authenticateProxyUserCacheCleanup: Removing user %s from cache due to timeout.\n", username); | |
597 | /* the minus 1 accounts for the cache lock */ | |
598 | if ((authenticateAuthUserInuse(auth_user) - 1)) | |
599 | debug(29, 4) ("authenticateProxyUserCacheCleanup: this cache entry has expired AND has a non-zero ref count.\n"); | |
600 | else | |
601 | authenticateAuthUserUnlock(auth_user); | |
602 | } | |
603 | } | |
604 | debug(29, 3) ("authenticateProxyUserCacheCleanup: Finished cleaning the user cache.\n"); | |
605 | eventAdd("User Cache Maintenance", authenticateProxyUserCacheCleanup, NULL, Config.authenticateGCInterval, 1); | |
606 | } | |
607 | ||
608 | /* | |
609 | * authenticateUserCacheRestart() cleans all config-dependent data from the | |
610 | * auth_user cache. It DOES NOT Flush the user cache. | |
611 | */ | |
612 | ||
613 | void | |
614 | authenticateUserCacheRestart() | |
615 | { | |
616 | auth_user_hash_pointer *usernamehash; | |
617 | auth_user_t *auth_user; | |
618 | char *username = NULL; | |
619 | debug(29, 3) ("authenticateUserCacheRestart: Clearing config dependent cache data.\n"); | |
620 | hash_first(proxy_auth_username_cache); | |
621 | while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) { | |
622 | auth_user = usernamehash->auth_user; | |
623 | username = authenticateUserUsername(auth_user); | |
624 | debug(29, 5) ("authenticateUserCacheRestat: Clearing cache ACL results for user: %s\n", username); | |
625 | aclCacheMatchFlush(&auth_user->proxy_match_cache); | |
626 | } | |
627 | ||
628 | } | |
629 | ||
630 | /* | |
631 | * called to add another auth scheme module | |
632 | */ | |
633 | void | |
634 | authSchemeAdd(char *type, AUTHSSETUP * setup) | |
635 | { | |
636 | int i; | |
637 | /* find the number of currently known authscheme types */ | |
638 | for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) { | |
639 | assert(strcmp(authscheme_list[i].typestr, type) != 0); | |
640 | } | |
641 | /* add the new type */ | |
642 | authscheme_list = xrealloc(authscheme_list, (i + 2) * sizeof(authscheme_entry_t)); | |
643 | memset(&authscheme_list[i + 1], 0, sizeof(authscheme_entry_t)); | |
644 | authscheme_list[i].typestr = type; | |
645 | /* Call the scheme module to set up capabilities and initialize any global data */ | |
646 | setup(&authscheme_list[i]); | |
647 | } | |
648 | ||
649 | ||
650 | ||
651 | /* UserNameCacheAdd: add a auth_user structure to the username cache */ | |
652 | void | |
653 | authenticateUserNameCacheAdd(auth_user_t * auth_user) | |
654 | { | |
655 | auth_user_hash_pointer *usernamehash; | |
656 | usernamehash = memAllocate(MEM_AUTH_USER_HASH); | |
657 | usernamehash->key = authenticateUserUsername(auth_user); | |
658 | usernamehash->auth_user = auth_user; | |
659 | hash_join(proxy_auth_username_cache, (hash_link *) usernamehash); | |
660 | auth_user->usernamehash = usernamehash; | |
661 | /* lock for presence in the cache */ | |
662 | authenticateAuthUserLock(auth_user); | |
663 | } | |
664 | ||
665 | ||
666 | ||
667 | /* | |
668 | * check the user for ip changes timeouts | |
669 | * 0 = failed check | |
670 | * 1 = ip requirements are ok. | |
671 | */ | |
672 | /* TODO: | |
673 | * ip_expire data should be in a struct of it's own - for code reuse */ | |
674 | int | |
675 | authenticateCheckAuthUserIP(struct in_addr request_src_addr, auth_user_request_t * auth_user_request) | |
676 | { | |
677 | char *username = authenticateUserRequestUsername(auth_user_request); | |
678 | if (request_src_addr.s_addr == auth_user_request->auth_user->ipaddr.s_addr || auth_user_request->auth_user->ip_expiretime + Config.authenticateIpTTL <= squid_curtime) { | |
679 | /* user has not moved ip or had the ip timeout expire */ | |
680 | if ((auth_user_request->auth_user->auth_type == AUTH_UNKNOWN) || | |
681 | (auth_user_request->auth_user->auth_type == AUTH_BROKEN)) { | |
682 | debug(29, 1) ("authenticateCheckProxyAuthIP: broken or unknown auth type %d.\n", auth_user_request->auth_user->auth_type); | |
683 | return 0; | |
684 | } | |
685 | username = authenticateUserRequestUsername(auth_user_request); | |
686 | /* Update IP ttl */ | |
687 | auth_user_request->auth_user->ip_expiretime = squid_curtime; | |
688 | auth_user_request->auth_user->ipaddr = request_src_addr; | |
689 | return 1; | |
690 | } else { | |
691 | char *ip1 = xstrdup(inet_ntoa(auth_user_request->auth_user->ipaddr)); | |
692 | char *ip2 = xstrdup(inet_ntoa(request_src_addr)); | |
693 | if (Config.onoff.authenticateIpTTLStrict) { | |
694 | debug(29, 1) ("aclMatchProxyAuth: user '%s' tried to use multiple IP addresses! (%s, %s)\n ", username, ip1, ip2); | |
695 | } else { | |
696 | /* Non-strict mode. Reassign ownership to the new IP */ | |
697 | auth_user_request->auth_user->ipaddr.s_addr = request_src_addr.s_addr; | |
698 | debug(29, 1) ("aclMatchProxyAuth: user '%s' has changed IP address (%s, %s)\n ", username, ip1, ip2); | |
699 | } | |
700 | safe_free(ip1); | |
701 | safe_free(ip2); | |
702 | /* and deny access */ | |
703 | return 0; | |
704 | } | |
705 | } |