FreeRDP
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 
39 struct SmartcardKeyInfo_st
40 {
41  char* certPath;
42  char* keyPath;
43 };
44 
45 static 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 
79 static 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 
90 void 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 
111 void 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(cert_list);
123 }
124 
125 static 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 =
143  realloc(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 
158 static 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 
199 static 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
250 static 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
266 static 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 
371 out:
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 
384 static BOOL list_provider_keys(const rdpSettings* settings, NCRYPT_PROV_HANDLE provider,
385  LPCWSTR csp, LPCWSTR scope, const char* userFilter,
386  const char* domainFilter, SmartcardCertInfo*** pcerts,
387  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;
545 out:
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 
561 static 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 
701 out:
702  if (!ret)
703  smartcardCertList_Free(cert_list, count);
704  free(scope);
705  return ret;
706 }
707 
708 static 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 
721 static 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;
777 fail:
778  smartcardCertInfo_Free(cert);
779  return NULL;
780 }
781 
782 static 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 = 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 
820 out_error:
821  if (!rc)
822  smartcardCertList_Free(cert_list, 1);
823  return rc;
824 }
825 
826 BOOL 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 
869 static 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 
881 BOOL 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(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