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