FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
35#include <openssl/obj_mac.h>
36
37#define TAG FREERDP_TAG("smartcardlogon")
38
39struct SmartcardKeyInfo_st
40{
41 char* certPath;
42 char* keyPath;
43};
44
45static void delete_file(char* path)
46{
47 if (!path)
48 return;
49
50 /* Overwrite data in files before deletion */
51 {
52 FILE* fp = winpr_fopen(path, "r+");
53 if (fp)
54 {
55 const char buffer[8192] = { 0 };
56 INT64 size = 0;
57 int rs = _fseeki64(fp, 0, SEEK_END);
58 if (rs == 0)
59 size = _ftelli64(fp);
60 (void)_fseeki64(fp, 0, SEEK_SET);
61
62 for (INT64 x = 0; x < size; x += sizeof(buffer))
63 {
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);
67 if (count != 1)
68 break;
69 }
70
71 (void)fclose(fp);
72 }
73 }
74
75 winpr_DeleteFile(path);
76 free(path);
77}
78
79static void smartcardKeyInfo_Free(SmartcardKeyInfo* key_info)
80{
81 if (!key_info)
82 return;
83
84 delete_file(key_info->certPath);
85 delete_file(key_info->keyPath);
86
87 free(key_info);
88}
89
90void smartcardCertInfo_Free(SmartcardCertInfo* scCert)
91{
92 if (!scCert)
93 return;
94
95 free(scCert->csp);
96 free(scCert->reader);
97 freerdp_certificate_free(scCert->certificate);
98 free(scCert->pkinitArgs);
99 free(scCert->keyName);
100 free(scCert->containerName);
101 free(scCert->upn);
102 free(scCert->userHint);
103 free(scCert->domainHint);
104 free(scCert->subject);
105 free(scCert->issuer);
106 smartcardKeyInfo_Free(scCert->key_info);
107
108 free(scCert);
109}
110
111void smartcardCertList_Free(SmartcardCertInfo** cert_list, size_t count)
112{
113 if (!cert_list)
114 return;
115
116 for (size_t i = 0; i < count; i++)
117 {
118 SmartcardCertInfo* cert = cert_list[i];
119 smartcardCertInfo_Free(cert);
120 }
121
122 free((void*)cert_list);
123}
124
125static BOOL add_cert_to_list(SmartcardCertInfo*** certInfoList, size_t* count,
126 SmartcardCertInfo* certInfo)
127{
128 size_t curCount = *count;
129 SmartcardCertInfo** curInfoList = *certInfoList;
130
131 /* Check if the certificate is already in the list */
132 for (size_t i = 0; i < curCount; ++i)
133 {
134 if (_wcscmp(curInfoList[i]->containerName, certInfo->containerName) == 0)
135 {
136 smartcardCertInfo_Free(certInfo);
137 return TRUE;
138 }
139 }
140
141 {
142 SmartcardCertInfo** tmpInfoList = (SmartcardCertInfo**)realloc(
143 (void*)curInfoList, sizeof(SmartcardCertInfo*) * (curCount + 1));
144 if (!tmpInfoList)
145 {
146 WLog_ERR(TAG, "unable to reallocate certs");
147 return FALSE;
148 }
149 curInfoList = tmpInfoList;
150 }
151
152 curInfoList[curCount++] = certInfo;
153 *certInfoList = curInfoList;
154 *count = curCount;
155 return TRUE;
156}
157
158static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
159{
160 WINPR_ASSERT(scCert);
161
162 scCert->upn = freerdp_certificate_get_upn(scCert->certificate);
163 if (!scCert->upn)
164 {
165 WLog_DBG(TAG, "%s has no UPN, trying emailAddress", scCert->keyName);
166 scCert->upn = freerdp_certificate_get_email(scCert->certificate);
167 }
168
169 if (scCert->upn)
170 {
171 size_t userLen = 0;
172 const char* atPos = strchr(scCert->upn, '@');
173
174 if (!atPos)
175 {
176 WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->keyName);
177 return FALSE;
178 }
179
180 userLen = (size_t)(atPos - scCert->upn);
181 scCert->userHint = malloc(userLen + 1);
182 scCert->domainHint = _strdup(atPos + 1);
183
184 if (!scCert->userHint || !scCert->domainHint)
185 {
186 WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s", scCert->keyName);
187 return FALSE;
188 }
189
190 memcpy(scCert->userHint, scCert->upn, userLen);
191 scCert->userHint[userLen] = 0;
192 }
193
194 scCert->subject = freerdp_certificate_get_subject(scCert->certificate);
195 scCert->issuer = freerdp_certificate_get_issuer(scCert->certificate);
196 return TRUE;
197}
198
199static BOOL set_info_certificate(SmartcardCertInfo* cert, BYTE* certBytes, DWORD cbCertBytes,
200 const char* userFilter, const char* domainFilter)
201{
202 if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbCertBytes, cert->sha1Hash,
203 sizeof(cert->sha1Hash)))
204 {
205 WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->keyName);
206 return FALSE;
207 }
208
209 cert->certificate = freerdp_certificate_new_from_der(certBytes, cbCertBytes);
210 if (!cert->certificate)
211 {
212 WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->keyName);
213 return FALSE;
214 }
215
216 if (!freerdp_certificate_check_eku(cert->certificate, NID_ms_smartcard_login))
217 {
218 WLog_DBG(TAG, "discarding certificate without Smartcard Login EKU for key %s",
219 cert->keyName);
220 return FALSE;
221 }
222
223 if (!treat_sc_cert(cert))
224 {
225 WLog_DBG(TAG, "error treating cert");
226 return FALSE;
227 }
228
229 if (userFilter && (!cert->upn || (strcmp(cert->upn, userFilter) != 0)))
230 {
231 if (cert->userHint && strcmp(cert->userHint, userFilter) != 0)
232 {
233 WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint,
234 cert->domainHint);
235 return FALSE;
236 }
237 }
238
239 if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0)
240 {
241 WLog_DBG(TAG, "discarding non matching cert by domain(%s) %s@%s", domainFilter,
242 cert->userHint, cert->domainHint);
243 return FALSE;
244 }
245
246 return TRUE;
247}
248
249#ifndef _WIN32
250static BOOL build_pkinit_args(NCRYPT_PROV_HANDLE provider, SmartcardCertInfo* scCert)
251{
252 /* pkinit args only under windows
253 * PKCS11:module_name=opensc-pkcs11.so
254 */
255 const char* pkModule = winpr_NCryptGetModulePath(provider);
256 size_t size = 0;
257
258 if (winpr_asprintf(&scCert->pkinitArgs, &size, "PKCS11:module_name=%s:slotid=%" PRIu16,
259 pkModule, (UINT16)scCert->slotId) <= 0)
260 return FALSE;
261 return TRUE;
262}
263#endif /* _WIN32 */
264
265#ifdef _WIN32
266static BOOL list_capi_provider_keys(const rdpSettings* settings, LPCWSTR csp, LPCWSTR scope,
267 const char* userFilter, const char* domainFilter,
268 SmartcardCertInfo*** pcerts, size_t* pcount)
269{
270 BOOL ret = FALSE;
271 HCRYPTKEY hKey = 0;
272 HCRYPTPROV hProvider = 0;
273 SmartcardCertInfo* cert = NULL;
274 BYTE* certBytes = NULL;
275 CHAR* readerName = NULL;
276
277 if (!CryptAcquireContextW(&hProvider, scope, csp, PROV_RSA_FULL, CRYPT_SILENT))
278 {
279 WLog_DBG(TAG, "Unable to acquire context: %d", GetLastError());
280 goto out;
281 }
282
283 cert = calloc(1, sizeof(SmartcardCertInfo));
284 if (!cert)
285 goto out;
286
287 cert->csp = _wcsdup(csp);
288 if (!cert->csp)
289 goto out;
290
291 /* ====== retrieve key's reader ====== */
292 DWORD dwDataLen = 0;
293 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, NULL, &dwDataLen, 0))
294 {
295 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
296 goto out;
297 }
298
299 readerName = malloc(dwDataLen);
300 if (!readerName)
301 goto out;
302
303 if (!CryptGetProvParam(hProvider, PP_SMARTCARD_READER, readerName, &dwDataLen, 0))
304 {
305 WLog_DBG(TAG, "Unable to get reader name: %d", GetLastError());
306 goto out;
307 }
308
309 cert->reader = ConvertUtf8ToWCharAlloc(readerName, NULL);
310 if (!cert->reader)
311 goto out;
312
313 /* ====== retrieve key container name ====== */
314 dwDataLen = 0;
315 if (!CryptGetProvParam(hProvider, PP_CONTAINER, NULL, &dwDataLen, 0))
316 {
317 WLog_DBG(TAG, "Unable to get provider param: %d", GetLastError());
318 goto out;
319 }
320
321 cert->keyName = malloc(dwDataLen);
322 if (!cert->keyName)
323 goto out;
324
325 if (!CryptGetProvParam(hProvider, PP_CONTAINER, cert->keyName, &dwDataLen, 0))
326 {
327 WLog_DBG(TAG, "Unable to get container name: %d", GetLastError());
328 goto out;
329 }
330
331 cert->containerName = ConvertUtf8ToWCharAlloc(cert->keyName, NULL);
332 if (!cert->containerName)
333 goto out;
334
335 /* ========= retrieve the certificate ===============*/
336 if (!CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hKey))
337 {
338 WLog_DBG(TAG, "Unable to get user key for %s: %d", cert->keyName, GetLastError());
339 goto out;
340 }
341
342 dwDataLen = 0;
343 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &dwDataLen, 0))
344 {
345 WLog_DBG(TAG, "Unable to get key param for key %s: %d", cert->keyName, GetLastError());
346 goto out;
347 }
348
349 certBytes = malloc(dwDataLen);
350 if (!certBytes)
351 {
352 WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", dwDataLen,
353 cert->keyName);
354 goto out;
355 }
356
357 if (!CryptGetKeyParam(hKey, KP_CERTIFICATE, certBytes, &dwDataLen, 0))
358 {
359 WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
360 goto out;
361 }
362
363 if (!set_info_certificate(cert, certBytes, dwDataLen, userFilter, domainFilter))
364 goto out;
365
366 if (!add_cert_to_list(pcerts, pcount, cert))
367 goto out;
368
369 ret = TRUE;
370
371out:
372 free(readerName);
373 free(certBytes);
374 if (hKey)
375 CryptDestroyKey(hKey);
376 if (hProvider)
377 CryptReleaseContext(hProvider, 0);
378 if (!ret)
379 smartcardCertInfo_Free(cert);
380 return ret;
381}
382#endif /* _WIN32 */
383
384static BOOL list_provider_keys(WINPR_ATTR_UNUSED const rdpSettings* settings,
385 NCRYPT_PROV_HANDLE provider, LPCWSTR csp, LPCWSTR scope,
386 const char* userFilter, const char* domainFilter,
387 SmartcardCertInfo*** pcerts, size_t* pcount)
388{
389 BOOL ret = FALSE;
390 NCryptKeyName* keyName = NULL;
391 PVOID enumState = NULL;
392 SmartcardCertInfo** cert_list = *pcerts;
393 size_t count = *pcount;
394
395 while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) ==
396 ERROR_SUCCESS)
397 {
398 NCRYPT_KEY_HANDLE phKey = 0;
399 PBYTE certBytes = NULL;
400 DWORD dwFlags = NCRYPT_SILENT_FLAG;
401 DWORD cbOutput = 0;
402 SmartcardCertInfo* cert = NULL;
403 BOOL haveError = TRUE;
404 SECURITY_STATUS status = 0;
405
406 cert = calloc(1, sizeof(SmartcardCertInfo));
407 if (!cert)
408 goto out;
409
410 cert->keyName = ConvertWCharToUtf8Alloc(keyName->pszName, NULL);
411 if (!cert->keyName)
412 goto endofloop;
413
414 WLog_DBG(TAG, "opening key %s", cert->keyName);
415
416 status =
417 NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
418 if (status != ERROR_SUCCESS)
419 {
420 WLog_DBG(TAG,
421 "unable to NCryptOpenKey(dwLegacyKeySpec=0x%" PRIx32 " dwFlags=0x%" PRIx32
422 "), status=%s, skipping",
423 status, keyName->dwLegacyKeySpec, keyName->dwFlags,
424 winpr_NCryptSecurityStatusError(status));
425 goto endofloop;
426 }
427
428 cert->csp = _wcsdup(csp);
429 if (!cert->csp)
430 goto endofloop;
431
432#ifndef _WIN32
433 status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput,
434 dwFlags);
435 if (status != ERROR_SUCCESS)
436 {
437 WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->keyName,
438 winpr_NCryptSecurityStatusError(status));
439 goto endofloop;
440 }
441#endif /* _WIN32 */
442
443 /* ====== retrieve key's reader ====== */
444 cbOutput = 0;
445 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, dwFlags);
446 if (status != ERROR_SUCCESS)
447 {
448 WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", cert->keyName);
449 goto endofloop;
450 }
451
452 cert->reader = calloc(1, cbOutput + 2);
453 if (!cert->reader)
454 {
455 WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->keyName);
456 goto endofloop;
457 }
458
459 status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2,
460 &cbOutput, dwFlags);
461 if (status != ERROR_SUCCESS)
462 {
463 WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->keyName);
464 goto endofloop;
465 }
466
467 /* ====== retrieve key container name ====== */
468 /* When using PKCS11, this will try to return what Windows would use for the key's name */
469 cbOutput = 0;
470 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, NULL, 0, &cbOutput, dwFlags);
471 if (status == ERROR_SUCCESS)
472 {
473 cert->containerName = calloc(1, cbOutput + sizeof(WCHAR));
474 if (!cert->containerName)
475 {
476 WLog_ERR(TAG, "unable to allocate key container name for key %s", cert->keyName);
477 goto endofloop;
478 }
479
480 status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
481 cbOutput, &cbOutput, dwFlags);
482 }
483
484 if (status != ERROR_SUCCESS)
485 {
486 WLog_ERR(TAG, "unable to retrieve key container name for key %s", cert->keyName);
487 goto endofloop;
488 }
489
490 /* ========= retrieve the certificate ===============*/
491 cbOutput = 0;
492 status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 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=0x%lx, skipping",
497 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] = { 0 };
549
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);
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 = NULL;
568 NCRYPT_PROV_HANDLE provider = 0;
569 SECURITY_STATUS status = 0;
570 size_t count = 0;
571 SmartcardCertInfo** cert_list = NULL;
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 + 5, "\\\\.\\%s\\", reader);
585 scope = ConvertUtf8NToWCharAlloc(scopeStr, readerSz + 5, NULL);
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, NULL };
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 = NULL;
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, NULL, 0, &provType, NULL, &cbProvName); ++i)
625 {
626 char providerNameStr[256] = { 0 };
627 LPWSTR szProvName = malloc(cbProvName * sizeof(WCHAR));
628 if (!CryptEnumProvidersW(i, NULL, 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] = { 0 };
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];
711 char* hex = NULL;
712 char* path = NULL;
713
714 winpr_RAND(buffer, sizeof(buffer));
715 hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
716 path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
717 free(hex);
718 return path;
719}
720
721static SmartcardCertInfo* smartcardCertInfo_New(const char* privKeyPEM, const char* certPEM)
722{
723 size_t size = 0;
724
725 WINPR_ASSERT(privKeyPEM);
726 WINPR_ASSERT(certPEM);
727
728 SmartcardCertInfo* cert = calloc(1, sizeof(SmartcardCertInfo));
729 if (!cert)
730 goto fail;
731
732 SmartcardKeyInfo* info = cert->key_info = calloc(1, sizeof(SmartcardKeyInfo));
733 if (!info)
734 goto fail;
735
736 cert->certificate = freerdp_certificate_new_from_pem(certPEM);
737 if (!cert->certificate)
738 {
739 WLog_ERR(TAG, "unable to read smartcard certificate");
740 goto fail;
741 }
742
743 if (!treat_sc_cert(cert))
744 {
745 WLog_ERR(TAG, "unable to treat smartcard certificate");
746 goto fail;
747 }
748
749 cert->reader = ConvertUtf8ToWCharAlloc("FreeRDP Emulator", NULL);
750 if (!cert->reader)
751 goto fail;
752
753 cert->containerName = ConvertUtf8ToWCharAlloc("Private Key 00", NULL);
754 if (!cert->containerName)
755 goto fail;
756
757 /* compute PKINIT args FILE:<cert file>,<key file>
758 *
759 * We need files for PKINIT to read, so write the certificate to some
760 * temporary location and use that.
761 */
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)))
765 goto fail;
766
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)))
770 goto fail;
771
772 int res = winpr_asprintf(&cert->pkinitArgs, &size, "FILE:%s,%s", info->certPath, info->keyPath);
773 if (res <= 0)
774 goto fail;
775
776 return cert;
777fail:
778 smartcardCertInfo_Free(cert);
779 return NULL;
780}
781
782static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
783 size_t* retCount)
784{
785 BOOL rc = FALSE;
786 SmartcardCertInfo** cert_list = NULL;
787
788 WINPR_ASSERT(settings);
789 WINPR_ASSERT(scCerts);
790 WINPR_ASSERT(retCount);
791
792 const char* privKeyPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
793 const char* certPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
794 if (!privKeyPEM)
795 {
796 WLog_ERR(TAG, "Invalid smartcard private key PEM, aborting");
797 goto out_error;
798 }
799 if (!certPEM)
800 {
801 WLog_ERR(TAG, "Invalid smartcard certificate PEM, aborting");
802 goto out_error;
803 }
804
805 cert_list = (SmartcardCertInfo**)calloc(1, sizeof(SmartcardCertInfo*));
806 if (!cert_list)
807 goto out_error;
808
809 {
810 SmartcardCertInfo* cert = smartcardCertInfo_New(privKeyPEM, certPEM);
811 if (!cert)
812 goto out_error;
813 cert_list[0] = cert;
814 }
815
816 rc = TRUE;
817 *scCerts = cert_list;
818 *retCount = 1;
819
820out_error:
821 if (!rc)
822 smartcardCertList_Free(cert_list, 1);
823 return rc;
824}
825
826BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
827 size_t* retCount, BOOL gateway)
828{
829 BOOL ret = 0;
830 LPWSTR csp = NULL;
831 const char* ReaderName = freerdp_settings_get_string(settings, FreeRDP_ReaderName);
832 const char* CspName = freerdp_settings_get_string(settings, FreeRDP_CspName);
833 const char* Username = NULL;
834 const char* Domain = NULL;
835
836 if (gateway)
837 {
838 Username = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
839 Domain = freerdp_settings_get_string(settings, FreeRDP_GatewayDomain);
840 }
841 else
842 {
843 Username = freerdp_settings_get_string(settings, FreeRDP_Username);
844 Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
845 }
846
847 WINPR_ASSERT(settings);
848 WINPR_ASSERT(scCerts);
849 WINPR_ASSERT(retCount);
850
851 if (Domain && !strlen(Domain))
852 Domain = NULL;
853
854 if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation))
855 return smartcard_sw_enumerateCerts(settings, scCerts, retCount);
856
857 if (CspName && (!(csp = ConvertUtf8ToWCharAlloc(CspName, NULL))))
858 {
859 WLog_ERR(TAG, "error while converting CSP to WCHAR");
860 return FALSE;
861 }
862
863 ret =
864 smartcard_hw_enumerateCerts(settings, csp, ReaderName, Username, Domain, scCerts, retCount);
865 free(csp);
866 return ret;
867}
868
869static BOOL set_settings_from_smartcard(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
870 const char* value)
871{
872 WINPR_ASSERT(settings);
873
874 if (!freerdp_settings_get_string(settings, id) && value)
875 if (!freerdp_settings_set_string(settings, id, value))
876 return FALSE;
877
878 return TRUE;
879}
880
881BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert, BOOL gateway)
882{
883 WINPR_ASSERT(context);
884
885 const freerdp* instance = context->instance;
886 rdpSettings* settings = context->settings;
887 SmartcardCertInfo** cert_list = NULL;
888 size_t count = 0;
889
890 WINPR_ASSERT(instance);
891 WINPR_ASSERT(settings);
892
893 if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
894 return FALSE;
895
896 if (count < 1)
897 {
898 WLog_ERR(TAG, "no suitable smartcard certificates were found");
899 return FALSE;
900 }
901
902 if (count > UINT32_MAX)
903 {
904 WLog_ERR(TAG, "smartcard certificate count %" PRIuz " exceeds UINT32_MAX", count);
905 return FALSE;
906 }
907
908 if (count > 1)
909 {
910 DWORD index = 0;
911
912 if (!instance->ChooseSmartcard ||
913 !instance->ChooseSmartcard(context->instance, cert_list, (UINT32)count, &index,
914 gateway))
915 {
916 WLog_ERR(TAG, "more than one suitable smartcard certificate was found");
917 smartcardCertList_Free(cert_list, count);
918 return FALSE;
919 }
920 *cert = cert_list[index];
921
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]);
926 }
927 else
928 *cert = cert_list[0];
929
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;
933
934 free((void*)cert_list);
935
936 if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
937 !set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
938 {
939 WLog_ERR(TAG, "unable to set settings from smartcard!");
940 smartcardCertInfo_Free(*cert);
941 return FALSE;
942 }
943
944 return TRUE;
945}
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 key name descriptor
a provider name descriptor