summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig13
-rw-r--r--security/capability.c6
-rw-r--r--security/commoncap.c1
-rw-r--r--security/dummy.c46
-rw-r--r--security/inode.c8
-rw-r--r--security/keys/compat.c6
-rw-r--r--security/keys/internal.h6
-rw-r--r--security/keys/key.c58
-rw-r--r--security/keys/keyctl.c152
-rw-r--r--security/keys/keyring.c200
-rw-r--r--security/keys/permission.c32
-rw-r--r--security/keys/process_keys.c71
-rw-r--r--security/keys/request_key.c108
-rw-r--r--security/keys/request_key_auth.c192
-rw-r--r--security/keys/user_defined.c33
-rw-r--r--security/security.c1
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/avc.c5
-rw-r--r--security/selinux/hooks.c43
-rw-r--r--security/selinux/include/av_perm_to_string.h1
-rw-r--r--security/selinux/include/av_permissions.h1
-rw-r--r--security/selinux/include/xfrm.h54
-rw-r--r--security/selinux/selinuxfs.c6
-rw-r--r--security/selinux/ss/avtab.c2
-rw-r--r--security/selinux/ss/policydb.c2
-rw-r--r--security/selinux/ss/services.c6
-rw-r--r--security/selinux/xfrm.c305
27 files changed, 926 insertions, 434 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 64d3f1e9ca85..34f593410d57 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -54,6 +54,19 @@ config SECURITY_NETWORK
implement socket and networking access controls.
If you are unsure how to answer this question, answer N.
+config SECURITY_NETWORK_XFRM
+ bool "XFRM (IPSec) Networking Security Hooks"
+ depends on XFRM && SECURITY_NETWORK
+ help
+ This enables the XFRM (IPSec) networking security hooks.
+ If enabled, a security module can use these hooks to
+ implement per-packet access controls based on labels
+ derived from IPSec policy. Non-IPSec communications are
+ designated as unlabelled, and only sockets authorized
+ to communicate unlabelled data can send without using
+ IPSec.
+ If you are unsure how to answer this question, answer N.
+
config SECURITY_CAPABILITIES
tristate "Default Linux Capabilities"
depends on SECURITY
diff --git a/security/capability.c b/security/capability.c
index ec18d6075625..f9b35cc0b248 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -49,8 +49,6 @@ static struct security_operations capability_ops = {
.vm_enough_memory = cap_vm_enough_memory,
};
-#define MY_NAME __stringify(KBUILD_MODNAME)
-
/* flag to keep track of how we were registered */
static int secondary;
@@ -67,7 +65,7 @@ static int __init capability_init (void)
/* register ourselves with the security framework */
if (register_security (&capability_ops)) {
/* try registering with primary module */
- if (mod_reg_security (MY_NAME, &capability_ops)) {
+ if (mod_reg_security (KBUILD_MODNAME, &capability_ops)) {
printk (KERN_INFO "Failure registering capabilities "
"with primary security module.\n");
return -EINVAL;
@@ -85,7 +83,7 @@ static void __exit capability_exit (void)
return;
/* remove ourselves from the security framework */
if (secondary) {
- if (mod_unreg_security (MY_NAME, &capability_ops))
+ if (mod_unreg_security (KBUILD_MODNAME, &capability_ops))
printk (KERN_INFO "Failure unregistering capabilities "
"with primary module.\n");
return;
diff --git a/security/commoncap.c b/security/commoncap.c
index 04c12f58d656..8a6e097f99ea 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -7,6 +7,7 @@
*
*/
+#include <linux/capability.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
diff --git a/security/dummy.c b/security/dummy.c
index 3ca5f2b828a0..f1a5bd98bf10 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -14,6 +14,7 @@
#undef DEBUG
+#include <linux/capability.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -776,8 +777,42 @@ static inline int dummy_sk_alloc_security (struct sock *sk, int family, gfp_t pr
static inline void dummy_sk_free_security (struct sock *sk)
{
}
+
+static unsigned int dummy_sk_getsid(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static inline int dummy_xfrm_policy_clone_security(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ return 0;
+}
+
+static void dummy_xfrm_policy_free_security(struct xfrm_policy *xp)
+{
+}
+
+static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static void dummy_xfrm_state_free_security(struct xfrm_state *x)
+{
+}
+
+static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
static int dummy_register_security (const char *name, struct security_operations *ops)
{
return -EINVAL;
@@ -970,7 +1005,16 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, socket_getpeersec);
set_to_dummy_if_null(ops, sk_alloc_security);
set_to_dummy_if_null(ops, sk_free_security);
-#endif /* CONFIG_SECURITY_NETWORK */
+ set_to_dummy_if_null(ops, sk_getsid);
+ #endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
+ set_to_dummy_if_null(ops, xfrm_policy_clone_security);
+ set_to_dummy_if_null(ops, xfrm_policy_free_security);
+ set_to_dummy_if_null(ops, xfrm_state_alloc_security);
+ set_to_dummy_if_null(ops, xfrm_state_free_security);
+ set_to_dummy_if_null(ops, xfrm_policy_lookup);
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS
set_to_dummy_if_null(ops, key_alloc);
set_to_dummy_if_null(ops, key_free);
diff --git a/security/inode.c b/security/inode.c
index a5964502ae30..0f77b0223662 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -172,7 +172,7 @@ static int create_by_name(const char *name, mode_t mode,
return -EFAULT;
}
- down(&parent->d_inode->i_sem);
+ mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name));
if (!IS_ERR(dentry)) {
if ((mode & S_IFMT) == S_IFDIR)
@@ -181,7 +181,7 @@ static int create_by_name(const char *name, mode_t mode,
error = create(parent->d_inode, *dentry, mode);
} else
error = PTR_ERR(dentry);
- up(&parent->d_inode->i_sem);
+ mutex_unlock(&parent->d_inode->i_mutex);
return error;
}
@@ -302,7 +302,7 @@ void securityfs_remove(struct dentry *dentry)
if (!parent || !parent->d_inode)
return;
- down(&parent->d_inode->i_sem);
+ mutex_lock(&parent->d_inode->i_mutex);
if (positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode))
@@ -312,7 +312,7 @@ void securityfs_remove(struct dentry *dentry)
dput(dentry);
}
}
- up(&parent->d_inode->i_sem);
+ mutex_unlock(&parent->d_inode->i_mutex);
simple_release_fs(&mount, &mount_count);
}
EXPORT_SYMBOL_GPL(securityfs_remove);
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 3303673c636e..bcdb28533733 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -74,6 +74,12 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_SET_REQKEY_KEYRING:
return keyctl_set_reqkey_keyring(arg2);
+ case KEYCTL_SET_TIMEOUT:
+ return keyctl_set_timeout(arg2, arg3);
+
+ case KEYCTL_ASSUME_AUTHORITY:
+ return keyctl_assume_authority(arg2);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index db99ed434f3a..e066e6057955 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -25,7 +25,6 @@
#define kdebug(FMT, a...) do {} while(0)
#endif
-extern struct key_type key_type_dead;
extern struct key_type key_type_user;
/*****************************************************************************/
@@ -108,12 +107,13 @@ extern struct key *request_key_and_link(struct key_type *type,
struct request_key_auth {
struct key *target_key;
struct task_struct *context;
+ const char *callout_info;
pid_t pid;
};
extern struct key_type key_type_request_key_auth;
extern struct key *request_key_auth_new(struct key *target,
- struct key **_rkakey);
+ const char *callout_info);
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
@@ -137,6 +137,8 @@ extern long keyctl_instantiate_key(key_serial_t, const void __user *,
size_t, key_serial_t);
extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
extern long keyctl_set_reqkey_keyring(int);
+extern long keyctl_set_timeout(key_serial_t, unsigned);
+extern long keyctl_assume_authority(key_serial_t);
/*
diff --git a/security/keys/key.c b/security/keys/key.c
index 01bcfecb7eae..99781b798312 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -36,7 +36,7 @@ static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL);
DECLARE_RWSEM(key_construction_sem);
/* any key who's type gets unegistered will be re-typed to this */
-struct key_type key_type_dead = {
+static struct key_type key_type_dead = {
.name = "dead",
};
@@ -240,9 +240,9 @@ static inline void key_alloc_serial(struct key *key)
/*
* allocate a key of the specified type
* - update the user's quota to reflect the existence of the key
- * - called from a key-type operation with key_types_sem read-locked by either
- * key_create_or_update() or by key_duplicate(); this prevents unregistration
- * of the key type
+ * - called from a key-type operation with key_types_sem read-locked by
+ * key_create_or_update()
+ * - this prevents unregistration of the key type
* - upon return the key is as yet uninstantiated; the caller needs to either
* instantiate the key or discard it before returning
*/
@@ -889,56 +889,6 @@ EXPORT_SYMBOL(key_update);
/*****************************************************************************/
/*
- * duplicate a key, potentially with a revised description
- * - must be supported by the keytype (keyrings for instance can be duplicated)
- */
-struct key *key_duplicate(struct key *source, const char *desc)
-{
- struct key *key;
- int ret;
-
- key_check(source);
-
- if (!desc)
- desc = source->description;
-
- down_read(&key_types_sem);
-
- ret = -EINVAL;
- if (!source->type->duplicate)
- goto error;
-
- /* allocate and instantiate a key */
- key = key_alloc(source->type, desc, current->fsuid, current->fsgid,
- source->perm, 0);
- if (IS_ERR(key))
- goto error_k;
-
- down_read(&source->sem);
- ret = key->type->duplicate(key, source);
- up_read(&source->sem);
- if (ret < 0)
- goto error2;
-
- atomic_inc(&key->user->nikeys);
- set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
-
- error_k:
- up_read(&key_types_sem);
- out:
- return key;
-
- error2:
- key_put(key);
- error:
- up_read(&key_types_sem);
- key = ERR_PTR(ret);
- goto out;
-
-} /* end key_duplicate() */
-
-/*****************************************************************************/
-/*
* revoke a key
*/
void key_revoke(struct key *key)
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index b7a468fabdf9..90db5c76cf6e 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
+#include <linux/capability.h>
#include <linux/err.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -834,6 +835,17 @@ long keyctl_instantiate_key(key_serial_t id,
if (plen > 32767)
goto error;
+ /* the appropriate instantiation authorisation key must have been
+ * assumed before calling this */
+ ret = -EPERM;
+ instkey = current->request_key_auth;
+ if (!instkey)
+ goto error;
+
+ rka = instkey->payload.data;
+ if (rka->target_key->serial != id)
+ goto error;
+
/* pull the payload in if one was supplied */
payload = NULL;
@@ -848,15 +860,6 @@ long keyctl_instantiate_key(key_serial_t id,
goto error2;
}
- /* find the instantiation authorisation key */
- instkey = key_get_instantiation_authkey(id);
- if (IS_ERR(instkey)) {
- ret = PTR_ERR(instkey);
- goto error2;
- }
-
- rka = instkey->payload.data;
-
/* find the destination keyring amongst those belonging to the
* requesting task */
keyring_ref = NULL;
@@ -865,7 +868,7 @@ long keyctl_instantiate_key(key_serial_t id,
KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
- goto error3;
+ goto error2;
}
}
@@ -874,11 +877,17 @@ long keyctl_instantiate_key(key_serial_t id,
key_ref_to_ptr(keyring_ref), instkey);
key_ref_put(keyring_ref);
- error3:
- key_put(instkey);
- error2:
+
+ /* discard the assumed authority if it's just been disabled by
+ * instantiation of the key */
+ if (ret == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ }
+
+error2:
kfree(payload);
- error:
+error:
return ret;
} /* end keyctl_instantiate_key() */
@@ -895,14 +904,16 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
key_ref_t keyring_ref;
long ret;
- /* find the instantiation authorisation key */
- instkey = key_get_instantiation_authkey(id);
- if (IS_ERR(instkey)) {
- ret = PTR_ERR(instkey);
+ /* the appropriate instantiation authorisation key must have been
+ * assumed before calling this */
+ ret = -EPERM;
+ instkey = current->request_key_auth;
+ if (!instkey)
goto error;
- }
rka = instkey->payload.data;
+ if (rka->target_key->serial != id)
+ goto error;
/* find the destination keyring if present (which must also be
* writable) */
@@ -911,7 +922,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
- goto error2;
+ goto error;
}
}
@@ -920,9 +931,15 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
key_ref_to_ptr(keyring_ref), instkey);
key_ref_put(keyring_ref);
- error2:
- key_put(instkey);
- error:
+
+ /* discard the assumed authority if it's just been disabled by
+ * instantiation of the key */
+ if (ret == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ }
+
+error:
return ret;
} /* end keyctl_negate_key() */
@@ -967,6 +984,88 @@ long keyctl_set_reqkey_keyring(int reqkey_defl)
/*****************************************************************************/
/*
+ * set or clear the timeout for a key
+ */
+long keyctl_set_timeout(key_serial_t id, unsigned timeout)
+{
+ struct timespec now;
+ struct key *key;
+ key_ref_t key_ref;
+ time_t expiry;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* make the changes with the locks held to prevent races */
+ down_write(&key->sem);
+
+ expiry = 0;
+ if (timeout > 0) {
+ now = current_kernel_time();
+ expiry = now.tv_sec + timeout;
+ }
+
+ key->expiry = expiry;
+
+ up_write(&key->sem);
+ key_put(key);
+
+ ret = 0;
+error:
+ return ret;
+
+} /* end keyctl_set_timeout() */
+
+/*****************************************************************************/
+/*
+ * assume the authority to instantiate the specified key
+ */
+long keyctl_assume_authority(key_serial_t id)
+{
+ struct key *authkey;
+ long ret;
+
+ /* special key IDs aren't permitted */
+ ret = -EINVAL;
+ if (id < 0)
+ goto error;
+
+ /* we divest ourselves of authority if given an ID of 0 */
+ if (id == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ ret = 0;
+ goto error;
+ }
+
+ /* attempt to assume the authority temporarily granted to us whilst we
+ * instantiate the specified key
+ * - the authorisation key must be in the current task's keyrings
+ * somewhere
+ */
+ authkey = key_get_instantiation_authkey(id);
+ if (IS_ERR(authkey)) {
+ ret = PTR_ERR(authkey);
+ goto error;
+ }
+
+ key_put(current->request_key_auth);
+ current->request_key_auth = authkey;
+ ret = authkey->serial;
+
+error:
+ return ret;
+
+} /* end keyctl_assume_authority() */
+
+/*****************************************************************************/
+/*
* the key control system call
*/
asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
@@ -1038,6 +1137,13 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
case KEYCTL_SET_REQKEY_KEYRING:
return keyctl_set_reqkey_keyring(arg2);
+ case KEYCTL_SET_TIMEOUT:
+ return keyctl_set_timeout((key_serial_t) arg2,
+ (unsigned) arg3);
+
+ case KEYCTL_ASSUME_AUTHORITY:
+ return keyctl_assume_authority((key_serial_t) arg2);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index c7a0ab1cfda3..d65a180f888d 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -48,7 +48,6 @@ static inline unsigned keyring_hash(const char *desc)
*/
static int keyring_instantiate(struct key *keyring,
const void *data, size_t datalen);
-static int keyring_duplicate(struct key *keyring, const struct key *source);
static int keyring_match(const struct key *keyring, const void *criterion);
static void keyring_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m);
@@ -59,7 +58,6 @@ struct key_type key_type_keyring = {
.name = "keyring",
.def_datalen = sizeof(struct keyring_list),
.instantiate = keyring_instantiate,
- .duplicate = keyring_duplicate,
.match = keyring_match,
.destroy = keyring_destroy,
.describe = keyring_describe,
@@ -70,7 +68,7 @@ struct key_type key_type_keyring = {
* semaphore to serialise link/link calls to prevent two link calls in parallel
* introducing a cycle
*/
-DECLARE_RWSEM(keyring_serialise_link_sem);
+static DECLARE_RWSEM(keyring_serialise_link_sem);
/*****************************************************************************/
/*
@@ -120,68 +118,6 @@ static int keyring_instantiate(struct key *keyring,
/*****************************************************************************/
/*
- * duplicate the list of subscribed keys from a source keyring into this one
- */
-static int keyring_duplicate(struct key *keyring, const struct key *source)
-{
- struct keyring_list *sklist, *klist;
- unsigned max;
- size_t size;
- int loop, ret;
-
- const unsigned limit =
- (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key *);
-
- ret = 0;
-
- /* find out how many keys are currently linked */
- rcu_read_lock();
- sklist = rcu_dereference(source->payload.subscriptions);
- max = 0;
- if (sklist)
- max = sklist->nkeys;
- rcu_read_unlock();
-
- /* allocate a new payload and stuff load with key links */
- if (max > 0) {
- BUG_ON(max > limit);
-
- max = (max + 3) & ~3;
- if (max > limit)
- max = limit;
-
- ret = -ENOMEM;
- size = sizeof(*klist) + sizeof(struct key *) * max;
- klist = kmalloc(size, GFP_KERNEL);
- if (!klist)
- goto error;
-
- /* set links */
- rcu_read_lock();
- sklist = rcu_dereference(source->payload.subscriptions);
-
- klist->maxkeys = max;
- klist->nkeys = sklist->nkeys;
- memcpy(klist->keys,
- sklist->keys,
- sklist->nkeys * sizeof(struct key *));
-
- for (loop = klist->nkeys - 1; loop >= 0; loop--)
- atomic_inc(&klist->keys[loop]->usage);
-
- rcu_read_unlock();
-
- rcu_assign_pointer(keyring->payload.subscriptions, klist);
- ret = 0;
- }
-
- error:
- return ret;
-
-} /* end keyring_duplicate() */
-
-/*****************************************************************************/
-/*
* match keyrings on their name
*/
static int keyring_match(const struct key *keyring, const void *description)
@@ -526,7 +462,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) < 0 &&
+ perm) == 0 &&
!test_bit(KEY_FLAG_REVOKED, &key->flags)
)
goto found;
@@ -545,51 +481,6 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
/*****************************************************************************/
/*
- * search for an instantiation authorisation key matching a target key
- * - the RCU read lock must be held by the caller
- * - a target_id of zero specifies any valid token
- */
-struct key *keyring_search_instkey(struct key *keyring,
- key_serial_t target_id)
-{
- struct request_key_auth *rka;
- struct keyring_list *klist;
- struct key *instkey;
- int loop;
-
- klist = rcu_dereference(keyring->payload.subscriptions);
- if (klist) {
- for (loop = 0; loop < klist->nkeys; loop++) {
- instkey = klist->keys[loop];
-
- if (instkey->type != &key_type_request_key_auth)
- continue;
-
- rka = instkey->payload.data;
- if (target_id && rka->target_key->serial != target_id)
- continue;
-
- /* the auth key is revoked during instantiation */
- if (!test_bit(KEY_FLAG_REVOKED, &instkey->flags))
- goto found;
-
- instkey = ERR_PTR(-EKEYREVOKED);
- goto error;
- }
- }
-
- instkey = ERR_PTR(-EACCES);
- goto error;
-
-found:
- atomic_inc(&instkey->usage);
-error:
- return instkey;
-
-} /* end keyring_search_instkey() */
-
-/*****************************************************************************/
-/*
* find a keyring with the specified name
* - all named keyrings are searched
* - only find keyrings with search permission for the process
@@ -748,15 +639,31 @@ static void keyring_link_rcu_disposal(struct rcu_head *rcu)
/*****************************************************************************/
/*
+ * dispose of a keyring list after the RCU grace period, freeing the unlinked
+ * key
+ */
+static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
+{
+ struct keyring_list *klist =
+ container_of(rcu, struct keyring_list, rcu);
+
+ key_put(klist->keys[klist->delkey]);
+ kfree(klist);
+
+} /* end keyring_unlink_rcu_disposal() */
+
+/*****************************************************************************/
+/*
* link a key into to a keyring
* - must be called with the keyring's semaphore write-locked
+ * - discard already extant link to matching key if there is one
*/
int __key_link(struct key *keyring, struct key *key)
{
struct keyring_list *klist, *nklist;
unsigned max;
size_t size;
- int ret;
+ int loop, ret;
ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
@@ -778,6 +685,48 @@ int __key_link(struct key *keyring, struct key *key)
goto error2;
}
+ /* see if there's a matching key we can displace */
+ klist = keyring->payload.subscriptions;
+
+ if (klist && klist->nkeys > 0) {
+ struct key_type *type = key->type;
+
+ for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+ if (klist->keys[loop]->type == type &&
+ strcmp(klist->keys[loop]->description,
+ key->description) == 0
+ ) {
+ /* found a match - replace with new key */
+ size = sizeof(struct key *) * klist->maxkeys;
+ size += sizeof(*klist);
+ BUG_ON(size > PAGE_SIZE);
+
+ ret = -ENOMEM;
+ nklist = kmalloc(size, GFP_KERNEL);
+ if (!nklist)
+ goto error2;
+
+ memcpy(nklist, klist, size);
+
+ /* replace matched key */
+ atomic_inc(&key->usage);
+ nklist->keys[loop] = key;
+
+ rcu_assign_pointer(
+ keyring->payload.subscriptions,
+ nklist);
+
+ /* dispose of the old keyring list and the
+ * displaced key */
+ klist->delkey = loop;
+ call_rcu(&klist->rcu,
+ keyring_unlink_rcu_disposal);
+
+ goto done;
+ }
+ }
+ }
+
/* check that we aren't going to overrun the user's quota */
ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES);
@@ -794,8 +743,6 @@ int __key_link(struct key *keyring, struct key *key)
smp_wmb();
klist->nkeys++;
smp_wmb();
-
- ret = 0;
}
else {
/* grow the key list */
@@ -833,16 +780,16 @@ int __key_link(struct key *keyring, struct key *key)
/* dispose of the old keyring list */
if (klist)
call_rcu(&klist->rcu, keyring_link_rcu_disposal);
-
- ret = 0;
}
- error2:
+done:
+ ret = 0;
+error2:
up_write(&keyring_serialise_link_sem);
- error:
+error:
return ret;
- error3:
+error3:
/* undo the quota changes */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
@@ -873,21 +820,6 @@ EXPORT_SYMBOL(key_link);
/*****************************************************************************/
/*
- * dispose of a keyring list after the RCU grace period, freeing the unlinked
- * key
- */
-static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
-{
- struct keyring_list *klist =
- container_of(rcu, struct keyring_list, rcu);
-
- key_put(klist->keys[klist->delkey]);
- kfree(klist);
-
-} /* end keyring_unlink_rcu_disposal() */
-
-/*****************************************************************************/
-/*
* unlink the first link to a key from a keyring
*/
int key_unlink(struct key *keyring, struct key *key)
diff --git a/security/keys/permission.c b/security/keys/permission.c
index e7f579c0eaf5..3b41f9b52537 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -73,3 +73,35 @@ use_these_perms:
} /* end key_task_permission() */
EXPORT_SYMBOL(key_task_permission);
+
+/*****************************************************************************/
+/*
+ * validate a key
+ */
+int key_validate(struct key *key)
+{
+ struct timespec now;
+ int ret = 0;
+
+ if (key) {
+ /* check it's still accessible */
+ ret = -EKEYREVOKED;
+ if (test_bit(KEY_FLAG_REVOKED, &key->flags) ||
+ test_bit(KEY_FLAG_DEAD, &key->flags))
+ goto error;
+
+ /* check it hasn't expired */
+ ret = 0;
+ if (key->expiry) {
+ now = current_kernel_time();
+ if (now.tv_sec >= key->expiry)
+ ret = -EKEYEXPIRED;
+ }
+ }
+
+ error:
+ return ret;
+
+} /* end key_validate() */
+
+EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 566b1cc0118a..74cb79eb917e 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -270,9 +270,14 @@ int copy_thread_group_keys(struct task_struct *tsk)
int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
{
key_check(tsk->thread_keyring);
+ key_check(tsk->request_key_auth);
/* no thread keyring yet */
tsk->thread_keyring = NULL;
+
+ /* copy the request_key() authorisation for this thread */
+ key_get(tsk->request_key_auth);
+
return 0;
} /* end copy_keys() */
@@ -290,11 +295,12 @@ void exit_thread_group_keys(struct signal_struct *tg)
/*****************************************************************************/
/*
- * dispose of keys upon thread exit
+ * dispose of per-thread keys upon thread exit
*/
void exit_keys(struct task_struct *tsk)
{
key_put(tsk->thread_keyring);
+ key_put(tsk->request_key_auth);
} /* end exit_keys() */
@@ -382,7 +388,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
struct task_struct *context)
{
struct request_key_auth *rka;
- key_ref_t key_ref, ret, err, instkey_ref;
+ key_ref_t key_ref, ret, err;
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
* searchable, but we failed to find a key or we found a negative key;
@@ -461,30 +467,12 @@ key_ref_t search_process_keyrings(struct key_type *type,
err = key_ref;
break;
}
-
- /* if this process has a session keyring and that has an
- * instantiation authorisation key in the bottom level, then we
- * also search the keyrings of the process mentioned there */
- if (context != current)
- goto no_key;
-
- rcu_read_lock();
- instkey_ref = __keyring_search_one(
- make_key_ref(rcu_dereference(
- context->signal->session_keyring),
- 1),
- &key_type_request_key_auth, NULL, 0);
- rcu_read_unlock();
-
- if (IS_ERR(instkey_ref))
- goto no_key;
-
- rka = key_ref_to_ptr(instkey_ref)->payload.data;
-
- key_ref = search_process_keyrings(type, description, match,
- rka->context);
- key_ref_put(instkey_ref);
-
+ }
+ /* or search the user-session keyring */
+ else {
+ key_ref = keyring_search_aux(
+ make_key_ref(context->user->session_keyring, 1),
+ context, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -500,11 +488,21 @@ key_ref_t search_process_keyrings(struct key_type *type,
break;
}
}
- /* or search the user-session keyring */
- else {
- key_ref = keyring_search_aux(
- make_key_ref(context->user->session_keyring, 1),
- context, type, description, match);
+
+ /* if this process has an instantiation authorisation key, then we also
+ * search the keyrings of the process mentioned there
+ * - we don't permit access to request_key auth keys via this method
+ */
+ if (context->request_key_auth &&
+ context == current &&
+ type != &key_type_request_key_auth &&
+ key_validate(context->request_key_auth) == 0
+ ) {
+ rka = context->request_key_auth->payload.data;
+
+ key_ref = search_process_keyrings(type, description, match,
+ rka->context);
+
if (!IS_ERR(key_ref))
goto found;
@@ -521,8 +519,6 @@ key_ref_t search_process_keyrings(struct key_type *type,
}
}
-
-no_key:
/* no key - decide on the error we're going to go for */
key_ref = ret ? ret : err;
@@ -628,6 +624,15 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
key = ERR_PTR(-EINVAL);
goto error;
+ case KEY_SPEC_REQKEY_AUTH_KEY:
+ key = context->request_key_auth;
+ if (!key)
+ goto error;
+
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
default:
key_ref = ERR_PTR(-EINVAL);
if (id < 1)
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 5cc4bba70db6..f030a0ccbb93 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -29,28 +29,36 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
/*****************************************************************************/
/*
* request userspace finish the construction of a key
- * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
*/
-static int call_request_key(struct key *key,
- const char *op,
- const char *callout_info)
+static int call_sbin_request_key(struct key *key,
+ struct key *authkey,
+ const char *op)
{
struct task_struct *tsk = current;
key_serial_t prkey, sskey;
- struct key *session_keyring, *rkakey;
- char *argv[10], *envp[3], uid_str[12], gid_str[12];
+ struct key *keyring;
+ char *argv[9], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
+ char desc[20];
int ret, i;
- kenter("{%d},%s,%s", key->serial, op, callout_info);
+ kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
- /* generate a new session keyring with an auth key in it */
- session_keyring = request_key_auth_new(key, &rkakey);
- if (IS_ERR(session_keyring)) {
- ret = PTR_ERR(session_keyring);
- goto error;
+ /* allocate a new session keyring */
+ sprintf(desc, "_req.%u", key->serial);
+
+ keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error_alloc;
}
+ /* attach the auth key to the session keyring */
+ ret = __key_link(keyring, authkey);
+ if (ret < 0)
+ goto error_link;
+
/* record the UID and GID */
sprintf(uid_str, "%d", current->fsuid);
sprintf(gid_str, "%d", current->fsgid);
@@ -95,22 +103,19 @@ static int call_request_key(struct key *key,
argv[i++] = keyring_str[0];
argv[i++] = keyring_str[1];
argv[i++] = keyring_str[2];
- argv[i++] = (char *) callout_info;
argv[i] = NULL;
/* do it */
- ret = call_usermodehelper_keys(argv[0], argv, envp, session_keyring, 1);
+ ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, 1);
- /* dispose of the special keys */
- key_revoke(rkakey);
- key_put(rkakey);
- key_put(session_keyring);
+error_link:
+ key_put(keyring);
- error:
+error_alloc:
kleave(" = %d", ret);
return ret;
-} /* end call_request_key() */
+} /* end call_sbin_request_key() */
/*****************************************************************************/
/*
@@ -122,9 +127,10 @@ static struct key *__request_key_construction(struct key_type *type,
const char *description,
const char *callout_info)
{
+ request_key_actor_t actor;
struct key_construction cons;
struct timespec now;
- struct key *key;
+ struct key *key, *authkey;
int ret, negated;
kenter("%s,%s,%s", type->name, description, callout_info);
@@ -143,8 +149,19 @@ static struct key *__request_key_construction(struct key_type *type,
/* we drop the construction sem here on behalf of the caller */
up_write(&key_construction_sem);
+ /* allocate an authorisation key */
+ authkey = request_key_auth_new(key, callout_info);
+ if (IS_ERR(authkey)) {
+ ret = PTR_ERR(authkey);
+ authkey = NULL;
+ goto alloc_authkey_failed;
+ }
+
/* make the call */
- ret = call_request_key(key, "create", callout_info);
+ actor = call_sbin_request_key;
+ if (type->request_key)
+ actor = type->request_key;
+ ret = actor(key, authkey, "create");
if (ret < 0)
goto request_failed;
@@ -153,22 +170,29 @@ static struct key *__request_key_construction(struct key_type *type,
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
goto request_failed;
+ key_revoke(authkey);
+ key_put(authkey);
+
down_write(&key_construction_sem);
list_del(&cons.link);
up_write(&key_construction_sem);
/* also give an error if the key was negatively instantiated */
- check_not_negative:
+check_not_negative:
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
key_put(key);
key = ERR_PTR(-ENOKEY);
}
- out:
+out:
kleave(" = %p", key);
return key;
- request_failed:
+request_failed:
+ key_revoke(authkey);
+ key_put(authkey);
+
+alloc_authkey_failed:
/* it wasn't instantiated
* - remove from construction queue
* - mark the key as dead
@@ -217,7 +241,7 @@ static struct key *__request_key_construction(struct key_type *type,
key = ERR_PTR(ret);
goto out;
- alloc_failed:
+alloc_failed:
up_write(&key_construction_sem);
goto out;
@@ -464,35 +488,3 @@ struct key *request_key(struct key_type *type,
} /* end request_key() */
EXPORT_SYMBOL(request_key);
-
-/*****************************************************************************/
-/*
- * validate a key
- */
-int key_validate(struct key *key)
-{
- struct timespec now;
- int ret = 0;
-
- if (key) {
- /* check it's still accessible */
- ret = -EKEYREVOKED;
- if (test_bit(KEY_FLAG_REVOKED, &key->flags) ||
- test_bit(KEY_FLAG_DEAD, &key->flags))
- goto error;
-
- /* check it hasn't expired */
- ret = 0;
- if (key->expiry) {
- now = current_kernel_time();
- if (now.tv_sec >= key->expiry)
- ret = -EKEYEXPIRED;
- }
- }
-
- error:
- return ret;
-
-} /* end key_validate() */
-
-EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index a8e4069d48cb..cce6ba6b0323 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -15,11 +15,13 @@
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/seq_file.h>
+#include <asm/uaccess.h>
#include "internal.h"
static int request_key_auth_instantiate(struct key *, const void *, size_t);
static void request_key_auth_describe(const struct key *, struct seq_file *);
static void request_key_auth_destroy(struct key *);
+static long request_key_auth_read(const struct key *, char __user *, size_t);
/*
* the request-key authorisation key type definition
@@ -30,51 +32,25 @@ struct key_type key_type_request_key_auth = {
.instantiate = request_key_auth_instantiate,
.describe = request_key_auth_describe,
.destroy = request_key_auth_destroy,
+ .read = request_key_auth_read,
};
/*****************************************************************************/
/*
- * instantiate a request-key authorisation record
+ * instantiate a request-key authorisation key
*/
static int request_key_auth_instantiate(struct key *key,
const void *data,
size_t datalen)
{
- struct request_key_auth *rka, *irka;
- struct key *instkey;
- int ret;
-
- ret = -ENOMEM;
- rka = kmalloc(sizeof(*rka), GFP_KERNEL);
- if (rka) {
- /* see if the calling process is already servicing the key
- * request of another process */
- instkey = key_get_instantiation_authkey(0);
- if (!IS_ERR(instkey)) {
- /* it is - use that instantiation context here too */
- irka = instkey->payload.data;
- rka->context = irka->context;
- rka->pid = irka->pid;
- key_put(instkey);
- }
- else {
- /* it isn't - use this process as the context */
- rka->context = current;
- rka->pid = current->pid;
- }
-
- rka->target_key = key_get((struct key *) data);
- key->payload.data = rka;
- ret = 0;
- }
-
- return ret;
+ key->payload.data = (struct request_key_auth *) data;
+ return 0;
} /* end request_key_auth_instantiate() */
/*****************************************************************************/
/*
- *
+ * reading a request-key authorisation key retrieves the callout information
*/
static void request_key_auth_describe(const struct key *key,
struct seq_file *m)
@@ -83,12 +59,40 @@ static void request_key_auth_describe(const struct key *key,
seq_puts(m, "key:");
seq_puts(m, key->description);
- seq_printf(m, " pid:%d", rka->pid);
+ seq_printf(m, " pid:%d ci:%zu", rka->pid, strlen(rka->callout_info));
} /* end request_key_auth_describe() */
/*****************************************************************************/
/*
+ * read the callout_info data
+ * - the key's semaphore is read-locked
+ */
+static long request_key_auth_read(const struct key *key,
+ char __user *buffer, size_t buflen)
+{
+ struct request_key_auth *rka = key->payload.data;
+ size_t datalen;
+ long ret;
+
+ datalen = strlen(rka->callout_info);
+ ret = datalen;
+
+ /* we can return the data as is */
+ if (buffer && buflen > 0) {
+ if (buflen > datalen)
+ buflen = datalen;
+
+ if (copy_to_user(buffer, rka->callout_info, buflen) != 0)
+ ret = -EFAULT;
+ }
+
+ return ret;
+
+} /* end request_key_auth_read() */
+
+/*****************************************************************************/
+/*
* destroy an instantiation authorisation token key
*/
static void request_key_auth_destroy(struct key *key)
@@ -104,56 +108,89 @@ static void request_key_auth_destroy(struct key *key)
/*****************************************************************************/
/*
- * create a session keyring to be for the invokation of /sbin/request-key and
- * stick an authorisation token in it
+ * create an authorisation token for /sbin/request-key or whoever to gain
+ * access to the caller's security data
*/
-struct key *request_key_auth_new(struct key *target, struct key **_rkakey)
+struct key *request_key_auth_new(struct key *target, const char *callout_info)
{
- struct key *keyring, *rkakey = NULL;
+ struct request_key_auth *rka, *irka;
+ struct key *authkey = NULL;
char desc[20];
int ret;
kenter("%d,", target->serial);
- /* allocate a new session keyring */
- sprintf(desc, "_req.%u", target->serial);
+ /* allocate a auth record */
+ rka = kmalloc(sizeof(*rka), GFP_KERNEL);
+ if (!rka) {
+ kleave(" = -ENOMEM");
+ return ERR_PTR(-ENOMEM);
+ }
- keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL);
- if (IS_ERR(keyring)) {
- kleave("= %ld", PTR_ERR(keyring));
- return keyring;
+ /* see if the calling process is already servicing the key request of
+ * another process */
+ if (current->request_key_auth) {
+ /* it is - use that instantiation context here too */
+ irka = current->request_key_auth->payload.data;
+ rka->context = irka->context;
+ rka->pid = irka->pid;
}
+ else {
+ /* it isn't - use this process as the context */
+ rka->context = current;
+ rka->pid = current->pid;
+ }
+
+ rka->target_key = key_get(target);
+ rka->callout_info = callout_info;
/* allocate the auth key */
sprintf(desc, "%x", target->serial);
- rkakey = key_alloc(&key_type_request_key_auth, desc,
- current->fsuid, current->fsgid,
- KEY_POS_VIEW | KEY_USR_VIEW, 1);
- if (IS_ERR(rkakey)) {
- key_put(keyring);
- kleave("= %ld", PTR_ERR(rkakey));
- return rkakey;
+ authkey = key_alloc(&key_type_request_key_auth, desc,
+ current->fsuid, current->fsgid,
+ KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
+ KEY_USR_VIEW, 1);
+ if (IS_ERR(authkey)) {
+ ret = PTR_ERR(authkey);
+ goto error_alloc;
}
/* construct and attach to the keyring */
- ret = key_instantiate_and_link(rkakey, target, 0, keyring, NULL);
- if (ret < 0) {
- key_revoke(rkakey);
- key_put(rkakey);
- key_put(keyring);
- kleave("= %d", ret);
- return ERR_PTR(ret);
- }
+ ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
+ if (ret < 0)
+ goto error_inst;
- *_rkakey = rkakey;
- kleave(" = {%d} ({%d})", keyring->serial, rkakey->serial);
- return keyring;
+ kleave(" = {%d})", authkey->serial);
+ return authkey;
+
+error_inst:
+ key_revoke(authkey);
+ key_put(authkey);
+error_alloc:
+ key_put(rka->target_key);
+ kfree(rka);
+ kleave("= %d", ret);
+ return ERR_PTR(ret);
} /* end request_key_auth_new() */
/*****************************************************************************/
/*
+ * see if an authorisation key is associated with a particular key
+ */
+static int key_get_instantiation_authkey_match(const struct key *key,
+ const void *_id)
+{
+ struct request_key_auth *rka = key->payload.data;
+ key_serial_t id = (key_serial_t)(unsigned long) _id;
+
+ return rka->target_key->serial == id;
+
+} /* end key_get_instantiation_authkey_match() */
+
+/*****************************************************************************/
+/*
* get the authorisation key for instantiation of a specific key if attached to
* the current process's keyrings
* - this key is inserted into a keyring and that is set as /sbin/request-key's
@@ -162,22 +199,27 @@ struct key *request_key_auth_new(struct key *target, struct key **_rkakey)
*/
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
- struct task_struct *tsk = current;
- struct key *instkey;
-
- /* we must have our own personal session keyring */
- if (!tsk->signal->session_keyring)
- return ERR_PTR(-EACCES);
-
- /* and it must contain a suitable request authorisation key
- * - lock RCU against session keyring changing
- */
- rcu_read_lock();
+ struct key *authkey;
+ key_ref_t authkey_ref;
+
+ authkey_ref = search_process_keyrings(
+ &key_type_request_key_auth,
+ (void *) (unsigned long) target_id,
+ key_get_instantiation_authkey_match,
+ current);
+
+ if (IS_ERR(authkey_ref)) {
+ authkey = ERR_PTR(PTR_ERR(authkey_ref));
+ goto error;
+ }
- instkey = keyring_search_instkey(
- rcu_dereference(tsk->signal->session_keyring), target_id);
+ authkey = key_ref_to_ptr(authkey_ref);
+ if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
+ key_put(authkey);
+ authkey = ERR_PTR(-EKEYREVOKED);
+ }
- rcu_read_unlock();
- return instkey;
+error:
+ return authkey;
} /* end key_get_instantiation_authkey() */
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index cbda3b2780a1..8e71895b97a7 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -26,7 +26,6 @@
struct key_type key_type_user = {
.name = "user",
.instantiate = user_instantiate,
- .duplicate = user_duplicate,
.update = user_update,
.match = user_match,
.destroy = user_destroy,
@@ -68,42 +67,10 @@ 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
- */
-int user_duplicate(struct key *key, const struct key *source)
-{
- struct user_key_payload *upayload, *spayload;
- int ret;
-
- /* just copy the payload */
- ret = -ENOMEM;
- upayload = kmalloc(sizeof(*upayload) + source->datalen, GFP_KERNEL);
- if (upayload) {
- spayload = rcu_dereference(source->payload.data);
- BUG_ON(source->datalen != spayload->datalen);
-
- upayload->datalen = key->datalen = spayload->datalen;
- memcpy(upayload->data, spayload->data, key->datalen);
-
- key->payload.data = upayload;
- ret = 0;
- }
-
- return ret;
-
-} /* end user_duplicate() */
-
-EXPORT_SYMBOL_GPL(user_duplicate);
-
-/*****************************************************************************/
-/*
* dispose of the old data from an updated user defined key
*/
static void user_update_rcu_disposal(struct rcu_head *rcu)
diff --git a/security/security.c b/security/security.c
index ed5fb80769c3..f693e1f66b98 100644
--- a/security/security.c
+++ b/security/security.c
@@ -11,6 +11,7 @@
* (at your option) any later version.
*/
+#include <linux/capability.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index b038cd0fae2e..06d54d9d20a5 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -8,5 +8,7 @@ selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o
selinux-$(CONFIG_SECURITY_NETWORK) += netif.o
+selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
+
EXTRA_CFLAGS += -Isecurity/selinux/include
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 12e4fb72bf0f..53d6c7bbf564 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -494,8 +494,7 @@ static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
- audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:"
- "%04x:%04x:%04x", name1, NIP6(*addr));
+ audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr));
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
@@ -504,7 +503,7 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr,
__be16 port, char *name1, char *name2)
{
if (addr)
- audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
+ audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr));
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index fc774436a264..b9f8d9731c3d 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -73,6 +73,7 @@
#include "avc.h"
#include "objsec.h"
#include "netif.h"
+#include "xfrm.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -1018,7 +1019,7 @@ static inline int dentry_has_perm(struct task_struct *tsk,
has the same SID as the process. If av is zero, then
access to the file is not checked, e.g. for cases
where only the descriptor is affected like seek. */
-static inline int file_has_perm(struct task_struct *tsk,
+static int file_has_perm(struct task_struct *tsk,
struct file *file,
u32 av)
{
@@ -1662,7 +1663,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
continue;
}
if (devnull) {
- rcuref_inc(&devnull->f_count);
+ get_file(devnull);
} else {
devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
if (!devnull) {
@@ -3349,6 +3350,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = avc_has_perm(sock_sid, port_sid,
sock_class, recv_perm, &ad);
}
+
+ if (!err)
+ err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+
out:
return err;
}
@@ -3401,6 +3406,24 @@ static void selinux_sk_free_security(struct sock *sk)
sk_free_security(sk);
}
+static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ struct inode_security_struct *isec;
+ u32 sock_sid = SECINITSID_ANY_SOCKET;
+
+ if (!sk)
+ return selinux_no_sk_sid(fl);
+
+ read_lock_bh(&sk->sk_callback_lock);
+ isec = get_sock_isec(sk);
+
+ if (isec)
+ sock_sid = isec->sid;
+
+ read_unlock_bh(&sk->sk_callback_lock);
+ return sock_sid;
+}
+
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
{
int err = 0;
@@ -3536,6 +3559,11 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
send_perm, &ad) ? NF_DROP : NF_ACCEPT;
}
+ if (err != NF_ACCEPT)
+ goto out;
+
+ err = selinux_xfrm_postroute_last(isec->sid, skb);
+
out:
return err;
}
@@ -4380,6 +4408,16 @@ static struct security_operations selinux_ops = {
.socket_getpeersec = selinux_socket_getpeersec,
.sk_alloc_security = selinux_sk_alloc_security,
.sk_free_security = selinux_sk_free_security,
+ .sk_getsid = selinux_sk_getsid_security,
+#endif
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
+ .xfrm_policy_clone_security = selinux_xfrm_policy_clone,
+ .xfrm_policy_free_security = selinux_xfrm_policy_free,
+ .xfrm_state_alloc_security = selinux_xfrm_state_alloc,
+ .xfrm_state_free_security = selinux_xfrm_state_free,
+ .xfrm_policy_lookup = selinux_xfrm_policy_lookup,
#endif
};
@@ -4491,6 +4529,7 @@ static int __init selinux_nf_ip_init(void)
panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
#endif /* IPV6 */
+
out:
return err;
}
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 1deb59e1b762..591e98d9315a 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -238,3 +238,4 @@
S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index a78b5d59c9fc..d7f02edf3930 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -908,6 +908,7 @@
#define ASSOCIATION__SENDTO 0x00000001UL
#define ASSOCIATION__RECVFROM 0x00000002UL
+#define ASSOCIATION__SETCONTEXT 0x00000004UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
new file mode 100644
index 000000000000..8e87996c6dd5
--- /dev/null
+++ b/security/selinux/include/xfrm.h
@@ -0,0 +1,54 @@
+/*
+ * SELinux support for the XFRM LSM hooks
+ *
+ * Author : Trent Jaeger, <jaegert@us.ibm.com>
+ */
+#ifndef _SELINUX_XFRM_H_
+#define _SELINUX_XFRM_H_
+
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new);
+void selinux_xfrm_policy_free(struct xfrm_policy *xp);
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+void selinux_xfrm_state_free(struct xfrm_state *x);
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+
+/*
+ * Extract the security blob from the sock (it's actually on the socket)
+ */
+static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
+{
+ if (!sk->sk_socket)
+ return NULL;
+
+ return SOCK_INODE(sk->sk_socket)->i_security;
+}
+
+
+static inline u32 selinux_no_sk_sid(struct flowi *fl)
+{
+ /* NOTE: no sock occurs on ICMP reply, forwards, ... */
+ /* icmp_reply: authorize as kernel packet */
+ if (fl && fl->proto == IPPROTO_ICMP) {
+ return SECINITSID_KERNEL;
+ }
+
+ return SECINITSID_ANY_SOCKET;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb);
+#else
+static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+{
+ return 0;
+}
+
+static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+{
+ return NF_ACCEPT;
+}
+#endif
+
+#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 0e1352a555c8..b5fa02d17b1e 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -376,7 +376,7 @@ static ssize_t selinux_transaction_write(struct file *file, const char __user *b
char *data;
ssize_t rv;
- if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
+ if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
return -EINVAL;
data = simple_transaction_get(file, buf, size);
@@ -889,7 +889,7 @@ static void sel_remove_bools(struct dentry *de)
spin_lock(&dcache_lock);
node = de->d_subdirs.next;
while (node != &de->d_subdirs) {
- struct dentry *d = list_entry(node, struct dentry, d_child);
+ struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
list_del_init(node);
if (d->d_inode) {
@@ -1161,7 +1161,7 @@ static int sel_make_avc_files(struct dentry *dir)
#endif
};
- for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
struct inode *inode;
struct dentry *dentry;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index dde094feb20d..d049c7acbc8b 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -359,7 +359,7 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
return -1;
}
- for (i = 0; i < sizeof(spec_order)/sizeof(u16); i++) {
+ for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
key.specified = spec_order[i] | enabled;
datum.data = le32_to_cpu(buf32[items++]);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 0ac311dc8371..0111990ba837 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -103,7 +103,7 @@ static struct policydb_compat_info *policydb_lookup_compat(int version)
int i;
struct policydb_compat_info *info = NULL;
- for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) {
+ for (i = 0; i < ARRAY_SIZE(policydb_compat); i++) {
if (policydb_compat[i].version == version) {
info = &policydb_compat[i];
break;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 44eb4d74908d..8a764928ff4b 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1712,11 +1712,11 @@ int security_get_bools(int *len, char ***names, int **values)
goto out;
}
- *names = (char**)kcalloc(*len, sizeof(char*), GFP_ATOMIC);
+ *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC);
if (!*names)
goto err;
- *values = (int*)kcalloc(*len, sizeof(int), GFP_ATOMIC);
+ *values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
if (!*values)
goto err;
@@ -1724,7 +1724,7 @@ int security_get_bools(int *len, char ***names, int **values)
size_t name_len;
(*values)[i] = policydb.bool_val_to_struct[i]->state;
name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
- (*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+ (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
new file mode 100644
index 000000000000..b2af7ca496c1
--- /dev/null
+++ b/security/selinux/xfrm.c
@@ -0,0 +1,305 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux XFRM hook function implementations.
+ *
+ * Authors: Serge Hallyn <sergeh@us.ibm.com>
+ * Trent Jaeger <jaegert@us.ibm.com>
+ *
+ * Copyright (C) 2005 International Business Machines Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+/*
+ * USAGE:
+ * NOTES:
+ * 1. Make sure to enable the following options in your kernel config:
+ * CONFIG_SECURITY=y
+ * CONFIG_SECURITY_NETWORK=y
+ * CONFIG_SECURITY_NETWORK_XFRM=y
+ * CONFIG_SECURITY_SELINUX=m/y
+ * ISSUES:
+ * 1. Caching packets, so they are not dropped during negotiation
+ * 2. Emulating a reasonable SO_PEERSEC across machines
+ * 3. Testing addition of sk_policy's with security context via setsockopt
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#include <net/checksum.h>
+#include <net/udp.h>
+#include <asm/semaphore.h>
+
+#include "avc.h"
+#include "objsec.h"
+#include "xfrm.h"
+
+
+/*
+ * Returns true if an LSM/SELinux context
+ */
+static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
+{
+ return (ctx &&
+ (ctx->ctx_doi == XFRM_SC_DOI_LSM) &&
+ (ctx->ctx_alg == XFRM_SC_ALG_SELINUX));
+}
+
+/*
+ * Returns true if the xfrm contains a security blob for SELinux
+ */
+static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
+{
+ return selinux_authorizable_ctx(x->security);
+}
+
+/*
+ * LSM hook implementation that authorizes that a socket can be used
+ * with the corresponding xfrm_sec_ctx and direction.
+ */
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ int rc = 0;
+ u32 sel_sid = SECINITSID_UNLABELED;
+ struct xfrm_sec_ctx *ctx;
+
+ /* Context sid is either set to label or ANY_ASSOC */
+ if ((ctx = xp->security)) {
+ if (!selinux_authorizable_ctx(ctx))
+ return -EINVAL;
+
+ sel_sid = ctx->ctx_sid;
+ }
+
+ rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION,
+ ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM :
+ ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO :
+ (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
+ NULL);
+
+ return rc;
+}
+
+/*
+ * Security blob allocation for xfrm_policy and xfrm_state
+ * CTX does not have a meaningful value on input
+ */
+static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx)
+{
+ int rc = 0;
+ struct task_security_struct *tsec = current->security;
+ struct xfrm_sec_ctx *ctx;
+
+ BUG_ON(!uctx);
+ BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX);
+
+ if (uctx->ctx_len >= PAGE_SIZE)
+ return -ENOMEM;
+
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
+ uctx->ctx_len,
+ GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->ctx_doi = uctx->ctx_doi;
+ ctx->ctx_len = uctx->ctx_len;
+ ctx->ctx_alg = uctx->ctx_alg;
+
+ memcpy(ctx->ctx_str,
+ uctx+1,
+ ctx->ctx_len);
+ rc = security_context_to_sid(ctx->ctx_str,
+ ctx->ctx_len,
+ &ctx->ctx_sid);
+
+ if (rc)
+ goto out;
+
+ /*
+ * Does the subject have permission to set security or permission to
+ * do the relabel?
+ * Must be permitted to relabel from default socket type (process type)
+ * to specified context
+ */
+ rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__SETCONTEXT, NULL);
+ if (rc)
+ goto out;
+
+ return rc;
+
+out:
+ *ctxp = NULL;
+ kfree(ctx);
+ return rc;
+}
+
+/*
+ * LSM hook implementation that allocs and transfers uctx spec to
+ * xfrm_policy.
+ */
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx)
+{
+ int err;
+
+ BUG_ON(!xp);
+
+ err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx);
+ return err;
+}
+
+
+/*
+ * LSM hook implementation that copies security data structure from old to
+ * new for policy cloning.
+ */
+int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ struct xfrm_sec_ctx *old_ctx, *new_ctx;
+
+ old_ctx = old->security;
+
+ if (old_ctx) {
+ new_ctx = new->security = kmalloc(sizeof(*new_ctx) +
+ old_ctx->ctx_len,
+ GFP_KERNEL);
+
+ if (!new_ctx)
+ return -ENOMEM;
+
+ memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
+ memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
+ }
+ return 0;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_policy security information.
+ */
+void selinux_xfrm_policy_free(struct xfrm_policy *xp)
+{
+ struct xfrm_sec_ctx *ctx = xp->security;
+ if (ctx)
+ kfree(ctx);
+}
+
+/*
+ * LSM hook implementation that allocs and transfers sec_ctx spec to
+ * xfrm_state.
+ */
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx)
+{
+ int err;
+
+ BUG_ON(!x);
+
+ err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx);
+ return err;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_state security information.
+ */
+void selinux_xfrm_state_free(struct xfrm_state *x)
+{
+ struct xfrm_sec_ctx *ctx = x->security;
+ if (ctx)
+ kfree(ctx);
+}
+
+/*
+ * LSM hook that controls access to unlabelled packets. If
+ * a xfrm_state is authorizable (defined by macro) then it was
+ * already authorized by the IPSec process. If not, then
+ * we need to check for unlabelled access since this may not have
+ * gone thru the IPSec process.
+ */
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+{
+ int i, rc = 0;
+ struct sec_path *sp;
+
+ sp = skb->sp;
+
+ if (sp) {
+ /*
+ * __xfrm_policy_check does not approve unless xfrm_policy_ok
+ * says that spi's match for policy and the socket.
+ *
+ * Only need to verify the existence of an authorizable sp.
+ */
+ for (i = 0; i < sp->len; i++) {
+ struct xfrm_state *x = sp->x[i].xvec;
+
+ if (x && selinux_authorizable_xfrm(x))
+ goto accept;
+ }
+ }
+
+ /* check SELinux sock for unlabelled access */
+ rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
+ ASSOCIATION__RECVFROM, NULL);
+ if (rc)
+ goto drop;
+
+accept:
+ return 0;
+
+drop:
+ return rc;
+}
+
+/*
+ * POSTROUTE_LAST hook's XFRM processing:
+ * If we have no security association, then we need to determine
+ * whether the socket is allowed to send to an unlabelled destination.
+ * If we do have a authorizable security association, then it has already been
+ * checked in xfrm_policy_lookup hook.
+ */
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+{
+ struct dst_entry *dst;
+ int rc = 0;
+
+ dst = skb->dst;
+
+ if (dst) {
+ struct dst_entry *dst_test;
+
+ for (dst_test = dst; dst_test != 0;
+ dst_test = dst_test->child) {
+ struct xfrm_state *x = dst_test->xfrm;
+
+ if (x && selinux_authorizable_xfrm(x))
+ goto accept;
+ }
+ }
+
+ rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
+ ASSOCIATION__SENDTO, NULL);
+ if (rc)
+ goto drop;
+
+accept:
+ return NF_ACCEPT;
+
+drop:
+ return NF_DROP;
+}