summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 15:18:12 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 15:18:12 +0000
commit3c32c5f717bea6e39acaf5f6fd1bcdc9e33ec9d6 (patch)
tree692232f5006fdc0a2ab8dd20970048961b82c98b /net
parent0ed9468567ab680c92db9ac6660f484cbd5cad2c (diff)
downloadchromium_src-3c32c5f717bea6e39acaf5f6fd1bcdc9e33ec9d6.zip
chromium_src-3c32c5f717bea6e39acaf5f6fd1bcdc9e33ec9d6.tar.gz
chromium_src-3c32c5f717bea6e39acaf5f6fd1bcdc9e33ec9d6.tar.bz2
Digest Authentication: Test that "nc" is incremented for same "nonce".
If the client generates multiple Authorization headers for the same server (or proxy) authenticate header the "nc" (nonce-count) field should be incremented. This tends to happen when preemptive Authentication headers are sent using the previous Authenticate challenge. BUG=NONE TEST=net_unittests --gtest_filter="HttpNetworkTransactionTest.DigestPreAuthNonceCount" Review URL: http://codereview.chromium.org/2112007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47514 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_auth_handler_digest.cc5
-rw-r--r--net/http/http_auth_handler_digest.h9
-rw-r--r--net/http/http_network_transaction_unittest.cc136
3 files changed, 150 insertions, 0 deletions
diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc
index f7aea5b..107cfcf 100644
--- a/net/http/http_auth_handler_digest.cc
+++ b/net/http/http_auth_handler_digest.cc
@@ -45,10 +45,15 @@ namespace net {
//=====================+==========================================+
+//static
+bool HttpAuthHandlerDigest::fixed_cnonce_ = false;
+
// static
std::string HttpAuthHandlerDigest::GenerateNonce() {
// This is how mozilla generates their cnonce -- a 16 digit hex string.
static const char domain[] = "0123456789abcdef";
+ if (fixed_cnonce_)
+ return std::string(domain);
std::string cnonce;
cnonce.reserve(16);
for (int i = 0; i < 16; ++i)
diff --git a/net/http/http_auth_handler_digest.h b/net/http/http_auth_handler_digest.h
index f718a14..876e346 100644
--- a/net/http/http_auth_handler_digest.h
+++ b/net/http/http_auth_handler_digest.h
@@ -46,6 +46,7 @@ class HttpAuthHandlerDigest : public HttpAuthHandler {
private:
FRIEND_TEST(HttpAuthHandlerDigestTest, ParseChallenge);
FRIEND_TEST(HttpAuthHandlerDigestTest, AssembleCredentials);
+ FRIEND_TEST(HttpNetworkTransactionTest, DigestPreAuthNonceCount);
// Possible values for the "algorithm" property.
enum DigestAlgorithm {
@@ -109,6 +110,11 @@ class HttpAuthHandlerDigest : public HttpAuthHandler {
const std::string& cnonce,
int nonce_count) const;
+ // Forces cnonce to be the same each time. This is used for unit tests.
+ static void SetFixedCnonce(bool fixed_cnonce) {
+ fixed_cnonce_ = fixed_cnonce;
+ }
+
// Information parsed from the challenge.
std::string nonce_;
std::string domain_;
@@ -118,6 +124,9 @@ class HttpAuthHandlerDigest : public HttpAuthHandler {
int qop_; // Bitfield of QualityOfProtection
int nonce_count_;
+
+ // Forces the cnonce to be the same each time, for unit tests.
+ static bool fixed_cnonce_;
};
} // namespace net
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 8ad5d2f..9985d94 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -22,6 +22,7 @@
#include "net/base/ssl_info.h"
#include "net/base/test_completion_callback.h"
#include "net/base/upload_data.h"
+#include "net/http/http_auth_handler_digest.h"
#include "net/http/http_auth_handler_ntlm.h"
#include "net/http/http_basic_stream.h"
#include "net/http/http_network_session.h"
@@ -2994,6 +2995,141 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) {
}
}
+// Tests that nonce count increments when multiple auth attempts
+// are started with the same nonce.
+TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) {
+ SessionDependencies session_deps;
+ scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
+ HttpAuthHandlerDigest::SetFixedCnonce(true);
+
+ // Transaction 1: authenticate (foo, bar) on MyRealm1
+ {
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/x/y/z");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/y/z HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Digest realm=\"digestive\", nonce=\"OU812\", "
+ "algorithm=MD5, qop=\"auth\"\r\n\r\n"),
+ MockRead(false, OK),
+ };
+
+ // Resend with authorization (username=foo, password=bar)
+ MockWrite data_writes2[] = {
+ MockWrite("GET /x/y/z HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Digest username=\"foo\", realm=\"digestive\", "
+ "nonce=\"OU812\", uri=\"/x/y/z\", algorithm=MD5, "
+ "response=\"03ffbcd30add722589c1de345d7a927f\", qop=auth, "
+ "nc=00000001, cnonce=\"0123456789abcdef\"\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+ data_writes2, arraysize(data_writes2));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+ session_deps.socket_factory.AddSocketDataProvider(&data2);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_FALSE(response == NULL);
+
+ // The password prompt info should have been set in
+ // response->auth_challenge.
+ ASSERT_FALSE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+ EXPECT_EQ(L"digestive", response->auth_challenge->realm);
+ EXPECT_EQ(L"digest", response->auth_challenge->scheme);
+
+ TestCompletionCallback callback2;
+
+ rv = trans->RestartWithAuth(L"foo", L"bar", &callback2);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ response = trans->GetResponseInfo();
+ ASSERT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ }
+
+ // ------------------------------------------------------------------------
+
+ // Transaction 2: Request another resource in digestive's protection space.
+ // This will preemptively add an Authorization header which should have an
+ // "nc" value of 2 (as compared to 1 in the first use.
+ {
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ // Note that Transaction 1 was at /x/y/z, so this is in the same
+ // protection space as digest.
+ request.url = GURL("http://www.google.com/x/y/a/b");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/y/a/b HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Digest username=\"foo\", realm=\"digestive\", "
+ "nonce=\"OU812\", uri=\"/x/y/a/b\", algorithm=MD5, "
+ "response=\"d6f9a2c07d1c5df7b89379dca1269b35\", qop=auth, "
+ "nc=00000002, cnonce=\"0123456789abcdef\"\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ }
+}
+
// Test the ResetStateForRestart() private method.
TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) {
// Create a transaction (the dependencies aren't important).