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