]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pkcs11-util.c
Merge pull request #18990 from yuwata/network-dhcpv6-use-domains
[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"
6#include "escape.h"
7#include "fd-util.h"
f240cbb6 8#include "format-table.h"
839fddbe
LP
9#include "io-util.h"
10#include "memory-util.h"
11#if HAVE_OPENSSL
12#include "openssl-util.h"
13#endif
14#include "pkcs11-util.h"
15#include "random-util.h"
16#include "string-util.h"
17#include "strv.h"
18
19bool pkcs11_uri_valid(const char *uri) {
20 const char *p;
21
22 /* A very superficial checker for RFC7512 PKCS#11 URI syntax */
23
24 if (isempty(uri))
25 return false;
26
27 p = startswith(uri, "pkcs11:");
28 if (!p)
29 return false;
30
31 if (isempty(p))
32 return false;
33
34 if (!in_charset(p, ALPHANUMERICAL "-_?;&%="))
35 return false;
36
37 return true;
38}
39
40#if HAVE_P11KIT
41
42int uri_from_string(const char *p, P11KitUri **ret) {
43 _cleanup_(p11_kit_uri_freep) P11KitUri *uri = NULL;
44
45 assert(p);
46 assert(ret);
47
48 uri = p11_kit_uri_new();
49 if (!uri)
50 return -ENOMEM;
51
52 if (p11_kit_uri_parse(p, P11_KIT_URI_FOR_ANY, uri) != P11_KIT_URI_OK)
53 return -EINVAL;
54
55 *ret = TAKE_PTR(uri);
56 return 0;
57}
58
59P11KitUri *uri_from_module_info(const CK_INFO *info) {
60 P11KitUri *uri;
61
62 assert(info);
63
64 uri = p11_kit_uri_new();
65 if (!uri)
66 return NULL;
67
68 *p11_kit_uri_get_module_info(uri) = *info;
69 return uri;
70}
71
72P11KitUri *uri_from_slot_info(const CK_SLOT_INFO *slot_info) {
73 P11KitUri *uri;
74
75 assert(slot_info);
76
77 uri = p11_kit_uri_new();
78 if (!uri)
79 return NULL;
80
81 *p11_kit_uri_get_slot_info(uri) = *slot_info;
82 return uri;
83}
84
85P11KitUri *uri_from_token_info(const CK_TOKEN_INFO *token_info) {
86 P11KitUri *uri;
87
88 assert(token_info);
89
90 uri = p11_kit_uri_new();
91 if (!uri)
92 return NULL;
93
94 *p11_kit_uri_get_token_info(uri) = *token_info;
95 return uri;
96}
97
98CK_RV pkcs11_get_slot_list_malloc(
99 CK_FUNCTION_LIST *m,
100 CK_SLOT_ID **ret_slotids,
101 CK_ULONG *ret_n_slotids) {
102
103 CK_RV rv;
104
105 assert(m);
106 assert(ret_slotids);
107 assert(ret_n_slotids);
108
109 for (unsigned tries = 0; tries < 16; tries++) {
110 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
111 CK_ULONG n_slotids = 0;
112
113 rv = m->C_GetSlotList(0, NULL, &n_slotids);
114 if (rv != CKR_OK)
115 return rv;
116 if (n_slotids == 0) {
117 *ret_slotids = NULL;
118 *ret_n_slotids = 0;
119 return CKR_OK;
120 }
121
122 slotids = new(CK_SLOT_ID, n_slotids);
123 if (!slotids)
124 return CKR_HOST_MEMORY;
125
126 rv = m->C_GetSlotList(0, slotids, &n_slotids);
127 if (rv == CKR_OK) {
128 *ret_slotids = TAKE_PTR(slotids);
129 *ret_n_slotids = n_slotids;
130 return CKR_OK;
131 }
132
133 if (rv != CKR_BUFFER_TOO_SMALL)
134 return rv;
135
136 /* Hu? Maybe somebody plugged something in and things changed? Let's try again */
137 }
138
139 return CKR_BUFFER_TOO_SMALL;
140}
141
142char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
143 char *t;
144
145 /* The label is not NUL terminated and likely padded with spaces, let's make a copy here, so that we
146 * can strip that. */
147 t = strndup((char*) token_info->label, sizeof(token_info->label));
148 if (!t)
149 return NULL;
150
151 strstrip(t);
152 return t;
153}
154
0eb3be46
LP
155char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info) {
156 char *t;
157
158 t = strndup((char*) token_info->manufacturerID, sizeof(token_info->manufacturerID));
159 if (!t)
160 return NULL;
161
162 strstrip(t);
163 return t;
164}
165
166char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
167 char *t;
168
169 t = strndup((char*) token_info->model, sizeof(token_info->model));
170 if (!t)
171 return NULL;
172
173 strstrip(t);
174 return t;
175}
176
839fddbe
LP
177int pkcs11_token_login(
178 CK_FUNCTION_LIST *m,
179 CK_SESSION_HANDLE session,
180 CK_SLOT_ID slotid,
181 const CK_TOKEN_INFO *token_info,
182 const char *friendly_name,
183 const char *icon_name,
8806bb4b
LP
184 const char *key_name,
185 const char *credential_name,
839fddbe
LP
186 usec_t until,
187 char **ret_used_pin) {
188
189 _cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
190 _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
191 CK_TOKEN_INFO updated_token_info;
0eb3be46 192 int uri_result, r;
839fddbe 193 CK_RV rv;
839fddbe
LP
194
195 assert(m);
196 assert(token_info);
197
198 token_label = pkcs11_token_label(token_info);
199 if (!token_label)
200 return log_oom();
201
202 token_uri = uri_from_token_info(token_info);
203 if (!token_uri)
204 return log_oom();
205
206 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
207 if (uri_result != P11_KIT_URI_OK)
208 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
209
210 if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
211 rv = m->C_Login(session, CKU_USER, NULL, 0);
212 if (rv != CKR_OK)
213 return log_error_errno(SYNTHETIC_ERRNO(EIO),
214 "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
215
162392b7 216 log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
664ad0f6
MS
217 if (ret_used_pin)
218 *ret_used_pin = NULL;
839fddbe
LP
219 return 0;
220 }
221
222 if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
223 log_info("No login into security token '%s' required.", token_label);
664ad0f6
MS
224 if (ret_used_pin)
225 *ret_used_pin = NULL;
839fddbe
LP
226 return 0;
227 }
228
229 token_uri_escaped = cescape(token_uri_string);
230 if (!token_uri_escaped)
231 return log_oom();
232
233 id = strjoin("pkcs11:", token_uri_escaped);
234 if (!id)
235 return log_oom();
236
237 for (unsigned tries = 0; tries < 3; tries++) {
238 _cleanup_strv_free_erase_ char **passwords = NULL;
839fddbe
LP
239 char **i, *e;
240
839fddbe
LP
241 e = getenv("PIN");
242 if (e) {
243 passwords = strv_new(e);
244 if (!passwords)
245 return log_oom();
246
247 string_erase(e);
248 if (unsetenv("PIN") < 0)
249 return log_error_errno(errno, "Failed to unset $PIN: %m");
250 } else {
c63ec11b
LP
251 _cleanup_free_ char *text = NULL;
252
253 if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
254 r = asprintf(&text,
255 "Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
256 token_label, friendly_name);
257 else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
258 r = asprintf(&text,
259 "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
260 token_label, friendly_name);
261 else if (tries == 0)
262 r = asprintf(&text,
263 "Please enter PIN for security token '%s' in order to unlock %s:",
264 token_label, friendly_name);
265 else
266 r = asprintf(&text,
267 "Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
268 token_label, friendly_name, tries+1);
269 if (r < 0)
270 return log_oom();
271
839fddbe 272 /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
8806bb4b 273 r = ask_password_auto(text, icon_name, id, key_name, credential_name, until, 0, &passwords);
839fddbe
LP
274 if (r < 0)
275 return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
276 }
277
278 STRV_FOREACH(i, passwords) {
279 rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
280 if (rv == CKR_OK) {
281
282 if (ret_used_pin) {
283 char *c;
284
285 c = strdup(*i);
286 if (!c)
287 return log_oom();
288
289 *ret_used_pin = c;
290 }
291
292 log_info("Successfully logged into security token '%s'.", token_label);
293 return 0;
294 }
295 if (rv == CKR_PIN_LOCKED)
296 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
297 "PIN has been locked, please reset PIN of security token '%s'.", token_label);
298 if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
299 return log_error_errno(SYNTHETIC_ERRNO(EIO),
300 "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
301
302 /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
303 rv = m->C_GetTokenInfo(slotid, &updated_token_info);
304 if (rv != CKR_OK)
305 return log_error_errno(SYNTHETIC_ERRNO(EIO),
306 "Failed to acquire updated security token information for slot %lu: %s",
307 slotid, p11_kit_strerror(rv));
308
309 token_info = &updated_token_info;
310 log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
311 }
312 }
313
314 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to log into token '%s'.", token_label);
315}
316
317int pkcs11_token_find_x509_certificate(
318 CK_FUNCTION_LIST *m,
319 CK_SESSION_HANDLE session,
320 P11KitUri *search_uri,
321 CK_OBJECT_HANDLE *ret_object) {
322
323 bool found_class = false, found_certificate_type = false;
324 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
325 CK_ULONG n_attributes, a, n_objects;
326 CK_ATTRIBUTE *attributes = NULL;
327 CK_OBJECT_HANDLE objects[2];
328 CK_RV rv, rv2;
329
330 assert(m);
331 assert(search_uri);
332 assert(ret_object);
333
334 attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
335 for (a = 0; a < n_attributes; a++) {
336
337 /* We use the URI's included match attributes, but make them more strict. This allows users
338 * to specify a token URL instead of an object URL and the right thing should happen if
339 * there's only one suitable key on the token. */
340
341 switch (attributes[a].type) {
342
343 case CKA_CLASS: {
344 CK_OBJECT_CLASS c;
345
346 if (attributes[a].ulValueLen != sizeof(c))
347 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
348
349 memcpy(&c, attributes[a].pValue, sizeof(c));
350 if (c != CKO_CERTIFICATE)
351 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
352
353 found_class = true;
354 break;
355 }
356
357 case CKA_CERTIFICATE_TYPE: {
358 CK_CERTIFICATE_TYPE t;
359
360 if (attributes[a].ulValueLen != sizeof(t))
361 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CERTIFICATE_TYPE attribute size.");
362
363 memcpy(&t, attributes[a].pValue, sizeof(t));
364 if (t != CKC_X_509)
365 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
366
367 found_certificate_type = true;
368 break;
369 }}
370 }
371
372 if (!found_class || !found_certificate_type) {
373 /* Hmm, let's slightly extend the attribute list we search for */
374
375 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_class + !found_certificate_type);
376 if (!attributes_buffer)
377 return log_oom();
378
379 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
380
381 if (!found_class) {
382 static const CK_OBJECT_CLASS class = CKO_CERTIFICATE;
383
384 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
385 .type = CKA_CLASS,
386 .pValue = (CK_OBJECT_CLASS*) &class,
387 .ulValueLen = sizeof(class),
388 };
389 }
390
391 if (!found_certificate_type) {
392 static const CK_CERTIFICATE_TYPE type = CKC_X_509;
393
394 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
395 .type = CKA_CERTIFICATE_TYPE,
396 .pValue = (CK_CERTIFICATE_TYPE*) &type,
397 .ulValueLen = sizeof(type),
398 };
399 }
400
401 attributes = attributes_buffer;
402 }
403
404 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
405 if (rv != CKR_OK)
406 return log_error_errno(SYNTHETIC_ERRNO(EIO),
407 "Failed to initialize object find call: %s", p11_kit_strerror(rv));
408
409 rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
410 rv2 = m->C_FindObjectsFinal(session);
411 if (rv != CKR_OK)
412 return log_error_errno(SYNTHETIC_ERRNO(EIO),
413 "Failed to find objects: %s", p11_kit_strerror(rv));
414 if (rv2 != CKR_OK)
415 return log_error_errno(SYNTHETIC_ERRNO(EIO),
416 "Failed to finalize object find call: %s", p11_kit_strerror(rv));
417 if (n_objects == 0)
418 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
419 "Failed to find selected X509 certificate on token.");
420 if (n_objects > 1)
421 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
422 "Configured URI matches multiple certificates, refusing.");
423
424 *ret_object = objects[0];
425 return 0;
426}
427
428#if HAVE_OPENSSL
429int pkcs11_token_read_x509_certificate(
430 CK_FUNCTION_LIST *m,
431 CK_SESSION_HANDLE session,
432 CK_OBJECT_HANDLE object,
433 X509 **ret_cert) {
434
435 _cleanup_free_ void *buffer = NULL;
436 _cleanup_free_ char *t = NULL;
437 CK_ATTRIBUTE attribute = {
438 .type = CKA_VALUE
439 };
440 CK_RV rv;
441 _cleanup_(X509_freep) X509 *x509 = NULL;
442 X509_NAME *name = NULL;
443 const unsigned char *p;
444
445 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
446 if (rv != CKR_OK)
447 return log_error_errno(SYNTHETIC_ERRNO(EIO),
448 "Failed to read X.509 certificate size off token: %s", p11_kit_strerror(rv));
449
450 buffer = malloc(attribute.ulValueLen);
451 if (!buffer)
452 return log_oom();
453
454 attribute.pValue = buffer;
455
456 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
457 if (rv != CKR_OK)
458 return log_error_errno(SYNTHETIC_ERRNO(EIO),
459 "Failed to read X.509 certificate data off token: %s", p11_kit_strerror(rv));
460
461 p = attribute.pValue;
462 x509 = d2i_X509(NULL, &p, attribute.ulValueLen);
463 if (!x509)
464 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed parse X.509 certificate.");
465
466 name = X509_get_subject_name(x509);
467 if (!name)
468 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to acquire X.509 subject name.");
469
470 t = X509_NAME_oneline(name, NULL, 0);
471 if (!t)
472 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format X.509 subject name as string.");
473
474 log_debug("Using X.509 certificate issued for '%s'.", t);
475
476 *ret_cert = TAKE_PTR(x509);
477 return 0;
478}
479#endif
480
481int pkcs11_token_find_private_key(
482 CK_FUNCTION_LIST *m,
483 CK_SESSION_HANDLE session,
484 P11KitUri *search_uri,
485 CK_OBJECT_HANDLE *ret_object) {
486
487 bool found_decrypt = false, found_class = false, found_key_type = false;
488 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
489 CK_ULONG n_attributes, a, n_objects;
490 CK_ATTRIBUTE *attributes = NULL;
491 CK_OBJECT_HANDLE objects[2];
492 CK_RV rv, rv2;
493
494 assert(m);
495 assert(search_uri);
496 assert(ret_object);
497
498 attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
499 for (a = 0; a < n_attributes; a++) {
500
501 /* We use the URI's included match attributes, but make them more strict. This allows users
502 * to specify a token URL instead of an object URL and the right thing should happen if
503 * there's only one suitable key on the token. */
504
505 switch (attributes[a].type) {
506
507 case CKA_CLASS: {
508 CK_OBJECT_CLASS c;
509
510 if (attributes[a].ulValueLen != sizeof(c))
511 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
512
513 memcpy(&c, attributes[a].pValue, sizeof(c));
514 if (c != CKO_PRIVATE_KEY)
515 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
516 "Selected PKCS#11 object is not a private key, refusing.");
517
518 found_class = true;
519 break;
520 }
521
522 case CKA_DECRYPT: {
523 CK_BBOOL b;
524
525 if (attributes[a].ulValueLen != sizeof(b))
526 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
527
528 memcpy(&b, attributes[a].pValue, sizeof(b));
529 if (!b)
530 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
531 "Selected PKCS#11 object is not suitable for decryption, refusing.");
532
533 found_decrypt = true;
534 break;
535 }
536
537 case CKA_KEY_TYPE: {
538 CK_KEY_TYPE t;
539
540 if (attributes[a].ulValueLen != sizeof(t))
541 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
542
543 memcpy(&t, attributes[a].pValue, sizeof(t));
544 if (t != CKK_RSA)
545 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing.");
546
547 found_key_type = true;
548 break;
549 }}
550 }
551
552 if (!found_decrypt || !found_class || !found_key_type) {
553 /* Hmm, let's slightly extend the attribute list we search for */
554
555 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
556 if (!attributes_buffer)
557 return log_oom();
558
559 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
560
561 if (!found_decrypt) {
562 static const CK_BBOOL yes = true;
563
564 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
565 .type = CKA_DECRYPT,
566 .pValue = (CK_BBOOL*) &yes,
567 .ulValueLen = sizeof(yes),
568 };
569 }
570
571 if (!found_class) {
572 static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
573
574 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
575 .type = CKA_CLASS,
576 .pValue = (CK_OBJECT_CLASS*) &class,
577 .ulValueLen = sizeof(class),
578 };
579 }
580
581 if (!found_key_type) {
582 static const CK_KEY_TYPE type = CKK_RSA;
583
584 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
585 .type = CKA_KEY_TYPE,
586 .pValue = (CK_KEY_TYPE*) &type,
587 .ulValueLen = sizeof(type),
588 };
589 }
590
591 attributes = attributes_buffer;
592 }
593
594 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
595 if (rv != CKR_OK)
596 return log_error_errno(SYNTHETIC_ERRNO(EIO),
597 "Failed to initialize object find call: %s", p11_kit_strerror(rv));
598
599 rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
600 rv2 = m->C_FindObjectsFinal(session);
601 if (rv != CKR_OK)
602 return log_error_errno(SYNTHETIC_ERRNO(EIO),
603 "Failed to find objects: %s", p11_kit_strerror(rv));
604 if (rv2 != CKR_OK)
605 return log_error_errno(SYNTHETIC_ERRNO(EIO),
606 "Failed to finalize object find call: %s", p11_kit_strerror(rv));
607 if (n_objects == 0)
608 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
609 "Failed to find selected private key suitable for decryption on token.");
610 if (n_objects > 1)
611 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
612 "Configured private key URI matches multiple keys, refusing.");
613
614 *ret_object = objects[0];
615 return 0;
616}
617
618int pkcs11_token_decrypt_data(
619 CK_FUNCTION_LIST *m,
620 CK_SESSION_HANDLE session,
621 CK_OBJECT_HANDLE object,
622 const void *encrypted_data,
623 size_t encrypted_data_size,
624 void **ret_decrypted_data,
625 size_t *ret_decrypted_data_size) {
626
627 static const CK_MECHANISM mechanism = {
628 .mechanism = CKM_RSA_PKCS
629 };
630 _cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
631 CK_ULONG dbuffer_size = 0;
632 CK_RV rv;
633
634 assert(m);
635 assert(encrypted_data);
636 assert(encrypted_data_size > 0);
637 assert(ret_decrypted_data);
638 assert(ret_decrypted_data_size);
639
640 rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
641 if (rv != CKR_OK)
642 return log_error_errno(SYNTHETIC_ERRNO(EIO),
643 "Failed to initialize decryption on security token: %s", p11_kit_strerror(rv));
644
645 dbuffer_size = encrypted_data_size; /* Start with something reasonable */
646 dbuffer = malloc(dbuffer_size);
647 if (!dbuffer)
648 return log_oom();
649
650 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
651 if (rv == CKR_BUFFER_TOO_SMALL) {
652 erase_and_free(dbuffer);
653
654 dbuffer = malloc(dbuffer_size);
655 if (!dbuffer)
656 return log_oom();
657
658 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
659 }
660 if (rv != CKR_OK)
661 return log_error_errno(SYNTHETIC_ERRNO(EIO),
662 "Failed to decrypt key on security token: %s", p11_kit_strerror(rv));
663
664 log_info("Successfully decrypted key with security token.");
665
666 *ret_decrypted_data = TAKE_PTR(dbuffer);
667 *ret_decrypted_data_size = dbuffer_size;
668 return 0;
669}
670
671int pkcs11_token_acquire_rng(
672 CK_FUNCTION_LIST *m,
673 CK_SESSION_HANDLE session) {
674
675 _cleanup_free_ void *buffer = NULL;
839fddbe
LP
676 size_t rps;
677 CK_RV rv;
678 int r;
679
680 assert(m);
681
682 /* While we are at it, let's read some RNG data from the PKCS#11 token and pass it to the kernel
683 * random pool. This should be cheap if we are talking to the device already. Note that we don't
684 * credit any entropy, since we don't know about the quality of the pkcs#11 token's RNG. Why bother
685 * at all? There are two sides to the argument whether to generate private keys on tokens or on the
686 * host. By crediting some data from the token RNG to the host's pool we at least can say that any
687 * key generated from it is at least as good as both sources individually. */
688
689 rps = random_pool_size();
690
691 buffer = malloc(rps);
692 if (!buffer)
693 return log_oom();
694
695 rv = m->C_GenerateRandom(session, buffer, rps);
696 if (rv != CKR_OK)
697 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
698 "Failed to generate RNG data on security token: %s", p11_kit_strerror(rv));
699
61bd7d1e 700 r = random_write_entropy(-1, buffer, rps, false);
839fddbe
LP
701 if (r < 0)
702 return log_debug_errno(r, "Failed to write PKCS#11 acquired random data to /dev/urandom: %m");
703
704 log_debug("Successfully written %zu bytes random data acquired via PKCS#11 to kernel random pool.", rps);
705
706 return 0;
707}
708
709static int token_process(
710 CK_FUNCTION_LIST *m,
711 CK_SLOT_ID slotid,
712 const CK_SLOT_INFO *slot_info,
713 const CK_TOKEN_INFO *token_info,
714 P11KitUri *search_uri,
715 pkcs11_find_token_callback_t callback,
716 void *userdata) {
717
718 _cleanup_free_ char *token_label = NULL;
719 CK_SESSION_HANDLE session;
720 CK_RV rv;
721 int r;
722
723 assert(m);
724 assert(slot_info);
725 assert(token_info);
839fddbe
LP
726
727 token_label = pkcs11_token_label(token_info);
728 if (!token_label)
729 return log_oom();
730
731 rv = m->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session);
732 if (rv != CKR_OK)
733 return log_error_errno(SYNTHETIC_ERRNO(EIO),
734 "Failed to create session for security token '%s': %s", token_label, p11_kit_strerror(rv));
735
736 if (callback)
737 r = callback(m, session, slotid, slot_info, token_info, search_uri, userdata);
738 else
739 r = 1; /* if not callback was specified, just say we found what we were looking for */
740
741 rv = m->C_CloseSession(session);
742 if (rv != CKR_OK)
743 log_warning("Failed to close session on PKCS#11 token, ignoring: %s", p11_kit_strerror(rv));
744
745 return r;
746}
747
748static int slot_process(
749 CK_FUNCTION_LIST *m,
750 CK_SLOT_ID slotid,
751 P11KitUri *search_uri,
752 pkcs11_find_token_callback_t callback,
753 void *userdata) {
754
755 _cleanup_(p11_kit_uri_freep) P11KitUri* slot_uri = NULL, *token_uri = NULL;
756 _cleanup_free_ char *token_uri_string = NULL;
757 CK_TOKEN_INFO token_info;
758 CK_SLOT_INFO slot_info;
759 int uri_result;
760 CK_RV rv;
761
762 assert(m);
839fddbe
LP
763
764 /* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the
765 * caller might try other slots before giving up. */
766
767 rv = m->C_GetSlotInfo(slotid, &slot_info);
768 if (rv != CKR_OK) {
769 log_warning("Failed to acquire slot info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
770 return -EAGAIN;
771 }
772
773 slot_uri = uri_from_slot_info(&slot_info);
774 if (!slot_uri)
775 return log_oom();
776
777 if (DEBUG_LOGGING) {
778 _cleanup_free_ char *slot_uri_string = NULL;
779
780 uri_result = p11_kit_uri_format(slot_uri, P11_KIT_URI_FOR_ANY, &slot_uri_string);
781 if (uri_result != P11_KIT_URI_OK) {
782 log_warning("Failed to format slot URI, ignoring slot: %s", p11_kit_uri_message(uri_result));
783 return -EAGAIN;
784 }
785
786 log_debug("Found slot with URI %s", slot_uri_string);
787 }
788
789 rv = m->C_GetTokenInfo(slotid, &token_info);
790 if (rv == CKR_TOKEN_NOT_PRESENT) {
d7a0f1f4
FS
791 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
792 "Token not present in slot, ignoring.");
839fddbe
LP
793 } else if (rv != CKR_OK) {
794 log_warning("Failed to acquire token info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
795 return -EAGAIN;
796 }
797
798 token_uri = uri_from_token_info(&token_info);
799 if (!token_uri)
800 return log_oom();
801
802 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
803 if (uri_result != P11_KIT_URI_OK) {
804 log_warning("Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
805 return -EAGAIN;
806 }
807
d7a0f1f4
FS
808 if (search_uri && !p11_kit_uri_match_token_info(search_uri, &token_info))
809 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
810 "Found non-matching token with URI %s.",
811 token_uri_string);
839fddbe
LP
812
813 log_debug("Found matching token with URI %s.", token_uri_string);
814
815 return token_process(
816 m,
817 slotid,
818 &slot_info,
819 &token_info,
820 search_uri,
821 callback,
822 userdata);
823}
824
825static int module_process(
826 CK_FUNCTION_LIST *m,
827 P11KitUri *search_uri,
828 pkcs11_find_token_callback_t callback,
829 void *userdata) {
830
831 _cleanup_free_ char *name = NULL, *module_uri_string = NULL;
832 _cleanup_(p11_kit_uri_freep) P11KitUri* module_uri = NULL;
833 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
834 CK_ULONG n_slotids = 0;
835 int uri_result;
836 CK_INFO info;
837 size_t k;
838 CK_RV rv;
839 int r;
840
841 assert(m);
839fddbe
LP
842
843 /* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module
844 * should not have the effect that we don't try the others anymore. We indicate such per-module
845 * failures with -EAGAIN, which let's the caller try the next module. */
846
847 name = p11_kit_module_get_name(m);
848 if (!name)
849 return log_oom();
850
851 log_debug("Trying PKCS#11 module %s.", name);
852
853 rv = m->C_GetInfo(&info);
854 if (rv != CKR_OK) {
855 log_warning("Failed to get info on PKCS#11 module, ignoring module: %s", p11_kit_strerror(rv));
856 return -EAGAIN;
857 }
858
859 module_uri = uri_from_module_info(&info);
860 if (!module_uri)
861 return log_oom();
862
863 uri_result = p11_kit_uri_format(module_uri, P11_KIT_URI_FOR_ANY, &module_uri_string);
864 if (uri_result != P11_KIT_URI_OK) {
865 log_warning("Failed to format module URI, ignoring module: %s", p11_kit_uri_message(uri_result));
866 return -EAGAIN;
867 }
868
869 log_debug("Found module with URI %s", module_uri_string);
870
871 rv = pkcs11_get_slot_list_malloc(m, &slotids, &n_slotids);
872 if (rv != CKR_OK) {
873 log_warning("Failed to get slot list, ignoring module: %s", p11_kit_strerror(rv));
874 return -EAGAIN;
875 }
d7a0f1f4
FS
876 if (n_slotids == 0)
877 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
878 "This module has no slots? Ignoring module.");
839fddbe
LP
879
880 for (k = 0; k < n_slotids; k++) {
881 r = slot_process(
882 m,
883 slotids[k],
884 search_uri,
885 callback,
886 userdata);
887 if (r != -EAGAIN)
888 return r;
889 }
890
891 return -EAGAIN;
892}
893
894int pkcs11_find_token(
895 const char *pkcs11_uri,
896 pkcs11_find_token_callback_t callback,
897 void *userdata) {
898
899 _cleanup_(p11_kit_modules_finalize_and_releasep) CK_FUNCTION_LIST **modules = NULL;
900 _cleanup_(p11_kit_uri_freep) P11KitUri *search_uri = NULL;
901 int r;
902
839fddbe
LP
903 /* Execute the specified callback for each matching token found. If nothing is found returns
904 * -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */
905
0eb3be46
LP
906 if (pkcs11_uri) {
907 r = uri_from_string(pkcs11_uri, &search_uri);
908 if (r < 0)
909 return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
910 }
839fddbe
LP
911
912 modules = p11_kit_modules_load_and_initialize(0);
913 if (!modules)
914 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize pkcs11 modules");
915
916 for (CK_FUNCTION_LIST **i = modules; *i; i++) {
917 r = module_process(
918 *i,
919 search_uri,
920 callback,
921 userdata);
922 if (r != -EAGAIN)
923 return r;
924 }
925
926 return -EAGAIN;
927}
928
2289a784
LP
929#if HAVE_OPENSSL
930struct pkcs11_acquire_certificate_callback_data {
931 char *pin_used;
932 X509 *cert;
933 const char *askpw_friendly_name, *askpw_icon_name;
934};
935
936static void pkcs11_acquire_certificate_callback_data_release(struct pkcs11_acquire_certificate_callback_data *data) {
937 erase_and_free(data->pin_used);
938 X509_free(data->cert);
939}
940
941static int pkcs11_acquire_certificate_callback(
942 CK_FUNCTION_LIST *m,
943 CK_SESSION_HANDLE session,
944 CK_SLOT_ID slot_id,
945 const CK_SLOT_INFO *slot_info,
946 const CK_TOKEN_INFO *token_info,
947 P11KitUri *uri,
948 void *userdata) {
949
950 _cleanup_(erase_and_freep) char *pin_used = NULL;
951 struct pkcs11_acquire_certificate_callback_data *data = userdata;
952 CK_OBJECT_HANDLE object;
953 int r;
954
955 assert(m);
956 assert(slot_info);
957 assert(token_info);
958 assert(uri);
959 assert(data);
960
961 /* Called for every token matching our URI */
962
8806bb4b 963 r = pkcs11_token_login(m, session, slot_id, token_info, data->askpw_friendly_name, data->askpw_icon_name, "pkcs11-pin", "pkcs11-pin", UINT64_MAX, &pin_used);
2289a784
LP
964 if (r < 0)
965 return r;
966
967 r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
968 if (r < 0)
969 return r;
970
971 r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
972 if (r < 0)
973 return r;
974
975 /* Let's read some random data off the token and write it to the kernel pool before we generate our
976 * random key from it. This way we can claim the quality of the RNG is at least as good as the
977 * kernel's and the token's pool */
978 (void) pkcs11_token_acquire_rng(m, session);
979
980 data->pin_used = TAKE_PTR(pin_used);
981 return 1;
982}
983
984int pkcs11_acquire_certificate(
985 const char *uri,
986 const char *askpw_friendly_name,
987 const char *askpw_icon_name,
988 X509 **ret_cert,
989 char **ret_pin_used) {
990
991 _cleanup_(pkcs11_acquire_certificate_callback_data_release) struct pkcs11_acquire_certificate_callback_data data = {
992 .askpw_friendly_name = askpw_friendly_name,
993 .askpw_icon_name = askpw_icon_name,
994 };
995 int r;
996
997 assert(uri);
998 assert(ret_cert);
999
1000 r = pkcs11_find_token(uri, pkcs11_acquire_certificate_callback, &data);
1001 if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
1002 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1003 "Specified PKCS#11 token with URI '%s' not found.",
1004 uri);
1005 if (r < 0)
1006 return r;
1007
1008 *ret_cert = TAKE_PTR(data.cert);
1009
1010 if (ret_pin_used)
1011 *ret_pin_used = TAKE_PTR(data.pin_used);
1012
1013 return 0;
1014}
f240cbb6
LP
1015#endif
1016
1017static int list_callback(
1018 CK_FUNCTION_LIST *m,
1019 CK_SESSION_HANDLE session,
1020 CK_SLOT_ID slot_id,
1021 const CK_SLOT_INFO *slot_info,
1022 const CK_TOKEN_INFO *token_info,
1023 P11KitUri *uri,
1024 void *userdata) {
1025
1026 _cleanup_free_ char *token_uri_string = NULL, *token_label = NULL, *token_manufacturer_id = NULL, *token_model = NULL;
1027 _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
1028 Table *t = userdata;
1029 int uri_result, r;
1030
1031 assert(slot_info);
1032 assert(token_info);
1033
1034 /* We only care about hardware devices here with a token inserted. Let's filter everything else
1035 * out. (Note that the user can explicitly specify non-hardware tokens if they like, but during
1036 * enumeration we'll filter those, since software tokens are typically the system certificate store
1037 * and such, and it's typically not what people want to bind their home directories to.) */
1038 if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
1039 return -EAGAIN;
1040
1041 token_label = pkcs11_token_label(token_info);
1042 if (!token_label)
1043 return log_oom();
1044
1045 token_manufacturer_id = pkcs11_token_manufacturer_id(token_info);
1046 if (!token_manufacturer_id)
1047 return log_oom();
1048
1049 token_model = pkcs11_token_model(token_info);
1050 if (!token_model)
1051 return log_oom();
1052
1053 token_uri = uri_from_token_info(token_info);
1054 if (!token_uri)
1055 return log_oom();
1056
1057 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
1058 if (uri_result != P11_KIT_URI_OK)
1059 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
1060
1061 r = table_add_many(
1062 t,
1063 TABLE_STRING, token_uri_string,
1064 TABLE_STRING, token_label,
1065 TABLE_STRING, token_manufacturer_id,
1066 TABLE_STRING, token_model);
1067 if (r < 0)
1068 return table_log_add_error(r);
1069
1070 return -EAGAIN; /* keep scanning */
1071}
1072#endif
1073
1074int pkcs11_list_tokens(void) {
1075#if HAVE_P11KIT
1076 _cleanup_(table_unrefp) Table *t = NULL;
1077 int r;
1078
1079 t = table_new("uri", "label", "manufacturer", "model");
1080 if (!t)
1081 return log_oom();
1082
1083 r = pkcs11_find_token(NULL, list_callback, t);
1084 if (r < 0 && r != -EAGAIN)
1085 return r;
2289a784 1086
f240cbb6
LP
1087 if (table_get_rows(t) <= 1) {
1088 log_info("No suitable PKCS#11 tokens found.");
1089 return 0;
1090 }
1091
1092 r = table_print(t, stdout);
1093 if (r < 0)
1094 return log_error_errno(r, "Failed to show device table: %m");
1095
1096 return 0;
1097#else
1098 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1099 "PKCS#11 tokens not supported on this build.");
1100#endif
1101}
1102
1103#if HAVE_P11KIT
1104static int auto_callback(
1105 CK_FUNCTION_LIST *m,
1106 CK_SESSION_HANDLE session,
1107 CK_SLOT_ID slot_id,
1108 const CK_SLOT_INFO *slot_info,
1109 const CK_TOKEN_INFO *token_info,
1110 P11KitUri *uri,
1111 void *userdata) {
1112
1113 _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
1114 char **t = userdata;
1115 int uri_result;
1116
1117 assert(slot_info);
1118 assert(token_info);
1119
1120 if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
1121 return -EAGAIN;
1122
1123 if (*t)
1124 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
1125 "More than one suitable PKCS#11 token found.");
1126
1127 token_uri = uri_from_token_info(token_info);
1128 if (!token_uri)
1129 return log_oom();
1130
1131 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, t);
1132 if (uri_result != P11_KIT_URI_OK)
1133 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
1134
1135 return 0;
1136}
2289a784 1137#endif
f240cbb6
LP
1138
1139int pkcs11_find_token_auto(char **ret) {
1140#if HAVE_P11KIT
1141 int r;
1142
1143 r = pkcs11_find_token(NULL, auto_callback, ret);
1144 if (r == -EAGAIN)
1145 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No suitable PKCS#11 tokens found.");
1146 if (r < 0)
1147 return r;
1148
1149 return 0;
1150#else
1151 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1152 "PKCS#11 tokens not supported on this build.");
839fddbe 1153#endif
f240cbb6 1154}