diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-27 17:35:02 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-27 17:35:02 +0000 |
commit | d201b200e947d18ede55706197c62dbaeace8d5f (patch) | |
tree | 7413f58244f2c7eb3352284411f3b72a8287442d /net | |
parent | c58030205f55b793ec6cedbaa1967ccc4a9f3465 (diff) | |
download | chromium_src-d201b200e947d18ede55706197c62dbaeace8d5f.zip chromium_src-d201b200e947d18ede55706197c62dbaeace8d5f.tar.gz chromium_src-d201b200e947d18ede55706197c62dbaeace8d5f.tar.bz2 |
Add support for delegated kerberos tickets to Negotiate authentication.
This is controlled by the --auth-negotiate-delegate-whitelist command line. By default no servers are delegated to.
BUG=50076
TEST=net_unittests, go to an IIS server specified in --auth-negotiate-delegate-whitelist and see if this works correctly.
Review URL: http://codereview.chromium.org/3155046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57695 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_auth_filter.cc | 4 | ||||
-rw-r--r-- | net/http/http_auth_filter.h | 10 | ||||
-rw-r--r-- | net/http/http_auth_filter_unittest.cc | 5 | ||||
-rw-r--r-- | net/http/http_auth_gssapi_posix.cc | 9 | ||||
-rw-r--r-- | net/http/http_auth_gssapi_posix.h | 6 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate.cc | 11 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate.h | 3 | ||||
-rw-r--r-- | net/http/http_auth_sspi_win.cc | 28 | ||||
-rw-r--r-- | net/http/http_auth_sspi_win.h | 6 | ||||
-rw-r--r-- | net/http/url_security_manager.cc | 17 | ||||
-rw-r--r-- | net/http/url_security_manager.h | 46 | ||||
-rw-r--r-- | net/http/url_security_manager_posix.cc | 5 | ||||
-rw-r--r-- | net/http/url_security_manager_unittest.cc | 6 | ||||
-rw-r--r-- | net/http/url_security_manager_win.cc | 58 |
14 files changed, 157 insertions, 57 deletions
diff --git a/net/http/http_auth_filter.cc b/net/http/http_auth_filter.cc index 80d6e0c..a61e7f7 100644 --- a/net/http/http_auth_filter.cc +++ b/net/http/http_auth_filter.cc @@ -16,7 +16,9 @@ typedef std::set<string16> RegistryWhitelist; // entries in the registry mean that you are only allowed to connect to the site // via HTTPS and still be considered 'safe'. -HttpAuthFilterWhitelist::HttpAuthFilterWhitelist() { +HttpAuthFilterWhitelist::HttpAuthFilterWhitelist( + const std::string& server_whitelist) { + SetWhitelist(server_whitelist); } HttpAuthFilterWhitelist::~HttpAuthFilterWhitelist() { diff --git a/net/http/http_auth_filter.h b/net/http/http_auth_filter.h index 73d4148..334bc91 100644 --- a/net/http/http_auth_filter.h +++ b/net/http/http_auth_filter.h @@ -34,16 +34,12 @@ class HttpAuthFilter { // All proxies are allowed. class HttpAuthFilterWhitelist : public HttpAuthFilter { public: - HttpAuthFilterWhitelist(); + explicit HttpAuthFilterWhitelist(const std::string& server_whitelist); virtual ~HttpAuthFilterWhitelist(); // HttpAuthFilter methods: virtual bool IsValid(const GURL& url, HttpAuth::Target target) const; - // Installs the whitelist. - // |server_whitelist| is parsed by ProxyBypassRules. - void SetWhitelist(const std::string& server_whitelist); - // Adds an individual URL |filter| to the list, of the specified |target|. bool AddFilter(const std::string& filter, HttpAuth::Target target); @@ -53,6 +49,10 @@ class HttpAuthFilterWhitelist : public HttpAuthFilter { const ProxyBypassRules& rules() const { return rules_; } private: + // Installs the whitelist. + // |server_whitelist| is parsed by ProxyBypassRules. + void SetWhitelist(const std::string& server_whitelist); + // We are using ProxyBypassRules because they have the functionality that we // want, but we are not using it for proxy bypass. ProxyBypassRules rules_; diff --git a/net/http/http_auth_filter_unittest.cc b/net/http/http_auth_filter_unittest.cc index df61e14..f483342 100644 --- a/net/http/http_auth_filter_unittest.cc +++ b/net/http/http_auth_filter_unittest.cc @@ -78,7 +78,7 @@ static const UrlData urls[] = { TEST(HttpAuthFilterTest, EmptyFilter) { // Create an empty filter - HttpAuthFilterWhitelist filter; + HttpAuthFilterWhitelist filter(""); for (size_t i = 0; i < arraysize(urls); i++) { EXPECT_EQ(urls[i].target == HttpAuth::AUTH_PROXY, filter.IsValid(urls[i].url, urls[i].target)) @@ -88,7 +88,6 @@ TEST(HttpAuthFilterTest, EmptyFilter) { TEST(HttpAuthFilterTest, NonEmptyFilter) { // Create an non-empty filter - HttpAuthFilterWhitelist filter; std::string server_whitelist_filter_string; for (size_t i = 0; i < arraysize(server_whitelist_array); ++i) { if (!server_whitelist_filter_string.empty()) @@ -96,7 +95,7 @@ TEST(HttpAuthFilterTest, NonEmptyFilter) { server_whitelist_filter_string += "*"; server_whitelist_filter_string += server_whitelist_array[i]; } - filter.SetWhitelist(server_whitelist_filter_string); + HttpAuthFilterWhitelist filter(server_whitelist_filter_string); for (size_t i = 0; i < arraysize(urls); i++) { EXPECT_EQ(urls[i].matches, filter.IsValid(urls[i].url, urls[i].target)) << " " << i << ": " << urls[i].url; diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc index 444f882..aeed731 100644 --- a/net/http/http_auth_gssapi_posix.cc +++ b/net/http/http_auth_gssapi_posix.cc @@ -677,7 +677,8 @@ HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, : scheme_(scheme), gss_oid_(gss_oid), library_(library), - scoped_sec_context_(library) { + scoped_sec_context_(library), + can_delegate_(false) { DCHECK(library_); } @@ -698,6 +699,10 @@ bool HttpAuthGSSAPI::IsFinalRound() const { return !NeedsIdentity(); } +void HttpAuthGSSAPI::Delegate() { + can_delegate_ = true; +} + bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) { // Verify the challenge's auth-scheme. if (!tok->valid() || @@ -799,6 +804,8 @@ int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn, // Continue creating a security context. OM_uint32 req_flags = 0; + if (can_delegate_) + req_flags |= GSS_C_DELEG_FLAG; major_status = library_->init_sec_context( &minor_status, GSS_C_NO_CREDENTIAL, diff --git a/net/http/http_auth_gssapi_posix.h b/net/http/http_auth_gssapi_posix.h index 4748c13..4dddddb 100644 --- a/net/http/http_auth_gssapi_posix.h +++ b/net/http/http_auth_gssapi_posix.h @@ -240,6 +240,11 @@ class HttpAuthGSSAPI { const std::wstring& spn, std::string* auth_token); + // Delegation is allowed on the Kerberos ticket. This allows certain servers + // to act as the user, such as an IIS server retrieiving data from a + // Kerberized MSSQL server. + void Delegate(); + private: int OnFirstRound(const string16* username, const string16* password); @@ -254,6 +259,7 @@ class HttpAuthGSSAPI { GSSAPILibrary* library_; std::string decoded_server_auth_token_; ScopedSecurityContext scoped_sec_context_; + bool can_delegate_; }; } // namespace net diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc index 6986264..27e2571 100644 --- a/net/http/http_auth_handler_negotiate.cc +++ b/net/http/http_auth_handler_negotiate.cc @@ -90,6 +90,8 @@ bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { if (!AllowsDefaultCredentials()) return false; #endif + if (CanDelegate()) + auth_system_.Delegate(); scheme_ = "negotiate"; score_ = 4; properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; @@ -113,6 +115,15 @@ bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { return url_security_manager_->CanUseDefaultCredentials(origin_); } +bool HttpAuthHandlerNegotiate::CanDelegate() const { + // TODO(cbentzel): Should delegation be allowed on proxies? + if (target_ == HttpAuth::AUTH_PROXY) + return false; + if (!url_security_manager_) + return false; + return url_security_manager_->CanDelegate(origin_); +} + std::wstring HttpAuthHandlerNegotiate::CreateSPN( const AddressList& address_list, const GURL& origin) { // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h index 005e665..ca9a577 100644 --- a/net/http/http_auth_handler_negotiate.h +++ b/net/http/http_auth_handler_negotiate.h @@ -141,6 +141,7 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { int DoResolveCanonicalNameComplete(int rv); int DoGenerateAuthToken(); int DoGenerateAuthTokenComplete(int rv); + bool CanDelegate() const; AuthSystem auth_system_; bool disable_cname_lookup_; @@ -165,7 +166,7 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { State next_state_; - URLSecurityManager* url_security_manager_; + const URLSecurityManager* url_security_manager_; }; } // namespace net diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc index 9c195df..4431e28 100644 --- a/net/http/http_auth_sspi_win.cc +++ b/net/http/http_auth_sspi_win.cc @@ -108,7 +108,8 @@ HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library, : library_(library), scheme_(scheme), security_package_(security_package), - max_token_length_(max_token_length) { + max_token_length_(max_token_length), + can_delegate_(false) { DCHECK(library_); SecInvalidateHandle(&cred_); SecInvalidateHandle(&ctxt_); @@ -130,6 +131,10 @@ bool HttpAuthSSPI::IsFinalRound() const { return !decoded_server_auth_token_.empty(); } +void HttpAuthSSPI::Delegate() { + can_delegate_ = true; +} + void HttpAuthSSPI::ResetSecurityContext() { if (SecIsValidHandle(&ctxt_)) { library_->DeleteSecurityContext(&ctxt_); @@ -224,14 +229,10 @@ int HttpAuthSSPI::OnFirstRound(const string16* username, int HttpAuthSSPI::GetNextSecurityToken( const std::wstring& spn, - const void * in_token, + const void* in_token, int in_token_len, void** out_token, int* out_token_len) { - SECURITY_STATUS status; - TimeStamp expiry; - - DWORD ctxt_attr; CtxtHandle* ctxt_ptr; SecBufferDesc in_buffer_desc, out_buffer_desc; SecBufferDesc* in_buffer_desc_ptr; @@ -269,20 +270,27 @@ int HttpAuthSSPI::GetNextSecurityToken( if (!out_buffer.pvBuffer) return ERR_OUT_OF_MEMORY; + DWORD context_flags = 0; + // Firefox only sets ISC_REQ_DELEGATE, but MSDN documentation indicates that + // ISC_REQ_MUTUAL_AUTH must also be set. + if (can_delegate_) + context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH); + // This returns a token that is passed to the remote server. - status = library_->InitializeSecurityContext( + DWORD context_attribute; + SECURITY_STATUS status = library_->InitializeSecurityContext( &cred_, // phCredential ctxt_ptr, // phContext const_cast<wchar_t *>(spn.c_str()), // pszTargetName - 0, // fContextReq + context_flags, // fContextReq 0, // Reserved1 (must be 0) SECURITY_NATIVE_DREP, // TargetDataRep in_buffer_desc_ptr, // pInput 0, // Reserved2 (must be 0) &ctxt_, // phNewContext &out_buffer_desc, // pOutput - &ctxt_attr, // pfContextAttr - &expiry); // ptsExpiry + &context_attribute, // pfContextAttr + NULL); // ptsExpiry // On success, the function returns SEC_I_CONTINUE_NEEDED on the first call // and SEC_E_OK on the second call. On failure, the function returns an // error code. diff --git a/net/http/http_auth_sspi_win.h b/net/http/http_auth_sspi_win.h index 1c8b741..482ab6b 100644 --- a/net/http/http_auth_sspi_win.h +++ b/net/http/http_auth_sspi_win.h @@ -96,6 +96,11 @@ class HttpAuthSSPI { const std::wstring& spn, std::string* auth_token); + // Delegation is allowed on the Kerberos ticket. This allows certain servers + // to act as the user, such as an IIS server retrieiving data from a + // Kerberized MSSQL server. + void Delegate(); + private: int OnFirstRound(const string16* username, const string16* password); @@ -116,6 +121,7 @@ class HttpAuthSSPI { ULONG max_token_length_; CredHandle cred_; CtxtHandle ctxt_; + bool can_delegate_; }; // Splits |combined| into domain and username. diff --git a/net/http/url_security_manager.cc b/net/http/url_security_manager.cc index d3bc42e..d848644 100644 --- a/net/http/url_security_manager.cc +++ b/net/http/url_security_manager.cc @@ -9,13 +9,22 @@ namespace net { URLSecurityManagerWhitelist::URLSecurityManagerWhitelist( - HttpAuthFilter* whitelist) : whitelist_(whitelist) { + const HttpAuthFilter* whitelist_default, + const HttpAuthFilter* whitelist_delegate) + : whitelist_default_(whitelist_default), + whitelist_delegate_(whitelist_delegate) { } bool URLSecurityManagerWhitelist::CanUseDefaultCredentials( - const GURL& auth_origin) { - if (whitelist_.get()) - return whitelist_->IsValid(auth_origin, HttpAuth::AUTH_SERVER); + const GURL& auth_origin) const { + if (whitelist_default_.get()) + return whitelist_default_->IsValid(auth_origin, HttpAuth::AUTH_SERVER); + return false; +} + +bool URLSecurityManagerWhitelist::CanDelegate(const GURL& auth_origin) const { + if (whitelist_delegate_.get()) + return whitelist_delegate_->IsValid(auth_origin, HttpAuth::AUTH_SERVER); return false; } diff --git a/net/http/url_security_manager.h b/net/http/url_security_manager.h index 151a446..5e148b4 100644 --- a/net/http/url_security_manager.h +++ b/net/http/url_security_manager.h @@ -23,12 +23,34 @@ class URLSecurityManager { virtual ~URLSecurityManager() {} // Creates a platform-dependent instance of URLSecurityManager. - // The URLSecurityManager takes ownership of the HttpAuthFilter. - static URLSecurityManager* Create(HttpAuthFilter* whitelist); + // + // |whitelist_default| is the whitelist of servers that default credentials + // can be used with during NTLM or Negotiate authentication. If + // |whitelist_default| is NULL and the platform is Windows, it indicates + // that security zone mapping should be used to determine whether default + // credentials sxhould be used. If |whitelist_default| is NULL and the + // platform is non-Windows, it indicates that no servers should be + // whitelisted. + // + // |whitelist_delegate| is the whitelist of servers that are allowed + // to have Delegated Kerberos tickets. If |whitelist_delegate| is NULL, + // no servers can have delegated Kerberos tickets. + // + // Both |whitelist_default| and |whitelist_delegate| will be owned by + // the created URLSecurityManager. + // + // TODO(cbentzel): Perhaps it's better to make a non-abstract HttpAuthFilter + // and just copy into the URLSecurityManager? + static URLSecurityManager* Create(const HttpAuthFilter* whitelist_default, + const HttpAuthFilter* whitelist_delegate); // Returns true if we can send the default credentials to the server at // |auth_origin| for HTTP NTLM or Negotiate authentication. - virtual bool CanUseDefaultCredentials(const GURL& auth_origin) = 0; + virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const = 0; + + // Returns true if Kerberos delegation is allowed for the server at + // |auth_origin| for HTTP Negotiate authentication. + virtual bool CanDelegate(const GURL& auth_origin) const = 0; private: DISALLOW_COPY_AND_ASSIGN(URLSecurityManager); @@ -36,26 +58,32 @@ class URLSecurityManager { class URLSecurityManagerWhitelist : public URLSecurityManager { public: - // The URLSecurityManagerWhitelist takes ownership of the HttpAuthFilter. - explicit URLSecurityManagerWhitelist(HttpAuthFilter* whitelist); + // The URLSecurityManagerWhitelist takes ownership of the whitelists. + URLSecurityManagerWhitelist(const HttpAuthFilter* whitelist_default, + const HttpAuthFilter* whitelist_delegation); // URLSecurityManager methods. - virtual bool CanUseDefaultCredentials(const GURL& auth_origin); + virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const; + virtual bool CanDelegate(const GURL& auth_origin) const; private: - scoped_ptr<HttpAuthFilter> whitelist_; + scoped_ptr<const HttpAuthFilter> whitelist_default_; + scoped_ptr<const HttpAuthFilter> whitelist_delegate_; DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWhitelist); }; #if defined(UNIT_TEST) -// An URLSecurityManager which always allows default credentials. +// An URLSecurityManager which is very permissive. class URLSecurityManagerAllow : public URLSecurityManager { public: URLSecurityManagerAllow() {} virtual ~URLSecurityManagerAllow() {} - virtual bool CanUseDefaultCredentials(const GURL& auth_origin) { + virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const { + return true; + } + virtual bool CanDelegate(const GURL& auth_origin) const { return true; } diff --git a/net/http/url_security_manager_posix.cc b/net/http/url_security_manager_posix.cc index 931d9cc..d3b42fb 100644 --- a/net/http/url_security_manager_posix.cc +++ b/net/http/url_security_manager_posix.cc @@ -10,8 +10,9 @@ namespace net { // static URLSecurityManager* URLSecurityManager::Create( - HttpAuthFilter* whitelist) { - return new URLSecurityManagerWhitelist(whitelist); + const HttpAuthFilter* whitelist_default, + const HttpAuthFilter* whitelist_delegate) { + return new URLSecurityManagerWhitelist(whitelist_default, whitelist_delegate); } } // namespace net diff --git a/net/http/url_security_manager_unittest.cc b/net/http/url_security_manager_unittest.cc index 7c52efd..f83bb47 100644 --- a/net/http/url_security_manager_unittest.cc +++ b/net/http/url_security_manager_unittest.cc @@ -44,12 +44,12 @@ const TestData kTestDataList[] = { } // namespace TEST(URLSecurityManager, CreateWhitelist) { - HttpAuthFilterWhitelist* auth_filter = new HttpAuthFilterWhitelist(); + HttpAuthFilterWhitelist* auth_filter = new HttpAuthFilterWhitelist( + kTestAuthWhitelist); ASSERT_TRUE(auth_filter); - auth_filter->SetWhitelist(kTestAuthWhitelist); // The URL security manager takes ownership of |auth_filter|. scoped_ptr<URLSecurityManager> url_security_manager( - URLSecurityManager::Create(auth_filter)); + URLSecurityManager::Create(auth_filter, NULL)); ASSERT_TRUE(url_security_manager.get()); for (size_t i = 0; i < arraysize(kTestDataList); ++i) { diff --git a/net/http/url_security_manager_win.cc b/net/http/url_security_manager_win.cc index 76250d0..1770a19 100644 --- a/net/http/url_security_manager_win.cc +++ b/net/http/url_security_manager_win.cc @@ -11,6 +11,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "googleurl/src/gurl.h" +#include "net/http/http_auth_filter.h" // The Windows implementation of URLSecurityManager uses WinINet/IE's // URL security zone manager. See the MSDN page "URL Security Zones" at @@ -27,32 +28,30 @@ namespace net { class URLSecurityManagerWin : public URLSecurityManager { public: - URLSecurityManagerWin(); + explicit URLSecurityManagerWin(const HttpAuthFilter* whitelist_delegate); // URLSecurityManager methods: - virtual bool CanUseDefaultCredentials(const GURL& auth_origin); + virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const; + virtual bool CanDelegate(const GURL& auth_origin) const; private: + bool EnsureSystemSecurityManager(); + ScopedComPtr<IInternetSecurityManager> security_manager_; + scoped_ptr<const HttpAuthFilter> whitelist_delegate_; DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWin); }; -URLSecurityManagerWin::URLSecurityManagerWin() { +URLSecurityManagerWin::URLSecurityManagerWin( + const HttpAuthFilter* whitelist_delegate) + : whitelist_delegate_(whitelist_delegate) { } - bool URLSecurityManagerWin::CanUseDefaultCredentials( - const GURL& auth_origin) { - if (!security_manager_) { - HRESULT hr = CoInternetCreateSecurityManager(NULL, - security_manager_.Receive(), - NULL); - if (FAILED(hr) || !security_manager_) { - LOG(ERROR) << "Unable to create the Windows Security Manager instance"; - return false; - } - } + const GURL& auth_origin) const { + if (!const_cast<URLSecurityManagerWin*>(this)->EnsureSystemSecurityManager()) + return false; std::wstring url_w = ASCIIToWide(auth_origin.spec()); DWORD policy = 0; @@ -103,13 +102,36 @@ bool URLSecurityManagerWin::CanUseDefaultCredentials( } } +bool URLSecurityManagerWin::CanDelegate(const GURL& auth_origin) const { + // TODO(cbentzel): Could this just use the security zone as well? Apparently + // this is what IE does as well. + if (whitelist_delegate_.get()) + return whitelist_delegate_->IsValid(auth_origin, HttpAuth::AUTH_SERVER); + return false; +} + +bool URLSecurityManagerWin::EnsureSystemSecurityManager() { + if (!security_manager_) { + HRESULT hr = CoInternetCreateSecurityManager(NULL, + security_manager_.Receive(), + NULL); + if (FAILED(hr) || !security_manager_) { + LOG(ERROR) << "Unable to create the Windows Security Manager instance"; + return false; + } + } + return true; +} + // static URLSecurityManager* URLSecurityManager::Create( - HttpAuthFilter* whitelist) { + const HttpAuthFilter* whitelist_default, + const HttpAuthFilter* whitelist_delegate) { // If we have a whitelist, just use that. - if (whitelist) - return new URLSecurityManagerWhitelist(whitelist); - return new URLSecurityManagerWin(); + if (whitelist_default) + return new URLSecurityManagerWhitelist(whitelist_default, + whitelist_delegate); + return new URLSecurityManagerWin(whitelist_delegate); } } // namespace net |