FreeRDP
Loading...
Searching...
No Matches
smartcardlogon.c
1
19#include <string.h>
20
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>
27
28#include <freerdp/log.h>
29#include <freerdp/freerdp.h>
30#include <winpr/print.h>
31
32#include <freerdp/utils/smartcardlogon.h>
33#include <freerdp/crypto/crypto.h>
34#include <freerdp/utils/helpers.h>
35
36#include <openssl/obj_mac.h>
37
38#define TAG FREERDP_TAG("smartcardlogon")
39
40struct SmartcardKeyInfo_st
41{
42 char* certPath;
43 char* keyPath;
44};
45
46static void delete_file(char* path)
47{
48 if (!path)
49 return;
50
51 /* Overwrite data in files before deletion */
52 {
53 FILE* fp = winpr_fopen(path, "r+");
54 if (fp)
55 {
56 const char buffer[8192] = WINPR_C_ARRAY_INIT;
57 INT64 size = 0;
58 int rs = _fseeki64(fp, 0, SEEK_END);
59 if (rs == 0)
60 size = _ftelli64(fp);
61 (void)_fseeki64(fp, 0, SEEK_SET);
62
63 for (INT64 x = 0; x < size; x += sizeof(buffer))
64 {
65 const size_t dnmemb = (size_t)(size - x);
66 const size_t nmemb = MIN(sizeof(buffer), dnmemb);
67 const size_t count = fwrite(buffer, nmemb, 1, fp);
68 if (count != 1)
69 break;
70 }
71
72 (void)fclose(fp);
73 }
74 }
75
76 winpr_DeleteFile(path);
77 free(path);
78}
79
80static void smartcardKeyInfo_Free(SmartcardKeyInfo* key_info)
81{
82 if (!key_info)
83 return;
84
85 delete_file(key_info->certPath);
86 delete_file(key_info->keyPath);
87
88 free(key_info);
89}
90
91void smartcardCertInfo_Free(SmartcardCertInfo* scCert)
92{
93 if (!scCert)
94 return;
95
96 free(scCert->csp);
97 free(scCert->reader);
98 freerdp_certificate_free(scCert->certificate);
99 free(scCert->pkinitArgs);
100 free(scCert->keyName);
101 free(scCert->containerName);
102 free(scCert->upn);
103 free(scCert->userHint);
104 free(scCert->domainHint);
105 free(scCert->subject);
106 free(scCert->issuer);
107 smartcardKeyInfo_Free(scCert->key_info);
108
109 free(scCert);
110}
111
112void smartcardCertList_Free(SmartcardCertInfo** pscCert, size_t count)
113{
114 if (!pscCert)
115 return;
116
117 for (size_t i = 0; i < count; i++)
118 {
119 SmartcardCertInfo* cert = pscCert[i];
120 smartcardCertInfo_Free(cert);
121 }
122
123 free((void*)pscCert);
124}
125
126static BOOL add_cert_to_list(SmartcardCertInfo*** certInfoList, size_t* count,
127 SmartcardCertInfo* certInfo)
128{
129 size_t curCount = *count;
130 SmartcardCertInfo** curInfoList = *certInfoList;
131
132 /* Check if the certificate is already in the list */
133 for (size_t i = 0; i < curCount; ++i)
134 {
135 if (_wcscmp(curInfoList[i]->containerName, certInfo->containerName) == 0)
136 {
137 smartcardCertInfo_Free(certInfo);
138 return TRUE;
139 }
140 }
141
142 {
143 SmartcardCertInfo** tmpInfoList = (SmartcardCertInfo**)realloc(
144 (void*)curInfoList, sizeof(SmartcardCertInfo*) * (curCount + 1));
145 if (!tmpInfoList)
146 {
147 WLog_ERR(TAG, "unable to reallocate certs");
148 return FALSE;
149 }
150 curInfoList = tmpInfoList;
151 }
152
153 curInfoList[curCount++] = certInfo;
154 *certInfoList = curInfoList;
155 *count = curCount;
156 return TRUE;
157}
158
159static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
160{
161 WINPR_ASSERT(scCert);
162
163 scCert->upn = freerdp_certificate_get_upn(scCert->certificate);
164 if (!scCert->upn)
165 {
166 WLog_DBG(TAG, "%s has no UPN, trying emailAddress", scCert->keyName);
167 scCert->upn = freerdp_certificate_get_email(scCert->certificate);
168 }
169
170 if (scCert->upn)
171 {
172 size_t userLen = 0;
173 const char* atPos = strchr(scCert->upn, '@');
174
175 if (!atPos)
176 {
177 WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->keyName);
178 return FALSE;
179 }
180
181 userLen = (size_t)(atPos - scCert->upn);
182 scCert->userHint = malloc(userLen + 1);
183 scCert->domainHint = _strdup(atPos + 1);
184
185 if (!scCert->userHint || !scCert->domainHint)
186 {
187 WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s", scCert->keyName);
188 return FALSE;
189 }
190
191 memcpy(scCert->userHint, scCert->upn, userLen);
192 scCert->userHint[userLen] = 0;
193 }
194
195 scCert->subject = freerdp_certificate_get_subject(scCert->certificate);
196 scCert->issuer = freerdp_certificate_get_issuer(scCert->certificate);
197 return TRUE;
198}
199
200static BOOL set_info_certificate(SmartcardCertInfo* cert, BYTE* certBytes, DWORD cbCertBytes,
201 const char* userFilter, const char* domainFilter)
202{
203 if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbCertBytes, cert->sha1Hash,
204 sizeof(cert->sha1Hash)))
205 {
206 WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->keyName);
207 return FALSE;
208 }
209
210 cert->certificate = freerdp_certificate_new_from_der(certBytes, cbCertBytes);
211 if (!cert->certificate)
212 {
213 WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->keyName);
214 return FALSE;
215 }
216
217 if (!freerdp_certificate_check_eku(cert->certificate, NID_ms_smartcard_login))
218 {
219 WLog_DBG(TAG, "discarding certificate without Smartcard Login EKU for key %s",
220 cert->keyName);
221 return FALSE;
222 }
223
224 if (!treat_sc_cert(cert))
225 {
226 WLog_DBG(TAG, "error treating cert");
227 return FALSE;
228 }
229
230 if (userFilter && (!cert->upn || (strcmp(cert->upn, userFilter) != 0)))
231 {
232 if (cert->userHint && strcmp(cert->userHint, userFilter) != 0)
233 {
234 WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint,
235 cert->domainHint);
236 return FALSE;
237 }
238 }
239
240 if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0)
241 {
242 WLog_DBG(TAG, "discarding non matching cert by domain(%s) %s@%s", domainFilter,
243 cert->userHint, cert->domainHint);
244 return FALSE;
245 }
246
247 return TRUE;
248}
249
250#ifndef _WIN32
251static BOOL build_pkinit_args(NCRYPT_PROV_HANDLE provider, SmartcardCertInfo* scCert)
252{
253 /* pkinit args only under windows
254 * PKCS11:module_name=opensc-pkcs11.so
255 */
256 const char* pkModule = winpr_NCryptGetModulePath(provider);
257 size_t size = 0;
258
259 return (winpr_asprintf(&scCert->pkinitArgs, &size, "PKCS11:module_name=%s:slotid=%" PRIu16,
260 pkModule, (UINT16)scCert->slotId) > 0);
261}
262#endif /* _WIN32 */
263
264#ifdef _WIN32
265static BOOL list_capi_provider_keys(const rdpSettings* settings, LPCWSTR csp, LPCWSTR scope,
266 const char* userFilter, const char* domainFilter,
267 SmartcardCertInfo*** pcerts, size_t* pcount)
268{
269 BOOL ret = FALSE;
270 HCRYPTKEY hKey = 0;
271 HCRYPTPROV hProvider = 0;
272 SmartcardCertInfo* cert = nullptr;
273 BYTE* certBytes = nullptr;
274 CHAR* readerName = nullptr;
275
276 if (!CryptAcquireContextW(&hProvider, scope, csp, PROV_RSA_FULL, CRYPT_SILENT))
277 {
278 WLog_DBG(TAG, "Unable to acquire context: %d", GetLastError());
279 goto out;
280 }
281
282 cert = calloc(1, sizeof(SmartcardCertInfo));
283 if (!cert)
284 goto out;
285
286 cert->csp = _wcsdup(csp);
287 if (!cert->csp)
288 goto out;
289
290 /* ====== retrieve key's reader ====== */
291 DWORD dwDataLen = 0;
292 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, nullptr, &dwDataLen, 0))
293 {
294 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
295 goto out;
296 }
297
298 readerName = malloc(dwDataLen);
299 if (!readerName)
300 goto out;
301
302 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, readerName, &dwDataLen, 0))
303 {
304 WLog_DBG(TAG, "Unable to get reader name: %d", GetLastError());
305 goto out;
306 }
307
308 cert->reader = ConvertUtf8ToWCharAlloc(readerName, nullptr);
309 if (!cert->reader)
310 goto out;
311
312 /* ====== retrieve key container name ====== */
313 dwDataLen = 0;
314 if (!CryptGetProvParam(hProvider, PP_CONTAINER, nullptr, &dwDataLen, 0))
315 {
316 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
317 goto out;
318 }
319
320 cert->keyName = malloc(dwDataLen);
321 if (!cert->keyName)
322 goto out;
323
324 if (!CryptGetProvParam(hProvider, PP_CONTAINER, cert->keyName, &dwDataLen, 0))
325 {
326 WLog_DBG(TAG, "Unable to get container name: %d", GetLastError());
327 goto out;
328 }
329
330 cert->containerName = ConvertUtf8ToWCharAlloc(cert->keyName, nullptr);
331 if (!cert->containerName)
332 goto out;
333
334 /* ========= retrieve the certificate ===============*/
335 if (!CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hKey))
336 {
337 WLog_DBG(TAG, "Unable to get user key for %s: %d", cert->keyName, GetLastError());
338 goto out;
339 }
340
341 dwDataLen = 0;
342 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, nullptr, &dwDataLen, 0))
343 {
344 WLog_DBG(TAG, "Unable to get key param for key %s: %d", cert->keyName, GetLastError());
345 goto out;
346 }
347
348 certBytes = malloc(dwDataLen);
349 if (!certBytes)
350 {
351 WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", dwDataLen,
352 cert->keyName);
353 goto out;
354 }
355
356 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, certBytes, &dwDataLen, 0))
357 {
358 WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
359 goto out;
360 }
361
362 if (!set_info_certificate(cert, certBytes, dwDataLen, userFilter, domainFilter))
363 goto out;
364
365 if (!add_cert_to_list(pcerts, pcount, cert))
366 goto out;
367
368 ret = TRUE;
369
370out:
371 free(readerName);
372 free(certBytes);
373 if (hKey)
374 CryptDestroyKey(hKey);
375 if (hProvider)
376 CryptReleaseContext(hProvider, 0);
377 if (!ret)
378 smartcardCertInfo_Free(cert);
379 return ret;
380}
381#endif /* _WIN32 */
382
383static BOOL list_provider_keys(WINPR_ATTR_UNUSED const rdpSettings* settings,
384 NCRYPT_PROV_HANDLE provider, LPCWSTR csp, LPCWSTR scope,
385 const char* userFilter, const char* domainFilter,
386 SmartcardCertInfo*** pcerts, size_t* pcount)
387{
388 BOOL ret = FALSE;
389 NCryptKeyName* keyName = nullptr;
390 PVOID enumState = nullptr;
391 SmartcardCertInfo** cert_list = *pcerts;
392 size_t count = *pcount;
393
394 while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) ==
395 ERROR_SUCCESS)
396 {
397 NCRYPT_KEY_HANDLE phKey = 0;
398 PBYTE certBytes = nullptr;
399 DWORD dwFlags = NCRYPT_SILENT_FLAG;
400 DWORD cbOutput = 0;
401 SmartcardCertInfo* cert = nullptr;
402 BOOL haveError = TRUE;
403 SECURITY_STATUS status = 0;
404
405 cert = calloc(1, sizeof(SmartcardCertInfo));
406 if (!cert)
407 goto out;
408
409 cert->keyName = ConvertWCharToUtf8Alloc(keyName->pszName, nullptr);
410 if (!cert->keyName)
411 goto endofloop;
412
413 WLog_DBG(TAG, "opening key %s", cert->keyName);
414
415 status =
416 NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
417 if (status != ERROR_SUCCESS)
418 {
419 WLog_DBG(TAG,
420 "unable to NCryptOpenKey(dwLegacyKeySpec=0x%08" PRIx32 " dwFlags=0x%08" PRIx32
421 "), status=%s, skipping",
422 keyName->dwLegacyKeySpec, keyName->dwFlags,
423 winpr_NCryptSecurityStatusError(status));
424 goto endofloop;
425 }
426
427 cert->csp = _wcsdup(csp);
428 if (!cert->csp)
429 goto endofloop;
430
431#ifndef _WIN32
432 status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput,
433 dwFlags);
434 if (status != ERROR_SUCCESS)
435 {
436 WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->keyName,
437 winpr_NCryptSecurityStatusError(status));
438 goto endofloop;
439 }
440#endif /* _WIN32 */
441
442 /* ====== retrieve key's reader ====== */
443 cbOutput = 0;
444 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, nullptr, 0, &cbOutput, dwFlags);
445 if (status != ERROR_SUCCESS)
446 {
447 WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", cert->keyName);
448 goto endofloop;
449 }
450
451 cert->reader = calloc(1, cbOutput + 2);
452 if (!cert->reader)
453 {
454 WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->keyName);
455 goto endofloop;
456 }
457
458 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2,
459 &cbOutput, dwFlags);
460 if (status != ERROR_SUCCESS)
461 {
462 WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->keyName);
463 goto endofloop;
464 }
465
466 /* ====== retrieve key container name ====== */
467 /* When using PKCS11, this will try to return what Windows would use for the key's name */
468 cbOutput = 0;
469 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, nullptr, 0, &cbOutput, dwFlags);
470 if (status == ERROR_SUCCESS)
471 {
472 cert->containerName = calloc(1, cbOutput + sizeof(WCHAR));
473 if (!cert->containerName)
474 {
475 WLog_ERR(TAG, "unable to allocate key container name for key %s", cert->keyName);
476 goto endofloop;
477 }
478
479 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
480 cbOutput, &cbOutput, dwFlags);
481 }
482
483 if (status != ERROR_SUCCESS)
484 {
485 WLog_ERR(TAG, "unable to retrieve key container name for key %s", cert->keyName);
486 goto endofloop;
487 }
488
489 /* ========= retrieve the certificate ===============*/
490 cbOutput = 0;
491 status =
492 NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, nullptr, 0, &cbOutput, dwFlags);
493 if (status != ERROR_SUCCESS)
494 {
495 /* can happen that key don't have certificates */
496 WLog_DBG(TAG, "unable to retrieve certificate property len, status=%s, skipping",
497 winpr_NCryptSecurityStatusError(status));
498 goto endofloop;
499 }
500
501 certBytes = calloc(1, cbOutput);
502 if (!certBytes)
503 {
504 WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", cbOutput,
505 cert->keyName);
506 goto endofloop;
507 }
508
509 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput,
510 &cbOutput, dwFlags);
511 if (status != ERROR_SUCCESS)
512 {
513 WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
514 goto endofloop;
515 }
516
517 if (!set_info_certificate(cert, certBytes, cbOutput, userFilter, domainFilter))
518 goto endofloop;
519
520#ifndef _WIN32
521 if (!build_pkinit_args(provider, cert))
522 {
523 WLog_ERR(TAG, "error build pkinit args");
524 goto endofloop;
525 }
526#endif
527 haveError = FALSE;
528
529 endofloop:
530 free(certBytes);
531 NCryptFreeBuffer(keyName);
532 if (phKey)
533 NCryptFreeObject((NCRYPT_HANDLE)phKey);
534
535 if (haveError)
536 smartcardCertInfo_Free(cert);
537 else
538 {
539 if (!add_cert_to_list(&cert_list, &count, cert))
540 goto out;
541 }
542 }
543
544 ret = TRUE;
545out:
546 if (count == 0)
547 {
548 char cspa[128] = WINPR_C_ARRAY_INIT;
549
550 (void)ConvertWCharToUtf8(csp, cspa, sizeof(cspa));
551 char scopea[128] = WINPR_C_ARRAY_INIT;
552 (void)ConvertWCharToUtf8(scope, scopea, sizeof(scopea));
553 WLog_WARN(TAG, "%s [%s] no certificates found", cspa, scopea);
554 }
555 *pcount = count;
556 *pcerts = cert_list;
557 NCryptFreeBuffer(enumState);
558 return ret;
559}
560
561static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp,
562 const char* reader, const char* userFilter,
563 const char* domainFilter, SmartcardCertInfo*** scCerts,
564 size_t* retCount)
565{
566 BOOL ret = FALSE;
567 LPWSTR scope = nullptr;
568 NCRYPT_PROV_HANDLE provider = 0;
569 SECURITY_STATUS status = 0;
570 size_t count = 0;
571 SmartcardCertInfo** cert_list = nullptr;
572 const char* Pkcs11Module = freerdp_settings_get_string(settings, FreeRDP_Pkcs11Module);
573
574 WINPR_ASSERT(scCerts);
575 WINPR_ASSERT(retCount);
576
577 if (reader)
578 {
579 size_t readerSz = strlen(reader);
580 char* scopeStr = malloc(4 + readerSz + 1 + 1);
581 if (!scopeStr)
582 goto out;
583
584 (void)_snprintf(scopeStr, readerSz + 6, "\\\\.\\%s\\", reader);
585 scope = ConvertUtf8NToWCharAlloc(scopeStr, readerSz + 6, nullptr);
586 free(scopeStr);
587
588 if (!scope)
589 goto out;
590 }
591
592 if (Pkcs11Module)
593 {
594 /* load a unique CSP by pkcs11 module path */
595 LPCSTR paths[] = { Pkcs11Module, nullptr };
596
597 if (!csp)
598 csp = MS_SCARD_PROV;
599
600 status = winpr_NCryptOpenStorageProviderEx(&provider, csp, 0, paths);
601 if (status != ERROR_SUCCESS)
602 {
603 WLog_ERR(TAG, "unable to open provider given by pkcs11 module");
604 goto out;
605 }
606
607 status = list_provider_keys(settings, provider, csp, scope, userFilter, domainFilter,
608 &cert_list, &count);
609 NCryptFreeObject((NCRYPT_HANDLE)provider);
610 if (!status)
611 {
612 WLog_ERR(TAG, "error listing keys from CSP loaded from %s", Pkcs11Module);
613 goto out;
614 }
615 }
616 else
617 {
618 NCryptProviderName* names = nullptr;
619 DWORD nproviders = 0;
620
621#ifdef _WIN32
622 /* On Windows, mstsc first enumerates the legacy CAPI providers for usable certificates. */
623 DWORD provType, cbProvName = 0;
624 for (DWORD i = 0; CryptEnumProvidersW(i, nullptr, 0, &provType, nullptr, &cbProvName); ++i)
625 {
626 char providerNameStr[256] = WINPR_C_ARRAY_INIT;
627 LPWSTR szProvName = malloc(cbProvName * sizeof(WCHAR));
628 if (!CryptEnumProvidersW(i, nullptr, 0, &provType, szProvName, &cbProvName))
629 {
630 free(szProvName);
631 break;
632 }
633
634 if (ConvertWCharToUtf8(szProvName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
635 {
636 _snprintf(providerNameStr, sizeof(providerNameStr), "<unknown>");
637 WLog_ERR(TAG, "unable to convert provider name to char*, will show it as '%s'",
638 providerNameStr);
639 }
640
641 WLog_DBG(TAG, "exploring CSP '%s'", providerNameStr);
642 if (provType != PROV_RSA_FULL || (csp && _wcscmp(szProvName, csp) != 0))
643 {
644 WLog_DBG(TAG, "CSP '%s' filtered out", providerNameStr);
645 goto end_of_loop;
646 }
647
648 if (!list_capi_provider_keys(settings, szProvName, scope, userFilter, domainFilter,
649 &cert_list, &count))
650 WLog_INFO(TAG, "error when retrieving keys in CSP '%s'", providerNameStr);
651
652 end_of_loop:
653 free(szProvName);
654 }
655#endif
656
657 status = NCryptEnumStorageProviders(&nproviders, &names, NCRYPT_SILENT_FLAG);
658 if (status != ERROR_SUCCESS)
659 {
660 WLog_ERR(TAG, "error listing providers");
661 goto out;
662 }
663
664 for (DWORD i = 0; i < nproviders; i++)
665 {
666 char providerNameStr[256] = WINPR_C_ARRAY_INIT;
667 const NCryptProviderName* name = &names[i];
668
669 if (ConvertWCharToUtf8(name->pszName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
670 {
671 (void)_snprintf(providerNameStr, sizeof(providerNameStr), "<unknown>");
672 WLog_ERR(TAG, "unable to convert provider name to char*, will show it as '%s'",
673 providerNameStr);
674 }
675
676 WLog_DBG(TAG, "exploring CSP '%s'", providerNameStr);
677 if (csp && _wcscmp(name->pszName, csp) != 0)
678 {
679 WLog_DBG(TAG, "CSP '%s' filtered out", providerNameStr);
680 continue;
681 }
682
683 status = NCryptOpenStorageProvider(&provider, name->pszName, 0);
684 if (status != ERROR_SUCCESS)
685 continue;
686
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);
690
691 NCryptFreeObject((NCRYPT_HANDLE)provider);
692 }
693
694 NCryptFreeBuffer(names);
695 }
696
697 *scCerts = cert_list;
698 *retCount = count;
699 ret = TRUE;
700
701out:
702 if (!ret)
703 smartcardCertList_Free(cert_list, count);
704 free(scope);
705 return ret;
706}
707
708static char* create_temporary_file(void)
709{
710 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
711 char* path = nullptr;
712
713 if (winpr_RAND(buffer, sizeof(buffer)) < 0)
714 return nullptr;
715
716 char* hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
717 if (hex)
718 path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
719 free(hex);
720 return path;
721}
722
723static SmartcardCertInfo* smartcardCertInfo_New(const char* privKeyPEM, const char* certPEM)
724{
725 size_t size = 0;
726
727 WINPR_ASSERT(privKeyPEM);
728 WINPR_ASSERT(certPEM);
729
730 SmartcardCertInfo* cert = calloc(1, sizeof(SmartcardCertInfo));
731 if (!cert)
732 goto fail;
733
734 {
735 SmartcardKeyInfo* info = cert->key_info = calloc(1, sizeof(SmartcardKeyInfo));
736 if (!info)
737 goto fail;
738
739 cert->certificate = freerdp_certificate_new_from_pem(certPEM);
740 if (!cert->certificate)
741 {
742 WLog_ERR(TAG, "unable to read smartcard certificate");
743 goto fail;
744 }
745
746 if (!treat_sc_cert(cert))
747 {
748 WLog_ERR(TAG, "unable to treat smartcard certificate");
749 goto fail;
750 }
751
752 {
753 char* str = nullptr;
754 size_t len = 0;
755 (void)winpr_asprintf(&str, &len, "%s Emulator", freerdp_getApplicationDetailsString());
756 if (str)
757 cert->reader = ConvertUtf8NToWCharAlloc(str, len, nullptr);
758 free(str);
759 }
760 if (!cert->reader)
761 goto fail;
762
763 cert->containerName = ConvertUtf8ToWCharAlloc("Private Key 00", nullptr);
764 if (!cert->containerName)
765 goto fail;
766
767 /* compute PKINIT args FILE:<cert file>,<key file>
768 *
769 * We need files for PKINIT to read, so write the certificate to some
770 * temporary location and use that.
771 */
772 info->keyPath = create_temporary_file();
773 WLog_DBG(TAG, "writing PKINIT key to %s", info->keyPath);
774 if (!crypto_write_pem(info->keyPath, privKeyPEM, strlen(privKeyPEM)))
775 goto fail;
776
777 info->certPath = create_temporary_file();
778 WLog_DBG(TAG, "writing PKINIT cert to %s", info->certPath);
779 if (!crypto_write_pem(info->certPath, certPEM, strlen(certPEM)))
780 goto fail;
781
782 {
783 const int res = winpr_asprintf(&cert->pkinitArgs, &size, "FILE:%s,%s", info->certPath,
784 info->keyPath);
785 if (res <= 0)
786 goto fail;
787 }
788 }
789
790 return cert;
791fail:
792 smartcardCertInfo_Free(cert);
793 return nullptr;
794}
795
796static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
797 size_t* retCount)
798{
799 BOOL rc = FALSE;
800 SmartcardCertInfo** cert_list = nullptr;
801
802 WINPR_ASSERT(settings);
803 WINPR_ASSERT(scCerts);
804 WINPR_ASSERT(retCount);
805
806 const char* privKeyPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
807 const char* certPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
808 if (!privKeyPEM)
809 {
810 WLog_ERR(TAG, "Invalid smartcard private key PEM, aborting");
811 goto out_error;
812 }
813 if (!certPEM)
814 {
815 WLog_ERR(TAG, "Invalid smartcard certificate PEM, aborting");
816 goto out_error;
817 }
818
819 cert_list = (SmartcardCertInfo**)calloc(1, sizeof(SmartcardCertInfo*));
820 if (!cert_list)
821 goto out_error;
822
823 {
824 SmartcardCertInfo* cert = smartcardCertInfo_New(privKeyPEM, certPEM);
825 if (!cert)
826 goto out_error;
827 cert_list[0] = cert;
828 }
829
830 rc = TRUE;
831 *scCerts = cert_list;
832 *retCount = 1;
833
834out_error:
835 if (!rc)
836 smartcardCertList_Free(cert_list, 1);
837 return rc;
838}
839
840BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
841 size_t* retCount, BOOL gateway)
842{
843 BOOL ret = 0;
844 LPWSTR csp = nullptr;
845 const char* ReaderName = freerdp_settings_get_string(settings, FreeRDP_ReaderName);
846 const char* CspName = freerdp_settings_get_string(settings, FreeRDP_CspName);
847 const char* Username = nullptr;
848 const char* Domain = nullptr;
849
850 if (gateway)
851 {
852 Username = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
853 Domain = freerdp_settings_get_string(settings, FreeRDP_GatewayDomain);
854 }
855 else
856 {
857 Username = freerdp_settings_get_string(settings, FreeRDP_Username);
858 Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
859 }
860
861 WINPR_ASSERT(settings);
862 WINPR_ASSERT(scCerts);
863 WINPR_ASSERT(retCount);
864
865 if (Domain && !strlen(Domain))
866 Domain = nullptr;
867
868 if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation))
869 return smartcard_sw_enumerateCerts(settings, scCerts, retCount);
870
871 if (CspName && (!(csp = ConvertUtf8ToWCharAlloc(CspName, nullptr))))
872 {
873 WLog_ERR(TAG, "error while converting CSP to WCHAR");
874 return FALSE;
875 }
876
877 ret =
878 smartcard_hw_enumerateCerts(settings, csp, ReaderName, Username, Domain, scCerts, retCount);
879 free(csp);
880 return ret;
881}
882
883static BOOL set_settings_from_smartcard(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
884 const char* value)
885{
886 WINPR_ASSERT(settings);
887
888 if (!freerdp_settings_get_string(settings, id) && value)
889 if (!freerdp_settings_set_string(settings, id, value))
890 return FALSE;
891
892 return TRUE;
893}
894
895BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert, BOOL gateway)
896{
897 WINPR_ASSERT(context);
898
899 const freerdp* instance = context->instance;
900 rdpSettings* settings = context->settings;
901 SmartcardCertInfo** cert_list = nullptr;
902 size_t count = 0;
903
904 WINPR_ASSERT(instance);
905 WINPR_ASSERT(settings);
906
907 if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
908 return FALSE;
909
910 if (count < 1)
911 {
912 WLog_ERR(TAG, "no suitable smartcard certificates were found");
913 return FALSE;
914 }
915
916 if (count > UINT32_MAX)
917 {
918 WLog_ERR(TAG, "smartcard certificate count %" PRIuz " exceeds UINT32_MAX", count);
919 return FALSE;
920 }
921
922 if (count > 1)
923 {
924 DWORD index = 0;
925
926 if (!instance->ChooseSmartcard ||
927 !instance->ChooseSmartcard(context->instance, cert_list, (UINT32)count, &index,
928 gateway))
929 {
930 WLog_ERR(TAG, "more than one suitable smartcard certificate was found");
931 smartcardCertList_Free(cert_list, count);
932 return FALSE;
933 }
934 *cert = cert_list[index];
935
936 for (DWORD i = 0; i < index; i++)
937 smartcardCertInfo_Free(cert_list[i]);
938 for (DWORD i = index + 1; i < count; i++)
939 smartcardCertInfo_Free(cert_list[i]);
940 }
941 else
942 *cert = cert_list[0];
943
944 FreeRDP_Settings_Keys_String username_setting =
945 gateway ? FreeRDP_GatewayUsername : FreeRDP_Username;
946 FreeRDP_Settings_Keys_String domain_setting = gateway ? FreeRDP_GatewayDomain : FreeRDP_Domain;
947
948 free((void*)cert_list);
949
950 if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
951 !set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
952 {
953 WLog_ERR(TAG, "unable to set settings from smartcard!");
954 smartcardCertInfo_Free(*cert);
955 return FALSE;
956 }
957
958 return TRUE;
959}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
a key name descriptor
a provider name descriptor