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