304 lines
6.7 KiB
C
304 lines
6.7 KiB
C
/**
|
|
* 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 */
|