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