]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/User.cc
SourceLayout: Add Ip namespace for internal libip
[thirdparty/squid.git] / src / auth / User.cc
1 /*
2 * $Id$
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.
23 *
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.
28 *
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
36 #include "squid.h"
37 #include "auth/User.h"
38 #include "auth/UserRequest.h"
39 #include "auth/Config.h"
40 #include "auth/Gadgets.h"
41 #include "acl/Acl.h"
42 #include "acl/Gadgets.h"
43 #include "event.h"
44 #include "SquidTime.h"
45
46 #if !_USE_INLINE_
47 #include "auth/User.cci"
48 #endif
49
50 // This should be converted into a pooled type. Does not need to be cbdata
51 CBDATA_TYPE(auth_user_ip_t);
52
53 AuthUser::AuthUser (AuthConfig *aConfig) :
54 auth_type (AUTH_UNKNOWN), config(aConfig),
55 usernamehash (NULL), ipcount (0), expiretime (0), references (0), username_(NULL)
56 {
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 << "'.");
62 }
63
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.
69 */
70 void
71 AuthUser::absorb (AuthUser *from)
72 {
73 AuthUserRequest *auth_user_request;
74 /*
75 * XXX combine two authuser structs. Incomplete: it should merge
76 * in hash references too and ask the module to merge in scheme
77 * data
78 */
79 debugs(29, 5, "authenticateAuthUserMerge auth_user '" << from << "' into auth_user '" << this << "'.");
80 dlink_node *link = from->requests.head;
81
82 while (link) {
83 auth_user_request = static_cast<AuthUserRequest *>(link->data);
84 dlink_node *tmplink = link;
85 link = link->next;
86 dlinkDelete(tmplink, &from->requests);
87 dlinkAddTail(auth_user_request, tmplink, &requests);
88 auth_user_request->user(this);
89 }
90
91 references += from->references;
92 from->references = 0;
93 delete from;
94 }
95
96 AuthUser::~AuthUser()
97 {
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 ? */
103
104 if (usernamehash) {
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
110 * structure */
111 delete usernamehash;
112 }
113
114 /* remove any outstanding requests */
115 link = requests.head;
116
117 while (link) {
118 debugs(29, 5, "AuthUser::~AuthUser: removing request entry '" << link->data << "'");
119 auth_user_request = static_cast<AuthUserRequest *>(link->data);
120 tmplink = link;
121 link = link->next;
122 dlinkDelete(tmplink, &requests);
123 dlinkNodeDelete(tmplink);
124 delete auth_user_request;
125 }
126
127 /* free cached acl results */
128 aclCacheMatchFlush(&proxy_match_cache);
129
130 /* free seen ip address's */
131 clearIp();
132
133 if (username_)
134 xfree((char*)username_);
135
136 /* prevent accidental reuse */
137 auth_type = AUTH_UNKNOWN;
138 }
139
140 void
141 AuthUser::cacheInit(void)
142 {
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);
149 }
150 }
151
152 void
153 AuthUser::CachedACLsReset()
154 {
155 /*
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.
158 */
159 AuthUserHashPointer *usernamehash;
160 AuthUser *auth_user;
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);
164
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);
170
171 }
172
173 debugs(29, 3, "AuthUser::CachedACLsReset: Finished.");
174 }
175
176 void
177 AuthUser::cacheCleanup(void *datanotused)
178 {
179 /*
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.
183 */
184 AuthUserHashPointer *usernamehash;
185 AuthUser *auth_user;
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);
190
191 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
192 auth_user = usernamehash->user();
193 username = auth_user->username();
194
195 /* if we need to have inpedendent expiry clauses, insert a module call
196 * here */
197 debugs(29, 4, "AuthUser::cacheCleanup: Cache entry:\n\tType: " <<
198 auth_user->auth_type << "\n\tUsername: " << username <<
199 "\n\texpires: " <<
200 (long int) (auth_user->expiretime + Config.authenticateTTL) <<
201 "\n\treferences: " << (long int) auth_user->references);
202
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 */
206
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
211 */
212 auth_user->unlock();
213 }
214 }
215
216 debugs(29, 3, "AuthUser::cacheCleanup: Finished cleaning the user cache.");
217 eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1);
218 }
219
220 void
221 AuthUser::clearIp()
222 {
223 auth_user_ip_t *ipdata, *tempnode;
224
225 ipdata = (auth_user_ip_t *) ip_list.head;
226
227 while (ipdata) {
228 tempnode = (auth_user_ip_t *) ipdata->node.next;
229 /* walk the ip list */
230 dlinkDelete(&ipdata->node, &ip_list);
231 cbdataFree(ipdata);
232 /* catch incipient underflow */
233 assert(ipcount);
234 ipcount--;
235 ipdata = tempnode;
236 }
237
238 /* integrity check */
239 assert(ipcount == 0);
240 }
241
242 void
243 AuthUser::removeIp(Ip::Address ipaddr)
244 {
245 auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
246
247 while (ipdata) {
248 /* walk the ip list */
249
250 if (ipdata->ipaddr == ipaddr) {
251 /* remove the node */
252 dlinkDelete(&ipdata->node, &ip_list);
253 cbdataFree(ipdata);
254 /* catch incipient underflow */
255 assert(ipcount);
256 ipcount--;
257 return;
258 }
259
260 ipdata = (auth_user_ip_t *) ipdata->node.next;
261 }
262
263 }
264
265 void
266 AuthUser::addIp(Ip::Address ipaddr)
267 {
268 auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
269 int found = 0;
270
271 CBDATA_INIT_TYPE(auth_user_ip_t);
272
273 /*
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
277 */
278 while (ipdata) {
279 auth_user_ip_t *tempnode = (auth_user_ip_t *) ipdata->node.next;
280 /* walk the ip list */
281
282 if (ipdata->ipaddr == ipaddr) {
283 /* This ip has already been seen. */
284 found = 1;
285 /* update IP ttl */
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);
290 cbdataFree(ipdata);
291 /* catch incipient underflow */
292 assert(ipcount);
293 ipcount--;
294 }
295
296 ipdata = tempnode;
297 }
298
299 if (found)
300 return;
301
302 /* This ip is not in the seen list */
303 ipdata = cbdataAlloc(auth_user_ip_t);
304
305 ipdata->ip_expiretime = squid_curtime;
306
307 ipdata->ipaddr = ipaddr;
308
309 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
310
311 ipcount++;
312
313 debugs(29, 2, "authenticateAuthUserAddIp: user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
314 }
315
316
317 void
318 AuthUser::lock()
319 {
320 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "'.");
321 assert(this != NULL);
322 references++;
323 debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "' now at '" << references << "'.");
324 }
325
326 void
327 AuthUser::unlock()
328 {
329 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "'.");
330 assert(this != NULL);
331
332 if (references > 0) {
333 references--;
334 } else {
335 debugs(29, 1, "Attempt to lower Auth User " << this << " refcount below 0!");
336 }
337
338 debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "' now at '" << references << "'.");
339
340 if (references == 0)
341 delete this;
342 }
343
344 /* addToNameCache: add a auth_user structure to the username cache */
345 void
346 AuthUser::addToNameCache()
347 {
348 usernamehash = new AuthUserHashPointer (this);
349 }