1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "cryptenroll-wipe.h"
4 #include "cryptenroll.h"
6 #include "memory-util.h"
7 #include "parse-util.h"
11 static int find_all_slots(struct crypt_device
*cd
, Set
*wipe_slots
, Set
*keep_slots
) {
16 assert_se((slot_max
= crypt_keyslot_max(CRYPT_LUKS2
)) > 0);
18 /* Finds all currently assigned slots, and adds them to 'wipe_slots', except if listed already in 'keep_slots' */
20 for (int slot
= 0; slot
< slot_max
; slot
++) {
21 crypt_keyslot_info status
;
23 /* No need to check this slot if we already know we want to wipe it or definitely keep it. */
24 if (set_contains(keep_slots
, INT_TO_PTR(slot
)) ||
25 set_contains(wipe_slots
, INT_TO_PTR(slot
)))
28 status
= crypt_keyslot_status(cd
, slot
);
29 if (!IN_SET(status
, CRYPT_SLOT_ACTIVE
, CRYPT_SLOT_ACTIVE_LAST
))
32 if (set_put(wipe_slots
, INT_TO_PTR(slot
)) < 0)
39 static int find_empty_passphrase_slots(struct crypt_device
*cd
, Set
*wipe_slots
, Set
*keep_slots
) {
45 assert_se((slot_max
= crypt_keyslot_max(CRYPT_LUKS2
)) > 0);
47 /* Finds all slots with an empty passphrase assigned (i.e. "") and adds them to 'wipe_slots', except
48 * if listed already in 'keep_slots' */
50 r
= crypt_get_volume_key_size(cd
);
52 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to determine LUKS volume key size");
55 for (int slot
= 0; slot
< slot_max
; slot
++) {
56 _cleanup_(erase_and_freep
) char *vk
= NULL
;
57 crypt_keyslot_info status
;
59 /* No need to check this slot if we already know we want to wipe it or definitely keep it. */
60 if (set_contains(keep_slots
, INT_TO_PTR(slot
)) ||
61 set_contains(wipe_slots
, INT_TO_PTR(slot
)))
64 status
= crypt_keyslot_status(cd
, slot
);
65 if (!IN_SET(status
, CRYPT_SLOT_ACTIVE
, CRYPT_SLOT_ACTIVE_LAST
))
72 r
= crypt_volume_key_get(cd
, slot
, vk
, &vks
, "", 0);
74 log_debug_errno(r
, "Failed to acquire volume key from slot %i with empty password, ignoring: %m", slot
);
78 if (set_put(wipe_slots
, INT_TO_PTR(r
)) < 0)
85 static int find_slots_by_mask(
86 struct crypt_device
*cd
,
91 _cleanup_set_free_ Set
*listed_slots
= NULL
;
100 /* Find all slots that are associated with a token of a type in the specified token type mask */
102 for (int token
= 0; token
< sym_crypt_token_max(CRYPT_LUKS2
); token
++) {
103 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
107 r
= cryptsetup_get_token_as_json(cd
, token
, NULL
, &v
);
108 if (IN_SET(r
, -ENOENT
, -EINVAL
))
111 log_warning_errno(r
, "Failed to read JSON token data off disk, ignoring: %m");
115 w
= json_variant_by_key(v
, "type");
116 if (!w
|| !json_variant_is_string(w
)) {
117 log_warning("Token JSON data lacks type field, ignoring.");
121 t
= luks2_token_type_from_string(json_variant_string(w
));
123 w
= json_variant_by_key(v
, "keyslots");
124 if (!w
|| !json_variant_is_array(w
)) {
125 log_warning("Token JSON data lacks keyslots field, ignoring.");
129 JSON_VARIANT_ARRAY_FOREACH(z
, w
) {
132 if (!json_variant_is_string(z
)) {
133 log_warning("Token JSON data's keyslot field is not an array of strings, ignoring.");
137 r
= safe_atoi(json_variant_string(z
), &slot
);
139 log_warning_errno(r
, "Token JSON data's keyslot filed is not an integer formatted as string, ignoring.");
143 if (t
>= 0 && (by_mask
& (1U << t
)) != 0) {
144 /* Selected by token type */
145 if (set_put(wipe_slots
, INT_TO_PTR(slot
)) < 0)
147 } else if ((by_mask
& (1U << ENROLL_PASSWORD
)) != 0) {
148 /* If we shall remove all plain password slots, let's maintain a list of
149 * slots that are listed in any tokens, since those are *NOT* plain
151 if (set_ensure_allocated(&listed_slots
, NULL
) < 0)
154 if (set_put(listed_slots
, INT_TO_PTR(slot
)) < 0)
160 /* "password" slots are those which have no token assigned. If we shall remove those, iterate through
161 * all slots and mark those for wiping that weren't listed in any token */
162 if ((by_mask
& (1U << ENROLL_PASSWORD
)) != 0) {
165 assert_se((slot_max
= crypt_keyslot_max(CRYPT_LUKS2
)) > 0);
167 for (int slot
= 0; slot
< slot_max
; slot
++) {
168 crypt_keyslot_info status
;
170 /* No need to check this slot if we already know we want to wipe it or definitely keep it. */
171 if (set_contains(keep_slots
, INT_TO_PTR(slot
)) ||
172 set_contains(wipe_slots
, INT_TO_PTR(slot
)))
175 if (set_contains(listed_slots
, INT_TO_PTR(slot
))) /* This has a token, hence is not a password. */
178 status
= crypt_keyslot_status(cd
, slot
);
179 if (!IN_SET(status
, CRYPT_SLOT_ACTIVE
, CRYPT_SLOT_ACTIVE_LAST
)) /* Not actually assigned? */
182 /* Finally, we found a password, add it to the list of slots to wipe */
183 if (set_put(wipe_slots
, INT_TO_PTR(slot
)) < 0)
191 static int find_slot_tokens(struct crypt_device
*cd
, Set
*wipe_slots
, Set
*keep_slots
, Set
*wipe_tokens
) {
199 /* Find all tokens matching the slots we want to wipe, so that we can wipe them too. Also, for update
200 * the slots sets according to the token data: add any other slots listed in the tokens we act on. */
202 for (int token
= 0; token
< sym_crypt_token_max(CRYPT_LUKS2
); token
++) {
203 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
204 bool shall_wipe
= false;
207 r
= cryptsetup_get_token_as_json(cd
, token
, NULL
, &v
);
208 if (IN_SET(r
, -ENOENT
, -EINVAL
))
211 log_warning_errno(r
, "Failed to read JSON token data off disk, ignoring: %m");
215 w
= json_variant_by_key(v
, "keyslots");
216 if (!w
|| !json_variant_is_array(w
)) {
217 log_warning("Token JSON data lacks keyslots field, ignoring.");
221 /* Go through the slots associated with this token: if we shall keep any slot of them, the token shall stay too. */
222 JSON_VARIANT_ARRAY_FOREACH(z
, w
) {
225 if (!json_variant_is_string(z
)) {
226 log_warning("Token JSON data's keyslot field is not an array of strings, ignoring.");
230 r
= safe_atoi(json_variant_string(z
), &slot
);
232 log_warning_errno(r
, "Token JSON data's keyslot filed is not an integer formatted as string, ignoring.");
236 if (set_contains(keep_slots
, INT_TO_PTR(slot
))) {
238 break; /* If we shall keep this slot, then this is definite: we will keep its token too */
241 /* If there's a slot associated with this token that we shall wipe, then remove the
242 * token too. But we are careful here: let's continue iterating, maybe there's a slot
243 * that we need to keep, in which case we can reverse the decision again. */
244 if (set_contains(wipe_slots
, INT_TO_PTR(slot
)))
248 /* Go through the slots again, and this time add them to the list of slots to keep/remove */
249 JSON_VARIANT_ARRAY_FOREACH(z
, w
) {
252 if (!json_variant_is_string(z
))
254 if (safe_atoi(json_variant_string(z
), &slot
) < 0)
257 if (set_put(shall_wipe
? wipe_slots
: keep_slots
, INT_TO_PTR(slot
)) < 0)
261 /* And of course, also remember the tokens to remove. */
263 if (set_put(wipe_tokens
, INT_TO_PTR(token
)) < 0)
270 static bool slots_remain(struct crypt_device
*cd
, Set
*wipe_slots
, Set
*keep_slots
) {
274 assert_se((slot_max
= crypt_keyslot_max(CRYPT_LUKS2
)) > 0);
276 /* Checks if any slots remaining in the LUKS2 header if we remove all slots listed in 'wipe_slots'
277 * (keeping those listed in 'keep_slots') */
279 for (int slot
= 0; slot
< slot_max
; slot
++) {
280 crypt_keyslot_info status
;
282 status
= crypt_keyslot_status(cd
, slot
);
283 if (!IN_SET(status
, CRYPT_SLOT_ACTIVE
, CRYPT_SLOT_ACTIVE_LAST
))
286 /* The "keep" set wins if a slot is listed in both sets. This is important so that we can
287 * safely add a new slot and remove all others of the same type, which in a naive
288 * implementation might mean we remove what we just added — which we of course don't want. */
289 if (set_contains(keep_slots
, INT_TO_PTR(slot
)) ||
290 !set_contains(wipe_slots
, INT_TO_PTR(slot
)))
297 int wipe_slots(struct crypt_device
*cd
,
298 const int explicit_slots
[],
299 size_t n_explicit_slots
,
304 _cleanup_set_free_ Set
*wipe_slots
= NULL
, *wipe_tokens
= NULL
, *keep_slots
= NULL
;
305 _cleanup_free_
int *ordered_slots
= NULL
, *ordered_tokens
= NULL
;
306 size_t n_ordered_slots
= 0, n_ordered_tokens
= 0;
307 int r
, slot_max
, ret
;
312 /* Shortcut if nothing to wipe. */
313 if (n_explicit_slots
== 0 && by_mask
== 0 && by_scope
== WIPE_EXPLICIT
)
316 /* So this is a bit more complicated than I'd wish, but we want support three different axis for wiping slots:
318 * 1. Wiping by slot indexes
319 * 2. Wiping slots of specified token types
320 * 3. Wiping "all" entries, or entries with an empty password (i.e. "")
322 * (or any combination of the above)
324 * Plus: We always want to remove tokens matching the slots.
325 * Plus: We always want to exclude the slots/tokens we just added.
328 wipe_slots
= set_new(NULL
);
329 keep_slots
= set_new(NULL
);
330 wipe_tokens
= set_new(NULL
);
331 if (!wipe_slots
|| !keep_slots
|| !wipe_tokens
)
334 /* Let's maintain one set of slots for the slots we definitely want to keep */
335 if (except_slot
>= 0)
336 if (set_put(keep_slots
, INT_TO_PTR(except_slot
)) < 0)
339 assert_se((slot_max
= crypt_keyslot_max(CRYPT_LUKS2
)) > 0);
341 /* Maintain another set of the slots we intend to wipe */
342 for (size_t i
= 0; i
< n_explicit_slots
; i
++) {
343 if (explicit_slots
[i
] >= slot_max
)
344 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Slot index %i out of range.", explicit_slots
[i
]);
346 if (set_put(wipe_slots
, INT_TO_PTR(explicit_slots
[i
])) < 0)
350 /* Now, handle the "all" and "empty passphrase" cases. */
354 break; /* Nothing to do here */
357 r
= find_all_slots(cd
, wipe_slots
, keep_slots
);
363 case WIPE_EMPTY_PASSPHRASE
:
364 r
= find_empty_passphrase_slots(cd
, wipe_slots
, keep_slots
);
370 assert_not_reached();
373 /* Then add all slots that match a token type */
374 r
= find_slots_by_mask(cd
, wipe_slots
, keep_slots
, by_mask
);
378 /* And determine tokens that we shall remove */
379 r
= find_slot_tokens(cd
, wipe_slots
, keep_slots
, wipe_tokens
);
383 /* Safety check: let's make sure that after we are done there's at least one slot remaining */
384 if (!slots_remain(cd
, wipe_slots
, keep_slots
))
385 return log_error_errno(SYNTHETIC_ERRNO(EPERM
),
386 "Wipe operation would leave no valid slots around, can't allow that, sorry.");
388 /* Generated ordered lists of the slots and the tokens to remove */
389 ordered_slots
= new(int, set_size(wipe_slots
));
392 SET_FOREACH(e
, wipe_slots
) {
393 int slot
= PTR_TO_INT(e
);
395 if (set_contains(keep_slots
, INT_TO_PTR(slot
)))
398 ordered_slots
[n_ordered_slots
++] = slot
;
400 typesafe_qsort(ordered_slots
, n_ordered_slots
, cmp_int
);
402 ordered_tokens
= new(int, set_size(wipe_tokens
));
405 SET_FOREACH(e
, wipe_tokens
)
406 ordered_tokens
[n_ordered_tokens
++] = PTR_TO_INT(e
);
407 typesafe_qsort(ordered_tokens
, n_ordered_tokens
, cmp_int
);
409 if (n_ordered_slots
== 0 && n_ordered_tokens
== 0) {
410 log_full(except_slot
< 0 ? LOG_NOTICE
: LOG_DEBUG
,
411 "No slots to remove selected.");
416 for (size_t i
= 0; i
< n_ordered_slots
; i
++)
417 log_debug("Going to wipe slot %i.", ordered_slots
[i
]);
418 for (size_t i
= 0; i
< n_ordered_tokens
; i
++)
419 log_debug("Going to wipe token %i.", ordered_tokens
[i
]);
422 /* Now, let's actually start wiping things. (We go from back to front, to make space at the end
425 for (size_t i
= n_ordered_slots
; i
> 0; i
--) {
426 r
= crypt_keyslot_destroy(cd
, ordered_slots
[i
- 1]);
428 log_warning_errno(r
, "Failed to wipe slot %i, continuing: %m", ordered_slots
[i
- 1]);
432 log_info("Wiped slot %i.", ordered_slots
[i
- 1]);
435 for (size_t i
= n_ordered_tokens
; i
> 0; i
--) {
436 r
= crypt_token_json_set(cd
, ordered_tokens
[i
- 1], NULL
);
438 log_warning_errno(r
, "Failed to wipe token %i, continuing: %m", ordered_tokens
[i
- 1]);