| // 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. |
| |
| #include "net/ntlm/ntlm_buffer_reader.h" |
| |
| #include <string.h> |
| |
| #include "base/logging.h" |
| #include "starboard/memory.h" |
| #include "starboard/types.h" |
| |
| namespace net { |
| namespace ntlm { |
| |
| NtlmBufferReader::NtlmBufferReader() {} |
| |
| NtlmBufferReader::NtlmBufferReader(base::span<const uint8_t> buffer) |
| : buffer_(buffer) {} |
| |
| NtlmBufferReader::~NtlmBufferReader() = default; |
| |
| bool NtlmBufferReader::CanRead(size_t len) const { |
| return CanReadFrom(GetCursor(), len); |
| } |
| |
| bool NtlmBufferReader::CanReadFrom(size_t offset, size_t len) const { |
| if (len == 0) |
| return true; |
| |
| return (len <= GetLength() && offset <= GetLength() - len); |
| } |
| |
| bool NtlmBufferReader::ReadUInt16(uint16_t* value) { |
| return ReadUInt<uint16_t>(value); |
| } |
| |
| bool NtlmBufferReader::ReadUInt32(uint32_t* value) { |
| return ReadUInt<uint32_t>(value); |
| } |
| |
| bool NtlmBufferReader::ReadUInt64(uint64_t* value) { |
| return ReadUInt<uint64_t>(value); |
| } |
| |
| bool NtlmBufferReader::ReadFlags(NegotiateFlags* flags) { |
| uint32_t raw; |
| if (!ReadUInt32(&raw)) |
| return false; |
| |
| *flags = static_cast<NegotiateFlags>(raw); |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadBytes(base::span<uint8_t> buffer) { |
| if (!CanRead(buffer.size())) |
| return false; |
| |
| if (buffer.empty()) |
| return true; |
| |
| memcpy(buffer.data(), GetBufferAtCursor(), buffer.size()); |
| |
| AdvanceCursor(buffer.size()); |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadBytesFrom(const SecurityBuffer& sec_buf, |
| base::span<uint8_t> buffer) { |
| if (!CanReadFrom(sec_buf) || buffer.size() < sec_buf.length) |
| return false; |
| |
| if (buffer.empty()) |
| return true; |
| |
| memcpy(buffer.data(), GetBufferPtr() + sec_buf.offset, sec_buf.length); |
| |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadPayloadAsBufferReader(const SecurityBuffer& sec_buf, |
| NtlmBufferReader* reader) { |
| if (!CanReadFrom(sec_buf)) |
| return false; |
| |
| *reader = NtlmBufferReader( |
| base::make_span(GetBufferPtr() + sec_buf.offset, sec_buf.length)); |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadSecurityBuffer(SecurityBuffer* sec_buf) { |
| return ReadUInt16(&sec_buf->length) && SkipBytes(sizeof(uint16_t)) && |
| ReadUInt32(&sec_buf->offset); |
| } |
| |
| bool NtlmBufferReader::ReadAvPairHeader(TargetInfoAvId* avid, uint16_t* avlen) { |
| if (!CanRead(kAvPairHeaderLen)) |
| return false; |
| |
| uint16_t raw_avid; |
| bool result = ReadUInt16(&raw_avid) && ReadUInt16(avlen); |
| DCHECK(result); |
| |
| // Don't try and validate the avid because the code only cares about a few |
| // specific ones and it is likely a future version might extend this field. |
| // The implementation can ignore and skip over AV Pairs it doesn't |
| // understand. |
| *avid = static_cast<TargetInfoAvId>(raw_avid); |
| |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadTargetInfo(size_t target_info_len, |
| std::vector<AvPair>* av_pairs) { |
| DCHECK(av_pairs->empty()); |
| |
| // A completely empty target info is allowed. |
| if (target_info_len == 0) |
| return true; |
| |
| // If there is any content there has to be at least one terminating header. |
| if (!CanRead(target_info_len) || target_info_len < kAvPairHeaderLen) { |
| return false; |
| } |
| |
| size_t target_info_end = GetCursor() + target_info_len; |
| bool saw_eol = false; |
| |
| while ((GetCursor() < target_info_end)) { |
| AvPair pair; |
| if (!ReadAvPairHeader(&pair.avid, &pair.avlen)) |
| break; |
| |
| // Make sure the length wouldn't read outside the buffer. |
| if (!CanRead(pair.avlen)) |
| return false; |
| |
| // Take a copy of the payload in the AVPair. |
| pair.buffer.assign(GetBufferAtCursor(), GetBufferAtCursor() + pair.avlen); |
| if (pair.avid == TargetInfoAvId::kEol) { |
| // Terminator must have zero length. |
| if (pair.avlen != 0) |
| return false; |
| |
| // Break out of the loop once a valid terminator is found. After the |
| // loop it will be validated that the whole target info was consumed. |
| saw_eol = true; |
| break; |
| } |
| |
| switch (pair.avid) { |
| case TargetInfoAvId::kFlags: |
| // For flags also populate the flags field so it doesn't |
| // have to be modified through the raw buffer later. |
| if (pair.avlen != sizeof(uint32_t) || |
| !ReadUInt32(reinterpret_cast<uint32_t*>(&pair.flags))) |
| return false; |
| break; |
| case TargetInfoAvId::kTimestamp: |
| // Populate timestamp so it doesn't need to be read through the |
| // raw buffer later. |
| if (pair.avlen != sizeof(uint64_t) || !ReadUInt64(&pair.timestamp)) |
| return false; |
| break; |
| case TargetInfoAvId::kChannelBindings: |
| case TargetInfoAvId::kTargetName: |
| // The server should never send these, and with EPA enabled the client |
| // will add these to the authenticate message. To avoid issues with |
| // duplicates or only one being read, just don't allow them. |
| return false; |
| default: |
| // For all other types, just jump over the payload to the next pair. |
| // If there aren't enough bytes left, then fail. |
| if (!SkipBytes(pair.avlen)) |
| return false; |
| break; |
| } |
| |
| av_pairs->push_back(std::move(pair)); |
| } |
| |
| // Fail if the buffer wasn't properly formed. The entire payload should have |
| // been consumed and a terminator found. |
| if ((GetCursor() != target_info_end) || !saw_eol) |
| return false; |
| |
| return true; |
| } |
| |
| bool NtlmBufferReader::ReadTargetInfoPayload(std::vector<AvPair>* av_pairs) { |
| DCHECK(av_pairs->empty()); |
| |
| SecurityBuffer sec_buf; |
| |
| // First read the security buffer. |
| if (!ReadSecurityBuffer(&sec_buf)) |
| return false; |
| |
| NtlmBufferReader payload_reader; |
| if (!ReadPayloadAsBufferReader(sec_buf, &payload_reader)) |
| return false; |
| |
| if (!payload_reader.ReadTargetInfo(sec_buf.length, av_pairs)) |
| return false; |
| |
| // |ReadTargetInfo| should have consumed the entire contents. |
| return payload_reader.IsEndOfBuffer(); |
| } |
| |
| bool NtlmBufferReader::ReadMessageType(MessageType* message_type) { |
| uint32_t raw_message_type; |
| if (!ReadUInt32(&raw_message_type)) |
| return false; |
| |
| *message_type = static_cast<MessageType>(raw_message_type); |
| |
| if (*message_type != MessageType::kNegotiate && |
| *message_type != MessageType::kChallenge && |
| *message_type != MessageType::kAuthenticate) |
| return false; |
| |
| return true; |
| } |
| |
| bool NtlmBufferReader::SkipSecurityBuffer() { |
| return SkipBytes(kSecurityBufferLen); |
| } |
| |
| bool NtlmBufferReader::SkipSecurityBufferWithValidation() { |
| SecurityBuffer sec_buf; |
| return ReadSecurityBuffer(&sec_buf) && CanReadFrom(sec_buf); |
| } |
| |
| bool NtlmBufferReader::SkipBytes(size_t count) { |
| if (!CanRead(count)) |
| return false; |
| |
| AdvanceCursor(count); |
| return true; |
| } |
| |
| bool NtlmBufferReader::MatchSignature() { |
| if (!CanRead(kSignatureLen)) |
| return false; |
| |
| if (memcmp(kSignature, GetBufferAtCursor(), kSignatureLen) != 0) |
| return false; |
| |
| AdvanceCursor(kSignatureLen); |
| return true; |
| } |
| |
| bool NtlmBufferReader::MatchMessageType(MessageType message_type) { |
| MessageType actual_message_type; |
| return ReadMessageType(&actual_message_type) && |
| (actual_message_type == message_type); |
| } |
| |
| bool NtlmBufferReader::MatchMessageHeader(MessageType message_type) { |
| return MatchSignature() && MatchMessageType(message_type); |
| } |
| |
| bool NtlmBufferReader::MatchZeros(size_t count) { |
| if (!CanRead(count)) |
| return false; |
| |
| for (size_t i = 0; i < count; i++) { |
| if (GetBufferAtCursor()[i] != 0) |
| return false; |
| } |
| |
| AdvanceCursor(count); |
| return true; |
| } |
| |
| bool NtlmBufferReader::MatchEmptySecurityBuffer() { |
| SecurityBuffer sec_buf; |
| return ReadSecurityBuffer(&sec_buf) && (sec_buf.offset <= GetLength()) && |
| (sec_buf.length == 0); |
| } |
| |
| template <typename T> |
| bool NtlmBufferReader::ReadUInt(T* value) { |
| size_t int_size = sizeof(T); |
| if (!CanRead(int_size)) |
| return false; |
| |
| *value = 0; |
| for (size_t i = 0; i < int_size; i++) { |
| *value += static_cast<T>(GetByteAtCursor()) << (i * 8); |
| AdvanceCursor(1); |
| } |
| |
| return true; |
| } |
| |
| void NtlmBufferReader::SetCursor(size_t cursor) { |
| DCHECK_LE(cursor, GetLength()); |
| |
| cursor_ = cursor; |
| } |
| |
| } // namespace ntlm |
| } // namespace net |