1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/http/url_security_manager.h"
6
7#include <urlmon.h>
8#pragma comment(lib, "urlmon.lib")
9
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "base/win/scoped_comptr.h"
13#include "googleurl/src/gurl.h"
14#include "net/http/http_auth_filter.h"
15
16// The Windows implementation of URLSecurityManager uses WinINet/IE's
17// URL security zone manager.  See the MSDN page "URL Security Zones" at
18// http://msdn.microsoft.com/en-us/library/ms537021(VS.85).aspx for more
19// info on the Internet Security Manager and Internet Zone Manager objects.
20//
21// On Windows, we honor the WinINet/IE settings and group policy related to
22// URL Security Zones.  See the Microsoft Knowledge Base article 182569
23// "Internet Explorer security zones registry entries for advanced users"
24// (http://support.microsoft.com/kb/182569) for more info on these registry
25// keys.
26
27namespace net {
28
29class URLSecurityManagerWin : public URLSecurityManager {
30 public:
31  explicit URLSecurityManagerWin(const HttpAuthFilter* whitelist_delegate);
32
33  // URLSecurityManager methods:
34  virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const;
35  virtual bool CanDelegate(const GURL& auth_origin) const;
36
37 private:
38  bool EnsureSystemSecurityManager();
39
40  base::win::ScopedComPtr<IInternetSecurityManager> security_manager_;
41  scoped_ptr<const HttpAuthFilter> whitelist_delegate_;
42
43  DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWin);
44};
45
46URLSecurityManagerWin::URLSecurityManagerWin(
47    const HttpAuthFilter* whitelist_delegate)
48    : whitelist_delegate_(whitelist_delegate) {
49}
50
51bool URLSecurityManagerWin::CanUseDefaultCredentials(
52    const GURL& auth_origin) const {
53  if (!const_cast<URLSecurityManagerWin*>(this)->EnsureSystemSecurityManager())
54    return false;
55
56  std::wstring url_w = ASCIIToWide(auth_origin.spec());
57  DWORD policy = 0;
58  HRESULT hr;
59  hr = security_manager_->ProcessUrlAction(url_w.c_str(),
60                                           URLACTION_CREDENTIALS_USE,
61                                           reinterpret_cast<BYTE*>(&policy),
62                                           sizeof(policy), NULL, 0,
63                                           PUAF_NOUI, 0);
64  if (FAILED(hr)) {
65    LOG(ERROR) << "IInternetSecurityManager::ProcessUrlAction failed: " << hr;
66    return false;
67  }
68
69  // Four possible policies for URLACTION_CREDENTIALS_USE.  See the MSDN page
70  // "About URL Security Zones" at
71  // http://msdn.microsoft.com/en-us/library/ms537183(VS.85).aspx
72  switch (policy) {
73    case URLPOLICY_CREDENTIALS_SILENT_LOGON_OK:
74      return true;
75    case URLPOLICY_CREDENTIALS_CONDITIONAL_PROMPT: {
76      // This policy means "prompt the user for permission if the resource is
77      // not located in the Intranet zone".  TODO(wtc): Note that it's
78      // prompting for permission (to use the default credentials), as opposed
79      // to prompting the user to enter a user name and password.
80
81      // URLZONE_LOCAL_MACHINE 0
82      // URLZONE_INTRANET      1
83      // URLZONE_TRUSTED       2
84      // URLZONE_INTERNET      3
85      // URLZONE_UNTRUSTED     4
86      DWORD zone = 0;
87      hr = security_manager_->MapUrlToZone(url_w.c_str(), &zone, 0);
88      if (FAILED(hr)) {
89        LOG(ERROR) << "IInternetSecurityManager::MapUrlToZone failed: " << hr;
90        return false;
91      }
92      return zone <= URLZONE_INTRANET;
93    }
94    case URLPOLICY_CREDENTIALS_MUST_PROMPT_USER:
95      return false;
96    case URLPOLICY_CREDENTIALS_ANONYMOUS_ONLY:
97      // TODO(wtc): we should fail the authentication.
98      return false;
99    default:
100      NOTREACHED();
101      return false;
102  }
103}
104
105bool URLSecurityManagerWin::CanDelegate(const GURL& auth_origin) const {
106  // TODO(cbentzel): Could this just use the security zone as well? Apparently
107  //                 this is what IE does as well.
108  if (whitelist_delegate_.get())
109    return whitelist_delegate_->IsValid(auth_origin, HttpAuth::AUTH_SERVER);
110  return false;
111}
112
113bool URLSecurityManagerWin::EnsureSystemSecurityManager() {
114  if (!security_manager_) {
115    HRESULT hr = CoInternetCreateSecurityManager(NULL,
116                                                 security_manager_.Receive(),
117                                                 NULL);
118    if (FAILED(hr) || !security_manager_) {
119      LOG(ERROR) << "Unable to create the Windows Security Manager instance";
120      return false;
121    }
122  }
123  return true;
124}
125
126// static
127URLSecurityManager* URLSecurityManager::Create(
128    const HttpAuthFilter* whitelist_default,
129    const HttpAuthFilter* whitelist_delegate) {
130  // If we have a whitelist, just use that.
131  if (whitelist_default)
132    return new URLSecurityManagerWhitelist(whitelist_default,
133                                           whitelist_delegate);
134  return new URLSecurityManagerWin(whitelist_delegate);
135}
136
137}  //  namespace net
138