mirror of
https://github.com/feigeCode/navop.git
synced 2026-09-22 00:01:25 +00:00
274 lines
8.8 KiB
C++
274 lines
8.8 KiB
C++
#include "host_internal.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <new>
|
|
|
|
namespace {
|
|
|
|
NavopRdpResult validate_struct_size(
|
|
uint32_t struct_size,
|
|
uint32_t required_size) noexcept {
|
|
if (struct_size < required_size) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
|
|
NavopRdpResult validate_abi_version(uint32_t abi_version) noexcept {
|
|
if (abi_version != NAVOP_RDP_ABI_VERSION) {
|
|
return NAVOP_RDP_RESULT_ABI_MISMATCH;
|
|
}
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
|
|
NavopRdpResult validate_borrowed_secret(
|
|
NavopRdpBorrowedSecret secret) noexcept {
|
|
if (secret.len == UINT32_C(0)) {
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
if (secret.data == nullptr) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
const size_t code_unit_count = static_cast<size_t>(secret.len);
|
|
if (code_unit_count >
|
|
(std::numeric_limits<size_t>::max)() / sizeof(uint16_t)) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
|
|
NavopRdpResult validate_borrowed_utf16(
|
|
NavopRdpBorrowedUtf16 text) noexcept {
|
|
if (text.len == UINT32_C(0)) {
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
if (text.data == nullptr) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
const size_t code_unit_count = static_cast<size_t>(text.len);
|
|
if (code_unit_count >
|
|
(std::numeric_limits<size_t>::max)() / sizeof(uint16_t)) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
|
|
template <typename Field>
|
|
bool credential_field_available(
|
|
uint32_t struct_size,
|
|
size_t field_offset) noexcept {
|
|
return static_cast<size_t>(struct_size) >= field_offset + sizeof(Field);
|
|
}
|
|
|
|
template <typename Field>
|
|
Field read_credential_field(
|
|
const NavopRdpCredentialBundle* credentials,
|
|
size_t field_offset) noexcept {
|
|
Field value{};
|
|
std::memcpy(
|
|
&value,
|
|
reinterpret_cast<const uint8_t*>(credentials) + field_offset,
|
|
sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
class SensitiveUtf16Buffer {
|
|
public:
|
|
SensitiveUtf16Buffer() noexcept
|
|
: data_(nullptr),
|
|
byte_len_(0) {}
|
|
|
|
~SensitiveUtf16Buffer() noexcept {
|
|
reset();
|
|
}
|
|
|
|
SensitiveUtf16Buffer(const SensitiveUtf16Buffer&) = delete;
|
|
SensitiveUtf16Buffer& operator=(const SensitiveUtf16Buffer&) = delete;
|
|
SensitiveUtf16Buffer(SensitiveUtf16Buffer&&) = delete;
|
|
SensitiveUtf16Buffer& operator=(SensitiveUtf16Buffer&&) = delete;
|
|
|
|
NavopRdpResult copy_from(NavopRdpBorrowedSecret secret) noexcept {
|
|
reset();
|
|
if (secret.len == UINT32_C(0)) {
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
if (secret.data == nullptr) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
const size_t code_unit_count = static_cast<size_t>(secret.len);
|
|
if (code_unit_count >
|
|
(std::numeric_limits<size_t>::max)() / sizeof(uint16_t)) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
const size_t byte_len = code_unit_count * sizeof(uint16_t);
|
|
uint16_t* copied = new (std::nothrow) uint16_t[code_unit_count];
|
|
if (copied == nullptr) {
|
|
return NAVOP_RDP_RESULT_ALLOCATION_FAILED;
|
|
}
|
|
|
|
std::memcpy(copied, secret.data, byte_len);
|
|
data_ = copied;
|
|
byte_len_ = byte_len;
|
|
return NAVOP_RDP_RESULT_OK;
|
|
}
|
|
|
|
NavopRdpBorrowedSecret borrowed() const noexcept {
|
|
return NavopRdpBorrowedSecret {
|
|
data_,
|
|
static_cast<uint32_t>(byte_len_ / sizeof(uint16_t)),
|
|
};
|
|
}
|
|
|
|
private:
|
|
void reset() noexcept {
|
|
if (data_ == nullptr) {
|
|
return;
|
|
}
|
|
SecureZeroMemory(data_, byte_len_);
|
|
delete[] data_;
|
|
data_ = nullptr;
|
|
byte_len_ = 0;
|
|
}
|
|
|
|
uint16_t* data_;
|
|
size_t byte_len_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
extern "C" NavopRdpResult navop_rdp_apply_credentials(
|
|
NativeRdpHost* host,
|
|
const NavopRdpCredentialBundle* credentials) noexcept {
|
|
try {
|
|
if (host == nullptr) {
|
|
return NAVOP_RDP_RESULT_INVALID_ARGUMENT;
|
|
}
|
|
|
|
NavopRdpResult result = ensure_owner_thread(host);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return result;
|
|
}
|
|
clear_last_error(host);
|
|
if (credentials == nullptr) {
|
|
return record_last_error(host, NAVOP_RDP_RESULT_INVALID_ARGUMENT);
|
|
}
|
|
|
|
result = validate_struct_size(
|
|
credentials->struct_size,
|
|
NAVOP_RDP_CREDENTIAL_LEGACY_SIZE);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
|
|
result = validate_abi_version(credentials->abi_version);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
if (credentials->flags != UINT32_C(0)) {
|
|
return record_last_error(host, NAVOP_RDP_RESULT_INVALID_ARGUMENT);
|
|
}
|
|
if (host->callback_state != CallbackState::Open) {
|
|
return record_last_error(host, NAVOP_RDP_RESULT_INVALID_ARGUMENT);
|
|
}
|
|
|
|
result = validate_borrowed_secret(credentials->server_password);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
result = validate_borrowed_secret(credentials->gateway_password);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
|
|
NavopRdpBorrowedUtf16 username{};
|
|
if (credential_field_available<NavopRdpBorrowedUtf16>(
|
|
credentials->struct_size,
|
|
offsetof(NavopRdpCredentialBundle, username))) {
|
|
username = read_credential_field<NavopRdpBorrowedUtf16>(
|
|
credentials,
|
|
offsetof(NavopRdpCredentialBundle, username));
|
|
result = validate_borrowed_utf16(username);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
}
|
|
|
|
NavopRdpBorrowedUtf16 domain{};
|
|
if (credential_field_available<NavopRdpBorrowedUtf16>(
|
|
credentials->struct_size,
|
|
offsetof(NavopRdpCredentialBundle, domain))) {
|
|
domain = read_credential_field<NavopRdpBorrowedUtf16>(
|
|
credentials,
|
|
offsetof(NavopRdpCredentialBundle, domain));
|
|
result = validate_borrowed_utf16(domain);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
}
|
|
|
|
NavopRdpBorrowedUtf16 gateway_username{};
|
|
if (credential_field_available<NavopRdpBorrowedUtf16>(
|
|
credentials->struct_size,
|
|
offsetof(NavopRdpCredentialBundle, gateway_username))) {
|
|
gateway_username = read_credential_field<NavopRdpBorrowedUtf16>(
|
|
credentials,
|
|
offsetof(NavopRdpCredentialBundle, gateway_username));
|
|
result = validate_borrowed_utf16(gateway_username);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
}
|
|
|
|
NavopRdpBorrowedUtf16 gateway_domain{};
|
|
if (credential_field_available<NavopRdpBorrowedUtf16>(
|
|
credentials->struct_size,
|
|
offsetof(NavopRdpCredentialBundle, gateway_domain))) {
|
|
gateway_domain = read_credential_field<NavopRdpBorrowedUtf16>(
|
|
credentials,
|
|
offsetof(NavopRdpCredentialBundle, gateway_domain));
|
|
result = validate_borrowed_utf16(gateway_domain);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
}
|
|
|
|
SensitiveUtf16Buffer server_password;
|
|
SensitiveUtf16Buffer gateway_password;
|
|
result = server_password.copy_from(credentials->server_password);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
result = gateway_password.copy_from(credentials->gateway_password);
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return record_last_error(host, result);
|
|
}
|
|
|
|
if (host->active_x_resources != nullptr) {
|
|
result = apply_active_x_credentials(
|
|
host,
|
|
host->active_x_resources,
|
|
username,
|
|
domain,
|
|
server_password.borrowed(),
|
|
gateway_username,
|
|
gateway_domain,
|
|
gateway_password.borrowed());
|
|
if (result != NAVOP_RDP_RESULT_OK) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Both temporary password buffers are wiped by RAII on success,
|
|
// failure, or exception before this synchronous ABI call returns.
|
|
return NAVOP_RDP_RESULT_OK;
|
|
} catch (...) {
|
|
return record_last_error(host, NAVOP_RDP_RESULT_INTERNAL_ERROR);
|
|
}
|
|
}
|