Coverage Report

Created: 2023-09-25 06:56

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