mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-14 00:14:11 +09:00
Merge pull request #11790 from akallabeth/fix-cred-marshal
[winpr,credentials] prefer utf-8 over utf-16-LE
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
static BYTE wchar_decode(WCHAR c)
|
||||
static BYTE char_decode(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return (BYTE)(c - 'A');
|
||||
@@ -49,26 +49,26 @@ static BYTE wchar_decode(WCHAR c)
|
||||
return 64;
|
||||
}
|
||||
|
||||
static BOOL cred_decode(const WCHAR* cred, size_t len, BYTE* buf)
|
||||
static BOOL cred_decode(const char* cred, size_t len, BYTE* buf)
|
||||
{
|
||||
size_t i = 0;
|
||||
const WCHAR* p = cred;
|
||||
const char* p = cred;
|
||||
|
||||
while (len >= 4)
|
||||
{
|
||||
BYTE c0 = wchar_decode(p[0]);
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = wchar_decode(p[1]);
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c2 = wchar_decode(p[2]);
|
||||
BYTE c2 = char_decode(p[2]);
|
||||
if (c2 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c3 = wchar_decode(p[3]);
|
||||
BYTE c3 = char_decode(p[3]);
|
||||
if (c3 > 63)
|
||||
return FALSE;
|
||||
|
||||
@@ -82,15 +82,15 @@ static BOOL cred_decode(const WCHAR* cred, size_t len, BYTE* buf)
|
||||
|
||||
if (len == 3)
|
||||
{
|
||||
BYTE c0 = wchar_decode(p[0]);
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = wchar_decode(p[1]);
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c2 = wchar_decode(p[2]);
|
||||
BYTE c2 = char_decode(p[2]);
|
||||
if (c2 > 63)
|
||||
return FALSE;
|
||||
|
||||
@@ -99,11 +99,11 @@ static BOOL cred_decode(const WCHAR* cred, size_t len, BYTE* buf)
|
||||
}
|
||||
else if (len == 2)
|
||||
{
|
||||
BYTE c0 = wchar_decode(p[0]);
|
||||
BYTE c0 = char_decode(p[0]);
|
||||
if (c0 > 63)
|
||||
return FALSE;
|
||||
|
||||
BYTE c1 = wchar_decode(p[1]);
|
||||
BYTE c1 = char_decode(p[1]);
|
||||
if (c1 > 63)
|
||||
return FALSE;
|
||||
|
||||
@@ -117,54 +117,72 @@ static BOOL cred_decode(const WCHAR* cred, size_t len, BYTE* buf)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static size_t cred_encode(const BYTE* bin, size_t len, WCHAR* cred)
|
||||
static size_t cred_encode(const BYTE* bin, size_t len, char* cred, size_t credlen)
|
||||
{
|
||||
static const WCHAR encodingChars[] = {
|
||||
/* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
|
||||
/* abcdefghijklmnopqrstuvwxyz */
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
|
||||
/* 0123456789 */
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
/* #- */
|
||||
0x23, 0x2d
|
||||
};
|
||||
static const char encodingChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"#-";
|
||||
size_t n = 0;
|
||||
|
||||
while (len > 0)
|
||||
size_t offset = 0;
|
||||
while (offset < len)
|
||||
{
|
||||
cred[n++] = encodingChars[bin[0] & 0x3f];
|
||||
BYTE x = (bin[0] & 0xc0) >> 6;
|
||||
if (len == 1)
|
||||
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;
|
||||
}
|
||||
|
||||
cred[n++] = encodingChars[((bin[1] & 0xf) << 2) | x];
|
||||
x = (bin[1] & 0xf0) >> 4;
|
||||
if (len == 2)
|
||||
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;
|
||||
}
|
||||
|
||||
cred[n++] = encodingChars[((bin[2] & 0x3) << 4) | x];
|
||||
cred[n++] = encodingChars[(bin[2] & 0xfc) >> 2];
|
||||
bin += 3;
|
||||
len -= 3;
|
||||
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 cred, LPWSTR* MarshaledCredential)
|
||||
BOOL CredMarshalCredentialW(CRED_MARSHAL_TYPE CredType, PVOID Credential,
|
||||
LPWSTR* MarshaledCredential)
|
||||
{
|
||||
CERT_CREDENTIAL_INFO* cert = cred;
|
||||
WCHAR* p = NULL;
|
||||
char* b = NULL;
|
||||
if (!CredMarshalCredentialA(CredType, Credential, &b) || !b)
|
||||
return FALSE;
|
||||
|
||||
if (!cred || (CredType == CertCredential && cert->cbSize < sizeof(*cert)))
|
||||
*MarshaledCredential = ConvertUtf8ToWCharAlloc(b, NULL);
|
||||
free(b);
|
||||
return (*MarshaledCredential != NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -174,96 +192,86 @@ BOOL CredMarshalCredentialW(CRED_MARSHAL_TYPE CredType, PVOID cred, LPWSTR* Mars
|
||||
{
|
||||
case CertCredential:
|
||||
{
|
||||
size_t size = (sizeof(cert->rgbHashOfCert) + 2) * 4 / 3;
|
||||
if (!(p = malloc((size + 4) * sizeof(WCHAR))))
|
||||
return FALSE;
|
||||
p[0] = '@';
|
||||
p[1] = '@';
|
||||
p[2] = (WCHAR)('A' + CredType);
|
||||
size_t len = cred_encode(cert->rgbHashOfCert, sizeof(cert->rgbHashOfCert), p + 3);
|
||||
p[len + 3] = 0;
|
||||
break;
|
||||
char buffer[3ULL + (sizeof(cert->rgbHashOfCert) * 4 / 3) +
|
||||
1ULL /* rounding error */] = { 0 };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
*MarshaledCredential = p;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CredMarshalCredentialA(CRED_MARSHAL_TYPE CredType, PVOID Credential,
|
||||
LPSTR* MarshaledCredential)
|
||||
{
|
||||
WCHAR* b = NULL;
|
||||
if (!CredMarshalCredentialW(CredType, Credential, &b) || !b)
|
||||
return FALSE;
|
||||
|
||||
*MarshaledCredential = ConvertWCharNToUtf8Alloc(b, _wcslen(b), NULL);
|
||||
free(b);
|
||||
return (*MarshaledCredential != NULL);
|
||||
}
|
||||
|
||||
BOOL CredUnmarshalCredentialW(LPCWSTR cred, PCRED_MARSHAL_TYPE pcredType, PVOID* out)
|
||||
{
|
||||
if (!cred || !pcredType || !out || cred[0] != '@' || cred[1] != '@')
|
||||
char* str = NULL;
|
||||
if (cred)
|
||||
str = ConvertWCharToUtf8Alloc(cred, NULL);
|
||||
const BOOL rc = CredUnmarshalCredentialA(str, pcredType, out);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL CredUnmarshalCredentialA(LPCSTR cred, PCRED_MARSHAL_TYPE CredType, PVOID* Credential)
|
||||
{
|
||||
if (!cred)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BYTE b = wchar_decode(cred[2]);
|
||||
if (!b || b > BinaryBlobForSystem)
|
||||
const size_t len = strlen(cred);
|
||||
if ((len < 3) || !CredType || !Credential || (cred[0] != '@') || (cred[1] != '@'))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*pcredType = (CRED_MARSHAL_TYPE)b;
|
||||
BYTE b = char_decode(cred[2]);
|
||||
if (!b || (b > BinaryBlobForSystem))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
size_t len = _wcslen(cred + 3);
|
||||
switch (*pcredType)
|
||||
*CredType = (CRED_MARSHAL_TYPE)b;
|
||||
|
||||
switch (*CredType)
|
||||
{
|
||||
case CertCredential:
|
||||
{
|
||||
BYTE hash[CERT_HASH_LENGTH];
|
||||
BYTE hash[CERT_HASH_LENGTH] = { 0 };
|
||||
|
||||
if (len != 27 || !cred_decode(cred + 3, len, hash))
|
||||
if ((len != 30) || !cred_decode(&cred[3], len - 3, hash))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CERT_CREDENTIAL_INFO* cert = malloc(sizeof(*cert));
|
||||
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));
|
||||
cert->cbSize = sizeof(*cert);
|
||||
*out = cert;
|
||||
*Credential = cert;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WLog_ERR(TAG, "unhandled credType 0x%x", *pcredType);
|
||||
WLog_ERR(TAG, "unhandled credType 0x%x", *CredType);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CredUnmarshalCredentialA(LPCSTR cred, PCRED_MARSHAL_TYPE CredType, PVOID* Credential)
|
||||
{
|
||||
WCHAR* b = ConvertUtf8NToWCharAlloc(cred, strlen(cred), NULL);
|
||||
if (!b)
|
||||
return FALSE;
|
||||
|
||||
BOOL ret = CredUnmarshalCredentialW(b, CredType, Credential);
|
||||
free(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL CredIsMarshaledCredentialW(LPCWSTR MarshaledCredential)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
|
||||
@@ -25,29 +25,32 @@ typedef struct
|
||||
BYTE source[CERT_HASH_LENGTH];
|
||||
} TestItem;
|
||||
|
||||
static 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 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(int argc, char** argv)
|
||||
static int TestUnmarshal(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char** argv)
|
||||
{
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
for (size_t i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
CERT_CREDENTIAL_INFO* certInfo = NULL;
|
||||
const TestItem* const val = &testValues[i];
|
||||
|
||||
if (!CredUnmarshalCredentialA(testValues[i].marshalled, &t, (void**)&certInfo) ||
|
||||
!certInfo || t != CertCredential)
|
||||
if (!CredUnmarshalCredentialA(val->marshalled, &t, (void**)&certInfo) || !certInfo ||
|
||||
(t != CertCredential))
|
||||
return -1;
|
||||
|
||||
BOOL ok = memcmp(testValues[i].source, certInfo->rgbHashOfCert,
|
||||
sizeof(certInfo->rgbHashOfCert)) == 0;
|
||||
const BOOL ok =
|
||||
memcmp(val->source, certInfo->rgbHashOfCert, sizeof(certInfo->rgbHashOfCert)) == 0;
|
||||
|
||||
free(certInfo);
|
||||
|
||||
@@ -57,20 +60,21 @@ static int TestUnmarshal(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TestMarshal(int argc, char** argv)
|
||||
static int TestMarshal(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char** argv)
|
||||
{
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
for (size_t i = 0; i < ARRAYSIZE(testValues); i++)
|
||||
{
|
||||
CRED_MARSHAL_TYPE t = BinaryBlobForSystem;
|
||||
CERT_CREDENTIAL_INFO certInfo = { sizeof(certInfo), { 0 } };
|
||||
memcpy(certInfo.rgbHashOfCert, testValues[i].source, sizeof(certInfo.rgbHashOfCert));
|
||||
const TestItem* const val = &testValues[i];
|
||||
|
||||
memcpy(certInfo.rgbHashOfCert, val->source, sizeof(certInfo.rgbHashOfCert));
|
||||
LPSTR out = NULL;
|
||||
|
||||
if (!CredMarshalCredentialA(CertCredential, &certInfo, &out) || !out)
|
||||
return -1;
|
||||
|
||||
BOOL ok = (strcmp(testValues[i].marshalled, out) == 0);
|
||||
BOOL ok = (strcmp(val->marshalled, out) == 0);
|
||||
|
||||
free(out);
|
||||
|
||||
|
||||
@@ -124,6 +124,26 @@ static const uint32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2
|
||||
*/
|
||||
static const uint8_t firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
/* We always need UTF-16LE, even on big endian systems! */
|
||||
static WCHAR setWcharFrom(WCHAR w)
|
||||
{
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
union
|
||||
{
|
||||
WCHAR w;
|
||||
char c[2];
|
||||
} cnv;
|
||||
|
||||
cnv.w = w;
|
||||
const char c = cnv.c[0];
|
||||
cnv.c[0] = cnv.c[1];
|
||||
cnv.c[1] = c;
|
||||
return cnv.w;
|
||||
#else
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* The interface converts a whole buffer to avoid function-call overhead.
|
||||
@@ -155,7 +175,7 @@ static ConversionResult winpr_ConvertUTF16toUTF8_Internal(const uint16_t** sourc
|
||||
const uint16_t* oldSource =
|
||||
source; /* In case we have to back up because of target overflow. */
|
||||
|
||||
ch = *source++;
|
||||
ch = setWcharFrom(*source++);
|
||||
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
|
||||
@@ -163,7 +183,7 @@ static ConversionResult winpr_ConvertUTF16toUTF8_Internal(const uint16_t** sourc
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd)
|
||||
{
|
||||
uint32_t ch2 = *source;
|
||||
uint32_t ch2 = setWcharFrom(*source);
|
||||
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
|
||||
@@ -472,7 +492,7 @@ static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** source
|
||||
else
|
||||
{
|
||||
if (!computeLength)
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
*target++ = setWcharFrom(UNI_REPLACEMENT_CHAR);
|
||||
else
|
||||
target++;
|
||||
}
|
||||
@@ -480,7 +500,7 @@ static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** source
|
||||
else
|
||||
{
|
||||
if (!computeLength)
|
||||
*target++ = (uint16_t)ch; /* normal case */
|
||||
*target++ = setWcharFrom((WCHAR)ch); /* normal case */
|
||||
else
|
||||
target++;
|
||||
}
|
||||
@@ -496,7 +516,7 @@ static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** source
|
||||
else
|
||||
{
|
||||
if (!computeLength)
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
*target++ = setWcharFrom(UNI_REPLACEMENT_CHAR);
|
||||
else
|
||||
target++;
|
||||
}
|
||||
@@ -515,8 +535,8 @@ static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** source
|
||||
|
||||
if (!computeLength)
|
||||
{
|
||||
*target++ = (uint16_t)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (uint16_t)((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
*target++ = setWcharFrom((WCHAR)((ch >> halfShift) + UNI_SUR_HIGH_START));
|
||||
*target++ = setWcharFrom((WCHAR)((ch & halfMask) + UNI_SUR_LOW_START));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user