/*
* 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
#include "luks2_internal.h"
#include "../integrity/integrity.h"
-#include <assert.h>
#include <ctype.h>
#include <uuid/uuid.h>
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++) {
if (s >= 0)
return s;
- if (LUKS2_segments_count(hdr) == 1)
+ if (LUKS2_segments_count(hdr) >= 1)
return 0;
return -EINVAL;
}
/* 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;
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)
/*
* 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,
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);
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);
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;
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;
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);
{
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;
}
{
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))
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;
}
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,
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)" : "",
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) {
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;
/* 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;
}
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);
/* 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;
}
{
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;
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);
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)) {
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;
}
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)
{
} 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;
}
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)
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)
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;
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);
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);
r = -EIO;
} else
r = 0;
-
- crypt_safe_free(buffer);
+out:
+ crypt_safe_memzero(buffer, buffer_size);
+ free(buffer);
return r;
}
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);
}
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;
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;
}
/* LUKS2 library requirements */
struct requirement_flag {
uint32_t flag;
- uint32_t version;
+ uint8_t version;
const char *description;
};
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 }
};
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);
/* 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;
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);
*/
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);
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
*/
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;
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;
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));
}
}
}
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;
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;
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);
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,
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));