Skip to content

Commit 93edd39

Browse files
committed
fscrypt: support passing a keyring key to FS_IOC_ADD_ENCRYPTION_KEY
Extend the FS_IOC_ADD_ENCRYPTION_KEY ioctl to allow the raw key to be specified by a Linux keyring key, rather than specified directly. This is useful because fscrypt keys belong to a particular filesystem instance, so they are destroyed when that filesystem is unmounted. Usually this is desired. But in some cases, userspace may need to unmount and re-mount the filesystem while keeping the keys, e.g. during a system update. This requires keeping the keys somewhere else too. The keys could be kept in memory in a userspace daemon. But depending on the security architecture and assumptions, it can be preferable to keep them only in kernel memory, where they are unreadable by userspace. We also can't solve this by going back to the original fscrypt API (where for each file, the master key was looked up in the process's keyring hierarchy) because that caused lots of problems of its own. Therefore, add the ability for FS_IOC_ADD_ENCRYPTION_KEY to accept a Linux keyring key. This solves the problem by allowing userspace to (if needed) save the keys securely in a Linux keyring for re-provisioning, while still using the new fscrypt key management ioctls. This is analogous to how dm-crypt accepts a Linux keyring key, but the key is then stored internally in the dm-crypt data structures rather than being looked up again each time the dm-crypt device is accessed. Use a custom key type "fscrypt-provisioning" rather than one of the existing key types such as "logon". This is strongly desired because it enforces that these keys are only usable for a particular purpose: for fscrypt as input to a particular KDF. Otherwise, the keys could also be passed to any kernel API that accepts a "logon" key with any service prefix, e.g. dm-crypt, UBIFS, or (recently proposed) AF_ALG. This would risk leaking information about the raw key despite it ostensibly being unreadable. Of course, this mistake has already been made for multiple kernel APIs; but since this is a new API, let's do it right. This patch has been tested using an xfstest which I wrote to test it. Link: https://lore.kernel.org/r/20191119222447.226853-1-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@google.com>
1 parent fd69884 commit 93edd39

File tree

3 files changed

+168
-12
lines changed

3 files changed

+168
-12
lines changed

Documentation/filesystems/fscrypt.rst

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,8 @@ follows::
638638
struct fscrypt_add_key_arg {
639639
struct fscrypt_key_specifier key_spec;
640640
__u32 raw_size;
641-
__u32 __reserved[9];
641+
__u32 key_id;
642+
__u32 __reserved[8];
642643
__u8 raw[];
643644
};
644645

@@ -655,6 +656,12 @@ follows::
655656
} u;
656657
};
657658

659+
struct fscrypt_provisioning_key_payload {
660+
__u32 type;
661+
__u32 __reserved;
662+
__u8 raw[];
663+
};
664+
658665
:c:type:`struct fscrypt_add_key_arg` must be zeroed, then initialized
659666
as follows:
660667

@@ -677,9 +684,26 @@ as follows:
677684
``Documentation/security/keys/core.rst``).
678685

679686
- ``raw_size`` must be the size of the ``raw`` key provided, in bytes.
687+
Alternatively, if ``key_id`` is nonzero, this field must be 0, since
688+
in that case the size is implied by the specified Linux keyring key.
689+
690+
- ``key_id`` is 0 if the raw key is given directly in the ``raw``
691+
field. Otherwise ``key_id`` is the ID of a Linux keyring key of
692+
type "fscrypt-provisioning" whose payload is a :c:type:`struct
693+
fscrypt_provisioning_key_payload` whose ``raw`` field contains the
694+
raw key and whose ``type`` field matches ``key_spec.type``. Since
695+
``raw`` is variable-length, the total size of this key's payload
696+
must be ``sizeof(struct fscrypt_provisioning_key_payload)`` plus the
697+
raw key size. The process must have Search permission on this key.
698+
699+
Most users should leave this 0 and specify the raw key directly.
700+
The support for specifying a Linux keyring key is intended mainly to
701+
allow re-adding keys after a filesystem is unmounted and re-mounted,
702+
without having to store the raw keys in userspace memory.
680703

681704
- ``raw`` is a variable-length field which must contain the actual
682-
key, ``raw_size`` bytes long.
705+
key, ``raw_size`` bytes long. Alternatively, if ``key_id`` is
706+
nonzero, then this field is unused.
683707

684708
For v2 policy keys, the kernel keeps track of which user (identified
685709
by effective user ID) added the key, and only allows the key to be
@@ -701,11 +725,16 @@ FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors:
701725

702726
- ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the
703727
caller does not have the CAP_SYS_ADMIN capability in the initial
704-
user namespace
728+
user namespace; or the raw key was specified by Linux key ID but the
729+
process lacks Search permission on the key.
705730
- ``EDQUOT``: the key quota for this user would be exceeded by adding
706731
the key
707732
- ``EINVAL``: invalid key size or key specifier type, or reserved bits
708733
were set
734+
- ``EKEYREJECTED``: the raw key was specified by Linux key ID, but the
735+
key has the wrong type
736+
- ``ENOKEY``: the raw key was specified by Linux key ID, but no key
737+
exists with that ID
709738
- ``ENOTTY``: this type of filesystem does not implement encryption
710739
- ``EOPNOTSUPP``: the kernel was not configured with encryption
711740
support for this filesystem, or the filesystem superblock has not

fs/crypto/keyring.c

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,109 @@ static int add_master_key(struct super_block *sb,
465465
return err;
466466
}
467467

468+
static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep)
469+
{
470+
const struct fscrypt_provisioning_key_payload *payload = prep->data;
471+
472+
if (prep->datalen < sizeof(*payload) + FSCRYPT_MIN_KEY_SIZE ||
473+
prep->datalen > sizeof(*payload) + FSCRYPT_MAX_KEY_SIZE)
474+
return -EINVAL;
475+
476+
if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
477+
payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)
478+
return -EINVAL;
479+
480+
if (payload->__reserved)
481+
return -EINVAL;
482+
483+
prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL);
484+
if (!prep->payload.data[0])
485+
return -ENOMEM;
486+
487+
prep->quotalen = prep->datalen;
488+
return 0;
489+
}
490+
491+
static void fscrypt_provisioning_key_free_preparse(
492+
struct key_preparsed_payload *prep)
493+
{
494+
kzfree(prep->payload.data[0]);
495+
}
496+
497+
static void fscrypt_provisioning_key_describe(const struct key *key,
498+
struct seq_file *m)
499+
{
500+
seq_puts(m, key->description);
501+
if (key_is_positive(key)) {
502+
const struct fscrypt_provisioning_key_payload *payload =
503+
key->payload.data[0];
504+
505+
seq_printf(m, ": %u [%u]", key->datalen, payload->type);
506+
}
507+
}
508+
509+
static void fscrypt_provisioning_key_destroy(struct key *key)
510+
{
511+
kzfree(key->payload.data[0]);
512+
}
513+
514+
static struct key_type key_type_fscrypt_provisioning = {
515+
.name = "fscrypt-provisioning",
516+
.preparse = fscrypt_provisioning_key_preparse,
517+
.free_preparse = fscrypt_provisioning_key_free_preparse,
518+
.instantiate = generic_key_instantiate,
519+
.describe = fscrypt_provisioning_key_describe,
520+
.destroy = fscrypt_provisioning_key_destroy,
521+
};
522+
523+
/*
524+
* Retrieve the raw key from the Linux keyring key specified by 'key_id', and
525+
* store it into 'secret'.
526+
*
527+
* The key must be of type "fscrypt-provisioning" and must have the field
528+
* fscrypt_provisioning_key_payload::type set to 'type', indicating that it's
529+
* only usable with fscrypt with the particular KDF version identified by
530+
* 'type'. We don't use the "logon" key type because there's no way to
531+
* completely restrict the use of such keys; they can be used by any kernel API
532+
* that accepts "logon" keys and doesn't require a specific service prefix.
533+
*
534+
* The ability to specify the key via Linux keyring key is intended for cases
535+
* where userspace needs to re-add keys after the filesystem is unmounted and
536+
* re-mounted. Most users should just provide the raw key directly instead.
537+
*/
538+
static int get_keyring_key(u32 key_id, u32 type,
539+
struct fscrypt_master_key_secret *secret)
540+
{
541+
key_ref_t ref;
542+
struct key *key;
543+
const struct fscrypt_provisioning_key_payload *payload;
544+
int err;
545+
546+
ref = lookup_user_key(key_id, 0, KEY_NEED_SEARCH);
547+
if (IS_ERR(ref))
548+
return PTR_ERR(ref);
549+
key = key_ref_to_ptr(ref);
550+
551+
if (key->type != &key_type_fscrypt_provisioning)
552+
goto bad_key;
553+
payload = key->payload.data[0];
554+
555+
/* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */
556+
if (payload->type != type)
557+
goto bad_key;
558+
559+
secret->size = key->datalen - sizeof(*payload);
560+
memcpy(secret->raw, payload->raw, secret->size);
561+
err = 0;
562+
goto out_put;
563+
564+
bad_key:
565+
err = -EKEYREJECTED;
566+
out_put:
567+
key_ref_put(ref);
568+
return err;
569+
}
570+
468571
/*
469572
* Add a master encryption key to the filesystem, causing all files which were
470573
* encrypted with it to appear "unlocked" (decrypted) when accessed.
@@ -503,18 +606,25 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
503606
if (!valid_key_spec(&arg.key_spec))
504607
return -EINVAL;
505608

506-
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
507-
arg.raw_size > FSCRYPT_MAX_KEY_SIZE)
508-
return -EINVAL;
509-
510609
if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
511610
return -EINVAL;
512611

513612
memset(&secret, 0, sizeof(secret));
514-
secret.size = arg.raw_size;
515-
err = -EFAULT;
516-
if (copy_from_user(secret.raw, uarg->raw, secret.size))
517-
goto out_wipe_secret;
613+
if (arg.key_id) {
614+
if (arg.raw_size != 0)
615+
return -EINVAL;
616+
err = get_keyring_key(arg.key_id, arg.key_spec.type, &secret);
617+
if (err)
618+
goto out_wipe_secret;
619+
} else {
620+
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
621+
arg.raw_size > FSCRYPT_MAX_KEY_SIZE)
622+
return -EINVAL;
623+
secret.size = arg.raw_size;
624+
err = -EFAULT;
625+
if (copy_from_user(secret.raw, uarg->raw, secret.size))
626+
goto out_wipe_secret;
627+
}
518628

519629
switch (arg.key_spec.type) {
520630
case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
@@ -978,8 +1088,14 @@ int __init fscrypt_init_keyring(void)
9781088
if (err)
9791089
goto err_unregister_fscrypt;
9801090

1091+
err = register_key_type(&key_type_fscrypt_provisioning);
1092+
if (err)
1093+
goto err_unregister_fscrypt_user;
1094+
9811095
return 0;
9821096

1097+
err_unregister_fscrypt_user:
1098+
unregister_key_type(&key_type_fscrypt_user);
9831099
err_unregister_fscrypt:
9841100
unregister_key_type(&key_type_fscrypt);
9851101
return err;

include/uapi/linux/fscrypt.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,22 @@ struct fscrypt_key_specifier {
109109
} u;
110110
};
111111

112+
/*
113+
* Payload of Linux keyring key of type "fscrypt-provisioning", referenced by
114+
* fscrypt_add_key_arg::key_id as an alternative to fscrypt_add_key_arg::raw.
115+
*/
116+
struct fscrypt_provisioning_key_payload {
117+
__u32 type;
118+
__u32 __reserved;
119+
__u8 raw[];
120+
};
121+
112122
/* Struct passed to FS_IOC_ADD_ENCRYPTION_KEY */
113123
struct fscrypt_add_key_arg {
114124
struct fscrypt_key_specifier key_spec;
115125
__u32 raw_size;
116-
__u32 __reserved[9];
126+
__u32 key_id;
127+
__u32 __reserved[8];
117128
__u8 raw[];
118129
};
119130

0 commit comments

Comments
 (0)