]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/User.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / User.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 29 Authenticator */
10
11 #include "squid.h"
12 #include "acl/Acl.h"
13 #include "acl/Gadgets.h"
14 #include "auth/Config.h"
15 #include "auth/Gadgets.h"
16 #include "auth/User.h"
17 #include "auth/UserRequest.h"
18 #include "event.h"
19 #include "globals.h"
20 #include "SquidConfig.h"
21 #include "SquidTime.h"
22 #include "Store.h"
23
24 time_t Auth::User::last_discard = 0;
25
26 Auth::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
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)
35 {
36 proxy_match_cache.head = proxy_match_cache.tail = NULL;
37 ip_list.head = ip_list.tail = NULL;
38 debugs(29, 5, HERE << "Initialised auth_user '" << this << "'.");
39 }
40
41 Auth::CredentialState
42 Auth::User::credentials() const
43 {
44 return credentials_state;
45 }
46
47 void
48 Auth::User::credentials(CredentialState newCreds)
49 {
50 credentials_state = newCreds;
51 }
52
53 /**
54 * Combine two user structs. ONLY to be called from within a scheme
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.
59 * The caller is responsible for altering all refcount pointers to
60 * the 'from' object. They are invalid once this method is complete.
61 */
62 void
63 Auth::User::absorb(Auth::User::Pointer from)
64 {
65 /*
66 * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
67 * dlink_list proxy_match_cache;
68 */
69
70 debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
71
72 // combine the helper response annotations. Ensuring no duplicates are copied.
73 notes.appendNewOnly(&from->notes);
74
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. */
81 if (new_ipdata->ip_expiretime <= squid_curtime) {
82 /* This IP has expired - remove from the source list */
83 dlinkDelete(&new_ipdata->node, &(from->ip_list));
84 delete new_ipdata;
85 /* catch incipient underflow */
86 -- from->ipcount;
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;
100 } else if (ipdata->ip_expiretime <= squid_curtime) {
101 /* This IP has expired - cleanup the destination list */
102 dlinkDelete(&ipdata->node, &ip_list);
103 delete ipdata;
104 /* catch incipient underflow */
105 assert(ipcount);
106 -- ipcount;
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);
115 ++ipcount;
116 /* remove from the source list */
117 dlinkDelete(&new_ipdata->node, &(from->ip_list));
118 ++from->ipcount;
119 }
120 }
121 }
122 }
123
124 Auth::User::~User()
125 {
126 debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
127 assert(LockCount() == 0);
128
129 /* free cached acl results */
130 aclCacheMatchFlush(&proxy_match_cache);
131
132 /* free seen ip address's */
133 clearIp();
134
135 if (username_)
136 xfree((char*)username_);
137
138 /* prevent accidental reuse */
139 auth_type = Auth::AUTH_UNKNOWN;
140 }
141
142 void
143 Auth::User::cacheInit(void)
144 {
145 if (!proxy_auth_username_cache) {
146 /* First time around, 7921 should be big enough */
147 proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
148 assert(proxy_auth_username_cache);
149 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
150 last_discard = squid_curtime;
151 }
152 }
153
154 void
155 Auth::User::CachedACLsReset()
156 {
157 /*
158 * This must complete all at once, because we are ensuring correctness.
159 */
160 AuthUserHashPointer *usernamehash;
161 Auth::User::Pointer auth_user;
162 debugs(29, 3, HERE << "Flushing the ACL caches for all users.");
163 hash_first(proxy_auth_username_cache);
164
165 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
166 auth_user = usernamehash->user();
167 /* free cached acl results */
168 aclCacheMatchFlush(&auth_user->proxy_match_cache);
169 }
170
171 debugs(29, 3, HERE << "Finished.");
172 }
173
174 void
175 Auth::User::cacheCleanup(void *datanotused)
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;
183 Auth::User::Pointer auth_user;
184 char const *username = NULL;
185 debugs(29, 3, HERE << "Cleaning the user cache now");
186 debugs(29, 3, HERE << "Current time: " << current_time.tv_sec);
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
193 /* if we need to have indedendent expiry clauses, insert a module call
194 * here */
195 debugs(29, 4, HERE << "Cache entry:\n\tType: " <<
196 auth_user->auth_type << "\n\tUsername: " << username <<
197 "\n\texpires: " <<
198 (long int) (auth_user->expiretime + ::Config.authenticateTTL) <<
199 "\n\treferences: " << auth_user->LockCount());
200
201 if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) {
202 debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout.");
203
204 /* Old credentials are always removed. Existing users must hold their own
205 * Auth::User::Pointer to the credentials. Cache exists only for finding
206 * and re-using current valid credentials.
207 */
208 hash_remove_link(proxy_auth_username_cache, usernamehash);
209 delete usernamehash;
210 }
211 }
212
213 debugs(29, 3, HERE << "Finished cleaning the user cache.");
214 eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
215 last_discard = squid_curtime;
216 }
217
218 void
219 Auth::User::clearIp()
220 {
221 AuthUserIP *ipdata, *tempnode;
222
223 ipdata = (AuthUserIP *) ip_list.head;
224
225 while (ipdata) {
226 tempnode = (AuthUserIP *) ipdata->node.next;
227 /* walk the ip list */
228 dlinkDelete(&ipdata->node, &ip_list);
229 delete ipdata;
230 /* catch incipient underflow */
231 assert(ipcount);
232 -- ipcount;
233 ipdata = tempnode;
234 }
235
236 /* integrity check */
237 assert(ipcount == 0);
238 }
239
240 void
241 Auth::User::removeIp(Ip::Address ipaddr)
242 {
243 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
244
245 while (ipdata) {
246 /* walk the ip list */
247
248 if (ipdata->ipaddr == ipaddr) {
249 /* remove the node */
250 dlinkDelete(&ipdata->node, &ip_list);
251 delete ipdata;
252 /* catch incipient underflow */
253 assert(ipcount);
254 -- ipcount;
255 return;
256 }
257
258 ipdata = (AuthUserIP *) ipdata->node.next;
259 }
260
261 }
262
263 void
264 Auth::User::addIp(Ip::Address ipaddr)
265 {
266 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
267 int found = 0;
268
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 */
274 while (ipdata) {
275 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
276 /* walk the ip list */
277
278 if (ipdata->ipaddr == ipaddr) {
279 /* This ip has already been seen. */
280 found = 1;
281 /* update IP ttl */
282 ipdata->ip_expiretime = squid_curtime;
283 } else if (ipdata->ip_expiretime <= squid_curtime) {
284 /* This IP has expired - remove from the seen list */
285 dlinkDelete(&ipdata->node, &ip_list);
286 delete ipdata;
287 /* catch incipient underflow */
288 assert(ipcount);
289 -- ipcount;
290 }
291
292 ipdata = tempnode;
293 }
294
295 if (found)
296 return;
297
298 /* This ip is not in the seen list */
299 ipdata = new AuthUserIP(ipaddr, squid_curtime + ::Config.authenticateIpTTL);
300
301 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
302
303 ++ipcount;
304
305 debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
306 }
307
308 SBuf
309 Auth::User::BuildUserKey(const char *username, const char *realm)
310 {
311 SBuf key;
312 key.Printf("%s:%s", username, realm);
313 return key;
314 }
315
316 /**
317 * Add the Auth::User structure to the username cache.
318 */
319 void
320 Auth::User::addToNameCache()
321 {
322 /* AuthUserHashPointer will self-register with the username cache */
323 new AuthUserHashPointer(this);
324 }
325
326 /**
327 * Dump the username cache statictics for viewing...
328 */
329 void
330 Auth::User::UsernameCacheStats(StoreEntry *output)
331 {
332 AuthUserHashPointer *usernamehash;
333
334 /* overview of username cache */
335 storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
336 storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n",
337 static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime));
338
339 /* cache dump column titles */
340 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
341 "Type",
342 "State",
343 "Check TTL",
344 "Cache TTL",
345 "Username");
346 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
347
348 hash_first(proxy_auth_username_cache);
349 while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
350 Auth::User::Pointer auth_user = usernamehash->user();
351
352 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
353 Auth::Type_str[auth_user->auth_type],
354 CredentialState_str[auth_user->credentials()],
355 auth_user->ttl(),
356 static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL),
357 auth_user->username()
358 );
359 }
360 }
361
362 void
363 Auth::User::username(char const *aString)
364 {
365 if (aString) {
366 assert(!username_);
367 username_ = xstrdup(aString);
368 if (!requestRealm_.isEmpty())
369 userKey_ = BuildUserKey(username_, requestRealm_.c_str());
370 } else {
371 safe_free(username_);
372 }
373 }
374