]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pkcs11-util.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / shared / pkcs11-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
839fddbe
LP
2
3#include <fcntl.h>
4
5#include "ask-password-api.h"
da035a3a 6#include "dlfcn-util.h"
7a6abbe9 7#include "env-util.h"
839fddbe
LP
8#include "escape.h"
9#include "fd-util.h"
f240cbb6 10#include "format-table.h"
839fddbe
LP
11#include "io-util.h"
12#include "memory-util.h"
13#if HAVE_OPENSSL
14#include "openssl-util.h"
15#endif
16#include "pkcs11-util.h"
17#include "random-util.h"
18#include "string-util.h"
19#include "strv.h"
20
21bool pkcs11_uri_valid(const char *uri) {
22 const char *p;
23
24 /* A very superficial checker for RFC7512 PKCS#11 URI syntax */
25
26 if (isempty(uri))
27 return false;
28
29 p = startswith(uri, "pkcs11:");
30 if (!p)
31 return false;
32
33 if (isempty(p))
34 return false;
35
b204bdd4 36 if (!in_charset(p, ALPHANUMERICAL ".~/-_?;&%="))
839fddbe
LP
37 return false;
38
39 return true;
40}
41
42#if HAVE_P11KIT
43
da035a3a
LB
44static void *p11kit_dl = NULL;
45
46char *(*sym_p11_kit_module_get_name)(CK_FUNCTION_LIST *module);
47void (*sym_p11_kit_modules_finalize_and_release)(CK_FUNCTION_LIST **modules);
48CK_FUNCTION_LIST **(*sym_p11_kit_modules_load_and_initialize)(int flags);
49const char *(*sym_p11_kit_strerror)(CK_RV rv);
50int (*sym_p11_kit_uri_format)(P11KitUri *uri, P11KitUriType uri_type, char **string);
51void (*sym_p11_kit_uri_free)(P11KitUri *uri);
52CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attributes)(P11KitUri *uri, CK_ULONG *n_attrs);
53CK_INFO_PTR (*sym_p11_kit_uri_get_module_info)(P11KitUri *uri);
54CK_SLOT_INFO_PTR (*sym_p11_kit_uri_get_slot_info)(P11KitUri *uri);
55CK_TOKEN_INFO_PTR (*sym_p11_kit_uri_get_token_info)(P11KitUri *uri);
56int (*sym_p11_kit_uri_match_token_info)(const P11KitUri *uri, const CK_TOKEN_INFO *token_info);
57const char *(*sym_p11_kit_uri_message)(int code);
58P11KitUri *(*sym_p11_kit_uri_new)(void);
59int (*sym_p11_kit_uri_parse)(const char *string, P11KitUriType uri_type, P11KitUri *uri);
60
61int dlopen_p11kit(void) {
62 return dlopen_many_sym_or_warn(
63 &p11kit_dl,
64 "libp11-kit.so.0", LOG_DEBUG,
65 DLSYM_ARG(p11_kit_module_get_name),
66 DLSYM_ARG(p11_kit_modules_finalize_and_release),
67 DLSYM_ARG(p11_kit_modules_load_and_initialize),
68 DLSYM_ARG(p11_kit_strerror),
69 DLSYM_ARG(p11_kit_uri_format),
70 DLSYM_ARG(p11_kit_uri_free),
71 DLSYM_ARG(p11_kit_uri_get_attributes),
72 DLSYM_ARG(p11_kit_uri_get_module_info),
73 DLSYM_ARG(p11_kit_uri_get_slot_info),
74 DLSYM_ARG(p11_kit_uri_get_token_info),
75 DLSYM_ARG(p11_kit_uri_match_token_info),
76 DLSYM_ARG(p11_kit_uri_message),
77 DLSYM_ARG(p11_kit_uri_new),
78 DLSYM_ARG(p11_kit_uri_parse));
79}
80
839fddbe 81int uri_from_string(const char *p, P11KitUri **ret) {
da035a3a
LB
82 _cleanup_(sym_p11_kit_uri_freep) P11KitUri *uri = NULL;
83 int r;
839fddbe
LP
84
85 assert(p);
86 assert(ret);
87
da035a3a
LB
88 r = dlopen_p11kit();
89 if (r < 0)
90 return r;
91
92 uri = sym_p11_kit_uri_new();
839fddbe
LP
93 if (!uri)
94 return -ENOMEM;
95
da035a3a 96 if (sym_p11_kit_uri_parse(p, P11_KIT_URI_FOR_ANY, uri) != P11_KIT_URI_OK)
839fddbe
LP
97 return -EINVAL;
98
99 *ret = TAKE_PTR(uri);
100 return 0;
101}
102
103P11KitUri *uri_from_module_info(const CK_INFO *info) {
104 P11KitUri *uri;
105
106 assert(info);
107
da035a3a
LB
108 if (dlopen_p11kit() < 0)
109 return NULL;
110
111 uri = sym_p11_kit_uri_new();
839fddbe
LP
112 if (!uri)
113 return NULL;
114
da035a3a 115 *sym_p11_kit_uri_get_module_info(uri) = *info;
839fddbe
LP
116 return uri;
117}
118
119P11KitUri *uri_from_slot_info(const CK_SLOT_INFO *slot_info) {
120 P11KitUri *uri;
121
122 assert(slot_info);
123
da035a3a
LB
124 if (dlopen_p11kit() < 0)
125 return NULL;
126
127 uri = sym_p11_kit_uri_new();
839fddbe
LP
128 if (!uri)
129 return NULL;
130
da035a3a 131 *sym_p11_kit_uri_get_slot_info(uri) = *slot_info;
839fddbe
LP
132 return uri;
133}
134
135P11KitUri *uri_from_token_info(const CK_TOKEN_INFO *token_info) {
136 P11KitUri *uri;
137
138 assert(token_info);
139
da035a3a
LB
140 if (dlopen_p11kit() < 0)
141 return NULL;
142
143 uri = sym_p11_kit_uri_new();
839fddbe
LP
144 if (!uri)
145 return NULL;
146
da035a3a 147 *sym_p11_kit_uri_get_token_info(uri) = *token_info;
839fddbe
LP
148 return uri;
149}
150
151CK_RV pkcs11_get_slot_list_malloc(
152 CK_FUNCTION_LIST *m,
153 CK_SLOT_ID **ret_slotids,
154 CK_ULONG *ret_n_slotids) {
155
156 CK_RV rv;
157
158 assert(m);
159 assert(ret_slotids);
160 assert(ret_n_slotids);
161
162 for (unsigned tries = 0; tries < 16; tries++) {
163 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
164 CK_ULONG n_slotids = 0;
165
166 rv = m->C_GetSlotList(0, NULL, &n_slotids);
167 if (rv != CKR_OK)
168 return rv;
169 if (n_slotids == 0) {
170 *ret_slotids = NULL;
171 *ret_n_slotids = 0;
172 return CKR_OK;
173 }
174
175 slotids = new(CK_SLOT_ID, n_slotids);
176 if (!slotids)
177 return CKR_HOST_MEMORY;
178
179 rv = m->C_GetSlotList(0, slotids, &n_slotids);
180 if (rv == CKR_OK) {
181 *ret_slotids = TAKE_PTR(slotids);
182 *ret_n_slotids = n_slotids;
183 return CKR_OK;
184 }
185
186 if (rv != CKR_BUFFER_TOO_SMALL)
187 return rv;
188
189 /* Hu? Maybe somebody plugged something in and things changed? Let's try again */
190 }
191
192 return CKR_BUFFER_TOO_SMALL;
193}
194
195char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
196 char *t;
197
198 /* The label is not NUL terminated and likely padded with spaces, let's make a copy here, so that we
199 * can strip that. */
200 t = strndup((char*) token_info->label, sizeof(token_info->label));
201 if (!t)
202 return NULL;
203
204 strstrip(t);
205 return t;
206}
207
0eb3be46
LP
208char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info) {
209 char *t;
210
211 t = strndup((char*) token_info->manufacturerID, sizeof(token_info->manufacturerID));
212 if (!t)
213 return NULL;
214
215 strstrip(t);
216 return t;
217}
218
219char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
220 char *t;
221
222 t = strndup((char*) token_info->model, sizeof(token_info->model));
223 if (!t)
224 return NULL;
225
226 strstrip(t);
227 return t;
228}
229
0ff60566
OK
230int pkcs11_token_login_by_pin(
231 CK_FUNCTION_LIST *m,
232 CK_SESSION_HANDLE session,
233 const CK_TOKEN_INFO *token_info,
234 const char *token_label,
235 const void *pin,
236 size_t pin_size) {
237
238 CK_RV rv;
da035a3a 239 int r;
0ff60566
OK
240
241 assert(m);
242 assert(token_info);
243
da035a3a
LB
244 r = dlopen_p11kit();
245 if (r < 0)
246 return r;
247
0ff60566
OK
248 if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
249 rv = m->C_Login(session, CKU_USER, NULL, 0);
250 if (rv != CKR_OK)
251 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 252 "Failed to log into security token '%s': %s", token_label, sym_p11_kit_strerror(rv));
0ff60566
OK
253
254 log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
255 return 0;
256 }
257
258 if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
259 log_info("No login into security token '%s' required.", token_label);
260 return 0;
261 }
262
263 if (!pin)
264 return -ENOANO;
265
266 rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) pin, pin_size);
267 if (rv == CKR_OK) {
268 log_info("Successfully logged into security token '%s'.", token_label);
269 return 0;
270 }
271
272 if (rv == CKR_PIN_LOCKED)
273 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
274 "PIN has been locked, please reset PIN of security token '%s'.", token_label);
275 if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
276 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 277 "Failed to log into security token '%s': %s", token_label, sym_p11_kit_strerror(rv));
0ff60566 278
b98416e1
FS
279 return log_notice_errno(SYNTHETIC_ERRNO(ENOLCK),
280 "PIN for token '%s' is incorrect, please try again.",
281 token_label);
0ff60566
OK
282}
283
839fddbe
LP
284int pkcs11_token_login(
285 CK_FUNCTION_LIST *m,
286 CK_SESSION_HANDLE session,
287 CK_SLOT_ID slotid,
288 const CK_TOKEN_INFO *token_info,
289 const char *friendly_name,
290 const char *icon_name,
8806bb4b
LP
291 const char *key_name,
292 const char *credential_name,
839fddbe 293 usec_t until,
a758a128 294 AskPasswordFlags ask_password_flags,
cd5f57bd 295 bool headless,
839fddbe
LP
296 char **ret_used_pin) {
297
298 _cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
da035a3a 299 _cleanup_(sym_p11_kit_uri_freep) P11KitUri *token_uri = NULL;
839fddbe 300 CK_TOKEN_INFO updated_token_info;
0eb3be46 301 int uri_result, r;
839fddbe 302 CK_RV rv;
839fddbe
LP
303
304 assert(m);
305 assert(token_info);
306
da035a3a
LB
307 r = dlopen_p11kit();
308 if (r < 0)
309 return r;
310
839fddbe
LP
311 token_label = pkcs11_token_label(token_info);
312 if (!token_label)
313 return log_oom();
314
315 token_uri = uri_from_token_info(token_info);
316 if (!token_uri)
317 return log_oom();
318
da035a3a 319 uri_result = sym_p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
839fddbe 320 if (uri_result != P11_KIT_URI_OK)
da035a3a 321 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", sym_p11_kit_uri_message(uri_result));
839fddbe 322
0ff60566
OK
323 r = pkcs11_token_login_by_pin(m, session, token_info, token_label, /* pin= */ NULL, 0);
324 if (r == 0 && ret_used_pin)
325 *ret_used_pin = NULL;
839fddbe 326
0ff60566
OK
327 if (r != -ENOANO) /* pin required */
328 return r;
839fddbe
LP
329
330 token_uri_escaped = cescape(token_uri_string);
331 if (!token_uri_escaped)
332 return log_oom();
333
334 id = strjoin("pkcs11:", token_uri_escaped);
335 if (!id)
336 return log_oom();
337
338 for (unsigned tries = 0; tries < 3; tries++) {
339 _cleanup_strv_free_erase_ char **passwords = NULL;
e99ca147 340 _cleanup_(erase_and_freep) char *envpin = NULL;
e99ca147
LP
341
342 r = getenv_steal_erase("PIN", &envpin);
343 if (r < 0)
344 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
345 if (r > 0) {
346 passwords = strv_new(envpin);
839fddbe
LP
347 if (!passwords)
348 return log_oom();
349
cd5f57bd
LB
350 } else if (headless)
351 return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the 'PIN' environment variable.");
352 else {
c63ec11b
LP
353 _cleanup_free_ char *text = NULL;
354
355 if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
356 r = asprintf(&text,
357 "Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
358 token_label, friendly_name);
359 else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
360 r = asprintf(&text,
361 "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
362 token_label, friendly_name);
363 else if (tries == 0)
364 r = asprintf(&text,
365 "Please enter PIN for security token '%s' in order to unlock %s:",
366 token_label, friendly_name);
367 else
368 r = asprintf(&text,
369 "Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
370 token_label, friendly_name, tries+1);
371 if (r < 0)
372 return log_oom();
373
839fddbe 374 /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
a758a128 375 r = ask_password_auto(text, icon_name, id, key_name, credential_name, until, ask_password_flags, &passwords);
839fddbe
LP
376 if (r < 0)
377 return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
378 }
379
380 STRV_FOREACH(i, passwords) {
0ff60566
OK
381 r = pkcs11_token_login_by_pin(m, session, token_info, token_label, *i, strlen(*i));
382 if (r == 0 && ret_used_pin) {
383 char *c;
839fddbe 384
0ff60566
OK
385 c = strdup(*i);
386 if (!c)
387 return log_oom();
839fddbe 388
0ff60566 389 *ret_used_pin = c;
839fddbe 390 }
0ff60566
OK
391
392 if (r != -ENOLCK)
393 return r;
839fddbe 394
fcdd21ec 395 /* Refresh the token info, so that we can prompt knowing the new flags if they changed. */
839fddbe
LP
396 rv = m->C_GetTokenInfo(slotid, &updated_token_info);
397 if (rv != CKR_OK)
398 return log_error_errno(SYNTHETIC_ERRNO(EIO),
399 "Failed to acquire updated security token information for slot %lu: %s",
da035a3a 400 slotid, sym_p11_kit_strerror(rv));
839fddbe
LP
401
402 token_info = &updated_token_info;
839fddbe
LP
403 }
404 }
405
406 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to log into token '%s'.", token_label);
407}
408
409int pkcs11_token_find_x509_certificate(
410 CK_FUNCTION_LIST *m,
411 CK_SESSION_HANDLE session,
412 P11KitUri *search_uri,
413 CK_OBJECT_HANDLE *ret_object) {
414
415 bool found_class = false, found_certificate_type = false;
416 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
417 CK_ULONG n_attributes, a, n_objects;
418 CK_ATTRIBUTE *attributes = NULL;
419 CK_OBJECT_HANDLE objects[2];
420 CK_RV rv, rv2;
da035a3a 421 int r;
839fddbe
LP
422
423 assert(m);
424 assert(search_uri);
425 assert(ret_object);
426
da035a3a
LB
427 r = dlopen_p11kit();
428 if (r < 0)
429 return r;
430
431 attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
839fddbe
LP
432 for (a = 0; a < n_attributes; a++) {
433
434 /* We use the URI's included match attributes, but make them more strict. This allows users
435 * to specify a token URL instead of an object URL and the right thing should happen if
436 * there's only one suitable key on the token. */
437
438 switch (attributes[a].type) {
439
440 case CKA_CLASS: {
441 CK_OBJECT_CLASS c;
442
443 if (attributes[a].ulValueLen != sizeof(c))
444 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
445
446 memcpy(&c, attributes[a].pValue, sizeof(c));
447 if (c != CKO_CERTIFICATE)
448 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
449
450 found_class = true;
451 break;
452 }
453
454 case CKA_CERTIFICATE_TYPE: {
455 CK_CERTIFICATE_TYPE t;
456
457 if (attributes[a].ulValueLen != sizeof(t))
458 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CERTIFICATE_TYPE attribute size.");
459
460 memcpy(&t, attributes[a].pValue, sizeof(t));
461 if (t != CKC_X_509)
462 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
463
464 found_certificate_type = true;
465 break;
466 }}
467 }
468
469 if (!found_class || !found_certificate_type) {
470 /* Hmm, let's slightly extend the attribute list we search for */
471
472 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_class + !found_certificate_type);
473 if (!attributes_buffer)
474 return log_oom();
475
476 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
477
478 if (!found_class) {
479 static const CK_OBJECT_CLASS class = CKO_CERTIFICATE;
480
481 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
482 .type = CKA_CLASS,
483 .pValue = (CK_OBJECT_CLASS*) &class,
484 .ulValueLen = sizeof(class),
485 };
486 }
487
488 if (!found_certificate_type) {
489 static const CK_CERTIFICATE_TYPE type = CKC_X_509;
490
491 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
492 .type = CKA_CERTIFICATE_TYPE,
493 .pValue = (CK_CERTIFICATE_TYPE*) &type,
494 .ulValueLen = sizeof(type),
495 };
496 }
497
498 attributes = attributes_buffer;
499 }
500
501 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
502 if (rv != CKR_OK)
503 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 504 "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
505
506 rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
507 rv2 = m->C_FindObjectsFinal(session);
508 if (rv != CKR_OK)
509 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 510 "Failed to find objects: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
511 if (rv2 != CKR_OK)
512 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 513 "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
514 if (n_objects == 0)
515 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
516 "Failed to find selected X509 certificate on token.");
517 if (n_objects > 1)
518 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
519 "Configured URI matches multiple certificates, refusing.");
520
521 *ret_object = objects[0];
522 return 0;
523}
524
525#if HAVE_OPENSSL
526int pkcs11_token_read_x509_certificate(
527 CK_FUNCTION_LIST *m,
528 CK_SESSION_HANDLE session,
529 CK_OBJECT_HANDLE object,
530 X509 **ret_cert) {
531
532 _cleanup_free_ void *buffer = NULL;
533 _cleanup_free_ char *t = NULL;
534 CK_ATTRIBUTE attribute = {
535 .type = CKA_VALUE
536 };
537 CK_RV rv;
538 _cleanup_(X509_freep) X509 *x509 = NULL;
539 X509_NAME *name = NULL;
540 const unsigned char *p;
da035a3a
LB
541 int r;
542
543 r = dlopen_p11kit();
544 if (r < 0)
545 return r;
839fddbe
LP
546
547 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
548 if (rv != CKR_OK)
549 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 550 "Failed to read X.509 certificate size off token: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
551
552 buffer = malloc(attribute.ulValueLen);
553 if (!buffer)
554 return log_oom();
555
556 attribute.pValue = buffer;
557
558 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
559 if (rv != CKR_OK)
560 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 561 "Failed to read X.509 certificate data off token: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
562
563 p = attribute.pValue;
564 x509 = d2i_X509(NULL, &p, attribute.ulValueLen);
565 if (!x509)
566 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed parse X.509 certificate.");
567
568 name = X509_get_subject_name(x509);
569 if (!name)
570 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to acquire X.509 subject name.");
571
572 t = X509_NAME_oneline(name, NULL, 0);
573 if (!t)
574 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format X.509 subject name as string.");
575
576 log_debug("Using X.509 certificate issued for '%s'.", t);
577
578 *ret_cert = TAKE_PTR(x509);
579 return 0;
580}
581#endif
582
583int pkcs11_token_find_private_key(
584 CK_FUNCTION_LIST *m,
585 CK_SESSION_HANDLE session,
586 P11KitUri *search_uri,
587 CK_OBJECT_HANDLE *ret_object) {
588
3d05c058
VS
589 uint_fast8_t n_objects = 0;
590 bool found_class = false;
839fddbe 591 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
3d05c058
VS
592 CK_OBJECT_HANDLE object, candidate;
593 static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
594 CK_BBOOL decrypt_value, derive_value;
595 CK_ATTRIBUTE optional_attributes[] = {
596 { CKA_DECRYPT, &decrypt_value, sizeof(decrypt_value) },
597 { CKA_DERIVE, &derive_value, sizeof(derive_value) }
598 };
599 CK_RV rv;
839fddbe
LP
600
601 assert(m);
602 assert(search_uri);
603 assert(ret_object);
604
3d05c058
VS
605 CK_ULONG n_attributes;
606 CK_ATTRIBUTE *attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
607 for (CK_ULONG i = 0; i < n_attributes; i++) {
839fddbe
LP
608
609 /* We use the URI's included match attributes, but make them more strict. This allows users
610 * to specify a token URL instead of an object URL and the right thing should happen if
611 * there's only one suitable key on the token. */
612
3d05c058 613 switch (attributes[i].type) {
839fddbe
LP
614 case CKA_CLASS: {
615 CK_OBJECT_CLASS c;
616
3d05c058 617 if (attributes[i].ulValueLen != sizeof(c))
839fddbe
LP
618 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
619
3d05c058 620 memcpy(&c, attributes[i].pValue, sizeof(c));
839fddbe
LP
621 if (c != CKO_PRIVATE_KEY)
622 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
623 "Selected PKCS#11 object is not a private key, refusing.");
624
625 found_class = true;
626 break;
839fddbe
LP
627 }}
628 }
629
3d05c058 630 if (!found_class) {
839fddbe
LP
631 /* Hmm, let's slightly extend the attribute list we search for */
632
3d05c058 633 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + 1);
839fddbe
LP
634 if (!attributes_buffer)
635 return log_oom();
636
637 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
638
3d05c058
VS
639 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
640 .type = CKA_CLASS,
641 .pValue = (CK_OBJECT_CLASS*) &class,
642 .ulValueLen = sizeof(class),
643 };
839fddbe
LP
644
645 attributes = attributes_buffer;
646 }
647
648 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
649 if (rv != CKR_OK)
650 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 651 "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
839fddbe 652
3d05c058
VS
653 for (;;) {
654 CK_ULONG b;
655 rv = m->C_FindObjects(session, &candidate, 1, &b);
656 if (rv != CKR_OK)
657 return log_error_errno(SYNTHETIC_ERRNO(EIO),
658 "Failed to find objects: %s", sym_p11_kit_strerror(rv));
659
660 if (b == 0)
661 break;
662
663 bool can_decrypt = false, can_derive = false;
664 optional_attributes[0].ulValueLen = sizeof(decrypt_value);
665 optional_attributes[1].ulValueLen = sizeof(derive_value);
666
667 rv = m->C_GetAttributeValue(session, candidate, optional_attributes, ELEMENTSOF(optional_attributes));
668 if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
669 return log_error_errno(SYNTHETIC_ERRNO(EIO),
670 "Failed to get attributes of a selected private key: %s", sym_p11_kit_strerror(rv));
671
672 if (optional_attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION && decrypt_value == CK_TRUE)
673 can_decrypt = true;
674
675 if (optional_attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION && derive_value == CK_TRUE)
676 can_derive = true;
677
678 if (can_decrypt || can_derive) {
679 n_objects++;
680 if (n_objects > 1)
681 break;
682 object = candidate;
683 }
684 }
685
686 rv = m->C_FindObjectsFinal(session);
839fddbe
LP
687 if (rv != CKR_OK)
688 return log_error_errno(SYNTHETIC_ERRNO(EIO),
3d05c058
VS
689 "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
690
839fddbe
LP
691 if (n_objects == 0)
692 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
3d05c058
VS
693 "Failed to find selected private key suitable for decryption or derivation on token.");
694
839fddbe
LP
695 if (n_objects > 1)
696 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
3d05c058 697 "Configured private key URI matches multiple keys, refusing.");
839fddbe 698
3d05c058 699 *ret_object = object;
839fddbe
LP
700 return 0;
701}
702
06b77382
VS
703static const char* object_class_to_string(CK_OBJECT_CLASS class) {
704 switch (class) {
705 case CKO_CERTIFICATE:
706 return "CKO_CERTIFICATE";
707 case CKO_PUBLIC_KEY:
708 return "CKO_PUBLIC_KEY";
709 case CKO_PRIVATE_KEY:
710 return "CKO_PRIVATE_KEY";
711 case CKO_SECRET_KEY:
712 return "CKO_SECRET_KEY";
713 default:
714 return NULL;
715 }
716}
717
718/* Returns an object with the given class and the same CKA_ID or CKA_LABEL as prototype */
719int pkcs11_token_find_related_object(
720 CK_FUNCTION_LIST *m,
721 CK_SESSION_HANDLE session,
722 CK_OBJECT_HANDLE prototype,
723 CK_OBJECT_CLASS class,
724 CK_OBJECT_HANDLE *ret_object ) {
725
726 _cleanup_free_ void *buffer = NULL;
727 CK_ATTRIBUTE attributes[] = {
728 { CKA_ID, NULL_PTR, 0 },
729 { CKA_LABEL, NULL_PTR, 0 }
730 };
731 CK_OBJECT_CLASS search_class = class;
732 CK_ATTRIBUTE search_attributes[2] = {
733 { CKA_CLASS, &search_class, sizeof(search_class) }
734 };
735 CK_ULONG n_objects;
736 CK_OBJECT_HANDLE objects[2];
737 CK_RV rv;
738
739 rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes));
740 if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
741 return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv));
742
743 if (attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
744 buffer = malloc(attributes[0].ulValueLen);
745 if (!buffer)
746 return log_oom();
747
748 attributes[0].pValue = buffer;
749 rv = m->C_GetAttributeValue(session, prototype, &attributes[0], 1);
750 if (rv != CKR_OK)
751 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
752 "Failed to retrieve CKA_ID: %s", sym_p11_kit_strerror(rv));
753
754 search_attributes[1] = attributes[0];
755
756 } else if (attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
757 buffer = malloc(attributes[1].ulValueLen);
758 if (!buffer)
759 return log_oom();
760
761 attributes[1].pValue = buffer;
762 rv = m->C_GetAttributeValue(session, prototype, &attributes[1], 1);
763 if (rv != CKR_OK)
764 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
765 "Failed to retrieve CKA_LABEL: %s", sym_p11_kit_strerror(rv));
766
767 search_attributes[1] = attributes[1];
768
769 } else
770 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The prototype does not have CKA_ID or CKA_LABEL");
771
772 rv = m->C_FindObjectsInit(session, search_attributes, 2);
773 if (rv != CKR_OK)
774 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
775 "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
776
777 rv = m->C_FindObjects(session, objects, 2, &n_objects);
778 if (rv != CKR_OK)
779 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
780 "Failed to find objects: %s", sym_p11_kit_strerror(rv));
781
782 rv = m->C_FindObjectsFinal(session);
783 if (rv != CKR_OK)
784 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
785 "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
786
787 if (n_objects == 0)
788 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
789 "Failed to find a related object with class %s", object_class_to_string(class));
790
791 if (n_objects > 1)
792 log_warning("Found multiple related objects with class %s, using the first object.",
793 object_class_to_string(class));
794
795 *ret_object = objects[0];
796 return 0;
797}
798
799#if HAVE_OPENSSL
800static int ecc_convert_to_compressed(
801 CK_FUNCTION_LIST *m,
802 CK_SESSION_HANDLE session,
803 CK_OBJECT_HANDLE object,
804 const void *uncompressed_point,
805 size_t uncompressed_point_size,
806 void **ret_compressed_point,
807 size_t *ret_compressed_point_size) {
808
809 _cleanup_free_ void *ec_params_buffer = NULL;
810 CK_ATTRIBUTE ec_params_attr = { CKA_EC_PARAMS, NULL_PTR, 0 };
811 CK_RV rv;
812 int r;
813
814 rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
815 if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
816 return log_error_errno(SYNTHETIC_ERRNO(EIO),
817 "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
818
819 if (ec_params_attr.ulValueLen != CK_UNAVAILABLE_INFORMATION) {
820 ec_params_buffer = malloc(ec_params_attr.ulValueLen);
821 if (!ec_params_buffer)
822 return log_oom();
823
824 ec_params_attr.pValue = ec_params_buffer;
825 rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
826 if (rv != CKR_OK)
827 return log_error_errno(SYNTHETIC_ERRNO(EIO),
828 "Failed to retrieve CKA_EC_PARAMS from a private key: %s", sym_p11_kit_strerror(rv));
829 } else {
830 CK_OBJECT_HANDLE public_key;
831 r = pkcs11_token_find_related_object(m, session, object, CKO_PUBLIC_KEY, &public_key);
832 if (r < 0)
833 return log_error_errno(r, "Failed to find a public key for compressing a EC point");
834
835 ec_params_attr.ulValueLen = 0;
836 rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
837 if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
838 return log_error_errno(SYNTHETIC_ERRNO(EIO),
839 "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
840
841 if (ec_params_attr.ulValueLen == CK_UNAVAILABLE_INFORMATION)
842 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
843 "The public key does not have CKA_EC_PARAMS");
844
845 ec_params_buffer = malloc(ec_params_attr.ulValueLen);
846 if (!ec_params_buffer)
847 return log_oom();
848
849 ec_params_attr.pValue = ec_params_buffer;
850 rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
851 if (rv != CKR_OK)
852 return log_error_errno(SYNTHETIC_ERRNO(EIO),
853 "Failed to retrieve CKA_EC_PARAMS from a public key: %s", sym_p11_kit_strerror(rv));
854 }
855
856 _cleanup_(EC_GROUP_freep) EC_GROUP *group = NULL;
857 _cleanup_(EC_POINT_freep) EC_POINT *point = NULL;
858 _cleanup_(BN_CTX_freep) BN_CTX *bnctx = NULL;
859 _cleanup_free_ void *compressed_point = NULL;
860 size_t compressed_point_size;
861
862 const unsigned char *ec_params_value = ec_params_attr.pValue;
863 group = d2i_ECPKParameters(NULL, &ec_params_value, ec_params_attr.ulValueLen);
864 if (!group)
865 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode CKA_EC_PARAMS");
866
867 point = EC_POINT_new(group);
868 if (!point)
869 return log_oom();
870
871 bnctx = BN_CTX_new();
872 if (!bnctx)
873 return log_oom();
874
875 if (EC_POINT_oct2point(group, point, uncompressed_point, uncompressed_point_size, bnctx) != 1)
876 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode an uncompressed EC point");
877
878 compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, bnctx);
879 if (compressed_point_size == 0)
880 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine size of a compressed EC point");
881
882 compressed_point = malloc(compressed_point_size);
883 if (!compressed_point)
884 return log_oom();
885
886 compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, compressed_point, compressed_point_size, bnctx);
887 if (compressed_point_size == 0)
888 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert a EC point to compressed format");
889
890 *ret_compressed_point = TAKE_PTR(compressed_point);
891 *ret_compressed_point_size = compressed_point_size;
892 return 0;
893}
894#endif
895
3d05c058
VS
896/* Since EC keys doesn't support encryption directly, we use ECDH protocol to derive shared secret here.
897 * We use PKCS#11 C_DeriveKey function to derive a shared secret with a private key stored in the token and
898 * a public key saved on enrollment. */
899static int pkcs11_token_decrypt_data_ecc(
900 CK_FUNCTION_LIST *m,
901 CK_SESSION_HANDLE session,
902 CK_OBJECT_HANDLE object,
903 const void *encrypted_data,
904 size_t encrypted_data_size,
905 void **ret_decrypted_data,
906 size_t *ret_decrypted_data_size) {
907
908 static const CK_BBOOL yes = CK_TRUE, no = CK_FALSE;
909 static const CK_OBJECT_CLASS shared_secret_class = CKO_SECRET_KEY;
910 static const CK_KEY_TYPE shared_secret_type = CKK_GENERIC_SECRET;
911 static const CK_ATTRIBUTE shared_secret_template[] = {
912 { CKA_TOKEN, (void*) &no, sizeof(no) },
913 { CKA_CLASS, (void*) &shared_secret_class, sizeof(shared_secret_class) },
914 { CKA_KEY_TYPE, (void*) &shared_secret_type, sizeof(shared_secret_type) },
915 { CKA_SENSITIVE, (void*) &no, sizeof(no) },
916 { CKA_EXTRACTABLE, (void*) &yes, sizeof(yes) }
917 };
918 CK_ECDH1_DERIVE_PARAMS params = {
919 .kdf = CKD_NULL,
920 .pPublicData = (void*) encrypted_data,
921 .ulPublicDataLen = encrypted_data_size
922 };
923 CK_MECHANISM mechanism = {
924 .mechanism = CKM_ECDH1_DERIVE,
925 .pParameter = &params,
926 .ulParameterLen = sizeof(params)
927 };
928 CK_OBJECT_HANDLE shared_secret_handle;
06b77382
VS
929 CK_SESSION_INFO session_info;
930 CK_MECHANISM_INFO mechanism_info;
3d05c058 931 CK_RV rv, rv2;
06b77382
VS
932#if HAVE_OPENSSL
933 _cleanup_free_ void *compressed_point = NULL;
934 int r;
935#endif
936
937 rv = m->C_GetSessionInfo(session, &session_info);
938 if (rv != CKR_OK)
939 return log_error_errno(SYNTHETIC_ERRNO(EIO),
940 "Failed to get information about the PKCS#11 session: %s", sym_p11_kit_strerror(rv));
941
942 rv = m->C_GetMechanismInfo(session_info.slotID, CKM_ECDH1_DERIVE, &mechanism_info);
943 if (rv != CKR_OK)
944 return log_error_errno(SYNTHETIC_ERRNO(EIO),
945 "Failed to get information about CKM_ECDH1_DERIVE: %s", sym_p11_kit_strerror(rv));
946
947 if (!(mechanism_info.flags & CKF_EC_UNCOMPRESS)) {
948 if (mechanism_info.flags & CKF_EC_COMPRESS) {
949#if HAVE_OPENSSL
950 log_debug("CKM_ECDH1_DERIVE accepts compressed EC points only, trying to convert.");
951 size_t compressed_point_size;
952 r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size);
953 if (r < 0)
954 return r;
955
956 params.pPublicData = compressed_point;
957 params.ulPublicDataLen = compressed_point_size;
958#else
959 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
960 "CKM_ECDH1_DERIVE does not support uncompressed format of EC points");
961#endif
962 } else
963 log_debug("Both CKF_EC_UNCOMPRESS and CKF_EC_COMPRESS are false for CKM_ECDH1_DERIVE, ignoring.");
964 }
3d05c058
VS
965
966 rv = m->C_DeriveKey(session, &mechanism, object, (CK_ATTRIBUTE*) shared_secret_template, ELEMENTSOF(shared_secret_template), &shared_secret_handle);
967 if (rv != CKR_OK)
968 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to derive a shared secret: %s", sym_p11_kit_strerror(rv));
969
970 CK_ATTRIBUTE shared_secret_attr = { CKA_VALUE, NULL_PTR, 0};
971
972 rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
973 if (rv != CKR_OK) {
974 rv2 = m->C_DestroyObject(session, shared_secret_handle);
975 if (rv2 != CKR_OK)
976 log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
977 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve shared secret length: %s", sym_p11_kit_strerror(rv));
978 }
979
980 shared_secret_attr.pValue = malloc(shared_secret_attr.ulValueLen);
981 if (!shared_secret_attr.pValue)
982 return log_oom();
983
984 rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
985 rv2 = m->C_DestroyObject(session, shared_secret_handle);
986 if (rv2 != CKR_OK)
987 log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
988
989 if (rv != CKR_OK) {
990 erase_and_free(shared_secret_attr.pValue);
991 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve a shared secret: %s", sym_p11_kit_strerror(rv));
992 }
993
994 log_info("Successfully derived key with security token.");
995
996 *ret_decrypted_data = shared_secret_attr.pValue;
997 *ret_decrypted_data_size = shared_secret_attr.ulValueLen;
998 return 0;
999}
1000
1001static int pkcs11_token_decrypt_data_rsa(
839fddbe
LP
1002 CK_FUNCTION_LIST *m,
1003 CK_SESSION_HANDLE session,
1004 CK_OBJECT_HANDLE object,
1005 const void *encrypted_data,
1006 size_t encrypted_data_size,
1007 void **ret_decrypted_data,
1008 size_t *ret_decrypted_data_size) {
1009
1010 static const CK_MECHANISM mechanism = {
1011 .mechanism = CKM_RSA_PKCS
1012 };
1013 _cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
1014 CK_ULONG dbuffer_size = 0;
1015 CK_RV rv;
da035a3a 1016
839fddbe
LP
1017 rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
1018 if (rv != CKR_OK)
1019 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 1020 "Failed to initialize decryption on security token: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
1021
1022 dbuffer_size = encrypted_data_size; /* Start with something reasonable */
1023 dbuffer = malloc(dbuffer_size);
1024 if (!dbuffer)
1025 return log_oom();
1026
1027 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
1028 if (rv == CKR_BUFFER_TOO_SMALL) {
1029 erase_and_free(dbuffer);
1030
1031 dbuffer = malloc(dbuffer_size);
1032 if (!dbuffer)
1033 return log_oom();
1034
1035 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
1036 }
1037 if (rv != CKR_OK)
1038 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 1039 "Failed to decrypt key on security token: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
1040
1041 log_info("Successfully decrypted key with security token.");
1042
1043 *ret_decrypted_data = TAKE_PTR(dbuffer);
1044 *ret_decrypted_data_size = dbuffer_size;
1045 return 0;
1046}
1047
3d05c058
VS
1048int pkcs11_token_decrypt_data(
1049 CK_FUNCTION_LIST *m,
1050 CK_SESSION_HANDLE session,
1051 CK_OBJECT_HANDLE object,
1052 const void *encrypted_data,
1053 size_t encrypted_data_size,
1054 void **ret_decrypted_data,
1055 size_t *ret_decrypted_data_size) {
1056
1057 CK_KEY_TYPE key_type;
1058 CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
1059 CK_RV rv;
1060
1061 assert(m);
1062 assert(encrypted_data);
1063 assert(encrypted_data_size > 0);
1064 assert(ret_decrypted_data);
1065 assert(ret_decrypted_data_size);
1066
1067 rv = m->C_GetAttributeValue(session, object, &key_type_template, 1);
1068 if (rv != CKR_OK)
1069 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve private key type");
1070
1071 switch (key_type) {
1072
1073 case CKK_RSA:
1074 return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
1075
1076 case CKK_EC:
1077 return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
1078
1079 default:
1080 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported private key type: %lu", key_type);
1081 }
1082}
1083
839fddbe
LP
1084int pkcs11_token_acquire_rng(
1085 CK_FUNCTION_LIST *m,
1086 CK_SESSION_HANDLE session) {
1087
1088 _cleanup_free_ void *buffer = NULL;
839fddbe
LP
1089 size_t rps;
1090 CK_RV rv;
1091 int r;
1092
1093 assert(m);
1094
da035a3a
LB
1095 r = dlopen_p11kit();
1096 if (r < 0)
1097 return r;
1098
839fddbe
LP
1099 /* While we are at it, let's read some RNG data from the PKCS#11 token and pass it to the kernel
1100 * random pool. This should be cheap if we are talking to the device already. Note that we don't
1101 * credit any entropy, since we don't know about the quality of the pkcs#11 token's RNG. Why bother
1102 * at all? There are two sides to the argument whether to generate private keys on tokens or on the
1103 * host. By crediting some data from the token RNG to the host's pool we at least can say that any
1104 * key generated from it is at least as good as both sources individually. */
1105
1106 rps = random_pool_size();
1107
1108 buffer = malloc(rps);
1109 if (!buffer)
1110 return log_oom();
1111
1112 rv = m->C_GenerateRandom(session, buffer, rps);
1113 if (rv != CKR_OK)
1114 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
da035a3a 1115 "Failed to generate RNG data on security token: %s", sym_p11_kit_strerror(rv));
839fddbe 1116
61bd7d1e 1117 r = random_write_entropy(-1, buffer, rps, false);
839fddbe
LP
1118 if (r < 0)
1119 return log_debug_errno(r, "Failed to write PKCS#11 acquired random data to /dev/urandom: %m");
1120
1121 log_debug("Successfully written %zu bytes random data acquired via PKCS#11 to kernel random pool.", rps);
1122
1123 return 0;
1124}
1125
1126static int token_process(
1127 CK_FUNCTION_LIST *m,
1128 CK_SLOT_ID slotid,
1129 const CK_SLOT_INFO *slot_info,
1130 const CK_TOKEN_INFO *token_info,
1131 P11KitUri *search_uri,
1132 pkcs11_find_token_callback_t callback,
1133 void *userdata) {
1134
1135 _cleanup_free_ char *token_label = NULL;
1136 CK_SESSION_HANDLE session;
1137 CK_RV rv;
1138 int r;
1139
1140 assert(m);
1141 assert(slot_info);
1142 assert(token_info);
839fddbe
LP
1143
1144 token_label = pkcs11_token_label(token_info);
1145 if (!token_label)
1146 return log_oom();
1147
1148 rv = m->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session);
1149 if (rv != CKR_OK)
1150 return log_error_errno(SYNTHETIC_ERRNO(EIO),
da035a3a 1151 "Failed to create session for security token '%s': %s", token_label, sym_p11_kit_strerror(rv));
839fddbe
LP
1152
1153 if (callback)
1154 r = callback(m, session, slotid, slot_info, token_info, search_uri, userdata);
1155 else
1156 r = 1; /* if not callback was specified, just say we found what we were looking for */
1157
1158 rv = m->C_CloseSession(session);
1159 if (rv != CKR_OK)
da035a3a 1160 log_warning("Failed to close session on PKCS#11 token, ignoring: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
1161
1162 return r;
1163}
1164
1165static int slot_process(
1166 CK_FUNCTION_LIST *m,
1167 CK_SLOT_ID slotid,
1168 P11KitUri *search_uri,
1169 pkcs11_find_token_callback_t callback,
1170 void *userdata) {
1171
da035a3a 1172 _cleanup_(sym_p11_kit_uri_freep) P11KitUri* slot_uri = NULL, *token_uri = NULL;
839fddbe
LP
1173 _cleanup_free_ char *token_uri_string = NULL;
1174 CK_TOKEN_INFO token_info;
1175 CK_SLOT_INFO slot_info;
da035a3a 1176 int uri_result, r;
839fddbe
LP
1177 CK_RV rv;
1178
1179 assert(m);
839fddbe 1180
da035a3a
LB
1181 r = dlopen_p11kit();
1182 if (r < 0)
1183 return r;
1184
839fddbe
LP
1185 /* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the
1186 * caller might try other slots before giving up. */
1187
1188 rv = m->C_GetSlotInfo(slotid, &slot_info);
1189 if (rv != CKR_OK) {
da035a3a 1190 log_warning("Failed to acquire slot info for slot %lu, ignoring slot: %s", slotid, sym_p11_kit_strerror(rv));
839fddbe
LP
1191 return -EAGAIN;
1192 }
1193
1194 slot_uri = uri_from_slot_info(&slot_info);
1195 if (!slot_uri)
1196 return log_oom();
1197
1198 if (DEBUG_LOGGING) {
1199 _cleanup_free_ char *slot_uri_string = NULL;
1200
da035a3a 1201 uri_result = sym_p11_kit_uri_format(slot_uri, P11_KIT_URI_FOR_ANY, &slot_uri_string);
839fddbe 1202 if (uri_result != P11_KIT_URI_OK) {
da035a3a 1203 log_warning("Failed to format slot URI, ignoring slot: %s", sym_p11_kit_uri_message(uri_result));
839fddbe
LP
1204 return -EAGAIN;
1205 }
1206
1207 log_debug("Found slot with URI %s", slot_uri_string);
1208 }
1209
1210 rv = m->C_GetTokenInfo(slotid, &token_info);
1211 if (rv == CKR_TOKEN_NOT_PRESENT) {
d7a0f1f4
FS
1212 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
1213 "Token not present in slot, ignoring.");
839fddbe 1214 } else if (rv != CKR_OK) {
da035a3a 1215 log_warning("Failed to acquire token info for slot %lu, ignoring slot: %s", slotid, sym_p11_kit_strerror(rv));
839fddbe
LP
1216 return -EAGAIN;
1217 }
1218
1219 token_uri = uri_from_token_info(&token_info);
1220 if (!token_uri)
1221 return log_oom();
1222
da035a3a 1223 uri_result = sym_p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
839fddbe 1224 if (uri_result != P11_KIT_URI_OK) {
da035a3a 1225 log_warning("Failed to format slot URI: %s", sym_p11_kit_uri_message(uri_result));
839fddbe
LP
1226 return -EAGAIN;
1227 }
1228
da035a3a 1229 if (search_uri && !sym_p11_kit_uri_match_token_info(search_uri, &token_info))
d7a0f1f4
FS
1230 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
1231 "Found non-matching token with URI %s.",
1232 token_uri_string);
839fddbe
LP
1233
1234 log_debug("Found matching token with URI %s.", token_uri_string);
1235
1236 return token_process(
1237 m,
1238 slotid,
1239 &slot_info,
1240 &token_info,
1241 search_uri,
1242 callback,
1243 userdata);
1244}
1245
1246static int module_process(
1247 CK_FUNCTION_LIST *m,
1248 P11KitUri *search_uri,
1249 pkcs11_find_token_callback_t callback,
1250 void *userdata) {
1251
da035a3a 1252 _cleanup_(sym_p11_kit_uri_freep) P11KitUri* module_uri = NULL;
839fddbe 1253 _cleanup_free_ char *name = NULL, *module_uri_string = NULL;
839fddbe
LP
1254 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
1255 CK_ULONG n_slotids = 0;
1256 int uri_result;
1257 CK_INFO info;
1258 size_t k;
1259 CK_RV rv;
1260 int r;
1261
1262 assert(m);
839fddbe 1263
da035a3a
LB
1264 r = dlopen_p11kit();
1265 if (r < 0)
1266 return r;
1267
839fddbe
LP
1268 /* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module
1269 * should not have the effect that we don't try the others anymore. We indicate such per-module
1270 * failures with -EAGAIN, which let's the caller try the next module. */
1271
da035a3a 1272 name = sym_p11_kit_module_get_name(m);
839fddbe
LP
1273 if (!name)
1274 return log_oom();
1275
1276 log_debug("Trying PKCS#11 module %s.", name);
1277
1278 rv = m->C_GetInfo(&info);
1279 if (rv != CKR_OK) {
da035a3a 1280 log_warning("Failed to get info on PKCS#11 module, ignoring module: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
1281 return -EAGAIN;
1282 }
1283
1284 module_uri = uri_from_module_info(&info);
1285 if (!module_uri)
1286 return log_oom();
1287
da035a3a 1288 uri_result = sym_p11_kit_uri_format(module_uri, P11_KIT_URI_FOR_ANY, &module_uri_string);
839fddbe 1289 if (uri_result != P11_KIT_URI_OK) {
da035a3a 1290 log_warning("Failed to format module URI, ignoring module: %s", sym_p11_kit_uri_message(uri_result));
839fddbe
LP
1291 return -EAGAIN;
1292 }
1293
1294 log_debug("Found module with URI %s", module_uri_string);
1295
1296 rv = pkcs11_get_slot_list_malloc(m, &slotids, &n_slotids);
1297 if (rv != CKR_OK) {
da035a3a 1298 log_warning("Failed to get slot list, ignoring module: %s", sym_p11_kit_strerror(rv));
839fddbe
LP
1299 return -EAGAIN;
1300 }
d7a0f1f4
FS
1301 if (n_slotids == 0)
1302 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
1303 "This module has no slots? Ignoring module.");
839fddbe
LP
1304
1305 for (k = 0; k < n_slotids; k++) {
1306 r = slot_process(
1307 m,
1308 slotids[k],
1309 search_uri,
1310 callback,
1311 userdata);
1312 if (r != -EAGAIN)
1313 return r;
1314 }
1315
1316 return -EAGAIN;
1317}
1318
1319int pkcs11_find_token(
1320 const char *pkcs11_uri,
1321 pkcs11_find_token_callback_t callback,
1322 void *userdata) {
1323
da035a3a
LB
1324 _cleanup_(sym_p11_kit_modules_finalize_and_releasep) CK_FUNCTION_LIST **modules = NULL;
1325 _cleanup_(sym_p11_kit_uri_freep) P11KitUri *search_uri = NULL;
839fddbe
LP
1326 int r;
1327
da035a3a
LB
1328 r = dlopen_p11kit();
1329 if (r < 0)
1330 return r;
1331
839fddbe
LP
1332 /* Execute the specified callback for each matching token found. If nothing is found returns
1333 * -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */
1334
0eb3be46
LP
1335 if (pkcs11_uri) {
1336 r = uri_from_string(pkcs11_uri, &search_uri);
1337 if (r < 0)
1338 return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
1339 }
839fddbe 1340
da035a3a 1341 modules = sym_p11_kit_modules_load_and_initialize(0);
839fddbe
LP
1342 if (!modules)
1343 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize pkcs11 modules");
1344
1345 for (CK_FUNCTION_LIST **i = modules; *i; i++) {
1346 r = module_process(
1347 *i,
1348 search_uri,
1349 callback,
1350 userdata);
1351 if (r != -EAGAIN)
1352 return r;
1353 }
1354
1355 return -EAGAIN;
1356}
1357
2289a784
LP
1358#if HAVE_OPENSSL
1359struct pkcs11_acquire_certificate_callback_data {
1360 char *pin_used;
1361 X509 *cert;
1362 const char *askpw_friendly_name, *askpw_icon_name;
a758a128
LP
1363 AskPasswordFlags askpw_flags;
1364 bool headless;
2289a784
LP
1365};
1366
1367static void pkcs11_acquire_certificate_callback_data_release(struct pkcs11_acquire_certificate_callback_data *data) {
1368 erase_and_free(data->pin_used);
1369 X509_free(data->cert);
1370}
1371
1372static int pkcs11_acquire_certificate_callback(
1373 CK_FUNCTION_LIST *m,
1374 CK_SESSION_HANDLE session,
1375 CK_SLOT_ID slot_id,
1376 const CK_SLOT_INFO *slot_info,
1377 const CK_TOKEN_INFO *token_info,
1378 P11KitUri *uri,
1379 void *userdata) {
1380
1381 _cleanup_(erase_and_freep) char *pin_used = NULL;
99534007 1382 struct pkcs11_acquire_certificate_callback_data *data = ASSERT_PTR(userdata);
2289a784
LP
1383 CK_OBJECT_HANDLE object;
1384 int r;
1385
1386 assert(m);
1387 assert(slot_info);
1388 assert(token_info);
1389 assert(uri);
2289a784
LP
1390
1391 /* Called for every token matching our URI */
1392
a758a128
LP
1393 r = pkcs11_token_login(
1394 m,
1395 session,
1396 slot_id,
1397 token_info,
1398 data->askpw_friendly_name,
1399 data->askpw_icon_name,
1400 "pkcs11-pin",
1401 "pkcs11-pin",
1402 UINT64_MAX,
1403 data->askpw_flags,
1404 data->headless,
1405 &pin_used);
2289a784
LP
1406 if (r < 0)
1407 return r;
1408
1409 r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
1410 if (r < 0)
1411 return r;
1412
1413 r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
1414 if (r < 0)
1415 return r;
1416
1417 /* Let's read some random data off the token and write it to the kernel pool before we generate our
1418 * random key from it. This way we can claim the quality of the RNG is at least as good as the
1419 * kernel's and the token's pool */
1420 (void) pkcs11_token_acquire_rng(m, session);
1421
1422 data->pin_used = TAKE_PTR(pin_used);
1423 return 1;
1424}
1425
1426int pkcs11_acquire_certificate(
1427 const char *uri,
1428 const char *askpw_friendly_name,
1429 const char *askpw_icon_name,
1430 X509 **ret_cert,
1431 char **ret_pin_used) {
1432
1433 _cleanup_(pkcs11_acquire_certificate_callback_data_release) struct pkcs11_acquire_certificate_callback_data data = {
1434 .askpw_friendly_name = askpw_friendly_name,
1435 .askpw_icon_name = askpw_icon_name,
1436 };
1437 int r;
1438
1439 assert(uri);
1440 assert(ret_cert);
1441
1442 r = pkcs11_find_token(uri, pkcs11_acquire_certificate_callback, &data);
1443 if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
1444 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1445 "Specified PKCS#11 token with URI '%s' not found.",
1446 uri);
1447 if (r < 0)
1448 return r;
1449
1450 *ret_cert = TAKE_PTR(data.cert);
1451
1452 if (ret_pin_used)
1453 *ret_pin_used = TAKE_PTR(data.pin_used);
1454
1455 return 0;
1456}
f240cbb6
LP
1457#endif
1458
1459static int list_callback(
1460 CK_FUNCTION_LIST *m,
1461 CK_SESSION_HANDLE session,
1462 CK_SLOT_ID slot_id,
1463 const CK_SLOT_INFO *slot_info,
1464 const CK_TOKEN_INFO *token_info,
1465 P11KitUri *uri,
1466 void *userdata) {
1467
1468 _cleanup_free_ char *token_uri_string = NULL, *token_label = NULL, *token_manufacturer_id = NULL, *token_model = NULL;
da035a3a 1469 _cleanup_(sym_p11_kit_uri_freep) P11KitUri *token_uri = NULL;
f240cbb6
LP
1470 Table *t = userdata;
1471 int uri_result, r;
1472
1473 assert(slot_info);
1474 assert(token_info);
1475
da035a3a
LB
1476 r = dlopen_p11kit();
1477 if (r < 0)
1478 return r;
1479
f240cbb6
LP
1480 /* We only care about hardware devices here with a token inserted. Let's filter everything else
1481 * out. (Note that the user can explicitly specify non-hardware tokens if they like, but during
1482 * enumeration we'll filter those, since software tokens are typically the system certificate store
1483 * and such, and it's typically not what people want to bind their home directories to.) */
50079424 1484 if (!FLAGS_SET(slot_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
f240cbb6
LP
1485 return -EAGAIN;
1486
1487 token_label = pkcs11_token_label(token_info);
1488 if (!token_label)
1489 return log_oom();
1490
1491 token_manufacturer_id = pkcs11_token_manufacturer_id(token_info);
1492 if (!token_manufacturer_id)
1493 return log_oom();
1494
1495 token_model = pkcs11_token_model(token_info);
1496 if (!token_model)
1497 return log_oom();
1498
1499 token_uri = uri_from_token_info(token_info);
1500 if (!token_uri)
1501 return log_oom();
1502
da035a3a 1503 uri_result = sym_p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
f240cbb6 1504 if (uri_result != P11_KIT_URI_OK)
da035a3a 1505 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", sym_p11_kit_uri_message(uri_result));
f240cbb6
LP
1506
1507 r = table_add_many(
1508 t,
1509 TABLE_STRING, token_uri_string,
1510 TABLE_STRING, token_label,
1511 TABLE_STRING, token_manufacturer_id,
1512 TABLE_STRING, token_model);
1513 if (r < 0)
1514 return table_log_add_error(r);
1515
1516 return -EAGAIN; /* keep scanning */
1517}
1518#endif
1519
1520int pkcs11_list_tokens(void) {
1521#if HAVE_P11KIT
1522 _cleanup_(table_unrefp) Table *t = NULL;
1523 int r;
1524
1525 t = table_new("uri", "label", "manufacturer", "model");
1526 if (!t)
1527 return log_oom();
1528
1529 r = pkcs11_find_token(NULL, list_callback, t);
1530 if (r < 0 && r != -EAGAIN)
1531 return r;
2289a784 1532
f240cbb6
LP
1533 if (table_get_rows(t) <= 1) {
1534 log_info("No suitable PKCS#11 tokens found.");
1535 return 0;
1536 }
1537
1538 r = table_print(t, stdout);
1539 if (r < 0)
1540 return log_error_errno(r, "Failed to show device table: %m");
1541
1542 return 0;
1543#else
1544 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1545 "PKCS#11 tokens not supported on this build.");
1546#endif
1547}
1548
1549#if HAVE_P11KIT
1550static int auto_callback(
1551 CK_FUNCTION_LIST *m,
1552 CK_SESSION_HANDLE session,
1553 CK_SLOT_ID slot_id,
1554 const CK_SLOT_INFO *slot_info,
1555 const CK_TOKEN_INFO *token_info,
1556 P11KitUri *uri,
1557 void *userdata) {
1558
da035a3a 1559 _cleanup_(sym_p11_kit_uri_freep) P11KitUri *token_uri = NULL;
f240cbb6 1560 char **t = userdata;
da035a3a 1561 int uri_result, r;
f240cbb6
LP
1562
1563 assert(slot_info);
1564 assert(token_info);
1565
da035a3a
LB
1566 r = dlopen_p11kit();
1567 if (r < 0)
1568 return r;
1569
f240cbb6
LP
1570 if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
1571 return -EAGAIN;
1572
1573 if (*t)
1574 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
1575 "More than one suitable PKCS#11 token found.");
1576
1577 token_uri = uri_from_token_info(token_info);
1578 if (!token_uri)
1579 return log_oom();
1580
da035a3a 1581 uri_result = sym_p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, t);
f240cbb6 1582 if (uri_result != P11_KIT_URI_OK)
da035a3a 1583 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", sym_p11_kit_uri_message(uri_result));
f240cbb6
LP
1584
1585 return 0;
1586}
2289a784 1587#endif
f240cbb6
LP
1588
1589int pkcs11_find_token_auto(char **ret) {
1590#if HAVE_P11KIT
1591 int r;
1592
1593 r = pkcs11_find_token(NULL, auto_callback, ret);
1594 if (r == -EAGAIN)
1595 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No suitable PKCS#11 tokens found.");
1596 if (r < 0)
1597 return r;
1598
1599 return 0;
1600#else
1601 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1602 "PKCS#11 tokens not supported on this build.");
839fddbe 1603#endif
f240cbb6 1604}
ed3d3af1
OK
1605
1606#if HAVE_P11KIT
1607void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data) {
1608 erase_and_free(data->decrypted_key);
1609
1610 if (data->free_encrypted_key)
1611 free(data->encrypted_key);
1612}
1613
1614int pkcs11_crypt_device_callback(
1615 CK_FUNCTION_LIST *m,
1616 CK_SESSION_HANDLE session,
1617 CK_SLOT_ID slot_id,
1618 const CK_SLOT_INFO *slot_info,
1619 const CK_TOKEN_INFO *token_info,
1620 P11KitUri *uri,
1621 void *userdata) {
1622
99534007 1623 pkcs11_crypt_device_callback_data *data = ASSERT_PTR(userdata);
ed3d3af1
OK
1624 CK_OBJECT_HANDLE object;
1625 int r;
1626
1627 assert(m);
1628 assert(slot_info);
1629 assert(token_info);
1630 assert(uri);
ed3d3af1
OK
1631
1632 /* Called for every token matching our URI */
1633
1634 r = pkcs11_token_login(
1635 m,
1636 session,
1637 slot_id,
1638 token_info,
1639 data->friendly_name,
1640 "drive-harddisk",
1641 "pkcs11-pin",
1642 "cryptsetup.pkcs11-pin",
1643 data->until,
a758a128 1644 data->askpw_flags,
ed3d3af1
OK
1645 data->headless,
1646 NULL);
1647 if (r < 0)
1648 return r;
1649
1650 /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
1651 * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
1652 * shouldn't hurt. */
1653 (void) pkcs11_token_acquire_rng(m, session);
1654
1655 r = pkcs11_token_find_private_key(m, session, uri, &object);
1656 if (r < 0)
1657 return r;
1658
1659 r = pkcs11_token_decrypt_data(
1660 m,
1661 session,
1662 object,
1663 data->encrypted_key,
1664 data->encrypted_key_size,
1665 &data->decrypted_key,
1666 &data->decrypted_key_size);
1667 if (r < 0)
1668 return r;
1669
1670 return 0;
1671}
1672#endif