Add integritysetup command line tool for the dm-integrity standalone setting.

The dm-integrity target is intended to be used for authenticated
encryption through LUKS and dm-crypt.

It can be used in standalone as well; for this use case there
is a simple configuration utility called integritysetup
(similar to veritysetup to dm-verity).
This commit is contained in:
Milan Broz
2017-04-30 13:41:42 +02:00
parent 290b593d0b
commit 0bb7098fd8
19 changed files with 1838 additions and 22 deletions

View File

@@ -268,6 +268,11 @@ AC_ARG_ENABLE([cryptsetup-reencrypt],
[enable cryptsetup-reencrypt tool]))
AM_CONDITIONAL(REENCRYPT, test x$enable_cryptsetup_reencrypt = xyes)
AC_ARG_ENABLE(integritysetup,
AS_HELP_STRING([--disable-integritysetup],
[disable integritysetup support]),[], [enable_integritysetup=yes])
AM_CONDITIONAL(INTEGRITYSETUP, test x$enable_integritysetup = xyes)
AC_ARG_ENABLE(selinux,
AS_HELP_STRING([--disable-selinux],
[disable selinux support [default=auto]]),[], [])
@@ -468,6 +473,7 @@ lib/luks1/Makefile
lib/loopaes/Makefile
lib/verity/Makefile
lib/tcrypt/Makefile
lib/integrity/Makefile
src/Makefile
po/Makefile.in
man/Makefile

View File

@@ -1,4 +1,4 @@
SUBDIRS = crypto_backend luks1 loopaes verity tcrypt
SUBDIRS = crypto_backend luks1 loopaes verity tcrypt integrity
moduledir = $(libdir)/cryptsetup
@@ -12,6 +12,7 @@ AM_CPPFLAGS = -include config.h \
-I$(top_srcdir)/lib/loopaes \
-I$(top_srcdir)/lib/verity \
-I$(top_srcdir)/lib/tcrypt \
-I$(top_srcdir)/lib/integrity \
-DDATADIR=\""$(datadir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DPREFIX=\""$(prefix)"\" \
@@ -25,7 +26,8 @@ common_ldadd = \
luks1/libluks1.la \
loopaes/libloopaes.la \
verity/libverity.la \
tcrypt/libtcrypt.la
tcrypt/libtcrypt.la \
integrity/libintegrity.la
libcryptsetup_la_DEPENDENCIES = $(common_ldadd) libcryptsetup.sym

13
lib/integrity/Makefile.am Normal file
View File

@@ -0,0 +1,13 @@
moduledir = $(libdir)/cryptsetup
noinst_LTLIBRARIES = libintegrity.la
libintegrity_la_CFLAGS = -Wall $(AM_CFLAGS) @CRYPTO_CFLAGS@
libintegrity_la_SOURCES = \
integrity.c \
integrity.h
AM_CPPFLAGS = -include config.h \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/lib/crypto_backend

260
lib/integrity/integrity.c Normal file
View File

@@ -0,0 +1,260 @@
/*
* Integrity volume handling
*
* Copyright (C) 2016-2017, Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include "integrity.h"
#include "internal.h"
static int INTEGRITY_read_superblock(struct crypt_device *cd,
struct device *device,
uint64_t offset, struct superblock *sb)
{
int devfd, r;
devfd = device_open(device, O_RDONLY);
if(devfd < 0) {
return -EINVAL;
}
if (read_lseek_blockwise(devfd, device_block_size(device),
sb, sizeof(*sb), offset) != sizeof(*sb) ||
memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
sb->version != SB_VERSION) {
log_std(cd, "No integrity superblock detected on %s.\n",
device_path(device));
r = -EINVAL;
} else {
sb->integrity_tag_size = le16toh(sb->integrity_tag_size);
sb->journal_sections = le32toh(sb->journal_sections);
sb->provided_data_sectors = le64toh(sb->provided_data_sectors);
r = 0;
}
close(devfd);
return r;
}
int INTEGRITY_read_sb(struct crypt_device *cd, struct crypt_params_integrity *params)
{
struct superblock sb;
int r;
r = INTEGRITY_read_superblock(cd, crypt_data_device(cd), 0, &sb);
if (r)
return r;
params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block;
params->tag_size = sb.integrity_tag_size;
return 0;
}
int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset)
{
struct superblock sb;
int r;
r = INTEGRITY_read_superblock(cd, device, offset, &sb);
if (r)
return r;
log_std(cd, "Info for integrity device %s.\n", device_path(device));
log_std(cd, "log2_interleave_sectors %d\n", sb.log2_interleave_sectors);
log_std(cd, "integrity_tag_size %u\n", sb.integrity_tag_size);
log_std(cd, "journal_sections %u\n", sb.journal_sections);
log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors);
log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block);
return 0;
}
int INTEGRITY_data_sectors(struct crypt_device *cd,
struct device *device, uint64_t offset,
uint64_t *data_sectors)
{
struct superblock sb;
int r;
r = INTEGRITY_read_superblock(cd, device, offset, &sb);
if (r)
return r;
*data_sectors = sb.provided_data_sectors;
return 0;
}
int INTEGRITY_key_size(struct crypt_device *cd)
{
const char *integrity = crypt_get_integrity(cd);
if (!integrity)
return 0;
//FIXME: use crypto backend hash size
if (!strcmp(integrity, "aead"))
return 0;
else if (!strcmp(integrity, "hmac(sha256)"))
return 32;
else if (!strcmp(integrity, "hmac(sha512)"))
return 64;
else if (!strcmp(integrity, "poly1305"))
return 0;
else if (!strcmp(integrity, "none"))
return 0;
return -EINVAL;
}
int INTEGRITY_tag_size(struct crypt_device *cd)
{
const char *integrity = crypt_get_integrity(cd);
const char *cipher_mode = crypt_get_cipher_mode(cd);
int iv_tag_size = 0, auth_tag_size = 0;
if (!strcmp(cipher_mode, "xts-random"))
iv_tag_size = 16;
else if (!strcmp(cipher_mode, "gcm-random"))
iv_tag_size = 12;
else if (!strcmp(cipher_mode, "ccm-random"))
iv_tag_size = 8;
else if (!strcmp(cipher_mode, "ctr-random"))
iv_tag_size = 16;
else if (!strcmp(cipher_mode, "random"))
iv_tag_size = 16;
//FIXME: use crypto backend hash size
if (!integrity || !strcmp(integrity, "none"))
auth_tag_size = 0;
else if (!strcmp(integrity, "aead"))
auth_tag_size = 16; //FIXME gcm- mode only
else if (!strcmp(integrity, "cmac(aes)"))
auth_tag_size = 16;
else if (!strcmp(integrity, "hmac(sha256)"))
auth_tag_size = 32;
else if (!strcmp(integrity, "hmac(sha512)"))
auth_tag_size = 64;
else if (!strcmp(integrity, "poly1305")) {
if (iv_tag_size)
iv_tag_size = 12;
auth_tag_size = 16;
}
return iv_tag_size + auth_tag_size;
}
int INTEGRITY_activate(struct crypt_device *cd,
const char *name,
struct crypt_params_integrity *params,
struct volume_key *vk,
struct volume_key *journal_crypt_key,
struct volume_key *journal_mac_key,
uint32_t flags)
{
struct crypt_dm_active_device dmdi = {
.target = DM_INTEGRITY,
.data_device = crypt_data_device(cd),
.size = crypt_get_integrity_sectors(cd),
.flags = flags,
.u.integrity = {
.offset = crypt_get_data_offset(cd),
.tag_size = crypt_get_integrity_tag_size(cd),
.sector_size = crypt_get_sector_size(cd),
}
};
int r;
dmdi.u.integrity.journal_size = params->journal_size;
dmdi.u.integrity.journal_watermark = params->journal_watermark;
dmdi.u.integrity.journal_commit_time = params->journal_commit_time;
dmdi.u.integrity.interleave_sectors = params->interleave_sectors;
dmdi.u.integrity.buffer_sectors = params->buffer_sectors;
dmdi.u.integrity.integrity = params->integrity;
dmdi.u.integrity.vk = vk;
dmdi.u.integrity.journal_integrity = params->journal_integrity;
dmdi.u.integrity.journal_integrity_key = journal_mac_key;
dmdi.u.integrity.journal_crypt = params->journal_crypt;
dmdi.u.integrity.journal_crypt_key = journal_crypt_key;
log_dbg("Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".",
device_path(dmdi.data_device), name, dmdi.u.integrity.tag_size, dmdi.size);
r = device_block_adjust(cd, dmdi.data_device, DEV_EXCL,
dmdi.u.integrity.offset, NULL, &dmdi.flags);
if (r)
return r;
return dm_create_device(cd, name, "INTEGRITY", &dmdi, 0);
}
int INTEGRITY_format(struct crypt_device *cd,
struct crypt_params_integrity *params,
struct volume_key *journal_crypt_key,
struct volume_key *journal_mac_key)
{
char tmp_name[64], tmp_uuid[40];
struct crypt_dm_active_device dmdi = {
.target = DM_INTEGRITY,
.data_device = crypt_data_device(cd),
.size = 8,
.flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */
.u.integrity = {
.offset = crypt_get_data_offset(cd),
.tag_size = crypt_get_integrity_tag_size(cd),
.sector_size = crypt_get_sector_size(cd),
}
};
int r;
uuid_t tmp_uuid_bin;
dmdi.u.integrity.journal_size = params->journal_size;
dmdi.u.integrity.journal_watermark = params->journal_watermark;
dmdi.u.integrity.journal_commit_time = params->journal_commit_time;
dmdi.u.integrity.interleave_sectors = params->interleave_sectors;
dmdi.u.integrity.buffer_sectors = params->buffer_sectors;
dmdi.u.integrity.integrity = params->integrity;
dmdi.u.integrity.journal_integrity = params->journal_integrity;
dmdi.u.integrity.journal_integrity_key = journal_mac_key;
dmdi.u.integrity.journal_crypt = params->journal_crypt;
dmdi.u.integrity.journal_crypt_key = journal_crypt_key;
uuid_generate(tmp_uuid_bin);
uuid_unparse(tmp_uuid_bin, tmp_uuid);
snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid);
log_dbg("Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.",
device_path(dmdi.data_device), tmp_name, dmdi.u.integrity.tag_size);
r = device_block_adjust(cd, dmdi.data_device, DEV_EXCL, dmdi.u.integrity.offset, NULL, NULL);
if (r)
return r;
r = dm_create_device(cd, tmp_name, "INTEGRITY", &dmdi, 0);
if (r)
return r;
return dm_remove_device(cd, tmp_name, 1, dmdi.size);
}

68
lib/integrity/integrity.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Integrity header defitinion
*
* Copyright (C) 2016-2017, Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _CRYPTSETUP_INTEGRITY_H
#define _CRYPTSETUP_INTEGRITY_H
#include <stdint.h>
struct crypt_device;
struct device;
struct crypt_params_integrity;
struct volume_key;
/* dm-integrity helper */
#define SB_MAGIC "integrt"
#define SB_VERSION 1
struct superblock {
uint8_t magic[8];
uint8_t version;
int8_t log2_interleave_sectors;
uint16_t integrity_tag_size;
uint32_t journal_sections;
uint64_t provided_data_sectors;
uint32_t flags;
uint8_t log2_sectors_per_block;
} __attribute__ ((packed));
int INTEGRITY_read_sb(struct crypt_device *cd, struct crypt_params_integrity *params);
int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset);
int INTEGRITY_data_sectors(struct crypt_device *cd,
struct device *device, uint64_t offset,
uint64_t *data_sectors);
int INTEGRITY_key_size(struct crypt_device *cd);
int INTEGRITY_tag_size(struct crypt_device *cd);
int INTEGRITY_format(struct crypt_device *cd,
struct crypt_params_integrity *params,
struct volume_key *journal_crypt_key,
struct volume_key *journal_mac_key);
int INTEGRITY_activate(struct crypt_device *cd,
const char *name,
struct crypt_params_integrity *params,
struct volume_key *vk,
struct volume_key *journal_crypt_key,
struct volume_key *journal_mac_key,
uint32_t flags);
#endif

View File

@@ -243,6 +243,8 @@ int crypt_memory_lock(struct crypt_device *cd, int lock);
#define CRYPT_VERITY "VERITY"
/** TCRYPT (TrueCrypt-compatible and VeraCrypt-compatible) mode */
#define CRYPT_TCRYPT "TCRYPT"
/** INTEGRITY dm-integrity device */
#define CRYPT_INTEGRITY "INTEGRITY"
/**
* Get device type
@@ -358,6 +360,32 @@ struct crypt_params_tcrypt {
*/
#define CRYPT_TCRYPT_VERA_MODES (1 << 4)
/**
*
* Structure used as parameter for dm-integrity device type.
*
* @see crypt_format, crypt_load
*
*/
struct crypt_params_integrity {
uint64_t journal_size;
unsigned int journal_watermark;
unsigned int journal_commit_time;
uint32_t interleave_sectors;
uint32_t tag_size;
uint32_t sector_size; /* integrity sector size */
uint32_t buffer_sectors;
const char *integrity;
const char *journal_integrity;
const char *journal_integrity_key; /* only for crypt_load */
uint32_t journal_integrity_key_size; /* only for crypt_load */
const char *journal_crypt;
const char *journal_crypt_key; /* only for crypt_load */
uint32_t journal_crypt_key_size; /* only for crypt_load */
};
/** @} */
/**
@@ -677,7 +705,10 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
#define CRYPT_ACTIVATE_RESTART_ON_CORRUPTION (1 << 9)
/** dm-verity: ignore_zero_blocks - do not verify zero blocks */
#define CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS (1 << 10)
/** dm-integrity: direct writes, do not use journal */
#define CRYPT_ACTIVATE_NO_JOURNAL (1 << 12)
/** dm-integrity: recovery mode - no journal, no integrity checks */
#define CRYPT_ACTIVATE_RECOVERY (1 << 13)
/**
* Active device runtime attributes
@@ -886,6 +917,46 @@ const char *crypt_get_cipher(struct crypt_device *cd);
*/
const char *crypt_get_cipher_mode(struct crypt_device *cd);
/**
* Get cipher integrity mode used in device.
*
* @param cd crypt device handle
*
* @return used cipher mode e.g. "hmac(sha256)" or @e otherwise
*
*/
const char *crypt_get_integrity(struct crypt_device *cd);
/**
* Get size (in bytes) of integrity key (if present) for crypt device.
*
* @param cd crypt device handle
*
* @return integrity key size
*
*/
int crypt_get_integrity_key_size(struct crypt_device *cd);
/**
* Get size (in bytes) of integrity tag (if present) for crypt device.
*
* @param cd crypt device handle
*
* @return integrity tag size
*
*/
int crypt_get_integrity_tag_size(struct crypt_device *cd);
/**
* Get size (in bytes) of provided data sectors for integrity device.
*
* @param cd crypt device handle
*
* @return provided device size in 512-bytes sectors
*
*/
uint64_t crypt_get_integrity_sectors(struct crypt_device *cd);
/**
* Get device UUID.
*
@@ -936,6 +1007,16 @@ uint64_t crypt_get_iv_offset(struct crypt_device *cd);
*/
int crypt_get_volume_key_size(struct crypt_device *cd);
/**
* Get size (in bytes) of encryption sector for crypt device.
*
* @param cd crypt device handle
*
* @return sector size
*
*/
int crypt_get_sector_size(struct crypt_device *cd);
/**
* Get device parameters for VERITY device.
*

View File

@@ -39,12 +39,17 @@ CRYPTSETUP_1.0 {
crypt_benchmark_kdf;
crypt_get_cipher;
crypt_get_cipher_mode;
crypt_get_integrity;
crypt_get_integrity_key_size;
crypt_get_integrity_tag_size;
crypt_get_integrity_sectors;
crypt_get_uuid;
crypt_get_data_offset;
crypt_get_iv_offset;
crypt_get_volume_key_size;
crypt_get_device_name;
crypt_get_verity_info;
crypt_get_sector_size;
crypt_get_type;
crypt_get_active_device;

View File

@@ -22,6 +22,7 @@
*/
#include <stdio.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <libdevmapper.h>
@@ -37,6 +38,7 @@
#define DM_UUID_PREFIX_LEN 6
#define DM_CRYPT_TARGET "crypt"
#define DM_VERITY_TARGET "verity"
#define DM_INTEGRITY_TARGET "integrity"
#define RETRY_COUNT 5
/* Set if dm-crypt version was probed */
@@ -176,6 +178,18 @@ static void _dm_set_verity_compat(const char *dm_version, unsigned verity_maj,
verity_maj, verity_min, verity_patch);
}
static void _dm_set_integrity_compat(const char *dm_version, unsigned integrity_maj,
unsigned integrity_min, unsigned integrity_patch)
{
if (integrity_maj > 0)
_dm_crypt_flags |= DM_INTEGRITY_SUPPORTED;
else
return;
log_dbg("Detected dm-integrity version %i.%i.%i.",
integrity_maj, integrity_min, integrity_patch);
}
static int _dm_check_versions(void)
{
struct dm_task *dmt;
@@ -212,6 +226,11 @@ static int _dm_check_versions(void)
(unsigned)target->version[0],
(unsigned)target->version[1],
(unsigned)target->version[2]);
} else if (!strcmp(DM_INTEGRITY_TARGET, target->name)) {
_dm_set_integrity_compat(dm_version,
(unsigned)target->version[0],
(unsigned)target->version[1],
(unsigned)target->version[2]);
}
target = (struct dm_versions *)((char *) target + target->next);
} while (last_target != target);
@@ -447,6 +466,143 @@ out:
}
static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_t flags)
{
int r, max_size, num_options = 0;
char *params, *hexkey, mode;
char features[256], feature[256];
if (!dmd)
return NULL;
max_size = strlen(device_block_path(dmd->data_device)) +
(dmd->u.integrity.vk ? dmd->u.integrity.vk->keylength * 2 : 0) +
(dmd->u.integrity.journal_integrity_key ? dmd->u.integrity.journal_crypt_key->keylength * 2 : 0) +
(dmd->u.integrity.journal_crypt_key ? dmd->u.integrity.journal_crypt_key->keylength * 2 : 0) +
(dmd->u.integrity.integrity ? strlen(dmd->u.integrity.integrity) : 0) +
(dmd->u.integrity.journal_integrity ? strlen(dmd->u.integrity.journal_integrity) : 0) +
(dmd->u.integrity.journal_crypt ? strlen(dmd->u.integrity.journal_crypt) : 0) +
128;
params = crypt_safe_alloc(max_size);
if (!params)
return NULL;
*features = '\0';
if (dmd->u.integrity.journal_size) {
num_options++;
snprintf(feature, sizeof(feature), "journal_sectors:%u ",
(unsigned)(dmd->u.integrity.journal_size / SECTOR_SIZE));
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.journal_watermark) {
num_options++;
snprintf(feature, sizeof(feature), "journal_watermark:%u ",
dmd->u.integrity.journal_watermark);
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.journal_commit_time) {
num_options++;
snprintf(feature, sizeof(feature), "commit_time:%u ",
dmd->u.integrity.journal_commit_time);
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.interleave_sectors) {
num_options++;
snprintf(feature, sizeof(feature), "interleave_sectors:%u ",
dmd->u.integrity.interleave_sectors);
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.sector_size) {
num_options++;
snprintf(feature, sizeof(feature), "block_size:%u ",
dmd->u.integrity.sector_size);
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.buffer_sectors) {
num_options++;
snprintf(feature, sizeof(feature), "buffer_sectors:%u ",
dmd->u.integrity.buffer_sectors);
strncat(features, feature, sizeof(features));
}
if (dmd->u.integrity.integrity) {
num_options++;
if (dmd->u.integrity.vk) {
hexkey = crypt_safe_alloc(dmd->u.integrity.vk->keylength * 2 + 1);
if (!hexkey) {
crypt_safe_free(params);
return NULL;
}
hex_key(hexkey, dmd->u.integrity.vk->keylength, dmd->u.integrity.vk->key);
} else
hexkey = NULL;
snprintf(feature, sizeof(feature), "internal_hash:%s%s%s ",
dmd->u.integrity.integrity, hexkey ? ":" : "", hexkey ?: "");
strncat(features, feature, sizeof(features));
crypt_safe_free(hexkey);
}
if (dmd->u.integrity.journal_integrity) {
num_options++;
if (dmd->u.integrity.journal_integrity_key) {
hexkey = crypt_safe_alloc(dmd->u.integrity.journal_integrity_key->keylength * 2 + 1);
if (!hexkey) {
crypt_safe_free(params);
return NULL;
}
hex_key(hexkey, dmd->u.integrity.journal_integrity_key->keylength,
dmd->u.integrity.journal_integrity_key->key);
} else
hexkey = NULL;
snprintf(feature, sizeof(feature), "journal_mac:%s%s%s ",
dmd->u.integrity.journal_integrity, hexkey ? ":" : "", hexkey ?: "");
strncat(features, feature, sizeof(features));
crypt_safe_free(hexkey);
}
if (dmd->u.integrity.journal_crypt) {
num_options++;
if (dmd->u.integrity.journal_crypt_key) {
hexkey = crypt_safe_alloc(dmd->u.integrity.journal_crypt_key->keylength * 2 + 1);
if (!hexkey) {
crypt_safe_free(params);
return NULL;
}
hex_key(hexkey, dmd->u.integrity.journal_crypt_key->keylength,
dmd->u.integrity.journal_crypt_key->key);
} else
hexkey = NULL;
snprintf(feature, sizeof(feature), "journal_crypt:%s%s%s ",
dmd->u.integrity.journal_crypt, hexkey ? ":" : "", hexkey ?: "");
strncat(features, feature, sizeof(features));
crypt_safe_free(hexkey);
}
if (flags & CRYPT_ACTIVATE_RECOVERY)
mode = 'R';
else if (flags & CRYPT_ACTIVATE_NO_JOURNAL)
mode = 'D';
else
mode = 'J';
r = snprintf(params, max_size, "%s %" PRIu64 " %d %c %d %s",
device_block_path(dmd->data_device), dmd->u.integrity.offset,
dmd->u.integrity.tag_size, mode,
num_options, *features ? features : "");
if (r < 0 || r >= max_size) {
crypt_safe_free(params);
params = NULL;
}
return params;
}
/* DM helpers */
static int _dm_simple(int task, const char *name, int udev_wait)
{
@@ -598,7 +754,7 @@ static int dm_prepare_uuid(const char *name, const char *type, const char *uuid,
static int _dm_create_device(const char *name, const char *type,
struct device *device, uint32_t flags,
const char *uuid, uint64_t size,
char *params, int reload)
const char *target, char *params, int reload)
{
struct dm_task *dmt = NULL;
struct dm_info dmi;
@@ -640,8 +796,7 @@ static int _dm_create_device(const char *name, const char *type,
if ((flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt))
goto out_no_removal;
if (!dm_task_add_target(dmt, 0, size,
!strcmp("VERITY", type) ? DM_VERITY_TARGET : DM_CRYPT_TARGET, params))
if (!dm_task_add_target(dmt, 0, size, target, params))
goto out_no_removal;
#ifdef DM_READ_AHEAD_MINIMUM_FLAG
@@ -698,12 +853,27 @@ out_no_removal:
return r;
}
static int check_retry(uint32_t *dmd_flags)
{
int ret = 0;
/* If discard not supported try to load without discard */
if ((*dmd_flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
!(dm_flags() & DM_DISCARDS_SUPPORTED)) {
log_dbg("Discard/TRIM is not supported");
*dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_ALLOW_DISCARDS;
ret = 1;
}
return ret;
}
int dm_create_device(struct crypt_device *cd, const char *name,
const char *type,
struct crypt_dm_active_device *dmd,
int reload)
{
char *table_params = NULL;
char *table_params = NULL, *target;
uint32_t dmd_flags;
int r;
@@ -715,24 +885,28 @@ int dm_create_device(struct crypt_device *cd, const char *name,
dmd_flags = dmd->flags;
if (dmd->target == DM_CRYPT)
if (dmd->target == DM_CRYPT) {
table_params = get_dm_crypt_params(dmd, dmd_flags);
else if (dmd->target == DM_VERITY)
target = DM_CRYPT_TARGET;
} else if (dmd->target == DM_VERITY) {
table_params = get_dm_verity_params(dmd->u.verity.vp, dmd, dmd_flags);
target = DM_VERITY_TARGET;
} else if (dmd->target == DM_INTEGRITY) {
table_params = get_dm_integrity_params(dmd, dmd_flags);
target = DM_INTEGRITY_TARGET;
} else {
dm_exit_context();
return -EINVAL;
}
r = _dm_create_device(name, type, dmd->data_device, dmd_flags,
dmd->uuid, dmd->size, table_params, reload);
dmd->uuid, dmd->size, target, table_params, reload);
/* If discard not supported try to load without discard */
if (!reload && r && dmd->target == DM_CRYPT &&
(dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
!(dm_flags() & DM_DISCARDS_SUPPORTED)) {
log_dbg("Discard/TRIM is not supported, retrying activation.");
dmd_flags = dmd_flags & ~CRYPT_ACTIVATE_ALLOW_DISCARDS;
if (!reload && r && dmd->target == DM_CRYPT && check_retry(&dmd_flags)) {
crypt_safe_free(table_params);
table_params = get_dm_crypt_params(dmd, dmd_flags);
r = _dm_create_device(name, type, dmd->data_device, dmd_flags,
dmd->uuid, dmd->size, table_params, reload);
dmd->uuid, dmd->size, target, table_params, reload);
}
if (r == -EINVAL &&
@@ -792,7 +966,8 @@ static int dm_status_dmi(const char *name, struct dm_info *dmi,
/* for target == NULL check all supported */
if (!target && (strcmp(target_type, DM_CRYPT_TARGET) &&
strcmp(target_type, DM_VERITY_TARGET)))
strcmp(target_type, DM_VERITY_TARGET) &&
strcmp(target_type, DM_INTEGRITY_TARGET)))
goto out;
r = 0;
out:
@@ -1163,6 +1338,122 @@ static int _dm_query_verity(uint32_t get_flags,
return 0;
}
static int _dm_query_integrity(uint32_t get_flags,
struct dm_info *dmi,
char *params,
struct crypt_dm_active_device *dmd)
{
uint32_t val32;
uint64_t val64;
char c, *str, *str2, *arg;
unsigned int features, val;
ssize_t len;
int i, r;
memset(dmd, 0, sizeof(*dmd));
dmd->target = DM_INTEGRITY;
/* data device */
str = strsep(&params, " ");
if (get_flags & DM_ACTIVE_DEVICE) {
str2 = crypt_lookup_dev(str);
r = device_alloc(&dmd->data_device, str2);
free(str2);
if (r < 0 && r != -ENOTBLK)
return r;
}
/*offset */
if (!params)
return -EINVAL;
val64 = strtoull(params, &params, 10);
if (!*params || *params != ' ')
return -EINVAL;
dmd->u.integrity.offset = val64;
/* tag size*/
val32 = strtoul(params, &params, 10);
dmd->u.integrity.tag_size = val32;
if (!*params || *params != ' ')
return -EINVAL;
/* journal */
c = toupper(*(++params));
if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R'))
return -EINVAL;
if (c == 'D')
dmd->flags |= CRYPT_ACTIVATE_NO_JOURNAL;
if (c == 'R')
dmd->flags |= CRYPT_ACTIVATE_RECOVERY;
dmd->u.integrity.sector_size = SECTOR_SIZE;
/* Features section */
if (params) {
/* Number of arguments */
val64 = strtoull(params, &params, 10);
if (*params != ' ')
return -EINVAL;
params++;
features = (int)val64;
for (i = 0; i < features; i++) {
if (!params)
return -EINVAL;
arg = strsep(&params, " ");
if (sscanf(arg, "journal_sectors:%u", &val) == 1)
dmd->u.integrity.journal_size = val * SECTOR_SIZE;
else if (sscanf(arg, "journal_watermark:%u", &val) == 1)
dmd->u.integrity.journal_watermark = val;
else if (sscanf(arg, "commit_time:%u", &val) == 1)
dmd->u.integrity.journal_commit_time = val;
else if (sscanf(arg, "interleave_sectors:%u", &val) == 1)
dmd->u.integrity.interleave_sectors = val;
else if (sscanf(arg, "block_size:%u", &val) == 1)
dmd->u.integrity.sector_size = val;
else if (sscanf(arg, "buffer_sectors:%u", &val) == 1)
dmd->u.integrity.buffer_sectors = val;
else if (!strncmp(arg, "internal_hash:", 14)) {
str = &arg[14];
arg = strsep(&str, ":");
dmd->u.integrity.integrity = strdup(arg);
if (str) {
len = crypt_hex_to_bytes(str, &str2, 1);
if (len < 0)
return len;
r = 0;
if (get_flags & DM_ACTIVE_CRYPT_KEY) {
dmd->u.integrity.vk = crypt_alloc_volume_key(len, str2);
if (!dmd->u.integrity.vk)
r = -ENOMEM;
} else if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
dmd->u.integrity.vk = crypt_alloc_volume_key(len, NULL);
if (!dmd->u.integrity.vk)
r = -ENOMEM;
}
crypt_safe_free(str2);
if (r)
return r;
}
} else if (!strncmp(arg, "journal_crypt:", 14))
;/* ignore it for now */
else if (!strncmp(arg, "journal_mac:", 12))
;/* ignore it for now */
else /* unknown option */
return -EINVAL;
}
/* All parameters should be processed */
if (params && *params)
return -EINVAL;
}
return 0;
}
int dm_query_device(struct crypt_device *cd, const char *name,
uint32_t get_flags, struct crypt_dm_active_device *dmd)
{
@@ -1213,6 +1504,8 @@ int dm_query_device(struct crypt_device *cd, const char *name,
if (r == 0)
dmd->flags |= CRYPT_ACTIVATE_CORRUPTED;
r = 0;
} else if (!strcmp(target_type, DM_INTEGRITY_TARGET)) {
r = _dm_query_integrity(get_flags, &dmi, params, dmd);
} else
r = -EINVAL;

View File

@@ -34,6 +34,7 @@
#include "loopaes.h"
#include "verity.h"
#include "tcrypt.h"
#include "integrity.h"
#include "internal.h"
struct crypt_device {
@@ -77,6 +78,11 @@ struct crypt_device {
struct crypt_params_tcrypt params;
struct tcrypt_phdr hdr;
} tcrypt;
struct { /* used in CRYPT_INTEGRITY */
struct crypt_params_integrity params;
struct volume_key *journal_mac_key;
struct volume_key *journal_crypt_key;
} integrity;
struct { /* used if initialized without header by name */
char *active_name;
/* buffers, must refresh from kernel on every query */
@@ -247,6 +253,11 @@ static int isTCRYPT(const char *type)
return (type && !strcmp(CRYPT_TCRYPT, type));
}
static int isINTEGRITY(const char *type)
{
return (type && !strcmp(CRYPT_INTEGRITY, type));
}
static int onlyLUKS(struct crypt_device *cd)
{
int r = 0;
@@ -635,6 +646,55 @@ static int _crypt_load_verity(struct crypt_device *cd, struct crypt_params_verit
return r;
}
static int _crypt_load_integrity(struct crypt_device *cd,
struct crypt_params_integrity *params)
{
int r;
r = init_crypto(cd);
if (r < 0)
return r;
r = INTEGRITY_read_sb(cd, &cd->u.integrity.params);
if (r < 0)
return r;
if (params) {
cd->u.integrity.params.journal_watermark = params->journal_watermark;
cd->u.integrity.params.journal_commit_time = params->journal_commit_time;
cd->u.integrity.params.buffer_sectors = params->buffer_sectors;
// FIXME: check ENOMEM
if (params->integrity)
cd->u.integrity.params.integrity = strdup(params->integrity);
if (params->journal_integrity)
cd->u.integrity.params.journal_integrity = strdup(params->journal_integrity);
if (params->journal_crypt)
cd->u.integrity.params.journal_crypt = strdup(params->journal_crypt);
if (params->journal_crypt_key) {
cd->u.integrity.journal_crypt_key =
crypt_alloc_volume_key(params->journal_crypt_key_size,
params->journal_crypt_key);
if (!cd->u.integrity.journal_crypt_key)
return -ENOMEM;
}
if (params->journal_integrity_key) {
cd->u.integrity.journal_mac_key =
crypt_alloc_volume_key(params->journal_integrity_key_size,
params->journal_integrity_key);
if (!cd->u.integrity.journal_mac_key)
return -ENOMEM;
}
}
if (!cd->type && !(cd->type = strdup(CRYPT_INTEGRITY))) {
free(CONST_CAST(void*)cd->u.integrity.params.integrity);
return -ENOMEM;
}
return 0;
}
static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
{
struct crypt_dm_active_device dmd = {};
@@ -754,6 +814,38 @@ out:
return r;
}
static int _init_by_name_integrity(struct crypt_device *cd, const char *name)
{
struct crypt_dm_active_device dmd = {
.target = DM_INTEGRITY,
};
int r;
r = dm_query_device(cd, name, DM_ACTIVE_DEVICE |
DM_ACTIVE_CRYPT_KEY |
DM_ACTIVE_CRYPT_KEYSIZE, &dmd);
if (r < 0)
goto out;
if (r > 0)
r = 0;
if (isINTEGRITY(cd->type)) {
cd->u.integrity.params.tag_size = dmd.u.integrity.tag_size;
cd->u.integrity.params.sector_size = dmd.u.integrity.sector_size;
cd->u.integrity.params.journal_size = dmd.u.integrity.journal_size;
cd->u.integrity.params.journal_watermark = dmd.u.integrity.journal_watermark;
cd->u.integrity.params.journal_commit_time = dmd.u.integrity.journal_commit_time;
cd->u.integrity.params.interleave_sectors = dmd.u.integrity.interleave_sectors;
cd->u.integrity.params.buffer_sectors = dmd.u.integrity.buffer_sectors;
cd->u.integrity.params.integrity = dmd.u.integrity.integrity;
//FIXME init keys?
}
out:
crypt_free_volume_key(dmd.u.integrity.vk);
device_free(dmd.data_device);
return r;
}
int crypt_init_by_name_and_header(struct crypt_device **cd,
const char *name,
const char *header_device)
@@ -811,6 +903,8 @@ int crypt_init_by_name_and_header(struct crypt_device **cd,
(*cd)->type = strdup(CRYPT_VERITY);
else if (!strncmp(CRYPT_TCRYPT, dmd.uuid, sizeof(CRYPT_TCRYPT)-1))
(*cd)->type = strdup(CRYPT_TCRYPT);
else if (!strncmp(CRYPT_INTEGRITY, dmd.uuid, sizeof(CRYPT_INTEGRITY)-1))
(*cd)->type = strdup(CRYPT_INTEGRITY);
else
log_dbg("Unknown UUID set, some parameters are not set.");
} else
@@ -828,6 +922,8 @@ int crypt_init_by_name_and_header(struct crypt_device **cd,
r = _init_by_name_crypt(*cd, name);
else if (dmd.target == DM_VERITY)
r = _init_by_name_verity(*cd, name);
else if (dmd.target == DM_INTEGRITY)
r = _init_by_name_integrity(*cd, name);
out:
if (r < 0) {
crypt_free(*cd);
@@ -1129,6 +1225,78 @@ static int _crypt_format_verity(struct crypt_device *cd,
return r;
}
static int _crypt_format_integrity(struct crypt_device *cd,
const char *uuid,
struct crypt_params_integrity *params)
{
int r;
if (!params)
return -EINVAL;
if (uuid) {
log_err(cd, _("UUID is not supported for this crypt type.\n"));
return -EINVAL;
}
/* Wipe first 8 sectors - fs magic numbers etc. */
r = crypt_wipe(crypt_metadata_device(cd), 0, 8 * SECTOR_SIZE, CRYPT_WIPE_ZERO, 1);
if (r < 0) {
if (r == -EBUSY)
log_err(cd, _("Cannot format device %s which is still in use.\n"),
mdata_device_path(cd));
else if (r == -EACCES) {
log_err(cd, _("Cannot format device %s, permission denied.\n"),
mdata_device_path(cd));
r = -EINVAL;
} else
log_err(cd, _("Cannot wipe header on device %s.\n"),
mdata_device_path(cd));
return r;
}
if (!(cd->type = strdup(CRYPT_INTEGRITY)))
return -ENOMEM;
if (params->journal_crypt_key) {
cd->u.integrity.journal_crypt_key =
crypt_alloc_volume_key(params->journal_crypt_key_size,
params->journal_crypt_key);
if (!cd->u.integrity.journal_crypt_key) {
crypt_reset_null_type(cd);
return -ENOMEM;
}
}
if (params->journal_integrity_key) {
cd->u.integrity.journal_mac_key =
crypt_alloc_volume_key(params->journal_integrity_key_size,
params->journal_integrity_key);
if (!cd->u.integrity.journal_mac_key) {
crypt_reset_null_type(cd);
return -ENOMEM;
}
}
cd->u.integrity.params.journal_size = params->journal_size;
cd->u.integrity.params.journal_watermark = params->journal_watermark;
cd->u.integrity.params.journal_commit_time = params->journal_commit_time;
cd->u.integrity.params.interleave_sectors = params->interleave_sectors;
cd->u.integrity.params.buffer_sectors = params->buffer_sectors;
cd->u.integrity.params.sector_size = params->sector_size;
cd->u.integrity.params.tag_size = params->tag_size;
cd->u.integrity.params.integrity = params->integrity;
cd->u.integrity.params.journal_integrity = params->journal_integrity;
cd->u.integrity.params.journal_crypt = params->journal_crypt;
r = INTEGRITY_format(cd, params, cd->u.integrity.journal_crypt_key, cd->u.integrity.journal_mac_key);
if (r)
log_err(cd, _("Cannot format integrity for device %s.\n"),
mdata_device_path(cd));
return r;
}
int crypt_format(struct crypt_device *cd,
const char *type,
const char *cipher,
@@ -1166,6 +1334,8 @@ int crypt_format(struct crypt_device *cd,
r = _crypt_format_loopaes(cd, cipher, uuid, volume_key_size, params);
else if (isVERITY(type))
r = _crypt_format_verity(cd, uuid, params);
else if (isINTEGRITY(type))
r = _crypt_format_integrity(cd, uuid, params);
else {
log_err(cd, _("Unknown crypt device type %s requested.\n"), type);
r = -EINVAL;
@@ -1213,6 +1383,12 @@ int crypt_load(struct crypt_device *cd,
return -EINVAL;
}
r = _crypt_load_tcrypt(cd, params);
} else if (isINTEGRITY(requested_type)) {
if (cd->type && !isINTEGRITY(cd->type)) {
log_dbg("Context is already initialised to type %s", cd->type);
return -EINVAL;
}
r = _crypt_load_integrity(cd, params);
} else
return -EINVAL;
@@ -1404,6 +1580,10 @@ void crypt_free(struct crypt_device *cd)
free(cd->u.verity.root_hash);
free(cd->u.verity.uuid);
device_free(cd->u.verity.fec_device);
} else if (isINTEGRITY(cd->type)) {
free(CONST_CAST(void*)cd->u.integrity.params.integrity);
crypt_free_volume_key(cd->u.integrity.journal_crypt_key);
crypt_free_volume_key(cd->u.integrity.journal_mac_key);
} else if (!cd->type) {
free(cd->u.none.active_name);
}
@@ -2069,6 +2249,17 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
return 0;
r = TCRYPT_activate(cd, name, &cd->u.tcrypt.hdr,
&cd->u.tcrypt.params, flags);
} else if (isINTEGRITY(cd->type)) {
if (!name)
return 0;
if (volume_key) {
vk = crypt_alloc_volume_key(volume_key_size, volume_key);
if (!vk)
return -ENOMEM;
}
r = INTEGRITY_activate(cd, name, &cd->u.integrity.params, vk,
cd->u.integrity.journal_crypt_key,
cd->u.integrity.journal_mac_key, flags);
} else
log_err(cd, _("Device type is not properly initialised.\n"));
@@ -2326,6 +2517,8 @@ int crypt_dump(struct crypt_device *cd)
return _verity_dump(cd);
else if (isTCRYPT(cd->type))
return TCRYPT_dump(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params);
else if (isINTEGRITY(cd->type))
return INTEGRITY_dump(cd, crypt_data_device(cd), 0);
log_err(cd, _("Dump operation is not supported for this device type.\n"));
return -EINVAL;
@@ -2396,6 +2589,52 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd)
return NULL;
}
const char *crypt_get_integrity(struct crypt_device *cd)
{
if (isINTEGRITY(cd->type))
return cd->u.integrity.params.integrity;
return NULL;
}
int crypt_get_integrity_key_size(struct crypt_device *cd)
{
if (isINTEGRITY(cd->type))
return INTEGRITY_key_size(cd);
return 0;
}
int crypt_get_integrity_tag_size(struct crypt_device *cd)
{
if (isINTEGRITY(cd->type))
return cd->u.integrity.params.tag_size;
return 0;
}
uint64_t crypt_get_integrity_sectors(struct crypt_device *cd)
{
uint64_t sectors;
if (!isINTEGRITY(cd->type))
return 0;
if (INTEGRITY_data_sectors(cd, crypt_data_device(cd),
crypt_get_data_offset(cd) * SECTOR_SIZE, &sectors) < 0)
return 0;
return sectors;
}
int crypt_get_sector_size(struct crypt_device *cd)
{
if (isINTEGRITY(cd->type))
return cd->u.integrity.params.sector_size;
return SECTOR_SIZE;
}
const char *crypt_get_uuid(struct crypt_device *cd)
{
if (isLUKS(cd->type))
@@ -2539,13 +2778,15 @@ int crypt_get_active_device(struct crypt_device *cd, const char *name,
if (r < 0)
return r;
if (dmd.target != DM_CRYPT && dmd.target != DM_VERITY)
if (dmd.target != DM_CRYPT &&
dmd.target != DM_VERITY &&
dmd.target != DM_INTEGRITY)
return -ENOTSUP;
if (cd && isTCRYPT(cd->type)) {
cad->offset = TCRYPT_get_data_offset(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params);
cad->iv_offset = TCRYPT_get_iv_offset(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params);
} else {
} else if (dmd.target == DM_CRYPT) {
cad->offset = dmd.u.crypt.offset;
cad->iv_offset = dmd.u.crypt.iv_offset;
}

View File

@@ -84,6 +84,28 @@ int crypt_parse_name_and_mode(const char *s, char *cipher, int *key_nums,
return -EINVAL;
}
int crypt_parse_hash_integrity_mode(const char *s, char *integrity)
{
char mode[MAX_CIPHER_LEN], hash[MAX_CIPHER_LEN];
int r;
if (!s || !integrity || strchr(s, '(') || strchr(s, ')'))
return -EINVAL;
r = sscanf(s, "%" MAX_CIPHER_LEN_STR "[^-]-%" MAX_CIPHER_LEN_STR "s", mode, hash);
if (r == 2)
r = snprintf(integrity, MAX_CIPHER_LEN, "%s(%s)", mode, hash);
else if (r == 1)
r = snprintf(integrity, MAX_CIPHER_LEN, "%s", mode);
else
return -EINVAL;
if (r < 0 || r == MAX_CIPHER_LEN)
return -EINVAL;
return 0;
}
/*
* Replacement for memset(s, 0, n) on stack that can be optimized out
* Also used in safe allocations for explicit memory wipe.

View File

@@ -33,6 +33,7 @@ struct crypt_device;
int crypt_parse_name_and_mode(const char *s, char *cipher,
int *key_nums, char *cipher_mode);
int crypt_parse_hash_integrity_mode(const char *s, char *integrity);
void *crypt_safe_alloc(size_t size);
void crypt_safe_free(void *data);

View File

@@ -44,6 +44,7 @@ struct device;
#define DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED (1 << 8) /* submit_from_crypt_cpus */
#define DM_VERITY_ON_CORRUPTION_SUPPORTED (1 << 9) /* ignore/restart_on_corruption, ignore_zero_block */
#define DM_VERITY_FEC_SUPPORTED (1 << 10) /* Forward Error Correction (FEC) */
#define DM_INTEGRITY_SUPPORTED (1 << 12) /* dm-integrity target supported */
uint32_t dm_flags(void);
@@ -59,7 +60,7 @@ uint32_t dm_flags(void);
#define DM_ACTIVE_VERITY_PARAMS (1 << 7)
struct crypt_dm_active_device {
enum { DM_CRYPT = 0, DM_VERITY } target;
enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY } target;
uint64_t size; /* active device size */
uint32_t flags; /* activation flags */
const char *uuid;
@@ -67,6 +68,7 @@ struct crypt_dm_active_device {
union {
struct {
const char *cipher;
const char *integrity;
/* Active key for device */
struct volume_key *vk;
@@ -88,6 +90,26 @@ struct crypt_dm_active_device {
uint64_t fec_blocks; /* size of FEC device (in hash blocks) */
struct crypt_params_verity *vp;
} verity;
struct {
uint64_t journal_size;
uint32_t journal_watermark;
uint32_t journal_commit_time;
uint32_t interleave_sectors;
uint32_t tag_size;
uint64_t offset; /* offset in sectors */
uint32_t sector_size; /* integrity sector size */
uint32_t buffer_sectors;
const char *integrity;
/* Active key for device */
struct volume_key *vk;
const char *journal_integrity;
struct volume_key *journal_integrity_key;
const char *journal_crypt;
struct volume_key *journal_crypt_key;
} integrity;
} u;
};

View File

@@ -8,4 +8,8 @@ if REENCRYPT
man8_MANS += cryptsetup-reencrypt.8
endif
EXTRA_DIST = cryptsetup.8 veritysetup.8 cryptsetup-reencrypt.8
if INTEGRITYSETUP
man8_MANS += integritysetup.8
endif
EXTRA_DIST = cryptsetup.8 integritysetup.8 veritysetup.8 cryptsetup-reencrypt.8

96
man/integritysetup.8 Normal file
View File

@@ -0,0 +1,96 @@
.TH INTEGRITYSETUP "8" "May 2017" "integritysetup" "Maintenance Commands"
.SH NAME
integritysetup - manage dm-integrity (block level integrity) volumes
.SH SYNOPSIS
.B integritysetup <options> <action> <action args>
.SH DESCRIPTION
.PP
Integritysetup is used to configure dm-integrity managed device-mapper mappings.
Device-mapper integrity target provides read-write transparent integrity
checking of block devices. The dm-integrity target emulates additional data
integrity field per-sector. You can use this additional field directly
with integritysetup utility, or indirectly (for authenticated encryption)
through cryptsetup.
Integritysetup supports these operations:
.PP
\fIformat\fR <device>
.IP
Formats <device> (calculates space and dm-integrity superblock).
\fB<options>\fR can be []
.PP
\fIcreate\fR <name> <device>
.IP
Creates a mapping with <name> backed by device <device>.
.PP
\fIremove\fR <name>
.IP
Removes existing mapping <name>.
.PP
\fIstatus\fR <name>
.IP
Reports status for the active integrity mapping <name>.
.PP
\fIdump\fR <device>
.IP
Reports parameters from on-disk stored superblock.
.SH OPTIONS
.TP
.B "\-\-verbose, \-v"
Print more information on command execution.
.TP
.B "\-\-debug"
Run in debug mode with full diagnostic logs. Debug output
lines are always prefixed by '#'.
.B "\-\-version"
Show the program version.
.TP
\fBWARNING:\fR Use these options only for very specific cases.
The dm-integrity target is available since Linux kernel version 4.12.
.TP
.SH RETURN CODES
Integritysetup returns 0 on success and a non-zero value on error.
Error codes are:
1 wrong parameters
2 no permission
3 out of memory
4 wrong device specified
5 device already exists or device is busy.
.SH EXAMPLES
.B "integritysetup format <device> --tag-size 4"
Formats device to use additional 4 bytes per-sector for integrity data.
.B "integritysetup create test-device <device> --integrity crc32"
Acivates the integrity device named test-device and automaticaly calculate specified
checsum on write (and verifies it on read).
.SH REPORTING BUGS
Report bugs, including ones in the documentation, on
the cryptsetup mailing list at <dm-crypt@saout.de>
or in the 'Issues' section on LUKS website.
Please attach the output of the failed command with the
\-\-debug option added.
.SH AUTHORS
The integritysetup tool and code is written by Milan Broz <gmazyland@gmail.com>
and is part of cryptsetup project.
.SH COPYRIGHT
Copyright \(co 2016-2017 Red Hat, Inc.
.br
Copyright \(co 2016-2017 Milan Broz
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
The project website at \fBhttps://gitlab.com/cryptsetup/cryptsetup\fR
The integrity on-disk format specification available at
\fBhttps://gitlab.com/cryptsetup/cryptsetup/wikis/DMIntegrity\fR

View File

@@ -17,6 +17,7 @@ lib/verity/verity_hash.c
lib/verity/verity_fec.c
src/cryptsetup.c
src/veritysetup.c
src/integritysetup.c
src/cryptsetup_reencrypt.c
src/utils_tools.c
src/utils_password.c

View File

@@ -69,6 +69,36 @@ veritysetup_static_LDADD = $(veritysetup_LDADD) \
endif
endif
# integritysetup
if INTEGRITYSETUP
integritysetup_SOURCES = \
$(top_builddir)/lib/utils_crypt.c \
$(top_builddir)/lib/utils_loop.c \
utils_tools.c \
integritysetup.c \
cryptsetup.h
integritysetup_LDADD = \
$(top_builddir)/lib/libcryptsetup.la \
@POPT_LIBS@
integritysetup_CFLAGS = $(cryptsetup_CFLAGS)
sbin_PROGRAMS += integritysetup
if STATIC_TOOLS
sbin_PROGRAMS += integritysetup.static
integritysetup_static_SOURCES = $(integritysetup_SOURCES)
integritysetup_static_CFLAGS = $(integritysetup_CFLAGS)
integritysetup_static_LDFLAGS = $(AM_LDFLAGS) -all-static
integritysetup_static_LDADD = $(integritysetup_LDADD) \
@CRYPTO_STATIC_LIBS@ \
@DEVMAPPER_STATIC_LIBS@ \
@UUID_LIBS@
endif
endif
# reencrypt
if REENCRYPT
cryptsetup_reencrypt_SOURCES = \

553
src/integritysetup.c Normal file
View File

@@ -0,0 +1,553 @@
/*
* integritysetup - setup integrity protected volumes for dm-integrity
*
* Copyright (C) 2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2017, Milan Broz
*
* 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 "cryptsetup.h"
#define PACKAGE_INTEGRITY "integritysetup"
#define DEFAULT_TAG_SIZE 4
#define DEFAULT_ALG_NAME "crc32"
#define MAX_KEY_SIZE 4096
static const char *opt_journal_size_str = NULL;
static uint64_t opt_journal_size = 0;
static int opt_interleave_sectors = 0;
static int opt_journal_watermark = 0;
static int opt_journal_commit_time = 0;
static int opt_tag_size = DEFAULT_TAG_SIZE;
static int opt_sector_size = 0;
static int opt_buffer_sectors = 0;
static const char *opt_integrity = DEFAULT_ALG_NAME;
static const char *opt_integrity_key_file = NULL;
static int opt_integrity_key_size = 0;
static const char *opt_journal_integrity = NULL; /* none */
static const char *opt_journal_integrity_key_file = NULL;
static int opt_journal_integrity_key_size = 0;
static const char *opt_journal_crypt = NULL; /* none */
static const char *opt_journal_crypt_key_file = NULL;
static int opt_journal_crypt_key_size = 0;
static int opt_integrity_nojournal = 0;
static int opt_integrity_recovery = 0;
static int opt_version_mode = 0;
static const char **action_argv;
static int action_argc;
// FIXME: move this to tools and handle EINTR
static int _read_mk(const char *file, char **key, int keysize)
{
int fd;
if (keysize <= 0 || keysize > MAX_KEY_SIZE) {
log_err(_("Invalid key size.\n"));
return -EINVAL;
}
*key = crypt_safe_alloc(keysize);
if (!*key)
return -ENOMEM;
fd = open(file, O_RDONLY);
if (fd == -1) {
log_err(_("Cannot read keyfile %s.\n"), file);
goto fail;
}
if ((read(fd, *key, keysize) != keysize)) {
log_err(_("Cannot read %d bytes from keyfile %s.\n"), keysize, file);
close(fd);
goto fail;
}
close(fd);
return 0;
fail:
crypt_safe_free(*key);
*key = NULL;
return -EINVAL;
}
static int _read_keys(char **integrity_key, struct crypt_params_integrity *params)
{
char *int_key = NULL, *journal_integrity_key = NULL, *journal_crypt_key = NULL;
int r;
if (integrity_key && opt_integrity_key_file) {
r = _read_mk(opt_integrity_key_file, &int_key, opt_integrity_key_size);
if (r < 0)
return r;
}
if (opt_journal_integrity_key_file) {
r = _read_mk(opt_journal_integrity_key_file, &journal_integrity_key, opt_journal_integrity_key_size);
if (r < 0) {
crypt_safe_free(*integrity_key);
*integrity_key = NULL;
return r;
}
params->journal_integrity_key = journal_integrity_key;
params->journal_integrity_key_size = opt_journal_integrity_key_size;
}
if (opt_journal_crypt_key_file) {
r = _read_mk(opt_journal_crypt_key_file, &journal_crypt_key, opt_journal_crypt_key_size);
if (r < 0) {
crypt_safe_free(*integrity_key);
*integrity_key = NULL;
crypt_safe_free(journal_integrity_key);
return r;
}
params->journal_crypt_key = journal_crypt_key;
params->journal_crypt_key_size = opt_journal_crypt_key_size;
}
if (integrity_key)
*integrity_key = int_key;
return 0;
}
static int action_format(int arg)
{
struct crypt_device *cd = NULL;
struct crypt_params_integrity params = {
.journal_size = opt_journal_size,
.interleave_sectors = opt_interleave_sectors,
.journal_watermark = opt_journal_watermark,
.journal_commit_time = opt_journal_commit_time,
.buffer_sectors = opt_buffer_sectors,
.tag_size = opt_tag_size,
.sector_size = opt_sector_size ?: SECTOR_SIZE,
};
char journal_integrity[MAX_CIPHER_LEN], journal_crypt[MAX_CIPHER_LEN];
int r;
if (opt_journal_integrity) {
r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
return r;
}
params.journal_integrity = journal_integrity;
}
if (opt_journal_crypt) {
r = crypt_parse_hash_integrity_mode(opt_journal_crypt, journal_crypt);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
return r;
}
params.journal_crypt = journal_crypt;
}
r = _read_keys(NULL, &params);
if (r)
return r;
r = crypt_init(&cd, action_argv[0]);
if (r < 0)
return r;
r = crypt_format(cd, CRYPT_INTEGRITY, NULL,
NULL, NULL, NULL, 0, &params);
check_signal(&r);
crypt_safe_free(CONST_CAST(void*)params.journal_integrity_key);
crypt_safe_free(CONST_CAST(void*)params.journal_crypt_key);
crypt_free(cd);
return r;
}
static int action_create(int arg)
{
struct crypt_device *cd = NULL;
struct crypt_params_integrity params = {
.journal_watermark = opt_journal_watermark,
.journal_commit_time = opt_journal_commit_time,
.buffer_sectors = opt_buffer_sectors,
};
uint32_t activate_flags = 0;
char integrity[MAX_CIPHER_LEN], journal_integrity[MAX_CIPHER_LEN], journal_crypt[MAX_CIPHER_LEN];
char *integrity_key = NULL;
int r;
if (opt_integrity) {
r = crypt_parse_hash_integrity_mode(opt_integrity, integrity);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
return r;
}
params.integrity = integrity;
}
if (opt_journal_integrity) {
r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
return r;
}
params.journal_integrity = journal_integrity;
}
if (opt_journal_crypt) {
r = crypt_parse_hash_integrity_mode(opt_journal_crypt, journal_crypt);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
return r;
}
params.journal_crypt = journal_crypt;
}
if (opt_integrity_nojournal)
activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
if (opt_integrity_recovery)
activate_flags |= CRYPT_ACTIVATE_RECOVERY;
if ((r = crypt_init(&cd, action_argv[1])))
return r;
r = _read_keys(&integrity_key, &params);
if (r)
goto out;
r = crypt_load(cd, CRYPT_INTEGRITY, &params);
if (r)
goto out;
r = crypt_activate_by_volume_key(cd, action_argv[0], integrity_key,
opt_integrity_key_size, activate_flags);
out:
crypt_safe_free(integrity_key);
crypt_safe_free(CONST_CAST(void*)params.journal_integrity_key);
crypt_safe_free(CONST_CAST(void*)params.journal_crypt_key);
crypt_free(cd);
return r;
}
static int action_remove(int arg)
{
struct crypt_device *cd = NULL;
int r;
r = crypt_init_by_name(&cd, action_argv[0]);
if (r == 0)
r = crypt_deactivate(cd, action_argv[0]);
crypt_free(cd);
return r;
}
static int action_status(int arg)
{
crypt_status_info ci;
struct crypt_active_device cad;
struct crypt_device *cd = NULL;
char *backing_file;
const char *device;
int path = 0, r = 0;
/* perhaps a path, not a dm device name */
if (strchr(action_argv[0], '/'))
path = 1;
ci = crypt_status(NULL, action_argv[0]);
switch (ci) {
case CRYPT_INVALID:
r = -EINVAL;
break;
case CRYPT_INACTIVE:
if (path)
log_std("%s is inactive.\n", action_argv[0]);
else
log_std("%s/%s is inactive.\n", crypt_get_dir(), action_argv[0]);
r = -ENODEV;
break;
case CRYPT_ACTIVE:
case CRYPT_BUSY:
if (path)
log_std("%s is active%s.\n", action_argv[0],
ci == CRYPT_BUSY ? " and is in use" : "");
else
log_std("%s/%s is active%s.\n", crypt_get_dir(), action_argv[0],
ci == CRYPT_BUSY ? " and is in use" : "");
r = crypt_init_by_name_and_header(&cd, action_argv[0], NULL);
if (r < 0)
goto out;
log_std(" type: %s\n", crypt_get_type(cd) ?: "n/a");
r = crypt_get_active_device(cd, action_argv[0], &cad);
if (r < 0)
goto out;
log_std(" tag size: %u\n", crypt_get_integrity_tag_size(cd));
log_std(" integrity: %s\n", crypt_get_integrity(cd) ?: "(none)");
device = crypt_get_device_name(cd);
log_std(" device: %s\n", device);
if (crypt_loop_device(device)) {
backing_file = crypt_loop_backing_file(device);
log_std(" loop: %s\n", backing_file);
free(backing_file);
}
log_std(" sector size: %u sectors\n", crypt_get_sector_size(cd));
log_std(" size: %" PRIu64 " sectors\n", cad.size);
log_std(" mode: %s\n", cad.flags & CRYPT_ACTIVATE_READONLY ?
"readonly" : "read/write");
}
out:
crypt_free(cd);
if (r == -ENOTSUP)
r = 0;
return r;
return -EINVAL;
}
static int action_dump(int arg)
{
struct crypt_device *cd = NULL;
struct crypt_params_integrity params = {};
int r;
if ((r = crypt_init(&cd, action_argv[0])))
return r;
r = crypt_load(cd, CRYPT_INTEGRITY, &params);
if (!r)
crypt_dump(cd);
crypt_free(cd);
return r;
}
static struct action_type {
const char *type;
int (*handler)(int);
int required_action_argc;
const char *arg_desc;
const char *desc;
} action_types[] = {
{ "format", action_format, 1, N_("<integrity_device>"),N_("format device") },
{ "create", action_create, 2, N_("<name> <integrity_device>"),N_("create active device") },
{ "remove", action_remove, 1, N_("<name>"),N_("remove (deactivate) device") },
{ "status", action_status, 1, N_("<name>"),N_("show active device status") },
{ "dump", action_dump, 1, N_("<integrity_device>"),N_("show on-disk information") },
{ NULL, NULL, 0, NULL, NULL }
};
static void help(poptContext popt_context,
enum poptCallbackReason reason __attribute__((unused)),
struct poptOption *key,
const char *arg __attribute__((unused)),
void *data __attribute__((unused)))
{
struct action_type *action;
if (key->shortName == '?') {
log_std("%s %s\n", PACKAGE_INTEGRITY, PACKAGE_VERSION);
poptPrintHelp(popt_context, stdout, 0);
log_std(_("\n"
"<action> is one of:\n"));
for(action = action_types; action->type; action++)
log_std("\t%s %s - %s\n", action->type, _(action->arg_desc), _(action->desc));
log_std(_("\n"
"<name> is the device to create under %s\n"
"<integrity_device> is the device containing data with integrity tags\n"),
crypt_get_dir());
log_std(_("\nDefault compiled-in dm-integrity parameters:\n"
"\tTag size: %u bytes, Checksum algorithm: %s\n"),
DEFAULT_TAG_SIZE, DEFAULT_ALG_NAME);
exit(EXIT_SUCCESS);
} else
usage(popt_context, EXIT_SUCCESS, NULL, NULL);
}
static int run_action(struct action_type *action)
{
int r;
log_dbg("Running command %s.", action->type);
r = action->handler(0);
show_status(r);
return translate_errno(r);
}
int main(int argc, const char **argv)
{
static char *popt_tmp;
static const char *null_action_argv[] = {NULL};
static struct poptOption popt_help_options[] = {
{ NULL, '\0', POPT_ARG_CALLBACK, help, 0, NULL, NULL },
{ "help", '?', POPT_ARG_NONE, NULL, 0, N_("Show this help message"), NULL },
{ "usage", '\0', POPT_ARG_NONE, NULL, 0, N_("Display brief usage"), NULL },
POPT_TABLEEND
};
static struct poptOption popt_options[] = {
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL },
{ "version", '\0', POPT_ARG_NONE, &opt_version_mode, 0, N_("Print package version"), NULL },
{ "verbose", 'v', POPT_ARG_NONE, &opt_verbose, 0, N_("Shows more detailed error messages"), NULL },
{ "debug", '\0', POPT_ARG_NONE, &opt_debug, 0, N_("Show debug messages"), NULL },
{ "journal-size", '\0', POPT_ARG_STRING,&opt_journal_size_str, 0, N_("Journal size"), N_("bytes") },
{ "interleave-sectors", '\0', POPT_ARG_INT, &opt_interleave_sectors, 0, N_("Interleave sectors"), N_("SECTORS") },
{ "journal-watermark", '\0', POPT_ARG_INT, &opt_journal_watermark, 0, N_("Journal watermark"),N_("percent") },
{ "journal-commit-time",'\0', POPT_ARG_INT, &opt_journal_commit_time,0, N_("Journal commit time"), N_("ms") },
{ "tag-size", '\0', POPT_ARG_INT, &opt_tag_size, 0, N_("Tag size per-sector"), N_("bytes") },
{ "sector-size", '\0', POPT_ARG_INT, &opt_sector_size, 0, N_("Sector size"), N_("bytes") },
{ "buffer-sectors", '\0', POPT_ARG_INT, &opt_buffer_sectors, 0, N_("Buffers size"), N_("SECTORS") },
{ "integrity", '\0', POPT_ARG_STRING, &opt_integrity, 0, N_("Data integrity algorithm (default "DEFAULT_ALG_NAME ")"), NULL },
{ "integrity-key-size", '\0', POPT_ARG_INT, &opt_integrity_key_size, 0, N_("The size of the data integrity key"), N_("BITS") },
{ "integrity-key-file", '\0', POPT_ARG_STRING, &opt_integrity_key_file, 0, N_("Read the integrity key from a file."), NULL },
{ "journal-integrity", '\0', POPT_ARG_STRING, &opt_journal_integrity, 0, N_("Journal integrity algorithm"), NULL },
{ "journal-integrity-key-size",'\0', POPT_ARG_INT, &opt_journal_integrity_key_size,0, N_("The size of the journal integrity key"), N_("BITS") },
{ "journal-integrity-key-file",'\0', POPT_ARG_STRING, &opt_journal_integrity_key_file,0, N_("Read the journal integrity key from a file."), NULL },
{ "journal-crypt", '\0', POPT_ARG_STRING, &opt_journal_crypt, 0, N_("Journal encryption algorithm"), NULL },
{ "journal-crypt-key-size", '\0', POPT_ARG_INT, &opt_journal_crypt_key_size, 0, N_("The size of the journal encryption key"), N_("BITS") },
{ "journal-crypt-key-file", '\0', POPT_ARG_STRING, &opt_journal_crypt_key_file, 0, N_("Read the journal encryption key from a file."), NULL },
{ "integrity-no-journal", '\0',POPT_ARG_NONE, &opt_integrity_nojournal, 0, N_("Disable journal for integrity device."), NULL },
{ "integrity-recovery-mode", '\0',POPT_ARG_NONE, &opt_integrity_recovery, 0, N_("Recovery mode (no journal, no tag checking)."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
struct action_type *action;
const char *aname;
int r;
crypt_set_log_callback(NULL, tool_log, NULL);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
popt_context = poptGetContext("integrity", argc, argv, popt_options, 0);
poptSetOtherOptionHelp(popt_context,
_("[OPTION...] <action> <action-specific>"));
while ((r = poptGetNextOpt(popt_context)) > 0) {
unsigned long long ull_value;
char *endp;
errno = 0;
ull_value = strtoull(popt_tmp, &endp, 10);
if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
(errno == ERANGE && ull_value == ULLONG_MAX) ||
(errno != 0 && ull_value == 0))
r = POPT_ERROR_BADNUMBER;
if (r < 0)
break;
}
if (r < -1)
usage(popt_context, EXIT_FAILURE, poptStrerror(r),
poptBadOption(popt_context, POPT_BADOPTION_NOALIAS));
if (opt_version_mode) {
log_std("%s %s\n", PACKAGE_INTEGRITY, PACKAGE_VERSION);
poptFreeContext(popt_context);
exit(EXIT_SUCCESS);
}
if (!(aname = poptGetArg(popt_context)))
usage(popt_context, EXIT_FAILURE, _("Argument <action> missing."),
poptGetInvocationName(popt_context));
for (action = action_types; action->type; action++)
if (strcmp(action->type, aname) == 0)
break;
if (!action->type)
usage(popt_context, EXIT_FAILURE, _("Unknown action."),
poptGetInvocationName(popt_context));
action_argc = 0;
action_argv = poptGetArgs(popt_context);
/* Make return values of poptGetArgs more consistent in case of remaining argc = 0 */
if (!action_argv)
action_argv = null_action_argv;
/* Count args, somewhat unnice, change? */
while (action_argv[action_argc] != NULL)
action_argc++;
if (action_argc < action->required_action_argc) {
char buf[128];
snprintf(buf, 128,_("%s: requires %s as arguments"), action->type, action->arg_desc);
usage(popt_context, EXIT_FAILURE, buf,
poptGetInvocationName(popt_context));
}
if (strcmp(aname, "format") && (opt_journal_size_str || opt_interleave_sectors || opt_sector_size))
usage(popt_context, EXIT_FAILURE,
_("Options --journal-size, --interleave--sectors, --sector-size and --tag-size can be "
"used only for format action.\n"),
poptGetInvocationName(popt_context));
if (strcmp(aname, "format") && opt_tag_size <= 0)
usage(popt_context, EXIT_FAILURE,
_("Option --tag-size must be set.\n"),
poptGetInvocationName(popt_context));
if (opt_journal_size_str &&
tools_string_to_size(NULL, opt_journal_size_str, &opt_journal_size))
usage(popt_context, EXIT_FAILURE, _("Invalid journal size specification."),
poptGetInvocationName(popt_context));
if ((opt_integrity_key_file && !opt_integrity_key_size) ||
(!opt_integrity_key_file && opt_integrity_key_size))
usage(popt_context, EXIT_FAILURE, _("Both key file and key size options must be specified."),
poptGetInvocationName(popt_context));
if (!opt_integrity && opt_integrity_key_file)
usage(popt_context, EXIT_FAILURE, _("Integrity algorithm must be specified if integrity key is used."),
poptGetInvocationName(popt_context));
if ((opt_journal_integrity_key_file && !opt_journal_integrity_key_size) ||
(!opt_journal_integrity_key_file && opt_journal_integrity_key_size))
usage(popt_context, EXIT_FAILURE, _("Both journal integrity key file and key size options must be specified."),
poptGetInvocationName(popt_context));
if (!opt_journal_integrity && opt_journal_integrity_key_file)
usage(popt_context, EXIT_FAILURE, _("Journal integrity algorithm must be specified if journal integrity key is used."),
poptGetInvocationName(popt_context));
if ((opt_journal_crypt_key_file && !opt_journal_crypt_key_size) ||
(!opt_journal_crypt_key_file && opt_journal_crypt_key_size))
usage(popt_context, EXIT_FAILURE, _("Both journal encryption key file and key size options must be specified."),
poptGetInvocationName(popt_context));
if (!opt_journal_crypt && opt_journal_crypt_key_file)
usage(popt_context, EXIT_FAILURE, _("Journal encryption algorithm must be specified if journal encryption key is used."),
poptGetInvocationName(popt_context));
if (opt_debug) {
opt_verbose = 1;
crypt_set_debug_level(-1);
dbg_version_and_cmd(argc, argv);
}
r = run_action(action);
poptFreeContext(popt_context);
return r;
}

View File

@@ -17,6 +17,10 @@ if REENCRYPT
TESTS += reencryption-compat-test
endif
if INTEGRITYSETUP
TESTS += integrity-compat-test
endif
EXTRA_DIST = compatimage.img.bz2 compatv10image.img.bz2 \
img_fs_ext4.img.bz2 img_fs_vfat.img.bz2 img_fs_xfs.img.bz2 \
valid_header_file.bz2 \
@@ -32,6 +36,7 @@ EXTRA_DIST = compatimage.img.bz2 compatv10image.img.bz2 \
tcrypt-compat-test \
luks1-compat-test \
device-test \
integrity-compat-test \
cryptsetup-valg-supps valg.sh valg-api.sh
CLEANFILES = cryptsetup-tst* valglog*

113
tests/integrity-compat-test Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
#
# Test integritysetup compatibility.
#
INTSETUP=../src/integritysetup
DEV_NAME=dmc_test
DEV=mode-test.img
KEY_FILE=key.img
dmremove() { # device
udevadm settle >/dev/null 2>&1
dmsetup remove $1 >/dev/null 2>&1
}
cleanup() {
[ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
rm -f $DEV $KEY_FILE >/dev/null 2>&1
}
fail()
{
echo
[ -n "$1" ] && echo "FAIL: $1"
cleanup
exit 100
}
skip()
{
[ -n "$1" ] && echo "$1"
exit 0
}
add_device() {
cleanup
dd if=/dev/urandom of=$KEY_FILE bs=1 count=512 >/dev/null 2>&1
dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
sync
}
status_check() # name value
{
X=$($INTSETUP status $DEV_NAME | grep "$1" | sed 's/.*: //' | cut -d' ' -f 2)
if [ "$X" != "$2" ] ; then
echo "[status FAIL]"
echo " Expecting $1:$2 got \"$X\"."
fail
fi
}
dump_check() # name value
{
X=$($INTSETUP dump $DEV | grep "$1" | cut -d' ' -f 2)
if [ "$X" != "$2" ] ; then
echo "[dump FAIL]"
echo " Expecting $1:$2 got \"$X\"."
fail
fi
}
int_check_sum() # alg checksum
{
# Fill device with zeroes and reopen it
dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1M oflag=direct >/dev/null 2>&1
dmremove $DEV_NAME
$INTSETUP create $DEV_NAME $DEV --integrity $1 || fail "Cannot activate device."
VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
if [ "$VSUM" = "$2" ] ; then
echo -n "[CHECKSUM OK]"
else
echo "[FAIL]"
echo " Expecting $2 got $VSUM."
fail
fi
}
intformat() # alg alg_out tagsize sector_size csum
{
echo -n "[INTEGRITY:$2:$3:$4]"
echo -n "[FORMAT]"
$INTSETUP format --integrity $1 --tag-size $3 --sector-size $4 $DEV || fail "Cannot format device."
dump_check "tag_size" $3
dump_check "sector_size" $4
echo -n "[ACTIVATE]"
$INTSETUP create $DEV_NAME $DEV --integrity $1 || fail "Cannot activate device."
status_check "tag size" $3
status_check "integrity" $2
status_check "sector size" $4
int_check_sum $1 $5
echo -n "[REMOVE]"
$INTSETUP remove $DEV_NAME || fail "Cannot deactivate device."
echo "[OK]"
}
[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
[ ! -x "$INTSETUP" ] && skip "Cannot find $INTSETUP, test skipped."
add_device
intformat crc32 crc32 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
intformat sha1 sha1 20 512 6eedd6344dab8875cd185fcd6565dfc869ab36bc57e577f40c685290b1fa7fe7
intformat sha1 sha1 16 4096 e152ec88227b539cd9cafd8bdb587a1072d720cd6bcebe1398d4136c9e7f337b
intformat sha256 sha256 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11
intformat hmac-sha256 hmac\(sha256\) 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11
intformat sha256 sha256 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4
intformat hmac-sha256 hmac\(sha256\) 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4
# FIXME: key, journal encryption, mode/recovery, journal params
cleanup