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