Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/core/smartcardlogon.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Logging in with smartcards
4
 *
5
 * Copyright 2022 David Fort <contact@hardening-consulting.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
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
40
struct SmartcardKeyInfo_st
41
{
42
  char* certPath;
43
  char* keyPath;
44
};
45
46
static void delete_file(char* path)
47
0
{
48
0
  if (!path)
49
0
    return;
50
51
  /* Overwrite data in files before deletion */
52
0
  {
53
0
    FILE* fp = winpr_fopen(path, "r+");
54
0
    if (fp)
55
0
    {
56
0
      const char buffer[8192] = WINPR_C_ARRAY_INIT;
57
0
      INT64 size = 0;
58
0
      int rs = _fseeki64(fp, 0, SEEK_END);
59
0
      if (rs == 0)
60
0
        size = _ftelli64(fp);
61
0
      (void)_fseeki64(fp, 0, SEEK_SET);
62
63
0
      for (INT64 x = 0; x < size; x += sizeof(buffer))
64
0
      {
65
0
        const size_t dnmemb = (size_t)(size - x);
66
0
        const size_t nmemb = MIN(sizeof(buffer), dnmemb);
67
0
        const size_t count = fwrite(buffer, nmemb, 1, fp);
68
0
        if (count != 1)
69
0
          break;
70
0
      }
71
72
0
      (void)fclose(fp);
73
0
    }
74
0
  }
75
76
0
  winpr_DeleteFile(path);
77
0
  free(path);
78
0
}
79
80
static void smartcardKeyInfo_Free(SmartcardKeyInfo* key_info)
81
0
{
82
0
  if (!key_info)
83
0
    return;
84
85
0
  delete_file(key_info->certPath);
86
0
  delete_file(key_info->keyPath);
87
88
0
  free(key_info);
89
0
}
90
91
void smartcardCertInfo_Free(SmartcardCertInfo* scCert)
92
14.4k
{
93
14.4k
  if (!scCert)
94
14.4k
    return;
95
96
0
  free(scCert->csp);
97
0
  free(scCert->reader);
98
0
  freerdp_certificate_free(scCert->certificate);
99
0
  free(scCert->pkinitArgs);
100
0
  free(scCert->keyName);
101
0
  free(scCert->containerName);
102
0
  free(scCert->upn);
103
0
  free(scCert->userHint);
104
0
  free(scCert->domainHint);
105
0
  free(scCert->subject);
106
0
  free(scCert->issuer);
107
0
  smartcardKeyInfo_Free(scCert->key_info);
108
109
0
  free(scCert);
110
0
}
111
112
void smartcardCertList_Free(SmartcardCertInfo** cert_list, size_t count)
113
0
{
114
0
  if (!cert_list)
115
0
    return;
116
117
0
  for (size_t i = 0; i < count; i++)
118
0
  {
119
0
    SmartcardCertInfo* cert = cert_list[i];
120
0
    smartcardCertInfo_Free(cert);
121
0
  }
122
123
0
  free((void*)cert_list);
124
0
}
125
126
static BOOL add_cert_to_list(SmartcardCertInfo*** certInfoList, size_t* count,
127
                             SmartcardCertInfo* certInfo)
128
0
{
129
0
  size_t curCount = *count;
130
0
  SmartcardCertInfo** curInfoList = *certInfoList;
131
132
  /* Check if the certificate is already in the list */
133
0
  for (size_t i = 0; i < curCount; ++i)
134
0
  {
135
0
    if (_wcscmp(curInfoList[i]->containerName, certInfo->containerName) == 0)
136
0
    {
137
0
      smartcardCertInfo_Free(certInfo);
138
0
      return TRUE;
139
0
    }
140
0
  }
141
142
0
  {
143
0
    SmartcardCertInfo** tmpInfoList = (SmartcardCertInfo**)realloc(
144
0
        (void*)curInfoList, sizeof(SmartcardCertInfo*) * (curCount + 1));
145
0
    if (!tmpInfoList)
146
0
    {
147
0
      WLog_ERR(TAG, "unable to reallocate certs");
148
0
      return FALSE;
149
0
    }
150
0
    curInfoList = tmpInfoList;
151
0
  }
152
153
0
  curInfoList[curCount++] = certInfo;
154
0
  *certInfoList = curInfoList;
155
0
  *count = curCount;
156
0
  return TRUE;
157
0
}
158
159
static BOOL treat_sc_cert(SmartcardCertInfo* scCert)
160
0
{
161
0
  WINPR_ASSERT(scCert);
162
163
0
  scCert->upn = freerdp_certificate_get_upn(scCert->certificate);
164
0
  if (!scCert->upn)
165
0
  {
166
0
    WLog_DBG(TAG, "%s has no UPN, trying emailAddress", scCert->keyName);
167
0
    scCert->upn = freerdp_certificate_get_email(scCert->certificate);
168
0
  }
169
170
0
  if (scCert->upn)
171
0
  {
172
0
    size_t userLen = 0;
173
0
    const char* atPos = strchr(scCert->upn, '@');
174
175
0
    if (!atPos)
176
0
    {
177
0
      WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->keyName);
178
0
      return FALSE;
179
0
    }
180
181
0
    userLen = (size_t)(atPos - scCert->upn);
182
0
    scCert->userHint = malloc(userLen + 1);
183
0
    scCert->domainHint = _strdup(atPos + 1);
184
185
0
    if (!scCert->userHint || !scCert->domainHint)
186
0
    {
187
0
      WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s", scCert->keyName);
188
0
      return FALSE;
189
0
    }
190
191
0
    memcpy(scCert->userHint, scCert->upn, userLen);
192
0
    scCert->userHint[userLen] = 0;
193
0
  }
194
195
0
  scCert->subject = freerdp_certificate_get_subject(scCert->certificate);
196
0
  scCert->issuer = freerdp_certificate_get_issuer(scCert->certificate);
197
0
  return TRUE;
198
0
}
199
200
static BOOL set_info_certificate(SmartcardCertInfo* cert, BYTE* certBytes, DWORD cbCertBytes,
201
                                 const char* userFilter, const char* domainFilter)
202
0
{
203
0
  if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbCertBytes, cert->sha1Hash,
204
0
                    sizeof(cert->sha1Hash)))
205
0
  {
206
0
    WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", cert->keyName);
207
0
    return FALSE;
208
0
  }
209
210
0
  cert->certificate = freerdp_certificate_new_from_der(certBytes, cbCertBytes);
211
0
  if (!cert->certificate)
212
0
  {
213
0
    WLog_ERR(TAG, "unable to parse X509 certificate for key %s", cert->keyName);
214
0
    return FALSE;
215
0
  }
216
217
0
  if (!freerdp_certificate_check_eku(cert->certificate, NID_ms_smartcard_login))
218
0
  {
219
0
    WLog_DBG(TAG, "discarding certificate without Smartcard Login EKU for key %s",
220
0
             cert->keyName);
221
0
    return FALSE;
222
0
  }
223
224
0
  if (!treat_sc_cert(cert))
225
0
  {
226
0
    WLog_DBG(TAG, "error treating cert");
227
0
    return FALSE;
228
0
  }
229
230
0
  if (userFilter && (!cert->upn || (strcmp(cert->upn, userFilter) != 0)))
231
0
  {
232
0
    if (cert->userHint && strcmp(cert->userHint, userFilter) != 0)
233
0
    {
234
0
      WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint,
235
0
               cert->domainHint);
236
0
      return FALSE;
237
0
    }
238
0
  }
239
240
0
  if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0)
241
0
  {
242
0
    WLog_DBG(TAG, "discarding non matching cert by domain(%s) %s@%s", domainFilter,
243
0
             cert->userHint, cert->domainHint);
244
0
    return FALSE;
245
0
  }
246
247
0
  return TRUE;
248
0
}
249
250
#ifndef _WIN32
251
static BOOL build_pkinit_args(NCRYPT_PROV_HANDLE provider, SmartcardCertInfo* scCert)
252
0
{
253
  /* pkinit args only under windows
254
   *    PKCS11:module_name=opensc-pkcs11.so
255
   */
256
0
  const char* pkModule = winpr_NCryptGetModulePath(provider);
257
0
  size_t size = 0;
258
259
0
  if (winpr_asprintf(&scCert->pkinitArgs, &size, "PKCS11:module_name=%s:slotid=%" PRIu16,
260
0
                     pkModule, (UINT16)scCert->slotId) <= 0)
261
0
    return FALSE;
262
0
  return TRUE;
263
0
}
264
#endif /* _WIN32 */
265
266
#ifdef _WIN32
267
static 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
372
out:
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
385
static 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
0
{
390
0
  BOOL ret = FALSE;
391
0
  NCryptKeyName* keyName = NULL;
392
0
  PVOID enumState = NULL;
393
0
  SmartcardCertInfo** cert_list = *pcerts;
394
0
  size_t count = *pcount;
395
396
0
  while (NCryptEnumKeys(provider, scope, &keyName, &enumState, NCRYPT_SILENT_FLAG) ==
397
0
         ERROR_SUCCESS)
398
0
  {
399
0
    NCRYPT_KEY_HANDLE phKey = 0;
400
0
    PBYTE certBytes = NULL;
401
0
    DWORD dwFlags = NCRYPT_SILENT_FLAG;
402
0
    DWORD cbOutput = 0;
403
0
    SmartcardCertInfo* cert = NULL;
404
0
    BOOL haveError = TRUE;
405
0
    SECURITY_STATUS status = 0;
406
407
0
    cert = calloc(1, sizeof(SmartcardCertInfo));
408
0
    if (!cert)
409
0
      goto out;
410
411
0
    cert->keyName = ConvertWCharToUtf8Alloc(keyName->pszName, NULL);
412
0
    if (!cert->keyName)
413
0
      goto endofloop;
414
415
0
    WLog_DBG(TAG, "opening key %s", cert->keyName);
416
417
0
    status =
418
0
        NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, dwFlags);
419
0
    if (status != ERROR_SUCCESS)
420
0
    {
421
0
      WLog_DBG(TAG,
422
0
               "unable to NCryptOpenKey(dwLegacyKeySpec=0x%08" PRIx32 " dwFlags=0x%08" PRIx32
423
0
               "), status=%s, skipping",
424
0
               keyName->dwLegacyKeySpec, keyName->dwFlags,
425
0
               winpr_NCryptSecurityStatusError(status));
426
0
      goto endofloop;
427
0
    }
428
429
0
    cert->csp = _wcsdup(csp);
430
0
    if (!cert->csp)
431
0
      goto endofloop;
432
433
0
#ifndef _WIN32
434
0
    status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&cert->slotId, 4, &cbOutput,
435
0
                               dwFlags);
436
0
    if (status != ERROR_SUCCESS)
437
0
    {
438
0
      WLog_ERR(TAG, "unable to retrieve slotId for key %s, status=%s", cert->keyName,
439
0
               winpr_NCryptSecurityStatusError(status));
440
0
      goto endofloop;
441
0
    }
442
0
#endif /* _WIN32 */
443
444
    /* ====== retrieve key's reader ====== */
445
0
    cbOutput = 0;
446
0
    status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, dwFlags);
447
0
    if (status != ERROR_SUCCESS)
448
0
    {
449
0
      WLog_DBG(TAG, "unable to retrieve reader's name length for key %s", cert->keyName);
450
0
      goto endofloop;
451
0
    }
452
453
0
    cert->reader = calloc(1, cbOutput + 2);
454
0
    if (!cert->reader)
455
0
    {
456
0
      WLog_ERR(TAG, "unable to allocate reader's name for key %s", cert->keyName);
457
0
      goto endofloop;
458
0
    }
459
460
0
    status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)cert->reader, cbOutput + 2,
461
0
                               &cbOutput, dwFlags);
462
0
    if (status != ERROR_SUCCESS)
463
0
    {
464
0
      WLog_ERR(TAG, "unable to retrieve reader's name for key %s", cert->keyName);
465
0
      goto endofloop;
466
0
    }
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
0
    cbOutput = 0;
471
0
    status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, NULL, 0, &cbOutput, dwFlags);
472
0
    if (status == ERROR_SUCCESS)
473
0
    {
474
0
      cert->containerName = calloc(1, cbOutput + sizeof(WCHAR));
475
0
      if (!cert->containerName)
476
0
      {
477
0
        WLog_ERR(TAG, "unable to allocate key container name for key %s", cert->keyName);
478
0
        goto endofloop;
479
0
      }
480
481
0
      status = NCryptGetProperty(phKey, NCRYPT_NAME_PROPERTY, (BYTE*)cert->containerName,
482
0
                                 cbOutput, &cbOutput, dwFlags);
483
0
    }
484
485
0
    if (status != ERROR_SUCCESS)
486
0
    {
487
0
      WLog_ERR(TAG, "unable to retrieve key container name for key %s", cert->keyName);
488
0
      goto endofloop;
489
0
    }
490
491
    /* ========= retrieve the certificate ===============*/
492
0
    cbOutput = 0;
493
0
    status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 0, &cbOutput, dwFlags);
494
0
    if (status != ERROR_SUCCESS)
495
0
    {
496
      /* can happen that key don't have certificates */
497
0
      WLog_DBG(TAG, "unable to retrieve certificate property len, status=%s, skipping",
498
0
               winpr_NCryptSecurityStatusError(status));
499
0
      goto endofloop;
500
0
    }
501
502
0
    certBytes = calloc(1, cbOutput);
503
0
    if (!certBytes)
504
0
    {
505
0
      WLog_ERR(TAG, "unable to allocate %" PRIu32 " certBytes for key %s", cbOutput,
506
0
               cert->keyName);
507
0
      goto endofloop;
508
0
    }
509
510
0
    status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput,
511
0
                               &cbOutput, dwFlags);
512
0
    if (status != ERROR_SUCCESS)
513
0
    {
514
0
      WLog_ERR(TAG, "unable to retrieve certificate for key %s", cert->keyName);
515
0
      goto endofloop;
516
0
    }
517
518
0
    if (!set_info_certificate(cert, certBytes, cbOutput, userFilter, domainFilter))
519
0
      goto endofloop;
520
521
0
#ifndef _WIN32
522
0
    if (!build_pkinit_args(provider, cert))
523
0
    {
524
0
      WLog_ERR(TAG, "error build pkinit args");
525
0
      goto endofloop;
526
0
    }
527
0
#endif
528
0
    haveError = FALSE;
529
530
0
  endofloop:
531
0
    free(certBytes);
532
0
    NCryptFreeBuffer(keyName);
533
0
    if (phKey)
534
0
      NCryptFreeObject((NCRYPT_HANDLE)phKey);
535
536
0
    if (haveError)
537
0
      smartcardCertInfo_Free(cert);
538
0
    else
539
0
    {
540
0
      if (!add_cert_to_list(&cert_list, &count, cert))
541
0
        goto out;
542
0
    }
543
0
  }
544
545
0
  ret = TRUE;
546
0
out:
547
0
  if (count == 0)
548
0
  {
549
0
    char cspa[128] = WINPR_C_ARRAY_INIT;
550
551
0
    (void)ConvertWCharToUtf8(csp, cspa, sizeof(cspa));
552
0
    char scopea[128] = WINPR_C_ARRAY_INIT;
553
0
    (void)ConvertWCharToUtf8(scope, scopea, sizeof(scopea));
554
0
    WLog_WARN(TAG, "%s [%s] no certificates found", cspa, scopea);
555
0
  }
556
0
  *pcount = count;
557
0
  *pcerts = cert_list;
558
0
  NCryptFreeBuffer(enumState);
559
0
  return ret;
560
0
}
561
562
static 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
0
{
567
0
  BOOL ret = FALSE;
568
0
  LPWSTR scope = NULL;
569
0
  NCRYPT_PROV_HANDLE provider = 0;
570
0
  SECURITY_STATUS status = 0;
571
0
  size_t count = 0;
572
0
  SmartcardCertInfo** cert_list = NULL;
573
0
  const char* Pkcs11Module = freerdp_settings_get_string(settings, FreeRDP_Pkcs11Module);
574
575
0
  WINPR_ASSERT(scCerts);
576
0
  WINPR_ASSERT(retCount);
577
578
0
  if (reader)
579
0
  {
580
0
    size_t readerSz = strlen(reader);
581
0
    char* scopeStr = malloc(4 + readerSz + 1 + 1);
582
0
    if (!scopeStr)
583
0
      goto out;
584
585
0
    (void)_snprintf(scopeStr, readerSz + 6, "\\\\.\\%s\\", reader);
586
0
    scope = ConvertUtf8NToWCharAlloc(scopeStr, readerSz + 6, NULL);
587
0
    free(scopeStr);
588
589
0
    if (!scope)
590
0
      goto out;
591
0
  }
592
593
0
  if (Pkcs11Module)
594
0
  {
595
    /* load a unique CSP by pkcs11 module path */
596
0
    LPCSTR paths[] = { Pkcs11Module, NULL };
597
598
0
    if (!csp)
599
0
      csp = MS_SCARD_PROV;
600
601
0
    status = winpr_NCryptOpenStorageProviderEx(&provider, csp, 0, paths);
602
0
    if (status != ERROR_SUCCESS)
603
0
    {
604
0
      WLog_ERR(TAG, "unable to open provider given by pkcs11 module");
605
0
      goto out;
606
0
    }
607
608
0
    status = list_provider_keys(settings, provider, csp, scope, userFilter, domainFilter,
609
0
                                &cert_list, &count);
610
0
    NCryptFreeObject((NCRYPT_HANDLE)provider);
611
0
    if (!status)
612
0
    {
613
0
      WLog_ERR(TAG, "error listing keys from CSP loaded from %s", Pkcs11Module);
614
0
      goto out;
615
0
    }
616
0
  }
617
0
  else
618
0
  {
619
0
    NCryptProviderName* names = NULL;
620
0
    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] = WINPR_C_ARRAY_INIT;
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
0
    status = NCryptEnumStorageProviders(&nproviders, &names, NCRYPT_SILENT_FLAG);
659
0
    if (status != ERROR_SUCCESS)
660
0
    {
661
0
      WLog_ERR(TAG, "error listing providers");
662
0
      goto out;
663
0
    }
664
665
0
    for (DWORD i = 0; i < nproviders; i++)
666
0
    {
667
0
      char providerNameStr[256] = WINPR_C_ARRAY_INIT;
668
0
      const NCryptProviderName* name = &names[i];
669
670
0
      if (ConvertWCharToUtf8(name->pszName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0)
671
0
      {
672
0
        (void)_snprintf(providerNameStr, sizeof(providerNameStr), "<unknown>");
673
0
        WLog_ERR(TAG, "unable to convert provider name to char*, will show it as '%s'",
674
0
                 providerNameStr);
675
0
      }
676
677
0
      WLog_DBG(TAG, "exploring CSP '%s'", providerNameStr);
678
0
      if (csp && _wcscmp(name->pszName, csp) != 0)
679
0
      {
680
0
        WLog_DBG(TAG, "CSP '%s' filtered out", providerNameStr);
681
0
        continue;
682
0
      }
683
684
0
      status = NCryptOpenStorageProvider(&provider, name->pszName, 0);
685
0
      if (status != ERROR_SUCCESS)
686
0
        continue;
687
688
0
      if (!list_provider_keys(settings, provider, name->pszName, scope, userFilter,
689
0
                              domainFilter, &cert_list, &count))
690
0
        WLog_INFO(TAG, "error when retrieving keys in CSP '%s'", providerNameStr);
691
692
0
      NCryptFreeObject((NCRYPT_HANDLE)provider);
693
0
    }
694
695
0
    NCryptFreeBuffer(names);
696
0
  }
697
698
0
  *scCerts = cert_list;
699
0
  *retCount = count;
700
0
  ret = TRUE;
701
702
0
out:
703
0
  if (!ret)
704
0
    smartcardCertList_Free(cert_list, count);
705
0
  free(scope);
706
0
  return ret;
707
0
}
708
709
static char* create_temporary_file(void)
710
0
{
711
0
  BYTE buffer[32];
712
0
  char* hex = NULL;
713
0
  char* path = NULL;
714
715
0
  winpr_RAND(buffer, sizeof(buffer));
716
0
  hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
717
0
  path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
718
0
  free(hex);
719
0
  return path;
720
0
}
721
722
static SmartcardCertInfo* smartcardCertInfo_New(const char* privKeyPEM, const char* certPEM)
723
0
{
724
0
  size_t size = 0;
725
726
0
  WINPR_ASSERT(privKeyPEM);
727
0
  WINPR_ASSERT(certPEM);
728
729
0
  SmartcardCertInfo* cert = calloc(1, sizeof(SmartcardCertInfo));
730
0
  if (!cert)
731
0
    goto fail;
732
733
0
  {
734
0
    SmartcardKeyInfo* info = cert->key_info = calloc(1, sizeof(SmartcardKeyInfo));
735
0
    if (!info)
736
0
      goto fail;
737
738
0
    cert->certificate = freerdp_certificate_new_from_pem(certPEM);
739
0
    if (!cert->certificate)
740
0
    {
741
0
      WLog_ERR(TAG, "unable to read smartcard certificate");
742
0
      goto fail;
743
0
    }
744
745
0
    if (!treat_sc_cert(cert))
746
0
    {
747
0
      WLog_ERR(TAG, "unable to treat smartcard certificate");
748
0
      goto fail;
749
0
    }
750
751
0
    {
752
0
      char* str = NULL;
753
0
      size_t len = 0;
754
0
      (void)winpr_asprintf(&str, &len, "%s Emulator", freerdp_getApplicationDetailsString());
755
0
      if (str)
756
0
        cert->reader = ConvertUtf8NToWCharAlloc(str, len, NULL);
757
0
      free(str);
758
0
    }
759
0
    if (!cert->reader)
760
0
      goto fail;
761
762
0
    cert->containerName = ConvertUtf8ToWCharAlloc("Private Key 00", NULL);
763
0
    if (!cert->containerName)
764
0
      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
0
    info->keyPath = create_temporary_file();
772
0
    WLog_DBG(TAG, "writing PKINIT key to %s", info->keyPath);
773
0
    if (!crypto_write_pem(info->keyPath, privKeyPEM, strlen(privKeyPEM)))
774
0
      goto fail;
775
776
0
    info->certPath = create_temporary_file();
777
0
    WLog_DBG(TAG, "writing PKINIT cert to %s", info->certPath);
778
0
    if (!crypto_write_pem(info->certPath, certPEM, strlen(certPEM)))
779
0
      goto fail;
780
781
0
    {
782
0
      const int res = winpr_asprintf(&cert->pkinitArgs, &size, "FILE:%s,%s", info->certPath,
783
0
                                     info->keyPath);
784
0
      if (res <= 0)
785
0
        goto fail;
786
0
    }
787
0
  }
788
789
0
  return cert;
790
0
fail:
791
0
  smartcardCertInfo_Free(cert);
792
0
  return NULL;
793
0
}
794
795
static BOOL smartcard_sw_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
796
                                        size_t* retCount)
797
0
{
798
0
  BOOL rc = FALSE;
799
0
  SmartcardCertInfo** cert_list = NULL;
800
801
0
  WINPR_ASSERT(settings);
802
0
  WINPR_ASSERT(scCerts);
803
0
  WINPR_ASSERT(retCount);
804
805
0
  const char* privKeyPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
806
0
  const char* certPEM = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
807
0
  if (!privKeyPEM)
808
0
  {
809
0
    WLog_ERR(TAG, "Invalid smartcard private key PEM, aborting");
810
0
    goto out_error;
811
0
  }
812
0
  if (!certPEM)
813
0
  {
814
0
    WLog_ERR(TAG, "Invalid smartcard certificate PEM, aborting");
815
0
    goto out_error;
816
0
  }
817
818
0
  cert_list = (SmartcardCertInfo**)calloc(1, sizeof(SmartcardCertInfo*));
819
0
  if (!cert_list)
820
0
    goto out_error;
821
822
0
  {
823
0
    SmartcardCertInfo* cert = smartcardCertInfo_New(privKeyPEM, certPEM);
824
0
    if (!cert)
825
0
      goto out_error;
826
0
    cert_list[0] = cert;
827
0
  }
828
829
0
  rc = TRUE;
830
0
  *scCerts = cert_list;
831
0
  *retCount = 1;
832
833
0
out_error:
834
0
  if (!rc)
835
0
    smartcardCertList_Free(cert_list, 1);
836
0
  return rc;
837
0
}
838
839
BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
840
                              size_t* retCount, BOOL gateway)
841
0
{
842
0
  BOOL ret = 0;
843
0
  LPWSTR csp = NULL;
844
0
  const char* ReaderName = freerdp_settings_get_string(settings, FreeRDP_ReaderName);
845
0
  const char* CspName = freerdp_settings_get_string(settings, FreeRDP_CspName);
846
0
  const char* Username = NULL;
847
0
  const char* Domain = NULL;
848
849
0
  if (gateway)
850
0
  {
851
0
    Username = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
852
0
    Domain = freerdp_settings_get_string(settings, FreeRDP_GatewayDomain);
853
0
  }
854
0
  else
855
0
  {
856
0
    Username = freerdp_settings_get_string(settings, FreeRDP_Username);
857
0
    Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
858
0
  }
859
860
0
  WINPR_ASSERT(settings);
861
0
  WINPR_ASSERT(scCerts);
862
0
  WINPR_ASSERT(retCount);
863
864
0
  if (Domain && !strlen(Domain))
865
0
    Domain = NULL;
866
867
0
  if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation))
868
0
    return smartcard_sw_enumerateCerts(settings, scCerts, retCount);
869
870
0
  if (CspName && (!(csp = ConvertUtf8ToWCharAlloc(CspName, NULL))))
871
0
  {
872
0
    WLog_ERR(TAG, "error while converting CSP to WCHAR");
873
0
    return FALSE;
874
0
  }
875
876
0
  ret =
877
0
      smartcard_hw_enumerateCerts(settings, csp, ReaderName, Username, Domain, scCerts, retCount);
878
0
  free(csp);
879
0
  return ret;
880
0
}
881
882
static BOOL set_settings_from_smartcard(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
883
                                        const char* value)
884
0
{
885
0
  WINPR_ASSERT(settings);
886
887
0
  if (!freerdp_settings_get_string(settings, id) && value)
888
0
    if (!freerdp_settings_set_string(settings, id, value))
889
0
      return FALSE;
890
891
0
  return TRUE;
892
0
}
893
894
BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert, BOOL gateway)
895
0
{
896
0
  WINPR_ASSERT(context);
897
898
0
  const freerdp* instance = context->instance;
899
0
  rdpSettings* settings = context->settings;
900
0
  SmartcardCertInfo** cert_list = NULL;
901
0
  size_t count = 0;
902
903
0
  WINPR_ASSERT(instance);
904
0
  WINPR_ASSERT(settings);
905
906
0
  if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
907
0
    return FALSE;
908
909
0
  if (count < 1)
910
0
  {
911
0
    WLog_ERR(TAG, "no suitable smartcard certificates were found");
912
0
    return FALSE;
913
0
  }
914
915
0
  if (count > UINT32_MAX)
916
0
  {
917
0
    WLog_ERR(TAG, "smartcard certificate count %" PRIuz " exceeds UINT32_MAX", count);
918
0
    return FALSE;
919
0
  }
920
921
0
  if (count > 1)
922
0
  {
923
0
    DWORD index = 0;
924
925
0
    if (!instance->ChooseSmartcard ||
926
0
        !instance->ChooseSmartcard(context->instance, cert_list, (UINT32)count, &index,
927
0
                                   gateway))
928
0
    {
929
0
      WLog_ERR(TAG, "more than one suitable smartcard certificate was found");
930
0
      smartcardCertList_Free(cert_list, count);
931
0
      return FALSE;
932
0
    }
933
0
    *cert = cert_list[index];
934
935
0
    for (DWORD i = 0; i < index; i++)
936
0
      smartcardCertInfo_Free(cert_list[i]);
937
0
    for (DWORD i = index + 1; i < count; i++)
938
0
      smartcardCertInfo_Free(cert_list[i]);
939
0
  }
940
0
  else
941
0
    *cert = cert_list[0];
942
943
0
  FreeRDP_Settings_Keys_String username_setting =
944
0
      gateway ? FreeRDP_GatewayUsername : FreeRDP_Username;
945
0
  FreeRDP_Settings_Keys_String domain_setting = gateway ? FreeRDP_GatewayDomain : FreeRDP_Domain;
946
947
0
  free((void*)cert_list);
948
949
0
  if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
950
0
      !set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
951
0
  {
952
0
    WLog_ERR(TAG, "unable to set settings from smartcard!");
953
0
    smartcardCertInfo_Free(*cert);
954
0
    return FALSE;
955
0
  }
956
957
0
  return TRUE;
958
0
}