2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 29 Authenticator */
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"
20 #include "SquidConfig.h"
21 #include "SquidTime.h"
24 time_t Auth::User::last_discard
= 0;
26 Auth::User::User(Auth::Config
*aConfig
, const char *aRequestRealm
) :
27 auth_type(Auth::AUTH_UNKNOWN
),
32 credentials_state(Auth::Unchecked
),
34 requestRealm_(aRequestRealm
)
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 << "'.");
42 Auth::User::credentials() const
44 return credentials_state
;
48 Auth::User::credentials(CredentialState newCreds
)
50 credentials_state
= newCreds
;
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.
63 Auth::User::absorb(Auth::User::Pointer from
)
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;
70 debugs(29, 5, HERE
<< "auth_user '" << from
<< "' into auth_user '" << this << "'.");
72 // combine the helper response annotations. Ensuring no duplicates are copied.
73 notes
.appendNewOnly(&from
->notes
);
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
);
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
));
85 /* catch incipient underflow */
88 /* add to our list. replace if already present. */
89 AuthUserIP
*ipdata
= static_cast<AuthUserIP
*>(ip_list
.head
->data
);
92 AuthUserIP
*tempnode
= static_cast<AuthUserIP
*>(ipdata
->node
.next
->data
);
94 if (ipdata
->ipaddr
== new_ipdata
->ipaddr
) {
95 /* This IP has already been seen. */
97 /* update IP ttl and stop searching. */
98 ipdata
->ip_expiretime
= max(ipdata
->ip_expiretime
, new_ipdata
->ip_expiretime
);
100 } else if (ipdata
->ip_expiretime
<= squid_curtime
) {
101 /* This IP has expired - cleanup the destination list */
102 dlinkDelete(&ipdata
->node
, &ip_list
);
104 /* catch incipient underflow */
113 /* This ip is not in the seen list. Add it. */
114 dlinkAddTail(&new_ipdata
->node
, &ipdata
->node
, &ip_list
);
116 /* remove from the source list */
117 dlinkDelete(&new_ipdata
->node
, &(from
->ip_list
));
126 debugs(29, 5, HERE
<< "Freeing auth_user '" << this << "'.");
127 assert(LockCount() == 0);
129 /* free cached acl results */
130 aclCacheMatchFlush(&proxy_match_cache
);
132 /* free seen ip address's */
136 xfree((char*)username_
);
138 /* prevent accidental reuse */
139 auth_type
= Auth::AUTH_UNKNOWN
;
143 Auth::User::cacheInit(void)
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
;
155 Auth::User::CachedACLsReset()
158 * This must complete all at once, because we are ensuring correctness.
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
);
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
);
171 debugs(29, 3, HERE
<< "Finished.");
175 Auth::User::cacheCleanup(void *datanotused
)
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.
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
);
189 while ((usernamehash
= ((AuthUserHashPointer
*) hash_next(proxy_auth_username_cache
)))) {
190 auth_user
= usernamehash
->user();
191 username
= auth_user
->username();
193 /* if we need to have indedendent expiry clauses, insert a module call
195 debugs(29, 4, HERE
<< "Cache entry:\n\tType: " <<
196 auth_user
->auth_type
<< "\n\tUsername: " << username
<<
198 (long int) (auth_user
->expiretime
+ ::Config
.authenticateTTL
) <<
199 "\n\treferences: " << auth_user
->LockCount());
201 if (auth_user
->expiretime
+ ::Config
.authenticateTTL
<= current_time
.tv_sec
) {
202 debugs(29, 5, HERE
<< "Removing user " << username
<< " from cache due to timeout.");
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.
208 hash_remove_link(proxy_auth_username_cache
, usernamehash
);
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
;
219 Auth::User::clearIp()
221 AuthUserIP
*ipdata
, *tempnode
;
223 ipdata
= (AuthUserIP
*) ip_list
.head
;
226 tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
227 /* walk the ip list */
228 dlinkDelete(&ipdata
->node
, &ip_list
);
230 /* catch incipient underflow */
236 /* integrity check */
237 assert(ipcount
== 0);
241 Auth::User::removeIp(Ip::Address ipaddr
)
243 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
246 /* walk the ip list */
248 if (ipdata
->ipaddr
== ipaddr
) {
249 /* remove the node */
250 dlinkDelete(&ipdata
->node
, &ip_list
);
252 /* catch incipient underflow */
258 ipdata
= (AuthUserIP
*) ipdata
->node
.next
;
264 Auth::User::addIp(Ip::Address ipaddr
)
266 AuthUserIP
*ipdata
= (AuthUserIP
*) ip_list
.head
;
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
275 AuthUserIP
*tempnode
= (AuthUserIP
*) ipdata
->node
.next
;
276 /* walk the ip list */
278 if (ipdata
->ipaddr
== ipaddr
) {
279 /* This ip has already been seen. */
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
);
287 /* catch incipient underflow */
298 /* This ip is not in the seen list */
299 ipdata
= new AuthUserIP(ipaddr
, squid_curtime
+ ::Config
.authenticateIpTTL
);
301 dlinkAddTail(ipdata
, &ipdata
->node
, &ip_list
);
305 debugs(29, 2, HERE
<< "user '" << username() << "' has been seen at a new IP address (" << ipaddr
<< ")");
309 Auth::User::BuildUserKey(const char *username
, const char *realm
)
312 key
.Printf("%s:%s", username
, realm
);
317 * Add the Auth::User structure to the username cache.
320 Auth::User::addToNameCache()
322 /* AuthUserHashPointer will self-register with the username cache */
323 new AuthUserHashPointer(this);
327 * Dump the username cache statictics for viewing...
330 Auth::User::UsernameCacheStats(StoreEntry
*output
)
332 AuthUserHashPointer
*usernamehash
;
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
));
339 /* cache dump column titles */
340 storeAppendPrintf(output
, "\n%-15s %-9s %-9s %-9s %s\n",
346 storeAppendPrintf(output
, "--------------- --------- --------- --------- ------------------------------\n");
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();
352 storeAppendPrintf(output
, "%-15s %-9s %-9d %-9d %s\n",
353 Auth::Type_str
[auth_user
->auth_type
],
354 CredentialState_str
[auth_user
->credentials()],
356 static_cast<int32_t>(auth_user
->expiretime
- squid_curtime
+ ::Config
.authenticateTTL
),
357 auth_user
->username()
363 Auth::User::username(char const *aString
)
367 username_
= xstrdup(aString
);
368 if (!requestRealm_
.isEmpty())
369 userKey_
= BuildUserKey(username_
, requestRealm_
.c_str());
371 safe_free(username_
);