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>
38 #include "AuthUserRequest.h"
39 #include "AuthConfig.h"
40 #include "authenticate.h"
43 #include "SquidTime.h"
46 #include "AuthUser.cci"
49 // This should be converted into a pooled type. Does not need to be cbdata
50 CBDATA_TYPE(auth_user_ip_t
);
52 AuthUser::AuthUser (AuthConfig
*aConfig
) :
53 auth_type (AUTH_UNKNOWN
), config(aConfig
),
54 usernamehash (NULL
), ipcount (0), expiretime (0), references (0), username_(NULL
)
56 proxy_auth_list
.head
= proxy_auth_list
.tail
= NULL
;
57 proxy_match_cache
.head
= proxy_match_cache
.tail
= NULL
;
58 ip_list
.head
= ip_list
.tail
= NULL
;
59 requests
.head
= requests
.tail
= NULL
;
60 debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "' with refcount '" << references
<< "'.");
63 /* Combine two user structs. ONLY to be called from within a scheme
64 * module. The scheme module is responsible for ensuring that the
65 * two users _can_ be merged without invalidating all the request
66 * scheme data. The scheme is also responsible for merging any user
67 * related scheme data itself.
70 AuthUser::absorb (AuthUser
*from
)
72 AuthUserRequest
*auth_user_request
;
74 * XXX combine two authuser structs. Incomplete: it should merge
75 * in hash references too and ask the module to merge in scheme
78 debugs(29, 5, "authenticateAuthUserMerge auth_user '" << from
<< "' into auth_user '" << this << "'.");
79 dlink_node
*link
= from
->requests
.head
;
82 auth_user_request
= static_cast<AuthUserRequest
*>(link
->data
);
83 dlink_node
*tmplink
= link
;
85 dlinkDelete(tmplink
, &from
->requests
);
86 dlinkAddTail(auth_user_request
, tmplink
, &requests
);
87 auth_user_request
->user(this);
90 references
+= from
->references
;
97 AuthUserRequest
*auth_user_request
;
98 dlink_node
*link
, *tmplink
;
99 debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "' with refcount '" << references
<< "'.");
100 assert(references
== 0);
101 /* were they linked in by username ? */
104 assert(usernamehash
->user() == this);
105 debugs(29, 5, "AuthUser::~AuthUser: removing usernamehash entry '" << usernamehash
<< "'");
106 hash_remove_link(proxy_auth_username_cache
,
107 (hash_link
*) usernamehash
);
108 /* don't free the key as we use the same user string as the auth_user
113 /* remove any outstanding requests */
114 link
= requests
.head
;
117 debugs(29, 5, "AuthUser::~AuthUser: removing request entry '" << link
->data
<< "'");
118 auth_user_request
= static_cast<AuthUserRequest
*>(link
->data
);
121 dlinkDelete(tmplink
, &requests
);
122 dlinkNodeDelete(tmplink
);
123 delete auth_user_request
;
126 /* free cached acl results */
127 aclCacheMatchFlush(&proxy_match_cache
);
129 /* free seen ip address's */
133 xfree((char*)username_
);
135 /* prevent accidental reuse */
136 auth_type
= AUTH_UNKNOWN
;
140 AuthUser::cacheInit(void)
142 if (!proxy_auth_username_cache
) {
143 /* First time around, 7921 should be big enough */
144 proxy_auth_username_cache
=
145 hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
146 assert(proxy_auth_username_cache
);
147 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, Config
.authenticateGCInterval
, 1);
152 AuthUser::CachedACLsReset()
155 * We walk the hash by username as that is the unique key we use.
156 * This must complete all at once, because we are ensuring correctness.
158 AuthUserHashPointer
*usernamehash
;
160 char const *username
= NULL
;
161 debugs(29, 3, "AuthUser::CachedACLsReset: Flushing the ACL caches for all users.");
162 hash_first(proxy_auth_username_cache
);
164 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
165 auth_user
= usernamehash
->user();
166 username
= auth_user
->username();
167 /* free cached acl results */
168 aclCacheMatchFlush(&auth_user
->proxy_match_cache
);
172 debugs(29, 3, "AuthUser::CachedACLsReset: Finished.");
176 AuthUser::cacheCleanup(void *datanotused
)
179 * We walk the hash by username as that is the unique key we use.
180 * For big hashs we could consider stepping through the cache, 100/200
181 * entries at a time. Lets see how it flys first.
183 AuthUserHashPointer
*usernamehash
;
185 char const *username
= NULL
;
186 debugs(29, 3, "AuthUser::cacheCleanup: Cleaning the user cache now");
187 debugs(29, 3, "AuthUser::cacheCleanup: Current time: " << current_time
.tv_sec
);
188 hash_first(proxy_auth_username_cache
);
190 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
191 auth_user
= usernamehash
->user();
192 username
= auth_user
->username();
194 /* if we need to have inpedendent expiry clauses, insert a module call
196 debugs(29, 4, "AuthUser::cacheCleanup: Cache entry:\n\tType: " <<
197 auth_user
->auth_type
<< "\n\tUsername: " << username
<<
199 (long int) (auth_user
->expiretime
+ Config
.authenticateTTL
) <<
200 "\n\treferences: " << (long int) auth_user
->references
);
202 if (auth_user
->expiretime
+ Config
.authenticateTTL
<= current_time
.tv_sec
) {
203 debugs(29, 5, "AuthUser::cacheCleanup: Removing user " << username
<< " from cache due to timeout.");
204 /* the minus 1 accounts for the cache lock */
206 if (!(authenticateAuthUserInuse(auth_user
) - 1))
207 /* we don't warn if we leave the user in the cache,
208 * because other modules (ie delay pools) may keep
209 * locks on users, and thats legitimate
215 debugs(29, 3, "AuthUser::cacheCleanup: Finished cleaning the user cache.");
216 eventAdd("User Cache Maintenance", cacheCleanup
, NULL
, Config
.authenticateGCInterval
, 1);
222 auth_user_ip_t
*ipdata
, *tempnode
;
224 ipdata
= (auth_user_ip_t
*) ip_list
.head
;
227 tempnode
= (auth_user_ip_t
*) ipdata
->node
.next
;
228 /* walk the ip list */
229 dlinkDelete(&ipdata
->node
, &ip_list
);
231 /* catch incipient underflow */
237 /* integrity check */
238 assert(ipcount
== 0);
242 AuthUser::removeIp(IpAddress ipaddr
)
244 auth_user_ip_t
*ipdata
= (auth_user_ip_t
*) ip_list
.head
;
247 /* walk the ip list */
249 if (ipdata
->ipaddr
== ipaddr
) {
250 /* remove the node */
251 dlinkDelete(&ipdata
->node
, &ip_list
);
253 /* catch incipient underflow */
259 ipdata
= (auth_user_ip_t
*) ipdata
->node
.next
;
265 AuthUser::addIp(IpAddress ipaddr
)
267 auth_user_ip_t
*ipdata
= (auth_user_ip_t
*) ip_list
.head
;
270 CBDATA_INIT_TYPE(auth_user_ip_t
);
273 * we walk the entire list to prevent the first item in the list
274 * preventing old entries being flushed and locking a user out after
275 * a timeout+reconfigure
278 auth_user_ip_t
*tempnode
= (auth_user_ip_t
*) ipdata
->node
.next
;
279 /* walk the ip list */
281 if (ipdata
->ipaddr
== ipaddr
) {
282 /* This ip has already been seen. */
285 ipdata
->ip_expiretime
= squid_curtime
;
286 } else if (ipdata
->ip_expiretime
+ Config
.authenticateIpTTL
< squid_curtime
) {
287 /* This IP has expired - remove from the seen list */
288 dlinkDelete(&ipdata
->node
, &ip_list
);
290 /* catch incipient underflow */
301 /* This ip is not in the seen list */
302 ipdata
= cbdataAlloc(auth_user_ip_t
);
304 ipdata
->ip_expiretime
= squid_curtime
;
306 ipdata
->ipaddr
= ipaddr
;
308 dlinkAddTail(ipdata
, &ipdata
->node
, &ip_list
);
312 debugs(29, 2, "authenticateAuthUserAddIp: user '" << username() << "' has been seen at a new IP address (" << ipaddr
<< ")");
319 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "'.");
320 assert(this != NULL
);
322 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "' now at '" << references
<< "'.");
328 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "'.");
329 assert(this != NULL
);
331 if (references
> 0) {
334 debugs(29, 1, "Attempt to lower Auth User " << this << " refcount below 0!");
337 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "' now at '" << references
<< "'.");
343 /* addToNameCache: add a auth_user structure to the username cache */
345 AuthUser::addToNameCache()
347 usernamehash
= new AuthUserHashPointer (this);