Merge branch 'fixes-v4.14-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull key handling fixes from James Morris: "This includes a fix for the capabilities code from Colin King, and a set of further fixes for the keys subsystem. From David: - Fix a bunch of places where kernel drivers may access revoked user-type keys and don't do it correctly. - Fix some ecryptfs bits. - Fix big_key to require CONFIG_CRYPTO. - Fix a couple of bugs in the asymmetric key type. - Fix a race between updating and finding negative keys. - Prevent add_key() from updating uninstantiated keys. - Make loading of key flags and expiry time atomic when not holding locks" * 'fixes-v4.14-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: commoncap: move assignment of fs_ns to avoid null pointer dereference pkcs7: Prevent NULL pointer dereference, since sinfo is not always set. KEYS: load key flags and expiry time atomically in proc_keys_show() KEYS: Load key expiry time atomically in keyring_search_iterator() KEYS: load key flags and expiry time atomically in key_validate() KEYS: don't let add_key() update an uninstantiated key KEYS: Fix race between updating and finding a negative key KEYS: checking the input id parameters before finding asymmetric key KEYS: Fix the wrong index when checking the existence of second id security/keys: BIG_KEY requires CONFIG_CRYPTO ecryptfs: fix dereference of NULL user_key_payload fscrypt: fix dereference of NULL user_key_payload lib/digsig: fix dereference of NULL user_key_payload FS-Cache: fix dereference of NULL user_key_payload KEYS: encrypted: fix dereference of NULL user_key_payload
This commit is contained in:
commit
03b652e5c0
|
@ -57,6 +57,8 @@ struct key *find_asymmetric_key(struct key *keyring,
|
||||||
char *req, *p;
|
char *req, *p;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
BUG_ON(!id_0 && !id_1);
|
||||||
|
|
||||||
if (id_0) {
|
if (id_0) {
|
||||||
lookup = id_0->data;
|
lookup = id_0->data;
|
||||||
len = id_0->len;
|
len = id_0->len;
|
||||||
|
@ -105,7 +107,7 @@ struct key *find_asymmetric_key(struct key *keyring,
|
||||||
if (id_0 && id_1) {
|
if (id_0 && id_1) {
|
||||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||||
|
|
||||||
if (!kids->id[0]) {
|
if (!kids->id[1]) {
|
||||||
pr_debug("First ID matches, but second is missing\n");
|
pr_debug("First ID matches, but second is missing\n");
|
||||||
goto reject;
|
goto reject;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,9 @@ static int pkcs7_check_authattrs(struct pkcs7_message *msg)
|
||||||
bool want = false;
|
bool want = false;
|
||||||
|
|
||||||
sinfo = msg->signed_infos;
|
sinfo = msg->signed_infos;
|
||||||
|
if (!sinfo)
|
||||||
|
goto inconsistent;
|
||||||
|
|
||||||
if (sinfo->authattrs) {
|
if (sinfo->authattrs) {
|
||||||
want = true;
|
want = true;
|
||||||
msg->have_authattrs = true;
|
msg->have_authattrs = true;
|
||||||
|
|
|
@ -109,6 +109,11 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ukp = user_key_payload_locked(keyring_key);
|
ukp = user_key_payload_locked(keyring_key);
|
||||||
|
if (!ukp) {
|
||||||
|
/* key was revoked before we acquired its semaphore */
|
||||||
|
res = -EKEYREVOKED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (ukp->datalen != sizeof(struct fscrypt_key)) {
|
if (ukp->datalen != sizeof(struct fscrypt_key)) {
|
||||||
res = -EINVAL;
|
res = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -84,11 +84,16 @@ struct ecryptfs_page_crypt_context {
|
||||||
static inline struct ecryptfs_auth_tok *
|
static inline struct ecryptfs_auth_tok *
|
||||||
ecryptfs_get_encrypted_key_payload_data(struct key *key)
|
ecryptfs_get_encrypted_key_payload_data(struct key *key)
|
||||||
{
|
{
|
||||||
if (key->type == &key_type_encrypted)
|
struct encrypted_key_payload *payload;
|
||||||
return (struct ecryptfs_auth_tok *)
|
|
||||||
(&((struct encrypted_key_payload *)key->payload.data[0])->payload_data);
|
if (key->type != &key_type_encrypted)
|
||||||
else
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
payload = key->payload.data[0];
|
||||||
|
if (!payload)
|
||||||
|
return ERR_PTR(-EKEYREVOKED);
|
||||||
|
|
||||||
|
return (struct ecryptfs_auth_tok *)payload->payload_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
|
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
|
||||||
|
@ -114,12 +119,17 @@ static inline struct ecryptfs_auth_tok *
|
||||||
ecryptfs_get_key_payload_data(struct key *key)
|
ecryptfs_get_key_payload_data(struct key *key)
|
||||||
{
|
{
|
||||||
struct ecryptfs_auth_tok *auth_tok;
|
struct ecryptfs_auth_tok *auth_tok;
|
||||||
|
struct user_key_payload *ukp;
|
||||||
|
|
||||||
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
|
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
|
||||||
if (!auth_tok)
|
if (auth_tok)
|
||||||
return (struct ecryptfs_auth_tok *)user_key_payload_locked(key)->data;
|
|
||||||
else
|
|
||||||
return auth_tok;
|
return auth_tok;
|
||||||
|
|
||||||
|
ukp = user_key_payload_locked(key);
|
||||||
|
if (!ukp)
|
||||||
|
return ERR_PTR(-EKEYREVOKED);
|
||||||
|
|
||||||
|
return (struct ecryptfs_auth_tok *)ukp->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
|
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
|
||||||
|
|
|
@ -459,7 +459,8 @@ static int ecryptfs_verify_version(u16 version)
|
||||||
* @auth_tok_key: key containing the authentication token
|
* @auth_tok_key: key containing the authentication token
|
||||||
* @auth_tok: authentication token
|
* @auth_tok: authentication token
|
||||||
*
|
*
|
||||||
* Returns zero on valid auth tok; -EINVAL otherwise
|
* Returns zero on valid auth tok; -EINVAL if the payload is invalid; or
|
||||||
|
* -EKEYREVOKED if the key was revoked before we acquired its semaphore.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key,
|
ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key,
|
||||||
|
@ -468,6 +469,12 @@ ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key,
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
(*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key);
|
(*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key);
|
||||||
|
if (IS_ERR(*auth_tok)) {
|
||||||
|
rc = PTR_ERR(*auth_tok);
|
||||||
|
*auth_tok = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (ecryptfs_verify_version((*auth_tok)->version)) {
|
if (ecryptfs_verify_version((*auth_tok)->version)) {
|
||||||
printk(KERN_ERR "Data structure version mismatch. Userspace "
|
printk(KERN_ERR "Data structure version mismatch. Userspace "
|
||||||
"tools must match eCryptfs kernel module with major "
|
"tools must match eCryptfs kernel module with major "
|
||||||
|
|
|
@ -331,6 +331,13 @@ static void fscache_objlist_config(struct fscache_objlist_data *data)
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
confkey = user_key_payload_rcu(key);
|
confkey = user_key_payload_rcu(key);
|
||||||
|
if (!confkey) {
|
||||||
|
/* key was revoked */
|
||||||
|
rcu_read_unlock();
|
||||||
|
key_put(key);
|
||||||
|
goto no_config;
|
||||||
|
}
|
||||||
|
|
||||||
buf = confkey->data;
|
buf = confkey->data;
|
||||||
|
|
||||||
for (len = confkey->datalen - 1; len >= 0; len--) {
|
for (len = confkey->datalen - 1; len >= 0; len--) {
|
||||||
|
|
|
@ -138,6 +138,11 @@ struct key_restriction {
|
||||||
struct key_type *keytype;
|
struct key_type *keytype;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum key_state {
|
||||||
|
KEY_IS_UNINSTANTIATED,
|
||||||
|
KEY_IS_POSITIVE, /* Positively instantiated */
|
||||||
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
* authentication token / access credential / keyring
|
* authentication token / access credential / keyring
|
||||||
|
@ -169,6 +174,7 @@ struct key {
|
||||||
* - may not match RCU dereferenced payload
|
* - may not match RCU dereferenced payload
|
||||||
* - payload should contain own length
|
* - payload should contain own length
|
||||||
*/
|
*/
|
||||||
|
short state; /* Key state (+) or rejection error (-) */
|
||||||
|
|
||||||
#ifdef KEY_DEBUGGING
|
#ifdef KEY_DEBUGGING
|
||||||
unsigned magic;
|
unsigned magic;
|
||||||
|
@ -176,18 +182,16 @@ struct key {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned long flags; /* status flags (change with bitops) */
|
unsigned long flags; /* status flags (change with bitops) */
|
||||||
#define KEY_FLAG_INSTANTIATED 0 /* set if key has been instantiated */
|
#define KEY_FLAG_DEAD 0 /* set if key type has been deleted */
|
||||||
#define KEY_FLAG_DEAD 1 /* set if key type has been deleted */
|
#define KEY_FLAG_REVOKED 1 /* set if key had been revoked */
|
||||||
#define KEY_FLAG_REVOKED 2 /* set if key had been revoked */
|
#define KEY_FLAG_IN_QUOTA 2 /* set if key consumes quota */
|
||||||
#define KEY_FLAG_IN_QUOTA 3 /* set if key consumes quota */
|
#define KEY_FLAG_USER_CONSTRUCT 3 /* set if key is being constructed in userspace */
|
||||||
#define KEY_FLAG_USER_CONSTRUCT 4 /* set if key is being constructed in userspace */
|
#define KEY_FLAG_ROOT_CAN_CLEAR 4 /* set if key can be cleared by root without permission */
|
||||||
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
|
#define KEY_FLAG_INVALIDATED 5 /* set if key has been invalidated */
|
||||||
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
|
#define KEY_FLAG_BUILTIN 6 /* set if key is built in to the kernel */
|
||||||
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
|
#define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */
|
||||||
#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */
|
#define KEY_FLAG_KEEP 8 /* set if key should not be removed */
|
||||||
#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
|
#define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */
|
||||||
#define KEY_FLAG_KEEP 10 /* set if key should not be removed */
|
|
||||||
#define KEY_FLAG_UID_KEYRING 11 /* set if key is a user or user session keyring */
|
|
||||||
|
|
||||||
/* the key type and key description string
|
/* the key type and key description string
|
||||||
* - the desc is used to match a key against search criteria
|
* - the desc is used to match a key against search criteria
|
||||||
|
@ -213,7 +217,6 @@ struct key {
|
||||||
struct list_head name_link;
|
struct list_head name_link;
|
||||||
struct assoc_array keys;
|
struct assoc_array keys;
|
||||||
};
|
};
|
||||||
int reject_error;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is set on a keyring to restrict the addition of a link to a key
|
/* This is set on a keyring to restrict the addition of a link to a key
|
||||||
|
@ -353,17 +356,27 @@ extern void key_set_timeout(struct key *, unsigned);
|
||||||
#define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */
|
#define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */
|
||||||
#define KEY_NEED_ALL 0x3f /* All the above permissions */
|
#define KEY_NEED_ALL 0x3f /* All the above permissions */
|
||||||
|
|
||||||
|
static inline short key_read_state(const struct key *key)
|
||||||
|
{
|
||||||
|
/* Barrier versus mark_key_instantiated(). */
|
||||||
|
return smp_load_acquire(&key->state);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key_is_instantiated - Determine if a key has been positively instantiated
|
* key_is_positive - Determine if a key has been positively instantiated
|
||||||
* @key: The key to check.
|
* @key: The key to check.
|
||||||
*
|
*
|
||||||
* Return true if the specified key has been positively instantiated, false
|
* Return true if the specified key has been positively instantiated, false
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
static inline bool key_is_instantiated(const struct key *key)
|
static inline bool key_is_positive(const struct key *key)
|
||||||
{
|
{
|
||||||
return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
|
return key_read_state(key) == KEY_IS_POSITIVE;
|
||||||
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
}
|
||||||
|
|
||||||
|
static inline bool key_is_negative(const struct key *key)
|
||||||
|
{
|
||||||
|
return key_read_state(key) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define dereference_key_rcu(KEY) \
|
#define dereference_key_rcu(KEY) \
|
||||||
|
|
|
@ -87,6 +87,12 @@ static int digsig_verify_rsa(struct key *key,
|
||||||
down_read(&key->sem);
|
down_read(&key->sem);
|
||||||
ukp = user_key_payload_locked(key);
|
ukp = user_key_payload_locked(key);
|
||||||
|
|
||||||
|
if (!ukp) {
|
||||||
|
/* key was revoked before we acquired its semaphore */
|
||||||
|
err = -EKEYREVOKED;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ukp->datalen < sizeof(*pkh))
|
if (ukp->datalen < sizeof(*pkh))
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ static int dns_resolver_match_preparse(struct key_match_data *match_data)
|
||||||
static void dns_resolver_describe(const struct key *key, struct seq_file *m)
|
static void dns_resolver_describe(const struct key *key, struct seq_file *m)
|
||||||
{
|
{
|
||||||
seq_puts(m, key->description);
|
seq_puts(m, key->description);
|
||||||
if (key_is_instantiated(key)) {
|
if (key_is_positive(key)) {
|
||||||
int err = PTR_ERR(key->payload.data[dns_key_error]);
|
int err = PTR_ERR(key->payload.data[dns_key_error]);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -585,13 +585,14 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
|
||||||
struct vfs_ns_cap_data data, *nscaps = &data;
|
struct vfs_ns_cap_data data, *nscaps = &data;
|
||||||
struct vfs_cap_data *caps = (struct vfs_cap_data *) &data;
|
struct vfs_cap_data *caps = (struct vfs_cap_data *) &data;
|
||||||
kuid_t rootkuid;
|
kuid_t rootkuid;
|
||||||
struct user_namespace *fs_ns = inode->i_sb->s_user_ns;
|
struct user_namespace *fs_ns;
|
||||||
|
|
||||||
memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
|
memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
|
||||||
|
|
||||||
if (!inode)
|
if (!inode)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
|
|
||||||
|
fs_ns = inode->i_sb->s_user_ns;
|
||||||
size = __vfs_getxattr((struct dentry *)dentry, inode,
|
size = __vfs_getxattr((struct dentry *)dentry, inode,
|
||||||
XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ);
|
XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ);
|
||||||
if (size == -ENODATA || size == -EOPNOTSUPP)
|
if (size == -ENODATA || size == -EOPNOTSUPP)
|
||||||
|
|
|
@ -45,6 +45,7 @@ config BIG_KEYS
|
||||||
bool "Large payload keys"
|
bool "Large payload keys"
|
||||||
depends on KEYS
|
depends on KEYS
|
||||||
depends on TMPFS
|
depends on TMPFS
|
||||||
|
select CRYPTO
|
||||||
select CRYPTO_AES
|
select CRYPTO_AES
|
||||||
select CRYPTO_GCM
|
select CRYPTO_GCM
|
||||||
help
|
help
|
||||||
|
|
|
@ -247,7 +247,7 @@ void big_key_revoke(struct key *key)
|
||||||
|
|
||||||
/* clear the quota */
|
/* clear the quota */
|
||||||
key_payload_reserve(key, 0);
|
key_payload_reserve(key, 0);
|
||||||
if (key_is_instantiated(key) &&
|
if (key_is_positive(key) &&
|
||||||
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
|
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
|
||||||
vfs_truncate(path, 0);
|
vfs_truncate(path, 0);
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ void big_key_describe(const struct key *key, struct seq_file *m)
|
||||||
|
|
||||||
seq_puts(m, key->description);
|
seq_puts(m, key->description);
|
||||||
|
|
||||||
if (key_is_instantiated(key))
|
if (key_is_positive(key))
|
||||||
seq_printf(m, ": %zu [%s]",
|
seq_printf(m, ": %zu [%s]",
|
||||||
datalen,
|
datalen,
|
||||||
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
||||||
|
|
|
@ -309,6 +309,13 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
|
||||||
|
|
||||||
down_read(&ukey->sem);
|
down_read(&ukey->sem);
|
||||||
upayload = user_key_payload_locked(ukey);
|
upayload = user_key_payload_locked(ukey);
|
||||||
|
if (!upayload) {
|
||||||
|
/* key was revoked before we acquired its semaphore */
|
||||||
|
up_read(&ukey->sem);
|
||||||
|
key_put(ukey);
|
||||||
|
ukey = ERR_PTR(-EKEYREVOKED);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
*master_key = upayload->data;
|
*master_key = upayload->data;
|
||||||
*master_keylen = upayload->datalen;
|
*master_keylen = upayload->datalen;
|
||||||
error:
|
error:
|
||||||
|
@ -847,7 +854,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||||
size_t datalen = prep->datalen;
|
size_t datalen = prep->datalen;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
if (key_is_negative(key))
|
||||||
return -ENOKEY;
|
return -ENOKEY;
|
||||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -129,15 +129,15 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
|
||||||
while (!list_empty(keys)) {
|
while (!list_empty(keys)) {
|
||||||
struct key *key =
|
struct key *key =
|
||||||
list_entry(keys->next, struct key, graveyard_link);
|
list_entry(keys->next, struct key, graveyard_link);
|
||||||
|
short state = key->state;
|
||||||
|
|
||||||
list_del(&key->graveyard_link);
|
list_del(&key->graveyard_link);
|
||||||
|
|
||||||
kdebug("- %u", key->serial);
|
kdebug("- %u", key->serial);
|
||||||
key_check(key);
|
key_check(key);
|
||||||
|
|
||||||
/* Throw away the key data if the key is instantiated */
|
/* Throw away the key data if the key is instantiated */
|
||||||
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
|
if (state == KEY_IS_POSITIVE && key->type->destroy)
|
||||||
!test_bit(KEY_FLAG_NEGATIVE, &key->flags) &&
|
|
||||||
key->type->destroy)
|
|
||||||
key->type->destroy(key);
|
key->type->destroy(key);
|
||||||
|
|
||||||
security_key_free(key);
|
security_key_free(key);
|
||||||
|
@ -151,7 +151,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_dec(&key->user->nkeys);
|
atomic_dec(&key->user->nkeys);
|
||||||
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
if (state != KEY_IS_UNINSTANTIATED)
|
||||||
atomic_dec(&key->user->nikeys);
|
atomic_dec(&key->user->nikeys);
|
||||||
|
|
||||||
key_user_put(key->user);
|
key_user_put(key->user);
|
||||||
|
|
|
@ -401,6 +401,18 @@ int key_payload_reserve(struct key *key, size_t datalen)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(key_payload_reserve);
|
EXPORT_SYMBOL(key_payload_reserve);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change the key state to being instantiated.
|
||||||
|
*/
|
||||||
|
static void mark_key_instantiated(struct key *key, int reject_error)
|
||||||
|
{
|
||||||
|
/* Commit the payload before setting the state; barrier versus
|
||||||
|
* key_read_state().
|
||||||
|
*/
|
||||||
|
smp_store_release(&key->state,
|
||||||
|
(reject_error < 0) ? reject_error : KEY_IS_POSITIVE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instantiate a key and link it into the target keyring atomically. Must be
|
* Instantiate a key and link it into the target keyring atomically. Must be
|
||||||
* called with the target keyring's semaphore writelocked. The target key's
|
* called with the target keyring's semaphore writelocked. The target key's
|
||||||
|
@ -424,14 +436,14 @@ static int __key_instantiate_and_link(struct key *key,
|
||||||
mutex_lock(&key_construction_mutex);
|
mutex_lock(&key_construction_mutex);
|
||||||
|
|
||||||
/* can't instantiate twice */
|
/* can't instantiate twice */
|
||||||
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
if (key->state == KEY_IS_UNINSTANTIATED) {
|
||||||
/* instantiate the key */
|
/* instantiate the key */
|
||||||
ret = key->type->instantiate(key, prep);
|
ret = key->type->instantiate(key, prep);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* mark the key as being instantiated */
|
/* mark the key as being instantiated */
|
||||||
atomic_inc(&key->user->nikeys);
|
atomic_inc(&key->user->nikeys);
|
||||||
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
mark_key_instantiated(key, 0);
|
||||||
|
|
||||||
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
|
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
|
||||||
awaken = 1;
|
awaken = 1;
|
||||||
|
@ -577,13 +589,10 @@ int key_reject_and_link(struct key *key,
|
||||||
mutex_lock(&key_construction_mutex);
|
mutex_lock(&key_construction_mutex);
|
||||||
|
|
||||||
/* can't instantiate twice */
|
/* can't instantiate twice */
|
||||||
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
if (key->state == KEY_IS_UNINSTANTIATED) {
|
||||||
/* mark the key as being negatively instantiated */
|
/* mark the key as being negatively instantiated */
|
||||||
atomic_inc(&key->user->nikeys);
|
atomic_inc(&key->user->nikeys);
|
||||||
key->reject_error = -error;
|
mark_key_instantiated(key, -error);
|
||||||
smp_wmb();
|
|
||||||
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
|
||||||
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
|
||||||
now = current_kernel_time();
|
now = current_kernel_time();
|
||||||
key->expiry = now.tv_sec + timeout;
|
key->expiry = now.tv_sec + timeout;
|
||||||
key_schedule_gc(key->expiry + key_gc_delay);
|
key_schedule_gc(key->expiry + key_gc_delay);
|
||||||
|
@ -752,8 +761,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
|
||||||
|
|
||||||
ret = key->type->update(key, prep);
|
ret = key->type->update(key, prep);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
/* updating a negative key instantiates it */
|
/* Updating a negative key positively instantiates it */
|
||||||
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
mark_key_instantiated(key, 0);
|
||||||
|
|
||||||
up_write(&key->sem);
|
up_write(&key->sem);
|
||||||
|
|
||||||
|
@ -936,6 +945,16 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||||
*/
|
*/
|
||||||
__key_link_end(keyring, &index_key, edit);
|
__key_link_end(keyring, &index_key, edit);
|
||||||
|
|
||||||
|
key = key_ref_to_ptr(key_ref);
|
||||||
|
if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) {
|
||||||
|
ret = wait_for_key_construction(key, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
key_ref_put(key_ref);
|
||||||
|
key_ref = ERR_PTR(ret);
|
||||||
|
goto error_free_prep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
key_ref = __key_update(key_ref, &prep);
|
key_ref = __key_update(key_ref, &prep);
|
||||||
goto error_free_prep;
|
goto error_free_prep;
|
||||||
}
|
}
|
||||||
|
@ -986,8 +1005,8 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
||||||
|
|
||||||
ret = key->type->update(key, &prep);
|
ret = key->type->update(key, &prep);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
/* updating a negative key instantiates it */
|
/* Updating a negative key positively instantiates it */
|
||||||
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
mark_key_instantiated(key, 0);
|
||||||
|
|
||||||
up_write(&key->sem);
|
up_write(&key->sem);
|
||||||
|
|
||||||
|
|
|
@ -766,10 +766,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
|
||||||
|
|
||||||
key = key_ref_to_ptr(key_ref);
|
key = key_ref_to_ptr(key_ref);
|
||||||
|
|
||||||
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
|
ret = key_read_state(key);
|
||||||
ret = -ENOKEY;
|
if (ret < 0)
|
||||||
goto error2;
|
goto error2; /* Negatively instantiated */
|
||||||
}
|
|
||||||
|
|
||||||
/* see if we can read it directly */
|
/* see if we can read it directly */
|
||||||
ret = key_permission(key_ref, KEY_NEED_READ);
|
ret = key_permission(key_ref, KEY_NEED_READ);
|
||||||
|
@ -901,7 +900,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
|
||||||
atomic_dec(&key->user->nkeys);
|
atomic_dec(&key->user->nkeys);
|
||||||
atomic_inc(&newowner->nkeys);
|
atomic_inc(&newowner->nkeys);
|
||||||
|
|
||||||
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
if (key->state != KEY_IS_UNINSTANTIATED) {
|
||||||
atomic_dec(&key->user->nikeys);
|
atomic_dec(&key->user->nikeys);
|
||||||
atomic_inc(&newowner->nikeys);
|
atomic_inc(&newowner->nikeys);
|
||||||
}
|
}
|
||||||
|
|
|
@ -414,7 +414,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
|
||||||
else
|
else
|
||||||
seq_puts(m, "[anon]");
|
seq_puts(m, "[anon]");
|
||||||
|
|
||||||
if (key_is_instantiated(keyring)) {
|
if (key_is_positive(keyring)) {
|
||||||
if (keyring->keys.nr_leaves_on_tree != 0)
|
if (keyring->keys.nr_leaves_on_tree != 0)
|
||||||
seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
|
seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
|
||||||
else
|
else
|
||||||
|
@ -553,7 +553,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
|
||||||
{
|
{
|
||||||
struct keyring_search_context *ctx = iterator_data;
|
struct keyring_search_context *ctx = iterator_data;
|
||||||
const struct key *key = keyring_ptr_to_key(object);
|
const struct key *key = keyring_ptr_to_key(object);
|
||||||
unsigned long kflags = key->flags;
|
unsigned long kflags = READ_ONCE(key->flags);
|
||||||
|
short state = READ_ONCE(key->state);
|
||||||
|
|
||||||
kenter("{%d}", key->serial);
|
kenter("{%d}", key->serial);
|
||||||
|
|
||||||
|
@ -565,6 +566,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
|
||||||
|
|
||||||
/* skip invalidated, revoked and expired keys */
|
/* skip invalidated, revoked and expired keys */
|
||||||
if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
|
if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
|
||||||
|
time_t expiry = READ_ONCE(key->expiry);
|
||||||
|
|
||||||
if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
|
if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||||
(1 << KEY_FLAG_REVOKED))) {
|
(1 << KEY_FLAG_REVOKED))) {
|
||||||
ctx->result = ERR_PTR(-EKEYREVOKED);
|
ctx->result = ERR_PTR(-EKEYREVOKED);
|
||||||
|
@ -572,7 +575,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
|
||||||
goto skipped;
|
goto skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key->expiry && ctx->now.tv_sec >= key->expiry) {
|
if (expiry && ctx->now.tv_sec >= expiry) {
|
||||||
if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED))
|
if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED))
|
||||||
ctx->result = ERR_PTR(-EKEYEXPIRED);
|
ctx->result = ERR_PTR(-EKEYEXPIRED);
|
||||||
kleave(" = %d [expire]", ctx->skipped_ret);
|
kleave(" = %d [expire]", ctx->skipped_ret);
|
||||||
|
@ -597,9 +600,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
|
||||||
|
|
||||||
if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
|
if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
|
||||||
/* we set a different error code if we pass a negative key */
|
/* we set a different error code if we pass a negative key */
|
||||||
if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
|
if (state < 0) {
|
||||||
smp_rmb();
|
ctx->result = ERR_PTR(state);
|
||||||
ctx->result = ERR_PTR(key->reject_error);
|
|
||||||
kleave(" = %d [neg]", ctx->skipped_ret);
|
kleave(" = %d [neg]", ctx->skipped_ret);
|
||||||
goto skipped;
|
goto skipped;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,8 @@ EXPORT_SYMBOL(key_task_permission);
|
||||||
*/
|
*/
|
||||||
int key_validate(const struct key *key)
|
int key_validate(const struct key *key)
|
||||||
{
|
{
|
||||||
unsigned long flags = key->flags;
|
unsigned long flags = READ_ONCE(key->flags);
|
||||||
|
time_t expiry = READ_ONCE(key->expiry);
|
||||||
|
|
||||||
if (flags & (1 << KEY_FLAG_INVALIDATED))
|
if (flags & (1 << KEY_FLAG_INVALIDATED))
|
||||||
return -ENOKEY;
|
return -ENOKEY;
|
||||||
|
@ -99,9 +100,9 @@ int key_validate(const struct key *key)
|
||||||
return -EKEYREVOKED;
|
return -EKEYREVOKED;
|
||||||
|
|
||||||
/* check it hasn't expired */
|
/* check it hasn't expired */
|
||||||
if (key->expiry) {
|
if (expiry) {
|
||||||
struct timespec now = current_kernel_time();
|
struct timespec now = current_kernel_time();
|
||||||
if (now.tv_sec >= key->expiry)
|
if (now.tv_sec >= expiry)
|
||||||
return -EKEYEXPIRED;
|
return -EKEYEXPIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,9 +179,12 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||||
struct rb_node *_p = v;
|
struct rb_node *_p = v;
|
||||||
struct key *key = rb_entry(_p, struct key, serial_node);
|
struct key *key = rb_entry(_p, struct key, serial_node);
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
|
time_t expiry;
|
||||||
unsigned long timo;
|
unsigned long timo;
|
||||||
|
unsigned long flags;
|
||||||
key_ref_t key_ref, skey_ref;
|
key_ref_t key_ref, skey_ref;
|
||||||
char xbuf[16];
|
char xbuf[16];
|
||||||
|
short state;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
struct keyring_search_context ctx = {
|
struct keyring_search_context ctx = {
|
||||||
|
@ -217,12 +220,13 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
/* come up with a suitable timeout value */
|
/* come up with a suitable timeout value */
|
||||||
if (key->expiry == 0) {
|
expiry = READ_ONCE(key->expiry);
|
||||||
|
if (expiry == 0) {
|
||||||
memcpy(xbuf, "perm", 5);
|
memcpy(xbuf, "perm", 5);
|
||||||
} else if (now.tv_sec >= key->expiry) {
|
} else if (now.tv_sec >= expiry) {
|
||||||
memcpy(xbuf, "expd", 5);
|
memcpy(xbuf, "expd", 5);
|
||||||
} else {
|
} else {
|
||||||
timo = key->expiry - now.tv_sec;
|
timo = expiry - now.tv_sec;
|
||||||
|
|
||||||
if (timo < 60)
|
if (timo < 60)
|
||||||
sprintf(xbuf, "%lus", timo);
|
sprintf(xbuf, "%lus", timo);
|
||||||
|
@ -236,18 +240,21 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||||
sprintf(xbuf, "%luw", timo / (60*60*24*7));
|
sprintf(xbuf, "%luw", timo / (60*60*24*7));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define showflag(KEY, LETTER, FLAG) \
|
state = key_read_state(key);
|
||||||
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
|
|
||||||
|
|
||||||
|
#define showflag(FLAGS, LETTER, FLAG) \
|
||||||
|
((FLAGS & (1 << FLAG)) ? LETTER : '-')
|
||||||
|
|
||||||
|
flags = READ_ONCE(key->flags);
|
||||||
seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
|
seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
|
||||||
key->serial,
|
key->serial,
|
||||||
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
|
state != KEY_IS_UNINSTANTIATED ? 'I' : '-',
|
||||||
showflag(key, 'R', KEY_FLAG_REVOKED),
|
showflag(flags, 'R', KEY_FLAG_REVOKED),
|
||||||
showflag(key, 'D', KEY_FLAG_DEAD),
|
showflag(flags, 'D', KEY_FLAG_DEAD),
|
||||||
showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
|
showflag(flags, 'Q', KEY_FLAG_IN_QUOTA),
|
||||||
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
|
showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT),
|
||||||
showflag(key, 'N', KEY_FLAG_NEGATIVE),
|
state < 0 ? 'N' : '-',
|
||||||
showflag(key, 'i', KEY_FLAG_INVALIDATED),
|
showflag(flags, 'i', KEY_FLAG_INVALIDATED),
|
||||||
refcount_read(&key->usage),
|
refcount_read(&key->usage),
|
||||||
xbuf,
|
xbuf,
|
||||||
key->perm,
|
key->perm,
|
||||||
|
|
|
@ -730,7 +730,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
|
||||||
|
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
if (!(lflags & KEY_LOOKUP_PARTIAL) &&
|
if (!(lflags & KEY_LOOKUP_PARTIAL) &&
|
||||||
!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
key_read_state(key) == KEY_IS_UNINSTANTIATED)
|
||||||
goto invalid_key;
|
goto invalid_key;
|
||||||
|
|
||||||
/* check the permissions */
|
/* check the permissions */
|
||||||
|
|
|
@ -595,10 +595,9 @@ int wait_for_key_construction(struct key *key, bool intr)
|
||||||
intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
|
ret = key_read_state(key);
|
||||||
smp_rmb();
|
if (ret < 0)
|
||||||
return key->reject_error;
|
return ret;
|
||||||
}
|
|
||||||
return key_validate(key);
|
return key_validate(key);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(wait_for_key_construction);
|
EXPORT_SYMBOL(wait_for_key_construction);
|
||||||
|
|
|
@ -73,7 +73,7 @@ static void request_key_auth_describe(const struct key *key,
|
||||||
|
|
||||||
seq_puts(m, "key:");
|
seq_puts(m, "key:");
|
||||||
seq_puts(m, key->description);
|
seq_puts(m, key->description);
|
||||||
if (key_is_instantiated(key))
|
if (key_is_positive(key))
|
||||||
seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
|
seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1066,7 +1066,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||||
char *datablob;
|
char *datablob;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
if (key_is_negative(key))
|
||||||
return -ENOKEY;
|
return -ENOKEY;
|
||||||
p = key->payload.data[0];
|
p = key->payload.data[0];
|
||||||
if (!p->migratable)
|
if (!p->migratable)
|
||||||
|
|
|
@ -114,7 +114,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||||
|
|
||||||
/* attach the new data, displacing the old */
|
/* attach the new data, displacing the old */
|
||||||
key->expiry = prep->expiry;
|
key->expiry = prep->expiry;
|
||||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
if (key_is_positive(key))
|
||||||
zap = dereference_key_locked(key);
|
zap = dereference_key_locked(key);
|
||||||
rcu_assign_keypointer(key, prep->payload.data[0]);
|
rcu_assign_keypointer(key, prep->payload.data[0]);
|
||||||
prep->payload.data[0] = NULL;
|
prep->payload.data[0] = NULL;
|
||||||
|
@ -162,7 +162,7 @@ EXPORT_SYMBOL_GPL(user_destroy);
|
||||||
void user_describe(const struct key *key, struct seq_file *m)
|
void user_describe(const struct key *key, struct seq_file *m)
|
||||||
{
|
{
|
||||||
seq_puts(m, key->description);
|
seq_puts(m, key->description);
|
||||||
if (key_is_instantiated(key))
|
if (key_is_positive(key))
|
||||||
seq_printf(m, ": %u", key->datalen);
|
seq_printf(m, ": %u", key->datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue