From 64ffd5903296a92e643d1f45596b32433310d086 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 25 Jul 2021 21:56:10 +0200 Subject: [PATCH] fscryptctl: add support for adding key by serial (ID) Since Linux commit 93edd392ca ("fscrypt: support passing a keyring key to FS_IOC_ADD_ENCRYPTION_KEY"), it's possible to pass the key ID of a "fscrypt-provisioning" key that Linux should retrieve the raw key material from instead of passing it directly from userspace. This is useful to add fscrypt keys after unmounting and re-mounting. It would also prove useful should additional key types like trusted keys be allowed in future. Thus add a new --serial parameter to add_key to facilitate this. --serial was chosen over --id to avoid confusion with the KEY_IDENTIFIER used in the remove_key, key_status and set_policy documentation, which it is not interchangeable with. Signed-off-by: Ahmad Fatoum --- README.md | 7 +++++- fscryptctl.1.md | 10 +++++++-- fscryptctl.c | 57 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 404321c..d17e2e2 100644 --- a/README.md +++ b/README.md @@ -74,12 +74,17 @@ tips](https://github.com/google/fscrypt#getting-encryption-not-enabled-on-an-ext For full usage details, see the manual page (`man fscryptctl`), or alternatively run `fscryptctl --help`. -The `add_key` command accepts the encryption key in binary on standard input. +The `add_key` command, by default, accepts the encryption key in binary on +standard input. It is critical that this be a real cryptographic key (and not a passphrase, for example), since `fscryptctl` doesn't do key stretching itself. Obviously, don't store the raw encryption key alongside the encrypted files. (If you need support for passphrases, use `fscrypt` instead of `fscryptctl`.) +Alternatively, `add_key --serial=$serial` will instruct the kernel to +extract the kernel material from an existing key of type "fscrypt-provisioning" +with the specified $serial (The ID returned and used by keyctl(1)). + After running the `add_key` command to add an encryption key to a filesystem, you can use the `set_policy` command to create an encrypted directory on that filesystem. The encryption key is specified by the 32-character hex "key diff --git a/fscryptctl.1.md b/fscryptctl.1.md index 0654b79..2bc9224 100644 --- a/fscryptctl.1.md +++ b/fscryptctl.1.md @@ -40,7 +40,7 @@ feature, see the references at the end of this page. # SUBCOMMANDS -## **fscryptctl add_key** *MOUNTPOINT* +## **fscryptctl add_key** [*OPTION*..] *MOUNTPOINT* Add an encryption key to the given mounted filesystem. This will "unlock" any files and directories that are protected by the given key on the given @@ -54,7 +54,13 @@ If successful, **fscryptctl add_key** will print the key identifier of the newly added key; this will be a 32-character hex string which can be passed to other **fscryptctl** commands. -**fscryptctl add_key** does not accept any options. +Options accepted by **fscryptctl add_key**: + +**\-\-serial**=*SERIAL* +: Don't read raw key material from standard input. Instead, instruct the + kernel to extract key material from an existing key of type + `fscrypt-provisioning` with specified SERIAL. SERIAL is the ID returned + and used by keyctl(1). ## **fscryptctl remove_key** [*OPTION*...] *KEY_IDENTIFIER* *MOUNTPOINT* diff --git a/fscryptctl.c b/fscryptctl.c index 4dc1479..96c673a 100644 --- a/fscryptctl.c +++ b/fscryptctl.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,7 @@ static const char *const mode_strings[] = { static const int padding_values[] = {4, 8, 16, 32}; enum { + OPT_KEY_SERIAL, OPT_ALL_USERS, OPT_CONTENTS, OPT_DIRECT_KEY, @@ -94,8 +96,8 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " fscryptctl [arguments] [options]\n" "\nCommands:\n" " fscryptctl add_key \n" - " Read a key from stdin, add it to the specified mounted filesystem,\n" - " and print its identifier.\n" + " Add a key add to the specified mounted filesystem and print its\n" + " identifier. Raw key is read from stdin by default.\n" " fscryptctl remove_key \n" " Remove the key with the specified identifier from the specified\n" " mounted filesystem.\n" @@ -112,6 +114,10 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " print this help screen\n" " -v, --version\n" " print the version of fscrypt\n" + " add_key\n" + " --serial=\n" + " Instead of reading raw key material over stdin, use the\n" + " payload of an existing \"fscrypt-provisioning\" key.\n" " remove_key\n" " --all-users\n" " force-remove all users' claims to the key (requires root)\n" @@ -129,7 +135,10 @@ static void __attribute__((__noreturn__)) usage(FILE *out) { " --iv-ino-lblk-32\n" " optimize for eMMC inline crypto hardware (not recommended)\n" "\nNotes:\n" - " Keys are identified by 32-character hex strings (key identifiers).\n" + " fscrypt keys are identified by 32-character hex strings\n" + " (key identifiers).\n" + "\n" + " Key serials are specified as decimal integers that fit into 31 bits.\n" "\n" " Raw keys are given on stdin in binary and usually must be 64 bytes.\n" "\n" @@ -267,6 +276,16 @@ static bool hex_to_bytes(const char *hex, uint8_t *bytes, size_t num_bytes) { return true; } +// Converts a decimal string to a kernel key_serial_t +static int32_t str_to_keyserial(const char *dec) { + char *endp; + unsigned long ret = strtoul(dec, &endp, 10); + if (ret < 1 || ret > INT32_MAX || *endp) { + return -1; + } + return ret; +} + // Builds a 'struct fscrypt_key_specifier' for passing to the kernel, given a // key identifier hex string. static bool build_key_specifier(const char *identifier_hex, @@ -372,7 +391,27 @@ static bool set_policy(const char *path, // ----------------------------------------------------------------------------- static int cmd_add_key(int argc, char *const argv[]) { - handle_no_options(&argc, &argv); + int32_t serial = 0; + + static const struct option add_key_options[] = { + {"serial", required_argument, NULL, OPT_KEY_SERIAL}, {NULL, 0, NULL, 0}}; + + int ch; + while ((ch = getopt_long(argc, argv, "", add_key_options, NULL)) != -1) { + switch (ch) { + case OPT_KEY_SERIAL: + serial = str_to_keyserial(optarg); + if (serial < 0) { + fputs("error: must specify valid key id\n", stderr); + return EXIT_FAILURE; + } + break; + default: + usage(stderr); + } + } + argc -= optind; + argv += optind; if (argc != 1) { fputs("error: must specify a single mountpoint\n", stderr); return EXIT_FAILURE; @@ -387,9 +426,13 @@ static int cmd_add_key(int argc, char *const argv[]) { } int status = EXIT_FAILURE; - arg->raw_size = read_key(arg->raw); - if (arg->raw_size == 0) { - goto cleanup; + if (serial) { + arg->key_id = serial; + } else { + arg->raw_size = read_key(arg->raw); + if (arg->raw_size == 0) { + goto cleanup; + } } arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;