Skip to content

Commit

Permalink
fscryptctl: add support for adding key by serial (ID)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
a3f committed Aug 6, 2021
1 parent e863642 commit 64ffd59
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 10 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions fscryptctl.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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*

Expand Down
57 changes: 50 additions & 7 deletions fscryptctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
Expand Down Expand Up @@ -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,
Expand All @@ -94,8 +96,8 @@ static void __attribute__((__noreturn__)) usage(FILE *out) {
" fscryptctl <command> [arguments] [options]\n"
"\nCommands:\n"
" fscryptctl add_key <mountpoint>\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 <key identifier> <mountpoint>\n"
" Remove the key with the specified identifier from the specified\n"
" mounted filesystem.\n"
Expand All @@ -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=<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"
Expand All @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down

0 comments on commit 64ffd59

Please sign in to comment.