| 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 "base/string_util.h" | 8 #include "base/string_util.h" |
| 9 #include "base/stringprintf.h" | 9 #include "base/stringprintf.h" |
| 10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| 11 #include "net/base/address_family.h" | 11 #include "net/base/address_family.h" |
| 12 #include "net/base/host_resolver.h" | 12 #include "net/base/host_resolver.h" |
| 13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
| 14 #include "net/http/http_auth_filter.h" | 14 #include "net/http/http_auth_filter.h" |
| 15 #include "net/http/url_security_manager.h" | 15 #include "net/http/url_security_manager.h" |
| 16 | 16 |
| 17 namespace net { | 17 namespace net { |
| 18 | 18 |
| 19 HttpAuthHandlerNegotiate::Factory::Factory() |
| 20 : disable_cname_lookup_(false), |
| 21 use_port_(false), |
| 22 #if defined(OS_WIN) |
| 23 max_token_length_(0), |
| 24 first_creation_(true), |
| 25 is_unsupported_(false), |
| 26 #endif |
| 27 auth_library_(NULL) { |
| 28 } |
| 29 |
| 30 HttpAuthHandlerNegotiate::Factory::~Factory() { |
| 31 } |
| 32 |
| 33 void HttpAuthHandlerNegotiate::Factory::set_host_resolver( |
| 34 HostResolver* resolver) { |
| 35 resolver_ = resolver; |
| 36 } |
| 37 |
| 38 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
| 39 HttpAuth::ChallengeTokenizer* challenge, |
| 40 HttpAuth::Target target, |
| 41 const GURL& origin, |
| 42 CreateReason reason, |
| 43 int digest_nonce_count, |
| 44 const BoundNetLog& net_log, |
| 45 scoped_ptr<HttpAuthHandler>* handler) { |
| 46 #if defined(OS_WIN) |
| 47 if (is_unsupported_ || reason == CREATE_PREEMPTIVE) |
| 48 return ERR_UNSUPPORTED_AUTH_SCHEME; |
| 49 if (max_token_length_ == 0) { |
| 50 int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, |
| 51 &max_token_length_); |
| 52 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) |
| 53 is_unsupported_ = true; |
| 54 if (rv != OK) |
| 55 return rv; |
| 56 } |
| 57 // TODO(cbentzel): Move towards model of parsing in the factory |
| 58 // method and only constructing when valid. |
| 59 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 60 new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, |
| 61 url_security_manager(), resolver_, |
| 62 disable_cname_lookup_, use_port_)); |
| 63 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 64 return ERR_INVALID_RESPONSE; |
| 65 handler->swap(tmp_handler); |
| 66 return OK; |
| 67 #elif defined(OS_POSIX) |
| 68 // TODO(ahendrickson): Move towards model of parsing in the factory |
| 69 // method and only constructing when valid. |
| 70 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 71 new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), |
| 72 resolver_, disable_cname_lookup_, |
| 73 use_port_)); |
| 74 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 75 return ERR_INVALID_RESPONSE; |
| 76 handler->swap(tmp_handler); |
| 77 return OK; |
| 78 #endif |
| 79 } |
| 80 |
| 19 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( | 81 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( |
| 20 AuthLibrary* auth_library, | 82 AuthLibrary* auth_library, |
| 21 #if defined(OS_WIN) | 83 #if defined(OS_WIN) |
| 22 ULONG max_token_length, | 84 ULONG max_token_length, |
| 23 #endif | 85 #endif |
| 24 URLSecurityManager* url_security_manager, | 86 URLSecurityManager* url_security_manager, |
| 25 HostResolver* resolver, | 87 HostResolver* resolver, |
| 26 bool disable_cname_lookup, | 88 bool disable_cname_lookup, |
| 27 bool use_port) | 89 bool use_port) |
| 28 #if defined(OS_WIN) | 90 #if defined(OS_WIN) |
| (...skipping 10 matching lines...) Expand all Loading... |
| 39 has_username_and_password_(false), | 101 has_username_and_password_(false), |
| 40 user_callback_(NULL), | 102 user_callback_(NULL), |
| 41 auth_token_(NULL), | 103 auth_token_(NULL), |
| 42 next_state_(STATE_NONE), | 104 next_state_(STATE_NONE), |
| 43 url_security_manager_(url_security_manager) { | 105 url_security_manager_(url_security_manager) { |
| 44 } | 106 } |
| 45 | 107 |
| 46 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { | 108 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { |
| 47 } | 109 } |
| 48 | 110 |
| 49 int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( | |
| 50 const string16* username, | |
| 51 const string16* password, | |
| 52 const HttpRequestInfo* request, | |
| 53 CompletionCallback* callback, | |
| 54 std::string* auth_token) { | |
| 55 DCHECK(user_callback_ == NULL); | |
| 56 DCHECK((username == NULL) == (password == NULL)); | |
| 57 DCHECK(auth_token_ == NULL); | |
| 58 auth_token_ = auth_token; | |
| 59 if (already_called_) { | |
| 60 DCHECK((!has_username_and_password_ && username == NULL) || | |
| 61 (has_username_and_password_ && *username == username_ && | |
| 62 *password == password_)); | |
| 63 next_state_ = STATE_GENERATE_AUTH_TOKEN; | |
| 64 } else { | |
| 65 already_called_ = true; | |
| 66 if (username) { | |
| 67 has_username_and_password_ = true; | |
| 68 username_ = *username; | |
| 69 password_ = *password; | |
| 70 } | |
| 71 next_state_ = STATE_RESOLVE_CANONICAL_NAME; | |
| 72 } | |
| 73 int rv = DoLoop(OK); | |
| 74 if (rv == ERR_IO_PENDING) | |
| 75 user_callback_ = callback; | |
| 76 return rv; | |
| 77 } | |
| 78 | |
| 79 // The Negotiate challenge header looks like: | |
| 80 // WWW-Authenticate: NEGOTIATE auth-data | |
| 81 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { | |
| 82 #if defined(OS_POSIX) | |
| 83 if (!auth_system_.Init()) { | |
| 84 VLOG(1) << "can't initialize GSSAPI library"; | |
| 85 return false; | |
| 86 } | |
| 87 // GSSAPI does not provide a way to enter username/password to | |
| 88 // obtain a TGT. If the default credentials are not allowed for | |
| 89 // a particular site (based on whitelist), fall back to a | |
| 90 // different scheme. | |
| 91 if (!AllowsDefaultCredentials()) | |
| 92 return false; | |
| 93 #endif | |
| 94 if (CanDelegate()) | |
| 95 auth_system_.Delegate(); | |
| 96 auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE; | |
| 97 score_ = 4; | |
| 98 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; | |
| 99 HttpAuth::AuthorizationResult auth_result = | |
| 100 auth_system_.ParseChallenge(challenge); | |
| 101 return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); | |
| 102 } | |
| 103 | |
| 104 HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( | |
| 105 HttpAuth::ChallengeTokenizer* challenge) { | |
| 106 return auth_system_.ParseChallenge(challenge); | |
| 107 } | |
| 108 | |
| 109 // Require identity on first pass instead of second. | |
| 110 bool HttpAuthHandlerNegotiate::NeedsIdentity() { | |
| 111 return auth_system_.NeedsIdentity(); | |
| 112 } | |
| 113 | |
| 114 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { | |
| 115 if (target_ == HttpAuth::AUTH_PROXY) | |
| 116 return true; | |
| 117 if (!url_security_manager_) | |
| 118 return false; | |
| 119 return url_security_manager_->CanUseDefaultCredentials(origin_); | |
| 120 } | |
| 121 | |
| 122 bool HttpAuthHandlerNegotiate::CanDelegate() const { | |
| 123 // TODO(cbentzel): Should delegation be allowed on proxies? | |
| 124 if (target_ == HttpAuth::AUTH_PROXY) | |
| 125 return false; | |
| 126 if (!url_security_manager_) | |
| 127 return false; | |
| 128 return url_security_manager_->CanDelegate(origin_); | |
| 129 } | |
| 130 | |
| 131 std::wstring HttpAuthHandlerNegotiate::CreateSPN( | 111 std::wstring HttpAuthHandlerNegotiate::CreateSPN( |
| 132 const AddressList& address_list, const GURL& origin) { | 112 const AddressList& address_list, const GURL& origin) { |
| 133 // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, | 113 // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, |
| 134 // and in the form HTTP@<host>:<port> through GSSAPI | 114 // and in the form HTTP@<host>:<port> through GSSAPI |
| 135 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx | 115 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx |
| 136 // | 116 // |
| 137 // However, reality differs from the specification. A good description of | 117 // However, reality differs from the specification. A good description of |
| 138 // the problems can be found here: | 118 // the problems can be found here: |
| 139 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx | 119 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx |
| 140 // | 120 // |
| (...skipping 29 matching lines...) Expand all Loading... |
| 170 #endif | 150 #endif |
| 171 if (port != 80 && port != 443 && use_port_) { | 151 if (port != 80 && port != 443 && use_port_) { |
| 172 return ASCIIToWide(base::StringPrintf("HTTP%c%s:%d", kSpnSeparator, | 152 return ASCIIToWide(base::StringPrintf("HTTP%c%s:%d", kSpnSeparator, |
| 173 server.c_str(), port)); | 153 server.c_str(), port)); |
| 174 } else { | 154 } else { |
| 175 return ASCIIToWide(base::StringPrintf("HTTP%c%s", kSpnSeparator, | 155 return ASCIIToWide(base::StringPrintf("HTTP%c%s", kSpnSeparator, |
| 176 server.c_str())); | 156 server.c_str())); |
| 177 } | 157 } |
| 178 } | 158 } |
| 179 | 159 |
| 160 HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( |
| 161 HttpAuth::ChallengeTokenizer* challenge) { |
| 162 return auth_system_.ParseChallenge(challenge); |
| 163 } |
| 164 |
| 165 // Require identity on first pass instead of second. |
| 166 bool HttpAuthHandlerNegotiate::NeedsIdentity() { |
| 167 return auth_system_.NeedsIdentity(); |
| 168 } |
| 169 |
| 170 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { |
| 171 if (target_ == HttpAuth::AUTH_PROXY) |
| 172 return true; |
| 173 if (!url_security_manager_) |
| 174 return false; |
| 175 return url_security_manager_->CanUseDefaultCredentials(origin_); |
| 176 } |
| 177 |
| 178 // The Negotiate challenge header looks like: |
| 179 // WWW-Authenticate: NEGOTIATE auth-data |
| 180 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { |
| 181 #if defined(OS_POSIX) |
| 182 if (!auth_system_.Init()) { |
| 183 VLOG(1) << "can't initialize GSSAPI library"; |
| 184 return false; |
| 185 } |
| 186 // GSSAPI does not provide a way to enter username/password to |
| 187 // obtain a TGT. If the default credentials are not allowed for |
| 188 // a particular site (based on whitelist), fall back to a |
| 189 // different scheme. |
| 190 if (!AllowsDefaultCredentials()) |
| 191 return false; |
| 192 #endif |
| 193 if (CanDelegate()) |
| 194 auth_system_.Delegate(); |
| 195 auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE; |
| 196 score_ = 4; |
| 197 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; |
| 198 HttpAuth::AuthorizationResult auth_result = |
| 199 auth_system_.ParseChallenge(challenge); |
| 200 return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); |
| 201 } |
| 202 |
| 203 int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( |
| 204 const string16* username, |
| 205 const string16* password, |
| 206 const HttpRequestInfo* request, |
| 207 CompletionCallback* callback, |
| 208 std::string* auth_token) { |
| 209 DCHECK(user_callback_ == NULL); |
| 210 DCHECK((username == NULL) == (password == NULL)); |
| 211 DCHECK(auth_token_ == NULL); |
| 212 auth_token_ = auth_token; |
| 213 if (already_called_) { |
| 214 DCHECK((!has_username_and_password_ && username == NULL) || |
| 215 (has_username_and_password_ && *username == username_ && |
| 216 *password == password_)); |
| 217 next_state_ = STATE_GENERATE_AUTH_TOKEN; |
| 218 } else { |
| 219 already_called_ = true; |
| 220 if (username) { |
| 221 has_username_and_password_ = true; |
| 222 username_ = *username; |
| 223 password_ = *password; |
| 224 } |
| 225 next_state_ = STATE_RESOLVE_CANONICAL_NAME; |
| 226 } |
| 227 int rv = DoLoop(OK); |
| 228 if (rv == ERR_IO_PENDING) |
| 229 user_callback_ = callback; |
| 230 return rv; |
| 231 } |
| 232 |
| 233 void HttpAuthHandlerNegotiate::OnIOComplete(int result) { |
| 234 int rv = DoLoop(result); |
| 235 if (rv != ERR_IO_PENDING) |
| 236 DoCallback(rv); |
| 237 } |
| 238 |
| 239 void HttpAuthHandlerNegotiate::DoCallback(int rv) { |
| 240 DCHECK(rv != ERR_IO_PENDING); |
| 241 DCHECK(user_callback_); |
| 242 CompletionCallback* callback = user_callback_; |
| 243 user_callback_ = NULL; |
| 244 callback->Run(rv); |
| 245 } |
| 246 |
| 180 int HttpAuthHandlerNegotiate::DoLoop(int result) { | 247 int HttpAuthHandlerNegotiate::DoLoop(int result) { |
| 181 DCHECK(next_state_ != STATE_NONE); | 248 DCHECK(next_state_ != STATE_NONE); |
| 182 | 249 |
| 183 int rv = result; | 250 int rv = result; |
| 184 do { | 251 do { |
| 185 State state = next_state_; | 252 State state = next_state_; |
| 186 next_state_ = STATE_NONE; | 253 next_state_ = STATE_NONE; |
| 187 switch (state) { | 254 switch (state) { |
| 188 case STATE_RESOLVE_CANONICAL_NAME: | 255 case STATE_RESOLVE_CANONICAL_NAME: |
| 189 DCHECK_EQ(OK, rv); | 256 DCHECK_EQ(OK, rv); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 // TODO(cbentzel): This should possibly be done async. | 313 // TODO(cbentzel): This should possibly be done async. |
| 247 return auth_system_.GenerateAuthToken(username, password, spn_, auth_token_); | 314 return auth_system_.GenerateAuthToken(username, password, spn_, auth_token_); |
| 248 } | 315 } |
| 249 | 316 |
| 250 int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) { | 317 int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) { |
| 251 DCHECK_NE(ERR_IO_PENDING, rv); | 318 DCHECK_NE(ERR_IO_PENDING, rv); |
| 252 auth_token_ = NULL; | 319 auth_token_ = NULL; |
| 253 return rv; | 320 return rv; |
| 254 } | 321 } |
| 255 | 322 |
| 256 void HttpAuthHandlerNegotiate::OnIOComplete(int result) { | 323 bool HttpAuthHandlerNegotiate::CanDelegate() const { |
| 257 int rv = DoLoop(result); | 324 // TODO(cbentzel): Should delegation be allowed on proxies? |
| 258 if (rv != ERR_IO_PENDING) | 325 if (target_ == HttpAuth::AUTH_PROXY) |
| 259 DoCallback(rv); | 326 return false; |
| 260 } | 327 if (!url_security_manager_) |
| 261 | 328 return false; |
| 262 void HttpAuthHandlerNegotiate::DoCallback(int rv) { | 329 return url_security_manager_->CanDelegate(origin_); |
| 263 DCHECK(rv != ERR_IO_PENDING); | |
| 264 DCHECK(user_callback_); | |
| 265 CompletionCallback* callback = user_callback_; | |
| 266 user_callback_ = NULL; | |
| 267 callback->Run(rv); | |
| 268 } | |
| 269 | |
| 270 HttpAuthHandlerNegotiate::Factory::Factory() | |
| 271 : disable_cname_lookup_(false), | |
| 272 use_port_(false), | |
| 273 #if defined(OS_WIN) | |
| 274 max_token_length_(0), | |
| 275 first_creation_(true), | |
| 276 is_unsupported_(false), | |
| 277 #endif | |
| 278 auth_library_(NULL) { | |
| 279 } | |
| 280 | |
| 281 HttpAuthHandlerNegotiate::Factory::~Factory() { | |
| 282 } | |
| 283 | |
| 284 void HttpAuthHandlerNegotiate::Factory::set_host_resolver( | |
| 285 HostResolver* resolver) { | |
| 286 resolver_ = resolver; | |
| 287 } | |
| 288 | |
| 289 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( | |
| 290 HttpAuth::ChallengeTokenizer* challenge, | |
| 291 HttpAuth::Target target, | |
| 292 const GURL& origin, | |
| 293 CreateReason reason, | |
| 294 int digest_nonce_count, | |
| 295 const BoundNetLog& net_log, | |
| 296 scoped_ptr<HttpAuthHandler>* handler) { | |
| 297 #if defined(OS_WIN) | |
| 298 if (is_unsupported_ || reason == CREATE_PREEMPTIVE) | |
| 299 return ERR_UNSUPPORTED_AUTH_SCHEME; | |
| 300 if (max_token_length_ == 0) { | |
| 301 int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, | |
| 302 &max_token_length_); | |
| 303 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) | |
| 304 is_unsupported_ = true; | |
| 305 if (rv != OK) | |
| 306 return rv; | |
| 307 } | |
| 308 // TODO(cbentzel): Move towards model of parsing in the factory | |
| 309 // method and only constructing when valid. | |
| 310 scoped_ptr<HttpAuthHandler> tmp_handler( | |
| 311 new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, | |
| 312 url_security_manager(), resolver_, | |
| 313 disable_cname_lookup_, use_port_)); | |
| 314 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | |
| 315 return ERR_INVALID_RESPONSE; | |
| 316 handler->swap(tmp_handler); | |
| 317 return OK; | |
| 318 #elif defined(OS_POSIX) | |
| 319 // TODO(ahendrickson): Move towards model of parsing in the factory | |
| 320 // method and only constructing when valid. | |
| 321 scoped_ptr<HttpAuthHandler> tmp_handler( | |
| 322 new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), | |
| 323 resolver_, disable_cname_lookup_, | |
| 324 use_port_)); | |
| 325 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | |
| 326 return ERR_INVALID_RESPONSE; | |
| 327 handler->swap(tmp_handler); | |
| 328 return OK; | |
| 329 #endif | |
| 330 } | 330 } |
| 331 | 331 |
| 332 } // namespace net | 332 } // namespace net |
| OLD | NEW |