]>
Commit | Line | Data |
---|---|---|
f5691f9c | 1 | /* |
f5691f9c | 2 | * DEBUG: section 29 Authenticator |
3 | * AUTHOR: Robert Collins | |
4 | * | |
5 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
6 | * ---------------------------------------------------------- | |
7 | * | |
8 | * Squid is the result of efforts by numerous individuals from | |
9 | * the Internet community; see the CONTRIBUTORS file for full | |
10 | * details. Many organizations have provided support for Squid's | |
11 | * development; see the SPONSORS file for full details. Squid is | |
12 | * Copyrighted (C) 2001 by the Regents of the University of | |
13 | * California; see the COPYRIGHT file for full details. Squid | |
14 | * incorporates software developed and/or copyrighted by other | |
15 | * sources; see the CREDITS file for full details. | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License as published by | |
19 | * the Free Software Foundation; either version 2 of the License, or | |
20 | * (at your option) any later version. | |
26ac0430 | 21 | * |
f5691f9c | 22 | * This program is distributed in the hope that it will be useful, |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26ac0430 | 26 | * |
f5691f9c | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
30 | * | |
31 | * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org> | |
32 | */ | |
33 | ||
582c2af2 | 34 | #include "squid.h" |
3ad63615 AR |
35 | #include "acl/Acl.h" |
36 | #include "acl/Gadgets.h" | |
602d9612 A |
37 | #include "auth/Config.h" |
38 | #include "auth/Gadgets.h" | |
39 | #include "auth/User.h" | |
40 | #include "auth/UserRequest.h" | |
a553a5a3 | 41 | #include "event.h" |
582c2af2 | 42 | #include "globals.h" |
4d5904f7 | 43 | #include "SquidConfig.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 | ||
ea0695f2 AJ |
82 | /** |
83 | * Combine two user structs. ONLY to be called from within a scheme | |
f5691f9c | 84 | * module. The scheme module is responsible for ensuring that the |
85 | * two users _can_ be merged without invalidating all the request | |
86 | * scheme data. The scheme is also responsible for merging any user | |
87 | * related scheme data itself. | |
58e94342 AJ |
88 | * The caller is responsible for altering all refcount pointers to |
89 | * the 'from' object. They are invalid once this method is complete. | |
f5691f9c | 90 | */ |
91 | void | |
d87154ee | 92 | Auth::User::absorb(Auth::User::Pointer from) |
f5691f9c | 93 | { |
f5691f9c | 94 | /* |
56a49fda AJ |
95 | * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data |
96 | * dlink_list proxy_auth_list; | |
97 | * dlink_list proxy_match_cache; | |
f5691f9c | 98 | */ |
56a49fda | 99 | |
d87154ee | 100 | debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'."); |
f5691f9c | 101 | |
56a49fda AJ |
102 | /* absorb the list of IP address sources (for max_user_ip controls) */ |
103 | AuthUserIP *new_ipdata; | |
104 | while (from->ip_list.head != NULL) { | |
105 | new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data); | |
106 | ||
107 | /* If this IP has expired - ignore the expensive merge actions. */ | |
c35dd848 | 108 | if (new_ipdata->ip_expiretime <= squid_curtime) { |
56a49fda AJ |
109 | /* This IP has expired - remove from the source list */ |
110 | dlinkDelete(&new_ipdata->node, &(from->ip_list)); | |
111 | cbdataFree(new_ipdata); | |
112 | /* catch incipient underflow */ | |
a2f5277a | 113 | -- from->ipcount; |
56a49fda AJ |
114 | } else { |
115 | /* add to our list. replace if already present. */ | |
116 | AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data); | |
117 | bool found = false; | |
118 | while (ipdata) { | |
119 | AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data); | |
120 | ||
121 | if (ipdata->ipaddr == new_ipdata->ipaddr) { | |
122 | /* This IP has already been seen. */ | |
123 | found = true; | |
124 | /* update IP ttl and stop searching. */ | |
125 | ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime); | |
126 | break; | |
c35dd848 | 127 | } else if (ipdata->ip_expiretime <= squid_curtime) { |
56a49fda AJ |
128 | /* This IP has expired - cleanup the destination list */ |
129 | dlinkDelete(&ipdata->node, &ip_list); | |
130 | cbdataFree(ipdata); | |
131 | /* catch incipient underflow */ | |
132 | assert(ipcount); | |
a2f5277a | 133 | -- ipcount; |
56a49fda AJ |
134 | } |
135 | ||
136 | ipdata = tempnode; | |
137 | } | |
138 | ||
139 | if (!found) { | |
140 | /* This ip is not in the seen list. Add it. */ | |
141 | dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list); | |
742a021b | 142 | ++ipcount; |
56a49fda AJ |
143 | /* remove from the source list */ |
144 | dlinkDelete(&new_ipdata->node, &(from->ip_list)); | |
742a021b | 145 | ++from->ipcount; |
56a49fda AJ |
146 | } |
147 | } | |
148 | } | |
f5691f9c | 149 | } |
150 | ||
d87154ee | 151 | Auth::User::~User() |
f5691f9c | 152 | { |
d87154ee | 153 | debugs(29, 5, HERE << "Freeing auth_user '" << this << "'."); |
8bf217bd | 154 | assert(LockCount() == 0); |
56a49fda | 155 | |
f5691f9c | 156 | /* free cached acl results */ |
157 | aclCacheMatchFlush(&proxy_match_cache); | |
158 | ||
159 | /* free seen ip address's */ | |
160 | clearIp(); | |
161 | ||
3f5f1a01 | 162 | if (username_) |
163 | xfree((char*)username_); | |
f5691f9c | 164 | |
165 | /* prevent accidental reuse */ | |
616cfc4c | 166 | auth_type = Auth::AUTH_UNKNOWN; |
f5691f9c | 167 | } |
168 | ||
169 | void | |
d87154ee | 170 | Auth::User::cacheInit(void) |
f5691f9c | 171 | { |
172 | if (!proxy_auth_username_cache) { | |
173 | /* First time around, 7921 should be big enough */ | |
56a49fda | 174 | proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); |
f5691f9c | 175 | assert(proxy_auth_username_cache); |
d87154ee | 176 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1); |
af70c154 | 177 | last_discard = squid_curtime; |
f5691f9c | 178 | } |
179 | } | |
180 | ||
181 | void | |
d87154ee | 182 | Auth::User::CachedACLsReset() |
f5691f9c | 183 | { |
184 | /* | |
f5691f9c | 185 | * This must complete all at once, because we are ensuring correctness. |
186 | */ | |
187 | AuthUserHashPointer *usernamehash; | |
d87154ee AJ |
188 | Auth::User::Pointer auth_user; |
189 | debugs(29, 3, HERE << "Flushing the ACL caches for all users."); | |
f5691f9c | 190 | hash_first(proxy_auth_username_cache); |
191 | ||
192 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
193 | auth_user = usernamehash->user(); | |
f5691f9c | 194 | /* free cached acl results */ |
195 | aclCacheMatchFlush(&auth_user->proxy_match_cache); | |
f5691f9c | 196 | } |
197 | ||
d87154ee | 198 | debugs(29, 3, HERE << "Finished."); |
f5691f9c | 199 | } |
200 | ||
201 | void | |
d87154ee | 202 | Auth::User::cacheCleanup(void *datanotused) |
f5691f9c | 203 | { |
204 | /* | |
205 | * We walk the hash by username as that is the unique key we use. | |
206 | * For big hashs we could consider stepping through the cache, 100/200 | |
207 | * entries at a time. Lets see how it flys first. | |
208 | */ | |
209 | AuthUserHashPointer *usernamehash; | |
d87154ee | 210 | Auth::User::Pointer auth_user; |
f5691f9c | 211 | char const *username = NULL; |
d87154ee AJ |
212 | debugs(29, 3, HERE << "Cleaning the user cache now"); |
213 | debugs(29, 3, HERE << "Current time: " << current_time.tv_sec); | |
f5691f9c | 214 | hash_first(proxy_auth_username_cache); |
215 | ||
216 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
217 | auth_user = usernamehash->user(); | |
218 | username = auth_user->username(); | |
219 | ||
427cb33a | 220 | /* if we need to have indedendent expiry clauses, insert a module call |
f5691f9c | 221 | * here */ |
d87154ee | 222 | debugs(29, 4, HERE << "Cache entry:\n\tType: " << |
bf8fe701 | 223 | auth_user->auth_type << "\n\tUsername: " << username << |
224 | "\n\texpires: " << | |
d87154ee | 225 | (long int) (auth_user->expiretime + ::Config.authenticateTTL) << |
8bf217bd | 226 | "\n\treferences: " << auth_user->LockCount()); |
f5691f9c | 227 | |
d87154ee AJ |
228 | if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) { |
229 | debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout."); | |
56a49fda AJ |
230 | |
231 | /* Old credentials are always removed. Existing users must hold their own | |
d87154ee | 232 | * Auth::User::Pointer to the credentials. Cache exists only for finding |
56a49fda AJ |
233 | * and re-using current valid credentials. |
234 | */ | |
235 | hash_remove_link(proxy_auth_username_cache, usernamehash); | |
236 | delete usernamehash; | |
f5691f9c | 237 | } |
238 | } | |
239 | ||
d87154ee AJ |
240 | debugs(29, 3, HERE << "Finished cleaning the user cache."); |
241 | eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1); | |
af70c154 | 242 | last_discard = squid_curtime; |
f5691f9c | 243 | } |
244 | ||
245 | void | |
d87154ee | 246 | Auth::User::clearIp() |
f5691f9c | 247 | { |
56a49fda | 248 | AuthUserIP *ipdata, *tempnode; |
f5691f9c | 249 | |
56a49fda | 250 | ipdata = (AuthUserIP *) ip_list.head; |
f5691f9c | 251 | |
252 | while (ipdata) { | |
56a49fda | 253 | tempnode = (AuthUserIP *) ipdata->node.next; |
f5691f9c | 254 | /* walk the ip list */ |
255 | dlinkDelete(&ipdata->node, &ip_list); | |
256 | cbdataFree(ipdata); | |
257 | /* catch incipient underflow */ | |
258 | assert(ipcount); | |
a2f5277a | 259 | -- ipcount; |
f5691f9c | 260 | ipdata = tempnode; |
261 | } | |
262 | ||
263 | /* integrity check */ | |
264 | assert(ipcount == 0); | |
265 | } | |
266 | ||
267 | void | |
d87154ee | 268 | Auth::User::removeIp(Ip::Address ipaddr) |
4c19ba24 | 269 | { |
56a49fda | 270 | AuthUserIP *ipdata = (AuthUserIP *) ip_list.head; |
4c19ba24 | 271 | |
26ac0430 | 272 | while (ipdata) { |
4c19ba24 | 273 | /* walk the ip list */ |
274 | ||
cc192b50 | 275 | if (ipdata->ipaddr == ipaddr) { |
4c19ba24 | 276 | /* remove the node */ |
277 | dlinkDelete(&ipdata->node, &ip_list); | |
278 | cbdataFree(ipdata); | |
279 | /* catch incipient underflow */ | |
280 | assert(ipcount); | |
a2f5277a | 281 | -- ipcount; |
4c19ba24 | 282 | return; |
283 | } | |
284 | ||
56a49fda | 285 | ipdata = (AuthUserIP *) ipdata->node.next; |
4c19ba24 | 286 | } |
287 | ||
288 | } | |
289 | ||
290 | void | |
d87154ee | 291 | Auth::User::addIp(Ip::Address ipaddr) |
4c19ba24 | 292 | { |
56a49fda | 293 | AuthUserIP *ipdata = (AuthUserIP *) ip_list.head; |
4c19ba24 | 294 | int found = 0; |
295 | ||
56a49fda | 296 | CBDATA_INIT_TYPE(AuthUserIP); |
4c19ba24 | 297 | |
298 | /* | |
299 | * we walk the entire list to prevent the first item in the list | |
300 | * preventing old entries being flushed and locking a user out after | |
301 | * a timeout+reconfigure | |
302 | */ | |
26ac0430 | 303 | while (ipdata) { |
56a49fda | 304 | AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next; |
4c19ba24 | 305 | /* walk the ip list */ |
f5691f9c | 306 | |
cc192b50 | 307 | if (ipdata->ipaddr == ipaddr) { |
308 | /* This ip has already been seen. */ | |
4c19ba24 | 309 | found = 1; |
310 | /* update IP ttl */ | |
311 | ipdata->ip_expiretime = squid_curtime; | |
c35dd848 | 312 | } else if (ipdata->ip_expiretime <= squid_curtime) { |
4c19ba24 | 313 | /* This IP has expired - remove from the seen list */ |
314 | dlinkDelete(&ipdata->node, &ip_list); | |
315 | cbdataFree(ipdata); | |
316 | /* catch incipient underflow */ | |
317 | assert(ipcount); | |
a2f5277a | 318 | -- ipcount; |
4c19ba24 | 319 | } |
320 | ||
321 | ipdata = tempnode; | |
322 | } | |
323 | ||
324 | if (found) | |
325 | return; | |
326 | ||
327 | /* This ip is not in the seen list */ | |
56a49fda | 328 | ipdata = cbdataAlloc(AuthUserIP); |
4c19ba24 | 329 | |
c35dd848 | 330 | ipdata->ip_expiretime = squid_curtime + ::Config.authenticateIpTTL; |
4c19ba24 | 331 | |
332 | ipdata->ipaddr = ipaddr; | |
333 | ||
334 | dlinkAddTail(ipdata, &ipdata->node, &ip_list); | |
335 | ||
742a021b | 336 | ++ipcount; |
4c19ba24 | 337 | |
d87154ee | 338 | debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")"); |
4c19ba24 | 339 | } |
340 | ||
427cb33a | 341 | /** |
d87154ee | 342 | * Add the Auth::User structure to the username cache. |
427cb33a | 343 | */ |
4c19ba24 | 344 | void |
d87154ee | 345 | Auth::User::addToNameCache() |
f5691f9c | 346 | { |
427cb33a | 347 | /* AuthUserHashPointer will self-register with the username cache */ |
af70c154 | 348 | new AuthUserHashPointer(this); |
f5691f9c | 349 | } |
350 | ||
56a49fda AJ |
351 | /** |
352 | * Dump the username cache statictics for viewing... | |
353 | */ | |
f5691f9c | 354 | void |
d87154ee | 355 | Auth::User::UsernameCacheStats(StoreEntry *output) |
f5691f9c | 356 | { |
56a49fda | 357 | AuthUserHashPointer *usernamehash; |
f5691f9c | 358 | |
56a49fda AJ |
359 | /* overview of username cache */ |
360 | storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size); | |
d87154ee AJ |
361 | storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n", |
362 | static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime)); | |
f5691f9c | 363 | |
56a49fda | 364 | /* cache dump column titles */ |
d232141d | 365 | storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n", |
56a49fda | 366 | "Type", |
d232141d | 367 | "State", |
af70c154 AJ |
368 | "Check TTL", |
369 | "Cache TTL", | |
56a49fda | 370 | "Username"); |
d232141d | 371 | storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n"); |
f5691f9c | 372 | |
56a49fda AJ |
373 | hash_first(proxy_auth_username_cache); |
374 | while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) { | |
d87154ee | 375 | Auth::User::Pointer auth_user = usernamehash->user(); |
f5691f9c | 376 | |
d232141d | 377 | storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n", |
616cfc4c | 378 | Auth::Type_str[auth_user->auth_type], |
d87154ee | 379 | CredentialState_str[auth_user->credentials()], |
56a49fda | 380 | auth_user->ttl(), |
d87154ee | 381 | static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL), |
56a49fda | 382 | auth_user->username() |
ec5858ff | 383 | ); |
56a49fda | 384 | } |
f5691f9c | 385 | } |