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