]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/User.cc
Broken: define and use stub_libauth.cc
[thirdparty/squid.git] / src / auth / User.cc
1 /*
2 * DEBUG: section 29 Authenticator
3 * AUTHOR: Robert Collins
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
32 */
33
34 #include "squid.h"
35 #include "acl/Acl.h"
36 #include "acl/Gadgets.h"
37 #include "auth/Config.h"
38 #include "auth/Gadgets.h"
39 #include "auth/User.h"
40 #include "auth/UserRequest.h"
41 #include "event.h"
42 #include "globals.h"
43 #include "SquidConfig.h"
44 #include "SquidTime.h"
45 #include "Store.h"
46
47 // This should be converted into a pooled type. Does not need to be cbdata
48 CBDATA_TYPE(AuthUserIP);
49
50 time_t Auth::User::last_discard = 0;
51
52 Auth::User::User(Auth::Config *aConfig) :
53 auth_type(Auth::AUTH_UNKNOWN),
54 config(aConfig),
55 ipcount(0),
56 expiretime(0),
57 credentials_state(Auth::Unchecked),
58 username_(NULL)
59 {
60 proxy_auth_list.head = proxy_auth_list.tail = NULL;
61 proxy_match_cache.head = proxy_match_cache.tail = NULL;
62 ip_list.head = ip_list.tail = NULL;
63 debugs(29, 5, HERE << "Initialised auth_user '" << this << "'.");
64 }
65
66 Auth::CredentialState
67 Auth::User::credentials() const
68 {
69 return credentials_state;
70 }
71
72 void
73 Auth::User::credentials(CredentialState newCreds)
74 {
75 credentials_state = newCreds;
76 }
77
78 /**
79 * Combine two user structs. ONLY to be called from within a scheme
80 * module. The scheme module is responsible for ensuring that the
81 * two users _can_ be merged without invalidating all the request
82 * scheme data. The scheme is also responsible for merging any user
83 * related scheme data itself.
84 * The caller is responsible for altering all refcount pointers to
85 * the 'from' object. They are invalid once this method is complete.
86 */
87 void
88 Auth::User::absorb(Auth::User::Pointer from)
89 {
90 /*
91 * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
92 * dlink_list proxy_auth_list;
93 * dlink_list proxy_match_cache;
94 */
95
96 debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
97
98 /* absorb the list of IP address sources (for max_user_ip controls) */
99 AuthUserIP *new_ipdata;
100 while (from->ip_list.head != NULL) {
101 new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data);
102
103 /* If this IP has expired - ignore the expensive merge actions. */
104 if (new_ipdata->ip_expiretime <= squid_curtime) {
105 /* This IP has expired - remove from the source list */
106 dlinkDelete(&new_ipdata->node, &(from->ip_list));
107 cbdataFree(new_ipdata);
108 /* catch incipient underflow */
109 -- from->ipcount;
110 } else {
111 /* add to our list. replace if already present. */
112 AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data);
113 bool found = false;
114 while (ipdata) {
115 AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data);
116
117 if (ipdata->ipaddr == new_ipdata->ipaddr) {
118 /* This IP has already been seen. */
119 found = true;
120 /* update IP ttl and stop searching. */
121 ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime);
122 break;
123 } else if (ipdata->ip_expiretime <= squid_curtime) {
124 /* This IP has expired - cleanup the destination list */
125 dlinkDelete(&ipdata->node, &ip_list);
126 cbdataFree(ipdata);
127 /* catch incipient underflow */
128 assert(ipcount);
129 -- ipcount;
130 }
131
132 ipdata = tempnode;
133 }
134
135 if (!found) {
136 /* This ip is not in the seen list. Add it. */
137 dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list);
138 ++ipcount;
139 /* remove from the source list */
140 dlinkDelete(&new_ipdata->node, &(from->ip_list));
141 ++from->ipcount;
142 }
143 }
144 }
145 }
146
147 Auth::User::~User()
148 {
149 debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
150 assert(LockCount() == 0);
151
152 /* free cached acl results */
153 aclCacheMatchFlush(&proxy_match_cache);
154
155 /* free seen ip address's */
156 clearIp();
157
158 if (username_)
159 xfree((char*)username_);
160
161 /* prevent accidental reuse */
162 auth_type = Auth::AUTH_UNKNOWN;
163 }
164
165 void
166 Auth::User::cacheInit(void)
167 {
168 if (!proxy_auth_username_cache) {
169 /* First time around, 7921 should be big enough */
170 proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
171 assert(proxy_auth_username_cache);
172 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
173 last_discard = squid_curtime;
174 }
175 }
176
177 void
178 Auth::User::CachedACLsReset()
179 {
180 /*
181 * This must complete all at once, because we are ensuring correctness.
182 */
183 AuthUserHashPointer *usernamehash;
184 Auth::User::Pointer auth_user;
185 debugs(29, 3, HERE << "Flushing the ACL caches for all users.");
186 hash_first(proxy_auth_username_cache);
187
188 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
189 auth_user = usernamehash->user();
190 /* free cached acl results */
191 aclCacheMatchFlush(&auth_user->proxy_match_cache);
192 }
193
194 debugs(29, 3, HERE << "Finished.");
195 }
196
197 void
198 Auth::User::cacheCleanup(void *datanotused)
199 {
200 /*
201 * We walk the hash by username as that is the unique key we use.
202 * For big hashs we could consider stepping through the cache, 100/200
203 * entries at a time. Lets see how it flys first.
204 */
205 AuthUserHashPointer *usernamehash;
206 Auth::User::Pointer auth_user;
207 char const *username = NULL;
208 debugs(29, 3, HERE << "Cleaning the user cache now");
209 debugs(29, 3, HERE << "Current time: " << current_time.tv_sec);
210 hash_first(proxy_auth_username_cache);
211
212 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
213 auth_user = usernamehash->user();
214 username = auth_user->username();
215
216 /* if we need to have indedendent expiry clauses, insert a module call
217 * here */
218 debugs(29, 4, HERE << "Cache entry:\n\tType: " <<
219 auth_user->auth_type << "\n\tUsername: " << username <<
220 "\n\texpires: " <<
221 (long int) (auth_user->expiretime + ::Config.authenticateTTL) <<
222 "\n\treferences: " << auth_user->LockCount());
223
224 if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) {
225 debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout.");
226
227 /* Old credentials are always removed. Existing users must hold their own
228 * Auth::User::Pointer to the credentials. Cache exists only for finding
229 * and re-using current valid credentials.
230 */
231 hash_remove_link(proxy_auth_username_cache, usernamehash);
232 delete usernamehash;
233 }
234 }
235
236 debugs(29, 3, HERE << "Finished cleaning the user cache.");
237 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
238 last_discard = squid_curtime;
239 }
240
241 void
242 Auth::User::clearIp()
243 {
244 AuthUserIP *ipdata, *tempnode;
245
246 ipdata = (AuthUserIP *) ip_list.head;
247
248 while (ipdata) {
249 tempnode = (AuthUserIP *) ipdata->node.next;
250 /* walk the ip list */
251 dlinkDelete(&ipdata->node, &ip_list);
252 cbdataFree(ipdata);
253 /* catch incipient underflow */
254 assert(ipcount);
255 -- ipcount;
256 ipdata = tempnode;
257 }
258
259 /* integrity check */
260 assert(ipcount == 0);
261 }
262
263 void
264 Auth::User::removeIp(Ip::Address ipaddr)
265 {
266 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
267
268 while (ipdata) {
269 /* walk the ip list */
270
271 if (ipdata->ipaddr == ipaddr) {
272 /* remove the node */
273 dlinkDelete(&ipdata->node, &ip_list);
274 cbdataFree(ipdata);
275 /* catch incipient underflow */
276 assert(ipcount);
277 -- ipcount;
278 return;
279 }
280
281 ipdata = (AuthUserIP *) ipdata->node.next;
282 }
283
284 }
285
286 void
287 Auth::User::addIp(Ip::Address ipaddr)
288 {
289 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
290 int found = 0;
291
292 CBDATA_INIT_TYPE(AuthUserIP);
293
294 /*
295 * we walk the entire list to prevent the first item in the list
296 * preventing old entries being flushed and locking a user out after
297 * a timeout+reconfigure
298 */
299 while (ipdata) {
300 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
301 /* walk the ip list */
302
303 if (ipdata->ipaddr == ipaddr) {
304 /* This ip has already been seen. */
305 found = 1;
306 /* update IP ttl */
307 ipdata->ip_expiretime = squid_curtime;
308 } else if (ipdata->ip_expiretime <= squid_curtime) {
309 /* This IP has expired - remove from the seen list */
310 dlinkDelete(&ipdata->node, &ip_list);
311 cbdataFree(ipdata);
312 /* catch incipient underflow */
313 assert(ipcount);
314 -- ipcount;
315 }
316
317 ipdata = tempnode;
318 }
319
320 if (found)
321 return;
322
323 /* This ip is not in the seen list */
324 ipdata = cbdataAlloc(AuthUserIP);
325
326 ipdata->ip_expiretime = squid_curtime + ::Config.authenticateIpTTL;
327
328 ipdata->ipaddr = ipaddr;
329
330 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
331
332 ++ipcount;
333
334 debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
335 }
336
337 /**
338 * Add the Auth::User structure to the username cache.
339 */
340 void
341 Auth::User::addToNameCache()
342 {
343 /* AuthUserHashPointer will self-register with the username cache */
344 new AuthUserHashPointer(this);
345 }
346
347 /**
348 * Dump the username cache statictics for viewing...
349 */
350 void
351 Auth::User::UsernameCacheStats(StoreEntry *output)
352 {
353 AuthUserHashPointer *usernamehash;
354
355 /* overview of username cache */
356 storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
357 storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n",
358 static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime));
359
360 /* cache dump column titles */
361 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
362 "Type",
363 "State",
364 "Check TTL",
365 "Cache TTL",
366 "Username");
367 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
368
369 hash_first(proxy_auth_username_cache);
370 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
371 Auth::User::Pointer auth_user = usernamehash->user();
372
373 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
374 Auth::Type_str[auth_user->auth_type],
375 CredentialState_str[auth_user->credentials()],
376 auth_user->ttl(),
377 static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL),
378 auth_user->username()
379 );
380 }
381 }