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