diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-05 19:13:29 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-05 19:13:29 +0000 |
commit | ac3fa8e2ef53e83b113d726209525f4af4aa46f1 (patch) | |
tree | cf3550a3155d263943062b68ea5b205035d22182 | |
parent | 5502208efd91894a34ef43a38d348b7f9acb3770 (diff) | |
download | chromium_src-ac3fa8e2ef53e83b113d726209525f4af4aa46f1.zip chromium_src-ac3fa8e2ef53e83b113d726209525f4af4aa46f1.tar.gz chromium_src-ac3fa8e2ef53e83b113d726209525f4af4aa46f1.tar.bz2 |
Add Single Sign On support to HTTP Authentication handlers.
Currently this is implemented on Windows for the NTLM and Negotiate schemes.
This CL does not introduce the hooks to actually use Single Sign On in response to a 401/407 request - that will come in a later CL.
This behavior is disabled for now as well.
BUG=29862
TEST=Ran unittests, and Chrome against a server with authentication challenges.
Review URL: http://codereview.chromium.org/555174
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38227 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 457 insertions, 185 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 7c0c3de..6ea5457 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -305,6 +305,13 @@ NET_ERROR(NO_SUPPORTED_PROXIES, -336) // There is a FLIP protocol framing error. NET_ERROR(FLIP_PROTOCOL_ERROR, -337) +// Credentials could not be estalished during HTTP Authentication. +NET_ERROR(INVALID_AUTH_CREDENTIALS, -338) + +// An HTTP Authentication scheme was tried which is not supported on this +// machine. +NET_ERROR(UNSUPPORTED_AUTH_SCHEME, -339) + // The cache does not have the requested entry. NET_ERROR(CACHE_MISS, -400) diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc index 52fa5cd..b00e8d0 100644 --- a/net/http/http_auth_cache_unittest.cc +++ b/net/http/http_auth_cache_unittest.cc @@ -1,8 +1,9 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/string_util.h" +#include "net/base/net_errors.h" #include "net/http/http_auth_cache.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,11 +24,20 @@ class MockAuthHandler : public HttpAuthHandler { properties_ = 0; } - virtual std::string GenerateCredentials(const std::wstring&, - const std::wstring&, - const HttpRequestInfo*, - const ProxyInfo*) { - return "mock-credentials"; // Unused. + virtual int GenerateAuthToken(const std::wstring&, + const std::wstring&, + const HttpRequestInfo*, + const ProxyInfo*, + std::string* auth_token) { + *auth_token = "mock-credentials"; + return OK; + } + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo*, + const ProxyInfo*, + std::string* auth_token) { + *auth_token = "mock-credentials"; + return OK; } protected: @@ -52,12 +62,12 @@ TEST(HttpAuthCacheTest, Basic) { scoped_refptr<HttpAuthHandler> realm1_handler = new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER); cache.Add(origin, realm1_handler, L"realm1-user", L"realm1-password", - "/foo/bar/index.html"); + "/foo/bar/index.html"); scoped_refptr<HttpAuthHandler> realm2_handler = new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER); cache.Add(origin, realm2_handler, L"realm2-user", L"realm2-password", - "/foo2/index.html"); + "/foo2/index.html"); scoped_refptr<HttpAuthHandler> realm3_handler = new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_PROXY); @@ -227,7 +237,8 @@ class HttpAuthCacheEvictionTest : public testing::Test { } void AddPathToRealm(int realm_i, int path_i) { - scoped_refptr<HttpAuthHandler> handler = new MockAuthHandler("basic", + scoped_refptr<HttpAuthHandler> handler = new MockAuthHandler( + "basic", GenerateRealm(realm_i), HttpAuth::AUTH_SERVER); std::string path = GeneratePath(realm_i, path_i); cache_.Add(origin_, handler, L"username", L"password", path); diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc index 9abdd9a..9d70ca0 100644 --- a/net/http/http_auth_handler.cc +++ b/net/http/http_auth_handler.cc @@ -1,10 +1,12 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/logging.h" #include "net/http/http_auth_handler.h" +#include "base/logging.h" +#include "net/base/net_errors.h" + namespace net { bool HttpAuthHandler::InitFromChallenge(std::string::const_iterator begin, diff --git a/net/http/http_auth_handler.h b/net/http/http_auth_handler.h index 02a410e..4b8f5261 100644 --- a/net/http/http_auth_handler.h +++ b/net/http/http_auth_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -72,11 +72,32 @@ class HttpAuthHandler : public base::RefCounted<HttpAuthHandler> { // single-round schemes. virtual bool IsFinalRound() { return true; } - // Generate the Authorization header value. - virtual std::string GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const HttpRequestInfo* request, - const ProxyInfo* proxy) = 0; + // Returns whether the default credentials may be used for the |origin| passed + // into |InitFromChallenge|. If true, the user does not need to be prompted + // for username and password to establish credentials. + virtual bool AllowDefaultCredentials() { return false; } + + // TODO(cbentzel): Separate providing credentials from generating the + // authentication token in the API. + + // Generates an authentication token. + // The return value is an error code. If the code is not |OK|, the value of + // |*auth_token| is unspecified. + // |auth_token| is a return value and must be non-NULL. + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) = 0; + + // Generates an authentication token using default credentials. + // The return value is an error code. If the code is not |OK|, the value of + // |*auth_token| is unspecified. + // |auth_token| is a return value and must be non-NULL. + // This should only be called after |AllowDefaultCredentials| returns true. + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) = 0; protected: enum Property { diff --git a/net/http/http_auth_handler_basic.cc b/net/http/http_auth_handler_basic.cc index 71c310c..2c70577 100644 --- a/net/http/http_auth_handler_basic.cc +++ b/net/http/http_auth_handler_basic.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include "base/base64.h" #include "base/string_util.h" +#include "net/base/net_errors.h" #include "net/http/http_auth.h" namespace net { @@ -41,17 +42,31 @@ bool HttpAuthHandlerBasic::Init(std::string::const_iterator challenge_begin, return challenge_tok.valid(); } -std::string HttpAuthHandlerBasic::GenerateCredentials( +int HttpAuthHandlerBasic::GenerateAuthToken( const std::wstring& username, const std::wstring& password, const HttpRequestInfo*, - const ProxyInfo*) { + const ProxyInfo*, + std::string* auth_token) { // TODO(eroman): is this the right encoding of username/password? std::string base64_username_password; if (!base::Base64Encode(WideToUTF8(username) + ":" + WideToUTF8(password), - &base64_username_password)) - return std::string(); // FAIL - return std::string("Basic ") + base64_username_password; + &base64_username_password)) { + LOG(ERROR) << "Unexpected problem Base64 encoding."; + return ERR_UNEXPECTED; + } + *auth_token = "Basic " + base64_username_password; + return OK; +} + +int HttpAuthHandlerBasic::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return ERR_NOT_IMPLEMENTED; } + } // namespace net diff --git a/net/http/http_auth_handler_basic.h b/net/http/http_auth_handler_basic.h index 679c91a..e4d2f25 100644 --- a/net/http/http_auth_handler_basic.h +++ b/net/http/http_auth_handler_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,10 +12,17 @@ namespace net { // Code for handling http basic authentication. class HttpAuthHandlerBasic : public HttpAuthHandler { public: - virtual std::string GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const HttpRequestInfo*, - const ProxyInfo*); + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo*, + const ProxyInfo*, + std::string* auth_token); + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); + + protected: virtual bool Init(std::string::const_iterator challenge_begin, std::string::const_iterator challenge_end); diff --git a/net/http/http_auth_handler_basic_unittest.cc b/net/http/http_auth_handler_basic_unittest.cc index d7a1437..59d9a56 100644 --- a/net/http/http_auth_handler_basic_unittest.cc +++ b/net/http/http_auth_handler_basic_unittest.cc @@ -1,15 +1,16 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "testing/gtest/include/gtest/gtest.h" #include "base/basictypes.h" +#include "net/base/net_errors.h" #include "net/http/http_auth_handler_basic.h" namespace net { -TEST(HttpAuthHandlerBasicTest, GenerateCredentials) { +TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) { static const struct { const wchar_t* username; const wchar_t* password; @@ -30,9 +31,13 @@ TEST(HttpAuthHandlerBasicTest, GenerateCredentials) { bool ok = basic->InitFromChallenge(challenge.begin(), challenge.end(), HttpAuth::AUTH_SERVER, origin); EXPECT_TRUE(ok); - std::string credentials = basic->GenerateCredentials(tests[i].username, - tests[i].password, - NULL, NULL); + std::string credentials; + int rv = basic->GenerateAuthToken(tests[i].username, + tests[i].password, + NULL, + NULL, + &credentials); + EXPECT_EQ(OK, rv); EXPECT_STREQ(tests[i].expected_credentials, credentials.c_str()); } } diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc index a0c443c..10af291 100644 --- a/net/http/http_auth_handler_digest.cc +++ b/net/http/http_auth_handler_digest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,7 @@ #include "base/md5.h" #include "base/rand_util.h" #include "base/string_util.h" +#include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/http/http_auth.h" #include "net/http/http_request_info.h" @@ -77,11 +78,12 @@ std::string HttpAuthHandlerDigest::AlgorithmToString(int algorithm) { } } -std::string HttpAuthHandlerDigest::GenerateCredentials( +int HttpAuthHandlerDigest::GenerateAuthToken( const std::wstring& username, const std::wstring& password, const HttpRequestInfo* request, - const ProxyInfo* proxy) { + const ProxyInfo* proxy, + std::string* auth_token) { // Generate a random client nonce. std::string cnonce = GenerateNonce(); @@ -97,11 +99,21 @@ std::string HttpAuthHandlerDigest::GenerateCredentials( std::string path; GetRequestMethodAndPath(request, proxy, &method, &path); - return AssembleCredentials(method, path, - // TODO(eroman): is this the right encoding? - WideToUTF8(username), - WideToUTF8(password), - cnonce, nonce_count); + *auth_token = AssembleCredentials(method, path, + // TODO(eroman): is this the right encoding? + WideToUTF8(username), + WideToUTF8(password), + cnonce, nonce_count); + return OK; +} + +int HttpAuthHandlerDigest::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return ERR_NOT_IMPLEMENTED; } void HttpAuthHandlerDigest::GetRequestMethodAndPath( diff --git a/net/http/http_auth_handler_digest.h b/net/http/http_auth_handler_digest.h index b4c8687..686f354 100644 --- a/net/http/http_auth_handler_digest.h +++ b/net/http/http_auth_handler_digest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,10 +15,15 @@ namespace net { // Code for handling http digest authentication. class HttpAuthHandlerDigest : public HttpAuthHandler { public: - virtual std::string GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const HttpRequestInfo* request, - const ProxyInfo* proxy); + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); protected: virtual bool Init(std::string::const_iterator challenge_begin, diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h index eb4d11f..b057f5c 100644 --- a/net/http/http_auth_handler_negotiate.h +++ b/net/http/http_auth_handler_negotiate.h @@ -30,11 +30,17 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { virtual bool IsFinalRound(); - virtual std::string GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const HttpRequestInfo* request, - const ProxyInfo* proxy); + virtual bool AllowDefaultCredentials(); + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); protected: virtual bool Init(std::string::const_iterator challenge_begin, diff --git a/net/http/http_auth_handler_negotiate_posix.cc b/net/http/http_auth_handler_negotiate_posix.cc index 581b489..f70b58b 100755..100644 --- a/net/http/http_auth_handler_negotiate_posix.cc +++ b/net/http/http_auth_handler_negotiate_posix.cc @@ -5,6 +5,7 @@ #include "net/http/http_auth_handler_negotiate.h" #include "base/logging.h" +#include "net/base/net_errors.h" namespace net { @@ -20,11 +21,19 @@ HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { bool HttpAuthHandlerNegotiate::NeedsIdentity() { NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); return false; } bool HttpAuthHandlerNegotiate::IsFinalRound() { NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return false; +} + +bool HttpAuthHandlerNegotiate::AllowDefaultCredentials() { + NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); return false; } @@ -33,13 +42,24 @@ bool HttpAuthHandlerNegotiate::Init(std::string::const_iterator challenge_begin, return false; } -std::string HttpAuthHandlerNegotiate::GenerateCredentials( +int HttpAuthHandlerNegotiate::GenerateAuthToken( const std::wstring& username, const std::wstring& password, const HttpRequestInfo* request, - const ProxyInfo* proxy) { + const ProxyInfo* proxy, + std::string* auth_token) { + NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return ERR_NOT_IMPLEMENTED; +} + +int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { NOTREACHED(); - return std::string(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return ERR_NOT_IMPLEMENTED; } } // namespace net diff --git a/net/http/http_auth_handler_negotiate_win.cc b/net/http/http_auth_handler_negotiate_win.cc index 782cb5c..c005590 100644 --- a/net/http/http_auth_handler_negotiate_win.cc +++ b/net/http/http_auth_handler_negotiate_win.cc @@ -15,23 +15,19 @@ HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate() : HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { } -std::string HttpAuthHandlerNegotiate::GenerateCredentials( +int HttpAuthHandlerNegotiate::GenerateAuthToken( const std::wstring& username, const std::wstring& password, const HttpRequestInfo* request, - const ProxyInfo* proxy) { - std::string auth_credentials; - - int rv = auth_sspi_.GenerateCredentials( - username, - password, + const ProxyInfo* proxy, + std::string* auth_token) { + return auth_sspi_.GenerateAuthToken( + &username, + &password, origin_, request, proxy, - &auth_credentials); - if (rv == OK) - return auth_credentials; - return std::string(); + auth_token); } // The Negotiate challenge header looks like: @@ -54,4 +50,30 @@ bool HttpAuthHandlerNegotiate::IsFinalRound() { return auth_sspi_.IsFinalRound(); } +bool HttpAuthHandlerNegotiate::AllowDefaultCredentials() { + // NOTE: Temporarily disabled. SSO is a potential security risk. + // TODO(cbentzel): Add a pointer to Firefox documentation about risk. + + // TODO(cbentzel): Add a blanket command line flag to enable/disable? + // TODO(cbentzel): Add a whitelist regexp command line flag? + // TODO(cbentzel): Resolve the origin_ (helpful if doing already) and see if + // it is in private IP space? + // TODO(cbentzel): Compare origin_ to this machine's hostname and allow if + // it matches at least two or three layers deep? + return false; +} + +int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + return auth_sspi_.GenerateAuthToken( + NULL, // username + NULL, // password + origin_, + request, + proxy, + auth_token); +} + } // namespace net diff --git a/net/http/http_auth_handler_ntlm.cc b/net/http/http_auth_handler_ntlm.cc index 6e10e79..e191ce4 100755..100644 --- a/net/http/http_auth_handler_ntlm.cc +++ b/net/http/http_auth_handler_ntlm.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,24 +10,20 @@ namespace net { -std::string HttpAuthHandlerNTLM::GenerateCredentials( +int HttpAuthHandlerNTLM::GenerateAuthToken( const std::wstring& username, const std::wstring& password, const HttpRequestInfo* request, - const ProxyInfo* proxy) { + const ProxyInfo* proxy, + std::string* auth_token) { #if defined(NTLM_SSPI) - std::string auth_credentials; - - int rv = auth_sspi_.GenerateCredentials( - username, - password, + return auth_sspi_.GenerateAuthToken( + &username, + &password, origin_, request, proxy, - &auth_credentials); - if (rv == OK) - return auth_credentials; - return std::string(); + auth_token); #else // !defined(NTLM_SSPI) // TODO(wtc): See if we can use char* instead of void* for in_buf and // out_buf. This change will need to propagate to GetNextToken, @@ -58,7 +54,7 @@ std::string HttpAuthHandlerNTLM::GenerateCredentials( in_buf = NULL; int rv = InitializeBeforeFirstChallenge(); if (rv != OK) - return std::string(); + return rv; } else { // Decode |auth_data_| into the input buffer. int len = auth_data_.length(); @@ -71,25 +67,30 @@ std::string HttpAuthHandlerNTLM::GenerateCredentials( len--; auth_data_.erase(len); - if (!base::Base64Decode(auth_data_, &decoded_auth_data)) - return std::string(); // Improper base64 encoding + if (!base::Base64Decode(auth_data_, &decoded_auth_data)) { + LOG(ERROR) << "Unexpected problem Base64 decoding."; + return ERR_UNEXPECTED; + } in_buf_len = decoded_auth_data.length(); in_buf = decoded_auth_data.data(); } int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len); if (rv != OK) - return std::string(); + return rv; // Base64 encode data in output buffer and prepend "NTLM ". std::string encode_input(static_cast<char*>(out_buf), out_buf_len); std::string encode_output; - bool ok = base::Base64Encode(encode_input, &encode_output); + bool base64_rv = base::Base64Encode(encode_input, &encode_output); // OK, we are done with |out_buf| free(out_buf); - if (!ok) - return std::string(); - return std::string("NTLM ") + encode_output; + if (!base64_rv) { + LOG(ERROR) << "Unexpected problem Base64 encoding."; + return ERR_UNEXPECTED; + } + *auth_token = std::string("NTLM ") + encode_output; + return OK; #endif } diff --git a/net/http/http_auth_handler_ntlm.h b/net/http/http_auth_handler_ntlm.h index c3bda62..c4b9dab 100644 --- a/net/http/http_auth_handler_ntlm.h +++ b/net/http/http_auth_handler_ntlm.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -68,10 +68,17 @@ class HttpAuthHandlerNTLM : public HttpAuthHandler { virtual bool IsFinalRound(); - virtual std::string GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const HttpRequestInfo* request, - const ProxyInfo* proxy); + virtual bool AllowDefaultCredentials(); + + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); protected: virtual bool Init(std::string::const_iterator challenge_begin, diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc index 7976878..7fccfce 100644 --- a/net/http/http_auth_handler_ntlm_portable.cc +++ b/net/http/http_auth_handler_ntlm_portable.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -657,6 +657,12 @@ bool HttpAuthHandlerNTLM::IsFinalRound() { return !auth_data_.empty(); } +bool HttpAuthHandlerNTLM::AllowDefaultCredentials() { + // Default credentials are not supported in the portable implementation of + // NTLM, but are supported in the SSPI implementation. + return false; +} + // static HttpAuthHandlerNTLM::GenerateRandomProc HttpAuthHandlerNTLM::SetGenerateRandomProc( @@ -704,4 +710,13 @@ int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { return OK; } +int HttpAuthHandlerNTLM::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + NOTREACHED(); + LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); + return ERR_NOT_IMPLEMENTED; +} + } // namespace net diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc index fba9c1b..c096aaf 100644 --- a/net/http/http_auth_handler_ntlm_win.cc +++ b/net/http/http_auth_handler_ntlm_win.cc @@ -34,6 +34,31 @@ bool HttpAuthHandlerNTLM::IsFinalRound() { return auth_sspi_.IsFinalRound(); } +bool HttpAuthHandlerNTLM::AllowDefaultCredentials() { + // NOTE: Temporarily disabled. SSO is a potential security risk with NTLM. + // TODO(cbentzel): Add a pointer to Firefox documentation about risk. + + // TODO(cbentzel): Add a blanket command line flag to enable/disable? + // TODO(cbentzel): Add a whitelist regexp command line flag? + // TODO(cbentzel): Resolve the origin_ (helpful if doing already) and see if + // it is in private IP space? + // TODO(cbentzel): Compare origin_ to this machine's hostname and allow if + // it matches at least two or three layers deep? + return false; +} + +int HttpAuthHandlerNTLM::GenerateDefaultAuthToken( + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + return auth_sspi_.GenerateAuthToken( + NULL, // username + NULL, // password + origin_, + request, + proxy, + auth_token); +} } // namespace net diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc index 00861e5..c858d36 100644 --- a/net/http/http_auth_sspi_win.cc +++ b/net/http/http_auth_sspi_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,6 +15,87 @@ #include "net/http/http_auth.h" namespace net { +namespace { + +int MapAcquireCredentialsStatusToError(SECURITY_STATUS status, + const SEC_WCHAR* package) { + switch (status) { + case SEC_E_OK: + return OK; + case SEC_E_INSUFFICIENT_MEMORY: + return ERR_OUT_OF_MEMORY; + case SEC_E_INTERNAL_ERROR: + return ERR_UNEXPECTED; + case SEC_E_NO_CREDENTIALS: + case SEC_E_NOT_OWNER: + case SEC_E_UNKNOWN_CREDENTIALS: + return ERR_INVALID_AUTH_CREDENTIALS; + case SEC_E_SECPKG_NOT_FOUND: + // This indicates that the SSPI configuration does not match expectations + LOG(ERROR) << "Received SEC_E_SECPKG_NOT_FOUND for " << package; + return ERR_UNSUPPORTED_AUTH_SCHEME; + default: + LOG(ERROR) << "Unexpected SECURITY_STATUS " << status; + return ERR_UNEXPECTED; + } +} + +int AcquireCredentials(const SEC_WCHAR* package, + const std::wstring& domain, + const std::wstring& user, + const std::wstring& password, + CredHandle* cred) { + SEC_WINNT_AUTH_IDENTITY identity; + identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + identity.User = + reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str())); + identity.UserLength = user.size(); + identity.Domain = + reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str())); + identity.DomainLength = domain.size(); + identity.Password = + reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str())); + identity.PasswordLength = password.size(); + + TimeStamp expiry; + + // Pass the username/password to get the credentials handle. + SECURITY_STATUS status = AcquireCredentialsHandle( + NULL, // pszPrincipal + const_cast<SEC_WCHAR*>(package), // pszPackage + SECPKG_CRED_OUTBOUND, // fCredentialUse + NULL, // pvLogonID + &identity, // pAuthData + NULL, // pGetKeyFn (not used) + NULL, // pvGetKeyArgument (not used) + cred, // phCredential + &expiry); // ptsExpiry + + return MapAcquireCredentialsStatusToError(status, package); +} + +int AcquireDefaultCredentials(const SEC_WCHAR* package, CredHandle* cred) { + TimeStamp expiry; + + // Pass the username/password to get the credentials handle. + // Note: Since the 5th argument is NULL, it uses the default + // cached credentials for the logged in user, which can be used + // for a single sign-on. + SECURITY_STATUS status = AcquireCredentialsHandle( + NULL, // pszPrincipal + const_cast<SEC_WCHAR*>(package), // pszPackage + SECPKG_CRED_OUTBOUND, // fCredentialUse + NULL, // pvLogonID + NULL, // pAuthData + NULL, // pGetKeyFn (not used) + NULL, // pvGetKeyArgument (not used) + cred, // phCredential + &expiry); // ptsExpiry + + return MapAcquireCredentialsStatusToError(status, package); +} + +} // anonymous namespace HttpAuthSSPI::HttpAuthSSPI(const std::string& scheme, SEC_WCHAR* security_package) @@ -73,28 +154,26 @@ bool HttpAuthSSPI::ParseChallenge(std::string::const_iterator challenge_begin, encoded_auth_token.erase(encoded_length); std::string decoded_auth_token; - bool rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token); - if (rv) { - decoded_server_auth_token_ = decoded_auth_token; + bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token); + if (!base64_rv) { + LOG(ERROR) << "Base64 decoding of auth token failed."; + return false; } - return rv; + decoded_server_auth_token_ = decoded_auth_token; + return true; } -int HttpAuthSSPI::GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const GURL& origin, - const HttpRequestInfo* request, - const ProxyInfo* proxy, - std::string* out_credentials) { - // |username| may be in the form "DOMAIN\user". Parse it into the two - // components. - std::wstring domain; - std::wstring user; - SplitDomainAndUser(username, &domain, &user); +int HttpAuthSSPI::GenerateAuthToken(const std::wstring* username, + const std::wstring* password, + const GURL& origin, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + DCHECK((username == NULL) == (password == NULL)); // Initial challenge. if (!IsFinalRound()) { - int rv = OnFirstRound(domain, user, password); + int rv = OnFirstRound(username, password); if (rv != OK) return rv; } @@ -114,23 +193,38 @@ int HttpAuthSSPI::GenerateCredentials(const std::wstring& username, // Base64 encode data in output buffer and prepend the scheme. std::string encode_input(static_cast<char*>(out_buf), out_buf_len); std::string encode_output; - bool ok = base::Base64Encode(encode_input, &encode_output); + bool base64_rv = base::Base64Encode(encode_input, &encode_output); // OK, we are done with |out_buf| free(out_buf); - if (!ok) - return rv; - *out_credentials = scheme_ + " " + encode_output; + if (!base64_rv) { + LOG(ERROR) << "Base64 encoding of auth token failed."; + return ERR_UNEXPECTED; + } + *auth_token = scheme_ + " " + encode_output; return OK; } -int HttpAuthSSPI::OnFirstRound(const std::wstring& domain, - const std::wstring& user, - const std::wstring& password) { +int HttpAuthSSPI::OnFirstRound(const std::wstring* username, + const std::wstring* password) { + DCHECK((username == NULL) == (password == NULL)); + int rv = DetermineMaxTokenLength(security_package_, &max_token_length_); - if (rv != OK) { + if (rv != OK) return rv; + + if (username) { + std::wstring domain; + std::wstring user; + SplitDomainAndUser(*username, &domain, &user); + rv = AcquireCredentials(security_package_, domain, user, *password, &cred_); + if (rv != OK) + return rv; + } else { + rv = AcquireDefaultCredentials(security_package_, &cred_); + if (rv != OK) + return rv; } - rv = AcquireCredentials(security_package_, domain, user, password, &cred_); + return rv; } @@ -204,7 +298,7 @@ int HttpAuthSSPI::GetNextSecurityToken( // and SEC_E_OK on the second call. On failure, the function returns an // error code. if (status != SEC_I_CONTINUE_NEEDED && status != SEC_E_OK) { - LOG(ERROR) << "InitializeSecurityContext failed: " << status; + LOG(ERROR) << "InitializeSecurityContext failed " << status; ResetSecurityContext(); free(out_buffer.pvBuffer); return ERR_UNEXPECTED; // TODO(wtc): map error code. @@ -221,6 +315,9 @@ int HttpAuthSSPI::GetNextSecurityToken( void SplitDomainAndUser(const std::wstring& combined, std::wstring* domain, std::wstring* user) { + // |combined| may be in the form "user" or "DOMAIN\user". + // Separatethe two parts if they exist. + // TODO(cbentzel): I believe user@domain is also a valid form. size_t backslash_idx = combined.find(L'\\'); if (backslash_idx == std::wstring::npos) { domain->clear(); @@ -233,54 +330,36 @@ void SplitDomainAndUser(const std::wstring& combined, int DetermineMaxTokenLength(const std::wstring& package, ULONG* max_token_length) { - PSecPkgInfo pkg_info; + PSecPkgInfo pkg_info = NULL; SECURITY_STATUS status = QuerySecurityPackageInfo( const_cast<wchar_t *>(package.c_str()), &pkg_info); if (status != SEC_E_OK) { - LOG(ERROR) << "Security package " << package << " not found"; - return ERR_UNEXPECTED; + // The documentation at + // http://msdn.microsoft.com/en-us/library/aa379359(VS.85).aspx + // only mentions that a non-zero (or non-SEC_E_OK) value is returned + // if the function fails. In practice, it appears to return + // SEC_E_SECPKG_NOT_FOUND for invalid/unknown packages. + LOG(ERROR) << "Security package " << package << " not found." + << " Status code: " << status; + if (status == SEC_E_SECPKG_NOT_FOUND) + return ERR_UNSUPPORTED_AUTH_SCHEME; + else + return ERR_UNEXPECTED; } *max_token_length = pkg_info->cbMaxToken; - FreeContextBuffer(pkg_info); - return OK; -} - -int AcquireCredentials(const SEC_WCHAR* package, - const std::wstring& domain, - const std::wstring& user, - const std::wstring& password, - CredHandle* cred) { - SEC_WINNT_AUTH_IDENTITY identity; - identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - identity.User = - reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str())); - identity.UserLength = user.size(); - identity.Domain = - reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str())); - identity.DomainLength = domain.size(); - identity.Password = - reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str())); - identity.PasswordLength = password.size(); - - TimeStamp expiry; - - // Pass the username/password to get the credentials handle. - // Note: If the 5th argument is NULL, it uses the default cached credentials - // for the logged in user, which can be used for single sign-on. - SECURITY_STATUS status = AcquireCredentialsHandle( - NULL, // pszPrincipal - const_cast<SEC_WCHAR*>(package), // pszPackage - SECPKG_CRED_OUTBOUND, // fCredentialUse - NULL, // pvLogonID - &identity, // pAuthData - NULL, // pGetKeyFn (not used) - NULL, // pvGetKeyArgument (not used) - cred, // phCredential - &expiry); // ptsExpiry - - if (status != SEC_E_OK) + status = FreeContextBuffer(pkg_info); + if (status != SEC_E_OK) { + // The documentation at + // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx + // only mentions that a non-zero (or non-SEC_E_OK) value is returned + // if the function fails, and does not indicate what the failure conditions + // are. + LOG(ERROR) << "Unexpected problem freeing context buffer. Status code: " + << status; return ERR_UNEXPECTED; + } return OK; } + } // namespace net diff --git a/net/http/http_auth_sspi_win.h b/net/http/http_auth_sspi_win.h index 4d340aa..b94ffbc 100755..100644 --- a/net/http/http_auth_sspi_win.h +++ b/net/http/http_auth_sspi_win.h @@ -35,17 +35,22 @@ class HttpAuthSSPI { bool ParseChallenge(std::string::const_iterator challenge_begin, std::string::const_iterator challenge_end); - int GenerateCredentials(const std::wstring& username, - const std::wstring& password, - const GURL& origin, - const HttpRequestInfo* request, - const ProxyInfo* proxy, - std::string* out_credentials); + // Generates an authentication token. + // The return value is an error code. If it's not |OK|, the value of + // |*auth_token| is unspecified. + // If this is the first round of a multiple round scheme, credentials are + // obtained using |*username| and |*password|. If |username| and |password| + // are NULL, the default credentials are used instead. + int GenerateAuthToken(const std::wstring* username, + const std::wstring* password, + const GURL& origin, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token); private: - int OnFirstRound(const std::wstring& domain, - const std::wstring& user, - const std::wstring& password); + int OnFirstRound(const std::wstring* username, + const std::wstring* password); int GetNextSecurityToken( const GURL& origin, @@ -80,13 +85,6 @@ void SplitDomainAndUser(const std::wstring& combined, int DetermineMaxTokenLength(const std::wstring& package, ULONG* max_token_length); -// Acquire credentials for a user. -int AcquireCredentials(const SEC_WCHAR* package, - const std::wstring& domain, - const std::wstring& user, - const std::wstring& password, - CredHandle* cred); - } // namespace net #endif // NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index a960d70..a960d70 100755..100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 300482f..d89a84a 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -1609,14 +1609,21 @@ std::string HttpNetworkTransaction::BuildAuthorizationHeader( DCHECK(HaveAuth(target)); // Add a Authorization/Proxy-Authorization header line. - std::string credentials = auth_handler_[target]->GenerateCredentials( + std::string auth_token; + int rv = auth_handler_[target]->GenerateAuthToken( auth_identity_[target].username, auth_identity_[target].password, request_, - &proxy_info_); - - return HttpAuth::GetAuthorizationHeaderName(target) + - ": " + credentials + "\r\n"; + &proxy_info_, + &auth_token); + if (rv == OK) + return HttpAuth::GetAuthorizationHeaderName(target) + + ": " + auth_token + "\r\n"; + + // TODO(cbentzel): Evict username and password from cache on non-OK return? + // TODO(cbentzel): Never use this scheme again if + // ERR_UNSUPPORTED_AUTH_SCHEME is returned. + return std::string(); } GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const { diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc index 2a01f17..aae234e 100644 --- a/net/socket_stream/socket_stream.cc +++ b/net/socket_stream/socket_stream.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -556,14 +556,21 @@ int SocketStream::DoWriteTunnelHeaders() { // HttpRequestInfo. // TODO(ukai): Add support other authentication scheme. if (auth_handler_.get() && auth_handler_->scheme() == "basic") { - std::string credentials = auth_handler_->GenerateCredentials( + std::string auth_token; + int rv = auth_handler_->GenerateAuthToken( auth_identity_.username, auth_identity_.password, NULL, - &proxy_info_); + &proxy_info_, + &auth_token); + // TODO(cbentzel): Should do something different if credentials + // can't be generated. In this case, only Basic is allowed which + // should only fail if Base64 encoding fails. + if (rv != OK) + auth_token = std::string(); authorization_headers.append( HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_PROXY) + - ": " + credentials + "\r\n"); + ": " + auth_token + "\r\n"); } tunnel_request_headers_->headers_ = StringPrintf( |