summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/commoncap.c4
-rw-r--r--security/dummy.c13
-rw-r--r--security/keys/key.c18
-rw-r--r--security/keys/keyctl.c155
-rw-r--r--security/keys/process_keys.c7
-rw-r--r--security/seclvl.c210
-rw-r--r--security/security.c23
-rw-r--r--security/selinux/hooks.c60
-rw-r--r--security/selinux/include/xfrm.h12
-rw-r--r--security/selinux/nlmsgtab.c7
-rw-r--r--security/selinux/selinuxfs.c112
-rw-r--r--security/selinux/ss/services.c9
-rw-r--r--security/selinux/xfrm.c68
13 files changed, 380 insertions, 318 deletions
diff --git a/security/commoncap.c b/security/commoncap.c
index 8a6e097f99ea..841eb4e5c62b 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -60,8 +60,8 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
int cap_ptrace (struct task_struct *parent, struct task_struct *child)
{
/* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
- if (!cap_issubset (child->cap_permitted, current->cap_permitted) &&
- !capable(CAP_SYS_PTRACE))
+ if (!cap_issubset(child->cap_permitted, parent->cap_permitted) &&
+ !__capable(parent, CAP_SYS_PTRACE))
return -EPERM;
return 0;
}
diff --git a/security/dummy.c b/security/dummy.c
index 0a553d39729f..fd99429278e9 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -773,8 +773,14 @@ static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
return 0;
}
-static int dummy_socket_getpeersec(struct socket *sock, char __user *optval,
- int __user *optlen, unsigned len)
+static int dummy_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ return -ENOPROTOOPT;
+}
+
+static int dummy_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata,
+ u32 *seclen)
{
return -ENOPROTOOPT;
}
@@ -1014,7 +1020,8 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, socket_getsockopt);
set_to_dummy_if_null(ops, socket_shutdown);
set_to_dummy_if_null(ops, socket_sock_rcv_skb);
- set_to_dummy_if_null(ops, socket_getpeersec);
+ set_to_dummy_if_null(ops, socket_getpeersec_stream);
+ set_to_dummy_if_null(ops, socket_getpeersec_dgram);
set_to_dummy_if_null(ops, sk_alloc_security);
set_to_dummy_if_null(ops, sk_free_security);
set_to_dummy_if_null(ops, sk_getsid);
diff --git a/security/keys/key.c b/security/keys/key.c
index 99781b798312..a057e3311aad 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 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -271,7 +271,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
* its description */
if (!not_in_quota) {
spin_lock(&user->lock);
- if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS &&
+ if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS ||
user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
)
goto no_quota;
@@ -795,12 +795,16 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
goto error_3;
}
- /* search for an existing key of the same type and description in the
- * destination keyring
+ /* if it's possible to update this type of key, search for an existing
+ * key of the same type and description in the destination keyring and
+ * update that instead if possible
*/
- key_ref = __keyring_search_one(keyring_ref, ktype, description, 0);
- if (!IS_ERR(key_ref))
- goto found_matching_key;
+ if (ktype->update) {
+ key_ref = __keyring_search_one(keyring_ref, ktype, description,
+ 0);
+ if (!IS_ERR(key_ref))
+ goto found_matching_key;
+ }
/* decide on the permissions we want */
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 0c62798ac7d8..ed71d86d2ce2 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -17,10 +17,33 @@
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/capability.h>
+#include <linux/string.h>
#include <linux/err.h>
#include <asm/uaccess.h>
#include "internal.h"
+static int key_get_type_from_user(char *type,
+ const char __user *_type,
+ unsigned len)
+{
+ int ret;
+
+ ret = strncpy_from_user(type, _type, len);
+
+ if (ret < 0)
+ return -EFAULT;
+
+ if (ret == 0 || ret >= len)
+ return -EINVAL;
+
+ if (type[0] == '.')
+ return -EPERM;
+
+ type[len - 1] = '\0';
+
+ return 0;
+}
+
/*****************************************************************************/
/*
* extract the description of a new key from userspace and either add it as a
@@ -38,40 +61,22 @@ asmlinkage long sys_add_key(const char __user *_type,
key_ref_t keyring_ref, key_ref;
char type[32], *description;
void *payload;
- long dlen, ret;
+ long ret;
ret = -EINVAL;
if (plen > 32767)
goto error;
/* draw all the data into kernel space */
- ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ ret = key_get_type_from_user(type, _type, sizeof(type));
if (ret < 0)
goto error;
- type[31] = '\0';
-
- ret = -EPERM;
- if (type[0] == '.')
- goto error;
-
- ret = -EFAULT;
- dlen = strnlen_user(_description, PAGE_SIZE - 1);
- if (dlen <= 0)
- goto error;
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error;
-
- ret = -ENOMEM;
- description = kmalloc(dlen + 1, GFP_KERNEL);
- if (!description)
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
goto error;
- description[dlen] = '\0';
-
- ret = -EFAULT;
- if (copy_from_user(description, _description, dlen) != 0)
- goto error2;
+ }
/* pull the payload in if one was supplied */
payload = NULL;
@@ -136,59 +141,28 @@ asmlinkage long sys_request_key(const char __user *_type,
struct key *key;
key_ref_t dest_ref;
char type[32], *description, *callout_info;
- long dlen, ret;
+ long ret;
/* pull the type into kernel space */
- ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ ret = key_get_type_from_user(type, _type, sizeof(type));
if (ret < 0)
goto error;
- type[31] = '\0';
-
- ret = -EPERM;
- if (type[0] == '.')
- goto error;
/* pull the description into kernel space */
- ret = -EFAULT;
- dlen = strnlen_user(_description, PAGE_SIZE - 1);
- if (dlen <= 0)
- goto error;
-
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error;
-
- ret = -ENOMEM;
- description = kmalloc(dlen + 1, GFP_KERNEL);
- if (!description)
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
goto error;
- description[dlen] = '\0';
-
- ret = -EFAULT;
- if (copy_from_user(description, _description, dlen) != 0)
- goto error2;
+ }
/* pull the callout info into kernel space */
callout_info = NULL;
if (_callout_info) {
- ret = -EFAULT;
- dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
- if (dlen <= 0)
- goto error2;
-
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error2;
-
- ret = -ENOMEM;
- callout_info = kmalloc(dlen + 1, GFP_KERNEL);
- if (!callout_info)
+ callout_info = strndup_user(_callout_info, PAGE_SIZE);
+ if (IS_ERR(callout_info)) {
+ ret = PTR_ERR(callout_info);
goto error2;
- callout_info[dlen] = '\0';
-
- ret = -EFAULT;
- if (copy_from_user(callout_info, _callout_info, dlen) != 0)
- goto error3;
+ }
}
/* get the destination keyring if specified */
@@ -264,36 +238,21 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
long keyctl_join_session_keyring(const char __user *_name)
{
char *name;
- long nlen, ret;
+ long ret;
/* fetch the name from userspace */
name = NULL;
if (_name) {
- ret = -EFAULT;
- nlen = strnlen_user(_name, PAGE_SIZE - 1);
- if (nlen <= 0)
- goto error;
-
- ret = -EINVAL;
- if (nlen > PAGE_SIZE - 1)
+ name = strndup_user(_name, PAGE_SIZE);
+ if (IS_ERR(name)) {
+ ret = PTR_ERR(name);
goto error;
-
- ret = -ENOMEM;
- name = kmalloc(nlen + 1, GFP_KERNEL);
- if (!name)
- goto error;
- name[nlen] = '\0';
-
- ret = -EFAULT;
- if (copy_from_user(name, _name, nlen) != 0)
- goto error2;
+ }
}
/* join the session */
ret = join_session_keyring(name);
- error2:
- kfree(name);
error:
return ret;
@@ -566,32 +525,18 @@ long keyctl_keyring_search(key_serial_t ringid,
struct key_type *ktype;
key_ref_t keyring_ref, key_ref, dest_ref;
char type[32], *description;
- long dlen, ret;
+ long ret;
/* pull the type and description into kernel space */
- ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ ret = key_get_type_from_user(type, _type, sizeof(type));
if (ret < 0)
goto error;
- type[31] = '\0';
- ret = -EFAULT;
- dlen = strnlen_user(_description, PAGE_SIZE - 1);
- if (dlen <= 0)
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
goto error;
-
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error;
-
- ret = -ENOMEM;
- description = kmalloc(dlen + 1, GFP_KERNEL);
- if (!description)
- goto error;
- description[dlen] = '\0';
-
- ret = -EFAULT;
- if (copy_from_user(description, _description, dlen) != 0)
- goto error2;
+ }
/* get the keyring at which to begin the search */
keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH);
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 74cb79eb917e..f6940618e345 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -16,11 +16,12 @@
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include "internal.h"
/* session keyring create vs join semaphore */
-static DECLARE_MUTEX(key_session_sem);
+static DEFINE_MUTEX(key_session_mutex);
/* the root user's tracking struct */
struct key_user root_key_user = {
@@ -711,7 +712,7 @@ long join_session_keyring(const char *name)
}
/* allow the user to join or create a named keyring */
- down(&key_session_sem);
+ mutex_lock(&key_session_mutex);
/* look for an existing keyring of this name */
keyring = find_keyring_by_name(name, 0);
@@ -737,7 +738,7 @@ long join_session_keyring(const char *name)
key_put(keyring);
error2:
- up(&key_session_sem);
+ mutex_unlock(&key_session_mutex);
error:
return ret;
diff --git a/security/seclvl.c b/security/seclvl.c
index 8529ea6f7aa8..441beaf1bbc1 100644
--- a/security/seclvl.c
+++ b/security/seclvl.c
@@ -8,6 +8,7 @@
* Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2002 International Business Machines <robb@austin.ibm.com>
+ * Copyright (c) 2006 Davi E. M. Arnaut <davi.arnaut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,6 +32,7 @@
#include <linux/kobject.h>
#include <linux/crypto.h>
#include <asm/scatterlist.h>
+#include <linux/scatterlist.h>
#include <linux/gfp.h>
#include <linux/sysfs.h>
@@ -194,35 +196,27 @@ static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
* people...
*/
static int
-plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len)
+plaintext_to_sha1(unsigned char *hash, const char *plaintext, unsigned int len)
{
- char *pgVirtAddr;
struct crypto_tfm *tfm;
- struct scatterlist sg[1];
+ struct scatterlist sg;
if (len > PAGE_SIZE) {
seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
"characters). Largest possible is %lu "
"bytes.\n", len, PAGE_SIZE);
- return -ENOMEM;
+ return -EINVAL;
}
tfm = crypto_alloc_tfm("sha1", CRYPTO_TFM_REQ_MAY_SLEEP);
if (tfm == NULL) {
seclvl_printk(0, KERN_ERR,
"Failed to load transform for SHA1\n");
- return -ENOSYS;
+ return -EINVAL;
}
- // Just get a new page; don't play around with page boundaries
- // and scatterlists.
- pgVirtAddr = (char *)__get_free_page(GFP_KERNEL);
- sg[0].page = virt_to_page(pgVirtAddr);
- sg[0].offset = 0;
- sg[0].length = len;
- strncpy(pgVirtAddr, plaintext, len);
+ sg_init_one(&sg, (u8 *)plaintext, len);
crypto_digest_init(tfm);
- crypto_digest_update(tfm, sg, 1);
+ crypto_digest_update(tfm, &sg, 1);
crypto_digest_final(tfm, hash);
crypto_free_tfm(tfm);
- free_page((unsigned long)pgVirtAddr);
return 0;
}
@@ -234,11 +228,9 @@ static ssize_t
passwd_write_file(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
- int i;
- unsigned char tmp[SHA1_DIGEST_SIZE];
- char *page;
- int rc;
+ char *p;
int len;
+ unsigned char tmp[SHA1_DIGEST_SIZE];
if (!*passwd && !*sha1_passwd) {
seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
@@ -251,38 +243,39 @@ passwd_write_file(struct file * file, const char __user * buf,
return -EINVAL;
}
- if (count < 0 || count >= PAGE_SIZE)
+ if (count >= PAGE_SIZE)
return -EINVAL;
if (*ppos != 0)
return -EINVAL;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
+ p = kmalloc(count, GFP_KERNEL);
+ if (!p)
return -ENOMEM;
len = -EFAULT;
- if (copy_from_user(page, buf, count))
+ if (copy_from_user(p, buf, count))
goto out;
- len = strlen(page);
+ len = count;
/* ``echo "secret" > seclvl/passwd'' includes a newline */
- if (page[len - 1] == '\n')
+ if (p[len - 1] == '\n')
len--;
/* Hash the password, then compare the hashed values */
- if ((rc = plaintext_to_sha1(tmp, page, len))) {
+ if ((len = plaintext_to_sha1(tmp, p, len))) {
seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
- "[%d]\n", rc);
- return rc;
- }
- for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
- if (hashedPassword[i] != tmp[i])
- return -EPERM;
+ "[%d]\n", len);
+ goto out;
}
+
+ len = -EPERM;
+ if (memcmp(hashedPassword, tmp, SHA1_DIGEST_SIZE))
+ goto out;
+
seclvl_printk(0, KERN_INFO,
"Password accepted; seclvl reduced to 0.\n");
seclvl = 0;
len = count;
out:
- free_page((unsigned long)page);
+ kfree (p);
return len;
}
@@ -295,13 +288,11 @@ static struct file_operations passwd_file_ops = {
*/
static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
{
- if (seclvl >= 0) {
- if (child->pid == 1) {
- seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
- "the init process dissallowed in "
- "secure level %d\n", seclvl);
- return -EPERM;
- }
+ if (seclvl >= 0 && child->pid == 1) {
+ seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
+ "the init process dissallowed in "
+ "secure level %d\n", seclvl);
+ return -EPERM;
}
return 0;
}
@@ -312,55 +303,54 @@ static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
*/
static int seclvl_capable(struct task_struct *tsk, int cap)
{
+ int rc = 0;
+
/* init can do anything it wants */
if (tsk->pid == 1)
return 0;
- switch (seclvl) {
- case 2:
- /* fall through */
- case 1:
- if (cap == CAP_LINUX_IMMUTABLE) {
+ if (seclvl > 0) {
+ rc = -EPERM;
+
+ if (cap == CAP_LINUX_IMMUTABLE)
seclvl_printk(1, KERN_WARNING, "Attempt to modify "
"the IMMUTABLE and/or APPEND extended "
"attribute on a file with the IMMUTABLE "
"and/or APPEND extended attribute set "
"denied in seclvl [%d]\n", seclvl);
- return -EPERM;
- } else if (cap == CAP_SYS_RAWIO) { // Somewhat broad...
+ else if (cap == CAP_SYS_RAWIO)
seclvl_printk(1, KERN_WARNING, "Attempt to perform "
"raw I/O while in secure level [%d] "
"denied\n", seclvl);
- return -EPERM;
- } else if (cap == CAP_NET_ADMIN) {
+ else if (cap == CAP_NET_ADMIN)
seclvl_printk(1, KERN_WARNING, "Attempt to perform "
"network administrative task while "
"in secure level [%d] denied\n", seclvl);
- return -EPERM;
- } else if (cap == CAP_SETUID) {
+ else if (cap == CAP_SETUID)
seclvl_printk(1, KERN_WARNING, "Attempt to setuid "
"while in secure level [%d] denied\n",
seclvl);
- return -EPERM;
- } else if (cap == CAP_SETGID) {
+ else if (cap == CAP_SETGID)
seclvl_printk(1, KERN_WARNING, "Attempt to setgid "
"while in secure level [%d] denied\n",
seclvl);
- } else if (cap == CAP_SYS_MODULE) {
+ else if (cap == CAP_SYS_MODULE)
seclvl_printk(1, KERN_WARNING, "Attempt to perform "
"a module operation while in secure "
"level [%d] denied\n", seclvl);
- return -EPERM;
- }
- break;
- default:
- break;
+ else
+ rc = 0;
}
- /* from dummy.c */
- if (cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0)
- return 0; /* capability granted */
- seclvl_printk(1, KERN_WARNING, "Capability denied\n");
- return -EPERM; /* capability denied */
+
+ if (!rc) {
+ if (!(cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0))
+ rc = -EPERM;
+ }
+
+ if (rc)
+ seclvl_printk(1, KERN_WARNING, "Capability denied\n");
+
+ return rc;
}
/**
@@ -466,12 +456,9 @@ static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr)
static void seclvl_file_free_security(struct file *filp)
{
struct dentry *dentry = filp->f_dentry;
- struct inode *inode = NULL;
- if (dentry) {
- inode = dentry->d_inode;
- seclvl_bd_release(inode);
- }
+ if (dentry)
+ seclvl_bd_release(dentry->d_inode);
}
/**
@@ -479,9 +466,7 @@ static void seclvl_file_free_security(struct file *filp)
*/
static int seclvl_umount(struct vfsmount *mnt, int flags)
{
- if (current->pid == 1)
- return 0;
- if (seclvl == 2) {
+ if (current->pid != 1 && seclvl == 2) {
seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "
"level %d\n", seclvl);
return -EPERM;
@@ -505,8 +490,9 @@ static struct security_operations seclvl_ops = {
static int processPassword(void)
{
int rc = 0;
- hashedPassword[0] = '\0';
if (*passwd) {
+ char *p;
+
if (*sha1_passwd) {
seclvl_printk(0, KERN_ERR, "Error: Both "
"passwd and sha1_passwd "
@@ -514,12 +500,16 @@ static int processPassword(void)
"exclusive.\n");
return -EINVAL;
}
- if ((rc = plaintext_to_sha1(hashedPassword, passwd,
- strlen(passwd)))) {
+
+ p = kstrdup(passwd, GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+
+ if ((rc = plaintext_to_sha1(hashedPassword, p, strlen(p))))
seclvl_printk(0, KERN_ERR, "Error: SHA1 support not "
"in kernel\n");
- return rc;
- }
+
+ kfree (p);
/* All static data goes to the BSS, which zero's the
* plaintext password out for us. */
} else if (*sha1_passwd) { // Base 16
@@ -542,7 +532,7 @@ static int processPassword(void)
sha1_passwd[i + 2] = tmp;
}
}
- return 0;
+ return rc;
}
/**
@@ -552,28 +542,46 @@ struct dentry *dir_ino, *seclvl_ino, *passwd_ino;
static int seclvlfs_register(void)
{
+ int rc = 0;
+
dir_ino = securityfs_create_dir("seclvl", NULL);
- if (!dir_ino)
- return -EFAULT;
+
+ if (IS_ERR(dir_ino))
+ return PTR_ERR(dir_ino);
seclvl_ino = securityfs_create_file("seclvl", S_IRUGO | S_IWUSR,
dir_ino, &seclvl, &seclvl_file_ops);
- if (!seclvl_ino)
+ if (IS_ERR(seclvl_ino)) {
+ rc = PTR_ERR(seclvl_ino);
goto out_deldir;
+ }
if (*passwd || *sha1_passwd) {
passwd_ino = securityfs_create_file("passwd", S_IRUGO | S_IWUSR,
dir_ino, NULL, &passwd_file_ops);
- if (!passwd_ino)
+ if (IS_ERR(passwd_ino)) {
+ rc = PTR_ERR(passwd_ino);
goto out_delf;
+ }
}
- return 0;
+ return rc;
+
+out_delf:
+ securityfs_remove(seclvl_ino);
out_deldir:
securityfs_remove(dir_ino);
-out_delf:
+
+ return rc;
+}
+
+static void seclvlfs_unregister(void)
+{
securityfs_remove(seclvl_ino);
- return -EFAULT;
+ if (*passwd || *sha1_passwd)
+ securityfs_remove(passwd_ino);
+
+ securityfs_remove(dir_ino);
}
/**
@@ -582,6 +590,8 @@ out_delf:
static int __init seclvl_init(void)
{
int rc = 0;
+ static char once;
+
if (verbosity < 0 || verbosity > 1) {
printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
"are valid values\n", verbosity);
@@ -600,6 +610,11 @@ static int __init seclvl_init(void)
"module parameter(s): rc = [%d]\n", rc);
goto exit;
}
+
+ if ((rc = seclvlfs_register())) {
+ seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
+ goto exit;
+ }
/* register ourselves with the security framework */
if (register_security(&seclvl_ops)) {
seclvl_printk(0, KERN_ERR,
@@ -611,20 +626,24 @@ static int __init seclvl_init(void)
seclvl_printk(0, KERN_ERR, "seclvl: Failure "
"registering with primary security "
"module.\n");
+ seclvlfs_unregister();
goto exit;
} /* if primary module registered */
secondary = 1;
} /* if we registered ourselves with the security framework */
- if ((rc = seclvlfs_register())) {
- seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
- goto exit;
- }
+
seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n");
+
+ if (once) {
+ once = 1;
+ seclvl_printk(0, KERN_INFO, "seclvl is going away. It has been "
+ "buggy for ages. Also, be warned that "
+ "Securelevels are useless.");
+ }
exit:
- if (rc) {
+ if (rc)
printk(KERN_ERR "seclvl: Error during initialization: rc = "
"[%d]\n", rc);
- }
return rc;
}
@@ -633,17 +652,14 @@ static int __init seclvl_init(void)
*/
static void __exit seclvl_exit(void)
{
- securityfs_remove(seclvl_ino);
- if (*passwd || *sha1_passwd)
- securityfs_remove(passwd_ino);
- securityfs_remove(dir_ino);
- if (secondary == 1) {
+ seclvlfs_unregister();
+
+ if (secondary)
mod_unreg_security(MY_NAME, &seclvl_ops);
- } else if (unregister_security(&seclvl_ops)) {
+ else if (unregister_security(&seclvl_ops))
seclvl_printk(0, KERN_INFO,
"seclvl: Failure unregistering with the "
"kernel\n");
- }
}
module_init(seclvl_init);
diff --git a/security/security.c b/security/security.c
index f693e1f66b98..51ef509710b9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -174,31 +174,8 @@ int mod_unreg_security(const char *name, struct security_operations *ops)
return security_ops->unregister_security(name, ops);
}
-/**
- * capable - calls the currently loaded security module's capable() function with the specified capability
- * @cap: the requested capability level.
- *
- * This function calls the currently loaded security module's capable()
- * function with a pointer to the current task and the specified @cap value.
- *
- * This allows the security module to implement the capable function call
- * however it chooses to.
- */
-int capable(int cap)
-{
- if (security_ops->capable(current, cap)) {
- /* capability denied */
- return 0;
- }
-
- /* capability granted */
- current->flags |= PF_SUPERPRIV;
- return 1;
-}
-
EXPORT_SYMBOL_GPL(register_security);
EXPORT_SYMBOL_GPL(unregister_security);
EXPORT_SYMBOL_GPL(mod_reg_security);
EXPORT_SYMBOL_GPL(mod_unreg_security);
-EXPORT_SYMBOL(capable);
EXPORT_SYMBOL(security_ops);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 81b726b1a419..b61b9554bc27 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -117,6 +117,8 @@ static struct security_operations *secondary_ops = NULL;
static LIST_HEAD(superblock_security_head);
static DEFINE_SPINLOCK(sb_security_lock);
+static kmem_cache_t *sel_inode_cache;
+
/* Return security context for a given sid or just the context
length if the buffer is null or length is 0 */
static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
@@ -172,10 +174,11 @@ static int inode_alloc_security(struct inode *inode)
struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec;
- isec = kzalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
+ isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL);
if (!isec)
return -ENOMEM;
+ memset(isec, 0, sizeof(*isec));
init_MUTEX(&isec->sem);
INIT_LIST_HEAD(&isec->list);
isec->inode = inode;
@@ -198,7 +201,7 @@ static void inode_free_security(struct inode *inode)
spin_unlock(&sbsec->isec_lock);
inode->i_security = NULL;
- kfree(isec);
+ kmem_cache_free(sel_inode_cache, isec);
}
static int file_alloc_security(struct file *file)
@@ -1955,7 +1958,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
struct task_security_struct *tsec;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
- struct inode_security_struct *isec;
u32 newsid, clen;
int rc;
char *namep = NULL, *context;
@@ -1963,7 +1965,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
tsec = current->security;
dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
- isec = inode->i_security;
if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
newsid = tsec->create_sid;
@@ -1983,7 +1984,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
inode_security_set_sid(inode, newsid);
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
if (name) {
@@ -3316,24 +3317,38 @@ out:
return err;
}
-static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
- int __user *optlen, unsigned len)
+static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+ int __user *optlen, unsigned len)
{
int err = 0;
char *scontext;
u32 scontext_len;
struct sk_security_struct *ssec;
struct inode_security_struct *isec;
+ u32 peer_sid = 0;
isec = SOCK_INODE(sock)->i_security;
- if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
+
+ /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */
+ if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) {
+ ssec = sock->sk->sk_security;
+ peer_sid = ssec->peer_sid;
+ }
+ else if (isec->sclass == SECCLASS_TCP_SOCKET) {
+ peer_sid = selinux_socket_getpeer_stream(sock->sk);
+
+ if (peer_sid == SECSID_NULL) {
+ err = -ENOPROTOOPT;
+ goto out;
+ }
+ }
+ else {
err = -ENOPROTOOPT;
goto out;
}
- ssec = sock->sk->sk_security;
-
- err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
+ err = security_sid_to_context(peer_sid, &scontext, &scontext_len);
+
if (err)
goto out;
@@ -3354,6 +3369,23 @@ out:
return err;
}
+static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen)
+{
+ int err = 0;
+ u32 peer_sid = selinux_socket_getpeer_dgram(skb);
+
+ if (peer_sid == SECSID_NULL)
+ return -EINVAL;
+
+ err = security_sid_to_context(peer_sid, secdata, seclen);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+
static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
return sk_alloc_security(sk, family, priority);
@@ -4338,7 +4370,8 @@ static struct security_operations selinux_ops = {
.socket_setsockopt = selinux_socket_setsockopt,
.socket_shutdown = selinux_socket_shutdown,
.socket_sock_rcv_skb = selinux_socket_sock_rcv_skb,
- .socket_getpeersec = selinux_socket_getpeersec,
+ .socket_getpeersec_stream = selinux_socket_getpeersec_stream,
+ .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram,
.sk_alloc_security = selinux_sk_alloc_security,
.sk_free_security = selinux_sk_free_security,
.sk_getsid = selinux_sk_getsid_security,
@@ -4370,6 +4403,9 @@ static __init int selinux_init(void)
tsec = current->security;
tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ sel_inode_cache = kmem_cache_create("selinux_inode_security",
+ sizeof(struct inode_security_struct),
+ 0, SLAB_PANIC, NULL, NULL);
avc_init();
original_ops = secondary_ops = security_ops;
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 8e87996c6dd5..c10f1fc41502 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -39,6 +39,8 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl)
#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);
+u32 selinux_socket_getpeer_stream(struct sock *sk);
+u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
#else
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
{
@@ -49,6 +51,16 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
{
return NF_ACCEPT;
}
+
+static inline int selinux_socket_getpeer_stream(struct sock *sk)
+{
+ return SECSID_NULL;
+}
+
+static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb)
+{
+ return SECSID_NULL;
+}
#endif
#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 73158244cf8c..b8f4d25cf335 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -88,8 +88,15 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] =
{ XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
{ XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
};
static struct nlmsg_perm nlmsg_audit_perms[] =
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 5eba6664eac0..a4efc966f065 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/security.h>
@@ -45,7 +46,7 @@ static int __init checkreqprot_setup(char *str)
__setup("checkreqprot=", checkreqprot_setup);
-static DECLARE_MUTEX(sel_sem);
+static DEFINE_MUTEX(sel_mutex);
/* global data for booleans */
static struct dentry *bool_dir = NULL;
@@ -238,7 +239,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
ssize_t length;
void *data = NULL;
- down(&sel_sem);
+ mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__LOAD_POLICY);
if (length)
@@ -273,7 +274,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
"policy loaded auid=%u",
audit_get_loginuid(current->audit_context));
out:
- up(&sel_sem);
+ mutex_unlock(&sel_mutex);
vfree(data);
return length;
}
@@ -720,12 +721,11 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
{
char *page = NULL;
ssize_t length;
- ssize_t end;
ssize_t ret;
int cur_enforcing;
struct inode *inode;
- down(&sel_sem);
+ mutex_lock(&sel_mutex);
ret = -EFAULT;
@@ -751,26 +751,9 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]);
- if (length < 0) {
- ret = length;
- goto out;
- }
-
- if (*ppos >= length) {
- ret = 0;
- goto out;
- }
- if (count + *ppos > length)
- count = length - *ppos;
- end = count + *ppos;
- if (copy_to_user(buf, (char *) page + *ppos, count)) {
- ret = -EFAULT;
- goto out;
- }
- *ppos = end;
- ret = count;
+ ret = simple_read_from_buffer(buf, count, ppos, page, length);
out:
- up(&sel_sem);
+ mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long)page);
return ret;
@@ -784,7 +767,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
int new_value;
struct inode *inode;
- down(&sel_sem);
+ mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__SETBOOL);
if (length)
@@ -823,7 +806,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
length = count;
out:
- up(&sel_sem);
+ mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long) page);
return length;
@@ -842,7 +825,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
ssize_t length = -EFAULT;
int new_value;
- down(&sel_sem);
+ mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__SETBOOL);
if (length)
@@ -880,7 +863,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
length = count;
out:
- up(&sel_sem);
+ mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long) page);
return length;
@@ -998,7 +981,7 @@ out:
return ret;
err:
kfree(values);
- d_genocide(dir);
+ sel_remove_bools(dir);
ret = -ENOMEM;
goto out;
}
@@ -1179,37 +1162,38 @@ static int sel_make_avc_files(struct dentry *dir)
dentry = d_alloc_name(dir, files[i].name);
if (!dentry) {
ret = -ENOMEM;
- goto err;
+ goto out;
}
inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
if (!inode) {
ret = -ENOMEM;
- goto err;
+ goto out;
}
inode->i_fop = files[i].ops;
d_add(dentry, inode);
}
out:
return ret;
-err:
- d_genocide(dir);
- goto out;
}
-static int sel_make_dir(struct super_block *sb, struct dentry *dentry)
+static int sel_make_dir(struct inode *dir, struct dentry *dentry)
{
int ret = 0;
struct inode *inode;
- inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
+ inode = sel_make_inode(dir->i_sb, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode) {
ret = -ENOMEM;
goto out;
}
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
d_add(dentry, inode);
+ /* bump link count on parent directory, too */
+ dir->i_nlink++;
out:
return ret;
}
@@ -1218,7 +1202,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
{
int ret;
struct dentry *dentry;
- struct inode *inode;
+ struct inode *inode, *root_inode;
struct inode_security_struct *isec;
static struct tree_descr selinux_files[] = {
@@ -1239,30 +1223,33 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
};
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
if (ret)
- return ret;
+ goto err;
+
+ root_inode = sb->s_root->d_inode;
dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
- if (!dentry)
- return -ENOMEM;
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
- inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
- if (!inode)
- goto out;
- inode->i_op = &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
- d_add(dentry, inode);
- bool_dir = dentry;
- ret = sel_make_bools();
+ ret = sel_make_dir(root_inode, dentry);
if (ret)
- goto out;
+ goto err;
+
+ bool_dir = dentry;
dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
- if (!dentry)
- return -ENOMEM;
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
- if (!inode)
- goto out;
+ if (!inode) {
+ ret = -ENOMEM;
+ goto err;
+ }
isec = (struct inode_security_struct*)inode->i_security;
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
@@ -1273,22 +1260,23 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
selinux_null = dentry;
dentry = d_alloc_name(sb->s_root, "avc");
- if (!dentry)
- return -ENOMEM;
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
- ret = sel_make_dir(sb, dentry);
+ ret = sel_make_dir(root_inode, dentry);
if (ret)
- goto out;
+ goto err;
ret = sel_make_avc_files(dentry);
if (ret)
- goto out;
-
- return 0;
+ goto err;
out:
- dput(dentry);
+ return ret;
+err:
printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__);
- return -ENOMEM;
+ goto out;
}
static struct super_block *sel_get_sb(struct file_system_type *fs_type,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index d877cd16a813..61492485de84 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -27,7 +27,8 @@
#include <linux/in.h>
#include <linux/sched.h>
#include <linux/audit.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
+
#include "flask.h"
#include "avc.h"
#include "avc_ss.h"
@@ -48,9 +49,9 @@ static DEFINE_RWLOCK(policy_rwlock);
#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
-static DECLARE_MUTEX(load_sem);
-#define LOAD_LOCK down(&load_sem)
-#define LOAD_UNLOCK up(&load_sem)
+static DEFINE_MUTEX(load_mutex);
+#define LOAD_LOCK mutex_lock(&load_mutex)
+#define LOAD_UNLOCK mutex_unlock(&load_mutex)
static struct sidtab sidtab;
struct policydb policydb;
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index b2af7ca496c1..dfab6c886698 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -225,6 +225,74 @@ void selinux_xfrm_state_free(struct xfrm_state *x)
}
/*
+ * SELinux internal function to retrieve the context of a connected
+ * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security
+ * association used to connect to the remote socket.
+ *
+ * Retrieve via getsockopt SO_PEERSEC.
+ */
+u32 selinux_socket_getpeer_stream(struct sock *sk)
+{
+ struct dst_entry *dst, *dst_test;
+ u32 peer_sid = SECSID_NULL;
+
+ if (sk->sk_state != TCP_ESTABLISHED)
+ goto out;
+
+ dst = sk_dst_get(sk);
+ if (!dst)
+ goto out;
+
+ 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)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+ peer_sid = ctx->ctx_sid;
+ break;
+ }
+ }
+ dst_release(dst);
+
+out:
+ return peer_sid;
+}
+
+/*
+ * SELinux internal function to retrieve the context of a UDP packet
+ * based on its security association used to connect to the remote socket.
+ *
+ * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message
+ * type SCM_SECURITY.
+ */
+u32 selinux_socket_getpeer_dgram(struct sk_buff *skb)
+{
+ struct sec_path *sp;
+
+ if (skb == NULL)
+ return SECSID_NULL;
+
+ if (skb->sk->sk_protocol != IPPROTO_UDP)
+ return SECSID_NULL;
+
+ sp = skb->sp;
+ if (sp) {
+ int i;
+
+ for (i = sp->len-1; i >= 0; i--) {
+ struct xfrm_state *x = sp->x[i].xvec;
+ if (selinux_authorizable_xfrm(x)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+ return ctx->ctx_sid;
+ }
+ }
+ }
+
+ return SECSID_NULL;
+}
+
+/*
* 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