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