Try to use the smartcard key name Windows uses

Windows expects the containerName field in TSSmartCardCreds to be what
it would use for a smartcard key's name. Try to accomodate that (at
least for PIV and GIDS cards).
This commit is contained in:
fifthdegree
2022-10-16 11:36:20 -04:00
committed by David Fort
parent 9d0beaccae
commit e847f159a6
9 changed files with 230 additions and 23 deletions

View File

@@ -528,14 +528,17 @@ BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, DWO
{
const SmartcardCertInfo* cert = cert_list[i];
char* reader = NULL;
char* container_name = NULL;
ConvertFromUnicode(CP_UTF8, 0, cert->reader, -1, &reader, 0, NULL, NULL);
ConvertFromUnicode(CP_UTF8, 0, cert->containerName, -1, &container_name, 0, NULL, NULL);
printf("[%" PRIu32
"] %s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s\n",
i, cert->containerName, reader, cert->userHint, cert->domainHint, cert->subject,
i, container_name, reader, cert->userHint, cert->domainHint, cert->subject,
cert->issuer, cert->upn);
free(reader);
free(container_name);
}
while (1)

View File

@@ -50,7 +50,9 @@ BOOL freerdp_smartcard_list(const rdpSettings* settings)
printf("\t* slotId: %" PRIu32 "\n", info->slotId);
printf("\t* pkinitArgs: %s\n", info->pkinitArgs);
#endif
printf("\t* containerName: %s\n", info->containerName);
if (WideCharToMultiByte(CP_UTF8, 0, info->containerName, -1, asciiStr, sizeof(asciiStr),
NULL, NULL) > 0)
printf("\t* containerName: %s\n", asciiStr);
if (info->upn)
printf("\t* UPN: %s\n", info->upn);
}

View File

@@ -31,7 +31,8 @@ typedef struct SmartcardCertInfo_st
CryptoCert certificate;
char* pkinitArgs;
UINT32 slotId;
char* containerName;
char* keyName;
WCHAR* containerName;
char* upn;
char* userHint;
char* domainHint;

View File

@@ -251,8 +251,8 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla)
if (!settings->ContainerName && nla->smartcardCert->containerName)
{
if (!freerdp_settings_set_string(settings, FreeRDP_ContainerName,
nla->smartcardCert->containerName))
if (ConvertFromUnicode(CP_UTF8, 0, nla->smartcardCert->containerName, -1,
&settings->ContainerName, 0, NULL, NULL) < 0)
{
WLog_ERR(TAG, "unable to copy container name");
goto out;

View File

@@ -90,6 +90,7 @@ void smartcardCertInfo_Free(SmartcardCertInfo* scCert)
free(scCert->reader);
crypto_cert_free(scCert->certificate);
free(scCert->pkinitArgs);
free(scCert->keyName);
free(scCert->containerName);
free(scCert->upn);
free(scCert->userHint);
@@ -117,7 +118,7 @@ static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
scCert->upn = crypto_cert_get_upn(scCert->certificate->px509);
if (!scCert->upn)
{
WLog_DBG(TAG, "%s has no UPN, trying emailAddress", scCert->containerName);
WLog_DBG(TAG, "%s has no UPN, trying emailAddress", scCert->keyName);
scCert->upn = crypto_cert_get_email(scCert->certificate->px509);
}
@@ -128,7 +129,7 @@ static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
if (!atPos)
{
WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->containerName);
WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->keyName);
return FALSE;
}
@@ -138,8 +139,7 @@ static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
if (!scCert->userHint || !scCert->domainHint)
{
WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s",
scCert->containerName);
WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s", scCert->keyName);
return FALSE;
}
@@ -217,11 +217,11 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
if (!cert)
goto out;
if (ConvertFromUnicode(CP_UTF8, 0, keyName->pszName, -1, &cert->containerName, 0, NULL,
NULL) <= 0)
if (ConvertFromUnicode(CP_UTF8, 0, keyName->pszName, -1, &cert->keyName, 0, NULL, NULL) <=
0)
goto endofloop;
WLog_DBG(TAG, "opening key %s", cert->containerName);
WLog_DBG(TAG, "opening key %s", cert->keyName);
status =
NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
@@ -244,7 +244,7 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
dwFlags);
if (status != ERROR_SUCCESS)
{
WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->containerName,
WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->keyName,
winpr_NCryptSecurityStatusError(status));
goto endofloop;
}
@@ -255,15 +255,14 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, dwFlags);
if (status != ERROR_SUCCESS)
{
WLog_DBG(TAG, "unable to retrieve reader's name length for key %s",
cert->containerName);
WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", cert->keyName);
goto endofloop;
}
cert->reader = calloc(1, cbOutput + 2);
if (!cert->reader)
{
WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->containerName);
WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->keyName);
goto endofloop;
}
@@ -271,7 +270,30 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
&cbOutput, dwFlags);
if (status != ERROR_SUCCESS)
{
WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->containerName);
WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->keyName);
goto endofloop;
}
/* ====== retrieve key container name ====== */
/* When using PKCS11, this will try to return what Windows would use for the key's name */
cbOutput = 0;
status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, NULL, 0, &cbOutput, dwFlags);
if (status == ERROR_SUCCESS)
{
cert->containerName = calloc(1, cbOutput + sizeof(WCHAR));
if (!cert->containerName)
{
WLog_ERR(TAG, "unable to allocate key container name for key %s", cert->keyName);
goto endofloop;
}
status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
cbOutput, &cbOutput, dwFlags);
}
if (status != ERROR_SUCCESS)
{
WLog_ERR(TAG, "unable to retrieve key container name for key %s", cert->keyName);
goto endofloop;
}
@@ -290,7 +312,7 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
if (!certBytes)
{
WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", cbOutput,
cert->containerName);
cert->keyName);
goto endofloop;
}
@@ -298,14 +320,14 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
&cbOutput, dwFlags);
if (status != ERROR_SUCCESS)
{
WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->containerName);
WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
goto endofloop;
}
if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbOutput, cert->sha1Hash,
sizeof(cert->sha1Hash)))
{
WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->containerName);
WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->keyName);
goto endofloop;
}
@@ -313,7 +335,7 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p
if (!cert->certificate)
{
WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->containerName);
WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->keyName);
goto endofloop;
}
@@ -554,8 +576,7 @@ static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCe
if (ConvertToUnicode(CP_UTF8, 0, "FreeRDP Emulator", -1, &cert->reader, 0) < 0)
goto out_error;
cert->containerName = _strdup("Private Key 00");
if (!cert->containerName)
if (ConvertToUnicode(CP_UTF8, 0, "Private Key 00", -1, &cert->containerName, 0) < 0)
goto out_error;
/* compute PKINIT args FILE:<cert file>,<key file>

View File

@@ -114,6 +114,7 @@ typedef ULONG_PTR NCRYPT_KEY_HANDLE;
"c\x00" \
"a\x00t\x00" \
"e\x00\x00"
#define NCRYPT_NAME_PROPERTY (const WCHAR*)"N\x00a\x00m\x00e\x00\x00"
#define NCRYPT_UNIQUE_NAME_PROPERTY \
(const WCHAR*)"U\x00n\x00i\x00q\x00u\x00" \
"e\x00 \x00N\x00" \

View File

@@ -197,6 +197,10 @@ static NCryptKeyGetPropertyEnum propertyStringToEnum(LPCWSTR pszProperty)
{
return NCRYPT_PROPERTY_SLOTID;
}
else if (_wcscmp(pszProperty, NCRYPT_NAME_PROPERTY) == 0)
{
return NCRYPT_PROPERTY_NAME;
}
return NCRYPT_PROPERTY_UNKNOWN;
}

View File

@@ -45,6 +45,7 @@ typedef enum
NCRYPT_PROPERTY_CERTIFICATE,
NCRYPT_PROPERTY_READER,
NCRYPT_PROPERTY_SLOTID,
NCRYPT_PROPERTY_NAME,
NCRYPT_PROPERTY_UNKNOWN
} NCryptKeyGetPropertyEnum;

View File

@@ -23,6 +23,8 @@
#include <winpr/library.h>
#include <winpr/assert.h>
#include <winpr/spec.h>
#include <winpr/smartcard.h>
#include <winpr/asn1.h>
#include "../log.h"
#include "ncrypt.h"
@@ -73,6 +75,24 @@ typedef struct
CK_ULONG keyIndex;
} P11EnumKeysState;
struct
{
const char* label;
BYTE tag[3];
} piv_cert_tags[] = {
{ "Certificate for PIV Authentication", "\x5F\xC1\x05" },
{ "Certificate for Digital Signature", "\x5F\xC1\x0A" },
{ "Certificate for Key Management", "\x5F\xC1\x0B" },
{ "Certificate for Card Authentication", "\x5F\xC1\x01" },
};
const BYTE APDU_PIV_SELECT_AID[] = { 0x00, 0xA4, 0x04, 0x00, 0x09, 0xA0, 0x00, 0x00,
0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00 };
const BYTE APDU_PIV_GET_CHUID[] = {
0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5C, 0x03, 0x5F, 0xC1, 0x02, 0x00
};
#define PIV_CONTAINER_NAME_LEN 36
static CK_OBJECT_CLASS object_class_public_key = CKO_PUBLIC_KEY;
static CK_BBOOL object_verify = CK_TRUE;
static CK_KEY_TYPE object_ktype_rsa = CKK_RSA;
@@ -793,6 +813,119 @@ static SECURITY_STATUS NCryptP11EnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR p
return NTE_NO_MORE_ITEMS;
}
static SECURITY_STATUS get_piv_container_name(NCryptP11KeyHandle* key, BYTE* piv_tag, BYTE* output,
size_t output_len)
{
CK_SLOT_INFO slot_info = { 0 };
CK_FUNCTION_LIST_PTR p11 = NULL;
WCHAR* reader = NULL;
SCARDCONTEXT context = 0;
SCARDHANDLE card = 0;
DWORD proto = 0;
const SCARD_IO_REQUEST* pci = NULL;
BYTE buf[258] = { 0 };
char container_name[PIV_CONTAINER_NAME_LEN + 1] = { 0 };
DWORD buf_len = 0;
SECURITY_STATUS ret = NTE_BAD_KEY;
WinPrAsn1Decoder dec = { 0 };
WinPrAsn1Decoder dec2 = { 0 };
size_t len = 0;
BYTE tag = 0;
BYTE* p = NULL;
wStream s = { 0 };
WINPR_ASSERT(key);
WINPR_ASSERT(piv_tag);
WINPR_ASSERT(key->provider);
p11 = key->provider->p11;
WINPR_ASSERT(p11);
/* Get the reader the card is in */
WINPR_ASSERT(p11->C_GetSlotInfo);
if (p11->C_GetSlotInfo(key->slotId, &slot_info) != CKR_OK)
return NTE_BAD_KEY;
fix_padded_string((char*)slot_info.slotDescription, sizeof(slot_info.slotDescription));
if (ConvertToUnicode(
CP_UTF8, 0, (char*)slot_info.slotDescription,
strnlen((char*)slot_info.slotDescription, sizeof(slot_info.slotDescription)), &reader,
0) < 0)
return NTE_NO_MEMORY;
if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &context) != SCARD_S_SUCCESS)
return NTE_BAD_KEY;
if (SCardConnectW(context, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &card, &proto) !=
SCARD_S_SUCCESS)
goto out;
pci = (proto == SCARD_PROTOCOL_T0) ? SCARD_PCI_T0 : SCARD_PCI_T1;
buf_len = sizeof(buf);
if (SCardTransmit(card, pci, APDU_PIV_SELECT_AID, sizeof(APDU_PIV_SELECT_AID), NULL, buf,
&buf_len) != SCARD_S_SUCCESS)
goto out;
if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
goto out;
buf_len = sizeof(buf);
if (SCardTransmit(card, pci, APDU_PIV_GET_CHUID, sizeof(APDU_PIV_GET_CHUID), NULL, buf,
&buf_len) != SCARD_S_SUCCESS)
goto out;
if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
goto out;
/* Find the GUID field in the CHUID data object */
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_BER, buf, buf_len);
if (!WinPrAsn1DecReadTagAndLen(&dec, &tag, &len) || tag != 0x53)
goto out;
while (WinPrAsn1DecReadTagLenValue(&dec, &tag, &len, &dec2) && tag != 0x34)
;
if (tag != 0x34 || len != 16)
goto out;
s = WinPrAsn1DecGetStream(&dec2);
p = Stream_Buffer(&s);
/* Construct the value Windows would use for a PIV key's container name */
snprintf(container_name, PIV_CONTAINER_NAME_LEN + 1,
"%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", p[3], p[2],
p[1], p[0], p[5], p[4], p[7], p[6], p[8], p[9], p[10], p[11], p[12], piv_tag[0],
piv_tag[1], piv_tag[2]);
/* And convert it to UTF-16 */
if (MultiByteToWideChar(CP_UTF8, 0, container_name, PIV_CONTAINER_NAME_LEN, (WCHAR*)output,
output_len) == PIV_CONTAINER_NAME_LEN)
ret = ERROR_SUCCESS;
out:
if (card)
SCardDisconnect(card, SCARD_LEAVE_CARD);
if (context)
SCardReleaseContext(context);
return ret;
}
static SECURITY_STATUS check_for_piv_container_name(NCryptP11KeyHandle* key, BYTE* pbOutput,
DWORD cbOutput, DWORD* pcbResult, char* label,
size_t label_len)
{
for (int i = 0; i < ARRAYSIZE(piv_cert_tags); i++)
{
if (strncmp(label, piv_cert_tags[i].label, label_len) == 0)
{
*pcbResult = PIV_CONTAINER_NAME_LEN * sizeof(WCHAR);
if (!pbOutput)
return ERROR_SUCCESS;
else if (cbOutput < PIV_CONTAINER_NAME_LEN * sizeof(WCHAR))
return NTE_NO_MEMORY;
else
return get_piv_container_name(key, piv_cert_tags[i].tag, pbOutput, cbOutput);
}
}
return NTE_NOT_FOUND;
}
static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle,
NCryptKeyGetPropertyEnum property, PBYTE pbOutput,
DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags)
@@ -820,6 +953,7 @@ static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle,
{
case NCRYPT_PROPERTY_CERTIFICATE:
case NCRYPT_PROPERTY_NAME:
break;
case NCRYPT_PROPERTY_READER:
{
@@ -909,6 +1043,46 @@ static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle,
ret = ERROR_SUCCESS;
break;
}
case NCRYPT_PROPERTY_NAME:
{
CK_ATTRIBUTE attr = { CKA_LABEL, NULL, 0 };
char* label = NULL;
WINPR_ASSERT(provider->p11->C_GetAttributeValue);
rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
if (rv == CKR_OK)
{
label = calloc(1, attr.ulValueLen);
if (!label)
{
ret = NTE_NO_MEMORY;
break;
}
attr.pValue = label;
rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
}
if (rv == CKR_OK)
{
/* Check if we have a PIV card */
ret = check_for_piv_container_name(keyHandle, pbOutput, cbOutput, pcbResult, label,
attr.ulValueLen);
/* Otherwise, at least for GIDS cards the label will be the correct value */
if (ret == NTE_NOT_FOUND)
{
*pcbResult =
MultiByteToWideChar(CP_UTF8, 0, label, attr.ulValueLen, (LPWSTR)pbOutput,
pbOutput ? cbOutput / sizeof(WCHAR) : 0) *
sizeof(WCHAR);
ret = ERROR_SUCCESS;
}
}
free(label);
break;
}
default:
ret = NTE_NOT_SUPPORTED;
break;