]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/digest/UserRequest.cc
Fix compile issues in rev.13288
[thirdparty/squid.git] / src / auth / digest / UserRequest.cc
CommitLineData
f7f3304a 1#include "squid.h"
d4806c91 2#include "AccessLogEntry.h"
928f3421 3#include "auth/digest/auth_digest.h"
aa110616 4#include "auth/digest/User.h"
616cfc4c 5#include "auth/digest/UserRequest.h"
928f3421 6#include "auth/State.h"
25f98340 7#include "charset.h"
86c63190 8#include "format/Format.h"
a5bac1d2 9#include "HttpHeaderTools.h"
928f3421
AJ
10#include "HttpReply.h"
11#include "HttpRequest.h"
d4806c91 12#include "MemBuf.h"
928f3421
AJ
13#include "SquidTime.h"
14
c7baff40 15Auth::Digest::UserRequest::UserRequest() :
928f3421
AJ
16 nonceb64(NULL),
17 cnonce(NULL),
18 realm(NULL),
19 pszPass(NULL),
20 algorithm(NULL),
21 pszMethod(NULL),
22 qop(NULL),
23 uri(NULL),
24 response(NULL),
d232141d 25 nonce(NULL)
1032a194
AJ
26{
27 memset(nc, 0, sizeof(nc));
28 memset(&flags, 0, sizeof(flags));
29}
928f3421
AJ
30
31/**
32 * Delete the digest request structure.
33 * Does NOT delete related AuthUser structures
34 */
c7baff40 35Auth::Digest::UserRequest::~UserRequest()
928f3421 36{
8bf217bd 37 assert(LockCount()==0);
ea0695f2 38
928f3421
AJ
39 safe_free(nonceb64);
40 safe_free(cnonce);
41 safe_free(realm);
42 safe_free(pszPass);
43 safe_free(algorithm);
44 safe_free(pszMethod);
45 safe_free(qop);
46 safe_free(uri);
47 safe_free(response);
48
49 if (nonce)
50 authDigestNonceUnlink(nonce);
51}
52
928f3421 53int
c7baff40 54Auth::Digest::UserRequest::authenticated() const
928f3421 55{
d87154ee 56 if (user() != NULL && user()->credentials() == Auth::Ok)
928f3421
AJ
57 return 1;
58
59 return 0;
60}
61
d4806c91
CT
62const char *
63Auth::Digest::UserRequest::credentialsStr()
64{
65 return realm;
66}
67
928f3421
AJ
68/** log a digest user in
69 */
70void
c7baff40 71Auth::Digest::UserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
928f3421 72{
928f3421
AJ
73 HASHHEX SESSIONKEY;
74 HASHHEX HA2 = "";
75 HASHHEX Response;
76
928f3421 77 /* if the check has corrupted the user, just return */
d87154ee 78 if (user() == NULL || user()->credentials() == Auth::Failed) {
928f3421
AJ
79 return;
80 }
81
d87154ee 82 Auth::User::Pointer auth_user = user();
928f3421 83
aa110616 84 Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User*>(auth_user.getRaw());
56a49fda 85 assert(digest_user != NULL);
928f3421 86
c7baff40 87 Auth::Digest::UserRequest *digest_request = this;
56a49fda
AJ
88
89 /* do we have the HA1 */
928f3421 90 if (!digest_user->HA1created) {
d87154ee 91 auth_user->credentials(Auth::Pending);
928f3421
AJ
92 return;
93 }
94
95 if (digest_request->nonce == NULL) {
96 /* this isn't a nonce we issued */
d87154ee 97 auth_user->credentials(Auth::Failed);
928f3421
AJ
98 return;
99 }
100
101 DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
102 authenticateDigestNonceNonceb64(digest_request->nonce),
103 digest_request->cnonce,
104 digest_user->HA1, SESSIONKEY);
105 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
106 digest_request->nc, digest_request->cnonce, digest_request->qop,
107 RequestMethodStr(request->method), digest_request->uri, HA2, Response);
108
109 debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
110
111 if (strcasecmp(digest_request->response, Response) != 0) {
112 if (!digest_request->flags.helper_queried) {
113 /* Query the helper in case the password has changed */
3dd52a0b 114 digest_request->flags.helper_queried = true;
d87154ee 115 auth_user->credentials(Auth::Pending);
928f3421
AJ
116 return;
117 }
118
c2a7cefd 119 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->PostWorkaround && request->method != Http::METHOD_GET) {
928f3421
AJ
120 /* Ugly workaround for certain very broken browsers using the
121 * wrong method to calculate the request-digest on POST request.
122 * This should be deleted once Digest authentication becomes more
123 * widespread and such broken browsers no longer are commonly
124 * used.
125 */
126 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
127 digest_request->nc, digest_request->cnonce, digest_request->qop,
c2a7cefd 128 RequestMethodStr(Http::METHOD_GET), digest_request->uri, HA2, Response);
928f3421
AJ
129
130 if (strcasecmp(digest_request->response, Response)) {
d87154ee 131 auth_user->credentials(Auth::Failed);
3dd52a0b 132 digest_request->flags.invalid_password = true;
928f3421
AJ
133 digest_request->setDenyMessage("Incorrect password");
134 return;
135 } else {
136 const char *useragent = request->header.getStr(HDR_USER_AGENT);
137
08acdd08 138 static Ip::Address last_broken_addr;
928f3421
AJ
139 static int seen_broken_client = 0;
140
141 if (!seen_broken_client) {
4dd643d5 142 last_broken_addr.setNoAddr();
928f3421
AJ
143 seen_broken_client = 1;
144 }
145
146 if (last_broken_addr != request->client_addr) {
c7baff40 147 debugs(29, DBG_IMPORTANT, "Digest POST bug detected from " <<
928f3421
AJ
148 request->client_addr << " using '" <<
149 (useragent ? useragent : "-") <<
150 "'. Please upgrade browser. See Bug #630 for details.");
151
152 last_broken_addr = request->client_addr;
153 }
154 }
155 } else {
d87154ee 156 auth_user->credentials(Auth::Failed);
3dd52a0b 157 digest_request->flags.invalid_password = true;
928f3421
AJ
158 digest_request->setDenyMessage("Incorrect password");
159 return;
160 }
c9dbe80f 161 }
928f3421 162
c9dbe80f
FB
163 /* check for stale nonce */
164 if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
165 debugs(29, 3, "user '" << auth_user->username() << "' validated OK but nonce stale");
166 auth_user->credentials(Auth::Failed);
167 digest_request->setDenyMessage("Stale nonce");
168 return;
928f3421
AJ
169 }
170
d87154ee 171 auth_user->credentials(Auth::Ok);
928f3421
AJ
172
173 /* password was checked and did match */
c7baff40 174 debugs(29, 4, HERE << "user '" << auth_user->username() << "' validated OK");
928f3421
AJ
175
176 /* auth_user is now linked, we reset these values
177 * after external auth occurs anyway */
178 auth_user->expiretime = current_time.tv_sec;
179 return;
180}
181
51a3dd58 182Auth::Direction
c7baff40 183Auth::Digest::UserRequest::module_direction()
928f3421 184{
616cfc4c 185 if (user()->auth_type != Auth::AUTH_DIGEST)
51a3dd58 186 return Auth::CRED_ERROR;
928f3421 187
d232141d 188 switch (user()->credentials()) {
928f3421 189
d87154ee 190 case Auth::Ok:
51a3dd58 191 return Auth::CRED_VALID;
928f3421 192
d87154ee 193 case Auth::Failed:
928f3421 194 /* send new challenge */
51a3dd58 195 return Auth::CRED_CHALLENGE;
d232141d 196
d87154ee
AJ
197 case Auth::Unchecked:
198 case Auth::Pending:
51a3dd58 199 return Auth::CRED_LOOKUP;
d232141d
AJ
200
201 default:
51a3dd58 202 return Auth::CRED_ERROR;
928f3421 203 }
928f3421
AJ
204}
205
928f3421 206void
c7baff40 207Auth::Digest::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
928f3421
AJ
208{
209 http_hdr_type type;
210
211 /* don't add to authentication error pages */
9b769c67
AJ
212 if ((!accel && rep->sline.status() == Http::scProxyAuthenticationRequired)
213 || (accel && rep->sline.status() == Http::scUnauthorized))
928f3421
AJ
214 return;
215
216 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
217
218#if WAITING_FOR_TE
219 /* test for http/1.1 transfer chunked encoding */
220 if (chunkedtest)
221 return;
222#endif
223
372fccd6 224 if ((static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram) && authDigestNonceLastRequest(nonce)) {
3dd52a0b 225 flags.authinfo_sent = true;
c7baff40 226 debugs(29, 9, HERE << "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
928f3421
AJ
227 httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
228 }
229}
230
231#if WAITING_FOR_TE
928f3421 232void
c7baff40 233Auth::Digest::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accel)
928f3421
AJ
234{
235 int type;
236
237 if (!auth_user_request)
238 return;
239
240 /* has the header already been send? */
241 if (flags.authinfo_sent)
242 return;
243
244 /* don't add to authentication error pages */
9b769c67
AJ
245 if ((!accel && rep->sline.status() == Http::scProxyAuthenticationRequired)
246 || (accel && rep->sline.status() == Http::scUnauthorized))
928f3421
AJ
247 return;
248
249 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
250
372fccd6 251 if ((static_cast<Auth::Digest::Config*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
c7baff40 252 debugs(29, 9, HERE << "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
928f3421
AJ
253 httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
254 }
255}
256#endif
257
258/* send the initial data to a digest authenticator module */
259void
d4806c91 260Auth::Digest::UserRequest::module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
928f3421 261{
928f3421 262 char buf[8192];
56a49fda 263
616cfc4c 264 assert(user() != NULL && user()->auth_type == Auth::AUTH_DIGEST);
c7baff40 265 debugs(29, 9, HERE << "'\"" << user()->username() << "\":\"" << realm << "\"'");
928f3421 266
372fccd6 267 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram == NULL) {
d232141d 268 debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
4c535e87 269 handler(data);
928f3421
AJ
270 return;
271 }
272
d4806c91 273 const char *keyExtras = helperRequestKeyExtras(request, al);
372fccd6 274 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->utf8) {
928f3421 275 char userstr[1024];
56a49fda 276 latin1_to_utf8(userstr, sizeof(userstr), user()->username());
d4806c91
CT
277 if (keyExtras)
278 snprintf(buf, 8192, "\"%s\":\"%s\" %s\n", userstr, realm, keyExtras);
279 else
280 snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
928f3421 281 } else {
d4806c91
CT
282 if (keyExtras)
283 snprintf(buf, 8192, "\"%s\":\"%s\" %s\n", user()->username(), realm, keyExtras);
284 else
285 snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
928f3421
AJ
286 }
287
c7baff40 288 helperSubmit(digestauthenticators, buf, Auth::Digest::UserRequest::HandleReply,
1c756645 289 new Auth::StateData(this, handler, data));
928f3421
AJ
290}
291
292void
e166785a 293Auth::Digest::UserRequest::HandleReply(void *data, const HelperReply &reply)
928f3421 294{
1c756645 295 Auth::StateData *replyData = static_cast<Auth::StateData *>(data);
e166785a 296 debugs(29, 9, HERE << "reply=" << reply);
928f3421
AJ
297
298 assert(replyData->auth_user_request != NULL);
c7baff40 299 Auth::UserRequest::Pointer auth_user_request = replyData->auth_user_request;
928f3421 300
71e7400c
AJ
301 // add new helper kv-pair notes to the credentials object
302 // so that any transaction using those credentials can access them
303 auth_user_request->user()->notes.appendNewOnly(&reply.notes);
304
c69199bb 305 static bool oldHelperWarningDone = false;
dacb64b9 306 switch (reply.result) {
c69199bb
AJ
307 case HelperReply::Unknown: {
308 // Squid 3.3 and older the digest helper only returns a HA1 hash (no "OK")
309 // the HA1 will be found in content() for these responses.
310 if (!oldHelperWarningDone) {
311 debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format HA1 response. It needs to be upgraded.");
312 oldHelperWarningDone=true;
313 }
56a49fda 314
c69199bb
AJ
315 /* allow this because the digest_request pointer is purely local */
316 Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
317 assert(digest_user != NULL);
928f3421 318
c69199bb
AJ
319 CvtBin(reply.other().content(), digest_user->HA1);
320 digest_user->HA1created = 1;
e166785a 321 }
dacb64b9 322 break;
e166785a 323
dacb64b9 324 case HelperReply::Okay: {
56a49fda 325 /* allow this because the digest_request pointer is purely local */
aa110616 326 Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
56a49fda
AJ
327 assert(digest_user != NULL);
328
cf9f0261 329 const char *ha1Note = reply.notes.findFirst("ha1");
c69199bb 330 if (ha1Note != NULL) {
cf9f0261 331 CvtBin(ha1Note, digest_user->HA1);
c69199bb
AJ
332 digest_user->HA1created = 1;
333 } else {
334 debugs(29, DBG_IMPORTANT, "ERROR: Digest auth helper did not produce a HA1. Using the wrong helper program? received: " << reply);
335 }
928f3421 336 }
dacb64b9 337 break;
e166785a 338
c69199bb
AJ
339 case HelperReply::TT:
340 debugs(29, DBG_IMPORTANT, "ERROR: Digest auth does not support the result code received. Using the wrong helper program? received: " << reply);
341 // fall through to next case. Handle this as an ERR response.
342
343 case HelperReply::BrokenHelper:
344 // TODO retry the broken lookup on another helper?
345 // fall through to next case for now. Handle this as an ERR response silently.
346
347 case HelperReply::Error: {
348 /* allow this because the digest_request pointer is purely local */
349 Auth::Digest::UserRequest *digest_request = dynamic_cast<Auth::Digest::UserRequest *>(auth_user_request.getRaw());
350 assert(digest_request);
351
352 digest_request->user()->credentials(Auth::Failed);
3dd52a0b 353 digest_request->flags.invalid_password = true;
c69199bb 354
cf9f0261 355 const char *msgNote = reply.notes.find("message");
c69199bb 356 if (msgNote != NULL) {
cf9f0261 357 digest_request->setDenyMessage(msgNote);
c69199bb
AJ
358 } else if (reply.other().hasContent()) {
359 // old helpers did send ERR result but a bare message string instead of message= key name.
360 digest_request->setDenyMessage(reply.other().content());
361 if (!oldHelperWarningDone) {
362 debugs(29, DBG_IMPORTANT, "WARNING: Digest auth helper returned old format ERR response. It needs to be upgraded.");
363 oldHelperWarningDone=true;
364 }
365 }
366 }
367 break;
e166785a 368 }
928f3421 369
e166785a 370 void *cbdata = NULL;
928f3421 371 if (cbdataReferenceValidDone(replyData->data, &cbdata))
4c535e87 372 replyData->handler(cbdata);
928f3421 373
1c756645 374 delete replyData;
928f3421 375}