diff options
-rw-r--r-- | chrome/browser/io_thread.cc | 30 | ||||
-rw-r--r-- | chrome/browser/io_thread.h | 2 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-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 | ||||
-rw-r--r-- | net/net.gyp | 3 |
15 files changed, 683 insertions, 11 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index f4d4698..cda63ef 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -17,6 +17,7 @@ #include "net/base/host_resolver_impl.h" #include "net/base/net_util.h" #include "net/base/network_change_notifier.h" +#include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler_factory.h" #include "net/url_request/url_request.h" @@ -127,8 +128,7 @@ void IOThread::Init() { net::NetworkChangeNotifier::CreateDefaultNetworkChangeNotifier()); globals_->host_resolver = CreateGlobalHostResolver(globals_->network_change_notifier.get()); - globals_->http_auth_handler_factory.reset( - net::HttpAuthHandlerFactory::CreateDefault()); + globals_->http_auth_handler_factory.reset(CreateDefaultAuthHandlerFactory()); } void IOThread::CleanUp() { @@ -176,6 +176,32 @@ void IOThread::CleanUp() { BrowserProcessSubThread::CleanUp(); } +net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory() { + net::HttpAuthHandlerRegistryFactory* registry_factory = + net::HttpAuthHandlerFactory::CreateDefault(); + + // Get the whitelist information from the command line, create an + // HttpAuthFilterWhitelist, and attach it to the HttpAuthHandlerFactory. + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + // Set the NTLM and Negotiate filters (from the same whitelist) + if (command_line.HasSwitch(switches::kAuthServerWhitelist)) { + std::string ntlm_server_whitelist = + command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist); + net::HttpAuthFilterWhitelist* ntlm_whitelist = + new net::HttpAuthFilterWhitelist(); + net::HttpAuthFilterWhitelist* negotiate_whitelist = + new net::HttpAuthFilterWhitelist(); + + ntlm_whitelist->SetFilters(ntlm_server_whitelist); + negotiate_whitelist->SetFilters(ntlm_server_whitelist); + registry_factory->SetFilter("ntlm", ntlm_whitelist); + registry_factory->SetFilter("negotiate", negotiate_whitelist); + } + + return registry_factory; +} + void IOThread::InitDnsMasterOnIOThread( bool prefetching_enabled, base::TimeDelta max_queue_delay, diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h index c0b9ccd..6de1bae 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -60,6 +60,8 @@ class IOThread : public BrowserProcessSubThread { virtual void CleanUp(); private: + net::HttpAuthHandlerFactory* CreateDefaultAuthHandlerFactory(); + void InitDnsMasterOnIOThread( bool prefetching_enabled, base::TimeDelta max_queue_delay, diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 505b07b..c521395 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -39,6 +39,9 @@ const char kAppId[] = "app-id"; // panel window. const char kAppLaunchAsPanel[] = "app-launch-as-panel"; +// Authentication white list for servers +const char kAuthServerWhitelist[] = "auth-server-whitelist"; + // The value of this switch tells the app to listen for and broadcast // automation-related messages on IPC channel with the given ID. const char kAutomationClientChannelID[] = "automation-channel"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 9af1ec9..0ce84a6 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -26,6 +26,7 @@ extern const char kAlwaysEnableDevTools[]; extern const char kApp[]; extern const char kAppId[]; extern const char kAppLaunchAsPanel[]; +extern const char kAuthServerWhitelist[]; extern const char kAutomationClientChannelID[]; extern const char kBookmarkMenu[]; extern const char kBrowserAssertTest[]; 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()); } } diff --git a/net/net.gyp b/net/net.gyp index 7cabfc7..b359f6a 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -312,6 +312,8 @@ 'http/http_auth.h', 'http/http_auth_cache.cc', 'http/http_auth_cache.h', + 'http/http_auth_filter.cc', + 'http/http_auth_filter.h', 'http/http_auth_handler.cc', 'http/http_auth_handler.h', 'http/http_auth_handler_basic.cc', @@ -641,6 +643,7 @@ 'ftp/ftp_util_unittest.cc', 'http/des_unittest.cc', 'http/http_auth_cache_unittest.cc', + 'http/http_auth_filter_unittest.cc', 'http/http_auth_handler_basic_unittest.cc', 'http/http_auth_handler_digest_unittest.cc', 'http/http_auth_handler_factory_unittest.cc', |