diff options
author | ahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 12:17:44 +0000 |
---|---|---|
committer | ahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 12:17:44 +0000 |
commit | 1ce73ededde9015c4834d258d1b78a73ff449d1a (patch) | |
tree | 0c5aafc2b14f32f411ddb74e3b9ba9009050123f /net | |
parent | e25c955957145bd1b772cf1b716c102da73bc6dc (diff) | |
download | chromium_src-1ce73ededde9015c4834d258d1b78a73ff449d1a.zip chromium_src-1ce73ededde9015c4834d258d1b78a73ff449d1a.tar.gz chromium_src-1ce73ededde9015c4834d258d1b78a73ff449d1a.tar.bz2 |
HttpAuthFilterWhitelist is now getting entries from the Windows
registry, and adding them to whatever is in the command line.
Added a basic IsIntranetHost() member function to HttpNetworkTransaction.
Removed it until we figure out what to do with it.
Added unit tests.
Refactored SetFilters() to be less confusing.
Unit tests now use a dummy registry key.
BUG=29596
TEST=net_unittests.exe --gtest_filter=HttpAuthFilterTest.*
Review URL: http://codereview.chromium.org/669068
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42600 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_auth_filter.cc | 179 | ||||
-rw-r--r-- | net/http/http_auth_filter.h | 23 | ||||
-rw-r--r-- | net/http/http_auth_filter_unittest.cc | 421 | ||||
-rw-r--r-- | net/http/http_auth_filter_win.h | 39 | ||||
-rw-r--r-- | net/http/http_auth_handler_factory_unittest.cc | 8 | ||||
-rw-r--r-- | net/http/http_auth_unittest.cc | 8 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 2 | ||||
-rw-r--r-- | net/net.gyp | 1 |
8 files changed, 641 insertions, 40 deletions
diff --git a/net/http/http_auth_filter.cc b/net/http/http_auth_filter.cc index a75aa727..44d9ce3 100644 --- a/net/http/http_auth_filter.cc +++ b/net/http/http_auth_filter.cc @@ -4,21 +4,190 @@ #include "net/http/http_auth_filter.h" +#if defined(OS_WIN) +#include "base/registry.h" +#endif + #include "base/string_util.h" #include "googleurl/src/gurl.h" +#if defined(OS_WIN) +#include "net/http/http_auth_filter_win.h" +#endif + namespace net { +// Using a std::set<> has the benefit of removing duplicates automatically. +typedef std::set<string16> RegistryWhitelist; + +#if defined(OS_WIN) +namespace http_auth { + +// The common path to all the registry keys containing domain zone information. +const char16 kRegistryWhitelistKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\" + L"Internet Settings\\ZoneMap\\Domains"; +const char16 kRegistryInternetSettings[] = + L"Software\\Policies\\Microsoft\\Windows\\" + L"CurrentVersion\\Internet Settings"; + +const char16 kSettingsMachineOnly[] = L"Security_HKLM_only"; +static const char16 kRegistryHttp[] = L"http"; +static const char16 kRegistryHttps[] = L"https"; +static const char16 kRegistryStar[] = L"*"; +const char16* kRegistryEntries[3] = { + kRegistryHttp, + kRegistryHttps, + kRegistryStar +}; + +const char16* g_registry_whitelist_key_override = NULL; + +const char16* GetRegistryWhitelistKey() { + // If we've overridden the whitelist key, return that instead of the default. + if (g_registry_whitelist_key_override) + return g_registry_whitelist_key_override; + return kRegistryWhitelistKey; +} + +void SetRegistryWhitelistKey(const char16* new_whitelist_key) { + g_registry_whitelist_key_override = new_whitelist_key; +} + +bool UseOnlyMachineSettings() { + DWORD machine_only = 0; + // TODO(ahendrickson) -- Check if the "Use only machine settings" option is + // enabled in the Security Zones section of the Group Policy, and return + // false if not. + + // Get the key indicating whether or not to use only machine settings. + RegKey InternetSettingsKey(HKEY_LOCAL_MACHINE, + http_auth::kRegistryInternetSettings); + if (!InternetSettingsKey.ReadValueDW(http_auth::kSettingsMachineOnly, + &machine_only)) { + return false; + } + return machine_only != 0; +} + +} // namespace http_auth + +namespace { + +// |whitelist| is the list of whitelist entries to populate, initially empty. +// +// |subkeys| holds the list of keys from the base key to our current one. +// For example the key ".../ZoneMap/Domains/example.com/foo.bar" would have +// subkeys { "example.com", "foo.bar" }. +void GetRegistryWhitelistInfo(RegistryWhitelist* whitelist, + std::list<string16>* subkeys, + RegistryHiveType hive_type) { + // Iterate through all the subkeys of GetRegistryWhitelistKey(), looking + // for values whose names are in |kRegistryEntries| and whose data is less + // than or equal to 2. The key names are the domain names, and the values + // are the zone that the domain is in. Values are: + // 0 - My Computer + // 1 - Local Intranet Zone + // 2 - Trusted Sites Zone (specifically, zones to access via HTTPS) + // 3 - Internet Zone + // 4 - Restricted Sites Zone + // See http://support.microsoft.com/kb/182569 for more information. + string16 full_domain_key = http_auth::GetRegistryWhitelistKey(); + HKEY hive = + (hive_type == CURRENT_USER) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + // Build the key + std::list<string16>::iterator iter = subkeys->begin(); + for (; iter != subkeys->end(); ++iter) { + full_domain_key += L"\\"; + full_domain_key += *iter; + } + // Check all the sub-keys, recursively. + RegistryKeyIterator key_iter(hive, full_domain_key.c_str()); + for (; key_iter.Valid(); ++key_iter) { + subkeys->push_back(key_iter.Name()); // Add the new key, + GetRegistryWhitelistInfo(whitelist, subkeys, hive_type); + subkeys->pop_back(); // and remove it when done. + } + // Check the value(s) in this key. + RegKey key(hive, full_domain_key.c_str()); + for (size_t i = 0; i < arraysize(http_auth::kRegistryEntries); ++i) { + DWORD val = 0; + if (!key.ReadValueDW(http_auth::kRegistryEntries[i], &val)) + continue; + // Check if the setting is for Trusted sites zone (2) or better. + // TODO(ahendrickson) - Something that we need to handle (at some point) is + // that we can have a specific url "downgraded" to the internet zone even + // if a previous rule says it is an intranet address. (*.foo.com intranet, + // *.external.foo.com internet). This also has to do with some user setting + // overriding a machine setting (if allowed by policy). + if (val > 2) + continue; + // TODO(ahendrickson) -- How do we handle ranges? + // Concatenate the subkeys (in reverse order), separated with a period. + bool start = true; + string16 entry; + std::list<string16>::reverse_iterator rev_iter = subkeys->rbegin(); + for (; rev_iter != subkeys->rend(); ++rev_iter) { + if (!start) + entry += L"."; + start = false; + entry += *rev_iter; + } + if (!entry.empty()) + whitelist->insert(entry); + } +} + +void GetRegistryWhitelistInfoTop(RegistryWhitelist* whitelist) { + std::list<string16> subkeys; + GetRegistryWhitelistInfo(whitelist, &subkeys, LOCAL_MACHINE); + if (!http_auth::UseOnlyMachineSettings()) + GetRegistryWhitelistInfo(whitelist, &subkeys, CURRENT_USER); +} + +} // namespace +#endif // OS_WIN + +// TODO(ahendrickson) -- Determine if we want separate whitelists for HTTP and +// HTTPS, one for both, or only an HTTP one. My understanding is that the HTTPS +// 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() { } -bool HttpAuthFilterWhitelist::SetFilters(const std::string& server_whitelist) { - // Parse the input string using commas as separators. - rules_.ParseFromString(server_whitelist); - return true; +void HttpAuthFilterWhitelist::UpdateRegistryWhitelist() { + // Updates the whitelist from the Windows registry. + // |extra_whitelist_entries_| are the ones passed in via + // the command line, so we add those first. + // If there are no command line entries, and no registry entries, then + // |rules_| will be empty. + rules_.Clear(); + // Get the registry whitelist entries. + RegistryWhitelist registry_whitelist; +#if defined(OS_WIN) + GetRegistryWhitelistInfoTop(®istry_whitelist); +#endif + // Parse the saved input string using commas as separators. + if (!extra_whitelist_entries_.empty()) + rules_.ParseFromString(extra_whitelist_entries_); + // Add the entries from the registry. + const std::string prefix = "*"; + RegistryWhitelist::const_iterator iter = registry_whitelist.begin(); + for (; iter != registry_whitelist.end(); ++iter) { + const std::string& s = UTF16ToASCII(*iter); + AddFilter(prefix + s, HttpAuth::AUTH_SERVER); + } +} + +void HttpAuthFilterWhitelist::SetWhitelist( + const std::string& server_whitelist) { + // Save for when we update. + extra_whitelist_entries_ = server_whitelist; + UpdateRegistryWhitelist(); } bool HttpAuthFilterWhitelist::IsValid(const GURL& url, @@ -47,4 +216,4 @@ void HttpAuthFilterWhitelist::AddRuleToBypassLocal() { rules_.AddRuleToBypassLocal(); } -} // namespace net +} // namespace net diff --git a/net/http/http_auth_filter.h b/net/http/http_auth_filter.h index 49fbe96..27e2b58 100644 --- a/net/http/http_auth_filter.h +++ b/net/http/http_auth_filter.h @@ -5,9 +5,11 @@ #ifndef NET_HTTP_HTTP_AUTH_FILTER_H_ #define NET_HTTP_HTTP_AUTH_FILTER_H_ +#include <list> +#include <set> #include <string> -#include <vector> +#include "base/string_util.h" #include "net/http/http_auth.h" #include "net/proxy/proxy_bypass_rules.h" @@ -27,7 +29,7 @@ class HttpAuthFilter { }; // Whitelist HTTP authentication filter. -// Explicit whitelists of domains are set via SetFilters(). +// Explicit whitelists of domains are set via SetWhitelist(). // // Uses the ProxyBypassRules class to do whitelisting for servers. // All proxies are allowed. @@ -36,13 +38,16 @@ class HttpAuthFilterWhitelist : public HttpAuthFilter { 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; + // HttpAuthFilter methods: + virtual bool IsValid(const GURL& url, HttpAuth::Target target) const; - // Installs the whitelist filters. + // Installs the whitelist. // |server_whitelist| is parsed by ProxyBypassRules. - bool SetFilters(const std::string& server_whitelist); + void SetWhitelist(const std::string& server_whitelist); + + // Updates the whitelist rules, from the command line and (on that platform) + // the Windows registry. May be called periodically. + void UpdateRegistryWhitelist(); // Adds an individual URL |filter| to the list, of the specified |target|. bool AddFilter(const std::string& filter, HttpAuth::Target target); @@ -53,6 +58,10 @@ class HttpAuthFilterWhitelist : public HttpAuthFilter { const ProxyBypassRules& rules() const { return rules_; } private: + std::string extra_whitelist_entries_; + + // We are using ProxyBypassRules because they have the functionality that we + // want, but we are not using it for proxy bypass. ProxyBypassRules rules_; DISALLOW_COPY_AND_ASSIGN(HttpAuthFilterWhitelist); diff --git a/net/http/http_auth_filter_unittest.cc b/net/http/http_auth_filter_unittest.cc index a2ffb51..eea2c8a 100644 --- a/net/http/http_auth_filter_unittest.cc +++ b/net/http/http_auth_filter_unittest.cc @@ -5,58 +5,78 @@ #include <iostream> #include "base/logging.h" + +#if defined(OS_WIN) +#include "base/registry.h" +#endif // OS_WIN + #include "base/scoped_ptr.h" #include "googleurl/src/gurl.h" #include "net/http/http_auth_filter.h" +#include "net/http/http_auth_filter_win.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { +static const char* const server_whitelist_array[] = { + "google.com", + "linkedin.com", + "book.com", + ".chromium.org", + ".gag", + "gog" +}; + +enum { + ALL_SERVERS_MATCH = (1 << arraysize(server_whitelist_array)) - 1 +}; + struct UrlData { GURL url; HttpAuth::Target target; bool matches; + int match_bits; }; -static UrlData urls[] = { +static const UrlData urls[] = { { GURL(""), - HttpAuth::AUTH_NONE, false }, + HttpAuth::AUTH_NONE, false, 0 }, { GURL("http://foo.cn"), - HttpAuth::AUTH_PROXY, true }, + HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH }, { GURL("http://foo.cn"), - HttpAuth::AUTH_SERVER, false }, + HttpAuth::AUTH_SERVER, false, 0 }, { GURL("http://slashdot.org"), - HttpAuth::AUTH_NONE, false }, + HttpAuth::AUTH_NONE, false, 0 }, { GURL("http://www.google.com"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 0 }, { GURL("http://www.google.com"), - HttpAuth::AUTH_PROXY, true }, + HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH }, { GURL("https://login.facebook.com/login.php?login_attempt=1"), - HttpAuth::AUTH_NONE, false }, + HttpAuth::AUTH_NONE, false, 0 }, { GURL("http://codereview.chromium.org/634002/show"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 3 }, { GURL("http://code.google.com/p/chromium/issues/detail?id=34505"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 0 }, { 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 }, + HttpAuth::AUTH_SERVER, true, 1 << 3 }, { GURL("https://www.linkedin.com/secure/login?trk=hb_signin"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 1 }, { GURL("http://www.linkedin.com/mbox?displayMBoxItem=&" "itemID=I1717980652_2&trk=COMM_HP_MSGVW_MEBC_MEBC&goback=.hom"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 1 }, { GURL("http://news.slashdot.org/story/10/02/18/190236/" "New-Plan-Lets-Top-HS-Students-Graduate-2-Years-Early"), - HttpAuth::AUTH_PROXY, true }, + HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH }, { GURL("http://codereview.chromium.org/646068/diff/4001/5003"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 3 }, { GURL("http://codereview.chromium.gag/646068/diff/4001/5003"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 4 }, { GURL("http://codereview.chromium.gog/646068/diff/4001/5003"), - HttpAuth::AUTH_SERVER, true }, + HttpAuth::AUTH_SERVER, true, 1 << 5 }, }; } // namespace @@ -74,13 +94,374 @@ TEST(HttpAuthFilterTest, EmptyFilter) { 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); + std::string server_whitelist_filter_string; + for (size_t i = 0; i < arraysize(server_whitelist_array); ++i) { + if (!server_whitelist_filter_string.empty()) + server_whitelist_filter_string += ","; + server_whitelist_filter_string += "*"; + server_whitelist_filter_string += server_whitelist_array[i]; + } + filter.SetWhitelist(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; } } +#if defined(OS_WIN) +namespace { + +static const char16 kTopKey[] = L"Domains"; + +bool RegKeyExists(HKEY root, const string16& key) { + RegKey reg_key(root, key.c_str()); + return reg_key.Valid(); +} + +// Split |key| into |parent_key| and |key_segment|, at the last backslash. +// If there is no backslash, then |parent_key| is empty and |key_segment| gets +// the whole key. +// Returns true if a backslash was found. +bool Split(const string16& key, string16* parent_key, string16* key_segment) { + parent_key->clear(); + *key_segment = key; + string16::size_type last_slash = key.rfind(L"\\"); + // Check if this is the last segment. + if (last_slash != string16::npos) { + *parent_key = key.substr(0, last_slash); + *key_segment = key.substr(last_slash + 1); + return true; + } + return false; // Not allowed to destroy top-level keys. +} + +// Recursively destroys registry keys, starting with |key| as long as it +// has no sub-keys or values, until it finds a |key| segment matching |top|. +// Works from the bottom up. +// We only delete the |key| segment matching |top| if |created_top| is set. +// Returns false on failure, true on success. +bool DestroyRegKeysToTop(HKEY root, + const string16& key, + const string16& top, + bool created_top) { + if (key.empty()) + return false; + RegKey reg_leaf_key(root, key.c_str()); + if (!reg_leaf_key.Valid()) + return false; // Can't destroy a non-existent |key|. + DWORD count = reg_leaf_key.ValueCount(); + if (count > 0) + return false; // Not allowed to destroy the |key| if it has values. + RegistryKeyIterator reg_leaf_iter(root, key.c_str()); + count = reg_leaf_iter.SubkeyCount(); + if (count > 0) + return false; // Not allowed to destroy the |key| if it has sub-keys. + // At this point, we know the |key| has no values or sub-keys. + + // Split into parent key, and leaf segment. + string16 parent_key; + string16 key_segment; + // Check if this is the last segment. + bool can_recurse = Split(key, &parent_key, &key_segment); + if (!can_recurse) + return false; // Not allowed to destroy top-level keys. + + // Check if we've reached a key segment matching |top|. + can_recurse &= (key_segment != top); + if (!can_recurse && !created_top) { + // We've reached the root, and didn't create it, so we're done. + return true; + } + // Destroy the current leaf |key|. + RegKey parent_reg_key(root, parent_key.c_str(), KEY_WRITE); + if (!parent_reg_key.DeleteKey(key_segment.c_str())) + return false; // Failed to delete the |key|. + + if (can_recurse) { + // Destroy the registry key above the current one. + DestroyRegKeysToTop(root, parent_key, top, created_top); + } + return true; // Destroyed at least the leaf |key| segment. +} + +enum RegKeyCreateResult { + REGKEY_DOESNT_EXIST, + REGKEY_EXISTS, + REGKEY_CREATED_TOP +}; + +// Recursively create a registry |key|, until it finds a |key| segment matching +// |top|. Works from the bottom up. +// Returns 0 on failure, > 0 on success (1 on normal success, 2 if it creates +// the |top| key segment). +RegKeyCreateResult CreateRegKeyIfNotExists(HKEY root, + const string16& key, + const string16& top) { + RegKeyCreateResult result = REGKEY_DOESNT_EXIST; + if (key.empty()) + return REGKEY_DOESNT_EXIST; + if (RegKeyExists(root, key)) + return REGKEY_EXISTS; + + // Split into parent key, and leaf segment. + string16 parent_key; + string16 key_segment; + // Check if this is the last segment. + bool can_recurse = Split(key, &parent_key, &key_segment); + if (!can_recurse) + return REGKEY_DOESNT_EXIST; // Not allowed to create top-level keys. + // Check if we've reached a segment matching |top|. + can_recurse = (key_segment != top); + result = REGKEY_EXISTS; + if (can_recurse) { + // Create the registry key above the current one. + result = CreateRegKeyIfNotExists(root, parent_key, top); + if (result == REGKEY_DOESNT_EXIST) + return REGKEY_DOESNT_EXIST; + DCHECK(RegKeyExists(root, parent_key)) + << "Unable to create registry key '" << parent_key << "'"; + } else if (!RegKeyExists(root, parent_key)) { + // Don't have parent key, and not allowed to create it. + return REGKEY_DOESNT_EXIST; + } + // Create the new key segment. + RegKey parent_reg_key(root, parent_key.c_str(), KEY_WRITE); + if (!parent_reg_key.CreateKey(key_segment.c_str(), KEY_WRITE)) { + DLOG(INFO) << "Unable to create key '" << parent_key + << "\\" << key_segment << "' in hive 0x" << root + << ((root == HKEY_LOCAL_MACHINE) ? " (HKEY_LOCAL_MACHINE)" : + ((root == HKEY_CURRENT_USER) ? " (HKEY_CURRENT_USER)" : "")); + return REGKEY_DOESNT_EXIST; + } + if (key_segment == top) + return REGKEY_CREATED_TOP; // We created the |top| key segment. + return result; +} + +bool HasZoneMapKey(RegistryHiveType hive_type, const char* key_name) { + HKEY root_key = (hive_type == CURRENT_USER) ? + HKEY_CURRENT_USER : + HKEY_LOCAL_MACHINE; + string16 key_name_utf16 = ASCIIToUTF16(key_name); + string16 full_key_name_utf16 = http_auth::GetRegistryWhitelistKey(); + full_key_name_utf16 += L"\\"; + full_key_name_utf16 += key_name_utf16; + RegKey reg_leaf_key(root_key, full_key_name_utf16.c_str()); + return reg_leaf_key.Valid(); +} + +std::set<std::string> GetZoneMapKeys(RegistryHiveType hive_type) { + std::set<std::string> keys; + HKEY root_key = (hive_type == CURRENT_USER) ? + HKEY_CURRENT_USER : + HKEY_LOCAL_MACHINE; + RegistryKeyIterator reg_base_key_iter(root_key, + http_auth::GetRegistryWhitelistKey()); + DWORD count = reg_base_key_iter.SubkeyCount(); + for (DWORD k = 0; k < count; ++k, ++reg_base_key_iter) + keys.insert(UTF16ToASCII(reg_base_key_iter.Name())); + return keys; +} + +bool SetupZoneMapEntry(RegistryHiveType hive_type, + const char* key_name, + const char16* name, + DWORD value, + bool* created_entry) { + bool created_top = false; + bool created = false; + HKEY root_key = (hive_type == CURRENT_USER) ? + HKEY_CURRENT_USER : + HKEY_LOCAL_MACHINE; + string16 key_name_utf16 = ASCIIToUTF16(key_name); + string16 full_key_name_utf16 = http_auth::GetRegistryWhitelistKey(); + full_key_name_utf16 += L"\\"; + full_key_name_utf16 += key_name_utf16; + RegKeyCreateResult have_key = + CreateRegKeyIfNotExists(root_key, full_key_name_utf16, kTopKey); + created_top = (have_key == REGKEY_CREATED_TOP); + if (have_key != REGKEY_DOESNT_EXIST) { + RegKey reg_leaf_key(root_key, full_key_name_utf16.c_str(), KEY_WRITE); + created = reg_leaf_key.WriteValue(name, value); + } + if (created_entry) + *created_entry = created; + return created_top; +} + +void TearDownZoneMapEntry(RegistryHiveType hive_type, + const char* key_name, + const char16* name, + bool created_top) { + HKEY root_key = (hive_type == CURRENT_USER) ? + HKEY_CURRENT_USER : + HKEY_LOCAL_MACHINE; + string16 key_name_utf16 = ASCIIToUTF16(key_name); + string16 full_key_name_utf16 = http_auth::GetRegistryWhitelistKey(); + full_key_name_utf16 += L"\\"; + full_key_name_utf16 += key_name_utf16; + RegKey reg_leaf_key(root_key, full_key_name_utf16.c_str(), KEY_WRITE); + reg_leaf_key.DeleteValue(name); + DestroyRegKeysToTop(root_key, full_key_name_utf16, kTopKey, created_top); +} + +// Sets the registry whitelist key to the given value, and automatically +// restores it on destruction. +class AutoRestoreWhitelistKey { + public: + explicit AutoRestoreWhitelistKey(const char16* temp_value) { + http_auth::SetRegistryWhitelistKey(temp_value); + } + ~AutoRestoreWhitelistKey() { + http_auth::SetRegistryWhitelistKey(NULL); + } +}; + +} // namespace + +// NOTE: Doing unit tests that involve the Windows registry is tricky: +// 1. You may not be able to write to a particular location in the +// registry. Specifically, authentication is needed to write to +// HKEY_LOCAL_MACHINE. +// 2. There may already be values in the registry. This is handled by +// changing the registry key that is used for testing. +// 3. The registry should be left in the same state as it was before the +// test. Using a different registry key helps to insure this. +// +// We want to disable tests when: +// - The test URL's host matches a registry entry that already exists. +// - We are unable to write an entry to the registry. +TEST(HttpAuthFilterTest, FilterFromRegistry) { + // We want to avoid testing the writing (and later deleting) of URLs that + // are already in the registry. + AutoRestoreWhitelistKey auto_restore( + L"Software\\Microsoft\\Windows\\CurrentVersion\\" + L"Internet Settings\\ZoneMap\\ChromeHttpAuthUnitTests"); + + // If set, only the LOCAL_MACHINE registry entries are valid. + bool test_only_machine = http_auth::UseOnlyMachineSettings(); + + // This array records whether or not a test URL is already in the registry. + bool owned_urls[2][arraysize(server_whitelist_array)] = { false }; + for (size_t w = 0; w < arraysize(server_whitelist_array); ++w) { + owned_urls[CURRENT_USER][w] = + !test_only_machine && + !HasZoneMapKey(CURRENT_USER, server_whitelist_array[w]); + owned_urls[LOCAL_MACHINE][w] = + !HasZoneMapKey(LOCAL_MACHINE, server_whitelist_array[w]); + } + + // This array records whether or not any of the test URLs already match some + // in the registry. We won't test those because the tests might come out + // differently than we expect. + bool skip_tests[2][arraysize(urls)] = { false }; + RegistryHiveType hives[] = { CURRENT_USER, LOCAL_MACHINE }; + const char* type_string[] = { "USER", "MACHINE" }; + for (int t = 0; t < 2; ++t) { + std::set<std::string> keys = GetZoneMapKeys(hives[t]); + for (size_t u = 0; u < arraysize(urls); ++u) { + if (urls[u].url.HostNoBrackets().empty()) + continue; + if (test_only_machine && !t) { + skip_tests[t][u] = true; + } else { + std::set<std::string>::const_iterator next = keys.begin(); + std::set<std::string>::const_iterator last = keys.end(); + for (; next != last; ++next) { + const std::string& key = *next; + if (std::string::npos != key.find(urls[u].url.HostNoBrackets())) { + skip_tests[t][u] = true; + break; + } + } + } + } + } + + // Set up our test registry entries. + bool have_test_case = false; + bool created_user_root = false; + bool created_machine_root = false; + bool created_entry = false; + size_t bit = 1; + bool created_roots[] = { false, false }; + size_t num_reg_entries = arraysize(http_auth::kRegistryEntries); + size_t reg_index; + for (size_t w = 0; w < arraysize(server_whitelist_array); ++w, bit <<= 1) { + for (int t = 0; t < 2; ++t) { + if (owned_urls[t][w]) { + created_entry = false; + reg_index = (w + t) % num_reg_entries; + created_roots[t] |= + SetupZoneMapEntry( + hives[t], + server_whitelist_array[w], + // Cycle through the types: { HTTP, HTTPS, * }. + http_auth::kRegistryEntries[reg_index], + // Cycle through zones 0, 1, 2. + (w + t) % 3, + &created_entry); + // If we weren't able to create the entry, we don't own it, disable the + // test. + have_test_case |= created_entry; + if (!created_entry) { + owned_urls[t][w] = false; + // Mark any URLs that are dependent on this as not tested. + for (size_t u = 0; u < arraysize(urls); ++u) { + if (urls[u].match_bits & bit) + skip_tests[t][u] = true; + } + } + } + } + } + + // Report any problems or skipped tests. + for (int t = 0; t < 2; ++t) { + for (size_t w = 0; w < arraysize(server_whitelist_array); ++w) { + if (!owned_urls[t][w]) { + DLOG(INFO) << "Cannot write " << type_string[t] << " registry key '" + << server_whitelist_array[w] << "'"; + } + } + } + for (int t = 0; t < 2; ++t) { + for (size_t u = 0; u < arraysize(urls); ++u) { + if (skip_tests[t][u]) { + DLOG(INFO) << "Skipping " << type_string[t] << " test for URL '" + << urls[u].url << "'"; + } + } + } + + if (have_test_case) { + // OK, now we're ready to start the tests! + // Create an non-empty filter, using only registry entries. + HttpAuthFilterWhitelist filter; + filter.SetWhitelist(""); + for (size_t u = 0; u < arraysize(urls); u++) { + if (!skip_tests[CURRENT_USER][u] || !skip_tests[LOCAL_MACHINE][u]) { + EXPECT_EQ(urls[u].matches, filter.IsValid(urls[u].url, urls[u].target)) + << " " << u << ": " << urls[u].url; + } + } + } + + // Tear down our test registry entries. + for (size_t w = 0; w < arraysize(server_whitelist_array); ++w) { + for (int t = 0; t < 2; ++t) { + reg_index = (w + t) % num_reg_entries; + if (owned_urls[t][w]) { + TearDownZoneMapEntry( + hives[t], + server_whitelist_array[w], + // Cycle through the types: { HTTP, HTTPS, * }. + http_auth::kRegistryEntries[reg_index], + created_roots[t]); + } + } + } +} +#endif // OS_WIN + } // namespace net diff --git a/net/http/http_auth_filter_win.h b/net/http/http_auth_filter_win.h new file mode 100644 index 0000000..f819523 --- /dev/null +++ b/net/http/http_auth_filter_win.h @@ -0,0 +1,39 @@ +// 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_WIN_H_ +#define NET_HTTP_HTTP_AUTH_FILTER_WIN_H_ + +#include <string> + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/string_util.h" + +namespace net { + +enum RegistryHiveType { + CURRENT_USER, + LOCAL_MACHINE +}; + +namespace http_auth { + +// The common path to all the registry keys containing domain zone information. +extern const char16 kRegistryInternetSettings[]; +extern const char16 kSettingsMachineOnly[]; +extern const char16* kRegistryEntries[3]; // L"http", L"https", and L"*" + +extern const char16* GetRegistryWhitelistKey(); +// Override the whitelist key. Passing in NULL restores the default value. +extern void SetRegistryWhitelistKey(const char16* new_whitelist_key); +extern bool UseOnlyMachineSettings(); + +} // namespace http_auth + +} // namespace net +#endif // OS_WIN + +#endif // NET_HTTP_HTTP_AUTH_FILTER_WIN_H_ diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc index 6bbc7b8..a1e4a5c 100644 --- a/net/http/http_auth_handler_factory_unittest.cc +++ b/net/http/http_auth_handler_factory_unittest.cc @@ -187,8 +187,8 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactoryWithFilters) { HttpAuthFilterWhitelist* ntlm_whitelist = new HttpAuthFilterWhitelist; HttpAuthFilterWhitelist* negotiate_whitelist = new HttpAuthFilterWhitelist; - ntlm_whitelist->SetFilters(ntlm_server_whitelist); - negotiate_whitelist->SetFilters(negotiate_server_whitelist); + ntlm_whitelist->SetWhitelist(ntlm_server_whitelist); + negotiate_whitelist->SetWhitelist(negotiate_server_whitelist); http_auth_handler_registry_factory->SetFilter("ntlm", ntlm_whitelist); http_auth_handler_registry_factory->SetFilter("negotiate", @@ -281,8 +281,8 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactoryWithFilters) { #endif // !defined(OS_WIN) // Now change the whitelist and expect failures. - ntlm_whitelist->SetFilters(ntlm_server_whitelist2); - negotiate_whitelist->SetFilters(negotiate_server_whitelist2); + ntlm_whitelist->SetWhitelist(ntlm_server_whitelist2); + negotiate_whitelist->SetWhitelist(negotiate_server_whitelist2); { scoped_refptr<HttpAuthHandler> handler; diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index 70fc194..8522cca 100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc @@ -269,8 +269,8 @@ TEST(HttpAuthTest, ChooseBestChallengeFiltered) { 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); + ntlm_whitelist->SetWhitelist(tests[i].filter_string); + negotiate_whitelist->SetWhitelist(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; @@ -334,8 +334,8 @@ TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedFiltered) { HttpAuthFilterWhitelist* ntlm_whitelist = new HttpAuthFilterWhitelist; HttpAuthFilterWhitelist* negotiate_whitelist = new HttpAuthFilterWhitelist; - ntlm_whitelist->SetFilters(ntlm_server_whitelist); - negotiate_whitelist->SetFilters(negotiate_server_whitelist); + ntlm_whitelist->SetWhitelist(ntlm_server_whitelist); + negotiate_whitelist->SetWhitelist(negotiate_server_whitelist); http_auth_handler_registry_factory->SetFilter("ntlm", ntlm_whitelist); http_auth_handler_registry_factory->SetFilter("negotiate", diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 218f26d..2f06113 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -489,6 +489,7 @@ void HttpNetworkTransaction::DoCallback(int rv) { } void HttpNetworkTransaction::OnIOComplete(int result) { + DLOG(INFO) << " >> " << __FUNCTION__ << "()"; int rv = DoLoop(result); if (rv != ERR_IO_PENDING) DoCallback(rv); @@ -499,6 +500,7 @@ int HttpNetworkTransaction::DoLoop(int result) { int rv = result; do { + DLOG(INFO) << " * " << __FUNCTION__ << "() state = " << next_state_; State state = next_state_; next_state_ = STATE_NONE; switch (state) { diff --git a/net/net.gyp b/net/net.gyp index 1b66ab8..8d60977 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -318,6 +318,7 @@ 'http/http_auth_cache.h', 'http/http_auth_filter.cc', 'http/http_auth_filter.h', + 'http/http_auth_filter_win.h', 'http/http_auth_handler.cc', 'http/http_auth_handler.h', 'http/http_auth_handler_basic.cc', |