Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
22
third_party/FreeRDP/winpr/libwinpr/credentials/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/winpr/libwinpr/credentials/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# WinPR: Windows Portable Runtime
|
||||
# libwinpr-credentials cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
winpr_module_add(credentials.c)
|
||||
|
||||
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
9
third_party/FreeRDP/winpr/libwinpr/credentials/ModuleOptions.cmake
vendored
Normal file
9
third_party/FreeRDP/winpr/libwinpr/credentials/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
set(MINWIN_LAYER "1")
|
||||
set(MINWIN_GROUP "security")
|
||||
set(MINWIN_MAJOR_VERSION "1")
|
||||
set(MINWIN_MINOR_VERSION "0")
|
||||
set(MINWIN_SHORT_NAME "credentials")
|
||||
set(MINWIN_LONG_NAME "Credentials Management Functions")
|
||||
set(MODULE_LIBRARY_NAME
|
||||
"api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}"
|
||||
)
|
||||
303
third_party/FreeRDP/winpr/libwinpr/credentials/credentials.c
vendored
Normal file
303
third_party/FreeRDP/winpr/libwinpr/credentials/credentials.c
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Credentials Management
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2025 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cred.h>
|
||||
#include <winpr/error.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include "../log.h"
|
||||
|
||||
#define TAG WINPR_TAG("Cred")
|
||||
|
||||
/*
|
||||
* Low-Level Credentials Management Functions:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374731(v=vs.85).aspx#low_level_credentials_management_functions
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
static BYTE char_decode(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return (BYTE)(c - 'A');
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return (BYTE)(c - 'a' + 26);
|
||||
if (c >= '0' && c <= '9')
|
||||
return (BYTE)(c - '0' + 52);
|
||||
if (c == '#')
|
||||
return 62;
|
||||
if (c == '-')
|
||||
return 63;
|
||||
return 64;
|
||||
}
|
||||
|
||||
static BOOL cred_decode(const char* cred, size_t len, BYTE* buf)
|
||||
{
|
||||
size_t i = 0;
|
||||
const char* p = cred;
|
||||
|
||||
while (len >= 4)
|
||||
{
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c2 = char_decode(p[2]);
|
||||
if (c2 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c3 = char_decode(p[3]);
|
||||
if (c3 > 63)
|
||||
return FALSE;
|
||||
|
||||
buf[i + 0] = (BYTE)((c1 << 6) | c0);
|
||||
buf[i + 1] = (BYTE)((c2 << 4) | (c1 >> 2));
|
||||
buf[i + 2] = (BYTE)((c3 << 2) | (c2 >> 4));
|
||||
len -= 4;
|
||||
i += 3;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
if (len == 3)
|
||||
{
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c2 = char_decode(p[2]);
|
||||
if (c2 > 63)
|
||||
return FALSE;
|
||||
|
||||
buf[i + 0] = (BYTE)((c1 << 6) | c0);
|
||||
buf[i + 1] = (BYTE)((c2 << 4) | (c1 >> 2));
|
||||
}
|
||||
else if (len == 2)
|
||||
{
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
buf[i + 0] = (BYTE)((c1 << 6) | c0);
|
||||
}
|
||||
else if (len == 1)
|
||||
{
|
||||
WLog_ERR(TAG, "invalid string length");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static size_t cred_encode(const BYTE* bin, size_t len, char* cred, size_t credlen)
|
||||
{
|
||||
static const char encodingChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"#-";
|
||||
size_t n = 0;
|
||||
size_t offset = 0;
|
||||
while (offset < len)
|
||||
{
|
||||
if (n >= credlen)
|
||||
break;
|
||||
|
||||
cred[n++] = encodingChars[bin[offset] & 0x3f];
|
||||
BYTE x = (bin[offset] & 0xc0) >> 6;
|
||||
offset++;
|
||||
|
||||
if (offset >= len)
|
||||
{
|
||||
cred[n++] = encodingChars[x];
|
||||
break;
|
||||
}
|
||||
|
||||
if (n >= credlen)
|
||||
break;
|
||||
cred[n++] = encodingChars[((bin[offset] & 0xf) << 2) | x];
|
||||
x = (bin[offset] & 0xf0) >> 4;
|
||||
offset++;
|
||||
|
||||
if (offset >= len)
|
||||
{
|
||||
cred[n++] = encodingChars[x];
|
||||
break;
|
||||
}
|
||||
|
||||
if (n >= credlen)
|
||||
break;
|
||||
cred[n++] = encodingChars[((bin[offset] & 0x3) << 4) | x];
|
||||
|
||||
if (n >= credlen)
|
||||
break;
|
||||
cred[n++] = encodingChars[(bin[offset] & 0xfc) >> 2];
|
||||
offset++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
BOOL CredMarshalCredentialW(CRED_MARSHAL_TYPE CredType, PVOID Credential,
|
||||
LPWSTR* MarshaledCredential)
|
||||
{
|
||||
char* b = nullptr;
|
||||
if (!CredMarshalCredentialA(CredType, Credential, &b) || !b)
|
||||
return FALSE;
|
||||
|
||||
*MarshaledCredential = ConvertUtf8ToWCharAlloc(b, nullptr);
|
||||
free(b);
|
||||
return (*MarshaledCredential != nullptr);
|
||||
}
|
||||
|
||||
BOOL CredMarshalCredentialA(CRED_MARSHAL_TYPE CredType, PVOID Credential,
|
||||
LPSTR* MarshaledCredential)
|
||||
{
|
||||
CERT_CREDENTIAL_INFO* cert = Credential;
|
||||
|
||||
if (!cert || ((CredType == CertCredential) && (cert->cbSize < sizeof(CERT_CREDENTIAL_INFO))) ||
|
||||
!MarshaledCredential)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (CredType)
|
||||
{
|
||||
case CertCredential:
|
||||
{
|
||||
char buffer[3ULL + (sizeof(cert->rgbHashOfCert) * 4 / 3) + 1ULL /* rounding error */] =
|
||||
WINPR_C_ARRAY_INIT;
|
||||
|
||||
const char c = WINPR_ASSERTING_INT_CAST(char, 'A' + CredType);
|
||||
(void)_snprintf(buffer, sizeof(buffer), "@@%c", c);
|
||||
size_t len = cred_encode(cert->rgbHashOfCert, sizeof(cert->rgbHashOfCert), &buffer[3],
|
||||
sizeof(buffer) - 3);
|
||||
*MarshaledCredential = strndup(buffer, len + 3);
|
||||
return TRUE;
|
||||
}
|
||||
default:
|
||||
WLog_ERR(TAG, "unhandled type 0x%x", CredType);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CredUnmarshalCredentialW(LPCWSTR cred, PCRED_MARSHAL_TYPE CredType, PVOID* Credential)
|
||||
{
|
||||
char* str = nullptr;
|
||||
if (cred)
|
||||
str = ConvertWCharToUtf8Alloc(cred, nullptr);
|
||||
const BOOL rc = CredUnmarshalCredentialA(str, CredType, Credential);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL CredUnmarshalCredentialA(LPCSTR cred, PCRED_MARSHAL_TYPE CredType, PVOID* Credential)
|
||||
{
|
||||
if (!cred)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const size_t len = strlen(cred);
|
||||
if ((len < 3) || !CredType || !Credential || (cred[0] != '@') || (cred[1] != '@'))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BYTE b = char_decode(cred[2]);
|
||||
if (!b || (b > BinaryBlobForSystem))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*CredType = (CRED_MARSHAL_TYPE)b;
|
||||
|
||||
switch (*CredType)
|
||||
{
|
||||
case CertCredential:
|
||||
{
|
||||
BYTE hash[CERT_HASH_LENGTH] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if ((len != 30) || !cred_decode(&cred[3], len - 3, hash))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CERT_CREDENTIAL_INFO* cert = calloc(1, sizeof(CERT_CREDENTIAL_INFO));
|
||||
if (!cert)
|
||||
return FALSE;
|
||||
|
||||
cert->cbSize = sizeof(CERT_CREDENTIAL_INFO);
|
||||
memcpy(cert->rgbHashOfCert, hash, sizeof(cert->rgbHashOfCert));
|
||||
*Credential = cert;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WLog_ERR(TAG, "unhandled credType 0x%x", *CredType);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CredIsMarshaledCredentialW(LPCWSTR MarshaledCredential)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
void* out = nullptr;
|
||||
|
||||
BOOL ret = CredUnmarshalCredentialW(MarshaledCredential, &t, &out);
|
||||
if (out)
|
||||
CredFree(out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL CredIsMarshaledCredentialA(LPCSTR MarshaledCredential)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
void* out = nullptr;
|
||||
BOOL ret = CredUnmarshalCredentialA(MarshaledCredential, &t, &out);
|
||||
if (out)
|
||||
CredFree(out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
VOID CredFree(PVOID Buffer)
|
||||
{
|
||||
free(Buffer);
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
23
third_party/FreeRDP/winpr/libwinpr/credentials/test/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/winpr/libwinpr/credentials/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
set(MODULE_NAME "TestCredentials")
|
||||
set(MODULE_PREFIX "TEST_CREDENTIALS")
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS TestMarshalUnmarshal.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr ${OPENSSL_LIBRARIES})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
95
third_party/FreeRDP/winpr/libwinpr/credentials/test/TestMarshalUnmarshal.c
vendored
Normal file
95
third_party/FreeRDP/winpr/libwinpr/credentials/test/TestMarshalUnmarshal.c
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Buffer Manipulation
|
||||
*
|
||||
* Copyright 2025 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <winpr/cred.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LPCSTR marshalled;
|
||||
BYTE source[CERT_HASH_LENGTH];
|
||||
} TestItem;
|
||||
|
||||
static const TestItem testValues[] = {
|
||||
{ "@@BQ9eNR0KWVU-CT8sPCp8z37POZHJ",
|
||||
{ 0x50, 0xef, 0x35, 0x11, 0xad, 0x58, 0x15, 0xf5, 0x0b, 0x13,
|
||||
0xcf, 0x3e, 0x42, 0xca, 0xcf, 0xf7, 0xfe, 0x38, 0xd9, 0x91 } },
|
||||
{ "@@BKay-HwJsFZzclXAWZ#nO6Eluc7P",
|
||||
{ 0x8a, 0x26, 0xff, 0x07, 0x9c, 0xb0, 0x45, 0x36, 0x73, 0xe5,
|
||||
0x05, 0x58, 0x99, 0x7f, 0x3a, 0x3a, 0x51, 0xba, 0xdc, 0xfe }
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
static int TestUnmarshal(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char** argv)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
CERT_CREDENTIAL_INFO* certInfo = nullptr;
|
||||
const TestItem* const val = &testValues[i];
|
||||
|
||||
if (!CredUnmarshalCredentialA(val->marshalled, &t, (void**)&certInfo) || !certInfo ||
|
||||
(t != CertCredential))
|
||||
return -1;
|
||||
|
||||
const BOOL ok =
|
||||
memcmp(val->source, certInfo->rgbHashOfCert, sizeof(certInfo->rgbHashOfCert)) == 0;
|
||||
|
||||
free(certInfo);
|
||||
|
||||
if (!ok)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TestMarshal(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char** argv)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
{
|
||||
CERT_CREDENTIAL_INFO certInfo = { sizeof(certInfo), { 0 } };
|
||||
const TestItem* const val = &testValues[i];
|
||||
|
||||
memcpy(certInfo.rgbHashOfCert, val->source, sizeof(certInfo.rgbHashOfCert));
|
||||
LPSTR out = nullptr;
|
||||
|
||||
if (!CredMarshalCredentialA(CertCredential, &certInfo, &out) || !out)
|
||||
return -1;
|
||||
|
||||
BOOL ok = (strcmp(val->marshalled, out) == 0);
|
||||
|
||||
free(out);
|
||||
|
||||
if (!ok)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestMarshalUnmarshal(int argc, char** argv)
|
||||
{
|
||||
int ret = TestUnmarshal(argc, argv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = TestMarshal(argc, argv);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user