mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
201 lines
6.6 KiB
C
201 lines
6.6 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "argv-util.h"
|
|
#include "cryptsetup-util.h"
|
|
#include "fileio.h"
|
|
#include "integrity-util.h"
|
|
#include "log.h"
|
|
#include "main-func.h"
|
|
#include "path-util.h"
|
|
#include "pretty-print.h"
|
|
#include "string-util.h"
|
|
#include "time-util.h"
|
|
#include "verbs.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 verb_attach(int argc, char *argv[], void *userdata) {
|
|
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
|
crypt_status_info status;
|
|
_cleanup_(erase_and_freep) void *key_buf = NULL;
|
|
size_t key_buf_size = 0;
|
|
int r;
|
|
|
|
/* attach name device optional_key_file optional_options */
|
|
|
|
assert(argc >= 3 && argc <= 5);
|
|
|
|
const char *volume = argv[1],
|
|
*device = argv[2],
|
|
*key_file = mangle_none(argc > 3 ? argv[3] : NULL),
|
|
*options = mangle_none(argc > 4 ? argv[4] : NULL);
|
|
|
|
if (!filename_is_valid(volume))
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
|
|
if (!isempty(arg_existing_data_device)) {
|
|
r = crypt_set_data_device(cd, arg_existing_data_device);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to add separate data device: %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");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int verb_detach(int argc, char *argv[], void *userdata) {
|
|
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
|
int r;
|
|
|
|
assert(argc == 2);
|
|
|
|
const char *volume = argv[1];
|
|
|
|
if (!filename_is_valid(volume))
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
|
|
|
|
r = crypt_init_by_name(&cd, volume);
|
|
if (r == -ENODEV) {
|
|
log_info("Volume %s already inactive.", volume);
|
|
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");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run(int argc, char *argv[]) {
|
|
if (argv_looks_like_help(argc, argv))
|
|
return help();
|
|
|
|
log_setup();
|
|
|
|
cryptsetup_enable_logging(NULL);
|
|
|
|
umask(0022);
|
|
|
|
static const Verb verbs[] = {
|
|
{ "attach", 3, 5, 0, verb_attach },
|
|
{ "detach", 2, 2, 0, verb_detach },
|
|
{}
|
|
};
|
|
|
|
return dispatch_verb(argc, argv, verbs, NULL);
|
|
}
|
|
|
|
DEFINE_MAIN_FUNCTION(run);
|