]> 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-2017 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/CredentialsCache.h"
16 #include "auth/Gadgets.h"
17 #include "auth/User.h"
18 #include "auth/UserRequest.h"
19 #include "event.h"
20 #include "globals.h"
21 #include "SquidTime.h"
22 #include "Store.h"
23
24 Auth::User::User(Auth::SchemeConfig *aConfig, const char *aRequestRealm) :
25 auth_type(Auth::AUTH_UNKNOWN),
26 config(aConfig),
27 ipcount(0),
28 expiretime(0),
29 credentials_state(Auth::Unchecked),
30 username_(nullptr),
31 requestRealm_(aRequestRealm)
32 {
33 proxy_match_cache.head = proxy_match_cache.tail = NULL;
34 ip_list.head = ip_list.tail = NULL;
35 debugs(29, 5, HERE << "Initialised auth_user '" << this << "'.");
36 }
37
38 Auth::CredentialState
39 Auth::User::credentials() const
40 {
41 return credentials_state;
42 }
43
44 void
45 Auth::User::credentials(CredentialState newCreds)
46 {
47 credentials_state = newCreds;
48 }
49
50 /**
51 * Combine two user structs. ONLY to be called from within a scheme
52 * module. The scheme module is responsible for ensuring that the
53 * two users _can_ be merged without invalidating all the request
54 * scheme data. The scheme is also responsible for merging any user
55 * related scheme data itself.
56 * The caller is responsible for altering all refcount pointers to
57 * the 'from' object. They are invalid once this method is complete.
58 */
59 void
60 Auth::User::absorb(Auth::User::Pointer from)
61 {
62 /*
63 * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
64 * dlink_list proxy_match_cache;
65 */
66
67 debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
68
69 // combine the helper response annotations. Ensuring no duplicates are copied.
70 notes.appendNewOnly(&from->notes);
71
72 /* absorb the list of IP address sources (for max_user_ip controls) */
73 AuthUserIP *new_ipdata;
74 while (from->ip_list.head != NULL) {
75 new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data);
76
77 /* If this IP has expired - ignore the expensive merge actions. */
78 if (new_ipdata->ip_expiretime <= squid_curtime) {
79 /* This IP has expired - remove from the source list */
80 dlinkDelete(&new_ipdata->node, &(from->ip_list));
81 delete new_ipdata;
82 /* catch incipient underflow */
83 -- from->ipcount;
84 } else {
85 /* add to our list. replace if already present. */
86 AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data);
87 bool found = false;
88 while (ipdata) {
89 AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data);
90
91 if (ipdata->ipaddr == new_ipdata->ipaddr) {
92 /* This IP has already been seen. */
93 found = true;
94 /* update IP ttl and stop searching. */
95 ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime);
96 break;
97 } else if (ipdata->ip_expiretime <= squid_curtime) {
98 /* This IP has expired - cleanup the destination list */
99 dlinkDelete(&ipdata->node, &ip_list);
100 delete ipdata;
101 /* catch incipient underflow */
102 assert(ipcount);
103 -- ipcount;
104 }
105
106 ipdata = tempnode;
107 }
108
109 if (!found) {
110 /* This ip is not in the seen list. Add it. */
111 dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list);
112 ++ipcount;
113 /* remove from the source list */
114 dlinkDelete(&new_ipdata->node, &(from->ip_list));
115 ++from->ipcount;
116 }
117 }
118 }
119 }
120
121 Auth::User::~User()
122 {
123 debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
124 assert(LockCount() == 0);
125
126 /* free cached acl results */
127 aclCacheMatchFlush(&proxy_match_cache);
128
129 /* free seen ip address's */
130 clearIp();
131
132 if (username_)
133 xfree((char*)username_);
134
135 /* prevent accidental reuse */
136 auth_type = Auth::AUTH_UNKNOWN;
137 }
138
139 void
140 Auth::User::clearIp()
141 {
142 AuthUserIP *ipdata, *tempnode;
143
144 ipdata = (AuthUserIP *) ip_list.head;
145
146 while (ipdata) {
147 tempnode = (AuthUserIP *) ipdata->node.next;
148 /* walk the ip list */
149 dlinkDelete(&ipdata->node, &ip_list);
150 delete ipdata;
151 /* catch incipient underflow */
152 assert(ipcount);
153 -- ipcount;
154 ipdata = tempnode;
155 }
156
157 /* integrity check */
158 assert(ipcount == 0);
159 }
160
161 void
162 Auth::User::removeIp(Ip::Address ipaddr)
163 {
164 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
165
166 while (ipdata) {
167 /* walk the ip list */
168
169 if (ipdata->ipaddr == ipaddr) {
170 /* remove the node */
171 dlinkDelete(&ipdata->node, &ip_list);
172 delete ipdata;
173 /* catch incipient underflow */
174 assert(ipcount);
175 -- ipcount;
176 return;
177 }
178
179 ipdata = (AuthUserIP *) ipdata->node.next;
180 }
181
182 }
183
184 void
185 Auth::User::addIp(Ip::Address ipaddr)
186 {
187 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
188 int found = 0;
189
190 /*
191 * we walk the entire list to prevent the first item in the list
192 * preventing old entries being flushed and locking a user out after
193 * a timeout+reconfigure
194 */
195 while (ipdata) {
196 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
197 /* walk the ip list */
198
199 if (ipdata->ipaddr == ipaddr) {
200 /* This ip has already been seen. */
201 found = 1;
202 /* update IP ttl */
203 ipdata->ip_expiretime = squid_curtime + Auth::TheConfig.ipTtl;
204 } else if (ipdata->ip_expiretime <= squid_curtime) {
205 /* This IP has expired - remove from the seen list */
206 dlinkDelete(&ipdata->node, &ip_list);
207 delete ipdata;
208 /* catch incipient underflow */
209 assert(ipcount);
210 -- ipcount;
211 }
212
213 ipdata = tempnode;
214 }
215
216 if (found)
217 return;
218
219 /* This ip is not in the seen list */
220 ipdata = new AuthUserIP(ipaddr, squid_curtime + Auth::TheConfig.ipTtl);
221
222 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
223
224 ++ipcount;
225
226 debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
227 }
228
229 SBuf
230 Auth::User::BuildUserKey(const char *username, const char *realm)
231 {
232 SBuf key;
233 if (realm)
234 key.Printf("%s:%s", username, realm);
235 else
236 key.append(username, strlen(username));
237 return key;
238 }
239
240 /**
241 * Dump the username cache statictics for viewing...
242 */
243 void
244 Auth::User::CredentialsCacheStats(StoreEntry *output)
245 {
246 auto userlist = authenticateCachedUsersList();
247 storeAppendPrintf(output, "Cached Usernames: %d", static_cast<int32_t>(userlist.size()));
248 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\t%s\n",
249 "Type",
250 "State",
251 "Check TTL",
252 "Cache TTL",
253 "Username", "Key");
254 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
255 for ( auto auth_user : userlist ) {
256 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\t" SQUIDSBUFPH "\n",
257 Auth::Type_str[auth_user->auth_type],
258 CredentialState_str[auth_user->credentials()],
259 auth_user->ttl(),
260 static_cast<int32_t>(auth_user->expiretime - squid_curtime + Auth::TheConfig.credentialsTtl),
261 auth_user->username(),
262 SQUIDSBUFPRINT(auth_user->userKey())
263 );
264 }
265 }
266
267 void
268 Auth::User::username(char const *aString)
269 {
270 if (aString) {
271 assert(!username_);
272 username_ = xstrdup(aString);
273 // NP: param #2 is working around a c_str() data-copy performance regression
274 userKey_ = BuildUserKey(username_, (!requestRealm_.isEmpty() ? requestRealm_.c_str() : NULL));
275 } else {
276 safe_free(username_);
277 userKey_.clear();
278 }
279 }
280