Coverage Report

Created: 2024-05-20 06:11

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