blob: d8cfd12e9522307626005578c6de40326c9c101d [file] [log] [blame] [raw]
/* 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);