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