]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
70a5db58 | 2 | |
70a5db58 LP |
3 | #include <linux/loop.h> |
4 | #include <poll.h> | |
5 | #include <sys/file.h> | |
6 | #include <sys/ioctl.h> | |
565ac8b1 | 7 | #include <sys/xattr.h> |
70a5db58 | 8 | |
48f46254 LP |
9 | #if HAVE_VALGRIND_MEMCHECK_H |
10 | #include <valgrind/memcheck.h> | |
11 | #endif | |
12 | ||
2aaf565a | 13 | #include "sd-daemon.h" |
491347bd LP |
14 | #include "sd-device.h" |
15 | #include "sd-event.h" | |
e1614484 | 16 | #include "sd-id128.h" |
2aaf565a | 17 | |
70a5db58 LP |
18 | #include "blkid-util.h" |
19 | #include "blockdev-util.h" | |
0be94a19 | 20 | #include "btrfs-util.h" |
70a5db58 | 21 | #include "chattr-util.h" |
ca822829 | 22 | #include "device-util.h" |
7176f06c | 23 | #include "devnum-util.h" |
70a5db58 | 24 | #include "dm-util.h" |
2aaf565a | 25 | #include "env-util.h" |
70a5db58 LP |
26 | #include "errno-util.h" |
27 | #include "fd-util.h" | |
4dc07c3a | 28 | #include "fdisk-util.h" |
70a5db58 | 29 | #include "fileio.h" |
c8caf53c | 30 | #include "filesystems.h" |
70a5db58 LP |
31 | #include "fs-util.h" |
32 | #include "fsck-util.h" | |
e2341b6b | 33 | #include "glyph-util.h" |
e46433bb | 34 | #include "gpt.h" |
c07bf7a4 | 35 | #include "home-util.h" |
70a5db58 LP |
36 | #include "homework-luks.h" |
37 | #include "homework-mount.h" | |
70a5db58 | 38 | #include "io-util.h" |
d26cdde3 | 39 | #include "keyring-util.h" |
70a5db58 LP |
40 | #include "memory-util.h" |
41 | #include "missing_magic.h" | |
42 | #include "mkdir.h" | |
c95f9a23 | 43 | #include "mkfs-util.h" |
70a5db58 LP |
44 | #include "mount-util.h" |
45 | #include "openssl-util.h" | |
46 | #include "parse-util.h" | |
47 | #include "path-util.h" | |
48 | #include "process-util.h" | |
49 | #include "random-util.h" | |
50 | #include "resize-fs.h" | |
70a5db58 | 51 | #include "strv.h" |
bf819d3a | 52 | #include "sync-util.h" |
70a5db58 | 53 | #include "tmpfile-util.h" |
491347bd | 54 | #include "udev-util.h" |
1147c538 | 55 | #include "user-util.h" |
70a5db58 | 56 | |
04190cf1 LP |
57 | /* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our |
58 | * partitions to that too. In the worst case we'll waste 3.5K per partition that way, but I think I can live | |
59 | * with that. */ | |
60 | #define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(4095)) | |
61 | ||
62 | /* Rounds up to the nearest 4K boundary. Returns UINT64_MAX on overflow */ | |
63 | #define DISK_SIZE_ROUND_UP(x) \ | |
64 | ({ \ | |
65 | uint64_t _x = (x); \ | |
66 | _x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \ | |
67 | }) | |
68 | ||
31ea1bfe LP |
69 | /* How much larger will the image on disk be than the fs inside it, i.e. the space we pay for the GPT and |
70 | * LUKS2 envelope. (As measured on cryptsetup 2.4.1) */ | |
71 | #define GPT_LUKS2_OVERHEAD UINT64_C(18874368) | |
72 | ||
716bc200 | 73 | static int resize_image_loop(UserRecord *h, HomeSetup *setup, uint64_t old_image_size, uint64_t new_image_size, uint64_t *ret_image_size); |
70a5db58 | 74 | |
565ac8b1 LP |
75 | int run_mark_dirty(int fd, bool b) { |
76 | char x = '1'; | |
77 | int r, ret; | |
78 | ||
79 | /* Sets or removes the 'user.home-dirty' xattr on the specified file. We use this to detect when a | |
80 | * home directory was not properly unmounted. */ | |
81 | ||
82 | assert(fd >= 0); | |
83 | ||
84 | r = fd_verify_regular(fd); | |
85 | if (r < 0) | |
86 | return r; | |
87 | ||
88 | if (b) { | |
89 | ret = fsetxattr(fd, "user.home-dirty", &x, 1, XATTR_CREATE); | |
90 | if (ret < 0 && errno != EEXIST) | |
91 | return log_debug_errno(errno, "Could not mark home directory as dirty: %m"); | |
92 | ||
93 | } else { | |
94 | r = fsync_full(fd); | |
95 | if (r < 0) | |
96 | return log_debug_errno(r, "Failed to synchronize image before marking it clean: %m"); | |
97 | ||
98 | ret = fremovexattr(fd, "user.home-dirty"); | |
00675c36 | 99 | if (ret < 0 && !ERRNO_IS_XATTR_ABSENT(errno)) |
565ac8b1 LP |
100 | return log_debug_errno(errno, "Could not mark home directory as clean: %m"); |
101 | } | |
102 | ||
103 | r = fsync_full(fd); | |
104 | if (r < 0) | |
105 | return log_debug_errno(r, "Failed to synchronize dirty flag to disk: %m"); | |
106 | ||
107 | return ret >= 0; | |
108 | } | |
109 | ||
110 | int run_mark_dirty_by_path(const char *path, bool b) { | |
254d1313 | 111 | _cleanup_close_ int fd = -EBADF; |
565ac8b1 LP |
112 | |
113 | assert(path); | |
114 | ||
115 | fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY); | |
116 | if (fd < 0) | |
117 | return log_debug_errno(errno, "Failed to open %s to mark dirty or clean: %m", path); | |
118 | ||
119 | return run_mark_dirty(fd, b); | |
120 | } | |
121 | ||
70a5db58 LP |
122 | static int probe_file_system_by_fd( |
123 | int fd, | |
124 | char **ret_fstype, | |
125 | sd_id128_t *ret_uuid) { | |
126 | ||
127 | _cleanup_(blkid_free_probep) blkid_probe b = NULL; | |
128 | _cleanup_free_ char *s = NULL; | |
129 | const char *fstype = NULL, *uuid = NULL; | |
130 | sd_id128_t id; | |
131 | int r; | |
132 | ||
133 | assert(fd >= 0); | |
134 | assert(ret_fstype); | |
135 | assert(ret_uuid); | |
136 | ||
137 | b = blkid_new_probe(); | |
138 | if (!b) | |
139 | return -ENOMEM; | |
140 | ||
141 | errno = 0; | |
142 | r = blkid_probe_set_device(b, fd, 0, 0); | |
143 | if (r != 0) | |
ef1f0a14 | 144 | return errno_or_else(ENOMEM); |
70a5db58 LP |
145 | |
146 | (void) blkid_probe_enable_superblocks(b, 1); | |
147 | (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID); | |
148 | ||
149 | errno = 0; | |
150 | r = blkid_do_safeprobe(b); | |
2e3944b8 LP |
151 | if (r == _BLKID_SAFEPROBE_ERROR) |
152 | return errno_or_else(EIO); | |
153 | if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) | |
70a5db58 | 154 | return -ENOPKG; |
2e3944b8 LP |
155 | |
156 | assert(r == _BLKID_SAFEPROBE_FOUND); | |
70a5db58 LP |
157 | |
158 | (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); | |
159 | if (!fstype) | |
160 | return -ENOPKG; | |
161 | ||
162 | (void) blkid_probe_lookup_value(b, "UUID", &uuid, NULL); | |
163 | if (!uuid) | |
164 | return -ENOPKG; | |
165 | ||
166 | r = sd_id128_from_string(uuid, &id); | |
167 | if (r < 0) | |
168 | return r; | |
169 | ||
170 | s = strdup(fstype); | |
171 | if (!s) | |
172 | return -ENOMEM; | |
173 | ||
174 | *ret_fstype = TAKE_PTR(s); | |
175 | *ret_uuid = id; | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static int probe_file_system_by_path(const char *path, char **ret_fstype, sd_id128_t *ret_uuid) { | |
254d1313 | 181 | _cleanup_close_ int fd = -EBADF; |
70a5db58 LP |
182 | |
183 | fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); | |
184 | if (fd < 0) | |
1474e66a | 185 | return negative_errno(); |
70a5db58 LP |
186 | |
187 | return probe_file_system_by_fd(fd, ret_fstype, ret_uuid); | |
188 | } | |
189 | ||
190 | static int block_get_size_by_fd(int fd, uint64_t *ret) { | |
191 | struct stat st; | |
192 | ||
193 | assert(fd >= 0); | |
194 | assert(ret); | |
195 | ||
196 | if (fstat(fd, &st) < 0) | |
197 | return -errno; | |
198 | ||
199 | if (!S_ISBLK(st.st_mode)) | |
200 | return -ENOTBLK; | |
201 | ||
7c248223 | 202 | return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret)); |
70a5db58 LP |
203 | } |
204 | ||
205 | static int block_get_size_by_path(const char *path, uint64_t *ret) { | |
254d1313 | 206 | _cleanup_close_ int fd = -EBADF; |
70a5db58 LP |
207 | |
208 | fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); | |
209 | if (fd < 0) | |
210 | return -errno; | |
211 | ||
212 | return block_get_size_by_fd(fd, ret); | |
213 | } | |
214 | ||
215 | static int run_fsck(const char *node, const char *fstype) { | |
216 | int r, exit_status; | |
217 | pid_t fsck_pid; | |
218 | ||
219 | assert(node); | |
220 | assert(fstype); | |
221 | ||
13556724 | 222 | r = fsck_exists_for_fstype(fstype); |
70a5db58 LP |
223 | if (r < 0) |
224 | return log_error_errno(r, "Failed to check if fsck for file system %s exists: %m", fstype); | |
225 | if (r == 0) { | |
226 | log_warning("No fsck for file system %s installed, ignoring.", fstype); | |
227 | return 0; | |
228 | } | |
229 | ||
fbdacd72 SB |
230 | r = safe_fork("(fsck)", |
231 | FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, | |
232 | &fsck_pid); | |
70a5db58 LP |
233 | if (r < 0) |
234 | return r; | |
235 | if (r == 0) { | |
236 | /* Child */ | |
237 | execl("/sbin/fsck", "/sbin/fsck", "-aTl", node, NULL); | |
fbdacd72 | 238 | log_open(); |
70a5db58 LP |
239 | log_error_errno(errno, "Failed to execute fsck: %m"); |
240 | _exit(FSCK_OPERATIONAL_ERROR); | |
241 | } | |
242 | ||
243 | exit_status = wait_for_terminate_and_check("fsck", fsck_pid, WAIT_LOG_ABNORMAL); | |
244 | if (exit_status < 0) | |
245 | return exit_status; | |
246 | if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) { | |
247 | log_warning("fsck failed with exit status %i.", exit_status); | |
248 | ||
249 | if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0) | |
250 | return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing."); | |
251 | ||
252 | log_warning("Ignoring fsck error."); | |
253 | } | |
254 | ||
255 | log_info("File system check completed."); | |
256 | ||
257 | return 1; | |
258 | } | |
259 | ||
d26cdde3 LP |
260 | DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(key_serial_t, keyring_unlink, -1); |
261 | ||
262 | static int upload_to_keyring( | |
263 | UserRecord *h, | |
264 | const char *password, | |
265 | key_serial_t *ret_key_serial) { | |
266 | ||
267 | _cleanup_free_ char *name = NULL; | |
268 | key_serial_t serial; | |
269 | ||
270 | assert(h); | |
271 | assert(password); | |
272 | ||
273 | /* If auto-shrink-on-logout is turned on, we need to keep the key we used to unlock the LUKS volume | |
274 | * around, since we'll need it when automatically resizing (since we can't ask the user there | |
275 | * again). We do this by uploading it into the kernel keyring, specifically the "session" one. This | |
276 | * is done under the assumption systemd-homed gets its private per-session keyring (i.e. default | |
277 | * service behaviour, given that KeyringMode=private is the default). It will survive between our | |
278 | * systemd-homework invocations that way. | |
279 | * | |
280 | * If auto-shrink-on-logout is disabled we'll skip this step, to be frugal with sensitive data. */ | |
281 | ||
282 | if (user_record_auto_resize_mode(h) != AUTO_RESIZE_SHRINK_AND_GROW) { /* Won't need it */ | |
283 | if (ret_key_serial) | |
284 | *ret_key_serial = -1; | |
285 | return 0; | |
286 | } | |
287 | ||
288 | name = strjoin("homework-user-", h->user_name); | |
289 | if (!name) | |
290 | return -ENOMEM; | |
291 | ||
292 | serial = add_key("user", name, password, strlen(password), KEY_SPEC_SESSION_KEYRING); | |
293 | if (serial == -1) | |
294 | return -errno; | |
295 | ||
296 | if (ret_key_serial) | |
297 | *ret_key_serial = serial; | |
298 | ||
299 | return 1; | |
300 | } | |
301 | ||
70a5db58 | 302 | static int luks_try_passwords( |
d26cdde3 | 303 | UserRecord *h, |
70a5db58 LP |
304 | struct crypt_device *cd, |
305 | char **passwords, | |
306 | void *volume_key, | |
d26cdde3 LP |
307 | size_t *volume_key_size, |
308 | key_serial_t *ret_key_serial) { | |
70a5db58 | 309 | |
70a5db58 LP |
310 | int r; |
311 | ||
d26cdde3 | 312 | assert(h); |
70a5db58 LP |
313 | assert(cd); |
314 | ||
315 | STRV_FOREACH(pp, passwords) { | |
316 | size_t vks = *volume_key_size; | |
317 | ||
71eceff6 | 318 | r = sym_crypt_volume_key_get( |
70a5db58 LP |
319 | cd, |
320 | CRYPT_ANY_SLOT, | |
321 | volume_key, | |
322 | &vks, | |
323 | *pp, | |
324 | strlen(*pp)); | |
325 | if (r >= 0) { | |
d26cdde3 LP |
326 | if (ret_key_serial) { |
327 | /* If ret_key_serial is non-NULL, let's try to upload the password that | |
328 | * worked, and return its serial. */ | |
329 | r = upload_to_keyring(h, *pp, ret_key_serial); | |
330 | if (r < 0) { | |
331 | log_debug_errno(r, "Failed to upload LUKS password to kernel keyring, ignoring: %m"); | |
332 | *ret_key_serial = -1; | |
333 | } | |
334 | } | |
335 | ||
70a5db58 LP |
336 | *volume_key_size = vks; |
337 | return 0; | |
338 | } | |
339 | ||
340 | log_debug_errno(r, "Password %zu didn't work for unlocking LUKS superblock: %m", (size_t) (pp - passwords)); | |
341 | } | |
342 | ||
343 | return -ENOKEY; | |
344 | } | |
345 | ||
346 | static int luks_setup( | |
d26cdde3 | 347 | UserRecord *h, |
70a5db58 LP |
348 | const char *node, |
349 | const char *dm_name, | |
350 | sd_id128_t uuid, | |
351 | const char *cipher, | |
352 | const char *cipher_mode, | |
353 | uint64_t volume_key_size, | |
354 | char **passwords, | |
7b78db28 | 355 | const PasswordCache *cache, |
70a5db58 LP |
356 | bool discard, |
357 | struct crypt_device **ret, | |
358 | sd_id128_t *ret_found_uuid, | |
359 | void **ret_volume_key, | |
d26cdde3 LP |
360 | size_t *ret_volume_key_size, |
361 | key_serial_t *ret_key_serial) { | |
70a5db58 | 362 | |
d26cdde3 | 363 | _cleanup_(keyring_unlinkp) key_serial_t key_serial = -1; |
71eceff6 | 364 | _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; |
70a5db58 LP |
365 | _cleanup_(erase_and_freep) void *vk = NULL; |
366 | sd_id128_t p; | |
367 | size_t vks; | |
7b78db28 | 368 | char **list; |
70a5db58 LP |
369 | int r; |
370 | ||
d26cdde3 | 371 | assert(h); |
70a5db58 LP |
372 | assert(node); |
373 | assert(dm_name); | |
374 | assert(ret); | |
375 | ||
71eceff6 | 376 | r = sym_crypt_init(&cd, node); |
70a5db58 LP |
377 | if (r < 0) |
378 | return log_error_errno(r, "Failed to allocate libcryptsetup context: %m"); | |
379 | ||
efc3b12f | 380 | cryptsetup_enable_logging(cd); |
70a5db58 | 381 | |
71eceff6 | 382 | r = sym_crypt_load(cd, CRYPT_LUKS2, NULL); |
70a5db58 LP |
383 | if (r < 0) |
384 | return log_error_errno(r, "Failed to load LUKS superblock: %m"); | |
385 | ||
71eceff6 | 386 | r = sym_crypt_get_volume_key_size(cd); |
70a5db58 LP |
387 | if (r <= 0) |
388 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size"); | |
389 | vks = (size_t) r; | |
390 | ||
391 | if (!sd_id128_is_null(uuid) || ret_found_uuid) { | |
392 | const char *s; | |
393 | ||
71eceff6 | 394 | s = sym_crypt_get_uuid(cd); |
70a5db58 LP |
395 | if (!s) |
396 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID."); | |
397 | ||
398 | r = sd_id128_from_string(s, &p); | |
399 | if (r < 0) | |
400 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID."); | |
401 | ||
402 | /* Check that the UUID matches, if specified */ | |
403 | if (!sd_id128_is_null(uuid) && | |
404 | !sd_id128_equal(uuid, p)) | |
405 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has wrong UUID."); | |
406 | } | |
407 | ||
71eceff6 | 408 | if (cipher && !streq_ptr(cipher, sym_crypt_get_cipher(cd))) |
70a5db58 LP |
409 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher."); |
410 | ||
71eceff6 | 411 | if (cipher_mode && !streq_ptr(cipher_mode, sym_crypt_get_cipher_mode(cd))) |
70a5db58 LP |
412 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher mode."); |
413 | ||
414 | if (volume_key_size != UINT64_MAX && vks != volume_key_size) | |
415 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong volume key size."); | |
416 | ||
417 | vk = malloc(vks); | |
418 | if (!vk) | |
419 | return log_oom(); | |
420 | ||
7b78db28 | 421 | r = -ENOKEY; |
3361d1ca | 422 | FOREACH_POINTER(list, |
d26cdde3 | 423 | cache ? cache->keyring_passswords : NULL, |
3361d1ca LP |
424 | cache ? cache->pkcs11_passwords : NULL, |
425 | cache ? cache->fido2_passwords : NULL, | |
426 | passwords) { | |
d26cdde3 | 427 | r = luks_try_passwords(h, cd, list, vk, &vks, ret_key_serial ? &key_serial : NULL); |
7b78db28 LP |
428 | if (r != -ENOKEY) |
429 | break; | |
70a5db58 | 430 | } |
7b78db28 LP |
431 | if (r == -ENOKEY) |
432 | return log_error_errno(r, "No valid password for LUKS superblock."); | |
70a5db58 | 433 | if (r < 0) |
eae3a681 | 434 | return log_error_errno(r, "Failed to unlock LUKS superblock: %m"); |
70a5db58 | 435 | |
71eceff6 | 436 | r = sym_crypt_activate_by_volume_key( |
70a5db58 LP |
437 | cd, |
438 | dm_name, | |
439 | vk, vks, | |
440 | discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0); | |
441 | if (r < 0) | |
442 | return log_error_errno(r, "Failed to unlock LUKS superblock: %m"); | |
443 | ||
444 | log_info("Setting up LUKS device /dev/mapper/%s completed.", dm_name); | |
445 | ||
446 | *ret = TAKE_PTR(cd); | |
447 | ||
448 | if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */ | |
449 | *ret_found_uuid = p; | |
450 | if (ret_volume_key) | |
451 | *ret_volume_key = TAKE_PTR(vk); | |
452 | if (ret_volume_key_size) | |
453 | *ret_volume_key_size = vks; | |
d26cdde3 LP |
454 | if (ret_key_serial) |
455 | *ret_key_serial = TAKE_KEY_SERIAL(key_serial); | |
70a5db58 LP |
456 | |
457 | return 0; | |
458 | } | |
459 | ||
a23cf7f4 LP |
460 | static int make_dm_names(UserRecord *h, HomeSetup *setup) { |
461 | assert(h); | |
462 | assert(h->user_name); | |
463 | assert(setup); | |
464 | ||
465 | if (!setup->dm_name) { | |
466 | setup->dm_name = strjoin("home-", h->user_name); | |
467 | if (!setup->dm_name) | |
468 | return log_oom(); | |
469 | } | |
470 | ||
471 | if (!setup->dm_node) { | |
472 | setup->dm_node = path_join("/dev/mapper/", setup->dm_name); | |
473 | if (!setup->dm_node) | |
474 | return log_oom(); | |
475 | } | |
476 | ||
477 | return 0; | |
478 | } | |
479 | ||
480 | static int acquire_open_luks_device( | |
481 | UserRecord *h, | |
482 | HomeSetup *setup, | |
483 | bool graceful) { | |
484 | ||
485 | _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; | |
486 | int r; | |
487 | ||
488 | assert(h); | |
489 | assert(setup); | |
490 | assert(!setup->crypt_device); | |
491 | ||
492 | r = dlopen_cryptsetup(); | |
493 | if (r < 0) | |
494 | return r; | |
495 | ||
496 | r = make_dm_names(h, setup); | |
497 | if (r < 0) | |
498 | return r; | |
499 | ||
500 | r = sym_crypt_init_by_name(&cd, setup->dm_name); | |
3f2ada89 | 501 | if ((ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) && graceful) |
a23cf7f4 LP |
502 | return 0; |
503 | if (r < 0) | |
504 | return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name); | |
505 | ||
506 | cryptsetup_enable_logging(cd); | |
507 | ||
508 | setup->crypt_device = TAKE_PTR(cd); | |
509 | return 1; | |
510 | } | |
511 | ||
70a5db58 | 512 | static int luks_open( |
a23cf7f4 LP |
513 | UserRecord *h, |
514 | HomeSetup *setup, | |
37a1bf7f | 515 | const PasswordCache *cache, |
70a5db58 LP |
516 | sd_id128_t *ret_found_uuid, |
517 | void **ret_volume_key, | |
518 | size_t *ret_volume_key_size) { | |
519 | ||
70a5db58 LP |
520 | _cleanup_(erase_and_freep) void *vk = NULL; |
521 | sd_id128_t p; | |
7b78db28 | 522 | char **list; |
70a5db58 LP |
523 | size_t vks; |
524 | int r; | |
525 | ||
a23cf7f4 LP |
526 | assert(h); |
527 | assert(setup); | |
528 | assert(!setup->crypt_device); | |
70a5db58 LP |
529 | |
530 | /* Opens a LUKS device that is already set up. Re-validates the password while doing so (which also | |
531 | * provides us with the volume key, which we want). */ | |
532 | ||
a23cf7f4 | 533 | r = acquire_open_luks_device(h, setup, /* graceful= */ false); |
70a5db58 | 534 | if (r < 0) |
a23cf7f4 | 535 | return r; |
70a5db58 | 536 | |
a23cf7f4 | 537 | r = sym_crypt_load(setup->crypt_device, CRYPT_LUKS2, NULL); |
70a5db58 LP |
538 | if (r < 0) |
539 | return log_error_errno(r, "Failed to load LUKS superblock: %m"); | |
540 | ||
a23cf7f4 | 541 | r = sym_crypt_get_volume_key_size(setup->crypt_device); |
70a5db58 LP |
542 | if (r <= 0) |
543 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size"); | |
544 | vks = (size_t) r; | |
545 | ||
546 | if (ret_found_uuid) { | |
547 | const char *s; | |
548 | ||
a23cf7f4 | 549 | s = sym_crypt_get_uuid(setup->crypt_device); |
70a5db58 LP |
550 | if (!s) |
551 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID."); | |
552 | ||
553 | r = sd_id128_from_string(s, &p); | |
554 | if (r < 0) | |
555 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID."); | |
556 | } | |
557 | ||
558 | vk = malloc(vks); | |
559 | if (!vk) | |
560 | return log_oom(); | |
561 | ||
7b78db28 | 562 | r = -ENOKEY; |
3361d1ca | 563 | FOREACH_POINTER(list, |
d26cdde3 | 564 | cache ? cache->keyring_passswords : NULL, |
3361d1ca LP |
565 | cache ? cache->pkcs11_passwords : NULL, |
566 | cache ? cache->fido2_passwords : NULL, | |
a23cf7f4 | 567 | h->password) { |
d26cdde3 | 568 | r = luks_try_passwords(h, setup->crypt_device, list, vk, &vks, NULL); |
7b78db28 LP |
569 | if (r != -ENOKEY) |
570 | break; | |
70a5db58 | 571 | } |
7b78db28 LP |
572 | if (r == -ENOKEY) |
573 | return log_error_errno(r, "No valid password for LUKS superblock."); | |
70a5db58 LP |
574 | if (r < 0) |
575 | return log_error_errno(r, "Failed to unlocks LUKS superblock: %m"); | |
576 | ||
a23cf7f4 | 577 | log_info("Discovered used LUKS device /dev/mapper/%s, and validated password.", setup->dm_name); |
70a5db58 LP |
578 | |
579 | /* This is needed so that crypt_resize() can operate correctly for pre-existing LUKS devices. We need | |
580 | * to tell libcryptsetup the volume key explicitly, so that it is in the kernel keyring. */ | |
a23cf7f4 | 581 | r = sym_crypt_activate_by_volume_key(setup->crypt_device, NULL, vk, vks, CRYPT_ACTIVATE_KEYRING_KEY); |
70a5db58 LP |
582 | if (r < 0) |
583 | return log_error_errno(r, "Failed to upload volume key again: %m"); | |
584 | ||
585 | log_info("Successfully re-activated LUKS device."); | |
586 | ||
70a5db58 LP |
587 | if (ret_found_uuid) |
588 | *ret_found_uuid = p; | |
589 | if (ret_volume_key) | |
590 | *ret_volume_key = TAKE_PTR(vk); | |
591 | if (ret_volume_key_size) | |
592 | *ret_volume_key_size = vks; | |
593 | ||
594 | return 0; | |
595 | } | |
596 | ||
597 | static int fs_validate( | |
598 | const char *dm_node, | |
599 | sd_id128_t uuid, | |
600 | char **ret_fstype, | |
601 | sd_id128_t *ret_found_uuid) { | |
602 | ||
603 | _cleanup_free_ char *fstype = NULL; | |
604 | sd_id128_t u; | |
605 | int r; | |
606 | ||
607 | assert(dm_node); | |
608 | assert(ret_fstype); | |
609 | ||
610 | r = probe_file_system_by_path(dm_node, &fstype, &u); | |
611 | if (r < 0) | |
612 | return log_error_errno(r, "Failed to probe file system: %m"); | |
613 | ||
614 | /* Limit the set of supported file systems a bit, as protection against little tested kernel file | |
615 | * systems. Also, we only support the resize ioctls for these file systems. */ | |
616 | if (!supported_fstype(fstype)) | |
617 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Image contains unsupported file system: %s", strna(fstype)); | |
618 | ||
619 | if (!sd_id128_is_null(uuid) && | |
620 | !sd_id128_equal(uuid, u)) | |
621 | return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "File system has wrong UUID."); | |
622 | ||
623 | log_info("Probing file system completed (found %s).", fstype); | |
624 | ||
625 | *ret_fstype = TAKE_PTR(fstype); | |
626 | ||
627 | if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */ | |
628 | *ret_found_uuid = u; | |
629 | ||
630 | return 0; | |
631 | } | |
632 | ||
70a5db58 LP |
633 | static int luks_validate( |
634 | int fd, | |
635 | const char *label, | |
636 | sd_id128_t partition_uuid, | |
637 | sd_id128_t *ret_partition_uuid, | |
638 | uint64_t *ret_offset, | |
639 | uint64_t *ret_size) { | |
640 | ||
641 | _cleanup_(blkid_free_probep) blkid_probe b = NULL; | |
642 | sd_id128_t found_partition_uuid = SD_ID128_NULL; | |
643 | const char *fstype = NULL, *pttype = NULL; | |
644 | blkid_loff_t offset = 0, size = 0; | |
645 | blkid_partlist pl; | |
646 | bool found = false; | |
78b4e9ed | 647 | int r, n; |
70a5db58 LP |
648 | |
649 | assert(fd >= 0); | |
650 | assert(label); | |
651 | assert(ret_offset); | |
652 | assert(ret_size); | |
653 | ||
654 | b = blkid_new_probe(); | |
655 | if (!b) | |
656 | return -ENOMEM; | |
657 | ||
658 | errno = 0; | |
659 | r = blkid_probe_set_device(b, fd, 0, 0); | |
660 | if (r != 0) | |
ef1f0a14 | 661 | return errno_or_else(ENOMEM); |
70a5db58 LP |
662 | |
663 | (void) blkid_probe_enable_superblocks(b, 1); | |
664 | (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); | |
665 | (void) blkid_probe_enable_partitions(b, 1); | |
666 | (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); | |
667 | ||
668 | errno = 0; | |
669 | r = blkid_do_safeprobe(b); | |
2e3944b8 LP |
670 | if (r == _BLKID_SAFEPROBE_ERROR) |
671 | return errno_or_else(EIO); | |
672 | if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) | |
70a5db58 | 673 | return -ENOPKG; |
2e3944b8 LP |
674 | |
675 | assert(r == _BLKID_SAFEPROBE_FOUND); | |
70a5db58 LP |
676 | |
677 | (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); | |
678 | if (streq_ptr(fstype, "crypto_LUKS")) { | |
679 | /* Directly a LUKS image */ | |
680 | *ret_offset = 0; | |
681 | *ret_size = UINT64_MAX; /* full disk */ | |
682 | *ret_partition_uuid = SD_ID128_NULL; | |
683 | return 0; | |
684 | } else if (fstype) | |
685 | return -ENOPKG; | |
686 | ||
687 | (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); | |
688 | if (!streq_ptr(pttype, "gpt")) | |
689 | return -ENOPKG; | |
690 | ||
691 | errno = 0; | |
692 | pl = blkid_probe_get_partitions(b); | |
693 | if (!pl) | |
ef1f0a14 | 694 | return errno_or_else(ENOMEM); |
70a5db58 LP |
695 | |
696 | errno = 0; | |
697 | n = blkid_partlist_numof_partitions(pl); | |
698 | if (n < 0) | |
ef1f0a14 | 699 | return errno_or_else(EIO); |
70a5db58 | 700 | |
78b4e9ed | 701 | for (int i = 0; i < n; i++) { |
a4aa5742 | 702 | sd_id128_t id = SD_ID128_NULL; |
e3b9a5ff | 703 | blkid_partition pp; |
70a5db58 LP |
704 | |
705 | errno = 0; | |
706 | pp = blkid_partlist_get_partition(pl, i); | |
707 | if (!pp) | |
ef1f0a14 | 708 | return errno_or_else(EIO); |
70a5db58 | 709 | |
92e72028 | 710 | if (sd_id128_string_equal(blkid_partition_get_type_string(pp), SD_GPT_USER_HOME) <= 0) |
70a5db58 LP |
711 | continue; |
712 | ||
713 | if (!streq_ptr(blkid_partition_get_name(pp), label)) | |
714 | continue; | |
715 | ||
70a5db58 | 716 | |
e3b9a5ff LP |
717 | r = blkid_partition_get_uuid_id128(pp, &id); |
718 | if (r < 0) | |
719 | log_debug_errno(r, "Failed to read partition UUID, ignoring: %m"); | |
720 | else if (!sd_id128_is_null(partition_uuid) && !sd_id128_equal(id, partition_uuid)) | |
721 | continue; | |
70a5db58 LP |
722 | |
723 | if (found) | |
724 | return -ENOPKG; | |
725 | ||
726 | offset = blkid_partition_get_start(pp); | |
727 | size = blkid_partition_get_size(pp); | |
728 | found_partition_uuid = id; | |
729 | ||
730 | found = true; | |
731 | } | |
732 | ||
733 | if (!found) | |
734 | return -ENOPKG; | |
735 | ||
736 | if (offset < 0) | |
737 | return -EINVAL; | |
738 | if ((uint64_t) offset > UINT64_MAX / 512U) | |
739 | return -EINVAL; | |
740 | if (size <= 0) | |
741 | return -EINVAL; | |
742 | if ((uint64_t) size > UINT64_MAX / 512U) | |
743 | return -EINVAL; | |
744 | ||
745 | *ret_offset = offset * 512U; | |
746 | *ret_size = size * 512U; | |
747 | *ret_partition_uuid = found_partition_uuid; | |
748 | ||
749 | return 0; | |
750 | } | |
751 | ||
752 | static int crypt_device_to_evp_cipher(struct crypt_device *cd, const EVP_CIPHER **ret) { | |
753 | _cleanup_free_ char *cipher_name = NULL; | |
754 | const char *cipher, *cipher_mode, *e; | |
755 | size_t key_size, key_bits; | |
756 | const EVP_CIPHER *cc; | |
757 | int r; | |
758 | ||
759 | assert(cd); | |
760 | ||
761 | /* Let's find the right OpenSSL EVP_CIPHER object that matches the encryption settings of the LUKS | |
762 | * device */ | |
763 | ||
71eceff6 | 764 | cipher = sym_crypt_get_cipher(cd); |
70a5db58 LP |
765 | if (!cipher) |
766 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher from LUKS device."); | |
767 | ||
71eceff6 | 768 | cipher_mode = sym_crypt_get_cipher_mode(cd); |
70a5db58 LP |
769 | if (!cipher_mode) |
770 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher mode from LUKS device."); | |
771 | ||
772 | e = strchr(cipher_mode, '-'); | |
773 | if (e) | |
2f82562b | 774 | cipher_mode = strndupa_safe(cipher_mode, e - cipher_mode); |
70a5db58 | 775 | |
71eceff6 | 776 | r = sym_crypt_get_volume_key_size(cd); |
70a5db58 LP |
777 | if (r <= 0) |
778 | return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Cannot get volume key size from LUKS device."); | |
779 | ||
780 | key_size = r; | |
781 | key_bits = key_size * 8; | |
782 | if (streq(cipher_mode, "xts")) | |
783 | key_bits /= 2; | |
784 | ||
785 | if (asprintf(&cipher_name, "%s-%zu-%s", cipher, key_bits, cipher_mode) < 0) | |
786 | return log_oom(); | |
787 | ||
788 | cc = EVP_get_cipherbyname(cipher_name); | |
789 | if (!cc) | |
790 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Selected cipher mode '%s' not supported, can't encrypt JSON record.", cipher_name); | |
791 | ||
792 | /* Verify that our key length calculations match what OpenSSL thinks */ | |
793 | r = EVP_CIPHER_key_length(cc); | |
794 | if (r < 0 || (uint64_t) r != key_size) | |
80ace4f2 | 795 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key size of selected cipher doesn't meet our expectations."); |
70a5db58 LP |
796 | |
797 | *ret = cc; | |
798 | return 0; | |
799 | } | |
800 | ||
801 | static int luks_validate_home_record( | |
802 | struct crypt_device *cd, | |
803 | UserRecord *h, | |
804 | const void *volume_key, | |
7b78db28 | 805 | PasswordCache *cache, |
70a5db58 LP |
806 | UserRecord **ret_luks_home_record) { |
807 | ||
78b4e9ed | 808 | int r; |
70a5db58 LP |
809 | |
810 | assert(cd); | |
811 | assert(h); | |
812 | ||
e7e30330 | 813 | for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) { |
70a5db58 LP |
814 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *rr = NULL; |
815 | _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL; | |
816 | _cleanup_(user_record_unrefp) UserRecord *lhr = NULL; | |
817 | _cleanup_free_ void *encrypted = NULL, *iv = NULL; | |
818 | size_t decrypted_size, encrypted_size, iv_size; | |
819 | int decrypted_size_out1, decrypted_size_out2; | |
820 | _cleanup_free_ char *decrypted = NULL; | |
821 | const char *text, *type; | |
822 | crypt_token_info state; | |
823 | JsonVariant *jr, *jiv; | |
824 | unsigned line, column; | |
825 | const EVP_CIPHER *cc; | |
826 | ||
71eceff6 | 827 | state = sym_crypt_token_status(cd, token, &type); |
70a5db58 LP |
828 | if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, give up */ |
829 | break; | |
830 | if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL)) | |
831 | continue; | |
832 | if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN) | |
833 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state); | |
834 | ||
835 | if (!streq(type, "systemd-homed")) | |
836 | continue; | |
837 | ||
71eceff6 | 838 | r = sym_crypt_token_json_get(cd, token, &text); |
70a5db58 LP |
839 | if (r < 0) |
840 | return log_error_errno(r, "Failed to read LUKS token %i: %m", token); | |
841 | ||
842 | r = json_parse(text, JSON_PARSE_SENSITIVE, &v, &line, &column); | |
843 | if (r < 0) | |
844 | return log_error_errno(r, "Failed to parse LUKS token JSON data %u:%u: %m", line, column); | |
845 | ||
846 | jr = json_variant_by_key(v, "record"); | |
847 | if (!jr) | |
848 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'record' field."); | |
849 | jiv = json_variant_by_key(v, "iv"); | |
850 | if (!jiv) | |
851 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'iv' field."); | |
852 | ||
853 | r = json_variant_unbase64(jr, &encrypted, &encrypted_size); | |
854 | if (r < 0) | |
855 | return log_error_errno(r, "Failed to base64 decode record: %m"); | |
856 | ||
857 | r = json_variant_unbase64(jiv, &iv, &iv_size); | |
858 | if (r < 0) | |
859 | return log_error_errno(r, "Failed to base64 decode IV: %m"); | |
860 | ||
861 | r = crypt_device_to_evp_cipher(cd, &cc); | |
862 | if (r < 0) | |
863 | return r; | |
864 | if (iv_size > INT_MAX || EVP_CIPHER_iv_length(cc) != (int) iv_size) | |
865 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IV size doesn't match."); | |
866 | ||
867 | context = EVP_CIPHER_CTX_new(); | |
868 | if (!context) | |
869 | return log_oom(); | |
870 | ||
871 | if (EVP_DecryptInit_ex(context, cc, NULL, volume_key, iv) != 1) | |
872 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context."); | |
873 | ||
874 | decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2; | |
875 | decrypted = new(char, decrypted_size); | |
876 | if (!decrypted) | |
877 | return log_oom(); | |
878 | ||
879 | if (EVP_DecryptUpdate(context, (uint8_t*) decrypted, &decrypted_size_out1, encrypted, encrypted_size) != 1) | |
880 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt JSON record."); | |
881 | ||
882 | assert((size_t) decrypted_size_out1 <= decrypted_size); | |
883 | ||
884 | if (EVP_DecryptFinal_ex(context, (uint8_t*) decrypted + decrypted_size_out1, &decrypted_size_out2) != 1) | |
885 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish decryption of JSON record."); | |
886 | ||
887 | assert((size_t) decrypted_size_out1 + (size_t) decrypted_size_out2 < decrypted_size); | |
888 | decrypted_size = (size_t) decrypted_size_out1 + (size_t) decrypted_size_out2; | |
889 | ||
890 | if (memchr(decrypted, 0, decrypted_size)) | |
891 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Inner NUL byte in JSON record, refusing."); | |
892 | ||
893 | decrypted[decrypted_size] = 0; | |
894 | ||
895 | r = json_parse(decrypted, JSON_PARSE_SENSITIVE, &rr, NULL, NULL); | |
896 | if (r < 0) | |
897 | return log_error_errno(r, "Failed to parse decrypted JSON record, refusing."); | |
898 | ||
899 | lhr = user_record_new(); | |
900 | if (!lhr) | |
901 | return log_oom(); | |
902 | ||
bfc0cc1a | 903 | r = user_record_load(lhr, rr, USER_RECORD_LOAD_EMBEDDED|USER_RECORD_PERMISSIVE); |
70a5db58 LP |
904 | if (r < 0) |
905 | return log_error_errno(r, "Failed to parse user record: %m"); | |
906 | ||
907 | if (!user_record_compatible(h, lhr)) | |
908 | return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing."); | |
909 | ||
7b78db28 | 910 | r = user_record_authenticate(lhr, h, cache, /* strict_verify= */ true); |
70a5db58 LP |
911 | if (r < 0) |
912 | return r; | |
c8f145ad | 913 | assert(r > 0); /* Insist that a password was verified */ |
70a5db58 LP |
914 | |
915 | *ret_luks_home_record = TAKE_PTR(lhr); | |
916 | return 0; | |
917 | } | |
918 | ||
919 | return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find home record in LUKS2 header, refusing."); | |
920 | } | |
921 | ||
922 | static int format_luks_token_text( | |
923 | struct crypt_device *cd, | |
924 | UserRecord *hr, | |
925 | const void *volume_key, | |
926 | char **ret) { | |
927 | ||
928 | int r, encrypted_size_out1 = 0, encrypted_size_out2 = 0, iv_size, key_size; | |
929 | _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL; | |
930 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
931 | _cleanup_free_ void *iv = NULL, *encrypted = NULL; | |
932 | size_t text_length, encrypted_size; | |
933 | _cleanup_free_ char *text = NULL; | |
934 | const EVP_CIPHER *cc; | |
935 | ||
936 | assert(cd); | |
937 | assert(hr); | |
938 | assert(volume_key); | |
939 | assert(ret); | |
940 | ||
941 | r = crypt_device_to_evp_cipher(cd, &cc); | |
942 | if (r < 0) | |
943 | return r; | |
944 | ||
945 | key_size = EVP_CIPHER_key_length(cc); | |
946 | iv_size = EVP_CIPHER_iv_length(cc); | |
947 | ||
948 | if (iv_size > 0) { | |
949 | iv = malloc(iv_size); | |
950 | if (!iv) | |
951 | return log_oom(); | |
952 | ||
87cb1ab6 | 953 | r = crypto_random_bytes(iv, iv_size); |
70a5db58 LP |
954 | if (r < 0) |
955 | return log_error_errno(r, "Failed to generate IV: %m"); | |
956 | } | |
957 | ||
958 | context = EVP_CIPHER_CTX_new(); | |
959 | if (!context) | |
960 | return log_oom(); | |
961 | ||
962 | if (EVP_EncryptInit_ex(context, cc, NULL, volume_key, iv) != 1) | |
963 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context."); | |
964 | ||
965 | r = json_variant_format(hr->json, 0, &text); | |
966 | if (r < 0) | |
80ace4f2 | 967 | return log_error_errno(r, "Failed to format user record for LUKS: %m"); |
70a5db58 LP |
968 | |
969 | text_length = strlen(text); | |
970 | encrypted_size = text_length + 2*key_size - 1; | |
971 | ||
972 | encrypted = malloc(encrypted_size); | |
973 | if (!encrypted) | |
974 | return log_oom(); | |
975 | ||
976 | if (EVP_EncryptUpdate(context, encrypted, &encrypted_size_out1, (uint8_t*) text, text_length) != 1) | |
977 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt JSON record."); | |
978 | ||
979 | assert((size_t) encrypted_size_out1 <= encrypted_size); | |
980 | ||
981 | if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted + encrypted_size_out1, &encrypted_size_out2) != 1) | |
982 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of JSON record. "); | |
983 | ||
984 | assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 <= encrypted_size); | |
985 | ||
986 | r = json_build(&v, | |
987 | JSON_BUILD_OBJECT( | |
0cdf6b14 | 988 | JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-homed")), |
70a5db58 LP |
989 | JSON_BUILD_PAIR("keyslots", JSON_BUILD_EMPTY_ARRAY), |
990 | JSON_BUILD_PAIR("record", JSON_BUILD_BASE64(encrypted, encrypted_size_out1 + encrypted_size_out2)), | |
991 | JSON_BUILD_PAIR("iv", JSON_BUILD_BASE64(iv, iv_size)))); | |
992 | if (r < 0) | |
993 | return log_error_errno(r, "Failed to prepare LUKS JSON token object: %m"); | |
994 | ||
995 | r = json_variant_format(v, 0, ret); | |
996 | if (r < 0) | |
997 | return log_error_errno(r, "Failed to format encrypted user record for LUKS: %m"); | |
998 | ||
999 | return 0; | |
1000 | } | |
1001 | ||
1002 | int home_store_header_identity_luks( | |
1003 | UserRecord *h, | |
1004 | HomeSetup *setup, | |
1005 | UserRecord *old_home) { | |
1006 | ||
1007 | _cleanup_(user_record_unrefp) UserRecord *header_home = NULL; | |
1008 | _cleanup_free_ char *text = NULL; | |
e7e30330 | 1009 | int r; |
70a5db58 LP |
1010 | |
1011 | assert(h); | |
1012 | ||
1013 | if (!setup->crypt_device) | |
1014 | return 0; | |
1015 | ||
1016 | assert(setup->volume_key); | |
1017 | ||
1018 | /* Let's store the user's identity record in the LUKS2 "token" header data fields, in an encrypted | |
1019 | * fashion. Why that? If we'd rely on the record being embedded in the payload file system itself we | |
1020 | * would have to mount the file system before we can validate the JSON record, its signatures and | |
1021 | * whether it matches what we are looking for. However, kernel file system implementations are | |
1022 | * generally not ready to be used on untrusted media. Hence let's store the record independently of | |
1023 | * the file system, so that we can validate it first, and only then mount the file system. To keep | |
1024 | * things simple we use the same encryption settings for this record as for the file system itself. */ | |
1025 | ||
bfc0cc1a | 1026 | r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &header_home); |
70a5db58 LP |
1027 | if (r < 0) |
1028 | return log_error_errno(r, "Failed to determine new header record: %m"); | |
1029 | ||
1030 | if (old_home && user_record_equal(old_home, header_home)) { | |
1031 | log_debug("Not updating header home record."); | |
1032 | return 0; | |
1033 | } | |
1034 | ||
1035 | r = format_luks_token_text(setup->crypt_device, header_home, setup->volume_key, &text); | |
1036 | if (r < 0) | |
1037 | return r; | |
1038 | ||
e7e30330 | 1039 | for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) { |
70a5db58 LP |
1040 | crypt_token_info state; |
1041 | const char *type; | |
1042 | ||
71eceff6 | 1043 | state = sym_crypt_token_status(setup->crypt_device, token, &type); |
70a5db58 LP |
1044 | if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, we are done */ |
1045 | break; | |
1046 | if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL)) | |
1047 | continue; /* Not ours */ | |
1048 | if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN) | |
1049 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state); | |
1050 | ||
1051 | if (!streq(type, "systemd-homed")) | |
1052 | continue; | |
1053 | ||
71eceff6 | 1054 | r = sym_crypt_token_json_set(setup->crypt_device, token, text); |
70a5db58 LP |
1055 | if (r < 0) |
1056 | return log_error_errno(r, "Failed to set JSON token for slot %i: %m", token); | |
1057 | ||
1058 | /* Now, let's free the text so that for all further matching tokens we all crypt_json_token_set() | |
1059 | * with a NULL text in order to invalidate the tokens. */ | |
1060 | text = mfree(text); | |
70a5db58 LP |
1061 | } |
1062 | ||
1063 | if (text) | |
1064 | return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Didn't find any record token to update."); | |
1065 | ||
1066 | log_info("Wrote LUKS header user record."); | |
1067 | ||
1068 | return 1; | |
1069 | } | |
1070 | ||
28a7f106 | 1071 | int run_fitrim(int root_fd) { |
70a5db58 LP |
1072 | struct fstrim_range range = { |
1073 | .len = UINT64_MAX, | |
1074 | }; | |
1075 | ||
1076 | /* If discarding is on, discard everything right after mounting, so that the discard setting takes | |
28a7f106 | 1077 | * effect on activation. (Also, optionally, trim on logout) */ |
70a5db58 LP |
1078 | |
1079 | assert(root_fd >= 0); | |
1080 | ||
1081 | if (ioctl(root_fd, FITRIM, &range) < 0) { | |
28a7f106 | 1082 | if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EBADF) { |
70a5db58 LP |
1083 | log_debug_errno(errno, "File system does not support FITRIM, not trimming."); |
1084 | return 0; | |
1085 | } | |
1086 | ||
1087 | return log_warning_errno(errno, "Failed to invoke FITRIM, ignoring: %m"); | |
1088 | } | |
1089 | ||
2b59bf51 | 1090 | log_info("Discarded unused %s.", FORMAT_BYTES(range.len)); |
70a5db58 LP |
1091 | return 1; |
1092 | } | |
1093 | ||
28a7f106 | 1094 | int run_fallocate(int backing_fd, const struct stat *st) { |
28a7f106 | 1095 | struct stat stbuf; |
70a5db58 LP |
1096 | |
1097 | assert(backing_fd >= 0); | |
70a5db58 LP |
1098 | |
1099 | /* If discarding is off, let's allocate the whole image before mounting, so that the setting takes | |
1100 | * effect on activation */ | |
1101 | ||
28a7f106 LP |
1102 | if (!st) { |
1103 | if (fstat(backing_fd, &stbuf) < 0) | |
1104 | return log_error_errno(errno, "Failed to fstat(): %m"); | |
1105 | ||
1106 | st = &stbuf; | |
1107 | } | |
1108 | ||
70a5db58 LP |
1109 | if (!S_ISREG(st->st_mode)) |
1110 | return 0; | |
1111 | ||
1112 | if (st->st_blocks >= DIV_ROUND_UP(st->st_size, 512)) { | |
1113 | log_info("Backing file is fully allocated already."); | |
1114 | return 0; | |
1115 | } | |
1116 | ||
1117 | if (fallocate(backing_fd, FALLOC_FL_KEEP_SIZE, 0, st->st_size) < 0) { | |
1118 | ||
1119 | if (ERRNO_IS_NOT_SUPPORTED(errno)) { | |
1120 | log_debug_errno(errno, "fallocate() not supported on file system, ignoring."); | |
1121 | return 0; | |
1122 | } | |
1123 | ||
1124 | if (ERRNO_IS_DISK_SPACE(errno)) { | |
1125 | log_debug_errno(errno, "Not enough disk space to fully allocate home."); | |
1126 | return -ENOSPC; /* make recognizable */ | |
1127 | } | |
1128 | ||
1129 | return log_error_errno(errno, "Failed to allocate backing file blocks: %m"); | |
1130 | } | |
1131 | ||
1132 | log_info("Allocated additional %s.", | |
2b59bf51 | 1133 | FORMAT_BYTES((DIV_ROUND_UP(st->st_size, 512) - st->st_blocks) * 512)); |
70a5db58 LP |
1134 | return 1; |
1135 | } | |
1136 | ||
28a7f106 | 1137 | int run_fallocate_by_path(const char *backing_path) { |
254d1313 | 1138 | _cleanup_close_ int backing_fd = -EBADF; |
28a7f106 LP |
1139 | |
1140 | backing_fd = open(backing_path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); | |
1141 | if (backing_fd < 0) | |
1142 | return log_error_errno(errno, "Failed to open '%s' for fallocate(): %m", backing_path); | |
1143 | ||
1144 | return run_fallocate(backing_fd, NULL); | |
1145 | } | |
1146 | ||
2aaf565a LP |
1147 | static int lock_image_fd(int image_fd, const char *ip) { |
1148 | int r; | |
1149 | ||
1150 | /* If the $SYSTEMD_LUKS_LOCK environment variable is set we'll take an exclusive BSD lock on the | |
1151 | * image file, and send it to our parent. homed will keep it open to ensure no other instance of | |
1152 | * homed (across the network or such) will also mount the file. */ | |
1153 | ||
e60c3c72 YW |
1154 | assert(image_fd >= 0); |
1155 | assert(ip); | |
1156 | ||
2aaf565a LP |
1157 | r = getenv_bool("SYSTEMD_LUKS_LOCK"); |
1158 | if (r == -ENXIO) | |
1159 | return 0; | |
1160 | if (r < 0) | |
1161 | return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m"); | |
e60c3c72 YW |
1162 | if (r == 0) |
1163 | return 0; | |
2aa94bb8 | 1164 | |
e60c3c72 | 1165 | if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) { |
2aaf565a | 1166 | |
5cad4c70 | 1167 | if (errno == EAGAIN) |
e60c3c72 YW |
1168 | log_error_errno(errno, "Image file '%s' already locked, can't use.", ip); |
1169 | else | |
1170 | log_error_errno(errno, "Failed to lock image file '%s': %m", ip); | |
2aaf565a | 1171 | |
5cad4c70 | 1172 | return errno != EAGAIN ? -errno : -EADDRINUSE; /* Make error recognizable */ |
e60c3c72 | 1173 | } |
2aaf565a | 1174 | |
e60c3c72 | 1175 | log_info("Successfully locked image file '%s'.", ip); |
2aaf565a | 1176 | |
e60c3c72 YW |
1177 | /* Now send it to our parent to keep safe while the home dir is active */ |
1178 | r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1); | |
1179 | if (r < 0) | |
1180 | log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m"); | |
2aaf565a LP |
1181 | |
1182 | return 0; | |
1183 | } | |
1184 | ||
4e660eca LP |
1185 | static int open_image_file( |
1186 | UserRecord *h, | |
1187 | const char *force_image_path, | |
1188 | struct stat *ret_stat) { | |
1189 | ||
254d1313 | 1190 | _cleanup_close_ int image_fd = -EBADF; |
4e660eca LP |
1191 | struct stat st; |
1192 | const char *ip; | |
1193 | int r; | |
1194 | ||
e60c3c72 YW |
1195 | assert(h || force_image_path); |
1196 | ||
4e660eca LP |
1197 | ip = force_image_path ?: user_record_image_path(h); |
1198 | ||
1199 | image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); | |
1200 | if (image_fd < 0) | |
1201 | return log_error_errno(errno, "Failed to open image file %s: %m", ip); | |
1202 | ||
1203 | if (fstat(image_fd, &st) < 0) | |
1204 | return log_error_errno(errno, "Failed to fstat() image file: %m"); | |
1205 | if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode)) | |
1206 | return log_error_errno( | |
1207 | S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD), | |
1208 | "Image file %s is not a regular file or block device: %m", ip); | |
1209 | ||
e60c3c72 YW |
1210 | /* Locking block devices doesn't really make sense, as this might interfere with |
1211 | * udev's workings, and these locks aren't network propagated anyway, hence not what | |
1212 | * we are after here. */ | |
1213 | if (S_ISREG(st.st_mode)) { | |
1214 | r = lock_image_fd(image_fd, ip); | |
1215 | if (r < 0) | |
1216 | return r; | |
1217 | } | |
4e660eca LP |
1218 | |
1219 | if (ret_stat) | |
1220 | *ret_stat = st; | |
1221 | ||
1222 | return TAKE_FD(image_fd); | |
1223 | } | |
1224 | ||
aa0a6214 | 1225 | int home_setup_luks( |
70a5db58 | 1226 | UserRecord *h, |
e1df968b | 1227 | HomeSetupFlags flags, |
70a5db58 | 1228 | const char *force_image_path, |
70a5db58 | 1229 | HomeSetup *setup, |
c00b2ddc | 1230 | PasswordCache *cache, |
70a5db58 LP |
1231 | UserRecord **ret_luks_home) { |
1232 | ||
1474e66a | 1233 | sd_id128_t found_partition_uuid, found_fs_uuid, found_luks_uuid = SD_ID128_NULL; |
70a5db58 | 1234 | _cleanup_(user_record_unrefp) UserRecord *luks_home = NULL; |
70a5db58 | 1235 | _cleanup_(erase_and_freep) void *volume_key = NULL; |
70a5db58 LP |
1236 | size_t volume_key_size = 0; |
1237 | uint64_t offset, size; | |
34081f6b | 1238 | struct stat st; |
80ffbbfb | 1239 | int r; |
70a5db58 LP |
1240 | |
1241 | assert(h); | |
1242 | assert(setup); | |
70a5db58 LP |
1243 | assert(user_record_storage(h) == USER_LUKS); |
1244 | ||
71eceff6 LP |
1245 | r = dlopen_cryptsetup(); |
1246 | if (r < 0) | |
1247 | return r; | |
1248 | ||
34081f6b LP |
1249 | r = make_dm_names(h, setup); |
1250 | if (r < 0) | |
1251 | return r; | |
1252 | ||
1253 | /* Reuse the image fd if it has already been opened by an earlier step */ | |
1254 | if (setup->image_fd < 0) { | |
1255 | setup->image_fd = open_image_file(h, force_image_path, &st); | |
1256 | if (setup->image_fd < 0) | |
1257 | return setup->image_fd; | |
1258 | } else if (fstat(setup->image_fd, &st) < 0) | |
1259 | return log_error_errno(errno, "Failed to stat image: %m"); | |
1260 | ||
e1df968b | 1261 | if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) { |
70a5db58 LP |
1262 | struct loop_info64 info; |
1263 | const char *n; | |
1264 | ||
34081f6b LP |
1265 | if (!setup->crypt_device) { |
1266 | r = luks_open(h, | |
1267 | setup, | |
1268 | cache, | |
1269 | &found_luks_uuid, | |
1270 | &volume_key, | |
1271 | &volume_key_size); | |
1272 | if (r < 0) | |
1273 | return r; | |
1274 | } | |
70a5db58 | 1275 | |
34081f6b LP |
1276 | if (ret_luks_home) { |
1277 | r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home); | |
1278 | if (r < 0) | |
1279 | return r; | |
1280 | } | |
70a5db58 | 1281 | |
f7800049 | 1282 | n = sym_crypt_get_device_name(setup->crypt_device); |
70a5db58 LP |
1283 | if (!n) |
1284 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name); | |
1285 | ||
34081f6b | 1286 | if (!setup->loop) { |
de3b7f16 | 1287 | r = loop_device_open_from_path(n, O_RDWR, LOCK_UN, &setup->loop); |
34081f6b LP |
1288 | if (r < 0) |
1289 | return log_error_errno(r, "Failed to open loopback device %s: %m", n); | |
1290 | } | |
70a5db58 | 1291 | |
e4d1e79b | 1292 | if (ioctl(setup->loop->fd, LOOP_GET_STATUS64, &info) < 0) { |
70a5db58 | 1293 | _cleanup_free_ char *sysfs = NULL; |
70a5db58 LP |
1294 | |
1295 | if (!IN_SET(errno, ENOTTY, EINVAL)) | |
1296 | return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n); | |
1297 | ||
e4d1e79b | 1298 | if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0) |
70a5db58 LP |
1299 | return log_error_errno(r, "Failed to read block device size of %s: %m", n); |
1300 | ||
e4d1e79b | 1301 | if (fstat(setup->loop->fd, &st) < 0) |
70a5db58 LP |
1302 | return log_error_errno(r, "Failed to stat block device %s: %m", n); |
1303 | assert(S_ISBLK(st.st_mode)); | |
1304 | ||
ec61371f | 1305 | if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0) |
70a5db58 LP |
1306 | return log_oom(); |
1307 | ||
1308 | if (access(sysfs, F_OK) < 0) { | |
1309 | if (errno != ENOENT) | |
1310 | return log_error_errno(errno, "Failed to determine whether %s exists: %m", sysfs); | |
1311 | ||
1312 | offset = 0; | |
1313 | } else { | |
1314 | _cleanup_free_ char *buffer = NULL; | |
1315 | ||
ec61371f | 1316 | if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/start", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0) |
70a5db58 LP |
1317 | return log_oom(); |
1318 | ||
1319 | r = read_one_line_file(sysfs, &buffer); | |
1320 | if (r < 0) | |
1321 | return log_error_errno(r, "Failed to read partition start offset: %m"); | |
1322 | ||
1323 | r = safe_atou64(buffer, &offset); | |
1324 | if (r < 0) | |
1325 | return log_error_errno(r, "Failed to parse partition start offset: %m"); | |
1326 | ||
1327 | if (offset > UINT64_MAX / 512U) | |
1328 | return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Offset too large for 64 byte range, refusing."); | |
1329 | ||
1330 | offset *= 512U; | |
1331 | } | |
1332 | } else { | |
48f46254 LP |
1333 | #if HAVE_VALGRIND_MEMCHECK_H |
1334 | VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); | |
1335 | #endif | |
1336 | ||
70a5db58 LP |
1337 | offset = info.lo_offset; |
1338 | size = info.lo_sizelimit; | |
1339 | } | |
1340 | ||
1341 | found_partition_uuid = found_fs_uuid = SD_ID128_NULL; | |
1342 | ||
e4d1e79b | 1343 | log_info("Discovered used loopback device %s.", setup->loop->node); |
70a5db58 | 1344 | |
34081f6b LP |
1345 | if (setup->root_fd < 0) { |
1346 | setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); | |
1347 | if (setup->root_fd < 0) | |
1348 | return log_error_errno(errno, "Failed to open home directory: %m"); | |
1349 | } | |
70a5db58 LP |
1350 | } else { |
1351 | _cleanup_free_ char *fstype = NULL, *subdir = NULL; | |
70a5db58 | 1352 | const char *ip; |
34081f6b LP |
1353 | |
1354 | /* When we aren't reopening the home directory we are allocating it fresh, hence the relevant | |
1355 | * objects can't be allocated yet. */ | |
1356 | assert(setup->root_fd < 0); | |
1357 | assert(!setup->crypt_device); | |
1358 | assert(!setup->loop); | |
70a5db58 LP |
1359 | |
1360 | ip = force_image_path ?: user_record_image_path(h); | |
1361 | ||
498abadb | 1362 | subdir = path_join(HOME_RUNTIME_WORK_DIR, user_record_user_name_and_realm(h)); |
70a5db58 LP |
1363 | if (!subdir) |
1364 | return log_oom(); | |
1365 | ||
80ffbbfb | 1366 | r = luks_validate(setup->image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size); |
70a5db58 LP |
1367 | if (r < 0) |
1368 | return log_error_errno(r, "Failed to validate disk label: %m"); | |
1369 | ||
565ac8b1 LP |
1370 | /* Everything before this point left the image untouched. We are now starting to make |
1371 | * changes, hence mark the image dirty */ | |
80ffbbfb LP |
1372 | if (run_mark_dirty(setup->image_fd, true) > 0) |
1373 | setup->do_mark_clean = true; | |
565ac8b1 | 1374 | |
70a5db58 | 1375 | if (!user_record_luks_discard(h)) { |
34081f6b | 1376 | r = run_fallocate(setup->image_fd, &st); |
70a5db58 LP |
1377 | if (r < 0) |
1378 | return r; | |
1379 | } | |
1380 | ||
22ee78a8 LP |
1381 | r = loop_device_make( |
1382 | setup->image_fd, | |
1383 | O_RDWR, | |
1384 | offset, | |
1385 | size, | |
1386 | h->luks_sector_size == UINT64_MAX ? UINT32_MAX : user_record_luks_sector_size(h), /* if sector size is not specified, select UINT32_MAX, i.e. auto-probe */ | |
1387 | /* loop_flags= */ 0, | |
1388 | LOCK_UN, | |
1389 | &setup->loop); | |
70a5db58 LP |
1390 | if (r == -ENOENT) { |
1391 | log_error_errno(r, "Loopback block device support is not available on this system."); | |
1392 | return -ENOLINK; /* make recognizable */ | |
1393 | } | |
1394 | if (r < 0) | |
1395 | return log_error_errno(r, "Failed to allocate loopback context: %m"); | |
1396 | ||
e4d1e79b | 1397 | log_info("Setting up loopback device %s completed.", setup->loop->node ?: ip); |
70a5db58 | 1398 | |
d26cdde3 LP |
1399 | r = luks_setup(h, |
1400 | setup->loop->node ?: ip, | |
70a5db58 LP |
1401 | setup->dm_name, |
1402 | h->luks_uuid, | |
1403 | h->luks_cipher, | |
1404 | h->luks_cipher_mode, | |
1405 | h->luks_volume_key_size, | |
1406 | h->password, | |
7b78db28 | 1407 | cache, |
28a7f106 | 1408 | user_record_luks_discard(h) || user_record_luks_offline_discard(h), |
f7800049 | 1409 | &setup->crypt_device, |
70a5db58 LP |
1410 | &found_luks_uuid, |
1411 | &volume_key, | |
d26cdde3 LP |
1412 | &volume_key_size, |
1413 | &setup->key_serial); | |
70a5db58 LP |
1414 | if (r < 0) |
1415 | return r; | |
1416 | ||
f7800049 | 1417 | setup->undo_dm = true; |
70a5db58 | 1418 | |
34081f6b LP |
1419 | if (ret_luks_home) { |
1420 | r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home); | |
1421 | if (r < 0) | |
1422 | return r; | |
1423 | } | |
70a5db58 LP |
1424 | |
1425 | r = fs_validate(setup->dm_node, h->file_system_uuid, &fstype, &found_fs_uuid); | |
1426 | if (r < 0) | |
d33f0241 | 1427 | return r; |
70a5db58 LP |
1428 | |
1429 | r = run_fsck(setup->dm_node, fstype); | |
1430 | if (r < 0) | |
d33f0241 | 1431 | return r; |
70a5db58 | 1432 | |
2e0001c2 | 1433 | r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); |
70a5db58 | 1434 | if (r < 0) |
d33f0241 | 1435 | return r; |
70a5db58 | 1436 | |
203f06aa | 1437 | setup->undo_mount = true; |
70a5db58 | 1438 | |
ae4d05f6 | 1439 | setup->root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); |
d33f0241 LP |
1440 | if (setup->root_fd < 0) |
1441 | return log_error_errno(errno, "Failed to open home directory: %m"); | |
70a5db58 LP |
1442 | |
1443 | if (user_record_luks_discard(h)) | |
ae4d05f6 | 1444 | (void) run_fitrim(setup->root_fd); |
28a7f106 | 1445 | |
28a7f106 | 1446 | setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h)); |
70a5db58 LP |
1447 | } |
1448 | ||
34081f6b LP |
1449 | if (!sd_id128_is_null(found_partition_uuid)) |
1450 | setup->found_partition_uuid = found_partition_uuid; | |
1451 | if (!sd_id128_is_null(found_luks_uuid)) | |
1452 | setup->found_luks_uuid = found_luks_uuid; | |
1453 | if (!sd_id128_is_null(found_fs_uuid)) | |
1454 | setup->found_fs_uuid = found_fs_uuid; | |
1455 | ||
70a5db58 LP |
1456 | setup->partition_offset = offset; |
1457 | setup->partition_size = size; | |
34081f6b LP |
1458 | |
1459 | if (volume_key) { | |
1460 | erase_and_free(setup->volume_key); | |
1461 | setup->volume_key = TAKE_PTR(volume_key); | |
1462 | setup->volume_key_size = volume_key_size; | |
1463 | } | |
70a5db58 | 1464 | |
70a5db58 LP |
1465 | if (ret_luks_home) |
1466 | *ret_luks_home = TAKE_PTR(luks_home); | |
1467 | ||
1468 | return 0; | |
70a5db58 LP |
1469 | } |
1470 | ||
1783a48c | 1471 | static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, const struct statfs *sfs) { |
70a5db58 LP |
1472 | assert(sfs); |
1473 | ||
1474 | log_info("Image size is %s, file system size is %s, file system payload size is %s, file system free is %s.", | |
2b59bf51 ZJS |
1475 | FORMAT_BYTES(host_size), |
1476 | FORMAT_BYTES(encrypted_size), | |
1477 | FORMAT_BYTES((uint64_t) sfs->f_blocks * (uint64_t) sfs->f_frsize), | |
1478 | FORMAT_BYTES((uint64_t) sfs->f_bfree * (uint64_t) sfs->f_frsize)); | |
70a5db58 LP |
1479 | } |
1480 | ||
26191000 LP |
1481 | static int home_auto_grow_luks( |
1482 | UserRecord *h, | |
1483 | HomeSetup *setup, | |
1484 | PasswordCache *cache) { | |
1485 | ||
1486 | struct statfs sfs; | |
1487 | ||
1488 | assert(h); | |
1489 | assert(setup); | |
1490 | ||
1491 | if (!IN_SET(user_record_auto_resize_mode(h), AUTO_RESIZE_GROW, AUTO_RESIZE_SHRINK_AND_GROW)) | |
1492 | return 0; | |
1493 | ||
1494 | assert(setup->root_fd >= 0); | |
1495 | ||
1496 | if (fstatfs(setup->root_fd, &sfs) < 0) | |
1497 | return log_error_errno(errno, "Failed to statfs home directory: %m"); | |
1498 | ||
1499 | if (!fs_can_online_shrink_and_grow(sfs.f_type)) { | |
1500 | log_debug("Not auto-grow file system, since selected file system cannot do both online shrink and grow."); | |
1501 | return 0; | |
1502 | } | |
1503 | ||
1504 | log_debug("Initiating auto-grow..."); | |
1505 | ||
1506 | return home_resize_luks( | |
1507 | h, | |
1508 | HOME_SETUP_ALREADY_ACTIVATED| | |
1509 | HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES| | |
1510 | HOME_SETUP_RESIZE_DONT_SHRINK| | |
1511 | HOME_SETUP_RESIZE_DONT_UNDO, | |
1512 | setup, | |
1513 | cache, | |
1514 | NULL); | |
1515 | } | |
1516 | ||
70a5db58 LP |
1517 | int home_activate_luks( |
1518 | UserRecord *h, | |
6f2c8136 | 1519 | HomeSetupFlags flags, |
a74e2e44 | 1520 | HomeSetup *setup, |
7b78db28 | 1521 | PasswordCache *cache, |
70a5db58 LP |
1522 | UserRecord **ret_home) { |
1523 | ||
1524 | _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL; | |
70a5db58 LP |
1525 | uint64_t host_size, encrypted_size; |
1526 | const char *hdo, *hd; | |
1527 | struct statfs sfs; | |
1528 | int r; | |
1529 | ||
1530 | assert(h); | |
1531 | assert(user_record_storage(h) == USER_LUKS); | |
a74e2e44 | 1532 | assert(setup); |
70a5db58 LP |
1533 | assert(ret_home); |
1534 | ||
71eceff6 LP |
1535 | r = dlopen_cryptsetup(); |
1536 | if (r < 0) | |
1537 | return r; | |
1538 | ||
70a5db58 | 1539 | assert_se(hdo = user_record_home_directory(h)); |
2f82562b | 1540 | hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */ |
70a5db58 | 1541 | |
a74e2e44 | 1542 | r = home_get_state_luks(h, setup); |
70a5db58 LP |
1543 | if (r < 0) |
1544 | return r; | |
e1ab6635 | 1545 | if (r > 0) |
a74e2e44 | 1546 | return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup->dm_node); |
70a5db58 | 1547 | |
aa0a6214 | 1548 | r = home_setup_luks( |
70a5db58 | 1549 | h, |
e1df968b | 1550 | 0, |
70a5db58 | 1551 | NULL, |
a74e2e44 | 1552 | setup, |
c00b2ddc | 1553 | cache, |
70a5db58 LP |
1554 | &luks_home_record); |
1555 | if (r < 0) | |
1556 | return r; | |
1557 | ||
26191000 LP |
1558 | r = home_auto_grow_luks(h, setup, cache); |
1559 | if (r < 0) | |
1560 | return r; | |
1561 | ||
a74e2e44 | 1562 | r = block_get_size_by_fd(setup->loop->fd, &host_size); |
70a5db58 LP |
1563 | if (r < 0) |
1564 | return log_error_errno(r, "Failed to get loopback block device size: %m"); | |
1565 | ||
a74e2e44 | 1566 | r = block_get_size_by_path(setup->dm_node, &encrypted_size); |
70a5db58 LP |
1567 | if (r < 0) |
1568 | return log_error_errno(r, "Failed to get LUKS block device size: %m"); | |
1569 | ||
1570 | r = home_refresh( | |
1571 | h, | |
6f2c8136 | 1572 | flags, |
a74e2e44 | 1573 | setup, |
70a5db58 | 1574 | luks_home_record, |
7b78db28 | 1575 | cache, |
70a5db58 LP |
1576 | &sfs, |
1577 | &new_home); | |
1578 | if (r < 0) | |
1579 | return r; | |
1580 | ||
a74e2e44 | 1581 | r = home_extend_embedded_identity(new_home, h, setup); |
70a5db58 LP |
1582 | if (r < 0) |
1583 | return r; | |
1584 | ||
a74e2e44 | 1585 | setup->root_fd = safe_close(setup->root_fd); |
70a5db58 LP |
1586 | |
1587 | r = home_move_mount(user_record_user_name_and_realm(h), hd); | |
1588 | if (r < 0) | |
1589 | return r; | |
1590 | ||
a74e2e44 LP |
1591 | setup->undo_mount = false; |
1592 | setup->do_offline_fitrim = false; | |
70a5db58 | 1593 | |
a74e2e44 | 1594 | loop_device_relinquish(setup->loop); |
70a5db58 | 1595 | |
a74e2e44 | 1596 | r = sym_crypt_deactivate_by_name(NULL, setup->dm_name, CRYPT_DEACTIVATE_DEFERRED); |
70a5db58 | 1597 | if (r < 0) |
80ace4f2 | 1598 | log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m"); |
70a5db58 | 1599 | |
a74e2e44 LP |
1600 | setup->undo_dm = false; |
1601 | setup->do_offline_fallocate = false; | |
1602 | setup->do_mark_clean = false; | |
aa0379f1 | 1603 | setup->do_drop_caches = false; |
d26cdde3 | 1604 | TAKE_KEY_SERIAL(setup->key_serial); /* Leave key in kernel keyring */ |
70a5db58 | 1605 | |
26191000 | 1606 | log_info("Activation completed."); |
70a5db58 LP |
1607 | |
1608 | print_size_summary(host_size, encrypted_size, &sfs); | |
1609 | ||
1610 | *ret_home = TAKE_PTR(new_home); | |
1611 | return 1; | |
1612 | } | |
1613 | ||
fc032ae1 | 1614 | int home_deactivate_luks(UserRecord *h, HomeSetup *setup) { |
2f4ad535 | 1615 | bool we_detached = false; |
70a5db58 LP |
1616 | int r; |
1617 | ||
fc032ae1 LP |
1618 | assert(h); |
1619 | assert(setup); | |
fc032ae1 | 1620 | |
70a5db58 LP |
1621 | /* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we |
1622 | * don't have to explicitly have to detach them. However, we do that nonetheless (in case of the DM | |
1623 | * device), to avoid races: by explicitly detaching them we know when the detaching is complete. We | |
1624 | * don't bother about the loopback device because unlike the DM device it doesn't have a fixed | |
1625 | * name. */ | |
1626 | ||
26191000 LP |
1627 | if (!setup->crypt_device) { |
1628 | r = acquire_open_luks_device(h, setup, /* graceful= */ true); | |
1629 | if (r < 0) | |
1630 | return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name); | |
2f4ad535 | 1631 | if (r == 0) |
26191000 | 1632 | log_debug("LUKS device %s has already been detached.", setup->dm_name); |
26191000 LP |
1633 | } |
1634 | ||
1635 | if (setup->crypt_device) { | |
f7800049 | 1636 | log_info("Discovered used LUKS device %s.", setup->dm_node); |
28a7f106 | 1637 | |
f7800049 | 1638 | cryptsetup_enable_logging(setup->crypt_device); |
28a7f106 | 1639 | |
f7800049 | 1640 | r = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0); |
2f4ad535 | 1641 | if (ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) |
f7800049 | 1642 | log_debug_errno(r, "LUKS device %s is already detached.", setup->dm_node); |
2f4ad535 | 1643 | else if (r < 0) |
f7800049 | 1644 | return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", setup->dm_node); |
28a7f106 LP |
1645 | else { |
1646 | log_info("LUKS device detaching completed."); | |
1647 | we_detached = true; | |
1648 | } | |
1649 | } | |
70a5db58 | 1650 | |
491347bd | 1651 | (void) wait_for_block_device_gone(setup, USEC_PER_SEC * 30); |
228b1dec LP |
1652 | setup->undo_dm = false; |
1653 | ||
28a7f106 LP |
1654 | if (user_record_luks_offline_discard(h)) |
1655 | log_debug("Not allocating on logout."); | |
1656 | else | |
1657 | (void) run_fallocate_by_path(user_record_image_path(h)); | |
70a5db58 | 1658 | |
565ac8b1 | 1659 | run_mark_dirty_by_path(user_record_image_path(h), false); |
28a7f106 LP |
1660 | return we_detached; |
1661 | } | |
70a5db58 | 1662 | |
a71a0693 | 1663 | int home_trim_luks(UserRecord *h, HomeSetup *setup) { |
28a7f106 | 1664 | assert(h); |
a71a0693 LP |
1665 | assert(setup); |
1666 | assert(setup->root_fd >= 0); | |
28a7f106 LP |
1667 | |
1668 | if (!user_record_luks_offline_discard(h)) { | |
1669 | log_debug("Not trimming on logout."); | |
1670 | return 0; | |
1671 | } | |
70a5db58 | 1672 | |
a71a0693 | 1673 | (void) run_fitrim(setup->root_fd); |
28a7f106 | 1674 | return 0; |
70a5db58 LP |
1675 | } |
1676 | ||
70a5db58 LP |
1677 | static struct crypt_pbkdf_type* build_good_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) { |
1678 | assert(buffer); | |
1679 | assert(hr); | |
1680 | ||
b04ff66b AD |
1681 | bool benchmark = user_record_luks_pbkdf_force_iterations(hr) == UINT64_MAX; |
1682 | ||
70a5db58 LP |
1683 | *buffer = (struct crypt_pbkdf_type) { |
1684 | .hash = user_record_luks_pbkdf_hash_algorithm(hr), | |
1685 | .type = user_record_luks_pbkdf_type(hr), | |
b04ff66b AD |
1686 | .time_ms = benchmark ? user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC : 0, |
1687 | .iterations = benchmark ? 0 : user_record_luks_pbkdf_force_iterations(hr), | |
70a5db58 LP |
1688 | .max_memory_kb = user_record_luks_pbkdf_memory_cost(hr) / 1024, |
1689 | .parallel_threads = user_record_luks_pbkdf_parallel_threads(hr), | |
b04ff66b | 1690 | .flags = benchmark ? 0 : CRYPT_PBKDF_NO_BENCHMARK, |
70a5db58 LP |
1691 | }; |
1692 | ||
1693 | return buffer; | |
1694 | } | |
1695 | ||
1696 | static struct crypt_pbkdf_type* build_minimal_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) { | |
1697 | assert(buffer); | |
1698 | assert(hr); | |
1699 | ||
1700 | /* For PKCS#11 derived keys (which are generated randomly and are of high quality already) we use a | |
1701 | * minimal PBKDF */ | |
1702 | *buffer = (struct crypt_pbkdf_type) { | |
1703 | .hash = user_record_luks_pbkdf_hash_algorithm(hr), | |
1704 | .type = CRYPT_KDF_PBKDF2, | |
1705 | .iterations = 1, | |
1706 | .time_ms = 1, | |
1707 | }; | |
1708 | ||
1709 | return buffer; | |
1710 | } | |
1711 | ||
1712 | static int luks_format( | |
1713 | const char *node, | |
1714 | const char *dm_name, | |
1715 | sd_id128_t uuid, | |
1716 | const char *label, | |
7b78db28 | 1717 | const PasswordCache *cache, |
70a5db58 LP |
1718 | char **effective_passwords, |
1719 | bool discard, | |
1720 | UserRecord *hr, | |
1721 | struct crypt_device **ret) { | |
1722 | ||
1723 | _cleanup_(user_record_unrefp) UserRecord *reduced = NULL; | |
71eceff6 | 1724 | _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; |
70a5db58 LP |
1725 | _cleanup_(erase_and_freep) void *volume_key = NULL; |
1726 | struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf; | |
1727 | _cleanup_free_ char *text = NULL; | |
1728 | size_t volume_key_size; | |
70a5db58 LP |
1729 | int slot = 0, r; |
1730 | ||
1731 | assert(node); | |
1732 | assert(dm_name); | |
1733 | assert(hr); | |
1734 | assert(ret); | |
1735 | ||
71eceff6 | 1736 | r = sym_crypt_init(&cd, node); |
70a5db58 LP |
1737 | if (r < 0) |
1738 | return log_error_errno(r, "Failed to allocate libcryptsetup context: %m"); | |
1739 | ||
efc3b12f | 1740 | cryptsetup_enable_logging(cd); |
70a5db58 LP |
1741 | |
1742 | /* Normally we'd, just leave volume key generation to libcryptsetup. However, we can't, since we | |
1743 | * can't extract the volume key from the library again, but we need it in order to encrypt the JSON | |
1744 | * record. Hence, let's generate it on our own, so that we can keep track of it. */ | |
1745 | ||
1746 | volume_key_size = user_record_luks_volume_key_size(hr); | |
1747 | volume_key = malloc(volume_key_size); | |
1748 | if (!volume_key) | |
1749 | return log_oom(); | |
1750 | ||
87cb1ab6 | 1751 | r = crypto_random_bytes(volume_key, volume_key_size); |
70a5db58 LP |
1752 | if (r < 0) |
1753 | return log_error_errno(r, "Failed to generate volume key: %m"); | |
1754 | ||
1755 | #if HAVE_CRYPT_SET_METADATA_SIZE | |
1756 | /* Increase the metadata space to 4M, the largest LUKS2 supports */ | |
71eceff6 | 1757 | r = sym_crypt_set_metadata_size(cd, 4096U*1024U, 0); |
70a5db58 LP |
1758 | if (r < 0) |
1759 | return log_error_errno(r, "Failed to change LUKS2 metadata size: %m"); | |
1760 | #endif | |
1761 | ||
1762 | build_good_pbkdf(&good_pbkdf, hr); | |
1763 | build_minimal_pbkdf(&minimal_pbkdf, hr); | |
1764 | ||
85b55869 LP |
1765 | r = sym_crypt_format( |
1766 | cd, | |
1767 | CRYPT_LUKS2, | |
1768 | user_record_luks_cipher(hr), | |
1769 | user_record_luks_cipher_mode(hr), | |
b7416360 | 1770 | SD_ID128_TO_UUID_STRING(uuid), |
85b55869 LP |
1771 | volume_key, |
1772 | volume_key_size, | |
1773 | &(struct crypt_params_luks2) { | |
1774 | .label = label, | |
1775 | .subsystem = "systemd-home", | |
fd83c98e | 1776 | .sector_size = user_record_luks_sector_size(hr), |
85b55869 LP |
1777 | .pbkdf = &good_pbkdf, |
1778 | }); | |
70a5db58 LP |
1779 | if (r < 0) |
1780 | return log_error_errno(r, "Failed to format LUKS image: %m"); | |
1781 | ||
1782 | log_info("LUKS formatting completed."); | |
1783 | ||
1784 | STRV_FOREACH(pp, effective_passwords) { | |
1785 | ||
3361d1ca | 1786 | if (password_cache_contains(cache, *pp)) { /* is this a fido2 or pkcs11 password? */ |
70a5db58 | 1787 | log_debug("Using minimal PBKDF for slot %i", slot); |
71eceff6 | 1788 | r = sym_crypt_set_pbkdf_type(cd, &minimal_pbkdf); |
70a5db58 LP |
1789 | } else { |
1790 | log_debug("Using good PBKDF for slot %i", slot); | |
71eceff6 | 1791 | r = sym_crypt_set_pbkdf_type(cd, &good_pbkdf); |
70a5db58 LP |
1792 | } |
1793 | if (r < 0) | |
1794 | return log_error_errno(r, "Failed to tweak PBKDF for slot %i: %m", slot); | |
1795 | ||
71eceff6 | 1796 | r = sym_crypt_keyslot_add_by_volume_key( |
70a5db58 LP |
1797 | cd, |
1798 | slot, | |
1799 | volume_key, | |
1800 | volume_key_size, | |
1801 | *pp, | |
1802 | strlen(*pp)); | |
1803 | if (r < 0) | |
1804 | return log_error_errno(r, "Failed to set up LUKS password for slot %i: %m", slot); | |
1805 | ||
1806 | log_info("Writing password to LUKS keyslot %i completed.", slot); | |
1807 | slot++; | |
1808 | } | |
1809 | ||
71eceff6 | 1810 | r = sym_crypt_activate_by_volume_key( |
70a5db58 LP |
1811 | cd, |
1812 | dm_name, | |
1813 | volume_key, | |
1814 | volume_key_size, | |
1815 | discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0); | |
1816 | if (r < 0) | |
1817 | return log_error_errno(r, "Failed to activate LUKS superblock: %m"); | |
1818 | ||
1819 | log_info("LUKS activation by volume key succeeded."); | |
1820 | ||
bfc0cc1a | 1821 | r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &reduced); |
70a5db58 LP |
1822 | if (r < 0) |
1823 | return log_error_errno(r, "Failed to prepare home record for LUKS: %m"); | |
1824 | ||
1825 | r = format_luks_token_text(cd, reduced, volume_key, &text); | |
1826 | if (r < 0) | |
1827 | return r; | |
1828 | ||
71eceff6 | 1829 | r = sym_crypt_token_json_set(cd, CRYPT_ANY_TOKEN, text); |
70a5db58 LP |
1830 | if (r < 0) |
1831 | return log_error_errno(r, "Failed to set LUKS JSON token: %m"); | |
1832 | ||
1833 | log_info("Writing user record as LUKS token completed."); | |
1834 | ||
1835 | if (ret) | |
1836 | *ret = TAKE_PTR(cd); | |
1837 | ||
1838 | return 0; | |
1839 | } | |
1840 | ||
70a5db58 LP |
1841 | static int make_partition_table( |
1842 | int fd, | |
81dde3d8 | 1843 | uint32_t sector_size, |
70a5db58 LP |
1844 | const char *label, |
1845 | sd_id128_t uuid, | |
1846 | uint64_t *ret_offset, | |
1847 | uint64_t *ret_size, | |
1848 | sd_id128_t *ret_disk_uuid) { | |
1849 | ||
1850 | _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *p = NULL, *q = NULL; | |
1851 | _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL; | |
1852 | _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; | |
f8cf3d19 | 1853 | _cleanup_free_ char *disk_uuid_as_string = NULL; |
04190cf1 | 1854 | uint64_t offset, size, first_lba, start, last_lba, end; |
70a5db58 | 1855 | sd_id128_t disk_uuid; |
70a5db58 LP |
1856 | int r; |
1857 | ||
1858 | assert(fd >= 0); | |
1859 | assert(label); | |
1860 | assert(ret_offset); | |
1861 | assert(ret_size); | |
1862 | ||
1863 | t = fdisk_new_parttype(); | |
1864 | if (!t) | |
1865 | return log_oom(); | |
1866 | ||
92e72028 | 1867 | r = fdisk_parttype_set_typestr(t, SD_GPT_USER_HOME_STR); |
70a5db58 LP |
1868 | if (r < 0) |
1869 | return log_error_errno(r, "Failed to initialize partition type: %m"); | |
1870 | ||
81dde3d8 | 1871 | r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c); |
70a5db58 LP |
1872 | if (r < 0) |
1873 | return log_error_errno(r, "Failed to open device: %m"); | |
1874 | ||
1875 | r = fdisk_create_disklabel(c, "gpt"); | |
1876 | if (r < 0) | |
80ace4f2 | 1877 | return log_error_errno(r, "Failed to create GPT disk label: %m"); |
70a5db58 LP |
1878 | |
1879 | p = fdisk_new_partition(); | |
1880 | if (!p) | |
1881 | return log_oom(); | |
1882 | ||
1883 | r = fdisk_partition_set_type(p, t); | |
1884 | if (r < 0) | |
1885 | return log_error_errno(r, "Failed to set partition type: %m"); | |
1886 | ||
70a5db58 LP |
1887 | r = fdisk_partition_partno_follow_default(p, 1); |
1888 | if (r < 0) | |
1889 | return log_error_errno(r, "Failed to place partition at first free partition index: %m"); | |
1890 | ||
04190cf1 LP |
1891 | first_lba = fdisk_get_first_lba(c); /* Boundary where usable space starts */ |
1892 | assert(first_lba <= UINT64_MAX/512); | |
1893 | start = DISK_SIZE_ROUND_UP(first_lba * 512); /* Round up to multiple of 4K */ | |
1894 | ||
31ea1bfe LP |
1895 | log_debug("Starting partition at offset %" PRIu64, start); |
1896 | ||
04190cf1 LP |
1897 | if (start == UINT64_MAX) |
1898 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Overflow while rounding up start LBA."); | |
1899 | ||
1900 | last_lba = fdisk_get_last_lba(c); /* One sector before boundary where usable space ends */ | |
1901 | assert(last_lba < UINT64_MAX/512); | |
1902 | end = DISK_SIZE_ROUND_DOWN((last_lba + 1) * 512); /* Round down to multiple of 4K */ | |
1903 | ||
1904 | if (end <= start) | |
1905 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Resulting partition size zero or negative."); | |
1906 | ||
1907 | r = fdisk_partition_set_start(p, start / 512); | |
70a5db58 | 1908 | if (r < 0) |
04190cf1 LP |
1909 | return log_error_errno(r, "Failed to place partition at offset %" PRIu64 ": %m", start); |
1910 | ||
1911 | r = fdisk_partition_set_size(p, (end - start) / 512); | |
1912 | if (r < 0) | |
1913 | return log_error_errno(r, "Failed to end partition at offset %" PRIu64 ": %m", end); | |
70a5db58 LP |
1914 | |
1915 | r = fdisk_partition_set_name(p, label); | |
1916 | if (r < 0) | |
1917 | return log_error_errno(r, "Failed to set partition name: %m"); | |
1918 | ||
b7416360 | 1919 | r = fdisk_partition_set_uuid(p, SD_ID128_TO_UUID_STRING(uuid)); |
70a5db58 LP |
1920 | if (r < 0) |
1921 | return log_error_errno(r, "Failed to set partition UUID: %m"); | |
1922 | ||
1923 | r = fdisk_add_partition(c, p, NULL); | |
1924 | if (r < 0) | |
1925 | return log_error_errno(r, "Failed to add partition: %m"); | |
1926 | ||
1927 | r = fdisk_write_disklabel(c); | |
1928 | if (r < 0) | |
1929 | return log_error_errno(r, "Failed to write disk label: %m"); | |
1930 | ||
1931 | r = fdisk_get_disklabel_id(c, &disk_uuid_as_string); | |
1932 | if (r < 0) | |
1933 | return log_error_errno(r, "Failed to determine disk label UUID: %m"); | |
1934 | ||
1935 | r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid); | |
1936 | if (r < 0) | |
1937 | return log_error_errno(r, "Failed to parse disk label UUID: %m"); | |
1938 | ||
1939 | r = fdisk_get_partition(c, 0, &q); | |
1940 | if (r < 0) | |
1941 | return log_error_errno(r, "Failed to read created partition metadata: %m"); | |
1942 | ||
1943 | assert(fdisk_partition_has_start(q)); | |
1944 | offset = fdisk_partition_get_start(q); | |
1945 | if (offset > UINT64_MAX / 512U) | |
1946 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition offset too large."); | |
1947 | ||
1948 | assert(fdisk_partition_has_size(q)); | |
1949 | size = fdisk_partition_get_size(q); | |
1950 | if (size > UINT64_MAX / 512U) | |
1951 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition size too large."); | |
1952 | ||
1953 | *ret_offset = offset * 512U; | |
1954 | *ret_size = size * 512U; | |
1955 | *ret_disk_uuid = disk_uuid; | |
1956 | ||
1957 | return 0; | |
1958 | } | |
1959 | ||
1960 | static bool supported_fs_size(const char *fstype, uint64_t host_size) { | |
1961 | uint64_t m; | |
1962 | ||
1963 | m = minimal_size_by_fs_name(fstype); | |
1964 | if (m == UINT64_MAX) | |
1965 | return false; | |
1966 | ||
1967 | return host_size >= m; | |
1968 | } | |
1969 | ||
1970 | static int wait_for_devlink(const char *path) { | |
254d1313 | 1971 | _cleanup_close_ int inotify_fd = -EBADF; |
70a5db58 LP |
1972 | usec_t until; |
1973 | int r; | |
1974 | ||
201632e3 | 1975 | /* let's wait for a device link to show up in /dev, with a timeout. This is good to do since we |
70a5db58 LP |
1976 | * return a /dev/disk/by-uuid/… link to our callers and they likely want to access it right-away, |
1977 | * hence let's wait until udev has caught up with our changes, and wait for the symlink to be | |
1978 | * created. */ | |
1979 | ||
1980 | until = usec_add(now(CLOCK_MONOTONIC), 45 * USEC_PER_SEC); | |
1981 | ||
1982 | for (;;) { | |
1983 | _cleanup_free_ char *dn = NULL; | |
1984 | usec_t w; | |
1985 | ||
1986 | if (laccess(path, F_OK) < 0) { | |
1987 | if (errno != ENOENT) | |
1988 | return log_error_errno(errno, "Failed to determine whether %s exists: %m", path); | |
1989 | } else | |
1990 | return 0; /* Found it */ | |
1991 | ||
1992 | if (inotify_fd < 0) { | |
1993 | /* We need to wait for the device symlink to show up, let's create an inotify watch for it */ | |
1994 | inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); | |
1995 | if (inotify_fd < 0) | |
1996 | return log_error_errno(errno, "Failed to allocate inotify fd: %m"); | |
1997 | } | |
1998 | ||
45519d13 LP |
1999 | r = path_extract_directory(path, &dn); |
2000 | if (r < 0) | |
2001 | return log_error_errno(r, "Failed to extract directory from device node path '%s': %m", path); | |
70a5db58 | 2002 | for (;;) { |
45519d13 | 2003 | _cleanup_free_ char *ndn = NULL; |
70a5db58 LP |
2004 | |
2005 | log_info("Watching %s", dn); | |
2006 | ||
2007 | if (inotify_add_watch(inotify_fd, dn, IN_CREATE|IN_MOVED_TO|IN_ONLYDIR|IN_DELETE_SELF|IN_MOVE_SELF) < 0) { | |
2008 | if (errno != ENOENT) | |
2009 | return log_error_errno(errno, "Failed to add watch on %s: %m", dn); | |
2010 | } else | |
2011 | break; | |
2012 | ||
45519d13 LP |
2013 | r = path_extract_directory(dn, &ndn); |
2014 | if (r == -EADDRNOTAVAIL) /* Arrived at the top? */ | |
70a5db58 | 2015 | break; |
45519d13 LP |
2016 | if (r < 0) |
2017 | return log_error_errno(r, "Failed to extract directory from device node path '%s': %m", dn); | |
70a5db58 | 2018 | |
45519d13 | 2019 | free_and_replace(dn, ndn); |
70a5db58 LP |
2020 | } |
2021 | ||
2022 | w = now(CLOCK_MONOTONIC); | |
2023 | if (w >= until) | |
2024 | return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Device link %s still hasn't shown up, giving up.", path); | |
2025 | ||
f3d9278f LP |
2026 | r = fd_wait_for_event(inotify_fd, POLLIN, until - w); |
2027 | if (r < 0) { | |
2028 | if (ERRNO_IS_TRANSIENT(r)) | |
2029 | continue; | |
70a5db58 | 2030 | return log_error_errno(r, "Failed to watch inotify: %m"); |
f3d9278f | 2031 | } |
70a5db58 LP |
2032 | |
2033 | (void) flush_fd(inotify_fd); | |
2034 | } | |
2035 | } | |
2036 | ||
716bc200 LP |
2037 | static int calculate_initial_image_size(UserRecord *h, int image_fd, const char *fstype, uint64_t *ret) { |
2038 | uint64_t upper_boundary, lower_boundary; | |
70a5db58 | 2039 | struct statfs sfs; |
70a5db58 LP |
2040 | |
2041 | assert(h); | |
716bc200 | 2042 | assert(image_fd >= 0); |
70a5db58 LP |
2043 | assert(ret); |
2044 | ||
716bc200 LP |
2045 | if (fstatfs(image_fd, &sfs) < 0) |
2046 | return log_error_errno(errno, "statfs() on image failed: %m"); | |
70a5db58 | 2047 | |
716bc200 | 2048 | upper_boundary = DISK_SIZE_ROUND_DOWN((uint64_t) sfs.f_bsize * sfs.f_bavail); |
70a5db58 | 2049 | |
716bc200 LP |
2050 | if (h->disk_size != UINT64_MAX) |
2051 | *ret = MIN(DISK_SIZE_ROUND_DOWN(h->disk_size), upper_boundary); | |
2052 | else if (h->disk_size_relative == UINT64_MAX) { | |
70a5db58 | 2053 | |
716bc200 | 2054 | if (upper_boundary > UINT64_MAX / USER_DISK_SIZE_DEFAULT_PERCENT) |
70a5db58 LP |
2055 | return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Disk size too large."); |
2056 | ||
716bc200 | 2057 | *ret = DISK_SIZE_ROUND_DOWN(upper_boundary * USER_DISK_SIZE_DEFAULT_PERCENT / 100); |
70a5db58 LP |
2058 | |
2059 | log_info("Sizing home to %u%% of available disk space, which is %s.", | |
2060 | USER_DISK_SIZE_DEFAULT_PERCENT, | |
2b59bf51 | 2061 | FORMAT_BYTES(*ret)); |
70a5db58 | 2062 | } else { |
716bc200 | 2063 | *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) upper_boundary * (double) CLAMP(h->disk_size_relative, 0U, UINT32_MAX) / (double) UINT32_MAX)); |
70a5db58 LP |
2064 | |
2065 | log_info("Sizing home to %" PRIu64 ".%01" PRIu64 "%% of available disk space, which is %s.", | |
2066 | (h->disk_size_relative * 100) / UINT32_MAX, | |
2067 | ((h->disk_size_relative * 1000) / UINT32_MAX) % 10, | |
2b59bf51 | 2068 | FORMAT_BYTES(*ret)); |
70a5db58 LP |
2069 | } |
2070 | ||
716bc200 | 2071 | lower_boundary = minimal_size_by_fs_name(fstype); |
31ea1bfe LP |
2072 | if (lower_boundary != UINT64_MAX) { |
2073 | assert(GPT_LUKS2_OVERHEAD < UINT64_MAX - lower_boundary); | |
2074 | lower_boundary += GPT_LUKS2_OVERHEAD; | |
2075 | } | |
716bc200 LP |
2076 | if (lower_boundary == UINT64_MAX || lower_boundary < USER_DISK_SIZE_MIN) |
2077 | lower_boundary = USER_DISK_SIZE_MIN; | |
2078 | ||
2079 | if (*ret < lower_boundary) | |
2080 | *ret = lower_boundary; | |
70a5db58 LP |
2081 | |
2082 | return 0; | |
2083 | } | |
2084 | ||
e46f877c LP |
2085 | static int home_truncate( |
2086 | UserRecord *h, | |
2087 | int fd, | |
e46f877c LP |
2088 | uint64_t size) { |
2089 | ||
2090 | bool trunc; | |
2091 | int r; | |
2092 | ||
2093 | assert(h); | |
2094 | assert(fd >= 0); | |
e46f877c LP |
2095 | |
2096 | trunc = user_record_luks_discard(h); | |
2097 | if (!trunc) { | |
2098 | r = fallocate(fd, 0, 0, size); | |
2099 | if (r < 0 && ERRNO_IS_NOT_SUPPORTED(errno)) { | |
2100 | /* Some file systems do not support fallocate(), let's gracefully degrade | |
2101 | * (ZFS, reiserfs, …) and fall back to truncation */ | |
2102 | log_notice_errno(errno, "Backing file system does not support fallocate(), falling back to ftruncate(), i.e. implicitly using non-discard mode."); | |
2103 | trunc = true; | |
2104 | } | |
2105 | } | |
2106 | ||
2107 | if (trunc) | |
2108 | r = ftruncate(fd, size); | |
2109 | ||
2110 | if (r < 0) { | |
2111 | if (ERRNO_IS_DISK_SPACE(errno)) { | |
2b02eb05 | 2112 | log_debug_errno(errno, "Not enough disk space to allocate home of size %s.", FORMAT_BYTES(size)); |
e46f877c LP |
2113 | return -ENOSPC; /* make recognizable */ |
2114 | } | |
2115 | ||
2b02eb05 | 2116 | return log_error_errno(errno, "Failed to truncate home image: %m"); |
e46f877c LP |
2117 | } |
2118 | ||
2b02eb05 | 2119 | return !trunc; /* Return == 0 if we managed to truncate, > 0 if we managed to allocate */ |
e46f877c LP |
2120 | } |
2121 | ||
8f30c00c AD |
2122 | static int mkfs_options_for_fstype(const char *fstype, char ***ret) { |
2123 | _cleanup_(strv_freep) char **l = NULL; | |
2124 | const char *e; | |
2125 | char *n; | |
2126 | ||
2127 | assert(fstype); | |
2128 | ||
2129 | n = strjoina("SYSTEMD_HOME_MKFS_OPTIONS_", fstype); | |
2130 | e = getenv(ascii_strupper(n)); | |
2131 | if (e) { | |
2132 | l = strv_split(e, NULL); | |
2133 | if (!l) | |
2134 | return -ENOMEM; | |
2135 | } | |
2136 | ||
2137 | *ret = TAKE_PTR(l); | |
2138 | return 0; | |
2139 | } | |
2140 | ||
70a5db58 LP |
2141 | int home_create_luks( |
2142 | UserRecord *h, | |
bc5890c6 | 2143 | HomeSetup *setup, |
37a1bf7f | 2144 | const PasswordCache *cache, |
70a5db58 LP |
2145 | char **effective_passwords, |
2146 | UserRecord **ret_home) { | |
2147 | ||
32dda527 | 2148 | _cleanup_free_ char *subdir = NULL, *disk_uuid_path = NULL; |
aff81b18 ZJS |
2149 | uint64_t encrypted_size, |
2150 | host_size = 0, partition_offset = 0, partition_size = 0; /* Unnecessary initialization to appease gcc */ | |
70a5db58 LP |
2151 | _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; |
2152 | sd_id128_t partition_uuid, fs_uuid, luks_uuid, disk_uuid; | |
254d1313 | 2153 | _cleanup_close_ int mount_fd = -EBADF; |
70a5db58 LP |
2154 | const char *fstype, *ip; |
2155 | struct statfs sfs; | |
2156 | int r; | |
8f30c00c | 2157 | _cleanup_strv_free_ char **extra_mkfs_options = NULL; |
70a5db58 LP |
2158 | |
2159 | assert(h); | |
2160 | assert(h->storage < 0 || h->storage == USER_LUKS); | |
bc5890c6 | 2161 | assert(setup); |
32dda527 | 2162 | assert(!setup->temporary_image_path); |
a2bc3978 | 2163 | assert(setup->image_fd < 0); |
70a5db58 LP |
2164 | assert(ret_home); |
2165 | ||
71eceff6 LP |
2166 | r = dlopen_cryptsetup(); |
2167 | if (r < 0) | |
2168 | return r; | |
2169 | ||
70a5db58 LP |
2170 | assert_se(ip = user_record_image_path(h)); |
2171 | ||
2172 | fstype = user_record_file_system_type(h); | |
2173 | if (!supported_fstype(fstype)) | |
f2ba663e | 2174 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Unsupported file system type: %s", fstype); |
70a5db58 | 2175 | |
a512e330 LP |
2176 | r = mkfs_exists(fstype); |
2177 | if (r < 0) | |
2178 | return log_error_errno(r, "Failed to check if mkfs binary for %s exists: %m", fstype); | |
2179 | if (r == 0) { | |
2180 | if (h->file_system_type || streq(fstype, "ext4") || !supported_fstype("ext4")) | |
2181 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for file system type %s does not exist.", fstype); | |
2182 | ||
2183 | /* If the record does not explicitly declare a file system to use, and the compiled-in | |
2184 | * default does not actually exist, than do an automatic fallback onto ext4, as the baseline | |
2185 | * fs of Linux. We won't search for a working fs type here beyond ext4, i.e. nothing fancier | |
2186 | * than a single, conservative fallback to baseline. This should be useful in minimal | |
2187 | * environments where mkfs.btrfs or so are not made available, but mkfs.ext4 as Linux' most | |
2188 | * boring, most basic fs is. */ | |
2189 | log_info("Formatting tool for compiled-in default file system %s not available, falling back to ext4 instead.", fstype); | |
2190 | fstype = "ext4"; | |
2191 | } | |
2192 | ||
70a5db58 LP |
2193 | if (sd_id128_is_null(h->partition_uuid)) { |
2194 | r = sd_id128_randomize(&partition_uuid); | |
2195 | if (r < 0) | |
2196 | return log_error_errno(r, "Failed to acquire partition UUID: %m"); | |
2197 | } else | |
2198 | partition_uuid = h->partition_uuid; | |
2199 | ||
2200 | if (sd_id128_is_null(h->luks_uuid)) { | |
2201 | r = sd_id128_randomize(&luks_uuid); | |
2202 | if (r < 0) | |
2203 | return log_error_errno(r, "Failed to acquire LUKS UUID: %m"); | |
2204 | } else | |
2205 | luks_uuid = h->luks_uuid; | |
2206 | ||
2207 | if (sd_id128_is_null(h->file_system_uuid)) { | |
2208 | r = sd_id128_randomize(&fs_uuid); | |
2209 | if (r < 0) | |
2210 | return log_error_errno(r, "Failed to acquire file system UUID: %m"); | |
2211 | } else | |
2212 | fs_uuid = h->file_system_uuid; | |
2213 | ||
f7800049 | 2214 | r = make_dm_names(h, setup); |
70a5db58 LP |
2215 | if (r < 0) |
2216 | return r; | |
2217 | ||
f7800049 | 2218 | r = access(setup->dm_node, F_OK); |
70a5db58 LP |
2219 | if (r < 0) { |
2220 | if (errno != ENOENT) | |
f7800049 | 2221 | return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup->dm_node); |
70a5db58 | 2222 | } else |
f7800049 | 2223 | return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup->dm_node); |
70a5db58 LP |
2224 | |
2225 | if (path_startswith(ip, "/dev/")) { | |
2226 | _cleanup_free_ char *sysfs = NULL; | |
2227 | uint64_t block_device_size; | |
2228 | struct stat st; | |
2229 | ||
2230 | /* Let's place the home directory on a real device, i.e. an USB stick or such */ | |
2231 | ||
172e3817 | 2232 | setup->image_fd = open_image_file(h, ip, &st); |
a2bc3978 | 2233 | if (setup->image_fd < 0) |
172e3817 | 2234 | return setup->image_fd; |
70a5db58 | 2235 | |
70a5db58 LP |
2236 | if (!S_ISBLK(st.st_mode)) |
2237 | return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing."); | |
2238 | ||
ec61371f | 2239 | if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0) |
70a5db58 LP |
2240 | return log_oom(); |
2241 | if (access(sysfs, F_OK) < 0) { | |
2242 | if (errno != ENOENT) | |
2243 | return log_error_errno(errno, "Failed to check whether %s exists: %m", sysfs); | |
2244 | } else | |
2245 | return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Operating on partitions is currently not supported, sorry. Please specify a top-level block device."); | |
2246 | ||
a2bc3978 | 2247 | if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ |
70a5db58 LP |
2248 | return log_error_errno(errno, "Failed to lock block device %s: %m", ip); |
2249 | ||
a2bc3978 | 2250 | if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0) |
70a5db58 LP |
2251 | return log_error_errno(errno, "Failed to read block device size: %m"); |
2252 | ||
2253 | if (h->disk_size == UINT64_MAX) { | |
2254 | ||
2255 | /* If a relative disk size is requested, apply it relative to the block device size */ | |
2256 | if (h->disk_size_relative < UINT32_MAX) | |
2257 | host_size = CLAMP(DISK_SIZE_ROUND_DOWN(block_device_size * h->disk_size_relative / UINT32_MAX), | |
2258 | USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX); | |
2259 | else | |
2260 | host_size = block_device_size; /* Otherwise, take the full device */ | |
2261 | ||
2262 | } else if (h->disk_size > block_device_size) | |
2263 | return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Selected disk size larger than backing block device, refusing."); | |
2264 | else | |
2265 | host_size = DISK_SIZE_ROUND_DOWN(h->disk_size); | |
2266 | ||
31ea1bfe | 2267 | if (!supported_fs_size(fstype, LESS_BY(host_size, GPT_LUKS2_OVERHEAD))) |
f2ba663e LP |
2268 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), |
2269 | "Selected file system size too small for %s.", fstype); | |
70a5db58 LP |
2270 | |
2271 | /* After creation we should reference this partition by its UUID instead of the block | |
2272 | * device. That's preferable since the user might have specified a device node such as | |
2273 | * /dev/sdb to us, which might look very different when replugged. */ | |
2274 | if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0) | |
2275 | return log_oom(); | |
2276 | ||
28a7f106 LP |
2277 | if (user_record_luks_discard(h) || user_record_luks_offline_discard(h)) { |
2278 | /* If we want online or offline discard, discard once before we start using things. */ | |
2279 | ||
a2bc3978 | 2280 | if (ioctl(setup->image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0) |
70a5db58 LP |
2281 | log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno, |
2282 | "Failed to issue full-device BLKDISCARD on device, ignoring: %m"); | |
2283 | else | |
2284 | log_info("Full device discard completed."); | |
2285 | } | |
2286 | } else { | |
716bc200 | 2287 | _cleanup_free_ char *t = NULL; |
70a5db58 | 2288 | |
716bc200 | 2289 | r = mkdir_parents(ip, 0755); |
70a5db58 | 2290 | if (r < 0) |
716bc200 | 2291 | return log_error_errno(r, "Failed to create parent directory of %s: %m", ip); |
70a5db58 | 2292 | |
32dda527 | 2293 | r = tempfn_random(ip, "homework", &t); |
70a5db58 LP |
2294 | if (r < 0) |
2295 | return log_error_errno(r, "Failed to derive temporary file name for %s: %m", ip); | |
2296 | ||
a2bc3978 LP |
2297 | setup->image_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600); |
2298 | if (setup->image_fd < 0) | |
32dda527 | 2299 | return log_error_errno(errno, "Failed to create home image %s: %m", t); |
70a5db58 | 2300 | |
32dda527 | 2301 | setup->temporary_image_path = TAKE_PTR(t); |
70a5db58 | 2302 | |
8ccb69ae LP |
2303 | r = chattr_full(t, setup->image_fd, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE); |
2304 | if (r < 0 && r != -ENOANO) /* ENOANO → some bits didn't work; which we skip logging about because chattr_full() already debug logs about those flags */ | |
64dc138d | 2305 | log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r, |
32dda527 | 2306 | "Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path); |
70a5db58 | 2307 | |
716bc200 LP |
2308 | r = calculate_initial_image_size(h, setup->image_fd, fstype, &host_size); |
2309 | if (r < 0) | |
2310 | return r; | |
2311 | ||
2312 | r = resize_image_loop(h, setup, 0, host_size, &host_size); | |
e46f877c | 2313 | if (r < 0) |
ebcdfc79 | 2314 | return r; |
70a5db58 LP |
2315 | |
2316 | log_info("Allocating image file completed."); | |
2317 | } | |
2318 | ||
2319 | r = make_partition_table( | |
a2bc3978 | 2320 | setup->image_fd, |
81dde3d8 | 2321 | user_record_luks_sector_size(h), |
70a5db58 LP |
2322 | user_record_user_name_and_realm(h), |
2323 | partition_uuid, | |
2324 | &partition_offset, | |
2325 | &partition_size, | |
2326 | &disk_uuid); | |
2327 | if (r < 0) | |
ebcdfc79 | 2328 | return r; |
70a5db58 LP |
2329 | |
2330 | log_info("Writing of partition table completed."); | |
2331 | ||
22ee78a8 LP |
2332 | r = loop_device_make( |
2333 | setup->image_fd, | |
2334 | O_RDWR, | |
2335 | partition_offset, | |
2336 | partition_size, | |
2337 | user_record_luks_sector_size(h), | |
2338 | 0, | |
2339 | LOCK_EX, | |
2340 | &setup->loop); | |
70a5db58 LP |
2341 | if (r < 0) { |
2342 | if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container | |
2343 | * or similar and loopback bock devices are not available, return a | |
2344 | * recognizable error in this case. */ | |
2345 | log_error_errno(r, "Loopback block device support is not available on this system."); | |
ebcdfc79 | 2346 | return -ENOLINK; /* Make recognizable */ |
70a5db58 LP |
2347 | } |
2348 | ||
ebcdfc79 | 2349 | return log_error_errno(r, "Failed to set up loopback device for %s: %m", setup->temporary_image_path); |
70a5db58 LP |
2350 | } |
2351 | ||
e4d1e79b | 2352 | log_info("Setting up loopback device %s completed.", setup->loop->node ?: ip); |
70a5db58 | 2353 | |
e4d1e79b | 2354 | r = luks_format(setup->loop->node, |
f7800049 | 2355 | setup->dm_name, |
70a5db58 LP |
2356 | luks_uuid, |
2357 | user_record_user_name_and_realm(h), | |
7b78db28 | 2358 | cache, |
70a5db58 | 2359 | effective_passwords, |
28a7f106 | 2360 | user_record_luks_discard(h) || user_record_luks_offline_discard(h), |
70a5db58 | 2361 | h, |
f7800049 | 2362 | &setup->crypt_device); |
70a5db58 | 2363 | if (r < 0) |
ebcdfc79 | 2364 | return r; |
70a5db58 | 2365 | |
f7800049 | 2366 | setup->undo_dm = true; |
70a5db58 | 2367 | |
f7800049 | 2368 | r = block_get_size_by_path(setup->dm_node, &encrypted_size); |
ebcdfc79 LP |
2369 | if (r < 0) |
2370 | return log_error_errno(r, "Failed to get encrypted block device size: %m"); | |
70a5db58 | 2371 | |
f7800049 | 2372 | log_info("Setting up LUKS device %s completed.", setup->dm_node); |
70a5db58 | 2373 | |
8f30c00c AD |
2374 | r = mkfs_options_for_fstype(fstype, &extra_mkfs_options); |
2375 | if (r < 0) | |
2376 | return log_error_errno(r, "Failed to determine mkfs command line options for '%s': %m", fstype); | |
e1878ef7 | 2377 | r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), NULL, fs_uuid, user_record_luks_discard(h), 0, extra_mkfs_options); |
70a5db58 | 2378 | if (r < 0) |
ebcdfc79 | 2379 | return r; |
70a5db58 LP |
2380 | |
2381 | log_info("Formatting file system completed."); | |
2382 | ||
2e0001c2 | 2383 | r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); |
70a5db58 | 2384 | if (r < 0) |
ebcdfc79 | 2385 | return r; |
70a5db58 | 2386 | |
203f06aa | 2387 | setup->undo_mount = true; |
70a5db58 | 2388 | |
498abadb | 2389 | subdir = path_join(HOME_RUNTIME_WORK_DIR, user_record_user_name_and_realm(h)); |
ebcdfc79 LP |
2390 | if (!subdir) |
2391 | return log_oom(); | |
70a5db58 | 2392 | |
0be94a19 LP |
2393 | /* Prefer using a btrfs subvolume if we can, fall back to directory otherwise */ |
2394 | r = btrfs_subvol_make_fallback(subdir, 0700); | |
ebcdfc79 LP |
2395 | if (r < 0) |
2396 | return log_error_errno(r, "Failed to create user directory in mounted image file: %m"); | |
70a5db58 | 2397 | |
bc5890c6 | 2398 | setup->root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); |
ebcdfc79 LP |
2399 | if (setup->root_fd < 0) |
2400 | return log_error_errno(errno, "Failed to open user directory in mounted image file: %m"); | |
70a5db58 | 2401 | |
1147c538 LP |
2402 | (void) home_shift_uid(setup->root_fd, NULL, UID_NOBODY, h->uid, &mount_fd); |
2403 | ||
2404 | if (mount_fd >= 0) { | |
2405 | /* If we have established a new mount, then we can use that as new root fd to our home directory. */ | |
2406 | safe_close(setup->root_fd); | |
2407 | ||
2408 | setup->root_fd = fd_reopen(mount_fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY); | |
2409 | if (setup->root_fd < 0) | |
2410 | return log_error_errno(setup->root_fd, "Unable to convert mount fd into proper directory fd: %m"); | |
2411 | ||
2412 | mount_fd = safe_close(mount_fd); | |
2413 | } | |
2414 | ||
bc5890c6 | 2415 | r = home_populate(h, setup->root_fd); |
70a5db58 | 2416 | if (r < 0) |
ebcdfc79 | 2417 | return r; |
70a5db58 | 2418 | |
bc5890c6 | 2419 | r = home_sync_and_statfs(setup->root_fd, &sfs); |
70a5db58 | 2420 | if (r < 0) |
ebcdfc79 | 2421 | return r; |
70a5db58 | 2422 | |
bfc0cc1a | 2423 | r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE, &new_home); |
ebcdfc79 LP |
2424 | if (r < 0) |
2425 | return log_error_errno(r, "Failed to clone record: %m"); | |
70a5db58 LP |
2426 | |
2427 | r = user_record_add_binding( | |
2428 | new_home, | |
2429 | USER_LUKS, | |
2430 | disk_uuid_path ?: ip, | |
2431 | partition_uuid, | |
2432 | luks_uuid, | |
2433 | fs_uuid, | |
f7800049 LP |
2434 | sym_crypt_get_cipher(setup->crypt_device), |
2435 | sym_crypt_get_cipher_mode(setup->crypt_device), | |
2436 | luks_volume_key_size_convert(setup->crypt_device), | |
70a5db58 LP |
2437 | fstype, |
2438 | NULL, | |
2439 | h->uid, | |
2440 | (gid_t) h->uid); | |
ebcdfc79 LP |
2441 | if (r < 0) |
2442 | return log_error_errno(r, "Failed to add binding to record: %m"); | |
70a5db58 | 2443 | |
28a7f106 | 2444 | if (user_record_luks_offline_discard(h)) { |
bc5890c6 | 2445 | r = run_fitrim(setup->root_fd); |
28a7f106 | 2446 | if (r < 0) |
ebcdfc79 | 2447 | return r; |
28a7f106 LP |
2448 | } |
2449 | ||
bc5890c6 | 2450 | setup->root_fd = safe_close(setup->root_fd); |
70a5db58 | 2451 | |
203f06aa | 2452 | r = home_setup_undo_mount(setup, LOG_ERR); |
70a5db58 | 2453 | if (r < 0) |
ebcdfc79 | 2454 | return r; |
70a5db58 | 2455 | |
f7800049 LP |
2456 | r = home_setup_undo_dm(setup, LOG_ERR); |
2457 | if (r < 0) | |
ebcdfc79 | 2458 | return r; |
70a5db58 | 2459 | |
e4d1e79b | 2460 | setup->loop = loop_device_unref(setup->loop); |
70a5db58 | 2461 | |
28a7f106 | 2462 | if (!user_record_luks_offline_discard(h)) { |
a2bc3978 | 2463 | r= run_fallocate(setup->image_fd, NULL /* refresh stat() data */); |
28a7f106 | 2464 | if (r < 0) |
ebcdfc79 | 2465 | return r; |
28a7f106 LP |
2466 | } |
2467 | ||
a2a8a509 | 2468 | /* Sync everything to disk before we move things into place under the final name. */ |
a2bc3978 | 2469 | if (fsync(setup->image_fd) < 0) |
ebcdfc79 | 2470 | return log_error_errno(r, "Failed to synchronize image to disk: %m"); |
a2a8a509 | 2471 | |
70a5db58 | 2472 | if (disk_uuid_path) |
bb562024 | 2473 | /* Reread partition table if this is a block device */ |
a2bc3978 | 2474 | (void) ioctl(setup->image_fd, BLKRRPART, 0); |
a2a8a509 | 2475 | else { |
bb562024 LP |
2476 | assert(setup->temporary_image_path); |
2477 | ||
2478 | if (rename(setup->temporary_image_path, ip) < 0) | |
2479 | return log_error_errno(errno, "Failed to rename image file: %m"); | |
2480 | ||
2481 | setup->temporary_image_path = mfree(setup->temporary_image_path); | |
2482 | ||
69e3234d | 2483 | /* If we operate on a file, sync the containing directory too. */ |
a2bc3978 | 2484 | r = fsync_directory_of_file(setup->image_fd); |
ebcdfc79 LP |
2485 | if (r < 0) |
2486 | return log_error_errno(r, "Failed to synchronize directory of image file to disk: %m"); | |
bb562024 LP |
2487 | |
2488 | log_info("Moved image file into place."); | |
a2a8a509 | 2489 | } |
70a5db58 LP |
2490 | |
2491 | /* Let's close the image fd now. If we are operating on a real block device this will release the BSD | |
2492 | * lock that ensures udev doesn't interfere with what we are doing */ | |
a2bc3978 | 2493 | setup->image_fd = safe_close(setup->image_fd); |
70a5db58 | 2494 | |
70a5db58 LP |
2495 | if (disk_uuid_path) |
2496 | (void) wait_for_devlink(disk_uuid_path); | |
2497 | ||
716bc200 | 2498 | log_info("Creation completed."); |
70a5db58 LP |
2499 | |
2500 | print_size_summary(host_size, encrypted_size, &sfs); | |
2501 | ||
31ea1bfe LP |
2502 | log_debug("GPT + LUKS2 overhead is %" PRIu64 " (expected %" PRIu64 ")", host_size - encrypted_size, GPT_LUKS2_OVERHEAD); |
2503 | ||
70a5db58 LP |
2504 | *ret_home = TAKE_PTR(new_home); |
2505 | return 0; | |
70a5db58 LP |
2506 | } |
2507 | ||
e1ab6635 | 2508 | int home_get_state_luks(UserRecord *h, HomeSetup *setup) { |
70a5db58 LP |
2509 | int r; |
2510 | ||
2511 | assert(h); | |
2512 | assert(setup); | |
2513 | ||
f7800049 | 2514 | r = make_dm_names(h, setup); |
70a5db58 LP |
2515 | if (r < 0) |
2516 | return r; | |
2517 | ||
f7800049 | 2518 | r = access(setup->dm_node, F_OK); |
70a5db58 | 2519 | if (r < 0 && errno != ENOENT) |
f7800049 | 2520 | return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup->dm_node); |
70a5db58 LP |
2521 | |
2522 | return r >= 0; | |
2523 | } | |
2524 | ||
2525 | enum { | |
2526 | CAN_RESIZE_ONLINE, | |
2527 | CAN_RESIZE_OFFLINE, | |
2528 | }; | |
2529 | ||
2530 | static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) { | |
2531 | struct statfs sfs; | |
2532 | ||
2533 | assert(fd >= 0); | |
2534 | ||
2535 | /* Filter out bogus requests early */ | |
2536 | if (old_size == 0 || old_size == UINT64_MAX || | |
2537 | new_size == 0 || new_size == UINT64_MAX) | |
2538 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid resize parameters."); | |
2539 | ||
2540 | if ((old_size & 511) != 0 || (new_size & 511) != 0) | |
2541 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Resize parameters not multiple of 512."); | |
2542 | ||
2543 | if (fstatfs(fd, &sfs) < 0) | |
2544 | return log_error_errno(errno, "Failed to fstatfs() file system: %m"); | |
2545 | ||
2546 | if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) { | |
2547 | ||
2548 | if (new_size < BTRFS_MINIMAL_SIZE) | |
2549 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for btrfs (needs to be 256M at least."); | |
2550 | ||
2551 | /* btrfs can grow and shrink online */ | |
2552 | ||
2553 | } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) { | |
2554 | ||
2555 | if (new_size < XFS_MINIMAL_SIZE) | |
2556 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least)."); | |
2557 | ||
2558 | /* XFS can grow, but not shrink */ | |
2559 | if (new_size < old_size) | |
2560 | return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Shrinking this type of file system is not supported."); | |
2561 | ||
2562 | } else if (is_fs_type(&sfs, EXT4_SUPER_MAGIC)) { | |
2563 | ||
2564 | if (new_size < EXT4_MINIMAL_SIZE) | |
2565 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for ext4 (needs to be 1M at least)."); | |
2566 | ||
2567 | /* ext4 can grow online, and shrink offline */ | |
2568 | if (new_size < old_size) | |
2569 | return CAN_RESIZE_OFFLINE; | |
2570 | ||
2571 | } else | |
2572 | return log_error_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT), "Resizing this type of file system is not supported."); | |
2573 | ||
2574 | return CAN_RESIZE_ONLINE; | |
2575 | } | |
2576 | ||
2e0001c2 LP |
2577 | static int ext4_offline_resize_fs( |
2578 | HomeSetup *setup, | |
2579 | uint64_t new_size, | |
2580 | bool discard, | |
2581 | unsigned long flags, | |
2582 | const char *extra_mount_options) { | |
2583 | ||
70a5db58 LP |
2584 | _cleanup_free_ char *size_str = NULL; |
2585 | bool re_open = false, re_mount = false; | |
2586 | pid_t resize_pid, fsck_pid; | |
2587 | int r, exit_status; | |
2588 | ||
2589 | assert(setup); | |
2590 | assert(setup->dm_node); | |
2591 | ||
2592 | /* First, unmount the file system */ | |
2593 | if (setup->root_fd >= 0) { | |
2594 | setup->root_fd = safe_close(setup->root_fd); | |
2595 | re_open = true; | |
2596 | } | |
2597 | ||
2598 | if (setup->undo_mount) { | |
203f06aa | 2599 | r = home_setup_undo_mount(setup, LOG_ERR); |
70a5db58 LP |
2600 | if (r < 0) |
2601 | return r; | |
2602 | ||
70a5db58 LP |
2603 | re_mount = true; |
2604 | } | |
2605 | ||
80ace4f2 | 2606 | log_info("Temporary unmounting of file system completed."); |
70a5db58 LP |
2607 | |
2608 | /* resize2fs requires that the file system is force checked first, do so. */ | |
fbdacd72 SB |
2609 | r = safe_fork("(e2fsck)", |
2610 | FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, | |
2611 | &fsck_pid); | |
70a5db58 LP |
2612 | if (r < 0) |
2613 | return r; | |
2614 | if (r == 0) { | |
2615 | /* Child */ | |
2616 | execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL); | |
fbdacd72 | 2617 | log_open(); |
70a5db58 LP |
2618 | log_error_errno(errno, "Failed to execute e2fsck: %m"); |
2619 | _exit(EXIT_FAILURE); | |
2620 | } | |
2621 | ||
2622 | exit_status = wait_for_terminate_and_check("e2fsck", fsck_pid, WAIT_LOG_ABNORMAL); | |
2623 | if (exit_status < 0) | |
2624 | return exit_status; | |
2625 | if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) { | |
2626 | log_warning("e2fsck failed with exit status %i.", exit_status); | |
2627 | ||
2628 | if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0) | |
2629 | return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing."); | |
2630 | ||
2631 | log_warning("Ignoring fsck error."); | |
2632 | } | |
2633 | ||
2634 | log_info("Forced file system check completed."); | |
2635 | ||
2636 | /* We use 512 sectors here, because resize2fs doesn't do byte sizes */ | |
2637 | if (asprintf(&size_str, "%" PRIu64 "s", new_size / 512) < 0) | |
2638 | return log_oom(); | |
2639 | ||
2640 | /* Resize the thing */ | |
fbdacd72 SB |
2641 | r = safe_fork("(e2resize)", |
2642 | FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, | |
2643 | &resize_pid); | |
70a5db58 LP |
2644 | if (r < 0) |
2645 | return r; | |
2646 | if (r == 0) { | |
2647 | /* Child */ | |
2648 | execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL); | |
fbdacd72 | 2649 | log_open(); |
70a5db58 LP |
2650 | log_error_errno(errno, "Failed to execute resize2fs: %m"); |
2651 | _exit(EXIT_FAILURE); | |
2652 | } | |
2653 | ||
2654 | log_info("Offline file system resize completed."); | |
2655 | ||
2656 | /* Re-establish mounts and reopen the directory */ | |
2657 | if (re_mount) { | |
2e0001c2 | 2658 | r = home_mount_node(setup->dm_node, "ext4", discard, flags, extra_mount_options); |
70a5db58 LP |
2659 | if (r < 0) |
2660 | return r; | |
2661 | ||
2662 | setup->undo_mount = true; | |
2663 | } | |
2664 | ||
2665 | if (re_open) { | |
498abadb | 2666 | setup->root_fd = open(HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); |
70a5db58 LP |
2667 | if (setup->root_fd < 0) |
2668 | return log_error_errno(errno, "Failed to reopen file system: %m"); | |
2669 | } | |
2670 | ||
2671 | log_info("File system mounted again."); | |
2672 | ||
2673 | return 0; | |
2674 | } | |
2675 | ||
2676 | static int prepare_resize_partition( | |
2677 | int fd, | |
2678 | uint64_t partition_offset, | |
2679 | uint64_t old_partition_size, | |
70a5db58 | 2680 | sd_id128_t *ret_disk_uuid, |
c8caf53c LP |
2681 | struct fdisk_table **ret_table, |
2682 | struct fdisk_partition **ret_partition) { | |
70a5db58 LP |
2683 | |
2684 | _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; | |
2685 | _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; | |
f8cf3d19 | 2686 | _cleanup_free_ char *disk_uuid_as_string = NULL; |
c8caf53c | 2687 | struct fdisk_partition *found = NULL; |
70a5db58 | 2688 | sd_id128_t disk_uuid; |
c8caf53c | 2689 | size_t n_partitions; |
70a5db58 LP |
2690 | int r; |
2691 | ||
2692 | assert(fd >= 0); | |
2693 | assert(ret_disk_uuid); | |
2694 | assert(ret_table); | |
2695 | ||
2696 | assert((partition_offset & 511) == 0); | |
2697 | assert((old_partition_size & 511) == 0); | |
70a5db58 | 2698 | assert(UINT64_MAX - old_partition_size >= partition_offset); |
70a5db58 LP |
2699 | |
2700 | if (partition_offset == 0) { | |
2701 | /* If the offset is at the beginning we assume no partition table, let's exit early. */ | |
2702 | log_debug("Not rewriting partition table, operating on naked device."); | |
2703 | *ret_disk_uuid = SD_ID128_NULL; | |
2704 | *ret_table = NULL; | |
3b1494ad | 2705 | *ret_partition = NULL; |
70a5db58 LP |
2706 | return 0; |
2707 | } | |
2708 | ||
81dde3d8 | 2709 | r = fdisk_new_context_fd(fd, /* read_only= */ false, UINT32_MAX, &c); |
70a5db58 LP |
2710 | if (r < 0) |
2711 | return log_error_errno(r, "Failed to open device: %m"); | |
2712 | ||
2713 | if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT)) | |
2714 | return log_error_errno(SYNTHETIC_ERRNO(ENOMEDIUM), "Disk has no GPT partition table."); | |
2715 | ||
2716 | r = fdisk_get_disklabel_id(c, &disk_uuid_as_string); | |
2717 | if (r < 0) | |
2718 | return log_error_errno(r, "Failed to acquire disk UUID: %m"); | |
2719 | ||
2720 | r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid); | |
2721 | if (r < 0) | |
2722 | return log_error_errno(r, "Failed parse disk UUID: %m"); | |
2723 | ||
2724 | r = fdisk_get_partitions(c, &t); | |
2725 | if (r < 0) | |
2726 | return log_error_errno(r, "Failed to acquire partition table: %m"); | |
2727 | ||
2728 | n_partitions = fdisk_table_get_nents(t); | |
78b4e9ed | 2729 | for (size_t i = 0; i < n_partitions; i++) { |
70a5db58 LP |
2730 | struct fdisk_partition *p; |
2731 | ||
2732 | p = fdisk_table_get_partition(t, i); | |
2733 | if (!p) | |
2734 | return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m"); | |
2735 | ||
2736 | if (fdisk_partition_is_used(p) <= 0) | |
2737 | continue; | |
2738 | if (fdisk_partition_has_start(p) <= 0 || fdisk_partition_has_size(p) <= 0 || fdisk_partition_has_end(p) <= 0) | |
2739 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found partition without a size."); | |
2740 | ||
2741 | if (fdisk_partition_get_start(p) == partition_offset / 512U && | |
2742 | fdisk_partition_get_size(p) == old_partition_size / 512U) { | |
2743 | ||
2744 | if (found) | |
2745 | return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Partition found twice, refusing."); | |
2746 | ||
c8caf53c LP |
2747 | found = p; |
2748 | } else if (fdisk_partition_get_end(p) > partition_offset / 512U) | |
2749 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't extend, not last partition in image."); | |
70a5db58 LP |
2750 | } |
2751 | ||
2752 | if (!found) | |
2753 | return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to find matching partition to resize."); | |
2754 | ||
70a5db58 | 2755 | *ret_disk_uuid = disk_uuid; |
c8caf53c LP |
2756 | *ret_table = TAKE_PTR(t); |
2757 | *ret_partition = found; | |
70a5db58 LP |
2758 | |
2759 | return 1; | |
2760 | } | |
2761 | ||
2762 | static int ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *userdata) { | |
2763 | char *result; | |
2764 | ||
2765 | assert(c); | |
2766 | ||
2767 | switch (fdisk_ask_get_type(ask)) { | |
2768 | ||
2769 | case FDISK_ASKTYPE_STRING: | |
2770 | result = new(char, 37); | |
2771 | if (!result) | |
2772 | return log_oom(); | |
2773 | ||
b7416360 | 2774 | fdisk_ask_string_set_result(ask, sd_id128_to_uuid_string(*(sd_id128_t*) userdata, result)); |
70a5db58 LP |
2775 | break; |
2776 | ||
2777 | default: | |
2778 | log_debug("Unexpected question from libfdisk, ignoring."); | |
2779 | } | |
2780 | ||
2781 | return 0; | |
2782 | } | |
2783 | ||
c8caf53c LP |
2784 | static int apply_resize_partition( |
2785 | int fd, | |
2786 | sd_id128_t disk_uuids, | |
2787 | struct fdisk_table *t, | |
2788 | struct fdisk_partition *p, | |
2789 | size_t new_partition_size) { | |
2790 | ||
70a5db58 LP |
2791 | _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; |
2792 | _cleanup_free_ void *two_zero_lbas = NULL; | |
81dde3d8 | 2793 | uint32_t ssz; |
70a5db58 LP |
2794 | ssize_t n; |
2795 | int r; | |
2796 | ||
2797 | assert(fd >= 0); | |
c8caf53c | 2798 | assert(!t == !p); |
70a5db58 LP |
2799 | |
2800 | if (!t) /* no partition table to apply, exit early */ | |
2801 | return 0; | |
2802 | ||
c8caf53c LP |
2803 | assert(p); |
2804 | ||
2805 | /* Before writing our partition patch the final size in */ | |
2806 | r = fdisk_partition_size_explicit(p, 1); | |
2807 | if (r < 0) | |
2808 | return log_error_errno(r, "Failed to enable explicit partition size: %m"); | |
2809 | ||
2810 | r = fdisk_partition_set_size(p, new_partition_size / 512U); | |
2811 | if (r < 0) | |
2812 | return log_error_errno(r, "Failed to change partition size: %m"); | |
2813 | ||
81dde3d8 LP |
2814 | r = probe_sector_size(fd, &ssz); |
2815 | if (r < 0) | |
2816 | return log_error_errno(r, "Failed to determine current sector size: %m"); | |
2817 | ||
2818 | two_zero_lbas = malloc0(ssz * 2); | |
70a5db58 LP |
2819 | if (!two_zero_lbas) |
2820 | return log_oom(); | |
2821 | ||
2822 | /* libfdisk appears to get confused by the existing PMBR. Let's explicitly flush it out. */ | |
81dde3d8 | 2823 | n = pwrite(fd, two_zero_lbas, ssz * 2, 0); |
70a5db58 LP |
2824 | if (n < 0) |
2825 | return log_error_errno(errno, "Failed to wipe partition table: %m"); | |
81dde3d8 | 2826 | if ((size_t) n != ssz * 2) |
80ace4f2 | 2827 | return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while wiping partition table."); |
70a5db58 | 2828 | |
81dde3d8 | 2829 | r = fdisk_new_context_fd(fd, /* read_only= */ false, ssz, &c); |
70a5db58 LP |
2830 | if (r < 0) |
2831 | return log_error_errno(r, "Failed to open device: %m"); | |
2832 | ||
2833 | r = fdisk_create_disklabel(c, "gpt"); | |
2834 | if (r < 0) | |
2835 | return log_error_errno(r, "Failed to create GPT disk label: %m"); | |
2836 | ||
2837 | r = fdisk_apply_table(c, t); | |
2838 | if (r < 0) | |
2839 | return log_error_errno(r, "Failed to apply partition table: %m"); | |
2840 | ||
2841 | r = fdisk_set_ask(c, ask_cb, &disk_uuids); | |
2842 | if (r < 0) | |
2843 | return log_error_errno(r, "Failed to set libfdisk query function: %m"); | |
2844 | ||
2845 | r = fdisk_set_disklabel_id(c); | |
2846 | if (r < 0) | |
2847 | return log_error_errno(r, "Failed to change disklabel ID: %m"); | |
2848 | ||
2849 | r = fdisk_write_disklabel(c); | |
2850 | if (r < 0) | |
2851 | return log_error_errno(r, "Failed to write disk label: %m"); | |
2852 | ||
2853 | return 1; | |
2854 | } | |
2855 | ||
c8caf53c LP |
2856 | /* Always keep at least 16M free, so that we can safely log in and update the user record while doing so */ |
2857 | #define HOME_MIN_FREE (16U*1024U*1024U) | |
2858 | ||
2859 | static int get_smallest_fs_size(int fd, uint64_t *ret) { | |
2860 | uint64_t minsz, needed; | |
2861 | struct statfs sfs; | |
2862 | ||
2863 | assert(fd >= 0); | |
2864 | assert(ret); | |
2865 | ||
2866 | /* Determines the minimal disk size we might be able to shrink the file system referenced by the fd to. */ | |
2867 | ||
2868 | if (syncfs(fd) < 0) /* let's sync before we query the size, so that the values returned are accurate */ | |
2869 | return log_error_errno(errno, "Failed to synchronize home file system: %m"); | |
2870 | ||
2871 | if (fstatfs(fd, &sfs) < 0) | |
2872 | return log_error_errno(errno, "Failed to statfs() home file system: %m"); | |
2873 | ||
a6f44d61 | 2874 | /* Let's determine the minimal file system size of the used fstype */ |
c8caf53c LP |
2875 | minsz = minimal_size_by_fs_magic(sfs.f_type); |
2876 | if (minsz == UINT64_MAX) | |
2877 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Don't know minimum file system size of file system type '%s' of home directory.", fs_type_to_string(sfs.f_type)); | |
2878 | ||
2879 | if (minsz < USER_DISK_SIZE_MIN) | |
2880 | minsz = USER_DISK_SIZE_MIN; | |
2881 | ||
2882 | if (sfs.f_bfree > sfs.f_blocks) | |
2883 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Detected amount of free blocks is greater than the total amount of file system blocks. Refusing."); | |
2884 | ||
2885 | /* Calculate how much disk space is currently in use. */ | |
2886 | needed = sfs.f_blocks - sfs.f_bfree; | |
2887 | if (needed > UINT64_MAX / sfs.f_bsize) | |
2888 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File system size out of range."); | |
2889 | ||
2890 | needed *= sfs.f_bsize; | |
2891 | ||
2892 | /* Add some safety margin of free space we'll always keep */ | |
2893 | if (needed > UINT64_MAX - HOME_MIN_FREE) /* Check for overflow */ | |
2894 | needed = UINT64_MAX; | |
2895 | else | |
2896 | needed += HOME_MIN_FREE; | |
2897 | ||
2898 | *ret = DISK_SIZE_ROUND_UP(MAX(needed, minsz)); | |
2899 | return 0; | |
2900 | } | |
2901 | ||
2b02eb05 LP |
2902 | static int get_largest_image_size(int fd, const struct stat *st, uint64_t *ret) { |
2903 | uint64_t used, avail, sum; | |
2904 | struct statfs sfs; | |
2905 | int r; | |
2906 | ||
2907 | assert(fd >= 0); | |
2908 | assert(st); | |
2909 | assert(ret); | |
2910 | ||
2911 | /* Determines the maximum file size we might be able to grow the image file referenced by the fd to. */ | |
2912 | ||
2913 | r = stat_verify_regular(st); | |
2914 | if (r < 0) | |
2915 | return log_error_errno(r, "Image file is not a regular file, refusing: %m"); | |
2916 | ||
2917 | if (syncfs(fd) < 0) | |
2918 | return log_error_errno(errno, "Failed to synchronize file system backing image file: %m"); | |
2919 | ||
2920 | if (fstatfs(fd, &sfs) < 0) | |
2921 | return log_error_errno(errno, "Failed to statfs() image file: %m"); | |
2922 | ||
2923 | used = (uint64_t) st->st_blocks * 512; | |
2924 | avail = (uint64_t) sfs.f_bsize * sfs.f_bavail; | |
2925 | ||
2926 | if (avail > UINT64_MAX - used) | |
2927 | sum = UINT64_MAX; | |
2928 | else | |
2929 | sum = avail + used; | |
2930 | ||
2931 | *ret = DISK_SIZE_ROUND_DOWN(MIN(sum, USER_DISK_SIZE_MAX)); | |
2932 | return 0; | |
2933 | } | |
2934 | ||
c8caf53c LP |
2935 | static int resize_fs_loop( |
2936 | UserRecord *h, | |
2937 | HomeSetup *setup, | |
2938 | int resize_type, | |
2939 | uint64_t old_fs_size, | |
2940 | uint64_t new_fs_size, | |
2941 | uint64_t *ret_fs_size) { | |
2942 | ||
2943 | uint64_t current_fs_size; | |
2944 | unsigned n_iterations = 0; | |
2945 | int r; | |
2946 | ||
2947 | assert(h); | |
2948 | assert(setup); | |
2949 | assert(setup->root_fd >= 0); | |
2950 | ||
2951 | /* A bisection loop trying to find the closest size to what the user asked for. (Well, we bisect like | |
2952 | * this only when we *shrink* the fs — if we grow the fs there's no need to bisect.) */ | |
2953 | ||
2954 | current_fs_size = old_fs_size; | |
2955 | for (uint64_t lower_boundary = new_fs_size, upper_boundary = old_fs_size, try_fs_size = new_fs_size;;) { | |
2956 | bool worked; | |
2957 | ||
2958 | n_iterations++; | |
2959 | ||
2960 | /* Now resize the file system */ | |
2961 | if (resize_type == CAN_RESIZE_ONLINE) { | |
2962 | r = resize_fs(setup->root_fd, try_fs_size, NULL); | |
2963 | if (r < 0) { | |
2964 | if (!ERRNO_IS_DISK_SPACE(r) || new_fs_size > old_fs_size) /* Not a disk space issue? Not trying to shrink? */ | |
2965 | return log_error_errno(r, "Failed to resize file system: %m"); | |
2966 | ||
2967 | log_debug_errno(r, "Shrinking from %s to %s didn't work, not enough space for contained data.", FORMAT_BYTES(current_fs_size), FORMAT_BYTES(try_fs_size)); | |
2968 | worked = false; | |
2969 | } else { | |
2970 | log_debug("Successfully resized from %s to %s.", FORMAT_BYTES(current_fs_size), FORMAT_BYTES(try_fs_size)); | |
2971 | current_fs_size = try_fs_size; | |
2972 | worked = true; | |
2973 | } | |
2974 | ||
2975 | /* If we hit a disk space issue and are shrinking the fs, then maybe it helps to | |
2976 | * increase the image size. */ | |
2977 | } else { | |
2978 | r = ext4_offline_resize_fs(setup, try_fs_size, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); | |
2979 | if (r < 0) | |
2980 | return r; | |
2981 | ||
2982 | /* For now, when we fail to shrink an ext4 image we'll not try again via the | |
2983 | * bisection logic. We might add that later, but give this involves shelling out | |
2984 | * multiple programs it's a bit too cumbersome to my taste. */ | |
2985 | ||
2986 | worked = true; | |
2987 | current_fs_size = try_fs_size; | |
2988 | } | |
2989 | ||
2990 | if (new_fs_size > old_fs_size) /* If we are growing we are done after one iteration */ | |
2991 | break; | |
2992 | ||
2993 | /* If we are shrinking then let's adjust our bisection boundaries and try again. */ | |
2994 | if (worked) | |
2995 | upper_boundary = MIN(upper_boundary, try_fs_size); | |
2996 | else | |
2997 | lower_boundary = MAX(lower_boundary, try_fs_size); | |
2998 | ||
2999 | /* OK, this attempt to shrink didn't work. Let's try between the old size and what worked. */ | |
3000 | if (lower_boundary >= upper_boundary) { | |
3001 | log_debug("Image can't be shrunk further (range to try is empty)."); | |
3002 | break; | |
3003 | } | |
3004 | ||
3005 | /* Let's find a new value to try half-way between the lower boundary and the upper boundary | |
3006 | * to try now. */ | |
3007 | try_fs_size = DISK_SIZE_ROUND_DOWN(lower_boundary + (upper_boundary - lower_boundary) / 2); | |
3008 | if (try_fs_size <= lower_boundary || try_fs_size >= upper_boundary) { | |
3009 | log_debug("Image can't be shrunk further (remaining range to try too small)."); | |
3010 | break; | |
3011 | } | |
3012 | } | |
3013 | ||
3014 | log_debug("Bisection loop completed after %u iterations.", n_iterations); | |
3015 | ||
3016 | if (ret_fs_size) | |
3017 | *ret_fs_size = current_fs_size; | |
3018 | ||
3019 | return 0; | |
3020 | } | |
3021 | ||
2b02eb05 LP |
3022 | static int resize_image_loop( |
3023 | UserRecord *h, | |
3024 | HomeSetup *setup, | |
3025 | uint64_t old_image_size, | |
3026 | uint64_t new_image_size, | |
3027 | uint64_t *ret_image_size) { | |
3028 | ||
3029 | uint64_t current_image_size; | |
3030 | unsigned n_iterations = 0; | |
3031 | int r; | |
3032 | ||
3033 | assert(h); | |
3034 | assert(setup); | |
3035 | assert(setup->image_fd >= 0); | |
3036 | ||
3037 | /* A bisection loop trying to find the closest size to what the user asked for. (Well, we bisect like | |
3038 | * this only when we *grow* the image — if we shrink the image then there's no need to bisect.) */ | |
3039 | ||
3040 | current_image_size = old_image_size; | |
3041 | for (uint64_t lower_boundary = old_image_size, upper_boundary = new_image_size, try_image_size = new_image_size;;) { | |
3042 | bool worked; | |
3043 | ||
3044 | n_iterations++; | |
3045 | ||
3046 | r = home_truncate(h, setup->image_fd, try_image_size); | |
3047 | if (r < 0) { | |
3048 | if (!ERRNO_IS_DISK_SPACE(r) || new_image_size < old_image_size) /* Not a disk space issue? Not trying to grow? */ | |
3049 | return r; | |
3050 | ||
3051 | log_debug_errno(r, "Growing from %s to %s didn't work, not enough space on backing disk.", FORMAT_BYTES(current_image_size), FORMAT_BYTES(try_image_size)); | |
3052 | worked = false; | |
3053 | } else if (r > 0) { /* Success: allocation worked */ | |
3054 | log_debug("Resizing from %s to %s via allocation worked successfully.", FORMAT_BYTES(current_image_size), FORMAT_BYTES(try_image_size)); | |
3055 | current_image_size = try_image_size; | |
3056 | worked = true; | |
3057 | } else { /* Success, but through truncation, not allocation. */ | |
3058 | log_debug("Resizing from %s to %s via truncation worked successfully.", FORMAT_BYTES(old_image_size), FORMAT_BYTES(try_image_size)); | |
3059 | current_image_size = try_image_size; | |
3060 | break; /* there's no point in the bisection logic if this was plain truncation and | |
3061 | * not allocation, let's exit immediately. */ | |
3062 | } | |
3063 | ||
3064 | if (new_image_size < old_image_size) /* If we are shrinking we are done after one iteration */ | |
3065 | break; | |
3066 | ||
3067 | /* If we are growing then let's adjust our bisection boundaries and try again */ | |
3068 | if (worked) | |
3069 | lower_boundary = MAX(lower_boundary, try_image_size); | |
3070 | else | |
3071 | upper_boundary = MIN(upper_boundary, try_image_size); | |
3072 | ||
3073 | if (lower_boundary >= upper_boundary) { | |
3074 | log_debug("Image can't be grown further (range to try is empty)."); | |
3075 | break; | |
3076 | } | |
3077 | ||
3078 | try_image_size = DISK_SIZE_ROUND_DOWN(lower_boundary + (upper_boundary - lower_boundary) / 2); | |
3079 | if (try_image_size <= lower_boundary || try_image_size >= upper_boundary) { | |
3080 | log_debug("Image can't be grown further (remaining range to try too small)."); | |
3081 | break; | |
3082 | } | |
3083 | } | |
3084 | ||
3085 | log_debug("Bisection loop completed after %u iterations.", n_iterations); | |
3086 | ||
3087 | if (ret_image_size) | |
3088 | *ret_image_size = current_image_size; | |
3089 | ||
3090 | return 0; | |
3091 | } | |
3092 | ||
70a5db58 LP |
3093 | int home_resize_luks( |
3094 | UserRecord *h, | |
e1df968b | 3095 | HomeSetupFlags flags, |
70a5db58 | 3096 | HomeSetup *setup, |
c00b2ddc | 3097 | PasswordCache *cache, |
70a5db58 LP |
3098 | UserRecord **ret_home) { |
3099 | ||
c8caf53c LP |
3100 | uint64_t old_image_size, new_image_size, old_fs_size, new_fs_size, crypto_offset, crypto_offset_bytes, |
3101 | new_partition_size, smallest_fs_size, resized_fs_size; | |
70a5db58 LP |
3102 | _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL; |
3103 | _cleanup_(fdisk_unref_tablep) struct fdisk_table *table = NULL; | |
c8caf53c | 3104 | struct fdisk_partition *partition = NULL; |
254d1313 | 3105 | _cleanup_close_ int opened_image_fd = -EBADF; |
70a5db58 | 3106 | _cleanup_free_ char *whole_disk = NULL; |
254d1313 | 3107 | int r, resize_type, image_fd = -EBADF; |
70a5db58 LP |
3108 | sd_id128_t disk_uuid; |
3109 | const char *ip, *ipo; | |
3110 | struct statfs sfs; | |
3111 | struct stat st; | |
c8caf53c LP |
3112 | enum { |
3113 | INTENTION_DONT_KNOW = 0, /* These happen to match the return codes of CMP() */ | |
3114 | INTENTION_SHRINK = -1, | |
3115 | INTENTION_GROW = 1, | |
3116 | } intention = INTENTION_DONT_KNOW; | |
70a5db58 LP |
3117 | |
3118 | assert(h); | |
3119 | assert(user_record_storage(h) == USER_LUKS); | |
3120 | assert(setup); | |
70a5db58 | 3121 | |
71eceff6 LP |
3122 | r = dlopen_cryptsetup(); |
3123 | if (r < 0) | |
3124 | return r; | |
3125 | ||
70a5db58 | 3126 | assert_se(ipo = user_record_image_path(h)); |
2f82562b | 3127 | ip = strdupa_safe(ipo); /* copy out since original might change later in home record object */ |
70a5db58 | 3128 | |
4e660eca LP |
3129 | if (setup->image_fd < 0) { |
3130 | setup->image_fd = open_image_file(h, NULL, &st); | |
3131 | if (setup->image_fd < 0) | |
3132 | return setup->image_fd; | |
3133 | } else { | |
3134 | if (fstat(setup->image_fd, &st) < 0) | |
3135 | return log_error_errno(errno, "Failed to stat image file %s: %m", ip); | |
3136 | } | |
3137 | ||
3138 | image_fd = setup->image_fd; | |
70a5db58 | 3139 | |
70a5db58 LP |
3140 | if (S_ISBLK(st.st_mode)) { |
3141 | dev_t parent; | |
3142 | ||
3143 | r = block_get_whole_disk(st.st_rdev, &parent); | |
3144 | if (r < 0) | |
3145 | return log_error_errno(r, "Failed to acquire whole block device for %s: %m", ip); | |
3146 | if (r > 0) { | |
3147 | /* If we shall resize a file system on a partition device, then let's figure out the | |
3148 | * whole disk device and operate on that instead, since we need to rewrite the | |
3149 | * partition table to resize the partition. */ | |
3150 | ||
3151 | log_info("Operating on partition device %s, using parent device.", ip); | |
3152 | ||
4bede0a0 | 3153 | opened_image_fd = r = device_open_from_devnum(S_IFBLK, parent, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK, &whole_disk); |
70a5db58 | 3154 | if (r < 0) |
ca822829 | 3155 | return log_error_errno(r, "Failed to open whole block device for %s: %m", ip); |
70a5db58 | 3156 | |
4e660eca LP |
3157 | image_fd = opened_image_fd; |
3158 | ||
70a5db58 LP |
3159 | if (fstat(image_fd, &st) < 0) |
3160 | return log_error_errno(errno, "Failed to stat whole block device %s: %m", whole_disk); | |
70a5db58 LP |
3161 | } else |
3162 | log_info("Operating on whole block device %s.", ip); | |
3163 | ||
3164 | if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0) | |
3165 | return log_error_errno(errno, "Failed to determine size of original block device: %m"); | |
3166 | ||
3167 | if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ | |
3168 | return log_error_errno(errno, "Failed to lock block device %s: %m", ip); | |
3169 | ||
3170 | new_image_size = old_image_size; /* we can't resize physical block devices */ | |
3171 | } else { | |
3172 | r = stat_verify_regular(&st); | |
3173 | if (r < 0) | |
80ace4f2 | 3174 | return log_error_errno(r, "Image %s is not a block device nor regular file: %m", ip); |
70a5db58 LP |
3175 | |
3176 | old_image_size = st.st_size; | |
3177 | ||
3178 | /* Note an asymetry here: when we operate on loopback files the specified disk size we get we | |
3179 | * apply onto the loopback file as a whole. When we operate on block devices we instead apply | |
3180 | * to the partition itself only. */ | |
3181 | ||
c8caf53c LP |
3182 | if (FLAGS_SET(flags, HOME_SETUP_RESIZE_MINIMIZE)) { |
3183 | new_image_size = 0; | |
3184 | intention = INTENTION_SHRINK; | |
3185 | } else { | |
3186 | uint64_t new_image_size_rounded; | |
04190cf1 | 3187 | |
c8caf53c LP |
3188 | new_image_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size); |
3189 | ||
3190 | if (old_image_size >= new_image_size_rounded && old_image_size <= h->disk_size) { | |
3191 | /* If exact match, or a match after we rounded down, don't do a thing */ | |
3192 | log_info("Image size already matching, skipping operation."); | |
3193 | return 0; | |
3194 | } | |
04190cf1 | 3195 | |
c8caf53c LP |
3196 | new_image_size = new_image_size_rounded; |
3197 | intention = CMP(new_image_size, old_image_size); /* Is this a shrink */ | |
3198 | } | |
70a5db58 LP |
3199 | } |
3200 | ||
c8caf53c LP |
3201 | r = home_setup_luks( |
3202 | h, | |
3203 | flags, | |
3204 | whole_disk, | |
3205 | setup, | |
3206 | cache, | |
3207 | FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES) ? NULL : &header_home); | |
70a5db58 LP |
3208 | if (r < 0) |
3209 | return r; | |
3210 | ||
4e6e72f1 LP |
3211 | if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) { |
3212 | r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home); | |
3213 | if (r < 0) | |
3214 | return r; | |
3215 | } | |
70a5db58 | 3216 | |
a8be0984 LP |
3217 | r = home_maybe_shift_uid(h, flags, setup); |
3218 | if (r < 0) | |
3219 | return r; | |
3220 | ||
70a5db58 LP |
3221 | log_info("offset = %" PRIu64 ", size = %" PRIu64 ", image = %" PRIu64, setup->partition_offset, setup->partition_size, old_image_size); |
3222 | ||
3223 | if ((UINT64_MAX - setup->partition_offset) < setup->partition_size || | |
3224 | setup->partition_offset + setup->partition_size > old_image_size) | |
3225 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Old partition doesn't fit in backing storage, refusing."); | |
3226 | ||
3227 | if (S_ISREG(st.st_mode)) { | |
2b02eb05 | 3228 | uint64_t partition_table_extra, largest_size; |
70a5db58 LP |
3229 | |
3230 | partition_table_extra = old_image_size - setup->partition_size; | |
c8caf53c | 3231 | |
2b02eb05 LP |
3232 | r = get_largest_image_size(setup->image_fd, &st, &largest_size); |
3233 | if (r < 0) | |
3234 | return r; | |
3235 | if (new_image_size > largest_size) | |
3236 | new_image_size = largest_size; | |
3237 | ||
3238 | if (new_image_size < partition_table_extra) | |
c8caf53c | 3239 | new_image_size = partition_table_extra; |
70a5db58 | 3240 | |
04190cf1 | 3241 | new_partition_size = DISK_SIZE_ROUND_DOWN(new_image_size - partition_table_extra); |
70a5db58 LP |
3242 | } else { |
3243 | assert(S_ISBLK(st.st_mode)); | |
3244 | ||
c8caf53c LP |
3245 | if (FLAGS_SET(flags, HOME_SETUP_RESIZE_MINIMIZE)) { |
3246 | new_partition_size = 0; | |
3247 | intention = INTENTION_SHRINK; | |
3248 | } else { | |
3249 | uint64_t new_partition_size_rounded; | |
04190cf1 | 3250 | |
c8caf53c | 3251 | new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size); |
04190cf1 | 3252 | |
c8caf53c LP |
3253 | if (setup->partition_size >= new_partition_size_rounded && |
3254 | setup->partition_size <= h->disk_size) { | |
3255 | log_info("Partition size already matching, skipping operation."); | |
3256 | return 0; | |
3257 | } | |
3258 | ||
3259 | new_partition_size = new_partition_size_rounded; | |
3260 | intention = CMP(new_partition_size, setup->partition_size); | |
3261 | } | |
70a5db58 LP |
3262 | } |
3263 | ||
3264 | if ((UINT64_MAX - setup->partition_offset) < new_partition_size || | |
3265 | setup->partition_offset + new_partition_size > new_image_size) | |
3266 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New partition doesn't fit into backing storage, refusing."); | |
3267 | ||
71eceff6 | 3268 | crypto_offset = sym_crypt_get_data_offset(setup->crypt_device); |
c8caf53c LP |
3269 | if (crypto_offset > UINT64_MAX/512U) |
3270 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS2 data offset out of range, refusing."); | |
3271 | crypto_offset_bytes = (uint64_t) crypto_offset * 512U; | |
3272 | if (setup->partition_size <= crypto_offset_bytes) | |
70a5db58 | 3273 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weird, old crypto payload offset doesn't actually fit in partition size?"); |
70a5db58 | 3274 | |
c8caf53c LP |
3275 | /* Make sure at least the LUKS header fit in */ |
3276 | if (new_partition_size <= crypto_offset_bytes) { | |
3277 | uint64_t add; | |
3278 | ||
3279 | add = DISK_SIZE_ROUND_UP(crypto_offset_bytes) - new_partition_size; | |
3280 | new_partition_size += add; | |
3281 | if (S_ISREG(st.st_mode)) | |
3282 | new_image_size += add; | |
3283 | } | |
3284 | ||
3285 | old_fs_size = setup->partition_size - crypto_offset_bytes; | |
3286 | new_fs_size = DISK_SIZE_ROUND_DOWN(new_partition_size - crypto_offset_bytes); | |
3287 | ||
3288 | r = get_smallest_fs_size(setup->root_fd, &smallest_fs_size); | |
3289 | if (r < 0) | |
3290 | return r; | |
3291 | ||
3292 | if (new_fs_size < smallest_fs_size) { | |
3293 | uint64_t add; | |
3294 | ||
3295 | add = DISK_SIZE_ROUND_UP(smallest_fs_size) - new_fs_size; | |
3296 | new_fs_size += add; | |
3297 | new_partition_size += add; | |
3298 | if (S_ISREG(st.st_mode)) | |
3299 | new_image_size += add; | |
3300 | } | |
3301 | ||
3302 | if (new_fs_size == old_fs_size) { | |
3303 | log_info("New file system size identical to old file system size, skipping operation."); | |
3304 | return 0; | |
3305 | } | |
3306 | ||
3307 | if (FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_GROW) && new_fs_size > old_fs_size) { | |
3308 | log_info("New file system size would be larger than old, but shrinking requested, skipping operation."); | |
3309 | return 0; | |
3310 | } | |
3311 | ||
3312 | if (FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SHRINK) && new_fs_size < old_fs_size) { | |
3313 | log_info("New file system size would be smaller than old, but growing requested, skipping operation."); | |
3314 | return 0; | |
3315 | } | |
3316 | ||
3317 | if (CMP(new_fs_size, old_fs_size) != intention) { | |
3318 | if (intention < 0) | |
3319 | log_info("Shrink operation would enlarge file system, skipping operation."); | |
3320 | else { | |
3321 | assert(intention > 0); | |
3322 | log_info("Grow operation would shrink file system, skipping operation."); | |
3323 | } | |
3324 | return 0; | |
3325 | } | |
70a5db58 LP |
3326 | |
3327 | /* Before we start doing anything, let's figure out if we actually can */ | |
3328 | resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size); | |
3329 | if (resize_type < 0) | |
3330 | return resize_type; | |
e1df968b | 3331 | if (resize_type == CAN_RESIZE_OFFLINE && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) |
70a5db58 LP |
3332 | return log_error_errno(SYNTHETIC_ERRNO(ETXTBSY), "File systems of this type can only be resized offline, but is currently online."); |
3333 | ||
e2341b6b | 3334 | log_info("Ready to resize image size %s %s %s, partition size %s %s %s, file system size %s %s %s.", |
2b59bf51 | 3335 | FORMAT_BYTES(old_image_size), |
e2341b6b | 3336 | special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), |
2b59bf51 ZJS |
3337 | FORMAT_BYTES(new_image_size), |
3338 | FORMAT_BYTES(setup->partition_size), | |
e2341b6b | 3339 | special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), |
2b59bf51 ZJS |
3340 | FORMAT_BYTES(new_partition_size), |
3341 | FORMAT_BYTES(old_fs_size), | |
e2341b6b | 3342 | special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), |
2b59bf51 | 3343 | FORMAT_BYTES(new_fs_size)); |
70a5db58 LP |
3344 | |
3345 | r = prepare_resize_partition( | |
3346 | image_fd, | |
3347 | setup->partition_offset, | |
3348 | setup->partition_size, | |
70a5db58 | 3349 | &disk_uuid, |
c8caf53c LP |
3350 | &table, |
3351 | &partition); | |
70a5db58 LP |
3352 | if (r < 0) |
3353 | return r; | |
3354 | ||
c8caf53c | 3355 | if (new_fs_size > old_fs_size) { /* → Grow */ |
70a5db58 LP |
3356 | |
3357 | if (S_ISREG(st.st_mode)) { | |
2b02eb05 LP |
3358 | uint64_t resized_image_size; |
3359 | ||
70a5db58 | 3360 | /* Grow file size */ |
2b02eb05 | 3361 | r = resize_image_loop(h, setup, old_image_size, new_image_size, &resized_image_size); |
e46f877c LP |
3362 | if (r < 0) |
3363 | return r; | |
70a5db58 | 3364 | |
2b02eb05 LP |
3365 | if (resized_image_size == old_image_size) { |
3366 | log_info("Couldn't change image size."); | |
3367 | return 0; | |
3368 | } | |
3369 | ||
3370 | assert(resized_image_size > old_image_size); | |
3371 | ||
3372 | log_info("Growing of image file from %s to %s completed.", FORMAT_BYTES(old_image_size), FORMAT_BYTES(resized_image_size)); | |
3373 | ||
3374 | if (resized_image_size < new_image_size) { | |
3375 | uint64_t sub; | |
3376 | ||
3377 | /* If the growing we managed to do is smaller than what we wanted we need to | |
3378 | * adjust the partition/file system sizes we are going for, too */ | |
3379 | sub = new_image_size - resized_image_size; | |
3380 | assert(new_partition_size >= sub); | |
3381 | new_partition_size -= sub; | |
3382 | assert(new_fs_size >= sub); | |
3383 | new_fs_size -= sub; | |
3384 | } | |
3385 | ||
3386 | new_image_size = resized_image_size; | |
c8caf53c LP |
3387 | } else { |
3388 | assert(S_ISBLK(st.st_mode)); | |
3389 | assert(new_image_size == old_image_size); | |
70a5db58 LP |
3390 | } |
3391 | ||
3392 | /* Make sure loopback device sees the new bigger size */ | |
3393 | r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size); | |
3394 | if (r == -ENOTTY) | |
3395 | log_debug_errno(r, "Device is not a loopback device, not refreshing size."); | |
3396 | else if (r < 0) | |
3397 | return log_error_errno(r, "Failed to refresh loopback device size: %m"); | |
3398 | else | |
3399 | log_info("Refreshing loop device size completed."); | |
3400 | ||
c8caf53c | 3401 | r = apply_resize_partition(image_fd, disk_uuid, table, partition, new_partition_size); |
70a5db58 LP |
3402 | if (r < 0) |
3403 | return r; | |
3404 | if (r > 0) | |
3405 | log_info("Growing of partition completed."); | |
3406 | ||
6a1301d8 | 3407 | if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0) |
70a5db58 LP |
3408 | log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m"); |
3409 | ||
3410 | /* Tell LUKS about the new bigger size too */ | |
71eceff6 | 3411 | r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U); |
70a5db58 LP |
3412 | if (r < 0) |
3413 | return log_error_errno(r, "Failed to grow LUKS device: %m"); | |
3414 | ||
3415 | log_info("LUKS device growing completed."); | |
3416 | } else { | |
c8caf53c LP |
3417 | /* → Shrink */ |
3418 | ||
4e6e72f1 LP |
3419 | if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) { |
3420 | r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home); | |
3421 | if (r < 0) | |
3422 | return r; | |
3423 | } | |
70a5db58 LP |
3424 | |
3425 | if (S_ISREG(st.st_mode)) { | |
3426 | if (user_record_luks_discard(h)) | |
3427 | /* Before we shrink, let's trim the file system, so that we need less space on disk during the shrinking */ | |
3428 | (void) run_fitrim(setup->root_fd); | |
3429 | else { | |
3430 | /* If discard is off, let's ensure all backing blocks are allocated, so that our resize operation doesn't fail half-way */ | |
3431 | r = run_fallocate(image_fd, &st); | |
3432 | if (r < 0) | |
3433 | return r; | |
3434 | } | |
3435 | } | |
3436 | } | |
3437 | ||
c8caf53c LP |
3438 | /* Now try to resize the file system. The requested size might not always be possible, in which case |
3439 | * we'll try to get as close as we can get. The result is returned in 'resized_fs_size' */ | |
3440 | r = resize_fs_loop(h, setup, resize_type, old_fs_size, new_fs_size, &resized_fs_size); | |
3441 | if (r < 0) | |
3442 | return r; | |
3443 | ||
3444 | if (resized_fs_size == old_fs_size) { | |
3445 | log_info("Couldn't change file system size."); | |
3446 | return 0; | |
c8e2a768 | 3447 | } |
70a5db58 | 3448 | |
c8caf53c LP |
3449 | log_info("File system resizing from %s to %s completed.", FORMAT_BYTES(old_fs_size), FORMAT_BYTES(resized_fs_size)); |
3450 | ||
3451 | if (resized_fs_size > new_fs_size) { | |
3452 | uint64_t add; | |
3453 | ||
3454 | /* If the shrinking we managed to do is larger than what we wanted we need to adjust the partition/image sizes. */ | |
3455 | add = resized_fs_size - new_fs_size; | |
3456 | new_partition_size += add; | |
3457 | if (S_ISREG(st.st_mode)) | |
3458 | new_image_size += add; | |
3459 | } | |
3460 | ||
3461 | new_fs_size = resized_fs_size; | |
70a5db58 LP |
3462 | |
3463 | /* Immediately sync afterwards */ | |
3464 | r = home_sync_and_statfs(setup->root_fd, NULL); | |
3465 | if (r < 0) | |
3466 | return r; | |
3467 | ||
c8caf53c | 3468 | if (new_fs_size < old_fs_size) { /* → Shrink */ |
70a5db58 LP |
3469 | |
3470 | /* Shrink the LUKS device now, matching the new file system size */ | |
71eceff6 | 3471 | r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512); |
70a5db58 LP |
3472 | if (r < 0) |
3473 | return log_error_errno(r, "Failed to shrink LUKS device: %m"); | |
3474 | ||
3475 | log_info("LUKS device shrinking completed."); | |
3476 | ||
70a5db58 LP |
3477 | /* Refresh the loop devices size */ |
3478 | r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size); | |
3479 | if (r == -ENOTTY) | |
3480 | log_debug_errno(r, "Device is not a loopback device, not refreshing size."); | |
3481 | else if (r < 0) | |
3482 | return log_error_errno(r, "Failed to refresh loopback device size: %m"); | |
3483 | else | |
3484 | log_info("Refreshing loop device size completed."); | |
3485 | ||
c8caf53c LP |
3486 | if (S_ISREG(st.st_mode)) { |
3487 | /* Shrink the image file */ | |
3488 | if (ftruncate(image_fd, new_image_size) < 0) | |
3489 | return log_error_errno(errno, "Failed to shrink image file %s: %m", ip); | |
3490 | ||
3491 | log_info("Shrinking of image file completed."); | |
3492 | } else { | |
3493 | assert(S_ISBLK(st.st_mode)); | |
3494 | assert(new_image_size == old_image_size); | |
3495 | } | |
3496 | ||
3497 | r = apply_resize_partition(image_fd, disk_uuid, table, partition, new_partition_size); | |
70a5db58 LP |
3498 | if (r < 0) |
3499 | return r; | |
3500 | if (r > 0) | |
3501 | log_info("Shrinking of partition completed."); | |
3502 | ||
6a1301d8 | 3503 | if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0) |
70a5db58 | 3504 | log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m"); |
c8caf53c LP |
3505 | |
3506 | } else { /* → Grow */ | |
3507 | if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) { | |
3508 | r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home); | |
3509 | if (r < 0) | |
3510 | return r; | |
3511 | } | |
70a5db58 LP |
3512 | } |
3513 | ||
4e6e72f1 LP |
3514 | if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) { |
3515 | r = home_store_header_identity_luks(new_home, setup, header_home); | |
3516 | if (r < 0) | |
3517 | return r; | |
70a5db58 | 3518 | |
4e6e72f1 LP |
3519 | r = home_extend_embedded_identity(new_home, h, setup); |
3520 | if (r < 0) | |
3521 | return r; | |
3522 | } | |
70a5db58 LP |
3523 | |
3524 | if (user_record_luks_discard(h)) | |
3525 | (void) run_fitrim(setup->root_fd); | |
3526 | ||
3527 | r = home_sync_and_statfs(setup->root_fd, &sfs); | |
3528 | if (r < 0) | |
3529 | return r; | |
3530 | ||
5813fca6 LP |
3531 | if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_UNDO)) { |
3532 | r = home_setup_done(setup); | |
3533 | if (r < 0) | |
3534 | return r; | |
3535 | } | |
70a5db58 | 3536 | |
26191000 | 3537 | log_info("Resizing completed."); |
70a5db58 LP |
3538 | |
3539 | print_size_summary(new_image_size, new_fs_size, &sfs); | |
3540 | ||
c8caf53c LP |
3541 | if (ret_home) |
3542 | *ret_home = TAKE_PTR(new_home); | |
3543 | ||
70a5db58 LP |
3544 | return 0; |
3545 | } | |
3546 | ||
3547 | int home_passwd_luks( | |
3548 | UserRecord *h, | |
d26cdde3 | 3549 | HomeSetupFlags flags, |
70a5db58 | 3550 | HomeSetup *setup, |
d26cdde3 LP |
3551 | const PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */ |
3552 | char **effective_passwords /* new passwords */) { | |
70a5db58 | 3553 | |
78b4e9ed | 3554 | size_t volume_key_size, max_key_slots, n_effective; |
70a5db58 LP |
3555 | _cleanup_(erase_and_freep) void *volume_key = NULL; |
3556 | struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf; | |
3557 | const char *type; | |
7b78db28 | 3558 | char **list; |
70a5db58 LP |
3559 | int r; |
3560 | ||
3561 | assert(h); | |
3562 | assert(user_record_storage(h) == USER_LUKS); | |
3563 | assert(setup); | |
3564 | ||
71eceff6 LP |
3565 | r = dlopen_cryptsetup(); |
3566 | if (r < 0) | |
3567 | return r; | |
3568 | ||
3569 | type = sym_crypt_get_type(setup->crypt_device); | |
70a5db58 LP |
3570 | if (!type) |
3571 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine crypto device type."); | |
3572 | ||
71eceff6 | 3573 | r = sym_crypt_keyslot_max(type); |
70a5db58 LP |
3574 | if (r <= 0) |
3575 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine number of key slots."); | |
3576 | max_key_slots = r; | |
3577 | ||
71eceff6 | 3578 | r = sym_crypt_get_volume_key_size(setup->crypt_device); |
70a5db58 LP |
3579 | if (r <= 0) |
3580 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine volume key size."); | |
3581 | volume_key_size = (size_t) r; | |
3582 | ||
3583 | volume_key = malloc(volume_key_size); | |
3584 | if (!volume_key) | |
3585 | return log_oom(); | |
3586 | ||
7b78db28 | 3587 | r = -ENOKEY; |
3361d1ca | 3588 | FOREACH_POINTER(list, |
d26cdde3 | 3589 | cache ? cache->keyring_passswords : NULL, |
3361d1ca LP |
3590 | cache ? cache->pkcs11_passwords : NULL, |
3591 | cache ? cache->fido2_passwords : NULL, | |
3592 | h->password) { | |
3593 | ||
d26cdde3 | 3594 | r = luks_try_passwords(h, setup->crypt_device, list, volume_key, &volume_key_size, NULL); |
7b78db28 LP |
3595 | if (r != -ENOKEY) |
3596 | break; | |
70a5db58 | 3597 | } |
7b78db28 LP |
3598 | if (r == -ENOKEY) |
3599 | return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords."); | |
70a5db58 LP |
3600 | if (r < 0) |
3601 | return log_error_errno(r, "Failed to unlocks LUKS superblock: %m"); | |
3602 | ||
3603 | n_effective = strv_length(effective_passwords); | |
3604 | ||
3605 | build_good_pbkdf(&good_pbkdf, h); | |
3606 | build_minimal_pbkdf(&minimal_pbkdf, h); | |
3607 | ||
78b4e9ed | 3608 | for (size_t i = 0; i < max_key_slots; i++) { |
71eceff6 | 3609 | r = sym_crypt_keyslot_destroy(setup->crypt_device, i); |
70a5db58 LP |
3610 | if (r < 0 && !IN_SET(r, -ENOENT, -EINVAL)) /* Returns EINVAL or ENOENT if there's no key in this slot already */ |
3611 | return log_error_errno(r, "Failed to destroy LUKS password: %m"); | |
3612 | ||
3613 | if (i >= n_effective) { | |
3614 | if (r >= 0) | |
3615 | log_info("Destroyed LUKS key slot %zu.", i); | |
3616 | continue; | |
3617 | } | |
3618 | ||
3361d1ca | 3619 | if (password_cache_contains(cache, effective_passwords[i])) { /* Is this a FIDO2 or PKCS#11 password? */ |
70a5db58 | 3620 | log_debug("Using minimal PBKDF for slot %zu", i); |
71eceff6 | 3621 | r = sym_crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf); |
70a5db58 LP |
3622 | } else { |
3623 | log_debug("Using good PBKDF for slot %zu", i); | |
71eceff6 | 3624 | r = sym_crypt_set_pbkdf_type(setup->crypt_device, &good_pbkdf); |
70a5db58 LP |
3625 | } |
3626 | if (r < 0) | |
3627 | return log_error_errno(r, "Failed to tweak PBKDF for slot %zu: %m", i); | |
3628 | ||
71eceff6 | 3629 | r = sym_crypt_keyslot_add_by_volume_key( |
70a5db58 LP |
3630 | setup->crypt_device, |
3631 | i, | |
3632 | volume_key, | |
3633 | volume_key_size, | |
3634 | effective_passwords[i], | |
3635 | strlen(effective_passwords[i])); | |
3636 | if (r < 0) | |
3637 | return log_error_errno(r, "Failed to set up LUKS password: %m"); | |
3638 | ||
3639 | log_info("Updated LUKS key slot %zu.", i); | |
d26cdde3 LP |
3640 | |
3641 | /* If we changed the password, then make sure to update the copy in the keyring, so that | |
3642 | * auto-rebalance continues to work. We only do this if we operate on an active home dir. */ | |
3643 | if (i == 0 && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) | |
3644 | upload_to_keyring(h, effective_passwords[i], NULL); | |
70a5db58 LP |
3645 | } |
3646 | ||
3647 | return 1; | |
3648 | } | |
3649 | ||
fc032ae1 | 3650 | int home_lock_luks(UserRecord *h, HomeSetup *setup) { |
70a5db58 LP |
3651 | const char *p; |
3652 | int r; | |
3653 | ||
3654 | assert(h); | |
fc032ae1 LP |
3655 | assert(setup); |
3656 | assert(setup->root_fd < 0); | |
f7800049 | 3657 | assert(!setup->crypt_device); |
70a5db58 | 3658 | |
a23cf7f4 | 3659 | r = acquire_open_luks_device(h, setup, /* graceful= */ false); |
70a5db58 LP |
3660 | if (r < 0) |
3661 | return r; | |
3662 | ||
a23cf7f4 | 3663 | log_info("Discovered used LUKS device %s.", setup->dm_node); |
71eceff6 | 3664 | |
a23cf7f4 | 3665 | assert_se(p = user_record_home_directory(h)); |
a70e0ad7 LP |
3666 | r = syncfs_path(AT_FDCWD, p); |
3667 | if (r < 0) /* Snake oil, but let's better be safe than sorry */ | |
3668 | return log_error_errno(r, "Failed to synchronize file system %s: %m", p); | |
70a5db58 LP |
3669 | |
3670 | log_info("File system synchronized."); | |
3671 | ||
3672 | /* Note that we don't invoke FIFREEZE here, it appears libcryptsetup/device-mapper already does that on its own for us */ | |
3673 | ||
f7800049 | 3674 | r = sym_crypt_suspend(setup->crypt_device, setup->dm_name); |
70a5db58 | 3675 | if (r < 0) |
f7800049 | 3676 | return log_error_errno(r, "Failed to suspend cryptsetup device: %s: %m", setup->dm_node); |
70a5db58 LP |
3677 | |
3678 | log_info("LUKS device suspended."); | |
3679 | return 0; | |
3680 | } | |
3681 | ||
3682 | static int luks_try_resume( | |
3683 | struct crypt_device *cd, | |
3684 | const char *dm_name, | |
3685 | char **password) { | |
3686 | ||
70a5db58 LP |
3687 | int r; |
3688 | ||
3689 | assert(cd); | |
3690 | assert(dm_name); | |
3691 | ||
3692 | STRV_FOREACH(pp, password) { | |
71eceff6 | 3693 | r = sym_crypt_resume_by_passphrase( |
70a5db58 LP |
3694 | cd, |
3695 | dm_name, | |
3696 | CRYPT_ANY_SLOT, | |
3697 | *pp, | |
3698 | strlen(*pp)); | |
3699 | if (r >= 0) { | |
3700 | log_info("Resumed LUKS device %s.", dm_name); | |
3701 | return 0; | |
3702 | } | |
3703 | ||
3704 | log_debug_errno(r, "Password %zu didn't work for resuming device: %m", (size_t) (pp - password)); | |
3705 | } | |
3706 | ||
3707 | return -ENOKEY; | |
3708 | } | |
3709 | ||
fc032ae1 | 3710 | int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache) { |
7b78db28 | 3711 | char **list; |
70a5db58 LP |
3712 | int r; |
3713 | ||
3714 | assert(h); | |
fc032ae1 | 3715 | assert(setup); |
f7800049 | 3716 | assert(!setup->crypt_device); |
70a5db58 | 3717 | |
a23cf7f4 | 3718 | r = acquire_open_luks_device(h, setup, /* graceful= */ false); |
70a5db58 LP |
3719 | if (r < 0) |
3720 | return r; | |
3721 | ||
f7800049 | 3722 | log_info("Discovered used LUKS device %s.", setup->dm_node); |
70a5db58 | 3723 | |
7b78db28 | 3724 | r = -ENOKEY; |
3361d1ca LP |
3725 | FOREACH_POINTER(list, |
3726 | cache ? cache->pkcs11_passwords : NULL, | |
3727 | cache ? cache->fido2_passwords : NULL, | |
3728 | h->password) { | |
f7800049 | 3729 | r = luks_try_resume(setup->crypt_device, setup->dm_name, list); |
7b78db28 LP |
3730 | if (r != -ENOKEY) |
3731 | break; | |
70a5db58 | 3732 | } |
7b78db28 LP |
3733 | if (r == -ENOKEY) |
3734 | return log_error_errno(r, "No valid password for LUKS superblock."); | |
70a5db58 LP |
3735 | if (r < 0) |
3736 | return log_error_errno(r, "Failed to resume LUKS superblock: %m"); | |
3737 | ||
3738 | log_info("LUKS device resumed."); | |
3739 | return 0; | |
3740 | } | |
491347bd LP |
3741 | |
3742 | static int device_is_gone(HomeSetup *setup) { | |
3743 | _cleanup_(sd_device_unrefp) sd_device *d = NULL; | |
3744 | struct stat st; | |
3745 | int r; | |
3746 | ||
3747 | assert(setup); | |
3748 | ||
3749 | if (!setup->dm_node) | |
3750 | return true; | |
3751 | ||
3752 | if (stat(setup->dm_node, &st) < 0) { | |
3753 | if (errno != ENOENT) | |
3754 | return log_error_errno(errno, "Failed to stat block device node %s: %m", setup->dm_node); | |
3755 | ||
3756 | return true; | |
3757 | } | |
3758 | ||
3759 | r = sd_device_new_from_stat_rdev(&d, &st); | |
3760 | if (r < 0) { | |
3761 | if (r != -ENODEV) | |
3762 | return log_error_errno(errno, "Failed to allocate device object from block device node %s: %m", setup->dm_node); | |
3763 | ||
3764 | return true; | |
3765 | } | |
3766 | ||
3767 | return false; | |
3768 | } | |
3769 | ||
3770 | static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { | |
99534007 | 3771 | HomeSetup *setup = ASSERT_PTR(userdata); |
491347bd LP |
3772 | int r; |
3773 | ||
491347bd LP |
3774 | if (!device_for_action(device, SD_DEVICE_REMOVE)) |
3775 | return 0; | |
3776 | ||
3777 | /* We don't really care for the device object passed to us, we just check if the device node still | |
3778 | * exists */ | |
3779 | ||
3780 | r = device_is_gone(setup); | |
3781 | if (r < 0) | |
3782 | return r; | |
3783 | if (r > 0) /* Yay! we are done! */ | |
3784 | (void) sd_event_exit(sd_device_monitor_get_event(monitor), 0); | |
3785 | ||
3786 | return 0; | |
3787 | } | |
3788 | ||
3789 | int wait_for_block_device_gone(HomeSetup *setup, usec_t timeout_usec) { | |
3790 | _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; | |
3791 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
3792 | int r; | |
3793 | ||
3794 | assert(setup); | |
3795 | ||
3796 | /* So here's the thing: we enable "deferred deactivation" on our dm-crypt volumes. This means they | |
3797 | * are automatically torn down once not used anymore (i.e. once unmounted). Which is great. It also | |
3798 | * means that when we deactivate a home directory and try to tear down the volume that backs it, it | |
a6f44d61 | 3799 | * possibly is already torn down or in the process of being torn down, since we race against the |
491347bd LP |
3800 | * automatic tearing down. Which is fine, we handle errors from that. However, we lose the ability to |
3801 | * naturally wait for the tear down operation to complete: if we are not the ones who tear down the | |
3802 | * device we are also not the ones who naturally block on that operation. Hence let's add some code | |
3803 | * to actively wait for the device to go away, via sd-device. We'll call this whenever tearing down a | |
3804 | * LUKS device, to ensure the device is really really gone before we proceed. Net effect: "homectl | |
3805 | * deactivate foo && homectl activate foo" will work reliably, i.e. deactivation immediately followed | |
3806 | * by activation will work. Also, by the time deactivation completes we can guarantee that all data | |
3807 | * is sync'ed down to the lowest block layer as all higher levels are fully and entirely | |
3808 | * destructed. */ | |
3809 | ||
3810 | if (!setup->dm_name) | |
3811 | return 0; | |
3812 | ||
3813 | assert(setup->dm_node); | |
3814 | log_debug("Waiting until %s disappears.", setup->dm_node); | |
3815 | ||
3816 | r = sd_event_new(&event); | |
3817 | if (r < 0) | |
3818 | return log_error_errno(r, "Failed to allocate event loop: %m"); | |
3819 | ||
3820 | r = sd_device_monitor_new(&m); | |
3821 | if (r < 0) | |
3822 | return log_error_errno(r, "Failed to allocate device monitor: %m"); | |
3823 | ||
3824 | r = sd_device_monitor_filter_add_match_subsystem_devtype(m, "block", "disk"); | |
3825 | if (r < 0) | |
3826 | return log_error_errno(r, "Failed to configure device monitor match: %m"); | |
3827 | ||
3828 | r = sd_device_monitor_attach_event(m, event); | |
3829 | if (r < 0) | |
3830 | return log_error_errno(r, "Failed to attach device monitor to event loop: %m"); | |
3831 | ||
3832 | r = sd_device_monitor_start(m, device_monitor_handler, setup); | |
3833 | if (r < 0) | |
3834 | return log_error_errno(r, "Failed to start device monitor: %m"); | |
3835 | ||
3836 | r = device_is_gone(setup); | |
3837 | if (r < 0) | |
3838 | return r; | |
3839 | if (r > 0) { | |
3840 | log_debug("%s has already disappeared before entering wait loop.", setup->dm_node); | |
3841 | return 0; /* gone already */ | |
3842 | } | |
3843 | ||
3844 | if (timeout_usec != USEC_INFINITY) { | |
3845 | r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, timeout_usec, 0, NULL, NULL); | |
3846 | if (r < 0) | |
3847 | return log_error_errno(r, "Failed to add timer event: %m"); | |
3848 | } | |
3849 | ||
3850 | r = sd_event_loop(event); | |
3851 | if (r < 0) | |
3852 | return log_error_errno(r, "Failed to run event loop: %m"); | |
3853 | ||
3854 | r = device_is_gone(setup); | |
3855 | if (r < 0) | |
3856 | return r; | |
3857 | if (r == 0) | |
3858 | return log_error_errno(r, "Device %s still around.", setup->dm_node); | |
3859 | ||
3860 | log_debug("Successfully waited until device %s disappeared.", setup->dm_node); | |
3861 | return 0; | |
3862 | } | |
26191000 LP |
3863 | |
3864 | int home_auto_shrink_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache) { | |
3865 | struct statfs sfs; | |
3866 | int r; | |
3867 | ||
3868 | assert(h); | |
3869 | assert(user_record_storage(h) == USER_LUKS); | |
3870 | assert(setup); | |
3871 | assert(setup->root_fd >= 0); | |
3872 | ||
3873 | if (user_record_auto_resize_mode(h) != AUTO_RESIZE_SHRINK_AND_GROW) | |
3874 | return 0; | |
3875 | ||
3876 | if (fstatfs(setup->root_fd, &sfs) < 0) | |
3877 | return log_error_errno(errno, "Failed to statfs home directory: %m"); | |
3878 | ||
3879 | if (!fs_can_online_shrink_and_grow(sfs.f_type)) { | |
3880 | log_debug("Not auto-shrinking file system, since selected file system cannot do both online shrink and grow."); | |
3881 | return 0; | |
3882 | } | |
3883 | ||
3884 | r = home_resize_luks( | |
3885 | h, | |
3886 | HOME_SETUP_ALREADY_ACTIVATED| | |
3887 | HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES| | |
3888 | HOME_SETUP_RESIZE_MINIMIZE| | |
3889 | HOME_SETUP_RESIZE_DONT_GROW| | |
3890 | HOME_SETUP_RESIZE_DONT_UNDO, | |
3891 | setup, | |
3892 | cache, | |
3893 | NULL); | |
3894 | if (r < 0) | |
3895 | return r; | |
3896 | ||
3897 | return 1; | |
3898 | } |