Coverage Report

Created: 2024-09-08 06:16

/src/FreeRDP/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * NCrypt pkcs11 provider
4
 *
5
 * Copyright 2021 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
20
#include <stdlib.h>
21
22
#include <winpr/library.h>
23
#include <winpr/assert.h>
24
#include <winpr/spec.h>
25
#include <winpr/smartcard.h>
26
#include <winpr/asn1.h>
27
28
#include "../log.h"
29
#include "ncrypt.h"
30
31
/* https://github.com/latchset/pkcs11-headers/blob/main/public-domain/3.1/pkcs11.h */
32
#include "pkcs11-headers/pkcs11.h"
33
34
#define TAG WINPR_TAG("ncryptp11")
35
36
0
#define MAX_SLOTS 64
37
#define MAX_KEYS 64
38
#define MAX_KEYS_PER_SLOT 64
39
40
/** @brief ncrypt provider handle */
41
typedef struct
42
{
43
  NCryptBaseProvider baseProvider;
44
45
  HANDLE library;
46
  CK_FUNCTION_LIST_PTR p11;
47
  char* modulePath;
48
} NCryptP11ProviderHandle;
49
50
/** @brief a handle returned by NCryptOpenKey */
51
typedef struct
52
{
53
  NCryptBaseHandle base;
54
  NCryptP11ProviderHandle* provider;
55
  CK_SLOT_ID slotId;
56
  CK_BYTE keyCertId[64];
57
  CK_ULONG keyCertIdLen;
58
} NCryptP11KeyHandle;
59
60
typedef struct
61
{
62
  CK_SLOT_ID slotId;
63
  CK_SLOT_INFO slotInfo;
64
  CK_KEY_TYPE keyType;
65
  CK_CHAR keyLabel[256];
66
  CK_ULONG idLen;
67
  CK_BYTE id[64];
68
} NCryptKeyEnum;
69
70
typedef struct
71
{
72
  CK_ULONG nslots;
73
  CK_SLOT_ID slots[MAX_SLOTS];
74
  CK_ULONG nKeys;
75
  NCryptKeyEnum keys[MAX_KEYS];
76
  CK_ULONG keyIndex;
77
} P11EnumKeysState;
78
79
typedef struct
80
{
81
  const char* label;
82
  BYTE tag[3];
83
} piv_cert_tags_t;
84
static const piv_cert_tags_t piv_cert_tags[] = {
85
  { "Certificate for PIV Authentication", "\x5F\xC1\x05" },
86
  { "Certificate for Digital Signature", "\x5F\xC1\x0A" },
87
  { "Certificate for Key Management", "\x5F\xC1\x0B" },
88
  { "Certificate for Card Authentication", "\x5F\xC1\x01" },
89
};
90
91
static const BYTE APDU_PIV_SELECT_AID[] = { 0x00, 0xA4, 0x04, 0x00, 0x09, 0xA0, 0x00, 0x00,
92
                                          0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00 };
93
static const BYTE APDU_PIV_GET_CHUID[] = { 0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5C,
94
                                         0x03, 0x5F, 0xC1, 0x02, 0x00 };
95
0
#define PIV_CONTAINER_NAME_LEN 36
96
97
static CK_OBJECT_CLASS object_class_public_key = CKO_PUBLIC_KEY;
98
static CK_BBOOL object_verify = CK_TRUE;
99
static CK_KEY_TYPE object_ktype_rsa = CKK_RSA;
100
101
static CK_ATTRIBUTE public_key_filter[] = {
102
  { CKA_CLASS, &object_class_public_key, sizeof(object_class_public_key) },
103
  { CKA_VERIFY, &object_verify, sizeof(object_verify) },
104
  { CKA_KEY_TYPE, &object_ktype_rsa, sizeof(object_ktype_rsa) }
105
};
106
107
static SECURITY_STATUS NCryptP11StorageProvider_dtor(NCRYPT_HANDLE handle)
108
0
{
109
0
  NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)handle;
110
0
  CK_RV rv = CKR_OK;
111
112
0
  if (provider)
113
0
  {
114
0
    if (provider->p11 && provider->p11->C_Finalize)
115
0
      rv = provider->p11->C_Finalize(NULL);
116
0
    if (rv != CKR_OK)
117
0
    {
118
0
    }
119
120
0
    free(provider->modulePath);
121
122
0
    if (provider->library)
123
0
      FreeLibrary(provider->library);
124
0
  }
125
126
0
  return winpr_NCryptDefault_dtor(handle);
127
0
}
128
129
static void fix_padded_string(char* str, size_t maxlen)
130
0
{
131
0
  char* ptr = str + maxlen - 1;
132
133
0
  while (ptr > str && *ptr == ' ')
134
0
    ptr--;
135
0
  ptr++;
136
0
  *ptr = 0;
137
0
}
138
139
static BOOL attributes_have_unallocated_buffers(CK_ATTRIBUTE_PTR attributes, CK_ULONG count)
140
0
{
141
0
  for (CK_ULONG i = 0; i < count; i++)
142
0
  {
143
0
    if (!attributes[i].pValue && (attributes[i].ulValueLen != CK_UNAVAILABLE_INFORMATION))
144
0
      return TRUE;
145
0
  }
146
147
0
  return FALSE;
148
0
}
149
150
static BOOL attribute_allocate_attribute_array(CK_ATTRIBUTE_PTR attribute)
151
0
{
152
0
  WINPR_ASSERT(attribute);
153
0
  attribute->pValue = calloc(attribute->ulValueLen, sizeof(void*));
154
0
  return !!attribute->pValue;
155
0
}
156
157
static BOOL attribute_allocate_ulong_array(CK_ATTRIBUTE_PTR attribute)
158
0
{
159
0
  attribute->pValue = calloc(attribute->ulValueLen, sizeof(CK_ULONG));
160
0
  return !!attribute->pValue;
161
0
}
162
163
static BOOL attribute_allocate_buffer(CK_ATTRIBUTE_PTR attribute)
164
0
{
165
0
  attribute->pValue = calloc(attribute->ulValueLen, 1);
166
0
  return !!attribute->pValue;
167
0
}
168
169
static BOOL attributes_allocate_buffers(CK_ATTRIBUTE_PTR attributes, CK_ULONG count)
170
0
{
171
0
  BOOL ret = TRUE;
172
173
0
  for (CK_ULONG i = 0; i < count; i++)
174
0
  {
175
0
    if (attributes[i].pValue || (attributes[i].ulValueLen == CK_UNAVAILABLE_INFORMATION))
176
0
      continue;
177
178
0
    switch (attributes[i].type)
179
0
    {
180
0
      case CKA_WRAP_TEMPLATE:
181
0
      case CKA_UNWRAP_TEMPLATE:
182
0
        ret &= attribute_allocate_attribute_array(&attributes[i]);
183
0
        break;
184
185
0
      case CKA_ALLOWED_MECHANISMS:
186
0
        ret &= attribute_allocate_ulong_array(&attributes[i]);
187
0
        break;
188
189
0
      default:
190
0
        ret &= attribute_allocate_buffer(&attributes[i]);
191
0
        break;
192
0
    }
193
0
  }
194
195
0
  return ret;
196
0
}
197
198
static CK_RV object_load_attributes(NCryptP11ProviderHandle* provider, CK_SESSION_HANDLE session,
199
                                    CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR attributes,
200
                                    CK_ULONG count)
201
0
{
202
0
  CK_RV rv = 0;
203
204
0
  WINPR_ASSERT(provider);
205
0
  WINPR_ASSERT(provider->p11);
206
0
  WINPR_ASSERT(provider->p11->C_GetAttributeValue);
207
208
0
  rv = provider->p11->C_GetAttributeValue(session, object, attributes, count);
209
210
0
  switch (rv)
211
0
  {
212
0
    case CKR_OK:
213
0
      if (!attributes_have_unallocated_buffers(attributes, count))
214
0
        return rv;
215
      /* fallthrough */
216
0
      WINPR_FALLTHROUGH
217
0
    case CKR_ATTRIBUTE_SENSITIVE:
218
0
    case CKR_ATTRIBUTE_TYPE_INVALID:
219
0
    case CKR_BUFFER_TOO_SMALL:
220
      /* attributes need some buffers for the result value */
221
0
      if (!attributes_allocate_buffers(attributes, count))
222
0
        return CKR_HOST_MEMORY;
223
224
0
      rv = provider->p11->C_GetAttributeValue(session, object, attributes, count);
225
0
      break;
226
0
    default:
227
0
      return rv;
228
0
  }
229
230
0
  switch (rv)
231
0
  {
232
0
    case CKR_ATTRIBUTE_SENSITIVE:
233
0
    case CKR_ATTRIBUTE_TYPE_INVALID:
234
0
    case CKR_BUFFER_TOO_SMALL:
235
0
      WLog_ERR(TAG, "C_GetAttributeValue return %d even after buffer allocation", rv);
236
0
      break;
237
0
  }
238
0
  return rv;
239
0
}
240
241
static const char* CK_RV_error_string(CK_RV rv)
242
0
{
243
0
  static char generic_buffer[200];
244
0
#define ERR_ENTRY(X) \
245
0
  case X:          \
246
0
    return #X
247
248
0
  switch (rv)
249
0
  {
250
0
    ERR_ENTRY(CKR_OK);
251
0
    ERR_ENTRY(CKR_CANCEL);
252
0
    ERR_ENTRY(CKR_HOST_MEMORY);
253
0
    ERR_ENTRY(CKR_SLOT_ID_INVALID);
254
0
    ERR_ENTRY(CKR_GENERAL_ERROR);
255
0
    ERR_ENTRY(CKR_FUNCTION_FAILED);
256
0
    ERR_ENTRY(CKR_ARGUMENTS_BAD);
257
0
    ERR_ENTRY(CKR_NO_EVENT);
258
0
    ERR_ENTRY(CKR_NEED_TO_CREATE_THREADS);
259
0
    ERR_ENTRY(CKR_CANT_LOCK);
260
0
    ERR_ENTRY(CKR_ATTRIBUTE_READ_ONLY);
261
0
    ERR_ENTRY(CKR_ATTRIBUTE_SENSITIVE);
262
0
    ERR_ENTRY(CKR_ATTRIBUTE_TYPE_INVALID);
263
0
    ERR_ENTRY(CKR_ATTRIBUTE_VALUE_INVALID);
264
0
    ERR_ENTRY(CKR_DATA_INVALID);
265
0
    ERR_ENTRY(CKR_DATA_LEN_RANGE);
266
0
    ERR_ENTRY(CKR_DEVICE_ERROR);
267
0
    ERR_ENTRY(CKR_DEVICE_MEMORY);
268
0
    ERR_ENTRY(CKR_DEVICE_REMOVED);
269
0
    ERR_ENTRY(CKR_ENCRYPTED_DATA_INVALID);
270
0
    ERR_ENTRY(CKR_ENCRYPTED_DATA_LEN_RANGE);
271
0
    ERR_ENTRY(CKR_FUNCTION_CANCELED);
272
0
    ERR_ENTRY(CKR_FUNCTION_NOT_PARALLEL);
273
0
    ERR_ENTRY(CKR_FUNCTION_NOT_SUPPORTED);
274
0
    ERR_ENTRY(CKR_KEY_HANDLE_INVALID);
275
0
    ERR_ENTRY(CKR_KEY_SIZE_RANGE);
276
0
    ERR_ENTRY(CKR_KEY_TYPE_INCONSISTENT);
277
0
    ERR_ENTRY(CKR_KEY_NOT_NEEDED);
278
0
    ERR_ENTRY(CKR_KEY_CHANGED);
279
0
    ERR_ENTRY(CKR_KEY_NEEDED);
280
0
    ERR_ENTRY(CKR_KEY_INDIGESTIBLE);
281
0
    ERR_ENTRY(CKR_KEY_FUNCTION_NOT_PERMITTED);
282
0
    ERR_ENTRY(CKR_KEY_NOT_WRAPPABLE);
283
0
    ERR_ENTRY(CKR_KEY_UNEXTRACTABLE);
284
0
    ERR_ENTRY(CKR_MECHANISM_INVALID);
285
0
    ERR_ENTRY(CKR_MECHANISM_PARAM_INVALID);
286
0
    ERR_ENTRY(CKR_OBJECT_HANDLE_INVALID);
287
0
    ERR_ENTRY(CKR_OPERATION_ACTIVE);
288
0
    ERR_ENTRY(CKR_OPERATION_NOT_INITIALIZED);
289
0
    ERR_ENTRY(CKR_PIN_INCORRECT);
290
0
    ERR_ENTRY(CKR_PIN_INVALID);
291
0
    ERR_ENTRY(CKR_PIN_LEN_RANGE);
292
0
    ERR_ENTRY(CKR_PIN_EXPIRED);
293
0
    ERR_ENTRY(CKR_PIN_LOCKED);
294
0
    ERR_ENTRY(CKR_SESSION_CLOSED);
295
0
    ERR_ENTRY(CKR_SESSION_COUNT);
296
0
    ERR_ENTRY(CKR_SESSION_HANDLE_INVALID);
297
0
    ERR_ENTRY(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
298
0
    ERR_ENTRY(CKR_SESSION_READ_ONLY);
299
0
    ERR_ENTRY(CKR_SESSION_EXISTS);
300
0
    ERR_ENTRY(CKR_SESSION_READ_ONLY_EXISTS);
301
0
    ERR_ENTRY(CKR_SESSION_READ_WRITE_SO_EXISTS);
302
0
    ERR_ENTRY(CKR_SIGNATURE_INVALID);
303
0
    ERR_ENTRY(CKR_SIGNATURE_LEN_RANGE);
304
0
    ERR_ENTRY(CKR_TEMPLATE_INCOMPLETE);
305
0
    ERR_ENTRY(CKR_TEMPLATE_INCONSISTENT);
306
0
    ERR_ENTRY(CKR_TOKEN_NOT_PRESENT);
307
0
    ERR_ENTRY(CKR_TOKEN_NOT_RECOGNIZED);
308
0
    ERR_ENTRY(CKR_TOKEN_WRITE_PROTECTED);
309
0
    ERR_ENTRY(CKR_UNWRAPPING_KEY_HANDLE_INVALID);
310
0
    ERR_ENTRY(CKR_UNWRAPPING_KEY_SIZE_RANGE);
311
0
    ERR_ENTRY(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT);
312
0
    ERR_ENTRY(CKR_USER_ALREADY_LOGGED_IN);
313
0
    ERR_ENTRY(CKR_USER_NOT_LOGGED_IN);
314
0
    ERR_ENTRY(CKR_USER_PIN_NOT_INITIALIZED);
315
0
    ERR_ENTRY(CKR_USER_TYPE_INVALID);
316
0
    ERR_ENTRY(CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
317
0
    ERR_ENTRY(CKR_USER_TOO_MANY_TYPES);
318
0
    ERR_ENTRY(CKR_WRAPPED_KEY_INVALID);
319
0
    ERR_ENTRY(CKR_WRAPPED_KEY_LEN_RANGE);
320
0
    ERR_ENTRY(CKR_WRAPPING_KEY_HANDLE_INVALID);
321
0
    ERR_ENTRY(CKR_WRAPPING_KEY_SIZE_RANGE);
322
0
    ERR_ENTRY(CKR_WRAPPING_KEY_TYPE_INCONSISTENT);
323
0
    ERR_ENTRY(CKR_RANDOM_SEED_NOT_SUPPORTED);
324
0
    ERR_ENTRY(CKR_RANDOM_NO_RNG);
325
0
    ERR_ENTRY(CKR_DOMAIN_PARAMS_INVALID);
326
0
    ERR_ENTRY(CKR_BUFFER_TOO_SMALL);
327
0
    ERR_ENTRY(CKR_SAVED_STATE_INVALID);
328
0
    ERR_ENTRY(CKR_INFORMATION_SENSITIVE);
329
0
    ERR_ENTRY(CKR_STATE_UNSAVEABLE);
330
0
    ERR_ENTRY(CKR_CRYPTOKI_NOT_INITIALIZED);
331
0
    ERR_ENTRY(CKR_CRYPTOKI_ALREADY_INITIALIZED);
332
0
    ERR_ENTRY(CKR_MUTEX_BAD);
333
0
    ERR_ENTRY(CKR_MUTEX_NOT_LOCKED);
334
0
    ERR_ENTRY(CKR_FUNCTION_REJECTED);
335
0
    default:
336
0
      (void)snprintf(generic_buffer, sizeof(generic_buffer), "unknown 0x%lx", rv);
337
0
      return generic_buffer;
338
0
  }
339
0
#undef ERR_ENTRY
340
0
}
341
342
#define loge(tag, msg, rv, index, slot) \
343
0
  log_((tag), (msg), (rv), (index), (slot), __FILE__, __func__, __LINE__)
344
static void log_(const char* tag, const char* msg, CK_RV rv, CK_ULONG index, CK_SLOT_ID slot,
345
                 const char* file, const char* fkt, size_t line)
346
0
{
347
0
  const DWORD log_level = WLOG_ERROR;
348
0
  static wLog* log_cached_ptr = NULL;
349
0
  if (!log_cached_ptr)
350
0
    log_cached_ptr = WLog_Get(tag);
351
0
  if (!WLog_IsLevelActive(log_cached_ptr, log_level))
352
0
    return;
353
354
0
  WLog_PrintMessage(log_cached_ptr, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
355
0
                    "%s for slot #%" PRIu32 "(%" PRIu32 "), rv=%s", msg, index, slot,
356
0
                    CK_RV_error_string(rv));
357
0
}
358
359
static SECURITY_STATUS collect_keys(NCryptP11ProviderHandle* provider, P11EnumKeysState* state)
360
0
{
361
0
  CK_OBJECT_HANDLE slotObjects[MAX_KEYS_PER_SLOT] = { 0 };
362
363
0
  WINPR_ASSERT(provider);
364
365
0
  CK_FUNCTION_LIST_PTR p11 = provider->p11;
366
0
  WINPR_ASSERT(p11);
367
368
0
  WLog_DBG(TAG, "checking %" PRIu32 " slots for valid keys...", state->nslots);
369
0
  state->nKeys = 0;
370
0
  for (CK_ULONG i = 0; i < state->nslots; i++)
371
0
  {
372
0
    CK_SESSION_HANDLE session = (CK_SESSION_HANDLE)NULL;
373
0
    CK_SLOT_INFO slotInfo = { 0 };
374
0
    CK_TOKEN_INFO tokenInfo = { 0 };
375
376
0
    WINPR_ASSERT(p11->C_GetSlotInfo);
377
0
    CK_RV rv = p11->C_GetSlotInfo(state->slots[i], &slotInfo);
378
0
    if (rv != CKR_OK)
379
0
    {
380
0
      loge(TAG, "unable to retrieve information", rv, i, state->slots[i]);
381
0
      continue;
382
0
    }
383
384
0
    fix_padded_string((char*)slotInfo.slotDescription, sizeof(slotInfo.slotDescription));
385
0
    WLog_DBG(TAG, "collecting keys for slot #%" PRIu32 "(%" PRIu32 ") descr='%s' flags=0x%x", i,
386
0
             state->slots[i], slotInfo.slotDescription, slotInfo.flags);
387
388
    /* this is a safety guard as we're supposed to have listed only readers with tokens in them
389
     */
390
0
    if (!(slotInfo.flags & CKF_TOKEN_PRESENT))
391
0
    {
392
0
      WLog_INFO(TAG, "token not present for slot #%" PRIu32 "(%" PRIu32 ")", i,
393
0
                state->slots[i]);
394
0
      continue;
395
0
    }
396
397
0
    WINPR_ASSERT(p11->C_GetTokenInfo);
398
0
    rv = p11->C_GetTokenInfo(state->slots[i], &tokenInfo);
399
0
    if (rv != CKR_OK)
400
0
      loge(TAG, "unable to retrieve token info", rv, i, state->slots[i]);
401
0
    else
402
0
    {
403
0
      fix_padded_string((char*)tokenInfo.label, sizeof(tokenInfo.label));
404
0
      WLog_DBG(TAG, "token, label='%s' flags=0x%x", tokenInfo.label, tokenInfo.flags);
405
0
    }
406
407
0
    WINPR_ASSERT(p11->C_OpenSession);
408
0
    rv = p11->C_OpenSession(state->slots[i], CKF_SERIAL_SESSION, NULL, NULL, &session);
409
0
    if (rv != CKR_OK)
410
0
    {
411
0
      WLog_ERR(TAG,
412
0
               "unable to openSession for slot #%" PRIu32 "(%" PRIu32 "), session=%p rv=%s",
413
0
               i, state->slots[i], session, CK_RV_error_string(rv));
414
0
      continue;
415
0
    }
416
417
0
    WINPR_ASSERT(p11->C_FindObjectsInit);
418
0
    rv = p11->C_FindObjectsInit(session, public_key_filter, ARRAYSIZE(public_key_filter));
419
0
    if (rv != CKR_OK)
420
0
    {
421
      // TODO: shall it be fatal ?
422
0
      loge(TAG, "unable to initiate search", rv, i, state->slots[i]);
423
0
      goto cleanup_FindObjectsInit;
424
0
    }
425
426
0
    CK_ULONG nslotObjects = 0;
427
0
    WINPR_ASSERT(p11->C_FindObjects);
428
0
    rv = p11->C_FindObjects(session, &slotObjects[0], ARRAYSIZE(slotObjects), &nslotObjects);
429
0
    if (rv != CKR_OK)
430
0
    {
431
0
      loge(TAG, "unable to findObjects", rv, i, state->slots[i]);
432
0
      goto cleanup_FindObjects;
433
0
    }
434
435
0
    WLog_DBG(TAG, "slot has %d objects", nslotObjects);
436
0
    for (CK_ULONG j = 0; j < nslotObjects; j++)
437
0
    {
438
0
      NCryptKeyEnum* key = &state->keys[state->nKeys];
439
0
      CK_OBJECT_CLASS dataClass = CKO_PUBLIC_KEY;
440
0
      CK_ATTRIBUTE key_or_certAttrs[] = {
441
0
        { CKA_ID, &key->id, sizeof(key->id) },
442
0
        { CKA_CLASS, &dataClass, sizeof(dataClass) },
443
0
        { CKA_LABEL, &key->keyLabel, sizeof(key->keyLabel) },
444
0
        { CKA_KEY_TYPE, &key->keyType, sizeof(key->keyType) }
445
0
      };
446
447
0
      rv = object_load_attributes(provider, session, slotObjects[j], key_or_certAttrs,
448
0
                                  ARRAYSIZE(key_or_certAttrs));
449
0
      if (rv != CKR_OK)
450
0
      {
451
0
        WLog_ERR(TAG, "error getting attributes, rv=%s", CK_RV_error_string(rv));
452
0
        continue;
453
0
      }
454
455
0
      key->idLen = key_or_certAttrs[0].ulValueLen;
456
0
      key->slotId = state->slots[i];
457
0
      key->slotInfo = slotInfo;
458
0
      state->nKeys++;
459
0
    }
460
461
0
  cleanup_FindObjects:
462
0
    WINPR_ASSERT(p11->C_FindObjectsFinal);
463
0
    rv = p11->C_FindObjectsFinal(session);
464
0
    if (rv != CKR_OK)
465
0
      loge(TAG, "error during C_FindObjectsFinal", rv, i, state->slots[i]);
466
0
  cleanup_FindObjectsInit:
467
0
    WINPR_ASSERT(p11->C_CloseSession);
468
0
    rv = p11->C_CloseSession(session);
469
0
    if (rv != CKR_OK)
470
0
      loge(TAG, "error closing session", rv, i, state->slots[i]);
471
0
  }
472
473
0
  return ERROR_SUCCESS;
474
0
}
475
476
static BOOL convertKeyType(CK_KEY_TYPE k, LPWSTR dest, DWORD len, DWORD* outlen)
477
0
{
478
0
  DWORD retLen = 0;
479
0
  const WCHAR* r = NULL;
480
481
0
#define ALGO_CASE(V, S) \
482
0
  case V:             \
483
0
    r = S;          \
484
0
    break
485
0
  switch (k)
486
0
  {
487
0
    ALGO_CASE(CKK_RSA, BCRYPT_RSA_ALGORITHM);
488
0
    ALGO_CASE(CKK_DSA, BCRYPT_DSA_ALGORITHM);
489
0
    ALGO_CASE(CKK_DH, BCRYPT_DH_ALGORITHM);
490
0
    ALGO_CASE(CKK_EC, BCRYPT_ECDSA_ALGORITHM);
491
0
    ALGO_CASE(CKK_RC2, BCRYPT_RC2_ALGORITHM);
492
0
    ALGO_CASE(CKK_RC4, BCRYPT_RC4_ALGORITHM);
493
0
    ALGO_CASE(CKK_DES, BCRYPT_DES_ALGORITHM);
494
0
    ALGO_CASE(CKK_DES3, BCRYPT_3DES_ALGORITHM);
495
0
    case CKK_DES2:
496
0
    case CKK_X9_42_DH:
497
0
    case CKK_KEA:
498
0
    case CKK_GENERIC_SECRET:
499
0
    case CKK_CAST:
500
0
    case CKK_CAST3:
501
0
    case CKK_CAST128:
502
0
    case CKK_RC5:
503
0
    case CKK_IDEA:
504
0
    case CKK_SKIPJACK:
505
0
    case CKK_BATON:
506
0
    case CKK_JUNIPER:
507
0
    case CKK_CDMF:
508
0
    case CKK_AES:
509
0
    case CKK_BLOWFISH:
510
0
    case CKK_TWOFISH:
511
0
    default:
512
0
      break;
513
0
  }
514
0
#undef ALGO_CASE
515
516
0
  retLen = _wcslen(r);
517
0
  if (outlen)
518
0
    *outlen = retLen;
519
520
0
  if (!r)
521
0
  {
522
0
    if (dest && len > 0)
523
0
      dest[0] = 0;
524
0
    return FALSE;
525
0
  }
526
0
  else
527
0
  {
528
0
    if (retLen + 1 < len)
529
0
    {
530
0
      WLog_ERR(TAG, "target buffer is too small for algo name");
531
0
      return FALSE;
532
0
    }
533
534
0
    if (dest)
535
0
    {
536
0
      memcpy(dest, r, sizeof(WCHAR) * retLen);
537
0
      dest[retLen] = 0;
538
0
    }
539
0
  }
540
541
0
  return TRUE;
542
0
}
543
544
static void wprintKeyName(LPWSTR str, CK_SLOT_ID slotId, CK_BYTE* id, CK_ULONG idLen)
545
0
{
546
0
  char asciiName[128] = { 0 };
547
0
  char* ptr = asciiName;
548
0
  const CK_BYTE* bytePtr = NULL;
549
550
0
  *ptr = '\\';
551
0
  ptr++;
552
553
0
  bytePtr = ((CK_BYTE*)&slotId);
554
0
  for (CK_ULONG i = 0; i < sizeof(slotId); i++, bytePtr++, ptr += 2)
555
0
    (void)snprintf(ptr, 3, "%.2x", *bytePtr);
556
557
0
  *ptr = '\\';
558
0
  ptr++;
559
560
0
  for (CK_ULONG i = 0; i < idLen; i++, id++, ptr += 2)
561
0
    (void)snprintf(ptr, 3, "%.2x", *id);
562
563
0
  ConvertUtf8NToWChar(asciiName, ARRAYSIZE(asciiName), str,
564
0
                      strnlen(asciiName, ARRAYSIZE(asciiName)) + 1);
565
0
}
566
567
static size_t parseHex(const char* str, const char* end, CK_BYTE* target)
568
0
{
569
0
  int ret = 0;
570
571
0
  for (; str != end && *str; str++, ret++, target++)
572
0
  {
573
0
    CK_BYTE v = 0;
574
0
    if (*str <= '9' && *str >= '0')
575
0
    {
576
0
      v = (*str - '0');
577
0
    }
578
0
    else if (*str <= 'f' && *str >= 'a')
579
0
    {
580
0
      v = (10 + *str - 'a');
581
0
    }
582
0
    else if (*str <= 'F' && *str >= 'A')
583
0
    {
584
0
      v |= (10 + *str - 'A');
585
0
    }
586
0
    else
587
0
    {
588
0
      return 0;
589
0
    }
590
0
    v <<= 4;
591
0
    str++;
592
593
0
    if (!*str || str == end)
594
0
      return 0;
595
596
0
    if (*str <= '9' && *str >= '0')
597
0
    {
598
0
      v |= (*str - '0');
599
0
    }
600
0
    else if (*str <= 'f' && *str >= 'a')
601
0
    {
602
0
      v |= (10 + *str - 'a');
603
0
    }
604
0
    else if (*str <= 'F' && *str >= 'A')
605
0
    {
606
0
      v |= (10 + *str - 'A');
607
0
    }
608
0
    else
609
0
    {
610
0
      return 0;
611
0
    }
612
613
0
    *target = v;
614
0
  }
615
0
  return ret;
616
0
}
617
618
static SECURITY_STATUS parseKeyName(LPCWSTR pszKeyName, CK_SLOT_ID* slotId, CK_BYTE* id,
619
                                    CK_ULONG* idLen)
620
0
{
621
0
  char asciiKeyName[128] = { 0 };
622
0
  char* pos = NULL;
623
624
0
  if (ConvertWCharToUtf8(pszKeyName, asciiKeyName, ARRAYSIZE(asciiKeyName)) < 0)
625
0
    return NTE_BAD_KEY;
626
627
0
  if (*asciiKeyName != '\\')
628
0
    return NTE_BAD_KEY;
629
630
0
  pos = strchr(&asciiKeyName[1], '\\');
631
0
  if (!pos)
632
0
    return NTE_BAD_KEY;
633
634
0
  if ((size_t)(pos - &asciiKeyName[1]) > sizeof(CK_SLOT_ID) * 2ull)
635
0
    return NTE_BAD_KEY;
636
637
0
  *slotId = (CK_SLOT_ID)0;
638
0
  if (parseHex(&asciiKeyName[1], pos, (CK_BYTE*)slotId) != sizeof(CK_SLOT_ID))
639
0
    return NTE_BAD_KEY;
640
641
0
  *idLen = parseHex(pos + 1, NULL, id);
642
0
  if (!*idLen)
643
0
    return NTE_BAD_KEY;
644
645
0
  return ERROR_SUCCESS;
646
0
}
647
648
static SECURITY_STATUS NCryptP11EnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope,
649
                                         NCryptKeyName** ppKeyName, PVOID* ppEnumState,
650
                                         DWORD dwFlags)
651
0
{
652
0
  NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)hProvider;
653
0
  P11EnumKeysState* state = (P11EnumKeysState*)*ppEnumState;
654
0
  CK_RV rv = { 0 };
655
0
  CK_SLOT_ID currentSlot = { 0 };
656
0
  CK_SESSION_HANDLE currentSession = (CK_SESSION_HANDLE)NULL;
657
0
  char slotFilterBuffer[65] = { 0 };
658
0
  char* slotFilter = NULL;
659
0
  size_t slotFilterLen = 0;
660
661
0
  SECURITY_STATUS ret = checkNCryptHandle((NCRYPT_HANDLE)hProvider, WINPR_NCRYPT_PROVIDER);
662
0
  if (ret != ERROR_SUCCESS)
663
0
    return ret;
664
665
0
  if (pszScope)
666
0
  {
667
    /*
668
     * check whether pszScope is of the form \\.\<reader name>\ for filtering by
669
     * card reader
670
     */
671
0
    char asciiScope[128 + 6 + 1] = { 0 };
672
0
    size_t asciiScopeLen = 0;
673
674
0
    if (ConvertWCharToUtf8(pszScope, asciiScope, ARRAYSIZE(asciiScope) - 1) < 0)
675
0
    {
676
0
      WLog_WARN(TAG, "Invalid scope");
677
0
      return NTE_INVALID_PARAMETER;
678
0
    }
679
680
0
    if (strstr(asciiScope, "\\\\.\\") != asciiScope)
681
0
    {
682
0
      WLog_WARN(TAG, "Invalid scope '%s'", asciiScope);
683
0
      return NTE_INVALID_PARAMETER;
684
0
    }
685
686
0
    asciiScopeLen = strnlen(asciiScope, ARRAYSIZE(asciiScope));
687
0
    if ((asciiScopeLen < 1) || (asciiScope[asciiScopeLen - 1] != '\\'))
688
0
    {
689
0
      WLog_WARN(TAG, "Invalid scope '%s'", asciiScope);
690
0
      return NTE_INVALID_PARAMETER;
691
0
    }
692
693
0
    asciiScope[asciiScopeLen - 1] = 0;
694
695
0
    strncpy(slotFilterBuffer, &asciiScope[4], sizeof(slotFilterBuffer));
696
0
    slotFilter = slotFilterBuffer;
697
0
    slotFilterLen = asciiScopeLen - 5;
698
0
  }
699
700
0
  if (!state)
701
0
  {
702
0
    state = (P11EnumKeysState*)calloc(1, sizeof(*state));
703
0
    if (!state)
704
0
      return NTE_NO_MEMORY;
705
706
0
    WINPR_ASSERT(provider->p11->C_GetSlotList);
707
0
    rv = provider->p11->C_GetSlotList(CK_TRUE, NULL, &state->nslots);
708
0
    if (rv != CKR_OK)
709
0
    {
710
      /* TODO: perhaps convert rv to NTE_*** errors */
711
0
      WLog_WARN(TAG, "C_GetSlotList failed with %u", rv);
712
0
      return NTE_FAIL;
713
0
    }
714
715
0
    if (state->nslots > MAX_SLOTS)
716
0
      state->nslots = MAX_SLOTS;
717
718
0
    rv = provider->p11->C_GetSlotList(CK_TRUE, state->slots, &state->nslots);
719
0
    if (rv != CKR_OK)
720
0
    {
721
0
      free(state);
722
      /* TODO: perhaps convert rv to NTE_*** errors */
723
0
      WLog_WARN(TAG, "C_GetSlotList failed with %u", rv);
724
0
      return NTE_FAIL;
725
0
    }
726
727
0
    ret = collect_keys(provider, state);
728
0
    if (ret != ERROR_SUCCESS)
729
0
    {
730
0
      free(state);
731
0
      return ret;
732
0
    }
733
734
0
    *ppEnumState = state;
735
0
  }
736
737
0
  for (; state->keyIndex < state->nKeys; state->keyIndex++)
738
0
  {
739
0
    NCryptKeyName* keyName = NULL;
740
0
    NCryptKeyEnum* key = &state->keys[state->keyIndex];
741
0
    CK_OBJECT_CLASS oclass = CKO_CERTIFICATE;
742
0
    CK_CERTIFICATE_TYPE ctype = CKC_X_509;
743
0
    CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) },
744
0
                                       { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) },
745
0
                                       { CKA_ID, key->id, key->idLen } };
746
0
    CK_ULONG ncertObjects = 0;
747
0
    CK_OBJECT_HANDLE certObject = 0;
748
749
    /* check the reader filter if any */
750
0
    if (slotFilter && memcmp(key->slotInfo.slotDescription, slotFilter, slotFilterLen) != 0)
751
0
      continue;
752
753
0
    if (!currentSession || (currentSlot != key->slotId))
754
0
    {
755
      /* if the current session doesn't match the current key's slot, open a new one
756
       */
757
0
      if (currentSession)
758
0
      {
759
0
        WINPR_ASSERT(provider->p11->C_CloseSession);
760
0
        rv = provider->p11->C_CloseSession(currentSession);
761
0
        currentSession = (CK_SESSION_HANDLE)NULL;
762
0
      }
763
764
0
      WINPR_ASSERT(provider->p11->C_OpenSession);
765
0
      rv = provider->p11->C_OpenSession(key->slotId, CKF_SERIAL_SESSION, NULL, NULL,
766
0
                                        &currentSession);
767
0
      if (rv != CKR_OK)
768
0
      {
769
0
        WLog_ERR(TAG, "unable to openSession for slot %d", key->slotId);
770
0
        continue;
771
0
      }
772
0
      currentSlot = key->slotId;
773
0
    }
774
775
    /* look if we can find a certificate that matches the key's id */
776
0
    WINPR_ASSERT(provider->p11->C_FindObjectsInit);
777
0
    rv = provider->p11->C_FindObjectsInit(currentSession, certificateFilter,
778
0
                                          ARRAYSIZE(certificateFilter));
779
0
    if (rv != CKR_OK)
780
0
    {
781
0
      WLog_ERR(TAG, "unable to initiate search for slot %d", key->slotId);
782
0
      continue;
783
0
    }
784
785
0
    WINPR_ASSERT(provider->p11->C_FindObjects);
786
0
    rv = provider->p11->C_FindObjects(currentSession, &certObject, 1, &ncertObjects);
787
0
    if (rv != CKR_OK)
788
0
    {
789
0
      WLog_ERR(TAG, "unable to findObjects for slot %d", currentSlot);
790
0
      goto cleanup_FindObjects;
791
0
    }
792
793
0
    if (ncertObjects)
794
0
    {
795
      /* sizeof keyName struct + "\<slotId>\<certId>" + keyName->pszAlgid */
796
0
      DWORD algoSz = 0;
797
0
      size_t KEYNAME_SZ =
798
0
          (1 + (sizeof(key->slotId) * 2) /*slotId*/ + 1 + (key->idLen * 2) + 1) * 2;
799
800
0
      convertKeyType(key->keyType, NULL, 0, &algoSz);
801
0
      KEYNAME_SZ += (1ULL + algoSz) * 2ULL;
802
803
0
      keyName = calloc(1, sizeof(*keyName) + KEYNAME_SZ);
804
0
      if (!keyName)
805
0
      {
806
0
        WLog_ERR(TAG, "unable to allocate keyName");
807
0
        goto cleanup_FindObjects;
808
0
      }
809
0
      keyName->dwLegacyKeySpec = AT_KEYEXCHANGE | AT_SIGNATURE;
810
0
      keyName->dwFlags = NCRYPT_MACHINE_KEY_FLAG;
811
0
      keyName->pszName = (LPWSTR)(keyName + 1);
812
0
      wprintKeyName(keyName->pszName, key->slotId, key->id, key->idLen);
813
814
0
      keyName->pszAlgid = keyName->pszName + _wcslen(keyName->pszName) + 1;
815
0
      convertKeyType(key->keyType, keyName->pszAlgid, algoSz + 1, NULL);
816
0
    }
817
818
0
  cleanup_FindObjects:
819
0
    WINPR_ASSERT(provider->p11->C_FindObjectsFinal);
820
0
    rv = provider->p11->C_FindObjectsFinal(currentSession);
821
822
0
    if (keyName)
823
0
    {
824
0
      *ppKeyName = keyName;
825
0
      state->keyIndex++;
826
0
      return ERROR_SUCCESS;
827
0
    }
828
0
  }
829
830
0
  return NTE_NO_MORE_ITEMS;
831
0
}
832
833
static SECURITY_STATUS get_piv_container_name(NCryptP11KeyHandle* key, const BYTE* piv_tag,
834
                                              BYTE* output, size_t output_len)
835
0
{
836
0
  CK_SLOT_INFO slot_info = { 0 };
837
0
  CK_FUNCTION_LIST_PTR p11 = NULL;
838
0
  WCHAR* reader = NULL;
839
0
  SCARDCONTEXT context = 0;
840
0
  SCARDHANDLE card = 0;
841
0
  DWORD proto = 0;
842
0
  const SCARD_IO_REQUEST* pci = NULL;
843
0
  BYTE buf[258] = { 0 };
844
0
  char container_name[PIV_CONTAINER_NAME_LEN + 1] = { 0 };
845
0
  DWORD buf_len = 0;
846
0
  SECURITY_STATUS ret = NTE_BAD_KEY;
847
0
  WinPrAsn1Decoder dec = { 0 };
848
0
  WinPrAsn1Decoder dec2 = { 0 };
849
0
  size_t len = 0;
850
0
  BYTE tag = 0;
851
0
  BYTE* p = NULL;
852
0
  wStream s = { 0 };
853
854
0
  WINPR_ASSERT(key);
855
0
  WINPR_ASSERT(piv_tag);
856
857
0
  WINPR_ASSERT(key->provider);
858
0
  p11 = key->provider->p11;
859
0
  WINPR_ASSERT(p11);
860
861
  /* Get the reader the card is in */
862
0
  WINPR_ASSERT(p11->C_GetSlotInfo);
863
0
  if (p11->C_GetSlotInfo(key->slotId, &slot_info) != CKR_OK)
864
0
    return NTE_BAD_KEY;
865
866
0
  fix_padded_string((char*)slot_info.slotDescription, sizeof(slot_info.slotDescription));
867
0
  reader = ConvertUtf8NToWCharAlloc((char*)slot_info.slotDescription,
868
0
                                    ARRAYSIZE(slot_info.slotDescription), NULL);
869
0
  ret = NTE_NO_MEMORY;
870
0
  if (!reader)
871
0
    goto out;
872
873
0
  ret = NTE_BAD_KEY;
874
0
  if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &context) != SCARD_S_SUCCESS)
875
0
    goto out;
876
877
0
  if (SCardConnectW(context, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &card, &proto) !=
878
0
      SCARD_S_SUCCESS)
879
0
    goto out;
880
0
  pci = (proto == SCARD_PROTOCOL_T0) ? SCARD_PCI_T0 : SCARD_PCI_T1;
881
882
0
  buf_len = sizeof(buf);
883
0
  if (SCardTransmit(card, pci, APDU_PIV_SELECT_AID, sizeof(APDU_PIV_SELECT_AID), NULL, buf,
884
0
                    &buf_len) != SCARD_S_SUCCESS)
885
0
    goto out;
886
0
  if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
887
0
    goto out;
888
889
0
  buf_len = sizeof(buf);
890
0
  if (SCardTransmit(card, pci, APDU_PIV_GET_CHUID, sizeof(APDU_PIV_GET_CHUID), NULL, buf,
891
0
                    &buf_len) != SCARD_S_SUCCESS)
892
0
    goto out;
893
0
  if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
894
0
    goto out;
895
896
  /* Find the GUID field in the CHUID data object */
897
0
  WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_BER, buf, buf_len);
898
0
  if (!WinPrAsn1DecReadTagAndLen(&dec, &tag, &len) || tag != 0x53)
899
0
    goto out;
900
0
  while (WinPrAsn1DecReadTagLenValue(&dec, &tag, &len, &dec2) && tag != 0x34)
901
0
    ;
902
0
  if (tag != 0x34 || len != 16)
903
0
    goto out;
904
905
0
  s = WinPrAsn1DecGetStream(&dec2);
906
0
  p = Stream_Buffer(&s);
907
908
  /* Construct the value Windows would use for a PIV key's container name */
909
0
  (void)snprintf(container_name, PIV_CONTAINER_NAME_LEN + 1,
910
0
                 "%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", p[3],
911
0
                 p[2], p[1], p[0], p[5], p[4], p[7], p[6], p[8], p[9], p[10], p[11], p[12],
912
0
                 piv_tag[0], piv_tag[1], piv_tag[2]);
913
914
  /* And convert it to UTF-16 */
915
0
  union
916
0
  {
917
0
    WCHAR* wc;
918
0
    BYTE* b;
919
0
  } cnv;
920
0
  cnv.b = output;
921
0
  if (ConvertUtf8NToWChar(container_name, ARRAYSIZE(container_name), cnv.wc,
922
0
                          output_len / sizeof(WCHAR)) > 0)
923
0
    ret = ERROR_SUCCESS;
924
925
0
out:
926
0
  free(reader);
927
0
  if (card)
928
0
    SCardDisconnect(card, SCARD_LEAVE_CARD);
929
0
  if (context)
930
0
    SCardReleaseContext(context);
931
0
  return ret;
932
0
}
933
934
static SECURITY_STATUS check_for_piv_container_name(NCryptP11KeyHandle* key, BYTE* pbOutput,
935
                                                    DWORD cbOutput, DWORD* pcbResult, char* label,
936
                                                    size_t label_len)
937
0
{
938
0
  for (size_t i = 0; i < ARRAYSIZE(piv_cert_tags); i++)
939
0
  {
940
0
    const piv_cert_tags_t* cur = &piv_cert_tags[i];
941
0
    if (strncmp(label, cur->label, label_len) == 0)
942
0
    {
943
0
      *pcbResult = (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR);
944
0
      if (!pbOutput)
945
0
        return ERROR_SUCCESS;
946
0
      else if (cbOutput < (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR))
947
0
        return NTE_NO_MEMORY;
948
0
      else
949
0
        return get_piv_container_name(key, cur->tag, pbOutput, cbOutput);
950
0
    }
951
0
  }
952
0
  return NTE_NOT_FOUND;
953
0
}
954
955
static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle,
956
                                                 NCryptKeyGetPropertyEnum property, PBYTE pbOutput,
957
                                                 DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags)
958
0
{
959
0
  SECURITY_STATUS ret = NTE_FAIL;
960
0
  CK_RV rv = 0;
961
0
  CK_SESSION_HANDLE session = 0;
962
0
  CK_OBJECT_HANDLE objectHandle = 0;
963
0
  CK_ULONG objectCount = 0;
964
0
  NCryptP11ProviderHandle* provider = NULL;
965
0
  CK_OBJECT_CLASS oclass = CKO_CERTIFICATE;
966
0
  CK_CERTIFICATE_TYPE ctype = CKC_X_509;
967
0
  CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) },
968
0
                                     { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) },
969
0
                                     { CKA_ID, keyHandle->keyCertId,
970
0
                                       keyHandle->keyCertIdLen } };
971
0
  CK_ATTRIBUTE* objectFilter = certificateFilter;
972
0
  CK_ULONG objectFilterLen = ARRAYSIZE(certificateFilter);
973
974
0
  WINPR_ASSERT(keyHandle);
975
0
  provider = keyHandle->provider;
976
0
  WINPR_ASSERT(provider);
977
978
0
  switch (property)
979
980
0
  {
981
0
    case NCRYPT_PROPERTY_CERTIFICATE:
982
0
    case NCRYPT_PROPERTY_NAME:
983
0
      break;
984
0
    case NCRYPT_PROPERTY_READER:
985
0
    {
986
0
      CK_SLOT_INFO slotInfo;
987
988
0
      WINPR_ASSERT(provider->p11->C_GetSlotInfo);
989
0
      rv = provider->p11->C_GetSlotInfo(keyHandle->slotId, &slotInfo);
990
0
      if (rv != CKR_OK)
991
0
        return NTE_BAD_KEY;
992
993
0
#define SLOT_DESC_SZ sizeof(slotInfo.slotDescription)
994
0
      fix_padded_string((char*)slotInfo.slotDescription, SLOT_DESC_SZ);
995
0
      *pcbResult = 2 * (strnlen((char*)slotInfo.slotDescription, SLOT_DESC_SZ) + 1);
996
0
      if (pbOutput)
997
0
      {
998
0
        union
999
0
        {
1000
0
          WCHAR* wc;
1001
0
          BYTE* b;
1002
0
        } cnv;
1003
0
        cnv.b = pbOutput;
1004
0
        if (cbOutput < *pcbResult)
1005
0
          return NTE_NO_MEMORY;
1006
1007
0
        if (ConvertUtf8ToWChar((char*)slotInfo.slotDescription, cnv.wc,
1008
0
                               cbOutput / sizeof(WCHAR)) < 0)
1009
0
          return NTE_NO_MEMORY;
1010
0
      }
1011
0
      return ERROR_SUCCESS;
1012
0
    }
1013
0
    case NCRYPT_PROPERTY_SLOTID:
1014
0
    {
1015
0
      *pcbResult = 4;
1016
0
      if (pbOutput)
1017
0
      {
1018
0
        UINT32* ptr = (UINT32*)pbOutput;
1019
1020
0
        if (cbOutput < 4)
1021
0
          return NTE_NO_MEMORY;
1022
1023
0
        *ptr = keyHandle->slotId;
1024
0
      }
1025
0
      return ERROR_SUCCESS;
1026
0
    }
1027
0
    case NCRYPT_PROPERTY_UNKNOWN:
1028
0
    default:
1029
0
      return NTE_NOT_SUPPORTED;
1030
0
  }
1031
1032
0
  WINPR_ASSERT(provider->p11->C_OpenSession);
1033
0
  rv = provider->p11->C_OpenSession(keyHandle->slotId, CKF_SERIAL_SESSION, NULL, NULL, &session);
1034
0
  if (rv != CKR_OK)
1035
0
  {
1036
0
    WLog_ERR(TAG, "error opening session on slot %d", keyHandle->slotId);
1037
0
    return NTE_FAIL;
1038
0
  }
1039
1040
0
  WINPR_ASSERT(provider->p11->C_FindObjectsInit);
1041
0
  rv = provider->p11->C_FindObjectsInit(session, objectFilter, objectFilterLen);
1042
0
  if (rv != CKR_OK)
1043
0
  {
1044
0
    WLog_ERR(TAG, "unable to initiate search for slot %d", keyHandle->slotId);
1045
0
    goto out;
1046
0
  }
1047
1048
0
  WINPR_ASSERT(provider->p11->C_FindObjects);
1049
0
  rv = provider->p11->C_FindObjects(session, &objectHandle, 1, &objectCount);
1050
0
  if (rv != CKR_OK)
1051
0
  {
1052
0
    WLog_ERR(TAG, "unable to findObjects for slot %d", keyHandle->slotId);
1053
0
    goto out_final;
1054
0
  }
1055
0
  if (!objectCount)
1056
0
  {
1057
0
    ret = NTE_NOT_FOUND;
1058
0
    goto out_final;
1059
0
  }
1060
1061
0
  switch (property)
1062
0
  {
1063
0
    case NCRYPT_PROPERTY_CERTIFICATE:
1064
0
    {
1065
0
      CK_ATTRIBUTE certValue = { CKA_VALUE, pbOutput, cbOutput };
1066
1067
0
      WINPR_ASSERT(provider->p11->C_GetAttributeValue);
1068
0
      rv = provider->p11->C_GetAttributeValue(session, objectHandle, &certValue, 1);
1069
0
      if (rv != CKR_OK)
1070
0
      {
1071
        // TODO: do a kind of translation from CKR_* to NTE_*
1072
0
      }
1073
1074
0
      *pcbResult = certValue.ulValueLen;
1075
0
      ret = ERROR_SUCCESS;
1076
0
      break;
1077
0
    }
1078
0
    case NCRYPT_PROPERTY_NAME:
1079
0
    {
1080
0
      CK_ATTRIBUTE attr = { CKA_LABEL, NULL, 0 };
1081
0
      char* label = NULL;
1082
1083
0
      WINPR_ASSERT(provider->p11->C_GetAttributeValue);
1084
0
      rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
1085
0
      if (rv == CKR_OK)
1086
0
      {
1087
0
        label = calloc(1, attr.ulValueLen);
1088
0
        if (!label)
1089
0
        {
1090
0
          ret = NTE_NO_MEMORY;
1091
0
          break;
1092
0
        }
1093
1094
0
        attr.pValue = label;
1095
0
        rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
1096
0
      }
1097
1098
0
      if (rv == CKR_OK)
1099
0
      {
1100
        /* Check if we have a PIV card */
1101
0
        ret = check_for_piv_container_name(keyHandle, pbOutput, cbOutput, pcbResult, label,
1102
0
                                           attr.ulValueLen);
1103
1104
        /* Otherwise, at least for GIDS cards the label will be the correct value */
1105
0
        if (ret == NTE_NOT_FOUND)
1106
0
        {
1107
0
          union
1108
0
          {
1109
0
            WCHAR* wc;
1110
0
            BYTE* b;
1111
0
          } cnv;
1112
0
          const size_t olen = pbOutput ? cbOutput / sizeof(WCHAR) : 0;
1113
0
          cnv.b = pbOutput;
1114
0
          SSIZE_T size = ConvertUtf8NToWChar(label, attr.ulValueLen, cnv.wc, olen);
1115
0
          if (size < 0)
1116
0
            ret = ERROR_CONVERT_TO_LARGE;
1117
0
          else
1118
0
            ret = ERROR_SUCCESS;
1119
0
        }
1120
0
      }
1121
1122
0
      free(label);
1123
0
      break;
1124
0
    }
1125
0
    default:
1126
0
      ret = NTE_NOT_SUPPORTED;
1127
0
      break;
1128
0
  }
1129
1130
0
out_final:
1131
0
  WINPR_ASSERT(provider->p11->C_FindObjectsFinal);
1132
0
  rv = provider->p11->C_FindObjectsFinal(session);
1133
0
  if (rv != CKR_OK)
1134
0
  {
1135
0
    WLog_ERR(TAG, "error in C_FindObjectsFinal() for slot %d", keyHandle->slotId);
1136
0
  }
1137
0
out:
1138
0
  WINPR_ASSERT(provider->p11->C_CloseSession);
1139
0
  rv = provider->p11->C_CloseSession(session);
1140
0
  if (rv != CKR_OK)
1141
0
  {
1142
0
    WLog_ERR(TAG, "error in C_CloseSession() for slot %d", keyHandle->slotId);
1143
0
  }
1144
0
  return ret;
1145
0
}
1146
1147
static SECURITY_STATUS NCryptP11GetProperty(NCRYPT_HANDLE hObject, NCryptKeyGetPropertyEnum prop,
1148
                                            PBYTE pbOutput, DWORD cbOutput, DWORD* pcbResult,
1149
                                            DWORD dwFlags)
1150
0
{
1151
0
  NCryptBaseHandle* base = (NCryptBaseHandle*)hObject;
1152
1153
0
  WINPR_ASSERT(base);
1154
0
  switch (base->type)
1155
0
  {
1156
0
    case WINPR_NCRYPT_PROVIDER:
1157
0
      return ERROR_CALL_NOT_IMPLEMENTED;
1158
0
    case WINPR_NCRYPT_KEY:
1159
0
      return NCryptP11KeyGetProperties((NCryptP11KeyHandle*)hObject, prop, pbOutput, cbOutput,
1160
0
                                       pcbResult, dwFlags);
1161
0
    default:
1162
0
      return ERROR_INVALID_HANDLE;
1163
0
  }
1164
0
  return ERROR_SUCCESS;
1165
0
}
1166
1167
static SECURITY_STATUS NCryptP11OpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey,
1168
                                        LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, DWORD dwFlags)
1169
0
{
1170
0
  SECURITY_STATUS ret = 0;
1171
0
  CK_SLOT_ID slotId = 0;
1172
0
  CK_BYTE keyCertId[64] = { 0 };
1173
0
  CK_ULONG keyCertIdLen = 0;
1174
0
  NCryptP11KeyHandle* keyHandle = NULL;
1175
1176
0
  ret = parseKeyName(pszKeyName, &slotId, keyCertId, &keyCertIdLen);
1177
0
  if (ret != ERROR_SUCCESS)
1178
0
    return ret;
1179
1180
0
  keyHandle = (NCryptP11KeyHandle*)ncrypt_new_handle(
1181
0
      WINPR_NCRYPT_KEY, sizeof(*keyHandle), NCryptP11GetProperty, winpr_NCryptDefault_dtor);
1182
0
  if (!keyHandle)
1183
0
    return NTE_NO_MEMORY;
1184
1185
0
  keyHandle->provider = (NCryptP11ProviderHandle*)hProvider;
1186
0
  keyHandle->slotId = slotId;
1187
0
  memcpy(keyHandle->keyCertId, keyCertId, sizeof(keyCertId));
1188
0
  keyHandle->keyCertIdLen = keyCertIdLen;
1189
0
  *phKey = (NCRYPT_KEY_HANDLE)keyHandle;
1190
0
  return ERROR_SUCCESS;
1191
0
}
1192
1193
static SECURITY_STATUS initialize_pkcs11(HANDLE handle,
1194
                                         CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR),
1195
                                         NCRYPT_PROV_HANDLE* phProvider)
1196
0
{
1197
0
  SECURITY_STATUS status = ERROR_SUCCESS;
1198
0
  NCryptP11ProviderHandle* ret = NULL;
1199
0
  CK_RV rv = 0;
1200
1201
0
  WINPR_ASSERT(c_get_function_list);
1202
0
  WINPR_ASSERT(phProvider);
1203
1204
0
  ret = (NCryptP11ProviderHandle*)ncrypt_new_handle(
1205
0
      WINPR_NCRYPT_PROVIDER, sizeof(*ret), NCryptP11GetProperty, NCryptP11StorageProvider_dtor);
1206
0
  if (!ret)
1207
0
    return NTE_NO_MEMORY;
1208
1209
0
  ret->library = handle;
1210
0
  ret->baseProvider.enumKeysFn = NCryptP11EnumKeys;
1211
0
  ret->baseProvider.openKeyFn = NCryptP11OpenKey;
1212
1213
0
  rv = c_get_function_list(&ret->p11);
1214
0
  if (rv != CKR_OK)
1215
0
  {
1216
0
    status = NTE_PROVIDER_DLL_FAIL;
1217
0
    goto fail;
1218
0
  }
1219
1220
0
  WINPR_ASSERT(ret->p11);
1221
0
  WINPR_ASSERT(ret->p11->C_Initialize);
1222
0
  rv = ret->p11->C_Initialize(NULL);
1223
0
  if (rv != CKR_OK)
1224
0
  {
1225
0
    status = NTE_PROVIDER_DLL_FAIL;
1226
0
    goto fail;
1227
0
  }
1228
1229
0
  *phProvider = (NCRYPT_PROV_HANDLE)ret;
1230
1231
0
fail:
1232
0
  if (status != ERROR_SUCCESS)
1233
0
    ret->baseProvider.baseHandle.releaseFn((NCRYPT_HANDLE)ret);
1234
0
  return status;
1235
0
}
1236
1237
SECURITY_STATUS NCryptOpenP11StorageProviderEx(NCRYPT_PROV_HANDLE* phProvider,
1238
                                               LPCWSTR pszProviderName, DWORD dwFlags,
1239
                                               LPCSTR* modulePaths)
1240
0
{
1241
0
  SECURITY_STATUS status = ERROR_INVALID_PARAMETER;
1242
0
  LPCSTR defaultPaths[] = { "p11-kit-proxy.so", "opensc-pkcs11.so", NULL };
1243
1244
0
  if (!phProvider)
1245
0
    return ERROR_INVALID_PARAMETER;
1246
1247
0
  if (!modulePaths)
1248
0
    modulePaths = defaultPaths;
1249
1250
0
  while (*modulePaths)
1251
0
  {
1252
0
    const char* modulePath = *modulePaths++;
1253
0
    HANDLE library = LoadLibrary(modulePath);
1254
0
    typedef CK_RV (*c_get_function_list_t)(CK_FUNCTION_LIST_PTR_PTR);
1255
0
    NCryptP11ProviderHandle* provider = NULL;
1256
1257
0
    WLog_DBG(TAG, "Trying pkcs11 module '%s'", modulePath);
1258
0
    if (!library)
1259
0
    {
1260
0
      status = NTE_PROV_DLL_NOT_FOUND;
1261
0
      goto out_load_library;
1262
0
    }
1263
1264
0
    c_get_function_list_t c_get_function_list =
1265
0
        GetProcAddressAs(library, "C_GetFunctionList", c_get_function_list_t);
1266
1267
0
    if (!c_get_function_list)
1268
0
    {
1269
0
      status = NTE_PROV_TYPE_ENTRY_BAD;
1270
0
      goto out_load_library;
1271
0
    }
1272
1273
0
    status = initialize_pkcs11(library, c_get_function_list, phProvider);
1274
0
    if (status != ERROR_SUCCESS)
1275
0
    {
1276
0
      status = NTE_PROVIDER_DLL_FAIL;
1277
0
      goto out_load_library;
1278
0
    }
1279
1280
0
    provider = (NCryptP11ProviderHandle*)*phProvider;
1281
0
    provider->modulePath = _strdup(modulePath);
1282
0
    if (!provider->modulePath)
1283
0
    {
1284
0
      status = NTE_NO_MEMORY;
1285
0
      goto out_load_library;
1286
0
    }
1287
1288
0
    WLog_DBG(TAG, "module '%s' loaded", modulePath);
1289
0
    return ERROR_SUCCESS;
1290
1291
0
  out_load_library:
1292
0
    if (library)
1293
0
      FreeLibrary(library);
1294
0
  }
1295
1296
0
  return status;
1297
0
}
1298
1299
const char* NCryptGetModulePath(NCRYPT_PROV_HANDLE phProvider)
1300
0
{
1301
0
  NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)phProvider;
1302
1303
0
  WINPR_ASSERT(provider);
1304
1305
0
  return provider->modulePath;
1306
0
}