]>
Commit | Line | Data |
---|---|---|
f7f3304a | 1 | #include "squid.h" |
928f3421 | 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" |
a5bac1d2 | 7 | #include "HttpHeaderTools.h" |
928f3421 AJ |
8 | #include "HttpReply.h" |
9 | #include "HttpRequest.h" | |
10 | #include "SquidTime.h" | |
11 | ||
c7baff40 | 12 | Auth::Digest::UserRequest::UserRequest() : |
928f3421 AJ |
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), | |
d232141d | 22 | nonce(NULL) |
928f3421 AJ |
23 | {} |
24 | ||
25 | /** | |
26 | * Delete the digest request structure. | |
27 | * Does NOT delete related AuthUser structures | |
28 | */ | |
c7baff40 | 29 | Auth::Digest::UserRequest::~UserRequest() |
928f3421 | 30 | { |
8bf217bd | 31 | assert(LockCount()==0); |
ea0695f2 | 32 | |
928f3421 AJ |
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 | ||
928f3421 | 47 | int |
c7baff40 | 48 | Auth::Digest::UserRequest::authenticated() const |
928f3421 | 49 | { |
d87154ee | 50 | if (user() != NULL && user()->credentials() == Auth::Ok) |
928f3421 AJ |
51 | return 1; |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
56 | /** log a digest user in | |
57 | */ | |
58 | void | |
c7baff40 | 59 | Auth::Digest::UserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) |
928f3421 | 60 | { |
928f3421 AJ |
61 | HASHHEX SESSIONKEY; |
62 | HASHHEX HA2 = ""; | |
63 | HASHHEX Response; | |
64 | ||
928f3421 | 65 | /* if the check has corrupted the user, just return */ |
d87154ee | 66 | if (user() == NULL || user()->credentials() == Auth::Failed) { |
928f3421 AJ |
67 | return; |
68 | } | |
69 | ||
d87154ee | 70 | Auth::User::Pointer auth_user = user(); |
928f3421 | 71 | |
aa110616 | 72 | Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User*>(auth_user.getRaw()); |
56a49fda | 73 | assert(digest_user != NULL); |
928f3421 | 74 | |
c7baff40 | 75 | Auth::Digest::UserRequest *digest_request = this; |
56a49fda AJ |
76 | |
77 | /* do we have the HA1 */ | |
928f3421 | 78 | if (!digest_user->HA1created) { |
d87154ee | 79 | auth_user->credentials(Auth::Pending); |
928f3421 AJ |
80 | return; |
81 | } | |
82 | ||
83 | if (digest_request->nonce == NULL) { | |
84 | /* this isn't a nonce we issued */ | |
d87154ee | 85 | auth_user->credentials(Auth::Failed); |
928f3421 AJ |
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; | |
d87154ee | 103 | auth_user->credentials(Auth::Pending); |
928f3421 AJ |
104 | return; |
105 | } | |
106 | ||
c2a7cefd | 107 | if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->PostWorkaround && request->method != Http::METHOD_GET) { |
928f3421 AJ |
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, | |
c2a7cefd | 116 | RequestMethodStr(Http::METHOD_GET), digest_request->uri, HA2, Response); |
928f3421 AJ |
117 | |
118 | if (strcasecmp(digest_request->response, Response)) { | |
d87154ee | 119 | auth_user->credentials(Auth::Failed); |
35247d59 | 120 | digest_request->flags.invalid_password = 1; |
928f3421 AJ |
121 | digest_request->setDenyMessage("Incorrect password"); |
122 | return; | |
123 | } else { | |
124 | const char *useragent = request->header.getStr(HDR_USER_AGENT); | |
125 | ||
08acdd08 | 126 | static Ip::Address last_broken_addr; |
928f3421 AJ |
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) { | |
c7baff40 | 135 | debugs(29, DBG_IMPORTANT, "Digest POST bug detected from " << |
928f3421 AJ |
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 { | |
d87154ee | 144 | auth_user->credentials(Auth::Failed); |
928f3421 AJ |
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)) { | |
c7baff40 | 152 | debugs(29, 3, HERE << "user '" << auth_user->username() << "' validated OK but nonce stale"); |
d87154ee | 153 | auth_user->credentials(Auth::Failed); |
928f3421 AJ |
154 | digest_request->setDenyMessage("Stale nonce"); |
155 | return; | |
156 | } | |
157 | } | |
158 | ||
d87154ee | 159 | auth_user->credentials(Auth::Ok); |
928f3421 AJ |
160 | |
161 | /* password was checked and did match */ | |
c7baff40 | 162 | debugs(29, 4, HERE << "user '" << auth_user->username() << "' validated OK"); |
928f3421 AJ |
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 | ||
51a3dd58 | 170 | Auth::Direction |
c7baff40 | 171 | Auth::Digest::UserRequest::module_direction() |
928f3421 | 172 | { |
616cfc4c | 173 | if (user()->auth_type != Auth::AUTH_DIGEST) |
51a3dd58 | 174 | return Auth::CRED_ERROR; |
928f3421 | 175 | |
d232141d | 176 | switch (user()->credentials()) { |
928f3421 | 177 | |
d87154ee | 178 | case Auth::Ok: |
51a3dd58 | 179 | return Auth::CRED_VALID; |
928f3421 | 180 | |
d87154ee | 181 | case Auth::Failed: |
928f3421 | 182 | /* send new challenge */ |
51a3dd58 | 183 | return Auth::CRED_CHALLENGE; |
d232141d | 184 | |
d87154ee AJ |
185 | case Auth::Unchecked: |
186 | case Auth::Pending: | |
51a3dd58 | 187 | return Auth::CRED_LOOKUP; |
d232141d AJ |
188 | |
189 | default: | |
51a3dd58 | 190 | return Auth::CRED_ERROR; |
928f3421 | 191 | } |
928f3421 AJ |
192 | } |
193 | ||
928f3421 | 194 | void |
c7baff40 | 195 | Auth::Digest::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel) |
928f3421 AJ |
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 | ||
372fccd6 | 213 | if ((static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram) && authDigestNonceLastRequest(nonce)) { |
928f3421 | 214 | flags.authinfo_sent = 1; |
c7baff40 | 215 | debugs(29, 9, HERE << "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); |
928f3421 AJ |
216 | httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); |
217 | } | |
218 | } | |
219 | ||
220 | #if WAITING_FOR_TE | |
928f3421 | 221 | void |
c7baff40 | 222 | Auth::Digest::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accel) |
928f3421 AJ |
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 | ||
372fccd6 | 240 | if ((static_cast<Auth::Digest::Config*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) { |
c7baff40 | 241 | debugs(29, 9, HERE << "Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); |
928f3421 AJ |
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 | |
4c535e87 | 249 | Auth::Digest::UserRequest::module_start(AUTHCB * handler, void *data) |
928f3421 | 250 | { |
928f3421 | 251 | char buf[8192]; |
56a49fda | 252 | |
616cfc4c | 253 | assert(user() != NULL && user()->auth_type == Auth::AUTH_DIGEST); |
c7baff40 | 254 | debugs(29, 9, HERE << "'\"" << user()->username() << "\":\"" << realm << "\"'"); |
928f3421 | 255 | |
372fccd6 | 256 | if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram == NULL) { |
d232141d | 257 | debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured."); |
4c535e87 | 258 | handler(data); |
928f3421 AJ |
259 | return; |
260 | } | |
261 | ||
372fccd6 | 262 | if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->utf8) { |
928f3421 | 263 | char userstr[1024]; |
56a49fda | 264 | latin1_to_utf8(userstr, sizeof(userstr), user()->username()); |
928f3421 AJ |
265 | snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); |
266 | } else { | |
56a49fda | 267 | snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm); |
928f3421 AJ |
268 | } |
269 | ||
c7baff40 | 270 | helperSubmit(digestauthenticators, buf, Auth::Digest::UserRequest::HandleReply, |
1c756645 | 271 | new Auth::StateData(this, handler, data)); |
928f3421 AJ |
272 | } |
273 | ||
274 | void | |
c7baff40 | 275 | Auth::Digest::UserRequest::HandleReply(void *data, char *reply) |
928f3421 | 276 | { |
1c756645 | 277 | Auth::StateData *replyData = static_cast<Auth::StateData *>(data); |
928f3421 AJ |
278 | char *t = NULL; |
279 | void *cbdata; | |
280 | debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}"); | |
281 | ||
282 | if (reply) { | |
f207fe64 FC |
283 | if ((t = strchr(reply, ' '))) { |
284 | *t = '\0'; | |
285 | ++t; | |
286 | } | |
928f3421 AJ |
287 | |
288 | if (*reply == '\0' || *reply == '\n') | |
289 | reply = NULL; | |
290 | } | |
291 | ||
292 | assert(replyData->auth_user_request != NULL); | |
c7baff40 | 293 | Auth::UserRequest::Pointer auth_user_request = replyData->auth_user_request; |
928f3421 | 294 | |
928f3421 | 295 | if (reply && (strncasecmp(reply, "ERR", 3) == 0)) { |
56a49fda | 296 | /* allow this because the digest_request pointer is purely local */ |
c7baff40 | 297 | Auth::Digest::UserRequest *digest_request = dynamic_cast<Auth::Digest::UserRequest *>(auth_user_request.getRaw()); |
56a49fda AJ |
298 | assert(digest_request); |
299 | ||
d87154ee | 300 | digest_request->user()->credentials(Auth::Failed); |
928f3421 AJ |
301 | digest_request->flags.invalid_password = 1; |
302 | ||
303 | if (t && *t) | |
304 | digest_request->setDenyMessage(t); | |
305 | } else if (reply) { | |
56a49fda | 306 | /* allow this because the digest_request pointer is purely local */ |
aa110616 | 307 | Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw()); |
56a49fda AJ |
308 | assert(digest_user != NULL); |
309 | ||
928f3421 AJ |
310 | CvtBin(reply, digest_user->HA1); |
311 | digest_user->HA1created = 1; | |
312 | } | |
313 | ||
314 | if (cbdataReferenceValidDone(replyData->data, &cbdata)) | |
4c535e87 | 315 | replyData->handler(cbdata); |
928f3421 | 316 | |
1c756645 | 317 | delete replyData; |
928f3421 | 318 | } |