From ba9e36ceae276778155eccbe0af23f1f49959c46 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 8 Jun 2022 14:35:23 +0200 Subject: [PATCH] Add empty string check to LUKS2 JSON validation. Most of the LUKS2 fields cannot be empty, add check for JSON validation for it to fail early. Fixes: #746 --- lib/luks2/luks2_internal.h | 2 + lib/luks2/luks2_json_metadata.c | 46 ++++++++++++------- lib/luks2/luks2_keyslot_luks2.c | 20 ++++---- lib/luks2/luks2_keyslot_reenc.c | 12 ++--- ...uks2-segment-crypt-empty-encryption.img.sh | 39 ++++++++++++++++ tests/luks2-validation-test | 1 + 6 files changed, 88 insertions(+), 32 deletions(-) create mode 100755 tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h index 21781abd..affdaaee 100644 --- a/lib/luks2/luks2_internal.h +++ b/lib/luks2/luks2_internal.h @@ -75,6 +75,8 @@ void JSON_DBG(struct crypt_device *cd, json_object *jobj, const char *desc); json_bool validate_json_uint32(json_object *jobj); json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name, const char *section, const char *key, json_type type); +json_object *json_contains_string(struct crypt_device *cd, json_object *jobj, + const char *name, const char *section, const char *key); int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t json_size); int LUKS2_check_json_size(struct crypt_device *cd, const struct luks2_hdr *hdr); diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index eb1c6f78..2ccb0bf3 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -292,6 +292,20 @@ json_object *json_contains(struct crypt_device *cd, json_object *jobj, const cha return sobj; } +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; +} + json_bool validate_json_uint32(json_object *jobj) { int64_t tmp; @@ -406,7 +420,7 @@ static int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_keys { 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; @@ -430,7 +444,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); @@ -518,17 +532,17 @@ static int hdr_validate_crypt_segment(struct crypt_device *cd, json_object *jobj 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; } @@ -689,9 +703,9 @@ 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)) || @@ -845,9 +859,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); @@ -895,7 +909,7 @@ static int hdr_validate_digests(struct crypt_device *cd, json_object *hdr_jobj) 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,7 +933,7 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj) 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)) || + if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "json_size")) || !json_str_to_uint64(jobj, &metadata_size)) return 1; @@ -927,7 +941,7 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj) * binary header size */ metadata_size += LUKS2_HDR_BIN_LEN; - if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "keyslots_size", json_type_string)) || + if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "keyslots_size")) || !json_str_to_uint64(jobj, &keyslots_size)) return 1; diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c index a76b245a..80eefa08 100644 --- a/lib/luks2/luks2_keyslot_luks2.c +++ b/lib/luks2/luks2_keyslot_luks2.c @@ -680,49 +680,49 @@ static int luks2_keyslot_validate(struct crypt_device *cd, json_object *jobj_key count = json_object_object_length(jobj_kdf); - jobj1 = json_contains(cd, jobj_kdf, "", "kdf section", "type", json_type_string); + jobj1 = json_contains_string(cd, jobj_kdf, "", "kdf section", "type"); if (!jobj1) return -EINVAL; type = json_object_get_string(jobj1); if (!strcmp(type, CRYPT_KDF_PBKDF2)) { if (count != 4 || /* type, salt, hash, iterations only */ - !json_contains(cd, jobj_kdf, "kdf type", type, "hash", json_type_string) || + !json_contains_string(cd, jobj_kdf, "kdf type", type, "hash") || !json_contains(cd, jobj_kdf, "kdf type", type, "iterations", json_type_int) || - !json_contains(cd, jobj_kdf, "kdf type", type, "salt", json_type_string)) + !json_contains_string(cd, jobj_kdf, "kdf type", type, "salt")) return -EINVAL; } else if (!strcmp(type, CRYPT_KDF_ARGON2I) || !strcmp(type, CRYPT_KDF_ARGON2ID)) { if (count != 5 || /* type, salt, time, memory, cpus only */ !json_contains(cd, jobj_kdf, "kdf type", type, "time", json_type_int) || !json_contains(cd, jobj_kdf, "kdf type", type, "memory", json_type_int) || !json_contains(cd, jobj_kdf, "kdf type", type, "cpus", json_type_int) || - !json_contains(cd, jobj_kdf, "kdf type", type, "salt", json_type_string)) + !json_contains_string(cd, jobj_kdf, "kdf type", type, "salt")) return -EINVAL; } - jobj1 = json_contains(cd, jobj_af, "", "af section", "type", json_type_string); + jobj1 = json_contains_string(cd, jobj_af, "", "af section", "type"); if (!jobj1) return -EINVAL; type = json_object_get_string(jobj1); if (!strcmp(type, "luks1")) { - if (!json_contains(cd, jobj_af, "", "luks1 af", "hash", json_type_string) || + if (!json_contains_string(cd, jobj_af, "", "luks1 af", "hash") || !json_contains(cd, jobj_af, "", "luks1 af", "stripes", json_type_int)) return -EINVAL; } else return -EINVAL; // FIXME check numbered - jobj1 = json_contains(cd, jobj_area, "", "area section", "type", json_type_string); + jobj1 = json_contains_string(cd, jobj_area, "", "area section", "type"); if (!jobj1) return -EINVAL; type = json_object_get_string(jobj1); if (!strcmp(type, "raw")) { - if (!json_contains(cd, jobj_area, "area", "raw type", "encryption", json_type_string) || + if (!json_contains_string(cd, jobj_area, "area", "raw type", "encryption") || !json_contains(cd, jobj_area, "area", "raw type", "key_size", json_type_int) || - !json_contains(cd, jobj_area, "area", "raw type", "offset", json_type_string) || - !json_contains(cd, jobj_area, "area", "raw type", "size", json_type_string)) + !json_contains_string(cd, jobj_area, "area", "raw type", "offset") || + !json_contains_string(cd, jobj_area, "area", "raw type", "size")) return -EINVAL; } else return -EINVAL; diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c index 614ce5f8..7ea6a964 100644 --- a/lib/luks2/luks2_keyslot_reenc.c +++ b/lib/luks2/luks2_keyslot_reenc.c @@ -310,8 +310,8 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key return -EINVAL; jobj_key_size = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "key_size", json_type_int); - jobj_mode = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "mode", json_type_string); - jobj_direction = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "direction", json_type_string); + jobj_mode = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "mode"); + jobj_direction = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "direction"); if (!jobj_mode || !jobj_direction || !jobj_key_size) return -EINVAL; @@ -337,8 +337,8 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key } if (!strcmp(type, "checksum") || !strcmp(type, "datashift-checksum")) { - jobj_hash = json_contains(cd, jobj_area, "type:checksum", - "Keyslot area", "hash", json_type_string); + jobj_hash = json_contains_string(cd, jobj_area, "type:checksum", + "Keyslot area", "hash"); jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "sector_size", json_type_int); if (!jobj_hash || !jobj_sector_size) @@ -354,8 +354,8 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key } else if (!strcmp(type, "datashift") || !strcmp(type, "datashift-checksum") || !strcmp(type, "datashift-journal")) { - if (!(jobj_shift_size = json_contains(cd, jobj_area, "type:datashift", - "Keyslot area", "shift_size", json_type_string))) + if (!(jobj_shift_size = json_contains_string(cd, jobj_area, "type:datashift", + "Keyslot area", "shift_size"))) return -EINVAL; shift_size = crypt_jobj_get_uint64(jobj_shift_size); diff --git a/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh b/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh new file mode 100755 index 00000000..ca17aaca --- /dev/null +++ b/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +. lib.sh + +# +# *** Description *** +# +# generate primary header with segment empty encryption field +# +# secondary header is corrupted on purpose as well +# + +# $1 full target dir +# $2 full source luks2 image + +function generate() +{ + # remove mandatory encryption field + json_str=$(jq -c '.segments."0".encryption = ""' $TMPDIR/json0) + test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2 + + write_luks2_json "$json_str" $TMPDIR/json0 + + lib_mangle_json_hdr0_kill_hdr1 +} + +function check() +{ + lib_hdr1_killed || exit 2 + + read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 + jq -c 'if .segments."0".encryption != "" + then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5 +} + +lib_prepare $@ +generate +check +lib_cleanup diff --git a/tests/luks2-validation-test b/tests/luks2-validation-test index d97f7d8f..b79ff57d 100755 --- a/tests/luks2-validation-test +++ b/tests/luks2-validation-test @@ -204,6 +204,7 @@ RUN luks2-segment-wrong-flags.img "F" "Failed to detect invalid flags field" RUN luks2-segment-wrong-flags-element.img "F" "Failed to detect invalid flags content" RUN luks2-segment-wrong-backup-key-0.img "F" "Failed to detect gap in backup segments" RUN luks2-segment-wrong-backup-key-1.img "F" "Failed to detect gap in backup segments" +RUN luks2-segment-crypt-empty-encryption.img "F" "Failed to detect empty encryption field" echo "[6] Test metadata size and keyslots size (config section)" RUN luks2-invalid-keyslots-size-c0.img "F" "Failed to detect too large keyslots_size in config section"