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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors