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