diff options
author | David Howells <dhowells@redhat.com> | 2005-10-30 15:02:44 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-30 17:37:23 -0800 |
commit | 29db9190634067c5a328ee5fcc2890251b836b4b (patch) | |
tree | 07ec242789230824f1fa8bcbbe681fd5bf166fa8 /security | |
parent | 2aa349f6e37ce030060c994d3aebbff4ab703565 (diff) | |
download | kernel-common-29db9190634067c5a328ee5fcc2890251b836b4b.tar.gz kernel-common-29db9190634067c5a328ee5fcc2890251b836b4b.tar.bz2 kernel-common-29db9190634067c5a328ee5fcc2890251b836b4b.zip |
[PATCH] Keys: Add LSM hooks for key management [try #3]
The attached patch adds LSM hooks for key management facilities. The notable
changes are:
(1) The key struct now supports a security pointer for the use of security
modules. This will permit key labelling and restrictions on which
programs may access a key.
(2) Security modules get a chance to note (or abort) the allocation of a key.
(3) The key permission checking can now be enhanced by the security modules;
the permissions check consults LSM if all other checks bear out.
(4) The key permissions checking functions now return an error code rather
than a boolean value.
(5) An extra permission has been added to govern the modification of
attributes (UID, GID, permissions).
Note that there isn't an LSM hook specifically for each keyctl() operation,
but rather the permissions hook allows control of individual operations based
on the permission request bits.
Key management access control through LSM is enabled by automatically if both
CONFIG_KEYS and CONFIG_SECURITY are enabled.
This should be applied on top of the patch ensubjected:
[PATCH] Keys: Possessor permissions should be additive
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Chris Wright <chrisw@osdl.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'security')
-rw-r--r-- | security/dummy.c | 23 | ||||
-rw-r--r-- | security/keys/key.c | 56 | ||||
-rw-r--r-- | security/keys/keyctl.c | 13 | ||||
-rw-r--r-- | security/keys/keyring.c | 21 | ||||
-rw-r--r-- | security/keys/permission.c | 7 | ||||
-rw-r--r-- | security/keys/process_keys.c | 9 |
6 files changed, 94 insertions, 35 deletions
diff --git a/security/dummy.c b/security/dummy.c index 2a0337a52d32..3ca5f2b828a0 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -803,6 +803,23 @@ static int dummy_setprocattr(struct task_struct *p, char *name, void *value, siz return -EINVAL; } +#ifdef CONFIG_KEYS +static inline int dummy_key_alloc(struct key *key) +{ + return 0; +} + +static inline void dummy_key_free(struct key *key) +{ +} + +static inline int dummy_key_permission(key_ref_t key_ref, + struct task_struct *context, + key_perm_t perm) +{ + return 0; +} +#endif /* CONFIG_KEYS */ struct security_operations dummy_security_ops; @@ -954,5 +971,11 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sk_alloc_security); set_to_dummy_if_null(ops, sk_free_security); #endif /* CONFIG_SECURITY_NETWORK */ +#ifdef CONFIG_KEYS + set_to_dummy_if_null(ops, key_alloc); + set_to_dummy_if_null(ops, key_free); + set_to_dummy_if_null(ops, key_permission); +#endif /* CONFIG_KEYS */ + } diff --git a/security/keys/key.c b/security/keys/key.c index 2182be9e9309..ccde17aff616 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1,6 +1,6 @@ /* key.c: basic authentication token and access key management * - * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/security.h> #include <linux/workqueue.h> #include <linux/err.h> #include "internal.h" @@ -253,6 +254,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, struct key_user *user = NULL; struct key *key; size_t desclen, quotalen; + int ret; key = ERR_PTR(-EINVAL); if (!desc || !*desc) @@ -305,6 +307,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->flags = 0; key->expiry = 0; key->payload.data = NULL; + key->security = NULL; if (!not_in_quota) key->flags |= 1 << KEY_FLAG_IN_QUOTA; @@ -315,16 +318,34 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->magic = KEY_DEBUG_MAGIC; #endif + /* let the security module know about the key */ + ret = security_key_alloc(key); + if (ret < 0) + goto security_error; + /* publish the key by giving it a serial number */ atomic_inc(&user->nkeys); key_alloc_serial(key); - error: +error: return key; - no_memory_3: +security_error: + kfree(key->description); + kmem_cache_free(key_jar, key); + if (!not_in_quota) { + spin_lock(&user->lock); + user->qnkeys--; + user->qnbytes -= quotalen; + spin_unlock(&user->lock); + } + key_user_put(user); + key = ERR_PTR(ret); + goto error; + +no_memory_3: kmem_cache_free(key_jar, key); - no_memory_2: +no_memory_2: if (!not_in_quota) { spin_lock(&user->lock); user->qnkeys--; @@ -332,11 +353,11 @@ struct key *key_alloc(struct key_type *type, const char *desc, spin_unlock(&user->lock); } key_user_put(user); - no_memory_1: +no_memory_1: key = ERR_PTR(-ENOMEM); goto error; - no_quota: +no_quota: spin_unlock(&user->lock); key_user_put(user); key = ERR_PTR(-EDQUOT); @@ -556,6 +577,8 @@ static void key_cleanup(void *data) key_check(key); + security_key_free(key); + /* deal with the user's key tracking and quota */ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { spin_lock(&key->user->lock); @@ -700,8 +723,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref, int ret; /* need write permission on the key to update it */ - ret = -EACCES; - if (!key_permission(key_ref, KEY_WRITE)) + ret = key_permission(key_ref, KEY_WRITE); + if (ret < 0) goto error; ret = -EEXIST; @@ -711,7 +734,6 @@ static inline key_ref_t __key_update(key_ref_t key_ref, down_write(&key->sem); ret = key->type->update(key, payload, plen); - if (ret == 0) /* updating a negative key instantiates it */ clear_bit(KEY_FLAG_NEGATIVE, &key->flags); @@ -768,9 +790,11 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* 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)) + ret = key_permission(keyring_ref, KEY_WRITE); + if (ret < 0) { + key_ref = ERR_PTR(ret); goto error_3; + } /* search for an existing key of the same type and description in the * destination keyring @@ -780,8 +804,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, goto found_matching_key; /* decide on the permissions we want */ - perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK; - perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK; + perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; + perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR; if (ktype->read) perm |= KEY_POS_READ | KEY_USR_READ; @@ -840,16 +864,16 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) key_check(key); /* the key must be writable */ - ret = -EACCES; - if (!key_permission(key_ref, KEY_WRITE)) + ret = key_permission(key_ref, KEY_WRITE); + if (ret < 0) goto error; /* attempt to update it if supported */ ret = -EOPNOTSUPP; if (key->type->update) { down_write(&key->sem); - ret = key->type->update(key, payload, plen); + ret = key->type->update(key, payload, plen); if (ret == 0) /* updating a negative key instantiates it */ clear_bit(KEY_FLAG_NEGATIVE, &key->flags); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 4c670ee6acf9..b7a468fabdf9 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -624,8 +624,8 @@ long keyctl_keyring_search(key_serial_t ringid, /* link the resulting key to the destination keyring if we can */ if (dest_ref) { - ret = -EACCES; - if (!key_permission(key_ref, KEY_LINK)) + ret = key_permission(key_ref, KEY_LINK); + if (ret < 0) goto error6; ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref)); @@ -676,8 +676,11 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) key = key_ref_to_ptr(key_ref); /* see if we can read it directly */ - if (key_permission(key_ref, KEY_READ)) + ret = key_permission(key_ref, KEY_READ); + if (ret == 0) goto can_read_key; + if (ret != -EACCES) + goto error; /* we can't; see if it's searchable from this process's keyrings * - we automatically take account of the fact that it may be @@ -726,7 +729,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) if (uid == (uid_t) -1 && gid == (gid_t) -1) goto error; - key_ref = lookup_user_key(NULL, id, 1, 1, 0); + key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -786,7 +789,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) goto error; - key_ref = lookup_user_key(NULL, id, 1, 1, 0); + key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 0639396dd441..e1cc4dd79012 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/security.h> #include <linux/seq_file.h> #include <linux/err.h> #include <asm/uaccess.h> @@ -309,7 +310,9 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, KEY_POS_ALL | KEY_USR_ALL, not_in_quota); + uid, gid, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, + not_in_quota); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); @@ -359,9 +362,11 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, key_check(keyring); /* top keyring must have search permission to begin the search */ - key_ref = ERR_PTR(-EACCES); - if (!key_task_permission(keyring_ref, context, KEY_SEARCH)) + err = key_task_permission(keyring_ref, context, KEY_SEARCH); + if (err < 0) { + key_ref = ERR_PTR(err); goto error; + } key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) @@ -402,8 +407,8 @@ descend: continue; /* key must have search permissions */ - if (!key_task_permission(make_key_ref(key, possessed), - context, KEY_SEARCH)) + if (key_task_permission(make_key_ref(key, possessed), + context, KEY_SEARCH) < 0) continue; /* we set a different error code if we find a negative key */ @@ -430,7 +435,7 @@ ascend: continue; if (!key_task_permission(make_key_ref(key, possessed), - context, KEY_SEARCH)) + context, KEY_SEARCH) < 0) continue; /* stack the current position */ @@ -521,7 +526,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, (!key->type->match || key->type->match(key, description)) && key_permission(make_key_ref(key, possessed), - perm) && + perm) < 0 && !test_bit(KEY_FLAG_REVOKED, &key->flags) ) goto found; @@ -617,7 +622,7 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound) continue; if (!key_permission(make_key_ref(keyring, 0), - KEY_SEARCH)) + KEY_SEARCH) < 0) continue; /* found a potential candidate, but we still need to diff --git a/security/keys/permission.c b/security/keys/permission.c index 03db073ba45c..e7f579c0eaf5 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -10,6 +10,7 @@ */ #include <linux/module.h> +#include <linux/security.h> #include "internal.h" /*****************************************************************************/ @@ -63,7 +64,11 @@ use_these_perms: kperm = kperm & perm & KEY_ALL; - return kperm == perm; + if (kperm != perm) + return -EACCES; + + /* let LSM be the final arbiter */ + return security_key_permission(key_ref, context, perm); } /* end key_task_permission() */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index d42d2158ce13..566b1cc0118a 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -39,7 +39,7 @@ struct key root_user_keyring = { .type = &key_type_keyring, .user = &root_key_user, .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), - .perm = KEY_POS_ALL | KEY_USR_ALL, + .perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid.0", #ifdef KEY_DEBUGGING @@ -54,7 +54,7 @@ struct key root_session_keyring = { .type = &key_type_keyring, .user = &root_key_user, .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), - .perm = KEY_POS_ALL | KEY_USR_ALL, + .perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid_ses.0", #ifdef KEY_DEBUGGING @@ -666,9 +666,8 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id, goto invalid_key; /* check the permissions */ - ret = -EACCES; - - if (!key_task_permission(key_ref, context, perm)) + ret = key_task_permission(key_ref, context, perm); + if (ret < 0) goto invalid_key; error: |