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