OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/http_auth_handler_negotiate.h" | 5 #include "net/http/http_auth_handler_negotiate.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "net/base/address_family.h" |
| 9 #include "net/base/host_resolver.h" |
8 #include "net/base/net_errors.h" | 10 #include "net/base/net_errors.h" |
9 #include "net/http/http_auth_filter.h" | 11 #include "net/http/http_auth_filter.h" |
10 | 12 |
11 namespace net { | 13 namespace net { |
12 | 14 |
13 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(SSPILibrary* library, | 15 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(SSPILibrary* library, |
14 ULONG max_token_length) | 16 ULONG max_token_length, |
15 : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length) { | 17 bool disable_cname_lookup, |
| 18 bool use_port) |
| 19 : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length), |
| 20 user_callback_(NULL), |
| 21 ALLOW_THIS_IN_INITIALIZER_LIST(resolve_cname_callback_( |
| 22 this, &HttpAuthHandlerNegotiate::OnResolveCanonicalName)), |
| 23 disable_cname_lookup_(disable_cname_lookup), |
| 24 use_port_(use_port) { |
16 } | 25 } |
17 | 26 |
18 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { | 27 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { |
19 } | 28 } |
20 | 29 |
21 int HttpAuthHandlerNegotiate::GenerateAuthToken( | 30 int HttpAuthHandlerNegotiate::GenerateAuthToken( |
22 const std::wstring& username, | 31 const std::wstring& username, |
23 const std::wstring& password, | 32 const std::wstring& password, |
24 const HttpRequestInfo* request, | 33 const HttpRequestInfo* request, |
25 const ProxyInfo* proxy, | 34 const ProxyInfo* proxy, |
26 std::string* auth_token) { | 35 std::string* auth_token) { |
27 return auth_sspi_.GenerateAuthToken( | 36 return auth_sspi_.GenerateAuthToken( |
28 &username, | 37 &username, |
29 &password, | 38 &password, |
30 origin_, | 39 spn_, |
31 request, | 40 request, |
32 proxy, | 41 proxy, |
33 auth_token); | 42 auth_token); |
34 } | 43 } |
35 | 44 |
36 // The Negotiate challenge header looks like: | 45 // The Negotiate challenge header looks like: |
37 // WWW-Authenticate: NEGOTIATE auth-data | 46 // WWW-Authenticate: NEGOTIATE auth-data |
38 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { | 47 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { |
39 scheme_ = "negotiate"; | 48 scheme_ = "negotiate"; |
40 score_ = 4; | 49 score_ = 4; |
41 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; | 50 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; |
42 return auth_sspi_.ParseChallenge(challenge); | 51 return auth_sspi_.ParseChallenge(challenge); |
43 } | 52 } |
44 | 53 |
45 // Require identity on first pass instead of second. | 54 // Require identity on first pass instead of second. |
46 bool HttpAuthHandlerNegotiate::NeedsIdentity() { | 55 bool HttpAuthHandlerNegotiate::NeedsIdentity() { |
47 return auth_sspi_.NeedsIdentity(); | 56 return auth_sspi_.NeedsIdentity(); |
48 } | 57 } |
49 | 58 |
50 bool HttpAuthHandlerNegotiate::IsFinalRound() { | 59 bool HttpAuthHandlerNegotiate::IsFinalRound() { |
51 return auth_sspi_.IsFinalRound(); | 60 return auth_sspi_.IsFinalRound(); |
52 } | 61 } |
53 | 62 |
54 bool HttpAuthHandlerNegotiate::SupportsDefaultCredentials() { | 63 bool HttpAuthHandlerNegotiate::SupportsDefaultCredentials() { |
55 return true; | 64 return true; |
56 } | 65 } |
57 | 66 |
| 67 bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { |
| 68 if (!spn_.empty()) |
| 69 return false; |
| 70 if (disable_cname_lookup_) { |
| 71 spn_ = CreateSPN(address_list_, origin_); |
| 72 address_list_.Reset(); |
| 73 return false; |
| 74 } |
| 75 return true; |
| 76 } |
| 77 |
| 78 int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* resolver, |
| 79 CompletionCallback* callback, |
| 80 const BoundNetLog& net_log) { |
| 81 // TODO(cbentzel): Add reverse DNS lookup for numeric addresses. |
| 82 DCHECK(!single_resolve_.get()); |
| 83 DCHECK(!disable_cname_lookup_); |
| 84 DCHECK(callback); |
| 85 |
| 86 HostResolver::RequestInfo info(origin_.host(), 0); |
| 87 info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME); |
| 88 single_resolve_.reset(new SingleRequestHostResolver(resolver)); |
| 89 int rv = single_resolve_->Resolve(info, &address_list_, |
| 90 &resolve_cname_callback_, |
| 91 net_log); |
| 92 if (rv == ERR_IO_PENDING) { |
| 93 user_callback_ = callback; |
| 94 return rv; |
| 95 } |
| 96 OnResolveCanonicalName(rv); |
| 97 // Always return OK. OnResolveCanonicalName logs the error code if not |
| 98 // OK and attempts to use the original origin_ hostname rather than failing |
| 99 // the auth attempt completely. |
| 100 return OK; |
| 101 } |
| 102 |
| 103 void HttpAuthHandlerNegotiate::OnResolveCanonicalName(int result) { |
| 104 if (result != OK) { |
| 105 // Even in the error case, try to use origin_.host instead of |
| 106 // passing the failure on to the caller. |
| 107 LOG(INFO) << "Problem finding canonical name for SPN for host " |
| 108 << origin_.host() << ": " << ErrorToString(result); |
| 109 result = OK; |
| 110 } |
| 111 spn_ = CreateSPN(address_list_, origin_); |
| 112 address_list_.Reset(); |
| 113 if (user_callback_) { |
| 114 CompletionCallback* callback = user_callback_; |
| 115 user_callback_ = NULL; |
| 116 callback->Run(result); |
| 117 } |
| 118 } |
| 119 |
| 120 std::wstring HttpAuthHandlerNegotiate::CreateSPN( |
| 121 const AddressList& address_list, const GURL& origin) { |
| 122 // Kerberos SPNs are in the form HTTP/<host>:<port> |
| 123 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx |
| 124 // |
| 125 // However, reality differs from the specification. A good description of |
| 126 // the problems can be found here: |
| 127 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx |
| 128 // |
| 129 // Typically the <host> portion should be the canonical FQDN for the service. |
| 130 // If this could not be resolved, the original hostname in the URL will be |
| 131 // attempted instead. However, some intranets register SPNs using aliases |
| 132 // for the same canonical DNS name to allow multiple web services to reside |
| 133 // on the same host machine without requiring different ports. IE6 and IE7 |
| 134 // have hotpatches that allow the default behavior to be overridden. |
| 135 // http://support.microsoft.com/kb/911149 |
| 136 // http://support.microsoft.com/kb/938305 |
| 137 // |
| 138 // According to the spec, the <port> option should be included if it is a |
| 139 // non-standard port (i.e. not 80 or 443 in the HTTP case). However, |
| 140 // historically browsers have not included the port, even on non-standard |
| 141 // ports. IE6 required a hotpatch and a registry setting to enable |
| 142 // including non-standard ports, and IE7 and IE8 also require the same |
| 143 // registry setting, but no hotpatch. Firefox does not appear to have an |
| 144 // option to include non-standard ports as of 3.6. |
| 145 // http://support.microsoft.com/kb/908209 |
| 146 // |
| 147 // Without any command-line flags, Chrome matches the behavior of Firefox |
| 148 // and IE. Users can override the behavior so aliases are allowed and |
| 149 // non-standard ports are included. |
| 150 int port = origin.EffectiveIntPort(); |
| 151 std::string server; |
| 152 if (!address_list.GetCanonicalName(&server)) |
| 153 server = origin.host(); |
| 154 if (port != 80 && port != 443 && use_port_) { |
| 155 return ASCIIToWide(StringPrintf("HTTP/%s:%d", server.c_str(), port)); |
| 156 } else { |
| 157 return ASCIIToWide(StringPrintf("HTTP/%s", server.c_str())); |
| 158 } |
| 159 } |
| 160 |
58 int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( | 161 int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( |
59 const HttpRequestInfo* request, | 162 const HttpRequestInfo* request, |
60 const ProxyInfo* proxy, | 163 const ProxyInfo* proxy, |
61 std::string* auth_token) { | 164 std::string* auth_token) { |
62 return auth_sspi_.GenerateAuthToken( | 165 return auth_sspi_.GenerateAuthToken( |
63 NULL, // username | 166 NULL, // username |
64 NULL, // password | 167 NULL, // password |
65 origin_, | 168 spn_, |
66 request, | 169 request, |
67 proxy, | 170 proxy, |
68 auth_token); | 171 auth_token); |
69 } | 172 } |
70 | 173 |
71 HttpAuthHandlerNegotiate::Factory::Factory() | 174 HttpAuthHandlerNegotiate::Factory::Factory() |
72 : max_token_length_(0), | 175 : disable_cname_lookup_(false), |
| 176 use_port_(false), |
| 177 max_token_length_(0), |
73 first_creation_(true), | 178 first_creation_(true), |
74 is_unsupported_(false), | 179 is_unsupported_(false), |
75 sspi_library_(SSPILibrary::GetDefault()) { | 180 sspi_library_(SSPILibrary::GetDefault()) { |
76 } | 181 } |
77 | 182 |
78 HttpAuthHandlerNegotiate::Factory::~Factory() { | 183 HttpAuthHandlerNegotiate::Factory::~Factory() { |
79 } | 184 } |
80 | 185 |
81 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( | 186 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
82 HttpAuth::ChallengeTokenizer* challenge, | 187 HttpAuth::ChallengeTokenizer* challenge, |
83 HttpAuth::Target target, | 188 HttpAuth::Target target, |
84 const GURL& origin, | 189 const GURL& origin, |
85 scoped_refptr<HttpAuthHandler>* handler) { | 190 scoped_refptr<HttpAuthHandler>* handler) { |
86 if (is_unsupported_) | 191 if (is_unsupported_) |
87 return ERR_UNSUPPORTED_AUTH_SCHEME; | 192 return ERR_UNSUPPORTED_AUTH_SCHEME; |
88 if (max_token_length_ == 0) { | 193 if (max_token_length_ == 0) { |
89 int rv = DetermineMaxTokenLength(sspi_library_, NEGOSSP_NAME, | 194 int rv = DetermineMaxTokenLength(sspi_library_, NEGOSSP_NAME, |
90 &max_token_length_); | 195 &max_token_length_); |
91 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) | 196 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) |
92 is_unsupported_ = true; | 197 is_unsupported_ = true; |
93 if (rv != OK) | 198 if (rv != OK) |
94 return rv; | 199 return rv; |
95 } | 200 } |
96 // TODO(cbentzel): Move towards model of parsing in the factory | 201 // TODO(cbentzel): Move towards model of parsing in the factory |
97 // method and only constructing when valid. | 202 // method and only constructing when valid. |
98 scoped_refptr<HttpAuthHandler> tmp_handler( | 203 scoped_refptr<HttpAuthHandler> tmp_handler( |
99 new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_)); | 204 new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_, |
| 205 disable_cname_lookup_, use_port_)); |
100 if (!tmp_handler->InitFromChallenge(challenge, target, origin)) | 206 if (!tmp_handler->InitFromChallenge(challenge, target, origin)) |
101 return ERR_INVALID_RESPONSE; | 207 return ERR_INVALID_RESPONSE; |
102 handler->swap(tmp_handler); | 208 handler->swap(tmp_handler); |
103 return OK; | 209 return OK; |
104 } | 210 } |
105 | 211 |
106 } // namespace net | 212 } // namespace net |
OLD | NEW |