Commit 664cceb0 authored by David Howells's avatar David Howells Committed by Linus Torvalds

[PATCH] Keys: Add possessor permissions to keys [try #3]

The attached patch adds extra permission grants to keys for the possessor of a
key in addition to the owner, group and other permissions bits. This makes
SUID binaries easier to support without going as far as labelling keys and key
targets using the LSM facilities.

This patch adds a second "pointer type" to key structures (struct key_ref *)
that can have the bottom bit of the address set to indicate the possession of
a key. This is propagated through searches from the keyring to the discovered
key. It has been made a separate type so that the compiler can spot attempts
to dereference a potentially incorrect pointer.

The "possession" attribute can't be attached to a key structure directly as
it's not an intrinsic property of a key.

Pointers to keys have been replaced with struct key_ref *'s wherever
possession information needs to be passed through.

This does assume that the bottom bit of the pointer will always be zero on
return from kmem_cache_alloc().

The key reference type has been made into a typedef so that at least it can be
located in the sources, even though it's basically a pointer to an undefined
type. I've also renamed the accessor functions to be more useful, and all
reference variables should now end in "_ref".
Signed-Off-By: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5134fc15
......@@ -195,8 +195,8 @@ KEY ACCESS PERMISSIONS
======================
Keys have an owner user ID, a group access ID, and a permissions mask. The mask
has up to eight bits each for user, group and other access. Only five of each
set of eight bits are defined. These permissions granted are:
has up to eight bits each for possessor, user, group and other access. Only
five of each set of eight bits are defined. These permissions granted are:
(*) View
......@@ -241,16 +241,16 @@ about the status of the key service:
type, description and permissions. The payload of the key is not available
this way:
SERIAL FLAGS USAGE EXPY PERM UID GID TYPE DESCRIPTION: SUMMARY
00000001 I----- 39 perm 1f0000 0 0 keyring _uid_ses.0: 1/4
00000002 I----- 2 perm 1f0000 0 0 keyring _uid.0: empty
00000007 I----- 1 perm 1f0000 0 0 keyring _pid.1: empty
0000018d I----- 1 perm 1f0000 0 0 keyring _pid.412: empty
000004d2 I--Q-- 1 perm 1f0000 32 -1 keyring _uid.32: 1/4
000004d3 I--Q-- 3 perm 1f0000 32 -1 keyring _uid_ses.32: empty
00000892 I--QU- 1 perm 1f0000 0 0 user metal:copper: 0
00000893 I--Q-N 1 35s 1f0000 0 0 user metal:silver: 0
00000894 I--Q-- 1 10h 1f0000 0 0 user metal:gold: 0
SERIAL FLAGS USAGE EXPY PERM UID GID TYPE DESCRIPTION: SUMMARY
00000001 I----- 39 perm 1f1f0000 0 0 keyring _uid_ses.0: 1/4
00000002 I----- 2 perm 1f1f0000 0 0 keyring _uid.0: empty
00000007 I----- 1 perm 1f1f0000 0 0 keyring _pid.1: empty
0000018d I----- 1 perm 1f1f0000 0 0 keyring _pid.412: empty
000004d2 I--Q-- 1 perm 1f1f0000 32 -1 keyring _uid.32: 1/4
000004d3 I--Q-- 3 perm 1f1f0000 32 -1 keyring _uid_ses.32: empty
00000892 I--QU- 1 perm 1f000000 0 0 user metal:copper: 0
00000893 I--Q-N 1 35s 1f1f0000 0 0 user metal:silver: 0
00000894 I--Q-- 1 10h 001f0000 0 0 user metal:gold: 0
The flags are:
......@@ -637,6 +637,34 @@ call, and the key released upon close. How to deal with conflicting keys due to
two different users opening the same file is left to the filesystem author to
solve.
Note that there are two different types of pointers to keys that may be
encountered:
(*) struct key *
This simply points to the key structure itself. Key structures will be at
least four-byte aligned.
(*) key_ref_t
This is equivalent to a struct key *, but the least significant bit is set
if the caller "possesses" the key. By "possession" it is meant that the
calling processes has a searchable link to the key from one of its
keyrings. There are three functions for dealing with these:
key_ref_t make_key_ref(const struct key *key,
unsigned long possession);
struct key *key_ref_to_ptr(const key_ref_t key_ref);
unsigned long is_key_possessed(const key_ref_t key_ref);
The first function constructs a key reference from a key pointer and
possession information (which must be 0 or 1 and not any other value).
The second function retrieves the key pointer from a reference and the
third retrieves the possession flag.
When accessing a key's payload contents, certain precautions must be taken to
prevent access vs modification races. See the section "Notes on accessing
payload contents" for more information.
......@@ -665,7 +693,11 @@ payload contents" for more information.
void key_put(struct key *key);
This can be called from interrupt context. If CONFIG_KEYS is not set then
Or:
void key_ref_put(key_ref_t key_ref);
These can be called from interrupt context. If CONFIG_KEYS is not set then
the argument will not be parsed.
......@@ -689,13 +721,17 @@ payload contents" for more information.
(*) If a keyring was found in the search, this can be further searched by:
struct key *keyring_search(struct key *keyring,
const struct key_type *type,
const char *description)
key_ref_t keyring_search(key_ref_t keyring_ref,
const struct key_type *type,
const char *description)
This searches the keyring tree specified for a matching key. Error ENOKEY
is returned upon failure. If successful, the returned key will need to be
released.
is returned upon failure (use IS_ERR/PTR_ERR to determine). If successful,
the returned key will need to be released.
The possession attribute from the keyring reference is used to control
access through the permissions mask and is propagated to the returned key
reference pointer if successful.
(*) To check the validity of a key, this function can be called:
......@@ -732,7 +768,7 @@ More complex payload contents must be allocated and a pointer to them set in
key->payload.data. One of the following ways must be selected to access the
data:
(1) Unmodifyable key type.
(1) Unmodifiable key type.
If the key type does not have a modify method, then the key's payload can
be accessed without any form of locking, provided that it's known to be
......
......@@ -42,11 +42,14 @@ struct keyring_list {
/*
* check to see whether permission is granted to use a key in the desired way
*/
static inline int key_permission(const struct key *key, key_perm_t perm)
static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
{
struct key *key = key_ref_to_ptr(key_ref);
key_perm_t kperm;
if (key->uid == current->fsuid)
if (is_key_possessed(key_ref))
kperm = key->perm >> 24;
else if (key->uid == current->fsuid)
kperm = key->perm >> 16;
else if (key->gid != -1 &&
key->perm & KEY_GRP_ALL &&
......@@ -65,11 +68,14 @@ static inline int key_permission(const struct key *key, key_perm_t perm)
* check to see whether permission is granted to use a key in at least one of
* the desired ways
*/
static inline int key_any_permission(const struct key *key, key_perm_t perm)
static inline int key_any_permission(const key_ref_t key_ref, key_perm_t perm)
{
struct key *key = key_ref_to_ptr(key_ref);
key_perm_t kperm;
if (key->uid == current->fsuid)
if (is_key_possessed(key_ref))
kperm = key->perm >> 24;
else if (key->uid == current->fsuid)
kperm = key->perm >> 16;
else if (key->gid != -1 &&
key->perm & KEY_GRP_ALL &&
......@@ -94,13 +100,17 @@ static inline int key_task_groups_search(struct task_struct *tsk, gid_t gid)
return ret;
}
static inline int key_task_permission(const struct key *key,
static inline int key_task_permission(const key_ref_t key_ref,
struct task_struct *context,
key_perm_t perm)
{
struct key *key = key_ref_to_ptr(key_ref);
key_perm_t kperm;
if (key->uid == context->fsuid) {
if (is_key_possessed(key_ref)) {
kperm = key->perm >> 24;
}
else if (key->uid == context->fsuid) {
kperm = key->perm >> 16;
}
else if (key->gid != -1 &&
......@@ -121,9 +131,9 @@ static inline int key_task_permission(const struct key *key,
}
extern struct key *lookup_user_key(struct task_struct *context,
key_serial_t id, int create, int partial,
key_perm_t perm);
extern key_ref_t lookup_user_key(struct task_struct *context,
key_serial_t id, int create, int partial,
key_perm_t perm);
extern long join_session_keyring(const char *name);
......
......@@ -35,11 +35,18 @@ struct key;
#undef KEY_DEBUGGING
#define KEY_USR_VIEW 0x00010000 /* user can view a key's attributes */
#define KEY_USR_READ 0x00020000 /* user can read key payload / view keyring */
#define KEY_USR_WRITE 0x00040000 /* user can update key payload / add link to keyring */
#define KEY_USR_SEARCH 0x00080000 /* user can find a key in search / search a keyring */
#define KEY_USR_LINK 0x00100000 /* user can create a link to a key/keyring */
#define KEY_POS_VIEW 0x01000000 /* possessor can view a key's attributes */
#define KEY_POS_READ 0x02000000 /* possessor can read key payload / view keyring */
#define KEY_POS_WRITE 0x04000000 /* possessor can update key payload / add link to keyring */
#define KEY_POS_SEARCH 0x08000000 /* possessor can find a key in search / search a keyring */
#define KEY_POS_LINK 0x10000000 /* possessor can create a link to a key/keyring */
#define KEY_POS_ALL 0x1f000000
#define KEY_USR_VIEW 0x00010000 /* user permissions... */
#define KEY_USR_READ 0x00020000
#define KEY_USR_WRITE 0x00040000
#define KEY_USR_SEARCH 0x00080000
#define KEY_USR_LINK 0x00100000
#define KEY_USR_ALL 0x001f0000
#define KEY_GRP_VIEW 0x00000100 /* group permissions... */
......@@ -65,6 +72,38 @@ struct key_owner;
struct keyring_list;
struct keyring_name;
/*****************************************************************************/
/*
* key reference with possession attribute handling
*
* NOTE! key_ref_t is a typedef'd pointer to a type that is not actually
* defined. This is because we abuse the bottom bit of the reference to carry a
* flag to indicate whether the calling process possesses that key in one of
* its keyrings.
*
* the key_ref_t has been made a separate type so that the compiler can reject
* attempts to dereference it without proper conversion.
*
* the three functions are used to assemble and disassemble references
*/
typedef struct __key_reference_with_attributes *key_ref_t;
static inline key_ref_t make_key_ref(const struct key *key,
unsigned long possession)
{
return (key_ref_t) ((unsigned long) key | possession);
}
static inline struct key *key_ref_to_ptr(const key_ref_t key_ref)
{
return (struct key *) ((unsigned long) key_ref & ~1UL);
}
static inline unsigned long is_key_possessed(const key_ref_t key_ref)
{
return (unsigned long) key_ref & 1UL;
}
/*****************************************************************************/
/*
* authentication token / access credential / keyring
......@@ -215,20 +254,25 @@ static inline struct key *key_get(struct key *key)
return key;
}
static inline void key_ref_put(key_ref_t key_ref)
{
key_put(key_ref_to_ptr(key_ref));
}
extern struct key *request_key(struct key_type *type,
const char *description,
const char *callout_info);
extern int key_validate(struct key *key);
extern struct key *key_create_or_update(struct key *keyring,
const char *type,
const char *description,
const void *payload,
size_t plen,
int not_in_quota);
extern key_ref_t key_create_or_update(key_ref_t keyring,
const char *type,
const char *description,
const void *payload,
size_t plen,
int not_in_quota);
extern int key_update(struct key *key,
extern int key_update(key_ref_t key,
const void *payload,
size_t plen);
......@@ -243,9 +287,9 @@ extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
extern int keyring_clear(struct key *keyring);
extern struct key *keyring_search(struct key *keyring,
struct key_type *type,
const char *description);
extern key_ref_t keyring_search(key_ref_t keyring,
struct key_type *type,
const char *description);
extern int keyring_add_key(struct key *keyring,
struct key *key);
......@@ -285,6 +329,10 @@ extern void key_init(void);
#define key_serial(k) 0
#define key_get(k) ({ NULL; })
#define key_put(k) do { } while(0)
#define key_ref_put(k) do { } while(0)
#define make_key_ref(k) ({ NULL; })
#define key_ref_to_ptr(k) ({ NULL; })
#define is_key_possessed(k) 0
#define alloc_uid_keyring(u) 0
#define switch_uid_keyring(u) do { } while(0)
#define __install_session_keyring(t, k) ({ NULL; })
......
......@@ -71,26 +71,26 @@ extern void keyring_publish_name(struct key *keyring);
extern int __key_link(struct key *keyring, struct key *key);
extern struct key *__keyring_search_one(struct key *keyring,
const struct key_type *type,
const char *description,
key_perm_t perm);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *type,
const char *description,
key_perm_t perm);
extern struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id);
typedef int (*key_match_func_t)(const struct key *, const void *);
extern struct key *keyring_search_aux(struct key *keyring,
struct task_struct *tsk,
struct key_type *type,
const void *description,
key_match_func_t match);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct task_struct *tsk,
struct key_type *type,
const void *description,
key_match_func_t match);
extern struct key *search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
struct task_struct *tsk);
extern key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
struct task_struct *tsk);
extern struct key *find_keyring_by_name(const char *name, key_serial_t bound);
......
......@@ -693,14 +693,15 @@ void key_type_put(struct key_type *ktype)
* - the key has an incremented refcount
* - we need to put the key if we get an error
*/
static inline struct key *__key_update(struct key *key, const void *payload,
size_t plen)
static inline key_ref_t __key_update(key_ref_t key_ref,
const void *payload, size_t plen)
{
struct key *key = key_ref_to_ptr(key_ref);
int ret;
/* need write permission on the key to update it */
ret = -EACCES;
if (!key_permission(key, KEY_WRITE))
if (!key_permission(key_ref, KEY_WRITE))
goto error;
ret = -EEXIST;
......@@ -719,12 +720,12 @@ static inline struct key *__key_update(struct key *key, const void *payload,
if (ret < 0)
goto error;
out:
return key;
out:
return key_ref;
error:
error:
key_put(key);
key = ERR_PTR(ret);
key_ref = ERR_PTR(ret);
goto out;
} /* end __key_update() */
......@@ -734,52 +735,56 @@ static inline struct key *__key_update(struct key *key, const void *payload,
* search the specified keyring for a key of the same description; if one is
* found, update it, otherwise add a new one
*/
struct key *key_create_or_update(struct key *keyring,
const char *type,
const char *description,
const void *payload,
size_t plen,
int not_in_quota)
key_ref_t key_create_or_update(key_ref_t keyring_ref,
const char *type,
const char *description,
const void *payload,
size_t plen,
int not_in_quota)
{
struct key_type *ktype;
struct key *key = NULL;
struct key *keyring, *key = NULL;
key_perm_t perm;
key_ref_t key_ref;
int ret;
key_check(keyring);
/* look up the key type to see if it's one of the registered kernel
* types */
ktype = key_type_lookup(type);
if (IS_ERR(ktype)) {
key = ERR_PTR(-ENODEV);
key_ref = ERR_PTR(-ENODEV);
goto error;
}
ret = -EINVAL;
key_ref = ERR_PTR(-EINVAL);
if (!ktype->match || !ktype->instantiate)
goto error_2;
keyring = key_ref_to_ptr(keyring_ref);
key_check(keyring);
down_write(&keyring->sem);
/* if we're going to allocate a new key, we're going to have
* to modify the keyring */
key_ref = ERR_PTR(-EACCES);
if (!key_permission(keyring_ref, KEY_WRITE))
goto error_3;
/* search for an existing key of the same type and description in the
* destination keyring
*/
down_write(&keyring->sem);
key = __keyring_search_one(keyring, ktype, description, 0);
if (!IS_ERR(key))
key_ref = __keyring_search_one(keyring_ref, ktype, description, 0);
if (!IS_ERR(key_ref))
goto found_matching_key;
/* if we're going to allocate a new key, we're going to have to modify
* the keyring */
ret = -EACCES;
if (!key_permission(keyring, KEY_WRITE))
goto error_3;
/* decide on the permissions we want */
perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK;
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK;
perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK;
if (ktype->read)
perm |= KEY_USR_READ;
perm |= KEY_POS_READ | KEY_USR_READ;
if (ktype == &key_type_keyring || ktype->update)
perm |= KEY_USR_WRITE;
......@@ -788,7 +793,7 @@ struct key *key_create_or_update(struct key *keyring,
key = key_alloc(ktype, description, current->fsuid, current->fsgid,
perm, not_in_quota);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
key_ref = ERR_PTR(PTR_ERR(key));
goto error_3;
}
......@@ -796,15 +801,18 @@ struct key *key_create_or_update(struct key *keyring,
ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL);
if (ret < 0) {
key_put(key);
key = ERR_PTR(ret);
key_ref = ERR_PTR(ret);
goto error_3;
}
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_3:
up_write(&keyring->sem);
error_2:
key_type_put(ktype);
error:
return key;
return key_ref;
found_matching_key:
/* we found a matching key, so we're going to try to update it
......@@ -813,7 +821,7 @@ struct key *key_create_or_update(struct key *keyring,
up_write(&keyring->sem);
key_type_put(ktype);
key = __key_update(key, payload, plen);
key_ref = __key_update(key_ref, payload, plen);
goto error;
} /* end key_create_or_update() */
......@@ -824,15 +832,16 @@ EXPORT_SYMBOL(key_create_or_update);
/*
* update a key
*/
int key_update(struct key *key, const void *payload, size_t plen)
int key_update(key_ref_t key_ref, const void *payload, size_t plen)
{
struct key *key = key_ref_to_ptr(key_ref);
int ret;
key_check(key);
/* the key must be writable */
ret = -EACCES;
if (!key_permission(key, KEY_WRITE))
if (!key_permission(key_ref, KEY_WRITE))
goto error;
/* attempt to update it if supported */
......
......@@ -34,7 +34,7 @@ asmlinkage long sys_add_key(const char __user *_type,
size_t plen,
key_serial_t ringid)
{
struct key *keyring, *key;
key_ref_t keyring_ref, key_ref;
char type[32], *description;
void *payload;
long dlen, ret;
......@@ -86,25 +86,25 @@ asmlinkage long sys_add_key(const char __user *_type,
}
/* find the target keyring (which must be writable) */
keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error3;
}
/* create or update the requested key and add it to the target
* keyring */
key = key_create_or_update(keyring, type, description,
payload, plen, 0);
if (!IS_ERR(key)) {
ret = key->serial;
key_put(key);
key_ref = key_create_or_update(keyring_ref, type, description,
payload, plen, 0);
if (!IS_ERR(key_ref)) {
ret = key_ref_to_ptr(key_ref)->serial;
key_ref_put(key_ref);
}
else {
ret = PTR_ERR(key);
ret = PTR_ERR(key_ref);
}
key_put(keyring);
key_ref_put(keyring_ref);
error3:
kfree(payload);
error2:
......@@ -131,7 +131,8 @@ asmlinkage long sys_request_key(const char __user *_type,
key_serial_t destringid)
{
struct key_type *ktype;
struct key *key, *dest;
struct key *key;
key_ref_t dest_ref;
char type[32], *description, *callout_info;
long dlen, ret;
......@@ -187,11 +188,11 @@ asmlinkage long sys_request_key(const char __user *_type,
}
/* get the destination keyring if specified */
dest = NULL;
dest_ref = NULL;
if (destringid) {
dest = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
if (IS_ERR(dest)) {
ret = PTR_ERR(dest);
dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref);
goto error3;
}
}
......@@ -204,7 +205,8 @@ asmlinkage long sys_request_key(const char __user *_type,
}
/* do the search */
key = request_key_and_link(ktype, description, callout_info, dest);
key = request_key_and_link(ktype, description, callout_info,
key_ref_to_ptr(dest_ref));
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error5;
......@@ -216,7 +218,7 @@ asmlinkage long sys_request_key(const char __user *_type,
error5:
key_type_put(ktype);
error4:
key_put(dest);
key_ref_put(dest_ref);
error3:
kfree(callout_info);
error2:
......@@ -234,17 +236,17 @@ asmlinkage long sys_request_key(const char __user *_type,
*/
long keyctl_get_keyring_ID(key_serial_t id, int create)
{
struct key *key;
key_ref_t key_ref;
long ret;
key = lookup_user_key(NULL, id, create, 0, KEY_SEARCH);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
}
ret = key->serial;
key_put(key);
ret = key_ref_to_ptr(key_ref)->serial;
key_ref_put(key_ref);
error:
return ret;
......@@ -302,7 +304,7 @@ long keyctl_update_key(key_serial_t id,
const void __user *_payload,
size_t plen)
{
struct key *key;
key_ref_t key_ref;