From: Amos Jeffries Date: Thu, 8 Apr 2010 12:14:42 +0000 (+1200) Subject: Merged from trunk X-Git-Tag: SQUID_3_2_0_1~167^2~31 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=35247d592e6ee7521eb54eac36fe5d286f51f8fe;p=thirdparty%2Fsquid.git Merged from trunk --- 35247d592e6ee7521eb54eac36fe5d286f51f8fe diff --cc src/acl/FilledChecklist.cc index 7cfd5306b3,dad2ed5010..f59e5f1aea --- a/src/acl/FilledChecklist.cc +++ b/src/acl/FilledChecklist.cc @@@ -67,12 -83,19 +67,13 @@@ ACLFilledChecklist::checkCallback(allow /* During reconfigure, we can end up not finishing call * sequences into the auth code */ - if (auth_user_request) { + if (auth_user_request != NULL) { /* the filled_checklist lock */ - AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist"); - + auth_user_request = NULL; /* it might have been connection based */ - if(conn()) { - /* - * DPW 2007-05-08 - * yuck, this make me uncomfortable. why do this here? - * ConnStateData will do its own unlocking. - */ + /* BUG 2827: the connection may also not exist. ie fast ACL tests vs client disconnection. */ + if (conn()) { - AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLFilledChecklist"); + conn()->auth_user_request = NULL; conn()->auth_type = AUTH_BROKEN; } } diff --cc src/auth/digest/digestUserRequest.cc index e3c06ce530,0000000000..3fd996c475 mode 100644,000000..100644 --- a/src/auth/digest/digestUserRequest.cc +++ b/src/auth/digest/digestUserRequest.cc @@@ -1,336 -1,0 +1,337 @@@ +#include "config.h" +#include "auth/digest/auth_digest.h" +#include "auth/digest/digestUserRequest.h" +#include "auth/State.h" +#include "HttpReply.h" +#include "HttpRequest.h" +#include "SquidTime.h" + +AuthDigestUserRequest::AuthDigestUserRequest() : + nonceb64(NULL), + cnonce(NULL), + realm(NULL), + pszPass(NULL), + algorithm(NULL), + pszMethod(NULL), + qop(NULL), + uri(NULL), + response(NULL), + nonce(NULL), + credentials_ok(Unchecked) +{} + +/** + * Delete the digest request structure. + * Does NOT delete related AuthUser structures + */ +AuthDigestUserRequest::~AuthDigestUserRequest() +{ + assert(RefCountCount()==0); + + safe_free(nonceb64); + safe_free(cnonce); + safe_free(realm); + safe_free(pszPass); + safe_free(algorithm); + safe_free(pszMethod); + safe_free(qop); + safe_free(uri); + safe_free(response); + + if (nonce) + authDigestNonceUnlink(nonce); +} + +AuthDigestUserRequest::CredentialsState +AuthDigestUserRequest::credentials() const +{ + return credentials_ok; +} + +void +AuthDigestUserRequest::credentials(CredentialsState newCreds) +{ + credentials_ok = newCreds; +} + +int +AuthDigestUserRequest::authenticated() const +{ + if (credentials() == Ok) + return 1; + + return 0; +} + +/** log a digest user in + */ +void +AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) +{ + AuthDigestUserRequest *digest_request; + digest_user_h *digest_user; + + HASHHEX SESSIONKEY; + HASHHEX HA2 = ""; + HASHHEX Response; + - assert(user() != NULL); ++ assert(authUser() != NULL); + AuthUser *auth_user = user(); + + digest_user = dynamic_cast(auth_user); + assert(digest_user != NULL); + + /* if the check has corrupted the user, just return */ + + if (credentials() == Failed) { + return; + } + + digest_request = this; + + /* do we have the HA1 */ + + if (!digest_user->HA1created) { + credentials(Pending); + return; + } + + if (digest_request->nonce == NULL) { + /* this isn't a nonce we issued */ + credentials(Failed); + return; + } + + DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL, + authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->cnonce, + digest_user->HA1, SESSIONKEY); + DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->nc, digest_request->cnonce, digest_request->qop, + RequestMethodStr(request->method), digest_request->uri, HA2, Response); + + debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'"); + + if (strcasecmp(digest_request->response, Response) != 0) { + if (!digest_request->flags.helper_queried) { + /* Query the helper in case the password has changed */ + digest_request->flags.helper_queried = 1; + digest_request->credentials_ok = Pending; + return; + } + + if (static_cast(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) { + /* Ugly workaround for certain very broken browsers using the + * wrong method to calculate the request-digest on POST request. + * This should be deleted once Digest authentication becomes more + * widespread and such broken browsers no longer are commonly + * used. + */ + DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->nc, digest_request->cnonce, digest_request->qop, + RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response); + + if (strcasecmp(digest_request->response, Response)) { + credentials(Failed); ++ digest_request->flags.invalid_password = 1; + digest_request->setDenyMessage("Incorrect password"); + return; + } else { + const char *useragent = request->header.getStr(HDR_USER_AGENT); + + static IpAddress last_broken_addr; + static int seen_broken_client = 0; + + if (!seen_broken_client) { + last_broken_addr.SetNoAddr(); + seen_broken_client = 1; + } + + if (last_broken_addr != request->client_addr) { + debugs(29, 1, "\nDigest POST bug detected from " << + request->client_addr << " using '" << + (useragent ? useragent : "-") << + "'. Please upgrade browser. See Bug #630 for details."); + + last_broken_addr = request->client_addr; + } + } + } else { + credentials(Failed); + digest_request->flags.invalid_password = 1; + digest_request->setDenyMessage("Incorrect password"); + return; + } + + /* check for stale nonce */ + if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) { + debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale"); + credentials(Failed); + digest_request->setDenyMessage("Stale nonce"); + return; + } + } + + credentials(Ok); + + /* password was checked and did match */ + debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK"); + + /* auth_user is now linked, we reset these values + * after external auth occurs anyway */ + auth_user->expiretime = current_time.tv_sec; + return; +} + +int +AuthDigestUserRequest::module_direction() +{ + switch (credentials()) { + + case Unchecked: + return -1; + + case Ok: + return 0; + + case Pending: + return -1; + + case Failed: + /* send new challenge */ + return 1; + } + return -2; +} + +/* add the [proxy]authorisation header */ +void +AuthDigestUserRequest::addHeader(HttpReply * rep, int accel) +{ + http_hdr_type type; + + /* don't add to authentication error pages */ + + if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) + || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) + return; + + type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; + +#if WAITING_FOR_TE + /* test for http/1.1 transfer chunked encoding */ + if (chunkedtest) + return; +#endif + + if ((static_cast(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) { + flags.authinfo_sent = 1; + debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); + httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); + } +} + +#if WAITING_FOR_TE +/** add the [proxy]authorisation header */ +void +AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel) +{ + int type; + + if (!auth_user_request) + return; + + /* has the header already been send? */ + if (flags.authinfo_sent) + return; + + /* don't add to authentication error pages */ + if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) + || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) + return; + + type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; + + if ((static_cast(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) { + debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); + httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); + } +} +#endif + +/* send the initial data to a digest authenticator module */ +void +AuthDigestUserRequest::module_start(RH * handler, void *data) +{ + authenticateStateData *r = NULL; + char buf[8192]; + digest_user_h *digest_user; + assert(user()->auth_type == AUTH_DIGEST); + digest_user = dynamic_cast(user()); + assert(digest_user != NULL); + debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'"); + + if (static_cast(AuthConfig::Find("digest"))->authenticate == NULL) { + handler(data, NULL); + return; + } + + r = cbdataAlloc(authenticateStateData); + r->handler = handler; + r->data = cbdataReference(data); + r->auth_user_request = static_cast(this); + if (static_cast(AuthConfig::Find("digest"))->utf8) { + char userstr[1024]; + latin1_to_utf8(userstr, sizeof(userstr), digest_user->username()); + snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); + } else { + snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm); + } + + helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply, r); +} + +void +AuthDigestUserRequest::HandleReply(void *data, char *reply) +{ + authenticateStateData *replyData = static_cast < authenticateStateData * >(data); + char *t = NULL; + void *cbdata; + debugs(29, 9, HERE << "{" << (reply ? reply : "") << "}"); + + if (reply) { + if ((t = strchr(reply, ' '))) + *t++ = '\0'; + + if (*reply == '\0' || *reply == '\n') + reply = NULL; + } + + assert(replyData->auth_user_request != NULL); + AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request; + + /* AYJ: 2009-12-12: allow this because the digest_request pointer is purely local */ + AuthDigestUserRequest *digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request.getRaw()); + assert(digest_request); + + digest_user_h *digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user()); + assert(digest_user != NULL); + + if (reply && (strncasecmp(reply, "ERR", 3) == 0)) { + digest_request->credentials(AuthDigestUserRequest::Failed); + digest_request->flags.invalid_password = 1; + + if (t && *t) + digest_request->setDenyMessage(t); + } else if (reply) { + CvtBin(reply, digest_user->HA1); + digest_user->HA1created = 1; + } + + if (cbdataReferenceValidDone(replyData->data, &cbdata)) + replyData->handler(cbdata, NULL); + + replyData->auth_user_request = NULL; + + cbdataFree(replyData); +}