]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-pkcs11.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-pkcs11.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6
7 #include <p11-kit/p11-kit.h>
8 #include <p11-kit/uri.h>
9
10 #include "alloc-util.h"
11 #include "ask-password-api.h"
12 #include "cryptsetup-pkcs11.h"
13 #include "escape.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "format-util.h"
17 #include "hexdecoct.h"
18 #include "json.h"
19 #include "macro.h"
20 #include "memory-util.h"
21 #include "parse-util.h"
22 #include "pkcs11-util.h"
23 #include "random-util.h"
24 #include "stat-util.h"
25 #include "strv.h"
26
27 int decrypt_pkcs11_key(
28 const char *volume_name,
29 const char *friendly_name,
30 const char *pkcs11_uri,
31 const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */
32 size_t key_file_size,
33 uint64_t key_file_offset,
34 const void *key_data, /* … or key_data and key_data_size (for literal keys) */
35 size_t key_data_size,
36 usec_t until,
37 bool headless,
38 void **ret_decrypted_key,
39 size_t *ret_decrypted_key_size) {
40
41 _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
42 .friendly_name = friendly_name,
43 .until = until,
44 .headless = headless,
45 };
46 int r;
47
48 assert(friendly_name);
49 assert(pkcs11_uri);
50 assert(key_file || key_data);
51 assert(ret_decrypted_key);
52 assert(ret_decrypted_key_size);
53
54 /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
55
56 if (key_data) {
57 data.encrypted_key = (void*) key_data;
58 data.encrypted_key_size = key_data_size;
59
60 data.free_encrypted_key = false;
61 } else {
62 _cleanup_free_ char *bindname = NULL;
63
64 /* If we read the key via AF_UNIX, make this client recognizable */
65 if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-pkcs11/%s", random_u64(), volume_name) < 0)
66 return log_oom();
67
68 r = read_full_file_full(
69 AT_FDCWD, key_file,
70 key_file_offset == 0 ? UINT64_MAX : key_file_offset,
71 key_file_size == 0 ? SIZE_MAX : key_file_size,
72 READ_FULL_FILE_CONNECT_SOCKET,
73 bindname,
74 (char**) &data.encrypted_key, &data.encrypted_key_size);
75 if (r < 0)
76 return r;
77
78 data.free_encrypted_key = true;
79 }
80
81 r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
82 if (r < 0)
83 return r;
84
85 *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
86 *ret_decrypted_key_size = data.decrypted_key_size;
87
88 return 0;
89 }
90
91 int find_pkcs11_auto_data(
92 struct crypt_device *cd,
93 char **ret_uri,
94 void **ret_encrypted_key,
95 size_t *ret_encrypted_key_size,
96 int *ret_keyslot) {
97
98 _cleanup_free_ char *uri = NULL;
99 _cleanup_free_ void *key = NULL;
100 int r, keyslot = -1;
101 size_t key_size = 0;
102
103 assert(cd);
104 assert(ret_uri);
105 assert(ret_encrypted_key);
106 assert(ret_encrypted_key_size);
107 assert(ret_keyslot);
108
109 /* Loads PKCS#11 metadata from LUKS2 JSON token headers. */
110
111 for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
112 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
113 JsonVariant *w;
114 int ks;
115
116 r = cryptsetup_get_token_as_json(cd, token, "systemd-pkcs11", &v);
117 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
118 continue;
119 if (r < 0)
120 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
121
122 ks = cryptsetup_get_keyslot_from_token(v);
123 if (ks < 0) {
124 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
125 * us, but by the LUKS2 spec */
126 log_warning_errno(ks, "Failed to extract keyslot index from PKCS#11 JSON data token %i, skipping: %m", token);
127 continue;
128 }
129
130 if (uri)
131 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
132 "Multiple PKCS#11 tokens enrolled, cannot automatically determine token.");
133
134 assert(keyslot < 0);
135 keyslot = ks;
136
137 w = json_variant_by_key(v, "pkcs11-uri");
138 if (!w || !json_variant_is_string(w))
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
140 "PKCS#11 token data lacks 'pkcs11-uri' field.");
141
142 uri = strdup(json_variant_string(w));
143 if (!uri)
144 return log_oom();
145
146 if (!pkcs11_uri_valid(uri))
147 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
148 "PKCS#11 token data contains invalid PKCS#11 URI.");
149
150 w = json_variant_by_key(v, "pkcs11-key");
151 if (!w || !json_variant_is_string(w))
152 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
153 "PKCS#11 token data lacks 'pkcs11-key' field.");
154
155 assert(!key);
156 assert(key_size == 0);
157 r = unbase64mem(json_variant_string(w), &key, &key_size);
158 if (r < 0)
159 return log_error_errno(r, "Failed to decode base64 encoded key.");
160 }
161
162 if (!uri)
163 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
164 "No valid PKCS#11 token data found.");
165
166 log_info("Automatically discovered security PKCS#11 token '%s' unlocks volume.", uri);
167
168 *ret_uri = TAKE_PTR(uri);
169 *ret_encrypted_key = TAKE_PTR(key);
170 *ret_encrypted_key_size = key_size;
171 *ret_keyslot = keyslot;
172 return 0;
173 }