| // 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_BASE_NTLM_BUFFER_READER_H_ | |
| #define NET_BASE_NTLM_BUFFER_READER_H_ | |
| #include <string> | |
| #include <vector> | |
| #include "base/containers/span.h" | |
| #include "net/base/net_export.h" | |
| #include "net/ntlm/ntlm_constants.h" | |
| #include "starboard/types.h" | |
| namespace net { | |
| namespace ntlm { | |
| // Supports various bounds-checked low level buffer operations required by an | |
| // NTLM implementation. | |
| // | |
| // The class supports the sequential read of a provided buffer. All reads | |
| // perform bounds checking to ensure enough space is remaining in the buffer. | |
| // | |
| // Read* methods read from the buffer at the current cursor position and | |
| // perform any necessary type conversion and provide the data in out params. | |
| // After a successful read the cursor position is advanced past the read | |
| // field. | |
| // | |
| // Failed Read*s or Match*s leave the cursor in an undefined position and the | |
| // buffer MUST be discarded with no further operations performed. | |
| // | |
| // Read*Payload methods first reads a security buffer (see | |
| // |ReadSecurityBuffer|), then reads the requested payload from the offset | |
| // and length stated in the security buffer. | |
| // | |
| // If the length and offset in the security buffer would cause a read outside | |
| // the message buffer the payload will not be read and the function will | |
| // return false. | |
| // | |
| // 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 NtlmBufferReader { | |
| public: | |
| NtlmBufferReader(); | |
| // |buffer| is not copied and must outlive the |NtlmBufferReader|. | |
| explicit NtlmBufferReader(base::span<const uint8_t> buffer); | |
| ~NtlmBufferReader(); | |
| size_t GetLength() const { return buffer_.size(); } | |
| size_t GetCursor() const { return cursor_; } | |
| bool IsEndOfBuffer() const { return cursor_ >= GetLength(); } | |
| // Returns true if there are |len| more bytes between the current cursor | |
| // position and the end of the buffer. | |
| bool CanRead(size_t len) const; | |
| // Returns true if there are |len| more bytes between |offset| and the end | |
| // of the buffer. The cursor position is not used or modified. | |
| bool CanReadFrom(size_t offset, size_t len) const; | |
| // Returns true if it would be possible to read the payload described by the | |
| // security buffer. | |
| bool CanReadFrom(SecurityBuffer sec_buf) const { | |
| return CanReadFrom(sec_buf.offset, sec_buf.length); | |
| } | |
| // Reads a 16 bit value (little endian) as a uint16_t. If there are not 16 | |
| // more bits available, it returns false. | |
| bool ReadUInt16(uint16_t* value) WARN_UNUSED_RESULT; | |
| // Reads a 32 bit value (little endian) as a uint32_t. If there are not 32 | |
| // more bits available, it returns false. | |
| bool ReadUInt32(uint32_t* value) WARN_UNUSED_RESULT; | |
| // Reads a 64 bit value (little endian) as a uint64_t. If there are not 64 | |
| // more bits available, it returns false. | |
| bool ReadUInt64(uint64_t* value) WARN_UNUSED_RESULT; | |
| // Calls |ReadUInt32| and returns it cast as |NegotiateFlags|. No | |
| // validation of the value takes place. | |
| bool ReadFlags(NegotiateFlags* flags) WARN_UNUSED_RESULT; | |
| // Reads |len| bytes and copies them into |buffer|. | |
| bool ReadBytes(base::span<uint8_t> buffer) WARN_UNUSED_RESULT; | |
| // Reads |sec_buf.length| bytes from offset |sec_buf.offset| and copies them | |
| // into |buffer|. If the security buffer specifies a payload outside the | |
| // buffer, then the call fails. Unlike the other Read* methods, this does | |
| // not move the cursor. | |
| bool ReadBytesFrom(const SecurityBuffer& sec_buf, | |
| base::span<uint8_t> buffer) WARN_UNUSED_RESULT; | |
| // Reads |sec_buf.length| bytes from offset |sec_buf.offset| and assigns | |
| // |reader| an |NtlmBufferReader| representing the payload. If the security | |
| // buffer specifies a payload outside the buffer, then the call fails, and | |
| // the state of |reader| is undefined. Unlike the other Read* methods, this | |
| // does not move the cursor. | |
| bool ReadPayloadAsBufferReader(const SecurityBuffer& sec_buf, | |
| NtlmBufferReader* reader) WARN_UNUSED_RESULT; | |
| // 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 is (little endian fields): | |
| // uint16 - |length| Length of payload | |
| // uint16 - Allocation (this is always ignored and not returned) | |
| // uint32 - |offset| Offset from start of message | |
| bool ReadSecurityBuffer(SecurityBuffer* sec_buf) WARN_UNUSED_RESULT; | |
| // Reads an AvPair header. AvPairs appear sequentially, terminated by a | |
| // special EOL AvPair, in the target info payload of the Challenge message. | |
| // See [MS-NLMP] Section 2.2.2.1. | |
| // | |
| // An AvPair contains an inline payload, and has the structure below ( | |
| // little endian fields): | |
| // uint16 - AvID: Identifies the type of the payload. | |
| // uint16 - AvLen: The length of the following payload. | |
| // (variable) - Payload: Variable length payload. The content and | |
| // format are determined by the AvId. | |
| bool ReadAvPairHeader(TargetInfoAvId* avid, | |
| uint16_t* avlen) WARN_UNUSED_RESULT; | |
| // There are 3 message types Negotiate (sent by client), Challenge (sent by | |
| // server), and Authenticate (sent by client). | |
| // | |
| // This reads the message type from the header and will return false if the | |
| // value is invalid. | |
| bool ReadMessageType(MessageType* message_type) WARN_UNUSED_RESULT; | |
| // Reads |target_info_len| bytes and parses them as a sequence of Av Pairs. | |
| // |av_pairs| should be empty on entry to this function. If |ReadTargetInfo| | |
| // returns false, the content of |av_pairs| is in an undefined state and | |
| // should be discarded. | |
| bool ReadTargetInfo(size_t target_info_len, | |
| std::vector<AvPair>* av_pairs) WARN_UNUSED_RESULT; | |
| // Reads a security buffer, then parses the security buffer payload as a | |
| // target info. The target info is returned as a sequence of AvPairs, with | |
| // the terminating AvPair omitted. A zero length payload is valid and will | |
| // result in an empty list in |av_pairs|. Any non-zero length payload must | |
| // have a terminating AvPair. | |
| // |av_pairs| should be empty on entry to this function. If |ReadTargetInfo| | |
| // returns false, the content of |av_pairs| is in an undefined state and | |
| // should be discarded. | |
| bool ReadTargetInfoPayload(std::vector<AvPair>* av_pairs) WARN_UNUSED_RESULT; | |
| // Skips over a security buffer field without reading the fields. This is | |
| // the equivalent of advancing the cursor 8 bytes. Returns false if there | |
| // are less than 8 bytes left in the buffer. | |
| bool SkipSecurityBuffer() WARN_UNUSED_RESULT; | |
| // Skips over the security buffer without returning the values, but fails if | |
| // the values would cause a read outside the buffer if the payload was | |
| // actually read. | |
| bool SkipSecurityBufferWithValidation() WARN_UNUSED_RESULT; | |
| // Skips over |count| bytes in the buffer. Returns false if there are not | |
| // |count| bytes left in the buffer. | |
| bool SkipBytes(size_t count) WARN_UNUSED_RESULT; | |
| // Reads and returns true if the next 8 bytes matches the signature in an | |
| // NTLM message "NTLMSSP\0". The cursor advances if the the signature | |
| // is matched. | |
| bool MatchSignature() WARN_UNUSED_RESULT; | |
| // Performs |ReadMessageType| and returns true if the value is | |
| // |message_type|. If the read fails or the message type does not match, | |
| // the buffer is invalid and MUST be discarded. | |
| bool MatchMessageType(MessageType message_type) WARN_UNUSED_RESULT; | |
| // Performs |MatchSignature| then |MatchMessageType|. | |
| bool MatchMessageHeader(MessageType message_type) WARN_UNUSED_RESULT; | |
| // Performs |ReadBytes(count)| and returns true if the contents is all | |
| // zero. | |
| bool MatchZeros(size_t count) WARN_UNUSED_RESULT; | |
| // Reads the security buffer and returns true if the length is 0 and | |
| // the offset is within the message. On failure, the buffer is invalid | |
| // and MUST be discarded. | |
| bool MatchEmptySecurityBuffer() WARN_UNUSED_RESULT; | |
| private: | |
| // Reads |sizeof(T)| bytes of an integer type from a little-endian buffer. | |
| template <typename T> | |
| bool ReadUInt(T* value); | |
| // Sets the cursor position. The caller should use |GetLength|, |CanRead|, | |
| // or |CanReadFrom| to verify the bounds before calling this method. | |
| void SetCursor(size_t cursor); | |
| // Advances the cursor by |count| bytes. The caller should use |GetLength|, | |
| // |CanRead|, or |CanReadFrom| to verify the bounds before calling this | |
| // method. | |
| void AdvanceCursor(size_t count) { SetCursor(GetCursor() + count); } | |
| // Returns a constant pointer to the start of the buffer. | |
| const uint8_t* GetBufferPtr() const { return buffer_.data(); } | |
| // Returns a pointer to the underlying buffer at the current cursor | |
| // position. | |
| const uint8_t* GetBufferAtCursor() const { return GetBufferPtr() + cursor_; } | |
| // Returns the byte at the current cursor position. | |
| uint8_t GetByteAtCursor() const { | |
| DCHECK(!IsEndOfBuffer()); | |
| return *(GetBufferAtCursor()); | |
| } | |
| base::span<const uint8_t> buffer_; | |
| size_t cursor_ = 0; | |
| }; | |
| } // namespace ntlm | |
| } // namespace net | |
| #endif // NET_BASE_NTLM_BUFFER_READER_H_ |