1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/ntlm/ntlm.h"
11
12 #include <string.h>
13
14 #include "base/check_op.h"
15 #include "base/containers/span.h"
16 #include "base/notreached.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "net/base/net_string_util.h"
19 #include "net/ntlm/ntlm_buffer_writer.h"
20 #include "net/ntlm/ntlm_constants.h"
21 #include "third_party/boringssl/src/include/openssl/des.h"
22 #include "third_party/boringssl/src/include/openssl/hmac.h"
23 #include "third_party/boringssl/src/include/openssl/md4.h"
24 #include "third_party/boringssl/src/include/openssl/md5.h"
25
26 namespace net::ntlm {
27
28 namespace {
29
30 // Takes the parsed target info in |av_pairs| and performs the following
31 // actions.
32 //
33 // 1) If a |TargetInfoAvId::kTimestamp| AvPair exists, |server_timestamp|
34 // is set to the payload.
35 // 2) If |is_mic_enabled| is true, the existing |TargetInfoAvId::kFlags| AvPair
36 // will have the |TargetInfoAvFlags::kMicPresent| bit set. If an existing
37 // flags AvPair does not already exist, a new one is added with the value of
38 // |TargetInfoAvFlags::kMicPresent|.
39 // 3) If |is_epa_enabled| is true, two new AvPair entries will be added to
40 // |av_pairs|. The first will be of type |TargetInfoAvId::kChannelBindings|
41 // and contains MD5(|channel_bindings|) as the payload. The second will be
42 // of type |TargetInfoAvId::kTargetName| and contains |spn| as a little
43 // endian UTF16 string.
44 // 4) Sets |target_info_len| to the size of |av_pairs| when serialized into
45 // a payload.
UpdateTargetInfoAvPairs(bool is_mic_enabled,bool is_epa_enabled,const std::string & channel_bindings,const std::string & spn,std::vector<AvPair> * av_pairs,uint64_t * server_timestamp,size_t * target_info_len)46 void UpdateTargetInfoAvPairs(bool is_mic_enabled,
47 bool is_epa_enabled,
48 const std::string& channel_bindings,
49 const std::string& spn,
50 std::vector<AvPair>* av_pairs,
51 uint64_t* server_timestamp,
52 size_t* target_info_len) {
53 // Do a pass to update flags and calculate current length and
54 // pull out the server timestamp if it is there.
55 *server_timestamp = UINT64_MAX;
56 *target_info_len = 0;
57
58 bool need_flags_added = is_mic_enabled;
59 for (AvPair& pair : *av_pairs) {
60 *target_info_len += pair.avlen + kAvPairHeaderLen;
61 switch (pair.avid) {
62 case TargetInfoAvId::kFlags:
63 // The parsing phase already set the payload to the |flags| field.
64 if (is_mic_enabled) {
65 pair.flags = pair.flags | TargetInfoAvFlags::kMicPresent;
66 }
67
68 need_flags_added = false;
69 break;
70 case TargetInfoAvId::kTimestamp:
71 // The parsing phase already set the payload to the |timestamp| field.
72 *server_timestamp = pair.timestamp;
73 break;
74 case TargetInfoAvId::kEol:
75 case TargetInfoAvId::kChannelBindings:
76 case TargetInfoAvId::kTargetName:
77 // The terminator, |kEol|, should already have been removed from the
78 // end of the list and would have been rejected if it has been inside
79 // the list. Additionally |kChannelBindings| and |kTargetName| pairs
80 // would have been rejected during the initial parsing. See
81 // |NtlmBufferReader::ReadTargetInfo|.
82 NOTREACHED();
83 default:
84 // Ignore entries we don't care about.
85 break;
86 }
87 }
88
89 if (need_flags_added) {
90 DCHECK(is_mic_enabled);
91 AvPair flags_pair(TargetInfoAvId::kFlags, sizeof(uint32_t));
92 flags_pair.flags = TargetInfoAvFlags::kMicPresent;
93
94 av_pairs->push_back(flags_pair);
95 *target_info_len += kAvPairHeaderLen + flags_pair.avlen;
96 }
97
98 if (is_epa_enabled) {
99 std::vector<uint8_t> channel_bindings_hash(kChannelBindingsHashLen, 0);
100
101 // Hash the channel bindings if they exist otherwise they remain zeros.
102 if (!channel_bindings.empty()) {
103 GenerateChannelBindingHashV2(
104 channel_bindings, *base::span(channel_bindings_hash)
105 .to_fixed_extent<kChannelBindingsHashLen>());
106 }
107
108 av_pairs->emplace_back(TargetInfoAvId::kChannelBindings,
109 std::move(channel_bindings_hash));
110
111 // Convert the SPN to little endian unicode.
112 std::u16string spn16 = base::UTF8ToUTF16(spn);
113 NtlmBufferWriter spn_writer(spn16.length() * 2);
114 bool spn_writer_result =
115 spn_writer.WriteUtf16String(spn16) && spn_writer.IsEndOfBuffer();
116 DCHECK(spn_writer_result);
117
118 av_pairs->emplace_back(TargetInfoAvId::kTargetName, spn_writer.Pass());
119
120 // Add the length of the two new AV Pairs to the total length.
121 *target_info_len +=
122 (2 * kAvPairHeaderLen) + kChannelBindingsHashLen + (spn16.length() * 2);
123 }
124
125 // Add extra space for the terminator at the end.
126 *target_info_len += kAvPairHeaderLen;
127 }
128
WriteUpdatedTargetInfo(const std::vector<AvPair> & av_pairs,size_t updated_target_info_len)129 std::vector<uint8_t> WriteUpdatedTargetInfo(const std::vector<AvPair>& av_pairs,
130 size_t updated_target_info_len) {
131 bool result = true;
132 NtlmBufferWriter writer(updated_target_info_len);
133 for (const AvPair& pair : av_pairs) {
134 result = writer.WriteAvPair(pair);
135 DCHECK(result);
136 }
137
138 result = writer.WriteAvPairTerminator() && writer.IsEndOfBuffer();
139 DCHECK(result);
140 return writer.Pass();
141 }
142
143 // Reads 7 bytes (56 bits) from |key_56| and writes them into 8 bytes of
144 // |key_64| with 7 bits in every byte. The least significant bits are
145 // undefined and a subsequent operation will set those bits with a parity bit.
146 // |key_56| must contain 7 bytes.
147 // |key_64| must contain 8 bytes.
Splay56To64(base::span<const uint8_t,7> key_56,base::span<uint8_t,8> key_64)148 void Splay56To64(base::span<const uint8_t, 7> key_56,
149 base::span<uint8_t, 8> key_64) {
150 key_64[0] = key_56[0];
151 key_64[1] = key_56[0] << 7 | key_56[1] >> 1;
152 key_64[2] = key_56[1] << 6 | key_56[2] >> 2;
153 key_64[3] = key_56[2] << 5 | key_56[3] >> 3;
154 key_64[4] = key_56[3] << 4 | key_56[4] >> 4;
155 key_64[5] = key_56[4] << 3 | key_56[5] >> 5;
156 key_64[6] = key_56[5] << 2 | key_56[6] >> 6;
157 key_64[7] = key_56[6] << 1;
158 }
159
160 } // namespace
161
Create3DesKeysFromNtlmHash(base::span<const uint8_t,kNtlmHashLen> ntlm_hash,base::span<uint8_t,24> keys)162 void Create3DesKeysFromNtlmHash(
163 base::span<const uint8_t, kNtlmHashLen> ntlm_hash,
164 base::span<uint8_t, 24> keys) {
165 // Put the first 112 bits from |ntlm_hash| into the first 16 bytes of
166 // |keys|.
167 Splay56To64(ntlm_hash.first<7>(), keys.first<8>());
168 Splay56To64(ntlm_hash.subspan<7, 7>(), keys.subspan<8, 8>());
169
170 // Put the next 2x 7 bits in bytes 16 and 17 of |keys|, then
171 // the last 2 bits in byte 18, then zero pad the rest of the final key.
172 keys[16] = ntlm_hash[14];
173 keys[17] = ntlm_hash[14] << 7 | ntlm_hash[15] >> 1;
174 keys[18] = ntlm_hash[15] << 6;
175 memset(keys.data() + 19, 0, 5);
176 }
177
GenerateNtlmHashV1(const std::u16string & password,base::span<uint8_t,kNtlmHashLen> hash)178 void GenerateNtlmHashV1(const std::u16string& password,
179 base::span<uint8_t, kNtlmHashLen> hash) {
180 size_t length = password.length() * 2;
181 NtlmBufferWriter writer(length);
182
183 // The writer will handle the big endian case if necessary.
184 bool result = writer.WriteUtf16String(password) && writer.IsEndOfBuffer();
185 DCHECK(result);
186
187 MD4(writer.GetBuffer().data(), writer.GetLength(), hash.data());
188 }
189
GenerateResponseDesl(base::span<const uint8_t,kNtlmHashLen> hash,base::span<const uint8_t,kChallengeLen> challenge,base::span<uint8_t,kResponseLenV1> response)190 void GenerateResponseDesl(base::span<const uint8_t, kNtlmHashLen> hash,
191 base::span<const uint8_t, kChallengeLen> challenge,
192 base::span<uint8_t, kResponseLenV1> response) {
193 constexpr size_t block_count = 3;
194 constexpr size_t block_size = sizeof(DES_cblock);
195 static_assert(kChallengeLen == block_size,
196 "kChallengeLen must equal block_size");
197 static_assert(kResponseLenV1 == block_count * block_size,
198 "kResponseLenV1 must equal block_count * block_size");
199
200 const DES_cblock* challenge_block =
201 reinterpret_cast<const DES_cblock*>(challenge.data());
202 uint8_t keys[block_count * block_size];
203
204 // Map the NTLM hash to three 8 byte DES keys, with 7 bits of the key in each
205 // byte and the least significant bit set with odd parity. Then encrypt the
206 // 8 byte challenge with each of the three keys. This produces three 8 byte
207 // encrypted blocks into |response|.
208 Create3DesKeysFromNtlmHash(hash, keys);
209 for (size_t ix = 0; ix < block_count * block_size; ix += block_size) {
210 DES_cblock* key_block = reinterpret_cast<DES_cblock*>(keys + ix);
211 DES_cblock* response_block =
212 reinterpret_cast<DES_cblock*>(response.data() + ix);
213
214 DES_key_schedule key_schedule;
215 DES_set_odd_parity(key_block);
216 DES_set_key(key_block, &key_schedule);
217 DES_ecb_encrypt(challenge_block, response_block, &key_schedule,
218 DES_ENCRYPT);
219 }
220 }
221
GenerateNtlmResponseV1(const std::u16string & password,base::span<const uint8_t,kChallengeLen> server_challenge,base::span<uint8_t,kResponseLenV1> ntlm_response)222 void GenerateNtlmResponseV1(
223 const std::u16string& password,
224 base::span<const uint8_t, kChallengeLen> server_challenge,
225 base::span<uint8_t, kResponseLenV1> ntlm_response) {
226 uint8_t ntlm_hash[kNtlmHashLen];
227 GenerateNtlmHashV1(password, ntlm_hash);
228 GenerateResponseDesl(ntlm_hash, server_challenge, ntlm_response);
229 }
230
GenerateResponsesV1(const std::u16string & password,base::span<const uint8_t,kChallengeLen> server_challenge,base::span<uint8_t,kResponseLenV1> lm_response,base::span<uint8_t,kResponseLenV1> ntlm_response)231 void GenerateResponsesV1(
232 const std::u16string& password,
233 base::span<const uint8_t, kChallengeLen> server_challenge,
234 base::span<uint8_t, kResponseLenV1> lm_response,
235 base::span<uint8_t, kResponseLenV1> ntlm_response) {
236 GenerateNtlmResponseV1(password, server_challenge, ntlm_response);
237
238 // In NTLM v1 (with LMv1 disabled), the lm_response and ntlm_response are the
239 // same. So just copy the ntlm_response into the lm_response.
240 memcpy(lm_response.data(), ntlm_response.data(), kResponseLenV1);
241 }
242
GenerateLMResponseV1WithSessionSecurity(base::span<const uint8_t,kChallengeLen> client_challenge,base::span<uint8_t,kResponseLenV1> lm_response)243 void GenerateLMResponseV1WithSessionSecurity(
244 base::span<const uint8_t, kChallengeLen> client_challenge,
245 base::span<uint8_t, kResponseLenV1> lm_response) {
246 // In NTLM v1 with Session Security (aka NTLM2) the lm_response is 8 bytes of
247 // client challenge and 16 bytes of zeros. (See 3.3.1)
248 memcpy(lm_response.data(), client_challenge.data(), kChallengeLen);
249 memset(lm_response.data() + kChallengeLen, 0, kResponseLenV1 - kChallengeLen);
250 }
251
GenerateSessionHashV1WithSessionSecurity(base::span<const uint8_t,kChallengeLen> server_challenge,base::span<const uint8_t,kChallengeLen> client_challenge,base::span<uint8_t,kNtlmHashLen> session_hash)252 void GenerateSessionHashV1WithSessionSecurity(
253 base::span<const uint8_t, kChallengeLen> server_challenge,
254 base::span<const uint8_t, kChallengeLen> client_challenge,
255 base::span<uint8_t, kNtlmHashLen> session_hash) {
256 MD5_CTX ctx;
257 MD5_Init(&ctx);
258 MD5_Update(&ctx, server_challenge.data(), kChallengeLen);
259 MD5_Update(&ctx, client_challenge.data(), kChallengeLen);
260 MD5_Final(session_hash.data(), &ctx);
261 }
262
GenerateNtlmResponseV1WithSessionSecurity(const std::u16string & password,base::span<const uint8_t,kChallengeLen> server_challenge,base::span<const uint8_t,kChallengeLen> client_challenge,base::span<uint8_t,kResponseLenV1> ntlm_response)263 void GenerateNtlmResponseV1WithSessionSecurity(
264 const std::u16string& password,
265 base::span<const uint8_t, kChallengeLen> server_challenge,
266 base::span<const uint8_t, kChallengeLen> client_challenge,
267 base::span<uint8_t, kResponseLenV1> ntlm_response) {
268 // Generate the NTLMv1 Hash.
269 uint8_t ntlm_hash[kNtlmHashLen];
270 GenerateNtlmHashV1(password, ntlm_hash);
271
272 // Generate the NTLMv1 Session Hash.
273 uint8_t session_hash[kNtlmHashLen];
274 GenerateSessionHashV1WithSessionSecurity(server_challenge, client_challenge,
275 session_hash);
276
277 GenerateResponseDesl(ntlm_hash,
278 base::span(session_hash).first<kChallengeLen>(),
279 ntlm_response);
280 }
281
GenerateResponsesV1WithSessionSecurity(const std::u16string & password,base::span<const uint8_t,kChallengeLen> server_challenge,base::span<const uint8_t,kChallengeLen> client_challenge,base::span<uint8_t,kResponseLenV1> lm_response,base::span<uint8_t,kResponseLenV1> ntlm_response)282 void GenerateResponsesV1WithSessionSecurity(
283 const std::u16string& password,
284 base::span<const uint8_t, kChallengeLen> server_challenge,
285 base::span<const uint8_t, kChallengeLen> client_challenge,
286 base::span<uint8_t, kResponseLenV1> lm_response,
287 base::span<uint8_t, kResponseLenV1> ntlm_response) {
288 GenerateLMResponseV1WithSessionSecurity(client_challenge, lm_response);
289 GenerateNtlmResponseV1WithSessionSecurity(password, server_challenge,
290 client_challenge, ntlm_response);
291 }
292
GenerateNtlmHashV2(const std::u16string & domain,const std::u16string & username,const std::u16string & password,base::span<uint8_t,kNtlmHashLen> v2_hash)293 void GenerateNtlmHashV2(const std::u16string& domain,
294 const std::u16string& username,
295 const std::u16string& password,
296 base::span<uint8_t, kNtlmHashLen> v2_hash) {
297 // NOTE: According to [MS-NLMP] Section 3.3.2 only the username and not the
298 // domain is uppercased.
299
300 // TODO(crbug.com/40674019): Using a locale-sensitive upper casing
301 // algorithm is problematic. A more predictable approach would be to only
302 // uppercase ASCII characters, so the hash does not change depending on the
303 // user's locale.
304 std::u16string upper_username;
305 bool result = ToUpperUsingLocale(username, &upper_username);
306 DCHECK(result);
307
308 uint8_t v1_hash[kNtlmHashLen];
309 GenerateNtlmHashV1(password, v1_hash);
310 NtlmBufferWriter input_writer((upper_username.length() + domain.length()) *
311 2);
312 bool writer_result = input_writer.WriteUtf16String(upper_username) &&
313 input_writer.WriteUtf16String(domain) &&
314 input_writer.IsEndOfBuffer();
315 DCHECK(writer_result);
316
317 unsigned int outlen = kNtlmHashLen;
318 uint8_t* out_hash =
319 HMAC(EVP_md5(), v1_hash, sizeof(v1_hash), input_writer.GetBuffer().data(),
320 input_writer.GetLength(), v2_hash.data(), &outlen);
321 DCHECK_EQ(v2_hash.data(), out_hash);
322 DCHECK_EQ(sizeof(v1_hash), outlen);
323 }
324
GenerateProofInputV2(uint64_t timestamp,base::span<const uint8_t,kChallengeLen> client_challenge)325 std::vector<uint8_t> GenerateProofInputV2(
326 uint64_t timestamp,
327 base::span<const uint8_t, kChallengeLen> client_challenge) {
328 NtlmBufferWriter writer(kProofInputLenV2);
329 bool result = writer.WriteUInt16(kProofInputVersionV2) &&
330 writer.WriteZeros(6) && writer.WriteUInt64(timestamp) &&
331 writer.WriteBytes(client_challenge) && writer.WriteZeros(4) &&
332 writer.IsEndOfBuffer();
333
334 DCHECK(result);
335 return writer.Pass();
336 }
337
GenerateNtlmProofV2(base::span<const uint8_t,kNtlmHashLen> v2_hash,base::span<const uint8_t,kChallengeLen> server_challenge,base::span<const uint8_t,kProofInputLenV2> v2_input,base::span<const uint8_t> target_info,base::span<uint8_t,kNtlmProofLenV2> v2_proof)338 void GenerateNtlmProofV2(
339 base::span<const uint8_t, kNtlmHashLen> v2_hash,
340 base::span<const uint8_t, kChallengeLen> server_challenge,
341 base::span<const uint8_t, kProofInputLenV2> v2_input,
342 base::span<const uint8_t> target_info,
343 base::span<uint8_t, kNtlmProofLenV2> v2_proof) {
344 bssl::ScopedHMAC_CTX ctx;
345 HMAC_Init_ex(ctx.get(), v2_hash.data(), kNtlmHashLen, EVP_md5(), nullptr);
346 DCHECK_EQ(kNtlmProofLenV2, HMAC_size(ctx.get()));
347 HMAC_Update(ctx.get(), server_challenge.data(), kChallengeLen);
348 HMAC_Update(ctx.get(), v2_input.data(), kProofInputLenV2);
349 HMAC_Update(ctx.get(), target_info.data(), target_info.size());
350 const uint32_t zero = 0;
351 HMAC_Update(ctx.get(), reinterpret_cast<const uint8_t*>(&zero),
352 sizeof(uint32_t));
353 HMAC_Final(ctx.get(), v2_proof.data(), nullptr);
354 }
355
GenerateSessionBaseKeyV2(base::span<const uint8_t,kNtlmHashLen> v2_hash,base::span<const uint8_t,kNtlmProofLenV2> v2_proof,base::span<uint8_t,kSessionKeyLenV2> session_key)356 void GenerateSessionBaseKeyV2(
357 base::span<const uint8_t, kNtlmHashLen> v2_hash,
358 base::span<const uint8_t, kNtlmProofLenV2> v2_proof,
359 base::span<uint8_t, kSessionKeyLenV2> session_key) {
360 unsigned int outlen = kSessionKeyLenV2;
361 uint8_t* result =
362 HMAC(EVP_md5(), v2_hash.data(), kNtlmHashLen, v2_proof.data(),
363 kNtlmProofLenV2, session_key.data(), &outlen);
364 DCHECK_EQ(session_key.data(), result);
365 DCHECK_EQ(kSessionKeyLenV2, outlen);
366 }
367
GenerateChannelBindingHashV2(const std::string & channel_bindings,base::span<uint8_t,kNtlmHashLen> channel_bindings_hash)368 void GenerateChannelBindingHashV2(
369 const std::string& channel_bindings,
370 base::span<uint8_t, kNtlmHashLen> channel_bindings_hash) {
371 NtlmBufferWriter writer(kEpaUnhashedStructHeaderLen);
372 bool result = writer.WriteZeros(16) &&
373 writer.WriteUInt32(channel_bindings.length()) &&
374 writer.IsEndOfBuffer();
375 DCHECK(result);
376
377 MD5_CTX ctx;
378 MD5_Init(&ctx);
379 MD5_Update(&ctx, writer.GetBuffer().data(), writer.GetBuffer().size());
380 MD5_Update(&ctx, channel_bindings.data(), channel_bindings.size());
381 MD5_Final(channel_bindings_hash.data(), &ctx);
382 }
383
GenerateMicV2(base::span<const uint8_t,kSessionKeyLenV2> session_key,base::span<const uint8_t> negotiate_msg,base::span<const uint8_t> challenge_msg,base::span<const uint8_t> authenticate_msg,base::span<uint8_t,kMicLenV2> mic)384 void GenerateMicV2(base::span<const uint8_t, kSessionKeyLenV2> session_key,
385 base::span<const uint8_t> negotiate_msg,
386 base::span<const uint8_t> challenge_msg,
387 base::span<const uint8_t> authenticate_msg,
388 base::span<uint8_t, kMicLenV2> mic) {
389 bssl::ScopedHMAC_CTX ctx;
390 HMAC_Init_ex(ctx.get(), session_key.data(), kSessionKeyLenV2, EVP_md5(),
391 nullptr);
392 DCHECK_EQ(kMicLenV2, HMAC_size(ctx.get()));
393 HMAC_Update(ctx.get(), negotiate_msg.data(), negotiate_msg.size());
394 HMAC_Update(ctx.get(), challenge_msg.data(), challenge_msg.size());
395 HMAC_Update(ctx.get(), authenticate_msg.data(), authenticate_msg.size());
396 HMAC_Final(ctx.get(), mic.data(), nullptr);
397 }
398
GenerateUpdatedTargetInfo(bool is_mic_enabled,bool is_epa_enabled,const std::string & channel_bindings,const std::string & spn,const std::vector<AvPair> & av_pairs,uint64_t * server_timestamp)399 NET_EXPORT_PRIVATE std::vector<uint8_t> GenerateUpdatedTargetInfo(
400 bool is_mic_enabled,
401 bool is_epa_enabled,
402 const std::string& channel_bindings,
403 const std::string& spn,
404 const std::vector<AvPair>& av_pairs,
405 uint64_t* server_timestamp) {
406 size_t updated_target_info_len = 0;
407 std::vector<AvPair> updated_av_pairs(av_pairs);
408 UpdateTargetInfoAvPairs(is_mic_enabled, is_epa_enabled, channel_bindings, spn,
409 &updated_av_pairs, server_timestamp,
410 &updated_target_info_len);
411 return WriteUpdatedTargetInfo(updated_av_pairs, updated_target_info_len);
412 }
413
414 } // namespace net::ntlm
415