X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fluks2%2Fluks2_json_metadata.c;h=4771f040dac90d085a4dcc5d5209cb6ee9f4bd34;hb=420ad62f0bb77970a6ba234bde9e1405f7df7789;hp=846925730cd76e2859fa9f37218bf73e5db57557;hpb=f7fc3bb4e50cce23dd95111b246b6e034537e2cf;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index 8469257..4771f04 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -1,9 +1,9 @@ /* * LUKS - Linux Unified Key Setup v2 * - * Copyright (C) 2015-2021 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2021 Milan Broz - * Copyright (C) 2015-2021 Ondrej Kozina + * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2023 Milan Broz + * Copyright (C) 2015-2023 Ondrej Kozina * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,7 +22,6 @@ #include "luks2_internal.h" #include "../integrity/integrity.h" -#include #include #include @@ -40,9 +39,8 @@ void hexprint_base64(struct crypt_device *cd, json_object *jobj, size_t buf_len; unsigned int i; - if (!base64_decode_alloc(json_object_get_string(jobj), - json_object_get_string_len(jobj), - &buf, &buf_len)) + if (crypt_base64_decode(&buf, &buf_len, json_object_get_string(jobj), + json_object_get_string_len(jobj))) return; for (i = 0; i < buf_len; i++) { @@ -209,7 +207,7 @@ int LUKS2_get_default_segment(struct luks2_hdr *hdr) if (s >= 0) return s; - if (LUKS2_segments_count(hdr) == 1) + if (LUKS2_segments_count(hdr) >= 1) return 0; return -EINVAL; @@ -225,7 +223,7 @@ uint32_t crypt_jobj_get_uint32(json_object *jobj) } /* jobj has to be json_type_string and numbered */ -static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value) +static bool json_str_to_uint64(json_object *jobj, uint64_t *value) { char *endptr; unsigned long long tmp; @@ -234,11 +232,11 @@ static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value) tmp = strtoull(json_object_get_string(jobj), &endptr, 10); if (*endptr || errno) { *value = 0; - return 0; + return false; } *value = tmp; - return 1; + return true; } uint64_t crypt_jobj_get_uint64(json_object *jobj) @@ -266,16 +264,16 @@ json_object *crypt_jobj_new_uint64(uint64_t value) /* * Validate helpers */ -static json_bool numbered(struct crypt_device *cd, const char *name, const char *key) +static bool numbered(struct crypt_device *cd, const char *name, const char *key) { int i; for (i = 0; key[i]; i++) if (!isdigit(key[i])) { log_dbg(cd, "%s \"%s\" is not in numbered form.", name, key); - return 0; + return false; } - return 1; + return true; } json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name, @@ -293,18 +291,31 @@ json_object *json_contains(struct crypt_device *cd, json_object *jobj, const cha return sobj; } -json_bool validate_json_uint32(json_object *jobj) +json_object *json_contains_string(struct crypt_device *cd, json_object *jobj, + const char *name, const char *section, const char *key) +{ + json_object *sobj = json_contains(cd, jobj, name, section, key, json_type_string); + + if (!sobj) + return NULL; + + if (strlen(json_object_get_string(sobj)) < 1) + return NULL; + + return sobj; +} + +bool validate_json_uint32(json_object *jobj) { int64_t tmp; errno = 0; tmp = json_object_get_int64(jobj); - return (errno || tmp < 0 || tmp > UINT32_MAX) ? 0 : 1; + return (errno || tmp < 0 || tmp > UINT32_MAX) ? false : true; } -static json_bool validate_keyslots_array(struct crypt_device *cd, - json_object *jarr, json_object *jobj_keys) +static bool validate_keyslots_array(struct crypt_device *cd, json_object *jarr, json_object *jobj_keys) { json_object *jobj; int i = 0, length = (int) json_object_array_length(jarr); @@ -313,21 +324,20 @@ static json_bool validate_keyslots_array(struct crypt_device *cd, jobj = json_object_array_get_idx(jarr, i); if (!json_object_is_type(jobj, json_type_string)) { log_dbg(cd, "Illegal value type in keyslots array at index %d.", i); - return 0; + return false; } if (!json_contains(cd, jobj_keys, "", "Keyslots section", json_object_get_string(jobj), json_type_object)) - return 0; + return false; i++; } - return 1; + return true; } -static json_bool validate_segments_array(struct crypt_device *cd, - json_object *jarr, json_object *jobj_segments) +static bool validate_segments_array(struct crypt_device *cd, json_object *jarr, json_object *jobj_segments) { json_object *jobj; int i = 0, length = (int) json_object_array_length(jarr); @@ -336,20 +346,20 @@ static json_bool validate_segments_array(struct crypt_device *cd, jobj = json_object_array_get_idx(jarr, i); if (!json_object_is_type(jobj, json_type_string)) { log_dbg(cd, "Illegal value type in segments array at index %d.", i); - return 0; + return false; } if (!json_contains(cd, jobj_segments, "", "Segments section", json_object_get_string(jobj), json_type_object)) - return 0; + return false; i++; } - return 1; + return true; } -static json_bool segment_has_digest(const char *segment_name, json_object *jobj_digests) +static bool segment_has_digest(const char *segment_name, json_object *jobj_digests) { json_object *jobj_segments; @@ -357,57 +367,70 @@ static json_bool segment_has_digest(const char *segment_name, json_object *jobj_ UNUSED(key); json_object_object_get_ex(val, "segments", &jobj_segments); if (LUKS2_array_jobj(jobj_segments, segment_name)) - return 1; + return true; } - return 0; + return false; } -static json_bool validate_intervals(struct crypt_device *cd, - int length, const struct interval *ix, - uint64_t metadata_size, uint64_t keyslots_area_end) + +static bool validate_intervals(struct crypt_device *cd, + int length, const struct interval *ix, + uint64_t metadata_size, uint64_t keyslots_area_end) { int j, i = 0; while (i < length) { + /* Offset cannot be inside primary or secondary JSON area */ if (ix[i].offset < 2 * metadata_size) { log_dbg(cd, "Illegal area offset: %" PRIu64 ".", ix[i].offset); - return 0; + return false; } if (!ix[i].length) { log_dbg(cd, "Area length must be greater than zero."); - return 0; + return false; + } + + if (ix[i].offset > (UINT64_MAX - ix[i].length)) { + log_dbg(cd, "Interval offset+length overflow."); + return false; } if ((ix[i].offset + ix[i].length) > keyslots_area_end) { log_dbg(cd, "Area [%" PRIu64 ", %" PRIu64 "] overflows binary keyslots area (ends at offset: %" PRIu64 ").", ix[i].offset, ix[i].offset + ix[i].length, keyslots_area_end); - return 0; + return false; } for (j = 0; j < length; j++) { if (i == j) continue; + + if (ix[j].offset > (UINT64_MAX - ix[j].length)) { + log_dbg(cd, "Interval offset+length overflow."); + return false; + } + if ((ix[i].offset >= ix[j].offset) && (ix[i].offset < (ix[j].offset + ix[j].length))) { log_dbg(cd, "Overlapping areas [%" PRIu64 ",%" PRIu64 "] and [%" PRIu64 ",%" PRIu64 "].", ix[i].offset, ix[i].offset + ix[i].length, ix[j].offset, ix[j].offset + ix[j].length); - return 0; + return false; } } i++; } - return 1; + return true; } -static int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_jobj, json_object *hdr_keyslot, const char *key) +static int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_keyslot, const char *key) { json_object *jobj_key_size; - if (!json_contains(cd, hdr_keyslot, key, "Keyslot", "type", json_type_string)) + if (!json_contains_string(cd, hdr_keyslot, key, "Keyslot", "type")) return 1; if (!(jobj_key_size = json_contains(cd, hdr_keyslot, key, "Keyslot", "key_size", json_type_int))) return 1; @@ -431,7 +454,7 @@ int LUKS2_token_validate(struct crypt_device *cd, if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots)) return 1; - if (!json_contains(cd, jobj_token, key, "Token", "type", json_type_string)) + if (!json_contains_string(cd, jobj_token, key, "Token", "type")) return 1; jarr = json_contains(cd, jobj_token, key, "Token", "keyslots", json_type_array); @@ -480,15 +503,13 @@ static int hdr_validate_keyslots(struct crypt_device *cd, json_object *hdr_jobj) { json_object *jobj; - if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj)) { - log_dbg(cd, "Missing keyslots section."); + if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "keyslots", json_type_object))) return 1; - } json_object_object_foreach(jobj, key, val) { if (!numbered(cd, "Keyslot", key)) return 1; - if (LUKS2_keyslot_validate(cd, hdr_jobj, val, key)) + if (LUKS2_keyslot_validate(cd, val, key)) return 1; } @@ -499,10 +520,8 @@ static int hdr_validate_tokens(struct crypt_device *cd, json_object *hdr_jobj) { json_object *jobj; - if (!json_object_object_get_ex(hdr_jobj, "tokens", &jobj)) { - log_dbg(cd, "Missing tokens section."); + if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "tokens", json_type_object))) return 1; - } json_object_object_foreach(jobj, key, val) { if (!numbered(cd, "Token", key)) @@ -514,25 +533,26 @@ static int hdr_validate_tokens(struct crypt_device *cd, json_object *hdr_jobj) return 0; } -static int hdr_validate_crypt_segment(struct crypt_device *cd, - json_object *jobj, const char *key, json_object *jobj_digests, - uint64_t offset, uint64_t size) +static int hdr_validate_crypt_segment(struct crypt_device *cd, json_object *jobj, + const char *key, json_object *jobj_digests, + uint64_t size) { + int r; json_object *jobj_ivoffset, *jobj_sector_size, *jobj_integrity; uint32_t sector_size; uint64_t ivoffset; - if (!(jobj_ivoffset = json_contains(cd, jobj, key, "Segment", "iv_tweak", json_type_string)) || - !json_contains(cd, jobj, key, "Segment", "encryption", json_type_string) || + if (!(jobj_ivoffset = json_contains_string(cd, jobj, key, "Segment", "iv_tweak")) || + !json_contains_string(cd, jobj, key, "Segment", "encryption") || !(jobj_sector_size = json_contains(cd, jobj, key, "Segment", "sector_size", json_type_int))) return 1; /* integrity */ if (json_object_object_get_ex(jobj, "integrity", &jobj_integrity)) { if (!json_contains(cd, jobj, key, "Segment", "integrity", json_type_object) || - !json_contains(cd, jobj_integrity, key, "Segment integrity", "type", json_type_string) || - !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_encryption", json_type_string) || - !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_integrity", json_type_string)) + !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "type") || + !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "journal_encryption") || + !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "journal_integrity")) return 1; } @@ -560,7 +580,12 @@ static int hdr_validate_crypt_segment(struct crypt_device *cd, return 1; } - return !segment_has_digest(key, jobj_digests); + r = segment_has_digest(key, jobj_digests); + + if (!r) + log_dbg(cd, "Crypt segment %s not assigned to key digest.", key); + + return !r; } static bool validate_segment_intervals(struct crypt_device *cd, @@ -577,6 +602,12 @@ static bool validate_segment_intervals(struct crypt_device *cd, for (j = 0; j < length; j++) { if (i == j) continue; + + if (ix[j].length != UINT64_MAX && ix[j].offset > (UINT64_MAX - ix[j].length)) { + log_dbg(cd, "Interval offset+length overflow."); + return false; + } + if ((ix[i].offset >= ix[j].offset) && (ix[j].length == UINT64_MAX || (ix[i].offset < (ix[j].offset + ix[j].length)))) { log_dbg(cd, "Overlapping segments [%" PRIu64 ",%" PRIu64 "]%s and [%" PRIu64 ",%" PRIu64 "]%s.", ix[i].offset, ix[i].offset + ix[i].length, ix[i].length == UINT64_MAX ? "(dynamic)" : "", @@ -670,10 +701,8 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj) int i, r, count, first_backup = -1; struct interval *intervals = NULL; - if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments)) { - log_dbg(cd, "Missing segments section."); + if (!(jobj_segments = json_contains(cd, hdr_jobj, "", "JSON area", "segments", json_type_object))) return 1; - } count = json_object_object_length(jobj_segments); if (count < 1) { @@ -690,20 +719,27 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj) return 1; /* those fields are mandatory for all segment types */ - if (!(jobj_type = json_contains(cd, val, key, "Segment", "type", json_type_string)) || - !(jobj_offset = json_contains(cd, val, key, "Segment", "offset", json_type_string)) || - !(jobj_size = json_contains(cd, val, key, "Segment", "size", json_type_string))) + if (!(jobj_type = json_contains_string(cd, val, key, "Segment", "type")) || + !(jobj_offset = json_contains_string(cd, val, key, "Segment", "offset")) || + !(jobj_size = json_contains_string(cd, val, key, "Segment", "size"))) + return 1; + + if (!numbered(cd, "offset", json_object_get_string(jobj_offset))) return 1; - if (!numbered(cd, "offset", json_object_get_string(jobj_offset)) || - !json_str_to_uint64(jobj_offset, &offset)) + if (!json_str_to_uint64(jobj_offset, &offset)) { + log_dbg(cd, "Illegal segment offset value."); return 1; + } /* size "dynamic" means whole device starting at 'offset' */ if (strcmp(json_object_get_string(jobj_size), "dynamic")) { - if (!numbered(cd, "size", json_object_get_string(jobj_size)) || - !json_str_to_uint64(jobj_size, &size) || !size) + if (!numbered(cd, "size", json_object_get_string(jobj_size))) return 1; + if (!json_str_to_uint64(jobj_size, &size) || !size) { + log_dbg(cd, "Illegal segment size value."); + return 1; + } } else size = 0; @@ -739,7 +775,7 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj) /* crypt */ if (!strcmp(json_object_get_string(jobj_type), "crypt") && - hdr_validate_crypt_segment(cd, val, key, jobj_digests, offset, size)) + hdr_validate_crypt_segment(cd, val, key, jobj_digests, size)) return 1; } @@ -846,9 +882,9 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_foreach(jobj_keyslots, key, val) { if (!(jobj_area = json_contains(cd, val, key, "Keyslot", "area", json_type_object)) || - !json_contains(cd, jobj_area, key, "Keyslot area", "type", json_type_string) || - !(jobj_offset = json_contains(cd, jobj_area, key, "Keyslot", "offset", json_type_string)) || - !(jobj_length = json_contains(cd, jobj_area, key, "Keyslot", "size", json_type_string)) || + !json_contains_string(cd, jobj_area, key, "Keyslot area", "type") || + !(jobj_offset = json_contains_string(cd, jobj_area, key, "Keyslot", "offset")) || + !(jobj_length = json_contains_string(cd, jobj_area, key, "Keyslot", "size")) || !numbered(cd, "offset", json_object_get_string(jobj_offset)) || !numbered(cd, "size", json_object_get_string(jobj_length))) { free(intervals); @@ -858,6 +894,7 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj) /* rule out values > UINT64_MAX */ if (!json_str_to_uint64(jobj_offset, &intervals[i].offset) || !json_str_to_uint64(jobj_length, &intervals[i].length)) { + log_dbg(cd, "Illegal keyslot area values."); free(intervals); return 1; } @@ -881,24 +918,22 @@ static int hdr_validate_digests(struct crypt_device *cd, json_object *hdr_jobj) { json_object *jarr_keys, *jarr_segs, *jobj, *jobj_keyslots, *jobj_segments; - if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj)) { - log_dbg(cd, "Missing digests section."); + if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "digests", json_type_object))) return 1; - } /* keyslots are not yet validated, but we need to know digest doesn't reference missing keyslot */ - if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots)) + if (!(jobj_keyslots = json_contains(cd, hdr_jobj, "", "JSON area", "keyslots", json_type_object))) return 1; /* segments are not yet validated, but we need to know digest doesn't reference missing segment */ - if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments)) + if (!(jobj_segments = json_contains(cd, hdr_jobj, "", "JSON area", "segments", json_type_object))) return 1; json_object_object_foreach(jobj, key, val) { if (!numbered(cd, "Digest", key)) return 1; - if (!json_contains(cd, val, key, "Digest", "type", json_type_string) || + if (!json_contains_string(cd, val, key, "Digest", "type") || !(jarr_keys = json_contains(cd, val, key, "Digest", "keyslots", json_type_array)) || !(jarr_segs = json_contains(cd, val, key, "Digest", "segments", json_type_array))) return 1; @@ -919,22 +954,26 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj) int i; uint64_t keyslots_size, metadata_size, segment_offset; - if (!json_object_object_get_ex(hdr_jobj, "config", &jobj_config)) { - log_dbg(cd, "Missing config section."); + if (!(jobj_config = json_contains(cd, hdr_jobj, "", "JSON area", "config", json_type_object))) return 1; - } - if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "json_size", json_type_string)) || - !json_str_to_uint64(jobj, &metadata_size)) + if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "json_size"))) + return 1; + if (!json_str_to_uint64(jobj, &metadata_size)) { + log_dbg(cd, "Illegal config json_size value."); return 1; + } /* single metadata instance is assembled from json area size plus * binary header size */ metadata_size += LUKS2_HDR_BIN_LEN; - if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "keyslots_size", json_type_string)) || - !json_str_to_uint64(jobj, &keyslots_size)) + if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "keyslots_size"))) + return 1; + if(!json_str_to_uint64(jobj, &keyslots_size)) { + log_dbg(cd, "Illegal config keyslot_size value."); return 1; + } if (LUKS2_check_metadata_area_size(metadata_size)) { log_dbg(cd, "Unsupported LUKS2 header size (%" PRIu64 ").", metadata_size); @@ -973,15 +1012,39 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj) return 0; } +static bool reencrypt_candidate_flag(const char *flag) +{ + const char *ptr; + + assert(flag); + + if (!strcmp(flag, "online-reencrypt")) + return true; + + if (strncmp(flag, "online-reencrypt-v", 18)) + return false; + + ptr = flag + 18; + if (!*ptr) + return false; + + while (*ptr) { + if (!isdigit(*ptr)) + return false; + ptr++; + } + + return true; +} + static int hdr_validate_requirements(struct crypt_device *cd, json_object *hdr_jobj) { int i; json_object *jobj_config, *jobj, *jobj1; + unsigned online_reencrypt_flag = 0; - if (!json_object_object_get_ex(hdr_jobj, "config", &jobj_config)) { - log_dbg(cd, "Missing config section."); + if (!(jobj_config = json_contains(cd, hdr_jobj, "", "JSON area", "config", json_type_object))) return 1; - } /* Requirements object is optional */ if (json_object_object_get_ex(jobj_config, "requirements", &jobj)) { @@ -994,12 +1057,22 @@ static int hdr_validate_requirements(struct crypt_device *cd, json_object *hdr_j return 1; /* All array members must be strings */ - for (i = 0; i < (int) json_object_array_length(jobj1); i++) + for (i = 0; i < (int) json_object_array_length(jobj1); i++) { if (!json_object_is_type(json_object_array_get_idx(jobj1, i), json_type_string)) return 1; + + if (reencrypt_candidate_flag(json_object_get_string(json_object_array_get_idx(jobj1, i)))) + online_reencrypt_flag++; + + } } } + if (online_reencrypt_flag > 1) { + log_dbg(cd, "Multiple online reencryption requirement flags detected."); + return 1; + } + return 0; } @@ -1036,6 +1109,33 @@ int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t return 0; } +static bool hdr_json_free(json_object **jobj) +{ + assert(jobj); + + if (json_object_put(*jobj)) + *jobj = NULL; + + return (*jobj == NULL); +} + +static int hdr_update_copy_for_rollback(struct crypt_device *cd, struct luks2_hdr *hdr) +{ + json_object **jobj_copy; + + assert(hdr); + assert(hdr->jobj); + + jobj_copy = (json_object **)&hdr->jobj_rollback; + + if (!hdr_json_free(jobj_copy)) { + log_dbg(cd, "LUKS2 rollback metadata copy still in use"); + return -EINVAL; + } + + return json_object_copy(hdr->jobj, jobj_copy) ? -ENOMEM : 0; +} + /* FIXME: should we expose do_recovery parameter explicitly? */ int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair) { @@ -1067,6 +1167,9 @@ int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair) } else device_read_unlock(cd, crypt_metadata_device(cd)); + if (!r && (r = hdr_update_copy_for_rollback(cd, hdr))) + log_dbg(cd, "Failed to update rollback LUKS2 metadata."); + return r; } @@ -1079,18 +1182,50 @@ static int hdr_cleanup_and_validate(struct crypt_device *cd, struct luks2_hdr *h int LUKS2_hdr_write_force(struct crypt_device *cd, struct luks2_hdr *hdr) { + int r; + if (hdr_cleanup_and_validate(cd, hdr)) return -EINVAL; - return LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), false); + r = LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), false); + + if (!r && (r = hdr_update_copy_for_rollback(cd, hdr))) + log_dbg(cd, "Failed to update rollback LUKS2 metadata."); + + return r; } int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr) { + int r; + if (hdr_cleanup_and_validate(cd, hdr)) return -EINVAL; - return LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), true); + r = LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), true); + + if (!r && (r = hdr_update_copy_for_rollback(cd, hdr))) + log_dbg(cd, "Failed to update rollback LUKS2 metadata."); + + return r; +} + +int LUKS2_hdr_rollback(struct crypt_device *cd, struct luks2_hdr *hdr) +{ + json_object **jobj_copy; + + assert(hdr->jobj_rollback); + + log_dbg(cd, "Rolling back in-memory LUKS2 json metadata."); + + jobj_copy = (json_object **)&hdr->jobj; + + if (!hdr_json_free(jobj_copy)) { + log_dbg(cd, "LUKS2 header still in use"); + return -EINVAL; + } + + return json_object_copy(hdr->jobj_rollback, jobj_copy) ? -ENOMEM : 0; } int LUKS2_hdr_uuid(struct crypt_device *cd, struct luks2_hdr *hdr, const char *uuid) @@ -1127,10 +1262,19 @@ int LUKS2_hdr_labels(struct crypt_device *cd, struct luks2_hdr *hdr, void LUKS2_hdr_free(struct crypt_device *cd, struct luks2_hdr *hdr) { - if (json_object_put(hdr->jobj)) - hdr->jobj = NULL; - else if (hdr->jobj) + json_object **jobj; + + assert(hdr); + + jobj = (json_object **)&hdr->jobj; + + if (!hdr_json_free(jobj)) log_dbg(cd, "LUKS2 header still in use"); + + jobj = (json_object **)&hdr->jobj_rollback; + + if (!hdr_json_free(jobj)) + log_dbg(cd, "LUKS2 rollback metadata copy still in use"); } static uint64_t LUKS2_keyslots_size_jobj(json_object *jobj) @@ -1172,7 +1316,7 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr, hdr_size = LUKS2_hdr_and_areas_size(hdr); buffer_size = size_round_up(hdr_size, crypt_getpagesize()); - buffer = crypt_safe_alloc(buffer_size); + buffer = malloc(buffer_size); if (!buffer) return -ENOMEM; @@ -1183,23 +1327,22 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr, if (r) { log_err(cd, _("Failed to acquire read lock on device %s."), device_path(crypt_metadata_device(cd))); - crypt_safe_free(buffer); - return r; + goto out; } devfd = device_open_locked(cd, device, O_RDONLY); if (devfd < 0) { device_read_unlock(cd, device); log_err(cd, _("Device %s is not a valid LUKS device."), device_path(device)); - crypt_safe_free(buffer); - return devfd == -1 ? -EINVAL : devfd; + r = (devfd == -1) ? -EINVAL : devfd; + goto out; } if (read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), buffer, hdr_size, 0) < hdr_size) { device_read_unlock(cd, device); - crypt_safe_free(buffer); - return -EIO; + r = -EIO; + goto out; } device_read_unlock(cd, device); @@ -1210,8 +1353,8 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr, log_err(cd, _("Requested header backup file %s already exists."), backup_file); else log_err(cd, _("Cannot create header backup file %s."), backup_file); - crypt_safe_free(buffer); - return -EINVAL; + r = -EINVAL; + goto out; } ret = write_buffer(fd, buffer, buffer_size); close(fd); @@ -1220,8 +1363,9 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr, r = -EIO; } else r = 0; - - crypt_safe_free(buffer); +out: + crypt_safe_memzero(buffer, buffer_size); + free(buffer); return r; } @@ -1232,8 +1376,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr, int r, fd, devfd = -1, diff_uuid = 0; ssize_t ret, buffer_size = 0; char *buffer = NULL, msg[1024]; - struct luks2_hdr hdr_file; - struct luks2_hdr tmp_hdr = {}; + struct luks2_hdr hdr_file = {}, tmp_hdr = {}; uint32_t reqs = 0; r = device_alloc(cd, &backup_device, backup_file); @@ -1266,7 +1409,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr, } buffer_size = LUKS2_hdr_and_areas_size(&hdr_file); - buffer = crypt_safe_alloc(buffer_size); + buffer = malloc(buffer_size); if (!buffer) { r = -ENOMEM; goto out; @@ -1366,10 +1509,9 @@ out: LUKS2_hdr_free(cd, &tmp_hdr); crypt_safe_memzero(&hdr_file, sizeof(hdr_file)); crypt_safe_memzero(&tmp_hdr, sizeof(tmp_hdr)); - crypt_safe_free(buffer); - + crypt_safe_memzero(buffer, buffer_size); + free(buffer); device_sync(cd, device); - return r; } @@ -1462,7 +1604,7 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3 /* LUKS2 library requirements */ struct requirement_flag { uint32_t flag; - uint32_t version; + uint8_t version; const char *description; }; @@ -1471,6 +1613,7 @@ static const struct requirement_flag unknown_requirement_flag = { CRYPT_REQUIREM static const struct requirement_flag requirements_flags[] = { { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT,1, "offline-reencrypt" }, { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" }, + { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 3, "online-reencrypt-v3" }, { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" }, { 0, 0, NULL } }; @@ -1486,23 +1629,58 @@ static const struct requirement_flag *get_requirement_by_name(const char *requir return &unknown_requirement_flag; } -int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint32_t *version) +static json_object *mandatory_requirements_jobj(struct luks2_hdr *hdr) { - json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; - int i, len; - const struct requirement_flag *req; + json_object *jobj_config, *jobj_requirements, *jobj_mandatory; - assert(hdr && version); - if (!hdr || !version) - return -EINVAL; + assert(hdr); if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) - return -EINVAL; + return NULL; if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) - return -ENOENT; + return NULL; if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) + return NULL; + + return jobj_mandatory; +} + +bool LUKS2_reencrypt_requirement_candidate(struct luks2_hdr *hdr) +{ + json_object *jobj_mandatory; + int i, len; + + assert(hdr); + + jobj_mandatory = mandatory_requirements_jobj(hdr); + if (!jobj_mandatory) + return false; + + len = (int) json_object_array_length(jobj_mandatory); + if (len <= 0) + return false; + + for (i = 0; i < len; i++) { + if (reencrypt_candidate_flag(json_object_get_string(json_object_array_get_idx(jobj_mandatory, i)))) + return true; + } + + return false; +} + +int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint8_t *version) +{ + json_object *jobj_mandatory, *jobj; + int i, len; + const struct requirement_flag *req; + + assert(hdr); + assert(version); + + jobj_mandatory = mandatory_requirements_jobj(hdr); + if (!jobj_mandatory) return -ENOENT; len = (int) json_object_array_length(jobj_mandatory); @@ -1518,7 +1696,7 @@ int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint32_t *version) /* check current library is aware of the requirement */ req = get_requirement_by_name(json_object_get_string(jobj)); - if (req->flag == (uint32_t)CRYPT_REQUIREMENT_UNKNOWN) + if (req->flag == CRYPT_REQUIREMENT_UNKNOWN) continue; *version = req->version; @@ -1531,26 +1709,19 @@ int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint32_t *version) static const struct requirement_flag *stored_requirement_name_by_id(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id) { - json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + json_object *jobj_mandatory, *jobj; int i, len; const struct requirement_flag *req; assert(hdr); - if (!hdr) - return NULL; - - if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) - return NULL; - if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) - return NULL; - - if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) + jobj_mandatory = mandatory_requirements_jobj(hdr); + if (!jobj_mandatory) return NULL; len = (int) json_object_array_length(jobj_mandatory); if (len <= 0) - return 0; + return NULL; for (i = 0; i < len; i++) { jobj = json_object_array_get_idx(jobj_mandatory, i); @@ -1567,23 +1738,17 @@ static const struct requirement_flag *stored_requirement_name_by_id(struct crypt */ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs) { - json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + json_object *jobj_mandatory, *jobj; int i, len; const struct requirement_flag *req; assert(hdr); - if (!hdr || !reqs) - return -EINVAL; + assert(reqs); *reqs = 0; - if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) - return 0; - - if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) - return 0; - - if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) + jobj_mandatory = mandatory_requirements_jobj(hdr); + if (!jobj_mandatory) return 0; len = (int) json_object_array_length(jobj_mandatory); @@ -1673,6 +1838,94 @@ err: return r; } +static json_object *LUKS2_get_mandatory_requirements_filtered_jobj(struct luks2_hdr *hdr, + uint32_t filter_req_ids) +{ + int i, len; + const struct requirement_flag *req; + json_object *jobj_mandatory, *jobj_mandatory_filtered, *jobj; + + jobj_mandatory_filtered = json_object_new_array(); + if (!jobj_mandatory_filtered) + return NULL; + + jobj_mandatory = mandatory_requirements_jobj(hdr); + if (!jobj_mandatory) + return jobj_mandatory_filtered; + + len = (int) json_object_array_length(jobj_mandatory); + + for (i = 0; i < len; i++) { + jobj = json_object_array_get_idx(jobj_mandatory, i); + req = get_requirement_by_name(json_object_get_string(jobj)); + if (req->flag == CRYPT_REQUIREMENT_UNKNOWN || req->flag & filter_req_ids) + continue; + json_object_array_add(jobj_mandatory_filtered, + json_object_new_string(req->description)); + } + + return jobj_mandatory_filtered; +} + +/* + * The function looks for specific version of requirement id. + * If it can't be fulfilled function fails. + */ +int LUKS2_config_set_requirement_version(struct crypt_device *cd, + struct luks2_hdr *hdr, + uint32_t req_id, + uint8_t req_version, + bool commit) +{ + json_object *jobj_config, *jobj_requirements, *jobj_mandatory; + const struct requirement_flag *req; + int r = -EINVAL; + + if (!hdr || req_id == CRYPT_REQUIREMENT_UNKNOWN) + return -EINVAL; + + req = requirements_flags; + + while (req->description) { + /* we have a match */ + if (req->flag == req_id && req->version == req_version) + break; + req++; + } + + if (!req->description) + return -EINVAL; + + /* + * Creates copy of mandatory requirements set without specific requirement + * (no matter the version) we want to set. + */ + jobj_mandatory = LUKS2_get_mandatory_requirements_filtered_jobj(hdr, req_id); + if (!jobj_mandatory) + return -ENOMEM; + + json_object_array_add(jobj_mandatory, json_object_new_string(req->description)); + + if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) + goto err; + + if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) { + jobj_requirements = json_object_new_object(); + if (!jobj_requirements) { + r = -ENOMEM; + goto err; + } + json_object_object_add(jobj_config, "requirements", jobj_requirements); + } + + json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory); + + return commit ? LUKS2_hdr_write(cd, hdr) : 0; +err: + json_object_put(jobj_mandatory); + return r; +} + /* * Header dump */ @@ -1741,7 +1994,8 @@ static void hdr_dump_keyslots(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(hdr_jobj, "keyslots", &keyslots_jobj); for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++) { - (void) snprintf(slot, sizeof(slot), "%i", j); + if (snprintf(slot, sizeof(slot), "%i", j) < 0) + slot[0] = '\0'; json_object_object_get_ex(keyslots_jobj, slot, &val); if (!val) continue; @@ -1783,7 +2037,8 @@ static void hdr_dump_tokens(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(hdr_jobj, "tokens", &tokens_jobj); for (j = 0; j < LUKS2_TOKENS_MAX; j++) { - (void) snprintf(token, sizeof(token), "%i", j); + if (snprintf(token, sizeof(token), "%i", j) < 0) + token[0] = '\0'; json_object_object_get_ex(tokens_jobj, token, &val); if (!val) continue; @@ -1797,7 +2052,7 @@ static void hdr_dump_tokens(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(val, "keyslots", &jobj2); for (i = 0; i < (int) json_object_array_length(jobj2); i++) { jobj3 = json_object_array_get_idx(jobj2, i); - log_std(cd, "\tKeyslot: %s\n", json_object_get_string(jobj3)); + log_std(cd, "\tKeyslot: %s\n", json_object_get_string(jobj3)); } } } @@ -1813,7 +2068,8 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments); for (i = 0; i < LUKS2_SEGMENT_MAX; i++) { - (void) snprintf(segment, sizeof(segment), "%i", i); + if (snprintf(segment, sizeof(segment), "%i", i) < 0) + segment[0] = '\0'; if (!json_object_object_get_ex(jobj_segments, segment, &jobj_segment)) continue; @@ -1868,7 +2124,8 @@ static void hdr_dump_digests(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(hdr_jobj, "digests", &jobj1); for (i = 0; i < LUKS2_DIGEST_MAX; i++) { - (void) snprintf(key, sizeof(key), "%i", i); + if (snprintf(key, sizeof(key), "%i", i) < 0) + key[0] = '\0'; json_object_object_get_ex(jobj1, key, &val); if (!val) continue; @@ -1906,23 +2163,45 @@ int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr) return 0; } +int LUKS2_hdr_dump_json(struct crypt_device *cd, struct luks2_hdr *hdr, const char **json) +{ + const char *json_buf; + + json_buf = json_object_to_json_string_ext(hdr->jobj, + JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE); + + if (!json_buf) + return -EINVAL; + + if (json) + *json = json_buf; + else + crypt_log(cd, CRYPT_LOG_NORMAL, json_buf); + + return 0; +} + int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size, bool *dynamic) { - int sector_size; - json_object *jobj_segments, *jobj_size; + int i, len, sector_size; + json_object *jobj_segments, *jobj_segment, *jobj_size; uint64_t tmp = 0; if (!size || !json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments)) return -EINVAL; - json_object_object_foreach(jobj_segments, key, val) { - UNUSED(key); - if (json_segment_is_backup(val)) - continue; + len = json_object_object_length(jobj_segments); + + for (i = 0; i < len; i++) { + if (!(jobj_segment = json_segments_get_segment(jobj_segments, i))) + return -EINVAL; + + if (json_segment_is_backup(jobj_segment)) + break; - json_object_object_get_ex(val, "size", &jobj_size); + json_object_object_get_ex(jobj_segment, "size", &jobj_size); if (!strcmp(json_object_get_string(jobj_size), "dynamic")) { - sector_size = json_segment_get_sector_size(val); + sector_size = json_segment_get_sector_size(jobj_segment); /* last dynamic segment must have at least one sector in size */ if (tmp) *size = tmp + (sector_size > 0 ? sector_size : SECTOR_SIZE); @@ -2129,15 +2408,9 @@ int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment) return -1; } -int LUKS2_get_sector_size(struct luks2_hdr *hdr) +uint32_t LUKS2_get_sector_size(struct luks2_hdr *hdr) { - json_object *jobj_segment; - - jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT); - if (!jobj_segment) - return SECTOR_SIZE; - - return json_segment_get_sector_size(jobj_segment) ?: SECTOR_SIZE; + return json_segment_get_sector_size(LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT)); } int LUKS2_assembly_multisegment_dmd(struct crypt_device *cd, @@ -2414,7 +2687,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr tgt = &dmd->segment; /* TODO: We have LUKS2 dependencies now */ - if (hdr && single_segment(dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd)) + if (single_segment(dmd) && tgt->type == DM_CRYPT && tgt->u.crypt.tag_size) namei = device_dm_name(tgt->data_device); r = dm_device_deps(cd, name, deps_uuid_prefix, deps, ARRAY_SIZE(deps));