]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-pkcs11.c
ec9186a6aaa527fd59b0dfc425f48553cf6b11af
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-pkcs11.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "format-util.h"
16 #include "macro.h"
17 #include "memory-util.h"
18 #include "pkcs11-util.h"
19 #include "stat-util.h"
20 #include "strv.h"
21
22 #define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
23
24 static int load_key_file(
25 const char *key_file,
26 size_t key_file_size,
27 uint64_t key_file_offset,
28 void **ret_encrypted_key,
29 size_t *ret_encrypted_key_size) {
30
31 _cleanup_(erase_and_freep) char *buffer = NULL;
32 _cleanup_close_ int fd = -1;
33 ssize_t n;
34 int r;
35
36 assert(key_file);
37 assert(ret_encrypted_key);
38 assert(ret_encrypted_key_size);
39
40 fd = open(key_file, O_RDONLY|O_CLOEXEC);
41 if (fd < 0)
42 return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m");
43
44 if (key_file_size == 0) {
45 struct stat st;
46
47 if (fstat(fd, &st) < 0)
48 return log_error_errno(errno, "Failed to stat key file: %m");
49
50 r = stat_verify_regular(&st);
51 if (r < 0)
52 return log_error_errno(r, "Key file is not a regular file: %m");
53
54 if (st.st_size == 0)
55 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
56 if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
57 char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
58 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
59 "Key file larger (%s) than allowed maximum size (%s), refusing.",
60 format_bytes(buf1, sizeof(buf1), st.st_size),
61 format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
62 }
63
64 if (key_file_offset >= (uint64_t) st.st_size)
65 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
66
67 key_file_size = st.st_size - key_file_offset;
68 }
69
70 buffer = malloc(key_file_size);
71 if (!buffer)
72 return log_oom();
73
74 if (key_file_offset > 0)
75 n = pread(fd, buffer, key_file_size, key_file_offset);
76 else
77 n = read(fd, buffer, key_file_size);
78 if (n < 0)
79 return log_error_errno(errno, "Failed to read PKCS#11 key file: %m");
80 if (n == 0)
81 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
82
83 *ret_encrypted_key = TAKE_PTR(buffer);
84 *ret_encrypted_key_size = (size_t) n;
85
86 return 0;
87 }
88
89 struct pkcs11_callback_data {
90 const char *friendly_name;
91 usec_t until;
92 void *encrypted_key;
93 size_t encrypted_key_size;
94 void *decrypted_key;
95 size_t decrypted_key_size;
96 };
97
98 static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
99 free(data->decrypted_key);
100 free(data->encrypted_key);
101 }
102
103 static int pkcs11_callback(
104 CK_FUNCTION_LIST *m,
105 CK_SESSION_HANDLE session,
106 CK_SLOT_ID slot_id,
107 const CK_SLOT_INFO *slot_info,
108 const CK_TOKEN_INFO *token_info,
109 P11KitUri *uri,
110 void *userdata) {
111
112 struct pkcs11_callback_data *data = userdata;
113 CK_OBJECT_HANDLE object;
114 int r;
115
116 assert(m);
117 assert(slot_info);
118 assert(token_info);
119 assert(uri);
120 assert(data);
121
122 /* Called for every token matching our URI */
123
124 r = pkcs11_token_login(
125 m,
126 session,
127 slot_id,
128 token_info,
129 data->friendly_name,
130 "drive-harddisk",
131 "pkcs11-pin",
132 data->until,
133 NULL);
134 if (r < 0)
135 return r;
136
137 /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
138 * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
139 * shouldn't hurt. */
140 (void) pkcs11_token_acquire_rng(m, session);
141
142 r = pkcs11_token_find_private_key(m, session, uri, &object);
143 if (r < 0)
144 return r;
145
146 r = pkcs11_token_decrypt_data(
147 m,
148 session,
149 object,
150 data->encrypted_key,
151 data->encrypted_key_size,
152 &data->decrypted_key,
153 &data->decrypted_key_size);
154 if (r < 0)
155 return r;
156
157 return 0;
158 }
159
160 int decrypt_pkcs11_key(
161 const char *friendly_name,
162 const char *pkcs11_uri,
163 const char *key_file,
164 size_t key_file_size,
165 uint64_t key_file_offset,
166 usec_t until,
167 void **ret_decrypted_key,
168 size_t *ret_decrypted_key_size) {
169
170 _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
171 .friendly_name = friendly_name,
172 .until = until,
173 };
174 int r;
175
176 assert(friendly_name);
177 assert(pkcs11_uri);
178 assert(key_file);
179 assert(ret_decrypted_key);
180 assert(ret_decrypted_key_size);
181
182 /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
183
184 r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
185 if (r < 0)
186 return r;
187
188 r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
189 if (r < 0)
190 return r;
191
192 *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
193 *ret_decrypted_key_size = data.decrypted_key_size;
194
195 return 0;
196 }