diff options
Diffstat (limited to 'src/integritysetup/integritysetup.c')
-rw-r--r-- | src/integritysetup/integritysetup.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/integritysetup/integritysetup.c b/src/integritysetup/integritysetup.c new file mode 100644 index 0000000..a602886 --- /dev/null +++ b/src/integritysetup/integritysetup.c @@ -0,0 +1,203 @@ +/* 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 "parse-util.h" +#include "path-util.h" +#include "pretty-print.h" +#include "process-util.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; + char *verb, *volume; + int r; + + if (argv_looks_like_help(argc, argv)) + return help(); + + if (argc < 3) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments."); + + verb = argv[1]; + volume = argv[2]; + + log_setup(); + + cryptsetup_enable_logging(NULL); + + umask(0022); + + if (streq(verb, "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 = mangle_none(argc > 4 ? argv[4] : NULL); + options = mangle_none(argc > 5 ? argv[5] : 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"); + + } else if (streq(verb, "detach")) { + + if (argc > 3) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach has a maximum of two arguments."); + + 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"); + + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", verb); + + return 0; +} + +DEFINE_MAIN_FUNCTION(run); |