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