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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors