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 3// found in the LICENSE file. 4 5#include "net/http/http_auth_handler_ntlm.h" 6 7#if !defined(NTLM_SSPI) 8#include "base/base64.h" 9#endif 10#include "base/logging.h" 11#include "base/string_util.h" 12#include "base/utf_string_conversions.h" 13#include "net/base/net_errors.h" 14#include "net/base/net_util.h" 15 16namespace net { 17 18HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge( 19 HttpAuth::ChallengeTokenizer* challenge) { 20 return ParseChallenge(challenge, false); 21} 22 23bool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) { 24 auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM; 25 score_ = 3; 26 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; 27 28 return ParseChallenge(tok, true) == HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 29} 30 31int HttpAuthHandlerNTLM::GenerateAuthTokenImpl( 32 const string16* username, 33 const string16* password, 34 const HttpRequestInfo* request, 35 CompletionCallback* callback, 36 std::string* auth_token) { 37#if defined(NTLM_SSPI) 38 return auth_sspi_.GenerateAuthToken( 39 username, 40 password, 41 CreateSPN(origin_), 42 auth_token); 43#else // !defined(NTLM_SSPI) 44 // TODO(cbentzel): Shouldn't be hitting this case. 45 if (!username || !password) { 46 LOG(ERROR) << "Username and password are expected to be non-NULL."; 47 return ERR_MISSING_AUTH_CREDENTIALS; 48 } 49 // TODO(wtc): See if we can use char* instead of void* for in_buf and 50 // out_buf. This change will need to propagate to GetNextToken, 51 // GenerateType1Msg, and GenerateType3Msg, and perhaps further. 52 const void* in_buf; 53 void* out_buf; 54 uint32 in_buf_len, out_buf_len; 55 std::string decoded_auth_data; 56 57 // |username| may be in the form "DOMAIN\user". Parse it into the two 58 // components. 59 string16 domain; 60 string16 user; 61 const char16 backslash_character = '\\'; 62 size_t backslash_idx = username->find(backslash_character); 63 if (backslash_idx == string16::npos) { 64 user = *username; 65 } else { 66 domain = username->substr(0, backslash_idx); 67 user = username->substr(backslash_idx + 1); 68 } 69 domain_ = domain; 70 username_ = user; 71 password_ = *password; 72 73 // Initial challenge. 74 if (auth_data_.empty()) { 75 in_buf_len = 0; 76 in_buf = NULL; 77 int rv = InitializeBeforeFirstChallenge(); 78 if (rv != OK) 79 return rv; 80 } else { 81 if (!base::Base64Decode(auth_data_, &decoded_auth_data)) { 82 LOG(ERROR) << "Unexpected problem Base64 decoding."; 83 return ERR_UNEXPECTED; 84 } 85 in_buf_len = decoded_auth_data.length(); 86 in_buf = decoded_auth_data.data(); 87 } 88 89 int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len); 90 if (rv != OK) 91 return rv; 92 93 // Base64 encode data in output buffer and prepend "NTLM ". 94 std::string encode_input(static_cast<char*>(out_buf), out_buf_len); 95 std::string encode_output; 96 bool base64_rv = base::Base64Encode(encode_input, &encode_output); 97 // OK, we are done with |out_buf| 98 free(out_buf); 99 if (!base64_rv) { 100 LOG(ERROR) << "Unexpected problem Base64 encoding."; 101 return ERR_UNEXPECTED; 102 } 103 *auth_token = std::string("NTLM ") + encode_output; 104 return OK; 105#endif 106} 107 108// The NTLM challenge header looks like: 109// WWW-Authenticate: NTLM auth-data 110HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge( 111 HttpAuth::ChallengeTokenizer* tok, bool initial_challenge) { 112#if defined(NTLM_SSPI) 113 // auth_sspi_ contains state for whether or not this is the initial challenge. 114 return auth_sspi_.ParseChallenge(tok); 115#else 116 // TODO(cbentzel): Most of the logic between SSPI, GSSAPI, and portable NTLM 117 // authentication parsing could probably be shared - just need to know if 118 // there was previously a challenge round. 119 // TODO(cbentzel): Write a test case to validate that auth_data_ is left empty 120 // in all failure conditions. 121 auth_data_.clear(); 122 123 // Verify the challenge's auth-scheme. 124 if (!LowerCaseEqualsASCII(tok->scheme(), "ntlm")) 125 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 126 127 std::string base64_param = tok->base64_param(); 128 if (base64_param.empty()) { 129 if (!initial_challenge) 130 return HttpAuth::AUTHORIZATION_RESULT_REJECT; 131 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 132 } else { 133 if (initial_challenge) 134 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 135 } 136 137 auth_data_ = base64_param; 138 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 139#endif // defined(NTLM_SSPI) 140} 141 142// static 143std::wstring HttpAuthHandlerNTLM::CreateSPN(const GURL& origin) { 144 // The service principal name of the destination server. See 145 // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx 146 std::wstring target(L"HTTP/"); 147 target.append(ASCIIToWide(GetHostAndPort(origin))); 148 return target; 149} 150 151} // namespace net 152