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