Index: squid/src/cf.data.pre diff -c squid/src/cf.data.pre:1.245.2.41 squid/src/cf.data.pre:1.245.2.42 *** squid/src/cf.data.pre:1.245.2.41 Sat Apr 26 18:33:17 2003 --- squid/src/cf.data.pre Sun May 18 15:49:19 2003 *************** *** 1351,1360 **** used. "nonce_strictness" on|off ! Determines if squid requires increment-by-1 behaviour for ! nonce counts (on - the default), or strictly incrementing ! (off - for use when useragents generate nonce counts that ! occasionally miss 1 (ie, 1,2,4,6)). === NTLM scheme options follow === --- 1351,1371 ---- used. "nonce_strictness" on|off ! Determines if squid requires strict increment-by-1 behaviour ! for nonce counts, or just incrementing (off - for use when ! useragents generate nonce counts that occasionally miss 1 ! (ie, 1,2,4,6)). Default off. ! ! "check_nonce_count" on|off ! This directive if set to off can disable the nonce count check ! completely to work around buggy digest qop implementations in ! certain mainstream browser versions. Default on to check the ! nonce count to protect from authentication replay attacks. ! ! "post_workaround" on|off ! This is a workaround to certain buggy browsers who sends ! an incorrect request digest in POST requests when reusing ! the same nonce as aquired earlier on a GET request. === NTLM scheme options follow === Index: squid/src/authenticate.c diff -c squid/src/authenticate.c:1.36.2.9 squid/src/authenticate.c:1.36.2.10 *** squid/src/authenticate.c:1.36.2.9 Sun May 11 15:44:31 2003 --- squid/src/authenticate.c Sun May 18 15:49:19 2003 *************** *** 538,543 **** --- 538,549 ---- conn, headertype); switch (authenticateDirection(*auth_user_request)) { case 1: + if (!request->auth_user_request) { + /* lock the user for the request structure link */ + authenticateAuthUserRequestLock(*auth_user_request); + request->auth_user_request = *auth_user_request; + } + /* fallthrough to -2 */ case -2: /* this ACL check is finished. Unlock. */ authenticateAuthUserRequestUnlock(*auth_user_request); Index: squid/src/auth/digest/auth_digest.h diff -c squid/src/auth/digest/auth_digest.h:1.5.2.2 squid/src/auth/digest/auth_digest.h:1.5.2.3 *** squid/src/auth/digest/auth_digest.h:1.5.2.2 Thu Feb 27 01:19:24 2003 --- squid/src/auth/digest/auth_digest.h Sun May 18 15:49:20 2003 *************** *** 42,48 **** char *response; struct { unsigned int authinfo_sent:1; ! unsigned int credentials_ok:2; /*0=unchecked,1=ok,2=failed */ } flags; digest_nonce_h *nonce; }; --- 42,49 ---- char *response; struct { unsigned int authinfo_sent:1; ! unsigned int credentials_ok:2; /*0=unchecked,1=ok,2=helper,3=failed */ ! unsigned int nonce_stale:1; } flags; digest_nonce_h *nonce; }; *************** *** 81,86 **** --- 82,89 ---- time_t noncemaxduration; int noncemaxuses; int NonceStrictness; + int CheckNonceCount; + int PostWorkaround; }; typedef struct _auth_digest_config auth_digest_config; Index: squid/src/auth/digest/auth_digest.c diff -c squid/src/auth/digest/auth_digest.c:1.10.2.5 squid/src/auth/digest/auth_digest.c:1.10.2.6 *** squid/src/auth/digest/auth_digest.c:1.10.2.5 Thu Feb 27 01:19:24 2003 --- squid/src/auth/digest/auth_digest.c Sun May 18 15:49:20 2003 *************** *** 343,359 **** if (!nonce) return 0; intnc = strtol(nc, NULL, 16); if ((digestConfig->NonceStrictness && intnc != nonce->nc + 1) || intnc < nonce->nc + 1) { debug(29, 4) ("authDigestNonceIsValid: Nonce count doesn't match\n"); nonce->flags.valid = 0; return 0; } - /* has it already been invalidated ? */ - if (!nonce->flags.valid) { - debug(29, 4) ("authDigestNonceIsValid: Nonce already invalidated\n"); - return 0; - } /* seems ok */ /* increment the nonce count - we've already checked that intnc is a * valid representation for us, so we don't need the test here. --- 343,364 ---- if (!nonce) return 0; intnc = strtol(nc, NULL, 16); + /* has it already been invalidated ? */ + if (!nonce->flags.valid) { + debug(29, 4) ("authDigestNonceIsValid: Nonce already invalidated\n"); + return 0; + } + /* is the nonce-count ok ? */ + if (!digestConfig->CheckNonceCount) { + nonce->nc++; + return -1; /* forced OK by configuration */ + } if ((digestConfig->NonceStrictness && intnc != nonce->nc + 1) || intnc < nonce->nc + 1) { debug(29, 4) ("authDigestNonceIsValid: Nonce count doesn't match\n"); nonce->flags.valid = 0; return 0; } /* seems ok */ /* increment the nonce count - we've already checked that intnc is a * valid representation for us, so we don't need the test here. *************** *** 691,701 **** "squid is = '%s'\n", digest_request->response, Response); if (strcasecmp(digest_request->response, Response)) { digest_request->flags.credentials_ok = 3; return; } - digest_request->flags.credentials_ok = 1; /* password was checked and did match */ debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n", digest_user->username); --- 696,739 ---- "squid is = '%s'\n", digest_request->response, Response); if (strcasecmp(digest_request->response, Response)) { + if (digestConfig->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)) { + digest_request->flags.credentials_ok = 3; + return; + } else { + const char *useragent = httpHeaderGetStr(&request->header, HDR_USER_AGENT); + static struct in_addr last_broken_addr = + {0}; + if (memcmp(&last_broken_addr, &request->client_addr, sizeof(last_broken_addr)) != 0) { + debug(29, 1) ("\nDigest POST bug detected from %s using '%s'. Please upgrade browser. See Bug #630 for details.\n", inet_ntoa(request->client_addr), useragent ? useragent : "-"); + last_broken_addr = request->client_addr; + } + } + } else { + digest_request->flags.credentials_ok = 3; + return; + } + } + /* check for stale nonce */ + if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) { + debug(29, 3) ("authenticateDigestAuthenticateuser: user '%s' validated OK but nonce stale\n", + digest_user->username); + digest_request->flags.nonce_stale = 1; digest_request->flags.credentials_ok = 3; return; } /* password was checked and did match */ + digest_request->flags.credentials_ok = 1; + debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n", digest_user->username); *************** *** 714,726 **** case 0: /* not checked */ return -1; case 1: /* checked & ok */ - if (authDigestNonceIsStale(digest_request->nonce)) - /* send stale response to the client agent */ - return -2; return 0; case 2: /* partway through checking. */ return -1; case 3: /* authentication process failed. */ return -2; } return -2; --- 752,764 ---- case 0: /* not checked */ return -1; case 1: /* checked & ok */ return 0; case 2: /* partway through checking. */ return -1; case 3: /* authentication process failed. */ + if (digest_request->flags.nonce_stale) + /* nonce is stale, send new challenge */ + return 1; return -2; } return -2; *************** *** 787,795 **** digest_request_h *digest_request; int stale = 0; digest_nonce_h *nonce = authenticateDigestNonceNew(); ! if (auth_user_request && auth_user_request->scheme_data && authDigestAuthenticated(auth_user_request)) { digest_request = auth_user_request->scheme_data; ! stale = authDigestNonceIsStale(digest_request->nonce); } if (digestConfig->authenticate) { debug(29, 9) ("authenticateFixHeader: Sending type:%d header: 'Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s\n", type, digestConfig->digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false"); --- 825,833 ---- digest_request_h *digest_request; int stale = 0; digest_nonce_h *nonce = authenticateDigestNonceNew(); ! if (auth_user_request && auth_user_request->scheme_data) { digest_request = auth_user_request->scheme_data; ! stale = digest_request->flags.nonce_stale; } if (digestConfig->authenticate) { debug(29, 9) ("authenticateFixHeader: Sending type:%d header: 'Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s\n", type, digestConfig->digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false"); *************** *** 915,922 **** digestConfig->noncemaxduration = 30 * 60; /* 50 requests */ digestConfig->noncemaxuses = 50; ! /* strict nonce count behaviour */ ! digestConfig->NonceStrictness = 1; } digestConfig = scheme->scheme_data; if (strcasecmp(param_str, "program") == 0) { --- 953,962 ---- digestConfig->noncemaxduration = 30 * 60; /* 50 requests */ digestConfig->noncemaxuses = 50; ! /* Not strict nonce count behaviour */ ! digestConfig->NonceStrictness = 0; ! /* Verify nonce count */ ! digestConfig->CheckNonceCount = 1; } digestConfig = scheme->scheme_data; if (strcasecmp(param_str, "program") == 0) { *************** *** 936,941 **** --- 976,985 ---- parse_int(&digestConfig->noncemaxuses); } else if (strcasecmp(param_str, "nonce_strictness") == 0) { parse_onoff(&digestConfig->NonceStrictness); + } else if (strcasecmp(param_str, "check_nonce_count") == 0) { + parse_onoff(&digestConfig->CheckNonceCount); + } else if (strcasecmp(param_str, "post_workaround") == 0) { + parse_onoff(&digestConfig->PostWorkaround); } else { debug(28, 0) ("unrecognised digest auth scheme parameter '%s'\n", param_str); } *************** *** 1184,1190 **** } /* now the nonce */ nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64); ! if ((nonce == NULL) || !(authDigestNonceIsValid(nonce, digest_request->nc))) { /* we couldn't find a matching nonce! */ debug(29, 4) ("authenticateDigestDecode: Unexpected or invalid nonce recieved\n"); authDigestLogUsername(auth_user_request, username); --- 1228,1234 ---- } /* now the nonce */ nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64); ! if (!nonce) { /* we couldn't find a matching nonce! */ debug(29, 4) ("authenticateDigestDecode: Unexpected or invalid nonce recieved\n"); authDigestLogUsername(auth_user_request, username);