summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-22 16:08:16 +0000
committerasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-22 16:08:16 +0000
commit2262e3a6855db97b9af8cc7f9584f744ce2e14bd (patch)
tree08fd09d0e65af81d502697264db25044f34929be /net
parenta7d8ca02e87c44d3efec8c354881434341e942d4 (diff)
downloadchromium_src-2262e3a6855db97b9af8cc7f9584f744ce2e14bd.zip
chromium_src-2262e3a6855db97b9af8cc7f9584f744ce2e14bd.tar.gz
chromium_src-2262e3a6855db97b9af8cc7f9584f744ce2e14bd.tar.bz2
Re-enable embedded identities in URLs for HTTP authentication.
This effectively reverts r121506. BUG=123150 TEST=net_unittests. Username/passwords specified in URLs work. XmlHttpRequests() with credentials passed as arguments work. Review URL: https://chromiumcodereview.appspot.com/10412025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138264 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_auth_controller.cc18
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc147
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc147
-rw-r--r--net/url_request/url_request_unittest.cc30
4 files changed, 331 insertions, 11 deletions
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index 34c2469..1ab12ad 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -450,17 +450,21 @@ bool HttpAuthController::SelectNextAuthIdentityToTry() {
DCHECK(handler_.get());
DCHECK(identity_.invalid);
- // Do not try to use the username:password encoded into the URL. At worst,
- // this represents a session fixation attack against basic auth, and as it
- // turns out, IE hasn't supported this for years. If a caller really wants
- // to use embedded identities, the can add an URLRequest::Delegate that
- // inspects the URL and supplies the username/password at OnAuthRequired()
- // time. Past data shows this is used extremely infrequently in web pages,
- // but continue to collect this data.
+ // Try to use the username:password encoded into the URL first.
if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
!embedded_identity_used_) {
+ identity_.source = HttpAuth::IDENT_SRC_URL;
+ identity_.invalid = false;
+ // Extract the username:password from the URL.
+ string16 username;
+ string16 password;
+ GetIdentityFromURL(auth_url_, &username, &password);
+ identity_.credentials.Set(username, password);
embedded_identity_used_ = true;
+ // TODO(eroman): If the password is blank, should we also try combining
+ // with a password from the cache?
UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true);
+ return true;
}
// Check the auth cache for a realm entry.
diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc
index 746cbe9..27470e0 100644
--- a/net/http/http_network_transaction_spdy2_unittest.cc
+++ b/net/http/http_network_transaction_spdy2_unittest.cc
@@ -3727,8 +3727,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, ResendRequestOnWriteBodyError) {
// Test the request-challenge-retry sequence for basic auth when there is
// an identity in the URL. The request should be sent as normal, but when
-// it fails the identity from the URL is no longer used.
-TEST_F(HttpNetworkTransactionSpdy2Test, IgnoreAuthIdentityInURL) {
+// it fails the identity from the URL is used to answer the challenge.
+TEST_F(HttpNetworkTransactionSpdy2Test, AuthIdentityInURL) {
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://foo:b@r@www.google.com/");
@@ -3755,17 +3755,160 @@ TEST_F(HttpNetworkTransactionSpdy2Test, IgnoreAuthIdentityInURL) {
MockRead(SYNCHRONOUS, ERR_FAILED),
};
+ // After the challenge above, the transaction will be restarted using the
+ // identity from the url (foo, b@r) to answer the challenge.
+ MockWrite data_writes2[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJAcg==\r\n\r\n"),
+ };
+
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 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.callback(), BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback1.WaitForResult();
EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+
+ TestCompletionCallback callback2;
+ rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ // There is no challenge info, since the identity in URL worked.
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(100, response->headers->GetContentLength());
+
+ // Empty the current queue.
+ MessageLoop::current()->RunAllPending();
+}
+
+// Test the request-challenge-retry sequence for basic auth when there is an
+// incorrect identity in the URL. The identity from the URL should be used only
+// once.
+TEST_F(HttpNetworkTransactionSpdy2Test, WrongAuthIdentityInURL) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ // Note: the URL has a username:password in it. The password "baz" is
+ // wrong (should be "bar").
+ request.url = GURL("http://foo:baz@www.google.com/");
+
+ request.load_flags = LOAD_NORMAL;
+
+ SessionDependencies session_deps;
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET / 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: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead(SYNCHRONOUS, ERR_FAILED),
+ };
+
+ // After the challenge above, the transaction will be restarted using the
+ // identity from the url (foo, baz) to answer the challenge.
+ MockWrite data_writes2[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead(SYNCHRONOUS, ERR_FAILED),
+ };
+
+ // After the challenge above, the transaction will be restarted using the
+ // identity supplied by the user (foo, bar) to answer the challenge.
+ MockWrite data_writes3[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ MockRead data_reads3[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 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));
+ StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+ data_writes3, arraysize(data_writes3));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+ session_deps.socket_factory.AddSocketDataProvider(&data2);
+ session_deps.socket_factory.AddSocketDataProvider(&data3);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, callback1.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+ TestCompletionCallback callback2;
+ rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
+
+ TestCompletionCallback callback3;
+ rv = trans->RestartWithAuth(
+ AuthCredentials(kFoo, kBar), callback3.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback3.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ // There is no challenge info, since the identity worked.
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(100, response->headers->GetContentLength());
+
// Empty the current queue.
MessageLoop::current()->RunAllPending();
}
diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc
index 2a9cbe0..6f7468d 100644
--- a/net/http/http_network_transaction_spdy3_unittest.cc
+++ b/net/http/http_network_transaction_spdy3_unittest.cc
@@ -3727,8 +3727,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ResendRequestOnWriteBodyError) {
// Test the request-challenge-retry sequence for basic auth when there is
// an identity in the URL. The request should be sent as normal, but when
-// it fails the identity from the URL is no longer used.
-TEST_F(HttpNetworkTransactionSpdy3Test, IgnoreAuthIdentityInURL) {
+// it fails the identity from the URL is used to answer the challenge.
+TEST_F(HttpNetworkTransactionSpdy3Test, AuthIdentityInURL) {
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://foo:b@r@www.google.com/");
@@ -3755,17 +3755,160 @@ TEST_F(HttpNetworkTransactionSpdy3Test, IgnoreAuthIdentityInURL) {
MockRead(SYNCHRONOUS, ERR_FAILED),
};
+ // After the challenge above, the transaction will be restarted using the
+ // identity from the url (foo, b@r) to answer the challenge.
+ MockWrite data_writes2[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJAcg==\r\n\r\n"),
+ };
+
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 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.callback(), BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback1.WaitForResult();
EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+
+ TestCompletionCallback callback2;
+ rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ // There is no challenge info, since the identity in URL worked.
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(100, response->headers->GetContentLength());
+
+ // Empty the current queue.
+ MessageLoop::current()->RunAllPending();
+}
+
+// Test the request-challenge-retry sequence for basic auth when there is an
+// incorrect identity in the URL. The identity from the URL should be used only
+// once.
+TEST_F(HttpNetworkTransactionSpdy3Test, WrongAuthIdentityInURL) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ // Note: the URL has a username:password in it. The password "baz" is
+ // wrong (should be "bar").
+ request.url = GURL("http://foo:baz@www.google.com/");
+
+ request.load_flags = LOAD_NORMAL;
+
+ SessionDependencies session_deps;
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET / 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: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead(SYNCHRONOUS, ERR_FAILED),
+ };
+
+ // After the challenge above, the transaction will be restarted using the
+ // identity from the url (foo, baz) to answer the challenge.
+ MockWrite data_writes2[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead(SYNCHRONOUS, ERR_FAILED),
+ };
+
+ // After the challenge above, the transaction will be restarted using the
+ // identity supplied by the user (foo, bar) to answer the challenge.
+ MockWrite data_writes3[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ MockRead data_reads3[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 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));
+ StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+ data_writes3, arraysize(data_writes3));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+ session_deps.socket_factory.AddSocketDataProvider(&data2);
+ session_deps.socket_factory.AddSocketDataProvider(&data3);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, callback1.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+ TestCompletionCallback callback2;
+ rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
+
+ TestCompletionCallback callback3;
+ rv = trans->RestartWithAuth(
+ AuthCredentials(kFoo, kBar), callback3.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback3.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ // There is no challenge info, since the identity worked.
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(100, response->headers->GetContentLength());
+
// Empty the current queue.
MessageLoop::current()->RunAllPending();
}
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 9fd411e..7a7038f 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2935,6 +2935,36 @@ TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) {
EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true")
!= std::string::npos);
}
+
+ // Same test as above, except this time the restart is initiated earlier
+ // (without user intervention since identity is embedded in the URL).
+ {
+ TestNetworkDelegate network_delegate; // must outlive URLRequest
+ TestURLRequestContext context(true);
+ context.set_network_delegate(&network_delegate);
+ context.Init();
+
+ TestDelegate d;
+
+ GURL::Replacements replacements;
+ std::string username("user2");
+ std::string password("secret");
+ replacements.SetUsernameStr(username);
+ replacements.SetPasswordStr(password);
+ GURL url_with_identity = url_requiring_auth.ReplaceComponents(replacements);
+
+ URLRequest r(url_with_identity, &d);
+ r.set_context(&context);
+ r.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(d.data_received().find("user2/secret") != std::string::npos);
+
+ // Make sure we sent the cookie in the restarted transaction.
+ EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true")
+ != std::string::npos);
+ }
}
TEST_F(URLRequestTest, DelayedCookieCallback) {