]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/digest/UserRequest.cc
Bug 3461: build regression in IPFilter NAT
[thirdparty/squid.git] / src / auth / digest / UserRequest.cc
CommitLineData
928f3421
AJ
1#include "config.h"
2#include "auth/digest/auth_digest.h"
aa110616 3#include "auth/digest/User.h"
616cfc4c 4#include "auth/digest/UserRequest.h"
928f3421 5#include "auth/State.h"
25f98340 6#include "charset.h"
928f3421
AJ
7#include "HttpReply.h"
8#include "HttpRequest.h"
9#include "SquidTime.h"
10
11AuthDigestUserRequest::AuthDigestUserRequest() :
12 nonceb64(NULL),
13 cnonce(NULL),
14 realm(NULL),
15 pszPass(NULL),
16 algorithm(NULL),
17 pszMethod(NULL),
18 qop(NULL),
19 uri(NULL),
20 response(NULL),
d232141d 21 nonce(NULL)
928f3421
AJ
22{}
23
24/**
25 * Delete the digest request structure.
26 * Does NOT delete related AuthUser structures
27 */
28AuthDigestUserRequest::~AuthDigestUserRequest()
29{
ea0695f2
AJ
30 assert(RefCountCount()==0);
31
928f3421
AJ
32 safe_free(nonceb64);
33 safe_free(cnonce);
34 safe_free(realm);
35 safe_free(pszPass);
36 safe_free(algorithm);
37 safe_free(pszMethod);
38 safe_free(qop);
39 safe_free(uri);
40 safe_free(response);
41
42 if (nonce)
43 authDigestNonceUnlink(nonce);
44}
45
928f3421
AJ
46int
47AuthDigestUserRequest::authenticated() const
48{
d87154ee 49 if (user() != NULL && user()->credentials() == Auth::Ok)
928f3421
AJ
50 return 1;
51
52 return 0;
53}
54
55/** log a digest user in
56 */
57void
58AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
59{
928f3421
AJ
60 HASHHEX SESSIONKEY;
61 HASHHEX HA2 = "";
62 HASHHEX Response;
63
928f3421 64 /* if the check has corrupted the user, just return */
d87154ee 65 if (user() == NULL || user()->credentials() == Auth::Failed) {
928f3421
AJ
66 return;
67 }
68
d87154ee 69 Auth::User::Pointer auth_user = user();
928f3421 70
aa110616 71 Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User*>(auth_user.getRaw());
56a49fda 72 assert(digest_user != NULL);
928f3421 73
56a49fda
AJ
74 AuthDigestUserRequest *digest_request = this;
75
76 /* do we have the HA1 */
928f3421 77 if (!digest_user->HA1created) {
d87154ee 78 auth_user->credentials(Auth::Pending);
928f3421
AJ
79 return;
80 }
81
82 if (digest_request->nonce == NULL) {
83 /* this isn't a nonce we issued */
d87154ee 84 auth_user->credentials(Auth::Failed);
928f3421
AJ
85 return;
86 }
87
88 DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
89 authenticateDigestNonceNonceb64(digest_request->nonce),
90 digest_request->cnonce,
91 digest_user->HA1, SESSIONKEY);
92 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
93 digest_request->nc, digest_request->cnonce, digest_request->qop,
94 RequestMethodStr(request->method), digest_request->uri, HA2, Response);
95
96 debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
97
98 if (strcasecmp(digest_request->response, Response) != 0) {
99 if (!digest_request->flags.helper_queried) {
100 /* Query the helper in case the password has changed */
101 digest_request->flags.helper_queried = 1;
d87154ee 102 auth_user->credentials(Auth::Pending);
928f3421
AJ
103 return;
104 }
105
372fccd6 106 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->PostWorkaround && request->method != METHOD_GET) {
928f3421
AJ
107 /* Ugly workaround for certain very broken browsers using the
108 * wrong method to calculate the request-digest on POST request.
109 * This should be deleted once Digest authentication becomes more
110 * widespread and such broken browsers no longer are commonly
111 * used.
112 */
113 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
114 digest_request->nc, digest_request->cnonce, digest_request->qop,
115 RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
116
117 if (strcasecmp(digest_request->response, Response)) {
d87154ee 118 auth_user->credentials(Auth::Failed);
35247d59 119 digest_request->flags.invalid_password = 1;
928f3421
AJ
120 digest_request->setDenyMessage("Incorrect password");
121 return;
122 } else {
123 const char *useragent = request->header.getStr(HDR_USER_AGENT);
124
08acdd08 125 static Ip::Address last_broken_addr;
928f3421
AJ
126 static int seen_broken_client = 0;
127
128 if (!seen_broken_client) {
129 last_broken_addr.SetNoAddr();
130 seen_broken_client = 1;
131 }
132
133 if (last_broken_addr != request->client_addr) {
134 debugs(29, 1, "\nDigest POST bug detected from " <<
135 request->client_addr << " using '" <<
136 (useragent ? useragent : "-") <<
137 "'. Please upgrade browser. See Bug #630 for details.");
138
139 last_broken_addr = request->client_addr;
140 }
141 }
142 } else {
d87154ee 143 auth_user->credentials(Auth::Failed);
928f3421
AJ
144 digest_request->flags.invalid_password = 1;
145 digest_request->setDenyMessage("Incorrect password");
146 return;
147 }
148
149 /* check for stale nonce */
150 if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
56a49fda 151 debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK but nonce stale");
d87154ee 152 auth_user->credentials(Auth::Failed);
928f3421
AJ
153 digest_request->setDenyMessage("Stale nonce");
154 return;
155 }
156 }
157
d87154ee 158 auth_user->credentials(Auth::Ok);
928f3421
AJ
159
160 /* password was checked and did match */
56a49fda 161 debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK");
928f3421
AJ
162
163 /* auth_user is now linked, we reset these values
164 * after external auth occurs anyway */
165 auth_user->expiretime = current_time.tv_sec;
166 return;
167}
168
51a3dd58 169Auth::Direction
928f3421
AJ
170AuthDigestUserRequest::module_direction()
171{
616cfc4c 172 if (user()->auth_type != Auth::AUTH_DIGEST)
51a3dd58 173 return Auth::CRED_ERROR;
928f3421 174
d232141d 175 switch (user()->credentials()) {
928f3421 176
d87154ee 177 case Auth::Ok:
51a3dd58 178 return Auth::CRED_VALID;
928f3421 179
d87154ee 180 case Auth::Failed:
928f3421 181 /* send new challenge */
51a3dd58 182 return Auth::CRED_CHALLENGE;
d232141d 183
d87154ee
AJ
184 case Auth::Unchecked:
185 case Auth::Pending:
51a3dd58 186 return Auth::CRED_LOOKUP;
d232141d
AJ
187
188 default:
51a3dd58 189 return Auth::CRED_ERROR;
928f3421 190 }
928f3421
AJ
191}
192
928f3421 193void
7afc3bf2 194AuthDigestUserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
928f3421
AJ
195{
196 http_hdr_type type;
197
198 /* don't add to authentication error pages */
199
200 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
201 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
202 return;
203
204 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
205
206#if WAITING_FOR_TE
207 /* test for http/1.1 transfer chunked encoding */
208 if (chunkedtest)
209 return;
210#endif
211
372fccd6 212 if ((static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram) && authDigestNonceLastRequest(nonce)) {
928f3421
AJ
213 flags.authinfo_sent = 1;
214 debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
215 httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
216 }
217}
218
219#if WAITING_FOR_TE
928f3421 220void
7afc3bf2 221AuthDigestUserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accel)
928f3421
AJ
222{
223 int type;
224
225 if (!auth_user_request)
226 return;
227
228 /* has the header already been send? */
229 if (flags.authinfo_sent)
230 return;
231
232 /* don't add to authentication error pages */
233 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
234 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
235 return;
236
237 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
238
372fccd6 239 if ((static_cast<Auth::Digest::Config*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
928f3421
AJ
240 debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
241 httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
242 }
243}
244#endif
245
246/* send the initial data to a digest authenticator module */
247void
248AuthDigestUserRequest::module_start(RH * handler, void *data)
249{
928f3421 250 char buf[8192];
56a49fda 251
616cfc4c 252 assert(user() != NULL && user()->auth_type == Auth::AUTH_DIGEST);
56a49fda 253 debugs(29, 9, "authenticateStart: '\"" << user()->username() << "\":\"" << realm << "\"'");
928f3421 254
372fccd6 255 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram == NULL) {
d232141d 256 debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
928f3421
AJ
257 handler(data, NULL);
258 return;
259 }
260
372fccd6 261 if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->utf8) {
928f3421 262 char userstr[1024];
56a49fda 263 latin1_to_utf8(userstr, sizeof(userstr), user()->username());
928f3421
AJ
264 snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
265 } else {
56a49fda 266 snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
928f3421
AJ
267 }
268
1c756645
AJ
269 helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply,
270 new Auth::StateData(this, handler, data));
928f3421
AJ
271}
272
273void
274AuthDigestUserRequest::HandleReply(void *data, char *reply)
275{
1c756645 276 Auth::StateData *replyData = static_cast<Auth::StateData *>(data);
928f3421
AJ
277 char *t = NULL;
278 void *cbdata;
279 debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
280
281 if (reply) {
282 if ((t = strchr(reply, ' ')))
283 *t++ = '\0';
284
285 if (*reply == '\0' || *reply == '\n')
286 reply = NULL;
287 }
288
289 assert(replyData->auth_user_request != NULL);
290 AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request;
291
928f3421 292 if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
56a49fda
AJ
293 /* allow this because the digest_request pointer is purely local */
294 AuthDigestUserRequest *digest_request = dynamic_cast<AuthDigestUserRequest *>(auth_user_request.getRaw());
295 assert(digest_request);
296
d87154ee 297 digest_request->user()->credentials(Auth::Failed);
928f3421
AJ
298 digest_request->flags.invalid_password = 1;
299
300 if (t && *t)
301 digest_request->setDenyMessage(t);
302 } else if (reply) {
56a49fda 303 /* allow this because the digest_request pointer is purely local */
aa110616 304 Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
56a49fda
AJ
305 assert(digest_user != NULL);
306
928f3421
AJ
307 CvtBin(reply, digest_user->HA1);
308 digest_user->HA1created = 1;
309 }
310
311 if (cbdataReferenceValidDone(replyData->data, &cbdata))
312 replyData->handler(cbdata, NULL);
313
1c756645 314 delete replyData;
928f3421 315}