Merge branch 'upstream' into tizen
[platform/upstream/cryptsetup.git] / lib / luks2 / luks2_json_metadata.c
index 8469257..4771f04 100644 (file)
@@ -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 <assert.h>
 #include <ctype.h>
 #include <uuid/uuid.h>
 
@@ -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));