]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/User.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[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-old.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 #include "Store.h"
46
47 #if !_USE_INLINE_
48 #include "auth/User.cci"
49 #endif
50
51 // This should be converted into a pooled type. Does not need to be cbdata
52 CBDATA_TYPE(AuthUserIP);
53
54 time_t Auth::User::last_discard = 0;
55
56 Auth::User::User(Auth::Config *aConfig) :
57 auth_type(Auth::AUTH_UNKNOWN),
58 config(aConfig),
59 ipcount(0),
60 expiretime(0),
61 credentials_state(Auth::Unchecked),
62 username_(NULL)
63 {
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 << "'.");
68 }
69
70 Auth::CredentialState
71 Auth::User::credentials() const
72 {
73 return credentials_state;
74 }
75
76 void
77 Auth::User::credentials(CredentialState newCreds)
78 {
79 credentials_state = newCreds;
80 }
81
82
83 /**
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.
91 */
92 void
93 Auth::User::absorb(Auth::User::Pointer from)
94 {
95 /*
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;
99 */
100
101 debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
102
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);
107
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 */
114 from->ipcount--;
115 } else {
116 /* add to our list. replace if already present. */
117 AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data);
118 bool found = false;
119 while (ipdata) {
120 AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data);
121
122 if (ipdata->ipaddr == new_ipdata->ipaddr) {
123 /* This IP has already been seen. */
124 found = true;
125 /* update IP ttl and stop searching. */
126 ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime);
127 break;
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);
131 cbdataFree(ipdata);
132 /* catch incipient underflow */
133 assert(ipcount);
134 ipcount--;
135 }
136
137 ipdata = tempnode;
138 }
139
140 if (!found) {
141 /* This ip is not in the seen list. Add it. */
142 dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list);
143 ipcount++;
144 /* remove from the source list */
145 dlinkDelete(&new_ipdata->node, &(from->ip_list));
146 from->ipcount--;
147 }
148 }
149 }
150 }
151
152 Auth::User::~User()
153 {
154 debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
155 assert(RefCountCount() == 0);
156
157 /* free cached acl results */
158 aclCacheMatchFlush(&proxy_match_cache);
159
160 /* free seen ip address's */
161 clearIp();
162
163 if (username_)
164 xfree((char*)username_);
165
166 /* prevent accidental reuse */
167 auth_type = Auth::AUTH_UNKNOWN;
168 }
169
170 void
171 Auth::User::cacheInit(void)
172 {
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;
179 }
180 }
181
182 void
183 Auth::User::CachedACLsReset()
184 {
185 /*
186 * This must complete all at once, because we are ensuring correctness.
187 */
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);
192
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);
197 }
198
199 debugs(29, 3, HERE << "Finished.");
200 }
201
202 void
203 Auth::User::cacheCleanup(void *datanotused)
204 {
205 /*
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.
209 */
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);
216
217 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
218 auth_user = usernamehash->user();
219 username = auth_user->username();
220
221 /* if we need to have indedendent expiry clauses, insert a module call
222 * here */
223 debugs(29, 4, HERE << "Cache entry:\n\tType: " <<
224 auth_user->auth_type << "\n\tUsername: " << username <<
225 "\n\texpires: " <<
226 (long int) (auth_user->expiretime + ::Config.authenticateTTL) <<
227 "\n\treferences: " << (long int) auth_user->RefCountCount());
228
229 if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) {
230 debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout.");
231
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.
235 */
236 hash_remove_link(proxy_auth_username_cache, usernamehash);
237 delete usernamehash;
238 }
239 }
240
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;
244 }
245
246 void
247 Auth::User::clearIp()
248 {
249 AuthUserIP *ipdata, *tempnode;
250
251 ipdata = (AuthUserIP *) ip_list.head;
252
253 while (ipdata) {
254 tempnode = (AuthUserIP *) ipdata->node.next;
255 /* walk the ip list */
256 dlinkDelete(&ipdata->node, &ip_list);
257 cbdataFree(ipdata);
258 /* catch incipient underflow */
259 assert(ipcount);
260 ipcount--;
261 ipdata = tempnode;
262 }
263
264 /* integrity check */
265 assert(ipcount == 0);
266 }
267
268 void
269 Auth::User::removeIp(Ip::Address ipaddr)
270 {
271 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
272
273 while (ipdata) {
274 /* walk the ip list */
275
276 if (ipdata->ipaddr == ipaddr) {
277 /* remove the node */
278 dlinkDelete(&ipdata->node, &ip_list);
279 cbdataFree(ipdata);
280 /* catch incipient underflow */
281 assert(ipcount);
282 ipcount--;
283 return;
284 }
285
286 ipdata = (AuthUserIP *) ipdata->node.next;
287 }
288
289 }
290
291 void
292 Auth::User::addIp(Ip::Address ipaddr)
293 {
294 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
295 int found = 0;
296
297 CBDATA_INIT_TYPE(AuthUserIP);
298
299 /*
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
303 */
304 while (ipdata) {
305 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
306 /* walk the ip list */
307
308 if (ipdata->ipaddr == ipaddr) {
309 /* This ip has already been seen. */
310 found = 1;
311 /* update IP ttl */
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);
316 cbdataFree(ipdata);
317 /* catch incipient underflow */
318 assert(ipcount);
319 ipcount--;
320 }
321
322 ipdata = tempnode;
323 }
324
325 if (found)
326 return;
327
328 /* This ip is not in the seen list */
329 ipdata = cbdataAlloc(AuthUserIP);
330
331 ipdata->ip_expiretime = squid_curtime;
332
333 ipdata->ipaddr = ipaddr;
334
335 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
336
337 ipcount++;
338
339 debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
340 }
341
342 /**
343 * Add the Auth::User structure to the username cache.
344 */
345 void
346 Auth::User::addToNameCache()
347 {
348 /* AuthUserHashPointer will self-register with the username cache */
349 new AuthUserHashPointer(this);
350 }
351
352 /**
353 * Dump the username cache statictics for viewing...
354 */
355 void
356 Auth::User::UsernameCacheStats(StoreEntry *output)
357 {
358 AuthUserHashPointer *usernamehash;
359
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));
364
365 /* cache dump column titles */
366 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
367 "Type",
368 "State",
369 "Check TTL",
370 "Cache TTL",
371 "Username");
372 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
373
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();
377
378 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
379 Auth::Type_str[auth_user->auth_type],
380 CredentialState_str[auth_user->credentials()],
381 auth_user->ttl(),
382 static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL),
383 auth_user->username()
384 );
385 }
386 }