Coverage Report

Created: 2026-10-28 06:49

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