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