]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/User.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / auth / User.cc
CommitLineData
f5691f9c 1/*
bf95c10a 2 * Copyright (C) 1996-2022 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"
5db226c8 14#include "auth/Config.h"
fde785ee 15#include "auth/CredentialsCache.h"
602d9612
A
16#include "auth/Gadgets.h"
17#include "auth/User.h"
18#include "auth/UserRequest.h"
a553a5a3 19#include "event.h"
582c2af2 20#include "globals.h"
56a49fda 21#include "Store.h"
f5691f9c 22
dc79fed8 23Auth::User::User(Auth::SchemeConfig *aConfig, const char *aRequestRealm) :
f53969cc
SM
24 auth_type(Auth::AUTH_UNKNOWN),
25 config(aConfig),
26 ipcount(0),
27 expiretime(0),
f53969cc 28 credentials_state(Auth::Unchecked),
d59e4742 29 username_(nullptr),
f53969cc 30 requestRealm_(aRequestRealm)
f5691f9c 31{
aee3523a
AR
32 proxy_match_cache.head = proxy_match_cache.tail = nullptr;
33 ip_list.head = ip_list.tail = nullptr;
bf95c10a 34 debugs(29, 5, "Initialised auth_user '" << this << "'.");
f5691f9c 35}
36
d87154ee
AJ
37Auth::CredentialState
38Auth::User::credentials() const
d232141d
AJ
39{
40 return credentials_state;
41}
42
43void
d87154ee 44Auth::User::credentials(CredentialState newCreds)
d232141d
AJ
45{
46 credentials_state = newCreds;
47}
48
ea0695f2
AJ
49/**
50 * Combine two user structs. ONLY to be called from within a scheme
f5691f9c 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.
58e94342
AJ
55 * The caller is responsible for altering all refcount pointers to
56 * the 'from' object. They are invalid once this method is complete.
f5691f9c 57 */
58void
d87154ee 59Auth::User::absorb(Auth::User::Pointer from)
f5691f9c 60{
f5691f9c 61 /*
56a49fda 62 * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
56a49fda 63 * dlink_list proxy_match_cache;
f5691f9c 64 */
56a49fda 65
bf95c10a 66 debugs(29, 5, "auth_user '" << from << "' into auth_user '" << this << "'.");
f5691f9c 67
71e7400c
AJ
68 // combine the helper response annotations. Ensuring no duplicates are copied.
69 notes.appendNewOnly(&from->notes);
70
56a49fda
AJ
71 /* absorb the list of IP address sources (for max_user_ip controls) */
72 AuthUserIP *new_ipdata;
aee3523a 73 while (from->ip_list.head != nullptr) {
56a49fda
AJ
74 new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data);
75
76 /* If this IP has expired - ignore the expensive merge actions. */
c35dd848 77 if (new_ipdata->ip_expiretime <= squid_curtime) {
56a49fda
AJ
78 /* This IP has expired - remove from the source list */
79 dlinkDelete(&new_ipdata->node, &(from->ip_list));
a98f21ac 80 delete new_ipdata;
56a49fda 81 /* catch incipient underflow */
a2f5277a 82 -- from->ipcount;
56a49fda
AJ
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;
c35dd848 96 } else if (ipdata->ip_expiretime <= squid_curtime) {
56a49fda
AJ
97 /* This IP has expired - cleanup the destination list */
98 dlinkDelete(&ipdata->node, &ip_list);
a98f21ac 99 delete ipdata;
56a49fda
AJ
100 /* catch incipient underflow */
101 assert(ipcount);
a2f5277a 102 -- ipcount;
56a49fda
AJ
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);
742a021b 111 ++ipcount;
56a49fda
AJ
112 /* remove from the source list */
113 dlinkDelete(&new_ipdata->node, &(from->ip_list));
742a021b 114 ++from->ipcount;
56a49fda
AJ
115 }
116 }
117 }
f5691f9c 118}
119
d87154ee 120Auth::User::~User()
f5691f9c 121{
bf95c10a 122 debugs(29, 5, "Freeing auth_user '" << this << "'.");
8bf217bd 123 assert(LockCount() == 0);
56a49fda 124
f5691f9c 125 /* free cached acl results */
126 aclCacheMatchFlush(&proxy_match_cache);
127
128 /* free seen ip address's */
129 clearIp();
130
3f5f1a01 131 if (username_)
132 xfree((char*)username_);
f5691f9c 133
134 /* prevent accidental reuse */
616cfc4c 135 auth_type = Auth::AUTH_UNKNOWN;
f5691f9c 136}
137
f5691f9c 138void
d87154ee 139Auth::User::clearIp()
f5691f9c 140{
56a49fda 141 AuthUserIP *ipdata, *tempnode;
f5691f9c 142
56a49fda 143 ipdata = (AuthUserIP *) ip_list.head;
f5691f9c 144
145 while (ipdata) {
56a49fda 146 tempnode = (AuthUserIP *) ipdata->node.next;
f5691f9c 147 /* walk the ip list */
148 dlinkDelete(&ipdata->node, &ip_list);
a98f21ac 149 delete ipdata;
f5691f9c 150 /* catch incipient underflow */
151 assert(ipcount);
a2f5277a 152 -- ipcount;
f5691f9c 153 ipdata = tempnode;
154 }
155
156 /* integrity check */
157 assert(ipcount == 0);
158}
159
160void
d87154ee 161Auth::User::removeIp(Ip::Address ipaddr)
4c19ba24 162{
56a49fda 163 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
4c19ba24 164
26ac0430 165 while (ipdata) {
4c19ba24 166 /* walk the ip list */
167
cc192b50 168 if (ipdata->ipaddr == ipaddr) {
4c19ba24 169 /* remove the node */
170 dlinkDelete(&ipdata->node, &ip_list);
a98f21ac 171 delete ipdata;
4c19ba24 172 /* catch incipient underflow */
173 assert(ipcount);
a2f5277a 174 -- ipcount;
4c19ba24 175 return;
176 }
177
56a49fda 178 ipdata = (AuthUserIP *) ipdata->node.next;
4c19ba24 179 }
180
181}
182
183void
d87154ee 184Auth::User::addIp(Ip::Address ipaddr)
4c19ba24 185{
56a49fda 186 AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
4c19ba24 187 int found = 0;
188
4c19ba24 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 */
26ac0430 194 while (ipdata) {
56a49fda 195 AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
4c19ba24 196 /* walk the ip list */
f5691f9c 197
cc192b50 198 if (ipdata->ipaddr == ipaddr) {
199 /* This ip has already been seen. */
4c19ba24 200 found = 1;
201 /* update IP ttl */
00ef8d82 202 ipdata->ip_expiretime = squid_curtime + Auth::TheConfig.ipTtl;
c35dd848 203 } else if (ipdata->ip_expiretime <= squid_curtime) {
4c19ba24 204 /* This IP has expired - remove from the seen list */
205 dlinkDelete(&ipdata->node, &ip_list);
a98f21ac 206 delete ipdata;
4c19ba24 207 /* catch incipient underflow */
208 assert(ipcount);
a2f5277a 209 -- ipcount;
4c19ba24 210 }
211
212 ipdata = tempnode;
213 }
214
215 if (found)
216 return;
217
218 /* This ip is not in the seen list */
00ef8d82 219 ipdata = new AuthUserIP(ipaddr, squid_curtime + Auth::TheConfig.ipTtl);
4c19ba24 220
221 dlinkAddTail(ipdata, &ipdata->node, &ip_list);
222
742a021b 223 ++ipcount;
4c19ba24 224
bf95c10a 225 debugs(29, 2, "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
4c19ba24 226}
227
d4806c91
CT
228SBuf
229Auth::User::BuildUserKey(const char *username, const char *realm)
230{
231 SBuf key;
0203ae29
AJ
232 if (realm)
233 key.Printf("%s:%s", username, realm);
234 else
235 key.append(username, strlen(username));
d4806c91
CT
236 return key;
237}
238
56a49fda 239/**
2f8abb64 240 * Dump the username cache statistics for viewing...
56a49fda 241 */
f5691f9c 242void
638cfbc4 243Auth::User::CredentialsCacheStats(StoreEntry *output)
f5691f9c 244{
d2123755
FC
245 auto userlist = authenticateCachedUsersList();
246 storeAppendPrintf(output, "Cached Usernames: %d", static_cast<int32_t>(userlist.size()));
0caa2820 247 storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\t%s\n",
56a49fda 248 "Type",
d232141d 249 "State",
af70c154
AJ
250 "Check TTL",
251 "Cache TTL",
0caa2820 252 "Username", "Key");
d232141d 253 storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
d2123755 254 for ( auto auth_user : userlist ) {
0caa2820 255 storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\t" SQUIDSBUFPH "\n",
616cfc4c 256 Auth::Type_str[auth_user->auth_type],
d87154ee 257 CredentialState_str[auth_user->credentials()],
56a49fda 258 auth_user->ttl(),
00ef8d82 259 static_cast<int32_t>(auth_user->expiretime - squid_curtime + Auth::TheConfig.credentialsTtl),
0caa2820
AJ
260 auth_user->username(),
261 SQUIDSBUFPRINT(auth_user->userKey())
ec5858ff 262 );
56a49fda 263 }
f5691f9c 264}
32113576
FC
265
266void
267Auth::User::username(char const *aString)
268{
269 if (aString) {
270 assert(!username_);
271 username_ = xstrdup(aString);
0203ae29 272 // NP: param #2 is working around a c_str() data-copy performance regression
aee3523a 273 userKey_ = BuildUserKey(username_, (!requestRealm_.isEmpty() ? requestRealm_.c_str() : nullptr));
32113576
FC
274 } else {
275 safe_free(username_);
0203ae29 276 userKey_.clear();
32113576
FC
277 }
278}
f53969cc 279