Merge pull request #20902 from tasleson/integritysetup-generator

Add stand-alone dm integrity support
This commit is contained in:
Lennart Poettering
2021-10-15 18:57:17 +02:00
committed by GitHub
22 changed files with 848 additions and 20 deletions
+66
View File
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "integrity-util.h"
#include "extract-word.h"
#include "fileio.h"
#include "path-util.h"
#include "percent-util.h"
static int supported_integrity_algorithm(char *user_supplied) {
if (!STR_IN_SET(user_supplied, "crc32", "crc32c", "sha1", "sha256", "hmac-sha256"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported integrity algorithm (%s)", user_supplied);
return 0;
}
int parse_integrity_options(
const char *options,
uint32_t *ret_activate_flags,
int *ret_percent,
usec_t *ret_commit_time,
char **ret_data_device,
char **ret_integrity_alg) {
int r;
for (;;) {
_cleanup_free_ char *word = NULL;
char *val;
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse options: %m");
if (r == 0)
break;
else if (streq(word, "allow-discards")) {
if (ret_activate_flags)
*ret_activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
} else if ((val = startswith(word, "journal-watermark="))) {
r = parse_percent(val);
if (r < 0)
return log_error_errno(r, "Failed to parse journal-watermark value or value out of range (%s)", val);
if (ret_percent)
*ret_percent = r;
} else if ((val = startswith(word, "journal-commit-time="))) {
usec_t tmp_commit_time;
r = parse_sec(val, &tmp_commit_time);
if (r < 0)
return log_error_errno(r, "Failed to parse journal-commit-time value (%s)", val);
if (ret_commit_time)
*ret_commit_time = tmp_commit_time;
} else if ((val = startswith(word, "data-device="))) {
r = free_and_strdup(ret_data_device, val);
if (r < 0)
return log_oom();
} else if ((val = startswith(word, "integrity-algorithm="))) {
r = free_and_strdup(ret_integrity_alg, val);
if (r < 0)
return log_oom();
r = supported_integrity_algorithm(*ret_integrity_alg);
if (r < 0)
return r;
} else
log_warning("Encountered unknown option '%s', ignoring.", word);
}
return r;
}
+19
View File
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdint.h>
#include "cryptsetup-util.h"
#include "time-util.h"
int parse_integrity_options(
const char *options,
uint32_t *ret_activate_flags,
int *ret_percent,
usec_t *ret_commit_time,
char **ret_data_device,
char **ret_integrity_alg);
#define DM_HMAC_256 "hmac(sha256)"
#define DM_MAX_KEY_SIZE 4096 /* Maximum size of key allowed for dm-integrity */
@@ -0,0 +1,181 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
#include "generator.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "integrity-util.h"
#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "specifier.h"
#include "string-util.h"
#include "unit-name.h"
static const char *arg_dest = NULL;
static const char *arg_integritytab = NULL;
static char *arg_options = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_options, freep);
static int create_disk(
const char *name,
const char *device,
const char *key_file,
const char *options) {
_cleanup_free_ char *n = NULL, *dd = NULL, *e = NULL, *name_escaped = NULL, *key_file_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
char *dmname = NULL;
assert(name);
assert(device);
name_escaped = specifier_escape(name);
if (!name_escaped)
return log_oom();
e = unit_name_escape(name);
if (!e)
return log_oom();
r = unit_name_build("systemd-integritysetup", e, ".service", &n);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = unit_name_from_path(device, ".device", &dd);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, NULL, n, &f);
if (r < 0)
return r;
if (key_file) {
if (!path_is_absolute(key_file))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute file path %s", key_file);
key_file_escaped = specifier_escape(key_file);
if (!key_file_escaped)
return log_oom();
}
if (options) {
r = parse_integrity_options(options, NULL, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
}
fprintf(f,
"[Unit]\n"
"Description=Integrity Setup for %%I\n"
"Documentation=man:integritytab(5) man:systemd-integritysetup-generator(8) man:systemd-integritysetup@.service(8)\n"
"SourcePath=%s\n"
"DefaultDependencies=no\n"
"IgnoreOnIsolate=true\n"
"After=integritysetup-pre.target systemd-udevd-kernel.socket\n"
"Before=blockdev@dev-mapper-%%i.target\n"
"Wants=blockdev@dev-mapper-%%i.target\n"
"Conflicts=umount.target\n"
"Before=integritysetup.target\n"
"BindsTo=%s\n"
"After=%s\n"
"Before=umount.target\n",
arg_integritytab,
dd, dd);
fprintf(f,
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n"
"ExecStart=" ROOTLIBEXECDIR "/systemd-integritysetup attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" ROOTLIBEXECDIR "/systemd-integritysetup detach '%s'\n",
name_escaped, device, empty_to_dash(key_file_escaped), empty_to_dash(options),
name_escaped);
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", n);
r = generator_add_symlink(arg_dest, "integritysetup.target", "requires", n);
if (r < 0)
return r;
dmname = strjoina("dev-mapper-", e, ".device");
return generator_add_symlink(arg_dest, dmname, "requires", n);
}
static int add_integritytab_devices(void) {
_cleanup_fclose_ FILE *f = NULL;
unsigned integritytab_line = 0;
int r;
r = fopen_unlocked(arg_integritytab, "re", &f);
if (r < 0) {
if (errno != ENOENT)
log_error_errno(errno, "Failed to open %s: %m", arg_integritytab);
return 0;
}
for (;;) {
_cleanup_free_ char *line = NULL, *name = NULL, *device_id = NULL, *device_path = NULL, *key_file = NULL, *options = NULL;
char *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", arg_integritytab);
if (r == 0)
break;
integritytab_line++;
l = strstrip(line);
if (!l)
continue;
if (IN_SET(l[0], 0, '#'))
continue;
/* The key file and the options are optional */
r = sscanf(l, "%ms %ms %ms %ms", &name, &device_id, &key_file, &options);
if (!IN_SET(r, 2, 3, 4)) {
log_error("Failed to parse %s:%u, ignoring.", l, integritytab_line);
continue;
}
device_path = fstab_node_to_udev_node(device_id);
if (!device_path) {
log_error("Failed to find device %s:%u, ignoring.", device_id, integritytab_line);
continue;
}
r = create_disk(name, device_path, empty_or_dash_to_null(key_file), empty_or_dash_to_null(options));
if (r < 0)
return r;
}
return 0;
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
assert_se(arg_dest = dest);
arg_integritytab = getenv("SYSTEMD_INTEGRITYTAB") ?: "/etc/integritytab";
return add_integritytab_devices();
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
+197
View File
@@ -0,0 +1,197 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include "alloc-util.h"
#include "cryptsetup-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "integrity-util.h"
#include "log.h"
#include "main-func.h"
#include "memory-util.h"
#include "path-util.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "string-util.h"
#include "terminal-util.h"
static uint32_t arg_activate_flags;
static int arg_percent;
static usec_t arg_commit_time;
static char *arg_existing_data_device;
static char *arg_integrity_algorithm;
STATIC_DESTRUCTOR_REGISTER(arg_existing_data_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_integrity_algorithm, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-integritysetup@.service", "8", &link);
if (r < 0)
return log_oom();
printf("%s attach VOLUME DEVICE [HMAC_KEY_FILE|-] [OPTIONS]\n"
"%s detach VOLUME\n\n"
"Attach or detach an integrity protected block device.\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
program_invocation_short_name,
link);
return 0;
}
static int load_key_file(
const char *key_file,
void **ret_key_file_contents,
size_t *ret_key_file_size) {
int r;
_cleanup_(erase_and_freep) char *tmp_key_file_contents = NULL;
size_t tmp_key_file_size;
if (!path_is_absolute(key_file))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute path: %s", key_file);
r = read_full_file_full(
AT_FDCWD, key_file, UINT64_MAX, DM_MAX_KEY_SIZE,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET|READ_FULL_FILE_FAIL_WHEN_LARGER,
NULL,
&tmp_key_file_contents, &tmp_key_file_size);
if (r < 0)
return log_error_errno(r, "Failed to process key file: %m");
if (ret_key_file_contents && ret_key_file_size) {
*ret_key_file_contents = TAKE_PTR(tmp_key_file_contents);
*ret_key_file_size = tmp_key_file_size;
}
return 0;
}
static const char *integrity_algorithm_select(const void *key_file_buf) {
/* To keep a bit of sanity for end users, the subset of integrity
algorithms we support will match what is used in integritysetup */
if (arg_integrity_algorithm) {
if (streq("hmac-sha256", arg_integrity_algorithm))
return DM_HMAC_256;
return arg_integrity_algorithm;
} else if (key_file_buf)
return DM_HMAC_256;
return "crc32c";
}
static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
char *action, *volume;
if (argc <= 1 ||
strv_contains(strv_skip(argv, 1), "--help") ||
strv_contains(strv_skip(argv, 1), "-h") ||
streq(argv[1], "help"))
return help();
if (argc < 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments.");
action = argv[1];
volume = argv[2];
log_setup();
cryptsetup_enable_logging(NULL);
umask(0022);
if (streq(action, "attach")) {
/* attach name device optional_key_file optional_options */
crypt_status_info status;
_cleanup_(erase_and_freep) void *key_buf = NULL;
const char *device, *key_file, *options;
size_t key_buf_size = 0;
if (argc < 4)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least three arguments.");
if (argc > 6)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach has a maximum of five arguments.");
device = argv[3];
key_file = (argc > 4) ? empty_or_dash_to_null(argv[4]) : NULL;
options = (argc > 5) ? empty_or_dash_to_null(argv[5]) : NULL;
if (key_file) {
r = load_key_file(key_file, &key_buf, &key_buf_size);
if (r < 0)
return r;
}
if (options) {
r = parse_integrity_options(options, &arg_activate_flags, &arg_percent,
&arg_commit_time, &arg_existing_data_device, &arg_integrity_algorithm);
if (r < 0)
return r;
}
r = crypt_init(&cd, device);
if (r < 0)
return log_error_errno(r, "Failed to open integrity device %s: %m", device);
cryptsetup_enable_logging(cd);
status = crypt_status(cd, volume);
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
log_info("Volume %s already active.", volume);
return 0;
}
if (!isempty(arg_existing_data_device)) {
r = crypt_init_data_device(&cd, device, arg_existing_data_device);
if (r < 0)
return log_error_errno(r, "Failed to add separate data device: %m");
}
r = crypt_load(cd,
CRYPT_INTEGRITY,
&(struct crypt_params_integrity) {
.journal_watermark = arg_percent,
.journal_commit_time = DIV_ROUND_UP(arg_commit_time, USEC_PER_SEC),
.integrity = integrity_algorithm_select(key_buf),
});
if (r < 0)
return log_error_errno(r, "Failed to load integrity superblock: %m");
r = crypt_activate_by_volume_key(cd, volume, key_buf, key_buf_size, arg_activate_flags);
if (r < 0)
return log_error_errno(r, "Failed to set up integrity device: %m");
} else if (streq(action, "detach")) {
if (argc > 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach has a maximum of two arguments.");
r = crypt_init_by_name(&cd, volume);
if (r == -ENODEV)
return 0;
if (r < 0)
return log_error_errno(r, "crypt_init_by_name() failed: %m");
cryptsetup_enable_logging(cd);
r = crypt_deactivate(cd, volume);
if (r < 0)
return log_error_errno(r, "Failed to deactivate: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", action);
return 0;
}
DEFINE_MAIN_FUNCTION(run);
+1 -1
View File
@@ -100,7 +100,7 @@ static int create_device(void) {
fprintf(f,
"[Unit]\n"
"Description=Integrity Protection Setup for %%I\n"
"Description=Verity Protection Setup for %%I\n"
"Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
"SourcePath=/proc/cmdline\n"
"DefaultDependencies=no\n"
+1 -1
View File
@@ -30,7 +30,7 @@ static int help(void) {
printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
"%s detach VOLUME\n\n"
"Attach or detach an integrity protected block device.\n"
"Attach or detach a verity protected block device.\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
program_invocation_short_name,