21 #include <winpr/error.h>
22 #include <winpr/ncrypt.h>
23 #include <winpr/string.h>
24 #include <winpr/wlog.h>
25 #include <winpr/crypto.h>
26 #include <winpr/path.h>
28 #include <freerdp/log.h>
29 #include <freerdp/freerdp.h>
30 #include <winpr/print.h>
32 #include <freerdp/utils/smartcardlogon.h>
33 #include <freerdp/crypto/crypto.h>
35 #include <openssl/obj_mac.h>
37 #define TAG FREERDP_TAG("smartcardlogon")
39 struct SmartcardKeyInfo_st
45 static void delete_file(
char* path)
52 FILE* fp = winpr_fopen(path,
"r+");
55 const char buffer[8192] = { 0 };
57 int rs = _fseeki64(fp, 0, SEEK_END);
60 (void)_fseeki64(fp, 0, SEEK_SET);
62 for (INT64 x = 0; x < size; x +=
sizeof(buffer))
64 const size_t dnmemb = (size_t)(size - x);
65 const size_t nmemb = MIN(
sizeof(buffer), dnmemb);
66 const size_t count = fwrite(buffer, nmemb, 1, fp);
75 winpr_DeleteFile(path);
79 static void smartcardKeyInfo_Free(SmartcardKeyInfo* key_info)
84 delete_file(key_info->certPath);
85 delete_file(key_info->keyPath);
97 freerdp_certificate_free(scCert->certificate);
98 free(scCert->pkinitArgs);
99 free(scCert->keyName);
100 free(scCert->containerName);
102 free(scCert->userHint);
103 free(scCert->domainHint);
104 free(scCert->subject);
105 free(scCert->issuer);
106 smartcardKeyInfo_Free(scCert->key_info);
116 for (
size_t i = 0; i < count; i++)
119 smartcardCertInfo_Free(cert);
128 size_t curCount = *count;
132 for (
size_t i = 0; i < curCount; ++i)
134 if (_wcscmp(curInfoList[i]->containerName, certInfo->containerName) == 0)
136 smartcardCertInfo_Free(certInfo);
146 WLog_ERR(TAG,
"unable to reallocate certs");
149 curInfoList = tmpInfoList;
152 curInfoList[curCount++] = certInfo;
153 *certInfoList = curInfoList;
160 WINPR_ASSERT(scCert);
162 scCert->upn = freerdp_certificate_get_upn(scCert->certificate);
165 WLog_DBG(TAG,
"%s has no UPN, trying emailAddress", scCert->keyName);
166 scCert->upn = freerdp_certificate_get_email(scCert->certificate);
172 const char* atPos = strchr(scCert->upn,
'@');
176 WLog_ERR(TAG,
"invalid UPN, for key %s (no @)", scCert->keyName);
180 userLen = (size_t)(atPos - scCert->upn);
181 scCert->userHint = malloc(userLen + 1);
182 scCert->domainHint = _strdup(atPos + 1);
184 if (!scCert->userHint || !scCert->domainHint)
186 WLog_ERR(TAG,
"error allocating userHint or domainHint, for key %s", scCert->keyName);
190 memcpy(scCert->userHint, scCert->upn, userLen);
191 scCert->userHint[userLen] = 0;
194 scCert->subject = freerdp_certificate_get_subject(scCert->certificate);
195 scCert->issuer = freerdp_certificate_get_issuer(scCert->certificate);
199 static BOOL set_info_certificate(
SmartcardCertInfo* cert, BYTE* certBytes, DWORD cbCertBytes,
200 const char* userFilter,
const char* domainFilter)
202 if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbCertBytes, cert->sha1Hash,
203 sizeof(cert->sha1Hash)))
205 WLog_ERR(TAG,
"unable to compute certificate sha1 for key %s", cert->keyName);
209 cert->certificate = freerdp_certificate_new_from_der(certBytes, cbCertBytes);
210 if (!cert->certificate)
212 WLog_ERR(TAG,
"unable to parse X509 certificate for key %s", cert->keyName);
216 if (!freerdp_certificate_check_eku(cert->certificate, NID_ms_smartcard_login))
218 WLog_DBG(TAG,
"discarding certificate without Smartcard Login EKU for key %s",
223 if (!treat_sc_cert(cert))
225 WLog_DBG(TAG,
"error treating cert");
229 if (userFilter && (!cert->upn || (strcmp(cert->upn, userFilter) != 0)))
231 if (cert->userHint && strcmp(cert->userHint, userFilter) != 0)
233 WLog_DBG(TAG,
"discarding non matching cert by user %s@%s", cert->userHint,
239 if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0)
241 WLog_DBG(TAG,
"discarding non matching cert by domain(%s) %s@%s", domainFilter,
242 cert->userHint, cert->domainHint);
250 static BOOL build_pkinit_args(NCRYPT_PROV_HANDLE provider,
SmartcardCertInfo* scCert)
255 const char* pkModule = winpr_NCryptGetModulePath(provider);
258 if (winpr_asprintf(&scCert->pkinitArgs, &size,
"PKCS11:module_name=%s:slotid=%" PRIu16,
259 pkModule, (UINT16)scCert->slotId) <= 0)
266 static BOOL list_capi_provider_keys(
const rdpSettings* settings, LPCWSTR csp, LPCWSTR scope,
267 const char* userFilter,
const char* domainFilter,
272 HCRYPTPROV hProvider = 0;
274 BYTE* certBytes = NULL;
275 CHAR* readerName = NULL;
277 if (!CryptAcquireContextW(&hProvider, scope, csp, PROV_RSA_FULL, CRYPT_SILENT))
279 WLog_DBG(TAG,
"Unable to acquire context: %d", GetLastError());
287 cert->csp = _wcsdup(csp);
293 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, NULL, &dwDataLen, 0))
295 WLog_DBG(TAG,
"Unable to get provider param: %d", GetLastError());
299 readerName = malloc(dwDataLen);
303 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, readerName, &dwDataLen, 0))
305 WLog_DBG(TAG,
"Unable to get reader name: %d", GetLastError());
309 cert->reader = ConvertUtf8ToWCharAlloc(readerName, NULL);
315 if (!CryptGetProvParam(hProvider, PP_CONTAINER, NULL, &dwDataLen, 0))
317 WLog_DBG(TAG,
"Unable to get provider param: %d", GetLastError());
321 cert->keyName = malloc(dwDataLen);
325 if (!CryptGetProvParam(hProvider, PP_CONTAINER, cert->keyName, &dwDataLen, 0))
327 WLog_DBG(TAG,
"Unable to get container name: %d", GetLastError());
331 cert->containerName = ConvertUtf8ToWCharAlloc(cert->keyName, NULL);
332 if (!cert->containerName)
336 if (!CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hKey))
338 WLog_DBG(TAG,
"Unable to get user key for %s: %d", cert->keyName, GetLastError());
343 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &dwDataLen, 0))
345 WLog_DBG(TAG,
"Unable to get key param for key %s: %d", cert->keyName, GetLastError());
349 certBytes = malloc(dwDataLen);
352 WLog_ERR(TAG,
"unable to allocate %" PRIu32
" certBytes for key %s", dwDataLen,
357 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, certBytes, &dwDataLen, 0))
359 WLog_ERR(TAG,
"unable to retrieve certificate for key %s", cert->keyName);
363 if (!set_info_certificate(cert, certBytes, dwDataLen, userFilter, domainFilter))
366 if (!add_cert_to_list(pcerts, pcount, cert))
375 CryptDestroyKey(hKey);
377 CryptReleaseContext(hProvider, 0);
379 smartcardCertInfo_Free(cert);
384 static BOOL list_provider_keys(
const rdpSettings* settings, NCRYPT_PROV_HANDLE provider,
385 LPCWSTR csp, LPCWSTR scope,
const char* userFilter,
391 PVOID enumState = NULL;
393 size_t count = *pcount;
395 while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) ==
398 NCRYPT_KEY_HANDLE phKey = 0;
399 PBYTE certBytes = NULL;
400 DWORD dwFlags = NCRYPT_SILENT_FLAG;
403 BOOL haveError = TRUE;
404 SECURITY_STATUS status = 0;
410 cert->keyName = ConvertWCharToUtf8Alloc(keyName->pszName, NULL);
414 WLog_DBG(TAG,
"opening key %s", cert->keyName);
417 NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
418 if (status != ERROR_SUCCESS)
421 "unable to NCryptOpenKey(dwLegacyKeySpec=0x%" PRIx32
" dwFlags=0x%" PRIx32
422 "), status=%s, skipping",
423 status, keyName->dwLegacyKeySpec, keyName->dwFlags,
424 winpr_NCryptSecurityStatusError(status));
428 cert->csp = _wcsdup(csp);
433 status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput,
435 if (status != ERROR_SUCCESS)
437 WLog_ERR(TAG,
"unable to retrieve slotId for key %s, status=%s", cert->keyName,
438 winpr_NCryptSecurityStatusError(status));
445 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, dwFlags);
446 if (status != ERROR_SUCCESS)
448 WLog_DBG(TAG,
"unable to retrieve reader's name length for key %s", cert->keyName);
452 cert->reader = calloc(1, cbOutput + 2);
455 WLog_ERR(TAG,
"unable to allocate reader's name for key %s", cert->keyName);
459 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2,
461 if (status != ERROR_SUCCESS)
463 WLog_ERR(TAG,
"unable to retrieve reader's name for key %s", cert->keyName);
470 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, NULL, 0, &cbOutput, dwFlags);
471 if (status == ERROR_SUCCESS)
473 cert->containerName = calloc(1, cbOutput +
sizeof(WCHAR));
474 if (!cert->containerName)
476 WLog_ERR(TAG,
"unable to allocate key container name for key %s", cert->keyName);
480 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
481 cbOutput, &cbOutput, dwFlags);
484 if (status != ERROR_SUCCESS)
486 WLog_ERR(TAG,
"unable to retrieve key container name for key %s", cert->keyName);
492 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 0, &cbOutput, dwFlags);
493 if (status != ERROR_SUCCESS)
496 WLog_DBG(TAG,
"unable to retrieve certificate property len, status=0x%lx, skipping",
501 certBytes = calloc(1, cbOutput);
504 WLog_ERR(TAG,
"unable to allocate %" PRIu32
" certBytes for key %s", cbOutput,
509 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput,
511 if (status != ERROR_SUCCESS)
513 WLog_ERR(TAG,
"unable to retrieve certificate for key %s", cert->keyName);
517 if (!set_info_certificate(cert, certBytes, cbOutput, userFilter, domainFilter))
521 if (!build_pkinit_args(provider, cert))
523 WLog_ERR(TAG,
"error build pkinit args");
531 NCryptFreeBuffer(keyName);
533 NCryptFreeObject((NCRYPT_HANDLE)phKey);
536 smartcardCertInfo_Free(cert);
539 if (!add_cert_to_list(&cert_list, &count, cert))
548 char cspa[128] = { 0 };
550 (void)ConvertWCharToUtf8(csp, cspa,
sizeof(cspa));
551 char scopea[128] = { 0 };
552 (void)ConvertWCharToUtf8(scope, scopea,
sizeof(scopea));
553 WLog_WARN(TAG,
"%s [%s] no certificates found", cspa, scopea);
557 NCryptFreeBuffer(enumState);
561 static BOOL smartcard_hw_enumerateCerts(
const rdpSettings* settings, LPCWSTR csp,
562 const char* reader,
const char* userFilter,
568 NCRYPT_PROV_HANDLE provider = 0;
569 SECURITY_STATUS status = 0;
574 WINPR_ASSERT(scCerts);
575 WINPR_ASSERT(retCount);
579 size_t readerSz = strlen(reader);
580 char* scopeStr = malloc(4 + readerSz + 1 + 1);
584 (void)_snprintf(scopeStr, readerSz + 5,
"\\\\.\\%s\\", reader);
585 scope = ConvertUtf8NToWCharAlloc(scopeStr, readerSz + 5, NULL);
595 LPCSTR paths[] = { Pkcs11Module, NULL };
600 status = winpr_NCryptOpenStorageProviderEx(&provider, csp, 0, paths);
601 if (status != ERROR_SUCCESS)
603 WLog_ERR(TAG,
"unable to open provider given by pkcs11 module");
607 status = list_provider_keys(settings, provider, csp, scope, userFilter, domainFilter,
609 NCryptFreeObject((NCRYPT_HANDLE)provider);
612 WLog_ERR(TAG,
"error listing keys from CSP loaded from %s", Pkcs11Module);
619 DWORD nproviders = 0;
623 DWORD provType, cbProvName = 0;
624 for (DWORD i = 0; CryptEnumProvidersW(i, NULL, 0, &provType, NULL, &cbProvName); ++i)
626 char providerNameStr[256] = { 0 };
627 LPWSTR szProvName = malloc(cbProvName *
sizeof(WCHAR));
628 if (!CryptEnumProvidersW(i, NULL, 0, &provType, szProvName, &cbProvName))
634 if (ConvertWCharToUtf8(szProvName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
636 _snprintf(providerNameStr,
sizeof(providerNameStr),
"<unknown>");
637 WLog_ERR(TAG,
"unable to convert provider name to char*, will show it as '%s'",
641 WLog_DBG(TAG,
"exploring CSP '%s'", providerNameStr);
642 if (provType != PROV_RSA_FULL || (csp && _wcscmp(szProvName, csp) != 0))
644 WLog_DBG(TAG,
"CSP '%s' filtered out", providerNameStr);
648 if (!list_capi_provider_keys(settings, szProvName, scope, userFilter, domainFilter,
650 WLog_INFO(TAG,
"error when retrieving keys in CSP '%s'", providerNameStr);
657 status = NCryptEnumStorageProviders(&nproviders, &names, NCRYPT_SILENT_FLAG);
658 if (status != ERROR_SUCCESS)
660 WLog_ERR(TAG,
"error listing providers");
664 for (DWORD i = 0; i < nproviders; i++)
666 char providerNameStr[256] = { 0 };
669 if (ConvertWCharToUtf8(name->pszName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
671 (void)_snprintf(providerNameStr,
sizeof(providerNameStr),
"<unknown>");
672 WLog_ERR(TAG,
"unable to convert provider name to char*, will show it as '%s'",
676 WLog_DBG(TAG,
"exploring CSP '%s'", providerNameStr);
677 if (csp && _wcscmp(name->pszName, csp) != 0)
679 WLog_DBG(TAG,
"CSP '%s' filtered out", providerNameStr);
683 status = NCryptOpenStorageProvider(&provider, name->pszName, 0);
684 if (status != ERROR_SUCCESS)
687 if (!list_provider_keys(settings, provider, name->pszName, scope, userFilter,
688 domainFilter, &cert_list, &count))
689 WLog_INFO(TAG,
"error when retrieving keys in CSP '%s'", providerNameStr);
691 NCryptFreeObject((NCRYPT_HANDLE)provider);
694 NCryptFreeBuffer(names);
697 *scCerts = cert_list;
703 smartcardCertList_Free(cert_list, count);
708 static char* create_temporary_file(
void)
714 winpr_RAND(buffer,
sizeof(buffer));
715 hex = winpr_BinToHexString(buffer,
sizeof(buffer), FALSE);
716 path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
721 static SmartcardCertInfo* smartcardCertInfo_New(
const char* privKeyPEM,
const char* certPEM)
725 WINPR_ASSERT(privKeyPEM);
726 WINPR_ASSERT(certPEM);
732 SmartcardKeyInfo* info = cert->key_info = calloc(1,
sizeof(SmartcardKeyInfo));
736 cert->certificate = freerdp_certificate_new_from_pem(certPEM);
737 if (!cert->certificate)
739 WLog_ERR(TAG,
"unable to read smartcard certificate");
743 if (!treat_sc_cert(cert))
745 WLog_ERR(TAG,
"unable to treat smartcard certificate");
749 cert->reader = ConvertUtf8ToWCharAlloc(
"FreeRDP Emulator", NULL);
753 cert->containerName = ConvertUtf8ToWCharAlloc(
"Private Key 00", NULL);
754 if (!cert->containerName)
762 info->keyPath = create_temporary_file();
763 WLog_DBG(TAG,
"writing PKINIT key to %s", info->keyPath);
764 if (!crypto_write_pem(info->keyPath, privKeyPEM, strlen(privKeyPEM)))
767 info->certPath = create_temporary_file();
768 WLog_DBG(TAG,
"writing PKINIT cert to %s", info->certPath);
769 if (!crypto_write_pem(info->certPath, certPEM, strlen(certPEM)))
772 int res = winpr_asprintf(&cert->pkinitArgs, &size,
"FILE:%s,%s", info->certPath, info->keyPath);
778 smartcardCertInfo_Free(cert);
782 static BOOL smartcard_sw_enumerateCerts(
const rdpSettings* settings,
SmartcardCertInfo*** scCerts,
788 WINPR_ASSERT(settings);
789 WINPR_ASSERT(scCerts);
790 WINPR_ASSERT(retCount);
796 WLog_ERR(TAG,
"Invalid smartcard private key PEM, aborting");
801 WLog_ERR(TAG,
"Invalid smartcard certificate PEM, aborting");
817 *scCerts = cert_list;
822 smartcardCertList_Free(cert_list, 1);
826 BOOL smartcard_enumerateCerts(
const rdpSettings* settings,
SmartcardCertInfo*** scCerts,
827 size_t* retCount, BOOL gateway)
833 const char* Username = NULL;
834 const char* Domain = NULL;
847 WINPR_ASSERT(settings);
848 WINPR_ASSERT(scCerts);
849 WINPR_ASSERT(retCount);
851 if (Domain && !strlen(Domain))
855 return smartcard_sw_enumerateCerts(settings, scCerts, retCount);
857 if (CspName && (!(csp = ConvertUtf8ToWCharAlloc(CspName, NULL))))
859 WLog_ERR(TAG,
"error while converting CSP to WCHAR");
864 smartcard_hw_enumerateCerts(settings, csp, ReaderName, Username, Domain, scCerts, retCount);
869 static BOOL set_settings_from_smartcard(rdpSettings* settings, FreeRDP_Settings_Keys_String
id,
872 WINPR_ASSERT(settings);
881 BOOL smartcard_getCert(
const rdpContext* context,
SmartcardCertInfo** cert, BOOL gateway)
883 WINPR_ASSERT(context);
885 const freerdp* instance = context->instance;
886 rdpSettings* settings = context->settings;
890 WINPR_ASSERT(instance);
891 WINPR_ASSERT(settings);
893 if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
898 WLog_ERR(TAG,
"no suitable smartcard certificates were found");
902 if (count > UINT32_MAX)
904 WLog_ERR(TAG,
"smartcard certificate count %" PRIuz
" exceeds UINT32_MAX", count);
912 if (!instance->ChooseSmartcard ||
913 !instance->ChooseSmartcard(context->instance, cert_list, (UINT32)count, &index,
916 WLog_ERR(TAG,
"more than one suitable smartcard certificate was found");
917 smartcardCertList_Free(cert_list, count);
920 *cert = cert_list[index];
922 for (DWORD i = 0; i < index; i++)
923 smartcardCertInfo_Free(cert_list[i]);
924 for (DWORD i = index + 1; i < count; i++)
925 smartcardCertInfo_Free(cert_list[i]);
928 *cert = cert_list[0];
930 FreeRDP_Settings_Keys_String username_setting =
931 gateway ? FreeRDP_GatewayUsername : FreeRDP_Username;
932 FreeRDP_Settings_Keys_String domain_setting = gateway ? FreeRDP_GatewayDomain : FreeRDP_Domain;
936 if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
937 !set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
939 WLog_ERR(TAG,
"unable to set settings from smartcard!");
940 smartcardCertInfo_Free(*cert);
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
a provider name descriptor