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 
490 #define ALGO_CASE(V, S) \
491  case V: \
492  r = S; \
493  break
494  switch (k)
495  {
496  ALGO_CASE(CKK_RSA, BCRYPT_RSA_ALGORITHM);
497  ALGO_CASE(CKK_DSA, BCRYPT_DSA_ALGORITHM);
498  ALGO_CASE(CKK_DH, BCRYPT_DH_ALGORITHM);
499  ALGO_CASE(CKK_EC, BCRYPT_ECDSA_ALGORITHM);
500  ALGO_CASE(CKK_RC2, BCRYPT_RC2_ALGORITHM);
501  ALGO_CASE(CKK_RC4, BCRYPT_RC4_ALGORITHM);
502  ALGO_CASE(CKK_DES, BCRYPT_DES_ALGORITHM);
503  ALGO_CASE(CKK_DES3, BCRYPT_3DES_ALGORITHM);
504  case CKK_DES2:
505  case CKK_X9_42_DH:
506  case CKK_KEA:
507  case CKK_GENERIC_SECRET:
508  case CKK_CAST:
509  case CKK_CAST3:
510  case CKK_CAST128:
511  case CKK_RC5:
512  case CKK_IDEA:
513  case CKK_SKIPJACK:
514  case CKK_BATON:
515  case CKK_JUNIPER:
516  case CKK_CDMF:
517  case CKK_AES:
518  case CKK_BLOWFISH:
519  case CKK_TWOFISH:
520  default:
521  break;
522  }
523 #undef ALGO_CASE
524 
525  size_t retLen = _wcslen(r);
526  if (retLen > UINT32_MAX)
527  return FALSE;
528 
529  if (outlen)
530  *outlen = (UINT32)retLen;
531 
532  if (!r)
533  {
534  if (dest && len > 0)
535  dest[0] = 0;
536  return FALSE;
537  }
538  else
539  {
540  if (retLen + 1 < len)
541  {
542  WLog_ERR(TAG, "target buffer is too small for algo name");
543  return FALSE;
544  }
545 
546  if (dest)
547  {
548  memcpy(dest, r, sizeof(WCHAR) * retLen);
549  dest[retLen] = 0;
550  }
551  }
552 
553  return TRUE;
554 }
555 
556 static void wprintKeyName(LPWSTR str, CK_SLOT_ID slotId, CK_BYTE* id, CK_ULONG idLen)
557 {
558  char asciiName[128] = { 0 };
559  char* ptr = asciiName;
560  const CK_BYTE* bytePtr = NULL;
561 
562  *ptr = '\\';
563  ptr++;
564 
565  bytePtr = ((CK_BYTE*)&slotId);
566  for (CK_ULONG i = 0; i < sizeof(slotId); i++, bytePtr++, ptr += 2)
567  (void)snprintf(ptr, 3, "%.2x", *bytePtr);
568 
569  *ptr = '\\';
570  ptr++;
571 
572  for (CK_ULONG i = 0; i < idLen; i++, id++, ptr += 2)
573  (void)snprintf(ptr, 3, "%.2x", *id);
574 
575  (void)ConvertUtf8NToWChar(asciiName, ARRAYSIZE(asciiName), str,
576  strnlen(asciiName, ARRAYSIZE(asciiName)) + 1);
577 }
578 
579 static size_t parseHex(const char* str, const char* end, CK_BYTE* target)
580 {
581  int ret = 0;
582 
583  for (; str != end && *str; str++, ret++, target++)
584  {
585  CK_BYTE v = 0;
586  if (*str <= '9' && *str >= '0')
587  {
588  v = (*str - '0');
589  }
590  else if (*str <= 'f' && *str >= 'a')
591  {
592  v = (10 + *str - 'a');
593  }
594  else if (*str <= 'F' && *str >= 'A')
595  {
596  v |= (10 + *str - 'A');
597  }
598  else
599  {
600  return 0;
601  }
602  v <<= 4;
603  str++;
604 
605  if (!*str || str == end)
606  return 0;
607 
608  if (*str <= '9' && *str >= '0')
609  {
610  v |= (*str - '0');
611  }
612  else if (*str <= 'f' && *str >= 'a')
613  {
614  v |= (10 + *str - 'a');
615  }
616  else if (*str <= 'F' && *str >= 'A')
617  {
618  v |= (10 + *str - 'A');
619  }
620  else
621  {
622  return 0;
623  }
624 
625  *target = v;
626  }
627  return ret;
628 }
629 
630 static SECURITY_STATUS parseKeyName(LPCWSTR pszKeyName, CK_SLOT_ID* slotId, CK_BYTE* id,
631  CK_ULONG* idLen)
632 {
633  char asciiKeyName[128] = { 0 };
634  char* pos = NULL;
635 
636  if (ConvertWCharToUtf8(pszKeyName, asciiKeyName, ARRAYSIZE(asciiKeyName)) < 0)
637  return NTE_BAD_KEY;
638 
639  if (*asciiKeyName != '\\')
640  return NTE_BAD_KEY;
641 
642  pos = strchr(&asciiKeyName[1], '\\');
643  if (!pos)
644  return NTE_BAD_KEY;
645 
646  if ((size_t)(pos - &asciiKeyName[1]) > sizeof(CK_SLOT_ID) * 2ull)
647  return NTE_BAD_KEY;
648 
649  *slotId = (CK_SLOT_ID)0;
650  if (parseHex(&asciiKeyName[1], pos, (CK_BYTE*)slotId) != sizeof(CK_SLOT_ID))
651  return NTE_BAD_KEY;
652 
653  *idLen = parseHex(pos + 1, NULL, id);
654  if (!*idLen)
655  return NTE_BAD_KEY;
656 
657  return ERROR_SUCCESS;
658 }
659 
660 static SECURITY_STATUS NCryptP11EnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope,
661  NCryptKeyName** ppKeyName, PVOID* ppEnumState,
662  DWORD dwFlags)
663 {
664  NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)hProvider;
665  P11EnumKeysState* state = (P11EnumKeysState*)*ppEnumState;
666  CK_RV rv = { 0 };
667  CK_SLOT_ID currentSlot = { 0 };
668  CK_SESSION_HANDLE currentSession = (CK_SESSION_HANDLE)NULL;
669  char slotFilterBuffer[65] = { 0 };
670  char* slotFilter = NULL;
671  size_t slotFilterLen = 0;
672 
673  SECURITY_STATUS ret = checkNCryptHandle((NCRYPT_HANDLE)hProvider, WINPR_NCRYPT_PROVIDER);
674  if (ret != ERROR_SUCCESS)
675  return ret;
676 
677  if (pszScope)
678  {
679  /*
680  * check whether pszScope is of the form \\.<reader name>\ for filtering by
681  * card reader
682  */
683  char asciiScope[128 + 6 + 1] = { 0 };
684  size_t asciiScopeLen = 0;
685 
686  if (ConvertWCharToUtf8(pszScope, asciiScope, ARRAYSIZE(asciiScope) - 1) < 0)
687  {
688  WLog_WARN(TAG, "Invalid scope");
689  return NTE_INVALID_PARAMETER;
690  }
691 
692  if (strstr(asciiScope, "\\\\.\\") != asciiScope)
693  {
694  WLog_WARN(TAG, "Invalid scope '%s'", asciiScope);
695  return NTE_INVALID_PARAMETER;
696  }
697 
698  asciiScopeLen = strnlen(asciiScope, ARRAYSIZE(asciiScope));
699  if ((asciiScopeLen < 1) || (asciiScope[asciiScopeLen - 1] != '\\'))
700  {
701  WLog_WARN(TAG, "Invalid scope '%s'", asciiScope);
702  return NTE_INVALID_PARAMETER;
703  }
704 
705  asciiScope[asciiScopeLen - 1] = 0;
706 
707  strncpy(slotFilterBuffer, &asciiScope[4], sizeof(slotFilterBuffer));
708  slotFilter = slotFilterBuffer;
709  slotFilterLen = asciiScopeLen - 5;
710  }
711 
712  if (!state)
713  {
714  state = (P11EnumKeysState*)calloc(1, sizeof(*state));
715  if (!state)
716  return NTE_NO_MEMORY;
717 
718  WINPR_ASSERT(provider->p11->C_GetSlotList);
719  rv = provider->p11->C_GetSlotList(CK_TRUE, NULL, &state->nslots);
720  if (rv != CKR_OK)
721  {
722  free(state);
723  /* TODO: perhaps convert rv to NTE_*** errors */
724  WLog_WARN(TAG, "C_GetSlotList failed with %s [0x%08" PRIx32 "]", CK_RV_error_string(rv),
725  rv);
726  return NTE_FAIL;
727  }
728 
729  if (state->nslots > MAX_SLOTS)
730  state->nslots = MAX_SLOTS;
731 
732  rv = provider->p11->C_GetSlotList(CK_TRUE, state->slots, &state->nslots);
733  if (rv != CKR_OK)
734  {
735  free(state);
736  /* TODO: perhaps convert rv to NTE_*** errors */
737  WLog_WARN(TAG, "C_GetSlotList failed with %s [0x%08" PRIx32 "]", CK_RV_error_string(rv),
738  rv);
739  return NTE_FAIL;
740  }
741 
742  ret = collect_keys(provider, state);
743  if (ret != ERROR_SUCCESS)
744  {
745  free(state);
746  return ret;
747  }
748 
749  *ppEnumState = state;
750  }
751 
752  for (; state->keyIndex < state->nKeys; state->keyIndex++)
753  {
754  NCryptKeyName* keyName = NULL;
755  NCryptKeyEnum* key = &state->keys[state->keyIndex];
756  CK_OBJECT_CLASS oclass = CKO_CERTIFICATE;
757  CK_CERTIFICATE_TYPE ctype = CKC_X_509;
758  CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) },
759  { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) },
760  { CKA_ID, key->id, key->idLen } };
761  CK_ULONG ncertObjects = 0;
762  CK_OBJECT_HANDLE certObject = 0;
763 
764  /* check the reader filter if any */
765  if (slotFilter && memcmp(key->slotInfo.slotDescription, slotFilter, slotFilterLen) != 0)
766  continue;
767 
768  if (!currentSession || (currentSlot != key->slotId))
769  {
770  /* if the current session doesn't match the current key's slot, open a new one
771  */
772  if (currentSession)
773  {
774  WINPR_ASSERT(provider->p11->C_CloseSession);
775  rv = provider->p11->C_CloseSession(currentSession);
776  if (rv != CKR_OK)
777  WLog_WARN(TAG, "C_CloseSession failed with %s [0x%08" PRIx32 "]",
778  CK_RV_error_string(rv), rv);
779  currentSession = (CK_SESSION_HANDLE)NULL;
780  }
781 
782  WINPR_ASSERT(provider->p11->C_OpenSession);
783  rv = provider->p11->C_OpenSession(key->slotId, CKF_SERIAL_SESSION, NULL, NULL,
784  &currentSession);
785  if (rv != CKR_OK)
786  {
787  WLog_ERR(TAG, "C_OpenSession failed with %s [0x%08" PRIx32 "] for slot %d",
788  CK_RV_error_string(rv), rv, key->slotId);
789  continue;
790  }
791  currentSlot = key->slotId;
792  }
793 
794  /* look if we can find a certificate that matches the key's id */
795  WINPR_ASSERT(provider->p11->C_FindObjectsInit);
796  rv = provider->p11->C_FindObjectsInit(currentSession, certificateFilter,
797  ARRAYSIZE(certificateFilter));
798  if (rv != CKR_OK)
799  {
800  WLog_ERR(TAG, "C_FindObjectsInit failed with %s [0x%08" PRIx32 "] for slot %d",
801  CK_RV_error_string(rv), rv, key->slotId);
802  continue;
803  }
804 
805  WINPR_ASSERT(provider->p11->C_FindObjects);
806  rv = provider->p11->C_FindObjects(currentSession, &certObject, 1, &ncertObjects);
807  if (rv != CKR_OK)
808  {
809  WLog_ERR(TAG, "C_FindObjects failed with %s [0x%08" PRIx32 "] for slot %d",
810  CK_RV_error_string(rv), rv, currentSlot);
811  goto cleanup_FindObjects;
812  }
813 
814  if (ncertObjects)
815  {
816  /* sizeof keyName struct + "<slotId><certId>" + keyName->pszAlgid */
817  DWORD algoSz = 0;
818  size_t KEYNAME_SZ =
819  (1 + (sizeof(key->slotId) * 2) /*slotId*/ + 1 + (key->idLen * 2) + 1) * 2;
820 
821  convertKeyType(key->keyType, NULL, 0, &algoSz);
822  KEYNAME_SZ += (1ULL + algoSz) * 2ULL;
823 
824  keyName = calloc(1, sizeof(*keyName) + KEYNAME_SZ);
825  if (!keyName)
826  {
827  WLog_ERR(TAG, "unable to allocate keyName");
828  goto cleanup_FindObjects;
829  }
830  keyName->dwLegacyKeySpec = AT_KEYEXCHANGE | AT_SIGNATURE;
831  keyName->dwFlags = NCRYPT_MACHINE_KEY_FLAG;
832  keyName->pszName = (LPWSTR)(keyName + 1);
833  wprintKeyName(keyName->pszName, key->slotId, key->id, key->idLen);
834 
835  keyName->pszAlgid = keyName->pszName + _wcslen(keyName->pszName) + 1;
836  convertKeyType(key->keyType, keyName->pszAlgid, algoSz + 1, NULL);
837  }
838 
839  cleanup_FindObjects:
840  WINPR_ASSERT(provider->p11->C_FindObjectsFinal);
841  rv = provider->p11->C_FindObjectsFinal(currentSession);
842  if (rv != CKR_OK)
843  WLog_ERR(TAG, "C_FindObjectsFinal failed with %s [0x%08" PRIx32 "]",
844  CK_RV_error_string(rv), rv);
845 
846  if (keyName)
847  {
848  *ppKeyName = keyName;
849  state->keyIndex++;
850  return ERROR_SUCCESS;
851  }
852  }
853 
854  return NTE_NO_MORE_ITEMS;
855 }
856 
857 static SECURITY_STATUS get_piv_container_name(NCryptP11KeyHandle* key, const BYTE* piv_tag,
858  BYTE* output, size_t output_len)
859 {
860  CK_SLOT_INFO slot_info = { 0 };
861  CK_FUNCTION_LIST_PTR p11 = NULL;
862  WCHAR* reader = NULL;
863  SCARDCONTEXT context = 0;
864  SCARDHANDLE card = 0;
865  DWORD proto = 0;
866  const SCARD_IO_REQUEST* pci = NULL;
867  BYTE buf[258] = { 0 };
868  char container_name[PIV_CONTAINER_NAME_LEN + 1] = { 0 };
869  DWORD buf_len = 0;
870  SECURITY_STATUS ret = NTE_BAD_KEY;
871  WinPrAsn1Decoder dec = { 0 };
872  WinPrAsn1Decoder dec2 = { 0 };
873  size_t len = 0;
874  BYTE tag = 0;
875  BYTE* p = NULL;
876  wStream s = { 0 };
877 
878  WINPR_ASSERT(key);
879  WINPR_ASSERT(piv_tag);
880 
881  WINPR_ASSERT(key->provider);
882  p11 = key->provider->p11;
883  WINPR_ASSERT(p11);
884 
885  /* Get the reader the card is in */
886  WINPR_ASSERT(p11->C_GetSlotInfo);
887  if (p11->C_GetSlotInfo(key->slotId, &slot_info) != CKR_OK)
888  return NTE_BAD_KEY;
889 
890  fix_padded_string((char*)slot_info.slotDescription, sizeof(slot_info.slotDescription));
891  reader = ConvertUtf8NToWCharAlloc((char*)slot_info.slotDescription,
892  ARRAYSIZE(slot_info.slotDescription), NULL);
893  ret = NTE_NO_MEMORY;
894  if (!reader)
895  goto out;
896 
897  ret = NTE_BAD_KEY;
898  if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &context) != SCARD_S_SUCCESS)
899  goto out;
900 
901  if (SCardConnectW(context, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &card, &proto) !=
902  SCARD_S_SUCCESS)
903  goto out;
904  pci = (proto == SCARD_PROTOCOL_T0) ? SCARD_PCI_T0 : SCARD_PCI_T1;
905 
906  buf_len = sizeof(buf);
907  if (SCardTransmit(card, pci, APDU_PIV_SELECT_AID, sizeof(APDU_PIV_SELECT_AID), NULL, buf,
908  &buf_len) != SCARD_S_SUCCESS)
909  goto out;
910  if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
911  goto out;
912 
913  buf_len = sizeof(buf);
914  if (SCardTransmit(card, pci, APDU_PIV_GET_CHUID, sizeof(APDU_PIV_GET_CHUID), NULL, buf,
915  &buf_len) != SCARD_S_SUCCESS)
916  goto out;
917  if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61)
918  goto out;
919 
920  /* Find the GUID field in the CHUID data object */
921  WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_BER, buf, buf_len);
922  if (!WinPrAsn1DecReadTagAndLen(&dec, &tag, &len) || tag != 0x53)
923  goto out;
924  while (WinPrAsn1DecReadTagLenValue(&dec, &tag, &len, &dec2) && tag != 0x34)
925  ;
926  if (tag != 0x34 || len != 16)
927  goto out;
928 
929  s = WinPrAsn1DecGetStream(&dec2);
930  p = Stream_Buffer(&s);
931 
932  /* Construct the value Windows would use for a PIV key's container name */
933  (void)snprintf(container_name, PIV_CONTAINER_NAME_LEN + 1,
934  "%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", p[3],
935  p[2], p[1], p[0], p[5], p[4], p[7], p[6], p[8], p[9], p[10], p[11], p[12],
936  piv_tag[0], piv_tag[1], piv_tag[2]);
937 
938  /* And convert it to UTF-16 */
939  union
940  {
941  WCHAR* wc;
942  BYTE* b;
943  } cnv;
944  cnv.b = output;
945  if (ConvertUtf8NToWChar(container_name, ARRAYSIZE(container_name), cnv.wc,
946  output_len / sizeof(WCHAR)) > 0)
947  ret = ERROR_SUCCESS;
948 
949 out:
950  free(reader);
951  if (card)
952  SCardDisconnect(card, SCARD_LEAVE_CARD);
953  if (context)
954  SCardReleaseContext(context);
955  return ret;
956 }
957 
958 static SECURITY_STATUS check_for_piv_container_name(NCryptP11KeyHandle* key, BYTE* pbOutput,
959  DWORD cbOutput, DWORD* pcbResult, char* label,
960  size_t label_len)
961 {
962  for (size_t i = 0; i < ARRAYSIZE(piv_cert_tags); i++)
963  {
964  const piv_cert_tags_t* cur = &piv_cert_tags[i];
965  if (strncmp(label, cur->label, label_len) == 0)
966  {
967  *pcbResult = (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR);
968  if (!pbOutput)
969  return ERROR_SUCCESS;
970  else if (cbOutput < (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR))
971  return NTE_NO_MEMORY;
972  else
973  return get_piv_container_name(key, cur->tag, pbOutput, cbOutput);
974  }
975  }
976  return NTE_NOT_FOUND;
977 }
978 
979 static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle,
980  NCryptKeyGetPropertyEnum property, PBYTE pbOutput,
981  DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags)
982 {
983  SECURITY_STATUS ret = NTE_FAIL;
984  CK_RV rv = 0;
985  CK_SESSION_HANDLE session = 0;
986  CK_OBJECT_HANDLE objectHandle = 0;
987  CK_ULONG objectCount = 0;
988  NCryptP11ProviderHandle* provider = NULL;
989  CK_OBJECT_CLASS oclass = CKO_CERTIFICATE;
990  CK_CERTIFICATE_TYPE ctype = CKC_X_509;
991  CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) },
992  { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) },
993  { CKA_ID, keyHandle->keyCertId,
994  keyHandle->keyCertIdLen } };
995  CK_ATTRIBUTE* objectFilter = certificateFilter;
996  CK_ULONG objectFilterLen = ARRAYSIZE(certificateFilter);
997 
998  WINPR_ASSERT(keyHandle);
999  provider = keyHandle->provider;
1000  WINPR_ASSERT(provider);
1001 
1002  switch (property)
1003 
1004  {
1005  case NCRYPT_PROPERTY_CERTIFICATE:
1006  case NCRYPT_PROPERTY_NAME:
1007  break;
1008  case NCRYPT_PROPERTY_READER:
1009  {
1010  CK_SLOT_INFO slotInfo;
1011 
1012  WINPR_ASSERT(provider->p11->C_GetSlotInfo);
1013  rv = provider->p11->C_GetSlotInfo(keyHandle->slotId, &slotInfo);
1014  if (rv != CKR_OK)
1015  return NTE_BAD_KEY;
1016 
1017 #define SLOT_DESC_SZ sizeof(slotInfo.slotDescription)
1018  fix_padded_string((char*)slotInfo.slotDescription, SLOT_DESC_SZ);
1019  const size_t len = 2ULL * (strnlen((char*)slotInfo.slotDescription, SLOT_DESC_SZ) + 1);
1020  if (len > UINT32_MAX)
1021  return NTE_BAD_DATA;
1022  *pcbResult = (UINT32)len;
1023  if (pbOutput)
1024  {
1025  union
1026  {
1027  WCHAR* wc;
1028  BYTE* b;
1029  } cnv;
1030  cnv.b = pbOutput;
1031  if (cbOutput < *pcbResult)
1032  return NTE_NO_MEMORY;
1033 
1034  if (ConvertUtf8ToWChar((char*)slotInfo.slotDescription, cnv.wc,
1035  cbOutput / sizeof(WCHAR)) < 0)
1036  return NTE_NO_MEMORY;
1037  }
1038  return ERROR_SUCCESS;
1039  }
1040  case NCRYPT_PROPERTY_SLOTID:
1041  {
1042  *pcbResult = 4;
1043  if (pbOutput)
1044  {
1045  UINT32* ptr = (UINT32*)pbOutput;
1046 
1047  if (cbOutput < 4)
1048  return NTE_NO_MEMORY;
1049  if (keyHandle->slotId > UINT32_MAX)
1050  {
1051  ret = NTE_BAD_DATA;
1052  goto out_final;
1053  }
1054  *ptr = (UINT32)keyHandle->slotId;
1055  }
1056  return ERROR_SUCCESS;
1057  }
1058  case NCRYPT_PROPERTY_UNKNOWN:
1059  default:
1060  return NTE_NOT_SUPPORTED;
1061  }
1062 
1063  WINPR_ASSERT(provider->p11->C_OpenSession);
1064  rv = provider->p11->C_OpenSession(keyHandle->slotId, CKF_SERIAL_SESSION, NULL, NULL, &session);
1065  if (rv != CKR_OK)
1066  {
1067  WLog_ERR(TAG, "error opening session on slot %d", keyHandle->slotId);
1068  return NTE_FAIL;
1069  }
1070 
1071  WINPR_ASSERT(provider->p11->C_FindObjectsInit);
1072  rv = provider->p11->C_FindObjectsInit(session, objectFilter, objectFilterLen);
1073  if (rv != CKR_OK)
1074  {
1075  WLog_ERR(TAG, "unable to initiate search for slot %d", keyHandle->slotId);
1076  goto out;
1077  }
1078 
1079  WINPR_ASSERT(provider->p11->C_FindObjects);
1080  rv = provider->p11->C_FindObjects(session, &objectHandle, 1, &objectCount);
1081  if (rv != CKR_OK)
1082  {
1083  WLog_ERR(TAG, "unable to findObjects for slot %d", keyHandle->slotId);
1084  goto out_final;
1085  }
1086  if (!objectCount)
1087  {
1088  ret = NTE_NOT_FOUND;
1089  goto out_final;
1090  }
1091 
1092  switch (property)
1093  {
1094  case NCRYPT_PROPERTY_CERTIFICATE:
1095  {
1096  CK_ATTRIBUTE certValue = { CKA_VALUE, pbOutput, cbOutput };
1097 
1098  WINPR_ASSERT(provider->p11->C_GetAttributeValue);
1099  rv = provider->p11->C_GetAttributeValue(session, objectHandle, &certValue, 1);
1100  if (rv != CKR_OK)
1101  {
1102  // TODO: do a kind of translation from CKR_* to NTE_*
1103  }
1104 
1105  if (certValue.ulValueLen > UINT32_MAX)
1106  {
1107  ret = NTE_BAD_DATA;
1108  goto out_final;
1109  }
1110  *pcbResult = (UINT32)certValue.ulValueLen;
1111  ret = ERROR_SUCCESS;
1112  break;
1113  }
1114  case NCRYPT_PROPERTY_NAME:
1115  {
1116  CK_ATTRIBUTE attr = { CKA_LABEL, NULL, 0 };
1117  char* label = NULL;
1118 
1119  WINPR_ASSERT(provider->p11->C_GetAttributeValue);
1120  rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
1121  if (rv == CKR_OK)
1122  {
1123  label = calloc(1, attr.ulValueLen);
1124  if (!label)
1125  {
1126  ret = NTE_NO_MEMORY;
1127  break;
1128  }
1129 
1130  attr.pValue = label;
1131  rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1);
1132  }
1133 
1134  if (rv == CKR_OK)
1135  {
1136  /* Check if we have a PIV card */
1137  ret = check_for_piv_container_name(keyHandle, pbOutput, cbOutput, pcbResult, label,
1138  attr.ulValueLen);
1139 
1140  /* Otherwise, at least for GIDS cards the label will be the correct value */
1141  if (ret == NTE_NOT_FOUND)
1142  {
1143  union
1144  {
1145  WCHAR* wc;
1146  BYTE* b;
1147  } cnv;
1148  const size_t olen = pbOutput ? cbOutput / sizeof(WCHAR) : 0;
1149  cnv.b = pbOutput;
1150  SSIZE_T size = ConvertUtf8NToWChar(label, attr.ulValueLen, cnv.wc, olen);
1151  if (size < 0)
1152  ret = ERROR_CONVERT_TO_LARGE;
1153  else
1154  ret = ERROR_SUCCESS;
1155  }
1156  }
1157 
1158  free(label);
1159  break;
1160  }
1161  default:
1162  ret = NTE_NOT_SUPPORTED;
1163  break;
1164  }
1165 
1166 out_final:
1167  WINPR_ASSERT(provider->p11->C_FindObjectsFinal);
1168  rv = provider->p11->C_FindObjectsFinal(session);
1169  if (rv != CKR_OK)
1170  {
1171  WLog_ERR(TAG, "error in C_FindObjectsFinal() for slot %d", keyHandle->slotId);
1172  }
1173 out:
1174  WINPR_ASSERT(provider->p11->C_CloseSession);
1175  rv = provider->p11->C_CloseSession(session);
1176  if (rv != CKR_OK)
1177  {
1178  WLog_ERR(TAG, "error in C_CloseSession() for slot %d", keyHandle->slotId);
1179  }
1180  return ret;
1181 }
1182 
1183 static SECURITY_STATUS NCryptP11GetProperty(NCRYPT_HANDLE hObject, NCryptKeyGetPropertyEnum prop,
1184  PBYTE pbOutput, DWORD cbOutput, DWORD* pcbResult,
1185  DWORD dwFlags)
1186 {
1187  NCryptBaseHandle* base = (NCryptBaseHandle*)hObject;
1188 
1189  WINPR_ASSERT(base);
1190  switch (base->type)
1191  {
1192  case WINPR_NCRYPT_PROVIDER:
1193  return ERROR_CALL_NOT_IMPLEMENTED;
1194  case WINPR_NCRYPT_KEY:
1195  return NCryptP11KeyGetProperties((NCryptP11KeyHandle*)hObject, prop, pbOutput, cbOutput,
1196  pcbResult, dwFlags);
1197  default:
1198  return ERROR_INVALID_HANDLE;
1199  }
1200  return ERROR_SUCCESS;
1201 }
1202 
1203 static SECURITY_STATUS NCryptP11OpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey,
1204  LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, DWORD dwFlags)
1205 {
1206  SECURITY_STATUS ret = 0;
1207  CK_SLOT_ID slotId = 0;
1208  CK_BYTE keyCertId[64] = { 0 };
1209  CK_ULONG keyCertIdLen = 0;
1210  NCryptP11KeyHandle* keyHandle = NULL;
1211 
1212  ret = parseKeyName(pszKeyName, &slotId, keyCertId, &keyCertIdLen);
1213  if (ret != ERROR_SUCCESS)
1214  return ret;
1215 
1216  keyHandle = (NCryptP11KeyHandle*)ncrypt_new_handle(
1217  WINPR_NCRYPT_KEY, sizeof(*keyHandle), NCryptP11GetProperty, winpr_NCryptDefault_dtor);
1218  if (!keyHandle)
1219  return NTE_NO_MEMORY;
1220 
1221  keyHandle->provider = (NCryptP11ProviderHandle*)hProvider;
1222  keyHandle->slotId = slotId;
1223  memcpy(keyHandle->keyCertId, keyCertId, sizeof(keyCertId));
1224  keyHandle->keyCertIdLen = keyCertIdLen;
1225  *phKey = (NCRYPT_KEY_HANDLE)keyHandle;
1226  return ERROR_SUCCESS;
1227 }
1228 
1229 static SECURITY_STATUS initialize_pkcs11(HANDLE handle,
1230  CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR),
1231  NCRYPT_PROV_HANDLE* phProvider)
1232 {
1233  SECURITY_STATUS status = ERROR_SUCCESS;
1234  NCryptP11ProviderHandle* ret = NULL;
1235  CK_RV rv = 0;
1236 
1237  WINPR_ASSERT(c_get_function_list);
1238  WINPR_ASSERT(phProvider);
1239 
1240  ret = (NCryptP11ProviderHandle*)ncrypt_new_handle(
1241  WINPR_NCRYPT_PROVIDER, sizeof(*ret), NCryptP11GetProperty, NCryptP11StorageProvider_dtor);
1242  if (!ret)
1243  return NTE_NO_MEMORY;
1244 
1245  ret->library = handle;
1246  ret->baseProvider.enumKeysFn = NCryptP11EnumKeys;
1247  ret->baseProvider.openKeyFn = NCryptP11OpenKey;
1248 
1249  rv = c_get_function_list(&ret->p11);
1250  if (rv != CKR_OK)
1251  {
1252  status = NTE_PROVIDER_DLL_FAIL;
1253  goto fail;
1254  }
1255 
1256  WINPR_ASSERT(ret->p11);
1257  WINPR_ASSERT(ret->p11->C_Initialize);
1258  rv = ret->p11->C_Initialize(NULL);
1259  if (rv != CKR_OK)
1260  {
1261  status = NTE_PROVIDER_DLL_FAIL;
1262  goto fail;
1263  }
1264 
1265  *phProvider = (NCRYPT_PROV_HANDLE)ret;
1266 
1267 fail:
1268  if (status != ERROR_SUCCESS)
1269  ret->baseProvider.baseHandle.releaseFn((NCRYPT_HANDLE)ret);
1270  return status;
1271 }
1272 
1273 SECURITY_STATUS NCryptOpenP11StorageProviderEx(NCRYPT_PROV_HANDLE* phProvider,
1274  LPCWSTR pszProviderName, DWORD dwFlags,
1275  LPCSTR* modulePaths)
1276 {
1277  SECURITY_STATUS status = ERROR_INVALID_PARAMETER;
1278  LPCSTR defaultPaths[] = { "p11-kit-proxy.so", "opensc-pkcs11.so", NULL };
1279 
1280  if (!phProvider)
1281  return ERROR_INVALID_PARAMETER;
1282 
1283  if (!modulePaths)
1284  modulePaths = defaultPaths;
1285 
1286  while (*modulePaths)
1287  {
1288  const char* modulePath = *modulePaths++;
1289  HANDLE library = LoadLibrary(modulePath);
1290  typedef CK_RV (*c_get_function_list_t)(CK_FUNCTION_LIST_PTR_PTR);
1291  NCryptP11ProviderHandle* provider = NULL;
1292 
1293  WLog_DBG(TAG, "Trying pkcs11 module '%s'", modulePath);
1294  if (!library)
1295  {
1296  status = NTE_PROV_DLL_NOT_FOUND;
1297  goto out_load_library;
1298  }
1299 
1300  c_get_function_list_t c_get_function_list =
1301  GetProcAddressAs(library, "C_GetFunctionList", c_get_function_list_t);
1302 
1303  if (!c_get_function_list)
1304  {
1305  status = NTE_PROV_TYPE_ENTRY_BAD;
1306  goto out_load_library;
1307  }
1308 
1309  status = initialize_pkcs11(library, c_get_function_list, phProvider);
1310  if (status != ERROR_SUCCESS)
1311  {
1312  status = NTE_PROVIDER_DLL_FAIL;
1313  goto out_load_library;
1314  }
1315 
1316  provider = (NCryptP11ProviderHandle*)*phProvider;
1317  provider->modulePath = _strdup(modulePath);
1318  if (!provider->modulePath)
1319  {
1320  status = NTE_NO_MEMORY;
1321  goto out_load_library;
1322  }
1323 
1324  WLog_DBG(TAG, "module '%s' loaded", modulePath);
1325  return ERROR_SUCCESS;
1326 
1327  out_load_library:
1328  if (library)
1329  FreeLibrary(library);
1330  }
1331 
1332  return status;
1333 }
1334 
1335 const char* NCryptGetModulePath(NCRYPT_PROV_HANDLE phProvider)
1336 {
1337  NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)phProvider;
1338 
1339  WINPR_ASSERT(provider);
1340 
1341  return provider->modulePath;
1342 }
common ncrypt handle items
common ncrypt provider items
a key name descriptor