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