From 9d0beaccaee0ce526b8e25a767a2fe7fb3d47e7f Mon Sep 17 00:00:00 2001 From: fifthdegree Date: Fri, 14 Oct 2022 12:58:30 -0400 Subject: [PATCH] smartcardlogon: choose a single smartcard to use Require a single smartcard certificate to be chosen and define a callback to choose when more than one is available. --- client/Wayland/wlfreerdp.c | 1 + client/X11/xf_client.c | 1 + client/common/client.c | 42 ++++ client/common/smartcard_cli.c | 6 +- include/freerdp/client.h | 3 + include/freerdp/freerdp.h | 9 +- include/freerdp/utils/smartcardlogon.h | 12 +- libfreerdp/core/nla.c | 60 ++---- libfreerdp/core/smartcardlogon.c | 285 +++++++++++++------------ 9 files changed, 239 insertions(+), 180 deletions(-) diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index 3042a0acb..762cbb135 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -623,6 +623,7 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context) instance->PostConnect = wl_post_connect; instance->PostDisconnect = wl_post_disconnect; instance->AuthenticateEx = client_cli_authenticate_ex; + instance->ChooseSmartcard = client_cli_choose_smartcard; instance->VerifyCertificateEx = client_cli_verify_certificate_ex; instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex; instance->PresentGatewayMessage = client_cli_present_gateway_message; diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index b18d22ee2..9746853e2 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -1754,6 +1754,7 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context) instance->PostConnect = xf_post_connect; instance->PostDisconnect = xf_post_disconnect; instance->AuthenticateEx = client_cli_authenticate_ex; + instance->ChooseSmartcard = client_cli_choose_smartcard; instance->VerifyCertificateEx = client_cli_verify_certificate_ex; instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex; instance->PresentGatewayMessage = client_cli_present_gateway_message; diff --git a/client/common/client.c b/client/common/client.c index f018095bf..b9276e6a1 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -30,6 +30,7 @@ #include #include #include +#include #if defined(CHANNEL_AINPUT_CLIENT) #include @@ -517,6 +518,47 @@ BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** passw return client_cli_authenticate_raw(instance, reason, username, password, domain); } +BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice) +{ + unsigned long answer; + char* p = NULL; + + printf("Multiple smartcards are available for use:\n"); + for (DWORD i = 0; i < count; i++) + { + const SmartcardCertInfo* cert = cert_list[i]; + char* reader = NULL; + + ConvertFromUnicode(CP_UTF8, 0, cert->reader, -1, &reader, 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, + cert->issuer, cert->upn); + + free(reader); + } + + while (1) + { + char input[10] = { 0 }; + + printf("\nChoose a smartcard to use (0 - %" PRIu32 "): ", count - 1); + fflush(stdout); + if (!fgets(input, 10, stdin)) + { + WLog_ERR(TAG, "could not read from stdin"); + return FALSE; + } + + answer = strtoul(input, &p, 10); + if ((*p == '\n' && p != input) && answer < count) + { + *choice = answer; + return TRUE; + } + } +} + #if defined(WITH_FREERDP_DEPRECATED) BOOL client_cli_authenticate(freerdp* instance, char** username, char** password, char** domain) { diff --git a/client/common/smartcard_cli.c b/client/common/smartcard_cli.c index f38abeb25..3a55e6b0d 100644 --- a/client/common/smartcard_cli.c +++ b/client/common/smartcard_cli.c @@ -24,7 +24,7 @@ BOOL freerdp_smartcard_list(const rdpSettings* settings) { - SmartcardCerts* certs = NULL; + SmartcardCertInfo** certs = NULL; DWORD i, count; if (!smartcard_enumerateCerts(settings, &certs, &count)) @@ -32,7 +32,7 @@ BOOL freerdp_smartcard_list(const rdpSettings* settings) for (i = 0; i < count; i++) { - const SmartcardCertInfo* info = smartcard_getCertInfo(certs, i); + const SmartcardCertInfo* info = certs[i]; char asciiStr[256] = { 0 }; WINPR_ASSERT(info); @@ -54,7 +54,7 @@ BOOL freerdp_smartcard_list(const rdpSettings* settings) if (info->upn) printf("\t* UPN: %s\n", info->upn); } - smartcardCerts_Free(&certs); + smartcardCertList_Free(certs, count); return TRUE; } diff --git a/include/freerdp/client.h b/include/freerdp/client.h index e6b1c17c7..c54fcdc96 100644 --- a/include/freerdp/client.h +++ b/include/freerdp/client.h @@ -138,6 +138,9 @@ extern "C" FREERDP_API BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** password, char** domain, rdp_auth_reason reason); + FREERDP_API BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, + DWORD* choice); + FREERDP_API void freerdp_client_OnChannelConnectedEventHandler(void* context, const ChannelConnectedEventArgs* e); diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index f6cef7742..5d9954343 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -58,6 +58,7 @@ typedef RDP_CLIENT_ENTRY_POINTS_V1 RDP_CLIENT_ENTRY_POINTS; #include typedef struct stream_dump_context rdpStreamDumpContext; +typedef struct SmartcardCertInfo_st SmartcardCertInfo; #ifdef __cplusplus extern "C" @@ -117,6 +118,7 @@ extern "C" char** domain); typedef BOOL (*pAuthenticateEx)(freerdp* instance, char** username, char** password, char** domain, rdp_auth_reason reason); + typedef BOOL (*pChooseSmartcard)(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice); /** @brief Callback used if user interaction is required to accept * an unknown certificate. @@ -493,7 +495,12 @@ owned by rdpRdp */ Callback for authentication. It is used to get the username/password. The reason argument tells why it was called. */ - UINT64 paddingE[80 - 70]; /* 70 */ + ALIGN64 pChooseSmartcard + ChooseSmartcard; /* (offset 70) + Callback for choosing a smartcard for logon. + Used when multiple smartcards are available. Returns an index into a list + of SmartcardCertInfo pointers */ + UINT64 paddingE[80 - 71]; /* 71 */ }; struct rdp_channel_handles diff --git a/include/freerdp/utils/smartcardlogon.h b/include/freerdp/utils/smartcardlogon.h index 67da657aa..eaa76a26f 100644 --- a/include/freerdp/utils/smartcardlogon.h +++ b/include/freerdp/utils/smartcardlogon.h @@ -22,9 +22,9 @@ #include #include -typedef struct sSmartCardCerts SmartcardCerts; +typedef struct SmartcardKeyInfo_st SmartcardKeyInfo; -typedef struct +typedef struct SmartcardCertInfo_st { LPWSTR csp; LPWSTR reader; @@ -38,11 +38,13 @@ typedef struct char* subject; char* issuer; BYTE sha1Hash[20]; + SmartcardKeyInfo* key_info; } SmartcardCertInfo; -FREERDP_API BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCerts** scCert, +FREERDP_API BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts, DWORD* retCount); -FREERDP_API const SmartcardCertInfo* smartcard_getCertInfo(SmartcardCerts* scCerts, DWORD index); -FREERDP_API void smartcardCerts_Free(SmartcardCerts** pscCert); +FREERDP_API BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert); +FREERDP_API void smartcardCertInfo_Free(SmartcardCertInfo* pscCert); +FREERDP_API void smartcardCertList_Free(SmartcardCertInfo** pscCert, DWORD count); #endif /* LIBFREERDP_CORE_SMARTCARDLOGON_H */ diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index ca89f78c8..e95d23247 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -124,8 +124,7 @@ struct rdp_nla rdpCredsspAuth* auth; char* pkinitArgs; - SmartcardCerts* smartcardCerts; - DWORD nsmartcardCerts; + SmartcardCertInfo* smartcardCert; BYTE certSha1[20]; }; @@ -187,7 +186,6 @@ static const UINT32 NonceLength = 32; static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla) { - const SmartcardCertInfo* info = NULL; rdpSettings* settings; BOOL ret = FALSE; @@ -200,35 +198,18 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla) if (!settings->SmartcardLogon) return TRUE; - smartcardCerts_Free(&nla->smartcardCerts); + smartcardCertInfo_Free(nla->smartcardCert); - if (!smartcard_enumerateCerts(settings, &nla->smartcardCerts, &nla->nsmartcardCerts)) + if (!smartcard_getCert(nla->rdpcontext, &nla->smartcardCert)) { - WLog_ERR(TAG, "unable to list smartcard certificates"); + WLog_ERR(TAG, "unable to get smartcard certificate for logon"); return FALSE; } - if (nla->nsmartcardCerts < 1) - { - WLog_ERR(TAG, "no smartcard certificates found"); - goto out; - } - - if (nla->nsmartcardCerts != 1) - goto setup_pin; - - info = smartcard_getCertInfo(nla->smartcardCerts, 0); - if (!info) - goto out; - - /* - * just one result let's try to fill missing parameters - */ - if (!settings->CspName) { - if (info->csp && - ConvertFromUnicode(CP_UTF8, 0, info->csp, -1, &settings->CspName, 0, NULL, FALSE) <= 0) + if (nla->smartcardCert->csp && ConvertFromUnicode(CP_UTF8, 0, nla->smartcardCert->csp, -1, + &settings->CspName, 0, NULL, FALSE) <= 0) { WLog_ERR(TAG, "unable to set CSP name"); goto out; @@ -240,48 +221,49 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla) } } - if (!settings->Username && info->userHint) + if (!settings->Username && nla->smartcardCert->userHint) { - if (!freerdp_settings_set_string(settings, FreeRDP_Username, info->userHint)) + if (!freerdp_settings_set_string(settings, FreeRDP_Username, nla->smartcardCert->userHint)) { WLog_ERR(TAG, "unable to copy certificate username"); goto out; } } - if (!settings->Domain && info->domainHint) + if (!settings->Domain && nla->smartcardCert->domainHint) { - if (!freerdp_settings_set_string(settings, FreeRDP_Domain, info->domainHint)) + if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nla->smartcardCert->domainHint)) { WLog_ERR(TAG, "unable to copy certificate domain"); goto out; } } - if (!settings->ReaderName && info->reader) + if (!settings->ReaderName && nla->smartcardCert->reader) { - if (ConvertFromUnicode(CP_UTF8, 0, info->reader, -1, &settings->ReaderName, 0, NULL, NULL) < - 0) + if (ConvertFromUnicode(CP_UTF8, 0, nla->smartcardCert->reader, -1, &settings->ReaderName, 0, + NULL, NULL) < 0) { WLog_ERR(TAG, "unable to copy reader name"); goto out; } } - if (!settings->ContainerName && info->containerName) + if (!settings->ContainerName && nla->smartcardCert->containerName) { - if (!freerdp_settings_set_string(settings, FreeRDP_ContainerName, info->containerName)) + if (!freerdp_settings_set_string(settings, FreeRDP_ContainerName, + nla->smartcardCert->containerName)) { WLog_ERR(TAG, "unable to copy container name"); goto out; } } - memcpy(nla->certSha1, info->sha1Hash, sizeof(nla->certSha1)); + memcpy(nla->certSha1, nla->smartcardCert->sha1Hash, sizeof(nla->certSha1)); - if (info->pkinitArgs) + if (nla->smartcardCert->pkinitArgs) { - nla->pkinitArgs = _strdup(info->pkinitArgs); + nla->pkinitArgs = _strdup(nla->smartcardCert->pkinitArgs); if (!nla->pkinitArgs) { WLog_ERR(TAG, "unable to copy pkinitArgs"); @@ -289,8 +271,6 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla) } } -setup_pin: - ret = TRUE; out: return ret; @@ -1699,7 +1679,7 @@ void nla_free(rdpNla* nla) if (!nla) return; - smartcardCerts_Free(&nla->smartcardCerts); + smartcardCertInfo_Free(nla->smartcardCert); sspi_SecBufferFree(&nla->pubKeyAuth); sspi_SecBufferFree(&nla->authInfo); sspi_SecBufferFree(&nla->negoToken); diff --git a/libfreerdp/core/smartcardlogon.c b/libfreerdp/core/smartcardlogon.c index d7eb99efc..f1a497c3a 100644 --- a/libfreerdp/core/smartcardlogon.c +++ b/libfreerdp/core/smartcardlogon.c @@ -36,39 +36,12 @@ #define TAG FREERDP_TAG("smartcardlogon") -typedef struct +struct SmartcardKeyInfo_st { - SmartcardCertInfo info; char* certPath; char* keyPath; -} SmartcardCertInfoPrivate; - -struct sSmartCardCerts -{ - size_t count; - SmartcardCertInfoPrivate* certs; }; -static void smartcardCertInfo_Free(SmartcardCertInfo* scCert) -{ - const SmartcardCertInfo empty = { 0 }; - - if (!scCert) - return; - free(scCert->csp); - free(scCert->reader); - crypto_cert_free(scCert->certificate); - free(scCert->pkinitArgs); - free(scCert->containerName); - free(scCert->upn); - free(scCert->userHint); - free(scCert->domainHint); - free(scCert->subject); - free(scCert->issuer); - - *scCert = empty; -} - static void delete_file(char* path) { WCHAR* wpath = NULL; @@ -97,33 +70,46 @@ static void delete_file(char* path) free(path); } -static void smartcardCertInfoPrivate_Free(SmartcardCertInfoPrivate* scCert) +static void smartcardKeyInfo_Free(SmartcardKeyInfo* key_info) { - const SmartcardCertInfoPrivate empty = { 0 }; - - if (!scCert) + if (!key_info) return; - smartcardCertInfo_Free(&scCert->info); - delete_file(scCert->keyPath); - delete_file(scCert->certPath); - *scCert = empty; + + delete_file(key_info->certPath); + delete_file(key_info->keyPath); + + free(key_info); } -void smartcardCerts_Free(SmartcardCerts** pscCert) +void smartcardCertInfo_Free(SmartcardCertInfo* scCert) { - size_t x; - SmartcardCerts* scCert; - - WINPR_ASSERT(pscCert); - scCert = *pscCert; if (!scCert) return; - for (x = 0; x < scCert->count; x++) - smartcardCertInfoPrivate_Free(&scCert->certs[x]); + free(scCert->csp); + free(scCert->reader); + crypto_cert_free(scCert->certificate); + free(scCert->pkinitArgs); + free(scCert->containerName); + free(scCert->upn); + free(scCert->userHint); + free(scCert->domainHint); + free(scCert->subject); + free(scCert->issuer); + smartcardKeyInfo_Free(scCert->key_info); free(scCert); - *pscCert = NULL; +} + +void smartcardCertList_Free(SmartcardCertInfo** cert_list, DWORD count) +{ + if (!cert_list) + return; + + for (DWORD i = 0; i < count; i++) + smartcardCertInfo_Free(cert_list[i]); + + free(cert_list); } static BOOL treat_sc_cert(SmartcardCertInfo* scCert) @@ -207,12 +193,13 @@ static BOOL build_pkinit_args(const rdpSettings* settings, SmartcardCertInfo* sc static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE provider, LPCWSTR csp, LPCWSTR scope, const char* userFilter, - const char* domainFilter, SmartcardCerts** pcerts, size_t* pcount) + const char* domainFilter, SmartcardCertInfo*** pcerts, + size_t* pcount) { BOOL ret = FALSE; NCryptKeyName* keyName = NULL; PVOID enumState = NULL; - SmartcardCerts* certs = *pcerts; + SmartcardCertInfo** cert_list = *pcerts; size_t count = *pcount; while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) == @@ -222,32 +209,19 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p PBYTE certBytes = NULL; DWORD dwFlags = NCRYPT_SILENT_FLAG; DWORD cbOutput; - SmartcardCertInfoPrivate* cert; + SmartcardCertInfo* cert = NULL; BOOL haveError = TRUE; SECURITY_STATUS status; - count++; - { - SmartcardCerts* tmp = - realloc(certs, sizeof(SmartcardCerts) + (sizeof(SmartcardCertInfoPrivate) * count)); - if (!tmp) - { - WLog_ERR(TAG, "unable to reallocate certs"); - goto out; - } - certs = tmp; - certs->count = count; - certs->certs = (SmartcardCertInfoPrivate*)(certs + 1); - } + cert = calloc(1, sizeof(SmartcardCertInfo)); + if (!cert) + goto out; - cert = &certs->certs[count - 1]; - ZeroMemory(cert, sizeof(*cert)); - - if (ConvertFromUnicode(CP_UTF8, 0, keyName->pszName, -1, &cert->info.containerName, 0, NULL, + if (ConvertFromUnicode(CP_UTF8, 0, keyName->pszName, -1, &cert->containerName, 0, NULL, NULL) <= 0) goto endofloop; - WLog_DBG(TAG, "opening key %s", cert->info.containerName); + WLog_DBG(TAG, "opening key %s", cert->containerName); status = NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags); @@ -261,17 +235,17 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p goto endofloop; } - cert->info.csp = _wcsdup(csp); - if (!cert->info.csp) + cert->csp = _wcsdup(csp); + if (!cert->csp) goto endofloop; #ifndef _WIN32 - status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->info.slotId, 4, - &cbOutput, dwFlags); + status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput, + dwFlags); if (status != ERROR_SUCCESS) { - WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", - cert->info.containerName, winpr_NCryptSecurityStatusError(status)); + WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->containerName, + winpr_NCryptSecurityStatusError(status)); goto endofloop; } #endif /* _WIN32 */ @@ -282,22 +256,22 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p if (status != ERROR_SUCCESS) { WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", - cert->info.containerName); + cert->containerName); goto endofloop; } - cert->info.reader = calloc(1, cbOutput + 2); - if (!cert->info.reader) + cert->reader = calloc(1, cbOutput + 2); + if (!cert->reader) { - WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->info.containerName); + WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->containerName); goto endofloop; } - status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->info.reader, - cbOutput + 2, &cbOutput, dwFlags); + status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2, + &cbOutput, dwFlags); if (status != ERROR_SUCCESS) { - WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->info.containerName); + WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->containerName); goto endofloop; } @@ -316,7 +290,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->info.containerName); + cert->containerName); goto endofloop; } @@ -324,49 +298,47 @@ 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->info.containerName); + WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->containerName); goto endofloop; } - if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbOutput, cert->info.sha1Hash, - sizeof(cert->info.sha1Hash))) + 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->info.containerName); + WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->containerName); goto endofloop; } - cert->info.certificate = crypto_cert_read(certBytes, cbOutput); + cert->certificate = crypto_cert_read(certBytes, cbOutput); - if (!cert->info.certificate) + if (!cert->certificate) { - WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->info.containerName); + WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->containerName); goto endofloop; } - if (!treat_sc_cert(&cert->info)) + if (!treat_sc_cert(cert)) { WLog_DBG(TAG, "error treating cert"); goto endofloop; } - if (userFilter && cert->info.userHint && strcmp(cert->info.userHint, userFilter) != 0) + if (userFilter && cert->userHint && strcmp(cert->userHint, userFilter) != 0) { - WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->info.userHint, - cert->info.domainHint); + WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint, + cert->domainHint); goto endofloop; } - if (domainFilter && cert->info.domainHint && - strcmp(cert->info.domainHint, domainFilter) != 0) + if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0) { WLog_DBG(TAG, "discarding non matching cert by domain(%s) %s@%s", domainFilter, - cert->info.userHint, cert->info.domainHint); + cert->userHint, cert->domainHint); goto endofloop; } #ifndef _WIN32 - if (!build_pkinit_args(settings, &cert->info)) + if (!build_pkinit_args(settings, cert)) { WLog_ERR(TAG, "error build pkinit args"); goto endofloop; @@ -381,23 +353,33 @@ static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE p NCryptFreeObject((NCRYPT_HANDLE)phKey); if (haveError) + smartcardCertInfo_Free(cert); + else { - smartcardCertInfoPrivate_Free(cert); - count--; + SmartcardCertInfo** tmp; + + tmp = realloc(cert_list, sizeof(SmartcardCertInfo*) * (count + 1)); + if (!tmp) + { + WLog_ERR(TAG, "unable to reallocate certs"); + goto out; + } + cert_list = tmp; + cert_list[count++] = cert; } } ret = TRUE; out: *pcount = count; - *pcerts = certs; + *pcerts = cert_list; NCryptFreeBuffer(enumState); return ret; } static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp, const char* reader, const char* userFilter, - const char* domainFilter, SmartcardCerts** scCerts, + const char* domainFilter, SmartcardCertInfo*** scCerts, DWORD* retCount) { BOOL ret = FALSE; @@ -405,7 +387,7 @@ static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp NCRYPT_PROV_HANDLE provider; SECURITY_STATUS status; size_t count = 0; - SmartcardCerts* certs = NULL; + SmartcardCertInfo** cert_list = NULL; const char* Pkcs11Module = freerdp_settings_get_string(settings, FreeRDP_Pkcs11Module); WINPR_ASSERT(scCerts); @@ -440,7 +422,7 @@ static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp } status = list_provider_keys(settings, provider, csp, scope, userFilter, domainFilter, - &certs, &count); + &cert_list, &count); NCryptFreeObject((NCRYPT_HANDLE)provider); if (status != ERROR_SUCCESS) { @@ -484,7 +466,7 @@ static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp continue; if (!list_provider_keys(settings, provider, names[i].pszName, scope, userFilter, - domainFilter, &certs, &count)) + domainFilter, &cert_list, &count)) WLog_INFO(TAG, "error when retrieving keys in CSP '%s'", providerNameStr); NCryptFreeObject((NCRYPT_HANDLE)provider); @@ -493,13 +475,13 @@ static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp NCryptFreeBuffer(names); } - *scCerts = certs; + *scCerts = cert_list; *retCount = (DWORD)count; ret = TRUE; out: if (!ret) - smartcardCerts_Free(&certs); + smartcardCertList_Free(cert_list, count); free(scope); return ret; } @@ -528,14 +510,13 @@ static char* create_temporary_file(void) return path; } -static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCerts** scCerts, +static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts, DWORD* retCount) { BOOL rc = FALSE; int res; - SmartcardCerts* certs = NULL; - SmartcardCertInfoPrivate* cert; - const size_t count = 1; + SmartcardCertInfo** cert_list = NULL; + SmartcardCertInfo* cert = NULL; char* keyPath = create_temporary_file(); char* certPath = create_temporary_file(); @@ -543,32 +524,38 @@ static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCe WINPR_ASSERT(scCerts); WINPR_ASSERT(retCount); - certs = calloc(count, sizeof(SmartcardCertInfoPrivate) + sizeof(SmartcardCerts)); - if (!certs) + cert_list = calloc(1, sizeof(SmartcardCertInfo*)); + if (!cert_list) goto out_error; - certs->count = count; - cert = certs->certs = (SmartcardCertInfoPrivate*)(certs + 1); + *cert_list = calloc(1, sizeof(SmartcardCertInfo)); + if (!(*cert_list)) + goto out_error; + cert = *cert_list; - cert->info.certificate = + cert->key_info = calloc(1, sizeof(SmartcardKeyInfo)); + if (!cert->key_info) + goto out_error; + + cert->certificate = crypto_cert_pem_read(freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate)); - if (!cert->info.certificate) + if (!cert->certificate) { WLog_ERR(TAG, "unable to read smartcard certificate"); goto out_error; } - if (!treat_sc_cert(&cert->info)) + if (!treat_sc_cert(cert)) { WLog_ERR(TAG, "unable to treat smartcard certificate"); goto out_error; } - if (ConvertToUnicode(CP_UTF8, 0, "FreeRDP Emulator", -1, &cert->info.reader, 0) < 0) + if (ConvertToUnicode(CP_UTF8, 0, "FreeRDP Emulator", -1, &cert->reader, 0) < 0) goto out_error; - cert->info.containerName = _strdup("Private Key 00"); - if (!cert->info.containerName) + cert->containerName = _strdup("Private Key 00"); + if (!cert->containerName) goto out_error; /* compute PKINIT args FILE:, @@ -581,24 +568,24 @@ static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCe goto out_error; if (!write_pem(certPath, freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate))) goto out_error; - res = allocating_sprintf(&cert->info.pkinitArgs, "FILE:%s,%s", certPath, keyPath); + res = allocating_sprintf(&cert->pkinitArgs, "FILE:%s,%s", certPath, keyPath); if (res <= 0) goto out_error; - cert->certPath = certPath; - cert->keyPath = keyPath; + cert->key_info->certPath = certPath; + cert->key_info->keyPath = keyPath; rc = TRUE; - *scCerts = certs; - *retCount = (DWORD)certs->count; + *scCerts = cert_list; + *retCount = 1; out_error: if (!rc) - smartcardCerts_Free(&certs); + smartcardCertList_Free(cert_list, 1); return rc; } -BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCerts** scCerts, +BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts, DWORD* retCount) { BOOL ret; @@ -630,11 +617,47 @@ BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCerts** scCe return ret; } -const SmartcardCertInfo* smartcard_getCertInfo(SmartcardCerts* scCerts, DWORD index) +BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert) { - WINPR_ASSERT(scCerts); - if (index >= scCerts->count) - return NULL; + WINPR_ASSERT(context); - return &scCerts->certs[index].info; + const freerdp* instance = context->instance; + const rdpSettings* settings = context->settings; + SmartcardCertInfo** cert_list; + DWORD count; + + WINPR_ASSERT(instance); + WINPR_ASSERT(settings); + + if (!smartcard_enumerateCerts(settings, &cert_list, &count)) + return FALSE; + + if (count < 1) + { + WLog_ERR(TAG, "no suitable smartcard certificates were found"); + return FALSE; + } + + if (count > 1) + { + DWORD index; + + if (!instance->ChooseSmartcard || !instance->ChooseSmartcard(cert_list, count, &index)) + { + WLog_ERR(TAG, "more than one suitable smartcard certificate was found"); + smartcardCertList_Free(cert_list, count); + return FALSE; + } + *cert = cert_list[index]; + + for (DWORD i = 0; i < index; i++) + smartcardCertInfo_Free(cert_list[i]); + for (DWORD i = index + 1; i < count; i++) + smartcardCertInfo_Free(cert_list[i]); + } + else + *cert = cert_list[0]; + + free(cert_list); + return TRUE; }