Add resilient LUKS2 reencryption library code.

This commit is contained in:
Ondrej Kozina
2015-10-02 17:21:10 +02:00
parent a5c5e3e876
commit a7f80a2770
17 changed files with 4942 additions and 191 deletions

View File

@@ -99,6 +99,8 @@ libcryptsetup_la_SOURCES = \
lib/luks2/luks2_digest_pbkdf2.c \
lib/luks2/luks2_keyslot.c \
lib/luks2/luks2_keyslot_luks2.c \
lib/luks2/luks2_keyslot_reenc.c \
lib/luks2/luks2_reencrypt.c \
lib/luks2/luks2_segment.c \
lib/luks2/luks2_token_keyring.c \
lib/luks2/luks2_token.c \

View File

@@ -40,6 +40,7 @@
#include "utils_keyring.h"
#include "utils_io.h"
#include "crypto_backend.h"
#include "utils_storage_wrappers.h"
#include "libcryptsetup.h"
@@ -73,6 +74,7 @@
} while (0)
struct crypt_device;
struct luks2_reenc_context;
struct volume_key {
int id;
@@ -120,6 +122,7 @@ size_t device_block_size(struct crypt_device *cd, struct device *device);
int device_read_ahead(struct device *device, uint32_t *read_ahead);
int device_size(struct device *device, uint64_t *size);
int device_open(struct crypt_device *cd, struct device *device, int flags);
int device_open_excl(struct crypt_device *cd, struct device *device, int flags);
void device_disable_direct_io(struct device *device);
int device_is_identical(struct device *device1, struct device *device2);
int device_is_rotational(struct device *device);
@@ -207,6 +210,11 @@ int PLAIN_activate(struct crypt_device *cd,
uint32_t flags);
void *crypt_get_hdr(struct crypt_device *cd, const char *type);
void crypt_set_reenc_context(struct crypt_device *cd, struct luks2_reenc_context *rh);
struct luks2_reenc_context *crypt_get_reenc_context(struct crypt_device *cd);
int onlyLUKS2(struct crypt_device *cd);
int onlyLUKS2mask(struct crypt_device *cd, uint32_t mask);
int crypt_wipe_device(struct crypt_device *cd,
struct device *device,

View File

@@ -1108,6 +1108,8 @@ uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd,
*/
/** Unfinished offline reencryption */
#define CRYPT_REQUIREMENT_OFFLINE_REENCRYPT (1 << 0)
/** Online reencryption in-progress */
#define CRYPT_REQUIREMENT_ONLINE_REENCRYPT (1 << 1)
/** unknown requirement in header (output only) */
#define CRYPT_REQUIREMENT_UNKNOWN (1 << 31)
@@ -2103,6 +2105,134 @@ int crypt_activate_by_token(struct crypt_device *cd,
uint32_t flags);
/** @} */
/**
* @defgroup crypt-reencryption LUKS2 volume reencryption support
*
* Set of functions to handling LUKS2 volume reencryption
*
* @addtogroup crypt-reencryption
* @{
*/
/** Initialize reencryption metadata but do not run reencryption yet. */
#define CRYPT_REENCRYPT_INITIALIZE_ONLY (1 << 0)
/** Move the first segment; used only with data shift. */
#define CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT (1 << 1)
/**
* Reencryption direction
*/
typedef enum {
CRYPT_REENCRYPT_FORWARD = 0, /**< forward direction */
CRYPT_REENCRYPT_BACKWARD /**< backward direction */
} crypt_reencrypt_direction_info;
/**
* LUKS2 reencryption options.
*/
struct crypt_params_reencrypt {
const char *mode; /**< Mode as "encrypt" / "reencrypt" / "decrypt", immutable after first init. */
crypt_reencrypt_direction_info direction; /**< Reencryption direction, immutable after first init. */
const char *resilience; /**< Resilience mode: "none", "checksum", "journal" or "shift" (only "shift" is immutable after init) */
const char *hash; /**< Used hash for "checksum" resilience type, ignored otherwise. */
uint64_t data_shift; /**< Used in "shift" mode, must be non-zero, immutable after first init. */
uint64_t max_hotzone_size; /**< Hotzone size for "none" mode; maximum hotzone size for "checksum" mode. */
const struct crypt_params_luks2 *luks2; /**< LUKS2 parameters for the final reencryption volume.*/
uint32_t flags; /**< Reencryption flags. */
};
/**
* Initialize reencryption metadata using passphrase.
*
* This function initializes on-disk metadata to include all reencryption segments,
* according to the provided options.
* If metadata already contains ongoing reencryption metadata, it loads these parameters
* (in this situation all parameters except @e name and @e passphrase can be omitted).
*
* @param cd crypt device handle
* @param name name of active device or @e NULL for offline reencryption
* @param passphrase passphrase used to unlock volume key
* @param passphrase_size size of @e passphrase (binary data)
* @param keyslot_old keyslot to unlock existing device or CRYPT_ANY_SLOT
* @param keyslot_new existing (unbound) reencryption keyslot; must be set except for decryption
* @param cipher cipher specification (e.g. "aes")
* @param cipher_mode cipher mode and IV (e.g. "xts-plain64")
* @param params reencryption parameters @link crypt_params_reencrypt @endlink.
*
* @return reencryption key slot number or negative errno otherwise.
*/
int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd,
const char *name,
const char *passphrase,
size_t passphrase_size,
int keyslot_old,
int keyslot_new,
const char *cipher,
const char *cipher_mode,
const struct crypt_params_reencrypt *params);
/**
* Initialize reencryption metadata using passphrase in keyring.
*
* This function initializes on-disk metadata to include all reencryption segments,
* according to the provided options.
* If metadata already contains ongoing reencryption metadata, it loads these parameters
* (in this situation all parameters except @e name and @e key_description can be omitted).
*
* @param cd crypt device handle
* @param name name of active device or @e NULL for offline reencryption
* @param key_description passphrase (key) identification in keyring
* @param keyslot_old keyslot to unlock existing device or CRYPT_ANY_SLOT
* @param keyslot_new existing (unbound) reencryption keyslot; must be set except for decryption
* @param cipher cipher specification (e.g. "aes")
* @param cipher_mode cipher mode and IV (e.g. "xts-plain64")
* @param params reencryption parameters @link crypt_params_reencrypt @endlink.
*
* @return reencryption key slot number or negative errno otherwise.
*/
int crypt_reencrypt_init_by_keyring(struct crypt_device *cd,
const char *name,
const char *key_description,
int keyslot_old,
int keyslot_new,
const char *cipher,
const char *cipher_mode,
const struct crypt_params_reencrypt *params);
/**
* Run data reencryption.
*
* @param cd crypt device handle
* @param progress is a callback funtion reporting device \b size,
* current \b offset of reencryption and provided \b usrptr identification
*
* @return @e 0 on success or negative errno value otherwise.
*/
int crypt_reencrypt(struct crypt_device *cd,
int (*progress)(uint64_t size, uint64_t offset, void *usrptr));
/**
* Reencryption status info
*/
typedef enum {
CRYPT_REENCRYPT_NONE = 0, /**< No reencryption in progress */
CRYPT_REENCRYPT_CLEAN, /**< Ongoing reencryption in a clean state. */
CRYPT_REENCRYPT_CRASH, /**< Aborted reencryption that need internal recovery. */
CRYPT_REENCRYPT_INVALID /**< Invalid state. */
} crypt_reencrypt_info;
/**
* LUKS2 reencryption status.
*
* @param cd crypt device handle
* @param params reecryption parameters
*
* @return reencryption status info and parameters.
*/
crypt_reencrypt_info crypt_reencrypt_status(struct crypt_device *cd,
struct crypt_params_reencrypt *params);
/** @} */
#ifdef __cplusplus
}
#endif

View File

@@ -113,6 +113,11 @@ CRYPTSETUP_2.0 {
crypt_keyfile_device_read;
crypt_wipe;
crypt_reencrypt_init_by_passphrase;
crypt_reencrypt_init_by_keyring;
crypt_reencrypt;
crypt_reencrypt_status;
local:
*;
};

View File

@@ -48,9 +48,13 @@
#define CRYPT_ANY_SEGMENT -1
#define CRYPT_DEFAULT_SEGMENT -2
#define CRYPT_ONE_SEGMENT -3
#define CRYPT_ANY_DIGEST -1
/* 20 MiBs */
#define LUKS2_DEFAULT_REENCRYPTION_LENGTH 0x1400000
/*
* LUKS2 header on-disk.
*
@@ -118,6 +122,80 @@ struct luks2_keyslot_params {
} area;
};
struct reenc_protection {
enum { REENC_PROTECTION_NOOP = 0, /* none should be 0 always */
REENC_PROTECTION_CHECKSUM,
REENC_PROTECTION_JOURNAL,
REENC_PROTECTION_DATASHIFT } type;
union {
struct {
} none;
struct {
char hash[LUKS2_CHECKSUM_ALG_L]; // or include luks.h
struct crypt_hash *ch;
size_t hash_size;
/* buffer for checksums */
void *checksums;
size_t checksums_len;
} csum;
struct {
} ds;
} p;
};
struct luks2_reenc_context {
/* reencryption window attributes */
uint64_t offset;
uint64_t progress;
uint64_t length;
uint64_t data_shift;
size_t alignment;
uint64_t device_size;
bool online;
crypt_reencrypt_direction_info direction;
enum { REENCRYPT = 0, ENCRYPT, DECRYPT } type;
char *device_name;
char *hotzone_name;
char *overlay_name;
/* reencryption window persistence attributes */
struct reenc_protection rp;
int reenc_keyslot;
/* already running reencryption */
json_object *jobj_segs_pre;
json_object *jobj_segs_after;
/* backup segments */
json_object *jobj_segment_new;
int digest_new;
json_object *jobj_segment_old;
int digest_old;
json_object *jobj_segment_moved;
struct volume_key *vks;
void *reenc_buffer;
ssize_t read;
struct crypt_storage_wrapper *cw1;
struct crypt_storage_wrapper *cw2;
uint32_t wflags1;
uint32_t wflags2;
struct crypt_lock_handle *reenc_lock;
};
int LUKS2_reenc_load_segments(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct luks2_reenc_context *rh);
crypt_reencrypt_info LUKS2_reenc_status(struct luks2_hdr *hdr);
/*
* Supportable header sizes (hdr_disk + JSON area)
* Also used as offset for the 2nd header.
@@ -179,6 +257,13 @@ int LUKS2_keyslot_open(struct crypt_device *cd,
size_t password_len,
struct volume_key **vk);
int LUKS2_keyslot_open_all_segments(struct crypt_device *cd,
int keyslot_old,
int keyslot_new,
const char *password,
size_t password_len,
struct volume_key **vks);
int LUKS2_keyslot_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -187,6 +272,20 @@ int LUKS2_keyslot_store(struct crypt_device *cd,
const struct volume_key *vk,
const struct luks2_keyslot_params *params);
int LUKS2_keyslot_reencrypt_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const void *buffer,
size_t buffer_length);
int LUKS2_keyslot_reencrypt_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const struct crypt_params_reencrypt *params);
int reenc_keyslot_update(struct crypt_device *cd,
const struct luks2_reenc_context *rh);
int LUKS2_keyslot_wipe(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -274,6 +373,8 @@ uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise);
const char *json_segment_get_cipher(json_object *jobj_segment);
int json_segment_get_sector_size(json_object *jobj_segment);
json_object *json_segment_get_flags(json_object *jobj_segment);
bool json_segment_is_backup(json_object *jobj_segment);
bool json_segment_is_reencrypt(json_object *jobj_segment);
json_object *json_segments_get_segment(json_object *jobj_segments, int segment);
int json_segments_count(json_object *jobj_segments);
json_object *json_segments_get_segment_by_flag(json_object *jobj_segments, const char *flag);
@@ -293,8 +394,6 @@ json_object *LUKS2_get_segment_by_flag(struct luks2_hdr *hdr, const char *flag);
int LUKS2_get_segment_id_by_flag(struct luks2_hdr *hdr, const char *flag);
int LUKS2_segment_ignore(json_object *jobj_segment);
json_object *LUKS2_get_ignored_segments(struct luks2_hdr *hdr);
int LUKS2_segments_set(struct crypt_device *cd,
@@ -324,22 +423,11 @@ int LUKS2_last_segment_by_type(struct luks2_hdr *hdr,
int LUKS2_get_default_segment(struct luks2_hdr *hdr);
json_object *LUKS2_reencrypt_segment_new(struct luks2_hdr *hdr);
json_object *LUKS2_reencrypt_segment_old(struct luks2_hdr *hdr);
const char *LUKS2_reencrypt_segment_cipher_new(struct luks2_hdr *hdr);
const char *LUKS2_reencrypt_segment_cipher_old(struct luks2_hdr *hdr);
int LUKS2_reencrypt_get_sector_size_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_get_sector_size_old(struct luks2_hdr *hdr);
uint64_t LUKS2_reencrypt_get_data_offset_new(struct luks2_hdr *hdr);
uint64_t LUKS2_reencrypt_get_data_offset_old(struct luks2_hdr *hdr);
uint64_t LUKS2_reencrypt_get_data_offset_moved(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr);
const char *LUKS2_reencrypt_protection_type(struct luks2_hdr *hdr);
const char *LUKS2_reencrypt_protection_hash(struct luks2_hdr *hdr);
uint32_t LUKS2_reencrypt_protection_sector_size(struct luks2_hdr *hdr);
int64_t LUKS2_reencrypt_data_dev_diff(struct luks2_hdr *hdr);
int64_t LUKS2_reencrypt_data_shift(struct luks2_hdr *hdr);
uint64_t LUKS2_reencrypt_data_shift(struct luks2_hdr *hdr);
const char *LUKS2_reencrypt_mode(struct luks2_hdr *hdr);
/*
@@ -351,6 +439,11 @@ int LUKS2_digest_any_matching(struct crypt_device *cd,
int LUKS2_digest_by_segment(struct luks2_hdr *hdr, int segment);
int LUKS2_digest_verify_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr,
int digest,
const struct volume_key *vk);
int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
@@ -396,6 +489,24 @@ int LUKS2_activate(struct crypt_device *cd,
struct volume_key *vk,
uint32_t flags);
int LUKS2_activate_multi(struct crypt_device *cd,
const char *name,
struct volume_key *vks,
uint32_t flags);
struct crypt_dm_active_device;
int LUKS2_deactivate(struct crypt_device *cd,
const char *name,
struct luks2_hdr *hdr,
struct crypt_dm_active_device *dmd,
uint32_t flags);
int LUKS2_reload(struct crypt_device *cd,
const char *name,
struct volume_key *vks,
uint32_t flags);
int LUKS2_keyslot_luks2_format(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -424,6 +535,7 @@ int LUKS2_wipe_header_areas(struct crypt_device *cd,
struct luks2_hdr *hdr);
uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr);
int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size);
int LUKS2_get_sector_size(struct luks2_hdr *hdr);
const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment);
const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment);
@@ -435,12 +547,17 @@ const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr);
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment);
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
int LUKS2_find_keyslot(struct luks2_hdr *hdr, const char *type);
int LUKS2_find_keyslot_for_segment(struct luks2_hdr *hdr, int segment, const char *type);
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot);
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
int keyslot,
uint64_t *offset,
uint64_t *length);
int LUKS2_keyslot_pbkdf(struct luks2_hdr *hdr, int keyslot, struct crypt_pbkdf_type *pbkdf);
int LUKS2_set_keyslots_size(struct crypt_device *cd,
struct luks2_hdr *hdr,
uint64_t data_offset);
/*
* Permanent activation flags stored in header
@@ -456,10 +573,13 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr
int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet);
char *LUKS2_key_description_by_digest(struct crypt_device *cd, int digest);
int LUKS2_key_description_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr, struct volume_key *vk, int segment);
int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd,
struct luks2_hdr *hdr, struct volume_key *vk, int keyslot);
int LUKS2_volume_key_load_in_keyring_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr, struct volume_key *vk, int digest);
struct luks_phdr;
int LUKS2_luks1_to_luks2(struct crypt_device *cd,
@@ -469,4 +589,37 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd,
struct luks2_hdr *hdr2,
struct luks_phdr *hdr1);
/*
* LUKS2 reencryption
*/
int LUKS2_verify_and_upload_keys(struct crypt_device *cd,
struct luks2_hdr *hdr,
int digest_old,
int digest_new,
struct volume_key *vks);
int LUKS2_reenc_load(struct crypt_device *cd,
struct luks2_hdr *hdr,
uint64_t device_size,
const struct crypt_params_reencrypt *params,
struct luks2_reenc_context **rh);
int LUKS2_reenc_update_segments(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct luks2_reenc_context *rh);
int LUKS2_reenc_recover(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct luks2_reenc_context *rh,
struct volume_key *vks);
void LUKS2_reenc_context_free(struct crypt_device *cd, struct luks2_reenc_context *rh);
int reenc_erase_backup_segments(struct crypt_device *cd, struct luks2_hdr *hdr);
int crypt_reencrypt_lock(struct crypt_device *cd, const char *uuid, struct crypt_lock_handle **reencrypt_lock);
void crypt_reencrypt_unlock(struct crypt_device *cd, struct crypt_lock_handle *reencrypt_lock);
int luks2_check_device_size(struct crypt_device *cd, struct luks2_hdr *hdr, uint64_t *device_size, bool activation);
#endif

View File

@@ -110,7 +110,8 @@ int LUKS2_digest_by_keyslot(struct luks2_hdr *hdr, int keyslot)
return -ENOENT;
}
static int _digest_verify(struct crypt_device *cd,
int LUKS2_digest_verify_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr,
int digest,
const struct volume_key *vk)
{
@@ -122,10 +123,12 @@ static int _digest_verify(struct crypt_device *cd,
return -EINVAL;
r = h->verify(cd, digest, vk->key, vk->keylength);
if (r < 0)
if (r < 0) {
log_dbg(cd, "Digest %d (%s) verify failed with %d.", digest, h->name, r);
return r;
}
return r;
return digest;
}
int LUKS2_digest_verify(struct crypt_device *cd,
@@ -133,15 +136,15 @@ int LUKS2_digest_verify(struct crypt_device *cd,
const struct volume_key *vk,
int keyslot)
{
int digest, r;
int digest;
digest = LUKS2_digest_by_keyslot(hdr, keyslot);
if (digest < 0)
return digest;
r = _digest_verify(cd, digest, vk);
log_dbg(cd, "Verifying key from keyslot %d, digest %d.", keyslot, digest);
return r < 0 ? r : digest;
return LUKS2_digest_verify_by_digest(cd, hdr, digest, vk);
}
int LUKS2_digest_dump(struct crypt_device *cd, int digest)
@@ -154,39 +157,27 @@ int LUKS2_digest_dump(struct crypt_device *cd, int digest)
return h->dump(cd, digest);
}
int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
const struct volume_key *vk)
{
int digest, r;
digest = LUKS2_digest_by_segment(hdr, segment);
if (digest < 0)
return digest;
log_dbg(cd, "Verifying key digest %d.", digest);
r = _digest_verify(cd, digest, vk);
return r < 0 ? r : digest;
}
int LUKS2_digest_any_matching(struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk)
{
int digest;
for (digest = 0; digest < LUKS2_DIGEST_MAX; digest++) {
if (_digest_verify(cd, digest, vk) < 0)
continue;
return digest;
}
for (digest = 0; digest < LUKS2_DIGEST_MAX; digest++)
if (LUKS2_digest_verify_by_digest(cd, hdr, digest, vk) == digest)
return digest;
return -ENOENT;
}
int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
const struct volume_key *vk)
{
return LUKS2_digest_verify_by_digest(cd, hdr, LUKS2_digest_by_segment(hdr, segment), vk);
}
/* FIXME: segment can have more digests */
int LUKS2_digest_by_segment(struct luks2_hdr *hdr, int segment)
{
@@ -424,6 +415,11 @@ static char *get_key_description_by_digest(struct crypt_device *cd, int digest)
return desc;
}
char *LUKS2_key_description_by_digest(struct crypt_device *cd, int digest)
{
return get_key_description_by_digest(cd, digest);
}
int LUKS2_key_description_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr, struct volume_key *vk, int segment)
{
@@ -448,3 +444,17 @@ int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd,
free(desc);
return r;
}
int LUKS2_volume_key_load_in_keyring_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr, struct volume_key *vk, int digest)
{
char *desc = get_key_description_by_digest(cd, digest);
int r;
r = crypt_volume_key_set_description(vk, desc);
if (!r)
r = crypt_volume_key_load_in_keyring(cd, vk);
free(desc);
return r;
}

View File

@@ -62,10 +62,8 @@ void hexprint_base64(struct crypt_device *cd, json_object *jobj,
json_object *parse_json_len(struct crypt_device *cd, const char *json_area,
uint64_t max_length, int *json_len);
uint64_t json_object_get_uint64(json_object *jobj);
int64_t json_object_get_int64_ex(json_object *jobj);
uint32_t json_object_get_uint32(json_object *jobj);
json_object *json_object_new_uint64(uint64_t value);
json_object *json_object_new_int64_ex(int64_t value);
int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val);
void json_object_object_del_by_uint(json_object *jobj, unsigned key);
@@ -78,6 +76,7 @@ void JSON_DBG(struct crypt_device *cd, json_object *jobj, const char *desc);
*/
/* validation helper */
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);
@@ -146,6 +145,12 @@ typedef struct {
keyslot_repair_func repair;
} keyslot_handler;
/* can not fit prototype alloc function */
int reenc_keyslot_alloc(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const struct crypt_params_reencrypt *params);
/**
* LUKS2 digest handlers (EXPERIMENTAL)
*/
@@ -186,4 +191,8 @@ int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
int LUKS2_find_area_max_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
uint64_t *area_offset, uint64_t *area_length);
int LUKS2_check_cipher(struct crypt_device *cd,
size_t keylength,
const char *cipher,
const char *cipher_mode);
#endif

View File

@@ -370,3 +370,30 @@ int LUKS2_wipe_header_areas(struct crypt_device *cd,
return crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_RANDOM,
offset, length, wipe_block, NULL, NULL);
}
/* FIXME: what if user wanted to keep original keyslots size? */
int LUKS2_set_keyslots_size(struct crypt_device *cd,
struct luks2_hdr *hdr,
uint64_t data_offset)
{
json_object *jobj_config;
uint64_t keyslots_size;
if (data_offset < get_min_offset(hdr))
return 1;
keyslots_size = data_offset - get_min_offset(hdr);
/* keep keyslots_size reasonable for custom data alignments */
if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
/* keyslots size has to be 4 KiB aligned */
keyslots_size -= (keyslots_size % 4096);
if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
return 1;
json_object_object_add(jobj_config, "keyslots_size", json_object_new_uint64(keyslots_size));
return 0;
}

View File

@@ -197,6 +197,10 @@ int LUKS2_segments_count(struct luks2_hdr *hdr)
int LUKS2_get_default_segment(struct luks2_hdr *hdr)
{
int s = LUKS2_get_segment_id_by_flag(hdr, "backup-final");
if (s >= 0)
return s;
if (LUKS2_segments_count(hdr) == 1)
return 0;
@@ -212,25 +216,6 @@ uint32_t json_object_get_uint32(json_object *jobj)
return json_object_get_int64(jobj);
}
/* jobj has to be json_type_string and numbered */
static json_bool json_str_to_int64(json_object *jobj, int64_t *value)
{
char *endptr;
long long int tmp;
errno = 0;
tmp = strtoll(json_object_get_string(jobj), &endptr, 10);
if (*endptr || errno) {
log_dbg(NULL, "Failed to parse int64_t type from string %s.",
json_object_get_string(jobj));
*value = 0;
return FALSE;
}
*value = tmp;
return TRUE;
}
/* jobj has to be json_type_string and numbered */
static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value)
{
@@ -248,13 +233,6 @@ static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value)
return TRUE;
}
int64_t json_object_get_int64_ex(json_object *jobj)
{
int64_t r;
json_str_to_int64(jobj, &r);
return r;
}
uint64_t json_object_get_uint64(json_object *jobj)
{
uint64_t r;
@@ -277,21 +255,6 @@ json_object *json_object_new_uint64(uint64_t value)
return jobj;
}
json_object *json_object_new_int64_ex(int64_t value)
{
/* 18446744073709551615 */
char num[21];
int r;
json_object *jobj;
r = snprintf(num, sizeof(num), "%" PRIi64, value);
if (r < 0 || (size_t)r >= sizeof(num))
return NULL;
jobj = json_object_new_string(num);
return jobj;
}
/*
* Validate helpers
*/
@@ -322,7 +285,7 @@ json_object *json_contains(struct crypt_device *cd, json_object *jobj, const cha
return sobj;
}
static json_bool validate_json_uint32(json_object *jobj)
json_bool validate_json_uint32(json_object *jobj)
{
int64_t tmp;
@@ -592,18 +555,48 @@ static int hdr_validate_crypt_segment(struct crypt_device *cd,
return !segment_has_digest(key, jobj_digests);
}
static bool validate_segment_intervals(struct crypt_device *cd,
int length, const struct interval *ix)
{
int j, i = 0;
while (i < length) {
if (ix[i].length == UINT64_MAX && (i != (length - 1))) {
log_dbg(cd, "Only last regular segment is allowed to have 'dynamic' size.");
return false;
}
for (j = 0; j < length; j++) {
if (i == j)
continue;
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)" : "",
ix[j].offset, ix[j].offset + ix[j].length, ix[j].length == UINT64_MAX ? "(dynamic)" : "");
return false;
}
}
i++;
}
return true;
}
static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
{
json_object *jobj, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags;
int i;
json_object *jobj_segments, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags, *jobj;
struct interval *intervals;
uint64_t offset, size;
int i, r, count, first_backup = -1;
if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj)) {
if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments)) {
log_dbg(cd, "Missing segments section.");
return 1;
}
if (json_object_object_length(jobj) < 1) {
count = json_object_object_length(jobj_segments);
if (count < 1) {
log_dbg(cd, "Empty segments section.");
return 1;
}
@@ -612,7 +605,7 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj_digests))
return 1;
json_object_object_foreach(jobj, key, val) {
json_object_object_foreach(jobj_segments, key, val) {
if (!numbered(cd, "Segment", key))
return 1;
@@ -653,12 +646,61 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
return 1;
}
i = atoi(key);
if (json_segment_is_backup(val)) {
if (first_backup < 0 || i < first_backup)
first_backup = i;
} else {
if ((first_backup >= 0) && i >= first_backup) {
log_dbg(cd, "Regular segment at %d is behind backup segment at %d", i, first_backup);
return 1;
}
}
/* crypt */
if (!strcmp(json_object_get_string(jobj_type), "crypt") &&
hdr_validate_crypt_segment(cd, val, key, jobj_digests, offset, size))
return 1;
}
if (first_backup == 0) {
log_dbg(cd, "No regular segment.");
return 1;
}
if (first_backup < 0)
first_backup = count;
intervals = malloc(first_backup * sizeof(*intervals));
if (!intervals) {
log_dbg(cd, "Not enough memory.");
return 1;
}
for (i = 0; i < first_backup; i++) {
jobj = json_segments_get_segment(jobj_segments, i);
if (!jobj) {
log_dbg(cd, "Gap at key %d in segments object.", i);
free(intervals);
return 1;
}
intervals[i].offset = json_segment_get_offset(jobj, 0);
intervals[i].length = json_segment_get_size(jobj, 0) ?: UINT64_MAX;
}
r = !validate_segment_intervals(cd, first_backup, intervals);
free(intervals);
if (r)
return 1;
for (; i < count; i++) {
if (!json_segments_get_segment(jobj_segments, i)) {
log_dbg(cd, "Gap at key %d in segments object.", i);
return 1;
}
}
return 0;
}
@@ -1064,6 +1106,11 @@ static int reqs_reencrypt(uint32_t reqs)
return reqs & CRYPT_REQUIREMENT_OFFLINE_REENCRYPT;
}
static int reqs_reencrypt_online(uint32_t reqs)
{
return reqs & CRYPT_REQUIREMENT_ONLINE_REENCRYPT;
}
int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
const char *backup_file)
{
@@ -1098,7 +1145,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
}
/* do not allow header restore from backup with unmet requirements */
if (LUKS2_unmet_requirements(cd, &hdr_file, 0, 1)) {
if (LUKS2_unmet_requirements(cd, &hdr_file, CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1)) {
log_err(cd, _("Forbidden LUKS2 requirements detected in backup %s."),
backup_file);
r = -ETXTBSY;
@@ -1309,6 +1356,7 @@ static const struct {
const char *description;
} requirements_flags[] = {
{ CRYPT_REQUIREMENT_OFFLINE_REENCRYPT, "offline-reencrypt" },
{ CRYPT_REQUIREMENT_ONLINE_REENCRYPT, "online-reencrypt" },
{ 0, NULL }
};
@@ -1502,7 +1550,7 @@ static void hdr_dump_keyslots(struct crypt_device *cd, json_object *hdr_jobj)
json_object_object_get_ex(val, "type", &jobj2);
tmps = json_object_get_string(jobj2);
r = LUKS2_keyslot_for_segment(crypt_get_hdr(cd, CRYPT_LUKS2), j, CRYPT_DEFAULT_SEGMENT);
r = LUKS2_keyslot_for_segment(crypt_get_hdr(cd, CRYPT_LUKS2), j, CRYPT_ONE_SEGMENT);
log_std(cd, " %s: %s%s\n", slot, tmps, r == -ENOENT ? " (unbound)" : "");
if (json_object_object_get_ex(val, "key_size", &jobj2))
@@ -1659,8 +1707,56 @@ int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr)
return 0;
}
int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size)
{
json_object *jobj_segments, *jobj_size;
uint64_t tmp = 0;
int sector_size;
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;
json_object_object_get_ex(val, "size", &jobj_size);
if (!strcmp(json_object_get_string(jobj_size), "dynamic")) {
sector_size = json_segment_get_sector_size(val);
/* last dynamic segment must have at least one sector in size */
if (tmp)
*size = tmp + (sector_size > 0 ? sector_size : SECTOR_SIZE);
else
*size = 0;
return 0;
}
tmp += json_object_get_uint64(jobj_size);
}
/* impossible, segments with size set to 0 are illegal */
if (!tmp)
return -EINVAL;
*size = tmp;
return 0;
}
uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr)
{
uint64_t new = 0, old = 0;
json_object *jobj;
jobj = LUKS2_get_segment_by_flag(hdr, "backup-final");
if (jobj) {
new = json_segment_get_offset(jobj, 1);
jobj = LUKS2_get_segment_by_flag(hdr, "backup-previous");
if (jobj)
old = json_segment_get_offset(jobj, 1);
return new > old ? new : old;
}
return json_segments_get_minimal_offset(LUKS2_get_segments_jobj(hdr), 1);
}
@@ -1682,6 +1778,26 @@ const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment)
return json_segment_get_cipher(jobj_segment) ?: "null";
}
crypt_reencrypt_info LUKS2_reenc_status(struct luks2_hdr *hdr)
{
uint32_t reqs;
/*
* Any unknown requirement or offline reencryption should abort
* anything related to online-reencryption handling
*/
if (LUKS2_config_get_requirements(NULL, hdr, &reqs))
return CRYPT_REENCRYPT_INVALID;
if (!reqs_reencrypt_online(reqs))
return CRYPT_REENCRYPT_NONE;
if (json_segments_segment_in_reencrypt(LUKS2_get_segments_jobj(hdr)) < 0)
return CRYPT_REENCRYPT_CLEAN;
return CRYPT_REENCRYPT_CRASH;
}
const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size)
{
json_object *jobj_keyslot, *jobj_area, *jobj1;
@@ -1726,6 +1842,7 @@ const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment)
}
/* FIXME: this only ensures that once we have journal encryption, it is not ignored. */
/* implement segment count and type restrictions (crypt and only single crypt) */
static int LUKS2_integrity_compatible(struct luks2_hdr *hdr)
{
json_object *jobj1, *jobj2, *jobj3, *jobj4;
@@ -1813,17 +1930,189 @@ int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment)
int LUKS2_get_sector_size(struct luks2_hdr *hdr)
{
json_object *jobj1, *jobj_segment;
json_object *jobj_segment;
jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT);
if (!jobj_segment)
return SECTOR_SIZE;
json_object_object_get_ex(jobj_segment, "sector_size", &jobj1);
if (!jobj1)
return SECTOR_SIZE;
return json_segment_get_sector_size(jobj_segment) ?: SECTOR_SIZE;
}
return json_object_get_int(jobj1);
static int _prepare_multi_dmd(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct volume_key *vks,
json_object *jobj_segments,
struct crypt_dm_active_device *dmd)
{
struct volume_key *vk;
json_object *jobj;
enum devcheck device_check;
int r;
unsigned s = 0;
uint64_t data_offset, segment_size, segment_offset, segment_start = 0;
struct dm_target *t = &dmd->segment;
if (dmd->flags & CRYPT_ACTIVATE_SHARED)
device_check = DEV_OK;
else
device_check = DEV_EXCL;
/* FIXME: replace == 0 with < LUKS2_header_size() */
data_offset = json_segments_get_minimal_offset(jobj_segments, 1);
if (data_offset == 0 &&
crypt_data_device(cd) == crypt_metadata_device(cd)) {
log_dbg(cd, "Internal error. Wrong data offset");
return -EINVAL;
}
r = device_block_adjust(cd, crypt_data_device(cd), device_check,
data_offset, &dmd->size, &dmd->flags);
if (r)
return r;
r = -EINVAL;
while (t) {
jobj = json_segments_get_segment(jobj_segments, s);
if (!jobj) {
log_dbg(cd, "Internal error. Segment %u is null.", s);
return -EINVAL;
}
segment_offset = json_segment_get_offset(jobj, 1);
segment_size = json_segment_get_size(jobj, 1);
/* 'dynamic' length allowed in last segment only */
if (!segment_size && !t->next)
segment_size = dmd->size - segment_start;
if (!segment_size) {
log_dbg(cd, "Internal error. Wrong segment size %u", s);
return -EINVAL;
}
if (!strcmp(json_segment_type(jobj), "crypt")) {
vk = crypt_volume_key_by_id(vks, LUKS2_digest_by_segment(hdr, s));
if (!vk) {
log_err(cd, _("Missing key for dm-crypt segment %u"), s);
return -EINVAL;
}
r = dm_crypt_target_set(t, segment_start, segment_size,
crypt_data_device(cd), vk,
json_segment_get_cipher(jobj),
json_segment_get_iv_offset(jobj),
segment_offset, "none", 0,
json_segment_get_sector_size(jobj));
if (r) {
log_err(cd, _("Failed to set dm-crypt segment."));
return r;
}
} else if (!strcmp(json_segment_type(jobj), "linear")) {
r = dm_linear_target_set(t, segment_start, segment_size, crypt_data_device(cd), segment_offset);
if (r) {
log_err(cd, _("Failed to set dm-linear segment."));
return r;
}
} else
return -EINVAL;
segment_start += segment_size;
t = t->next;
s++;
}
return r;
}
/* FIXME: This shares almost all code with activate_multi_custom */
static int _reload_custom_multi(struct crypt_device *cd,
const char *name,
struct volume_key *vks,
json_object *jobj_segments,
uint32_t flags)
{
int r, count = json_segments_count(jobj_segments);
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
struct crypt_dm_active_device dmd = {
.uuid = crypt_get_uuid(cd),
};
if (count < 0)
return -EINVAL;
/* do not allow activation when particular requirements detected */
if ((r = LUKS2_unmet_requirements(cd, hdr, CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 0)))
return r;
/* Add persistent activation flags */
if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT))
LUKS2_config_get_flags(cd, hdr, &dmd.flags);
dmd.flags |= (flags | CRYPT_ACTIVATE_SHARED);
r = dm_targets_allocate(&dmd.segment, count);
if (r) {
dm_targets_free(cd, &dmd);
return r;
}
r = _prepare_multi_dmd(cd, hdr, vks, jobj_segments, &dmd);
if (!r)
r = dm_reload_device(cd, name, &dmd, 0, 0);
dm_targets_free(cd, &dmd);
return r;
}
int LUKS2_reload(struct crypt_device *cd,
const char *name,
struct volume_key *vks,
uint32_t flags)
{
if (crypt_get_integrity_tag_size(cd))
return -ENOTSUP;
return _reload_custom_multi(cd, name, vks,
LUKS2_get_segments_jobj(crypt_get_hdr(cd, CRYPT_LUKS2)), flags);
}
int LUKS2_activate_multi(struct crypt_device *cd,
const char *name,
struct volume_key *vks,
uint32_t flags)
{
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
json_object *jobj_segments = LUKS2_get_segments_jobj(hdr);
int r, count = json_segments_count(jobj_segments);
struct crypt_dm_active_device dmd = {
.uuid = crypt_get_uuid(cd),
};
if (count < 0)
return -EINVAL;
/* do not allow activation when particular requirements detected */
if ((r = LUKS2_unmet_requirements(cd, hdr, CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 0)))
return r;
/* Add persistent activation flags */
if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT))
LUKS2_config_get_flags(cd, hdr, &dmd.flags);
dmd.flags |= flags;
r = dm_targets_allocate(&dmd.segment, count);
if (r) {
dm_targets_free(cd, &dmd);
return r;
}
r = _prepare_multi_dmd(cd, hdr, vks, jobj_segments, &dmd);
if (!r)
r = dm_create_device(cd, name, CRYPT_LUKS2, &dmd);
dm_targets_free(cd, &dmd);
return r;
}
int LUKS2_activate(struct crypt_device *cd,
@@ -1877,6 +2166,153 @@ int LUKS2_activate(struct crypt_device *cd,
return r;
}
static bool is_reencryption_helper(const char *name)
{
size_t len;
if (!name)
return false;
len = strlen(name);
return (len >= 9 && (!strcmp(name + len - 8, "-hotzone") ||
!strcmp(name + len - 8, "-overlay")));
}
static bool contains_reencryption_helper(char **names)
{
while (*names) {
if (is_reencryption_helper(*names++))
return true;
}
return false;
}
int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr *hdr, struct crypt_dm_active_device *dmd, uint32_t flags)
{
int r, ret;
struct dm_target *tgt;
crypt_status_info ci;
struct crypt_dm_active_device dmdc;
char **dep, uuid[37], deps_uuid_prefix[38], *deps[65] = { 0 };
const char *namei = NULL;
struct crypt_lock_handle *reencrypt_lock = NULL;
if (!dmd || !dmd->uuid)
return -EINVAL;
r = snprintf(deps_uuid_prefix, sizeof(deps_uuid_prefix), "TEMP-%.32s", dmd->uuid + 6);
if (r < 0 || (size_t)r != 37)
return -EINVAL;
r = snprintf(uuid, sizeof(uuid), "%.8s-%.4s-%.4s-%.4s-%.12s",
dmd->uuid + 6, dmd->uuid + 14, dmd->uuid + 18, dmd->uuid + 22, dmd->uuid + 26);
if (r < 0 || (size_t)r != 36)
return -EINVAL;
/* uuid mismatch with metadata (if available) */
if (hdr && strcmp(hdr->uuid, uuid))
return -EINVAL;
tgt = &dmd->segment;
/* TODO: We have LUKS2 dependencies now */
if (hdr && single_segment(dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd))
namei = device_dm_name(tgt->data_device);
r = dm_device_deps(cd, name, deps_uuid_prefix, deps, ARRAY_SIZE(deps));
if (r < 0)
goto out;
if (contains_reencryption_helper(deps)) {
r = crypt_reencrypt_lock(cd, uuid, &reencrypt_lock);
if (r) {
if (r == -EBUSY)
log_err(cd, _("Reencryption in-progress. Cannot deactivate device."));
else
log_err(cd, _("Failed to get reencryption lock."));
goto out;
}
}
dep = deps;
while (*dep) {
if (is_reencryption_helper(*dep) && (dm_status_suspended(cd, *dep) > 0)) {
if (dm_error_device(cd, *dep))
log_err(cd, _("Failed to error suspended device %s."), *dep);
}
dep++;
}
r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmdc);
if (r < 0) {
memset(&dmdc, 0, sizeof(dmdc));
dmdc.segment.type = DM_UNKNOWN;
}
/* Remove top level device first */
r = dm_remove_device(cd, name, flags);
if (!r) {
tgt = &dmdc.segment;
while (tgt) {
if (tgt->type == DM_CRYPT)
crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
tgt = tgt->next;
}
}
dm_targets_free(cd, &dmdc);
/* TODO: We have LUKS2 dependencies now */
if (r >= 0 && namei) {
log_dbg(cd, "Deactivating integrity device %s.", namei);
r = dm_remove_device(cd, namei, 0);
}
if (!r) {
ret = 0;
dep = deps;
while (*dep) {
log_dbg(cd, "Deactivating LUKS2 dependent device %s.", *dep);
r = dm_query_device(cd, *dep, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmdc);
if (r < 0) {
memset(&dmdc, 0, sizeof(dmdc));
dmdc.segment.type = DM_UNKNOWN;
}
r = dm_remove_device(cd, *dep, flags);
if (r < 0) {
ci = crypt_status(cd, *dep);
if (ci == CRYPT_BUSY)
log_err(cd, _("Device %s is still in use."), *dep);
if (ci == CRYPT_INACTIVE)
r = 0;
}
if (!r) {
tgt = &dmdc.segment;
while (tgt) {
if (tgt->type == DM_CRYPT)
crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
tgt = tgt->next;
}
}
dm_targets_free(cd, &dmdc);
if (r && !ret)
ret = r;
dep++;
}
r = ret;
}
out:
crypt_reencrypt_unlock(cd, reencrypt_lock);
dep = deps;
while (*dep)
free(*dep++);
return r;
}
int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet)
{
uint32_t reqs;
@@ -1900,6 +2336,8 @@ int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uin
if (reqs_reencrypt(reqs) && !quiet)
log_err(cd, _("Offline reencryption in progress. Aborting."));
if (reqs_reencrypt_online(reqs) && !quiet)
log_err(cd, _("Online reencryption in progress. Aborting."));
/* any remaining unmasked requirement fails the check */
return reqs ? -EINVAL : 0;

View File

@@ -23,9 +23,11 @@
/* Internal implementations */
extern const keyslot_handler luks2_keyslot;
extern const keyslot_handler reenc_keyslot;
static const keyslot_handler *keyslot_handlers[LUKS2_KEYSLOTS_MAX] = {
&luks2_keyslot,
&reenc_keyslot,
NULL
};
@@ -75,23 +77,58 @@ int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr)
}
/* Check if a keyslot is asssigned to specific segment */
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
static int _keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
{
int keyslot_digest, segment_digest;
/* no need to check anything */
if (segment == CRYPT_ANY_SEGMENT)
return 0;
int keyslot_digest, segment_digest, s, count = 0;
keyslot_digest = LUKS2_digest_by_keyslot(hdr, keyslot);
if (keyslot_digest < 0)
return -EINVAL;
return keyslot_digest;
segment_digest = LUKS2_digest_by_segment(hdr, segment);
if (segment_digest < 0)
return segment_digest;
if (segment >= 0) {
segment_digest = LUKS2_digest_by_segment(hdr, segment);
return segment_digest == keyslot_digest;
}
for (s = 0; s < 3; s++) {
segment_digest = LUKS2_digest_by_segment(hdr, s);
if (segment_digest == keyslot_digest)
count++;
}
return segment_digest == keyslot_digest ? 0 : -ENOENT;
return count;
}
static int _keyslot_for_digest(struct luks2_hdr *hdr, int keyslot, int digest)
{
int r = -EINVAL;
r = LUKS2_digest_by_keyslot(hdr, keyslot);
if (r < 0)
return r;
return r == digest ? 0 : -ENOENT;
}
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
{
int r = -EINVAL;
/* no need to check anything */
if (segment == CRYPT_ANY_SEGMENT)
return 0; /* ok */
if (segment == CRYPT_DEFAULT_SEGMENT) {
segment = LUKS2_get_default_segment(hdr);
if (segment < 0)
return segment;
}
r = _keyslot_for_segment(hdr, keyslot, segment);
if (r < 0)
return r;
if (segment == CRYPT_ONE_SEGMENT)
log_dbg(NULL, "At least one segment variant");
return r >= 1 ? 0 : -ENOENT;
}
/* Number of keyslots assigned to a segment or all keyslots for CRYPT_ANY_SEGMENT */
@@ -266,15 +303,58 @@ int LUKS2_keyslot_area(struct luks2_hdr *hdr,
if (!json_object_object_get_ex(jobj_area, "offset", &jobj))
return -EINVAL;
*offset = json_object_get_int64(jobj);
*offset = json_object_get_uint64(jobj);
if (!json_object_object_get_ex(jobj_area, "size", &jobj))
return -EINVAL;
*length = json_object_get_int64(jobj);
*length = json_object_get_uint64(jobj);
return 0;
}
static int LUKS2_open_and_verify_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int digest,
const char *password,
size_t password_len,
struct volume_key **vk)
{
const keyslot_handler *h;
int key_size, r;
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
return -ENOENT;
r = _keyslot_for_digest(hdr, keyslot, digest);
if (r) {
if (r == -ENOENT)
log_dbg(cd, "Keyslot %d unusable for digest %d.", keyslot, digest);
return r;
}
key_size = LUKS2_get_keyslot_stored_key_size(hdr, keyslot);
if (key_size < 0)
return -EINVAL;
*vk = crypt_alloc_volume_key(key_size, NULL);
if (!*vk)
return -ENOMEM;
r = h->open(cd, keyslot, password, password_len, (*vk)->key, (*vk)->keylength);
if (r < 0)
log_dbg(cd, "Keyslot %d (%s) open failed with %d.", keyslot, h->name, r);
else
r = LUKS2_digest_verify(cd, hdr, *vk, keyslot);
if (r < 0) {
crypt_free_volume_key(*vk);
*vk = NULL;
}
return r < 0 ? r : keyslot;
}
static int LUKS2_open_and_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -321,11 +401,50 @@ static int LUKS2_open_and_verify(struct crypt_device *cd,
if (r < 0) {
crypt_free_volume_key(*vk);
*vk = NULL;
}
} else
crypt_volume_key_set_id(*vk, r);
return r < 0 ? r : keyslot;
}
static int LUKS2_keyslot_open_priority_digest(struct crypt_device *cd,
struct luks2_hdr *hdr,
crypt_keyslot_priority priority,
const char *password,
size_t password_len,
int digest,
struct volume_key **vk)
{
json_object *jobj_keyslots, *jobj;
crypt_keyslot_priority slot_priority;
int keyslot, r = -ENOENT;
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
json_object_object_foreach(jobj_keyslots, slot, val) {
if (!json_object_object_get_ex(val, "priority", &jobj))
slot_priority = CRYPT_SLOT_PRIORITY_NORMAL;
else
slot_priority = json_object_get_int(jobj);
keyslot = atoi(slot);
if (slot_priority != priority) {
log_dbg(cd, "Keyslot %d priority %d != %d (required), skipped.",
keyslot, slot_priority, priority);
continue;
}
r = LUKS2_open_and_verify_by_digest(cd, hdr, keyslot, digest, password, password_len, vk);
/* Do not retry for errors that are no -EPERM or -ENOENT,
former meaning password wrong, latter key slot unusable for segment */
if ((r != -EPERM) && (r != -ENOENT))
break;
}
return r;
}
static int LUKS2_keyslot_open_priority(struct crypt_device *cd,
struct luks2_hdr *hdr,
crypt_keyslot_priority priority,
@@ -364,6 +483,76 @@ static int LUKS2_keyslot_open_priority(struct crypt_device *cd,
return r;
}
static int LUKS2_keyslot_open_by_digest(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int digest,
const char *password,
size_t password_len,
struct volume_key **vk)
{
int r_prio, r = -EINVAL;
if (digest < 0)
return r;
if (keyslot == CRYPT_ANY_SLOT) {
r_prio = LUKS2_keyslot_open_priority_digest(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
password, password_len, digest, vk);
if (r_prio >= 0)
r = r_prio;
else if (r_prio != -EPERM && r_prio != -ENOENT)
r = r_prio;
else
r = LUKS2_keyslot_open_priority_digest(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL,
password, password_len, digest, vk);
/* Prefer password wrong to no entry from priority slot */
if (r_prio == -EPERM && r == -ENOENT)
r = r_prio;
} else
r = LUKS2_open_and_verify_by_digest(cd, hdr, keyslot, digest, password, password_len, vk);
return r;
}
int LUKS2_keyslot_open_all_segments(struct crypt_device *cd,
int keyslot_old,
int keyslot_new,
const char *password,
size_t password_len,
struct volume_key **vks)
{
struct volume_key *vk;
int digest_old, digest_new, r = -EINVAL;
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
digest_old = LUKS2_reencrypt_digest_old(hdr);
if (digest_old >= 0) {
log_dbg(cd, "Trying to unlock volume key (digest: %d) using keyslot %d.", digest_old, keyslot_old);
r = LUKS2_keyslot_open_by_digest(cd, hdr, keyslot_old, digest_old, password, password_len, &vk);
if (r < 0)
goto out;
crypt_volume_key_set_id(vk, digest_old);
crypt_volume_key_add_next(vks, vk);
}
digest_new = LUKS2_reencrypt_digest_new(hdr);
if (digest_new >= 0 && digest_old != digest_new) {
log_dbg(cd, "Trying to unlock volume key (digest: %d) using keyslot %d.", digest_new, keyslot_new);
r = LUKS2_keyslot_open_by_digest(cd, hdr, keyslot_new, digest_new, password, password_len, &vk);
if (r < 0)
goto out;
crypt_volume_key_set_id(vk, digest_new);
crypt_volume_key_add_next(vks, vk);
}
out:
if (r < 0) {
crypt_free_volume_key(*vks);
*vks = NULL;
}
return r;
}
int LUKS2_keyslot_open(struct crypt_device *cd,
int keyslot,
int segment,
@@ -395,6 +584,64 @@ int LUKS2_keyslot_open(struct crypt_device *cd,
return r;
}
int LUKS2_keyslot_reencrypt_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const struct crypt_params_reencrypt *params)
{
const keyslot_handler *h;
int r;
if (keyslot == CRYPT_ANY_SLOT)
return -EINVAL;
/* FIXME: find keyslot by type */
h = LUKS2_keyslot_handler_type(cd, "reencrypt");
if (!h)
return -EINVAL;
r = reenc_keyslot_alloc(cd, hdr, keyslot, params);
if (r < 0)
return r;
r = LUKS2_keyslot_priority_set(cd, hdr, keyslot, CRYPT_SLOT_PRIORITY_IGNORE, 0);
if (r < 0)
return r;
r = h->validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot));
if (r) {
log_dbg(cd, "Keyslot validation failed.");
return r;
}
if (LUKS2_hdr_validate(cd, hdr->jobj, hdr->hdr_size - LUKS2_HDR_BIN_LEN))
return -EINVAL;
return 0;
}
int LUKS2_keyslot_reencrypt_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const void *buffer,
size_t buffer_length)
{
const keyslot_handler *h;
int r;
if (!(h = LUKS2_keyslot_handler(cd, keyslot)) || strcmp(h->name, "reencrypt"))
return -EINVAL;
r = h->validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot));
if (r) {
log_dbg(cd, "Keyslot validation failed.");
return r;
}
return h->store(cd, keyslot, NULL, 0,
buffer, buffer_length);
}
int LUKS2_keyslot_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -664,3 +911,46 @@ void LUKS2_keyslots_repair(struct crypt_device *cd, json_object *jobj_keyslots)
h->repair(cd, val);
}
}
/* assumes valid header */
int LUKS2_find_keyslot(struct luks2_hdr *hdr, const char *type)
{
int i;
json_object *jobj_keyslot, *jobj_type;
if (!type)
return -EINVAL;
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, i);
if (!jobj_keyslot)
continue;
json_object_object_get_ex(jobj_keyslot, "type", &jobj_type);
if (!strcmp(json_object_get_string(jobj_type), type))
return i;
}
return -ENOENT;
}
int LUKS2_find_keyslot_for_segment(struct luks2_hdr *hdr, int segment, const char *type)
{
int i;
json_object *jobj_keyslot, *jobj_type;
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, i);
if (!jobj_keyslot)
continue;
json_object_object_get_ex(jobj_keyslot, "type", &jobj_type);
if (strcmp(json_object_get_string(jobj_type), type))
continue;
if (!LUKS2_keyslot_for_segment(hdr, i, segment))
return i;
}
return -EINVAL;
}

View File

@@ -0,0 +1,300 @@
/*
* LUKS - Linux Unified Key Setup v2, reencryption keyslot handler
*
* Copyright (C) 2016-2018, Red Hat, Inc. All rights reserved.
* Copyright (C) 2016-2018, Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "luks2_internal.h"
static int reenc_keyslot_open(struct crypt_device *cd,
int keyslot,
const char *password,
size_t password_len,
char *volume_key,
size_t volume_key_len)
{
return -ENOENT; /* TODO: */
}
int reenc_keyslot_alloc(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const struct crypt_params_reencrypt *params)
{
int r;
json_object *jobj_keyslots, *jobj_keyslot, *jobj_area;
uint64_t area_offset, area_length;
log_dbg(cd, "Allocating reencrypt keyslot %d.", keyslot);
if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX)
return -ENOMEM;
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
return -EINVAL;
/* encryption doesn't require area (we shift data and backup will be available) */
if (!params->data_shift) {
r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length);
if (r < 0)
return r;
} else { /* we can't have keyslot w/o area...bug? */
r = LUKS2_find_area_gap(cd, hdr, 1, &area_offset, &area_length);
if (r < 0)
return r;
}
jobj_keyslot = json_object_new_object();
if (!jobj_keyslot)
return -ENOMEM;
jobj_area = json_object_new_object();
if (params->data_shift) {
json_object_object_add(jobj_area, "type", json_object_new_string("datashift"));
json_object_object_add(jobj_area, "shift_size", json_object_new_uint64(params->data_shift << SECTOR_SHIFT));
} else
/* except data shift protection, initial setting is irrelevant. Type can be changed during reencryption */
json_object_object_add(jobj_area, "type", json_object_new_string("none"));
json_object_object_add(jobj_area, "offset", json_object_new_uint64(area_offset));
json_object_object_add(jobj_area, "size", json_object_new_uint64(area_length));
json_object_object_add(jobj_keyslot, "type", json_object_new_string("reencrypt"));
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(1)); /* useless but mandatory */
json_object_object_add(jobj_keyslot, "mode", json_object_new_string(params->mode));
if (params->direction == CRYPT_REENCRYPT_FORWARD)
json_object_object_add(jobj_keyslot, "direction", json_object_new_string("forward"));
else if (params->direction == CRYPT_REENCRYPT_BACKWARD)
json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward"));
else
return -EINVAL;
json_object_object_add(jobj_keyslot, "area", jobj_area);
json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
if (LUKS2_check_json_size(cd, hdr)) {
log_dbg(cd, "New keyslot too large to fit in free metadata space.");
json_object_object_del_by_uint(jobj_keyslots, keyslot);
return -ENOSPC;
}
log_dbg(cd, "JSON: %s", json_object_to_json_string_ext(hdr->jobj, JSON_C_TO_STRING_PRETTY));
return 0;
}
static int reenc_keyslot_store_data(struct crypt_device *cd,
json_object *jobj_keyslot,
const void *buffer, size_t buffer_len)
{
int devfd, r;
json_object *jobj_area, *jobj_offset, *jobj_length;
uint64_t area_offset, area_length;
struct device *device = crypt_metadata_device(cd);
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
!json_object_object_get_ex(jobj_area, "offset", &jobj_offset) ||
!json_object_object_get_ex(jobj_area, "size", &jobj_length))
return -EINVAL;
area_offset = json_object_get_uint64(jobj_offset);
area_length = json_object_get_uint64(jobj_length);
if (!area_offset || !area_length || ((uint64_t)buffer_len > area_length))
return -EINVAL;
r = device_write_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire write lock on device %s."),
device_path(device));
return r;
}
devfd = device_open_locked(cd, device, O_RDWR);
if (devfd >= 0) {
if (write_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), CONST_CAST(void *)buffer,
buffer_len, area_offset) < 0)
r = -EIO;
else
r = 0;
close(devfd);
} else
r = -EINVAL;
device_write_unlock(cd, device);
if (r)
log_err(cd, _("IO error while encrypting keyslot."));
return r;
}
static int reenc_keyslot_store(struct crypt_device *cd,
int keyslot,
const char *password __attribute__((unused)),
size_t password_len __attribute__((unused)),
const char *buffer,
size_t buffer_len)
{
struct luks2_hdr *hdr;
json_object *jobj_keyslot;
int r = 0;
if (!cd || !buffer || !buffer_len)
return -EINVAL;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
log_dbg(cd, "Reencrypt keyslot %d store.", keyslot);
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -EINVAL;
r = reenc_keyslot_store_data(cd, jobj_keyslot, buffer, buffer_len);
if (r < 0)
return r;
r = LUKS2_hdr_write(cd, hdr);
if (r < 0)
return r;
return keyslot;
}
int reenc_keyslot_update(struct crypt_device *cd,
const struct luks2_reenc_context *rh)
{
json_object *jobj_keyslot, *jobj_area, *jobj_area_type;
struct luks2_hdr *hdr;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, rh->reenc_keyslot);
if (!jobj_keyslot)
return -EINVAL;
json_object_object_get_ex(jobj_keyslot, "area", &jobj_area);
json_object_object_get_ex(jobj_area, "type", &jobj_area_type);
if (rh->rp.type == REENC_PROTECTION_CHECKSUM) {
log_dbg(cd, "Updating reencrypt keyslot for checksum protection.");
json_object_object_add(jobj_area, "type", json_object_new_string("checksum"));
json_object_object_add(jobj_area, "hash", json_object_new_string(rh->rp.p.csum.hash));
json_object_object_add(jobj_area, "sector_size", json_object_new_int64(rh->alignment));
} else if (rh->rp.type == REENC_PROTECTION_NOOP) {
log_dbg(cd, "Updating reencrypt keyslot for none protection.");
json_object_object_add(jobj_area, "type", json_object_new_string("none"));
json_object_object_del(jobj_area, "hash");
} else if (rh->rp.type == REENC_PROTECTION_JOURNAL) {
log_dbg(cd, "Updating reencrypt keyslot for journal protection.");
json_object_object_add(jobj_area, "type", json_object_new_string("journal"));
json_object_object_del(jobj_area, "hash");
} else
log_dbg(cd, "No update of reencrypt keyslot needed.");
return 0;
}
static int reenc_keyslot_wipe(struct crypt_device *cd, int keyslot)
{
return 0;
}
static int reenc_keyslot_dump(struct crypt_device *cd, int keyslot)
{
return 0;
}
static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot)
{
json_object *jobj_mode, *jobj_area, *jobj_type, *jobj_shift_size, *jobj_hash, *jobj_sector_size;
const char *mode, *type;
uint32_t sector_size;
uint64_t shift_size;
/* mode (string: encrypt,reencrypt,decrypt)
* direction (string:)
* area {
* type: (string: datashift, journal, checksum, none)
* hash: (string: checksum only)
* sector_size (uint32: checksum only)
* shift_size (uint64: datashift only)
* }
*/
/* area and area type are validated in general validation code */
if (!jobj_keyslot || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
!json_object_object_get_ex(jobj_area, "type", &jobj_type))
return -EINVAL;
jobj_mode = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "mode", json_type_string);
if (!jobj_mode || !json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "direction", json_type_string))
return -EINVAL;
mode = json_object_get_string(jobj_mode);
type = json_object_get_string(jobj_type);
if (strcmp(mode, "reencrypt") && strcmp(mode, "encrypt") &&
strcmp(mode, "decrypt")) {
log_dbg(cd, "Illegal reencrypt mode %s.", mode);
return -EINVAL;
}
if (!strcmp(type, "checksum")) {
jobj_hash = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "hash", json_type_string);
jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "sector_size", json_type_int);
if (!jobj_hash || !jobj_sector_size)
return -EINVAL;
if (!validate_json_uint32(jobj_sector_size))
return -EINVAL;
sector_size = json_object_get_uint32(jobj_sector_size);
if (sector_size < SECTOR_SIZE || NOTPOW2(sector_size)) {
log_dbg(cd, "Invalid sector_size (%" PRIu32 ") for checksum resilience mode.", sector_size);
return -EINVAL;
}
} else if (!strcmp(type, "datashift")) {
if (!(jobj_shift_size = json_contains(cd, jobj_area, "type:datashift", "Keyslot area", "shift_size", json_type_string)))
return -EINVAL;
shift_size = json_object_get_uint64(jobj_shift_size);
if (!shift_size)
return -EINVAL;
if (MISALIGNED_512(shift_size)) {
log_dbg(cd, "Shift size field has to be aligned to sector size: %" PRIu32, SECTOR_SIZE);
return -EINVAL;
}
}
return 0;
}
const keyslot_handler reenc_keyslot = {
.name = "reencrypt",
.open = reenc_keyslot_open,
.store = reenc_keyslot_store, /* initialization only or also per every chunk write */
.wipe = reenc_keyslot_wipe,
.dump = reenc_keyslot_dump,
.validate = reenc_keyslot_validate
};

View File

@@ -24,6 +24,14 @@
#include "../luks1/luks.h"
#include "../luks1/af.h"
int LUKS2_check_cipher(struct crypt_device *cd,
size_t keylength,
const char *cipher,
const char *cipher_mode)
{
return LUKS_check_cipher(cd, keylength, cipher, cipher_mode);
}
static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struct json_object **keyslot_object)
{
char *base64_str, cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];

3108
lib/luks2/luks2_reencrypt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned b
json_object_object_foreach(jobj_segments, key, val) {
UNUSED(key);
if (LUKS2_segment_ignore(val))
if (json_segment_is_backup(val))
continue;
tmp = json_segment_get_offset(val, blockwise);
@@ -133,6 +133,37 @@ json_object *json_segment_get_flags(json_object *jobj_segment)
return jobj;
}
static bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len)
{
int r, i;
json_object *jobj, *jobj_flags = json_segment_get_flags(jobj_segment);
if (!jobj_flags)
return false;
for (i = 0; i < (int)json_object_array_length(jobj_flags); i++) {
jobj = json_object_array_get_idx(jobj_flags, i);
if (len)
r = strncmp(json_object_get_string(jobj), flag_str, len);
else
r = strcmp(json_object_get_string(jobj), flag_str);
if (!r)
return true;
}
return false;
}
bool json_segment_is_backup(json_object *jobj_segment)
{
return json_segment_contains_flag(jobj_segment, "backup-", 7);
}
bool json_segment_is_reencrypt(json_object *jobj_segment)
{
return json_segment_contains_flag(jobj_segment, "in-reencryption", 0);
}
json_object *json_segments_get_segment(json_object *jobj_segments, int segment)
{
json_object *jobj;
@@ -156,7 +187,7 @@ int json_segments_count(json_object *jobj_segments)
json_object_object_foreach(jobj_segments, slot, val) {
UNUSED(slot);
if (!LUKS2_segment_ignore(val))
if (!json_segment_is_backup(val))
count++;
}
@@ -255,28 +286,6 @@ json_object *json_segment_create_crypt(uint64_t offset,
return jobj;
}
int LUKS2_segment_ignore(json_object *jobj_segment)
{
int i;
json_object *jobj_flags, *jobj;
if (!jobj_segment)
return 0;
json_object_object_get_ex(jobj_segment, "flags", &jobj_flags);
if (!jobj_flags)
return 0;
for (i = 0; i < (int)json_object_array_length(jobj_flags); i++) {
jobj = json_object_array_get_idx(jobj_flags, i);
if (!strncmp(json_object_get_string(jobj), "reencrypt-", 10))
return 1;
}
return 0;
}
uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr, int segment, unsigned blockwise)
{
return json_segment_get_offset(LUKS2_get_segment_jobj(hdr, segment), blockwise);
@@ -319,7 +328,7 @@ int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, const char *type)
return -1;
json_object_object_foreach(jobj_segments, slot, val) {
if (LUKS2_segment_ignore(val))
if (json_segment_is_backup(val))
continue;
if (strcmp(type, json_segment_type(val) ?: ""))
continue;
@@ -343,7 +352,7 @@ int LUKS2_segment_by_type(struct luks2_hdr *hdr, const char *type)
return -EINVAL;
json_object_object_foreach(jobj_segments, slot, val) {
if (LUKS2_segment_ignore(val))
if (json_segment_is_backup(val))
continue;
if (strcmp(type, json_segment_type(val) ?: ""))
continue;
@@ -442,7 +451,7 @@ json_object *LUKS2_get_ignored_segments(struct luks2_hdr *hdr)
json_object_object_foreach(jobj_segments, key, value) {
UNUSED(key);
if (LUKS2_segment_ignore(value))
if (json_segment_is_backup(value))
json_object_object_add_by_uint(jobj, i++, json_object_get(value));
}

View File

@@ -77,6 +77,7 @@ struct crypt_device {
char cipher_mode[MAX_CIPHER_LEN]; /* only for compatibility */
char *keyslot_cipher;
unsigned int keyslot_key_size;
struct luks2_reenc_context *rh;
} luks2;
struct { /* used in CRYPT_PLAIN */
struct crypt_params_plain hdr;
@@ -338,7 +339,7 @@ static int onlyLUKS(struct crypt_device *cd)
return _onlyLUKS(cd, 0);
}
static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags)
static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags, uint32_t mask)
{
int r = 0;
@@ -357,12 +358,19 @@ static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags)
if (r || (cdflags & CRYPT_CD_UNRESTRICTED))
return r;
return LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, 0, cdflags & CRYPT_CD_QUIET);
return LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, mask, cdflags & CRYPT_CD_QUIET);
}
static int onlyLUKS2(struct crypt_device *cd)
/* Internal only */
int onlyLUKS2(struct crypt_device *cd)
{
return _onlyLUKS2(cd, 0);
return _onlyLUKS2(cd, 0, 0);
}
/* Internal only */
int onlyLUKS2mask(struct crypt_device *cd, uint32_t mask)
{
return _onlyLUKS2(cd, 0, mask);
}
static void crypt_set_null_type(struct crypt_device *cd)
@@ -629,7 +637,12 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device)
if (!isLUKS1(cd->type) && !isLUKS2(cd->type) && !isVERITY(cd->type) &&
!isINTEGRITY(cd->type)) {
log_err(cd, _("This operation is not supported for this device type."));
return -EINVAL;
return -EINVAL;
}
if (isLUKS2(cd->type) && crypt_get_reenc_context(cd)) {
log_err(cd, _("Illegal operation with reencryption in-progress."));
return -EINVAL;
}
return _crypt_set_data_device(cd, device);
@@ -697,6 +710,7 @@ static int _crypt_load_luks2(struct crypt_device *cd, int reload, int repair)
r = 0;
memcpy(&cd->u.luks2.hdr, &hdr2, sizeof(hdr2));
cd->u.luks2.keyslot_cipher = NULL;
cd->u.luks2.rh = NULL;
out:
if (r) {
@@ -1048,6 +1062,7 @@ static void crypt_free_type(struct crypt_device *cd)
free(cd->u.plain.cipher);
free(cd->u.plain.cipher_spec);
} else if (isLUKS2(cd->type)) {
LUKS2_reenc_context_free(cd, cd->u.luks2.rh);
LUKS2_hdr_free(cd, &cd->u.luks2.hdr);
free(cd->u.luks2.keyslot_cipher);
} else if (isLUKS1(cd->type)) {
@@ -1095,20 +1110,21 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
if (r < 0)
return r;
if (!single_segment(&dmd) || tgt->type != DM_CRYPT) {
/* TODO: segment count */
if (!(tgt->type == DM_CRYPT || tgt->type == DM_LINEAR)) {
log_dbg(cd, "Unsupported device table detected in %s.", name);
r = -EINVAL;
goto out;
}
r = crypt_parse_name_and_mode(tgt->u.crypt.cipher, cipher,
r = crypt_parse_name_and_mode(tgt->type == DM_LINEAR ? "null" : tgt->u.crypt.cipher, cipher,
&key_nums, cipher_mode);
if (r < 0) {
log_dbg(cd, "Cannot parse cipher and mode from active device.");
goto out;
}
if (tgt->u.crypt.integrity && (namei = device_dm_name(tgt->data_device))) {
if (tgt->type == DM_CRYPT && tgt->u.crypt.integrity && (namei = device_dm_name(tgt->data_device))) {
r = dm_query_device(cd, namei, DM_ACTIVE_DEVICE, &dmdi);
if (r < 0)
goto out;
@@ -1299,11 +1315,13 @@ int crypt_init_by_name_and_header(struct crypt_device **cd,
r = dm_query_device(NULL, name, DM_ACTIVE_DEVICE | DM_ACTIVE_UUID, &dmd);
if (r < 0)
return r;
if (!single_segment(&dmd)) {
/* TODO: segment count */
/*
if (dmd.segment_count > 4) {
log_dbg(NULL, "Unsupported device table detected in %s.", name);
r = -EINVAL;
goto out;
}
}*/
*cd = NULL;
@@ -1353,7 +1371,7 @@ int crypt_init_by_name_and_header(struct crypt_device **cd,
/* Try to initialise basic parameters from active device */
if (tgt->type == DM_CRYPT)
if (tgt->type == DM_CRYPT || tgt->type == DM_LINEAR)
r = _init_by_name_crypt(*cd, name);
else if (tgt->type == DM_VERITY)
r = _init_by_name_verity(*cd, name);
@@ -2849,7 +2867,6 @@ void crypt_free(struct crypt_device *cd)
free(CONST_CAST(void*)cd->pbkdf.type);
free(CONST_CAST(void*)cd->pbkdf.hash);
crypt_free_type(cd);
/* Some structures can contain keys (TCRYPT), wipe it */
@@ -3623,9 +3640,215 @@ out:
return r;
}
static int load_all_keys(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vks)
{
int r;
struct volume_key *vk = vks;
while (vk) {
r = LUKS2_volume_key_load_in_keyring_by_digest(cd, hdr, vk, crypt_volume_key_get_id(vk));
if (r < 0)
return r;
vk = vk->next;
}
return 0;
}
/* See fixmes in _open_and_activate_luks2 */
int update_reencryption_flag(struct crypt_device *cd, int enable, bool commit);
/* TODO: This function should 1:1 with pre-reencryption code */
static int _open_and_activate(struct crypt_device *cd,
int keyslot,
const char *name,
const char *passphrase,
size_t passphrase_size,
uint32_t flags)
{
int r;
struct volume_key *vk = NULL;
r = LUKS2_keyslot_open(cd, keyslot,
(flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT,
passphrase, passphrase_size, &vk);
if (r < 0)
return r;
keyslot = r;
if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) &&
crypt_use_keyring_for_vk(cd)) {
r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
&cd->u.luks2.hdr, vk, keyslot);
if (r < 0)
goto out;
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (name)
r = LUKS2_activate(cd, name, vk, flags);
out:
if (r < 0)
crypt_drop_keyring_key(cd, vk);
crypt_free_volume_key(vk);
return r < 0 ? r : keyslot;
}
static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
int keyslot,
const char *name,
const char *passphrase,
size_t passphrase_size,
uint32_t flags)
{
crypt_reencrypt_info ri;
uint64_t device_size;
bool use_keyring, keys_ready = false;
struct volume_key *vks = NULL;
int r = 0;
struct crypt_lock_handle *reencrypt_lock = NULL;
struct luks2_reenc_context *rh = NULL;
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
if (name)
r = crypt_reencrypt_lock(cd, NULL, &reencrypt_lock);
if (r) {
if (r == -EBUSY)
log_err(cd, _("Reencryption in-progress. Cannot activate device."));
else
log_err(cd, _("Failed to get reencryption lock."));
return r;
}
if (name && (r = crypt_load(cd, CRYPT_LUKS2, NULL)))
goto err;
ri = LUKS2_reenc_status(hdr);
if (name && (r = luks2_check_device_size(cd, hdr, &device_size, true)))
goto err;
if (name && ri == CRYPT_REENCRYPT_CRASH) {
log_dbg(cd, _("Entering reencryption crash recovery."));
r = LUKS2_reenc_load(cd, hdr, device_size, NULL, &rh);
if (r < 0) {
log_err(cd, _("Failed to load reencryption context from LUKS2 header."));
goto err;
}
r = LUKS2_keyslot_open_all_segments(cd, keyslot, keyslot, passphrase, passphrase_size, &vks);
if (r < 0)
goto err;
keyslot = r;
r = LUKS2_verify_and_upload_keys(cd, hdr, rh->digest_old, rh->digest_new, vks);
if (r) {
log_err(cd, _("Failed to verify and upload keys for recovery."));
goto err;
}
keys_ready = true;
r = LUKS2_reenc_recover(cd, hdr, rh, vks);
if (r < 0)
goto err;
if ((r = LUKS2_reenc_update_segments(cd, hdr, rh)))
goto err;
/* FIXME: Create unified cleanup routine when reencryption is finished */
if (LUKS2_segments_count(hdr) == 1) {
/* FIXME: remove also keyslots assigned to old digest */
crypt_keyslot_destroy(cd, rh->reenc_keyslot);
reenc_erase_backup_segments(cd, hdr);
if (update_reencryption_flag(cd, 0, true)) {
r = -EINVAL;
goto err;
}
}
ri = LUKS2_reenc_status(hdr);
LUKS2_reenc_context_free(cd, rh);
rh = NULL;
}
/* recovery finished reencryption or it's already finished */
if (ri == CRYPT_REENCRYPT_NONE) {
crypt_free_volume_key(vks);
crypt_reencrypt_unlock(cd, reencrypt_lock);
return _open_and_activate(cd, keyslot, name, passphrase, passphrase_size, flags);
}
if (ri > CRYPT_REENCRYPT_CLEAN && name) {
r = -EINVAL;
goto err;
}
use_keyring = (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) &&
crypt_use_keyring_for_vk(cd);
if (use_keyring)
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
if (!keys_ready) {
log_dbg(cd, "Entering clean reencryption state mode.");
/* we need all keys in this case */
if (use_keyring || name)
r = LUKS2_keyslot_open_all_segments(cd, keyslot, keyslot, passphrase, passphrase_size, &vks);
else
/* allow user to test passphrase only for whatever keyslot */
r = LUKS2_keyslot_open(cd, keyslot, CRYPT_ANY_SEGMENT, passphrase, passphrase_size, &vks);
if (r >= 0)
keyslot = r;
if (r < 0)
goto err;
keys_ready = true;
}
if (use_keyring && keys_ready)
r = load_all_keys(cd, hdr, vks);
if (r >= 0 && name)
r = LUKS2_activate_multi(cd, name, vks, flags);
err:
crypt_reencrypt_unlock(cd, reencrypt_lock);
if (r < 0)
crypt_drop_keyring_key(cd, vks);
crypt_free_volume_key(vks);
return r < 0 ? r : keyslot;
}
/*
* Activation/deactivation of a device
*/
static int _open_and_activate_luks2(struct crypt_device *cd,
int keyslot,
const char *name,
const char *passphrase,
size_t passphrase_size,
uint32_t flags)
{
crypt_reencrypt_info ri;
int r;
struct luks2_hdr *hdr = &cd->u.luks2.hdr;
ri = LUKS2_reenc_status(hdr);
if (ri == CRYPT_REENCRYPT_INVALID)
return -EINVAL;
if (ri > CRYPT_REENCRYPT_NONE)
r = _open_and_activate_reencrypt_device(cd, keyslot, name, passphrase,
passphrase_size, flags);
else
r = _open_and_activate(cd, keyslot, name, passphrase,
passphrase_size, flags);
return r;
}
static int _activate_by_passphrase(struct crypt_device *cd,
const char *name,
int keyslot,
@@ -3672,25 +3895,8 @@ static int _activate_by_passphrase(struct crypt_device *cd,
r = LUKS1_activate(cd, name, vk, flags);
}
} else if (isLUKS2(cd->type)) {
r = LUKS2_keyslot_open(cd, keyslot,
(flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT,
passphrase, passphrase_size, &vk);
if (r >= 0) {
keyslot = r;
if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) &&
crypt_use_keyring_for_vk(cd)) {
r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
&cd->u.luks2.hdr, vk, keyslot);
if (r < 0)
goto out;
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (name)
r = LUKS2_activate(cd, name, vk, flags);
}
r = _open_and_activate_luks2(cd, keyslot, name, passphrase, passphrase_size, flags);
keyslot = r;
} else {
log_err(cd, _("Device type is not properly initialised."));
r = -EINVAL;
@@ -3975,13 +4181,11 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t flags)
{
char *key_desc;
struct crypt_device *fake_cd = NULL;
const char *namei = NULL;
struct luks2_hdr *hdr2 = NULL;
struct crypt_dm_active_device dmd = {};
int r;
struct dm_target *tgt = &dmd.segment;
uint32_t get_flags = DM_ACTIVE_DEVICE | DM_ACTIVE_HOLDERS;
uint32_t get_flags = DM_ACTIVE_DEVICE | DM_ACTIVE_UUID | DM_ACTIVE_HOLDERS;
if (!name)
return -EINVAL;
@@ -4009,26 +4213,21 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t
r = -EBUSY;
break;
}
if (isLUKS2(cd->type) && single_segment(&dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd))
namei = device_dm_name(tgt->data_device);
}
key_desc = crypt_get_device_key_description(cd, name);
if (isLUKS2(cd->type))
hdr2 = crypt_get_hdr(cd, CRYPT_LUKS2);
if (isTCRYPT(cd->type))
if (!strncmp(CRYPT_LUKS2, dmd.uuid ?: "", sizeof(CRYPT_LUKS2)-1) || hdr2)
r = LUKS2_deactivate(cd, name, hdr2, &dmd, flags);
else if (isTCRYPT(cd->type))
r = TCRYPT_deactivate(cd, name, flags);
else
r = dm_remove_device(cd, name, flags);
if (r < 0 && crypt_status(cd, name) == CRYPT_BUSY) {
log_err(cd, _("Device %s is still in use."), name);
r = -EBUSY;
} else if (namei) {
log_dbg(cd, "Deactivating integrity device %s.", namei);
r = dm_remove_device(cd, namei, 0);
}
if (!r)
crypt_drop_keyring_key_by_description(cd, key_desc, LOGON_KEY);
free(key_desc);
break;
case CRYPT_INACTIVE:
log_err(cd, _("Device %s is not active."), name);
@@ -4040,6 +4239,7 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t
}
dm_targets_free(cd, &dmd);
free(CONST_CAST(void*)dmd.uuid);
crypt_free(fake_cd);
return r;
@@ -4991,6 +5191,18 @@ void *crypt_get_hdr(struct crypt_device *cd, const char *type)
return NULL;
}
/* internal only */
struct luks2_reenc_context *crypt_get_reenc_context(struct crypt_device *cd)
{
return cd->u.luks2.rh;
}
/* internal only */
void crypt_set_reenc_context(struct crypt_device *cd, struct luks2_reenc_context *rh)
{
cd->u.luks2.rh = rh;
}
/*
* Token handling
*/
@@ -5002,7 +5214,7 @@ int crypt_activate_by_token(struct crypt_device *cd,
log_dbg(cd, "%s volume %s using token %d.",
name ? "Activating" : "Checking", name ?: "passphrase", token);
if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED)))
if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0)))
return r;
if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd))
@@ -5026,7 +5238,7 @@ int crypt_token_json_get(struct crypt_device *cd, int token, const char **json)
log_dbg(cd, "Requesting JSON for token %d.", token);
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED)))
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
return r;
return LUKS2_token_json_get(cd, &cd->u.luks2.hdr, token, json) ?: token;
@@ -5046,7 +5258,7 @@ int crypt_token_json_set(struct crypt_device *cd, int token, const char *json)
crypt_token_info crypt_token_status(struct crypt_device *cd, int token, const char **type)
{
if (_onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED))
if (_onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0))
return CRYPT_TOKEN_INVALID;
return LUKS2_token_status(cd, &cd->u.luks2.hdr, token, type);
@@ -5065,7 +5277,7 @@ int crypt_token_luks2_keyring_get(struct crypt_device *cd,
log_dbg(cd, "Requesting LUKS2 keyring token %d.", token);
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED)))
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
return r;
token_info = LUKS2_token_status(cd, &cd->u.luks2.hdr, token, &type);
@@ -5131,7 +5343,7 @@ int crypt_token_is_assigned(struct crypt_device *cd, int token, int keyslot)
{
int r;
if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED)))
if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0)))
return r;
return LUKS2_token_is_assigned(cd, &cd->u.luks2.hdr, keyslot, token);
@@ -5175,7 +5387,7 @@ int crypt_persistent_flags_get(struct crypt_device *cd, crypt_flags_type type, u
if (!flags)
return -EINVAL;
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED)))
if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
return r;
if (type == CRYPT_FLAGS_ACTIVATION)
@@ -5521,6 +5733,30 @@ void crypt_serialize_unlock(struct crypt_device *cd)
cd->pbkdf_memory_hard_lock = NULL;
}
crypt_reencrypt_info crypt_reencrypt_status(struct crypt_device *cd,
struct crypt_params_reencrypt *params)
{
crypt_reencrypt_info ri;
struct luks2_hdr *hdr;
if (_onlyLUKS2(cd, CRYPT_CD_QUIET, CRYPT_REQUIREMENT_ONLINE_REENCRYPT))
return CRYPT_REENCRYPT_INVALID;
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
ri = LUKS2_reenc_status(hdr);
if (ri == CRYPT_REENCRYPT_NONE || ri == CRYPT_REENCRYPT_INVALID || !params)
return ri;
params->mode = LUKS2_reencrypt_mode(hdr);
params->resilience = LUKS2_reencrypt_protection_type(hdr);
params->hash = LUKS2_reencrypt_protection_hash(hdr);
params->data_shift = LUKS2_reencrypt_data_shift(hdr);
params->max_hotzone_size = 0;
return ri;
}
static void __attribute__((destructor)) libcryptsetup_exit(void)
{
crypt_backend_destroy();

View File

@@ -280,6 +280,19 @@ int device_open(struct crypt_device *cd, struct device *device, int flags)
return device_open_internal(cd, device, flags);
}
int device_open_excl(struct crypt_device *cd, struct device *device, int flags)
{
struct stat st;
if (stat(device_path(device), &st))
return -EINVAL;
if (S_ISBLK(st.st_mode))
flags |= O_EXCL;
assert(!device_locked(device->lh));
return device_open_internal(cd, device, flags);
}
int device_open_locked(struct crypt_device *cd, struct device *device, int flags)
{
assert(!crypt_metadata_locking_enabled() || device_locked(device->lh));

View File

@@ -16,6 +16,7 @@ lib/utils_wipe.c
lib/utils_keyring.c
lib/utils_blkid.c
lib/utils_io.c
lib/utils_storage_wrappers.c
lib/luks1/af.c
lib/luks1/keyencryption.c
lib/luks1/keymanage.c
@@ -32,7 +33,10 @@ lib/luks2/luks2_json_format.c
lib/luks2/luks2_json_metadata.c
lib/luks2/luks2_keyslot.c
lib/luks2/luks2_keyslot_luks2.c
lib/luks2/luks2_keyslot_reenc.c
lib/luks2/luks2_luks1_convert.c
lib/luks2/luks2_reencrypt.c
lib/luks2/luks2_segment.c
lib/luks2/luks2_token.c
lib/luks2/luks2_token_keyring.c
src/cryptsetup.c
@@ -42,3 +46,4 @@ src/cryptsetup_reencrypt.c
src/utils_tools.c
src/utils_password.c
src/utils_luks2.c
src/utils_blockdev.c