]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | AuthDigestUserRequest::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 | */ | |
28 | AuthDigestUserRequest::~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 |
46 | int |
47 | AuthDigestUserRequest::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 | */ | |
57 | void | |
58 | AuthDigestUserRequest::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 | 169 | Auth::Direction |
928f3421 AJ |
170 | AuthDigestUserRequest::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 | 193 | void |
7afc3bf2 | 194 | AuthDigestUserRequest::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 | 220 | void |
7afc3bf2 | 221 | AuthDigestUserRequest::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 */ | |
247 | void | |
248 | AuthDigestUserRequest::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 | ||
273 | void | |
274 | AuthDigestUserRequest::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 | } |