]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/pkcs11-util.c
Merge pull request #15463 from keszybz/resolvectl-query-formatting
[thirdparty/systemd.git] / src / shared / pkcs11-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <fcntl.h>
4
5 #include "ask-password-api.h"
6 #include "escape.h"
7 #include "fd-util.h"
8 #include "io-util.h"
9 #include "memory-util.h"
10 #if HAVE_OPENSSL
11 #include "openssl-util.h"
12 #endif
13 #include "pkcs11-util.h"
14 #include "random-util.h"
15 #include "string-util.h"
16 #include "strv.h"
17
18 bool pkcs11_uri_valid(const char *uri) {
19 const char *p;
20
21 /* A very superficial checker for RFC7512 PKCS#11 URI syntax */
22
23 if (isempty(uri))
24 return false;
25
26 p = startswith(uri, "pkcs11:");
27 if (!p)
28 return false;
29
30 if (isempty(p))
31 return false;
32
33 if (!in_charset(p, ALPHANUMERICAL "-_?;&%="))
34 return false;
35
36 return true;
37 }
38
39 #if HAVE_P11KIT
40
41 int uri_from_string(const char *p, P11KitUri **ret) {
42 _cleanup_(p11_kit_uri_freep) P11KitUri *uri = NULL;
43
44 assert(p);
45 assert(ret);
46
47 uri = p11_kit_uri_new();
48 if (!uri)
49 return -ENOMEM;
50
51 if (p11_kit_uri_parse(p, P11_KIT_URI_FOR_ANY, uri) != P11_KIT_URI_OK)
52 return -EINVAL;
53
54 *ret = TAKE_PTR(uri);
55 return 0;
56 }
57
58 P11KitUri *uri_from_module_info(const CK_INFO *info) {
59 P11KitUri *uri;
60
61 assert(info);
62
63 uri = p11_kit_uri_new();
64 if (!uri)
65 return NULL;
66
67 *p11_kit_uri_get_module_info(uri) = *info;
68 return uri;
69 }
70
71 P11KitUri *uri_from_slot_info(const CK_SLOT_INFO *slot_info) {
72 P11KitUri *uri;
73
74 assert(slot_info);
75
76 uri = p11_kit_uri_new();
77 if (!uri)
78 return NULL;
79
80 *p11_kit_uri_get_slot_info(uri) = *slot_info;
81 return uri;
82 }
83
84 P11KitUri *uri_from_token_info(const CK_TOKEN_INFO *token_info) {
85 P11KitUri *uri;
86
87 assert(token_info);
88
89 uri = p11_kit_uri_new();
90 if (!uri)
91 return NULL;
92
93 *p11_kit_uri_get_token_info(uri) = *token_info;
94 return uri;
95 }
96
97 CK_RV pkcs11_get_slot_list_malloc(
98 CK_FUNCTION_LIST *m,
99 CK_SLOT_ID **ret_slotids,
100 CK_ULONG *ret_n_slotids) {
101
102 CK_RV rv;
103
104 assert(m);
105 assert(ret_slotids);
106 assert(ret_n_slotids);
107
108 for (unsigned tries = 0; tries < 16; tries++) {
109 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
110 CK_ULONG n_slotids = 0;
111
112 rv = m->C_GetSlotList(0, NULL, &n_slotids);
113 if (rv != CKR_OK)
114 return rv;
115 if (n_slotids == 0) {
116 *ret_slotids = NULL;
117 *ret_n_slotids = 0;
118 return CKR_OK;
119 }
120
121 slotids = new(CK_SLOT_ID, n_slotids);
122 if (!slotids)
123 return CKR_HOST_MEMORY;
124
125 rv = m->C_GetSlotList(0, slotids, &n_slotids);
126 if (rv == CKR_OK) {
127 *ret_slotids = TAKE_PTR(slotids);
128 *ret_n_slotids = n_slotids;
129 return CKR_OK;
130 }
131
132 if (rv != CKR_BUFFER_TOO_SMALL)
133 return rv;
134
135 /* Hu? Maybe somebody plugged something in and things changed? Let's try again */
136 }
137
138 return CKR_BUFFER_TOO_SMALL;
139 }
140
141 char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
142 char *t;
143
144 /* The label is not NUL terminated and likely padded with spaces, let's make a copy here, so that we
145 * can strip that. */
146 t = strndup((char*) token_info->label, sizeof(token_info->label));
147 if (!t)
148 return NULL;
149
150 strstrip(t);
151 return t;
152 }
153
154 int pkcs11_token_login(
155 CK_FUNCTION_LIST *m,
156 CK_SESSION_HANDLE session,
157 CK_SLOT_ID slotid,
158 const CK_TOKEN_INFO *token_info,
159 const char *friendly_name,
160 const char *icon_name,
161 const char *keyname,
162 usec_t until,
163 char **ret_used_pin) {
164
165 _cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
166 _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
167 CK_TOKEN_INFO updated_token_info;
168 int uri_result;
169 CK_RV rv;
170 int r;
171
172 assert(m);
173 assert(token_info);
174
175 token_label = pkcs11_token_label(token_info);
176 if (!token_label)
177 return log_oom();
178
179 token_uri = uri_from_token_info(token_info);
180 if (!token_uri)
181 return log_oom();
182
183 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
184 if (uri_result != P11_KIT_URI_OK)
185 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
186
187 if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
188 rv = m->C_Login(session, CKU_USER, NULL, 0);
189 if (rv != CKR_OK)
190 return log_error_errno(SYNTHETIC_ERRNO(EIO),
191 "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
192
193 log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
194 *ret_used_pin = NULL;
195 return 0;
196 }
197
198 if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
199 log_info("No login into security token '%s' required.", token_label);
200 *ret_used_pin = NULL;
201 return 0;
202 }
203
204 token_uri_escaped = cescape(token_uri_string);
205 if (!token_uri_escaped)
206 return log_oom();
207
208 id = strjoin("pkcs11:", token_uri_escaped);
209 if (!id)
210 return log_oom();
211
212 for (unsigned tries = 0; tries < 3; tries++) {
213 _cleanup_strv_free_erase_ char **passwords = NULL;
214 _cleanup_free_ char *text = NULL;
215 char **i, *e;
216
217 if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
218 r = asprintf(&text,
219 "Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
220 token_label, friendly_name);
221 else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
222 r = asprintf(&text,
223 "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
224 token_label, friendly_name);
225 else if (tries == 0)
226 r = asprintf(&text,
227 "Please enter PIN for security token '%s' in order to unlock %s:",
228 token_label, friendly_name);
229 else
230 r = asprintf(&text,
231 "Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
232 token_label, friendly_name, tries+1);
233 if (r < 0)
234 return log_oom();
235
236 e = getenv("PIN");
237 if (e) {
238 passwords = strv_new(e);
239 if (!passwords)
240 return log_oom();
241
242 string_erase(e);
243 if (unsetenv("PIN") < 0)
244 return log_error_errno(errno, "Failed to unset $PIN: %m");
245 } else {
246 /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
247 r = ask_password_auto(text, icon_name, id, keyname, until, 0, &passwords);
248 if (r < 0)
249 return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
250 }
251
252 STRV_FOREACH(i, passwords) {
253 rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
254 if (rv == CKR_OK) {
255
256 if (ret_used_pin) {
257 char *c;
258
259 c = strdup(*i);
260 if (!c)
261 return log_oom();
262
263 *ret_used_pin = c;
264 }
265
266 log_info("Successfully logged into security token '%s'.", token_label);
267 return 0;
268 }
269 if (rv == CKR_PIN_LOCKED)
270 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
271 "PIN has been locked, please reset PIN of security token '%s'.", token_label);
272 if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
273 return log_error_errno(SYNTHETIC_ERRNO(EIO),
274 "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
275
276 /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
277 rv = m->C_GetTokenInfo(slotid, &updated_token_info);
278 if (rv != CKR_OK)
279 return log_error_errno(SYNTHETIC_ERRNO(EIO),
280 "Failed to acquire updated security token information for slot %lu: %s",
281 slotid, p11_kit_strerror(rv));
282
283 token_info = &updated_token_info;
284 log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
285 }
286 }
287
288 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to log into token '%s'.", token_label);
289 }
290
291 int pkcs11_token_find_x509_certificate(
292 CK_FUNCTION_LIST *m,
293 CK_SESSION_HANDLE session,
294 P11KitUri *search_uri,
295 CK_OBJECT_HANDLE *ret_object) {
296
297 bool found_class = false, found_certificate_type = false;
298 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
299 CK_ULONG n_attributes, a, n_objects;
300 CK_ATTRIBUTE *attributes = NULL;
301 CK_OBJECT_HANDLE objects[2];
302 CK_RV rv, rv2;
303
304 assert(m);
305 assert(search_uri);
306 assert(ret_object);
307
308 attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
309 for (a = 0; a < n_attributes; a++) {
310
311 /* We use the URI's included match attributes, but make them more strict. This allows users
312 * to specify a token URL instead of an object URL and the right thing should happen if
313 * there's only one suitable key on the token. */
314
315 switch (attributes[a].type) {
316
317 case CKA_CLASS: {
318 CK_OBJECT_CLASS c;
319
320 if (attributes[a].ulValueLen != sizeof(c))
321 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
322
323 memcpy(&c, attributes[a].pValue, sizeof(c));
324 if (c != CKO_CERTIFICATE)
325 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
326
327 found_class = true;
328 break;
329 }
330
331 case CKA_CERTIFICATE_TYPE: {
332 CK_CERTIFICATE_TYPE t;
333
334 if (attributes[a].ulValueLen != sizeof(t))
335 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CERTIFICATE_TYPE attribute size.");
336
337 memcpy(&t, attributes[a].pValue, sizeof(t));
338 if (t != CKC_X_509)
339 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
340
341 found_certificate_type = true;
342 break;
343 }}
344 }
345
346 if (!found_class || !found_certificate_type) {
347 /* Hmm, let's slightly extend the attribute list we search for */
348
349 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_class + !found_certificate_type);
350 if (!attributes_buffer)
351 return log_oom();
352
353 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
354
355 if (!found_class) {
356 static const CK_OBJECT_CLASS class = CKO_CERTIFICATE;
357
358 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
359 .type = CKA_CLASS,
360 .pValue = (CK_OBJECT_CLASS*) &class,
361 .ulValueLen = sizeof(class),
362 };
363 }
364
365 if (!found_certificate_type) {
366 static const CK_CERTIFICATE_TYPE type = CKC_X_509;
367
368 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
369 .type = CKA_CERTIFICATE_TYPE,
370 .pValue = (CK_CERTIFICATE_TYPE*) &type,
371 .ulValueLen = sizeof(type),
372 };
373 }
374
375 attributes = attributes_buffer;
376 }
377
378 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
379 if (rv != CKR_OK)
380 return log_error_errno(SYNTHETIC_ERRNO(EIO),
381 "Failed to initialize object find call: %s", p11_kit_strerror(rv));
382
383 rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
384 rv2 = m->C_FindObjectsFinal(session);
385 if (rv != CKR_OK)
386 return log_error_errno(SYNTHETIC_ERRNO(EIO),
387 "Failed to find objects: %s", p11_kit_strerror(rv));
388 if (rv2 != CKR_OK)
389 return log_error_errno(SYNTHETIC_ERRNO(EIO),
390 "Failed to finalize object find call: %s", p11_kit_strerror(rv));
391 if (n_objects == 0)
392 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
393 "Failed to find selected X509 certificate on token.");
394 if (n_objects > 1)
395 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
396 "Configured URI matches multiple certificates, refusing.");
397
398 *ret_object = objects[0];
399 return 0;
400 }
401
402 #if HAVE_OPENSSL
403 int pkcs11_token_read_x509_certificate(
404 CK_FUNCTION_LIST *m,
405 CK_SESSION_HANDLE session,
406 CK_OBJECT_HANDLE object,
407 X509 **ret_cert) {
408
409 _cleanup_free_ void *buffer = NULL;
410 _cleanup_free_ char *t = NULL;
411 CK_ATTRIBUTE attribute = {
412 .type = CKA_VALUE
413 };
414 CK_RV rv;
415 _cleanup_(X509_freep) X509 *x509 = NULL;
416 X509_NAME *name = NULL;
417 const unsigned char *p;
418
419 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
420 if (rv != CKR_OK)
421 return log_error_errno(SYNTHETIC_ERRNO(EIO),
422 "Failed to read X.509 certificate size off token: %s", p11_kit_strerror(rv));
423
424 buffer = malloc(attribute.ulValueLen);
425 if (!buffer)
426 return log_oom();
427
428 attribute.pValue = buffer;
429
430 rv = m->C_GetAttributeValue(session, object, &attribute, 1);
431 if (rv != CKR_OK)
432 return log_error_errno(SYNTHETIC_ERRNO(EIO),
433 "Failed to read X.509 certificate data off token: %s", p11_kit_strerror(rv));
434
435 p = attribute.pValue;
436 x509 = d2i_X509(NULL, &p, attribute.ulValueLen);
437 if (!x509)
438 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed parse X.509 certificate.");
439
440 name = X509_get_subject_name(x509);
441 if (!name)
442 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to acquire X.509 subject name.");
443
444 t = X509_NAME_oneline(name, NULL, 0);
445 if (!t)
446 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format X.509 subject name as string.");
447
448 log_debug("Using X.509 certificate issued for '%s'.", t);
449
450 *ret_cert = TAKE_PTR(x509);
451 return 0;
452 }
453 #endif
454
455 int pkcs11_token_find_private_key(
456 CK_FUNCTION_LIST *m,
457 CK_SESSION_HANDLE session,
458 P11KitUri *search_uri,
459 CK_OBJECT_HANDLE *ret_object) {
460
461 bool found_decrypt = false, found_class = false, found_key_type = false;
462 _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
463 CK_ULONG n_attributes, a, n_objects;
464 CK_ATTRIBUTE *attributes = NULL;
465 CK_OBJECT_HANDLE objects[2];
466 CK_RV rv, rv2;
467
468 assert(m);
469 assert(search_uri);
470 assert(ret_object);
471
472 attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
473 for (a = 0; a < n_attributes; a++) {
474
475 /* We use the URI's included match attributes, but make them more strict. This allows users
476 * to specify a token URL instead of an object URL and the right thing should happen if
477 * there's only one suitable key on the token. */
478
479 switch (attributes[a].type) {
480
481 case CKA_CLASS: {
482 CK_OBJECT_CLASS c;
483
484 if (attributes[a].ulValueLen != sizeof(c))
485 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
486
487 memcpy(&c, attributes[a].pValue, sizeof(c));
488 if (c != CKO_PRIVATE_KEY)
489 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
490 "Selected PKCS#11 object is not a private key, refusing.");
491
492 found_class = true;
493 break;
494 }
495
496 case CKA_DECRYPT: {
497 CK_BBOOL b;
498
499 if (attributes[a].ulValueLen != sizeof(b))
500 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
501
502 memcpy(&b, attributes[a].pValue, sizeof(b));
503 if (!b)
504 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
505 "Selected PKCS#11 object is not suitable for decryption, refusing.");
506
507 found_decrypt = true;
508 break;
509 }
510
511 case CKA_KEY_TYPE: {
512 CK_KEY_TYPE t;
513
514 if (attributes[a].ulValueLen != sizeof(t))
515 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
516
517 memcpy(&t, attributes[a].pValue, sizeof(t));
518 if (t != CKK_RSA)
519 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing.");
520
521 found_key_type = true;
522 break;
523 }}
524 }
525
526 if (!found_decrypt || !found_class || !found_key_type) {
527 /* Hmm, let's slightly extend the attribute list we search for */
528
529 attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
530 if (!attributes_buffer)
531 return log_oom();
532
533 memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
534
535 if (!found_decrypt) {
536 static const CK_BBOOL yes = true;
537
538 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
539 .type = CKA_DECRYPT,
540 .pValue = (CK_BBOOL*) &yes,
541 .ulValueLen = sizeof(yes),
542 };
543 }
544
545 if (!found_class) {
546 static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
547
548 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
549 .type = CKA_CLASS,
550 .pValue = (CK_OBJECT_CLASS*) &class,
551 .ulValueLen = sizeof(class),
552 };
553 }
554
555 if (!found_key_type) {
556 static const CK_KEY_TYPE type = CKK_RSA;
557
558 attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
559 .type = CKA_KEY_TYPE,
560 .pValue = (CK_KEY_TYPE*) &type,
561 .ulValueLen = sizeof(type),
562 };
563 }
564
565 attributes = attributes_buffer;
566 }
567
568 rv = m->C_FindObjectsInit(session, attributes, n_attributes);
569 if (rv != CKR_OK)
570 return log_error_errno(SYNTHETIC_ERRNO(EIO),
571 "Failed to initialize object find call: %s", p11_kit_strerror(rv));
572
573 rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
574 rv2 = m->C_FindObjectsFinal(session);
575 if (rv != CKR_OK)
576 return log_error_errno(SYNTHETIC_ERRNO(EIO),
577 "Failed to find objects: %s", p11_kit_strerror(rv));
578 if (rv2 != CKR_OK)
579 return log_error_errno(SYNTHETIC_ERRNO(EIO),
580 "Failed to finalize object find call: %s", p11_kit_strerror(rv));
581 if (n_objects == 0)
582 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
583 "Failed to find selected private key suitable for decryption on token.");
584 if (n_objects > 1)
585 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
586 "Configured private key URI matches multiple keys, refusing.");
587
588 *ret_object = objects[0];
589 return 0;
590 }
591
592 int pkcs11_token_decrypt_data(
593 CK_FUNCTION_LIST *m,
594 CK_SESSION_HANDLE session,
595 CK_OBJECT_HANDLE object,
596 const void *encrypted_data,
597 size_t encrypted_data_size,
598 void **ret_decrypted_data,
599 size_t *ret_decrypted_data_size) {
600
601 static const CK_MECHANISM mechanism = {
602 .mechanism = CKM_RSA_PKCS
603 };
604 _cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
605 CK_ULONG dbuffer_size = 0;
606 CK_RV rv;
607
608 assert(m);
609 assert(encrypted_data);
610 assert(encrypted_data_size > 0);
611 assert(ret_decrypted_data);
612 assert(ret_decrypted_data_size);
613
614 rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
615 if (rv != CKR_OK)
616 return log_error_errno(SYNTHETIC_ERRNO(EIO),
617 "Failed to initialize decryption on security token: %s", p11_kit_strerror(rv));
618
619 dbuffer_size = encrypted_data_size; /* Start with something reasonable */
620 dbuffer = malloc(dbuffer_size);
621 if (!dbuffer)
622 return log_oom();
623
624 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
625 if (rv == CKR_BUFFER_TOO_SMALL) {
626 erase_and_free(dbuffer);
627
628 dbuffer = malloc(dbuffer_size);
629 if (!dbuffer)
630 return log_oom();
631
632 rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
633 }
634 if (rv != CKR_OK)
635 return log_error_errno(SYNTHETIC_ERRNO(EIO),
636 "Failed to decrypt key on security token: %s", p11_kit_strerror(rv));
637
638 log_info("Successfully decrypted key with security token.");
639
640 *ret_decrypted_data = TAKE_PTR(dbuffer);
641 *ret_decrypted_data_size = dbuffer_size;
642 return 0;
643 }
644
645 int pkcs11_token_acquire_rng(
646 CK_FUNCTION_LIST *m,
647 CK_SESSION_HANDLE session) {
648
649 _cleanup_free_ void *buffer = NULL;
650 _cleanup_close_ int fd = -1;
651 size_t rps;
652 CK_RV rv;
653 int r;
654
655 assert(m);
656
657 /* While we are at it, let's read some RNG data from the PKCS#11 token and pass it to the kernel
658 * random pool. This should be cheap if we are talking to the device already. Note that we don't
659 * credit any entropy, since we don't know about the quality of the pkcs#11 token's RNG. Why bother
660 * at all? There are two sides to the argument whether to generate private keys on tokens or on the
661 * host. By crediting some data from the token RNG to the host's pool we at least can say that any
662 * key generated from it is at least as good as both sources individually. */
663
664 rps = random_pool_size();
665
666 buffer = malloc(rps);
667 if (!buffer)
668 return log_oom();
669
670 rv = m->C_GenerateRandom(session, buffer, rps);
671 if (rv != CKR_OK)
672 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
673 "Failed to generate RNG data on security token: %s", p11_kit_strerror(rv));
674
675 fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
676 if (fd < 0)
677 return log_debug_errno(errno, "Failed to open /dev/urandom for writing: %m");
678
679 r = loop_write(fd, buffer, rps, false);
680 if (r < 0)
681 return log_debug_errno(r, "Failed to write PKCS#11 acquired random data to /dev/urandom: %m");
682
683 log_debug("Successfully written %zu bytes random data acquired via PKCS#11 to kernel random pool.", rps);
684
685 return 0;
686 }
687
688 static int token_process(
689 CK_FUNCTION_LIST *m,
690 CK_SLOT_ID slotid,
691 const CK_SLOT_INFO *slot_info,
692 const CK_TOKEN_INFO *token_info,
693 P11KitUri *search_uri,
694 pkcs11_find_token_callback_t callback,
695 void *userdata) {
696
697 _cleanup_free_ char *token_label = NULL;
698 CK_SESSION_HANDLE session;
699 CK_RV rv;
700 int r;
701
702 assert(m);
703 assert(slot_info);
704 assert(token_info);
705 assert(search_uri);
706
707 token_label = pkcs11_token_label(token_info);
708 if (!token_label)
709 return log_oom();
710
711 rv = m->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session);
712 if (rv != CKR_OK)
713 return log_error_errno(SYNTHETIC_ERRNO(EIO),
714 "Failed to create session for security token '%s': %s", token_label, p11_kit_strerror(rv));
715
716 if (callback)
717 r = callback(m, session, slotid, slot_info, token_info, search_uri, userdata);
718 else
719 r = 1; /* if not callback was specified, just say we found what we were looking for */
720
721 rv = m->C_CloseSession(session);
722 if (rv != CKR_OK)
723 log_warning("Failed to close session on PKCS#11 token, ignoring: %s", p11_kit_strerror(rv));
724
725 return r;
726 }
727
728 static int slot_process(
729 CK_FUNCTION_LIST *m,
730 CK_SLOT_ID slotid,
731 P11KitUri *search_uri,
732 pkcs11_find_token_callback_t callback,
733 void *userdata) {
734
735 _cleanup_(p11_kit_uri_freep) P11KitUri* slot_uri = NULL, *token_uri = NULL;
736 _cleanup_free_ char *token_uri_string = NULL;
737 CK_TOKEN_INFO token_info;
738 CK_SLOT_INFO slot_info;
739 int uri_result;
740 CK_RV rv;
741
742 assert(m);
743 assert(search_uri);
744
745 /* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the
746 * caller might try other slots before giving up. */
747
748 rv = m->C_GetSlotInfo(slotid, &slot_info);
749 if (rv != CKR_OK) {
750 log_warning("Failed to acquire slot info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
751 return -EAGAIN;
752 }
753
754 slot_uri = uri_from_slot_info(&slot_info);
755 if (!slot_uri)
756 return log_oom();
757
758 if (DEBUG_LOGGING) {
759 _cleanup_free_ char *slot_uri_string = NULL;
760
761 uri_result = p11_kit_uri_format(slot_uri, P11_KIT_URI_FOR_ANY, &slot_uri_string);
762 if (uri_result != P11_KIT_URI_OK) {
763 log_warning("Failed to format slot URI, ignoring slot: %s", p11_kit_uri_message(uri_result));
764 return -EAGAIN;
765 }
766
767 log_debug("Found slot with URI %s", slot_uri_string);
768 }
769
770 rv = m->C_GetTokenInfo(slotid, &token_info);
771 if (rv == CKR_TOKEN_NOT_PRESENT) {
772 log_debug("Token not present in slot, ignoring.");
773 return -EAGAIN;
774 } else if (rv != CKR_OK) {
775 log_warning("Failed to acquire token info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
776 return -EAGAIN;
777 }
778
779 token_uri = uri_from_token_info(&token_info);
780 if (!token_uri)
781 return log_oom();
782
783 uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
784 if (uri_result != P11_KIT_URI_OK) {
785 log_warning("Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
786 return -EAGAIN;
787 }
788
789 if (!p11_kit_uri_match_token_info(search_uri, &token_info)) {
790 log_debug("Found non-matching token with URI %s.", token_uri_string);
791 return -EAGAIN;
792 }
793
794 log_debug("Found matching token with URI %s.", token_uri_string);
795
796 return token_process(
797 m,
798 slotid,
799 &slot_info,
800 &token_info,
801 search_uri,
802 callback,
803 userdata);
804 }
805
806 static int module_process(
807 CK_FUNCTION_LIST *m,
808 P11KitUri *search_uri,
809 pkcs11_find_token_callback_t callback,
810 void *userdata) {
811
812 _cleanup_free_ char *name = NULL, *module_uri_string = NULL;
813 _cleanup_(p11_kit_uri_freep) P11KitUri* module_uri = NULL;
814 _cleanup_free_ CK_SLOT_ID *slotids = NULL;
815 CK_ULONG n_slotids = 0;
816 int uri_result;
817 CK_INFO info;
818 size_t k;
819 CK_RV rv;
820 int r;
821
822 assert(m);
823 assert(search_uri);
824
825 /* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module
826 * should not have the effect that we don't try the others anymore. We indicate such per-module
827 * failures with -EAGAIN, which let's the caller try the next module. */
828
829 name = p11_kit_module_get_name(m);
830 if (!name)
831 return log_oom();
832
833 log_debug("Trying PKCS#11 module %s.", name);
834
835 rv = m->C_GetInfo(&info);
836 if (rv != CKR_OK) {
837 log_warning("Failed to get info on PKCS#11 module, ignoring module: %s", p11_kit_strerror(rv));
838 return -EAGAIN;
839 }
840
841 module_uri = uri_from_module_info(&info);
842 if (!module_uri)
843 return log_oom();
844
845 uri_result = p11_kit_uri_format(module_uri, P11_KIT_URI_FOR_ANY, &module_uri_string);
846 if (uri_result != P11_KIT_URI_OK) {
847 log_warning("Failed to format module URI, ignoring module: %s", p11_kit_uri_message(uri_result));
848 return -EAGAIN;
849 }
850
851 log_debug("Found module with URI %s", module_uri_string);
852
853 rv = pkcs11_get_slot_list_malloc(m, &slotids, &n_slotids);
854 if (rv != CKR_OK) {
855 log_warning("Failed to get slot list, ignoring module: %s", p11_kit_strerror(rv));
856 return -EAGAIN;
857 }
858 if (n_slotids == 0) {
859 log_debug("This module has no slots? Ignoring module.");
860 return -EAGAIN;
861 }
862
863 for (k = 0; k < n_slotids; k++) {
864 r = slot_process(
865 m,
866 slotids[k],
867 search_uri,
868 callback,
869 userdata);
870 if (r != -EAGAIN)
871 return r;
872 }
873
874 return -EAGAIN;
875 }
876
877 int pkcs11_find_token(
878 const char *pkcs11_uri,
879 pkcs11_find_token_callback_t callback,
880 void *userdata) {
881
882 _cleanup_(p11_kit_modules_finalize_and_releasep) CK_FUNCTION_LIST **modules = NULL;
883 _cleanup_(p11_kit_uri_freep) P11KitUri *search_uri = NULL;
884 int r;
885
886 assert(pkcs11_uri);
887
888 /* Execute the specified callback for each matching token found. If nothing is found returns
889 * -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */
890
891 r = uri_from_string(pkcs11_uri, &search_uri);
892 if (r < 0)
893 return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
894
895 modules = p11_kit_modules_load_and_initialize(0);
896 if (!modules)
897 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize pkcs11 modules");
898
899 for (CK_FUNCTION_LIST **i = modules; *i; i++) {
900 r = module_process(
901 *i,
902 search_uri,
903 callback,
904 userdata);
905 if (r != -EAGAIN)
906 return r;
907 }
908
909 return -EAGAIN;
910 }
911
912 #endif