summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 12:17:44 +0000
committerahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 12:17:44 +0000
commit1ce73ededde9015c4834d258d1b78a73ff449d1a (patch)
tree0c5aafc2b14f32f411ddb74e3b9ba9009050123f /net
parente25c955957145bd1b772cf1b716c102da73bc6dc (diff)
downloadchromium_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.cc179
-rw-r--r--net/http/http_auth_filter.h23
-rw-r--r--net/http/http_auth_filter_unittest.cc421
-rw-r--r--net/http/http_auth_filter_win.h39
-rw-r--r--net/http/http_auth_handler_factory_unittest.cc8
-rw-r--r--net/http/http_auth_unittest.cc8
-rw-r--r--net/http/http_network_transaction.cc2
-rw-r--r--net/net.gyp1
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(&registry_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',