• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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