OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/ntlm/ntlm_client.h" |
| 6 |
| 7 #include <string.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "net/ntlm/ntlm.h" |
| 12 #include "net/ntlm/ntlm_buffer_reader.h" |
| 13 #include "net/ntlm/ntlm_buffer_writer.h" |
| 14 |
| 15 namespace net { |
| 16 namespace ntlm { |
| 17 |
| 18 namespace { |
| 19 // Parses the challenge message and returns the |challenge_flags| and |
| 20 // |server_challenge| into the supplied buffer. |
| 21 // |server_challenge| must contain at least 8 bytes. |
| 22 bool ParseChallengeMessage(const Buffer& challenge_message, |
| 23 NegotiateFlags* challenge_flags, |
| 24 uint8_t* server_challenge) { |
| 25 NtlmBufferReader challenge_reader(challenge_message); |
| 26 |
| 27 return challenge_reader.MatchMessageHeader(MessageType::kChallenge) && |
| 28 challenge_reader.SkipSecurityBufferWithValidation() && |
| 29 challenge_reader.ReadFlags(challenge_flags) && |
| 30 challenge_reader.ReadBytes(server_challenge, kChallengeLen); |
| 31 } |
| 32 |
| 33 bool WriteAuthenticateMessage(NtlmBufferWriter* authenticate_writer, |
| 34 SecurityBuffer lm_payload, |
| 35 SecurityBuffer ntlm_payload, |
| 36 SecurityBuffer domain_payload, |
| 37 SecurityBuffer username_payload, |
| 38 SecurityBuffer hostname_payload, |
| 39 NegotiateFlags authenticate_flags) { |
| 40 return authenticate_writer->WriteMessageHeader(MessageType::kAuthenticate) && |
| 41 authenticate_writer->WriteSecurityBuffer(lm_payload) && |
| 42 authenticate_writer->WriteSecurityBuffer(ntlm_payload) && |
| 43 authenticate_writer->WriteSecurityBuffer(domain_payload) && |
| 44 authenticate_writer->WriteSecurityBuffer(username_payload) && |
| 45 authenticate_writer->WriteSecurityBuffer(hostname_payload) && |
| 46 authenticate_writer->WriteSecurityBuffer( |
| 47 SecurityBuffer(kAuthenticateHeaderLenV1, 0)) && |
| 48 authenticate_writer->WriteFlags(authenticate_flags); |
| 49 } |
| 50 |
| 51 bool WriteResponsePayloads(NtlmBufferWriter* authenticate_writer, |
| 52 const uint8_t* lm_response, |
| 53 size_t lm_response_len, |
| 54 const uint8_t* ntlm_response, |
| 55 size_t ntlm_response_len) { |
| 56 return authenticate_writer->WriteBytes(lm_response, lm_response_len) && |
| 57 authenticate_writer->WriteBytes(ntlm_response, ntlm_response_len); |
| 58 } |
| 59 |
| 60 bool WriteStringPayloads(NtlmBufferWriter* authenticate_writer, |
| 61 bool is_unicode, |
| 62 const base::string16& domain, |
| 63 const base::string16& username, |
| 64 const std::string& hostname) { |
| 65 if (is_unicode) { |
| 66 return authenticate_writer->WriteUtf16String(domain) && |
| 67 authenticate_writer->WriteUtf16String(username) && |
| 68 authenticate_writer->WriteUtf8AsUtf16String(hostname); |
| 69 } else { |
| 70 return authenticate_writer->WriteUtf16AsUtf8String(domain) && |
| 71 authenticate_writer->WriteUtf16AsUtf8String(username) && |
| 72 authenticate_writer->WriteUtf8String(hostname); |
| 73 } |
| 74 } |
| 75 |
| 76 // Returns the size in bytes of a string16 depending whether unicode |
| 77 // was negotiated. |
| 78 size_t GetStringPayloadLength(const base::string16& str, bool is_unicode) { |
| 79 if (is_unicode) |
| 80 return str.length() * 2; |
| 81 |
| 82 // When |WriteUtf16AsUtf8String| is called with a |base::string16|, the string |
| 83 // is converted to UTF8. Do the conversion to ensure that the character |
| 84 // count is correct. |
| 85 return base::UTF16ToUTF8(str).length(); |
| 86 } |
| 87 |
| 88 // Returns the size in bytes of a std::string depending whether unicode |
| 89 // was negotiated. |
| 90 size_t GetStringPayloadLength(const std::string& str, bool is_unicode) { |
| 91 if (!is_unicode) |
| 92 return str.length(); |
| 93 |
| 94 return base::UTF8ToUTF16(str).length() * 2; |
| 95 } |
| 96 |
| 97 } // namespace |
| 98 |
| 99 NtlmClient::NtlmClient() : negotiate_flags_(kNegotiateMessageFlags) { |
| 100 // Just generate the negotiate message once and hold on to it. It never |
| 101 // changes and in a NTLMv2 it's used as an input |
| 102 // to the Message Integrity Check in the Authenticate message. |
| 103 GenerateNegotiateMessage(); |
| 104 } |
| 105 |
| 106 NtlmClient::~NtlmClient() {} |
| 107 |
| 108 Buffer NtlmClient::GetNegotiateMessage() const { |
| 109 return negotiate_message_; |
| 110 } |
| 111 |
| 112 void NtlmClient::GenerateNegotiateMessage() { |
| 113 NtlmBufferWriter writer(kNegotiateMessageLen); |
| 114 bool result = |
| 115 writer.WriteMessageHeader(MessageType::kNegotiate) && |
| 116 writer.WriteFlags(negotiate_flags_) && |
| 117 writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) && |
| 118 writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) && |
| 119 writer.IsEndOfBuffer(); |
| 120 |
| 121 DCHECK(result); |
| 122 |
| 123 negotiate_message_ = writer.Pass(); |
| 124 } |
| 125 |
| 126 Buffer NtlmClient::GenerateAuthenticateMessage( |
| 127 const base::string16& domain, |
| 128 const base::string16& username, |
| 129 const base::string16& password, |
| 130 const std::string& hostname, |
| 131 const uint8_t* client_challenge, |
| 132 const Buffer& server_challenge_message) const { |
| 133 // Limit the size of strings that are accepted. As an absolute limit any |
| 134 // field represented by a |SecurityBuffer| or |AvPair| must be less than |
| 135 // UINT16_MAX bytes long. The strings are restricted to the maximum sizes |
| 136 // without regard to encoding. As such this isn't intended to restrict all |
| 137 // invalid inputs, only to allow all possible valid inputs. |
| 138 // |
| 139 // |domain| and |hostname| can be no longer than 255 characters. |
| 140 // |username| can be no longer than 104 characters. See [1]. |
| 141 // |password| can be no longer than 256 characters. See [2]. |
| 142 // |
| 143 // [1] - https://technet.microsoft.com/en-us/library/bb726984.aspx |
| 144 // [2] - https://technet.microsoft.com/en-us/library/cc512606.aspx |
| 145 if (hostname.length() > kMaxFqdnLen || domain.length() > kMaxFqdnLen || |
| 146 username.length() > kMaxUsernameLen || |
| 147 password.length() > kMaxPasswordLen) |
| 148 return Buffer(); |
| 149 |
| 150 NegotiateFlags challenge_flags; |
| 151 uint8_t server_challenge[kChallengeLen]; |
| 152 |
| 153 // Read the flags and the server's random challenge from the challenge |
| 154 // message. |
| 155 if (!ParseChallengeMessage(server_challenge_message, &challenge_flags, |
| 156 server_challenge)) { |
| 157 return Buffer(); |
| 158 } |
| 159 |
| 160 // Calculate the responses for the authenticate message. |
| 161 uint8_t lm_response[kResponseLenV1]; |
| 162 uint8_t ntlm_response[kResponseLenV1]; |
| 163 |
| 164 // Always use extended session security even if the server tries to downgrade. |
| 165 NegotiateFlags authenticate_flags = (challenge_flags & negotiate_flags_) | |
| 166 NegotiateFlags::kExtendedSessionSecurity; |
| 167 |
| 168 // Generate the LM and NTLM responses. |
| 169 GenerateResponsesV1WithSessionSecurity( |
| 170 password, server_challenge, client_challenge, lm_response, ntlm_response); |
| 171 |
| 172 // Calculate all the payload lengths and offsets. |
| 173 bool is_unicode = (authenticate_flags & NegotiateFlags::kUnicode) == |
| 174 NegotiateFlags::kUnicode; |
| 175 |
| 176 SecurityBuffer lm_info; |
| 177 SecurityBuffer ntlm_info; |
| 178 SecurityBuffer domain_info; |
| 179 SecurityBuffer username_info; |
| 180 SecurityBuffer hostname_info; |
| 181 size_t authenticate_message_len; |
| 182 CalculatePayloadLayout(is_unicode, domain, username, hostname, &lm_info, |
| 183 &ntlm_info, &domain_info, &username_info, |
| 184 &hostname_info, &authenticate_message_len); |
| 185 |
| 186 NtlmBufferWriter authenticate_writer(authenticate_message_len); |
| 187 bool writer_result = WriteAuthenticateMessage( |
| 188 &authenticate_writer, lm_info, ntlm_info, domain_info, username_info, |
| 189 hostname_info, authenticate_flags); |
| 190 DCHECK(writer_result); |
| 191 DCHECK_EQ(authenticate_writer.GetCursor(), GetAuthenticateHeaderLength()); |
| 192 |
| 193 writer_result = |
| 194 WriteResponsePayloads(&authenticate_writer, lm_response, lm_info.length, |
| 195 ntlm_response, ntlm_info.length); |
| 196 DCHECK(writer_result); |
| 197 DCHECK_EQ(authenticate_writer.GetCursor(), domain_info.offset); |
| 198 |
| 199 writer_result = WriteStringPayloads(&authenticate_writer, is_unicode, domain, |
| 200 username, hostname); |
| 201 DCHECK(writer_result); |
| 202 DCHECK(authenticate_writer.IsEndOfBuffer()); |
| 203 DCHECK_EQ(authenticate_message_len, authenticate_writer.GetLength()); |
| 204 |
| 205 return authenticate_writer.Pass(); |
| 206 } |
| 207 |
| 208 void NtlmClient::CalculatePayloadLayout( |
| 209 bool is_unicode, |
| 210 const base::string16& domain, |
| 211 const base::string16& username, |
| 212 const std::string& hostname, |
| 213 SecurityBuffer* lm_info, |
| 214 SecurityBuffer* ntlm_info, |
| 215 SecurityBuffer* domain_info, |
| 216 SecurityBuffer* username_info, |
| 217 SecurityBuffer* hostname_info, |
| 218 size_t* authenticate_message_len) const { |
| 219 size_t upto = GetAuthenticateHeaderLength(); |
| 220 |
| 221 lm_info->offset = upto; |
| 222 lm_info->length = kResponseLenV1; |
| 223 upto += lm_info->length; |
| 224 |
| 225 ntlm_info->offset = upto; |
| 226 ntlm_info->length = GetNtlmResponseLength(); |
| 227 upto += ntlm_info->length; |
| 228 |
| 229 domain_info->offset = upto; |
| 230 domain_info->length = GetStringPayloadLength(domain, is_unicode); |
| 231 upto += domain_info->length; |
| 232 |
| 233 username_info->offset = upto; |
| 234 username_info->length = GetStringPayloadLength(username, is_unicode); |
| 235 upto += username_info->length; |
| 236 |
| 237 hostname_info->offset = upto; |
| 238 hostname_info->length = GetStringPayloadLength(hostname, is_unicode); |
| 239 upto += hostname_info->length; |
| 240 |
| 241 *authenticate_message_len = upto; |
| 242 } |
| 243 |
| 244 size_t NtlmClient::GetAuthenticateHeaderLength() const { |
| 245 return kAuthenticateHeaderLenV1; |
| 246 } |
| 247 |
| 248 size_t NtlmClient::GetNtlmResponseLength() const { |
| 249 return kResponseLenV1; |
| 250 } |
| 251 |
| 252 } // namespace ntlm |
| 253 } // namespace net |
OLD | NEW |