mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-05 16:00:05 +01:00
Add libLUKS2.
This commit is contained in:
23
lib/luks2/Makefile.am
Normal file
23
lib/luks2/Makefile.am
Normal file
@@ -0,0 +1,23 @@
|
||||
moduledir = $(libdir)/cryptsetup
|
||||
|
||||
noinst_LTLIBRARIES = libluks2.la
|
||||
|
||||
libluks2_la_CFLAGS = -Wall $(AM_CFLAGS) @CRYPTO_CFLAGS@
|
||||
|
||||
libluks2_la_SOURCES = \
|
||||
luks2_disk_metadata.c \
|
||||
luks2_json_format.c \
|
||||
luks2_json_metadata.c \
|
||||
luks2_luks1_convert.c \
|
||||
luks2_digest.c \
|
||||
luks2_digest_pbkdf2.c \
|
||||
luks2_keyslot.c \
|
||||
luks2_keyslot_luks2.c \
|
||||
luks2_token_keyring.c \
|
||||
luks2_token.c \
|
||||
luks2_internal.h \
|
||||
luks2.h
|
||||
|
||||
AM_CPPFLAGS = -include config.h \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/lib/crypto_backend
|
||||
353
lib/luks2/luks2.h
Normal file
353
lib/luks2/luks2.h
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTSETUP_LUKS2_ONDISK_H
|
||||
#define _CRYPTSETUP_LUKS2_ONDISK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
|
||||
#define LUKS2_MAGIC_2ND "SKUL\xba\xbe"
|
||||
#define LUKS2_MAGIC_L 6
|
||||
#define LUKS2_UUID_L 40
|
||||
#define LUKS2_LABEL_L 48
|
||||
#define LUKS2_SALT_L 64
|
||||
#define LUKS2_CHECKSUM_ALG_L 32
|
||||
#define LUKS2_CHECKSUM_L 64
|
||||
|
||||
#define LUKS2_KEYSLOTS_MAX 32
|
||||
#define LUKS2_TOKENS_MAX 32
|
||||
|
||||
#define LUKS2_BUILTIN_TOKEN_PREFIX "luks2-"
|
||||
#define LUKS2_BUILTIN_TOKEN_PREFIX_LEN 6
|
||||
|
||||
#define LUKS2_TOKEN_KEYRING LUKS2_BUILTIN_TOKEN_PREFIX "keyring"
|
||||
|
||||
#define LUKS2_DIGEST_MAX 8
|
||||
|
||||
typedef int digests_t[LUKS2_DIGEST_MAX];
|
||||
|
||||
#define CRYPT_ANY_SEGMENT -1
|
||||
#define CRYPT_DEFAULT_SEGMENT 0
|
||||
#define CRYPT_DEFAULT_SEGMENT_STR "0"
|
||||
|
||||
/*
|
||||
* LUKS2 header on-disk.
|
||||
*
|
||||
* Binary header is followed by JSON area.
|
||||
* JSON area is followed by keyslot area and data area,
|
||||
* these are described in JSON metadata.
|
||||
*
|
||||
* Note: uuid, csum_alg are intentionally on the same offset as LUKS1
|
||||
* (checksum alg replaces hash in LUKS1)
|
||||
*
|
||||
* String (char) should be zero terminated.
|
||||
* Padding should be wiped.
|
||||
* Checksum is calculated with csum zeroed (+ full JSON area).
|
||||
*/
|
||||
struct luks2_hdr_disk {
|
||||
char magic[LUKS2_MAGIC_L];
|
||||
uint16_t version; /* Version 2 */
|
||||
uint64_t hdr_size; /* in bytes, including JSON area */
|
||||
uint64_t seqid; /* increased on every update */
|
||||
char label[LUKS2_LABEL_L];
|
||||
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
|
||||
uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
|
||||
char uuid[LUKS2_UUID_L];
|
||||
char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
|
||||
uint64_t hdr_offset; /* offset from device start in bytes */
|
||||
char _padding[184];
|
||||
uint8_t csum[LUKS2_CHECKSUM_L];
|
||||
char _padding4096[7*512];
|
||||
/* JSON area starts here */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* LUKS2 header in-memory.
|
||||
*/
|
||||
typedef struct json_object json_object;
|
||||
struct luks2_hdr {
|
||||
size_t hdr_size;
|
||||
uint64_t seqid;
|
||||
unsigned int version;
|
||||
char label[LUKS2_LABEL_L];
|
||||
char subsystem[LUKS2_LABEL_L];
|
||||
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
|
||||
uint8_t salt1[LUKS2_SALT_L];
|
||||
uint8_t salt2[LUKS2_SALT_L];
|
||||
char uuid[LUKS2_UUID_L];
|
||||
json_object *jobj;
|
||||
};
|
||||
|
||||
/*
|
||||
* Supportable header sizes (hdr_disk + JSON area)
|
||||
* Also used as offset for the 2nd header.
|
||||
*/
|
||||
#define LUKS2_HDR_16K_LEN 0x4000
|
||||
|
||||
#define LUKS2_HDR_BIN_LEN sizeof(struct luks2_hdr_disk)
|
||||
|
||||
#define LUKS2_HDR_DEFAULT_LEN 0x400000 /* 4 MiB */
|
||||
|
||||
#define LUKS2_MAX_KEYSLOTS_SIZE 0x8000000 /* 128 MiB */
|
||||
|
||||
int LUKS2_hdr_version_unlocked(struct crypt_device *cd);
|
||||
|
||||
int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_hdr_uuid(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *uuid);
|
||||
|
||||
int LUKS2_hdr_labels(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *label,
|
||||
const char *subsystem,
|
||||
int commit);
|
||||
|
||||
void LUKS2_hdr_free(struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_hdr_backup(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *backup_file);
|
||||
int LUKS2_hdr_restore(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *backup_file);
|
||||
|
||||
uint64_t LUKS2_hdr_and_areas_size(json_object *jobj);
|
||||
uint64_t LUKS2_keyslots_size(json_object *jobj);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 keyslot
|
||||
*/
|
||||
int LUKS2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
int segment,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
struct volume_key **vk);
|
||||
|
||||
int LUKS2_keyslot_store(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const struct volume_key *vk);
|
||||
|
||||
int LUKS2_keyslot_wipe(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int wipe_area_only);
|
||||
|
||||
int LUKS2_keyslot_dump(struct crypt_device *cd,
|
||||
int keyslot);
|
||||
|
||||
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot);
|
||||
|
||||
int LUKS2_keyslot_priority_set(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
crypt_keyslot_priority priority,
|
||||
int commit);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 token
|
||||
*/
|
||||
int LUKS2_token_json_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **json);
|
||||
|
||||
int LUKS2_token_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int token,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *json,
|
||||
int commit);
|
||||
|
||||
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **type);
|
||||
|
||||
int LUKS2_builtin_token_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
void *params);
|
||||
|
||||
int LUKS2_builtin_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
const void *params,
|
||||
int commit);
|
||||
|
||||
int LUKS2_token_open_and_activate(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *name,
|
||||
uint32_t flags,
|
||||
void *usrptr);
|
||||
|
||||
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *name,
|
||||
uint32_t flags);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 digest
|
||||
*/
|
||||
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const struct volume_key *vk,
|
||||
digests_t digests);
|
||||
|
||||
void LUKS2_digests_erase_unused(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_digest_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
struct volume_key *vk,
|
||||
int keyslot);
|
||||
|
||||
int LUKS2_digest_dump(struct crypt_device *cd,
|
||||
int digest);
|
||||
|
||||
int LUKS2_digest_json_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int digest,
|
||||
const char **json);
|
||||
|
||||
int LUKS2_digest_json_set(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int digest,
|
||||
const char *json);
|
||||
|
||||
int LUKS2_digests_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digest_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int digest,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digest_segment_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
int digest,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests);
|
||||
|
||||
int LUKS2_digest_create(struct crypt_device *cd,
|
||||
const char *type,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk);
|
||||
|
||||
/*
|
||||
* LUKS2 generic
|
||||
*/
|
||||
int LUKS2_activate(struct crypt_device *cd,
|
||||
const char *name,
|
||||
struct volume_key *vk,
|
||||
uint32_t flags);
|
||||
|
||||
int LUKS2_keyslot_luks2_format(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *cipher,
|
||||
size_t keylength);
|
||||
|
||||
int LUKS2_generate_hdr(
|
||||
struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk,
|
||||
const char *cipherName,
|
||||
const char *cipherMode,
|
||||
const char *integrity,
|
||||
const char *uuid,
|
||||
unsigned int sector_size,
|
||||
unsigned int alignPayload,
|
||||
unsigned int alignOffset,
|
||||
int detached_metadata_device);
|
||||
|
||||
uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr);
|
||||
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);
|
||||
int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment);
|
||||
int LUKS2_get_keyslot_key_size(struct luks2_hdr *hdr, int keyslot);
|
||||
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type);
|
||||
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment);
|
||||
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
|
||||
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);
|
||||
/*
|
||||
* Permanent activation flags stored in header
|
||||
*/
|
||||
int LUKS2_config_get_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *flags);
|
||||
int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t flags);
|
||||
|
||||
/*
|
||||
* Requirements for device activation or header modification
|
||||
*/
|
||||
int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *requirements);
|
||||
int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t requirements);
|
||||
|
||||
int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, int quiet);
|
||||
|
||||
int crypt_use_keyring_for_vk(const struct crypt_device *cd);
|
||||
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk);
|
||||
|
||||
struct luks_phdr;
|
||||
int LUKS2_luks1_to_luks2(struct crypt_device *cd,
|
||||
struct luks_phdr *hdr1,
|
||||
struct luks2_hdr *hdr2);
|
||||
int LUKS2_luks2_to_luks1(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr2,
|
||||
struct luks_phdr *hdr1);
|
||||
|
||||
#endif
|
||||
391
lib/luks2/luks2_digest.c
Normal file
391
lib/luks2/luks2_digest.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, digest handling
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
|
||||
extern const digest_handler PBKDF2_digest;
|
||||
|
||||
static const digest_handler *digest_handlers[LUKS2_DIGEST_MAX] = {
|
||||
&PBKDF2_digest,
|
||||
NULL
|
||||
};
|
||||
|
||||
int crypt_digest_register(const digest_handler *handler)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
|
||||
if (!strcmp(digest_handlers[i]->name, handler->name))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i == LUKS2_DIGEST_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
digest_handlers[i] = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
|
||||
if (!strcmp(digest_handlers[i]->name, type))
|
||||
return digest_handlers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const digest_handler *LUKS2_digest_handler(struct crypt_device *cd, int digest)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (digest < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_digest_jobj(hdr, digest)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_digest_handler_type(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static int LUKS2_digest_find_free(struct crypt_device *cd, struct luks2_hdr *hdr)
|
||||
{
|
||||
int digest = 0;
|
||||
|
||||
while (LUKS2_get_digest_jobj(hdr, digest) && digest < LUKS2_DIGEST_MAX)
|
||||
digest++;
|
||||
|
||||
return digest < LUKS2_DIGEST_MAX ? digest : -1;
|
||||
}
|
||||
|
||||
int LUKS2_digest_create(struct crypt_device *cd,
|
||||
const char *type,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk)
|
||||
{
|
||||
int digest;
|
||||
const digest_handler *dh;
|
||||
|
||||
dh = LUKS2_digest_handler_type(cd, type);
|
||||
if (!dh)
|
||||
return -EINVAL;
|
||||
|
||||
digest = LUKS2_digest_find_free(cd, hdr);
|
||||
if (digest < 0)
|
||||
return -EINVAL;
|
||||
|
||||
log_dbg("Creating new digest %d (%s).", digest, type);
|
||||
|
||||
return dh->store(cd, digest, vk->key, vk->keylength) ?: digest;
|
||||
}
|
||||
|
||||
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests)
|
||||
{
|
||||
char keyslot_name[16];
|
||||
int i = 0;
|
||||
json_object *jobj_digests, *jobj_digest_keyslots;
|
||||
|
||||
if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
|
||||
return -ENOMEM;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
json_object_object_get_ex(val, "keyslots", &jobj_digest_keyslots);
|
||||
if (LUKS2_array_jobj(jobj_digest_keyslots, keyslot_name))
|
||||
digests[i++] = atoi(key);
|
||||
}
|
||||
|
||||
if (i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_digest_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
struct volume_key *vk,
|
||||
int keyslot)
|
||||
{
|
||||
const digest_handler *h;
|
||||
digests_t digests;
|
||||
int i, r;
|
||||
|
||||
r = LUKS2_digests_by_keyslot(cd, hdr, keyslot, digests);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1 ; i++) {
|
||||
log_dbg("Verifying key from keyslot %d, digest %d.",
|
||||
keyslot, digests[i]);
|
||||
h = LUKS2_digest_handler(cd, digests[i]);
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->verify(cd, digests[i], vk->key, vk->keylength);
|
||||
if (r < 0) {
|
||||
log_dbg("Digest %d (%s) verify failed with %d.",
|
||||
digests[i], h->name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_dump(struct crypt_device *cd, int digest)
|
||||
{
|
||||
const digest_handler *h;
|
||||
|
||||
if (!(h = LUKS2_digest_handler(cd, digest)))
|
||||
return -EINVAL;
|
||||
|
||||
return h->dump(cd, digest);
|
||||
}
|
||||
|
||||
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const struct volume_key *vk,
|
||||
digests_t digests)
|
||||
{
|
||||
char segment_name[16];
|
||||
const digest_handler *h;
|
||||
json_object *jobj_digests, *jobj_digest_segments;
|
||||
int digest, r, i = 0;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
json_object_object_get_ex(val, "segments", &jobj_digest_segments);
|
||||
if (!LUKS2_array_jobj(jobj_digest_segments, segment_name))
|
||||
continue;
|
||||
|
||||
digest = atoi(key);
|
||||
log_dbg("Verifying key digest %d.", digest);
|
||||
|
||||
h = LUKS2_digest_handler(cd, digest);
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->verify(cd, digest, vk->key, vk->keylength);
|
||||
if (r < 0) {
|
||||
log_dbg("Digest %d (%s) verify failed with %d.", digest, h->name, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (digests)
|
||||
digests[i] = digest;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (digests && i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_digest_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int digest, const char **json)
|
||||
{
|
||||
json_object *jobj_digest;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
*json = json_object_to_json_string_ext(jobj_digest, JSON_C_TO_STRING_PLAIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_digest(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int digest, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_digest, *jobj_digest_keyslots;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Keyslot %i %s digest %i.", keyslot, assign ? "assigned to" : "unassigned from", digest);
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "keyslots", &jobj_digest_keyslots);
|
||||
if (!jobj_digest_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_digest_keyslots, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_digest_keyslots, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_digest_keyslots, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_digest, "keyslots", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digests_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, digests_t digests, int assign, int commit)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1; i++) {
|
||||
r = LUKS2_digest_assign(cd, hdr, keyslot, digests[i], assign, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int digest, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
int r = 0;
|
||||
|
||||
if (digest == CRYPT_ANY_DIGEST) {
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_digest(cd, hdr, keyslot, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_digest(cd, hdr, keyslot, digest, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
static int assign_one_segment(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int segment, int digest, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_digest, *jobj_digest_segments;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Segment %i %s digest %i.", segment, assign ? "assigned to" : "unassigned from", digest);
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "segments", &jobj_digest_segments);
|
||||
if (!jobj_digest_segments)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", segment);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_digest_segments, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_digest_segments, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_digest_segments, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_digest, "segments", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_segment_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int segment, int digest, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
int r = 0;
|
||||
|
||||
if (digest == CRYPT_ANY_DIGEST) {
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_segment(cd, hdr, segment, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_segment(cd, hdr, segment, digest, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
static int digest_unused(json_object *jobj_digest)
|
||||
{
|
||||
json_object *jobj;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "segments", &jobj);
|
||||
if (!jobj || !json_object_is_type(jobj, json_type_array) || json_object_array_length(jobj))
|
||||
return 0;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "keyslots", &jobj);
|
||||
if (!jobj || !json_object_is_type(jobj, json_type_array))
|
||||
return 0;
|
||||
|
||||
return json_object_array_length(jobj) ? 0 : 1;
|
||||
}
|
||||
|
||||
void LUKS2_digests_erase_unused(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
if (!jobj_digests || !json_object_is_type(jobj_digests, json_type_object))
|
||||
return;
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
if (digest_unused(val)) {
|
||||
log_dbg("Erasing unused digest %d.", atoi(key));
|
||||
json_object_object_del(jobj_digests, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
197
lib/luks2/luks2_digest_pbkdf2.c
Normal file
197
lib/luks2/luks2_digest_pbkdf2.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
|
||||
#define LUKS_DIGESTSIZE 20 // since SHA1
|
||||
#define LUKS_SALTSIZE 32
|
||||
#define LUKS_MKD_ITERATIONS_MS 125
|
||||
|
||||
static int PBKDF2_digest_verify(struct crypt_device *cd,
|
||||
int digest,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
char checkHashBuf[64];
|
||||
json_object *jobj_digest, *jobj1;
|
||||
const char *hashSpec;
|
||||
char *mkDigest = NULL, mkDigestSalt[LUKS_SALTSIZE];
|
||||
unsigned int mkDigestIterations;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
/* This can be done only for internally linked digests */
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
|
||||
return -EINVAL;
|
||||
hashSpec = json_object_get_string(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
|
||||
return -EINVAL;
|
||||
mkDigestIterations = json_object_get_int64(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(mkDigestSalt);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), mkDigestSalt, &len))
|
||||
return -EINVAL;
|
||||
if (len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
|
||||
return -EINVAL;
|
||||
len = 0;
|
||||
if (!base64_decode_alloc(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), &mkDigest, &len))
|
||||
return -EINVAL;
|
||||
if (len < LUKS_DIGESTSIZE ||
|
||||
len > sizeof(checkHashBuf) ||
|
||||
(len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec))) {
|
||||
free(mkDigest);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = -EPERM;
|
||||
if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
|
||||
mkDigestSalt, LUKS_SALTSIZE,
|
||||
checkHashBuf, len,
|
||||
mkDigestIterations, 0, 0) < 0) {
|
||||
r = -EINVAL;
|
||||
} else {
|
||||
if (memcmp(checkHashBuf, mkDigest, len) == 0)
|
||||
r = 0;
|
||||
}
|
||||
|
||||
free(mkDigest);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int PBKDF2_digest_store(struct crypt_device *cd,
|
||||
int digest,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
json_object *jobj_digest, *jobj_digests;
|
||||
char salt[LUKS_SALTSIZE], digest_raw[128], num[16];
|
||||
int r;
|
||||
char *base64_str;
|
||||
struct luks2_hdr *hdr;
|
||||
struct crypt_pbkdf_type pbkdf = {
|
||||
.type = CRYPT_KDF_PBKDF2,
|
||||
.hash = "sha256",
|
||||
.time_ms = LUKS_MKD_ITERATIONS_MS,
|
||||
};
|
||||
|
||||
log_dbg("Setting PBKDF2 type key digest %d.", digest);
|
||||
|
||||
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
|
||||
pbkdf.iterations = MIN_PBKDF2_ITERATIONS;
|
||||
else {
|
||||
r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
|
||||
salt, LUKS_SALTSIZE, digest_raw, crypt_hmac_size(pbkdf.hash),
|
||||
pbkdf.iterations, 0, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
jobj_digests = NULL;
|
||||
if (!jobj_digest) {
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
jobj_digest = json_object_new_object();
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
|
||||
json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
|
||||
json_object_object_add(jobj_digest, "segments", json_object_new_array());
|
||||
json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
|
||||
json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
|
||||
|
||||
base64_encode_alloc(salt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(jobj_digest);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
|
||||
free(base64_str);
|
||||
|
||||
base64_encode_alloc(digest_raw, crypt_hmac_size(pbkdf.hash), &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(jobj_digest);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
|
||||
free(base64_str);
|
||||
|
||||
if (jobj_digests) {
|
||||
snprintf(num, sizeof(num), "%d", digest);
|
||||
json_object_object_add(jobj_digests, num, jobj_digest);
|
||||
}
|
||||
|
||||
JSON_DBG(jobj_digest, "Digest JSON");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
|
||||
{
|
||||
json_object *jobj_digest, *jobj1;
|
||||
|
||||
/* This can be done only for internally linked digests */
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "hash", &jobj1);
|
||||
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
|
||||
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "salt", &jobj1);
|
||||
log_std(cd, "\tSalt: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "digest", &jobj1);
|
||||
log_std(cd, "\tDigest: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const digest_handler PBKDF2_digest = {
|
||||
.name = "pbkdf2",
|
||||
.verify = PBKDF2_digest_verify,
|
||||
.store = PBKDF2_digest_store,
|
||||
.dump = PBKDF2_digest_dump,
|
||||
};
|
||||
695
lib/luks2/luks2_disk_metadata.c
Normal file
695
lib/luks2/luks2_disk_metadata.c
Normal file
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
json_object *parse_json_len(const char *json_area, int length, int *end_offset)
|
||||
{
|
||||
json_object *jobj;
|
||||
struct json_tokener *jtok;
|
||||
|
||||
if (!json_area || length <= 0)
|
||||
return NULL;
|
||||
|
||||
jtok = json_tokener_new();
|
||||
if (!jtok) {
|
||||
log_dbg("ERROR: Failed to init json tokener");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobj = json_tokener_parse_ex(jtok, json_area, length);
|
||||
if (!jobj)
|
||||
log_dbg("ERROR: Failed to parse json data (%d): %s",
|
||||
json_tokener_get_error(jtok),
|
||||
json_tokener_error_desc(json_tokener_get_error(jtok)));
|
||||
else
|
||||
*end_offset = jtok->char_offset;
|
||||
|
||||
json_tokener_free(jtok);
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
static void log_dbg_checksum(const uint8_t *csum, const char *csum_alg, const char *info)
|
||||
{
|
||||
char csum_txt[2*LUKS2_CHECKSUM_L+1];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < crypt_hash_size(csum_alg); i++)
|
||||
snprintf(&csum_txt[i*2], 3, "%02hhx", (const char)csum[i]);
|
||||
csum_txt[i*2+1] = '\0'; /* Just to be safe, sprintf should write \0 there. */
|
||||
|
||||
log_dbg("Checksum:%s (%s)", &csum_txt[0], info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate hash (checksum) of |LUKS2_bin|LUKS2_JSON_area| from in-memory structs.
|
||||
* LUKS2 on-disk header contains uniques salt both for primary and secondary header.
|
||||
* Checksum is always calculated with zeroed checksum field in binary header.
|
||||
*/
|
||||
static int hdr_checksum_calculate(const char *alg, struct luks2_hdr_disk *hdr_disk,
|
||||
const char *json_area, size_t json_len)
|
||||
{
|
||||
struct crypt_hash *hd = NULL;
|
||||
int r;
|
||||
|
||||
if (crypt_hash_init(&hd, alg))
|
||||
return -EINVAL;
|
||||
|
||||
/* Binary header, csum zeroed. */
|
||||
r = crypt_hash_write(hd, (char*)hdr_disk, LUKS2_HDR_BIN_LEN);
|
||||
|
||||
/* JSON area (including unused space) */
|
||||
if (!r)
|
||||
r = crypt_hash_write(hd, json_area, json_len);
|
||||
|
||||
if (!r)
|
||||
r = crypt_hash_final(hd, (char*)hdr_disk->csum, crypt_hash_size(alg));
|
||||
|
||||
crypt_hash_destroy(hd);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare hash (checksum) of on-disk and in-memory header.
|
||||
*/
|
||||
static int hdr_checksum_check(const char *alg, struct luks2_hdr_disk *hdr_disk,
|
||||
const char *json_area, size_t json_len)
|
||||
{
|
||||
struct luks2_hdr_disk hdr_tmp;
|
||||
int r;
|
||||
|
||||
/* Copy header and zero checksum. */
|
||||
memcpy(&hdr_tmp, hdr_disk, LUKS2_HDR_BIN_LEN);
|
||||
memset(&hdr_tmp.csum, 0, sizeof(hdr_tmp.csum));
|
||||
|
||||
r = hdr_checksum_calculate(alg, &hdr_tmp, json_area, json_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_dbg_checksum(hdr_disk->csum, alg, "on-disk");
|
||||
log_dbg_checksum(hdr_tmp.csum, alg, "in-memory");
|
||||
|
||||
if (memcmp(hdr_tmp.csum, hdr_disk->csum, crypt_hash_size(alg)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert header from on-disk format to in-memory struct
|
||||
*/
|
||||
static void hdr_from_disk(struct luks2_hdr_disk *hdr_disk1,
|
||||
struct luks2_hdr_disk *hdr_disk2,
|
||||
struct luks2_hdr *hdr,
|
||||
int secondary)
|
||||
{
|
||||
hdr->version = be16_to_cpu(hdr_disk1->version);
|
||||
hdr->hdr_size = be64_to_cpu(hdr_disk1->hdr_size);
|
||||
hdr->seqid = be64_to_cpu(hdr_disk1->seqid);
|
||||
|
||||
memcpy(hdr->label, hdr_disk1->label, LUKS2_LABEL_L);
|
||||
hdr->label[LUKS2_LABEL_L - 1] = '\0';
|
||||
memcpy(hdr->subsystem, hdr_disk1->subsystem, LUKS2_LABEL_L);
|
||||
hdr->subsystem[LUKS2_LABEL_L - 1] = '\0';
|
||||
memcpy(hdr->checksum_alg, hdr_disk1->checksum_alg, LUKS2_CHECKSUM_ALG_L);
|
||||
hdr->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
|
||||
memcpy(hdr->uuid, hdr_disk1->uuid, LUKS2_UUID_L);
|
||||
hdr->uuid[LUKS2_UUID_L - 1] = '\0';
|
||||
|
||||
if (secondary) {
|
||||
memcpy(hdr->salt1, hdr_disk2->salt, LUKS2_SALT_L);
|
||||
memcpy(hdr->salt2, hdr_disk1->salt, LUKS2_SALT_L);
|
||||
} else {
|
||||
memcpy(hdr->salt1, hdr_disk1->salt, LUKS2_SALT_L);
|
||||
memcpy(hdr->salt2, hdr_disk2->salt, LUKS2_SALT_L);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert header from in-memory struct to on-disk format
|
||||
*/
|
||||
static void hdr_to_disk(struct luks2_hdr *hdr,
|
||||
struct luks2_hdr_disk *hdr_disk,
|
||||
int secondary, uint64_t offset)
|
||||
{
|
||||
assert(((char*)&(hdr_disk->_padding4096) - (char*)&(hdr_disk->magic)) == 512);
|
||||
|
||||
memset(hdr_disk, 0, LUKS2_HDR_BIN_LEN);
|
||||
|
||||
memcpy(&hdr_disk->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
|
||||
hdr_disk->version = cpu_to_be16(hdr->version);
|
||||
hdr_disk->hdr_size = cpu_to_be64(hdr->hdr_size);
|
||||
hdr_disk->hdr_offset = cpu_to_be64(offset);
|
||||
hdr_disk->seqid = cpu_to_be64(hdr->seqid);
|
||||
|
||||
strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L);
|
||||
hdr_disk->label[LUKS2_LABEL_L - 1] = '\0';
|
||||
strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L);
|
||||
hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0';
|
||||
strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L);
|
||||
hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
|
||||
strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L);
|
||||
hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0';
|
||||
|
||||
memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity checks before checkum is validated
|
||||
*/
|
||||
static int hdr_disk_sanity_check_pre(struct luks2_hdr_disk *hdr,
|
||||
size_t *hdr_json_size, int secondary,
|
||||
uint64_t offset)
|
||||
{
|
||||
if (memcmp(hdr->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
|
||||
return -EINVAL;
|
||||
|
||||
if (be16_to_cpu(hdr->version) != 2) {
|
||||
log_dbg("Unsupported LUKS2 header version %u.", be16_to_cpu(hdr->version));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (offset != be64_to_cpu(hdr->hdr_offset)) {
|
||||
log_dbg("LUKS2 offset 0x%04x on device differs to expected offset 0x%04x.",
|
||||
(unsigned)be64_to_cpu(hdr->hdr_offset), (unsigned)offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: sanity check checksum alg. */
|
||||
|
||||
log_dbg("LUKS2 header version %u of size %u bytes, checksum %s.",
|
||||
(unsigned)be16_to_cpu(hdr->version), (unsigned)be64_to_cpu(hdr->hdr_size),
|
||||
hdr->checksum_alg);
|
||||
|
||||
*hdr_json_size = be64_to_cpu(hdr->hdr_size) - LUKS2_HDR_BIN_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read LUKS2 header from disk at specific offset.
|
||||
*/
|
||||
static int hdr_read_disk(struct device *device, struct luks2_hdr_disk *hdr_disk,
|
||||
char **json_area, uint64_t offset, int secondary)
|
||||
{
|
||||
size_t hdr_json_size = 0;
|
||||
int devfd = -1, r;
|
||||
|
||||
log_dbg("Trying to read %s LUKS2 header at offset %" PRIu64 ".",
|
||||
secondary ? "secondary" : "primary", offset);
|
||||
|
||||
devfd = device_open_locked(device, O_RDONLY);
|
||||
if (devfd < 0)
|
||||
return devfd == -1 ? -EIO : devfd;
|
||||
|
||||
/*
|
||||
* Read binary header and run sanity check before reading
|
||||
* JSON area and validating checksum.
|
||||
*/
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
r = hdr_disk_sanity_check_pre(hdr_disk, &hdr_json_size, secondary, offset);
|
||||
if (r < 0) {
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and read JSON area. Always the whole area must be read.
|
||||
*/
|
||||
*json_area = malloc(hdr_json_size);
|
||||
if (!*json_area) {
|
||||
close(devfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), *json_area, hdr_json_size,
|
||||
offset + LUKS2_HDR_BIN_LEN) != (ssize_t)hdr_json_size) {
|
||||
close(devfd);
|
||||
free(*json_area);
|
||||
*json_area = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(devfd);
|
||||
|
||||
/*
|
||||
* Calculate and validate checksum and zero it afterwards.
|
||||
*/
|
||||
if (hdr_checksum_check(hdr_disk->checksum_alg, hdr_disk,
|
||||
*json_area, hdr_json_size)) {
|
||||
log_dbg("LUKS2 header checksum error (offset %" PRIu64 ").", offset);
|
||||
r = -EINVAL;
|
||||
}
|
||||
memset(hdr_disk->csum, 0, LUKS2_CHECKSUM_L);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write LUKS2 header to disk at specific offset.
|
||||
*/
|
||||
static int hdr_write_disk(struct device *device, struct luks2_hdr *hdr,
|
||||
const char *json_area, int secondary)
|
||||
{
|
||||
struct luks2_hdr_disk hdr_disk;
|
||||
uint64_t offset = secondary ? hdr->hdr_size : 0;
|
||||
size_t hdr_json_len;
|
||||
int devfd = -1, r;
|
||||
|
||||
log_dbg("Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".",
|
||||
hdr->hdr_size, offset);
|
||||
|
||||
/* FIXME: read-only device silent fail? */
|
||||
|
||||
devfd = device_open_locked(device, O_RDWR);
|
||||
if (devfd < 0)
|
||||
return devfd == -1 ? -EINVAL : devfd;
|
||||
|
||||
hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
|
||||
hdr_to_disk(hdr, &hdr_disk, secondary, offset);
|
||||
|
||||
/*
|
||||
* Write header without checksum but with proper seqid.
|
||||
*/
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), (char *)&hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write json area.
|
||||
*/
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device),
|
||||
CONST_CAST(char*)json_area, hdr_json_len,
|
||||
LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate checksum and write header with checkum.
|
||||
*/
|
||||
r = hdr_checksum_calculate(hdr_disk.checksum_alg, &hdr_disk,
|
||||
json_area, hdr_json_len);
|
||||
if (r < 0) {
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
log_dbg_checksum(hdr_disk.csum, hdr_disk.checksum_alg, "in-memory");
|
||||
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), (char *)&hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN)
|
||||
r = -EIO;
|
||||
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int LUKS2_check_device_size(struct crypt_device *cd, struct device *device, uint64_t hdr_size)
|
||||
{
|
||||
uint64_t dev_size;
|
||||
|
||||
if (device_size(device, &dev_size)) {
|
||||
log_dbg("Cannot get device size for device %s.", device_path(device));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
log_dbg("Device size %" PRIu64 ", header size %"
|
||||
PRIu64 ".", dev_size, hdr_size);
|
||||
|
||||
if (hdr_size > dev_size) {
|
||||
log_err(cd, _("Device %s is too small. (LUKS2 requires at least %" PRIu64 " bytes.)\n"),
|
||||
device_path(device), hdr_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert in-memory LUKS2 header and write it to disk.
|
||||
* This will increase sequence id, write both header copies and calculate checksum.
|
||||
*/
|
||||
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct device *device)
|
||||
{
|
||||
char *json_area;
|
||||
const char *json_text;
|
||||
size_t json_area_len;
|
||||
int r;
|
||||
|
||||
if (hdr->version != 2) {
|
||||
log_dbg("Unsupported LUKS2 header version (%u).", hdr->version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->hdr_size != LUKS2_HDR_16K_LEN) {
|
||||
log_dbg("Unsupported LUKS2 header size (%zu).", hdr->hdr_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = LUKS2_check_device_size(cd, crypt_metadata_device(cd), LUKS2_hdr_and_areas_size(hdr->jobj));
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* Allocate and zero JSON area (of proper header size).
|
||||
*/
|
||||
json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
json_area = malloc(json_area_len);
|
||||
if (!json_area)
|
||||
return -ENOMEM;
|
||||
memset(json_area, 0, json_area_len);
|
||||
|
||||
/*
|
||||
* Generate text space-efficient JSON representation to json area.
|
||||
*/
|
||||
json_text = json_object_to_json_string_ext(hdr->jobj, JSON_C_TO_STRING_PLAIN);
|
||||
if (!json_text || !*json_text) {
|
||||
log_dbg("Cannot parse JSON object to text representation.");
|
||||
free(json_area);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (strlen(json_text) > (json_area_len - 1)) {
|
||||
log_dbg("JSON is too large (%zu > %zu).", strlen(json_text), json_area_len);
|
||||
free(json_area);
|
||||
return -EINVAL;
|
||||
}
|
||||
strncpy(json_area, json_text, json_area_len);
|
||||
|
||||
/* Increase sequence id before writing it to disk. */
|
||||
hdr->seqid++;
|
||||
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write device lock.\n"));
|
||||
free(json_area);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Write primary and secondary header */
|
||||
r = hdr_write_disk(device, hdr, json_area, 0);
|
||||
if (!r)
|
||||
r = hdr_write_disk(device, hdr, json_area, 1);
|
||||
|
||||
if (r)
|
||||
log_dbg("LUKS2 header write failed (%d).", r);
|
||||
|
||||
device_write_unlock(device);
|
||||
|
||||
/* FIXME: try recovery here? */
|
||||
|
||||
free(json_area);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int validate_json_area(const char *json_area, int start, int length)
|
||||
{
|
||||
char c;
|
||||
|
||||
/* Enforce there are no needless opening bytes */
|
||||
if (*json_area != '{') {
|
||||
log_dbg("ERROR: Opening character must be left curly bracket: '{'.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (start >= length) {
|
||||
log_dbg("ERROR: Missing trailing null byte beyond parsed json data string.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* validate there are legal json format characters between
|
||||
* 'json_area' and 'json_area + start'
|
||||
*/
|
||||
|
||||
do {
|
||||
c = *(json_area + start);
|
||||
if (c != '\0') {
|
||||
log_dbg("ERROR: Forbidden ascii code 0x%02hhx found beyond json data string at offset %d.",
|
||||
c, start);
|
||||
return -EINVAL;
|
||||
}
|
||||
} while (++start < length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_luks2_json_object(json_object *jobj_hdr)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* we require top level object to be of json_type_object */
|
||||
r = !json_object_is_type(jobj_hdr, json_type_object);
|
||||
if (r) {
|
||||
log_dbg("ERROR: Resulting object is not a json object type");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = LUKS2_hdr_validate(jobj_hdr);
|
||||
if (r)
|
||||
log_dbg("ERROR: LUKS2 validation failed");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static json_object *parse_and_validate_json(const char *json_area, int length)
|
||||
{
|
||||
int offset, r;
|
||||
json_object *jobj = parse_json_len(json_area, length, &offset);
|
||||
|
||||
if (!jobj)
|
||||
return NULL;
|
||||
|
||||
/* successfull parse_json_len must not return offset <= 0 */
|
||||
assert(offset > 0);
|
||||
|
||||
r = validate_json_area(json_area, offset, length);
|
||||
if (!r)
|
||||
r = validate_luks2_json_object(jobj);
|
||||
|
||||
if (r) {
|
||||
json_object_put(jobj);
|
||||
jobj = NULL;
|
||||
}
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and convert on-disk LUKS2 header to in-memory representation..
|
||||
* Try to do recovery if on-disk state is not consistent.
|
||||
*/
|
||||
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device, int do_recovery)
|
||||
{
|
||||
enum { HDR_OK, HDR_OBSOLETE, HDR_FAIL, HDR_FAIL_IO } state_hdr1, state_hdr2;
|
||||
struct luks2_hdr_disk hdr_disk1, hdr_disk2;
|
||||
char *json_area1 = NULL, *json_area2 = NULL;
|
||||
json_object *jobj_hdr1 = NULL, *jobj_hdr2 = NULL;
|
||||
int i, r;
|
||||
uint64_t hdr_size;
|
||||
|
||||
if (do_recovery && !crypt_metadata_locking_enabled()) {
|
||||
do_recovery = 0;
|
||||
log_dbg("Disabling header auto-recovery due to locking being disabled.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Read primary LUKS2 header (offset 0).
|
||||
*/
|
||||
state_hdr1 = HDR_FAIL;
|
||||
r = hdr_read_disk(device, &hdr_disk1, &json_area1, 0, 0);
|
||||
if (r == 0) {
|
||||
jobj_hdr1 = parse_and_validate_json(json_area1, be64_to_cpu(hdr_disk1.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr1 = HDR_FAIL_IO;
|
||||
|
||||
/*
|
||||
* Read secondary LUKS2 header (follows primary).
|
||||
*/
|
||||
state_hdr2 = HDR_FAIL;
|
||||
if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) {
|
||||
r = hdr_read_disk(device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1);
|
||||
if (r == 0) {
|
||||
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr2 = HDR_FAIL_IO;
|
||||
} else {
|
||||
/*
|
||||
* No header size, check all known offsets.
|
||||
*/
|
||||
for (r = -EINVAL,i = 2; r < 0 && i <= 1024; i <<= 1)
|
||||
r = hdr_read_disk(device, &hdr_disk2, &json_area2, i * 4096, 1);
|
||||
|
||||
if (r == 0) {
|
||||
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr2 = HDR_FAIL_IO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check sequence id if both headers are read correctly.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK && state_hdr2 == HDR_OK) {
|
||||
if (be64_to_cpu(hdr_disk1.seqid) > be64_to_cpu(hdr_disk2.seqid))
|
||||
state_hdr2 = HDR_OBSOLETE;
|
||||
else if (be64_to_cpu(hdr_disk1.seqid) < be64_to_cpu(hdr_disk2.seqid))
|
||||
state_hdr1 = HDR_OBSOLETE;
|
||||
}
|
||||
|
||||
/* check header with keyslots fit the device */
|
||||
if (state_hdr1 == HDR_OK)
|
||||
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr1);
|
||||
else if (state_hdr2 == HDR_OK)
|
||||
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr2);
|
||||
else {
|
||||
r = (state_hdr1 == HDR_FAIL_IO && state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = LUKS2_check_device_size(cd, device, hdr_size);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Try to rewrite (recover) bad header. Always regenerate salt for bad header.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK && state_hdr2 != HDR_OK) {
|
||||
log_dbg("Secondary LUKS2 header requires recovery.");
|
||||
|
||||
if (do_recovery) {
|
||||
memcpy(&hdr_disk2, &hdr_disk1, LUKS2_HDR_BIN_LEN);
|
||||
r = crypt_random_get(NULL, (char*)hdr_disk2.salt, sizeof(hdr_disk2.salt), CRYPT_RND_SALT);
|
||||
if (r)
|
||||
log_dbg("Cannot generate master salt.");
|
||||
else {
|
||||
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
|
||||
r = hdr_write_disk(device, hdr, json_area1, 1);
|
||||
}
|
||||
if (r)
|
||||
log_dbg("Secondary LUKS2 header recovery failed.");
|
||||
}
|
||||
} else if (state_hdr1 != HDR_OK && state_hdr2 == HDR_OK) {
|
||||
log_dbg("Primary LUKS2 header requires recovery.");
|
||||
|
||||
if (do_recovery) {
|
||||
memcpy(&hdr_disk1, &hdr_disk2, LUKS2_HDR_BIN_LEN);
|
||||
r = crypt_random_get(NULL, (char*)hdr_disk1.salt, sizeof(hdr_disk1.salt), CRYPT_RND_SALT);
|
||||
if (r)
|
||||
log_dbg("Cannot generate master salt.");
|
||||
else {
|
||||
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
|
||||
r = hdr_write_disk(device, hdr, json_area2, 0);
|
||||
}
|
||||
if (r)
|
||||
log_dbg("Primary LUKS2 header recovery failed.");
|
||||
}
|
||||
}
|
||||
|
||||
free(json_area1);
|
||||
json_area1 = NULL;
|
||||
free(json_area2);
|
||||
json_area2 = NULL;
|
||||
|
||||
/* wrong lock for write mode during recovery attempt */
|
||||
if (r == -EAGAIN)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Even if status is failed, the second header includes salt.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK) {
|
||||
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
|
||||
hdr->jobj = jobj_hdr1;
|
||||
json_object_put(jobj_hdr2);
|
||||
return 0;
|
||||
} else if (state_hdr2 == HDR_OK) {
|
||||
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
|
||||
hdr->jobj = jobj_hdr2;
|
||||
json_object_put(jobj_hdr1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = (state_hdr1 == HDR_FAIL_IO || state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
|
||||
|
||||
err:
|
||||
log_dbg("LUKS2 header read failed (%d).", r);
|
||||
|
||||
free(json_area1);
|
||||
free(json_area2);
|
||||
json_object_put(jobj_hdr1);
|
||||
json_object_put(jobj_hdr2);
|
||||
hdr->jobj = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_hdr_version_unlocked(struct crypt_device *cd)
|
||||
{
|
||||
struct {
|
||||
char magic[LUKS2_MAGIC_L];
|
||||
uint16_t version;
|
||||
} __attribute__ ((packed)) hdr;
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
int r, devfd, flags;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
flags = O_RDONLY;
|
||||
if (device_direct_io(device))
|
||||
flags |= O_DIRECT;
|
||||
|
||||
devfd = open(device_path(device), flags);
|
||||
if (devfd < 0)
|
||||
return 0;
|
||||
|
||||
if ((read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), &hdr, sizeof(hdr), 0)
|
||||
!= sizeof(hdr)) ||
|
||||
memcmp(hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
|
||||
r = 0;
|
||||
else
|
||||
r = (int)be16_to_cpu(hdr.version);
|
||||
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
154
lib/luks2/luks2_internal.h
Normal file
154
lib/luks2/luks2_internal.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTSETUP_LUKS2_INTERNAL_H
|
||||
#define _CRYPTSETUP_LUKS2_INTERNAL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "base64.h"
|
||||
#include "luks2.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
/*
|
||||
* On-disk access function prototypes
|
||||
*/
|
||||
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device, int do_recovery);
|
||||
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device);
|
||||
|
||||
/*
|
||||
* JSON struct access helpers
|
||||
*/
|
||||
json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot);
|
||||
json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token);
|
||||
json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int keyslot);
|
||||
json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment);
|
||||
|
||||
void hexprint_base64(struct crypt_device *cd, json_object *jobj,
|
||||
const char *sep, const char *line_sep);
|
||||
|
||||
json_object *parse_json_len(const char *json_area, int length, int *end_offset);
|
||||
uint64_t json_object_get_uint64(json_object *jobj);
|
||||
uint32_t json_object_get_uint32(json_object *jobj);
|
||||
|
||||
void JSON_DBG(json_object *jobj, const char *desc);
|
||||
|
||||
/*
|
||||
* LUKS2 JSON validation
|
||||
*/
|
||||
|
||||
int LUKS2_hdr_validate(json_object *hdr_jobj);
|
||||
int LUKS2_keyslot_validate(json_object *hdr_jobj, json_object *hdr_keyslot, const char *key);
|
||||
int LUKS2_check_json_size(const struct luks2_hdr *hdr);
|
||||
int LUKS2_token_validate(json_object *hdr_jobj, json_object *jobj_token, const char *key);
|
||||
void LUKS2_token_dump(struct crypt_device *cd, int token);
|
||||
|
||||
/*
|
||||
* JSON array helpers
|
||||
*/
|
||||
struct json_object *LUKS2_array_jobj(struct json_object *array, const char *num);
|
||||
struct json_object *LUKS2_array_remove(struct json_object *array, const char *num);
|
||||
|
||||
/*
|
||||
* Plugins API
|
||||
*/
|
||||
|
||||
/**
|
||||
* LUKS2 keyslots handlers (EXPERIMENTAL)
|
||||
*/
|
||||
typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot,
|
||||
size_t volume_key_len);
|
||||
typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot,
|
||||
const char *password, size_t password_len,
|
||||
char *volume_key, size_t volume_key_len);
|
||||
typedef int (*keyslot_store_func)(struct crypt_device *cd, int keyslot,
|
||||
const char *password, size_t password_len,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*keyslot_wipe_func) (struct crypt_device *cd, int keyslot);
|
||||
typedef int (*keyslot_dump_func) (struct crypt_device *cd, int keyslot);
|
||||
typedef int (*keyslot_validate_func) (struct crypt_device *cd, int keyslot);
|
||||
|
||||
int luks2_keyslot_alloc(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
size_t volume_key_len);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
keyslot_alloc_func alloc;
|
||||
keyslot_open_func open;
|
||||
keyslot_store_func store;
|
||||
keyslot_wipe_func wipe;
|
||||
keyslot_dump_func dump;
|
||||
keyslot_validate_func validate;
|
||||
} keyslot_handler;
|
||||
|
||||
/**
|
||||
* LUKS2 digest handlers (EXPERIMENTAL)
|
||||
*/
|
||||
typedef int (*digest_verify_func)(struct crypt_device *cd, int digest,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*digest_store_func) (struct crypt_device *cd, int digest,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*digest_dump_func) (struct crypt_device *cd, int digest);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
digest_verify_func verify;
|
||||
digest_store_func store;
|
||||
digest_dump_func dump;
|
||||
} digest_handler;
|
||||
|
||||
int crypt_digest_register(const digest_handler *handler);
|
||||
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type);
|
||||
|
||||
#define CRYPT_ANY_DIGEST -1
|
||||
int crypt_keyslot_assign_digest(struct crypt_device *cd, int keyslot, int digest);
|
||||
int crypt_keyslot_unassign_digest(struct crypt_device *cd, int keyslot, int digest);
|
||||
|
||||
/**
|
||||
* LUKS2 token handlers (internal use only)
|
||||
*/
|
||||
typedef int (*builtin_token_get_func) (json_object *jobj_token, void *params);
|
||||
typedef int (*builtin_token_set_func) (json_object **jobj_token, const void *params);
|
||||
|
||||
typedef struct {
|
||||
/* internal only section used by builtin tokens */
|
||||
builtin_token_get_func get;
|
||||
builtin_token_set_func set;
|
||||
/* public token handler */
|
||||
const crypt_token_handler *h;
|
||||
} token_handler;
|
||||
|
||||
int token_keyring_set(json_object **, const void *);
|
||||
int token_keyring_get(json_object *, void *);
|
||||
|
||||
#define CRYPT_ANY_TOKEN -1
|
||||
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
size_t keylength, uint64_t *area_offset, uint64_t *area_length);
|
||||
|
||||
#endif
|
||||
233
lib/luks2/luks2_json_format.c
Normal file
233
lib/luks2/luks2_json_format.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS2 header format code
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
struct area {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
static size_t get_area_size(size_t keylength)
|
||||
{
|
||||
//FIXME: calculate this properly, for now it is AF_split_sectors
|
||||
return size_round_up(keylength * 4000, 4096);
|
||||
}
|
||||
|
||||
static size_t get_min_offset(struct luks2_hdr *hdr)
|
||||
{
|
||||
return 2 * hdr->hdr_size;
|
||||
}
|
||||
|
||||
static size_t get_max_offset(struct crypt_device *cd)
|
||||
{
|
||||
return crypt_get_data_offset(cd) * SECTOR_SIZE;
|
||||
}
|
||||
|
||||
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
size_t keylength, uint64_t *area_offset, uint64_t *area_length)
|
||||
{
|
||||
struct area areas[LUKS2_KEYSLOTS_MAX], sorted_areas[LUKS2_KEYSLOTS_MAX] = {};
|
||||
int i, j, k, area_i;
|
||||
size_t offset, length;
|
||||
|
||||
/* fill area offset + length table */
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
if (!LUKS2_keyslot_area(hdr, i, &areas[i].offset, &areas[i].length))
|
||||
continue;
|
||||
areas[i].length = 0;
|
||||
areas[i].offset = 0;
|
||||
}
|
||||
|
||||
/* sort table */
|
||||
k = 0; /* index in sorted table */
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
offset = get_max_offset(cd) ?: UINT64_MAX;
|
||||
area_i = -1;
|
||||
/* search for the smallest offset in table */
|
||||
for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++)
|
||||
if (areas[j].offset && areas[j].offset <= offset) {
|
||||
area_i = j;
|
||||
offset = areas[j].offset;
|
||||
}
|
||||
|
||||
if (area_i >= 0) {
|
||||
sorted_areas[k].length = areas[area_i].length;
|
||||
sorted_areas[k].offset = areas[area_i].offset;
|
||||
areas[area_i].length = 0;
|
||||
areas[area_i].offset = 0;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* search for the gap we can use */
|
||||
offset = get_min_offset(hdr);
|
||||
length = get_area_size(keylength);
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
/* skip empty */
|
||||
if (sorted_areas[i].offset == 0 || sorted_areas[i].length == 0)
|
||||
continue;
|
||||
|
||||
/* enough space before the used area */
|
||||
if ((offset < sorted_areas[i].offset) && ((offset + length) <= sorted_areas[i].offset))
|
||||
break;
|
||||
|
||||
/* both offset and length are already aligned to 4096 bytes */
|
||||
offset = sorted_areas[i].offset + sorted_areas[i].length;
|
||||
}
|
||||
|
||||
if (get_max_offset(cd) && (offset + length) > get_max_offset(cd)) {
|
||||
log_err(cd, _("No space for new keyslot.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_dbg("Found area %zu -> %zu", offset, length + offset);
|
||||
/*
|
||||
log_dbg("Area offset min: %zu, max %zu, slots max %u",
|
||||
get_min_offset(hdr), get_max_offset(cd), LUKS2_KEYSLOTS_MAX);
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
|
||||
log_dbg("SLOT[%02i]: %-8" PRIu64 " -> %-8" PRIu64, i,
|
||||
sorted_areas[i].offset,
|
||||
sorted_areas[i].length + sorted_areas[i].offset);
|
||||
*/
|
||||
*area_offset = offset;
|
||||
*area_length = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_generate_hdr(
|
||||
struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk,
|
||||
const char *cipherName,
|
||||
const char *cipherMode,
|
||||
const char *integrity,
|
||||
const char *uuid,
|
||||
unsigned int sector_size,
|
||||
unsigned int alignPayload,
|
||||
unsigned int alignOffset,
|
||||
int detached_metadata_device)
|
||||
{
|
||||
struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
|
||||
char num[24], cipher[128];
|
||||
uint64_t offset, json_size, keyslots_size;
|
||||
uuid_t partitionUuid;
|
||||
int digest;
|
||||
|
||||
hdr->hdr_size = LUKS2_HDR_16K_LEN;
|
||||
hdr->seqid = 1;
|
||||
hdr->version = 2;
|
||||
memset(hdr->label, 0, LUKS2_LABEL_L);
|
||||
strcpy(hdr->checksum_alg, "sha256");
|
||||
crypt_random_get(NULL, (char*)hdr->salt1, LUKS2_SALT_L, CRYPT_RND_SALT);
|
||||
crypt_random_get(NULL, (char*)hdr->salt2, LUKS2_SALT_L, CRYPT_RND_SALT);
|
||||
|
||||
if (uuid && uuid_parse(uuid, partitionUuid) == -1) {
|
||||
log_err(cd, _("Wrong LUKS UUID format provided.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!uuid)
|
||||
uuid_generate(partitionUuid);
|
||||
|
||||
uuid_unparse(partitionUuid, hdr->uuid);
|
||||
|
||||
if (*cipherMode != '\0')
|
||||
snprintf(cipher, sizeof(cipher), "%s-%s", cipherName, cipherMode);
|
||||
else
|
||||
snprintf(cipher, sizeof(cipher), "%s", cipherName);
|
||||
|
||||
hdr->jobj = json_object_new_object();
|
||||
|
||||
jobj_keyslots = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "keyslots", jobj_keyslots);
|
||||
json_object_object_add(hdr->jobj, "tokens", json_object_new_object());
|
||||
jobj_segments = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "segments", jobj_segments);
|
||||
json_object_object_add(hdr->jobj, "digests", json_object_new_object());
|
||||
jobj_config = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "config", jobj_config);
|
||||
|
||||
digest = LUKS2_digest_create(cd, "pbkdf2", hdr, vk);
|
||||
if (digest < 0) {
|
||||
json_object_put(hdr->jobj);
|
||||
hdr->jobj = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (LUKS2_digest_segment_assign(cd, hdr, CRYPT_DEFAULT_SEGMENT, digest, 1, 0) < 0) {
|
||||
json_object_put(hdr->jobj);
|
||||
hdr->jobj = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
jobj_segment = json_object_new_object();
|
||||
json_object_object_add(jobj_segment, "type", json_object_new_string("crypt"));
|
||||
if (detached_metadata_device)
|
||||
offset = alignPayload * sector_size;
|
||||
else {
|
||||
//FIXME
|
||||
//offset = size_round_up(areas[7].offset + areas[7].length, alignPayload * SECTOR_SIZE);
|
||||
offset = size_round_up(LUKS2_HDR_DEFAULT_LEN, alignPayload * sector_size);
|
||||
offset += alignOffset;
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_segment, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string("0"));
|
||||
json_object_object_add(jobj_segment, "size", json_object_new_string("dynamic"));
|
||||
json_object_object_add(jobj_segment, "encryption", json_object_new_string(cipher));
|
||||
json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
|
||||
|
||||
if (integrity) {
|
||||
jobj_integrity = json_object_new_object();
|
||||
json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
|
||||
json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
|
||||
json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
|
||||
json_object_object_add(jobj_segment, "integrity", jobj_integrity);
|
||||
}
|
||||
|
||||
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
|
||||
json_object_object_add(jobj_segments, num, jobj_segment);
|
||||
|
||||
json_size = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
json_object_object_add(jobj_config, "json_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
|
||||
|
||||
/* for detached metadata device compute reasonable keyslot areas size */
|
||||
// FIXME: this is coupled with FIXME above
|
||||
if (detached_metadata_device)
|
||||
keyslots_size = LUKS2_HDR_DEFAULT_LEN - get_min_offset(hdr);
|
||||
else
|
||||
keyslots_size = 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);
|
||||
|
||||
json_object_object_add(jobj_config, "keyslots_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
|
||||
|
||||
JSON_DBG(hdr->jobj, "Header JSON");
|
||||
return 0;
|
||||
}
|
||||
1810
lib/luks2/luks2_json_metadata.c
Normal file
1810
lib/luks2/luks2_json_metadata.c
Normal file
File diff suppressed because it is too large
Load Diff
477
lib/luks2/luks2_keyslot.c
Normal file
477
lib/luks2/luks2_keyslot.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, keyslot handling
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* Internal implementations */
|
||||
extern const keyslot_handler luks2_keyslot;
|
||||
|
||||
static const keyslot_handler *keyslot_handlers[LUKS2_KEYSLOTS_MAX] = {
|
||||
&luks2_keyslot,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const keyslot_handler
|
||||
*LUKS2_keyslot_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX && keyslot_handlers[i]; i++) {
|
||||
if (!strcmp(keyslot_handlers[i]->name, type))
|
||||
return keyslot_handlers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const keyslot_handler
|
||||
*LUKS2_keyslot_handler(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (keyslot < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_keyslot_jobj(hdr, keyslot)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static crypt_keyslot_info LUKS2_keyslot_active(struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
if (keyslot >= LUKS2_KEYSLOTS_MAX)
|
||||
return CRYPT_SLOT_INVALID;
|
||||
|
||||
return LUKS2_get_keyslot_jobj(hdr, keyslot) ? CRYPT_SLOT_ACTIVE : CRYPT_SLOT_INACTIVE;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
|
||||
if (!LUKS2_get_keyslot_jobj(hdr, i))
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int digests_by_segment(json_object *jobj_digests, const char *segment,
|
||||
digests_t digests)
|
||||
{
|
||||
json_object *jobj_segs;
|
||||
int i = 0;
|
||||
|
||||
json_object_object_foreach(jobj_digests, dig, val) {
|
||||
json_object_object_get_ex(val, "segments", &jobj_segs);
|
||||
if (LUKS2_array_jobj(jobj_segs, segment))
|
||||
digests[i++] = atoi(dig);
|
||||
}
|
||||
|
||||
if (i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
static int is_in(const int super[], int super_size, int elem)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < super_size && super[i] != -1; i++)
|
||||
if (super[i] == elem)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_subset(const int super[], const int sub[], int super_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < super_size && sub[i] != -1; i++)
|
||||
if (!is_in(super, super_size, sub[i]))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
|
||||
{
|
||||
char keyslot_name[16], segment_name[16];
|
||||
digests_t keyslot_digests, segment_digests;
|
||||
json_object *jobj_digests;
|
||||
int r = -ENOENT;
|
||||
|
||||
/* no need to check anything */
|
||||
if (segment == CRYPT_ANY_SEGMENT)
|
||||
return 0;
|
||||
|
||||
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1 ||
|
||||
snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* empty set is subset of any set and it'd be wrong */
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
r = LUKS2_digests_by_keyslot(NULL, hdr, keyslot, keyslot_digests);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* empty set can't be superset of non-empty one */
|
||||
if (digests_by_segment(jobj_digests, segment_name, segment_digests))
|
||||
return r;
|
||||
|
||||
/*
|
||||
* keyslot may activate segment if set of digests for keyslot
|
||||
* is actually subset of set of digests for segment
|
||||
*/
|
||||
return is_subset(segment_digests, keyslot_digests, LUKS2_DIGEST_MAX) ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment)
|
||||
{
|
||||
int num = 0;
|
||||
json_object *jobj_keyslots;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
|
||||
|
||||
/* keyslot digests must be subset of segment digests */
|
||||
json_object_object_foreach(jobj_keyslots, slot, val) {
|
||||
UNUSED(val);
|
||||
if (!LUKS2_keyslot_for_segment(hdr, atoi(slot), segment))
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
crypt_keyslot_info ki;
|
||||
|
||||
if(keyslot >= LUKS2_KEYSLOTS_MAX || keyslot < 0)
|
||||
return CRYPT_SLOT_INVALID;
|
||||
|
||||
ki = LUKS2_keyslot_active(hdr, keyslot);
|
||||
if (ki != CRYPT_SLOT_ACTIVE)
|
||||
return ki;
|
||||
|
||||
if (LUKS2_keyslot_active_count(hdr, CRYPT_DEFAULT_SEGMENT) == 1 && !LUKS2_keyslot_for_segment(hdr, keyslot, CRYPT_DEFAULT_SEGMENT))
|
||||
return CRYPT_SLOT_ACTIVE_LAST;
|
||||
|
||||
return CRYPT_SLOT_ACTIVE;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
uint64_t *offset,
|
||||
uint64_t *length)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj_area, *jobj;
|
||||
|
||||
if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -ENOENT;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj))
|
||||
return -EINVAL;
|
||||
*offset = json_object_get_int64(jobj);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "size", &jobj))
|
||||
return -EINVAL;
|
||||
*length = json_object_get_int64(jobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LUKS2_open_and_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int segment,
|
||||
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 = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
|
||||
if (r) {
|
||||
if (r == -ENOENT)
|
||||
log_dbg("Keyslot %d unusable for segment %d.", keyslot, segment);
|
||||
return r;
|
||||
}
|
||||
|
||||
key_size = LUKS2_get_volume_key_size(hdr, segment);
|
||||
if (key_size < 0)
|
||||
key_size = LUKS2_get_keyslot_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("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_keyslot_open_priority(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
crypt_keyslot_priority priority,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
int segment,
|
||||
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("Keyslot %d priority %d != %d (required), skipped.",
|
||||
keyslot, slot_priority, priority);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, 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;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
int segment,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
int r_prio, r = -EINVAL;
|
||||
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT) {
|
||||
r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
|
||||
password, password_len, segment, vk);
|
||||
if (r_prio >= 0)
|
||||
r = r_prio;
|
||||
else if (r_prio < 0 && (r_prio != -EPERM) && (r_prio != -ENOENT))
|
||||
r = r_prio;
|
||||
else
|
||||
r = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL,
|
||||
password, password_len, segment, 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(cd, hdr, keyslot, segment, password, password_len, vk);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_store(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const struct volume_key *vk)
|
||||
{
|
||||
const keyslot_handler *h;
|
||||
int r;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!LUKS2_get_keyslot_jobj(hdr, keyslot)) {
|
||||
/* Try to allocate default and empty keyslot type */
|
||||
h = LUKS2_keyslot_handler_type(cd, "luks2");
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->alloc(cd, keyslot, vk->keylength);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
|
||||
return -EINVAL;
|
||||
|
||||
r = h->validate(cd, keyslot);
|
||||
if (r) {
|
||||
log_dbg("Keyslot validation failed.");
|
||||
return r;
|
||||
}
|
||||
|
||||
return h->store(cd, keyslot, password, password_len,
|
||||
vk->key, vk->keylength);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_wipe(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int wipe_area_only)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
uint64_t area_offset, area_length;
|
||||
char num[16];
|
||||
int r;
|
||||
json_object *jobj_keyslot, *jobj_keyslots;
|
||||
const keyslot_handler *h;
|
||||
|
||||
h = LUKS2_keyslot_handler(cd, keyslot);
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -ENOENT;
|
||||
|
||||
if (wipe_area_only)
|
||||
log_dbg("Wiping keyslot %d area only.", keyslot);
|
||||
|
||||
/* Just check that nobody uses the metadata now */
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
device_write_unlock(device);
|
||||
|
||||
/* secure deletion of possible key material in keyslot area */
|
||||
r = crypt_keyslot_area(cd, keyslot, &area_offset, &area_length);
|
||||
if (r && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
/* We can destroy the binary keyslot area now without lock */
|
||||
if (!r) {
|
||||
r = crypt_wipe_device(cd, device, CRYPT_WIPE_SPECIAL, area_offset,
|
||||
area_length, area_length, NULL, NULL);
|
||||
if (r) {
|
||||
if (r == -EACCES) {
|
||||
log_err(cd, _("Cannot write to device %s, permission denied.\n"),
|
||||
device_path(device));
|
||||
r = -EINVAL;
|
||||
} else
|
||||
log_err(cd, _("Cannot wipe device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (wipe_area_only)
|
||||
return r;
|
||||
|
||||
/* Slot specific wipe */
|
||||
if (h) {
|
||||
r = h->wipe(cd, keyslot);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
log_dbg("Wiping keyslot %d without specific-slot handler loaded.", keyslot);
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
json_object_object_del(jobj_keyslots, num);
|
||||
|
||||
return LUKS2_hdr_write(cd, hdr);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_dump(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
const keyslot_handler *h;
|
||||
|
||||
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
|
||||
return -EINVAL;
|
||||
|
||||
return h->dump(cd, keyslot);
|
||||
}
|
||||
|
||||
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj_priority;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return CRYPT_SLOT_PRIORITY_INVALID;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "priority", &jobj_priority))
|
||||
return CRYPT_SLOT_PRIORITY_NORMAL;
|
||||
|
||||
return json_object_get_int(jobj_priority);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_priority_set(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, crypt_keyslot_priority priority, int commit)
|
||||
{
|
||||
json_object *jobj_keyslot;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
if (priority == CRYPT_SLOT_PRIORITY_NORMAL)
|
||||
json_object_object_del(jobj_keyslot, "priority");
|
||||
else
|
||||
json_object_object_add(jobj_keyslot, "priority", json_object_new_int(priority));
|
||||
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
687
lib/luks2/luks2_keyslot_luks2.c
Normal file
687
lib/luks2/luks2_keyslot_luks2.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* FIXME: move keyslot encryption to crypto backend */
|
||||
#include "../luks1/af.h"
|
||||
|
||||
#define LUKS_SALTSIZE 32
|
||||
#define LUKS_SLOT_ITERATIONS_MIN 1000
|
||||
#define LUKS_STRIPES 4000
|
||||
|
||||
static int luks2_encrypt_to_storage(char *src, size_t srcLength,
|
||||
const char *cipher, const char *cipher_mode,
|
||||
struct volume_key *vk, unsigned int sector,
|
||||
struct crypt_device *cd)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
|
||||
int r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
r = LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd);
|
||||
device_write_unlock(crypt_metadata_device(cd));
|
||||
return r;
|
||||
#else
|
||||
struct crypt_storage *s;
|
||||
int devfd = -1, r;
|
||||
|
||||
/* Only whole sector writes supported */
|
||||
if (srcLength % SECTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Encrypt buffer */
|
||||
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||
if (r) {
|
||||
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
|
||||
cipher, cipher_mode, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = crypt_storage_encrypt(s, 0, srcLength / SECTOR_SIZE, src);
|
||||
crypt_storage_destroy(s);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
|
||||
devfd = device_open_locked(device, O_RDWR);
|
||||
if (devfd >= 0) {
|
||||
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||
write_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), src,
|
||||
srcLength) == -1)
|
||||
r = -EIO;
|
||||
else
|
||||
r = 0;
|
||||
close(devfd);
|
||||
} else
|
||||
r = -EIO;
|
||||
|
||||
device_write_unlock(device);
|
||||
|
||||
if (r)
|
||||
log_err(cd, _("IO error while encrypting keyslot.\n"));
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int luks2_decrypt_from_storage(char *dst, size_t dstLength,
|
||||
const char *cipher, const char *cipher_mode, struct volume_key *vk,
|
||||
unsigned int sector, struct crypt_device *cd)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
|
||||
int r = device_read_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire read lock on device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
r = LUKS_decrypt_from_storage(dst, dstLength, cipher, cipher_mode, vk, sector, cd);
|
||||
device_read_unlock(crypt_metadata_device(cd));
|
||||
return r;
|
||||
#else
|
||||
struct crypt_storage *s;
|
||||
int devfd = -1, r;
|
||||
|
||||
/* Only whole sector writes supported */
|
||||
if (dstLength % SECTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||
if (r) {
|
||||
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
|
||||
cipher, cipher_mode, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = device_read_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire read lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
|
||||
devfd = device_open_locked(device, O_RDONLY);
|
||||
if (devfd >= 0) {
|
||||
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||
read_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), dst, dstLength) == -1)
|
||||
r = -EIO;
|
||||
else
|
||||
r = 0;
|
||||
close(devfd);
|
||||
} else
|
||||
r = -EIO;
|
||||
|
||||
device_read_unlock(device);
|
||||
|
||||
if (r) {
|
||||
log_err(cd, _("IO error while decrypting keyslot.\n"));
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Decrypt buffer */
|
||||
r = crypt_storage_decrypt(s, 0, dstLength / SECTOR_SIZE, dst);
|
||||
crypt_storage_destroy(s);
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int luks2_keyslot_set_key(struct crypt_device *cd,
|
||||
json_object *jobj_keyslot,
|
||||
const char *password, size_t passwordLen,
|
||||
const char *volume_key, size_t volume_key_len)
|
||||
{
|
||||
struct volume_key *derived_key;
|
||||
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
|
||||
char *AfKey = NULL, *salt_base64 = NULL;
|
||||
size_t AFEKSize, keyslot_key_len;
|
||||
json_object *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint64_t area_offset;
|
||||
const struct crypt_pbkdf_type *pbkdf;
|
||||
int r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
|
||||
return -EINVAL;
|
||||
area_offset = json_object_get_uint64(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
|
||||
return -EINVAL;
|
||||
keyslot_key_len = json_object_get_int(jobj2);
|
||||
|
||||
pbkdf = crypt_get_pbkdf_type(cd);
|
||||
if (!pbkdf)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
|
||||
} else {
|
||||
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
|
||||
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
|
||||
/*
|
||||
* Get salt and allocate derived key storage.
|
||||
*/
|
||||
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
base64_encode_alloc(salt, LUKS_SALTSIZE, &salt_base64);
|
||||
if (!salt_base64)
|
||||
return -ENOMEM;
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(salt_base64));
|
||||
free(salt_base64);
|
||||
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
|
||||
|
||||
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
|
||||
if (!derived_key)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Calculate keyslot content, split and store it to keyslot area.
|
||||
*/
|
||||
r = crypt_pbkdf(pbkdf->type, pbkdf->hash, password, passwordLen,
|
||||
salt, LUKS_SALTSIZE,
|
||||
derived_key->key, derived_key->keylength,
|
||||
pbkdf->iterations, pbkdf->max_memory_kb,
|
||||
pbkdf->parallel_threads);
|
||||
if (r < 0) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return r;
|
||||
}
|
||||
|
||||
// FIXME: verity key_size to AFEKSize
|
||||
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
|
||||
AfKey = crypt_safe_alloc(AFEKSize);
|
||||
if (!AfKey) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = AF_split(volume_key, AfKey, volume_key_len, LUKS_STRIPES, pbkdf->hash);
|
||||
|
||||
if (r == 0) {
|
||||
log_dbg("Updating keyslot area [0x%04x].", (unsigned)area_offset);
|
||||
/* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */
|
||||
r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode,
|
||||
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
|
||||
}
|
||||
|
||||
crypt_safe_free(AfKey);
|
||||
crypt_free_volume_key(derived_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
JSON_DBG(jobj_keyslot, "Keyslot JSON");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_get_key(struct crypt_device *cd,
|
||||
json_object *jobj_keyslot,
|
||||
const char *password, size_t passwordLen,
|
||||
char *volume_key, size_t volume_key_len)
|
||||
{
|
||||
struct volume_key *derived_key;
|
||||
char *AfKey;
|
||||
size_t AFEKSize;
|
||||
const char *hash = NULL, *af_hash = NULL, *kdf;
|
||||
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
|
||||
json_object *jobj1, *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint32_t iterations, memory, parallel;
|
||||
uint64_t area_offset;
|
||||
size_t salt_len, keyslot_key_len;
|
||||
int r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
kdf = json_object_get_string(jobj1);
|
||||
if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) {
|
||||
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj2))
|
||||
return -EINVAL;
|
||||
hash = json_object_get_string(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj2))
|
||||
return -EINVAL;
|
||||
iterations = json_object_get_int(jobj2);
|
||||
memory = 0;
|
||||
parallel = 0;
|
||||
} else {
|
||||
if (!json_object_object_get_ex(jobj_kdf, "time", &jobj2))
|
||||
return -EINVAL;
|
||||
iterations = json_object_get_int(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "memory", &jobj2))
|
||||
return -EINVAL;
|
||||
memory = json_object_get_int(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "cpus", &jobj2))
|
||||
return -EINVAL;
|
||||
parallel = json_object_get_int(jobj2);
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj2))
|
||||
return -EINVAL;
|
||||
salt_len = LUKS_SALTSIZE;
|
||||
base64_decode(json_object_get_string(jobj2),
|
||||
json_object_get_string_len(jobj2),
|
||||
salt, &salt_len);
|
||||
if (salt_len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_af, "hash", &jobj2))
|
||||
return -EINVAL;
|
||||
af_hash = json_object_get_string(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
|
||||
return -EINVAL;
|
||||
area_offset = json_object_get_uint64(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
|
||||
return -EINVAL;
|
||||
keyslot_key_len = json_object_get_int(jobj2);
|
||||
|
||||
/*
|
||||
* Allocate derived key storage space.
|
||||
*/
|
||||
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
|
||||
if (!derived_key)
|
||||
return -ENOMEM;
|
||||
|
||||
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
|
||||
AfKey = crypt_safe_alloc(AFEKSize);
|
||||
if (!AfKey) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Calculate derived key, decrypt keyslot content and merge it.
|
||||
*/
|
||||
r = crypt_pbkdf(kdf, hash, password, passwordLen,
|
||||
salt, LUKS_SALTSIZE,
|
||||
derived_key->key, derived_key->keylength,
|
||||
iterations, memory, parallel);
|
||||
|
||||
if (r == 0) {
|
||||
log_dbg("Reading keyslot area [0x%04x].", (unsigned)area_offset);
|
||||
/* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */
|
||||
r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode,
|
||||
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
r = AF_merge(AfKey, volume_key, volume_key_len, LUKS_STRIPES, af_hash);
|
||||
|
||||
crypt_free_volume_key(derived_key);
|
||||
crypt_safe_free(AfKey);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int luks2_keyslot_alloc(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
const struct crypt_pbkdf_type *pbkdf;
|
||||
char area_offset_string[24], area_length_string[24];
|
||||
char cipher[2 * MAX_CIPHER_LEN + 1], num[16];
|
||||
uint64_t area_offset, area_length;
|
||||
json_object *jobj_keyslots, *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
size_t keyslot_key_len;
|
||||
int r;
|
||||
|
||||
log_dbg("Trying to allocate LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT)
|
||||
keyslot = LUKS2_keyslot_find_empty(hdr, "luks2");
|
||||
|
||||
if (keyslot < 0 || keyslot > LUKS2_KEYSLOTS_MAX)
|
||||
return -ENOMEM;
|
||||
|
||||
if (LUKS2_get_keyslot_jobj(hdr, keyslot)) {
|
||||
log_dbg("Cannot modify already active keyslot %d.", keyslot);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
|
||||
return -EINVAL;
|
||||
|
||||
r = LUKS2_find_area_gap(cd, hdr, volume_key_len, &area_offset, &area_length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pbkdf = crypt_get_pbkdf_type(cd);
|
||||
if (!pbkdf)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
jobj_keyslot = json_object_new_object();
|
||||
json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2"));
|
||||
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len));
|
||||
|
||||
/* PBKDF object */
|
||||
jobj_kdf = json_object_new_object();
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
|
||||
} else {
|
||||
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
|
||||
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
|
||||
}
|
||||
json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
|
||||
|
||||
/* AF object */
|
||||
jobj_af = json_object_new_object();
|
||||
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
|
||||
json_object_object_add(jobj_keyslot, "af", jobj_af);
|
||||
|
||||
/* Area object */
|
||||
jobj_area = json_object_new_object();
|
||||
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
|
||||
|
||||
/* Slot encryption tries to use the same key size as fot the main algorithm */
|
||||
keyslot_key_len = volume_key_len - crypt_get_integrity_key_size(cd);
|
||||
|
||||
/* Cannot use metadata tags in keyslot */
|
||||
if (crypt_get_integrity_tag_size(cd)) {
|
||||
snprintf(cipher, sizeof(cipher), "aes-xts-plain64"); // FIXME: fixed cipher and key size can be wrong
|
||||
keyslot_key_len = 32;
|
||||
} else if (crypt_get_cipher_mode(cd))
|
||||
snprintf(cipher, sizeof(cipher), "%s-%s", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
|
||||
else
|
||||
snprintf(cipher, sizeof(cipher), "%s", crypt_get_cipher(cd));
|
||||
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
|
||||
json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_key_len));
|
||||
uint64_to_str(area_offset_string, sizeof(area_offset_string), &area_offset);
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(area_offset_string));
|
||||
uint64_to_str(area_length_string, sizeof(area_length_string), &area_length);
|
||||
json_object_object_add(jobj_area, "size", json_object_new_string(area_length_string));
|
||||
json_object_object_add(jobj_keyslot, "area", jobj_area);
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
|
||||
json_object_object_add(jobj_keyslots, num, jobj_keyslot);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new keyslot.");
|
||||
json_object_object_del(jobj_keyslots, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot;
|
||||
|
||||
log_dbg("Trying to open LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
return luks2_keyslot_get_key(cd, jobj_keyslot,
|
||||
password, password_len,
|
||||
volume_key, volume_key_len);
|
||||
}
|
||||
|
||||
static int luks2_keyslot_store(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot;
|
||||
int r;
|
||||
|
||||
log_dbg("Calculating attributes for LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
r = luks2_keyslot_set_key(cd, jobj_keyslot,
|
||||
password, password_len,
|
||||
volume_key, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = LUKS2_hdr_write(cd, hdr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return keyslot;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_wipe(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remove any reference of deleted keyslot from digests and tokens */
|
||||
LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0);
|
||||
LUKS2_token_assign(cd, hdr, keyslot, CRYPT_ANY_TOKEN, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj1, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_area, "encryption", &jobj1);
|
||||
log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "type", &jobj1);
|
||||
log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_get_ex(jobj_kdf, "hash", &jobj1);
|
||||
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "iterations", &jobj1);
|
||||
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_uint64(jobj1));
|
||||
} else {
|
||||
json_object_object_get_ex(jobj_kdf, "time", &jobj1);
|
||||
log_std(cd, "\tTime: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "memory", &jobj1);
|
||||
log_std(cd, "\tMemory: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "cpus", &jobj1);
|
||||
log_std(cd, "\tThreads: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
}
|
||||
json_object_object_get_ex(jobj_kdf, "salt", &jobj1);
|
||||
log_std(cd, "\tSalt: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
|
||||
json_object_object_get_ex(jobj_af, "stripes", &jobj1);
|
||||
log_std(cd, "\tAF stripes: %u\n", json_object_get_int(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_area, "offset", &jobj1);
|
||||
log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_area, "size", &jobj1);
|
||||
log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int contains(json_object *jobj, const char *key, json_type type)
|
||||
{
|
||||
json_object *sobj;
|
||||
|
||||
if (!json_object_object_get_ex(jobj, key, &sobj) ||
|
||||
!json_object_is_type(sobj, type))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_validate(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area, *jobj1;
|
||||
char num[16];
|
||||
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (LUKS2_keyslot_validate(hdr->jobj, jobj_keyslot, num))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
|
||||
if (!contains(jobj_kdf, "hash", json_type_string) ||
|
||||
!contains(jobj_kdf, "iterations", json_type_int) ||
|
||||
!contains(jobj_kdf, "salt", json_type_string))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (!contains(jobj_kdf, "time", json_type_int) ||
|
||||
!contains(jobj_kdf, "memory", json_type_int) ||
|
||||
!contains(jobj_kdf, "cpus", json_type_int) ||
|
||||
!contains(jobj_kdf, "salt", json_type_string))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_af, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
if (!strcmp(json_object_get_string(jobj1), "luks1")) {
|
||||
if (!contains(jobj_af, "hash", json_type_string) ||
|
||||
!contains(jobj_af, "stripes", json_type_int))
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
// FIXME check numbered
|
||||
if (!json_object_object_get_ex(jobj_area, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
if (!strcmp(json_object_get_string(jobj1), "raw")) {
|
||||
if (!contains(jobj_area, "encryption", json_type_string) ||
|
||||
!contains(jobj_area, "key_size", json_type_int) ||
|
||||
!contains(jobj_area, "offset", json_type_string) ||
|
||||
!contains(jobj_area, "size", json_type_string))
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const keyslot_handler luks2_keyslot = {
|
||||
.name = "luks2",
|
||||
.alloc = luks2_keyslot_alloc,
|
||||
.open = luks2_keyslot_open,
|
||||
.store = luks2_keyslot_store,
|
||||
.wipe = luks2_keyslot_wipe,
|
||||
.dump = luks2_keyslot_dump,
|
||||
.validate = luks2_keyslot_validate,
|
||||
};
|
||||
794
lib/luks2/luks2_luks1_convert.c
Normal file
794
lib/luks2/luks2_luks1_convert.c
Normal file
@@ -0,0 +1,794 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Ondrej Kozina. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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"
|
||||
#include "../luks1/luks.h"
|
||||
#include "../luks1/af.h"
|
||||
|
||||
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], num[24];
|
||||
size_t base64_len;
|
||||
struct json_object *keyslot_obj, *field, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint64_t offset, area_size, offs_a, offs_b, length;
|
||||
|
||||
keyslot_obj = json_object_new_object();
|
||||
json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
|
||||
json_object_object_add(keyslot_obj, "key_size", json_object_new_int64(hdr_v1->keyBytes));
|
||||
|
||||
/* KDF */
|
||||
jobj_kdf = json_object_new_object();
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(CRYPT_KDF_PBKDF2));
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
|
||||
/* salt field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(keyslot_obj);
|
||||
json_object_put(jobj_kdf);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
json_object_object_add(jobj_kdf, "salt", field);
|
||||
json_object_object_add(keyslot_obj, "kdf", jobj_kdf);
|
||||
|
||||
/* AF */
|
||||
jobj_af = json_object_new_object();
|
||||
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
|
||||
/* stripes field ignored, fixed to LUKS_STRIPES (4000) */
|
||||
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
|
||||
json_object_object_add(keyslot_obj, "af", jobj_af);
|
||||
|
||||
/* Area */
|
||||
jobj_area = json_object_new_object();
|
||||
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
|
||||
|
||||
/* encryption algorithm field */
|
||||
if (*hdr_v1->cipherMode != '\0') {
|
||||
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
|
||||
} else
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(hdr_v1->cipherName));
|
||||
|
||||
/* area */
|
||||
if (LUKS_keyslot_area(hdr_v1, 0, &offs_a, &length) ||
|
||||
LUKS_keyslot_area(hdr_v1, 1, &offs_b, &length) ||
|
||||
LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
|
||||
json_object_put(keyslot_obj);
|
||||
json_object_put(jobj_area);
|
||||
return -EINVAL;
|
||||
}
|
||||
area_size = offs_b - offs_a;
|
||||
json_object_object_add(jobj_area, "key_size", json_object_new_int(hdr_v1->keyBytes));
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
json_object_object_add(jobj_area, "size", json_object_new_string(uint64_to_str(num, sizeof(num), &area_size)));
|
||||
json_object_object_add(keyslot_obj, "area", jobj_area);
|
||||
|
||||
*keyslot_object = keyslot_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_object **keyslots_object)
|
||||
{
|
||||
char keyslot_str[2];
|
||||
int key_slot, r;
|
||||
struct json_object *keyslot_obj, *field;
|
||||
|
||||
keyslot_obj = json_object_new_object();
|
||||
if (!keyslot_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
for (key_slot = 0; key_slot < LUKS_NUMKEYS; key_slot++) {
|
||||
if (hdr_v1->keyblock[key_slot].active != LUKS_KEY_ENABLED)
|
||||
continue;
|
||||
r = json_luks1_keyslot(hdr_v1, key_slot, &field);
|
||||
if (r) {
|
||||
json_object_put(keyslot_obj);
|
||||
return r;
|
||||
}
|
||||
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", key_slot);
|
||||
json_object_object_add(keyslot_obj, keyslot_str, field);
|
||||
}
|
||||
|
||||
*keyslots_object = keyslot_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_segment(const struct luks_phdr *hdr_v1, struct json_object **segment_object)
|
||||
{
|
||||
const char *c;
|
||||
char cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
|
||||
char num[24]; /* uint64_t in string */
|
||||
struct json_object *segment_obj, *field;
|
||||
uint64_t number;
|
||||
|
||||
segment_obj = json_object_new_object();
|
||||
if (!segment_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* type field */
|
||||
field = json_object_new_string("crypt");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "type", field);
|
||||
|
||||
/* offset field */
|
||||
number = hdr_v1->payloadOffset * SECTOR_SIZE;
|
||||
|
||||
field = json_object_new_string(uint64_to_str(num, sizeof(num), &number));
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "offset", field);
|
||||
|
||||
/* iv_tweak field */
|
||||
field = json_object_new_string("0");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "iv_tweak", field);
|
||||
|
||||
/* length field */
|
||||
field = json_object_new_string("dynamic");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "size", field);
|
||||
|
||||
/* cipher field */
|
||||
if (*hdr_v1->cipherMode != '\0') {
|
||||
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
|
||||
c = cipher;
|
||||
} else
|
||||
c = hdr_v1->cipherName;
|
||||
|
||||
field = json_object_new_string(c);
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "encryption", field);
|
||||
|
||||
/* block field */
|
||||
field = json_object_new_int(SECTOR_SIZE);
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "sector_size", field);
|
||||
|
||||
*segment_object = segment_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_segments(const struct luks_phdr *hdr_v1, struct json_object **segments_object)
|
||||
{
|
||||
char num[16];
|
||||
int r;
|
||||
struct json_object *segments_obj, *field;
|
||||
|
||||
segments_obj = json_object_new_object();
|
||||
if (!segments_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_luks1_segment(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(segments_obj);
|
||||
return r;
|
||||
}
|
||||
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
|
||||
json_object_object_add(segments_obj, num, field);
|
||||
|
||||
*segments_object = segments_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_digest(const struct luks_phdr *hdr_v1, struct json_object **digest_object)
|
||||
{
|
||||
char keyslot_str[2], *base64_str;
|
||||
int ks;
|
||||
size_t base64_len;
|
||||
struct json_object *digest_obj, *array, *field;
|
||||
|
||||
digest_obj = json_object_new_object();
|
||||
if (!digest_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* type field */
|
||||
field = json_object_new_string("pbkdf2");
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "type", field);
|
||||
|
||||
/* keyslots array */
|
||||
array = json_object_new_array();
|
||||
if (!array) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "keyslots", json_object_get(array));
|
||||
|
||||
for (ks = 0; ks < LUKS_NUMKEYS; ks++) {
|
||||
if (hdr_v1->keyblock[ks].active != LUKS_KEY_ENABLED)
|
||||
continue;
|
||||
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks);
|
||||
|
||||
field = json_object_new_string(keyslot_str);
|
||||
if (!field || json_object_array_add(array, field) < 0) {
|
||||
json_object_put(field);
|
||||
json_object_put(array);
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(array);
|
||||
|
||||
/* segments array */
|
||||
array = json_object_new_array();
|
||||
if (!array) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "segments", json_object_get(array));
|
||||
|
||||
field = json_object_new_string("0");
|
||||
if (!field || json_object_array_add(array, field) < 0) {
|
||||
json_object_put(field);
|
||||
json_object_put(array);
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
json_object_put(array);
|
||||
|
||||
/* hash field */
|
||||
field = json_object_new_string(hdr_v1->hashSpec);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "hash", field);
|
||||
|
||||
/* salt field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->mkDigestSalt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(digest_obj);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "salt", field);
|
||||
|
||||
/* digest field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->mkDigest, LUKS_DIGESTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(digest_obj);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "digest", field);
|
||||
|
||||
/* iterations field */
|
||||
field = json_object_new_int64(hdr_v1->mkDigestIterations);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "iterations", field);
|
||||
|
||||
*digest_object = digest_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_digests(const struct luks_phdr *hdr_v1, struct json_object **digests_object)
|
||||
{
|
||||
int r;
|
||||
struct json_object *digests_obj, *field;
|
||||
|
||||
digests_obj = json_object_new_object();
|
||||
if (!digests_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_luks1_digest(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(digests_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(digests_obj, "0", field);
|
||||
|
||||
*digests_object = digests_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_object(struct luks_phdr *hdr_v1, struct json_object **luks1_object, uint64_t keyslots_size)
|
||||
{
|
||||
char num[24];
|
||||
int r;
|
||||
struct json_object *luks1_obj, *field;
|
||||
uint64_t json_size;
|
||||
|
||||
luks1_obj = json_object_new_object();
|
||||
if (!luks1_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* keyslots field */
|
||||
r = json_luks1_keyslots(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "keyslots", field);
|
||||
|
||||
/* tokens field */
|
||||
field = json_object_new_object();
|
||||
if (!field) {
|
||||
json_object_put(luks1_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "tokens", field);
|
||||
|
||||
/* segments field */
|
||||
r = json_luks1_segments(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "segments", field);
|
||||
|
||||
/* digests field */
|
||||
r = json_luks1_digests(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "digests", field);
|
||||
|
||||
/* config field */
|
||||
/* anything else? */
|
||||
field = json_object_new_object();
|
||||
if (!field) {
|
||||
json_object_put(luks1_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "config", field);
|
||||
|
||||
json_size = LUKS2_HDR_16K_LEN - LUKS2_HDR_BIN_LEN;
|
||||
json_object_object_add(field, "json_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
|
||||
json_object_object_add(field, "keyslots_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
|
||||
|
||||
*luks1_object = luks1_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void move_keyslot_offset(json_object *jobj, int offset_add)
|
||||
{
|
||||
char num[24];
|
||||
json_object *jobj1, *jobj2, *jobj_area;
|
||||
uint64_t offset = 0;
|
||||
|
||||
json_object_object_get_ex(jobj, "keyslots", &jobj1);
|
||||
json_object_object_foreach(jobj1, key, val) {
|
||||
UNUSED(key);
|
||||
json_object_object_get_ex(val, "area", &jobj_area);
|
||||
json_object_object_get_ex(jobj_area, "offset", &jobj2);
|
||||
offset = json_object_get_uint64(jobj2) + offset_add;
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: return specific error code for partial write error (aka keyslots are gone) */
|
||||
static int move_keyslot_areas(struct crypt_device *cd, off_t offset_from,
|
||||
off_t offset_to, size_t buf_size)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
void *buf = NULL;
|
||||
int devfd = -1;
|
||||
|
||||
log_dbg("Moving keyslot areas of size %zu from %jd to %jd.",
|
||||
buf_size, (intmax_t)offset_from, (intmax_t)offset_to);
|
||||
|
||||
// FIXME: export aligned_malloc from utils
|
||||
if (posix_memalign(&buf, crypt_getpagesize(), buf_size))
|
||||
return -ENOMEM;
|
||||
|
||||
devfd = device_open(device, O_RDWR);
|
||||
if (devfd == -1) {
|
||||
log_dbg("Cannot open device %s.", device_path(device));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), buf, buf_size,
|
||||
offset_from)!= (ssize_t)buf_size) {
|
||||
close(devfd);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), buf, buf_size,
|
||||
offset_to) != (ssize_t)buf_size) {
|
||||
close(devfd);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(devfd);
|
||||
crypt_memzero(buf, buf_size);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks_header_in_use(struct crypt_device *cd)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = lookup_dm_dev_by_uuid(crypt_get_uuid(cd), crypt_get_type(cd));
|
||||
if (r < 0)
|
||||
log_err(cd, _("Can not check status of device with uuid: %s.\n"), crypt_get_uuid(cd));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Convert LUKS1 -> LUKS2 */
|
||||
int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct luks2_hdr *hdr2)
|
||||
{
|
||||
int r;
|
||||
json_object *jobj = NULL;
|
||||
size_t buf_size, buf_offset, luks1_size, luks1_shift = 2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS;
|
||||
uint64_t max_size = crypt_get_data_offset(cd) * SECTOR_SIZE;
|
||||
|
||||
/* for detached headers max size == device size */
|
||||
if (!max_size && (r = device_size(crypt_metadata_device(cd), &max_size)))
|
||||
return r;
|
||||
|
||||
luks1_size = LUKS_device_sectors(hdr1) << SECTOR_SHIFT;
|
||||
if (!luks1_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (LUKS_keyslots_offset(hdr1) != (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
|
||||
log_dbg("Unsupported keyslots material offset: %zu.", LUKS_keyslots_offset(hdr1));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_dbg("Max size: %" PRIu64 ", LUKS1 (full) header size %zu , required shift: %zu",
|
||||
max_size, luks1_size, luks1_shift);
|
||||
if ((max_size - luks1_size) < luks1_shift) {
|
||||
log_err(cd, _("Unable to move keyslot materials. Not enough space\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = json_luks1_object(hdr1, &jobj, max_size - 2 * LUKS2_HDR_16K_LEN);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
move_keyslot_offset(jobj, luks1_shift);
|
||||
|
||||
// fill hdr2
|
||||
memset(hdr2, 0, sizeof(*hdr2));
|
||||
hdr2->hdr_size = LUKS2_HDR_16K_LEN;
|
||||
hdr2->seqid = 1;
|
||||
hdr2->version = 2;
|
||||
strncpy(hdr2->checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
|
||||
crypt_random_get(cd, (char*)hdr2->salt1, sizeof(hdr2->salt1), CRYPT_RND_SALT);
|
||||
crypt_random_get(cd, (char*)hdr2->salt2, sizeof(hdr2->salt2), CRYPT_RND_SALT);
|
||||
strncpy(hdr2->uuid, crypt_get_uuid(cd), LUKS2_UUID_L);
|
||||
hdr2->jobj = jobj;
|
||||
|
||||
/*
|
||||
* It duplicates check in LUKS2_hdr_write() but we don't want to move
|
||||
* keyslot areas in case it would fail later
|
||||
*/
|
||||
if (max_size < LUKS2_hdr_and_areas_size(hdr2->jobj)) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = luks_header_in_use(cd))) {
|
||||
if (r > 0)
|
||||
r = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// move keyslots 4k -> 32k offset
|
||||
buf_offset = 2 * LUKS2_HDR_16K_LEN;
|
||||
buf_size = luks1_size - LUKS_ALIGN_KEYSLOTS;
|
||||
if ((r = move_keyslot_areas(cd, 8 * SECTOR_SIZE, buf_offset, buf_size)) < 0)
|
||||
goto out;
|
||||
|
||||
// Write JSON hdr2
|
||||
r = LUKS2_hdr_write(cd, hdr2);
|
||||
out:
|
||||
LUKS2_hdr_free(hdr2);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int keyslot_LUKS1_compatible(struct luks2_hdr *hdr, int keyslot, uint32_t key_size)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj, *jobj_kdf, *jobj_af;
|
||||
uint64_t l2_offset, l2_length;
|
||||
int ks_key_size;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return 1;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj) ||
|
||||
strcmp(json_object_get_string(jobj), "luks2"))
|
||||
return 0;
|
||||
|
||||
/* Using PBKDF2, this implies memory and parallel is not used. */
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_kdf, "type", &jobj) ||
|
||||
strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2))
|
||||
return 0;
|
||||
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_af, "stripes", &jobj) ||
|
||||
json_object_get_int(jobj) != LUKS_STRIPES)
|
||||
return 0;
|
||||
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_af, "hash", &jobj) ||
|
||||
crypt_hash_size(json_object_get_string(jobj)) < 0)
|
||||
return 0;
|
||||
|
||||
/* FIXME: should this go to validation code instead (aka invalid luks2 header if assigned to segment 0)? */
|
||||
ks_key_size = LUKS2_get_keyslot_key_size(hdr, keyslot);
|
||||
if (ks_key_size < 0 || (int)key_size != LUKS2_get_keyslot_key_size(hdr, keyslot)) {
|
||||
log_dbg("Key length in keyslot %d is different from volume key length", keyslot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LUKS2_keyslot_area(hdr, keyslot, &l2_offset, &l2_length))
|
||||
return 0;
|
||||
|
||||
if (l2_length != (size_round_up(AF_split_sectors(key_size, LUKS_STRIPES) * SECTOR_SIZE, 4096))) {
|
||||
log_dbg("Area length in LUKS2 keyslot (%d) is not compatible with LUKS1", keyslot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert LUKS2 -> LUKS1 */
|
||||
int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct luks_phdr *hdr1)
|
||||
{
|
||||
size_t buf_size, buf_offset;
|
||||
char cipher[LUKS_CIPHERNAME_L], cipher_mode[LUKS_CIPHERMODE_L];
|
||||
char digest[LUKS_DIGESTSIZE], digest_salt[LUKS_SALTSIZE];
|
||||
size_t len;
|
||||
json_object *jobj_keyslot, *jobj_digest, *jobj_segment, *jobj_kdf, *jobj_area, *jobj1, *jobj2;
|
||||
uint32_t key_size;
|
||||
int i, r, last_active = 0;
|
||||
uint64_t offset, area_length;
|
||||
char buf[256], luksMagic[] = LUKS_MAGIC;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr2, 0);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
jobj_segment = LUKS2_get_segment_jobj(hdr2, CRYPT_DEFAULT_SEGMENT);
|
||||
if (!jobj_segment)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(hdr2->jobj, "digests", &jobj1);
|
||||
if (!json_object_object_get_ex(jobj_digest, "type", &jobj2) ||
|
||||
strcmp(json_object_get_string(jobj2), "pbkdf2") ||
|
||||
json_object_object_length(jobj1) != 1) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - key slot digests are not LUKS1 compatible.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key_size = r = LUKS2_get_volume_key_size(hdr2, 0);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE)
|
||||
continue;
|
||||
|
||||
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INVALID) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is in invalid state.\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i >= LUKS_NUMKEYS) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - slot %u (over maximum slots) is still active .\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!keyslot_LUKS1_compatible(hdr2, i, key_size)) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is not LUKS1 compatible.\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(hdr1, 0, sizeof(*hdr1));
|
||||
|
||||
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
||||
hdr1->keyblock[i].active = LUKS_KEY_DISABLED;
|
||||
hdr1->keyblock[i].stripes = LUKS_STRIPES;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, i);
|
||||
|
||||
if (jobj_keyslot) {
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj1))
|
||||
return -EINVAL;
|
||||
offset = json_object_get_uint64(jobj1);
|
||||
} else {
|
||||
if (LUKS2_find_area_gap(cd, hdr2, key_size, &offset, &area_length))
|
||||
return -EINVAL;
|
||||
/* FIXME: luks2 reload is required! */
|
||||
if (luks2_keyslot_alloc(cd, i, key_size))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset /= SECTOR_SIZE;
|
||||
if (offset > UINT32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
hdr1->keyblock[i].keyMaterialOffset = offset;
|
||||
hdr1->keyblock[i].keyMaterialOffset -=
|
||||
((2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS) / SECTOR_SIZE);
|
||||
|
||||
if (!jobj_keyslot)
|
||||
continue;
|
||||
|
||||
hdr1->keyblock[i].active = LUKS_KEY_ENABLED;
|
||||
last_active = i;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
|
||||
continue;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj1))
|
||||
continue;
|
||||
hdr1->keyblock[i].passwordIterations = json_object_get_uint32(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj1))
|
||||
continue;
|
||||
len = sizeof(buf);
|
||||
memset(buf, 0, len);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), buf, &len))
|
||||
continue;
|
||||
if (len > 0 && len != LUKS_SALTSIZE)
|
||||
continue;
|
||||
memcpy(hdr1->keyblock[i].passwordSalt, buf, LUKS_SALTSIZE);
|
||||
}
|
||||
|
||||
if (!jobj_keyslot) {
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, last_active);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj1), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strncpy(hdr1->cipherName, cipher, sizeof(hdr1->cipherName));
|
||||
strncpy(hdr1->cipherMode, cipher_mode, sizeof(hdr1->cipherMode));
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj1))
|
||||
return -EINVAL;
|
||||
strncpy(hdr1->hashSpec, json_object_get_string(jobj1), sizeof(hdr1->hashSpec));
|
||||
|
||||
hdr1->keyBytes = key_size;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
|
||||
return -EINVAL;
|
||||
hdr1->mkDigestIterations = json_object_get_uint32(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(digest);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), digest, &len))
|
||||
return -EINVAL;
|
||||
/* We can store full digest here, not only sha1 length */
|
||||
if (len < LUKS_DIGESTSIZE)
|
||||
return -EINVAL;
|
||||
memcpy(hdr1->mkDigest, digest, LUKS_DIGESTSIZE);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(digest_salt);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), digest_salt, &len))
|
||||
return -EINVAL;
|
||||
if (len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
memcpy(hdr1->mkDigestSalt, digest_salt, LUKS_SALTSIZE);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_segment, "offset", &jobj1))
|
||||
return -EINVAL;
|
||||
offset = json_object_get_uint64(jobj1) / SECTOR_SIZE;
|
||||
if (offset > UINT32_MAX)
|
||||
return -EINVAL;
|
||||
/* FIXME: LUKS1 requires offset == 0 || offset >= luks1_hdr_size */
|
||||
hdr1->payloadOffset = offset;
|
||||
|
||||
strncpy(hdr1->uuid, hdr2->uuid, UUID_STRING_L);
|
||||
|
||||
memcpy(hdr1->magic, luksMagic, LUKS_MAGIC_L);
|
||||
|
||||
hdr1->version = 1;
|
||||
|
||||
r = luks_header_in_use(cd);
|
||||
if (r)
|
||||
return r > 0 ? -EBUSY : r;
|
||||
|
||||
// move keyslots 32k -> 4k offset
|
||||
buf_offset = 2 * LUKS2_HDR_16K_LEN;
|
||||
buf_size = LUKS2_keyslots_size(hdr2->jobj);
|
||||
r = move_keyslot_areas(cd, buf_offset, 8 * SECTOR_SIZE, buf_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO, 0,
|
||||
8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
|
||||
|
||||
// Write LUKS1 hdr
|
||||
return LUKS_write_phdr(hdr1, cd);
|
||||
}
|
||||
560
lib/luks2/luks2_token.c
Normal file
560
lib/luks2/luks2_token.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, token handling
|
||||
*
|
||||
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2016-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/* Builtin tokens */
|
||||
extern const crypt_token_handler keyring_handler;
|
||||
|
||||
static token_handler token_handlers[LUKS2_TOKENS_MAX] = {
|
||||
/* keyring builtin token */
|
||||
{
|
||||
.get = token_keyring_get,
|
||||
.set = token_keyring_set,
|
||||
.h = &keyring_handler
|
||||
},
|
||||
};
|
||||
|
||||
static int is_builtin_candidate(const char *type)
|
||||
{
|
||||
return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN);
|
||||
}
|
||||
|
||||
int crypt_token_register(const crypt_token_handler *handler)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (is_builtin_candidate(handler->name)) {
|
||||
log_dbg("'" LUKS2_BUILTIN_TOKEN_PREFIX "' is reserved prefix for builtin tokens.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++) {
|
||||
if (!strcmp(token_handlers[i].h->name, handler->name)) {
|
||||
log_dbg("Keyslot handler %s is already registered.", handler->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == LUKS2_TOKENS_MAX) {
|
||||
log_dbg("No more space for another token handler.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
token_handlers[i].h = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const token_handler
|
||||
*LUKS2_token_handler_type_internal(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++)
|
||||
if (!strcmp(token_handlers[i].h->name, type))
|
||||
return token_handlers + i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const crypt_token_handler
|
||||
*LUKS2_token_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
return th ? th->h : NULL;
|
||||
}
|
||||
|
||||
static const token_handler
|
||||
*LUKS2_token_handler_internal(struct crypt_device *cd, int token)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (token < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_token_jobj(hdr, token)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_token_handler_type_internal(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static const crypt_token_handler
|
||||
*LUKS2_token_handler(struct crypt_device *cd, int token)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_internal(cd, token);
|
||||
|
||||
return th ? th->h : NULL;
|
||||
}
|
||||
|
||||
static int LUKS2_token_find_free(struct luks2_hdr *hdr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX; i++)
|
||||
if (!LUKS2_get_token_jobj(hdr, i))
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int LUKS2_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *json,
|
||||
int commit)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_tokens, *jobj_type, *jobj;
|
||||
enum json_tokener_error jerr;
|
||||
char num[16];
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
if (!json)
|
||||
return -EINVAL;
|
||||
token = LUKS2_token_find_free(hdr);
|
||||
}
|
||||
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remove token */
|
||||
if (!json) {
|
||||
snprintf(num, sizeof(num), "%d", token);
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
} else {
|
||||
|
||||
jobj = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj) {
|
||||
log_dbg("Token JSON parse failed.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(num, sizeof(num), "%d", token);
|
||||
|
||||
if (LUKS2_token_validate(hdr->jobj, jobj, num)) {
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
json_object_object_get_ex(jobj, "type", &jobj_type);
|
||||
if (is_builtin_candidate(json_object_get_string(jobj_type))) {
|
||||
log_dbg("%s is builtin token candidate", json_object_get_string(jobj_type));
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
h = LUKS2_token_handler_type(cd, json_object_get_string(jobj_type));
|
||||
if (h && h->validate && h->validate(cd, json)) {
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_tokens, num, jobj);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new token.");
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **type)
|
||||
{
|
||||
const char *tmp;
|
||||
const token_handler *th;
|
||||
json_object *jobj_type, *jobj_token;
|
||||
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return CRYPT_TOKEN_INVALID;
|
||||
|
||||
if (!(jobj_token = LUKS2_get_token_jobj(hdr, token)))
|
||||
return CRYPT_TOKEN_INACTIVE;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "type", &jobj_type);
|
||||
tmp = json_object_get_string(jobj_type);
|
||||
|
||||
if ((th = LUKS2_token_handler_type_internal(cd, tmp))) {
|
||||
if (type)
|
||||
*type = th->h->name;
|
||||
return th->set ? CRYPT_TOKEN_INTERNAL : CRYPT_TOKEN_EXTERNAL;
|
||||
}
|
||||
|
||||
if (type)
|
||||
*type = tmp;
|
||||
|
||||
return is_builtin_candidate(tmp) ? CRYPT_TOKEN_INTERNAL_UNKNOWN : CRYPT_TOKEN_EXTERNAL_UNKNOWN;
|
||||
}
|
||||
|
||||
int LUKS2_builtin_token_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
void *params)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
// internal error
|
||||
assert(th && th->get);
|
||||
|
||||
return th->get(LUKS2_get_token_jobj(hdr, token), params) ?: token;
|
||||
}
|
||||
|
||||
int LUKS2_builtin_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
const void *params,
|
||||
int commit)
|
||||
{
|
||||
const token_handler *th;
|
||||
char num[16];
|
||||
int r;
|
||||
json_object *jobj_token, *jobj_tokens;
|
||||
|
||||
th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
// at this point all builtin handlers must exist and have validate fn defined
|
||||
assert(th && th->set && th->h->validate);
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
if ((token = LUKS2_token_find_free(hdr)) < 0)
|
||||
log_err(cd, _("No free token slot\n"));
|
||||
}
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return -EINVAL;
|
||||
snprintf(num, sizeof(num), "%u", token);
|
||||
|
||||
r = th->set(&jobj_token, params);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to create builtin token %s\n"), type);
|
||||
return r;
|
||||
}
|
||||
|
||||
// builtin tokens must produce valid json
|
||||
r = LUKS2_token_validate(hdr->jobj, jobj_token, "new");
|
||||
assert(!r);
|
||||
r = th->h->validate(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
|
||||
assert(!r);
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
|
||||
json_object_object_add(jobj_tokens, num, jobj_token);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new %s token.", type);
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static int LUKS2_token_open(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
char **buffer,
|
||||
size_t *buffer_len,
|
||||
void *usrptr)
|
||||
{
|
||||
const char *json;
|
||||
const crypt_token_handler *h;
|
||||
int r;
|
||||
|
||||
if (!(h = LUKS2_token_handler(cd, token)))
|
||||
return -ENOENT;
|
||||
|
||||
if (h->validate) {
|
||||
if (LUKS2_token_json_get(cd, hdr, token, &json))
|
||||
return -EINVAL;
|
||||
|
||||
if (h->validate(cd, json)) {
|
||||
log_dbg("Token %d (%s) validation failed.", token, h->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
r = h->open(cd, token, buffer, buffer_len, usrptr);
|
||||
if (r < 0)
|
||||
log_dbg("Token %d (%s) open failed with %d.", token, h->name, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void LUKS2_token_buffer_free(struct crypt_device *cd,
|
||||
int token,
|
||||
void *buffer,
|
||||
size_t buffer_len)
|
||||
{
|
||||
const crypt_token_handler *h = LUKS2_token_handler(cd, token);
|
||||
|
||||
if (h->buffer_free)
|
||||
h->buffer_free(buffer, buffer_len);
|
||||
else {
|
||||
crypt_memzero(buffer, buffer_len);
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
int segment,
|
||||
const char *buffer,
|
||||
size_t buffer_len,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_token, *jobj_token_keyslots, *jobj;
|
||||
const char *num;
|
||||
int i, r;
|
||||
|
||||
if (!(h = LUKS2_token_handler(cd, token)))
|
||||
return -ENOENT;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
|
||||
if (!jobj_token_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
/* Try to open keyslot referenced in token */
|
||||
r = -EINVAL;
|
||||
for (i = 0; i < json_object_array_length(jobj_token_keyslots) && r < 0; i++) {
|
||||
jobj = json_object_array_get_idx(jobj_token_keyslots, i);
|
||||
num = json_object_get_string(jobj);
|
||||
log_dbg("Trying to open keyslot %s with token %d (type %s).", num, token, h->name);
|
||||
r = LUKS2_keyslot_open(cd, atoi(num), segment, buffer, buffer_len, vk);
|
||||
}
|
||||
|
||||
return r < 0 ? r : atoi(num);
|
||||
}
|
||||
|
||||
int LUKS2_token_open_and_activate(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *name,
|
||||
uint32_t flags,
|
||||
void *usrptr)
|
||||
{
|
||||
int keyslot, r;
|
||||
char *buffer;
|
||||
size_t buffer_len;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, usrptr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
|
||||
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
|
||||
buffer, buffer_len, &vk);
|
||||
|
||||
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
keyslot = r;
|
||||
|
||||
if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
|
||||
r = crypt_volume_key_load_in_keyring(cd, vk);
|
||||
|
||||
if (r >= 0 && name)
|
||||
r = LUKS2_activate(cd, name, vk, flags);
|
||||
|
||||
crypt_free_volume_key(vk);
|
||||
|
||||
return r < 0 ? r : keyslot;
|
||||
}
|
||||
|
||||
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *name,
|
||||
uint32_t flags)
|
||||
{
|
||||
char *buffer;
|
||||
json_object *tokens_jobj;
|
||||
size_t buffer_len;
|
||||
int keyslot, token, r = -EINVAL;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &tokens_jobj);
|
||||
|
||||
json_object_object_foreach(tokens_jobj, slot, val) {
|
||||
UNUSED(val);
|
||||
token = atoi(slot);
|
||||
|
||||
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, NULL);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
|
||||
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
|
||||
buffer, buffer_len, &vk);
|
||||
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
|
||||
if (r >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
keyslot = r;
|
||||
|
||||
if (r >= 0 && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
|
||||
r = crypt_volume_key_load_in_keyring(cd, vk);
|
||||
|
||||
if (r >= 0 && name)
|
||||
r = LUKS2_activate(cd, name, vk, flags);
|
||||
|
||||
crypt_free_volume_key(vk);
|
||||
|
||||
return r < 0 ? r : keyslot;
|
||||
}
|
||||
|
||||
void LUKS2_token_dump(struct crypt_device *cd, int token)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_token;
|
||||
|
||||
h = LUKS2_token_handler(cd, token);
|
||||
if (h && h->dump) {
|
||||
jobj_token = LUKS2_get_token_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), token);
|
||||
if (jobj_token)
|
||||
h->dump(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
|
||||
}
|
||||
}
|
||||
|
||||
int LUKS2_token_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int token, const char **json)
|
||||
{
|
||||
json_object *jobj_token;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
*json = json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_keyslot(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int token, int keyslot, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_token, *jobj_token_keyslots;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Keyslot %i %s token %i.", keyslot, assign ? "assigned to" : "unassigned from", token);
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
|
||||
if (!jobj_token_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_token_keyslots, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_token_keyslots, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_token_keyslots, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_token, "keyslots", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_token(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int token, int assign)
|
||||
{
|
||||
json_object *jobj_keyslots;
|
||||
int r = 0;
|
||||
|
||||
if (!LUKS2_get_token_jobj(hdr, token))
|
||||
return -EINVAL;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT) {
|
||||
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
|
||||
|
||||
json_object_object_foreach(jobj_keyslots, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_keyslot(cd, hdr, token, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_keyslot(cd, hdr, token, keyslot, assign);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int token, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_tokens;
|
||||
int r = 0;
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
|
||||
|
||||
json_object_object_foreach(jobj_tokens, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_token(cd, hdr, keyslot, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_token(cd, hdr, keyslot, token, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
159
lib/luks2/luks2_token_keyring.c
Normal file
159
lib/luks2/luks2_token_keyring.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, kernel keyring token
|
||||
*
|
||||
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2016-2017, Ondrej Kozina. All rights reserved.
|
||||
*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
static int keyring_open(struct crypt_device *cd,
|
||||
int token,
|
||||
char **buffer,
|
||||
size_t *buffer_len,
|
||||
void *usrptr __attribute__((unused)))
|
||||
{
|
||||
json_object *jobj_token, *jobj_key;
|
||||
struct luks2_hdr *hdr;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "key_description", &jobj_key);
|
||||
|
||||
/* TODO: if r == -ENOKEY then instantiate the key? */
|
||||
if (keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keyring_validate(struct crypt_device *cd __attribute__((unused)),
|
||||
const char *json)
|
||||
{
|
||||
enum json_tokener_error jerr;
|
||||
json_object *jobj_token, *jobj_key;
|
||||
int r = 1;
|
||||
|
||||
log_dbg("Validating keyring token json");
|
||||
|
||||
jobj_token = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj_token) {
|
||||
log_dbg("Keyring token JSON parse failed.");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
|
||||
log_dbg("missing key_description field.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!json_object_is_type(jobj_key, json_type_string)) {
|
||||
log_dbg("key_description is not a string.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: perhaps check that key description is in '%s:%s'
|
||||
* format where both strings are not empty */
|
||||
r = !strlen(json_object_get_string(jobj_key));
|
||||
out:
|
||||
json_object_put(jobj_token);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void keyring_dump(struct crypt_device *cd, const char *json)
|
||||
{
|
||||
enum json_tokener_error jerr;
|
||||
json_object *jobj_token, *jobj_key;
|
||||
|
||||
jobj_token = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj_token)
|
||||
return;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
|
||||
json_object_put(jobj_token);
|
||||
return;
|
||||
}
|
||||
|
||||
log_std(cd, "\tKey description: %s\n", json_object_get_string(jobj_key));
|
||||
|
||||
json_object_put(jobj_token);
|
||||
}
|
||||
|
||||
int token_keyring_set(json_object **jobj_builtin_token,
|
||||
const void *params)
|
||||
{
|
||||
json_object *jobj_token, *jobj;
|
||||
const struct crypt_token_params_luks2_keyring *keyring_params = (const struct crypt_token_params_luks2_keyring *) params;
|
||||
|
||||
jobj_token = json_object_new_object();
|
||||
if (!jobj_token)
|
||||
return -ENOMEM;
|
||||
|
||||
jobj = json_object_new_string(LUKS2_TOKEN_KEYRING);
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "type", jobj);
|
||||
|
||||
jobj = json_object_new_array();
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "keyslots", jobj);
|
||||
|
||||
jobj = json_object_new_string(keyring_params->key_description);
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "key_description", jobj);
|
||||
|
||||
*jobj_builtin_token = jobj_token;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int token_keyring_get(json_object *jobj_token,
|
||||
void *params)
|
||||
{
|
||||
json_object *jobj;
|
||||
struct crypt_token_params_luks2_keyring *keyring_params = (struct crypt_token_params_luks2_keyring *) params;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "type", &jobj);
|
||||
assert(!strcmp(json_object_get_string(jobj), LUKS2_TOKEN_KEYRING));
|
||||
|
||||
json_object_object_get_ex(jobj_token, "key_description", &jobj);
|
||||
|
||||
keyring_params->key_description = json_object_get_string(jobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const crypt_token_handler keyring_handler = {
|
||||
.name = LUKS2_TOKEN_KEYRING,
|
||||
.open = keyring_open,
|
||||
.validate = keyring_validate,
|
||||
.dump = keyring_dump
|
||||
};
|
||||
Reference in New Issue
Block a user