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