diff -u -r -N squid-3.0.STABLE24/ChangeLog squid-3.0.STABLE25/ChangeLog --- squid-3.0.STABLE24/ChangeLog 2010-02-13 02:52:58.000000000 +1300 +++ squid-3.0.STABLE25/ChangeLog 2010-03-14 18:45:39.000000000 +1300 @@ -1,3 +1,11 @@ +Changes to squid-3.0.STABLE25 (14 Mar 2010): + + - Bug 2845: Rework the http digest auth parser + - Bug 2787: unknown/unexpected status code messages + - Bug 2507: squid_ldap_group: Strip Domain name separated by + + - Bug 2367: stale=true on digest requests with unknown nonce + - ... and several other minor corrections + Changes to squid-3.0.STABLE24 (13 Feb 2010): - Bug 2858: Segment violation in HTCP diff -u -r -N squid-3.0.STABLE24/configure squid-3.0.STABLE25/configure --- squid-3.0.STABLE24/configure 2010-02-13 02:53:20.000000000 +1300 +++ squid-3.0.STABLE25/configure 2010-03-14 18:45:43.000000000 +1300 @@ -1,7 +1,7 @@ #! /bin/sh # From configure.in Revision. # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for Squid Web Proxy 3.0.STABLE24. +# Generated by GNU Autoconf 2.62 for Squid Web Proxy 3.0.STABLE25. # # Report bugs to . # @@ -751,8 +751,8 @@ # Identity of this package. PACKAGE_NAME='Squid Web Proxy' PACKAGE_TARNAME='squid' -PACKAGE_VERSION='3.0.STABLE24' -PACKAGE_STRING='Squid Web Proxy 3.0.STABLE24' +PACKAGE_VERSION='3.0.STABLE25' +PACKAGE_STRING='Squid Web Proxy 3.0.STABLE25' PACKAGE_BUGREPORT='http://www.squid-cache.org/bugs/' ac_unique_file="src/main.cc" @@ -1663,7 +1663,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Squid Web Proxy 3.0.STABLE24 to adapt to many kinds of systems. +\`configure' configures Squid Web Proxy 3.0.STABLE25 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1733,7 +1733,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Squid Web Proxy 3.0.STABLE24:";; + short | recursive ) echo "Configuration of Squid Web Proxy 3.0.STABLE25:";; esac cat <<\_ACEOF @@ -2047,7 +2047,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Squid Web Proxy configure 3.0.STABLE24 +Squid Web Proxy configure 3.0.STABLE25 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -2061,7 +2061,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Squid Web Proxy $as_me 3.0.STABLE24, which was +It was created by Squid Web Proxy $as_me 3.0.STABLE25, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -2779,7 +2779,7 @@ # Define the identity of the package. PACKAGE='squid' - VERSION='3.0.STABLE24' + VERSION='3.0.STABLE25' cat >>confdefs.h <<_ACEOF @@ -48207,7 +48207,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Squid Web Proxy $as_me 3.0.STABLE24, which was +This file was extended by Squid Web Proxy $as_me 3.0.STABLE25, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -48260,7 +48260,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -Squid Web Proxy config.status 3.0.STABLE24 +Squid Web Proxy config.status 3.0.STABLE25 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff -u -r -N squid-3.0.STABLE24/configure.in squid-3.0.STABLE25/configure.in --- squid-3.0.STABLE24/configure.in 2010-02-13 02:53:20.000000000 +1300 +++ squid-3.0.STABLE25/configure.in 2010-03-14 18:45:43.000000000 +1300 @@ -1,7 +1,7 @@ dnl Configuration input file for Squid dnl dnl -AC_INIT(Squid Web Proxy, 3.0.STABLE24, http://www.squid-cache.org/bugs/, squid) +AC_INIT(Squid Web Proxy, 3.0.STABLE25, http://www.squid-cache.org/bugs/, squid) AC_PREREQ(2.52) AM_CONFIG_HEADER(include/autoconf.h) AC_CONFIG_AUX_DIR(cfgaux) diff -u -r -N squid-3.0.STABLE24/helpers/external_acl/ldap_group/squid_ldap_group.c squid-3.0.STABLE25/helpers/external_acl/ldap_group/squid_ldap_group.c --- squid-3.0.STABLE24/helpers/external_acl/ldap_group/squid_ldap_group.c 2010-02-13 02:53:08.000000000 +1300 +++ squid-3.0.STABLE25/helpers/external_acl/ldap_group/squid_ldap_group.c 2010-03-14 18:45:40.000000000 +1300 @@ -475,6 +475,8 @@ char *u = strrchr(user, '\\'); if (!u) u = strrchr(user, '/'); + if (!u) + u = strrchr(user, '+'); if (u && u[1]) user = u + 1; } diff -u -r -N squid-3.0.STABLE24/include/version.h squid-3.0.STABLE25/include/version.h --- squid-3.0.STABLE24/include/version.h 2010-02-13 02:53:20.000000000 +1300 +++ squid-3.0.STABLE25/include/version.h 2010-03-14 18:45:43.000000000 +1300 @@ -9,5 +9,5 @@ */ #ifndef SQUID_RELEASE_TIME -#define SQUID_RELEASE_TIME 1265982776 +#define SQUID_RELEASE_TIME 1268545528 #endif diff -u -r -N squid-3.0.STABLE24/src/auth/digest/auth_digest.cc squid-3.0.STABLE25/src/auth/digest/auth_digest.cc --- squid-3.0.STABLE24/src/auth/digest/auth_digest.cc 2010-02-13 02:53:16.000000000 +1300 +++ squid-3.0.STABLE25/src/auth/digest/auth_digest.cc 2010-03-14 18:45:43.000000000 +1300 @@ -67,6 +67,33 @@ CBDATA_TYPE(DigestAuthenticateStateData); +enum http_digest_attr_type { + DIGEST_USERNAME, + DIGEST_REALM, + DIGEST_QOP, + DIGEST_ALGORITHM, + DIGEST_URI, + DIGEST_NONCE, + DIGEST_NC, + DIGEST_CNONCE, + DIGEST_RESPONSE, + DIGEST_ENUM_END +}; + +static const HttpHeaderFieldAttrs DigestAttrs[DIGEST_ENUM_END] = { + {"username", (http_hdr_type)DIGEST_USERNAME}, + {"realm", (http_hdr_type)DIGEST_REALM}, + {"qop", (http_hdr_type)DIGEST_QOP}, + {"algorithm", (http_hdr_type)DIGEST_ALGORITHM}, + {"uri", (http_hdr_type)DIGEST_URI}, + {"nonce", (http_hdr_type)DIGEST_NONCE}, + {"nc", (http_hdr_type)DIGEST_NC}, + {"cnonce", (http_hdr_type)DIGEST_CNONCE}, + {"response", (http_hdr_type)DIGEST_RESPONSE}, +}; + +static HttpHeaderFieldInfo *DigestFieldsInfo = NULL; + /* * * Nonce Functions @@ -507,6 +534,11 @@ if (digestauthenticators) helperShutdown(digestauthenticators); + if (DigestFieldsInfo) { + httpHeaderDestroyFieldsInfo(DigestFieldsInfo, DIGEST_ENUM_END); + DigestFieldsInfo = NULL; + } + authdigest_initialised = 0; if (!shutting_down) { @@ -642,6 +674,7 @@ if (strcasecmp(digest_request->response, Response)) { credentials(Failed); + digest_request->flags.invalid_password = 1; digest_request->setDenyMessage("Incorrect password"); return; } else { @@ -871,6 +904,7 @@ AuthDigestConfig::init(AuthConfig * scheme) { if (authenticate) { + DigestFieldsInfo = httpHeaderBuildFieldsInfo(DigestAttrs, DIGEST_ENUM_END); authenticateDigestNonceSetup(); authdigest_initialised = 1; @@ -1093,124 +1127,84 @@ String temp(proxy_auth); while (strListGetItem(&temp, ',', &item, &ilen, &pos)) { - if ((p = strchr(item, '=')) && (p - item < ilen)) - ilen = p++ - item; - - if (!strncmp(item, "username", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; + String value; + size_t nlen; + /* isolate directive name */ + if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) { + nlen = p++ - item; + if (!httpHeaderParseQuotedString(p, &value)) + value.limitInit(p, ilen - (p - item)); + } else + nlen = ilen; + + if (!value.buf()) { + debugs(29, 9, "authDigestDecodeAuth: Failed to parse attribute '" << item << "' in '" << temp << "'"); + continue; + } - /* quote mark */ - p++; + /* find type */ + http_digest_attr_type type = (http_digest_attr_type)httpHeaderIdByName(item, nlen, DigestFieldsInfo, DIGEST_ENUM_END); + switch (type) { + case DIGEST_USERNAME: safe_free(username); - username = xstrndup(p, strchr(p, '"') + 1 - p); - + username = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found Username '" << username << "'"); - } else if (!strncmp(item, "realm", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - /* quote mark */ - p++; + break; + case DIGEST_REALM: safe_free(digest_request->realm); - digest_request->realm = xstrndup(p, strchr(p, '"') + 1 - p); - + digest_request->realm = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found realm '" << digest_request->realm << "'"); - } else if (!strncmp(item, "qop", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - if (*p == '\"') - /* quote mark */ - p++; + break; + case DIGEST_QOP: safe_free(digest_request->qop); - digest_request->qop = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1); - + digest_request->qop = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found qop '" << digest_request->qop << "'"); - } else if (!strncmp(item, "algorithm", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - if (*p == '\"') - /* quote mark */ - p++; + break; + case DIGEST_ALGORITHM: safe_free(digest_request->algorithm); - digest_request->algorithm = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1); - + digest_request->algorithm = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found algorithm '" << digest_request->algorithm << "'"); - } else if (!strncmp(item, "uri", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - /* quote mark */ - p++; + break; + case DIGEST_URI: safe_free(digest_request->uri); - digest_request->uri = xstrndup(p, strchr(p, '"') + 1 - p); - + digest_request->uri = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found uri '" << digest_request->uri << "'"); - } else if (!strncmp(item, "nonce", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - /* quote mark */ - p++; + break; + case DIGEST_NONCE: safe_free(digest_request->nonceb64); - digest_request->nonceb64 = xstrndup(p, strchr(p, '"') + 1 - p); - + digest_request->nonceb64 = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found nonce '" << digest_request->nonceb64 << "'"); - } else if (!strncmp(item, "nc", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - xstrncpy(digest_request->nc, p, 9); + break; + case DIGEST_NC: + if (value.size() != 8) { + debugs(29, 9, "authDigestDecodeAuth: Invalid nc '" << value << "' in '" << temp << "'"); + } + xstrncpy(digest_request->nc, value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found noncecount '" << digest_request->nc << "'"); - } else if (!strncmp(item, "cnonce", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - /* quote mark */ - p++; + break; + case DIGEST_CNONCE: safe_free(digest_request->cnonce); - digest_request->cnonce = xstrndup(p, strchr(p, '"') + 1 - p); - + digest_request->cnonce = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found cnonce '" << digest_request->cnonce << "'"); - } else if (!strncmp(item, "response", ilen)) { - /* white space */ - - while (xisspace(*p)) - p++; - - /* quote mark */ - p++; + break; + case DIGEST_RESPONSE: safe_free(digest_request->response); - digest_request->response = xstrndup(p, strchr(p, '"') + 1 - p); - + digest_request->response = xstrndup(value.buf(), value.size() + 1); debugs(29, 9, "authDigestDecodeAuth: Found response '" << digest_request->response << "'"); + break; + + default: + debugs(29, 3, "authDigestDecodeAuth: Unknown attribute '" << item << "' in '" << temp << "'"); + break; } } @@ -1228,69 +1222,103 @@ * correct values - 400/401/407 */ - /* first the NONCE count */ + /* 2069 requirements */ - if (digest_request->cnonce && strlen(digest_request->nc) != 8) { - debugs(29, 4, "authenticateDigestDecode: nonce count length invalid"); + /* do we have a username ? */ + if (!username || username[0] == '\0') { + debugs(29, 2, "authenticateDigestDecode: Empty or not present username"); return authDigestLogUsername(username, digest_request); } - /* now the nonce */ - nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64); - - if (!nonce) { - /* we couldn't find a matching nonce! */ - debugs(29, 4, "authenticateDigestDecode: Unexpected or invalid nonce received"); + /* Sanity check of the username. + * " can not be allowed in usernames until * the digest helper protocol + * have been redone + */ + if (strchr(username, '"')) { + debugs(29, 2, "authenticateDigestDecode: Unacceptable username '" << username << "'"); return authDigestLogUsername(username, digest_request); } - digest_request->nonce = nonce; - authDigestNonceLink(nonce); - - /* check the qop is what we expected. Note that for compatability with - * RFC 2069 we should support a missing qop. Tough. */ + /* do we have a realm ? */ + if (!digest_request->realm || digest_request->realm[0] == '\0') { + debugs(29, 2, "authenticateDigestDecode: Empty or not present realm"); + return authDigestLogUsername(username, digest_request); + } - if (digest_request->qop && strcmp(digest_request->qop, QOP_AUTH) != 0) { - /* we received a qop option we didn't send */ - debugs(29, 4, "authenticateDigestDecode: Invalid qop option received"); + /* and a nonce? */ + if (!digest_request->nonceb64 || digest_request->nonceb64[0] == '\0') { + debugs(29, 2, "authenticateDigestDecode: Empty or not present nonce"); return authDigestLogUsername(username, digest_request); } /* we can't check the URI just yet. We'll check it in the - * authenticate phase */ + * authenticate phase, but needs to be given */ + if (!digest_request->uri || digest_request->uri[0] == '\0') { + debugs(29, 2, "authenticateDigestDecode: Missing URI field"); + return authDigestLogUsername(username, digest_request); + } /* is the response the correct length? */ - if (!digest_request->response || strlen(digest_request->response) != 32) { - debugs(29, 4, "authenticateDigestDecode: Response length invalid"); + debugs(29, 2, "authenticateDigestDecode: Response length invalid"); return authDigestLogUsername(username, digest_request); } - /* do we have a username ? */ - if (!username || username[0] == '\0') { - debugs(29, 4, "authenticateDigestDecode: Empty or not present username"); + /* check the algorithm is present and supported */ + if (!digest_request->algorithm) + digest_request->algorithm = xstrndup("MD5", 4); + else if (strcmp(digest_request->algorithm, "MD5") + && strcmp(digest_request->algorithm, "MD5-sess")) { + debugs(29, 2, "authenticateDigestDecode: invalid algorithm specified!"); return authDigestLogUsername(username, digest_request); } - /* check that we're not being hacked / the username hasn't changed */ - if (nonce->user && strcmp(username, nonce->user->username())) { - debugs(29, 4, "authenticateDigestDecode: Username for the nonce does not equal the username for the request"); - return authDigestLogUsername(username, digest_request); + /* 2617 requirements, indicated by qop */ + if (digest_request->qop) { + + /* check the qop is what we expected. */ + if (strcmp(digest_request->qop, QOP_AUTH) != 0) { + /* we received a qop option we didn't send */ + debugs(29, 2, "authenticateDigestDecode: Invalid qop option received"); + return authDigestLogUsername(username, digest_request); + } + + /* check cnonce */ + if (!digest_request->cnonce || digest_request->cnonce[0] == '\0') { + debugs(29, 2, "authenticateDigestDecode: Missing cnonce field"); + return authDigestLogUsername(username, digest_request); + } + + /* check nc */ + if (strlen(digest_request->nc) != 8 || strspn(digest_request->nc, "0123456789abcdefABCDEF") != 8) { + debugs(29, 2, "authenticateDigestDecode: invalid nonce count"); + return authDigestLogUsername(username, digest_request); + } + } else { + /* cnonce and nc both require qop */ + if (digest_request->cnonce || digest_request->nc) { + debugs(29, 2, "authenticateDigestDecode: missing qop!"); + return authDigestLogUsername(username, digest_request); + } } - /* if we got a qop, did we get a cnonce or did we get a cnonce wihtout a qop? */ - if ((digest_request->qop && !digest_request->cnonce) - || (!digest_request->qop && digest_request->cnonce)) { - debugs(29, 4, "authenticateDigestDecode: qop without cnonce, or vice versa!"); + /** below nonce state dependent **/ + + /* now the nonce */ + nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64); + if (!nonce) { + /* we couldn't find a matching nonce! */ + debugs(29, 2, "authenticateDigestDecode: Unexpected or invalid nonce received"); + digest_request->credentials(AuthDigestUserRequest::Failed); return authDigestLogUsername(username, digest_request); } - /* check the algorithm is present and supported */ - if (!digest_request->algorithm) - digest_request->algorithm = xstrndup("MD5", 4); - else if (strcmp(digest_request->algorithm, "MD5") - && strcmp(digest_request->algorithm, "MD5-sess")) { - debugs(29, 4, "authenticateDigestDecode: invalid algorithm specified!"); + digest_request->nonce = nonce; + authDigestNonceLink(nonce); + + /* check that we're not being hacked / the username hasn't changed */ + if (nonce->user && strcmp(username, nonce->user->username())) { + debugs(29, 2, "authenticateDigestDecode: Username for the nonce does not equal the username for the request"); return authDigestLogUsername(username, digest_request); } diff -u -r -N squid-3.0.STABLE24/src/cbdata.h squid-3.0.STABLE25/src/cbdata.h --- squid-3.0.STABLE24/src/cbdata.h 2010-02-13 02:53:17.000000000 +1300 +++ squid-3.0.STABLE25/src/cbdata.h 2010-03-14 18:45:39.000000000 +1300 @@ -83,8 +83,7 @@ } \ void operator delete (void *address) { \ if (address) cbdataInternalFreeDbg(address,__FILE__,__LINE__); \ - } \ -#else + } #else /* CBDATA_DEBUG */ #define cbdataAlloc(type) ((type *)cbdataInternalAlloc(CBDATA_##type)) #define cbdataFree(var) do {if (var) {cbdataInternalFree(var); var = NULL;}} while(0) diff -u -r -N squid-3.0.STABLE24/src/cf.data.pre squid-3.0.STABLE25/src/cf.data.pre --- squid-3.0.STABLE24/src/cf.data.pre 2010-02-13 02:53:17.000000000 +1300 +++ squid-3.0.STABLE25/src/cf.data.pre 2010-03-14 18:45:43.000000000 +1300 @@ -1595,7 +1595,7 @@ The syntax is identical to 'http_access' and the other lists of ACL elements. See the comments for 'http_access' below, or - the Squid FAQ (http://www.squid-cache.org/FAQ/FAQ-10.html). + the Squid FAQ (http://wiki.squid-cache.org/SquidFaq/SquidAcl). DOC_END NAME: neighbor_type_domain diff -u -r -N squid-3.0.STABLE24/src/http.cc squid-3.0.STABLE25/src/http.cc --- squid-3.0.STABLE24/src/http.cc 2010-02-13 02:53:18.000000000 +1300 +++ squid-3.0.STABLE25/src/http.cc 2010-03-14 18:45:41.000000000 +1300 @@ -569,8 +569,9 @@ return 0; - default: /* Unknown status code */ - debugs (11, DBG_IMPORTANT, "WARNING: Unexpected http status code " << rep->sline.status); + default: + /* RFC 2616 section 6.1.1: an unrecognized response MUST NOT be cached. */ + debugs (11, 3, HERE << "Unknown HTTP status code " << rep->sline.status << ". Not cacheable."); return 0; @@ -747,6 +748,19 @@ readBuf->consume(header_bytes_read); } + /* Skip 1xx messages for now. Advertised in Via as an internal 1.0 hop */ + if (newrep->sline.status >= 100 && newrep->sline.status < 200) { + delete newrep; + debugs(11, 2, HERE << "1xx headers consume " << header_bytes_read << " bytes header."); + header_bytes_read = 0; + if (reply_bytes_read > 0) + debugs(11, 2, HERE << "1xx headers consume " << reply_bytes_read << " bytes reply."); + reply_bytes_read = 0; + ctx_exit(ctx); + processReplyHeader(); + return; + } + flags.chunked = 0; if (newrep->header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',')) { flags.chunked = 1; diff -u -r -N squid-3.0.STABLE24/src/HttpHeaderTools.cc squid-3.0.STABLE25/src/HttpHeaderTools.cc --- squid-3.0.STABLE24/src/HttpHeaderTools.cc 2010-02-13 02:53:15.000000000 +1300 +++ squid-3.0.STABLE25/src/HttpHeaderTools.cc 2010-03-14 18:45:43.000000000 +1300 @@ -353,25 +353,29 @@ { const char *end, *pos; val->clean(); - assert (*start == '"'); + if (*start != '"') { + debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'"); + return 0; + } pos = start + 1; - while (1) { - if (!(end = index (pos,'"'))) { + while (*pos != '"') { + bool quoted = (*pos == '\\'); + if (quoted) + pos++; + if (!*pos) { debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'"); + val->clean(); return 0; } - - /* check for quoted-chars */ - if (*(end - 1) != '\\') { - /* done */ - val->append(start + 1, end-start-1); - return 1; - } - - /* try for the end again */ - pos = end + 1; + end = pos + strcspn(pos + quoted, "\"\\") + quoted; + val->append(pos, end-pos); + pos = end; } + /* Make sure it's defined even if empty "" */ + if (!val->buf()) + val->limitInit("", 0); + return 1; } /*