4 * DEBUG: section 29 Authenticator
5 * AUTHOR: Robert Collins
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
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.
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.
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.
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.
33 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
36 #include "squid-old.h"
37 #include "auth/User.h"
38 #include "auth/UserRequest.h"
39 #include "auth/Config.h"
40 #include "auth/Gadgets.h"
42 #include "acl/Gadgets.h"
44 #include "SquidTime.h"
48 #include "auth/User.cci"
51 // This should be converted into a pooled type. Does not need to be cbdata
52 CBDATA_TYPE(AuthUserIP
);
54 time_t Auth::User::last_discard
= 0;
56 Auth::User::User(Auth::Config
*aConfig
) :
57 auth_type(Auth::AUTH_UNKNOWN
),
61 credentials_state(Auth::Unchecked
),
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
;
67 debugs(29, 5, HERE
<< "Initialised auth_user '" << this << "'.");
71 Auth::User::credentials() const
73 return credentials_state
;
77 Auth::User::credentials(CredentialState newCreds
)
79 credentials_state
= newCreds
;
84 * Combine two user structs. ONLY to be called from within a scheme
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 * The caller is responsible for altering all refcount pointers to
90 * the 'from' object. They are invalid once this method is complete.
93 Auth::User::absorb(Auth::User::Pointer from
)
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;
101 debugs(29, 5, HERE
<< "auth_user '" << from
<< "' into auth_user '" << this << "'.");
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
);
108 /* If this IP has expired - ignore the expensive merge actions. */
109 if (new_ipdata
->ip_expiretime
+ ::Config
.authenticateIpTTL
< squid_curtime
) {
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 */
116 /* add to our list. replace if already present. */
117 AuthUserIP
*ipdata
= static_cast<AuthUserIP
*>(ip_list
.head
->data
);
120 AuthUserIP
*tempnode
= static_cast<AuthUserIP
*>(ipdata
->node
.next
->data
);
122 if (ipdata
->ipaddr
== new_ipdata
->ipaddr
) {
123 /* This IP has already been seen. */
125 /* update IP ttl and stop searching. */
126 ipdata
->ip_expiretime
= max(ipdata
->ip_expiretime
, new_ipdata
->ip_expiretime
);
128 } else if (ipdata
->ip_expiretime
+ ::Config
.authenticateIpTTL
< squid_curtime
) {
129 /* This IP has expired - cleanup the destination list */
130 dlinkDelete(&ipdata
->node
, &ip_list
);
132 /* catch incipient underflow */
141 /* This ip is not in the seen list. Add it. */
142 dlinkAddTail(&new_ipdata
->node
, &ipdata
->node
, &ip_list
);
144 /* remove from the source list */
145 dlinkDelete(&new_ipdata
->node
, &(from
->ip_list
));
154 debugs(29, 5, HERE
<< "Freeing auth_user '" << this << "'.");
155 assert(RefCountCount() == 0);
157 /* free cached acl results */
158 aclCacheMatchFlush(&proxy_match_cache
);
160 /* free seen ip address's */
164 xfree((char*)username_
);
166 /* prevent accidental reuse */
167 auth_type
= Auth::AUTH_UNKNOWN
;
171 Auth::User::cacheInit(void)
173 if (!proxy_auth_username_cache
) {
174 /* First time around, 7921 should be big enough */
175 proxy_auth_username_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
176 assert(proxy_auth_username_cache
);
177 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, ::Config
.authenticateGCInterval
, 1);
178 last_discard
= squid_curtime
;
183 Auth::User::CachedACLsReset()
186 * This must complete all at once, because we are ensuring correctness.
188 AuthUserHashPointer
*usernamehash
;
189 Auth::User::Pointer auth_user
;
190 debugs(29, 3, HERE
<< "Flushing the ACL caches for all users.");
191 hash_first(proxy_auth_username_cache
);
193 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
194 auth_user
= usernamehash
->user();
195 /* free cached acl results */
196 aclCacheMatchFlush(&auth_user
->proxy_match_cache
);
199 debugs(29, 3, HERE
<< "Finished.");
203 Auth::User::cacheCleanup(void *datanotused
)
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.
210 AuthUserHashPointer
*usernamehash
;
211 Auth::User::Pointer auth_user
;
212 char const *username
= NULL
;
213 debugs(29, 3, HERE
<< "Cleaning the user cache now");
214 debugs(29, 3, HERE
<< "Current time: " << current_time
.tv_sec
);
215 hash_first(proxy_auth_username_cache
);
217 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
218 auth_user
= usernamehash
->user();
219 username
= auth_user
->username();
221 /* if we need to have indedendent expiry clauses, insert a module call
223 debugs(29, 4, HERE
<< "Cache entry:\n\tType: " <<
224 auth_user
->auth_type
<< "\n\tUsername: " << username
<<
226 (long int) (auth_user
->expiretime
+ ::Config
.authenticateTTL
) <<
227 "\n\treferences: " << (long int) auth_user
->RefCountCount());
229 if (auth_user
->expiretime
+ ::Config
.authenticateTTL
<= current_time
.tv_sec
) {
230 debugs(29, 5, HERE
<< "Removing user " << username
<< " from cache due to timeout.");
232 /* Old credentials are always removed. Existing users must hold their own
233 * Auth::User::Pointer to the credentials. Cache exists only for finding
234 * and re-using current valid credentials.
236 hash_remove_link(proxy_auth_username_cache
, usernamehash
);
241 debugs(29, 3, HERE
<< "Finished cleaning the user cache.");
242 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, ::Config
.authenticateGCInterval
, 1);
243 last_discard
= squid_curtime
;
247 Auth::User::clearIp()
249 AuthUserIP
*ipdata
, *tempnode
;
251 ipdata
= (AuthUserIP
*) ip_list
.head
;
254 tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
255 /* walk the ip list */
256 dlinkDelete(&ipdata
->node
, &ip_list
);
258 /* catch incipient underflow */
264 /* integrity check */
265 assert(ipcount
== 0);
269 Auth::User::removeIp(Ip::Address ipaddr
)
271 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
274 /* walk the ip list */
276 if (ipdata
->ipaddr
== ipaddr
) {
277 /* remove the node */
278 dlinkDelete(&ipdata
->node
, &ip_list
);
280 /* catch incipient underflow */
286 ipdata
= (AuthUserIP
*) ipdata
->node
.next
;
292 Auth::User::addIp(Ip::Address ipaddr
)
294 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
297 CBDATA_INIT_TYPE(AuthUserIP
);
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
305 AuthUserIP
*tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
306 /* walk the ip list */
308 if (ipdata
->ipaddr
== ipaddr
) {
309 /* This ip has already been seen. */
312 ipdata
->ip_expiretime
= squid_curtime
;
313 } else if (ipdata
->ip_expiretime
+ ::Config
.authenticateIpTTL
< squid_curtime
) {
314 /* This IP has expired - remove from the seen list */
315 dlinkDelete(&ipdata
->node
, &ip_list
);
317 /* catch incipient underflow */
328 /* This ip is not in the seen list */
329 ipdata
= cbdataAlloc(AuthUserIP
);
331 ipdata
->ip_expiretime
= squid_curtime
;
333 ipdata
->ipaddr
= ipaddr
;
335 dlinkAddTail(ipdata
, &ipdata
->node
, &ip_list
);
339 debugs(29, 2, HERE
<< "user '" << username() << "' has been seen at a new IP address (" << ipaddr
<< ")");
343 * Add the Auth::User structure to the username cache.
346 Auth::User::addToNameCache()
348 /* AuthUserHashPointer will self-register with the username cache */
349 new AuthUserHashPointer(this);
353 * Dump the username cache statictics for viewing...
356 Auth::User::UsernameCacheStats(StoreEntry
*output
)
358 AuthUserHashPointer
*usernamehash
;
360 /* overview of username cache */
361 storeAppendPrintf(output
, "Cached Usernames: %d of %d\n", proxy_auth_username_cache
->count
, proxy_auth_username_cache
->size
);
362 storeAppendPrintf(output
, "Next Garbage Collection in %d seconds.\n",
363 static_cast<int32_t>(last_discard
+ ::Config
.authenticateGCInterval
- squid_curtime
));
365 /* cache dump column titles */
366 storeAppendPrintf(output
, "\n%-15s %-9s %-9s %-9s %s\n",
372 storeAppendPrintf(output
, "--------------- --------- --------- --------- ------------------------------\n");
374 hash_first(proxy_auth_username_cache
);
375 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
376 Auth::User::Pointer auth_user
= usernamehash
->user();
378 storeAppendPrintf(output
, "%-15s %-9s %-9d %-9d %s\n",
379 Auth::Type_str
[auth_user
->auth_type
],
380 CredentialState_str
[auth_user
->credentials()],
382 static_cast<int32_t>(auth_user
->expiretime
- squid_curtime
+ ::Config
.authenticateTTL
),
383 auth_user
->username()