Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / net / http / http_auth_handler_negotiate.cc
blob21f5d65f1f21565fdbaf596e24a23a4fcc1b7325
1 // Copyright (c) 2012 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.
5 #include "net/http/http_auth_handler_negotiate.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "net/base/address_family.h"
14 #include "net/base/net_errors.h"
15 #include "net/dns/host_resolver.h"
16 #include "net/dns/single_request_host_resolver.h"
17 #include "net/http/http_auth_filter.h"
18 #include "net/http/url_security_manager.h"
20 namespace net {
22 HttpAuthHandlerNegotiate::Factory::Factory()
23 : disable_cname_lookup_(false),
24 use_port_(false),
25 resolver_(NULL),
26 #if defined(OS_WIN)
27 max_token_length_(0),
28 first_creation_(true),
29 #endif
30 is_unsupported_(false) {
33 HttpAuthHandlerNegotiate::Factory::~Factory() {
36 void HttpAuthHandlerNegotiate::Factory::set_host_resolver(
37 HostResolver* resolver) {
38 resolver_ = resolver;
41 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler(
42 HttpAuth::ChallengeTokenizer* challenge,
43 HttpAuth::Target target,
44 const GURL& origin,
45 CreateReason reason,
46 int digest_nonce_count,
47 const BoundNetLog& net_log,
48 scoped_ptr<HttpAuthHandler>* handler) {
49 #if defined(OS_WIN)
50 if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
51 return ERR_UNSUPPORTED_AUTH_SCHEME;
52 if (max_token_length_ == 0) {
53 int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME,
54 &max_token_length_);
55 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME)
56 is_unsupported_ = true;
57 if (rv != OK)
58 return rv;
60 // TODO(cbentzel): Move towards model of parsing in the factory
61 // method and only constructing when valid.
62 scoped_ptr<HttpAuthHandler> tmp_handler(
63 new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_,
64 url_security_manager(), resolver_,
65 disable_cname_lookup_, use_port_));
66 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
67 return ERR_INVALID_RESPONSE;
68 handler->swap(tmp_handler);
69 return OK;
70 #elif defined(OS_POSIX)
71 if (is_unsupported_)
72 return ERR_UNSUPPORTED_AUTH_SCHEME;
73 if (!auth_library_->Init()) {
74 is_unsupported_ = true;
75 return ERR_UNSUPPORTED_AUTH_SCHEME;
77 // TODO(ahendrickson): Move towards model of parsing in the factory
78 // method and only constructing when valid.
79 scoped_ptr<HttpAuthHandler> tmp_handler(
80 new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(),
81 resolver_, disable_cname_lookup_,
82 use_port_));
83 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
84 return ERR_INVALID_RESPONSE;
85 handler->swap(tmp_handler);
86 return OK;
87 #endif
90 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(
91 AuthLibrary* auth_library,
92 #if defined(OS_WIN)
93 ULONG max_token_length,
94 #endif
95 URLSecurityManager* url_security_manager,
96 HostResolver* resolver,
97 bool disable_cname_lookup,
98 bool use_port)
99 #if defined(OS_WIN)
100 : auth_system_(auth_library, "Negotiate", NEGOSSP_NAME, max_token_length),
101 #elif defined(OS_POSIX)
102 : auth_system_(auth_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC),
103 #endif
104 disable_cname_lookup_(disable_cname_lookup),
105 use_port_(use_port),
106 resolver_(resolver),
107 already_called_(false),
108 has_credentials_(false),
109 auth_token_(NULL),
110 next_state_(STATE_NONE),
111 url_security_manager_(url_security_manager) {
114 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() {
117 std::wstring HttpAuthHandlerNegotiate::CreateSPN(
118 const AddressList& address_list, const GURL& origin) {
119 // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI,
120 // and in the form HTTP@<host>:<port> through GSSAPI
121 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx
123 // However, reality differs from the specification. A good description of
124 // the problems can be found here:
125 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-kb911149-and-kb908209-are-not-the-soluton.aspx
127 // Typically the <host> portion should be the canonical FQDN for the service.
128 // If this could not be resolved, the original hostname in the URL will be
129 // attempted instead. However, some intranets register SPNs using aliases
130 // for the same canonical DNS name to allow multiple web services to reside
131 // on the same host machine without requiring different ports. IE6 and IE7
132 // have hotpatches that allow the default behavior to be overridden.
133 // http://support.microsoft.com/kb/911149
134 // http://support.microsoft.com/kb/938305
136 // According to the spec, the <port> option should be included if it is a
137 // non-standard port (i.e. not 80 or 443 in the HTTP case). However,
138 // historically browsers have not included the port, even on non-standard
139 // ports. IE6 required a hotpatch and a registry setting to enable
140 // including non-standard ports, and IE7 and IE8 also require the same
141 // registry setting, but no hotpatch. Firefox does not appear to have an
142 // option to include non-standard ports as of 3.6.
143 // http://support.microsoft.com/kb/908209
145 // Without any command-line flags, Chrome matches the behavior of Firefox
146 // and IE. Users can override the behavior so aliases are allowed and
147 // non-standard ports are included.
148 int port = origin.EffectiveIntPort();
149 std::string server = address_list.canonical_name();
150 if (server.empty())
151 server = origin.host();
152 #if defined(OS_WIN)
153 static const char kSpnSeparator = '/';
154 #elif defined(OS_POSIX)
155 static const char kSpnSeparator = '@';
156 #endif
157 if (port != 80 && port != 443 && use_port_) {
158 return ASCIIToWide(base::StringPrintf("HTTP%c%s:%d", kSpnSeparator,
159 server.c_str(), port));
160 } else {
161 return ASCIIToWide(base::StringPrintf("HTTP%c%s", kSpnSeparator,
162 server.c_str()));
166 HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge(
167 HttpAuth::ChallengeTokenizer* challenge) {
168 return auth_system_.ParseChallenge(challenge);
171 // Require identity on first pass instead of second.
172 bool HttpAuthHandlerNegotiate::NeedsIdentity() {
173 return auth_system_.NeedsIdentity();
176 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() {
177 if (target_ == HttpAuth::AUTH_PROXY)
178 return true;
179 if (!url_security_manager_)
180 return false;
181 return url_security_manager_->CanUseDefaultCredentials(origin_);
184 bool HttpAuthHandlerNegotiate::AllowsExplicitCredentials() {
185 return auth_system_.AllowsExplicitCredentials();
188 // The Negotiate challenge header looks like:
189 // WWW-Authenticate: NEGOTIATE auth-data
190 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) {
191 #if defined(OS_POSIX)
192 if (!auth_system_.Init()) {
193 VLOG(1) << "can't initialize GSSAPI library";
194 return false;
196 // GSSAPI does not provide a way to enter username/password to
197 // obtain a TGT. If the default credentials are not allowed for
198 // a particular site (based on whitelist), fall back to a
199 // different scheme.
200 if (!AllowsDefaultCredentials())
201 return false;
202 #endif
203 if (CanDelegate())
204 auth_system_.Delegate();
205 auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE;
206 score_ = 4;
207 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
208 HttpAuth::AuthorizationResult auth_result =
209 auth_system_.ParseChallenge(challenge);
210 return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT);
213 int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl(
214 const AuthCredentials* credentials, const HttpRequestInfo* request,
215 const CompletionCallback& callback, std::string* auth_token) {
216 DCHECK(callback_.is_null());
217 DCHECK(auth_token_ == NULL);
218 auth_token_ = auth_token;
219 if (already_called_) {
220 DCHECK((!has_credentials_ && credentials == NULL) ||
221 (has_credentials_ && credentials->Equals(credentials_)));
222 next_state_ = STATE_GENERATE_AUTH_TOKEN;
223 } else {
224 already_called_ = true;
225 if (credentials) {
226 has_credentials_ = true;
227 credentials_ = *credentials;
229 next_state_ = STATE_RESOLVE_CANONICAL_NAME;
231 int rv = DoLoop(OK);
232 if (rv == ERR_IO_PENDING)
233 callback_ = callback;
234 return rv;
237 void HttpAuthHandlerNegotiate::OnIOComplete(int result) {
238 int rv = DoLoop(result);
239 if (rv != ERR_IO_PENDING)
240 DoCallback(rv);
243 void HttpAuthHandlerNegotiate::DoCallback(int rv) {
244 DCHECK(rv != ERR_IO_PENDING);
245 DCHECK(!callback_.is_null());
246 CompletionCallback callback = callback_;
247 callback_.Reset();
248 callback.Run(rv);
251 int HttpAuthHandlerNegotiate::DoLoop(int result) {
252 DCHECK(next_state_ != STATE_NONE);
254 int rv = result;
255 do {
256 State state = next_state_;
257 next_state_ = STATE_NONE;
258 switch (state) {
259 case STATE_RESOLVE_CANONICAL_NAME:
260 DCHECK_EQ(OK, rv);
261 rv = DoResolveCanonicalName();
262 break;
263 case STATE_RESOLVE_CANONICAL_NAME_COMPLETE:
264 rv = DoResolveCanonicalNameComplete(rv);
265 break;
266 case STATE_GENERATE_AUTH_TOKEN:
267 DCHECK_EQ(OK, rv);
268 rv = DoGenerateAuthToken();
269 break;
270 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
271 rv = DoGenerateAuthTokenComplete(rv);
272 break;
273 default:
274 NOTREACHED() << "bad state";
275 rv = ERR_FAILED;
276 break;
278 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
280 return rv;
283 int HttpAuthHandlerNegotiate::DoResolveCanonicalName() {
284 next_state_ = STATE_RESOLVE_CANONICAL_NAME_COMPLETE;
285 if (disable_cname_lookup_ || !resolver_)
286 return OK;
288 // TODO(cbentzel): Add reverse DNS lookup for numeric addresses.
289 DCHECK(!single_resolve_.get());
290 HostResolver::RequestInfo info(HostPortPair(origin_.host(), 0));
291 info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME);
292 single_resolve_.reset(new SingleRequestHostResolver(resolver_));
293 return single_resolve_->Resolve(
294 info, &address_list_,
295 base::Bind(&HttpAuthHandlerNegotiate::OnIOComplete,
296 base::Unretained(this)),
297 net_log_);
300 int HttpAuthHandlerNegotiate::DoResolveCanonicalNameComplete(int rv) {
301 DCHECK_NE(ERR_IO_PENDING, rv);
302 if (rv != OK) {
303 // Even in the error case, try to use origin_.host instead of
304 // passing the failure on to the caller.
305 VLOG(1) << "Problem finding canonical name for SPN for host "
306 << origin_.host() << ": " << ErrorToString(rv);
307 rv = OK;
310 next_state_ = STATE_GENERATE_AUTH_TOKEN;
311 spn_ = CreateSPN(address_list_, origin_);
312 address_list_ = AddressList();
313 return rv;
316 int HttpAuthHandlerNegotiate::DoGenerateAuthToken() {
317 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
318 AuthCredentials* credentials = has_credentials_ ? &credentials_ : NULL;
319 // TODO(cbentzel): This should possibly be done async.
320 return auth_system_.GenerateAuthToken(credentials, spn_, auth_token_);
323 int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) {
324 DCHECK_NE(ERR_IO_PENDING, rv);
325 auth_token_ = NULL;
326 return rv;
329 bool HttpAuthHandlerNegotiate::CanDelegate() const {
330 // TODO(cbentzel): Should delegation be allowed on proxies?
331 if (target_ == HttpAuth::AUTH_PROXY)
332 return false;
333 if (!url_security_manager_)
334 return false;
335 return url_security_manager_->CanDelegate(origin_);
338 } // namespace net