diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/commoncap.c | 4 | ||||
-rw-r--r-- | security/dummy.c | 14 | ||||
-rw-r--r-- | security/keys/key.c | 18 | ||||
-rw-r--r-- | security/keys/keyctl.c | 155 | ||||
-rw-r--r-- | security/keys/process_keys.c | 7 | ||||
-rw-r--r-- | security/seclvl.c | 210 | ||||
-rw-r--r-- | security/security.c | 23 | ||||
-rw-r--r-- | security/selinux/hooks.c | 112 | ||||
-rw-r--r-- | security/selinux/nlmsgtab.c | 9 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 123 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 24 |
11 files changed, 330 insertions, 369 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 8a6e097f99e..841eb4e5c62 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 a678f094b72..fd99429278e 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -378,7 +378,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name) return 0; } -static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { return -EOPNOTSUPP; } @@ -393,6 +393,11 @@ static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t bu return 0; } +static const char *dummy_inode_xattr_getsuffix(void) +{ + return NULL; +} + static int dummy_file_permission (struct file *file, int mask) { return 0; @@ -558,6 +563,11 @@ static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag) return 0; } +static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) +{ + return -EOPNOTSUPP; +} + static int dummy_msg_msg_alloc_security (struct msg_msg *msg) { return 0; @@ -931,6 +941,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, inode_getxattr); set_to_dummy_if_null(ops, inode_listxattr); set_to_dummy_if_null(ops, inode_removexattr); + set_to_dummy_if_null(ops, inode_xattr_getsuffix); set_to_dummy_if_null(ops, inode_getsecurity); set_to_dummy_if_null(ops, inode_setsecurity); set_to_dummy_if_null(ops, inode_listsecurity); @@ -965,6 +976,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, task_reparent_to_init); set_to_dummy_if_null(ops, task_to_inode); set_to_dummy_if_null(ops, ipc_permission); + set_to_dummy_if_null(ops, ipc_getsecurity); set_to_dummy_if_null(ops, msg_msg_alloc_security); set_to_dummy_if_null(ops, msg_msg_free_security); set_to_dummy_if_null(ops, msg_queue_alloc_security); diff --git a/security/keys/key.c b/security/keys/key.c index 99781b79831..a057e3311aa 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 0c62798ac7d..ed71d86d2ce 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 74cb79eb917..f6940618e34 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 8529ea6f7aa..441beaf1bbc 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 f693e1f66b9..51ef509710b 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 5b16196f282..b61b9554bc2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -117,6 +117,34 @@ 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) +{ + char *context; + unsigned len; + int rc; + + rc = security_sid_to_context(sid, &context, &len); + if (rc) + return rc; + + if (!buffer || !size) + goto getsecurity_exit; + + if (size < len) { + len = -ERANGE; + goto getsecurity_exit; + } + memcpy(buffer, context, len); + +getsecurity_exit: + kfree(context); + return len; +} + /* Allocate and free functions for each kind of security blob. */ static int task_alloc_security(struct task_struct *task) @@ -146,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; @@ -172,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) @@ -1929,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; @@ -1937,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; @@ -1957,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) { @@ -2209,6 +2236,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) return -EACCES; } +static const char *selinux_inode_xattr_getsuffix(void) +{ + return XATTR_SELINUX_SUFFIX; +} + /* * Copy the in-core inode security context value to the user. If the * getxattr() prior to this succeeded, check to see if we need to @@ -2216,47 +2248,14 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { struct inode_security_struct *isec = inode->i_security; - char *context; - unsigned len; - int rc; - - if (strcmp(name, XATTR_SELINUX_SUFFIX)) { - rc = -EOPNOTSUPP; - goto out; - } - rc = security_sid_to_context(isec->sid, &context, &len); - if (rc) - goto out; - - /* Probe for required buffer size */ - if (!buffer || !size) { - rc = len; - goto out_free; - } - - if (size < len) { - rc = -ERANGE; - goto out_free; - } + if (strcmp(name, XATTR_SELINUX_SUFFIX)) + return -EOPNOTSUPP; - if (err > 0) { - if ((len == err) && !(memcmp(context, buffer, len))) { - /* Don't need to canonicalize value */ - rc = err; - goto out_free; - } - memset(buffer, 0, size); - } - memcpy(buffer, context, len); - rc = len; -out_free: - kfree(context); -out: - return rc; + return selinux_getsecurity(isec->sid, buffer, size); } static int selinux_inode_setsecurity(struct inode *inode, const char *name, @@ -4053,6 +4052,13 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) return ipc_has_perm(ipcp, av); } +static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) +{ + struct ipc_security_struct *isec = ipcp->security; + + return selinux_getsecurity(isec->sid, buffer, size); +} + /* module stacking operations */ static int selinux_register_security (const char *name, struct security_operations *ops) { @@ -4094,8 +4100,7 @@ static int selinux_getprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - u32 sid, len; - char *context; + u32 sid; int error; if (current != p) { @@ -4104,9 +4109,6 @@ static int selinux_getprocattr(struct task_struct *p, return error; } - if (!size) - return -ERANGE; - tsec = p->security; if (!strcmp(name, "current")) @@ -4123,16 +4125,7 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, &context, &len); - if (error) - return error; - if (len > size) { - kfree(context); - return -ERANGE; - } - memcpy(value, context, len); - kfree(context); - return len; + return selinux_getsecurity(sid, value, size); } static int selinux_setprocattr(struct task_struct *p, @@ -4290,6 +4283,7 @@ static struct security_operations selinux_ops = { .inode_getxattr = selinux_inode_getxattr, .inode_listxattr = selinux_inode_listxattr, .inode_removexattr = selinux_inode_removexattr, + .inode_xattr_getsuffix = selinux_inode_xattr_getsuffix, .inode_getsecurity = selinux_inode_getsecurity, .inode_setsecurity = selinux_inode_setsecurity, .inode_listsecurity = selinux_inode_listsecurity, @@ -4327,6 +4321,7 @@ static struct security_operations selinux_ops = { .task_to_inode = selinux_task_to_inode, .ipc_permission = selinux_ipc_permission, + .ipc_getsecurity = selinux_ipc_getsecurity, .msg_msg_alloc_security = selinux_msg_msg_alloc_security, .msg_msg_free_security = selinux_msg_msg_free_security, @@ -4408,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/nlmsgtab.c b/security/selinux/nlmsgtab.c index 85e39925983..b8f4d25cf33 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -106,6 +106,9 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, + { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, }; @@ -152,8 +155,10 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) break; case SECCLASS_NETLINK_AUDIT_SOCKET: - if (nlmsg_type >= AUDIT_FIRST_USER_MSG && - nlmsg_type <= AUDIT_LAST_USER_MSG) { + if ((nlmsg_type >= AUDIT_FIRST_USER_MSG && + nlmsg_type <= AUDIT_LAST_USER_MSG) || + (nlmsg_type >= AUDIT_FIRST_USER_MSG2 && + nlmsg_type <= AUDIT_LAST_USER_MSG2)) { *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY; } else { err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index b5fa02d17b1..a4efc966f06 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -15,12 +15,14 @@ #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> #include <linux/major.h> #include <linux/seq_file.h> #include <linux/percpu.h> +#include <linux/audit.h> #include <asm/uaccess.h> #include <asm/semaphore.h> @@ -44,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; @@ -126,6 +128,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, length = task_has_security(current, SECURITY__SETENFORCE); if (length) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "enforcing=%d old_enforcing=%d auid=%u", new_value, + selinux_enforcing, + audit_get_loginuid(current->audit_context)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); @@ -176,6 +182,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, length = selinux_disable(); if (length < 0) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "selinux=0 auid=%u", + audit_get_loginuid(current->audit_context)); } length = count; @@ -230,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) @@ -261,8 +270,11 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, length = ret; else length = count; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, + "policy loaded auid=%u", + audit_get_loginuid(current->audit_context)); out: - up(&sel_sem); + mutex_unlock(&sel_mutex); vfree(data); return length; } @@ -709,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; @@ -740,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; @@ -773,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) @@ -812,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; @@ -831,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) @@ -869,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; @@ -987,7 +981,7 @@ out: return ret; err: kfree(values); - d_genocide(dir); + sel_remove_bools(dir); ret = -ENOMEM; goto out; } @@ -1168,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; } @@ -1207,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[] = { @@ -1228,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; @@ -1262,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 8a764928ff4..61492485de8 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; @@ -1758,19 +1759,22 @@ int security_set_bools(int len, int *values) goto out; } - printk(KERN_INFO "security: committed booleans { "); for (i = 0; i < len; i++) { + if (!!values[i] != policydb.bool_val_to_struct[i]->state) { + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_MAC_CONFIG_CHANGE, + "bool=%s val=%d old_val=%d auid=%u", + policydb.p_bool_val_to_name[i], + !!values[i], + policydb.bool_val_to_struct[i]->state, + audit_get_loginuid(current->audit_context)); + } if (values[i]) { policydb.bool_val_to_struct[i]->state = 1; } else { policydb.bool_val_to_struct[i]->state = 0; } - if (i != 0) - printk(", "); - printk("%s:%d", policydb.p_bool_val_to_name[i], - policydb.bool_val_to_struct[i]->state); } - printk(" }\n"); for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { rc = evaluate_cond_node(&policydb, cur); |