]>
Commit | Line | Data |
---|---|---|
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 | ||
9 | AuthDigestUserRequest::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 | */ | |
27 | AuthDigestUserRequest::~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 | ||
45 | AuthDigestUserRequest::CredentialsState | |
46 | AuthDigestUserRequest::credentials() const | |
47 | { | |
48 | return credentials_ok; | |
49 | } | |
50 | ||
51 | void | |
52 | AuthDigestUserRequest::credentials(CredentialsState newCreds) | |
53 | { | |
54 | credentials_ok = newCreds; | |
55 | } | |
56 | ||
57 | int | |
58 | AuthDigestUserRequest::authenticated() const | |
59 | { | |
60 | if (credentials() == Ok) | |
61 | return 1; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | /** log a digest user in | |
67 | */ | |
68 | void | |
69 | AuthDigestUserRequest::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 | ||
186 | int | |
187 | AuthDigestUserRequest::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 */ | |
208 | void | |
209 | AuthDigestUserRequest::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 */ | |
236 | void | |
237 | AuthDigestUserRequest::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 */ | |
263 | void | |
264 | AuthDigestUserRequest::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 | ||
294 | void | |
295 | AuthDigestUserRequest::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 | } |