]>
Commit | Line | Data |
---|---|---|
f5691f9c | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 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 | 23 | Auth::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 |
37 | Auth::CredentialState |
38 | Auth::User::credentials() const | |
d232141d AJ |
39 | { |
40 | return credentials_state; | |
41 | } | |
42 | ||
43 | void | |
d87154ee | 44 | Auth::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 | */ |
58 | void | |
d87154ee | 59 | Auth::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 | 120 | Auth::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 | 138 | void |
d87154ee | 139 | Auth::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 | ||
160 | void | |
d87154ee | 161 | Auth::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 | ||
183 | void | |
d87154ee | 184 | Auth::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 |
228 | SBuf |
229 | Auth::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 | 242 | void |
638cfbc4 | 243 | Auth::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 | |
266 | void | |
267 | Auth::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 |