UserRequest.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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 #include "squid.h"
10 #include "AccessLogEntry.h"
11 #include "auth/digest/Config.h"
12 #include "auth/digest/User.h"
14 #include "auth/State.h"
15 #include "format/Format.h"
16 #include "helper.h"
17 #include "helper/Reply.h"
18 #include "HttpHeaderTools.h"
19 #include "HttpReply.h"
20 #include "HttpRequest.h"
21 #include "MemBuf.h"
22 #include "SquidTime.h"
23 
24 Auth::Digest::UserRequest::UserRequest() :
25  noncehex(NULL),
26  cnonce(NULL),
27  realm(NULL),
28  pszPass(NULL),
29  algorithm(NULL),
30  pszMethod(NULL),
31  qop(NULL),
32  uri(NULL),
33  response(NULL),
34  nonce(NULL)
35 {
36  memset(nc, 0, sizeof(nc));
37  memset(&flags, 0, sizeof(flags));
38 }
39 
44 Auth::Digest::UserRequest::~UserRequest()
45 {
46  assert(LockCount()==0);
47 
48  safe_free(noncehex);
49  safe_free(cnonce);
50  safe_free(realm);
51  safe_free(pszPass);
52  safe_free(algorithm);
53  safe_free(pszMethod);
54  safe_free(qop);
55  safe_free(uri);
56  safe_free(response);
57 
58  if (nonce)
59  authDigestNonceUnlink(nonce);
60 }
61 
62 int
63 Auth::Digest::UserRequest::authenticated() const
64 {
65  if (user() != NULL && user()->credentials() == Auth::Ok)
66  return 1;
67 
68  return 0;
69 }
70 
71 const char *
72 Auth::Digest::UserRequest::credentialsStr()
73 {
74  return realm;
75 }
76 
79 void
81 {
82  HASHHEX SESSIONKEY;
83  HASHHEX HA2 = "";
84  HASHHEX Response;
85 
86  /* if the check has corrupted the user, just return */
87  if (user() == NULL || user()->credentials() == Auth::Failed) {
88  return;
89  }
90 
91  Auth::User::Pointer auth_user = user();
92 
93  Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User*>(auth_user.getRaw());
94  assert(digest_user != NULL);
95 
96  Auth::Digest::UserRequest *digest_request = this;
97 
98  /* do we have the HA1 */
99  if (!digest_user->HA1created) {
100  auth_user->credentials(Auth::Pending);
101  return;
102  }
103 
104  if (digest_request->nonce == NULL) {
105  /* this isn't a nonce we issued */
106  auth_user->credentials(Auth::Failed);
107  return;
108  }
109 
110  DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
111  authenticateDigestNonceNonceHex(digest_request->nonce),
112  digest_request->cnonce,
113  digest_user->HA1, SESSIONKEY);
114  SBuf sTmp = request->method.image();
115  DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceHex(digest_request->nonce),
116  digest_request->nc, digest_request->cnonce, digest_request->qop,
117  sTmp.c_str(), digest_request->uri, HA2, Response);
118 
119  debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
120 
121  if (strcasecmp(digest_request->response, Response) != 0) {
122  if (!digest_request->flags.helper_queried) {
123  /* Query the helper in case the password has changed */
124  digest_request->flags.helper_queried = true;
125  auth_user->credentials(Auth::Pending);
126  return;
127  }
128 
129  if (static_cast<Auth::Digest::Config*>(Auth::SchemeConfig::Find("digest"))->PostWorkaround && request->method != Http::METHOD_GET) {
130  /* Ugly workaround for certain very broken browsers using the
131  * wrong method to calculate the request-digest on POST request.
132  * This should be deleted once Digest authentication becomes more
133  * widespread and such broken browsers no longer are commonly
134  * used.
135  */
137  DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceHex(digest_request->nonce),
138  digest_request->nc, digest_request->cnonce, digest_request->qop,
139  sTmp.c_str(), digest_request->uri, HA2, Response);
140 
141  if (strcasecmp(digest_request->response, Response)) {
142  auth_user->credentials(Auth::Failed);
143  digest_request->flags.invalid_password = true;
144  digest_request->setDenyMessage("Incorrect password");
145  return;
146  } else {
147  const char *useragent = request->header.getStr(Http::HdrType::USER_AGENT);
148 
149  static Ip::Address last_broken_addr;
150  static int seen_broken_client = 0;
151 
152  if (!seen_broken_client) {
153  last_broken_addr.setNoAddr();
154  seen_broken_client = 1;
155  }
156 
157  if (last_broken_addr != request->client_addr) {
158  debugs(29, DBG_IMPORTANT, "Digest POST bug detected from " <<
159  request->client_addr << " using '" <<
160  (useragent ? useragent : "-") <<
161  "'. Please upgrade browser. See Bug #630 for details.");
162 
163  last_broken_addr = request->client_addr;
164  }
165  }
166  } else {
167  auth_user->credentials(Auth::Failed);
168  digest_request->flags.invalid_password = true;
169  digest_request->setDenyMessage("Incorrect password");
170  return;
171  }
172  }
173 
174  /* check for stale nonce */
175  /* check Auth::Pending to avoid loop */
176 
177  if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc) && user()->credentials() != Auth::Pending) {
178  debugs(29, 3, auth_user->username() << "' validated OK but nonce stale: " << digest_request->noncehex);
179  /* Pending prevent banner and makes a ldap control */
180  auth_user->credentials(Auth::Pending);
181  nonce->flags.valid = false;
182  authDigestNoncePurge(nonce);
183  return;
184  }
185 
186  auth_user->credentials(Auth::Ok);
187 
188  /* password was checked and did match */
189  debugs(29, 4, "user '" << auth_user->username() << "' validated OK");
190 }
191 
193 Auth::Digest::UserRequest::module_direction()
194 {
195  if (user()->auth_type != Auth::AUTH_DIGEST)
196  return Auth::CRED_ERROR;
197 
198  switch (user()->credentials()) {
199 
200  case Auth::Ok:
201  return Auth::CRED_VALID;
202 
203  case Auth::Handshake:
204  case Auth::Failed:
205  /* send new challenge */
206  return Auth::CRED_CHALLENGE;
207 
208  case Auth::Unchecked:
209  case Auth::Pending:
210  return Auth::CRED_LOOKUP;
211 
212  default:
213  return Auth::CRED_ERROR;
214  }
215 }
216 
217 void
218 Auth::Digest::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
219 {
221 
222  /* don't add to authentication error pages */
223  if ((!accel && rep->sline.status() == Http::scProxyAuthenticationRequired)
224  || (accel && rep->sline.status() == Http::scUnauthorized))
225  return;
226 
228 
229 #if WAITING_FOR_TE
230  /* test for http/1.1 transfer chunked encoding */
231  if (chunkedtest)
232  return;
233 #endif
234 
235  if ((static_cast<Auth::Digest::Config*>(Auth::SchemeConfig::Find("digest"))->authenticateProgram) && authDigestNonceLastRequest(nonce)) {
236  flags.authinfo_sent = true;
237  Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(user().getRaw());
238  if (!digest_user)
239  return;
240 
241  digest_nonce_h *nextnonce = digest_user->currentNonce();
242  if (!nextnonce || authDigestNonceLastRequest(nonce)) {
243  nextnonce = authenticateDigestNonceNew();
244  authDigestUserLinkNonce(digest_user, nextnonce);
245  }
246  debugs(29, 9, "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceHex(nextnonce) << "\"");
247  httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceHex(nextnonce));
248  }
249 }
250 
251 #if WAITING_FOR_TE
252 void
253 Auth::Digest::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accel)
254 {
255  int type;
256 
257  if (!auth_user_request)
258  return;
259 
260  /* has the header already been send? */
261  if (flags.authinfo_sent)
262  return;
263 
264  /* don't add to authentication error pages */
265  if ((!accel && rep->sline.status() == Http::scProxyAuthenticationRequired)
266  || (accel && rep->sline.status() == Http::scUnauthorized))
267  return;
268 
270 
271  if ((static_cast<Auth::Digest::Config*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
272  Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
273  nonce = digest_user->currentNonce();
274  if (!nonce) {
275  nonce = authenticateDigestNonceNew();
276  authDigestUserLinkNonce(digest_user, nonce);
277  }
278  debugs(29, 9, "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceHex(nonce) << "\"");
279  httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceHex(nonce));
280  }
281 }
282 #endif
283 
284 /* send the initial data to a digest authenticator module */
285 void
286 Auth::Digest::UserRequest::startHelperLookup(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
287 {
288  char buf[8192];
289 
290  assert(user() != NULL && user()->auth_type == Auth::AUTH_DIGEST);
291  debugs(29, 9, HERE << "'\"" << user()->username() << "\":\"" << realm << "\"'");
292 
293  if (static_cast<Auth::Digest::Config*>(Auth::SchemeConfig::Find("digest"))->authenticateProgram == NULL) {
294  debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
295  handler(data);
296  return;
297  }
298 
299  const char *keyExtras = helperRequestKeyExtras(request, al);
300  if (keyExtras)
301  snprintf(buf, 8192, "\"%s\":\"%s\" %s\n", user()->username(), realm, keyExtras);
302  else
303  snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
304 
305  helperSubmit(digestauthenticators, buf, Auth::Digest::UserRequest::HandleReply,
306  new Auth::StateData(this, handler, data));
307 }
308 
309 void
310 Auth::Digest::UserRequest::HandleReply(void *data, const Helper::Reply &reply)
311 {
312  Auth::StateData *replyData = static_cast<Auth::StateData *>(data);
313  debugs(29, 9, HERE << "reply=" << reply);
314 
315  assert(replyData->auth_user_request != NULL);
316  Auth::UserRequest::Pointer auth_user_request = replyData->auth_user_request;
317 
318  // add new helper kv-pair notes to the credentials object
319  // so that any transaction using those credentials can access them
320  static const NotePairs::Names appendables = { SBuf("group"), SBuf("nonce"), SBuf("tag") };
321  auth_user_request->user()->notes.replaceOrAddOrAppend(&reply.notes, appendables);
322  // remove any private credentials detail which got added.
323  auth_user_request->user()->notes.remove("ha1");
324 
325  static bool oldHelperWarningDone = false;
326  switch (reply.result) {
327  case Helper::Unknown: {
328  // Squid 3.3 and older the digest helper only returns a HA1 hash (no "OK")
329  // the HA1 will be found in content() for these responses.
330  if (!oldHelperWarningDone) {
331  debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format HA1 response. It needs to be upgraded.");
332  oldHelperWarningDone=true;
333  }
334 
335  /* allow this because the digest_request pointer is purely local */
336  Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
337  assert(digest_user != NULL);
338 
339  CvtBin(reply.other().content(), digest_user->HA1);
340  digest_user->HA1created = 1;
341  }
342  break;
343 
344  case Helper::Okay: {
345  /* allow this because the digest_request pointer is purely local */
346  Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
347  assert(digest_user != NULL);
348 
349  if (const char *ha1Note = reply.notes.findFirst("ha1")) {
350  CvtBin(ha1Note, digest_user->HA1);
351  digest_user->HA1created = 1;
352  } else {
353  debugs(29, DBG_IMPORTANT, "ERROR: Digest auth helper did not produce a HA1. Using the wrong helper program? received: " << reply);
354  }
355  }
356  break;
357 
358  case Helper::TT:
359  debugs(29, DBG_IMPORTANT, "ERROR: Digest auth does not support the result code received. Using the wrong helper program? received: " << reply);
360  // fall through to next case. Handle this as an ERR response.
361 
362  case Helper::TimedOut:
364  // TODO retry the broken lookup on another helper?
365  // fall through to next case for now. Handle this as an ERR response silently.
366  case Helper::Error: {
367  /* allow this because the digest_request pointer is purely local */
368  Auth::Digest::UserRequest *digest_request = dynamic_cast<Auth::Digest::UserRequest *>(auth_user_request.getRaw());
369  assert(digest_request);
370 
371  digest_request->user()->credentials(Auth::Failed);
372  digest_request->flags.invalid_password = true;
373 
374  SBuf msgNote;
375  if (reply.notes.find(msgNote, "message")) {
376  digest_request->setDenyMessage(msgNote.c_str());
377  } else if (reply.other().hasContent()) {
378  // old helpers did send ERR result but a bare message string instead of message= key name.
379  digest_request->setDenyMessage(reply.other().content());
380  if (!oldHelperWarningDone) {
381  debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format ERR response. It needs to be upgraded.");
382  oldHelperWarningDone=true;
383  }
384  }
385  }
386  break;
387  }
388 
389  void *cbdata = NULL;
390  if (cbdataReferenceValidDone(replyData->data, &cbdata))
391  replyData->handler(cbdata);
392 
393  delete replyData;
394 }
395 
char method[16]
Definition: tcp-banger2.c:115
static Auth::Config * getConfig(char const *type_str)
Definition: testAuth.cc:69
const char * findFirst(const char *noteKey) const
Definition: Notes.cc:265
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:256
HttpHeader header
Definition: Message.h:75
void * data
Definition: State.h:38
static void handler(int signo)
Definition: purge.cc:860
int authDigestNonceIsValid(digest_nonce_h *nonce, char nc[9])
Definition: Config.cc:357
AUTHCB * handler
Definition: State.h:40
bool hasContent() const
Definition: MemBuf.h:54
Definition: SBuf.h:86
static void authenticate(int socket_fd, const char *username, const char *passwd)
Credentials need to be validated with the backend helper.
Definition: UserRequest.h:67
Definition: cbdata.cc:60
int type
Definition: errorpage.cc:152
C * getRaw() const
Definition: RefCount.h:80
const SBuf & image() const
Http::StatusLine sline
Definition: HttpReply.h:60
#define DBG_CRITICAL
Definition: Debug.h:45
UserRequest::Pointer auth_user_request
Definition: State.h:39
Direction
Definition: UserRequest.h:64
#define DBG_IMPORTANT
Definition: Debug.h:46
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
std::vector< SBuf > Names
Definition: Notes.h:194
void DigestCalcResponse(const HASHHEX HA1, const char *pszNonce, const char *pszNonceCount, const char *pszCNonce, const char *pszQop, const char *pszMethod, const char *pszDigestUri, const HASHHEX HEntity, HASHHEX Response)
Definition: rfc2617.c:126
static SchemeConfig * Find(const char *proxy_auth)
Definition: SchemeConfig.cc:59
const MemBuf & other() const
Definition: Reply.h:42
Client needs to be challenged. secure token.
Definition: UserRequest.h:65
#define NULL
Definition: types.h:166
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
void setNoAddr()
Definition: Address.cc:292
Helper::ResultCode result
The helper response 'result' field.
Definition: Reply.h:59
void authDigestUserLinkNonce(Auth::Digest::User *user, digest_nonce_h *nonce)
Definition: Config.cc:683
ERROR in the auth module. Cannot determine the state of this request.
Definition: UserRequest.h:68
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:157
NotePairs notes
Definition: Reply.h:62
digest_nonce_h * authenticateDigestNonceNew(void)
Definition: Config.cc:124
void const char HLPCB void * data
Definition: stub_helper.cc:16
bool find(SBuf &resultNote, const char *noteKey, const char *sep=",") const
Definition: Notes.cc:240
#define safe_free(x)
Definition: xalloc.h:73
void CvtBin(const HASHHEX Hex, HASH Bin)
Definition: rfc2617.c:49
#define assert(EX)
Definition: assert.h:19
const char * c_str()
Definition: SBuf.cc:526
void authDigestNoncePurge(digest_nonce_h *nonce)
Definition: Config.cc:458
char HASHHEX[HASHHEXLEN+1]
Definition: rfc2617.h:33
int authDigestNonceLastRequest(digest_nonce_h *nonce)
Definition: Config.cc:438
void helperSubmit(helper *hlp, const char *buf, HLPCB *callback, void *data)
Definition: helper.cc:468
Credentials are valid and a up to date. The OK/Failed state is accurate.
Definition: UserRequest.h:66
char * content()
start of the added data
Definition: MemBuf.h:41
static char credentials[MAX_USERNAME_LEN+MAX_DOMAIN_LEN+2]
helper * digestauthenticators
Definition: Config.cc:50
void authDigestNonceUnlink(digest_nonce_h *nonce)
Definition: Config.cc:311
const char * authenticateDigestNonceNonceHex(const digest_nonce_h *nonce)
Definition: Config.cc:328
void DigestCalcHA1(const char *pszAlg, const char *pszUserName, const char *pszRealm, const char *pszPassword, const char *pszNonce, const char *pszCNonce, HASH HA1, HASHHEX SessionKey)
Definition: rfc2617.c:88
void AUTHCB(void *)
Definition: UserRequest.h:57
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void const char * buf
Definition: stub_helper.cc:16

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors