UserRequest.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 
31 char const *
33 {
34  if (user() != NULL)
35  return user()->username();
36  else
37  return NULL;
38 }
39 
40 /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
41 
42 /* send the initial data to an authenticator module */
43 void
45 {
46  assert(handler);
47  assert(data);
48  debugs(29, 9, this);
49  startHelperLookup(request, al, handler, data);
50 }
51 
52 bool
54 {
55  debugs(29, 9, HERE << "Validating Auth::UserRequest '" << this << "'.");
56 
57  if (user() == NULL) {
58  debugs(29, 4, HERE << "No associated Auth::User data");
59  return false;
60  }
61 
62  if (user()->auth_type == Auth::AUTH_UNKNOWN) {
63  debugs(29, 4, HERE << "Auth::User '" << user() << "' uses unknown scheme.");
64  return false;
65  }
66 
67  if (user()->auth_type == Auth::AUTH_BROKEN) {
68  debugs(29, 4, HERE << "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, HERE << "Validated. Auth::UserRequest '" << this << "'.");
76  return true;
77 }
78 
79 void *
80 Auth::UserRequest::operator new (size_t)
81 {
82  fatal("Auth::UserRequest not directly allocatable\n");
83  return (void *)1;
84 }
85 
86 void
87 Auth::UserRequest::operator delete (void *)
88 {
89  fatal("Auth::UserRequest child failed to override operator delete\n");
90 }
91 
93  _auth_user(NULL),
94  message(NULL),
96 {
97  debugs(29, 5, HERE << "initialised request " << this);
98 }
99 
101 {
102  assert(LockCount()==0);
103  debugs(29, 5, HERE << "freeing request " << this);
104 
105  if (user() != NULL) {
106  /* release our references to the user credentials */
107  user(NULL);
108  }
109 
110  safe_free(message);
111 }
112 
113 void
115 {
116  safe_free(message);
117  message = xstrdup(aString);
118 }
119 
120 char const *
122 {
123  return message;
124 }
125 
126 char const *
127 Auth::UserRequest::denyMessage(char const * const default_message) const
128 {
129  if (getDenyMessage() == NULL)
130  return default_message;
131 
132  return getDenyMessage();
133 }
134 
135 static 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 
146 void
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 
157 void
159 {
160  if (auth_user_request != NULL)
161  auth_user_request->user()->clearIp();
162 }
163 
164 int
166 {
167  assert(auth_user_request != NULL);
168  assert(auth_user_request->user() != NULL);
169  return auth_user_request->user()->ipcount;
170 }
171 
172 /*
173  * authenticateUserAuthenticated: is this auth_user structure logged in ?
174  */
175 int
177 {
178  if (auth_user_request == NULL || !auth_user_request->valid())
179  return 0;
180 
181  return auth_user_request->authenticated();
182 }
183 
186 {
187  if (user() == NULL)
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 
196 void
198 {}
199 
200 void
202 {}
203 
204 void
206 {}
207 
208 const char *
210 {
211  fatal("Auth::UserRequest::connLastHeader should always be overridden by conn based auth schemes");
212  return NULL;
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  */
221 static void
223 {
224  assert(auth_user_request.getRaw() != NULL);
225 
226  auth_user_request->authenticate(request, conn, type);
227 }
228 
231 {
233 
234  if (auth_user_request != NULL)
235  res = auth_user_request;
236  else if (request != NULL && request->auth_user_request != NULL)
237  res = request->auth_user_request;
238  else if (conn != NULL)
239  res = conn->getAuth();
240 
241  // attach the credential notes from helper to the transaction
242  if (request != NULL && res != NULL && res->user() != NULL) {
243  // XXX: we have no access to the transaction / AccessLogEntry so cant 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 recieve 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 == NULL && !authenticateUserAuthenticated(authTryGetUser(*auth_user_request,conn,request))) {
291  /* no header or authentication failed/got corrupted - restart */
292  debugs(29, 4, HERE << "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 != NULL) {
298  conn->setAuth(NULL, "HTTP request missing credentials");
299  }
300 
301  *auth_user_request = NULL;
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 != NULL && conn->getAuth() != NULL &&
312  conn->getAuth()->connLastHeader() != NULL &&
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 == NULL);
325  conn->setAuth(NULL, "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, HERE << "header " << (proxy_auth ? proxy_auth : "-") << ".");
331 
332  if (*auth_user_request == NULL) {
333  if (conn != NULL) {
334  debugs(29, 9, HERE << "This is a new checklist test on:" << conn->clientConnection);
335  }
336 
337  if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->getAuth() != NULL) {
338  Auth::SchemeConfig * scheme = Auth::SchemeConfig::Find(proxy_auth);
339 
340  if (conn->getAuth()->user() == NULL || conn->getAuth()->user()->config != scheme) {
341  debugs(29, DBG_IMPORTANT, "WARNING: Unexpected change of authentication scheme from '" <<
342  (conn->getAuth()->user()!=NULL?conn->getAuth()->user()->config->type():"[no user]") <<
343  "' to '" << proxy_auth << "' (client " <<
344  src_addr << ")");
345 
346  conn->setAuth(NULL, "changed auth scheme");
347  }
348  }
349 
350  if (request->auth_user_request == NULL && (conn == NULL || conn->getAuth() == NULL)) {
351  /* beginning of a new request check */
352  debugs(29, 4, HERE << "No connection authentication type");
353 
354  *auth_user_request = Auth::SchemeConfig::CreateAuthUser(proxy_auth, al);
355  if (*auth_user_request == NULL)
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 = NULL;
366  return AUTH_ACL_CHALLENGE;
367  }
368 
369  } else if (request->auth_user_request != NULL) {
370  *auth_user_request = request->auth_user_request;
371  } else {
372  assert (conn != NULL);
373  if (conn->getAuth() != NULL) {
374  *auth_user_request = conn->getAuth();
375  } else {
376  /* failed connection based authentication */
377  debugs(29, 4, HERE << "Auth user request " << *auth_user_request << " conn-auth missing and failed to authenticate.");
378  *auth_user_request = NULL;
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 == NULL) {
393  request->auth_user_request = *auth_user_request;
394  }
395 
396  /* fallthrough to ERROR case and do the challenge */
397 
398  case Auth::CRED_ERROR:
399  /* this ACL check is finished. */
400  *auth_user_request = NULL;
401  return AUTH_ACL_CHALLENGE;
402 
403  case Auth::CRED_LOOKUP:
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 = NULL;
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 == NULL) {
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
440  Auth::UserRequest::Pointer t = authTryGetUser(*aUR, conn, request);
441 
443  if (*aUR == NULL)
444  *aUR = t;
445 
446  if (request->auth_user_request == NULL && 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 != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
459  t->lastReply = result;
460 
461  return result;
462 }
463 
464 static Auth::ConfigVector &
466 {
467  if (!Auth::TheConfig.schemeLists.empty() && Auth::TheConfig.schemeAccess) {
468  ACLFilledChecklist ch(NULL, request, NULL);
469  ch.reply = rep;
470  HTTPMSGLOCK(ch.reply);
471  const allow_t answer = ch.fastCheck(Auth::TheConfig.schemeAccess);
472  if (answer.allowed())
473  return Auth::TheConfig.schemeLists.at(answer.kind).authConfigs;
474  }
475  return Auth::TheConfig.schemes;
476 }
477 
478 void
479 Auth::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 {
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 */
499  type = Http::HdrType::BAD_HDR;
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 != NULL && 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 != NULL && auth_user_request->scheme()->type() == scheme->type())
519  scheme->fixHeader(auth_user_request, rep, type, request);
520  else
521  scheme->fixHeader(NULL, rep, type, request);
522  } else
523  debugs(29, 4, HERE << "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 != NULL) {
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 
546 const 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 NULL;
561 }
562 
563 void
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 
void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause)
Definition: client_side.cc:503
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
#define assert(EX)
Definition: assert.h:17
void start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *handler, void *data)
Definition: UserRequest.cc:44
void UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
Definition: HttpRequest.cc:689
void denyMessageFromHelper(char const *proto, const Helper::Reply &reply)
Sets the reason of 'authentication denied' helper response.
Definition: UserRequest.cc:564
virtual void authenticate(HttpRequest *request, ConnStateData *conn, Http::HdrType type)=0
int type
Definition: errorpage.cc:79
Definition: Acl.h:113
Definition: SBuf.h:87
static void authenticate(int socket_fd, const char *username, const char *passwd)
int authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer auth_user_request)
Definition: UserRequest.cc:165
AuthAclState
Definition: AuthAclState.h:14
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
#define xstrdup
SBuf & append(const SBuf &S)
Definition: SBuf.cc:207
const char * helperRequestKeyExtras(HttpRequest *, AccessLogEntry::Pointer &al)
Definition: UserRequest.cc:547
char const * getDenyMessage() const
Definition: UserRequest.cc:121
virtual void addAuthenticationInfoHeader(HttpReply *rep, int accel)
Definition: UserRequest.cc:197
#define safe_free(x)
Definition: xalloc.h:73
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:154
Auth::SchemeConfig * config
Definition: User.h:50
Auth::ConfigVector schemes
set of auth_params directives
Definition: Config.h:32
Direction
Definition: UserRequest.h:64
std::vector< Auth::SchemeConfig * > ConfigVector
Definition: forward.h:23
virtual void fixHeader(UserRequest::Pointer, HttpReply *, Http::HdrType, HttpRequest *)=0
int conn
the current server connection FD
Definition: Transport.cc:26
int authenticateUserAuthenticated(Auth::UserRequest::Pointer auth_user_request)
Definition: UserRequest.cc:176
static Auth::UserRequest::Pointer authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData *conn, HttpRequest *request)
Definition: UserRequest.cc:230
AuthAclState lastReply
Definition: UserRequest.h:234
Client needs to be challenged. secure token.
Definition: UserRequest.h:65
void const char HLPCB void * data
Definition: stub_helper.cc:16
NotePairs notes
Definition: Reply.h:61
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
#define DBG_IMPORTANT
Definition: Debug.h:45
void addIp(Ip::Address)
Definition: User.cc:185
virtual void addAuthenticationInfoTrailer(HttpReply *rep, int accel)
Definition: UserRequest.cc:201
ERROR in the auth module. Cannot determine the state of this request.
Definition: UserRequest.h:68
void reset()
Definition: MemBuf.cc:141
void setDenyMessage(char const *)
Definition: UserRequest.cc:114
void authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
Definition: UserRequest.cc:147
void removeIp(Ip::Address)
Definition: User.cc:162
int kind
which custom access list verb matched
Definition: Acl.h:153
static Scheme::Pointer Find(const char *)
Definition: Scheme.cc:33
static Auth::ConfigVector & schemesConfig(HttpRequest *request, HttpReply *rep)
Definition: UserRequest.cc:465
void fatal(const char *message)
Definition: fatal.cc:39
bool allowed() const
Definition: Acl.h:141
virtual void releaseAuthServer()
Definition: UserRequest.cc:205
const char * c_str()
Definition: SBuf.cc:546
Http::StatusLine sline
Definition: HttpReply.h:60
static void authenticateAuthUserRequestSetIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
Definition: UserRequest.cc:136
virtual ~UserRequest()
Definition: UserRequest.cc:100
bool find(SBuf &resultNote, const char *noteKey, const char *sep=",") const
Definition: Notes.cc:238
static void handler(int signo)
Definition: purge.cc:860
char * content()
start of the added data
Definition: MemBuf.h:41
acl_access * schemeAccess
the ACL list for auth_schemes directives
Definition: Config.h:38
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:147
char const * denyMessage(char const *const default_message=NULL) const
Definition: UserRequest.cc:127
Auth::Config TheConfig
Definition: stub_libauth.cc:21
static SchemeConfig * Find(const char *proxy_auth)
Definition: SchemeConfig.cc:58
static UserRequest::Pointer CreateAuthUser(const char *proxy_auth, AccessLogEntry::Pointer &al)
Definition: SchemeConfig.cc:32
HttpHeader header
Definition: Message.h:74
Credentials are valid and a up to date. The OK/Failed state is accurate.
Definition: UserRequest.h:66
allow_t const & fastCheck()
Definition: Checklist.cc:336
bool valid() const
Definition: UserRequest.cc:53
virtual User::Pointer user()
Definition: UserRequest.h:143
char const * username() const
Definition: UserRequest.cc:32
Credentials need to be validated with the backend helper.
Definition: UserRequest.h:67
Direction direction()
Definition: UserRequest.cc:185
int const char size_t
Definition: stub_liblog.cc:84
Definition: MemBuf.h:23
Comm::ConnectionPointer clientConnection
Definition: Server.h:97
Auth::UserRequest::Pointer auth_user_request
Definition: HttpRequest.h:115
virtual const char * type() const =0
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1203
void authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer auth_user_request)
Definition: UserRequest.cc:158
virtual const char * connLastHeader()
Definition: UserRequest.cc:209
void AUTHCB(void *)
Definition: UserRequest.h:57
C * getRaw() const
Definition: RefCount.h:74
#define NULL
Definition: types.h:166
const Auth::UserRequest::Pointer & getAuth() const
Definition: client_side.h:114
static AuthAclState tryToAuthenticateAndSetAuthUser(UserRequest::Pointer *aUR, Http::HdrType, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &)
Definition: UserRequest.cc:437
Scheme::Pointer scheme() const
Definition: UserRequest.cc:541
static void authenticateAuthenticateUser(Auth::UserRequest::Pointer auth_user_request, HttpRequest *request, ConnStateData *conn, Http::HdrType type)
Definition: UserRequest.cc:222
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors