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