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>
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"
47 #include "auth/User.cci"
50 // This should be converted into a pooled type. Does not need to be cbdata
51 CBDATA_TYPE(auth_user_ip_t
);
53 AuthUser::AuthUser (AuthConfig
*aConfig
) :
54 auth_type (AUTH_UNKNOWN
), config(aConfig
),
55 usernamehash (NULL
), ipcount (0), expiretime (0), references (0), username_(NULL
)
57 proxy_auth_list
.head
= proxy_auth_list
.tail
= NULL
;
58 proxy_match_cache
.head
= proxy_match_cache
.tail
= NULL
;
59 ip_list
.head
= ip_list
.tail
= NULL
;
60 requests
.head
= requests
.tail
= NULL
;
61 debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "' with refcount '" << references
<< "'.");
64 /* Combine two user structs. ONLY to be called from within a scheme
65 * module. The scheme module is responsible for ensuring that the
66 * two users _can_ be merged without invalidating all the request
67 * scheme data. The scheme is also responsible for merging any user
68 * related scheme data itself.
71 AuthUser::absorb (AuthUser
*from
)
73 AuthUserRequest
*auth_user_request
;
75 * XXX combine two authuser structs. Incomplete: it should merge
76 * in hash references too and ask the module to merge in scheme
79 debugs(29, 5, "authenticateAuthUserMerge auth_user '" << from
<< "' into auth_user '" << this << "'.");
80 dlink_node
*link
= from
->requests
.head
;
83 auth_user_request
= static_cast<AuthUserRequest
*>(link
->data
);
84 dlink_node
*tmplink
= link
;
86 dlinkDelete(tmplink
, &from
->requests
);
87 dlinkAddTail(auth_user_request
, tmplink
, &requests
);
88 auth_user_request
->user(this);
91 references
+= from
->references
;
98 AuthUserRequest
*auth_user_request
;
99 dlink_node
*link
, *tmplink
;
100 debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "' with refcount '" << references
<< "'.");
101 assert(references
== 0);
102 /* were they linked in by username ? */
105 assert(usernamehash
->user() == this);
106 debugs(29, 5, "AuthUser::~AuthUser: removing usernamehash entry '" << usernamehash
<< "'");
107 hash_remove_link(proxy_auth_username_cache
,
108 (hash_link
*) usernamehash
);
109 /* don't free the key as we use the same user string as the auth_user
114 /* remove any outstanding requests */
115 link
= requests
.head
;
118 debugs(29, 5, "AuthUser::~AuthUser: removing request entry '" << link
->data
<< "'");
119 auth_user_request
= static_cast<AuthUserRequest
*>(link
->data
);
122 dlinkDelete(tmplink
, &requests
);
123 dlinkNodeDelete(tmplink
);
124 delete auth_user_request
;
127 /* free cached acl results */
128 aclCacheMatchFlush(&proxy_match_cache
);
130 /* free seen ip address's */
134 xfree((char*)username_
);
136 /* prevent accidental reuse */
137 auth_type
= AUTH_UNKNOWN
;
141 AuthUser::cacheInit(void)
143 if (!proxy_auth_username_cache
) {
144 /* First time around, 7921 should be big enough */
145 proxy_auth_username_cache
=
146 hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
147 assert(proxy_auth_username_cache
);
148 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, Config
.authenticateGCInterval
, 1);
153 AuthUser::CachedACLsReset()
156 * We walk the hash by username as that is the unique key we use.
157 * This must complete all at once, because we are ensuring correctness.
159 AuthUserHashPointer
*usernamehash
;
161 char const *username
= NULL
;
162 debugs(29, 3, "AuthUser::CachedACLsReset: Flushing the ACL caches for all users.");
163 hash_first(proxy_auth_username_cache
);
165 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
166 auth_user
= usernamehash
->user();
167 username
= auth_user
->username();
168 /* free cached acl results */
169 aclCacheMatchFlush(&auth_user
->proxy_match_cache
);
173 debugs(29, 3, "AuthUser::CachedACLsReset: Finished.");
177 AuthUser::cacheCleanup(void *datanotused
)
180 * We walk the hash by username as that is the unique key we use.
181 * For big hashs we could consider stepping through the cache, 100/200
182 * entries at a time. Lets see how it flys first.
184 AuthUserHashPointer
*usernamehash
;
186 char const *username
= NULL
;
187 debugs(29, 3, "AuthUser::cacheCleanup: Cleaning the user cache now");
188 debugs(29, 3, "AuthUser::cacheCleanup: Current time: " << current_time
.tv_sec
);
189 hash_first(proxy_auth_username_cache
);
191 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
192 auth_user
= usernamehash
->user();
193 username
= auth_user
->username();
195 /* if we need to have inpedendent expiry clauses, insert a module call
197 debugs(29, 4, "AuthUser::cacheCleanup: Cache entry:\n\tType: " <<
198 auth_user
->auth_type
<< "\n\tUsername: " << username
<<
200 (long int) (auth_user
->expiretime
+ Config
.authenticateTTL
) <<
201 "\n\treferences: " << (long int) auth_user
->references
);
203 if (auth_user
->expiretime
+ Config
.authenticateTTL
<= current_time
.tv_sec
) {
204 debugs(29, 5, "AuthUser::cacheCleanup: Removing user " << username
<< " from cache due to timeout.");
205 /* the minus 1 accounts for the cache lock */
207 if (!(authenticateAuthUserInuse(auth_user
) - 1))
208 /* we don't warn if we leave the user in the cache,
209 * because other modules (ie delay pools) may keep
210 * locks on users, and thats legitimate
216 debugs(29, 3, "AuthUser::cacheCleanup: Finished cleaning the user cache.");
217 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, Config
.authenticateGCInterval
, 1);
223 auth_user_ip_t
*ipdata
, *tempnode
;
225 ipdata
= (auth_user_ip_t
*) ip_list
.head
;
228 tempnode
= (auth_user_ip_t
*) ipdata
->node
.next
;
229 /* walk the ip list */
230 dlinkDelete(&ipdata
->node
, &ip_list
);
232 /* catch incipient underflow */
238 /* integrity check */
239 assert(ipcount
== 0);
243 AuthUser::removeIp(Ip::Address ipaddr
)
245 auth_user_ip_t
*ipdata
= (auth_user_ip_t
*) ip_list
.head
;
248 /* walk the ip list */
250 if (ipdata
->ipaddr
== ipaddr
) {
251 /* remove the node */
252 dlinkDelete(&ipdata
->node
, &ip_list
);
254 /* catch incipient underflow */
260 ipdata
= (auth_user_ip_t
*) ipdata
->node
.next
;
266 AuthUser::addIp(Ip::Address ipaddr
)
268 auth_user_ip_t
*ipdata
= (auth_user_ip_t
*) ip_list
.head
;
271 CBDATA_INIT_TYPE(auth_user_ip_t
);
274 * we walk the entire list to prevent the first item in the list
275 * preventing old entries being flushed and locking a user out after
276 * a timeout+reconfigure
279 auth_user_ip_t
*tempnode
= (auth_user_ip_t
*) ipdata
->node
.next
;
280 /* walk the ip list */
282 if (ipdata
->ipaddr
== ipaddr
) {
283 /* This ip has already been seen. */
286 ipdata
->ip_expiretime
= squid_curtime
;
287 } else if (ipdata
->ip_expiretime
+ Config
.authenticateIpTTL
< squid_curtime
) {
288 /* This IP has expired - remove from the seen list */
289 dlinkDelete(&ipdata
->node
, &ip_list
);
291 /* catch incipient underflow */
302 /* This ip is not in the seen list */
303 ipdata
= cbdataAlloc(auth_user_ip_t
);
305 ipdata
->ip_expiretime
= squid_curtime
;
307 ipdata
->ipaddr
= ipaddr
;
309 dlinkAddTail(ipdata
, &ipdata
->node
, &ip_list
);
313 debugs(29, 2, "authenticateAuthUserAddIp: user '" << username() << "' has been seen at a new IP address (" << ipaddr
<< ")");
320 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "'.");
321 assert(this != NULL
);
323 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "' now at '" << references
<< "'.");
329 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "'.");
330 assert(this != NULL
);
332 if (references
> 0) {
335 debugs(29, 1, "Attempt to lower Auth User " << this << " refcount below 0!");
338 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "' now at '" << references
<< "'.");
344 /* addToNameCache: add a auth_user structure to the username cache */
346 AuthUser::addToNameCache()
348 usernamehash
= new AuthUserHashPointer (this);