ntlm_client.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #include "net/ntlm/ntlm_client.h"
  5. #include <string.h>
  6. #include "base/check_op.h"
  7. #include "base/containers/span.h"
  8. #include "base/logging.h"
  9. #include "base/numerics/safe_math.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. #include "net/ntlm/ntlm_constants.h"
  15. namespace net::ntlm {
  16. namespace {
  17. // Parses the challenge message and returns the |challenge_flags| and
  18. // |server_challenge| into the supplied buffer.
  19. bool ParseChallengeMessage(
  20. base::span<const uint8_t> challenge_message,
  21. NegotiateFlags* challenge_flags,
  22. base::span<uint8_t, kChallengeLen> server_challenge) {
  23. NtlmBufferReader challenge_reader(challenge_message);
  24. return challenge_reader.MatchMessageHeader(MessageType::kChallenge) &&
  25. challenge_reader.SkipSecurityBufferWithValidation() &&
  26. challenge_reader.ReadFlags(challenge_flags) &&
  27. challenge_reader.ReadBytes(server_challenge);
  28. }
  29. // Parses the challenge message and extracts the information necessary to
  30. // make an NTLMv2 response.
  31. bool ParseChallengeMessageV2(
  32. base::span<const uint8_t> challenge_message,
  33. NegotiateFlags* challenge_flags,
  34. base::span<uint8_t, kChallengeLen> server_challenge,
  35. std::vector<AvPair>* av_pairs) {
  36. NtlmBufferReader challenge_reader(challenge_message);
  37. return challenge_reader.MatchMessageHeader(MessageType::kChallenge) &&
  38. challenge_reader.SkipSecurityBufferWithValidation() &&
  39. challenge_reader.ReadFlags(challenge_flags) &&
  40. challenge_reader.ReadBytes(server_challenge) &&
  41. challenge_reader.SkipBytes(8) &&
  42. // challenge_reader.ReadTargetInfoPayload(av_pairs);
  43. (((*challenge_flags & NegotiateFlags::kTargetInfo) ==
  44. NegotiateFlags::kTargetInfo)
  45. ? challenge_reader.ReadTargetInfoPayload(av_pairs)
  46. : true);
  47. }
  48. bool WriteAuthenticateMessage(NtlmBufferWriter* authenticate_writer,
  49. SecurityBuffer lm_payload,
  50. SecurityBuffer ntlm_payload,
  51. SecurityBuffer domain_payload,
  52. SecurityBuffer username_payload,
  53. SecurityBuffer hostname_payload,
  54. SecurityBuffer session_key_payload,
  55. NegotiateFlags authenticate_flags) {
  56. return authenticate_writer->WriteMessageHeader(MessageType::kAuthenticate) &&
  57. authenticate_writer->WriteSecurityBuffer(lm_payload) &&
  58. authenticate_writer->WriteSecurityBuffer(ntlm_payload) &&
  59. authenticate_writer->WriteSecurityBuffer(domain_payload) &&
  60. authenticate_writer->WriteSecurityBuffer(username_payload) &&
  61. authenticate_writer->WriteSecurityBuffer(hostname_payload) &&
  62. authenticate_writer->WriteSecurityBuffer(session_key_payload) &&
  63. authenticate_writer->WriteFlags(authenticate_flags);
  64. }
  65. // Writes the NTLMv1 LM Response and NTLM Response.
  66. bool WriteResponsePayloads(
  67. NtlmBufferWriter* authenticate_writer,
  68. base::span<const uint8_t, kResponseLenV1> lm_response,
  69. base::span<const uint8_t, kResponseLenV1> ntlm_response) {
  70. return authenticate_writer->WriteBytes(lm_response) &&
  71. authenticate_writer->WriteBytes(ntlm_response);
  72. }
  73. // Writes the |lm_response| and writes the NTLMv2 response by concatenating
  74. // |v2_proof|, |v2_proof_input|, |updated_target_info| and 4 zero bytes.
  75. bool WriteResponsePayloadsV2(
  76. NtlmBufferWriter* authenticate_writer,
  77. base::span<const uint8_t, kResponseLenV1> lm_response,
  78. base::span<const uint8_t, kNtlmProofLenV2> v2_proof,
  79. base::span<const uint8_t> v2_proof_input,
  80. base::span<const uint8_t> updated_target_info) {
  81. return authenticate_writer->WriteBytes(lm_response) &&
  82. authenticate_writer->WriteBytes(v2_proof) &&
  83. authenticate_writer->WriteBytes(v2_proof_input) &&
  84. authenticate_writer->WriteBytes(updated_target_info) &&
  85. authenticate_writer->WriteUInt32(0);
  86. }
  87. bool WriteStringPayloads(NtlmBufferWriter* authenticate_writer,
  88. bool is_unicode,
  89. const std::u16string& domain,
  90. const std::u16string& username,
  91. const std::string& hostname) {
  92. if (is_unicode) {
  93. return authenticate_writer->WriteUtf16String(domain) &&
  94. authenticate_writer->WriteUtf16String(username) &&
  95. authenticate_writer->WriteUtf8AsUtf16String(hostname);
  96. } else {
  97. return authenticate_writer->WriteUtf16AsUtf8String(domain) &&
  98. authenticate_writer->WriteUtf16AsUtf8String(username) &&
  99. authenticate_writer->WriteUtf8String(hostname);
  100. }
  101. }
  102. // Returns the size in bytes of a string16 depending whether unicode
  103. // was negotiated.
  104. size_t GetStringPayloadLength(const std::u16string& str, bool is_unicode) {
  105. if (is_unicode)
  106. return str.length() * 2;
  107. // When |WriteUtf16AsUtf8String| is called with a |std::u16string|, the string
  108. // is converted to UTF8. Do the conversion to ensure that the character
  109. // count is correct.
  110. return base::UTF16ToUTF8(str).length();
  111. }
  112. // Returns the size in bytes of a std::string depending whether unicode
  113. // was negotiated.
  114. size_t GetStringPayloadLength(const std::string& str, bool is_unicode) {
  115. if (!is_unicode)
  116. return str.length();
  117. return base::UTF8ToUTF16(str).length() * 2;
  118. }
  119. // Sets |buffer| to point to |length| bytes from |offset| and updates |offset|
  120. // past those bytes. In case of overflow, returns false.
  121. bool ComputeSecurityBuffer(uint32_t* offset,
  122. size_t length,
  123. SecurityBuffer* buffer) {
  124. base::CheckedNumeric<uint16_t> length_checked = length;
  125. if (!length_checked.IsValid()) {
  126. return false;
  127. }
  128. base::CheckedNumeric<uint32_t> new_offset = *offset + length_checked;
  129. if (!new_offset.IsValid()) {
  130. return false;
  131. }
  132. buffer->offset = *offset;
  133. buffer->length = length_checked.ValueOrDie();
  134. *offset = new_offset.ValueOrDie();
  135. return true;
  136. }
  137. } // namespace
  138. NtlmClient::NtlmClient(NtlmFeatures features)
  139. : features_(features), negotiate_flags_(kNegotiateMessageFlags) {
  140. // Just generate the negotiate message once and hold on to it. It never
  141. // changes and in NTLMv2 it's used as an input to the Message Integrity
  142. // Check (MIC) in the Authenticate message.
  143. GenerateNegotiateMessage();
  144. }
  145. NtlmClient::~NtlmClient() = default;
  146. std::vector<uint8_t> NtlmClient::GetNegotiateMessage() const {
  147. return negotiate_message_;
  148. }
  149. void NtlmClient::GenerateNegotiateMessage() {
  150. NtlmBufferWriter writer(kNegotiateMessageLen);
  151. bool result =
  152. writer.WriteMessageHeader(MessageType::kNegotiate) &&
  153. writer.WriteFlags(negotiate_flags_) &&
  154. writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) &&
  155. writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) &&
  156. writer.IsEndOfBuffer();
  157. DCHECK(result);
  158. negotiate_message_ = writer.Pass();
  159. }
  160. std::vector<uint8_t> NtlmClient::GenerateAuthenticateMessage(
  161. const std::u16string& domain,
  162. const std::u16string& username,
  163. const std::u16string& password,
  164. const std::string& hostname,
  165. const std::string& channel_bindings,
  166. const std::string& spn,
  167. uint64_t client_time,
  168. base::span<const uint8_t, kChallengeLen> client_challenge,
  169. base::span<const uint8_t> server_challenge_message) const {
  170. // Limit the size of strings that are accepted. As an absolute limit any
  171. // field represented by a |SecurityBuffer| or |AvPair| must be less than
  172. // UINT16_MAX bytes long. The strings are restricted to the maximum sizes
  173. // without regard to encoding. As such this isn't intended to restrict all
  174. // invalid inputs, only to allow all possible valid inputs.
  175. //
  176. // |domain| and |hostname| can be no longer than 255 characters.
  177. // |username| can be no longer than 104 characters. See [1].
  178. // |password| can be no longer than 256 characters. See [2].
  179. //
  180. // [1] - https://technet.microsoft.com/en-us/library/bb726984.aspx
  181. // [2] - https://technet.microsoft.com/en-us/library/cc512606.aspx
  182. if (hostname.length() > kMaxFqdnLen || domain.length() > kMaxFqdnLen ||
  183. username.length() > kMaxUsernameLen ||
  184. password.length() > kMaxPasswordLen) {
  185. return {};
  186. }
  187. NegotiateFlags challenge_flags;
  188. uint8_t server_challenge[kChallengeLen];
  189. uint8_t lm_response[kResponseLenV1];
  190. uint8_t ntlm_response[kResponseLenV1];
  191. // Response fields only for NTLMv2
  192. std::vector<uint8_t> updated_target_info;
  193. std::vector<uint8_t> v2_proof_input;
  194. uint8_t v2_proof[kNtlmProofLenV2];
  195. uint8_t v2_session_key[kSessionKeyLenV2];
  196. if (IsNtlmV2()) {
  197. std::vector<AvPair> av_pairs;
  198. if (!ParseChallengeMessageV2(server_challenge_message, &challenge_flags,
  199. server_challenge, &av_pairs)) {
  200. return {};
  201. }
  202. uint64_t timestamp;
  203. updated_target_info =
  204. GenerateUpdatedTargetInfo(IsMicEnabled(), IsEpaEnabled(),
  205. channel_bindings, spn, av_pairs, &timestamp);
  206. memset(lm_response, 0, kResponseLenV1);
  207. if (timestamp == UINT64_MAX) {
  208. // If the server didn't send a time, then use the clients time.
  209. timestamp = client_time;
  210. }
  211. uint8_t v2_hash[kNtlmHashLen];
  212. GenerateNtlmHashV2(domain, username, password, v2_hash);
  213. v2_proof_input = GenerateProofInputV2(timestamp, client_challenge);
  214. GenerateNtlmProofV2(v2_hash, server_challenge,
  215. base::make_span<kProofInputLenV2>(v2_proof_input),
  216. updated_target_info, v2_proof);
  217. GenerateSessionBaseKeyV2(v2_hash, v2_proof, v2_session_key);
  218. } else {
  219. if (!ParseChallengeMessage(server_challenge_message, &challenge_flags,
  220. server_challenge)) {
  221. return {};
  222. }
  223. // Calculate the responses for the authenticate message.
  224. GenerateResponsesV1WithSessionSecurity(password, server_challenge,
  225. client_challenge, lm_response,
  226. ntlm_response);
  227. }
  228. // Always use extended session security even if the server tries to downgrade.
  229. NegotiateFlags authenticate_flags = (challenge_flags & negotiate_flags_) |
  230. NegotiateFlags::kExtendedSessionSecurity;
  231. // Calculate all the payload lengths and offsets.
  232. bool is_unicode = (authenticate_flags & NegotiateFlags::kUnicode) ==
  233. NegotiateFlags::kUnicode;
  234. SecurityBuffer lm_info;
  235. SecurityBuffer ntlm_info;
  236. SecurityBuffer domain_info;
  237. SecurityBuffer username_info;
  238. SecurityBuffer hostname_info;
  239. SecurityBuffer session_key_info;
  240. size_t authenticate_message_len;
  241. if (!CalculatePayloadLayout(is_unicode, domain, username, hostname,
  242. updated_target_info.size(), &lm_info, &ntlm_info,
  243. &domain_info, &username_info, &hostname_info,
  244. &session_key_info, &authenticate_message_len)) {
  245. return {};
  246. }
  247. NtlmBufferWriter authenticate_writer(authenticate_message_len);
  248. bool writer_result = WriteAuthenticateMessage(
  249. &authenticate_writer, lm_info, ntlm_info, domain_info, username_info,
  250. hostname_info, session_key_info, authenticate_flags);
  251. DCHECK(writer_result);
  252. if (IsNtlmV2()) {
  253. // Write the optional (for V1) Version and MIC fields. Note that they
  254. // could also safely be sent in V1. However, the server should never try to
  255. // read them, because neither the version negotiate flag nor the
  256. // |TargetInfoAvFlags::kMicPresent| in the target info are set.
  257. //
  258. // Version is never supported so it is filled with zeros. MIC is a hash
  259. // calculated over all 3 messages while the MIC is set to zeros then
  260. // backfilled at the end if the MIC feature is enabled.
  261. writer_result = authenticate_writer.WriteZeros(kVersionFieldLen) &&
  262. authenticate_writer.WriteZeros(kMicLenV2);
  263. DCHECK(writer_result);
  264. }
  265. // Verify the location in the payload buffer.
  266. DCHECK(authenticate_writer.GetCursor() == GetAuthenticateHeaderLength());
  267. DCHECK(GetAuthenticateHeaderLength() == lm_info.offset);
  268. if (IsNtlmV2()) {
  269. // Write the response payloads for V2.
  270. writer_result =
  271. WriteResponsePayloadsV2(&authenticate_writer, lm_response, v2_proof,
  272. v2_proof_input, updated_target_info);
  273. } else {
  274. // Write the response payloads.
  275. DCHECK_EQ(kResponseLenV1, lm_info.length);
  276. DCHECK_EQ(kResponseLenV1, ntlm_info.length);
  277. writer_result =
  278. WriteResponsePayloads(&authenticate_writer, lm_response, ntlm_response);
  279. }
  280. DCHECK(writer_result);
  281. DCHECK_EQ(authenticate_writer.GetCursor(), domain_info.offset);
  282. writer_result = WriteStringPayloads(&authenticate_writer, is_unicode, domain,
  283. username, hostname);
  284. DCHECK(writer_result);
  285. DCHECK(authenticate_writer.IsEndOfBuffer());
  286. DCHECK_EQ(authenticate_message_len, authenticate_writer.GetLength());
  287. std::vector<uint8_t> auth_msg = authenticate_writer.Pass();
  288. // Backfill the MIC if enabled.
  289. if (IsMicEnabled()) {
  290. // The MIC has to be generated over all 3 completed messages with the MIC
  291. // set to zeros.
  292. DCHECK_LT(kMicOffsetV2 + kMicLenV2, authenticate_message_len);
  293. base::span<uint8_t, kMicLenV2> mic(
  294. const_cast<uint8_t*>(auth_msg.data()) + kMicOffsetV2, kMicLenV2);
  295. GenerateMicV2(v2_session_key, negotiate_message_, server_challenge_message,
  296. auth_msg, mic);
  297. }
  298. return auth_msg;
  299. }
  300. bool NtlmClient::CalculatePayloadLayout(
  301. bool is_unicode,
  302. const std::u16string& domain,
  303. const std::u16string& username,
  304. const std::string& hostname,
  305. size_t updated_target_info_len,
  306. SecurityBuffer* lm_info,
  307. SecurityBuffer* ntlm_info,
  308. SecurityBuffer* domain_info,
  309. SecurityBuffer* username_info,
  310. SecurityBuffer* hostname_info,
  311. SecurityBuffer* session_key_info,
  312. size_t* authenticate_message_len) const {
  313. uint32_t offset = GetAuthenticateHeaderLength();
  314. if (!ComputeSecurityBuffer(&offset, 0, session_key_info) ||
  315. !ComputeSecurityBuffer(&offset, kResponseLenV1, lm_info) ||
  316. !ComputeSecurityBuffer(
  317. &offset, GetNtlmResponseLength(updated_target_info_len), ntlm_info) ||
  318. !ComputeSecurityBuffer(
  319. &offset, GetStringPayloadLength(domain, is_unicode), domain_info) ||
  320. !ComputeSecurityBuffer(&offset,
  321. GetStringPayloadLength(username, is_unicode),
  322. username_info) ||
  323. !ComputeSecurityBuffer(&offset,
  324. GetStringPayloadLength(hostname, is_unicode),
  325. hostname_info)) {
  326. return false;
  327. }
  328. *authenticate_message_len = offset;
  329. return true;
  330. }
  331. size_t NtlmClient::GetAuthenticateHeaderLength() const {
  332. if (IsNtlmV2()) {
  333. return kAuthenticateHeaderLenV2;
  334. }
  335. return kAuthenticateHeaderLenV1;
  336. }
  337. size_t NtlmClient::GetNtlmResponseLength(size_t updated_target_info_len) const {
  338. if (IsNtlmV2()) {
  339. return kNtlmResponseHeaderLenV2 + updated_target_info_len + 4;
  340. }
  341. return kResponseLenV1;
  342. }
  343. } // namespace net::ntlm