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