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