]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/User.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / User.cc
CommitLineData
f5691f9c 1/*
262a0e14 2 * $Id$
f5691f9c 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.
26ac0430 23 *
f5691f9c 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.
26ac0430 28 *
f5691f9c 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
582c2af2 36#include "squid.h"
2d2b0bb7
AR
37#include "auth/User.h"
38#include "auth/UserRequest.h"
39#include "auth/Config.h"
3ad63615
AR
40#include "auth/Gadgets.h"
41#include "acl/Acl.h"
42#include "acl/Gadgets.h"
a553a5a3 43#include "event.h"
582c2af2 44#include "globals.h"
4c19ba24 45#include "SquidTime.h"
56a49fda 46#include "Store.h"
f5691f9c 47
32d002cb 48#if !_USE_INLINE_
2d2b0bb7 49#include "auth/User.cci"
f5691f9c 50#endif
51
4c19ba24 52// This should be converted into a pooled type. Does not need to be cbdata
56a49fda
AJ
53CBDATA_TYPE(AuthUserIP);
54
d87154ee 55time_t Auth::User::last_discard = 0;
af70c154 56
d87154ee 57Auth::User::User(Auth::Config *aConfig) :
616cfc4c 58 auth_type(Auth::AUTH_UNKNOWN),
56a49fda 59 config(aConfig),
56a49fda
AJ
60 ipcount(0),
61 expiretime(0),
d87154ee 62 credentials_state(Auth::Unchecked),
56a49fda 63 username_(NULL)
f5691f9c 64{
65 proxy_auth_list.head = proxy_auth_list.tail = NULL;
66 proxy_match_cache.head = proxy_match_cache.tail = NULL;
67 ip_list.head = ip_list.tail = NULL;
d87154ee 68 debugs(29, 5, HERE << "Initialised auth_user '" << this << "'.");
f5691f9c 69}
70
d87154ee
AJ
71Auth::CredentialState
72Auth::User::credentials() const
d232141d
AJ
73{
74 return credentials_state;
75}
76
77void
d87154ee 78Auth::User::credentials(CredentialState newCreds)
d232141d
AJ
79{
80 credentials_state = newCreds;
81}
82
ea0695f2
AJ
83/**
84 * Combine two user structs. ONLY to be called from within a scheme
f5691f9c 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.
58e94342
AJ
89 * The caller is responsible for altering all refcount pointers to
90 * the 'from' object. They are invalid once this method is complete.
f5691f9c 91 */
92void
d87154ee 93Auth::User::absorb(Auth::User::Pointer from)
f5691f9c 94{
f5691f9c 95 /*
56a49fda
AJ
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;
f5691f9c 99 */
56a49fda 100
d87154ee 101 debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
f5691f9c 102
56a49fda
AJ
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. */
d87154ee 109 if (new_ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) {
56a49fda
AJ
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 */
a2f5277a 114 -- from->ipcount;
56a49fda
AJ
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;
d87154ee 128 } else if (ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) {
56a49fda
AJ
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);
a2f5277a 134 -- ipcount;
56a49fda
AJ
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);
742a021b 143 ++ipcount;
56a49fda
AJ
144 /* remove from the source list */
145 dlinkDelete(&new_ipdata->node, &(from->ip_list));
742a021b 146 ++from->ipcount;
56a49fda
AJ
147 }
148 }
149 }
f5691f9c 150}
151
d87154ee 152Auth::User::~User()
f5691f9c 153{
d87154ee 154 debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
56a49fda
AJ
155 assert(RefCountCount() == 0);
156
f5691f9c 157 /* free cached acl results */
158 aclCacheMatchFlush(&proxy_match_cache);
159
160 /* free seen ip address's */
161 clearIp();
162
3f5f1a01 163 if (username_)
164 xfree((char*)username_);
f5691f9c 165
166 /* prevent accidental reuse */
616cfc4c 167 auth_type = Auth::AUTH_UNKNOWN;
f5691f9c 168}
169
170void
d87154ee 171Auth::User::cacheInit(void)
f5691f9c 172{
173 if (!proxy_auth_username_cache) {
174 /* First time around, 7921 should be big enough */
56a49fda 175 proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
f5691f9c 176 assert(proxy_auth_username_cache);
d87154ee 177 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
af70c154 178 last_discard = squid_curtime;
f5691f9c 179 }
180}
181
182void
d87154ee 183Auth::User::CachedACLsReset()
f5691f9c 184{
185 /*
f5691f9c 186 * This must complete all at once, because we are ensuring correctness.
187 */
188 AuthUserHashPointer *usernamehash;
d87154ee
AJ
189 Auth::User::Pointer auth_user;
190 debugs(29, 3, HERE << "Flushing the ACL caches for all users.");
f5691f9c 191 hash_first(proxy_auth_username_cache);
192
193 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
194 auth_user = usernamehash->user();
f5691f9c 195 /* free cached acl results */
196 aclCacheMatchFlush(&auth_user->proxy_match_cache);
f5691f9c 197 }
198
d87154ee 199 debugs(29, 3, HERE << "Finished.");
f5691f9c 200}
201
202void
d87154ee 203Auth::User::cacheCleanup(void *datanotused)
f5691f9c 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;
d87154ee 211 Auth::User::Pointer auth_user;
f5691f9c 212 char const *username = NULL;
d87154ee
AJ
213 debugs(29, 3, HERE << "Cleaning the user cache now");
214 debugs(29, 3, HERE << "Current time: " << current_time.tv_sec);
f5691f9c 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
427cb33a 221 /* if we need to have indedendent expiry clauses, insert a module call
f5691f9c 222 * here */
d87154ee 223 debugs(29, 4, HERE << "Cache entry:\n\tType: " <<
bf8fe701 224 auth_user->auth_type << "\n\tUsername: " << username <<
225 "\n\texpires: " <<
d87154ee 226 (long int) (auth_user->expiretime + ::Config.authenticateTTL) <<
56a49fda 227 "\n\treferences: " << (long int) auth_user->RefCountCount());
f5691f9c 228
d87154ee
AJ
229 if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) {
230 debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout.");
56a49fda
AJ
231
232 /* Old credentials are always removed. Existing users must hold their own
d87154ee 233 * Auth::User::Pointer to the credentials. Cache exists only for finding
56a49fda
AJ
234 * and re-using current valid credentials.
235 */
236 hash_remove_link(proxy_auth_username_cache, usernamehash);
237 delete usernamehash;
f5691f9c 238 }
239 }
240
d87154ee
AJ
241 debugs(29, 3, HERE << "Finished cleaning the user cache.");
242 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
af70c154 243 last_discard = squid_curtime;
f5691f9c 244}
245
246void
d87154ee 247Auth::User::clearIp()
f5691f9c 248{
56a49fda 249 AuthUserIP *ipdata, *tempnode;
f5691f9c 250
56a49fda 251 ipdata = (AuthUserIP *) ip_list.head;
f5691f9c 252
253 while (ipdata) {
56a49fda 254 tempnode = (AuthUserIP *) ipdata->node.next;
f5691f9c 255 /* walk the ip list */
256 dlinkDelete(&ipdata->node, &ip_list);
257 cbdataFree(ipdata);
258 /* catch incipient underflow */
259 assert(ipcount);
a2f5277a 260 -- ipcount;
f5691f9c 261 ipdata = tempnode;
262 }
263
264 /* integrity check */
265 assert(ipcount == 0);
266}
267
268void
d87154ee 269Auth::User::removeIp(Ip::Address ipaddr)
4c19ba24 270{
56a49fda 271 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
4c19ba24 272
26ac0430 273 while (ipdata) {
4c19ba24 274 /* walk the ip list */
275
cc192b50 276 if (ipdata->ipaddr == ipaddr) {
4c19ba24 277 /* remove the node */
278 dlinkDelete(&ipdata->node, &ip_list);
279 cbdataFree(ipdata);
280 /* catch incipient underflow */
281 assert(ipcount);
a2f5277a 282 -- ipcount;
4c19ba24 283 return;
284 }
285
56a49fda 286 ipdata = (AuthUserIP *) ipdata->node.next;
4c19ba24 287 }
288
289}
290
291void
d87154ee 292Auth::User::addIp(Ip::Address ipaddr)
4c19ba24 293{
56a49fda 294 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
4c19ba24 295 int found = 0;
296
56a49fda 297 CBDATA_INIT_TYPE(AuthUserIP);
4c19ba24 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 */
26ac0430 304 while (ipdata) {
56a49fda 305 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
4c19ba24 306 /* walk the ip list */
f5691f9c 307
cc192b50 308 if (ipdata->ipaddr == ipaddr) {
309 /* This ip has already been seen. */
4c19ba24 310 found = 1;
311 /* update IP ttl */
312 ipdata->ip_expiretime = squid_curtime;
d87154ee 313 } else if (ipdata->ip_expiretime + ::Config.authenticateIpTTL < squid_curtime) {
4c19ba24 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);
a2f5277a 319 -- ipcount;
4c19ba24 320 }
321
322 ipdata = tempnode;
323 }
324
325 if (found)
326 return;
327
328 /* This ip is not in the seen list */
56a49fda 329 ipdata = cbdataAlloc(AuthUserIP);
4c19ba24 330
331 ipdata->ip_expiretime = squid_curtime;
332
333 ipdata->ipaddr = ipaddr;
334
335 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
336
742a021b 337 ++ipcount;
4c19ba24 338
d87154ee 339 debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
4c19ba24 340}
341
427cb33a 342/**
d87154ee 343 * Add the Auth::User structure to the username cache.
427cb33a 344 */
4c19ba24 345void
d87154ee 346Auth::User::addToNameCache()
f5691f9c 347{
427cb33a 348 /* AuthUserHashPointer will self-register with the username cache */
af70c154 349 new AuthUserHashPointer(this);
f5691f9c 350}
351
56a49fda
AJ
352/**
353 * Dump the username cache statictics for viewing...
354 */
f5691f9c 355void
d87154ee 356Auth::User::UsernameCacheStats(StoreEntry *output)
f5691f9c 357{
56a49fda 358 AuthUserHashPointer *usernamehash;
f5691f9c 359
56a49fda
AJ
360 /* overview of username cache */
361 storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
d87154ee
AJ
362 storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n",
363 static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime));
f5691f9c 364
56a49fda 365 /* cache dump column titles */
d232141d 366 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
56a49fda 367 "Type",
d232141d 368 "State",
af70c154
AJ
369 "Check TTL",
370 "Cache TTL",
56a49fda 371 "Username");
d232141d 372 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
f5691f9c 373
56a49fda
AJ
374 hash_first(proxy_auth_username_cache);
375 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
d87154ee 376 Auth::User::Pointer auth_user = usernamehash->user();
f5691f9c 377
d232141d 378 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
616cfc4c 379 Auth::Type_str[auth_user->auth_type],
d87154ee 380 CredentialState_str[auth_user->credentials()],
56a49fda 381 auth_user->ttl(),
d87154ee 382 static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL),
56a49fda 383 auth_user->username()
ec5858ff 384 );
56a49fda 385 }
f5691f9c 386}