diff options
Diffstat (limited to 'security/keys')
-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 | ||||
-rw-r--r-- | security/keys/user_defined.c | 49 |
6 files changed, 95 insertions, 60 deletions
diff --git a/security/keys/key.c b/security/keys/key.c index 2182be9e930..ccde17aff61 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 4c670ee6acf..b7a468fabdf 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 0639396dd44..e1cc4dd7901 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 03db073ba45..e7f579c0eaf 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 d42d2158ce1..566b1cc0118 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: diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index e446acba73d..cbda3b2780a 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -15,18 +15,10 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/err.h> +#include <keys/user-type.h> #include <asm/uaccess.h> #include "internal.h" -static int user_instantiate(struct key *key, const void *data, size_t datalen); -static int user_duplicate(struct key *key, const struct key *source); -static int user_update(struct key *key, const void *data, size_t datalen); -static int user_match(const struct key *key, const void *criterion); -static void user_destroy(struct key *key); -static void user_describe(const struct key *user, struct seq_file *m); -static long user_read(const struct key *key, - char __user *buffer, size_t buflen); - /* * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload @@ -42,19 +34,13 @@ struct key_type key_type_user = { .read = user_read, }; -struct user_key_payload { - struct rcu_head rcu; /* RCU destructor */ - unsigned short datalen; /* length of this data */ - char data[0]; /* actual data */ -}; - EXPORT_SYMBOL_GPL(key_type_user); /*****************************************************************************/ /* * instantiate a user defined key */ -static int user_instantiate(struct key *key, const void *data, size_t datalen) +int user_instantiate(struct key *key, const void *data, size_t datalen) { struct user_key_payload *upayload; int ret; @@ -78,18 +64,20 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) rcu_assign_pointer(key->payload.data, upayload); ret = 0; - error: +error: return ret; } /* end user_instantiate() */ +EXPORT_SYMBOL_GPL(user_instantiate); + /*****************************************************************************/ /* * duplicate a user defined key * - both keys' semaphores are locked against further modification * - the new key cannot yet be accessed */ -static int user_duplicate(struct key *key, const struct key *source) +int user_duplicate(struct key *key, const struct key *source) { struct user_key_payload *upayload, *spayload; int ret; @@ -112,6 +100,8 @@ static int user_duplicate(struct key *key, const struct key *source) } /* end user_duplicate() */ +EXPORT_SYMBOL_GPL(user_duplicate); + /*****************************************************************************/ /* * dispose of the old data from an updated user defined key @@ -131,7 +121,7 @@ static void user_update_rcu_disposal(struct rcu_head *rcu) * update a user defined key * - the key's semaphore is write-locked */ -static int user_update(struct key *key, const void *data, size_t datalen) +int user_update(struct key *key, const void *data, size_t datalen) { struct user_key_payload *upayload, *zap; int ret; @@ -163,26 +153,30 @@ static int user_update(struct key *key, const void *data, size_t datalen) call_rcu(&zap->rcu, user_update_rcu_disposal); - error: +error: return ret; } /* end user_update() */ +EXPORT_SYMBOL_GPL(user_update); + /*****************************************************************************/ /* * match users on their name */ -static int user_match(const struct key *key, const void *description) +int user_match(const struct key *key, const void *description) { return strcmp(key->description, description) == 0; } /* end user_match() */ +EXPORT_SYMBOL_GPL(user_match); + /*****************************************************************************/ /* * dispose of the data dangling from the corpse of a user */ -static void user_destroy(struct key *key) +void user_destroy(struct key *key) { struct user_key_payload *upayload = key->payload.data; @@ -190,11 +184,13 @@ static void user_destroy(struct key *key) } /* end user_destroy() */ +EXPORT_SYMBOL_GPL(user_destroy); + /*****************************************************************************/ /* * describe the user key */ -static 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); @@ -202,13 +198,14 @@ static void user_describe(const struct key *key, struct seq_file *m) } /* end user_describe() */ +EXPORT_SYMBOL_GPL(user_describe); + /*****************************************************************************/ /* * read the key data * - the key's semaphore is read-locked */ -static long user_read(const struct key *key, - char __user *buffer, size_t buflen) +long user_read(const struct key *key, char __user *buffer, size_t buflen) { struct user_key_payload *upayload; long ret; @@ -228,3 +225,5 @@ static long user_read(const struct key *key, return ret; } /* end user_read() */ + +EXPORT_SYMBOL_GPL(user_read); |