2 * DEBUG: section 29 Authenticator
3 * AUTHOR: Robert Collins
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
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.
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.
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.
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.
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
36 #include "acl/Gadgets.h"
37 #include "auth/Config.h"
38 #include "auth/Gadgets.h"
39 #include "auth/User.h"
40 #include "auth/UserRequest.h"
43 #include "SquidConfig.h"
44 #include "SquidTime.h"
47 // This should be converted into a pooled type. Does not need to be cbdata
48 CBDATA_TYPE(AuthUserIP
);
50 time_t Auth::User::last_discard
= 0;
52 Auth::User::User(Auth::Config
*aConfig
) :
53 auth_type(Auth::AUTH_UNKNOWN
),
57 credentials_state(Auth::Unchecked
),
60 proxy_auth_list
.head
= proxy_auth_list
.tail
= NULL
;
61 proxy_match_cache
.head
= proxy_match_cache
.tail
= NULL
;
62 ip_list
.head
= ip_list
.tail
= NULL
;
63 debugs(29, 5, HERE
<< "Initialised auth_user '" << this << "'.");
67 Auth::User::credentials() const
69 return credentials_state
;
73 Auth::User::credentials(CredentialState newCreds
)
75 credentials_state
= newCreds
;
79 * Combine two user structs. ONLY to be called from within a scheme
80 * module. The scheme module is responsible for ensuring that the
81 * two users _can_ be merged without invalidating all the request
82 * scheme data. The scheme is also responsible for merging any user
83 * related scheme data itself.
84 * The caller is responsible for altering all refcount pointers to
85 * the 'from' object. They are invalid once this method is complete.
88 Auth::User::absorb(Auth::User::Pointer from
)
91 * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
92 * dlink_list proxy_auth_list;
93 * dlink_list proxy_match_cache;
96 debugs(29, 5, HERE
<< "auth_user '" << from
<< "' into auth_user '" << this << "'.");
98 /* absorb the list of IP address sources (for max_user_ip controls) */
99 AuthUserIP
*new_ipdata
;
100 while (from
->ip_list
.head
!= NULL
) {
101 new_ipdata
= static_cast<AuthUserIP
*>(from
->ip_list
.head
->data
);
103 /* If this IP has expired - ignore the expensive merge actions. */
104 if (new_ipdata
->ip_expiretime
<= squid_curtime
) {
105 /* This IP has expired - remove from the source list */
106 dlinkDelete(&new_ipdata
->node
, &(from
->ip_list
));
107 cbdataFree(new_ipdata
);
108 /* catch incipient underflow */
111 /* add to our list. replace if already present. */
112 AuthUserIP
*ipdata
= static_cast<AuthUserIP
*>(ip_list
.head
->data
);
115 AuthUserIP
*tempnode
= static_cast<AuthUserIP
*>(ipdata
->node
.next
->data
);
117 if (ipdata
->ipaddr
== new_ipdata
->ipaddr
) {
118 /* This IP has already been seen. */
120 /* update IP ttl and stop searching. */
121 ipdata
->ip_expiretime
= max(ipdata
->ip_expiretime
, new_ipdata
->ip_expiretime
);
123 } else if (ipdata
->ip_expiretime
<= squid_curtime
) {
124 /* This IP has expired - cleanup the destination list */
125 dlinkDelete(&ipdata
->node
, &ip_list
);
127 /* catch incipient underflow */
136 /* This ip is not in the seen list. Add it. */
137 dlinkAddTail(&new_ipdata
->node
, &ipdata
->node
, &ip_list
);
139 /* remove from the source list */
140 dlinkDelete(&new_ipdata
->node
, &(from
->ip_list
));
149 debugs(29, 5, HERE
<< "Freeing auth_user '" << this << "'.");
150 assert(LockCount() == 0);
152 /* free cached acl results */
153 aclCacheMatchFlush(&proxy_match_cache
);
155 /* free seen ip address's */
159 xfree((char*)username_
);
161 /* prevent accidental reuse */
162 auth_type
= Auth::AUTH_UNKNOWN
;
166 Auth::User::cacheInit(void)
168 if (!proxy_auth_username_cache
) {
169 /* First time around, 7921 should be big enough */
170 proxy_auth_username_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
171 assert(proxy_auth_username_cache
);
172 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, ::Config
.authenticateGCInterval
, 1);
173 last_discard
= squid_curtime
;
178 Auth::User::CachedACLsReset()
181 * This must complete all at once, because we are ensuring correctness.
183 AuthUserHashPointer
*usernamehash
;
184 Auth::User::Pointer auth_user
;
185 debugs(29, 3, HERE
<< "Flushing the ACL caches for all users.");
186 hash_first(proxy_auth_username_cache
);
188 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
189 auth_user
= usernamehash
->user();
190 /* free cached acl results */
191 aclCacheMatchFlush(&auth_user
->proxy_match_cache
);
194 debugs(29, 3, HERE
<< "Finished.");
198 Auth::User::cacheCleanup(void *datanotused
)
201 * We walk the hash by username as that is the unique key we use.
202 * For big hashs we could consider stepping through the cache, 100/200
203 * entries at a time. Lets see how it flys first.
205 AuthUserHashPointer
*usernamehash
;
206 Auth::User::Pointer auth_user
;
207 char const *username
= NULL
;
208 debugs(29, 3, HERE
<< "Cleaning the user cache now");
209 debugs(29, 3, HERE
<< "Current time: " << current_time
.tv_sec
);
210 hash_first(proxy_auth_username_cache
);
212 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
213 auth_user
= usernamehash
->user();
214 username
= auth_user
->username();
216 /* if we need to have indedendent expiry clauses, insert a module call
218 debugs(29, 4, HERE
<< "Cache entry:\n\tType: " <<
219 auth_user
->auth_type
<< "\n\tUsername: " << username
<<
221 (long int) (auth_user
->expiretime
+ ::Config
.authenticateTTL
) <<
222 "\n\treferences: " << auth_user
->LockCount());
224 if (auth_user
->expiretime
+ ::Config
.authenticateTTL
<= current_time
.tv_sec
) {
225 debugs(29, 5, HERE
<< "Removing user " << username
<< " from cache due to timeout.");
227 /* Old credentials are always removed. Existing users must hold their own
228 * Auth::User::Pointer to the credentials. Cache exists only for finding
229 * and re-using current valid credentials.
231 hash_remove_link(proxy_auth_username_cache
, usernamehash
);
236 debugs(29, 3, HERE
<< "Finished cleaning the user cache.");
237 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, ::Config
.authenticateGCInterval
, 1);
238 last_discard
= squid_curtime
;
242 Auth::User::clearIp()
244 AuthUserIP
*ipdata
, *tempnode
;
246 ipdata
= (AuthUserIP
*) ip_list
.head
;
249 tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
250 /* walk the ip list */
251 dlinkDelete(&ipdata
->node
, &ip_list
);
253 /* catch incipient underflow */
259 /* integrity check */
260 assert(ipcount
== 0);
264 Auth::User::removeIp(Ip::Address ipaddr
)
266 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
269 /* walk the ip list */
271 if (ipdata
->ipaddr
== ipaddr
) {
272 /* remove the node */
273 dlinkDelete(&ipdata
->node
, &ip_list
);
275 /* catch incipient underflow */
281 ipdata
= (AuthUserIP
*) ipdata
->node
.next
;
287 Auth::User::addIp(Ip::Address ipaddr
)
289 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
292 CBDATA_INIT_TYPE(AuthUserIP
);
295 * we walk the entire list to prevent the first item in the list
296 * preventing old entries being flushed and locking a user out after
297 * a timeout+reconfigure
300 AuthUserIP
*tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
301 /* walk the ip list */
303 if (ipdata
->ipaddr
== ipaddr
) {
304 /* This ip has already been seen. */
307 ipdata
->ip_expiretime
= squid_curtime
;
308 } else if (ipdata
->ip_expiretime
<= squid_curtime
) {
309 /* This IP has expired - remove from the seen list */
310 dlinkDelete(&ipdata
->node
, &ip_list
);
312 /* catch incipient underflow */
323 /* This ip is not in the seen list */
324 ipdata
= cbdataAlloc(AuthUserIP
);
326 ipdata
->ip_expiretime
= squid_curtime
+ ::Config
.authenticateIpTTL
;
328 ipdata
->ipaddr
= ipaddr
;
330 dlinkAddTail(ipdata
, &ipdata
->node
, &ip_list
);
334 debugs(29, 2, HERE
<< "user '" << username() << "' has been seen at a new IP address (" << ipaddr
<< ")");
338 * Add the Auth::User structure to the username cache.
341 Auth::User::addToNameCache()
343 /* AuthUserHashPointer will self-register with the username cache */
344 new AuthUserHashPointer(this);
348 * Dump the username cache statictics for viewing...
351 Auth::User::UsernameCacheStats(StoreEntry
*output
)
353 AuthUserHashPointer
*usernamehash
;
355 /* overview of username cache */
356 storeAppendPrintf(output
, "Cached Usernames: %d of %d\n", proxy_auth_username_cache
->count
, proxy_auth_username_cache
->size
);
357 storeAppendPrintf(output
, "Next Garbage Collection in %d seconds.\n",
358 static_cast<int32_t>(last_discard
+ ::Config
.authenticateGCInterval
- squid_curtime
));
360 /* cache dump column titles */
361 storeAppendPrintf(output
, "\n%-15s %-9s %-9s %-9s %s\n",
367 storeAppendPrintf(output
, "--------------- --------- --------- --------- ------------------------------\n");
369 hash_first(proxy_auth_username_cache
);
370 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
371 Auth::User::Pointer auth_user
= usernamehash
->user();
373 storeAppendPrintf(output
, "%-15s %-9s %-9d %-9d %s\n",
374 Auth::Type_str
[auth_user
->auth_type
],
375 CredentialState_str
[auth_user
->credentials()],
377 static_cast<int32_t>(auth_user
->expiretime
- squid_curtime
+ ::Config
.authenticateTTL
),
378 auth_user
->username()