mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 06:16:30 +03:00
226 lines
9.3 KiB
C++
226 lines
9.3 KiB
C++
// 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 <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "base/containers/span.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 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_
|