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 #ifndef NET_NTLM_NTLM_BUFFER_WRITER_H_ 11 #define NET_NTLM_NTLM_BUFFER_WRITER_H_ 12 13 #include <stddef.h> 14 #include <stdint.h> 15 16 #include <memory> 17 #include <string> 18 19 #include "base/containers/span.h" 20 #include "net/base/net_export.h" 21 #include "net/ntlm/ntlm_constants.h" 22 23 namespace net::ntlm { 24 25 // Supports various bounds checked low level buffer operations required by an 26 // NTLM implementation. 27 // 28 // The class supports sequential write to an internally managed buffer. All 29 // writes perform bounds checking to ensure enough space is remaining in the 30 // buffer. 31 // 32 // The internal buffer is allocated in the constructor with size |buffer_len| 33 // and owned by the class. 34 // 35 // Write* methods write the buffer at the current cursor position and perform 36 // any necessary type conversion and provide the data in out params. After a 37 // successful write the cursor position is advanced past the written field. 38 // 39 // Failed writes leave the internal cursor at the same position as before the 40 // call. 41 // 42 // Based on [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol 43 // Specification version 28.0 [1]. Additional NTLM reference [2]. 44 // 45 // [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx 46 // [2] http://davenport.sourceforge.net/ntlm.html 47 class NET_EXPORT_PRIVATE NtlmBufferWriter { 48 public: 49 explicit NtlmBufferWriter(size_t buffer_len); 50 51 NtlmBufferWriter(const NtlmBufferWriter&) = delete; 52 NtlmBufferWriter& operator=(const NtlmBufferWriter&) = delete; 53 54 ~NtlmBufferWriter(); 55 GetLength()56 size_t GetLength() const { return buffer_.size(); } GetCursor()57 size_t GetCursor() const { return cursor_; } IsEndOfBuffer()58 bool IsEndOfBuffer() const { return cursor_ >= GetLength(); } GetBuffer()59 base::span<const uint8_t> GetBuffer() const { return buffer_; } Pass()60 std::vector<uint8_t> Pass() { return std::move(buffer_); } 61 62 // Returns true if there are |len| more bytes between the current cursor 63 // position and the end of the buffer. 64 bool CanWrite(size_t len) const; 65 66 // Writes a 16 bit unsigned value (little endian). If there are not 16 67 // more bits available in the buffer, it returns false. 68 [[nodiscard]] bool WriteUInt16(uint16_t value); 69 70 // Writes a 32 bit unsigned value (little endian). If there are not 32 71 // more bits available in the buffer, it returns false. 72 [[nodiscard]] bool WriteUInt32(uint32_t value); 73 74 // Writes a 64 bit unsigned value (little endian). If there are not 64 75 // more bits available in the buffer, it returns false. 76 [[nodiscard]] bool WriteUInt64(uint64_t value); 77 78 // Writes flags as a 32 bit unsigned value (little endian). 79 [[nodiscard]] bool WriteFlags(NegotiateFlags flags); 80 81 // Writes the bytes from the |buffer|. If there are not enough 82 // bytes in the buffer, it returns false. 83 [[nodiscard]] bool WriteBytes(base::span<const uint8_t> buffer); 84 85 // Writes |count| bytes of zeros to the buffer. If there are not |count| 86 // more bytes in available in the buffer, it returns false. 87 [[nodiscard]] bool WriteZeros(size_t count); 88 89 // A security buffer is an 8 byte structure that defines the offset and 90 // length of a payload (string, struct, or byte array) that appears after 91 // the fixed part of the message. 92 // 93 // The structure in the NTLM message is (little endian fields): 94 // uint16 - |length| Length of payload 95 // uint16 - Allocation (ignored and always set to |length|) 96 // uint32 - |offset| Offset from start of message 97 [[nodiscard]] bool WriteSecurityBuffer(SecurityBuffer sec_buf); 98 99 // Writes an AvPair header. See [MS-NLMP] Section 2.2.2.1. 100 // 101 // The header has the following structure: 102 // uint16 - |avid| The identifier of the following payload. 103 // uint16 - |avlen| The length of the following payload. 104 [[nodiscard]] bool WriteAvPairHeader(TargetInfoAvId avid, uint16_t avlen); 105 106 // Writes an AvPair header for an |AvPair|. See [MS-NLMP] Section 2.2.2.1. WriteAvPairHeader(const AvPair & pair)107 [[nodiscard]] bool WriteAvPairHeader(const AvPair& pair) { 108 return WriteAvPairHeader(pair.avid, pair.avlen); 109 } 110 111 // Writes a special AvPair header with both fields equal to 0. This zero 112 // length AvPair signals the end of the AvPair list. 113 [[nodiscard]] bool WriteAvPairTerminator(); 114 115 // Writes an |AvPair| header and its payload to the buffer. If the |avid| 116 // is of type |TargetInfoAvId::kFlags| the |flags| field of |pair| will be 117 // used as the payload and the |buffer| field is ignored. In all other cases 118 // |buffer| is used as the payload. See also 119 // |NtlmBufferReader::ReadTargetInfo|. 120 [[nodiscard]] bool WriteAvPair(const AvPair& pair); 121 122 // Writes a string of 8 bit characters to the buffer. 123 // 124 // When Unicode was not negotiated only the hostname string will go through 125 // this code path. The 8 bit bytes of the hostname are written to the buffer. 126 // The encoding is not relevant. 127 [[nodiscard]] bool WriteUtf8String(const std::string& str); 128 129 // Converts the 16 bit characters to UTF8 and writes the resulting 8 bit 130 // characters. 131 // 132 // If Unicode was not negotiated, the username and domain get converted to 133 // UTF8 in the message. Since they are just treated as additional bytes 134 // input to hash the encoding doesn't matter. In practice, only a very old or 135 // non-Windows server might trigger this code path since we always attempt 136 // to negotiate Unicode and servers are supposed to honor that request. 137 [[nodiscard]] bool WriteUtf16AsUtf8String(const std::u16string& str); 138 139 // Treats |str| as UTF8, converts to UTF-16 and writes it with little-endian 140 // byte order to the buffer. 141 // 142 // Two specific strings go through this code path. 143 // 144 // One case is the hostname. When the the Unicode flag has been set during 145 // negotiation, the hostname needs to appear in the message with 16-bit 146 // characters. 147 // 148 // The other case is the Service Principal Name (SPN). With Extended 149 // Protection for Authentication (EPA) enabled, it appears in the target info 150 // inside an AV Pair, where strings always have 16-bit characters. 151 [[nodiscard]] bool WriteUtf8AsUtf16String(const std::string& str); 152 153 // Writes UTF-16 LE characters to the buffer. For these strings, such as 154 // username and the domain the actual encoding isn't important; they are just 155 // treated as additional bytes of input to the hash. 156 [[nodiscard]] bool WriteUtf16String(const std::u16string& str); 157 158 // Writes the 8 byte NTLM signature "NTLMSSP\0" into the buffer. 159 [[nodiscard]] bool WriteSignature(); 160 161 // There are 3 message types Negotiate (sent by client), Challenge (sent by 162 // server), and Authenticate (sent by client). 163 // 164 // This writes |message_type| as a uint32_t into the buffer. 165 [[nodiscard]] bool WriteMessageType(MessageType message_type); 166 167 // Performs |WriteSignature| then |WriteMessageType|. 168 [[nodiscard]] bool WriteMessageHeader(MessageType message_type); 169 170 private: 171 // Writes |sizeof(T)| bytes little-endian of an integer type to the buffer. 172 template <typename T> 173 bool WriteUInt(T value); 174 175 // Sets the cursor position. The caller should use |GetLength| or 176 // |CanWrite| to verify the bounds before calling this method. 177 void SetCursor(size_t cursor); 178 179 // Advances the cursor by |count|. The caller should use |GetLength| or 180 // |CanWrite| to verify the bounds before calling this method. AdvanceCursor(size_t count)181 void AdvanceCursor(size_t count) { SetCursor(GetCursor() + count); } 182 183 // Returns a pointer to the start of the buffer. GetBufferPtr()184 const uint8_t* GetBufferPtr() const { return buffer_.data(); } GetBufferPtr()185 uint8_t* GetBufferPtr() { return buffer_.data(); } 186 187 // Returns pointer into the buffer at the current cursor location. GetBufferPtrAtCursor()188 const uint8_t* GetBufferPtrAtCursor() const { 189 return GetBufferPtr() + GetCursor(); 190 } GetBufferPtrAtCursor()191 uint8_t* GetBufferPtrAtCursor() { return GetBufferPtr() + GetCursor(); } 192 193 std::vector<uint8_t> buffer_; 194 size_t cursor_ = 0; 195 }; 196 197 } // namespace net::ntlm 198 199 #endif // NET_NTLM_NTLM_BUFFER_WRITER_H_ 200