diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 21:07:45 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 21:07:45 +0000 |
commit | eb3cac713b9d8d6f1f12f55df9e05c56ba64af41 (patch) | |
tree | 7c4b924b753067f1800ea48ae1f3a47b587aa3bf /net/http | |
parent | 71bc0d0d454034744c1657e24edae9d31b569d66 (diff) | |
download | chromium_src-eb3cac713b9d8d6f1f12f55df9e05c56ba64af41.zip chromium_src-eb3cac713b9d8d6f1f12f55df9e05c56ba64af41.tar.gz chromium_src-eb3cac713b9d8d6f1f12f55df9e05c56ba64af41.tar.bz2 |
Added HttpAuthFilter.
Original patch by ahendrickson@chromium.org (http://codereview.chromium.org/646068)
BUG=29596
TEST=net_unittests.exe --gtest_filter="*HttpAuthFilterTest*"
Review URL: http://codereview.chromium.org/660193
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40157 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/http_auth_filter.cc | 50 | ||||
-rw-r--r-- | net/http/http_auth_filter.h | 63 | ||||
-rw-r--r-- | net/http/http_auth_filter_unittest.cc | 86 | ||||
-rw-r--r-- | net/http/http_auth_handler_factory.cc | 28 | ||||
-rw-r--r-- | net/http/http_auth_handler_factory.h | 45 | ||||
-rw-r--r-- | net/http/http_auth_handler_factory_unittest.cc | 145 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate_win.cc | 10 | ||||
-rw-r--r-- | net/http/http_auth_handler_ntlm_portable.cc | 7 | ||||
-rw-r--r-- | net/http/http_auth_handler_ntlm_win.cc | 7 | ||||
-rw-r--r-- | net/http/http_auth_unittest.cc | 214 |
10 files changed, 646 insertions, 9 deletions
diff --git a/net/http/http_auth_filter.cc b/net/http/http_auth_filter.cc new file mode 100644 index 0000000..a75aa727 --- /dev/null +++ b/net/http/http_auth_filter.cc @@ -0,0 +1,50 @@ +// 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 "net/http/http_auth_filter.h" + +#include "base/string_util.h" +#include "googleurl/src/gurl.h" + +namespace net { + +HttpAuthFilterWhitelist::HttpAuthFilterWhitelist() { +} + +HttpAuthFilterWhitelist::~HttpAuthFilterWhitelist() { +} + +bool HttpAuthFilterWhitelist::SetFilters(const std::string& server_whitelist) { + // Parse the input string using commas as separators. + rules_.ParseFromString(server_whitelist); + return true; +} + +bool HttpAuthFilterWhitelist::IsValid(const GURL& url, + HttpAuth::Target target) const { + if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY)) + return false; + // All proxies pass + if (target == HttpAuth::AUTH_PROXY) + return true; + return rules_.Matches(url); +} + +// Add a new domain |filter| to the whitelist, if it's not already there +bool HttpAuthFilterWhitelist::AddFilter(const std::string& filter, + HttpAuth::Target target) { + if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY)) + return false; + // All proxies pass + if (target == HttpAuth::AUTH_PROXY) + return true; + rules_.AddRuleFromString(filter); + return true; +} + +void HttpAuthFilterWhitelist::AddRuleToBypassLocal() { + rules_.AddRuleToBypassLocal(); +} + +} // namespace net diff --git a/net/http/http_auth_filter.h b/net/http/http_auth_filter.h new file mode 100644 index 0000000..49fbe96 --- /dev/null +++ b/net/http/http_auth_filter.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef NET_HTTP_HTTP_AUTH_FILTER_H_ +#define NET_HTTP_HTTP_AUTH_FILTER_H_ + +#include <string> +#include <vector> + +#include "net/http/http_auth.h" +#include "net/proxy/proxy_bypass_rules.h" + +class GURL; + +namespace net { + +// |HttpAuthFilter|s determine whether an authentication scheme should be +// allowed for a particular peer. +class HttpAuthFilter { + public: + virtual ~HttpAuthFilter() {} + + // Checks if (|url|, |target|) is supported by the authentication scheme. + // Only the host of |url| is examined. + virtual bool IsValid(const GURL& url, HttpAuth::Target target) const = 0; +}; + +// Whitelist HTTP authentication filter. +// Explicit whitelists of domains are set via SetFilters(). +// +// Uses the ProxyBypassRules class to do whitelisting for servers. +// All proxies are allowed. +class HttpAuthFilterWhitelist : public HttpAuthFilter { + public: + HttpAuthFilterWhitelist(); + virtual ~HttpAuthFilterWhitelist(); + + // Checks if (|url|, |target|) is supported by the authentication scheme. + // Only the host of |url| is examined. + bool IsValid(const GURL& url, HttpAuth::Target target) const; + + // Installs the whitelist filters. + // |server_whitelist| is parsed by ProxyBypassRules. + bool SetFilters(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); + + // Adds a rule that bypasses all "local" hostnames. + void AddRuleToBypassLocal(); + + const ProxyBypassRules& rules() const { return rules_; } + + private: + ProxyBypassRules rules_; + + DISALLOW_COPY_AND_ASSIGN(HttpAuthFilterWhitelist); +}; + +} // namespace net + +#endif // NET_HTTP_HTTP_AUTH_FILTER_H_ diff --git a/net/http/http_auth_filter_unittest.cc b/net/http/http_auth_filter_unittest.cc new file mode 100644 index 0000000..a2ffb51 --- /dev/null +++ b/net/http/http_auth_filter_unittest.cc @@ -0,0 +1,86 @@ +// 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 <iostream> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" +#include "net/http/http_auth_filter.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +struct UrlData { + GURL url; + HttpAuth::Target target; + bool matches; +}; + +static UrlData urls[] = { + { GURL(""), + HttpAuth::AUTH_NONE, false }, + { GURL("http://foo.cn"), + HttpAuth::AUTH_PROXY, true }, + { GURL("http://foo.cn"), + HttpAuth::AUTH_SERVER, false }, + { GURL("http://slashdot.org"), + HttpAuth::AUTH_NONE, false }, + { GURL("http://www.google.com"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://www.google.com"), + HttpAuth::AUTH_PROXY, true }, + { GURL("https://login.facebook.com/login.php?login_attempt=1"), + HttpAuth::AUTH_NONE, false }, + { GURL("http://codereview.chromium.org/634002/show"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://code.google.com/p/chromium/issues/detail?id=34505"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://code.google.com/p/chromium/issues/list?can=2&q=label:" + "spdy&sort=owner&colspec=ID%20Stars%20Pri%20Area%20Type%20Status%20" + "Summary%20Modified%20Owner%20Mstone%20OS"), + HttpAuth::AUTH_SERVER, true }, + { GURL("https://www.linkedin.com/secure/login?trk=hb_signin"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://www.linkedin.com/mbox?displayMBoxItem=&" + "itemID=I1717980652_2&trk=COMM_HP_MSGVW_MEBC_MEBC&goback=.hom"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://news.slashdot.org/story/10/02/18/190236/" + "New-Plan-Lets-Top-HS-Students-Graduate-2-Years-Early"), + HttpAuth::AUTH_PROXY, true }, + { GURL("http://codereview.chromium.org/646068/diff/4001/5003"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://codereview.chromium.gag/646068/diff/4001/5003"), + HttpAuth::AUTH_SERVER, true }, + { GURL("http://codereview.chromium.gog/646068/diff/4001/5003"), + HttpAuth::AUTH_SERVER, true }, +}; + +} // namespace + +TEST(HttpAuthFilterTest, EmptyFilter) { + // Create an empty 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)) + << " " << i << ": " << urls[i].url; + } +} + +TEST(HttpAuthFilterTest, NonEmptyFilter) { + // Create an non-empty filter + HttpAuthFilterWhitelist filter; + std::string server_filter = + "*google.com,*linkedin.com,*book.com,*.chromium.org,*.gag,*gog"; + filter.SetFilters(server_filter); + 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; + } +} + +} // namespace net diff --git a/net/http/http_auth_handler_factory.cc b/net/http/http_auth_handler_factory.cc index 36f1fcf..de7ef59 100644 --- a/net/http/http_auth_handler_factory.cc +++ b/net/http/http_auth_handler_factory.cc @@ -7,6 +7,7 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler_basic.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_auth_handler_negotiate.h" @@ -24,7 +25,7 @@ int HttpAuthHandlerFactory::CreateAuthHandlerFromString( } // static -HttpAuthHandlerFactory* HttpAuthHandlerFactory::CreateDefault() { +HttpAuthHandlerRegistryFactory* HttpAuthHandlerFactory::CreateDefault() { HttpAuthHandlerRegistryFactory* registry_factory = new HttpAuthHandlerRegistryFactory(); registry_factory->RegisterSchemeFactory( @@ -46,6 +47,21 @@ HttpAuthHandlerRegistryFactory::~HttpAuthHandlerRegistryFactory() { factory_map_.end()); } +void HttpAuthHandlerRegistryFactory::SetFilter(const std::string& scheme, + HttpAuthFilter* filter) { + HttpAuthHandlerFactory* factory = GetSchemeFactory(scheme); + if (factory) + factory->set_filter(filter); +} + +const HttpAuthFilter* HttpAuthHandlerRegistryFactory::GetFilter( + const std::string& scheme) const { + HttpAuthHandlerFactory* factory = GetSchemeFactory(scheme); + if (factory) + return factory->filter(); + return NULL; +} + void HttpAuthHandlerRegistryFactory::RegisterSchemeFactory( const std::string& scheme, HttpAuthHandlerFactory* factory) { @@ -79,4 +95,14 @@ int HttpAuthHandlerRegistryFactory::CreateAuthHandler( return it->second->CreateAuthHandler(challenge, target, origin, handler); } +HttpAuthHandlerFactory* HttpAuthHandlerRegistryFactory::GetSchemeFactory( + const std::string& scheme) const { + std::string lower_scheme = StringToLowerASCII(scheme); + FactoryMap::const_iterator it = factory_map_.find(lower_scheme); + if (it == factory_map_.end()) { + return NULL; // |scheme| is not registered. + } + return it->second; +} + } // namespace net diff --git a/net/http/http_auth_handler_factory.h b/net/http/http_auth_handler_factory.h index d0fec29..f677455 100644 --- a/net/http/http_auth_handler_factory.h +++ b/net/http/http_auth_handler_factory.h @@ -2,18 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H -#define NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H +#ifndef NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_ +#define NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_ #include <map> +#include <string> +#include "base/scoped_ptr.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_filter.h" class GURL; namespace net { class HttpAuthHandler; +class HttpAuthHandlerRegistryFactory; // An HttpAuthHandlerFactory is used to create HttpAuthHandler objects. class HttpAuthHandlerFactory { @@ -21,6 +25,16 @@ class HttpAuthHandlerFactory { HttpAuthHandlerFactory() {} virtual ~HttpAuthHandlerFactory() {} + // Sets an authentication filter. + void set_filter(HttpAuthFilter* filter) { + filter_.reset(filter); + } + + // Retrieves the associated authentication filter. + const HttpAuthFilter* filter() const { + return filter_.get(); + } + // Creates an HttpAuthHandler object based on the authentication // challenge specified by |*challenge|. |challenge| must point to a valid // non-NULL tokenizer. @@ -34,6 +48,11 @@ class HttpAuthHandlerFactory { // If |*challenge| is improperly formed, |*handler| is set to NULL and // ERR_INVALID_RESPONSE is returned. // + // For the NTLM and Negotiate handlers: + // If |origin| does not match the authentication method's filters for + // the specified |target|, ERR_INVALID_AUTH_CREDENTIALS is returned. + // NOTE: This will apply to ALL |origin| values if the filters are empty. + // // |*challenge| should not be reused after a call to |CreateAuthHandler()|, virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, @@ -50,12 +69,15 @@ class HttpAuthHandlerFactory { const GURL& origin, scoped_refptr<HttpAuthHandler>* handler); - // Creates a standard HttpAuthHandlerFactory. The caller is responsible - // for deleting the factory. - // The default factory support Basic, Digest, NTLM, and Negotiate schemes. - static HttpAuthHandlerFactory* CreateDefault(); + // Creates a standard HttpAuthHandlerRegistryFactory. The caller is + // responsible for deleting the factory. + // The default factory supports Basic, Digest, NTLM, and Negotiate schemes. + static HttpAuthHandlerRegistryFactory* CreateDefault(); private: + // The authentication filter + scoped_ptr<HttpAuthFilter> filter_; + DISALLOW_COPY_AND_ASSIGN(HttpAuthHandlerFactory); }; @@ -66,6 +88,12 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory { HttpAuthHandlerRegistryFactory(); virtual ~HttpAuthHandlerRegistryFactory(); + // Sets an authentication filter into the factory associated with |scheme|. + void SetFilter(const std::string& scheme, HttpAuthFilter* filter); + + // Retrieves the authentication filter associated with |scheme|. + const HttpAuthFilter* GetFilter(const std::string& scheme) const; + // Registers a |factory| that will be used for a particular HTTP // authentication scheme such as Basic, Digest, or Negotiate. // The |*factory| object is assumed to be new-allocated, and its lifetime @@ -85,6 +113,9 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory { scoped_refptr<HttpAuthHandler>* handler); private: + // Retrieve the factory for the specified |scheme| + HttpAuthHandlerFactory* GetSchemeFactory(const std::string& scheme) const; + typedef std::map<std::string, HttpAuthHandlerFactory*> FactoryMap; FactoryMap factory_map_; @@ -93,4 +124,4 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory { } // namespace net -#endif // NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H +#endif // NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_ diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc index e67bcca..6bbc7b8 100644 --- a/net/http/http_auth_handler_factory_unittest.cc +++ b/net/http/http_auth_handler_factory_unittest.cc @@ -137,7 +137,114 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { server_origin, &handler); EXPECT_EQ(OK, rv); + ASSERT_FALSE(handler.get() == NULL); + EXPECT_STREQ("ntlm", handler->scheme().c_str()); + EXPECT_STREQ("", handler->realm().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); + EXPECT_TRUE(handler->encrypts_identity()); + EXPECT_TRUE(handler->is_connection_based()); + } +#if defined(OS_WIN) + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Negotiate", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(OK, rv); EXPECT_FALSE(handler.get() == NULL); + EXPECT_STREQ("negotiate", handler->scheme().c_str()); + EXPECT_STREQ("", handler->realm().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); + EXPECT_TRUE(handler->encrypts_identity()); + EXPECT_TRUE(handler->is_connection_based()); + } +#else // !defined(OS_WIN) + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Negotiate", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv); + EXPECT_TRUE(handler.get() == NULL); + } +#endif // !defined(OS_WIN) +} + +TEST(HttpAuthHandlerFactoryTest, DefaultFactoryWithFilters) { + std::string ntlm_server_whitelist = "*example.com"; + std::string negotiate_server_whitelist = "*example.com"; + std::string ntlm_server_whitelist2 = "*example.org"; + std::string negotiate_server_whitelist2 = "*example.org"; + + HttpAuthHandlerRegistryFactory* http_auth_handler_registry_factory = + HttpAuthHandlerFactory::CreateDefault(); + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory( + http_auth_handler_registry_factory); + HttpAuthFilterWhitelist* ntlm_whitelist = new HttpAuthFilterWhitelist; + HttpAuthFilterWhitelist* negotiate_whitelist = new HttpAuthFilterWhitelist; + + ntlm_whitelist->SetFilters(ntlm_server_whitelist); + negotiate_whitelist->SetFilters(negotiate_server_whitelist); + + http_auth_handler_registry_factory->SetFilter("ntlm", ntlm_whitelist); + http_auth_handler_registry_factory->SetFilter("negotiate", + negotiate_whitelist); + + GURL server_origin("http://www.example.com"); + GURL proxy_origin("http://cache.example.com:3128"); + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Basic realm=\"FooBar\"", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(handler.get() == NULL); + EXPECT_STREQ("basic", handler->scheme().c_str()); + EXPECT_STREQ("FooBar", handler->realm().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); + EXPECT_FALSE(handler->encrypts_identity()); + EXPECT_FALSE(handler->is_connection_based()); + } + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "UNSUPPORTED realm=\"FooBar\"", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv); + EXPECT_TRUE(handler.get() == NULL); + } + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Digest realm=\"FooBar\", nonce=\"xyz\"", + HttpAuth::AUTH_PROXY, + proxy_origin, + &handler); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(handler.get() == NULL); + EXPECT_STREQ("digest", handler->scheme().c_str()); + EXPECT_STREQ("FooBar", handler->realm().c_str()); + EXPECT_EQ(HttpAuth::AUTH_PROXY, handler->target()); + EXPECT_TRUE(handler->encrypts_identity()); + EXPECT_FALSE(handler->is_connection_based()); + } + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "NTLM", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(OK, rv); + ASSERT_FALSE(handler.get() == NULL); EXPECT_STREQ("ntlm", handler->scheme().c_str()); EXPECT_STREQ("", handler->realm().c_str()); EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); @@ -172,6 +279,44 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { EXPECT_TRUE(handler.get() == NULL); } #endif // !defined(OS_WIN) + + // Now change the whitelist and expect failures. + ntlm_whitelist->SetFilters(ntlm_server_whitelist2); + negotiate_whitelist->SetFilters(negotiate_server_whitelist2); + + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "NTLM", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(ERR_INVALID_AUTH_CREDENTIALS, rv); + ASSERT_TRUE(handler.get() == NULL); + } +#if defined(OS_WIN) + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Negotiate", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(ERR_INVALID_AUTH_CREDENTIALS, rv); + ASSERT_TRUE(handler.get() == NULL); + } +#else // !defined(OS_WIN) + { + scoped_refptr<HttpAuthHandler> handler; + int rv = http_auth_handler_factory->CreateAuthHandlerFromString( + "Negotiate", + HttpAuth::AUTH_SERVER, + server_origin, + &handler); + EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv); + EXPECT_TRUE(handler.get() == NULL); + } +#endif // !defined(OS_WIN) } } // namespace net diff --git a/net/http/http_auth_handler_negotiate_win.cc b/net/http/http_auth_handler_negotiate_win.cc index ffa48ef9..c09beb0 100644 --- a/net/http/http_auth_handler_negotiate_win.cc +++ b/net/http/http_auth_handler_negotiate_win.cc @@ -4,7 +4,9 @@ #include "net/http/http_auth_handler_negotiate.h" +#include "base/logging.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_filter.h" namespace net { @@ -92,6 +94,14 @@ int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( scoped_refptr<HttpAuthHandler>* handler) { if (is_unsupported_) return ERR_UNSUPPORTED_AUTH_SCHEME; + if (filter() && !filter()->IsValid(origin, target)) { + LOG(INFO) << "URL " << origin + << "fails filter validation for authentication method " + << "Negotiate"; + + return ERR_INVALID_AUTH_CREDENTIALS; + } + if (max_token_length_ == 0) { int rv = DetermineMaxTokenLength(sspi_library_, NEGOSSP_NAME, &max_token_length_); diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc index b6c7d32..c1ecdca 100644 --- a/net/http/http_auth_handler_ntlm_portable.cc +++ b/net/http/http_auth_handler_ntlm_portable.cc @@ -730,6 +730,13 @@ int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( HttpAuth::Target target, const GURL& origin, scoped_refptr<HttpAuthHandler>* handler) { + if (filter() && !filter()->IsValid(origin, target)) { + LOG(INFO) << "URL " << origin + << "fails filter validation for authentication method " + << "NTLM"; + + return ERR_INVALID_AUTH_CREDENTIALS; + } // TODO(cbentzel): Move towards model of parsing in the factory // method and only constructing when valid. scoped_refptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNTLM()); diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc index 989f1db..2de3411 100644 --- a/net/http/http_auth_handler_ntlm_win.cc +++ b/net/http/http_auth_handler_ntlm_win.cc @@ -79,6 +79,13 @@ int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( scoped_refptr<HttpAuthHandler>* handler) { if (is_unsupported_) return ERR_UNSUPPORTED_AUTH_SCHEME; + if (filter() && !filter()->IsValid(origin, target)) { + LOG(INFO) << "URL " << origin + << "fails filter validation for authentication method " + << "NTLM"; + + return ERR_INVALID_AUTH_CREDENTIALS; + } if (max_token_length_ == 0) { int rv = DetermineMaxTokenLength(sspi_library_, NTLMSP_NAME, &max_token_length_); diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index 14308a0..70fc194 100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc @@ -6,7 +6,9 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/string_util.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_response_headers.h" @@ -70,7 +72,6 @@ TEST(HttpAuthTest, ChooseBestChallenge) { } }; GURL origin("http://www.example.com"); - scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory( HttpAuthHandlerFactory::CreateDefault()); @@ -154,6 +155,217 @@ TEST(HttpAuthTest, ChooseBestChallengeConnectionBased) { if (i != 0) EXPECT_EQ(old_handler, handler); + ASSERT_NE(reinterpret_cast<net::HttpAuthHandler *>(NULL), handler.get()); + + EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); + } +} + +TEST(HttpAuthTest, ChooseBestChallengeFiltered) { + static const struct { + const char* filter_string; + const char* headers; + const char* challenge_scheme; + const char* challenge_realm; + } tests[] = { + { + // Test that the filter does not affect Basic. + "*example.com", + "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" + "www-authenticate: Basic realm=\"BasicRealm\"\n", + + // Basic is the only challenge type, pick it. + "basic", + "BasicRealm", + }, + { + // Test that the filter does not affect Fake. + "*example.com", + "Y: Digest realm=\"FooBar\", nonce=\"aaaaaaaaaa\"\n" + "www-authenticate: Fake realm=\"FooBar\"\n", + + // Fake is the only challenge type, but it is unsupported. + "", + "", + }, + { + // Test that the filter does not affect Digest vs. Basic. + "*example.com", + "www-authenticate: Basic realm=\"FooBar\"\n" + "www-authenticate: Fake realm=\"FooBar\"\n" + "www-authenticate: nonce=\"aaaaaaaaaa\"\n" + "www-authenticate: Digest realm=\"DigestRealm\", nonce=\"aaaaaaaaaa\"\n", + + // Pick Digest over Basic. + "digest", + "DigestRealm", + }, + { + // Test that the filter does not affect null header. + "*example.com", + "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" + "www-authenticate:\n", + + // Handle null header value. + "", + "", + }, + { + // Test that the filter works with a valid whitelist. + "*example.com", + "WWW-Authenticate: Negotiate\n" + "WWW-Authenticate: NTLM\n", + + // Negotiate is not currently support on non-Windows platforms, so + // the choice varies depending on platform. +#if defined(OS_WIN) + "negotiate", + "", +#else + "ntlm", + "", +#endif + }, + { + // Test that fall back does not occur if NTLM is allowed by whitelist. + "*example.com", + "WWW-Authenticate: NTLM\n" + "www-authenticate: Digest realm=\"DigestRealm\", nonce=\"aaaaaaaaaa\"\n", + + "ntlm", + "", + }, + { + // Test that the filter prevents access if URL is not in whitelist. + "*example.org", + "WWW-Authenticate: Negotiate\n" + "WWW-Authenticate: NTLM\n", + + // Negotiate is not currently support on non-Windows platforms, so + // the choice varies depending on platform. + "", + "", + }, + { + // Test that fall back occurs if NTLM is not allowed by whitelist. + "*example.org", + "WWW-Authenticate: NTLM\n" + "www-authenticate: Digest realm=\"DigestRealm\", nonce=\"aaaaaaaaaa\"\n", + + "digest", + "DigestRealm", + } + }; + GURL origin("http://www.example.com"); + + HttpAuthHandlerRegistryFactory* http_auth_handler_registry_factory = + HttpAuthHandlerFactory::CreateDefault(); + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory( + http_auth_handler_registry_factory); + HttpAuthFilterWhitelist* ntlm_whitelist = new HttpAuthFilterWhitelist; + HttpAuthFilterWhitelist* negotiate_whitelist = new HttpAuthFilterWhitelist; + http_auth_handler_registry_factory->SetFilter("ntlm", ntlm_whitelist); + http_auth_handler_registry_factory->SetFilter("negotiate", + negotiate_whitelist); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + ntlm_whitelist->SetFilters(tests[i].filter_string); + negotiate_whitelist->SetFilters(tests[i].filter_string); + // Make a HttpResponseHeaders object. + std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n"); + headers_with_status_line += tests[i].headers; + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders( + net::HttpUtil::AssembleRawHeaders( + headers_with_status_line.c_str(), + headers_with_status_line.length()))); + + scoped_refptr<HttpAuthHandler> handler; + HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(), + headers.get(), + HttpAuth::AUTH_SERVER, + origin, + &handler); + + if (handler) { + EXPECT_STREQ(tests[i].challenge_scheme, handler->scheme().c_str()); + EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); + } else { + EXPECT_STREQ("", tests[i].challenge_scheme); + EXPECT_STREQ("", tests[i].challenge_realm); + } + } +} + +TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedFiltered) { + static const struct { + const char* headers; + const char* challenge_realm; + } tests[] = { + { + "WWW-Authenticate: NTLM\r\n", + + "", + }, + { + "WWW-Authenticate: NTLM " + "TlRMTVNTUAACAAAADAAMADgAAAAFgokCTroKF1e/DRcAAAAAAAAAALo" + "AugBEAAAABQEoCgAAAA9HAE8ATwBHAEwARQACAAwARwBPAE8ARwBMAE" + "UAAQAaAEEASwBFAEUAUwBBAFIAQQAtAEMATwBSAFAABAAeAGMAbwByA" + "HAALgBnAG8AbwBnAGwAZQAuAGMAbwBtAAMAQABhAGsAZQBlAHMAYQBy" + "AGEALQBjAG8AcgBwAC4AYQBkAC4AYwBvAHIAcAAuAGcAbwBvAGcAbAB" + "lAC4AYwBvAG0ABQAeAGMAbwByAHAALgBnAG8AbwBnAGwAZQAuAGMAbw" + "BtAAAAAAA=\r\n", + + // Realm is empty. + "", + } + }; + GURL origin("http://www.example.com"); + + scoped_refptr<HttpAuthHandler> handler; + std::string ntlm_server_whitelist = "*example.com"; + std::string negotiate_server_whitelist = "*example.com"; + + HttpAuthHandlerRegistryFactory* http_auth_handler_registry_factory = + HttpAuthHandlerFactory::CreateDefault(); + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory( + http_auth_handler_registry_factory); + HttpAuthFilterWhitelist* ntlm_whitelist = new HttpAuthFilterWhitelist; + HttpAuthFilterWhitelist* negotiate_whitelist = new HttpAuthFilterWhitelist; + + ntlm_whitelist->SetFilters(ntlm_server_whitelist); + negotiate_whitelist->SetFilters(negotiate_server_whitelist); + + http_auth_handler_registry_factory->SetFilter("ntlm", ntlm_whitelist); + http_auth_handler_registry_factory->SetFilter("negotiate", + negotiate_whitelist); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + // Make a HttpResponseHeaders object. + std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n"); + headers_with_status_line += tests[i].headers; + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders( + net::HttpUtil::AssembleRawHeaders( + headers_with_status_line.c_str(), + headers_with_status_line.length()))); + + scoped_refptr<HttpAuthHandler> old_handler = handler; + HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(), + headers.get(), + HttpAuth::AUTH_SERVER, + origin, + &handler); + + EXPECT_TRUE(handler != NULL); + // Since NTLM is connection-based, we should continue to use the existing + // handler rather than creating a new one. + if (i != 0) + EXPECT_EQ(old_handler, handler); + + ASSERT_NE(reinterpret_cast<net::HttpAuthHandler *>(NULL), handler.get()); + EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); } } |