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] = { 0 };
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** cert_list, size_t count)
113{
114 if (!cert_list)
115 return;
116
117 for (size_t i = 0; i < count; i++)
118 {
119 SmartcardCertInfo* cert = cert_list[i];
120 smartcardCertInfo_Free(cert);
121 }
122
123 free((void*)cert_list);
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 if (winpr_asprintf(&scCert->pkinitArgs, &size, "PKCS11:module_name=%s:slotid=%" PRIu16,
260 pkModule, (UINT16)scCert->slotId) <= 0)
261 return FALSE;
262 return TRUE;
263}
264#endif /* _WIN32 */
265
266#ifdef _WIN32
267static BOOL list_capi_provider_keys(const rdpSettings* settings, LPCWSTR csp, LPCWSTR scope,
268 const char* userFilter, const char* domainFilter,
269 SmartcardCertInfo*** pcerts, size_t* pcount)
270{
271 BOOL ret = FALSE;
272 HCRYPTKEY hKey = 0;
273 HCRYPTPROV hProvider = 0;
274 SmartcardCertInfo* cert = NULL;
275 BYTE* certBytes = NULL;
276 CHAR* readerName = NULL;
277
278 if (!CryptAcquireContextW(&hProvider, scope, csp, PROV_RSA_FULL, CRYPT_SILENT))
279 {
280 WLog_DBG(TAG, "Unable to acquire context: %d", GetLastError());
281 goto out;
282 }
283
284 cert = calloc(1, sizeof(SmartcardCertInfo));
285 if (!cert)
286 goto out;
287
288 cert->csp = _wcsdup(csp);
289 if (!cert->csp)
290 goto out;
291
292 /* ====== retrieve key's reader ====== */
293 DWORD dwDataLen = 0;
294 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, NULL, &dwDataLen, 0))
295 {
296 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
297 goto out;
298 }
299
300 readerName = malloc(dwDataLen);
301 if (!readerName)
302 goto out;
303
304 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, readerName, &dwDataLen, 0))
305 {
306 WLog_DBG(TAG, "Unable to get reader name: %d", GetLastError());
307 goto out;
308 }
309
310 cert->reader = ConvertUtf8ToWCharAlloc(readerName, NULL);
311 if (!cert->reader)
312 goto out;
313
314 /* ====== retrieve key container name ====== */
315 dwDataLen = 0;
316 if (!CryptGetProvParam(hProvider, PP_CONTAINER, NULL, &dwDataLen, 0))
317 {
318 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
319 goto out;
320 }
321
322 cert->keyName = malloc(dwDataLen);
323 if (!cert->keyName)
324 goto out;
325
326 if (!CryptGetProvParam(hProvider, PP_CONTAINER, cert->keyName, &dwDataLen, 0))
327 {
328 WLog_DBG(TAG, "Unable to get container name: %d", GetLastError());
329 goto out;
330 }
331
332 cert->containerName = ConvertUtf8ToWCharAlloc(cert->keyName, NULL);
333 if (!cert->containerName)
334 goto out;
335
336 /* ========= retrieve the certificate ===============*/
337 if (!CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hKey))
338 {
339 WLog_DBG(TAG, "Unable to get user key for %s: %d", cert->keyName, GetLastError());
340 goto out;
341 }
342
343 dwDataLen = 0;
344 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &dwDataLen, 0))
345 {
346 WLog_DBG(TAG, "Unable to get key param for key %s: %d", cert->keyName, GetLastError());
347 goto out;
348 }
349
350 certBytes = malloc(dwDataLen);
351 if (!certBytes)
352 {
353 WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", dwDataLen,
354 cert->keyName);
355 goto out;
356 }
357
358 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, certBytes, &dwDataLen, 0))
359 {
360 WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
361 goto out;
362 }
363
364 if (!set_info_certificate(cert, certBytes, dwDataLen, userFilter, domainFilter))
365 goto out;
366
367 if (!add_cert_to_list(pcerts, pcount, cert))
368 goto out;
369
370 ret = TRUE;
371
372out:
373 free(readerName);
374 free(certBytes);
375 if (hKey)
376 CryptDestroyKey(hKey);
377 if (hProvider)
378 CryptReleaseContext(hProvider, 0);
379 if (!ret)
380 smartcardCertInfo_Free(cert);
381 return ret;
382}
383#endif /* _WIN32 */
384
385static BOOL list_provider_keys(WINPR_ATTR_UNUSED const rdpSettings* settings,
386 NCRYPT_PROV_HANDLE provider, LPCWSTR csp, LPCWSTR scope,
387 const char* userFilter, const char* domainFilter,
388 SmartcardCertInfo*** pcerts, size_t* pcount)
389{
390 BOOL ret = FALSE;
391 NCryptKeyName* keyName = NULL;
392 PVOID enumState = NULL;
393 SmartcardCertInfo** cert_list = *pcerts;
394 size_t count = *pcount;
395
396 while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) ==
397 ERROR_SUCCESS)
398 {
399 NCRYPT_KEY_HANDLE phKey = 0;
400 PBYTE certBytes = NULL;
401 DWORD dwFlags = NCRYPT_SILENT_FLAG;
402 DWORD cbOutput = 0;
403 SmartcardCertInfo* cert = NULL;
404 BOOL haveError = TRUE;
405 SECURITY_STATUS status = 0;
406
407 cert = calloc(1, sizeof(SmartcardCertInfo));
408 if (!cert)
409 goto out;
410
411 cert->keyName = ConvertWCharToUtf8Alloc(keyName->pszName, NULL);
412 if (!cert->keyName)
413 goto endofloop;
414
415 WLog_DBG(TAG, "opening key %s", cert->keyName);
416
417 status =
418 NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
419 if (status != ERROR_SUCCESS)
420 {
421 WLog_DBG(TAG,
422 "unable to NCryptOpenKey(dwLegacyKeySpec=0x%08" PRIx32 " dwFlags=0x%08" PRIx32
423 "), status=%s, skipping",
424 keyName->dwLegacyKeySpec, keyName->dwFlags,
425 winpr_NCryptSecurityStatusError(status));
426 goto endofloop;
427 }
428
429 cert->csp = _wcsdup(csp);
430 if (!cert->csp)
431 goto endofloop;
432
433#ifndef _WIN32
434 status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput,
435 dwFlags);
436 if (status != ERROR_SUCCESS)
437 {
438 WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->keyName,
439 winpr_NCryptSecurityStatusError(status));
440 goto endofloop;
441 }
442#endif /* _WIN32 */
443
444 /* ====== retrieve key's reader ====== */
445 cbOutput = 0;
446 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, dwFlags);
447 if (status != ERROR_SUCCESS)
448 {
449 WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", cert->keyName);
450 goto endofloop;
451 }
452
453 cert->reader = calloc(1, cbOutput + 2);
454 if (!cert->reader)
455 {
456 WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->keyName);
457 goto endofloop;
458 }
459
460 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2,
461 &cbOutput, dwFlags);
462 if (status != ERROR_SUCCESS)
463 {
464 WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->keyName);
465 goto endofloop;
466 }
467
468 /* ====== retrieve key container name ====== */
469 /* When using PKCS11, this will try to return what Windows would use for the key's name */
470 cbOutput = 0;
471 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, NULL, 0, &cbOutput, dwFlags);
472 if (status == ERROR_SUCCESS)
473 {
474 cert->containerName = calloc(1, cbOutput + sizeof(WCHAR));
475 if (!cert->containerName)
476 {
477 WLog_ERR(TAG, "unable to allocate key container name for key %s", cert->keyName);
478 goto endofloop;
479 }
480
481 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
482 cbOutput, &cbOutput, dwFlags);
483 }
484
485 if (status != ERROR_SUCCESS)
486 {
487 WLog_ERR(TAG, "unable to retrieve key container name for key %s", cert->keyName);
488 goto endofloop;
489 }
490
491 /* ========= retrieve the certificate ===============*/
492 cbOutput = 0;
493 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 0, &cbOutput, dwFlags);
494 if (status != ERROR_SUCCESS)
495 {
496 /* can happen that key don't have certificates */
497 WLog_DBG(TAG, "unable to retrieve certificate property len, status=%s, skipping",
498 winpr_NCryptSecurityStatusError(status));
499 goto endofloop;
500 }
501
502 certBytes = calloc(1, cbOutput);
503 if (!certBytes)
504 {
505 WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", cbOutput,
506 cert->keyName);
507 goto endofloop;
508 }
509
510 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput,
511 &cbOutput, dwFlags);
512 if (status != ERROR_SUCCESS)
513 {
514 WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
515 goto endofloop;
516 }
517
518 if (!set_info_certificate(cert, certBytes, cbOutput, userFilter, domainFilter))
519 goto endofloop;
520
521#ifndef _WIN32
522 if (!build_pkinit_args(provider, cert))
523 {
524 WLog_ERR(TAG, "error build pkinit args");
525 goto endofloop;
526 }
527#endif
528 haveError = FALSE;
529
530 endofloop:
531 free(certBytes);
532 NCryptFreeBuffer(keyName);
533 if (phKey)
534 NCryptFreeObject((NCRYPT_HANDLE)phKey);
535
536 if (haveError)
537 smartcardCertInfo_Free(cert);
538 else
539 {
540 if (!add_cert_to_list(&cert_list, &count, cert))
541 goto out;
542 }
543 }
544
545 ret = TRUE;
546out:
547 if (count == 0)
548 {
549 char cspa[128] = { 0 };
550
551 (void)ConvertWCharToUtf8(csp, cspa, sizeof(cspa));
552 char scopea[128] = { 0 };
553 (void)ConvertWCharToUtf8(scope, scopea, sizeof(scopea));
554 WLog_WARN(TAG, "%s [%s] no certificates found", cspa, scopea);
555 }
556 *pcount = count;
557 *pcerts = cert_list;
558 NCryptFreeBuffer(enumState);
559 return ret;
560}
561
562static BOOL smartcard_hw_enumerateCerts(const rdpSettings* settings, LPCWSTR csp,
563 const char* reader, const char* userFilter,
564 const char* domainFilter, SmartcardCertInfo*** scCerts,
565 size_t* retCount)
566{
567 BOOL ret = FALSE;
568 LPWSTR scope = NULL;
569 NCRYPT_PROV_HANDLE provider = 0;
570 SECURITY_STATUS status = 0;
571 size_t count = 0;
572 SmartcardCertInfo** cert_list = NULL;
573 const char* Pkcs11Module = freerdp_settings_get_string(settings, FreeRDP_Pkcs11Module);
574
575 WINPR_ASSERT(scCerts);
576 WINPR_ASSERT(retCount);
577
578 if (reader)
579 {
580 size_t readerSz = strlen(reader);
581 char* scopeStr = malloc(4 + readerSz + 1 + 1);
582 if (!scopeStr)
583 goto out;
584
585 (void)_snprintf(scopeStr, readerSz + 6, "\\\\.\\%s\\", reader);
586 scope = ConvertUtf8NToWCharAlloc(scopeStr, readerSz + 6, NULL);
587 free(scopeStr);
588
589 if (!scope)
590 goto out;
591 }
592
593 if (Pkcs11Module)
594 {
595 /* load a unique CSP by pkcs11 module path */
596 LPCSTR paths[] = { Pkcs11Module, NULL };
597
598 if (!csp)
599 csp = MS_SCARD_PROV;
600
601 status = winpr_NCryptOpenStorageProviderEx(&provider, csp, 0, paths);
602 if (status != ERROR_SUCCESS)
603 {
604 WLog_ERR(TAG, "unable to open provider given by pkcs11 module");
605 goto out;
606 }
607
608 status = list_provider_keys(settings, provider, csp, scope, userFilter, domainFilter,
609 &cert_list, &count);
610 NCryptFreeObject((NCRYPT_HANDLE)provider);
611 if (!status)
612 {
613 WLog_ERR(TAG, "error listing keys from CSP loaded from %s", Pkcs11Module);
614 goto out;
615 }
616 }
617 else
618 {
619 NCryptProviderName* names = NULL;
620 DWORD nproviders = 0;
621
622#ifdef _WIN32
623 /* On Windows, mstsc first enumerates the legacy CAPI providers for usable certificates. */
624 DWORD provType, cbProvName = 0;
625 for (DWORD i = 0; CryptEnumProvidersW(i, NULL, 0, &provType, NULL, &cbProvName); ++i)
626 {
627 char providerNameStr[256] = { 0 };
628 LPWSTR szProvName = malloc(cbProvName * sizeof(WCHAR));
629 if (!CryptEnumProvidersW(i, NULL, 0, &provType, szProvName, &cbProvName))
630 {
631 free(szProvName);
632 break;
633 }
634
635 if (ConvertWCharToUtf8(szProvName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
636 {
637 _snprintf(providerNameStr, sizeof(providerNameStr), "<unknown>");
638 WLog_ERR(TAG, "unable to convert provider name to char*, will show it as '%s'",
639 providerNameStr);
640 }
641
642 WLog_DBG(TAG, "exploring CSP '%s'", providerNameStr);
643 if (provType != PROV_RSA_FULL || (csp && _wcscmp(szProvName, csp) != 0))
644 {
645 WLog_DBG(TAG, "CSP '%s' filtered out", providerNameStr);
646 goto end_of_loop;
647 }
648
649 if (!list_capi_provider_keys(settings, szProvName, scope, userFilter, domainFilter,
650 &cert_list, &count))
651 WLog_INFO(TAG, "error when retrieving keys in CSP '%s'", providerNameStr);
652
653 end_of_loop:
654 free(szProvName);
655 }
656#endif
657
658 status = NCryptEnumStorageProviders(&nproviders, &names, NCRYPT_SILENT_FLAG);
659 if (status != ERROR_SUCCESS)
660 {
661 WLog_ERR(TAG, "error listing providers");
662 goto out;
663 }
664
665 for (DWORD i = 0; i < nproviders; i++)
666 {
667 char providerNameStr[256] = { 0 };
668 const NCryptProviderName* name = &names[i];
669
670 if (ConvertWCharToUtf8(name->pszName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
671 {
672 (void)_snprintf(providerNameStr, sizeof(providerNameStr), "<unknown>");
673 WLog_ERR(TAG, "unable to convert provider name to char*, will show it as '%s'",
674 providerNameStr);
675 }
676
677 WLog_DBG(TAG, "exploring CSP '%s'", providerNameStr);
678 if (csp && _wcscmp(name->pszName, csp) != 0)
679 {
680 WLog_DBG(TAG, "CSP '%s' filtered out", providerNameStr);
681 continue;
682 }
683
684 status = NCryptOpenStorageProvider(&provider, name->pszName, 0);
685 if (status != ERROR_SUCCESS)
686 continue;
687
688 if (!list_provider_keys(settings, provider, name->pszName, scope, userFilter,
689 domainFilter, &cert_list, &count))
690 WLog_INFO(TAG, "error when retrieving keys in CSP '%s'", providerNameStr);
691
692 NCryptFreeObject((NCRYPT_HANDLE)provider);
693 }
694
695 NCryptFreeBuffer(names);
696 }
697
698 *scCerts = cert_list;
699 *retCount = count;
700 ret = TRUE;
701
702out:
703 if (!ret)
704 smartcardCertList_Free(cert_list, count);
705 free(scope);
706 return ret;
707}
708
709static char* create_temporary_file(void)
710{
711 BYTE buffer[32];
712 char* hex = NULL;
713 char* path = NULL;
714
715 winpr_RAND(buffer, sizeof(buffer));
716 hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
717 path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
718 free(hex);
719 return path;
720}
721
722static SmartcardCertInfo* smartcardCertInfo_New(const char* privKeyPEM, const char* certPEM)
723{
724 size_t size = 0;
725
726 WINPR_ASSERT(privKeyPEM);
727 WINPR_ASSERT(certPEM);
728
729 SmartcardCertInfo* cert = calloc(1, sizeof(SmartcardCertInfo));
730 if (!cert)
731 goto fail;
732
733 {
734 SmartcardKeyInfo* info = cert->key_info = calloc(1, sizeof(SmartcardKeyInfo));
735 if (!info)
736 goto fail;
737
738 cert->certificate = freerdp_certificate_new_from_pem(certPEM);
739 if (!cert->certificate)
740 {
741 WLog_ERR(TAG, "unable to read smartcard certificate");
742 goto fail;
743 }
744
745 if (!treat_sc_cert(cert))
746 {
747 WLog_ERR(TAG, "unable to treat smartcard certificate");
748 goto fail;
749 }
750
751 {
752 char* str = NULL;
753 size_t len = 0;
754 (void)winpr_asprintf(&str, &len, "%s Emulator", freerdp_getApplicationDetailsString());
755 if (str)
756 cert->reader = ConvertUtf8NToWCharAlloc(str, len, NULL);
757 free(str);
758 }
759 if (!cert->reader)
760 goto fail;
761
762 cert->containerName = ConvertUtf8ToWCharAlloc("Private Key 00", NULL);
763 if (!cert->containerName)
764 goto fail;
765
766 /* compute PKINIT args FILE:<cert file>,<key file>
767 *
768 * We need files for PKINIT to read, so write the certificate to some
769 * temporary location and use that.
770 */
771 info->keyPath = create_temporary_file();
772 WLog_DBG(TAG, "writing PKINIT key to %s", info->keyPath);
773 if (!crypto_write_pem(info->keyPath, privKeyPEM, strlen(privKeyPEM)))
774 goto fail;
775
776 info->certPath = create_temporary_file();
777 WLog_DBG(TAG, "writing PKINIT cert to %s", info->certPath);
778 if (!crypto_write_pem(info->certPath, certPEM, strlen(certPEM)))
779 goto fail;
780
781 {
782 const int res = winpr_asprintf(&cert->pkinitArgs, &size, "FILE:%s,%s", info->certPath,
783 info->keyPath);
784 if (res <= 0)
785 goto fail;
786 }
787 }
788
789 return cert;
790fail:
791 smartcardCertInfo_Free(cert);
792 return NULL;
793}
794
795static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
796 size_t* retCount)
797{
798 BOOL rc = FALSE;
799 SmartcardCertInfo** cert_list = NULL;
800
801 WINPR_ASSERT(settings);
802 WINPR_ASSERT(scCerts);
803 WINPR_ASSERT(retCount);
804
805 const char* privKeyPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
806 const char* certPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
807 if (!privKeyPEM)
808 {
809 WLog_ERR(TAG, "Invalid smartcard private key PEM, aborting");
810 goto out_error;
811 }
812 if (!certPEM)
813 {
814 WLog_ERR(TAG, "Invalid smartcard certificate PEM, aborting");
815 goto out_error;
816 }
817
818 cert_list = (SmartcardCertInfo**)calloc(1, sizeof(SmartcardCertInfo*));
819 if (!cert_list)
820 goto out_error;
821
822 {
823 SmartcardCertInfo* cert = smartcardCertInfo_New(privKeyPEM, certPEM);
824 if (!cert)
825 goto out_error;
826 cert_list[0] = cert;
827 }
828
829 rc = TRUE;
830 *scCerts = cert_list;
831 *retCount = 1;
832
833out_error:
834 if (!rc)
835 smartcardCertList_Free(cert_list, 1);
836 return rc;
837}
838
839BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
840 size_t* retCount, BOOL gateway)
841{
842 BOOL ret = 0;
843 LPWSTR csp = NULL;
844 const char* ReaderName = freerdp_settings_get_string(settings, FreeRDP_ReaderName);
845 const char* CspName = freerdp_settings_get_string(settings, FreeRDP_CspName);
846 const char* Username = NULL;
847 const char* Domain = NULL;
848
849 if (gateway)
850 {
851 Username = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
852 Domain = freerdp_settings_get_string(settings, FreeRDP_GatewayDomain);
853 }
854 else
855 {
856 Username = freerdp_settings_get_string(settings, FreeRDP_Username);
857 Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
858 }
859
860 WINPR_ASSERT(settings);
861 WINPR_ASSERT(scCerts);
862 WINPR_ASSERT(retCount);
863
864 if (Domain && !strlen(Domain))
865 Domain = NULL;
866
867 if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation))
868 return smartcard_sw_enumerateCerts(settings, scCerts, retCount);
869
870 if (CspName && (!(csp = ConvertUtf8ToWCharAlloc(CspName, NULL))))
871 {
872 WLog_ERR(TAG, "error while converting CSP to WCHAR");
873 return FALSE;
874 }
875
876 ret =
877 smartcard_hw_enumerateCerts(settings, csp, ReaderName, Username, Domain, scCerts, retCount);
878 free(csp);
879 return ret;
880}
881
882static BOOL set_settings_from_smartcard(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
883 const char* value)
884{
885 WINPR_ASSERT(settings);
886
887 if (!freerdp_settings_get_string(settings, id) && value)
888 if (!freerdp_settings_set_string(settings, id, value))
889 return FALSE;
890
891 return TRUE;
892}
893
894BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert, BOOL gateway)
895{
896 WINPR_ASSERT(context);
897
898 const freerdp* instance = context->instance;
899 rdpSettings* settings = context->settings;
900 SmartcardCertInfo** cert_list = NULL;
901 size_t count = 0;
902
903 WINPR_ASSERT(instance);
904 WINPR_ASSERT(settings);
905
906 if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
907 return FALSE;
908
909 if (count < 1)
910 {
911 WLog_ERR(TAG, "no suitable smartcard certificates were found");
912 return FALSE;
913 }
914
915 if (count > UINT32_MAX)
916 {
917 WLog_ERR(TAG, "smartcard certificate count %" PRIuz " exceeds UINT32_MAX", count);
918 return FALSE;
919 }
920
921 if (count > 1)
922 {
923 DWORD index = 0;
924
925 if (!instance->ChooseSmartcard ||
926 !instance->ChooseSmartcard(context->instance, cert_list, (UINT32)count, &index,
927 gateway))
928 {
929 WLog_ERR(TAG, "more than one suitable smartcard certificate was found");
930 smartcardCertList_Free(cert_list, count);
931 return FALSE;
932 }
933 *cert = cert_list[index];
934
935 for (DWORD i = 0; i < index; i++)
936 smartcardCertInfo_Free(cert_list[i]);
937 for (DWORD i = index + 1; i < count; i++)
938 smartcardCertInfo_Free(cert_list[i]);
939 }
940 else
941 *cert = cert_list[0];
942
943 FreeRDP_Settings_Keys_String username_setting =
944 gateway ? FreeRDP_GatewayUsername : FreeRDP_Username;
945 FreeRDP_Settings_Keys_String domain_setting = gateway ? FreeRDP_GatewayDomain : FreeRDP_Domain;
946
947 free((void*)cert_list);
948
949 if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
950 !set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
951 {
952 WLog_ERR(TAG, "unable to set settings from smartcard!");
953 smartcardCertInfo_Free(*cert);
954 return FALSE;
955 }
956
957 return TRUE;
958}
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.
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.
a key name descriptor
a provider name descriptor