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