Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(458)

Side by Side Diff: net/ntlm/ntlm_client.cc

Issue 2904633002: Replace NTLMv1 implementation with a functionally equivalent one.
Patch Set: Fix uninitialized read Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/ntlm/ntlm_client.h ('k') | net/ntlm/ntlm_client_fuzzer.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « net/ntlm/ntlm_client.h ('k') | net/ntlm/ntlm_client_fuzzer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698