summaryrefslogtreecommitdiff
path: root/chromium/net/ntlm/ntlm_buffer_writer.h
blob: 00745ce97e035be1631d396f4d2a074f77a69670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_NTLM_NTLM_BUFFER_WRITER_H_
#define NET_NTLM_NTLM_BUFFER_WRITER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <string>

#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/ntlm/ntlm_constants.h"

namespace net {
namespace ntlm {

// Supports various bounds checked low level buffer operations required by an
// NTLM implementation.
//
// The class supports sequential write to an internally managed buffer. All
// writes perform bounds checking to ensure enough space is remaining in the
// buffer.
//
// The internal buffer is allocated in the constructor with size |buffer_len|
// and owned by the class.
//
// Write* methods write the buffer at the current cursor position and perform
// any necessary type conversion and provide the data in out params. After a
// successful write the cursor position is advanced past the written field.
//
// Failed writes leave the internal cursor at the same position as before the
// call.
//
// Based on [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol
// Specification version 28.0 [1]. Additional NTLM reference [2].
//
// [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx
// [2] http://davenport.sourceforge.net/ntlm.html
class NET_EXPORT_PRIVATE NtlmBufferWriter {
 public:
  explicit NtlmBufferWriter(size_t buffer_len);

  NtlmBufferWriter(const NtlmBufferWriter&) = delete;
  NtlmBufferWriter& operator=(const NtlmBufferWriter&) = delete;

  ~NtlmBufferWriter();

  size_t GetLength() const { return buffer_.size(); }
  size_t GetCursor() const { return cursor_; }
  bool IsEndOfBuffer() const { return cursor_ >= GetLength(); }
  base::span<const uint8_t> GetBuffer() const { return buffer_; }
  std::vector<uint8_t> Pass() const { return std::move(buffer_); }

  // Returns true if there are |len| more bytes between the current cursor
  // position and the end of the buffer.
  bool CanWrite(size_t len) const;

  // Writes a 16 bit unsigned value (little endian). If there are not 16
  // more bits available in the buffer, it returns false.
  [[nodiscard]] bool WriteUInt16(uint16_t value);

  // Writes a 32 bit unsigned value (little endian). If there are not 32
  // more bits available in the buffer, it returns false.
  [[nodiscard]] bool WriteUInt32(uint32_t value);

  // Writes a 64 bit unsigned value (little endian). If there are not 64
  // more bits available in the buffer, it returns false.
  [[nodiscard]] bool WriteUInt64(uint64_t value);

  // Writes flags as a 32 bit unsigned value (little endian).
  [[nodiscard]] bool WriteFlags(NegotiateFlags flags);

  // Writes the bytes from the |buffer|. If there are not enough
  // bytes in the buffer, it returns false.
  [[nodiscard]] bool WriteBytes(base::span<const uint8_t> buffer);

  // Writes |count| bytes of zeros to the buffer. If there are not |count|
  // more bytes in available in the buffer, it returns false.
  [[nodiscard]] bool WriteZeros(size_t count);

  // A security buffer is an 8 byte structure that defines the offset and
  // length of a payload (string, struct, or byte array) that appears after
  // the fixed part of the message.
  //
  // The structure in the NTLM message is (little endian fields):
  //     uint16 - |length| Length of payload
  //     uint16 - Allocation (ignored and always set to |length|)
  //     uint32 - |offset| Offset from start of message
  [[nodiscard]] bool WriteSecurityBuffer(SecurityBuffer sec_buf);

  // Writes an AvPair header. See [MS-NLMP] Section 2.2.2.1.
  //
  // The header has the following structure:
  //    uint16 - |avid| The identifier of the following payload.
  //    uint16 - |avlen| The length of the following payload.
  [[nodiscard]] bool WriteAvPairHeader(TargetInfoAvId avid, uint16_t avlen);

  // Writes an AvPair header for an |AvPair|. See [MS-NLMP] Section 2.2.2.1.
  [[nodiscard]] bool WriteAvPairHeader(const AvPair& pair) {
    return WriteAvPairHeader(pair.avid, pair.avlen);
  }

  // Writes a special AvPair header with both fields equal to 0. This zero
  // length AvPair signals the end of the AvPair list.
  [[nodiscard]] bool WriteAvPairTerminator();

  // Writes an |AvPair| header and its payload to the buffer. If the |avid|
  // is of type |TargetInfoAvId::kFlags| the |flags| field of |pair| will be
  // used as the payload and the |buffer| field is ignored. In all other cases
  // |buffer| is used as the payload. See also
  // |NtlmBufferReader::ReadTargetInfo|.
  [[nodiscard]] bool WriteAvPair(const AvPair& pair);

  // Writes a string of 8 bit characters to the buffer.
  //
  // When Unicode was not negotiated only the hostname string will go through
  // this code path. The 8 bit bytes of the hostname are written to the buffer.
  // The encoding is not relevant.
  [[nodiscard]] bool WriteUtf8String(const std::string& str);

  // Converts the 16 bit characters to UTF8 and writes the resulting 8 bit
  // characters.
  //
  // If Unicode was not negotiated, the username and domain get converted to
  // UTF8 in the message. Since they are just treated as additional bytes
  // input to hash the encoding doesn't matter. In practice, only a very old or
  // non-Windows server might trigger this code path since we always attempt
  // to negotiate Unicode and servers are supposed to honor that request.
  [[nodiscard]] bool WriteUtf16AsUtf8String(const std::u16string& str);

  // Treats |str| as UTF8, converts to UTF-16 and writes it with little-endian
  // byte order to the buffer.
  //
  // Two specific strings go through this code path.
  //
  // One case is the hostname. When the the Unicode flag has been set during
  // negotiation, the hostname needs to appear in the message with 16-bit
  // characters.
  //
  // The other case is the Service Principal Name (SPN). With Extended
  // Protection for Authentication (EPA) enabled, it appears in the target info
  // inside an AV Pair, where strings always have 16-bit characters.
  [[nodiscard]] bool WriteUtf8AsUtf16String(const std::string& str);

  // Writes UTF-16 LE characters to the buffer. For these strings, such as
  // username and the domain the actual encoding isn't important; they are just
  // treated as additional bytes of input to the hash.
  [[nodiscard]] bool WriteUtf16String(const std::u16string& str);

  // Writes the 8 byte NTLM signature "NTLMSSP\0" into the buffer.
  [[nodiscard]] bool WriteSignature();

  // There are 3 message types Negotiate (sent by client), Challenge (sent by
  // server), and Authenticate (sent by client).
  //
  // This writes |message_type| as a uint32_t into the buffer.
  [[nodiscard]] bool WriteMessageType(MessageType message_type);

  // Performs |WriteSignature| then |WriteMessageType|.
  [[nodiscard]] bool WriteMessageHeader(MessageType message_type);

 private:
  // Writes |sizeof(T)| bytes little-endian of an integer type to the buffer.
  template <typename T>
  bool WriteUInt(T value);

  // Sets the cursor position. The caller should use |GetLength| or
  // |CanWrite| to verify the bounds before calling this method.
  void SetCursor(size_t cursor);

  // Advances the cursor by |count|. The caller should use |GetLength| or
  // |CanWrite| to verify the bounds before calling this method.
  void AdvanceCursor(size_t count) { SetCursor(GetCursor() + count); }

  // Returns a pointer to the start of the buffer.
  const uint8_t* GetBufferPtr() const { return buffer_.data(); }
  uint8_t* GetBufferPtr() { return buffer_.data(); }

  // Returns pointer into the buffer at the current cursor location.
  const uint8_t* GetBufferPtrAtCursor() const {
    return GetBufferPtr() + GetCursor();
  }
  uint8_t* GetBufferPtrAtCursor() { return GetBufferPtr() + GetCursor(); }

  std::vector<uint8_t> buffer_;
  size_t cursor_ = 0;
};

}  // namespace ntlm
}  // namespace net

#endif  // NET_NTLM_NTLM_BUFFER_WRITER_H_