1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_ntlm.h" 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if !defined(NTLM_SSPI) 8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/base64.h" 93345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#endif 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h" 11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/net_errors.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_util.h" 15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net { 17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge( 1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen HttpAuth::ChallengeTokenizer* challenge) { 2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ParseChallenge(challenge, false); 2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) { 2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM; 2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen score_ = 3; 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; 2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ParseChallenge(tok, true) == HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint HttpAuthHandlerNTLM::GenerateAuthTokenImpl( 323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const string16* username, 333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const string16* password, 34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott const HttpRequestInfo* request, 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CompletionCallback* callback, 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string* auth_token) { 37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#if defined(NTLM_SSPI) 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return auth_sspi_.GenerateAuthToken( 39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott username, 40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott password, 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CreateSPN(origin_), 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch auth_token); 43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#else // !defined(NTLM_SSPI) 443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // TODO(cbentzel): Shouldn't be hitting this case. 453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!username || !password) { 463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LOG(ERROR) << "Username and password are expected to be non-NULL."; 473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return ERR_MISSING_AUTH_CREDENTIALS; 483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // TODO(wtc): See if we can use char* instead of void* for in_buf and 50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // out_buf. This change will need to propagate to GetNextToken, 51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // GenerateType1Msg, and GenerateType3Msg, and perhaps further. 52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott const void* in_buf; 53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void* out_buf; 54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott uint32 in_buf_len, out_buf_len; 55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string decoded_auth_data; 56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // |username| may be in the form "DOMAIN\user". Parse it into the two 58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // components. 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick string16 domain; 603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick string16 user; 613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const char16 backslash_character = '\\'; 623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick size_t backslash_idx = username->find(backslash_character); 633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (backslash_idx == string16::npos) { 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch user = *username; 65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch domain = username->substr(0, backslash_idx); 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch user = username->substr(backslash_idx + 1); 68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick domain_ = domain; 703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick username_ = user; 713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick password_ = *password; 72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Initial challenge. 74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (auth_data_.empty()) { 75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott in_buf_len = 0; 76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott in_buf = NULL; 77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int rv = InitializeBeforeFirstChallenge(); 78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (rv != OK) 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return rv; 80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!base::Base64Decode(auth_data_, &decoded_auth_data)) { 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Unexpected problem Base64 decoding."; 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ERR_UNEXPECTED; 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott in_buf_len = decoded_auth_data.length(); 86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott in_buf = decoded_auth_data.data(); 87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len); 90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (rv != OK) 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return rv; 92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Base64 encode data in output buffer and prepend "NTLM ". 94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string encode_input(static_cast<char*>(out_buf), out_buf_len); 95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string encode_output; 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool base64_rv = base::Base64Encode(encode_input, &encode_output); 97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // OK, we are done with |out_buf| 98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott free(out_buf); 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!base64_rv) { 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Unexpected problem Base64 encoding."; 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ERR_UNEXPECTED; 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *auth_token = std::string("NTLM ") + encode_output; 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return OK; 105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif 106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// The NTLM challenge header looks like: 1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// WWW-Authenticate: NTLM auth-data 1103345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickHttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge( 1113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick HttpAuth::ChallengeTokenizer* tok, bool initial_challenge) { 112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#if defined(NTLM_SSPI) 1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // auth_sspi_ contains state for whether or not this is the initial challenge. 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return auth_sspi_.ParseChallenge(tok); 115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#else 1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // TODO(cbentzel): Most of the logic between SSPI, GSSAPI, and portable NTLM 1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // authentication parsing could probably be shared - just need to know if 1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // there was previously a challenge round. 119731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // TODO(cbentzel): Write a test case to validate that auth_data_ is left empty 120731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // in all failure conditions. 121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott auth_data_.clear(); 122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Verify the challenge's auth-scheme. 124731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!LowerCaseEqualsASCII(tok->scheme(), "ntlm")) 1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return HttpAuth::AUTHORIZATION_RESULT_INVALID; 126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 127731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string base64_param = tok->base64_param(); 128731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (base64_param.empty()) { 1293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!initial_challenge) 1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return HttpAuth::AUTHORIZATION_RESULT_REJECT; 1313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 1323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } else { 1333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (initial_challenge) 1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return HttpAuth::AUTHORIZATION_RESULT_INVALID; 1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 137731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick auth_data_ = base64_param; 1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif // defined(NTLM_SSPI) 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring HttpAuthHandlerNTLM::CreateSPN(const GURL& origin) { 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The service principal name of the destination server. See 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring target(L"HTTP/"); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch target.append(ASCIIToWide(GetHostAndPort(origin))); 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return target; 149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} // namespace net 152