Commit cc759ca5 authored by David Howells's avatar David Howells Committed by Thadeu Lima de Souza Cascardo

KEYS: Fix race between updating and finding a negative key

BugLink: http://bugs.launchpad.net/bugs/1729107

commit 363b02da upstream.

Consolidate KEY_FLAG_INSTANTIATED, KEY_FLAG_NEGATIVE and the rejection
error into one field such that:

 (1) The instantiation state can be modified/read atomically.

 (2) The error can be accessed atomically with the state.

 (3) The error isn't stored unioned with the payload pointers.

This deals with the problem that the state is spread over three different
objects (two bits and a separate variable) and reading or updating them
atomically isn't practical, given that not only can uninstantiated keys
change into instantiated or rejected keys, but rejected keys can also turn
into instantiated keys - and someone accessing the key might not be using
any locking.

The main side effect of this problem is that what was held in the payload
may change, depending on the state.  For instance, you might observe the
key to be in the rejected state.  You then read the cached error, but if
the key semaphore wasn't locked, the key might've become instantiated
between the two reads - and you might now have something in hand that isn't
actually an error code.

The state is now KEY_IS_UNINSTANTIATED, KEY_IS_POSITIVE or a negative error
code if the key is negatively instantiated.  The key_is_instantiated()
function is replaced with key_is_positive() to avoid confusion as negative
keys are also 'instantiated'.

Additionally, barriering is included:

 (1) Order payload-set before state-set during instantiation.

 (2) Order state-read before payload-read when using the key.

Further separate barriering is necessary if RCU is being used to access the
payload content after reading the payload pointers.

Fixes: 146aa8b1 ("KEYS: Merge the type-specific data with the payload data")
Reported-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1469bf48
...@@ -126,6 +126,11 @@ static inline bool is_key_possessed(const key_ref_t key_ref) ...@@ -126,6 +126,11 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
return (unsigned long) key_ref & 1UL; return (unsigned long) key_ref & 1UL;
} }
enum key_state {
KEY_IS_UNINSTANTIATED,
KEY_IS_POSITIVE, /* Positively instantiated */
};
/*****************************************************************************/ /*****************************************************************************/
/* /*
* authentication token / access credential / keyring * authentication token / access credential / keyring
...@@ -157,6 +162,7 @@ struct key { ...@@ -157,6 +162,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;
...@@ -165,19 +171,17 @@ struct key { ...@@ -165,19 +171,17 @@ 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_TRUSTED 6 /* set if key is trusted */
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED_ONLY 7 /* set if keyring only accepts links to trusted keys */
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_BUILTIN 8 /* set if key is builtin */
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ #define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ #define KEY_FLAG_UID_KEYRING 10 /* set if key is a user or user session keyring */
#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_UID_KEYRING 12 /* 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
...@@ -203,7 +207,6 @@ struct key { ...@@ -203,7 +207,6 @@ struct key {
struct list_head name_link; struct list_head name_link;
struct assoc_array keys; struct assoc_array keys;
}; };
int reject_error;
}; };
}; };
...@@ -320,17 +323,27 @@ extern void key_set_timeout(struct key *, unsigned); ...@@ -320,17 +323,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 key_read_state(key) == KEY_IS_POSITIVE;
}
static inline bool key_is_negative(const struct key *key)
{ {
return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && return key_read_state(key) < 0;
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
} }
#define rcu_dereference_key(KEY) \ #define rcu_dereference_key(KEY) \
......
...@@ -224,7 +224,7 @@ static int dns_resolver_match_preparse(struct key_match_data *match_data) ...@@ -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)
......
...@@ -138,7 +138,7 @@ void big_key_revoke(struct key *key) ...@@ -138,7 +138,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);
} }
...@@ -170,7 +170,7 @@ void big_key_describe(const struct key *key, struct seq_file *m) ...@@ -170,7 +170,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");
......
...@@ -852,7 +852,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) ...@@ -852,7 +852,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) ...@@ -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) ...@@ -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);
......
...@@ -397,6 +397,18 @@ int key_payload_reserve(struct key *key, size_t datalen) ...@@ -397,6 +397,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
...@@ -420,14 +432,14 @@ static int __key_instantiate_and_link(struct key *key, ...@@ -420,14 +432,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;
...@@ -555,13 +567,10 @@ int key_reject_and_link(struct key *key, ...@@ -555,13 +567,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);
...@@ -733,8 +742,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref, ...@@ -733,8 +742,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);
...@@ -969,8 +978,8 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) ...@@ -969,8 +978,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);
......
...@@ -738,10 +738,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) ...@@ -738,10 +738,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);
...@@ -873,7 +872,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) ...@@ -873,7 +872,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);
} }
......
...@@ -407,7 +407,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) ...@@ -407,7 +407,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
...@@ -522,7 +522,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) ...@@ -522,7 +522,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);
...@@ -566,9 +567,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) ...@@ -566,9 +567,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;
} }
......
...@@ -182,6 +182,7 @@ static int proc_keys_show(struct seq_file *m, void *v) ...@@ -182,6 +182,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
unsigned long timo; unsigned long timo;
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 = {
...@@ -240,17 +241,19 @@ static int proc_keys_show(struct seq_file *m, void *v) ...@@ -240,17 +241,19 @@ 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));
} }
state = key_read_state(key);
#define showflag(KEY, LETTER, FLAG) \ #define showflag(KEY, LETTER, FLAG) \
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
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(key, 'R', KEY_FLAG_REVOKED),
showflag(key, 'D', KEY_FLAG_DEAD), showflag(key, 'D', KEY_FLAG_DEAD),
showflag(key, 'Q', KEY_FLAG_IN_QUOTA), showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
showflag(key, 'N', KEY_FLAG_NEGATIVE), state < 0 ? 'N' : '-',
showflag(key, 'i', KEY_FLAG_INVALIDATED), showflag(key, 'i', KEY_FLAG_INVALIDATED),
atomic_read(&key->usage), atomic_read(&key->usage),
xbuf, xbuf,
......
...@@ -727,7 +727,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, ...@@ -727,7 +727,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 */
......
...@@ -594,10 +594,9 @@ int wait_for_key_construction(struct key *key, bool intr) ...@@ -594,10 +594,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, ...@@ -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);
} }
......
...@@ -1067,7 +1067,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ...@@ -1067,7 +1067,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)
......
...@@ -120,7 +120,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep) ...@@ -120,7 +120,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
if (ret == 0) { if (ret == 0) {
/* attach the new data, displacing the old */ /* attach the new data, displacing the old */
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) if (key_is_positive(key))
zap = key->payload.data[0]; zap = key->payload.data[0];
else else
zap = NULL; zap = NULL;
...@@ -174,7 +174,7 @@ EXPORT_SYMBOL_GPL(user_destroy); ...@@ -174,7 +174,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);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment