Squid Web Cache master
Loading...
Searching...
No Matches
UserRequest.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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/* The functions in this file handle authentication.
12 * They DO NOT perform access control or auditing.
13 * See acl.c for access control and client_side.c for auditing */
14
15#include "squid.h"
16#include "acl/FilledChecklist.h"
17#include "auth/Config.h"
18#include "client_side.h"
19#include "comm/Connection.h"
20#include "fatal.h"
21#include "format/Format.h"
22#include "helper.h"
23#include "helper/Reply.h"
24#include "http/Stream.h"
25#include "HttpReply.h"
26#include "HttpRequest.h"
27#include "MemBuf.h"
28#include "sbuf/Stream.h"
29
30/* Generic Functions */
31
32char const *
34{
35 if (user() != nullptr)
36 return user()->username();
37 else
38 return nullptr;
39}
40
41/**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
42
43/* send the initial data to an authenticator module */
44void
46{
47 assert(handler);
48 assert(data);
49 debugs(29, 9, this);
50 startHelperLookup(request, al, handler, data);
51}
52
53bool
55{
56 debugs(29, 9, "Validating Auth::UserRequest '" << this << "'.");
57
58 if (user() == nullptr) {
59 debugs(29, 4, "No associated Auth::User data");
60 return false;
61 }
62
63 if (user()->auth_type == Auth::AUTH_UNKNOWN) {
64 debugs(29, 4, "Auth::User '" << user() << "' uses unknown scheme.");
65 return false;
66 }
67
68 if (user()->auth_type == Auth::AUTH_BROKEN) {
69 debugs(29, 4, "Auth::User '" << user() << "' is broken for it's scheme.");
70 return false;
71 }
72
73 /* any other sanity checks that we need in the future */
74
75 /* finally return ok */
76 debugs(29, 5, "Validated. Auth::UserRequest '" << this << "'.");
77 return true;
78}
79
80void *
81Auth::UserRequest::operator new (size_t)
82{
83 fatal("Auth::UserRequest not directly allocatable\n");
84 return (void *)1;
85}
86
87void
88Auth::UserRequest::operator delete (void *)
89{
90 fatal("Auth::UserRequest child failed to override operator delete\n");
91}
92
94 _auth_user(nullptr),
95 message(nullptr),
97{
98 debugs(29, 5, "initialised request " << this);
99}
100
102{
103 assert(LockCount()==0);
104 debugs(29, 5, "freeing request " << this);
105
106 if (user() != nullptr) {
107 /* release our references to the user credentials */
108 user(nullptr);
109 }
110
111 safe_free(message);
112}
113
114void
116{
117 safe_free(message);
118 message = xstrdup(aString);
119}
120
121char const *
123{
124 return message;
125}
126
127char const *
128Auth::UserRequest::denyMessage(char const * const default_message) const
129{
130 if (getDenyMessage() == nullptr)
131 return default_message;
132
133 return getDenyMessage();
134}
135
136bool
138{
139 const auto u = user();
140 if (u && u->credentials() == Auth::Ok) {
141 debugs(29, 7, "yes");
142 return true;
143 }
144
145 debugs(29, 7, "no");
146 return false;
147}
148
149static void
151{
152 Auth::User::Pointer auth_user = auth_user_request->user();
153
154 if (!auth_user)
155 return;
156
157 auth_user->addIp(ipaddr);
158}
159
160void
162{
163 Auth::User::Pointer auth_user = auth_user_request->user();
164
165 if (!auth_user)
166 return;
167
168 auth_user->removeIp(ipaddr);
169}
170
171void
173{
174 if (auth_user_request != nullptr)
175 auth_user_request->user()->clearIp();
176}
177
178int
180{
181 assert(auth_user_request != nullptr);
182 assert(auth_user_request->user() != nullptr);
183 return auth_user_request->user()->ipcount;
184}
185
186/*
187 * authenticateUserAuthenticated: is this auth_user structure logged in ?
188 */
189bool
191{
192 if (!auth_user_request || !auth_user_request->valid())
193 return false;
194
195 return auth_user_request->authenticated();
196}
197
200{
201 if (user() == nullptr)
202 return Auth::CRED_ERROR; // No credentials. Should this be a CHALLENGE instead?
203
205 return Auth::CRED_VALID;
206
207 return module_direction();
208}
209
210void
213
214void
217
218void
221
222const char *
224{
225 fatal("Auth::UserRequest::connLastHeader should always be overridden by conn based auth schemes");
226 return nullptr;
227}
228
229/*
230 * authenticateAuthenticateUser: call the module specific code to
231 * log this user request in.
232 * Cache hits may change the auth_user pointer in the structure if needed.
233 * This is basically a handle approach.
234 */
235static void
237{
238 assert(auth_user_request.getRaw() != nullptr);
239
240 auth_user_request->authenticate(request, conn, type);
241}
242
245{
247
248 if (auth_user_request != nullptr)
249 res = auth_user_request;
250 else if (request != nullptr && request->auth_user_request != nullptr)
251 res = request->auth_user_request;
252 else if (conn != nullptr)
253 res = conn->getAuth();
254
255 // attach the credential notes from helper to the transaction
256 if (request != nullptr && res != nullptr && res->user() != nullptr) {
257 // XXX: we have no access to the transaction / AccessLogEntry so can't SyncNotes().
258 // workaround by using anything already set in HttpRequest
259 // OR use new and rely on a later Sync copying these to AccessLogEntry
260
261 UpdateRequestNotes(conn, *request, res->user()->notes);
262 }
263
264 return res;
265}
266
267/* returns one of
268 * AUTH_ACL_CHALLENGE,
269 * AUTH_ACL_HELPER,
270 * AUTH_ACL_CANNOT_AUTHENTICATE,
271 * AUTH_AUTHENTICATED
272 *
273 * How to use: In your proxy-auth dependent acl code, use the following
274 * construct:
275 * int rv;
276 * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED)
277 * return rv;
278 *
279 * when this code is reached, the request/connection is authenticated.
280 *
281 * if you have non-acl code, but want to force authentication, you need a
282 * callback mechanism like the acl testing routines that will send a 40[1|7] to
283 * the client when rv==AUTH_ACL_CHALLENGE, and will communicate with
284 * the authenticateStart routine for rv==AUTH_ACL_HELPER
285 *
286 * Caller is responsible for locking and unlocking their *auth_user_request!
287 */
290{
291 const char *proxy_auth;
292 assert(headertype != 0);
293
294 proxy_auth = request->header.getStr(headertype);
295
296 /*
297 * a note on proxy_auth logix here:
298 * proxy_auth==NULL -> unauthenticated request || already
299 * authenticated connection so we test for an authenticated
300 * connection when we receive no authentication header.
301 */
302
303 /* a) can we find other credentials to use? and b) are they logged in already? */
304 if (proxy_auth == nullptr && !authenticateUserAuthenticated(authTryGetUser(*auth_user_request,conn,request))) {
305 /* no header or authentication failed/got corrupted - restart */
306 debugs(29, 4, "No Proxy-Auth header and no working alternative. Requesting auth header.");
307
308 /* something wrong with the AUTH credentials. Force a new attempt */
309
310 /* connection auth we must reset on auth errors */
311 if (conn != nullptr) {
312 conn->setAuth(nullptr, "HTTP request missing credentials");
313 }
314
315 *auth_user_request = nullptr;
316 return AUTH_ACL_CHALLENGE;
317 }
318
319 /*
320 * Is this an already authenticated connection with a new auth header?
321 * No check for function required in the if: its compulsory for conn based
322 * auth modules
323 */
324 if (proxy_auth && conn != nullptr && conn->getAuth() != nullptr &&
326 conn->getAuth()->connLastHeader() != nullptr &&
327 strcmp(proxy_auth, conn->getAuth()->connLastHeader())) {
328 debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
329 conn->getAuth() << ", Current user '" <<
330 conn->getAuth()->username() << "' proxy_auth " <<
331 proxy_auth);
332
333 /* remove this request struct - the link is already authed and it can't be to reauth. */
334
335 /* This should _only_ ever occur on the first pass through
336 * authenticateAuthenticate
337 */
338 assert(*auth_user_request == nullptr);
339 conn->setAuth(nullptr, "changed credentials token");
340 }
341
342 /* we have a proxy auth header and as far as we know this connection has
343 * not had bungled connection oriented authentication happen on it. */
344 debugs(29, 9, "header " << (proxy_auth ? proxy_auth : "-") << ".");
345
346 if (*auth_user_request == nullptr) {
347 if (conn != nullptr) {
348 debugs(29, 9, "This is a new checklist test on:" << conn->clientConnection);
349 }
350
351 if (proxy_auth && request->auth_user_request == nullptr && conn != nullptr && conn->getAuth() != nullptr) {
352 Auth::SchemeConfig * scheme = Auth::SchemeConfig::Find(proxy_auth);
353
354 if (conn->getAuth()->user() == nullptr || conn->getAuth()->user()->config != scheme) {
355 debugs(29, DBG_IMPORTANT, "WARNING: Unexpected change of authentication scheme from '" <<
356 (conn->getAuth()->user()!=nullptr?conn->getAuth()->user()->config->type():"[no user]") <<
357 "' to '" << proxy_auth << "' (client " <<
358 src_addr << ")");
359
360 conn->setAuth(nullptr, "changed auth scheme");
361 }
362 }
363
364 if (request->auth_user_request == nullptr && (conn == nullptr || conn->getAuth() == nullptr)) {
365 /* beginning of a new request check */
366 debugs(29, 4, "No connection authentication type");
367
368 *auth_user_request = Auth::SchemeConfig::CreateAuthUser(proxy_auth, al);
369 if (*auth_user_request == nullptr)
370 return AUTH_ACL_CHALLENGE;
371 else if (!(*auth_user_request)->valid()) {
372 /* the decode might have left a username for logging, or a message to
373 * the user */
374
375 if ((*auth_user_request)->username()) {
376 request->auth_user_request = *auth_user_request;
377 }
378
379 *auth_user_request = nullptr;
380 return AUTH_ACL_CHALLENGE;
381 }
382
383 } else if (request->auth_user_request != nullptr) {
384 *auth_user_request = request->auth_user_request;
385 } else {
386 assert (conn != nullptr);
387 if (conn->getAuth() != nullptr) {
388 *auth_user_request = conn->getAuth();
389 } else {
390 /* failed connection based authentication */
391 debugs(29, 4, "Auth user request " << *auth_user_request << " conn-auth missing and failed to authenticate.");
392 *auth_user_request = nullptr;
393 return AUTH_ACL_CHALLENGE;
394 }
395 }
396 }
397
398 if (!authenticateUserAuthenticated(*auth_user_request)) {
399 /* User not logged in. Try to log them in */
400 authenticateAuthenticateUser(*auth_user_request, request, conn, headertype);
401
402 switch ((*auth_user_request)->direction()) {
403
405
406 if (request->auth_user_request == nullptr) {
407 request->auth_user_request = *auth_user_request;
408 }
409 *auth_user_request = nullptr;
410 return AUTH_ACL_CHALLENGE;
411
412 case Auth::CRED_ERROR:
413 /* this ACL check is finished. */
414 *auth_user_request = nullptr;
415 return AUTH_ACL_CHALLENGE;
416
418 /* we are partway through authentication within squid,
419 * the *auth_user_request variables stores the auth_user_request
420 * for the callback to here - Do not Unlock */
421 return AUTH_ACL_HELPER;
422
423 case Auth::CRED_VALID:
424 /* authentication is finished */
425 /* See if user authentication failed for some reason */
426 if (!authenticateUserAuthenticated(*auth_user_request)) {
427 if ((*auth_user_request)->username()) {
428 if (!request->auth_user_request) {
429 request->auth_user_request = *auth_user_request;
430 }
431 }
432
433 *auth_user_request = nullptr;
434 return AUTH_ACL_CHALLENGE;
435 }
436 // otherwise fallthrough to acceptance.
437 }
438 }
439
440 /* copy username to request for logging on client-side */
441 /* the credentials are correct at this point */
442 if (request->auth_user_request == nullptr) {
443 request->auth_user_request = *auth_user_request;
444 authenticateAuthUserRequestSetIp(*auth_user_request, src_addr);
445 }
446
447 return AUTH_AUTHENTICATED;
448}
449
452{
453 // If we have already been called, return the cached value
454 Auth::UserRequest::Pointer t = authTryGetUser(*aUR, conn, request);
455
456 if (t != nullptr && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
457 if (*aUR == nullptr)
458 *aUR = t;
459
460 if (request->auth_user_request == nullptr && t->lastReply == AUTH_AUTHENTICATED) {
461 request->auth_user_request = t;
462 }
463 return t->lastReply;
464 }
465
466 // ok, call the actual authenticator routine.
467 AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr, al);
468
469 // auth process may have changed the UserRequest we are dealing with
470 t = authTryGetUser(*aUR, conn, request);
471
472 if (t != nullptr && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
473 t->lastReply = result;
474
475 return result;
476}
477
478static Auth::ConfigVector &
480{
481 if (!Auth::TheConfig.schemeLists.empty() && Auth::TheConfig.schemeAccess) {
482 ACLFilledChecklist ch(nullptr, request);
483 ch.updateReply(rep);
484 const auto &answer = ch.fastCheck(Auth::TheConfig.schemeAccess);
485 if (answer.allowed())
486 return Auth::TheConfig.schemeLists.at(answer.kind).authConfigs;
487 }
489}
490
491void
492Auth::UserRequest::AddReplyAuthHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
493/* send the auth types we are configured to support (and have compiled in!) */
494{
495 Http::HdrType type;
496
497 switch (rep->sline.status()) {
498
500 /* Proxy authorisation needed */
502 break;
503
505 /* WWW Authorisation needed */
507 break;
508
509 default:
510 /* Keep GCC happy */
511 /* some other HTTP status */
513 break;
514 }
515
516 debugs(29, 9, "headertype:" << type << " authuser:" << auth_user_request);
517
519 || (rep->sline.status() == Http::scUnauthorized)) && internal)
520 /* this is a authenticate-needed response */
521 {
522
523 if (auth_user_request != nullptr && auth_user_request->direction() == Auth::CRED_CHALLENGE)
524 /* add the scheme specific challenge header to the response */
525 auth_user_request->user()->config->fixHeader(auth_user_request, rep, type, request);
526 else {
527 /* call each configured & running auth scheme */
528 Auth::ConfigVector &configs = schemesConfig(request, rep);
529 for (auto *scheme : configs) {
530 if (scheme->active()) {
531 if (auth_user_request != nullptr && auth_user_request->scheme()->type() == scheme->type())
532 scheme->fixHeader(auth_user_request, rep, type, request);
533 else
534 scheme->fixHeader(nullptr, rep, type, request);
535 } else
536 debugs(29, 4, "Configured scheme " << scheme->type() << " not Active");
537 }
538 }
539
540 }
541
542 /*
543 * allow protocol specific headers to be _added_ to the existing
544 * response - currently Digest or Negotiate auth
545 */
546 if (auth_user_request != nullptr) {
547 auth_user_request->addAuthenticationInfoHeader(rep, accelerated);
548 if (auth_user_request->lastReply != AUTH_AUTHENTICATED)
549 auth_user_request->lastReply = AUTH_ACL_CANNOT_AUTHENTICATE;
550 }
551}
552
555{
556 return Auth::Scheme::Find(user()->config->type());
557}
558
559const char *
561{
562 if (Format::Format *reqFmt = user()->config->keyExtras) {
563 static MemBuf mb;
564 mb.reset();
565 // We should pass AccessLogEntry as second argument ....
567 request->auth_user_request = this;
568 reqFmt->assemble(mb, al, 0);
569 request->auth_user_request = oldReq;
570 debugs(29, 5, "Assembled line to send :" << mb.content());
571 return mb.content();
572 }
573 return nullptr;
574}
575
576void
578{
579 auto messageNote = reply.notes.find("message");
580
581 if (!messageNote) {
582 messageNote = ToSBuf(proto, " Authentication denied with no reason given");
583 }
584
585 setDenyMessage(messageNote->c_str());
586}
AuthAclState
@ AUTH_AUTHENTICATED
@ AUTH_ACL_CANNOT_AUTHENTICATE
@ AUTH_ACL_CHALLENGE
@ AUTH_ACL_HELPER
void UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
static Auth::ConfigVector & schemesConfig(HttpRequest *request, HttpReply *rep)
void authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer auth_user_request)
void authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
static void authenticateAuthUserRequestSetIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
static Auth::UserRequest::Pointer authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData *conn, HttpRequest *request)
int authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer auth_user_request)
bool authenticateUserAuthenticated(const Auth::UserRequest::Pointer &auth_user_request)
static void authenticateAuthenticateUser(Auth::UserRequest::Pointer auth_user_request, HttpRequest *request, ConnStateData *conn, Http::HdrType type)
void AUTHCB(void *)
Definition UserRequest.h:57
#define assert(EX)
Definition assert.h:17
static void authenticate(int socket_fd, const char *username, const char *passwd)
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
void updateReply(const HttpReply::Pointer &)
Auth::ConfigVector schemes
set of auth_params directives
Definition Config.h:29
acl_access * schemeAccess
the ACL list for auth_schemes directives
Definition Config.h:35
std::vector< Auth::SchemesConfig > schemeLists
set of auth_schemes directives
Definition Config.h:32
virtual void fixHeader(UserRequest::Pointer, HttpReply *, Http::HdrType, HttpRequest *)=0
virtual const char * type() const =0
static SchemeConfig * Find(const char *proxy_auth)
static UserRequest::Pointer CreateAuthUser(const char *proxy_auth, AccessLogEntry::Pointer &al)
static Scheme::Pointer Find(const char *)
Definition Scheme.cc:31
virtual void addAuthenticationInfoHeader(HttpReply *rep, int accel)
static AuthAclState tryToAuthenticateAndSetAuthUser(UserRequest::Pointer *aUR, Http::HdrType, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &)
const char * helperRequestKeyExtras(HttpRequest *, AccessLogEntry::Pointer &al)
static void AddReplyAuthHeader(HttpReply *rep, UserRequest::Pointer auth_user_request, HttpRequest *request, int accelerated, int internal)
Add the appropriate [Proxy-]Authenticate header to the given reply.
void setDenyMessage(char const *)
virtual bool authenticated() const
void denyMessageFromHelper(char const *proto, const Helper::Reply &reply)
Sets the reason of 'authentication denied' helper response.
Scheme::Pointer scheme() const
virtual void releaseAuthServer()
virtual void addAuthenticationInfoTrailer(HttpReply *rep, int accel)
bool valid() const
char const * denyMessage(char const *const default_message=nullptr) const
void start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *handler, void *data)
char const * getDenyMessage() const
Direction direction()
AuthAclState lastReply
virtual const char * connLastHeader()
char const * username() const
virtual User::Pointer user()
virtual void authenticate(HttpRequest *request, ConnStateData *conn, Http::HdrType type)=0
~UserRequest() override
Auth::SchemeConfig * config
Definition User.h:50
NotePairs notes
list of key=value pairs the helper produced
Definition User.h:56
size_t ipcount
Definition User.h:52
void clearIp()
Definition User.cc:140
void removeIp(Ip::Address)
Definition User.cc:162
void addIp(Ip::Address)
Definition User.cc:185
void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause)
const Auth::UserRequest::Pointer & getAuth() const
NotePairs notes
Definition Reply.h:62
const char * getStr(Http::HdrType id) const
Http::StatusLine sline
Definition HttpReply.h:56
Auth::UserRequest::Pointer auth_user_request
HttpHeader header
Definition Message.h:74
Http::StatusCode status() const
retrieve the status code for this status line
Definition StatusLine.h:45
char * content()
start of the added data
Definition MemBuf.h:41
void reset()
Definition MemBuf.cc:129
std::optional< SBuf > find(const char *noteKey, const char *sep=",") const
Definition Notes.cc:281
C * getRaw() const
Definition RefCount.h:89
Comm::ConnectionPointer clientConnection
Definition Server.h:100
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
void fatal(const char *message)
Definition fatal.cc:28
@ AUTH_BROKEN
Definition Type.h:23
@ AUTH_UNKNOWN
Definition Type.h:18
std::vector< Auth::SchemeConfig * > ConfigVector
Definition forward.h:24
Auth::Config TheConfig
Definition Config.cc:15
@ CRED_ERROR
ERROR in the auth module. Cannot determine the state of this request.
Definition UserRequest.h:68
@ CRED_CHALLENGE
Client needs to be challenged. secure token.
Definition UserRequest.h:65
@ CRED_LOOKUP
Credentials need to be validated with the backend helper.
Definition UserRequest.h:67
@ CRED_VALID
Credentials are valid and a up to date. The OK/Failed state is accurate.
Definition UserRequest.h:66
@ scUnauthorized
Definition StatusCode.h:46
@ scProxyAuthenticationRequired
Definition StatusCode.h:52
@ PROXY_AUTHENTICATE
#define xstrdup
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
int const char size_t
#define safe_free(x)
Definition xalloc.h:73