repart: add basic support for LUKS2 integrity verification (#39295)
Authenticated disk encryption is experimentally supported by cryptsetup since v2.0.0 and allows for automatic dm-integrity setup for LUKS devices. Add support for the mode to systemd-repart. The PR adds support for `cryptsetup luksFormat --integrity` to systemd-repart and "encryptedwithintegrity" dissection policy. Limitations: - No discard, online-only mode for repart. Closes #39250
This commit is contained in:
@@ -137,6 +137,12 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* systemd-repart: implement Integrity=data/meta and Integrity=inline for non-LUKS
|
||||
case. Currently, only Integrity=inline combined with Encrypt= is implemented
|
||||
and uses libcryptsetup features. Add support for plain dm-integrity setups when
|
||||
integrity tags are stored by the device (inline), interleaved with data (data),
|
||||
and on a separate device (meta).
|
||||
|
||||
* networkd/machined: implement reverse name lookups in the resolved hook
|
||||
|
||||
* networkd's resolved hook: optionally map all lease IP addresses handed out to
|
||||
|
||||
@@ -909,6 +909,27 @@
|
||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Integrity=</varname></term>
|
||||
|
||||
<listitem><para>Enable integrity checking for the partition. Currently, the only supported option
|
||||
is <varname>Integrity=inline</varname> which enables authenticated disk encryption for LUKS2 devices.
|
||||
The option requires <varname>Encrypt=</varname> setting and can only be used in online image build.
|
||||
Defaults to <literal>off</literal>, i.e. integrity protection is disabled.</para>
|
||||
<para>Note: authenticated disk encryption is considered EXPERIMENTAL by cryptsetup.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>IntegrityAlgorithm=</varname></term>
|
||||
<listitem><para>Specify the integrity algorithm to be used for integrity verification. For
|
||||
<varname>Integrity=inline</varname> the supported values are: <literal>hmac-sha1</literal>,
|
||||
<literal>hmac-sha256</literal>(default), and <literal>hmac-sha512</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Compression=</varname></term>
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
|
||||
return log_oom();
|
||||
|
||||
color = (f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE ? ansi_grey() :
|
||||
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
|
||||
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
|
||||
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
|
||||
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
|
||||
(f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT ? ansi_highlight_red() :
|
||||
!(f & PARTITION_POLICY_UNPROTECTED) ? ansi_highlight_green() : NULL;
|
||||
|
||||
|
||||
+108
-6
@@ -105,7 +105,7 @@
|
||||
/* To do LUKS2 offline encryption, we need to keep some extra free space at the end of the partition. */
|
||||
#define LUKS2_METADATA_KEEP_FREE (LUKS2_METADATA_SIZE*2ULL)
|
||||
|
||||
/* LUKS2 volume key size. */
|
||||
/* LUKS2 default volume key size (no integrity). */
|
||||
#define VOLUME_KEY_SIZE (512ULL/8ULL)
|
||||
|
||||
/* Use 4K as the default filesystem sector size because as long as the partitions are aligned to 4K, the
|
||||
@@ -266,6 +266,21 @@ typedef enum EncryptMode {
|
||||
_ENCRYPT_MODE_INVALID = -EINVAL,
|
||||
} EncryptMode;
|
||||
|
||||
typedef enum IntegrityMode {
|
||||
INTEGRITY_OFF,
|
||||
INTEGRITY_INLINE,
|
||||
_INTEGRITY_MODE_MAX,
|
||||
_INTEGRITY_MODE_INVALID = -EINVAL,
|
||||
} IntegrityMode;
|
||||
|
||||
typedef enum IntegrityAlg {
|
||||
INTEGRITY_ALG_HMAC_SHA1,
|
||||
INTEGRITY_ALG_HMAC_SHA256,
|
||||
INTEGRITY_ALG_HMAC_SHA512,
|
||||
_INTEGRITY_ALG_MAX,
|
||||
_INTEGRITY_ALG_INVALID = -EINVAL,
|
||||
} IntegrityAlg;
|
||||
|
||||
typedef enum VerityMode {
|
||||
VERITY_OFF,
|
||||
VERITY_DATA,
|
||||
@@ -444,6 +459,8 @@ typedef struct Partition {
|
||||
struct iovec key;
|
||||
Tpm2PCRValue *tpm2_hash_pcr_values;
|
||||
size_t tpm2_n_hash_pcr_values;
|
||||
IntegrityMode integrity;
|
||||
IntegrityAlg integrity_alg;
|
||||
VerityMode verity;
|
||||
char *verity_match_key;
|
||||
MinimizeMode minimize;
|
||||
@@ -552,6 +569,22 @@ static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
|
||||
[ENCRYPT_KEY_FILE_TPM2] = "key-file+tpm2",
|
||||
};
|
||||
|
||||
/* Going forward, the plan is to add two more modes:
|
||||
* [INTEGRITY_DATA] = "data" (interleave data and integrity tags on the same device),
|
||||
* [INTEGRITY_META] = "meta" (use a separate device for storing integrity tags).
|
||||
* Also, INTEGRITY_INLINE will be using hardware sector integrity fields when used
|
||||
* without encryption. */
|
||||
static const char *integrity_mode_table[_INTEGRITY_MODE_MAX] = {
|
||||
[INTEGRITY_OFF] = "off", /* no integrity protection */
|
||||
[INTEGRITY_INLINE] = "inline", /* luks2 storage when encrypted */
|
||||
};
|
||||
|
||||
static const char *integrity_alg_table[_INTEGRITY_ALG_MAX] = {
|
||||
[INTEGRITY_ALG_HMAC_SHA1] = "hmac-sha1",
|
||||
[INTEGRITY_ALG_HMAC_SHA256] = "hmac-sha256",
|
||||
[INTEGRITY_ALG_HMAC_SHA512] = "hmac-sha512",
|
||||
};
|
||||
|
||||
static const char *verity_mode_table[_VERITY_MODE_MAX] = {
|
||||
[VERITY_OFF] = "off",
|
||||
[VERITY_DATA] = "data",
|
||||
@@ -584,6 +617,8 @@ static const char *progress_phase_table[_PROGRESS_PHASE_MAX] = {
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(integrity_mode, IntegrityMode, INTEGRITY_INLINE);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(integrity_alg, IntegrityAlg);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase, ProgressPhase);
|
||||
@@ -2633,6 +2668,9 @@ static int config_parse_key_file(
|
||||
return parse_key_file(rvalue, &partition->key);
|
||||
}
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity, integrity_mode, IntegrityMode, INTEGRITY_OFF);
|
||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity_alg, integrity_alg, IntegrityAlg, INTEGRITY_ALG_HMAC_SHA256);
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF);
|
||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF);
|
||||
|
||||
@@ -2787,6 +2825,8 @@ static int partition_read_definition(
|
||||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||
{ "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
|
||||
{ "Partition", "KeyFile", config_parse_key_file, 0, p },
|
||||
{ "Partition", "Integrity", config_parse_integrity, 0, &p->integrity },
|
||||
{ "Partition", "IntegrityAlgorithm", config_parse_integrity_alg, 0, &p->integrity_alg },
|
||||
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
||||
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
||||
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
|
||||
@@ -2921,6 +2961,10 @@ static int partition_read_definition(
|
||||
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
|
||||
verity_mode_to_string(p->verity));
|
||||
|
||||
if (p->integrity == INTEGRITY_INLINE && p->encrypt == ENCRYPT_OFF)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Integrity=inline requires Encrypt=.");
|
||||
|
||||
if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
||||
@@ -4989,6 +5033,39 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* libcryptsetup uses its own names for integrity algorithms, e.g. 'hmac(sha1)' but systemd
|
||||
* prefers more standardized 'hmac-sha1', do the conversion here. Default to hmac(sha256). */
|
||||
static const char* dmcrypt_integrity_alg_name(Partition *p) {
|
||||
if (p->integrity != INTEGRITY_INLINE)
|
||||
return NULL;
|
||||
|
||||
switch (p->integrity_alg) {
|
||||
case INTEGRITY_ALG_HMAC_SHA1:
|
||||
return "hmac(sha1)";
|
||||
case INTEGRITY_ALG_HMAC_SHA512:
|
||||
return "hmac(sha512)";
|
||||
case INTEGRITY_ALG_HMAC_SHA256:
|
||||
default:
|
||||
return "hmac(sha256)";
|
||||
}
|
||||
}
|
||||
|
||||
/* Integrity puts specific limitations on the key size depending on the algorithm */
|
||||
static size_t dmcrypt_proper_key_size(Partition *p) {
|
||||
if (p->integrity != INTEGRITY_INLINE)
|
||||
return VOLUME_KEY_SIZE;
|
||||
|
||||
switch (p->integrity_alg) {
|
||||
case INTEGRITY_ALG_HMAC_SHA1:
|
||||
return 672/8;
|
||||
case INTEGRITY_ALG_HMAC_SHA512:
|
||||
return 1024/8;
|
||||
case INTEGRITY_ALG_HMAC_SHA256:
|
||||
default:
|
||||
return 768/8;
|
||||
}
|
||||
}
|
||||
|
||||
static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#if HAVE_TPM2
|
||||
@@ -4997,6 +5074,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
_cleanup_fclose_ FILE *h = NULL;
|
||||
_cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
|
||||
const char *passphrase = NULL;
|
||||
const size_t volume_key_size = dmcrypt_proper_key_size(p);
|
||||
size_t passphrase_size = 0;
|
||||
const char *vt;
|
||||
int r;
|
||||
@@ -5021,6 +5099,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
.label = vl,
|
||||
.sector_size = partition_fs_sector_size(context, p),
|
||||
.data_device = offline ? node : NULL,
|
||||
.integrity = dmcrypt_integrity_alg_name(p),
|
||||
};
|
||||
struct crypt_params_reencrypt reencrypt_params = {
|
||||
.mode = CRYPT_REENCRYPT_ENCRYPT,
|
||||
@@ -5032,6 +5111,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
};
|
||||
|
||||
if (offline) {
|
||||
/* libcryptsetup does not currently support reencryption of devices with integrity profiles.*/
|
||||
if (p->integrity == INTEGRITY_INLINE)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Integrity=inline cannot be enabled in offline mode.");
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine temporary files directory: %m");
|
||||
@@ -5084,7 +5167,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
"xts-plain64",
|
||||
SD_ID128_TO_UUID_STRING(p->luks_uuid),
|
||||
NULL,
|
||||
VOLUME_KEY_SIZE,
|
||||
/* volume_key_size= */ volume_key_size,
|
||||
&luks_params);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
|
||||
@@ -5097,7 +5180,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
cd,
|
||||
CRYPT_ANY_SLOT,
|
||||
NULL,
|
||||
VOLUME_KEY_SIZE,
|
||||
/* volume_key_size= */ volume_key_size,
|
||||
strempty(iovec_key->iov_base),
|
||||
iovec_key->iov_len);
|
||||
if (r < 0)
|
||||
@@ -5272,7 +5355,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
cd,
|
||||
CRYPT_ANY_SLOT,
|
||||
/* volume_key= */ NULL,
|
||||
/* volume_key_size= */ VOLUME_KEY_SIZE,
|
||||
/* volume_key_size= */ volume_key_size,
|
||||
base64_encoded,
|
||||
base64_encoded_size);
|
||||
if (keyslot < 0)
|
||||
@@ -5372,11 +5455,30 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
cd,
|
||||
dm_name,
|
||||
NULL,
|
||||
VOLUME_KEY_SIZE,
|
||||
(arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0) | CRYPT_ACTIVATE_PRIVATE);
|
||||
/* volume_key_size= */ volume_key_size,
|
||||
(arg_discard && p->integrity != INTEGRITY_INLINE ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0) | CRYPT_ACTIVATE_PRIVATE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to activate LUKS superblock: %m");
|
||||
|
||||
/* crypt_wipe() the whole device to avoid integrity errors upon mkfs */
|
||||
if (p->integrity == INTEGRITY_INLINE) {
|
||||
r = sym_crypt_wipe(
|
||||
cd,
|
||||
vol,
|
||||
CRYPT_WIPE_ZERO,
|
||||
/* offset= */ 0,
|
||||
/* length= */ 0,
|
||||
/* wipe_block_size= */ 1 * U64_MB,
|
||||
/* flags= */ 0,
|
||||
/* progress= */ NULL,
|
||||
/* usrptr= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to wipe LUKS device: %m");
|
||||
|
||||
log_info("%s integrity protection for future partition %" PRIu64 " initialized.",
|
||||
integrity_alg_to_string(p->integrity_alg), p->partno);
|
||||
}
|
||||
|
||||
dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (dev_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
|
||||
|
||||
@@ -70,6 +70,8 @@ DLSYM_PROTOTYPE(crypt_token_set_external_path) = NULL;
|
||||
DLSYM_PROTOTYPE(crypt_token_status) = NULL;
|
||||
DLSYM_PROTOTYPE(crypt_volume_key_get) = NULL;
|
||||
DLSYM_PROTOTYPE(crypt_volume_key_keyring) = NULL;
|
||||
DLSYM_PROTOTYPE(crypt_wipe) = NULL;
|
||||
DLSYM_PROTOTYPE(crypt_get_integrity_info) = NULL;
|
||||
|
||||
static void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
|
||||
|
||||
@@ -274,7 +276,9 @@ int dlopen_cryptsetup(void) {
|
||||
#endif
|
||||
DLSYM_ARG(crypt_token_status),
|
||||
DLSYM_ARG(crypt_volume_key_get),
|
||||
DLSYM_ARG(crypt_volume_key_keyring));
|
||||
DLSYM_ARG(crypt_volume_key_keyring),
|
||||
DLSYM_ARG(crypt_wipe),
|
||||
DLSYM_ARG(crypt_get_integrity_info));
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ extern DLSYM_PROTOTYPE(crypt_token_set_external_path);
|
||||
extern DLSYM_PROTOTYPE(crypt_token_status);
|
||||
extern DLSYM_PROTOTYPE(crypt_volume_key_get);
|
||||
extern DLSYM_PROTOTYPE(crypt_volume_key_keyring);
|
||||
extern DLSYM_PROTOTYPE(crypt_wipe);
|
||||
extern DLSYM_PROTOTYPE(crypt_get_integrity_info);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, crypt_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, sym_crypt_free, NULL);
|
||||
|
||||
+127
-4
@@ -397,6 +397,114 @@ static int image_policy_check_protection(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* internal LUKS2 header defines */
|
||||
#define LUKS2_FIXED_HDR_SIZE UINT64_C(0x1000)
|
||||
#define LUKS2_MAGIC "LUKS\xba\xbe"
|
||||
|
||||
/* Matches the beginning of 'struct luks2_hdr_disk' from cryptsetup */
|
||||
struct luks_header_incomplete {
|
||||
char luks_magic[sizeof(LUKS2_MAGIC) - 1];
|
||||
be16_t version;
|
||||
be64_t hdr_len;
|
||||
};
|
||||
|
||||
/* 'integrity' information from LUKS JSON header. Currenly, only 'type' is extracted/checked. */
|
||||
struct luks_integrity_data {
|
||||
char *type;
|
||||
};
|
||||
|
||||
static int integrity_information(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field table[] = {
|
||||
{ "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct luks_integrity_data, type), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
return sd_json_dispatch(v, table, flags, userdata);
|
||||
}
|
||||
|
||||
/* cryptsetup needs a loop device to work with a partition which has offset/size but
|
||||
* dissect may be running unpriviliged. Implement a minimal custom LUKS header parser
|
||||
* checking integrity protection information. */
|
||||
static int partition_is_luks2_integrity(int part_fd, uint64_t offset, uint64_t size) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_free_ char *json = NULL;
|
||||
sd_json_variant *w;
|
||||
const char *key;
|
||||
struct luks_header_incomplete header;
|
||||
ssize_t sz, json_len;
|
||||
int r;
|
||||
|
||||
assert(part_fd >= 0);
|
||||
|
||||
if (size < LUKS2_FIXED_HDR_SIZE) {
|
||||
log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Partition is too small to contain a LUKS header.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sz = pread(part_fd, &header, sizeof(header), offset);
|
||||
if (sz < 0)
|
||||
return log_error_errno(errno, "Failed to read LUKS header.");
|
||||
if (sz != sizeof(header))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS header.");
|
||||
|
||||
if (memcmp(header.luks_magic, LUKS2_MAGIC, sizeof(header.luks_magic)) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition's magic is not LUKS.");
|
||||
|
||||
if (be16toh(header.version) != 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported LUKS header version: %" PRIu16 ".", be16toh(header.version));
|
||||
|
||||
if (be64toh(header.hdr_len) > size)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS header length exceeds partition size.");
|
||||
|
||||
if (be64toh(header.hdr_len) <= LUKS2_FIXED_HDR_SIZE || offset > UINT64_MAX - be64toh(header.hdr_len))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid LUKS header length: %" PRIu64 ".", be64toh(header.hdr_len));
|
||||
|
||||
json_len = be64toh(header.hdr_len) - LUKS2_FIXED_HDR_SIZE;
|
||||
json = malloc(json_len + 1);
|
||||
if (!json)
|
||||
return -ENOMEM;
|
||||
|
||||
sz = pread(part_fd, json, json_len, offset + LUKS2_FIXED_HDR_SIZE);
|
||||
if (sz < 0)
|
||||
return log_error_errno(errno, "Failed to read LUKS JSON header.");
|
||||
if (sz != json_len)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS JSON header.");
|
||||
json[sz] = '\0';
|
||||
|
||||
r = sd_json_parse(json, /* flags = */ 0, &v, /* reterr_line = */ NULL, /* reterr_column = */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse LUKS JSON header.");
|
||||
|
||||
v = sd_json_variant_by_key(v, "segments");
|
||||
if (!v || !sd_json_variant_is_object(v)) {
|
||||
log_debug("LUKS JSON header lacks 'segments' information, assuming no integrity.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify that all segments have integrity protection */
|
||||
JSON_VARIANT_OBJECT_FOREACH(key, w, v) {
|
||||
struct luks_integrity_data data = {};
|
||||
|
||||
static const sd_json_dispatch_field dispatch_segment[] = {
|
||||
{ "integrity", SD_JSON_VARIANT_OBJECT, integrity_information, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(w, dispatch_segment, SD_JSON_ALLOW_EXTENSIONS, &data);
|
||||
if (r < 0) {
|
||||
log_debug("Failed to get integrity information from LUKS JSON for segment %s, assuming no integrity.", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't require a particular integrity algorithm, everything but 'none' (which shouldn't
|
||||
* be there in the first place but is theoretically possible) works. */
|
||||
if (streq(data.type, "none"))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int image_policy_check_partition_flags(
|
||||
const ImagePolicy *policy,
|
||||
PartitionDesignator designator,
|
||||
@@ -457,7 +565,16 @@ static int dissected_image_probe_filesystems(
|
||||
|
||||
if (streq_ptr(p->fstype, "crypto_LUKS")) {
|
||||
m->encrypted = true;
|
||||
found_flags = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ENCRYPTED; /* found this one, and its definitely encrypted */
|
||||
|
||||
if (p->mount_node_fd >= 0)
|
||||
r = partition_is_luks2_integrity(p->mount_node_fd, /* offset = */ 0, /* size = */ UINT64_MAX);
|
||||
else
|
||||
r = partition_is_luks2_integrity(fd, p->offset, p->size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* found this one, it's definitely encrypted + with or without integrity checking */
|
||||
found_flags = PARTITION_POLICY_UNUSED|(r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED);
|
||||
} else
|
||||
/* found it, but it's definitely not encrypted, hence mask the encrypted flag, but
|
||||
* set all other ways that indicate "present". */
|
||||
@@ -948,8 +1065,14 @@ static int dissect_image(
|
||||
|
||||
if (verity_settings_data_covers(verity, PARTITION_ROOT))
|
||||
found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
|
||||
else
|
||||
found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
|
||||
else if (encrypted) {
|
||||
r = partition_is_luks2_integrity(fd, /* offset = */ 0, /* size = */ UINT64_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
found_flags = r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED;
|
||||
} else
|
||||
found_flags = PARTITION_POLICY_UNPROTECTED;
|
||||
|
||||
r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
|
||||
if (r < 0)
|
||||
@@ -1732,7 +1855,7 @@ static int dissect_image(
|
||||
/* Determine the verity protection level for this partition. */
|
||||
PartitionPolicyFlags found_flags;
|
||||
if (m->partitions[di].found) {
|
||||
found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
|
||||
found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
|
||||
|
||||
PartitionDesignator vi = partition_verity_hash_of(di);
|
||||
if (vi >= 0 && m->partitions[vi].found) {
|
||||
|
||||
+27
-23
@@ -78,7 +78,7 @@ static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPol
|
||||
* all needs no protection, because it *is* the protection */
|
||||
if (partition_verity_hash_to_data(policy->designator) >= 0 ||
|
||||
partition_verity_sig_to_data(policy->designator) >= 0)
|
||||
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED);
|
||||
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY);
|
||||
|
||||
/* if this designator has no verity concept, then mask off verity protection flags */
|
||||
if (partition_verity_hash_of(policy->designator) < 0)
|
||||
@@ -187,6 +187,8 @@ static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
|
||||
return PARTITION_POLICY_SIGNED;
|
||||
if (streq(s, "encrypted"))
|
||||
return PARTITION_POLICY_ENCRYPTED;
|
||||
if (streq(s, "encryptedwithintegrity"))
|
||||
return PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY;
|
||||
if (streq(s, "unprotected"))
|
||||
return PARTITION_POLICY_UNPROTECTED;
|
||||
if (streq(s, "unused"))
|
||||
@@ -423,6 +425,8 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
|
||||
l[m++] = "signed";
|
||||
if (flags & PARTITION_POLICY_ENCRYPTED)
|
||||
l[m++] = "encrypted";
|
||||
if (flags & PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY)
|
||||
l[m++] = "encryptedwithintegrity";
|
||||
if (flags & PARTITION_POLICY_UNPROTECTED)
|
||||
l[m++] = "unprotected";
|
||||
if (flags & PARTITION_POLICY_UNUSED)
|
||||
@@ -863,8 +867,8 @@ const ImagePolicy image_policy_sysext = {
|
||||
* be. */
|
||||
.n_policies = 2,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
@@ -884,7 +888,7 @@ const ImagePolicy image_policy_confext = {
|
||||
* are only interested in the /etc/ tree anyway, and that's really the only place it can be. */
|
||||
.n_policies = 1,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
@@ -901,14 +905,14 @@ const ImagePolicy image_policy_container = {
|
||||
/* For systemd-nspawn containers we use all partitions, with the exception of swap */
|
||||
.n_policies = 8,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
@@ -917,15 +921,15 @@ const ImagePolicy image_policy_host = {
|
||||
/* For the host policy we basically use everything */
|
||||
.n_policies = 9,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
@@ -934,12 +938,12 @@ const ImagePolicy image_policy_service = {
|
||||
/* For RootImage= in services we skip ESP/XBOOTLDR and swap */
|
||||
.n_policies = 6,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
+21
-20
@@ -14,29 +14,30 @@ typedef enum PartitionPolicyFlags {
|
||||
* on. Example: a default policy of "verity+encrypted" certainly makes sense, but for /home/
|
||||
* partitions this gracefully degrades to "encrypted" (as we do not have a concept of verity for
|
||||
* /home/), and so on. */
|
||||
PARTITION_POLICY_VERITY = 1 << 0, /* must exist, activate with verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_SIGNED = 1 << 1, /* must exist, activate with signed verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_ENCRYPTED = 1 << 2, /* must exist, activate with LUKS encryption (applies to any data partition, but not to verity/signature partitions */
|
||||
PARTITION_POLICY_UNPROTECTED = 1 << 3, /* must exist, activate without encryption/verity */
|
||||
PARTITION_POLICY_UNUSED = 1 << 4, /* must exist, don't use */
|
||||
PARTITION_POLICY_ABSENT = 1 << 5, /* must not exist */
|
||||
PARTITION_POLICY_OPEN = PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|
|
||||
PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
PARTITION_POLICY_IGNORE = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
_PARTITION_POLICY_USE_MASK = PARTITION_POLICY_OPEN,
|
||||
PARTITION_POLICY_VERITY = 1 << 0, /* must exist, activate with verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_SIGNED = 1 << 1, /* must exist, activate with signed verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_ENCRYPTED = 1 << 2, /* must exist, activate with LUKS encryption (applies to any data partition, but not to verity/signature partitions */
|
||||
PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY = 1 << 3, /* same as PARTITION_POLICY_ENCRYPTED but also requires integrity checking */
|
||||
PARTITION_POLICY_UNPROTECTED = 1 << 4, /* must exist, activate without encryption/verity */
|
||||
PARTITION_POLICY_UNUSED = 1 << 5, /* must exist, don't use */
|
||||
PARTITION_POLICY_ABSENT = 1 << 6, /* must not exist */
|
||||
PARTITION_POLICY_OPEN = PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|
|
||||
PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
PARTITION_POLICY_IGNORE = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
_PARTITION_POLICY_USE_MASK = PARTITION_POLICY_OPEN,
|
||||
|
||||
PARTITION_POLICY_READ_ONLY_OFF = 1 << 6, /* State of GPT partition flag "read-only" must be on */
|
||||
PARTITION_POLICY_READ_ONLY_ON = 1 << 7,
|
||||
_PARTITION_POLICY_READ_ONLY_MASK = PARTITION_POLICY_READ_ONLY_OFF|PARTITION_POLICY_READ_ONLY_ON,
|
||||
PARTITION_POLICY_GROWFS_OFF = 1 << 8, /* State of GPT partition flag "growfs" must be on */
|
||||
PARTITION_POLICY_GROWFS_ON = 1 << 9,
|
||||
_PARTITION_POLICY_GROWFS_MASK = PARTITION_POLICY_GROWFS_OFF|PARTITION_POLICY_GROWFS_ON,
|
||||
_PARTITION_POLICY_PFLAGS_MASK = _PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
PARTITION_POLICY_READ_ONLY_OFF = 1 << 7, /* State of GPT partition flag "read-only" must be on */
|
||||
PARTITION_POLICY_READ_ONLY_ON = 1 << 8,
|
||||
_PARTITION_POLICY_READ_ONLY_MASK = PARTITION_POLICY_READ_ONLY_OFF|PARTITION_POLICY_READ_ONLY_ON,
|
||||
PARTITION_POLICY_GROWFS_OFF = 1 << 9, /* State of GPT partition flag "growfs" must be on */
|
||||
PARTITION_POLICY_GROWFS_ON = 1 << 10,
|
||||
_PARTITION_POLICY_GROWFS_MASK = PARTITION_POLICY_GROWFS_OFF|PARTITION_POLICY_GROWFS_ON,
|
||||
_PARTITION_POLICY_PFLAGS_MASK = _PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
|
||||
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
|
||||
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
|
||||
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
|
||||
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
} PartitionPolicyFlags;
|
||||
|
||||
assert_cc((_PARTITION_POLICY_USE_MASK | _PARTITION_POLICY_PFLAGS_MASK) >= 0); /* ensure flags don't collide with errno range */
|
||||
|
||||
@@ -99,8 +99,8 @@ TEST_RET(test_image_policy_to_string) {
|
||||
test_policy_equiv("~", image_policy_equiv_deny);
|
||||
test_policy_equiv("=absent", image_policy_equiv_deny);
|
||||
test_policy_equiv("=open", image_policy_equiv_allow);
|
||||
test_policy_equiv("=verity+signed+encrypted+unprotected+unused+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=signed+verity+encrypted+unused+unprotected+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=verity+signed+encrypted+encryptedwithintegrity+unprotected+unused+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=signed+verity+encrypted+encryptedwithintegrity+unused+unprotected+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=ignore", image_policy_equiv_ignore);
|
||||
test_policy_equiv("=absent+unused", image_policy_equiv_ignore);
|
||||
test_policy_equiv("=unused+absent", image_policy_equiv_ignore);
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
|
||||
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
chmod 0755 "$defs"
|
||||
|
||||
echo "*** testcase for systemd-dissect and encrypted partitions ***"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=root
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
Integrity=off
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/encint.img"
|
||||
|
||||
systemd-dissect --validate --image-policy "root=encrypted" "$imgs/encint.img"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=root
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
Integrity=inline
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/encint_int.img"
|
||||
|
||||
systemd-dissect --validate --image-policy "root=encryptedwithintegrity" "$imgs/encint_int.img"
|
||||
@@ -1838,6 +1838,67 @@ EOF
|
||||
cmp "$imgs/disk1.img" "$imgs/disk3.img"
|
||||
}
|
||||
|
||||
_test_luks2_integrity() {
|
||||
local defs imgs output root
|
||||
|
||||
if [[ "$OFFLINE" != "no" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
|
||||
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
|
||||
root="$(mktemp --directory "/var/test-repart.root.XXXXXXXXXX")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs' '$root'" RETURN
|
||||
chmod 0755 "$defs"
|
||||
|
||||
echo "*** testcase for LUKS2 integrity ***"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=root
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
Integrity=inline
|
||||
EOF
|
||||
|
||||
[ -n "$1" ] && echo "IntegrityAlgorithm=$1" >> "$defs/root.conf"
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--seed="$seed" \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/encint.img"
|
||||
|
||||
loop="$(losetup -P --show --find "$imgs/encint.img")"
|
||||
udevadm wait --timeout=60 --settle "${loop:?}p1"
|
||||
|
||||
volume="test-repart-luksint-$RANDOM"
|
||||
dmstatus="$imgs/dmsetup-$RANDOM"
|
||||
|
||||
touch "$imgs/empty-password"
|
||||
|
||||
# the expectation for hmac-sha256 is 'integrity: hmac(sha256)'
|
||||
cryptsetup luksDump "${loop}p1" | grep -q "integrity: $(echo "$1" | sed -r 's/^hmac-(.*)$/hmac(\1)/')"
|
||||
|
||||
cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p1" "$volume"
|
||||
dmsetup status > "$dmstatus"
|
||||
cryptsetup close "$volume"
|
||||
losetup -d "$loop"
|
||||
# Check that there's a dm-integrity entry
|
||||
grep -q "$volume""_dif.* integrity " "$dmstatus"
|
||||
}
|
||||
|
||||
testcase_luks2_integrity() {
|
||||
_test_luks2_integrity ""
|
||||
_test_luks2_integrity "hmac-sha1"
|
||||
_test_luks2_integrity "hmac-sha256"
|
||||
_test_luks2_integrity "hmac-sha512"
|
||||
}
|
||||
|
||||
OFFLINE="yes"
|
||||
run_testcases
|
||||
|
||||
|
||||
@@ -1083,7 +1083,7 @@ check deny no "$name"
|
||||
|
||||
# Let's also test the "image-policy" verb
|
||||
|
||||
systemd-analyze image-policy '*' 2>&1 | grep -F "Long form: =verity+signed+encrypted+unprotected+unused+absent" >/dev/null
|
||||
systemd-analyze image-policy '*' 2>&1 | grep -F "Long form: =verity+signed+encrypted+encryptedwithintegrity+unprotected+unused+absent" >/dev/null
|
||||
systemd-analyze image-policy '-' 2>&1 | grep -F "Long form: =unused+absent" >/dev/null
|
||||
systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -F "Long form: usr=verity:home=encrypted:=unused+absent" >/dev/null
|
||||
systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -e '^home \+encrypted \+' >/dev/null
|
||||
|
||||
Reference in New Issue
Block a user