]>
Commit | Line | Data |
---|---|---|
f5691f9c | 1 | /* |
262a0e14 | 2 | * $Id$ |
f5691f9c | 3 | * |
4 | * DEBUG: section 29 Authenticator | |
5 | * AUTHOR: Robert Collins | |
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. | |
26ac0430 | 23 | * |
f5691f9c | 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. | |
26ac0430 | 28 | * |
f5691f9c | 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" | |
2d2b0bb7 AR |
37 | #include "auth/User.h" |
38 | #include "auth/UserRequest.h" | |
39 | #include "auth/Config.h" | |
3ad63615 AR |
40 | #include "auth/Gadgets.h" |
41 | #include "acl/Acl.h" | |
42 | #include "acl/Gadgets.h" | |
a553a5a3 | 43 | #include "event.h" |
4c19ba24 | 44 | #include "SquidTime.h" |
56a49fda | 45 | #include "Store.h" |
f5691f9c | 46 | |
32d002cb | 47 | #if !_USE_INLINE_ |
2d2b0bb7 | 48 | #include "auth/User.cci" |
f5691f9c | 49 | #endif |
50 | ||
4c19ba24 | 51 | // This should be converted into a pooled type. Does not need to be cbdata |
56a49fda AJ |
52 | CBDATA_TYPE(AuthUserIP); |
53 | ||
d87154ee | 54 | time_t Auth::User::last_discard = 0; |
af70c154 | 55 | |
d87154ee | 56 | Auth::User::User(Auth::Config *aConfig) : |
616cfc4c | 57 | auth_type(Auth::AUTH_UNKNOWN), |
56a49fda | 58 | config(aConfig), |
56a49fda AJ |
59 | ipcount(0), |
60 | expiretime(0), | |
d87154ee | 61 | credentials_state(Auth::Unchecked), |
56a49fda | 62 | username_(NULL) |
f5691f9c | 63 | { |
64 | proxy_auth_list.head = proxy_auth_list.tail = NULL; | |
65 | proxy_match_cache.head = proxy_match_cache.tail = NULL; | |
66 | ip_list.head = ip_list.tail = NULL; | |
d87154ee | 67 | debugs(29, 5, HERE << "Initialised auth_user '" << this << "'."); |
f5691f9c | 68 | } |
69 | ||
d87154ee AJ |
70 | Auth::CredentialState |
71 | Auth::User::credentials() const | |
d232141d AJ |
72 | { |
73 | return credentials_state; | |
74 | } | |
75 | ||
76 | void | |
d87154ee | 77 | Auth::User::credentials(CredentialState newCreds) |
d232141d AJ |
78 | { |
79 | credentials_state = newCreds; | |
80 | } | |
81 | ||
82 | ||
ea0695f2 AJ |
83 | /** |
84 | * Combine two user structs. ONLY to be called from within a scheme | |
f5691f9c | 85 | * module. The scheme module is responsible for ensuring that the |
86 | * two users _can_ be merged without invalidating all the request | |
87 | * scheme data. The scheme is also responsible for merging any user | |
88 | * related scheme data itself. | |
89 | */ | |
90 | void | |
d87154ee | 91 | Auth::User::absorb(Auth::User::Pointer from) |
f5691f9c | 92 | { |
56a49fda | 93 | |
d87154ee | 94 | /* RefCount children CANNOT be merged like this. The external Auth::User::Pointer's cannot be changed. */ |
56a49fda AJ |
95 | |
96 | /* check that we only have the two references: | |
97 | * 1) our function scope | |
98 | * 2) the parsing function scope) | |
99 | */ | |
100 | assert(from->RefCountCount() == 2); | |
101 | ||
f5691f9c | 102 | /* |
56a49fda AJ |
103 | * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data |
104 | * dlink_list proxy_auth_list; | |
105 | * dlink_list proxy_match_cache; | |
f5691f9c | 106 | */ |
56a49fda | 107 | |
d87154ee | 108 | debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'."); |
f5691f9c | 109 | |
56a49fda AJ |
110 | /* absorb the list of IP address sources (for max_user_ip controls) */ |
111 | AuthUserIP *new_ipdata; | |
112 | while (from->ip_list.head != NULL) { | |
113 | new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data); | |
114 | ||
115 | /* If this IP has expired - ignore the expensive merge actions. */ | |
d87154ee | 116 | if (new_ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) { |
56a49fda AJ |
117 | /* This IP has expired - remove from the source list */ |
118 | dlinkDelete(&new_ipdata->node, &(from->ip_list)); | |
119 | cbdataFree(new_ipdata); | |
120 | /* catch incipient underflow */ | |
121 | from->ipcount--; | |
122 | } else { | |
123 | /* add to our list. replace if already present. */ | |
124 | AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data); | |
125 | bool found = false; | |
126 | while (ipdata) { | |
127 | AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data); | |
128 | ||
129 | if (ipdata->ipaddr == new_ipdata->ipaddr) { | |
130 | /* This IP has already been seen. */ | |
131 | found = true; | |
132 | /* update IP ttl and stop searching. */ | |
133 | ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime); | |
134 | break; | |
d87154ee | 135 | } else if (ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) { |
56a49fda AJ |
136 | /* This IP has expired - cleanup the destination list */ |
137 | dlinkDelete(&ipdata->node, &ip_list); | |
138 | cbdataFree(ipdata); | |
139 | /* catch incipient underflow */ | |
140 | assert(ipcount); | |
141 | ipcount--; | |
142 | } | |
143 | ||
144 | ipdata = tempnode; | |
145 | } | |
146 | ||
147 | if (!found) { | |
148 | /* This ip is not in the seen list. Add it. */ | |
149 | dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list); | |
150 | ipcount++; | |
151 | /* remove from the source list */ | |
152 | dlinkDelete(&new_ipdata->node, &(from->ip_list)); | |
153 | from->ipcount--; | |
154 | } | |
155 | } | |
156 | } | |
f5691f9c | 157 | } |
158 | ||
d87154ee | 159 | Auth::User::~User() |
f5691f9c | 160 | { |
d87154ee | 161 | debugs(29, 5, HERE << "Freeing auth_user '" << this << "'."); |
56a49fda AJ |
162 | assert(RefCountCount() == 0); |
163 | ||
f5691f9c | 164 | /* free cached acl results */ |
165 | aclCacheMatchFlush(&proxy_match_cache); | |
166 | ||
167 | /* free seen ip address's */ | |
168 | clearIp(); | |
169 | ||
3f5f1a01 | 170 | if (username_) |
171 | xfree((char*)username_); | |
f5691f9c | 172 | |
173 | /* prevent accidental reuse */ | |
616cfc4c | 174 | auth_type = Auth::AUTH_UNKNOWN; |
f5691f9c | 175 | } |
176 | ||
177 | void | |
d87154ee | 178 | Auth::User::cacheInit(void) |
f5691f9c | 179 | { |
180 | if (!proxy_auth_username_cache) { | |
181 | /* First time around, 7921 should be big enough */ | |
56a49fda | 182 | proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); |
f5691f9c | 183 | assert(proxy_auth_username_cache); |
d87154ee | 184 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1); |
af70c154 | 185 | last_discard = squid_curtime; |
f5691f9c | 186 | } |
187 | } | |
188 | ||
189 | void | |
d87154ee | 190 | Auth::User::CachedACLsReset() |
f5691f9c | 191 | { |
192 | /* | |
f5691f9c | 193 | * This must complete all at once, because we are ensuring correctness. |
194 | */ | |
195 | AuthUserHashPointer *usernamehash; | |
d87154ee AJ |
196 | Auth::User::Pointer auth_user; |
197 | debugs(29, 3, HERE << "Flushing the ACL caches for all users."); | |
f5691f9c | 198 | hash_first(proxy_auth_username_cache); |
199 | ||
200 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
201 | auth_user = usernamehash->user(); | |
f5691f9c | 202 | /* free cached acl results */ |
203 | aclCacheMatchFlush(&auth_user->proxy_match_cache); | |
f5691f9c | 204 | } |
205 | ||
d87154ee | 206 | debugs(29, 3, HERE << "Finished."); |
f5691f9c | 207 | } |
208 | ||
209 | void | |
d87154ee | 210 | Auth::User::cacheCleanup(void *datanotused) |
f5691f9c | 211 | { |
212 | /* | |
213 | * We walk the hash by username as that is the unique key we use. | |
214 | * For big hashs we could consider stepping through the cache, 100/200 | |
215 | * entries at a time. Lets see how it flys first. | |
216 | */ | |
217 | AuthUserHashPointer *usernamehash; | |
d87154ee | 218 | Auth::User::Pointer auth_user; |
f5691f9c | 219 | char const *username = NULL; |
d87154ee AJ |
220 | debugs(29, 3, HERE << "Cleaning the user cache now"); |
221 | debugs(29, 3, HERE << "Current time: " << current_time.tv_sec); | |
f5691f9c | 222 | hash_first(proxy_auth_username_cache); |
223 | ||
224 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
225 | auth_user = usernamehash->user(); | |
226 | username = auth_user->username(); | |
227 | ||
427cb33a | 228 | /* if we need to have indedendent expiry clauses, insert a module call |
f5691f9c | 229 | * here */ |
d87154ee | 230 | debugs(29, 4, HERE << "Cache entry:\n\tType: " << |
bf8fe701 | 231 | auth_user->auth_type << "\n\tUsername: " << username << |
232 | "\n\texpires: " << | |
d87154ee | 233 | (long int) (auth_user->expiretime + ::Config.authenticateTTL) << |
56a49fda | 234 | "\n\treferences: " << (long int) auth_user->RefCountCount()); |
f5691f9c | 235 | |
d87154ee AJ |
236 | if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) { |
237 | debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout."); | |
56a49fda AJ |
238 | |
239 | /* Old credentials are always removed. Existing users must hold their own | |
d87154ee | 240 | * Auth::User::Pointer to the credentials. Cache exists only for finding |
56a49fda AJ |
241 | * and re-using current valid credentials. |
242 | */ | |
243 | hash_remove_link(proxy_auth_username_cache, usernamehash); | |
244 | delete usernamehash; | |
f5691f9c | 245 | } |
246 | } | |
247 | ||
d87154ee AJ |
248 | debugs(29, 3, HERE << "Finished cleaning the user cache."); |
249 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1); | |
af70c154 | 250 | last_discard = squid_curtime; |
f5691f9c | 251 | } |
252 | ||
253 | void | |
d87154ee | 254 | Auth::User::clearIp() |
f5691f9c | 255 | { |
56a49fda | 256 | AuthUserIP *ipdata, *tempnode; |
f5691f9c | 257 | |
56a49fda | 258 | ipdata = (AuthUserIP *) ip_list.head; |
f5691f9c | 259 | |
260 | while (ipdata) { | |
56a49fda | 261 | tempnode = (AuthUserIP *) ipdata->node.next; |
f5691f9c | 262 | /* walk the ip list */ |
263 | dlinkDelete(&ipdata->node, &ip_list); | |
264 | cbdataFree(ipdata); | |
265 | /* catch incipient underflow */ | |
266 | assert(ipcount); | |
267 | ipcount--; | |
268 | ipdata = tempnode; | |
269 | } | |
270 | ||
271 | /* integrity check */ | |
272 | assert(ipcount == 0); | |
273 | } | |
274 | ||
275 | void | |
d87154ee | 276 | Auth::User::removeIp(Ip::Address ipaddr) |
4c19ba24 | 277 | { |
56a49fda | 278 | AuthUserIP *ipdata = (AuthUserIP *) ip_list.head; |
4c19ba24 | 279 | |
26ac0430 | 280 | while (ipdata) { |
4c19ba24 | 281 | /* walk the ip list */ |
282 | ||
cc192b50 | 283 | if (ipdata->ipaddr == ipaddr) { |
4c19ba24 | 284 | /* remove the node */ |
285 | dlinkDelete(&ipdata->node, &ip_list); | |
286 | cbdataFree(ipdata); | |
287 | /* catch incipient underflow */ | |
288 | assert(ipcount); | |
289 | ipcount--; | |
290 | return; | |
291 | } | |
292 | ||
56a49fda | 293 | ipdata = (AuthUserIP *) ipdata->node.next; |
4c19ba24 | 294 | } |
295 | ||
296 | } | |
297 | ||
298 | void | |
d87154ee | 299 | Auth::User::addIp(Ip::Address ipaddr) |
4c19ba24 | 300 | { |
56a49fda | 301 | AuthUserIP *ipdata = (AuthUserIP *) ip_list.head; |
4c19ba24 | 302 | int found = 0; |
303 | ||
56a49fda | 304 | CBDATA_INIT_TYPE(AuthUserIP); |
4c19ba24 | 305 | |
306 | /* | |
307 | * we walk the entire list to prevent the first item in the list | |
308 | * preventing old entries being flushed and locking a user out after | |
309 | * a timeout+reconfigure | |
310 | */ | |
26ac0430 | 311 | while (ipdata) { |
56a49fda | 312 | AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next; |
4c19ba24 | 313 | /* walk the ip list */ |
f5691f9c | 314 | |
cc192b50 | 315 | if (ipdata->ipaddr == ipaddr) { |
316 | /* This ip has already been seen. */ | |
4c19ba24 | 317 | found = 1; |
318 | /* update IP ttl */ | |
319 | ipdata->ip_expiretime = squid_curtime; | |
d87154ee | 320 | } else if (ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) { |
4c19ba24 | 321 | /* This IP has expired - remove from the seen list */ |
322 | dlinkDelete(&ipdata->node, &ip_list); | |
323 | cbdataFree(ipdata); | |
324 | /* catch incipient underflow */ | |
325 | assert(ipcount); | |
326 | ipcount--; | |
327 | } | |
328 | ||
329 | ipdata = tempnode; | |
330 | } | |
331 | ||
332 | if (found) | |
333 | return; | |
334 | ||
335 | /* This ip is not in the seen list */ | |
56a49fda | 336 | ipdata = cbdataAlloc(AuthUserIP); |
4c19ba24 | 337 | |
338 | ipdata->ip_expiretime = squid_curtime; | |
339 | ||
340 | ipdata->ipaddr = ipaddr; | |
341 | ||
342 | dlinkAddTail(ipdata, &ipdata->node, &ip_list); | |
343 | ||
344 | ipcount++; | |
345 | ||
d87154ee | 346 | debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")"); |
4c19ba24 | 347 | } |
348 | ||
427cb33a | 349 | /** |
d87154ee | 350 | * Add the Auth::User structure to the username cache. |
427cb33a | 351 | */ |
4c19ba24 | 352 | void |
d87154ee | 353 | Auth::User::addToNameCache() |
f5691f9c | 354 | { |
427cb33a | 355 | /* AuthUserHashPointer will self-register with the username cache */ |
af70c154 | 356 | new AuthUserHashPointer(this); |
f5691f9c | 357 | } |
358 | ||
56a49fda AJ |
359 | /** |
360 | * Dump the username cache statictics for viewing... | |
361 | */ | |
f5691f9c | 362 | void |
d87154ee | 363 | Auth::User::UsernameCacheStats(StoreEntry *output) |
f5691f9c | 364 | { |
56a49fda | 365 | AuthUserHashPointer *usernamehash; |
f5691f9c | 366 | |
56a49fda AJ |
367 | /* overview of username cache */ |
368 | storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size); | |
d87154ee AJ |
369 | storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n", |
370 | static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime)); | |
f5691f9c | 371 | |
56a49fda | 372 | /* cache dump column titles */ |
d232141d | 373 | storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n", |
56a49fda | 374 | "Type", |
d232141d | 375 | "State", |
af70c154 AJ |
376 | "Check TTL", |
377 | "Cache TTL", | |
56a49fda | 378 | "Username"); |
d232141d | 379 | storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n"); |
f5691f9c | 380 | |
56a49fda AJ |
381 | hash_first(proxy_auth_username_cache); |
382 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
d87154ee | 383 | Auth::User::Pointer auth_user = usernamehash->user(); |
f5691f9c | 384 | |
d232141d | 385 | storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n", |
616cfc4c | 386 | Auth::Type_str[auth_user->auth_type], |
d87154ee | 387 | CredentialState_str[auth_user->credentials()], |
56a49fda | 388 | auth_user->ttl(), |
d87154ee | 389 | static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL), |
56a49fda | 390 | auth_user->username() |
ec5858ff | 391 | ); |
56a49fda | 392 | } |
f5691f9c | 393 | } |