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