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