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