@@ -469,8 +469,14 @@ static int __init fscrypt_init(void)
if (err)
goto fail_free_info;
+ err = register_key_type(&key_type_fscrypt_mk_user);
+ if (err)
+ goto fail_unregister_fscrypt_mk_type;
+
return 0;
+fail_unregister_fscrypt_mk_type:
+ unregister_key_type(&key_type_fscrypt_mk);
fail_free_info:
kmem_cache_destroy(fscrypt_info_cachep);
fail_free_ctx:
@@ -494,6 +500,7 @@ static void __exit fscrypt_exit(void)
kmem_cache_destroy(fscrypt_ctx_cachep);
kmem_cache_destroy(fscrypt_info_cachep);
unregister_key_type(&key_type_fscrypt_mk);
+ unregister_key_type(&key_type_fscrypt_mk_user);
fscrypt_essiv_cleanup();
}
@@ -235,6 +235,7 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
/* keyinfo.c */
extern struct key_type key_type_fscrypt_mk;
+extern struct key_type key_type_fscrypt_mk_user;
extern void __exit fscrypt_essiv_cleanup(void);
/* policy.c */
@@ -246,9 +246,16 @@ struct fscrypt_master_key {
* FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or
* FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
*
- * Locking: protected by key->sem.
+ * Locking: protected by key->sem (outer) and mk_secret_sem (inner).
+ * The reason for two locks is that key->sem also protects modifying
+ * mk_users, which ranks it above the semaphore for the keyring key
+ * type, which is in turn above page faults (via keyring_read). But
+ * sometimes filesystems call fscrypt_get_encryption_info() from within
+ * a transaction, which ranks it below page faults. So we need a
+ * separate lock which protects *just* mk_secret, not also mk_users.
*/
struct fscrypt_master_key_secret mk_secret;
+ struct rw_semaphore mk_secret_sem;
/*
* For v1 policy keys: an arbitrary key descriptor which was assigned by
@@ -258,6 +265,19 @@ struct fscrypt_master_key {
*/
struct fscrypt_key_specifier mk_spec;
+ /*
+ * Keyring which contains a key of type 'key_type_fscrypt_mk_user' for
+ * each user who has added this key. Normally there would be just one
+ * user who adds a particular key, but it's possible that multiple users
+ * would add the same key, and we don't want to allow one user to remove
+ * it before the others want it removed too.
+ *
+ * This is NULL for v1 policy keys.
+ *
+ * Locking: protected by key->sem.
+ */
+ struct key *mk_users;
+
/*
* Length of ->mk_decrypted_inodes, plus one if mk_secret is present.
* Once this goes to 0, the master key is removed from ->s_master_keys.
@@ -314,6 +334,7 @@ static void move_master_key_secret(struct fscrypt_master_key_secret *dst,
static void free_master_key(struct fscrypt_master_key *mk)
{
wipe_master_key_secret(&mk->mk_secret);
+ key_put(mk->mk_users);
kzfree(mk);
}
@@ -353,9 +374,42 @@ struct key_type key_type_fscrypt_mk = {
.describe = fscrypt_key_describe,
};
+static int fscrypt_mk_user_key_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
+{
+ /*
+ * We just charge FSCRYPT_MAX_KEY_SIZE bytes to the user's key quota for
+ * each key, regardless of the exact key size. The amount of memory
+ * actually used is greater than the size of the raw key anyway.
+ */
+ return key_payload_reserve(key, FSCRYPT_MAX_KEY_SIZE);
+}
+
+static void fscrypt_mk_user_key_describe(const struct key *key,
+ struct seq_file *m)
+{
+ seq_puts(m, key->description);
+}
+
+/*
+ * Type of key in ->mk_users. Each key of this type represents a particular
+ * user who has added a particular master key.
+ *
+ * Note that the name of this key type really should be something like
+ * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen
+ * mainly for simplicity of presentation in /proc/keys when read by a non-root
+ * user. And it is expected to be rare that a key is actually added by multiple
+ * users, since users should keep their encryption keys confidential.
+ */
+struct key_type key_type_fscrypt_mk_user = {
+ .name = ".fscrypt",
+ .instantiate = fscrypt_mk_user_key_instantiate,
+ .describe = fscrypt_mk_user_key_describe,
+};
+
/*
- * Search ->s_master_keys. Note that we mark the keyring reference as
- * "possessed" so that we can use the KEY_POS_SEARCH permission.
+ * Search either ->s_master_keys or ->mk_users. Note that we mark the keyring
+ * reference as "possessed" so that we can use the KEY_POS_SEARCH permission.
*/
static struct key *search_fscrypt_keyring(struct key *keyring,
struct key_type *type,
@@ -378,6 +432,13 @@ static struct key *search_fscrypt_keyring(struct key *keyring,
#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1)
+#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \
+ (sizeof("fscrypt-") - 1 + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \
+ sizeof("-users") - 1 + 1)
+
+#define FSCRYPT_MK_USER_DESCRIPTION_SIZE \
+ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + sizeof(".uid.") - 1 + 10 + 1)
+
static void format_fs_keyring_description(
char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE],
const struct super_block *sb)
@@ -393,6 +454,23 @@ static void format_mk_description(
master_key_spec_len(mk_spec), mk_spec->max_specifier);
}
+static void format_mk_users_keyring_description(
+ char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE],
+ const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE])
+{
+ sprintf(description, "fscrypt-%*phN-users",
+ FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier);
+}
+
+static void format_mk_user_description(
+ char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE],
+ const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE])
+{
+
+ sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE,
+ mk_identifier, __kuid_val(current_fsuid()));
+}
+
/*
* Find the specified master key in ->s_master_keys.
* Returns ERR_PTR(-ENOKEY) if not found.
@@ -413,6 +491,80 @@ static struct key *find_master_key(struct super_block *sb,
description);
}
+/*
+ * Find the current user's key in the master key's ->mk_users.
+ * Returns ERR_PTR(-ENOKEY) if not found.
+ */
+static struct key *find_master_key_user(struct fscrypt_master_key *mk)
+{
+ char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE];
+
+ format_mk_user_description(description, mk->mk_spec.identifier);
+ return search_fscrypt_keyring(mk->mk_users, &key_type_fscrypt_mk_user,
+ description);
+}
+
+static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk)
+{
+ char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE];
+ struct key *mk_users;
+
+ format_mk_users_keyring_description(description,
+ mk->mk_spec.identifier);
+
+ mk_users = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+ if (IS_ERR(mk_users))
+ return PTR_ERR(mk_users);
+
+ mk->mk_users = mk_users;
+ return 0;
+}
+
+/*
+ * Give the current user a key in ->mk_users. This charges the user's quota and
+ * marks the master key as added by the current user, so that it cannot be
+ * removed by another user with the key.
+ */
+static int add_master_key_user(struct fscrypt_master_key *mk)
+{
+ char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE];
+ struct key *mk_user;
+ int err;
+
+ format_mk_user_description(description, mk->mk_spec.identifier);
+ mk_user = key_alloc(&key_type_fscrypt_mk_user, description,
+ current_fsuid(), current_gid(), current_cred(),
+ KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL);
+ if (IS_ERR(mk_user))
+ return PTR_ERR(mk_user);
+
+ err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL);
+ key_put(mk_user);
+ return err;
+}
+
+/*
+ * Remove the current user's key from ->mk_users, if present.
+ */
+static int remove_master_key_user(struct fscrypt_master_key *mk)
+{
+ struct key *mk_user;
+ int err;
+
+ mk_user = find_master_key_user(mk);
+ if (IS_ERR(mk_user)) {
+ if (mk_user != ERR_PTR(-ENOKEY))
+ return PTR_ERR(mk_user);
+ return 0;
+ }
+ err = key_unlink(mk->mk_users, mk_user);
+ key_put(mk_user);
+ return err;
+}
+
static struct key *
allocate_master_key(struct fscrypt_master_key_secret *secret,
const struct fscrypt_key_specifier *mk_spec)
@@ -429,16 +581,36 @@ allocate_master_key(struct fscrypt_master_key_secret *secret,
mk->mk_spec = *mk_spec;
move_master_key_secret(&mk->mk_secret, secret);
+ init_rwsem(&mk->mk_secret_sem);
refcount_set(&mk->mk_refcount, 1); /* secret is present */
INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
spin_lock_init(&mk->mk_decrypted_inodes_lock);
+ if (mk_spec->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR) {
+ err = allocate_master_key_users_keyring(mk);
+ if (err) {
+ key = ERR_PTR(err);
+ goto out_free_mk;
+ }
+
+ err = add_master_key_user(mk);
+ if (err) {
+ key = ERR_PTR(err);
+ goto out_free_mk;
+ }
+ }
+
+ /*
+ * Note that we don't charge this key to anyone's quota since it's owned
+ * by root, and the keys in ->mk_users are charged instead.
+ */
format_mk_description(description, mk_spec);
key = key_alloc(&key_type_fscrypt_mk, description,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
KEY_POS_SEARCH | KEY_USR_SEARCH |
- KEY_USR_READ | KEY_USR_VIEW, 0, NULL);
+ KEY_USR_READ | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(key))
goto out_free_mk;
@@ -510,12 +682,31 @@ static int add_master_key(struct super_block *sb,
goto out_put_key;
} else {
struct fscrypt_master_key *mk = key->payload.data[0];
+ struct key *mk_user;
bool rekey;
/* Found the key in ->s_master_keys */
down_write(&key->sem);
+ /*
+ * If the current user is already in ->mk_users, then there's
+ * nothing to do.
+ */
+ if (mk->mk_users) {
+ mk_user = find_master_key_user(mk);
+ if (mk_user != ERR_PTR(-ENOKEY)) {
+ up_write(&key->sem);
+ if (IS_ERR(mk_user)) {
+ err = PTR_ERR(mk_user);
+ } else {
+ key_put(mk_user);
+ err = 0;
+ }
+ goto out_put_key;
+ }
+ }
+
/*
* Take a reference if we'll be re-adding ->mk_secret. If we
* couldn't take a reference, then the key is being removed from
@@ -531,9 +722,24 @@ static int add_master_key(struct super_block *sb,
goto retry;
}
+ /* Add the current user to ->mk_users */
+ if (mk->mk_users) {
+ err = add_master_key_user(mk);
+ if (err) {
+ up_write(&key->sem);
+ if (rekey &&
+ refcount_dec_and_test(&mk->mk_refcount))
+ key_invalidate(key);
+ goto out_put_key;
+ }
+ }
+
/* Re-add the secret key material if needed */
- if (rekey)
+ if (rekey) {
+ down_write(&mk->mk_secret_sem);
move_master_key_secret(&mk->mk_secret, secret);
+ up_write(&mk->mk_secret_sem);
+ }
up_write(&key->sem);
}
err = 0;
@@ -548,6 +754,23 @@ static int add_master_key(struct super_block *sb,
* Add a master encryption key to the filesystem, causing all files which were
* encrypted with it to appear "unlocked" (decrypted) when accessed. The key
* can be removed later by FS_IOC_REMOVE_ENCRYPTION_KEY.
+ *
+ * When adding a key for use by v1 encryption policies, this ioctl is
+ * privileged, and userspace must provide the 'key_descriptor'.
+ *
+ * When adding a key for use by v2+ encryption policies, this ioctl is
+ * unprivileged. This is needed, in general, to allow non-root users to use
+ * encryption without encountering the visibility problems of process-subscribed
+ * keyrings and the inability to properly remove keys. This works by having
+ * each key identified by its cryptographically secure hash --- the
+ * 'key_identifier'. The cryptographic hash ensures that a malicious user
+ * cannot add the wrong key for a given identifier. Furthermore, each added key
+ * is charged to the appropriate user's quota for the keyrings service, which
+ * prevents a malicious user from adding too many keys. Finally, we forbid a
+ * user from removing a key while other users have added it too, which prevents
+ * a user who knows another user's key from causing a denial-of-service by
+ * removing it at an inopportune time. (We tolerate that a user who knows a key
+ * can prevent other users from removing it.)
*/
int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
{
@@ -571,9 +794,6 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
if (!valid_key_spec(&arg.key_spec))
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
memset(&secret, 0, sizeof(secret));
secret.size = arg.raw_size;
err = -EFAULT;
@@ -613,6 +833,15 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
arg.key_spec.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE))
goto out_wipe_secret;
+ } else {
+ /*
+ * Only root can add keys that are identified by an arbitrary
+ * descriptor rather than by a cryptographic hash --- since
+ * otherwise a malicious user could add the wrong key.
+ */
+ err = -EACCES;
+ if (!capable(CAP_SYS_ADMIN))
+ goto out_wipe_secret;
}
err = add_master_key(sb, &secret, &arg.key_spec);
@@ -744,7 +973,9 @@ static int try_to_lock_encrypted_files(struct super_block *sb,
}
/*
- * Try to remove an fscrypt master encryption key.
+ * Try to remove an fscrypt master encryption key. If other users have also
+ * added the key, we'll remove the current user's usage of the key, then return
+ * -EUSERS. Otherwise we'll continue on and try to actually remove the key.
*
* First we wipe the actual master key secret from memory, so that no more
* inodes can be unlocked with it. Then, we try to evict all cached inodes that
@@ -775,13 +1006,33 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg)
if (copy_from_user(&arg, uarg, sizeof(arg)))
return -EFAULT;
- if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))
+ if (arg.flags & ~FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS)
+ return -EINVAL;
+
+ if (arg.reserved1)
+ return -EINVAL;
+
+ if (memchr_inv(arg.reserved2, 0, sizeof(arg.reserved2)))
return -EINVAL;
if (!valid_key_spec(&arg.key_spec))
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
+ /*
+ * Only root can request that the key be removed no matter how many
+ * user(s) have added it.
+ */
+ if ((arg.flags & FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ /*
+ * Only root can remove keys that are identified by an arbitrary
+ * descriptor rather than by a cryptographic hash --- since only root
+ * can add such keys.
+ */
+ if (arg.key_spec.type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER &&
+ !capable(CAP_SYS_ADMIN))
return -EACCES;
key = find_master_key(sb, &arg.key_spec);
@@ -790,10 +1041,34 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg)
mk = key->payload.data[0];
down_write(&key->sem);
+
+ if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) {
+ if (arg.flags & FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS)
+ err = keyring_clear(mk->mk_users);
+ else
+ err = remove_master_key_user(mk);
+ if (err) {
+ up_write(&key->sem);
+ goto out_put_key;
+ }
+ if (mk->mk_users->keys.nr_leaves_on_tree != 0) {
+ /*
+ * Other users have still added the key too. We removed
+ * the current user's usage of the key if there was one,
+ * but we still can't remove the key itself.
+ */
+ err = -EUSERS;
+ up_write(&key->sem);
+ goto out_put_key;
+ }
+ }
+
dead = false;
if (is_master_key_secret_present(&mk->mk_secret)) {
+ down_write(&mk->mk_secret_sem);
wipe_master_key_secret(&mk->mk_secret);
dead = refcount_dec_and_test(&mk->mk_refcount);
+ up_write(&mk->mk_secret_sem);
}
up_write(&key->sem);
if (dead) {
@@ -802,6 +1077,7 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg)
} else {
err = try_to_lock_encrypted_files(sb, mk);
}
+out_put_key:
key_put(key);
return err;
}
@@ -818,6 +1094,15 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key);
* regular file in it (which can confuse the "incompletely removed" state with
* absent or present).
*
+ * In addition, for v2 policy keys we allow applications to determine, via
+ * ->status_flags and ->user_count, whether the key has been added by the
+ * current user, by other users, or by both. Most applications should not need
+ * this, since ordinarily only one user should know a given key. However, if a
+ * secret key is shared by multiple users, applications may wish to add an
+ * already-present key to prevent other users from removing it. This ioctl can
+ * be used to check whether that really is the case before the work is done to
+ * add the key --- which might e.g. require prompting the user for a passphrase.
+ *
* Note: this ioctl only works with keys added to the filesystem-level keyring.
* It does *not* work with keys added via the old mechanism which involved
* process-subscribed keyrings.
@@ -839,6 +1124,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg)
if (!valid_key_spec(&arg.key_spec))
return -EINVAL;
+ arg.status_flags = 0;
+ arg.user_count = 0;
arg.reserved2 = 0;
memset(arg.reserved3, 0, sizeof(arg.reserved3));
@@ -860,6 +1147,19 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg)
}
arg.status = FSCRYPT_KEY_STATUS_PRESENT;
+ if (mk->mk_users) {
+ struct key *mk_user;
+
+ arg.user_count = mk->mk_users->keys.nr_leaves_on_tree;
+ mk_user = find_master_key_user(mk);
+ if (!IS_ERR(mk_user)) {
+ arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF;
+ key_put(mk_user);
+ } else if (mk_user != ERR_PTR(-ENOKEY)) {
+ err = PTR_ERR(mk_user);
+ goto out_release_key;
+ }
+ }
err = 0;
out_release_key:
up_read(&key->sem);
@@ -1029,11 +1329,12 @@ static int find_and_derive_key_legacy(const struct inode *inode,
* Find the master key, then derive the inode's actual encryption key.
*
* If the master key is found in the filesystem-level keyring, then the
- * corresponding 'struct key' is returned read-locked in *master_key_ret. This
- * is needed because we need to hold the semaphore until we link the new
- * fscrypt_info into ->mk_decrypted_inodes, but in the case where multiple tasks
- * are racing to set up an inode's ->i_crypt_info, only the winner should link
- * its fscrypt_info into ->mk_decrypted_inodes.
+ * corresponding 'struct key' is returned in *master_key_ret with
+ * ->mk_secret_sem read-locked. This is needed because we need to hold
+ * ->mk_secret_sem until we link the new fscrypt_info into
+ * ->mk_decrypted_inodes, but in the case where multiple tasks are racing to set
+ * up an inode's ->i_crypt_info, only the winner should link its fscrypt_info
+ * into ->mk_decrypted_inodes.
*/
static int find_and_derive_key(const struct inode *inode,
const union fscrypt_context *ctx,
@@ -1073,7 +1374,7 @@ static int find_and_derive_key(const struct inode *inode,
derived_keysize);
}
mk = key->payload.data[0];
- down_read(&key->sem);
+ down_read(&mk->mk_secret_sem);
/* Has the secret been removed using FS_IOC_REMOVE_ENCRYPTION_KEY? */
if (!is_master_key_secret_present(&mk->mk_secret)) {
@@ -1117,7 +1418,7 @@ static int find_and_derive_key(const struct inode *inode,
return 0;
out_release_key:
- up_read(&key->sem);
+ up_read(&mk->mk_secret_sem);
key_put(key);
return err;
}
@@ -1361,7 +1662,9 @@ int fscrypt_get_encryption_info(struct inode *inode)
}
out:
if (master_key) {
- up_read(&master_key->sem);
+ struct fscrypt_master_key *mk = master_key->payload.data[0];
+
+ up_read(&mk->mk_secret_sem);
key_put(master_key);
}
if (res == -ENOKEY)
@@ -102,7 +102,10 @@ struct fscrypt_add_key_args {
/* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */
struct fscrypt_remove_key_args {
- __u64 reserved[3];
+ __u32 flags;
+#define FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS 0x00000001
+ __u32 reserved1;
+ __u64 reserved2[2];
struct fscrypt_key_specifier key_spec;
};
@@ -117,9 +120,11 @@ struct fscrypt_get_key_status_args {
#define FSCRYPT_KEY_STATUS_ABSENT 1
#define FSCRYPT_KEY_STATUS_PRESENT 2
#define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3
+ __u32 status_flags;
+#define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001
+ __u32 user_count;
__u32 reserved2;
-
- __u64 reserved3[7];
+ __u64 reserved3[6];
};
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy)