Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

99
security/Kconfig Normal file
View File

@@ -0,0 +1,99 @@
#
# Security configuration
#
menu "Security options"
config KEYS
bool "Enable access key retention support"
help
This option provides support for retaining authentication tokens and
access keys in the kernel.
It also includes provision of methods by which such keys might be
associated with a process so that network filesystems, encryption
support and the like can find them.
Furthermore, a special type of key is available that acts as keyring:
a searchable sequence of keys. Each process is equipped with access
to five standard keyrings: UID-specific, GID-specific, session,
process and thread.
If you are unsure as to whether this is required, answer N.
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
help
This option turns on support for the /proc/keys file - through which
can be listed all the keys on the system that are viewable by the
reading process.
The only keys included in the list are those that grant View
permission to the reading process whether or not it possesses them.
Note that LSM security checks are still performed, and may further
filter out keys that the current process is not authorised to view.
Only key attributes are listed here; key payloads are not included in
the resulting table.
If you are unsure as to whether this is required, answer N.
config SECURITY
bool "Enable different security models"
depends on SYSFS
help
This allows you to choose different security modules to be
configured into your kernel.
If this option is not selected, the default Linux security
model will be used.
If you are unsure how to answer this question, answer N.
config SECURITY_NETWORK
bool "Socket and Networking Security Hooks"
depends on SECURITY
help
This enables the socket and networking security hooks.
If enabled, a security module can use these hooks to
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
help
This enables the "default" Linux capabilities functionality.
If you are unsure how to answer this question, answer Y.
config SECURITY_ROOTPLUG
tristate "Root Plug Support"
depends on USB && SECURITY
help
This is a sample LSM module that should only be used as such.
It prevents any programs running with egid == 0 if a specific
USB device is not present in the system.
See <http://www.linuxjournal.com/article.php?sid=6279> for
more information about this module.
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
endmenu

18
security/Makefile Normal file
View File

@@ -0,0 +1,18 @@
#
# Makefile for the kernel security code
#
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
obj-y += commoncap.o
endif
# Object file lists
obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o

101
security/capability.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* Capabilities Linux Security Module
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/ptrace.h>
#include <linux/moduleparam.h>
static struct security_operations capability_ops = {
.ptrace = cap_ptrace,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,
.capable = cap_capable,
.settime = cap_settime,
.netlink_send = cap_netlink_send,
.netlink_recv = cap_netlink_recv,
.bprm_apply_creds = cap_bprm_apply_creds,
.bprm_set_security = cap_bprm_set_security,
.bprm_secureexec = cap_bprm_secureexec,
.inode_setxattr = cap_inode_setxattr,
.inode_removexattr = cap_inode_removexattr,
.task_post_setuid = cap_task_post_setuid,
.task_reparent_to_init = cap_task_reparent_to_init,
.syslog = cap_syslog,
.vm_enough_memory = cap_vm_enough_memory,
};
/* flag to keep track of how we were registered */
static int secondary;
static int capability_disable;
module_param_named(disable, capability_disable, int, 0);
MODULE_PARM_DESC(disable, "To disable capabilities module set disable = 1");
static int __init capability_init (void)
{
if (capability_disable) {
printk(KERN_INFO "Capabilities disabled at initialization\n");
return 0;
}
/* register ourselves with the security framework */
if (register_security (&capability_ops)) {
/* try registering with primary module */
if (mod_reg_security (KBUILD_MODNAME, &capability_ops)) {
printk (KERN_INFO "Failure registering capabilities "
"with primary security module.\n");
return -EINVAL;
}
secondary = 1;
}
printk (KERN_INFO "Capability LSM initialized%s\n",
secondary ? " as secondary" : "");
return 0;
}
static void __exit capability_exit (void)
{
if (capability_disable)
return;
/* remove ourselves from the security framework */
if (secondary) {
if (mod_unreg_security (KBUILD_MODNAME, &capability_ops))
printk (KERN_INFO "Failure unregistering capabilities "
"with primary module.\n");
return;
}
if (unregister_security (&capability_ops)) {
printk (KERN_INFO
"Failure unregistering capabilities with the kernel\n");
}
}
security_initcall (capability_init);
module_exit (capability_exit);
MODULE_DESCRIPTION("Standard Linux Capabilities Security Module");
MODULE_LICENSE("GPL");

345
security/commoncap.c Normal file
View File

@@ -0,0 +1,345 @@
/* Common capabilities, needed by capability.o and root_plug.o
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/ptrace.h>
#include <linux/xattr.h>
#include <linux/hugetlb.h>
int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
{
NETLINK_CB(skb).eff_cap = current->cap_effective;
return 0;
}
EXPORT_SYMBOL(cap_netlink_send);
int cap_netlink_recv(struct sk_buff *skb, int cap)
{
if (!cap_raised(NETLINK_CB(skb).eff_cap, cap))
return -EPERM;
return 0;
}
EXPORT_SYMBOL(cap_netlink_recv);
int cap_capable (struct task_struct *tsk, int cap)
{
/* Derived from include/linux/sched.h:capable. */
if (cap_raised(tsk->cap_effective, cap))
return 0;
return -EPERM;
}
int cap_settime(struct timespec *ts, struct timezone *tz)
{
if (!capable(CAP_SYS_TIME))
return -EPERM;
return 0;
}
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, parent->cap_permitted) &&
!__capable(parent, CAP_SYS_PTRACE))
return -EPERM;
return 0;
}
int cap_capget (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
/* Derived from kernel/capability.c:sys_capget. */
*effective = cap_t (target->cap_effective);
*inheritable = cap_t (target->cap_inheritable);
*permitted = cap_t (target->cap_permitted);
return 0;
}
int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
/* Derived from kernel/capability.c:sys_capset. */
/* verify restrictions on target's new Inheritable set */
if (!cap_issubset (*inheritable,
cap_combine (target->cap_inheritable,
current->cap_permitted))) {
return -EPERM;
}
/* verify restrictions on target's new Permitted set */
if (!cap_issubset (*permitted,
cap_combine (target->cap_permitted,
current->cap_permitted))) {
return -EPERM;
}
/* verify the _new_Effective_ is a subset of the _new_Permitted_ */
if (!cap_issubset (*effective, *permitted)) {
return -EPERM;
}
return 0;
}
void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
target->cap_effective = *effective;
target->cap_inheritable = *inheritable;
target->cap_permitted = *permitted;
}
int cap_bprm_set_security (struct linux_binprm *bprm)
{
/* Copied from fs/exec.c:prepare_binprm. */
/* We don't have VFS support for capabilities yet */
cap_clear (bprm->cap_inheritable);
cap_clear (bprm->cap_permitted);
cap_clear (bprm->cap_effective);
/* To support inheritance of root-permissions and suid-root
* executables under compatibility mode, we raise all three
* capability sets for the file.
*
* If only the real uid is 0, we only raise the inheritable
* and permitted sets of the executable file.
*/
if (!issecure (SECURE_NOROOT)) {
if (bprm->e_uid == 0 || current->uid == 0) {
cap_set_full (bprm->cap_inheritable);
cap_set_full (bprm->cap_permitted);
}
if (bprm->e_uid == 0)
cap_set_full (bprm->cap_effective);
}
return 0;
}
void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
{
/* Derived from fs/exec.c:compute_creds. */
kernel_cap_t new_permitted, working;
new_permitted = cap_intersect (bprm->cap_permitted, cap_bset);
working = cap_intersect (bprm->cap_inheritable,
current->cap_inheritable);
new_permitted = cap_combine (new_permitted, working);
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
!cap_issubset (new_permitted, current->cap_permitted)) {
current->mm->dumpable = suid_dumpable;
if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
if (!capable(CAP_SETUID)) {
bprm->e_uid = current->uid;
bprm->e_gid = current->gid;
}
if (!capable (CAP_SETPCAP)) {
new_permitted = cap_intersect (new_permitted,
current->cap_permitted);
}
}
}
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
if (!is_init(current)) {
current->cap_permitted = new_permitted;
current->cap_effective =
cap_intersect (new_permitted, bprm->cap_effective);
}
/* AUD: Audit candidate if current->cap_effective is set */
current->keep_capabilities = 0;
}
int cap_bprm_secureexec (struct linux_binprm *bprm)
{
/* If/when this module is enhanced to incorporate capability
bits on files, the test below should be extended to also perform a
test between the old and new capability sets. For now,
it simply preserves the legacy decision algorithm used by
the old userland. */
return (current->euid != current->uid ||
current->egid != current->gid);
}
int cap_inode_setxattr(struct dentry *dentry, char *name, void *value,
size_t size, int flags)
{
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
int cap_inode_removexattr(struct dentry *dentry, char *name)
{
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
/* moved from kernel/sys.c. */
/*
* cap_emulate_setxuid() fixes the effective / permitted capabilities of
* a process after a call to setuid, setreuid, or setresuid.
*
* 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of
* {r,e,s}uid != 0, the permitted and effective capabilities are
* cleared.
*
* 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective
* capabilities of the process are cleared.
*
* 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
* capabilities are set to the permitted capabilities.
*
* fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
* never happen.
*
* -astor
*
* cevans - New behaviour, Oct '99
* A process may, via prctl(), elect to keep its capabilities when it
* calls setuid() and switches away from uid==0. Both permitted and
* effective sets will be retained.
* Without this change, it was impossible for a daemon to drop only some
* of its privilege. The call to setuid(!=0) would drop all privileges!
* Keeping uid 0 is not an option because uid 0 owns too many vital
* files..
* Thanks to Olaf Kirch and Peter Benie for spotting this.
*/
static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
int old_suid)
{
if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
(current->uid != 0 && current->euid != 0 && current->suid != 0) &&
!current->keep_capabilities) {
cap_clear (current->cap_permitted);
cap_clear (current->cap_effective);
}
if (old_euid == 0 && current->euid != 0) {
cap_clear (current->cap_effective);
}
if (old_euid != 0 && current->euid == 0) {
current->cap_effective = current->cap_permitted;
}
}
int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
int flags)
{
switch (flags) {
case LSM_SETID_RE:
case LSM_SETID_ID:
case LSM_SETID_RES:
/* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
if (!issecure (SECURE_NO_SETUID_FIXUP)) {
cap_emulate_setxuid (old_ruid, old_euid, old_suid);
}
break;
case LSM_SETID_FS:
{
uid_t old_fsuid = old_ruid;
/* Copied from kernel/sys.c:setfsuid. */
/*
* FIXME - is fsuser used for all CAP_FS_MASK capabilities?
* if not, we might be a bit too harsh here.
*/
if (!issecure (SECURE_NO_SETUID_FIXUP)) {
if (old_fsuid == 0 && current->fsuid != 0) {
cap_t (current->cap_effective) &=
~CAP_FS_MASK;
}
if (old_fsuid != 0 && current->fsuid == 0) {
cap_t (current->cap_effective) |=
(cap_t (current->cap_permitted) &
CAP_FS_MASK);
}
}
break;
}
default:
return -EINVAL;
}
return 0;
}
void cap_task_reparent_to_init (struct task_struct *p)
{
p->cap_effective = CAP_INIT_EFF_SET;
p->cap_inheritable = CAP_INIT_INH_SET;
p->cap_permitted = CAP_FULL_SET;
p->keep_capabilities = 0;
return;
}
int cap_syslog (int type)
{
if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
int cap_vm_enough_memory(long pages)
{
int cap_sys_admin = 0;
if (cap_capable(current, CAP_SYS_ADMIN) == 0)
cap_sys_admin = 1;
return __vm_enough_memory(pages, cap_sys_admin);
}
EXPORT_SYMBOL(cap_capable);
EXPORT_SYMBOL(cap_settime);
EXPORT_SYMBOL(cap_ptrace);
EXPORT_SYMBOL(cap_capget);
EXPORT_SYMBOL(cap_capset_check);
EXPORT_SYMBOL(cap_capset_set);
EXPORT_SYMBOL(cap_bprm_set_security);
EXPORT_SYMBOL(cap_bprm_apply_creds);
EXPORT_SYMBOL(cap_bprm_secureexec);
EXPORT_SYMBOL(cap_inode_setxattr);
EXPORT_SYMBOL(cap_inode_removexattr);
EXPORT_SYMBOL(cap_task_post_setuid);
EXPORT_SYMBOL(cap_task_reparent_to_init);
EXPORT_SYMBOL(cap_syslog);
EXPORT_SYMBOL(cap_vm_enough_memory);
MODULE_DESCRIPTION("Standard Linux Common Capabilities Security Module");
MODULE_LICENSE("GPL");

1132
security/dummy.c Normal file

File diff suppressed because it is too large Load Diff

345
security/inode.c Normal file
View File

@@ -0,0 +1,345 @@
/*
* inode.c - securityfs
*
* Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
*
* 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.
*
* Based on fs/debugfs/inode.c which had the following copyright notice:
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc.
*/
/* #define DEBUG */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/security.h>
#define SECURITYFS_MAGIC 0x73636673
static struct vfsmount *mount;
static int mount_count;
/*
* TODO:
* I think I can get rid of these default_file_ops, but not quite sure...
*/
static ssize_t default_read_file(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t default_write_file(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
static int default_open(struct inode *inode, struct file *file)
{
if (inode->i_private)
file->private_data = inode->i_private;
return 0;
}
static const struct file_operations default_file_ops = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
};
static struct inode *get_inode(struct super_block *sb, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &default_file_ops;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
break;
}
}
return inode;
}
/* SMP-safe */
static int mknod(struct inode *dir, struct dentry *dentry,
int mode, dev_t dev)
{
struct inode *inode;
int error = -EPERM;
if (dentry->d_inode)
return -EEXIST;
inode = get_inode(dir->i_sb, mode, dev);
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
error = 0;
}
return error;
}
static int mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int res;
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = mknod(dir, dentry, mode, 0);
if (!res)
inc_nlink(dir);
return res;
}
static int create(struct inode *dir, struct dentry *dentry, int mode)
{
mode = (mode & S_IALLUGO) | S_IFREG;
return mknod(dir, dentry, mode, 0);
}
static inline int positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static int fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr files[] = {{""}};
return simple_fill_super(sb, SECURITYFS_MAGIC, files);
}
static int get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data, struct vfsmount *mnt)
{
return get_sb_single(fs_type, flags, data, fill_super, mnt);
}
static struct file_system_type fs_type = {
.owner = THIS_MODULE,
.name = "securityfs",
.get_sb = get_sb,
.kill_sb = kill_litter_super,
};
static int create_by_name(const char *name, mode_t mode,
struct dentry *parent,
struct dentry **dentry)
{
int error = 0;
*dentry = NULL;
/* If the parent is not specified, we create it in the root.
* We need the root dentry to do this, which is in the super
* block. A pointer to that is in the struct vfsmount that we
* have around.
*/
if (!parent ) {
if (mount && mount->mnt_sb) {
parent = mount->mnt_sb->s_root;
}
}
if (!parent) {
pr_debug("securityfs: Ah! can not find a parent!\n");
return -EFAULT;
}
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)
error = mkdir(parent->d_inode, *dentry, mode);
else
error = create(parent->d_inode, *dentry, mode);
} else
error = PTR_ERR(dentry);
mutex_unlock(&parent->d_inode->i_mutex);
return error;
}
/**
* securityfs_create_file - create a file in the securityfs filesystem
*
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* file will be created in the root of the securityfs filesystem.
* @data: a pointer to something that the caller will want to get to later
* on. The inode.i_private pointer will point to this value on
* the open() call.
* @fops: a pointer to a struct file_operations that should be used for
* this file.
*
* This is the basic "create a file" function for securityfs. It allows for a
* wide range of flexibility in createing a file, or a directory (if you
* want to create a directory, the securityfs_create_dir() function is
* recommended to be used instead.)
*
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
*
* If securityfs is not enabled in the kernel, the value -ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *securityfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops)
{
struct dentry *dentry = NULL;
int error;
pr_debug("securityfs: creating file '%s'\n",name);
error = simple_pin_fs(&fs_type, &mount, &mount_count);
if (error) {
dentry = ERR_PTR(error);
goto exit;
}
error = create_by_name(name, mode, parent, &dentry);
if (error) {
dentry = ERR_PTR(error);
simple_release_fs(&mount, &mount_count);
goto exit;
}
if (dentry->d_inode) {
if (fops)
dentry->d_inode->i_fop = fops;
if (data)
dentry->d_inode->i_private = data;
}
exit:
return dentry;
}
EXPORT_SYMBOL_GPL(securityfs_create_file);
/**
* securityfs_create_dir - create a directory in the securityfs filesystem
*
* @name: a pointer to a string containing the name of the directory to
* create.
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory will be created in the root of the securityfs filesystem.
*
* This function creates a directory in securityfs with the given name.
*
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
*
* If securityfs is not enabled in the kernel, the value -ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *securityfs_create_dir(const char *name, struct dentry *parent)
{
return securityfs_create_file(name,
S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
parent, NULL, NULL);
}
EXPORT_SYMBOL_GPL(securityfs_create_dir);
/**
* securityfs_remove - removes a file or directory from the securityfs filesystem
*
* @dentry: a pointer to a the dentry of the file or directory to be
* removed.
*
* This function removes a file or directory in securityfs that was previously
* created with a call to another securityfs function (like
* securityfs_create_file() or variants thereof.)
*
* This function is required to be called in order for the file to be
* removed, no automatic cleanup of files will happen when a module is
* removed, you are responsible here.
*/
void securityfs_remove(struct dentry *dentry)
{
struct dentry *parent;
if (!dentry)
return;
parent = dentry->d_parent;
if (!parent || !parent->d_inode)
return;
mutex_lock(&parent->d_inode->i_mutex);
if (positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode))
simple_rmdir(parent->d_inode, dentry);
else
simple_unlink(parent->d_inode, dentry);
dput(dentry);
}
}
mutex_unlock(&parent->d_inode->i_mutex);
simple_release_fs(&mount, &mount_count);
}
EXPORT_SYMBOL_GPL(securityfs_remove);
static decl_subsys(security, NULL, NULL);
static int __init securityfs_init(void)
{
int retval;
kset_set_kset_s(&security_subsys, kernel_subsys);
retval = subsystem_register(&security_subsys);
if (retval)
return retval;
retval = register_filesystem(&fs_type);
if (retval)
subsystem_unregister(&security_subsys);
return retval;
}
static void __exit securityfs_exit(void)
{
simple_release_fs(&mount, &mount_count);
unregister_filesystem(&fs_type);
subsystem_unregister(&security_subsys);
}
core_initcall(securityfs_init);
module_exit(securityfs_exit);
MODULE_LICENSE("GPL");

16
security/keys/Makefile Normal file
View File

@@ -0,0 +1,16 @@
#
# Makefile for key management
#
obj-y := \
key.o \
keyring.o \
keyctl.o \
permission.o \
process_keys.o \
request_key.o \
request_key_auth.o \
user_defined.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o

86
security/keys/compat.c Normal file
View File

@@ -0,0 +1,86 @@
/* compat.c: 32-bit compatibility syscall for 64-bit systems
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/syscalls.h>
#include <linux/keyctl.h>
#include <linux/compat.h>
#include "internal.h"
/*****************************************************************************/
/*
* the key control system call, 32-bit compatibility version for 64-bit archs
* - this should only be called if the 64-bit arch uses weird pointers in
* 32-bit mode or doesn't guarantee that the top 32-bits of the argument
* registers on taking a 32-bit syscall are zero
* - if you can, you should call sys_keyctl directly
*/
asmlinkage long compat_sys_keyctl(u32 option,
u32 arg2, u32 arg3, u32 arg4, u32 arg5)
{
switch (option) {
case KEYCTL_GET_KEYRING_ID:
return keyctl_get_keyring_ID(arg2, arg3);
case KEYCTL_JOIN_SESSION_KEYRING:
return keyctl_join_session_keyring(compat_ptr(arg2));
case KEYCTL_UPDATE:
return keyctl_update_key(arg2, compat_ptr(arg3), arg4);
case KEYCTL_REVOKE:
return keyctl_revoke_key(arg2);
case KEYCTL_DESCRIBE:
return keyctl_describe_key(arg2, compat_ptr(arg3), arg4);
case KEYCTL_CLEAR:
return keyctl_keyring_clear(arg2);
case KEYCTL_LINK:
return keyctl_keyring_link(arg2, arg3);
case KEYCTL_UNLINK:
return keyctl_keyring_unlink(arg2, arg3);
case KEYCTL_SEARCH:
return keyctl_keyring_search(arg2, compat_ptr(arg3),
compat_ptr(arg4), arg5);
case KEYCTL_READ:
return keyctl_read_key(arg2, compat_ptr(arg3), arg4);
case KEYCTL_CHOWN:
return keyctl_chown_key(arg2, arg3, arg4);
case KEYCTL_SETPERM:
return keyctl_setperm_key(arg2, arg3);
case KEYCTL_INSTANTIATE:
return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4,
arg5);
case KEYCTL_NEGATE:
return keyctl_negate_key(arg2, arg3, arg4);
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;
}
} /* end compat_sys_keyctl() */

164
security/keys/internal.h Normal file
View File

@@ -0,0 +1,164 @@
/* internal.h: authentication token and access key management internal defs
*
* Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _INTERNAL_H
#define _INTERNAL_H
#include <linux/key.h>
#include <linux/key-ui.h>
#if 0
#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a)
#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
#define kdebug(FMT, a...) printk(FMT"\n" , ## a)
#else
#define kenter(FMT, a...) do {} while(0)
#define kleave(FMT, a...) do {} while(0)
#define kdebug(FMT, a...) do {} while(0)
#endif
extern struct key_type key_type_user;
/*****************************************************************************/
/*
* keep track of keys for a user
* - this needs to be separate to user_struct to avoid a refcount-loop
* (user_struct pins some keyrings which pin this struct)
* - this also keeps track of keys under request from userspace for this UID
*/
struct key_user {
struct rb_node node;
struct list_head consq; /* construction queue */
spinlock_t lock;
atomic_t usage; /* for accessing qnkeys & qnbytes */
atomic_t nkeys; /* number of keys */
atomic_t nikeys; /* number of instantiated keys */
uid_t uid;
int qnkeys; /* number of keys allocated to this user */
int qnbytes; /* number of bytes allocated to this user */
};
#define KEYQUOTA_MAX_KEYS 100
#define KEYQUOTA_MAX_BYTES 10000
#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */
extern struct rb_root key_user_tree;
extern spinlock_t key_user_lock;
extern struct key_user root_key_user;
extern struct key_user *key_user_lookup(uid_t uid);
extern void key_user_put(struct key_user *user);
extern struct rb_root key_serial_tree;
extern spinlock_t key_serial_lock;
extern struct semaphore key_alloc_sem;
extern struct rw_semaphore key_construction_sem;
extern wait_queue_head_t request_key_conswq;
extern void keyring_publish_name(struct key *keyring);
extern int __key_link(struct key *keyring, struct key *key);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *type,
const char *description,
key_perm_t perm);
extern struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id);
typedef int (*key_match_func_t)(const struct key *, const void *);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct task_struct *tsk,
struct key_type *type,
const void *description,
key_match_func_t match);
extern key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
struct task_struct *tsk);
extern struct key *find_keyring_by_name(const char *name, key_serial_t bound);
extern int install_thread_keyring(struct task_struct *tsk);
extern int install_process_keyring(struct task_struct *tsk);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
struct key *dest_keyring,
unsigned long flags);
/*
* request_key authorisation
*/
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,
const char *callout_info);
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
/*
* keyctl functions
*/
extern long keyctl_get_keyring_ID(key_serial_t, int);
extern long keyctl_join_session_keyring(const char __user *);
extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
extern long keyctl_revoke_key(key_serial_t);
extern long keyctl_keyring_clear(key_serial_t);
extern long keyctl_keyring_link(key_serial_t, key_serial_t);
extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
extern long keyctl_keyring_search(key_serial_t, const char __user *,
const char __user *, key_serial_t);
extern long keyctl_read_key(key_serial_t, char __user *, size_t);
extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
extern long keyctl_setperm_key(key_serial_t, key_perm_t);
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);
/*
* debugging key validation
*/
#ifdef KEY_DEBUGGING
extern void __key_check(const struct key *);
static inline void key_check(const struct key *key)
{
if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC))
__key_check(key);
}
#else
#define key_check(key) do {} while(0)
#endif
#endif /* _INTERNAL_H */

1032
security/keys/key.c Normal file

File diff suppressed because it is too large Load Diff

1142
security/keys/keyctl.c Normal file

File diff suppressed because it is too large Load Diff

974
security/keys/keyring.c Normal file
View File

@@ -0,0 +1,974 @@
/* keyring.c: keyring handling
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <asm/uaccess.h>
#include "internal.h"
/*
* when plumbing the depths of the key tree, this sets a hard limit set on how
* deep we're willing to go
*/
#define KEYRING_SEARCH_MAX_DEPTH 6
/*
* we keep all named keyrings in a hash to speed looking them up
*/
#define KEYRING_NAME_HASH_SIZE (1 << 5)
static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
static DEFINE_RWLOCK(keyring_name_lock);
static inline unsigned keyring_hash(const char *desc)
{
unsigned bucket = 0;
for (; *desc; desc++)
bucket += (unsigned char) *desc;
return bucket & (KEYRING_NAME_HASH_SIZE - 1);
}
/*
* the keyring type definition
*/
static int keyring_instantiate(struct key *keyring,
const void *data, size_t datalen);
static int keyring_match(const struct key *keyring, const void *criterion);
static void keyring_revoke(struct key *keyring);
static void keyring_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m);
static long keyring_read(const struct key *keyring,
char __user *buffer, size_t buflen);
struct key_type key_type_keyring = {
.name = "keyring",
.def_datalen = sizeof(struct keyring_list),
.instantiate = keyring_instantiate,
.match = keyring_match,
.revoke = keyring_revoke,
.destroy = keyring_destroy,
.describe = keyring_describe,
.read = keyring_read,
};
/*
* semaphore to serialise link/link calls to prevent two link calls in parallel
* introducing a cycle
*/
static DECLARE_RWSEM(keyring_serialise_link_sem);
/*****************************************************************************/
/*
* publish the name of a keyring so that it can be found by name (if it has
* one)
*/
void keyring_publish_name(struct key *keyring)
{
int bucket;
if (keyring->description) {
bucket = keyring_hash(keyring->description);
write_lock(&keyring_name_lock);
if (!keyring_name_hash[bucket].next)
INIT_LIST_HEAD(&keyring_name_hash[bucket]);
list_add_tail(&keyring->type_data.link,
&keyring_name_hash[bucket]);
write_unlock(&keyring_name_lock);
}
} /* end keyring_publish_name() */
/*****************************************************************************/
/*
* initialise a keyring
* - we object if we were given any data
*/
static int keyring_instantiate(struct key *keyring,
const void *data, size_t datalen)
{
int ret;
ret = -EINVAL;
if (datalen == 0) {
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
ret = 0;
}
return ret;
} /* end keyring_instantiate() */
/*****************************************************************************/
/*
* match keyrings on their name
*/
static int keyring_match(const struct key *keyring, const void *description)
{
return keyring->description &&
strcmp(keyring->description, description) == 0;
} /* end keyring_match() */
/*****************************************************************************/
/*
* dispose of the data dangling from the corpse of a keyring
*/
static void keyring_destroy(struct key *keyring)
{
struct keyring_list *klist;
int loop;
if (keyring->description) {
write_lock(&keyring_name_lock);
if (keyring->type_data.link.next != NULL &&
!list_empty(&keyring->type_data.link))
list_del(&keyring->type_data.link);
write_unlock(&keyring_name_lock);
}
klist = rcu_dereference(keyring->payload.subscriptions);
if (klist) {
for (loop = klist->nkeys - 1; loop >= 0; loop--)
key_put(klist->keys[loop]);
kfree(klist);
}
} /* end keyring_destroy() */
/*****************************************************************************/
/*
* describe the keyring
*/
static void keyring_describe(const struct key *keyring, struct seq_file *m)
{
struct keyring_list *klist;
if (keyring->description) {
seq_puts(m, keyring->description);
}
else {
seq_puts(m, "[anon]");
}
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
if (klist)
seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
else
seq_puts(m, ": empty");
rcu_read_unlock();
} /* end keyring_describe() */
/*****************************************************************************/
/*
* read a list of key IDs from the keyring's contents
* - the keyring's semaphore is read-locked
*/
static long keyring_read(const struct key *keyring,
char __user *buffer, size_t buflen)
{
struct keyring_list *klist;
struct key *key;
size_t qty, tmp;
int loop, ret;
ret = 0;
klist = rcu_dereference(keyring->payload.subscriptions);
if (klist) {
/* calculate how much data we could return */
qty = klist->nkeys * sizeof(key_serial_t);
if (buffer && buflen > 0) {
if (buflen > qty)
buflen = qty;
/* copy the IDs of the subscribed keys into the
* buffer */
ret = -EFAULT;
for (loop = 0; loop < klist->nkeys; loop++) {
key = klist->keys[loop];
tmp = sizeof(key_serial_t);
if (tmp > buflen)
tmp = buflen;
if (copy_to_user(buffer,
&key->serial,
tmp) != 0)
goto error;
buflen -= tmp;
if (buflen == 0)
break;
buffer += tmp;
}
}
ret = qty;
}
error:
return ret;
} /* end keyring_read() */
/*****************************************************************************/
/*
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
struct task_struct *ctx, unsigned long flags,
struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
uid, gid, ctx,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
flags);
if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
if (ret < 0) {
key_put(keyring);
keyring = ERR_PTR(ret);
}
}
return keyring;
} /* end keyring_alloc() */
/*****************************************************************************/
/*
* search the supplied keyring tree for a key that matches the criterion
* - perform a breadth-then-depth search up to the prescribed limit
* - we only find keys on which we have search permission
* - we use the supplied match function to see if the description (or other
* feature of interest) matches
* - we rely on RCU to prevent the keyring lists from disappearing on us
* - we return -EAGAIN if we didn't find any matching key
* - we return -ENOKEY if we only found negative matching keys
* - we propagate the possession attribute from the keyring ref to the key ref
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct task_struct *context,
struct key_type *type,
const void *description,
key_match_func_t match)
{
struct {
struct keyring_list *keylist;
int kix;
} stack[KEYRING_SEARCH_MAX_DEPTH];
struct keyring_list *keylist;
struct timespec now;
unsigned long possessed;
struct key *keyring, *key;
key_ref_t key_ref;
long err;
int sp, kix;
keyring = key_ref_to_ptr(keyring_ref);
possessed = is_key_possessed(keyring_ref);
key_check(keyring);
/* top keyring must have search permission to begin the search */
err = key_task_permission(keyring_ref, context, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
}
key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
goto error;
rcu_read_lock();
now = current_kernel_time();
err = -EAGAIN;
sp = 0;
/* start processing a new keyring */
descend:
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto not_this_keyring;
keylist = rcu_dereference(keyring->payload.subscriptions);
if (!keylist)
goto not_this_keyring;
/* iterate through the keys in this keyring first */
for (kix = 0; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
/* ignore keys not of this type */
if (key->type != type)
continue;
/* skip revoked keys and expired keys */
if (test_bit(KEY_FLAG_REVOKED, &key->flags))
continue;
if (key->expiry && now.tv_sec >= key->expiry)
continue;
/* keys that don't match */
if (!match(key, description))
continue;
/* key must have search permissions */
if (key_task_permission(make_key_ref(key, possessed),
context, KEY_SEARCH) < 0)
continue;
/* we set a different error code if we find a negative key */
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
err = -ENOKEY;
continue;
}
goto found;
}
/* search through the keyrings nested in this one */
kix = 0;
ascend:
for (; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
if (key->type != &key_type_keyring)
continue;
/* recursively search nested keyrings
* - only search keyrings for which we have search permission
*/
if (sp >= KEYRING_SEARCH_MAX_DEPTH)
continue;
if (key_task_permission(make_key_ref(key, possessed),
context, KEY_SEARCH) < 0)
continue;
/* stack the current position */
stack[sp].keylist = keylist;
stack[sp].kix = kix;
sp++;
/* begin again with the new keyring */
keyring = key;
goto descend;
}
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
not_this_keyring:
if (sp > 0) {
/* resume the processing of a keyring higher up in the tree */
sp--;
keylist = stack[sp].keylist;
kix = stack[sp].kix + 1;
goto ascend;
}
key_ref = ERR_PTR(err);
goto error_2;
/* we found a viable match */
found:
atomic_inc(&key->usage);
key_check(key);
key_ref = make_key_ref(key, possessed);
error_2:
rcu_read_unlock();
error:
return key_ref;
} /* end keyring_search_aux() */
/*****************************************************************************/
/*
* search the supplied keyring tree for a key that matches the criterion
* - perform a breadth-then-depth search up to the prescribed limit
* - we only find keys on which we have search permission
* - we readlock the keyrings as we search down the tree
* - we return -EAGAIN if we didn't find any matching key
* - we return -ENOKEY if we only found negative matching keys
*/
key_ref_t keyring_search(key_ref_t keyring,
struct key_type *type,
const char *description)
{
if (!type->match)
return ERR_PTR(-ENOKEY);
return keyring_search_aux(keyring, current,
type, description, type->match);
} /* end keyring_search() */
EXPORT_SYMBOL(keyring_search);
/*****************************************************************************/
/*
* search the given keyring only (no recursion)
* - keyring must be locked by caller
* - caller must guarantee that the keyring is a keyring
*/
key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *ktype,
const char *description,
key_perm_t perm)
{
struct keyring_list *klist;
unsigned long possessed;
struct key *keyring, *key;
int loop;
keyring = key_ref_to_ptr(keyring_ref);
possessed = is_key_possessed(keyring_ref);
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
if (klist) {
for (loop = 0; loop < klist->nkeys; loop++) {
key = klist->keys[loop];
if (key->type == ktype &&
(!key->type->match ||
key->type->match(key, description)) &&
key_permission(make_key_ref(key, possessed),
perm) == 0 &&
!test_bit(KEY_FLAG_REVOKED, &key->flags)
)
goto found;
}
}
rcu_read_unlock();
return ERR_PTR(-ENOKEY);
found:
atomic_inc(&key->usage);
rcu_read_unlock();
return make_key_ref(key, possessed);
} /* end __keyring_search_one() */
/*****************************************************************************/
/*
* find a keyring with the specified name
* - all named keyrings are searched
* - only find keyrings with search permission for the process
* - only find keyrings with a serial number greater than the one specified
*/
struct key *find_keyring_by_name(const char *name, key_serial_t bound)
{
struct key *keyring;
int bucket;
keyring = ERR_PTR(-EINVAL);
if (!name)
goto error;
bucket = keyring_hash(name);
read_lock(&keyring_name_lock);
if (keyring_name_hash[bucket].next) {
/* search this hash bucket for a keyring with a matching name
* that's readable and that hasn't been revoked */
list_for_each_entry(keyring,
&keyring_name_hash[bucket],
type_data.link
) {
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
continue;
if (strcmp(keyring->description, name) != 0)
continue;
if (key_permission(make_key_ref(keyring, 0),
KEY_SEARCH) < 0)
continue;
/* found a potential candidate, but we still need to
* check the serial number */
if (keyring->serial <= bound)
continue;
/* we've got a match */
atomic_inc(&keyring->usage);
read_unlock(&keyring_name_lock);
goto error;
}
}
read_unlock(&keyring_name_lock);
keyring = ERR_PTR(-ENOKEY);
error:
return keyring;
} /* end find_keyring_by_name() */
/*****************************************************************************/
/*
* see if a cycle will will be created by inserting acyclic tree B in acyclic
* tree A at the topmost level (ie: as a direct child of A)
* - since we are adding B to A at the top level, checking for cycles should
* just be a matter of seeing if node A is somewhere in tree B
*/
static int keyring_detect_cycle(struct key *A, struct key *B)
{
struct {
struct keyring_list *keylist;
int kix;
} stack[KEYRING_SEARCH_MAX_DEPTH];
struct keyring_list *keylist;
struct key *subtree, *key;
int sp, kix, ret;
rcu_read_lock();
ret = -EDEADLK;
if (A == B)
goto cycle_detected;
subtree = B;
sp = 0;
/* start processing a new keyring */
descend:
if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
goto not_this_keyring;
keylist = rcu_dereference(subtree->payload.subscriptions);
if (!keylist)
goto not_this_keyring;
kix = 0;
ascend:
/* iterate through the remaining keys in this keyring */
for (; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
if (key == A)
goto cycle_detected;
/* recursively check nested keyrings */
if (key->type == &key_type_keyring) {
if (sp >= KEYRING_SEARCH_MAX_DEPTH)
goto too_deep;
/* stack the current position */
stack[sp].keylist = keylist;
stack[sp].kix = kix;
sp++;
/* begin again with the new keyring */
subtree = key;
goto descend;
}
}
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
not_this_keyring:
if (sp > 0) {
/* resume the checking of a keyring higher up in the tree */
sp--;
keylist = stack[sp].keylist;
kix = stack[sp].kix + 1;
goto ascend;
}
ret = 0; /* no cycles detected */
error:
rcu_read_unlock();
return ret;
too_deep:
ret = -ELOOP;
goto error;
cycle_detected:
ret = -EDEADLK;
goto error;
} /* end keyring_detect_cycle() */
/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period
*/
static void keyring_link_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
kfree(klist);
} /* end keyring_link_rcu_disposal() */
/*****************************************************************************/
/*
* 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 loop, ret;
ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto error;
ret = -ENOTDIR;
if (keyring->type != &key_type_keyring)
goto error;
/* serialise link/link calls to prevent parallel calls causing a
* cycle when applied to two keyring in opposite orders */
down_write(&keyring_serialise_link_sem);
/* check that we aren't going to create a cycle adding one keyring to
* another */
if (key->type == &key_type_keyring) {
ret = keyring_detect_cycle(keyring, key);
if (ret < 0)
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 = kmemdup(klist, size, GFP_KERNEL);
if (!nklist)
goto error2;
/* 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);
if (ret < 0)
goto error2;
klist = keyring->payload.subscriptions;
if (klist && klist->nkeys < klist->maxkeys) {
/* there's sufficient slack space to add directly */
atomic_inc(&key->usage);
klist->keys[klist->nkeys] = key;
smp_wmb();
klist->nkeys++;
smp_wmb();
}
else {
/* grow the key list */
max = 4;
if (klist)
max += klist->maxkeys;
ret = -ENFILE;
if (max > 65535)
goto error3;
size = sizeof(*klist) + sizeof(struct key *) * max;
if (size > PAGE_SIZE)
goto error3;
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
if (!nklist)
goto error3;
nklist->maxkeys = max;
nklist->nkeys = 0;
if (klist) {
nklist->nkeys = klist->nkeys;
memcpy(nklist->keys,
klist->keys,
sizeof(struct key *) * klist->nkeys);
}
/* add the key into the new space */
atomic_inc(&key->usage);
nklist->keys[nklist->nkeys++] = key;
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
/* dispose of the old keyring list */
if (klist)
call_rcu(&klist->rcu, keyring_link_rcu_disposal);
}
done:
ret = 0;
error2:
up_write(&keyring_serialise_link_sem);
error:
return ret;
error3:
/* undo the quota changes */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
goto error2;
} /* end __key_link() */
/*****************************************************************************/
/*
* link a key to a keyring
*/
int key_link(struct key *keyring, struct key *key)
{
int ret;
key_check(keyring);
key_check(key);
down_write(&keyring->sem);
ret = __key_link(keyring, key);
up_write(&keyring->sem);
return ret;
} /* end key_link() */
EXPORT_SYMBOL(key_link);
/*****************************************************************************/
/*
* unlink the first link to a key from a keyring
*/
int key_unlink(struct key *keyring, struct key *key)
{
struct keyring_list *klist, *nklist;
int loop, ret;
key_check(keyring);
key_check(key);
ret = -ENOTDIR;
if (keyring->type != &key_type_keyring)
goto error;
down_write(&keyring->sem);
klist = keyring->payload.subscriptions;
if (klist) {
/* search the keyring for the key */
for (loop = 0; loop < klist->nkeys; loop++)
if (klist->keys[loop] == key)
goto key_is_present;
}
up_write(&keyring->sem);
ret = -ENOENT;
goto error;
key_is_present:
/* we need to copy the key list for RCU purposes */
nklist = kmalloc(sizeof(*klist) +
sizeof(struct key *) * klist->maxkeys,
GFP_KERNEL);
if (!nklist)
goto nomem;
nklist->maxkeys = klist->maxkeys;
nklist->nkeys = klist->nkeys - 1;
if (loop > 0)
memcpy(&nklist->keys[0],
&klist->keys[0],
loop * sizeof(struct key *));
if (loop < nklist->nkeys)
memcpy(&nklist->keys[loop],
&klist->keys[loop + 1],
(nklist->nkeys - loop) * sizeof(struct key *));
/* adjust the user's quota */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
up_write(&keyring->sem);
/* schedule for later cleanup */
klist->delkey = loop;
call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
ret = 0;
error:
return ret;
nomem:
ret = -ENOMEM;
up_write(&keyring->sem);
goto error;
} /* end key_unlink() */
EXPORT_SYMBOL(key_unlink);
/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period, releasing the keys it
* links to
*/
static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist;
int loop;
klist = container_of(rcu, struct keyring_list, rcu);
for (loop = klist->nkeys - 1; loop >= 0; loop--)
key_put(klist->keys[loop]);
kfree(klist);
} /* end keyring_clear_rcu_disposal() */
/*****************************************************************************/
/*
* clear the specified process keyring
* - implements keyctl(KEYCTL_CLEAR)
*/
int keyring_clear(struct key *keyring)
{
struct keyring_list *klist;
int ret;
ret = -ENOTDIR;
if (keyring->type == &key_type_keyring) {
/* detach the pointer block with the locks held */
down_write(&keyring->sem);
klist = keyring->payload.subscriptions;
if (klist) {
/* adjust the quota */
key_payload_reserve(keyring,
sizeof(struct keyring_list));
rcu_assign_pointer(keyring->payload.subscriptions,
NULL);
}
up_write(&keyring->sem);
/* free the keys after the locks have been dropped */
if (klist)
call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
ret = 0;
}
return ret;
} /* end keyring_clear() */
EXPORT_SYMBOL(keyring_clear);
/*****************************************************************************/
/*
* dispose of the links from a revoked keyring
* - called with the key sem write-locked
*/
static void keyring_revoke(struct key *keyring)
{
struct keyring_list *klist = keyring->payload.subscriptions;
/* adjust the quota */
key_payload_reserve(keyring, 0);
if (klist) {
rcu_assign_pointer(keyring->payload.subscriptions, NULL);
call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
}
} /* end keyring_revoke() */

107
security/keys/permission.c Normal file
View File

@@ -0,0 +1,107 @@
/* permission.c: key permission determination
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/security.h>
#include "internal.h"
/*****************************************************************************/
/*
* check to see whether permission is granted to use a key in the desired way,
* but permit the security modules to override
*/
int key_task_permission(const key_ref_t key_ref,
struct task_struct *context,
key_perm_t perm)
{
struct key *key;
key_perm_t kperm;
int ret;
key = key_ref_to_ptr(key_ref);
/* use the second 8-bits of permissions for keys the caller owns */
if (key->uid == context->fsuid) {
kperm = key->perm >> 16;
goto use_these_perms;
}
/* use the third 8-bits of permissions for keys the caller has a group
* membership in common with */
if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
if (key->gid == context->fsgid) {
kperm = key->perm >> 8;
goto use_these_perms;
}
task_lock(context);
ret = groups_search(context->group_info, key->gid);
task_unlock(context);
if (ret) {
kperm = key->perm >> 8;
goto use_these_perms;
}
}
/* otherwise use the least-significant 8-bits */
kperm = key->perm;
use_these_perms:
/* use the top 8-bits of permissions for keys the caller possesses
* - possessor permissions are additive with other permissions
*/
if (is_key_possessed(key_ref))
kperm |= key->perm >> 24;
kperm = kperm & perm & KEY_ALL;
if (kperm != perm)
return -EACCES;
/* let LSM be the final arbiter */
return security_key_permission(key_ref, context, perm);
} /* 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);

263
security/keys/proc.c Normal file
View File

@@ -0,0 +1,263 @@
/* proc.c: proc files for key database enumeration
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/errno.h>
#include "internal.h"
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
static int proc_keys_open(struct inode *inode, struct file *file);
static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
static void proc_keys_stop(struct seq_file *p, void *v);
static int proc_keys_show(struct seq_file *m, void *v);
static struct seq_operations proc_keys_ops = {
.start = proc_keys_start,
.next = proc_keys_next,
.stop = proc_keys_stop,
.show = proc_keys_show,
};
static const struct file_operations proc_keys_fops = {
.open = proc_keys_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif
static int proc_key_users_open(struct inode *inode, struct file *file);
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos);
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
static void proc_key_users_stop(struct seq_file *p, void *v);
static int proc_key_users_show(struct seq_file *m, void *v);
static struct seq_operations proc_key_users_ops = {
.start = proc_key_users_start,
.next = proc_key_users_next,
.stop = proc_key_users_stop,
.show = proc_key_users_show,
};
static const struct file_operations proc_key_users_fops = {
.open = proc_key_users_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/*****************************************************************************/
/*
* declare the /proc files
*/
static int __init key_proc_init(void)
{
struct proc_dir_entry *p;
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
p = create_proc_entry("keys", 0, NULL);
if (!p)
panic("Cannot create /proc/keys\n");
p->proc_fops = &proc_keys_fops;
#endif
p = create_proc_entry("key-users", 0, NULL);
if (!p)
panic("Cannot create /proc/key-users\n");
p->proc_fops = &proc_key_users_fops;
return 0;
} /* end key_proc_init() */
__initcall(key_proc_init);
/*****************************************************************************/
/*
* implement "/proc/keys" to provides a list of the keys on the system
*/
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
static int proc_keys_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_keys_ops);
}
static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
{
struct rb_node *_p;
loff_t pos = *_pos;
spin_lock(&key_serial_lock);
_p = rb_first(&key_serial_tree);
while (pos > 0 && _p) {
pos--;
_p = rb_next(_p);
}
return _p;
}
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
return rb_next((struct rb_node *) v);
}
static void proc_keys_stop(struct seq_file *p, void *v)
{
spin_unlock(&key_serial_lock);
}
static int proc_keys_show(struct seq_file *m, void *v)
{
struct rb_node *_p = v;
struct key *key = rb_entry(_p, struct key, serial_node);
struct timespec now;
unsigned long timo;
char xbuf[12];
int rc;
/* check whether the current task is allowed to view the key (assuming
* non-possession) */
rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
if (rc < 0)
return 0;
now = current_kernel_time();
rcu_read_lock();
/* come up with a suitable timeout value */
if (key->expiry == 0) {
memcpy(xbuf, "perm", 5);
}
else if (now.tv_sec >= key->expiry) {
memcpy(xbuf, "expd", 5);
}
else {
timo = key->expiry - now.tv_sec;
if (timo < 60)
sprintf(xbuf, "%lus", timo);
else if (timo < 60*60)
sprintf(xbuf, "%lum", timo / 60);
else if (timo < 60*60*24)
sprintf(xbuf, "%luh", timo / (60*60));
else if (timo < 60*60*24*7)
sprintf(xbuf, "%lud", timo / (60*60*24));
else
sprintf(xbuf, "%luw", timo / (60*60*24*7));
}
#define showflag(KEY, LETTER, FLAG) \
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
key->serial,
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
showflag(key, 'R', KEY_FLAG_REVOKED),
showflag(key, 'D', KEY_FLAG_DEAD),
showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
showflag(key, 'N', KEY_FLAG_NEGATIVE),
atomic_read(&key->usage),
xbuf,
key->perm,
key->uid,
key->gid,
key->type->name);
#undef showflag
if (key->type->describe)
key->type->describe(key, m);
seq_putc(m, '\n');
rcu_read_unlock();
return 0;
}
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
/*****************************************************************************/
/*
* implement "/proc/key-users" to provides a list of the key users
*/
static int proc_key_users_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_key_users_ops);
}
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
{
struct rb_node *_p;
loff_t pos = *_pos;
spin_lock(&key_user_lock);
_p = rb_first(&key_user_tree);
while (pos > 0 && _p) {
pos--;
_p = rb_next(_p);
}
return _p;
}
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
return rb_next((struct rb_node *) v);
}
static void proc_key_users_stop(struct seq_file *p, void *v)
{
spin_unlock(&key_user_lock);
}
static int proc_key_users_show(struct seq_file *m, void *v)
{
struct rb_node *_p = v;
struct key_user *user = rb_entry(_p, struct key_user, node);
seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
user->uid,
atomic_read(&user->usage),
atomic_read(&user->nkeys),
atomic_read(&user->nikeys),
user->qnkeys,
KEYQUOTA_MAX_KEYS,
user->qnbytes,
KEYQUOTA_MAX_BYTES
);
return 0;
}

View File

@@ -0,0 +1,765 @@
/* process_keys.c: management of a process's keyrings
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#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 DEFINE_MUTEX(key_session_mutex);
/* the root user's tracking struct */
struct key_user root_key_user = {
.usage = ATOMIC_INIT(3),
.consq = LIST_HEAD_INIT(root_key_user.consq),
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
.nkeys = ATOMIC_INIT(2),
.nikeys = ATOMIC_INIT(2),
.uid = 0,
};
/* the root user's UID keyring */
struct key root_user_keyring = {
.usage = ATOMIC_INIT(1),
.serial = 2,
.type = &key_type_keyring,
.user = &root_key_user,
.sem = __RWSEM_INITIALIZER(root_user_keyring.sem),
.perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
.flags = 1 << KEY_FLAG_INSTANTIATED,
.description = "_uid.0",
#ifdef KEY_DEBUGGING
.magic = KEY_DEBUG_MAGIC,
#endif
};
/* the root user's default session keyring */
struct key root_session_keyring = {
.usage = ATOMIC_INIT(1),
.serial = 1,
.type = &key_type_keyring,
.user = &root_key_user,
.sem = __RWSEM_INITIALIZER(root_session_keyring.sem),
.perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
.flags = 1 << KEY_FLAG_INSTANTIATED,
.description = "_uid_ses.0",
#ifdef KEY_DEBUGGING
.magic = KEY_DEBUG_MAGIC,
#endif
};
/*****************************************************************************/
/*
* allocate the keyrings to be associated with a UID
*/
int alloc_uid_keyring(struct user_struct *user,
struct task_struct *ctx)
{
struct key *uid_keyring, *session_keyring;
char buf[20];
int ret;
/* concoct a default session keyring */
sprintf(buf, "_uid_ses.%u", user->uid);
session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error;
}
/* and a UID specific keyring, pointed to by the default session
* keyring */
sprintf(buf, "_uid.%u", user->uid);
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
KEY_ALLOC_IN_QUOTA, session_keyring);
if (IS_ERR(uid_keyring)) {
key_put(session_keyring);
ret = PTR_ERR(uid_keyring);
goto error;
}
/* install the keyrings */
user->uid_keyring = uid_keyring;
user->session_keyring = session_keyring;
ret = 0;
error:
return ret;
} /* end alloc_uid_keyring() */
/*****************************************************************************/
/*
* deal with the UID changing
*/
void switch_uid_keyring(struct user_struct *new_user)
{
#if 0 /* do nothing for now */
struct key *old;
/* switch to the new user's session keyring if we were running under
* root's default session keyring */
if (new_user->uid != 0 &&
current->session_keyring == &root_session_keyring
) {
atomic_inc(&new_user->session_keyring->usage);
task_lock(current);
old = current->session_keyring;
current->session_keyring = new_user->session_keyring;
task_unlock(current);
key_put(old);
}
#endif
} /* end switch_uid_keyring() */
/*****************************************************************************/
/*
* install a fresh thread keyring, discarding the old one
*/
int install_thread_keyring(struct task_struct *tsk)
{
struct key *keyring, *old;
char buf[20];
int ret;
sprintf(buf, "_tid.%u", tsk->pid);
keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
}
task_lock(tsk);
old = tsk->thread_keyring;
tsk->thread_keyring = keyring;
task_unlock(tsk);
ret = 0;
key_put(old);
error:
return ret;
} /* end install_thread_keyring() */
/*****************************************************************************/
/*
* make sure a process keyring is installed
*/
int install_process_keyring(struct task_struct *tsk)
{
struct key *keyring;
char buf[20];
int ret;
might_sleep();
if (!tsk->signal->process_keyring) {
sprintf(buf, "_pid.%u", tsk->tgid);
keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
}
/* attach keyring */
spin_lock_irq(&tsk->sighand->siglock);
if (!tsk->signal->process_keyring) {
tsk->signal->process_keyring = keyring;
keyring = NULL;
}
spin_unlock_irq(&tsk->sighand->siglock);
key_put(keyring);
}
ret = 0;
error:
return ret;
} /* end install_process_keyring() */
/*****************************************************************************/
/*
* install a session keyring, discarding the old one
* - if a keyring is not supplied, an empty one is invented
*/
static int install_session_keyring(struct task_struct *tsk,
struct key *keyring)
{
unsigned long flags;
struct key *old;
char buf[20];
might_sleep();
/* create an empty session keyring */
if (!keyring) {
sprintf(buf, "_ses.%u", tsk->tgid);
flags = KEY_ALLOC_QUOTA_OVERRUN;
if (tsk->signal->session_keyring)
flags = KEY_ALLOC_IN_QUOTA;
keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
flags, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
}
else {
atomic_inc(&keyring->usage);
}
/* install the keyring */
spin_lock_irq(&tsk->sighand->siglock);
old = tsk->signal->session_keyring;
rcu_assign_pointer(tsk->signal->session_keyring, keyring);
spin_unlock_irq(&tsk->sighand->siglock);
/* we're using RCU on the pointer, but there's no point synchronising
* on it if it didn't previously point to anything */
if (old) {
synchronize_rcu();
key_put(old);
}
return 0;
} /* end install_session_keyring() */
/*****************************************************************************/
/*
* copy the keys in a thread group for fork without CLONE_THREAD
*/
int copy_thread_group_keys(struct task_struct *tsk)
{
key_check(current->thread_group->session_keyring);
key_check(current->thread_group->process_keyring);
/* no process keyring yet */
tsk->signal->process_keyring = NULL;
/* same session keyring */
rcu_read_lock();
tsk->signal->session_keyring =
key_get(rcu_dereference(current->signal->session_keyring));
rcu_read_unlock();
return 0;
} /* end copy_thread_group_keys() */
/*****************************************************************************/
/*
* copy the keys for fork
*/
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() */
/*****************************************************************************/
/*
* dispose of thread group keys upon thread group destruction
*/
void exit_thread_group_keys(struct signal_struct *tg)
{
key_put(tg->session_keyring);
key_put(tg->process_keyring);
} /* end exit_thread_group_keys() */
/*****************************************************************************/
/*
* 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() */
/*****************************************************************************/
/*
* deal with execve()
*/
int exec_keys(struct task_struct *tsk)
{
struct key *old;
/* newly exec'd tasks don't get a thread keyring */
task_lock(tsk);
old = tsk->thread_keyring;
tsk->thread_keyring = NULL;
task_unlock(tsk);
key_put(old);
/* discard the process keyring from a newly exec'd task */
spin_lock_irq(&tsk->sighand->siglock);
old = tsk->signal->process_keyring;
tsk->signal->process_keyring = NULL;
spin_unlock_irq(&tsk->sighand->siglock);
key_put(old);
return 0;
} /* end exec_keys() */
/*****************************************************************************/
/*
* deal with SUID programs
* - we might want to make this invent a new session keyring
*/
int suid_keys(struct task_struct *tsk)
{
return 0;
} /* end suid_keys() */
/*****************************************************************************/
/*
* the filesystem user ID changed
*/
void key_fsuid_changed(struct task_struct *tsk)
{
/* update the ownership of the thread keyring */
if (tsk->thread_keyring) {
down_write(&tsk->thread_keyring->sem);
tsk->thread_keyring->uid = tsk->fsuid;
up_write(&tsk->thread_keyring->sem);
}
} /* end key_fsuid_changed() */
/*****************************************************************************/
/*
* the filesystem group ID changed
*/
void key_fsgid_changed(struct task_struct *tsk)
{
/* update the ownership of the thread keyring */
if (tsk->thread_keyring) {
down_write(&tsk->thread_keyring->sem);
tsk->thread_keyring->gid = tsk->fsgid;
up_write(&tsk->thread_keyring->sem);
}
} /* end key_fsgid_changed() */
/*****************************************************************************/
/*
* search the process keyrings for the first matching key
* - we use the supplied match function to see if the description (or other
* feature of interest) matches
* - we return -EAGAIN if we didn't find any matching key
* - we return -ENOKEY if we found only negative matching keys
*/
key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
struct task_struct *context)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret, err;
might_sleep();
/* 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;
* otherwise we want to return a sample error (probably -EACCES) if
* none of the keyrings were searchable
*
* in terms of priority: success > -ENOKEY > -EAGAIN > other error
*/
key_ref = NULL;
ret = NULL;
err = ERR_PTR(-EAGAIN);
/* search the thread keyring first */
if (context->thread_keyring) {
key_ref = keyring_search_aux(
make_key_ref(context->thread_keyring, 1),
context, type, description, match);
if (!IS_ERR(key_ref))
goto found;
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
if (ret)
break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
default:
err = key_ref;
break;
}
}
/* search the process keyring second */
if (context->signal->process_keyring) {
key_ref = keyring_search_aux(
make_key_ref(context->signal->process_keyring, 1),
context, type, description, match);
if (!IS_ERR(key_ref))
goto found;
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
if (ret)
break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
default:
err = key_ref;
break;
}
}
/* search the session keyring */
if (context->signal->session_keyring) {
rcu_read_lock();
key_ref = keyring_search_aux(
make_key_ref(rcu_dereference(
context->signal->session_keyring),
1),
context, type, description, match);
rcu_read_unlock();
if (!IS_ERR(key_ref))
goto found;
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
if (ret)
break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
default:
err = key_ref;
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 (!IS_ERR(key_ref))
goto found;
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
if (ret)
break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
default:
err = key_ref;
break;
}
}
/* 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
) {
/* defend against the auth key being revoked */
down_read(&context->request_key_auth->sem);
if (key_validate(context->request_key_auth) == 0) {
rka = context->request_key_auth->payload.data;
key_ref = search_process_keyrings(type, description,
match, rka->context);
up_read(&context->request_key_auth->sem);
if (!IS_ERR(key_ref))
goto found;
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
if (ret)
break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
default:
err = key_ref;
break;
}
} else {
up_read(&context->request_key_auth->sem);
}
}
/* no key - decide on the error we're going to go for */
key_ref = ret ? ret : err;
found:
return key_ref;
} /* end search_process_keyrings() */
/*****************************************************************************/
/*
* see if the key we're looking at is the target key
*/
static int lookup_user_key_possessed(const struct key *key, const void *target)
{
return key == target;
} /* end lookup_user_key_possessed() */
/*****************************************************************************/
/*
* lookup a key given a key ID from userspace with a given permissions mask
* - don't create special keyrings unless so requested
* - partially constructed keys aren't found unless requested
*/
key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
int create, int partial, key_perm_t perm)
{
key_ref_t key_ref, skey_ref;
struct key *key;
int ret;
if (!context)
context = current;
key_ref = ERR_PTR(-ENOKEY);
switch (id) {
case KEY_SPEC_THREAD_KEYRING:
if (!context->thread_keyring) {
if (!create)
goto error;
ret = install_thread_keyring(context);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}
key = context->thread_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_PROCESS_KEYRING:
if (!context->signal->process_keyring) {
if (!create)
goto error;
ret = install_process_keyring(context);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}
key = context->signal->process_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_SESSION_KEYRING:
if (!context->signal->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_session_keyring(
context, context->user->session_keyring);
if (ret < 0)
goto error;
}
rcu_read_lock();
key = rcu_dereference(context->signal->session_keyring);
atomic_inc(&key->usage);
rcu_read_unlock();
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_KEYRING:
key = context->user->uid_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_SESSION_KEYRING:
key = context->user->session_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_GROUP_KEYRING:
/* group keyrings are not yet supported */
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)
goto error;
key = key_lookup(id);
if (IS_ERR(key)) {
key_ref = ERR_PTR(PTR_ERR(key));
goto error;
}
key_ref = make_key_ref(key, 0);
/* check to see if we possess the key */
skey_ref = search_process_keyrings(key->type, key,
lookup_user_key_possessed,
current);
if (!IS_ERR(skey_ref)) {
key_put(key);
key_ref = skey_ref;
}
break;
}
/* check the status */
if (perm) {
ret = key_validate(key);
if (ret < 0)
goto invalid_key;
}
ret = -EIO;
if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
goto invalid_key;
/* check the permissions */
ret = key_task_permission(key_ref, context, perm);
if (ret < 0)
goto invalid_key;
error:
return key_ref;
invalid_key:
key_ref_put(key_ref);
key_ref = ERR_PTR(ret);
goto error;
} /* end lookup_user_key() */
/*****************************************************************************/
/*
* join the named keyring as the session keyring if possible, or attempt to
* create a new one of that name if not
* - if the name is NULL, an empty anonymous keyring is installed instead
* - named session keyring joining is done with a semaphore held
*/
long join_session_keyring(const char *name)
{
struct task_struct *tsk = current;
struct key *keyring;
long ret;
/* if no name is provided, install an anonymous keyring */
if (!name) {
ret = install_session_keyring(tsk, NULL);
if (ret < 0)
goto error;
rcu_read_lock();
ret = rcu_dereference(tsk->signal->session_keyring)->serial;
rcu_read_unlock();
goto error;
}
/* allow the user to join or create a named keyring */
mutex_lock(&key_session_mutex);
/* look for an existing keyring of this name */
keyring = find_keyring_by_name(name, 0);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
}
else if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
/* we've got a keyring - now to install it */
ret = install_session_keyring(tsk, keyring);
if (ret < 0)
goto error2;
ret = keyring->serial;
key_put(keyring);
error2:
mutex_unlock(&key_session_mutex);
error:
return ret;
} /* end join_session_keyring() */

522
security/keys/request_key.c Normal file
View File

@@ -0,0 +1,522 @@
/* request_key.c: request a key from userspace
*
* 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
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* See Documentation/keys-request-key.txt
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kmod.h>
#include <linux/err.h>
#include <linux/keyctl.h>
#include "internal.h"
struct key_construction {
struct list_head link; /* link in construction queue */
struct key *key; /* key being constructed */
};
/* when waiting for someone else's keys, you get added to this */
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>"
*/
static int call_sbin_request_key(struct key *key,
struct key *authkey,
const char *op,
void *aux)
{
struct task_struct *tsk = current;
key_serial_t prkey, sskey;
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},{%d},%s", key->serial, authkey->serial, op);
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current,
KEY_ALLOC_QUOTA_OVERRUN, 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);
/* we say which key is under construction */
sprintf(key_str, "%d", key->serial);
/* we specify the process's default keyrings */
sprintf(keyring_str[0], "%d",
tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
prkey = 0;
if (tsk->signal->process_keyring)
prkey = tsk->signal->process_keyring->serial;
sprintf(keyring_str[1], "%d", prkey);
if (tsk->signal->session_keyring) {
rcu_read_lock();
sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
rcu_read_unlock();
}
else {
sskey = tsk->user->session_keyring->serial;
}
sprintf(keyring_str[2], "%d", sskey);
/* set up a minimal environment */
i = 0;
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
envp[i] = NULL;
/* set up the argument list */
i = 0;
argv[i++] = "/sbin/request-key";
argv[i++] = (char *) op;
argv[i++] = key_str;
argv[i++] = uid_str;
argv[i++] = gid_str;
argv[i++] = keyring_str[0];
argv[i++] = keyring_str[1];
argv[i++] = keyring_str[2];
argv[i] = NULL;
/* do it */
ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, 1);
error_link:
key_put(keyring);
error_alloc:
kleave(" = %d", ret);
return ret;
} /* end call_sbin_request_key() */
/*****************************************************************************/
/*
* call out to userspace for the key
* - called with the construction sem held, but the sem is dropped here
* - we ignore program failure and go on key status instead
*/
static struct key *__request_key_construction(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
unsigned long flags)
{
request_key_actor_t actor;
struct key_construction cons;
struct timespec now;
struct key *key, *authkey;
int ret, negated;
kenter("%s,%s,%s,%lx", type->name, description, callout_info, flags);
/* create a key and add it to the queue */
key = key_alloc(type, description,
current->fsuid, current->fsgid, current, KEY_POS_ALL,
flags);
if (IS_ERR(key))
goto alloc_failed;
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
cons.key = key;
list_add_tail(&cons.link, &key->user->consq);
/* 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 */
actor = call_sbin_request_key;
if (type->request_key)
actor = type->request_key;
ret = actor(key, authkey, "create", aux);
if (ret < 0)
goto request_failed;
/* if the key wasn't instantiated, then we want to give an error */
ret = -ENOKEY;
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:
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
key_put(key);
key = ERR_PTR(-ENOKEY);
}
out:
kleave(" = %p", key);
return key;
request_failed:
key_revoke(authkey);
key_put(authkey);
alloc_authkey_failed:
/* it wasn't instantiated
* - remove from construction queue
* - mark the key as dead
*/
negated = 0;
down_write(&key_construction_sem);
list_del(&cons.link);
/* check it didn't get instantiated between the check and the down */
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
negated = 1;
}
clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
up_write(&key_construction_sem);
if (!negated)
goto check_not_negative; /* surprisingly, the key got
* instantiated */
/* set the timeout and store in the session keyring if we can */
now = current_kernel_time();
key->expiry = now.tv_sec + key_negative_timeout;
if (current->signal->session_keyring) {
struct key *keyring;
rcu_read_lock();
keyring = rcu_dereference(current->signal->session_keyring);
atomic_inc(&keyring->usage);
rcu_read_unlock();
key_link(keyring, key);
key_put(keyring);
}
key_put(key);
/* notify anyone who was waiting */
wake_up_all(&request_key_conswq);
key = ERR_PTR(ret);
goto out;
alloc_failed:
up_write(&key_construction_sem);
goto out;
} /* end __request_key_construction() */
/*****************************************************************************/
/*
* call out to userspace to request the key
* - we check the construction queue first to see if an appropriate key is
* already being constructed by userspace
*/
static struct key *request_key_construction(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
struct key_user *user,
unsigned long flags)
{
struct key_construction *pcons;
struct key *key, *ckey;
DECLARE_WAITQUEUE(myself, current);
kenter("%s,%s,{%d},%s,%lx",
type->name, description, user->uid, callout_info, flags);
/* see if there's such a key under construction already */
down_write(&key_construction_sem);
list_for_each_entry(pcons, &user->consq, link) {
ckey = pcons->key;
if (ckey->type != type)
continue;
if (type->match(ckey, description))
goto found_key_under_construction;
}
/* see about getting userspace to construct the key */
key = __request_key_construction(type, description, callout_info, aux,
flags);
error:
kleave(" = %p", key);
return key;
/* someone else has the same key under construction
* - we want to keep an eye on their key
*/
found_key_under_construction:
atomic_inc(&ckey->usage);
up_write(&key_construction_sem);
/* wait for the key to be completed one way or another */
add_wait_queue(&request_key_conswq, &myself);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags))
break;
if (signal_pending(current))
break;
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&request_key_conswq, &myself);
/* we'll need to search this process's keyrings to see if the key is
* now there since we can't automatically assume it's also available
* there */
key_put(ckey);
ckey = NULL;
key = NULL; /* request a retry */
goto error;
} /* end request_key_construction() */
/*****************************************************************************/
/*
* link a freshly minted key to an appropriate destination keyring
*/
static void request_key_link(struct key *key, struct key *dest_keyring)
{
struct task_struct *tsk = current;
struct key *drop = NULL;
kenter("{%d},%p", key->serial, dest_keyring);
/* find the appropriate keyring */
if (!dest_keyring) {
switch (tsk->jit_keyring) {
case KEY_REQKEY_DEFL_DEFAULT:
case KEY_REQKEY_DEFL_THREAD_KEYRING:
dest_keyring = tsk->thread_keyring;
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
dest_keyring = tsk->signal->process_keyring;
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_SESSION_KEYRING:
rcu_read_lock();
dest_keyring = key_get(
rcu_dereference(tsk->signal->session_keyring));
rcu_read_unlock();
drop = dest_keyring;
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
dest_keyring = current->user->session_keyring;
break;
case KEY_REQKEY_DEFL_USER_KEYRING:
dest_keyring = current->user->uid_keyring;
break;
case KEY_REQKEY_DEFL_GROUP_KEYRING:
default:
BUG();
}
}
/* and attach the key to it */
key_link(dest_keyring, key);
key_put(drop);
kleave("");
} /* end request_key_link() */
/*****************************************************************************/
/*
* request a key
* - search the process's keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
* - cache the key in an appropriate keyring
*/
struct key *request_key_and_link(struct key_type *type,
const char *description,
const char *callout_info,
void *aux,
struct key *dest_keyring,
unsigned long flags)
{
struct key_user *user;
struct key *key;
key_ref_t key_ref;
kenter("%s,%s,%s,%p,%p,%lx",
type->name, description, callout_info, aux,
dest_keyring, flags);
/* search all the process keyrings for a key */
key_ref = search_process_keyrings(type, description, type->match,
current);
kdebug("search 1: %p", key_ref);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
}
else if (PTR_ERR(key_ref) != -EAGAIN) {
key = ERR_PTR(PTR_ERR(key_ref));
}
else {
/* the search failed, but the keyrings were searchable, so we
* should consult userspace if we can */
key = ERR_PTR(-ENOKEY);
if (!callout_info)
goto error;
/* - get hold of the user's construction queue */
user = key_user_lookup(current->fsuid);
if (!user)
goto nomem;
for (;;) {
if (signal_pending(current))
goto interrupted;
/* ask userspace (returns NULL if it waited on a key
* being constructed) */
key = request_key_construction(type, description,
callout_info, aux,
user, flags);
if (key)
break;
/* someone else made the key we want, so we need to
* search again as it might now be available to us */
key_ref = search_process_keyrings(type, description,
type->match,
current);
kdebug("search 2: %p", key_ref);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
break;
}
if (PTR_ERR(key_ref) != -EAGAIN) {
key = ERR_PTR(PTR_ERR(key_ref));
break;
}
}
key_user_put(user);
/* link the new key into the appropriate keyring */
if (!IS_ERR(key))
request_key_link(key, dest_keyring);
}
error:
kleave(" = %p", key);
return key;
nomem:
key = ERR_PTR(-ENOMEM);
goto error;
interrupted:
key_user_put(user);
key = ERR_PTR(-EINTR);
goto error;
} /* end request_key_and_link() */
/*****************************************************************************/
/*
* request a key
* - search the process's keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
*/
struct key *request_key(struct key_type *type,
const char *description,
const char *callout_info)
{
return request_key_and_link(type, description, callout_info, NULL,
NULL, KEY_ALLOC_IN_QUOTA);
} /* end request_key() */
EXPORT_SYMBOL(request_key);
/*****************************************************************************/
/*
* request a key with auxiliary data for the upcaller
* - search the process's keyrings
* - check the list of keys being created or updated
* - call out to userspace for a key if supplementary info was provided
*/
struct key *request_key_with_auxdata(struct key_type *type,
const char *description,
const char *callout_info,
void *aux)
{
return request_key_and_link(type, description, callout_info, aux,
NULL, KEY_ALLOC_IN_QUOTA);
} /* end request_key_with_auxdata() */
EXPORT_SYMBOL(request_key_with_auxdata);

View File

@@ -0,0 +1,268 @@
/* request_key_auth.c: request key authorisation controlling key def
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* See Documentation/keys-request-key.txt
*/
#include <linux/module.h>
#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_revoke(struct key *);
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
*/
struct key_type key_type_request_key_auth = {
.name = ".request_key_auth",
.def_datalen = sizeof(struct request_key_auth),
.instantiate = request_key_auth_instantiate,
.describe = request_key_auth_describe,
.revoke = request_key_auth_revoke,
.destroy = request_key_auth_destroy,
.read = request_key_auth_read,
};
/*****************************************************************************/
/*
* instantiate a request-key authorisation key
*/
static int request_key_auth_instantiate(struct key *key,
const void *data,
size_t datalen)
{
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)
{
struct request_key_auth *rka = key->payload.data;
seq_puts(m, "key:");
seq_puts(m, key->description);
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() */
/*****************************************************************************/
/*
* handle revocation of an authorisation token key
* - called with the key sem write-locked
*/
static void request_key_auth_revoke(struct key *key)
{
struct request_key_auth *rka = key->payload.data;
kenter("{%d}", key->serial);
if (rka->context) {
put_task_struct(rka->context);
rka->context = NULL;
}
} /* end request_key_auth_revoke() */
/*****************************************************************************/
/*
* destroy an instantiation authorisation token key
*/
static void request_key_auth_destroy(struct key *key)
{
struct request_key_auth *rka = key->payload.data;
kenter("{%d}", key->serial);
if (rka->context) {
put_task_struct(rka->context);
rka->context = NULL;
}
key_put(rka->target_key);
kfree(rka);
} /* end request_key_auth_destroy() */
/*****************************************************************************/
/*
* 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, const char *callout_info)
{
struct request_key_auth *rka, *irka;
struct key *authkey = NULL;
char desc[20];
int ret;
kenter("%d,", target->serial);
/* allocate a auth record */
rka = kmalloc(sizeof(*rka), GFP_KERNEL);
if (!rka) {
kleave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
/* 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 */
down_read(&current->request_key_auth->sem);
/* if the auth key has been revoked, then the key we're
* servicing is already instantiated */
if (test_bit(KEY_FLAG_REVOKED,
&current->request_key_auth->flags))
goto auth_key_revoked;
irka = current->request_key_auth->payload.data;
rka->context = irka->context;
rka->pid = irka->pid;
get_task_struct(rka->context);
up_read(&current->request_key_auth->sem);
}
else {
/* it isn't - use this process as the context */
rka->context = current;
rka->pid = current->pid;
get_task_struct(rka->context);
}
rka->target_key = key_get(target);
rka->callout_info = callout_info;
/* allocate the auth key */
sprintf(desc, "%x", target->serial);
authkey = key_alloc(&key_type_request_key_auth, desc,
current->fsuid, current->fsgid, current,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
goto error_alloc;
}
/* construct and attach to the keyring */
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
if (ret < 0)
goto error_inst;
kleave(" = {%d}", authkey->serial);
return authkey;
auth_key_revoked:
up_read(&current->request_key_auth->sem);
kfree(rka);
kleave("= -EKEYREVOKED");
return ERR_PTR(-EKEYREVOKED);
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
* session keyring
* - a target_id of zero specifies any valid token
*/
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
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;
}
authkey = key_ref_to_ptr(authkey_ref);
if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
key_put(authkey);
authkey = ERR_PTR(-EKEYREVOKED);
}
error:
return authkey;
} /* end key_get_instantiation_authkey() */

View File

@@ -0,0 +1,218 @@
/* user_defined.c: user defined key type
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/user-type.h>
#include <asm/uaccess.h>
#include "internal.h"
/*
* user defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
*/
struct key_type key_type_user = {
.name = "user",
.instantiate = user_instantiate,
.update = user_update,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
.read = user_read,
};
EXPORT_SYMBOL_GPL(key_type_user);
/*****************************************************************************/
/*
* instantiate a user defined key
*/
int user_instantiate(struct key *key, const void *data, size_t datalen)
{
struct user_key_payload *upayload;
int ret;
ret = -EINVAL;
if (datalen <= 0 || datalen > 32767 || !data)
goto error;
ret = key_payload_reserve(key, datalen);
if (ret < 0)
goto error;
ret = -ENOMEM;
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
if (!upayload)
goto error;
/* attach the data */
upayload->datalen = datalen;
memcpy(upayload->data, data, datalen);
rcu_assign_pointer(key->payload.data, upayload);
ret = 0;
error:
return ret;
} /* end user_instantiate() */
EXPORT_SYMBOL_GPL(user_instantiate);
/*****************************************************************************/
/*
* dispose of the old data from an updated user defined key
*/
static void user_update_rcu_disposal(struct rcu_head *rcu)
{
struct user_key_payload *upayload;
upayload = container_of(rcu, struct user_key_payload, rcu);
kfree(upayload);
} /* end user_update_rcu_disposal() */
/*****************************************************************************/
/*
* update a user defined key
* - the key's semaphore is write-locked
*/
int user_update(struct key *key, const void *data, size_t datalen)
{
struct user_key_payload *upayload, *zap;
int ret;
ret = -EINVAL;
if (datalen <= 0 || datalen > 32767 || !data)
goto error;
/* construct a replacement payload */
ret = -ENOMEM;
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
if (!upayload)
goto error;
upayload->datalen = datalen;
memcpy(upayload->data, data, datalen);
/* check the quota and attach the new data */
zap = upayload;
ret = key_payload_reserve(key, datalen);
if (ret == 0) {
/* attach the new data, displacing the old */
zap = key->payload.data;
rcu_assign_pointer(key->payload.data, upayload);
key->expiry = 0;
}
call_rcu(&zap->rcu, user_update_rcu_disposal);
error:
return ret;
} /* end user_update() */
EXPORT_SYMBOL_GPL(user_update);
/*****************************************************************************/
/*
* match users on their name
*/
int user_match(const struct key *key, const void *description)
{
return strcmp(key->description, description) == 0;
} /* end user_match() */
EXPORT_SYMBOL_GPL(user_match);
/*****************************************************************************/
/*
* dispose of the links from a revoked keyring
* - called with the key sem write-locked
*/
void user_revoke(struct key *key)
{
struct user_key_payload *upayload = key->payload.data;
/* clear the quota */
key_payload_reserve(key, 0);
if (upayload) {
rcu_assign_pointer(key->payload.data, NULL);
call_rcu(&upayload->rcu, user_update_rcu_disposal);
}
} /* end user_revoke() */
EXPORT_SYMBOL(user_revoke);
/*****************************************************************************/
/*
* dispose of the data dangling from the corpse of a user key
*/
void user_destroy(struct key *key)
{
struct user_key_payload *upayload = key->payload.data;
kfree(upayload);
} /* end user_destroy() */
EXPORT_SYMBOL_GPL(user_destroy);
/*****************************************************************************/
/*
* describe the user key
*/
void user_describe(const struct key *key, struct seq_file *m)
{
seq_puts(m, key->description);
seq_printf(m, ": %u", key->datalen);
} /* end user_describe() */
EXPORT_SYMBOL_GPL(user_describe);
/*****************************************************************************/
/*
* read the key data
* - the key's semaphore is read-locked
*/
long user_read(const struct key *key, char __user *buffer, size_t buflen)
{
struct user_key_payload *upayload;
long ret;
upayload = rcu_dereference(key->payload.data);
ret = upayload->datalen;
/* we can return the data as is */
if (buffer && buflen > 0) {
if (buflen > upayload->datalen)
buflen = upayload->datalen;
if (copy_to_user(buffer, upayload->data, buflen) != 0)
ret = -EFAULT;
}
return ret;
} /* end user_read() */
EXPORT_SYMBOL_GPL(user_read);

141
security/root_plug.c Normal file
View File

@@ -0,0 +1,141 @@
/*
* Root Plug sample LSM module
*
* Originally written for a Linux Journal.
*
* Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.com>
*
* Prevents any programs running with egid == 0 if a specific USB device
* is not present in the system. Yes, it can be gotten around, but is a
* nice starting point for people to play with, and learn the LSM
* interface.
*
* If you want to turn this into something with a semblance of security,
* you need to hook the task_* functions also.
*
* See http://www.linuxjournal.com/article.php?sid=6279 for more information
* about this code.
*
* 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 the Free Software Foundation, version 2 of the
* License.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/security.h>
#include <linux/usb.h>
/* flag to keep track of how we were registered */
static int secondary;
/* default is a generic type of usb to serial converter */
static int vendor_id = 0x0557;
static int product_id = 0x2008;
module_param(vendor_id, uint, 0400);
MODULE_PARM_DESC(vendor_id, "USB Vendor ID of device to look for");
module_param(product_id, uint, 0400);
MODULE_PARM_DESC(product_id, "USB Product ID of device to look for");
/* should we print out debug messages */
static int debug = 0;
module_param(debug, bool, 0600);
MODULE_PARM_DESC(debug, "Debug enabled or not");
#if defined(CONFIG_SECURITY_ROOTPLUG_MODULE)
#define MY_NAME THIS_MODULE->name
#else
#define MY_NAME "root_plug"
#endif
#define root_dbg(fmt, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: %s: " fmt , \
MY_NAME , __FUNCTION__ , \
## arg); \
} while (0)
static int rootplug_bprm_check_security (struct linux_binprm *bprm)
{
struct usb_device *dev;
root_dbg("file %s, e_uid = %d, e_gid = %d\n",
bprm->filename, bprm->e_uid, bprm->e_gid);
if (bprm->e_gid == 0) {
dev = usb_find_device(vendor_id, product_id);
if (!dev) {
root_dbg("e_gid = 0, and device not found, "
"task not allowed to run...\n");
return -EPERM;
}
usb_put_dev(dev);
}
return 0;
}
static struct security_operations rootplug_security_ops = {
/* Use the capability functions for some of the hooks */
.ptrace = cap_ptrace,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,
.capable = cap_capable,
.bprm_apply_creds = cap_bprm_apply_creds,
.bprm_set_security = cap_bprm_set_security,
.task_post_setuid = cap_task_post_setuid,
.task_reparent_to_init = cap_task_reparent_to_init,
.bprm_check_security = rootplug_bprm_check_security,
};
static int __init rootplug_init (void)
{
/* register ourselves with the security framework */
if (register_security (&rootplug_security_ops)) {
printk (KERN_INFO
"Failure registering Root Plug module with the kernel\n");
/* try registering with primary module */
if (mod_reg_security (MY_NAME, &rootplug_security_ops)) {
printk (KERN_INFO "Failure registering Root Plug "
" module with primary security module.\n");
return -EINVAL;
}
secondary = 1;
}
printk (KERN_INFO "Root Plug module initialized, "
"vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id);
return 0;
}
static void __exit rootplug_exit (void)
{
/* remove ourselves from the security framework */
if (secondary) {
if (mod_unreg_security (MY_NAME, &rootplug_security_ops))
printk (KERN_INFO "Failure unregistering Root Plug "
" module with primary module.\n");
} else {
if (unregister_security (&rootplug_security_ops)) {
printk (KERN_INFO "Failure unregistering Root Plug "
"module with the kernel\n");
}
}
printk (KERN_INFO "Root Plug module removed\n");
}
security_initcall (rootplug_init);
module_exit (rootplug_exit);
MODULE_DESCRIPTION("Root Plug sample LSM module, written for Linux Journal article");
MODULE_LICENSE("GPL");

179
security/security.c Normal file
View File

@@ -0,0 +1,179 @@
/*
* Security plug functions
*
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/security.h>
#define SECURITY_FRAMEWORK_VERSION "1.0.0"
/* things that live in dummy.c */
extern struct security_operations dummy_security_ops;
extern void security_fixup_ops(struct security_operations *ops);
struct security_operations *security_ops; /* Initialized to NULL */
static inline int verify(struct security_operations *ops)
{
/* verify the security_operations structure exists */
if (!ops)
return -EINVAL;
security_fixup_ops(ops);
return 0;
}
static void __init do_security_initcalls(void)
{
initcall_t *call;
call = __security_initcall_start;
while (call < __security_initcall_end) {
(*call) ();
call++;
}
}
/**
* security_init - initializes the security framework
*
* This should be called early in the kernel initialization sequence.
*/
int __init security_init(void)
{
printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION
" initialized\n");
if (verify(&dummy_security_ops)) {
printk(KERN_ERR "%s could not verify "
"dummy_security_ops structure.\n", __FUNCTION__);
return -EIO;
}
security_ops = &dummy_security_ops;
do_security_initcalls();
return 0;
}
/**
* register_security - registers a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
* This function is to allow a security module to register itself with the
* kernel security subsystem. Some rudimentary checking is done on the @ops
* value passed to this function. A call to unregister_security() should be
* done to remove this security_options structure from the kernel.
*
* If there is already a security module registered with the kernel,
* an error will be returned. Otherwise 0 is returned on success.
*/
int register_security(struct security_operations *ops)
{
if (verify(ops)) {
printk(KERN_DEBUG "%s could not verify "
"security_operations structure.\n", __FUNCTION__);
return -EINVAL;
}
if (security_ops != &dummy_security_ops)
return -EAGAIN;
security_ops = ops;
return 0;
}
/**
* unregister_security - unregisters a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
* This function removes a struct security_operations variable that had
* previously been registered with a successful call to register_security().
*
* If @ops does not match the valued previously passed to register_security()
* an error is returned. Otherwise the default security options is set to the
* the dummy_security_ops structure, and 0 is returned.
*/
int unregister_security(struct security_operations *ops)
{
if (ops != security_ops) {
printk(KERN_INFO "%s: trying to unregister "
"a security_opts structure that is not "
"registered, failing.\n", __FUNCTION__);
return -EINVAL;
}
security_ops = &dummy_security_ops;
return 0;
}
/**
* mod_reg_security - allows security modules to be "stacked"
* @name: a pointer to a string with the name of the security_options to be registered
* @ops: a pointer to the struct security_options that is to be registered
*
* This function allows security modules to be stacked if the currently loaded
* security module allows this to happen. It passes the @name and @ops to the
* register_security function of the currently loaded security module.
*
* The return value depends on the currently loaded security module, with 0 as
* success.
*/
int mod_reg_security(const char *name, struct security_operations *ops)
{
if (verify(ops)) {
printk(KERN_INFO "%s could not verify "
"security operations.\n", __FUNCTION__);
return -EINVAL;
}
if (ops == security_ops) {
printk(KERN_INFO "%s security operations "
"already registered.\n", __FUNCTION__);
return -EINVAL;
}
return security_ops->register_security(name, ops);
}
/**
* mod_unreg_security - allows a security module registered with mod_reg_security() to be unloaded
* @name: a pointer to a string with the name of the security_options to be removed
* @ops: a pointer to the struct security_options that is to be removed
*
* This function allows security modules that have been successfully registered
* with a call to mod_reg_security() to be unloaded from the system.
* This calls the currently loaded security module's unregister_security() call
* with the @name and @ops variables.
*
* The return value depends on the currently loaded security module, with 0 as
* success.
*/
int mod_unreg_security(const char *name, struct security_operations *ops)
{
if (ops == security_ops) {
printk(KERN_INFO "%s invalid attempt to unregister "
" primary security ops.\n", __FUNCTION__);
return -EINVAL;
}
return security_ops->unregister_security(name, ops);
}
EXPORT_SYMBOL_GPL(register_security);
EXPORT_SYMBOL_GPL(unregister_security);
EXPORT_SYMBOL_GPL(mod_reg_security);
EXPORT_SYMBOL_GPL(mod_unreg_security);
EXPORT_SYMBOL(security_ops);

163
security/selinux/Kconfig Normal file
View File

@@ -0,0 +1,163 @@
config SECURITY_SELINUX
bool "NSA SELinux Support"
depends on SECURITY_NETWORK && AUDIT && NET && INET
select NETWORK_SECMARK
default n
help
This selects NSA Security-Enhanced Linux (SELinux).
You will also need a policy configuration and a labeled filesystem.
You can obtain the policy compiler (checkpolicy), the utility for
labeling filesystems (setfiles), and an example policy configuration
from <http://www.nsa.gov/selinux/>.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_BOOTPARAM
bool "NSA SELinux boot parameter"
depends on SECURITY_SELINUX
default n
help
This option adds a kernel parameter 'selinux', which allows SELinux
to be disabled at boot. If this option is selected, SELinux
functionality can be disabled with selinux=0 on the kernel
command line. The purpose of this option is to allow a single
kernel image to be distributed with SELinux built in, but not
necessarily enabled.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_BOOTPARAM_VALUE
int "NSA SELinux boot parameter default value"
depends on SECURITY_SELINUX_BOOTPARAM
range 0 1
default 1
help
This option sets the default value for the kernel parameter
'selinux', which allows SELinux to be disabled at boot. If this
option is set to 0 (zero), the SELinux kernel parameter will
default to 0, disabling SELinux at bootup. If this option is
set to 1 (one), the SELinux kernel parameter will default to 1,
enabling SELinux at bootup.
If you are unsure how to answer this question, answer 1.
config SECURITY_SELINUX_DISABLE
bool "NSA SELinux runtime disable"
depends on SECURITY_SELINUX
default n
help
This option enables writing to a selinuxfs node 'disable', which
allows SELinux to be disabled at runtime prior to the policy load.
SELinux will then remain disabled until the next boot.
This option is similar to the selinux=0 boot parameter, but is to
support runtime disabling of SELinux, e.g. from /sbin/init, for
portability across platforms where boot parameters are difficult
to employ.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_DEVELOP
bool "NSA SELinux Development Support"
depends on SECURITY_SELINUX
default y
help
This enables the development support option of NSA SELinux,
which is useful for experimenting with SELinux and developing
policies. If unsure, say Y. With this option enabled, the
kernel will start in permissive mode (log everything, deny nothing)
unless you specify enforcing=1 on the kernel command line. You
can interactively toggle the kernel between enforcing mode and
permissive mode (if permitted by the policy) via /selinux/enforce.
config SECURITY_SELINUX_AVC_STATS
bool "NSA SELinux AVC Statistics"
depends on SECURITY_SELINUX
default y
help
This option collects access vector cache statistics to
/selinux/avc/cache_stats, which may be monitored via
tools such as avcstat.
config SECURITY_SELINUX_CHECKREQPROT_VALUE
int "NSA SELinux checkreqprot default value"
depends on SECURITY_SELINUX
range 0 1
default 1
help
This option sets the default value for the 'checkreqprot' flag
that determines whether SELinux checks the protection requested
by the application or the protection that will be applied by the
kernel (including any implied execute for read-implies-exec) for
mmap and mprotect calls. If this option is set to 0 (zero),
SELinux will default to checking the protection that will be applied
by the kernel. If this option is set to 1 (one), SELinux will
default to checking the protection requested by the application.
The checkreqprot flag may be changed from the default via the
'checkreqprot=' boot parameter. It may also be changed at runtime
via /selinux/checkreqprot if authorized by policy.
If you are unsure how to answer this question, answer 1.
config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
bool "NSA SELinux enable new secmark network controls by default"
depends on SECURITY_SELINUX
default n
help
This option determines whether the new secmark-based network
controls will be enabled by default. If not, the old internal
per-packet controls will be enabled by default, preserving
old behavior.
If you enable the new controls, you will need updated
SELinux userspace libraries, tools and policy. Typically,
your distribution will provide these and enable the new controls
in the kernel they also distribute.
Note that this option can be overridden at boot with the
selinux_compat_net parameter, and after boot via
/selinux/compat_net. See Documentation/kernel-parameters.txt
for details on this parameter.
If you enable the new network controls, you will likely
also require the SECMARK and CONNSECMARK targets, as
well as any conntrack helpers for protocols which you
wish to control.
If you are unsure what to do here, select N.
config SECURITY_SELINUX_POLICYDB_VERSION_MAX
bool "NSA SELinux maximum supported policy format version"
depends on SECURITY_SELINUX
default n
help
This option enables the maximum policy format version supported
by SELinux to be set to a particular value. This value is reported
to userspace via /selinux/policyvers and used at policy load time.
It can be adjusted downward to support legacy userland (init) that
does not correctly handle kernels that support newer policy versions.
Examples:
For the Fedora Core 3 or 4 Linux distributions, enable this option
and set the value via the next option. For Fedore Core 5 and later,
do not enable this option.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
int "NSA SELinux maximum supported policy format version value"
depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
range 15 21
default 19
help
This option sets the value for the maximum policy format version
supported by SELinux.
Examples:
For Fedora Core 3, use 18.
For Fedora Core 4, use 19.
If you are unsure how to answer this question, look for the
policy format version supported by your policy toolchain, by
running 'checkpolicy -V'. Or look at what policy you have
installed under /etc/selinux/$SELINUXTYPE/policy, where
SELINUXTYPE is defined in your /etc/selinux/config.

12
security/selinux/Makefile Normal file
View File

@@ -0,0 +1,12 @@
#
# Makefile for building the SELinux module as part of the kernel tree.
#
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
EXTRA_CFLAGS += -Isecurity/selinux/include

915
security/selinux/avc.c Normal file
View File

@@ -0,0 +1,915 @@
/*
* Implementation of the kernel access vector cache (AVC).
*
* Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
* James Morris <jmorris@redhat.com>
*
* Update: KaiGai, Kohei <kaigai@ak.jp.nec.com>
* Replaced the avc_lock spinlock by RCU.
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/percpu.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
#include <linux/ip.h>
#include <linux/audit.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include "avc.h"
#include "avc_ss.h"
static const struct av_perm_to_string av_perm_to_string[] = {
#define S_(c, v, s) { c, v, s },
#include "av_perm_to_string.h"
#undef S_
};
static const char *class_to_string[] = {
#define S_(s) s,
#include "class_to_string.h"
#undef S_
};
#define TB_(s) static const char * s [] = {
#define TE_(s) };
#define S_(s) s,
#include "common_perm_to_string.h"
#undef TB_
#undef TE_
#undef S_
static const struct av_inherit av_inherit[] = {
#define S_(c, i, b) { c, common_##i##_perm_to_string, b },
#include "av_inherit.h"
#undef S_
};
const struct selinux_class_perm selinux_class_perm = {
av_perm_to_string,
ARRAY_SIZE(av_perm_to_string),
class_to_string,
ARRAY_SIZE(class_to_string),
av_inherit,
ARRAY_SIZE(av_inherit)
};
#define AVC_CACHE_SLOTS 512
#define AVC_DEF_CACHE_THRESHOLD 512
#define AVC_CACHE_RECLAIM 16
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
#define avc_cache_stats_incr(field) \
do { \
per_cpu(avc_cache_stats, get_cpu()).field++; \
put_cpu(); \
} while (0)
#else
#define avc_cache_stats_incr(field) do {} while (0)
#endif
struct avc_entry {
u32 ssid;
u32 tsid;
u16 tclass;
struct av_decision avd;
atomic_t used; /* used recently */
};
struct avc_node {
struct avc_entry ae;
struct list_head list;
struct rcu_head rhead;
};
struct avc_cache {
struct list_head slots[AVC_CACHE_SLOTS];
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
atomic_t lru_hint; /* LRU hint for reclaim scan */
atomic_t active_nodes;
u32 latest_notif; /* latest revocation notification */
};
struct avc_callback_node {
int (*callback) (u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms,
u32 *out_retained);
u32 events;
u32 ssid;
u32 tsid;
u16 tclass;
u32 perms;
struct avc_callback_node *next;
};
/* Exported via selinufs */
unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
#endif
static struct avc_cache avc_cache;
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
}
/**
* avc_dump_av - Display an access vector in human-readable form.
* @tclass: target security class
* @av: access vector
*/
static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
{
const char **common_pts = NULL;
u32 common_base = 0;
int i, i2, perm;
if (av == 0) {
audit_log_format(ab, " null");
return;
}
for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
if (av_inherit[i].tclass == tclass) {
common_pts = av_inherit[i].common_pts;
common_base = av_inherit[i].common_base;
break;
}
}
audit_log_format(ab, " {");
i = 0;
perm = 1;
while (perm < common_base) {
if (perm & av) {
audit_log_format(ab, " %s", common_pts[i]);
av &= ~perm;
}
i++;
perm <<= 1;
}
while (i < sizeof(av) * 8) {
if (perm & av) {
for (i2 = 0; i2 < ARRAY_SIZE(av_perm_to_string); i2++) {
if ((av_perm_to_string[i2].tclass == tclass) &&
(av_perm_to_string[i2].value == perm))
break;
}
if (i2 < ARRAY_SIZE(av_perm_to_string)) {
audit_log_format(ab, " %s",
av_perm_to_string[i2].name);
av &= ~perm;
}
}
i++;
perm <<= 1;
}
if (av)
audit_log_format(ab, " 0x%x", av);
audit_log_format(ab, " }");
}
/**
* avc_dump_query - Display a SID pair and a class in human-readable form.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
*/
static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
{
int rc;
char *scontext;
u32 scontext_len;
rc = security_sid_to_context(ssid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, "ssid=%d", ssid);
else {
audit_log_format(ab, "scontext=%s", scontext);
kfree(scontext);
}
rc = security_sid_to_context(tsid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, " tsid=%d", tsid);
else {
audit_log_format(ab, " tcontext=%s", scontext);
kfree(scontext);
}
audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
}
/**
* avc_init - Initialize the AVC.
*
* Initialize the access vector cache.
*/
void __init avc_init(void)
{
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
INIT_LIST_HEAD(&avc_cache.slots[i]);
spin_lock_init(&avc_cache.slots_lock[i]);
}
atomic_set(&avc_cache.active_nodes, 0);
atomic_set(&avc_cache.lru_hint, 0);
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL, NULL);
audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
}
int avc_get_hash_stats(char *page)
{
int i, chain_len, max_chain_len, slots_used;
struct avc_node *node;
rcu_read_lock();
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
if (!list_empty(&avc_cache.slots[i])) {
slots_used++;
chain_len = 0;
list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
chain_len++;
if (chain_len > max_chain_len)
max_chain_len = chain_len;
}
}
rcu_read_unlock();
return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
"longest chain: %d\n",
atomic_read(&avc_cache.active_nodes),
slots_used, AVC_CACHE_SLOTS, max_chain_len);
}
static void avc_node_free(struct rcu_head *rhead)
{
struct avc_node *node = container_of(rhead, struct avc_node, rhead);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
}
static void avc_node_delete(struct avc_node *node)
{
list_del_rcu(&node->list);
call_rcu(&node->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
}
static void avc_node_kill(struct avc_node *node)
{
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
atomic_dec(&avc_cache.active_nodes);
}
static void avc_node_replace(struct avc_node *new, struct avc_node *old)
{
list_replace_rcu(&old->list, &new->list);
call_rcu(&old->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
}
static inline int avc_reclaim_node(void)
{
struct avc_node *node;
int hvalue, try, ecx;
unsigned long flags;
for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) {
hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
continue;
list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
if (atomic_dec_and_test(&node->ae.used)) {
/* Recently Unused */
avc_node_delete(node);
avc_cache_stats_incr(reclaims);
ecx++;
if (ecx >= AVC_CACHE_RECLAIM) {
spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
goto out;
}
}
}
spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
}
out:
return ecx;
}
static struct avc_node *avc_alloc_node(void)
{
struct avc_node *node;
node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC);
if (!node)
goto out;
INIT_RCU_HEAD(&node->rhead);
INIT_LIST_HEAD(&node->list);
atomic_set(&node->ae.used, 1);
avc_cache_stats_incr(allocations);
if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
avc_reclaim_node();
out:
return node;
}
static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
{
node->ae.ssid = ssid;
node->ae.tsid = tsid;
node->ae.tclass = tclass;
memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
}
static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node, *ret = NULL;
int hvalue;
hvalue = avc_hash(ssid, tsid, tclass);
list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
if (ssid == node->ae.ssid &&
tclass == node->ae.tclass &&
tsid == node->ae.tsid) {
ret = node;
break;
}
}
if (ret == NULL) {
/* cache miss */
goto out;
}
/* cache hit */
if (atomic_read(&ret->ae.used) != 1)
atomic_set(&ret->ae.used, 1);
out:
return ret;
}
/**
* avc_lookup - Look up an AVC entry.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
*
* Look up an AVC entry that is valid for the
* @requested permissions between the SID pair
* (@ssid, @tsid), interpreting the permissions
* based on @tclass. If a valid AVC entry exists,
* then this function return the avc_node.
* Otherwise, this function returns NULL.
*/
static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
{
struct avc_node *node;
avc_cache_stats_incr(lookups);
node = avc_search_node(ssid, tsid, tclass);
if (node && ((node->ae.avd.decided & requested) == requested)) {
avc_cache_stats_incr(hits);
goto out;
}
node = NULL;
avc_cache_stats_incr(misses);
out:
return node;
}
static int avc_latest_notif_update(int seqno, int is_insert)
{
int ret = 0;
static DEFINE_SPINLOCK(notif_lock);
unsigned long flag;
spin_lock_irqsave(&notif_lock, flag);
if (is_insert) {
if (seqno < avc_cache.latest_notif) {
printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n",
seqno, avc_cache.latest_notif);
ret = -EAGAIN;
}
} else {
if (seqno > avc_cache.latest_notif)
avc_cache.latest_notif = seqno;
}
spin_unlock_irqrestore(&notif_lock, flag);
return ret;
}
/**
* avc_insert - Insert an AVC entry.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @ae: AVC entry
*
* Insert an AVC entry for the SID pair
* (@ssid, @tsid) and class @tclass.
* The access vectors and the sequence number are
* normally provided by the security server in
* response to a security_compute_av() call. If the
* sequence number @ae->avd.seqno is not less than the latest
* revocation notification, then the function copies
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
{
struct avc_node *pos, *node = NULL;
int hvalue;
unsigned long flag;
if (avc_latest_notif_update(ae->avd.seqno, 1))
goto out;
node = avc_alloc_node();
if (node) {
hvalue = avc_hash(ssid, tsid, tclass);
avc_node_populate(node, ssid, tsid, tclass, ae);
spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
if (pos->ae.ssid == ssid &&
pos->ae.tsid == tsid &&
pos->ae.tclass == tclass) {
avc_node_replace(node, pos);
goto found;
}
}
list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
found:
spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
}
out:
return node;
}
static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
struct in6_addr *addr, __be16 port,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr));
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
__be16 port, char *name1, char *name2)
{
if (addr)
audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr));
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
/**
* avc_audit - Audit the granting or denial of permissions.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions
* @avd: access vector decisions
* @result: result from avc_has_perm_noaudit
* @a: auxiliary audit data
*
* Audit the granting or denial of permissions in accordance
* with the policy. This function is typically called by
* avc_has_perm() after a permission check, but can also be
* called directly by callers who use avc_has_perm_noaudit()
* in order to separate the permission check from the auditing.
* For example, this separation is useful when the permission check must
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *a)
{
struct task_struct *tsk = current;
struct inode *inode = NULL;
u32 denied, audited;
struct audit_buffer *ab;
denied = requested & ~avd->allowed;
if (denied) {
audited = denied;
if (!(audited & avd->auditdeny))
return;
} else if (result) {
audited = denied = requested;
} else {
audited = requested;
if (!(audited & avd->auditallow))
return;
}
ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
if (!ab)
return; /* audit_panic has been called */
audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
avc_dump_av(ab, tclass,audited);
audit_log_format(ab, " for ");
if (a && a->tsk)
tsk = a->tsk;
if (tsk && tsk->pid) {
audit_log_format(ab, " pid=%d comm=", tsk->pid);
audit_log_untrustedstring(ab, tsk->comm);
}
if (a) {
switch (a->type) {
case AVC_AUDIT_DATA_IPC:
audit_log_format(ab, " key=%d", a->u.ipc_id);
break;
case AVC_AUDIT_DATA_CAP:
audit_log_format(ab, " capability=%d", a->u.cap);
break;
case AVC_AUDIT_DATA_FS:
if (a->u.fs.dentry) {
struct dentry *dentry = a->u.fs.dentry;
if (a->u.fs.mnt)
audit_avc_path(dentry, a->u.fs.mnt);
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, dentry->d_name.name);
inode = dentry->d_inode;
} else if (a->u.fs.inode) {
struct dentry *dentry;
inode = a->u.fs.inode;
dentry = d_find_alias(inode);
if (dentry) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, dentry->d_name.name);
dput(dentry);
}
}
if (inode)
audit_log_format(ab, " dev=%s ino=%ld",
inode->i_sb->s_id,
inode->i_ino);
break;
case AVC_AUDIT_DATA_NET:
if (a->u.net.sk) {
struct sock *sk = a->u.net.sk;
struct unix_sock *u;
int len = 0;
char *p = NULL;
switch (sk->sk_family) {
case AF_INET: {
struct inet_sock *inet = inet_sk(sk);
avc_print_ipv4_addr(ab, inet->rcv_saddr,
inet->sport,
"laddr", "lport");
avc_print_ipv4_addr(ab, inet->daddr,
inet->dport,
"faddr", "fport");
break;
}
case AF_INET6: {
struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *inet6 = inet6_sk(sk);
avc_print_ipv6_addr(ab, &inet6->rcv_saddr,
inet->sport,
"laddr", "lport");
avc_print_ipv6_addr(ab, &inet6->daddr,
inet->dport,
"faddr", "fport");
break;
}
case AF_UNIX:
u = unix_sk(sk);
if (u->dentry) {
audit_avc_path(u->dentry, u->mnt);
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, u->dentry->d_name.name);
break;
}
if (!u->addr)
break;
len = u->addr->len-sizeof(short);
p = &u->addr->name->sun_path[0];
audit_log_format(ab, " path=");
if (*p)
audit_log_untrustedstring(ab, p);
else
audit_log_hex(ab, p, len);
break;
}
}
switch (a->u.net.family) {
case AF_INET:
avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
a->u.net.sport,
"saddr", "src");
avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
a->u.net.dport,
"daddr", "dest");
break;
case AF_INET6:
avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr,
a->u.net.sport,
"saddr", "src");
avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr,
a->u.net.dport,
"daddr", "dest");
break;
}
if (a->u.net.netif)
audit_log_format(ab, " netif=%s",
a->u.net.netif);
break;
}
}
audit_log_format(ab, " ");
avc_dump_query(ab, ssid, tsid, tclass);
audit_log_end(ab);
}
/**
* avc_add_callback - Register a callback for security events.
* @callback: callback function
* @events: security events
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions
*
* Register a callback function for events in the set @events
* related to the SID pair (@ssid, @tsid) and
* and the permissions @perms, interpreting
* @perms based on @tclass. Returns %0 on success or
* -%ENOMEM if insufficient memory exists to add the callback.
*/
int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms,
u32 *out_retained),
u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms)
{
struct avc_callback_node *c;
int rc = 0;
c = kmalloc(sizeof(*c), GFP_ATOMIC);
if (!c) {
rc = -ENOMEM;
goto out;
}
c->callback = callback;
c->events = events;
c->ssid = ssid;
c->tsid = tsid;
c->perms = perms;
c->next = avc_callbacks;
avc_callbacks = c;
out:
return rc;
}
static inline int avc_sidcmp(u32 x, u32 y)
{
return (x == y || x == SECSID_WILD || y == SECSID_WILD);
}
/**
* avc_update_node Update an AVC entry
* @event : Updating event
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
*
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
* otherwise, this function update the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
{
int hvalue, rc = 0;
unsigned long flag;
struct avc_node *pos, *node, *orig = NULL;
node = avc_alloc_node();
if (!node) {
rc = -ENOMEM;
goto out;
}
/* Lock the target slot */
hvalue = avc_hash(ssid, tsid, tclass);
spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
list_for_each_entry(pos, &avc_cache.slots[hvalue], list){
if ( ssid==pos->ae.ssid &&
tsid==pos->ae.tsid &&
tclass==pos->ae.tclass ){
orig = pos;
break;
}
}
if (!orig) {
rc = -ENOENT;
avc_node_kill(node);
goto out_unlock;
}
/*
* Copy and replace original node.
*/
avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
switch (event) {
case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms;
break;
case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_REVOKE:
node->ae.avd.allowed &= ~perms;
break;
case AVC_CALLBACK_AUDITALLOW_ENABLE:
node->ae.avd.auditallow |= perms;
break;
case AVC_CALLBACK_AUDITALLOW_DISABLE:
node->ae.avd.auditallow &= ~perms;
break;
case AVC_CALLBACK_AUDITDENY_ENABLE:
node->ae.avd.auditdeny |= perms;
break;
case AVC_CALLBACK_AUDITDENY_DISABLE:
node->ae.avd.auditdeny &= ~perms;
break;
}
avc_node_replace(node, orig);
out_unlock:
spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
out:
return rc;
}
/**
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
* @seqno: policy sequence number
*/
int avc_ss_reset(u32 seqno)
{
struct avc_callback_node *c;
int i, rc = 0, tmprc;
unsigned long flag;
struct avc_node *node;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
list_for_each_entry(node, &avc_cache.slots[i], list)
avc_node_delete(node);
spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
}
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
tmprc = c->callback(AVC_CALLBACK_RESET,
0, 0, 0, 0, NULL);
/* save the first error encountered for the return
value and continue processing the callbacks */
if (!rc)
rc = tmprc;
}
}
avc_latest_notif_update(seqno, 0);
return rc;
}
/**
* avc_has_perm_noaudit - Check permissions but perform no auditing.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
* @flags: AVC_STRICT or 0
* @avd: access vector decisions
*
* Check the AVC to determine whether the @requested permissions are granted
* for the SID pair (@ssid, @tsid), interpreting the permissions
* based on @tclass, and call the security server on a cache miss to obtain
* a new decision and add it to the cache. Return a copy of the decisions
* in @avd. Return %0 if all @requested permissions are granted,
* -%EACCES if any permissions are denied, or another -errno upon
* other errors. This function is typically called by avc_has_perm(),
* but may also be called directly to separate permission checking from
* auditing, e.g. in cases where a lock must be held for the check but
* should be released for the auditing.
*/
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd)
{
struct avc_node *node;
struct avc_entry entry, *p_ae;
int rc = 0;
u32 denied;
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass, requested);
if (!node) {
rcu_read_unlock();
rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd);
if (rc)
goto out;
rcu_read_lock();
node = avc_insert(ssid,tsid,tclass,&entry);
}
p_ae = node ? &node->ae : &entry;
if (avd)
memcpy(avd, &p_ae->avd, sizeof(*avd));
denied = requested & ~(p_ae->avd.allowed);
if (!requested || denied) {
if (selinux_enforcing || (flags & AVC_STRICT))
rc = -EACCES;
else
if (node)
avc_update_node(AVC_CALLBACK_GRANT,requested,
ssid,tsid,tclass);
}
rcu_read_unlock();
out:
return rc;
}
/**
* avc_has_perm - Check permissions and perform any appropriate auditing.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
* @auditdata: auxiliary audit data
*
* Check the AVC to determine whether the @requested permissions are granted
* for the SID pair (@ssid, @tsid), interpreting the permissions
* based on @tclass, and call the security server on a cache miss to obtain
* a new decision and add it to the cache. Audit the granting or denial of
* permissions in accordance with the policy. Return %0 if all @requested
* permissions are granted, -%EACCES if any permissions are denied, or
* another -errno upon other errors.
*/
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct avc_audit_data *auditdata)
{
struct av_decision avd;
int rc;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}

View File

@@ -0,0 +1,87 @@
/*
* SELinux services exported to the rest of the kernel.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
*
* 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.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/selinux.h>
#include <linux/fs.h>
#include <linux/ipc.h>
#include "security.h"
#include "objsec.h"
int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen)
{
if (selinux_enabled)
return security_sid_to_context(sid, ctx, ctxlen);
else {
*ctx = NULL;
*ctxlen = 0;
}
return 0;
}
void selinux_get_inode_sid(const struct inode *inode, u32 *sid)
{
if (selinux_enabled) {
struct inode_security_struct *isec = inode->i_security;
*sid = isec->sid;
return;
}
*sid = 0;
}
void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid)
{
if (selinux_enabled) {
struct ipc_security_struct *isec = ipcp->security;
*sid = isec->sid;
return;
}
*sid = 0;
}
void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
{
if (selinux_enabled) {
struct task_security_struct *tsec = tsk->security;
*sid = tsec->sid;
return;
}
*sid = 0;
}
int selinux_string_to_sid(char *str, u32 *sid)
{
if (selinux_enabled)
return security_context_to_sid(str, strlen(str), sid);
else {
*sid = 0;
return 0;
}
}
EXPORT_SYMBOL_GPL(selinux_string_to_sid);
int selinux_relabel_packet_permission(u32 sid)
{
if (selinux_enabled) {
struct task_security_struct *tsec = current->security;
return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
PACKET__RELABELTO, NULL);
}
return 0;
}
EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission);

5051
security/selinux/hooks.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
/* This file is automatically generated. Do not edit. */
S_(SECCLASS_DIR, file, 0x00020000UL)
S_(SECCLASS_FILE, file, 0x00020000UL)
S_(SECCLASS_LNK_FILE, file, 0x00020000UL)
S_(SECCLASS_CHR_FILE, file, 0x00020000UL)
S_(SECCLASS_BLK_FILE, file, 0x00020000UL)
S_(SECCLASS_SOCK_FILE, file, 0x00020000UL)
S_(SECCLASS_FIFO_FILE, file, 0x00020000UL)
S_(SECCLASS_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_TCP_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UDP_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_RAWIP_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_PACKET_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_IPC, ipc, 0x00000200UL)
S_(SECCLASS_SEM, ipc, 0x00000200UL)
S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
S_(SECCLASS_SHM, ipc, 0x00000200UL)
S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_NFLOG_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_XFRM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_SELINUX_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_AUDIT_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_APPLETALK_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_DCCP_SOCKET, socket, 0x00400000UL)

View File

@@ -0,0 +1,262 @@
/* This file is automatically generated. Do not edit. */
S_(SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod")
S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget")
S_(SECCLASS_DIR, DIR__ADD_NAME, "add_name")
S_(SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name")
S_(SECCLASS_DIR, DIR__REPARENT, "reparent")
S_(SECCLASS_DIR, DIR__SEARCH, "search")
S_(SECCLASS_DIR, DIR__RMDIR, "rmdir")
S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans")
S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint")
S_(SECCLASS_FILE, FILE__EXECMOD, "execmod")
S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans")
S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint")
S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
S_(SECCLASS_FD, FD__USE, "use")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NAME_CONNECT, "name_connect")
S_(SECCLASS_UDP_SOCKET, UDP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_RAWIP_SOCKET, RAWIP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv")
S_(SECCLASS_NODE, NODE__TCP_SEND, "tcp_send")
S_(SECCLASS_NODE, NODE__UDP_RECV, "udp_recv")
S_(SECCLASS_NODE, NODE__UDP_SEND, "udp_send")
S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv")
S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send")
S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest")
S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send")
S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv")
S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send")
S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv")
S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send")
S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv")
S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send")
S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom")
S_(SECCLASS_PROCESS, PROCESS__FORK, "fork")
S_(SECCLASS_PROCESS, PROCESS__TRANSITION, "transition")
S_(SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld")
S_(SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill")
S_(SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop")
S_(SECCLASS_PROCESS, PROCESS__SIGNULL, "signull")
S_(SECCLASS_PROCESS, PROCESS__SIGNAL, "signal")
S_(SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace")
S_(SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched")
S_(SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched")
S_(SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession")
S_(SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid")
S_(SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid")
S_(SECCLASS_PROCESS, PROCESS__GETCAP, "getcap")
S_(SECCLASS_PROCESS, PROCESS__SETCAP, "setcap")
S_(SECCLASS_PROCESS, PROCESS__SHARE, "share")
S_(SECCLASS_PROCESS, PROCESS__GETATTR, "getattr")
S_(SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec")
S_(SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate")
S_(SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure")
S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh")
S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit")
S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh")
S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition")
S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent")
S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem")
S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack")
S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap")
S_(SECCLASS_PROCESS, PROCESS__SETKEYCREATE, "setkeycreate")
S_(SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, "setsockcreate")
S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue")
S_(SECCLASS_MSG, MSG__SEND, "send")
S_(SECCLASS_MSG, MSG__RECEIVE, "receive")
S_(SECCLASS_SHM, SHM__LOCK, "lock")
S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av")
S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create")
S_(SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member")
S_(SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context")
S_(SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy")
S_(SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel")
S_(SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user")
S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce")
S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool")
S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam")
S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot")
S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
S_(SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner")
S_(SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid")
S_(SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill")
S_(SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid")
S_(SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid")
S_(SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap")
S_(SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable")
S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service")
S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast")
S_(SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin")
S_(SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw")
S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock")
S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time")
S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config")
S_(SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod")
S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease")
S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_WRITE, "audit_write")
S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_CONTROL, "audit_control")
S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd")
S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn")
S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh")
S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok")
S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab")
S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create")
S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy")
S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw")
S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy")
S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr")
S_(SECCLASS_GC, GC__CREATE, "create")
S_(SECCLASS_GC, GC__FREE, "free")
S_(SECCLASS_GC, GC__GETATTR, "getattr")
S_(SECCLASS_GC, GC__SETATTR, "setattr")
S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild")
S_(SECCLASS_WINDOW, WINDOW__CREATE, "create")
S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy")
S_(SECCLASS_WINDOW, WINDOW__MAP, "map")
S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap")
S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack")
S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist")
S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop")
S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop")
S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr")
S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr")
S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus")
S_(SECCLASS_WINDOW, WINDOW__MOVE, "move")
S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection")
S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent")
S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife")
S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate")
S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent")
S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion")
S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent")
S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent")
S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent")
S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent")
S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest")
S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent")
S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent")
S_(SECCLASS_FONT, FONT__LOAD, "load")
S_(SECCLASS_FONT, FONT__FREE, "free")
S_(SECCLASS_FONT, FONT__GETATTR, "getattr")
S_(SECCLASS_FONT, FONT__USE, "use")
S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create")
S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free")
S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install")
S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall")
S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list")
S_(SECCLASS_COLORMAP, COLORMAP__READ, "read")
S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store")
S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr")
S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr")
S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create")
S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free")
S_(SECCLASS_PROPERTY, PROPERTY__READ, "read")
S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write")
S_(SECCLASS_CURSOR, CURSOR__CREATE, "create")
S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph")
S_(SECCLASS_CURSOR, CURSOR__FREE, "free")
S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign")
S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr")
S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill")
S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup")
S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr")
S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr")
S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus")
S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer")
S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab")
S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab")
S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab")
S_(SECCLASS_XINPUT, XINPUT__BELL, "bell")
S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion")
S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput")
S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver")
S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist")
S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist")
S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath")
S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath")
S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr")
S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab")
S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab")
S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query")
S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use")
S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec")
S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp")
S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect")
S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap")
S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec")
S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec")
S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc")
S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg")
S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd")
S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp")
S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost")
S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat")
S_(SECCLASS_NSCD, NSCD__ADMIN, "admin")
S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd")
S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp")
S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, "polmatch")
S_(SECCLASS_PACKET, PACKET__SEND, "send")
S_(SECCLASS_PACKET, PACKET__RECV, "recv")
S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
S_(SECCLASS_KEY, KEY__VIEW, "view")
S_(SECCLASS_KEY, KEY__READ, "read")
S_(SECCLASS_KEY, KEY__WRITE, "write")
S_(SECCLASS_KEY, KEY__SEARCH, "search")
S_(SECCLASS_KEY, KEY__LINK, "link")
S_(SECCLASS_KEY, KEY__SETATTR, "setattr")
S_(SECCLASS_KEY, KEY__CREATE, "create")
S_(SECCLASS_CONTEXT, CONTEXT__TRANSLATE, "translate")
S_(SECCLASS_CONTEXT, CONTEXT__CONTAINS, "contains")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
/*
* Access vector cache interface for object managers.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SELINUX_AVC_H_
#define _SELINUX_AVC_H_
#include <linux/stddef.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <asm/system.h>
#include "flask.h"
#include "av_permissions.h"
#include "security.h"
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
extern int selinux_enforcing;
#else
#define selinux_enforcing 1
#endif
/*
* An entry in the AVC.
*/
struct avc_entry;
struct task_struct;
struct vfsmount;
struct dentry;
struct inode;
struct sock;
struct sk_buff;
/* Auxiliary data to use in generating the audit record. */
struct avc_audit_data {
char type;
#define AVC_AUDIT_DATA_FS 1
#define AVC_AUDIT_DATA_NET 2
#define AVC_AUDIT_DATA_CAP 3
#define AVC_AUDIT_DATA_IPC 4
struct task_struct *tsk;
union {
struct {
struct vfsmount *mnt;
struct dentry *dentry;
struct inode *inode;
} fs;
struct {
char *netif;
struct sock *sk;
u16 family;
__be16 dport;
__be16 sport;
union {
struct {
__be32 daddr;
__be32 saddr;
} v4;
struct {
struct in6_addr daddr;
struct in6_addr saddr;
} v6;
} fam;
} net;
int cap;
int ipc_id;
} u;
};
#define v4info fam.v4
#define v6info fam.v6
/* Initialize an AVC audit data structure. */
#define AVC_AUDIT_DATA_INIT(_d,_t) \
{ memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
/*
* AVC statistics
*/
struct avc_cache_stats
{
unsigned int lookups;
unsigned int hits;
unsigned int misses;
unsigned int allocations;
unsigned int reclaims;
unsigned int frees;
};
/*
* AVC operations
*/
void __init avc_init(void);
void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *auditdata);
#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd);
int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct avc_audit_data *auditdata);
#define AVC_CALLBACK_GRANT 1
#define AVC_CALLBACK_TRY_REVOKE 2
#define AVC_CALLBACK_REVOKE 4
#define AVC_CALLBACK_RESET 8
#define AVC_CALLBACK_AUDITALLOW_ENABLE 16
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms,
u32 *out_retained),
u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms);
/* Exported to selinuxfs */
int avc_get_hash_stats(char *page);
extern unsigned int avc_cache_threshold;
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
#endif
#endif /* _SELINUX_AVC_H_ */

View File

@@ -0,0 +1,38 @@
/*
* Access vector cache interface for the security server.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SELINUX_AVC_SS_H_
#define _SELINUX_AVC_SS_H_
#include "flask.h"
int avc_ss_reset(u32 seqno);
struct av_perm_to_string
{
u16 tclass;
u32 value;
const char *name;
};
struct av_inherit
{
u16 tclass;
const char **common_pts;
u32 common_base;
};
struct selinux_class_perm
{
const struct av_perm_to_string *av_perm_to_string;
u32 av_pts_len;
const char **class_to_string;
u32 cts_len;
const struct av_inherit *av_inherit;
u32 av_inherit_len;
};
#endif /* _SELINUX_AVC_SS_H_ */

View File

@@ -0,0 +1,65 @@
/* This file is automatically generated. Do not edit. */
/*
* Security object class definitions
*/
S_("null")
S_("security")
S_("process")
S_("system")
S_("capability")
S_("filesystem")
S_("file")
S_("dir")
S_("fd")
S_("lnk_file")
S_("chr_file")
S_("blk_file")
S_("sock_file")
S_("fifo_file")
S_("socket")
S_("tcp_socket")
S_("udp_socket")
S_("rawip_socket")
S_("node")
S_("netif")
S_("netlink_socket")
S_("packet_socket")
S_("key_socket")
S_("unix_stream_socket")
S_("unix_dgram_socket")
S_("sem")
S_("msg")
S_("msgq")
S_("shm")
S_("ipc")
S_("passwd")
S_("drawable")
S_("window")
S_("gc")
S_("font")
S_("colormap")
S_("property")
S_("cursor")
S_("xclient")
S_("xinput")
S_("xserver")
S_("xextension")
S_("pax")
S_("netlink_route_socket")
S_("netlink_firewall_socket")
S_("netlink_tcpdiag_socket")
S_("netlink_nflog_socket")
S_("netlink_xfrm_socket")
S_("netlink_selinux_socket")
S_("netlink_audit_socket")
S_("netlink_ip6fw_socket")
S_("netlink_dnrt_socket")
S_("dbus")
S_("nscd")
S_("association")
S_("netlink_kobject_uevent_socket")
S_("appletalk_socket")
S_("packet")
S_("key")
S_("context")
S_("dccp_socket")

View File

@@ -0,0 +1,58 @@
/* This file is automatically generated. Do not edit. */
TB_(common_file_perm_to_string)
S_("ioctl")
S_("read")
S_("write")
S_("create")
S_("getattr")
S_("setattr")
S_("lock")
S_("relabelfrom")
S_("relabelto")
S_("append")
S_("unlink")
S_("link")
S_("rename")
S_("execute")
S_("swapon")
S_("quotaon")
S_("mounton")
TE_(common_file_perm_to_string)
TB_(common_socket_perm_to_string)
S_("ioctl")
S_("read")
S_("write")
S_("create")
S_("getattr")
S_("setattr")
S_("lock")
S_("relabelfrom")
S_("relabelto")
S_("append")
S_("bind")
S_("connect")
S_("listen")
S_("accept")
S_("getopt")
S_("setopt")
S_("shutdown")
S_("recvfrom")
S_("sendto")
S_("recv_msg")
S_("send_msg")
S_("name_bind")
TE_(common_socket_perm_to_string)
TB_(common_ipc_perm_to_string)
S_("create")
S_("destroy")
S_("getattr")
S_("setattr")
S_("read")
S_("write")
S_("associate")
S_("unix_read")
S_("unix_write")
TE_(common_ipc_perm_to_string)

View File

@@ -0,0 +1,22 @@
/*
* Interface to booleans in the security server. This is exported
* for the selinuxfs.
*
* Author: Karl MacMillan <kmacmillan@tresys.com>
*
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#ifndef _SELINUX_CONDITIONAL_H_
#define _SELINUX_CONDITIONAL_H_
int security_get_bools(int *len, char ***names, int **values);
int security_set_bools(int len, int *values);
int security_get_bool_value(int bool);
#endif

View File

@@ -0,0 +1,102 @@
/* This file is automatically generated. Do not edit. */
#ifndef _SELINUX_FLASK_H_
#define _SELINUX_FLASK_H_
/*
* Security object class definitions
*/
#define SECCLASS_SECURITY 1
#define SECCLASS_PROCESS 2
#define SECCLASS_SYSTEM 3
#define SECCLASS_CAPABILITY 4
#define SECCLASS_FILESYSTEM 5
#define SECCLASS_FILE 6
#define SECCLASS_DIR 7
#define SECCLASS_FD 8
#define SECCLASS_LNK_FILE 9
#define SECCLASS_CHR_FILE 10
#define SECCLASS_BLK_FILE 11
#define SECCLASS_SOCK_FILE 12
#define SECCLASS_FIFO_FILE 13
#define SECCLASS_SOCKET 14
#define SECCLASS_TCP_SOCKET 15
#define SECCLASS_UDP_SOCKET 16
#define SECCLASS_RAWIP_SOCKET 17
#define SECCLASS_NODE 18
#define SECCLASS_NETIF 19
#define SECCLASS_NETLINK_SOCKET 20
#define SECCLASS_PACKET_SOCKET 21
#define SECCLASS_KEY_SOCKET 22
#define SECCLASS_UNIX_STREAM_SOCKET 23
#define SECCLASS_UNIX_DGRAM_SOCKET 24
#define SECCLASS_SEM 25
#define SECCLASS_MSG 26
#define SECCLASS_MSGQ 27
#define SECCLASS_SHM 28
#define SECCLASS_IPC 29
#define SECCLASS_PASSWD 30
#define SECCLASS_DRAWABLE 31
#define SECCLASS_WINDOW 32
#define SECCLASS_GC 33
#define SECCLASS_FONT 34
#define SECCLASS_COLORMAP 35
#define SECCLASS_PROPERTY 36
#define SECCLASS_CURSOR 37
#define SECCLASS_XCLIENT 38
#define SECCLASS_XINPUT 39
#define SECCLASS_XSERVER 40
#define SECCLASS_XEXTENSION 41
#define SECCLASS_PAX 42
#define SECCLASS_NETLINK_ROUTE_SOCKET 43
#define SECCLASS_NETLINK_FIREWALL_SOCKET 44
#define SECCLASS_NETLINK_TCPDIAG_SOCKET 45
#define SECCLASS_NETLINK_NFLOG_SOCKET 46
#define SECCLASS_NETLINK_XFRM_SOCKET 47
#define SECCLASS_NETLINK_SELINUX_SOCKET 48
#define SECCLASS_NETLINK_AUDIT_SOCKET 49
#define SECCLASS_NETLINK_IP6FW_SOCKET 50
#define SECCLASS_NETLINK_DNRT_SOCKET 51
#define SECCLASS_DBUS 52
#define SECCLASS_NSCD 53
#define SECCLASS_ASSOCIATION 54
#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55
#define SECCLASS_APPLETALK_SOCKET 56
#define SECCLASS_PACKET 57
#define SECCLASS_KEY 58
#define SECCLASS_CONTEXT 59
#define SECCLASS_DCCP_SOCKET 60
/*
* Security identifier indices for initial entities
*/
#define SECINITSID_KERNEL 1
#define SECINITSID_SECURITY 2
#define SECINITSID_UNLABELED 3
#define SECINITSID_FS 4
#define SECINITSID_FILE 5
#define SECINITSID_FILE_LABELS 6
#define SECINITSID_INIT 7
#define SECINITSID_ANY_SOCKET 8
#define SECINITSID_PORT 9
#define SECINITSID_NETIF 10
#define SECINITSID_NETMSG 11
#define SECINITSID_NODE 12
#define SECINITSID_IGMP_PACKET 13
#define SECINITSID_ICMP_SOCKET 14
#define SECINITSID_TCP_SOCKET 15
#define SECINITSID_SYSCTL_MODPROBE 16
#define SECINITSID_SYSCTL 17
#define SECINITSID_SYSCTL_FS 18
#define SECINITSID_SYSCTL_KERNEL 19
#define SECINITSID_SYSCTL_NET 20
#define SECINITSID_SYSCTL_NET_UNIX 21
#define SECINITSID_SYSCTL_VM 22
#define SECINITSID_SYSCTL_DEV 23
#define SECINITSID_KMOD 24
#define SECINITSID_POLICY 25
#define SECINITSID_SCMP_PACKET 26
#define SECINITSID_DEVNULL 27
#define SECINITSID_NUM 27
#endif

View File

@@ -0,0 +1,33 @@
/* This file is automatically generated. Do not edit. */
static char *initial_sid_to_string[] =
{
"null",
"kernel",
"security",
"unlabeled",
"fs",
"file",
"file_labels",
"init",
"any_socket",
"port",
"netif",
"netmsg",
"node",
"igmp_packet",
"icmp_socket",
"tcp_socket",
"sysctl_modprobe",
"sysctl",
"sysctl_fs",
"sysctl_kernel",
"sysctl_net",
"sysctl_net_unix",
"sysctl_vm",
"sysctl_dev",
"kmod",
"policy",
"scmp_packet",
"devnull",
};

View File

@@ -0,0 +1,21 @@
/*
* Network interface table.
*
* Network interfaces (devices) do not have a security field, so we
* maintain a table associating each interface with a SID.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#ifndef _SELINUX_NETIF_H_
#define _SELINUX_NETIF_H_
int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid);
#endif /* _SELINUX_NETIF_H_ */

View File

@@ -0,0 +1,123 @@
/*
* NSA Security-Enhanced Linux (SELinux) security module
*
* This file contains the SELinux security data structures for kernel objects.
*
* Author(s): Stephen Smalley, <sds@epoch.ncsc.mil>
* Chris Vance, <cvance@nai.com>
* Wayne Salamon, <wsalamon@nai.com>
* James Morris <jmorris@redhat.com>
*
* Copyright (C) 2001,2002 Networks Associates Technology, Inc.
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#ifndef _SELINUX_OBJSEC_H_
#define _SELINUX_OBJSEC_H_
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/in.h>
#include <linux/spinlock.h>
#include "flask.h"
#include "avc.h"
struct task_security_struct {
struct task_struct *task; /* back pointer to task object */
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
u32 ptrace_sid; /* SID of ptrace parent */
};
struct inode_security_struct {
struct inode *inode; /* back pointer to inode object */
struct list_head list; /* list of inode_security_struct */
u32 task_sid; /* SID of creating task */
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
struct mutex lock;
unsigned char inherit; /* inherit SID from parent entry */
};
struct file_security_struct {
struct file *file; /* back pointer to file object */
u32 sid; /* SID of open file description */
u32 fown_sid; /* SID of file owner (for SIGIO) */
};
struct superblock_security_struct {
struct super_block *sb; /* back pointer to sb object */
struct list_head list; /* list of superblock_security_struct */
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
unsigned int behavior; /* labeling behavior */
unsigned char initialized; /* initialization flag */
unsigned char proc; /* proc fs */
struct mutex lock;
struct list_head isec_head;
spinlock_t isec_lock;
};
struct msg_security_struct {
struct msg_msg *msg; /* back pointer */
u32 sid; /* SID of message */
};
struct ipc_security_struct {
struct kern_ipc_perm *ipc_perm; /* back pointer */
u16 sclass; /* security class of this object */
u32 sid; /* SID of IPC resource */
};
struct bprm_security_struct {
struct linux_binprm *bprm; /* back pointer to bprm object */
u32 sid; /* SID for transformed process */
unsigned char set;
/*
* unsafe is used to share failure information from bprm_apply_creds()
* to bprm_post_apply_creds().
*/
char unsafe;
};
struct netif_security_struct {
struct net_device *dev; /* back pointer */
u32 if_sid; /* SID for this interface */
u32 msg_sid; /* default SID for messages received on this interface */
};
struct sk_security_struct {
struct sock *sk; /* back pointer to sk object */
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
#ifdef CONFIG_NETLABEL
u16 sclass; /* sock security class */
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
NLBL_LABELED,
} nlbl_state;
spinlock_t nlbl_lock; /* protects nlbl_state */
#endif
};
struct key_security_struct {
struct key *obj; /* back pointer */
u32 sid; /* SID of key */
};
extern unsigned int selinux_checkreqprot;
#endif /* _SELINUX_OBJSEC_H_ */

View File

@@ -0,0 +1,106 @@
/*
* Security server interface.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*
*/
#ifndef _SELINUX_SECURITY_H_
#define _SELINUX_SECURITY_H_
#include "flask.h"
#define SECSID_NULL 0x00000000 /* unspecified SID */
#define SECSID_WILD 0xffffffff /* wildcard SID */
#define SECCLASS_NULL 0x0000 /* no class */
#define SELINUX_MAGIC 0xf97cff8c
/* Identify specific policy version changes */
#define POLICYDB_VERSION_BASE 15
#define POLICYDB_VERSION_BOOL 16
#define POLICYDB_VERSION_IPV6 17
#define POLICYDB_VERSION_NLCLASS 18
#define POLICYDB_VERSION_VALIDATETRANS 19
#define POLICYDB_VERSION_MLS 19
#define POLICYDB_VERSION_AVTAB 20
#define POLICYDB_VERSION_RANGETRANS 21
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS
#endif
struct sk_buff;
extern int selinux_enabled;
extern int selinux_mls_enabled;
int security_load_policy(void * data, size_t len);
struct av_decision {
u32 allowed;
u32 decided;
u32 auditallow;
u32 auditdeny;
u32 seqno;
};
int security_compute_av(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd);
int security_transition_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_member_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_change_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_sid_to_context(u32 sid, char **scontext,
u32 *scontext_len);
int security_context_to_sid(char *scontext, u32 scontext_len,
u32 *out_sid);
int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *out_sid, u32 def_sid);
int security_get_user_sids(u32 callsid, char *username,
u32 **sids, u32 *nel);
int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port,
u32 *out_sid);
int security_netif_sid(char *name, u32 *if_sid,
u32 *msg_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen,
u32 *out_sid);
void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid);
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
#define SECURITY_FS_USE_GENFS 4 /* use the genfs support */
#define SECURITY_FS_USE_NONE 5 /* no labeling support */
#define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */
int security_fs_use(const char *fstype, unsigned int *behavior,
u32 *sid);
int security_genfs_sid(const char *fstype, char *name, u16 sclass,
u32 *sid);
#endif /* _SELINUX_SECURITY_H_ */

View File

@@ -0,0 +1,124 @@
/*
* SELinux interface to the NetLabel subsystem
*
* Author : Paul Moore <paul.moore@hp.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _SELINUX_NETLABEL_H_
#define _SELINUX_NETLABEL_H_
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include "avc.h"
#include "objsec.h"
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid);
int selinux_netlbl_socket_post_create(struct socket *sock);
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
struct avc_audit_data *ad);
void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
int family);
void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
int family);
void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
struct sk_security_struct *newssec);
int selinux_netlbl_inode_permission(struct inode *inode, int mask);
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
return;
}
static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u32 base_sid,
u32 *sid)
{
*sid = SECSID_NULL;
return 0;
}
static inline int selinux_netlbl_socket_post_create(struct socket *sock)
{
return 0;
}
static inline void selinux_netlbl_sock_graft(struct sock *sk,
struct socket *sock)
{
return;
}
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
struct avc_audit_data *ad)
{
return 0;
}
static inline void selinux_netlbl_sk_security_reset(
struct sk_security_struct *ssec,
int family)
{
return;
}
static inline void selinux_netlbl_sk_security_init(
struct sk_security_struct *ssec,
int family)
{
return;
}
static inline void selinux_netlbl_sk_security_clone(
struct sk_security_struct *ssec,
struct sk_security_struct *newssec)
{
return;
}
static inline int selinux_netlbl_inode_permission(struct inode *inode,
int mask)
{
return 0;
}
static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname)
{
return 0;
}
#endif /* CONFIG_NETLABEL */
#endif

View File

@@ -0,0 +1,75 @@
/*
* SELinux support for the XFRM LSM hooks
*
* Author : Trent Jaeger, <jaegert@us.ibm.com>
* Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.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_policy_delete(struct xfrm_policy *xp);
int selinux_xfrm_state_alloc(struct xfrm_state *x,
struct xfrm_user_sec_ctx *sec_ctx, u32 secid);
void selinux_xfrm_state_free(struct xfrm_state *x);
int selinux_xfrm_state_delete(struct xfrm_state *x);
int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp, struct flowi *fl);
/*
* 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;
}
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
struct avc_audit_data *ad);
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad, u8 proto);
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
static inline void selinux_xfrm_notify_policyload(void)
{
atomic_inc(&flow_cache_genid);
}
#else
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad)
{
return 0;
}
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad, u8 proto)
{
return 0;
}
static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
{
*sid = SECSID_NULL;
return 0;
}
static inline void selinux_xfrm_notify_policyload(void)
{
}
#endif
static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
{
int err = selinux_xfrm_decode_session(skb, sid, 0);
BUG_ON(err);
}
#endif /* _SELINUX_XFRM_H_ */

269
security/selinux/netif.c Normal file
View File

@@ -0,0 +1,269 @@
/*
* Network interface table.
*
* Network interfaces (devices) do not have a security field, so we
* maintain a table associating each interface with a SID.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
#include "security.h"
#include "objsec.h"
#include "netif.h"
#define SEL_NETIF_HASH_SIZE 64
#define SEL_NETIF_HASH_MAX 1024
#undef DEBUG
#ifdef DEBUG
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
struct sel_netif
{
struct list_head list;
struct netif_security_struct nsec;
struct rcu_head rcu_head;
};
static u32 sel_netif_total;
static LIST_HEAD(sel_netif_list);
static DEFINE_SPINLOCK(sel_netif_lock);
static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
static inline u32 sel_netif_hasfn(struct net_device *dev)
{
return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1));
}
/*
* All of the devices should normally fit in the hash, so we optimize
* for that case.
*/
static inline struct sel_netif *sel_netif_find(struct net_device *dev)
{
struct list_head *pos;
int idx = sel_netif_hasfn(dev);
__list_for_each_rcu(pos, &sel_netif_hash[idx]) {
struct sel_netif *netif = list_entry(pos,
struct sel_netif, list);
if (likely(netif->nsec.dev == dev))
return netif;
}
return NULL;
}
static int sel_netif_insert(struct sel_netif *netif)
{
int idx, ret = 0;
if (sel_netif_total >= SEL_NETIF_HASH_MAX) {
ret = -ENOSPC;
goto out;
}
idx = sel_netif_hasfn(netif->nsec.dev);
list_add_rcu(&netif->list, &sel_netif_hash[idx]);
sel_netif_total++;
out:
return ret;
}
static void sel_netif_free(struct rcu_head *p)
{
struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head);
DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
kfree(netif);
}
static void sel_netif_destroy(struct sel_netif *netif)
{
DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
list_del_rcu(&netif->list);
sel_netif_total--;
call_rcu(&netif->rcu_head, sel_netif_free);
}
static struct sel_netif *sel_netif_lookup(struct net_device *dev)
{
int ret;
struct sel_netif *netif, *new;
struct netif_security_struct *nsec;
netif = sel_netif_find(dev);
if (likely(netif != NULL))
goto out;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
netif = ERR_PTR(-ENOMEM);
goto out;
}
nsec = &new->nsec;
ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid);
if (ret < 0) {
kfree(new);
netif = ERR_PTR(ret);
goto out;
}
nsec->dev = dev;
spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(dev);
if (netif) {
spin_unlock_bh(&sel_netif_lock);
kfree(new);
goto out;
}
ret = sel_netif_insert(new);
spin_unlock_bh(&sel_netif_lock);
if (ret) {
kfree(new);
netif = ERR_PTR(ret);
goto out;
}
netif = new;
DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name,
nsec->if_sid, nsec->msg_sid);
out:
return netif;
}
static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out)
{
if (if_sid_out)
*if_sid_out = if_sid_in;
if (msg_sid_out)
*msg_sid_out = msg_sid_in;
}
static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
{
int ret = 0;
u32 tmp_if_sid, tmp_msg_sid;
ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid);
if (!ret)
sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid);
return ret;
}
int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
{
int ret = 0;
struct sel_netif *netif;
rcu_read_lock();
netif = sel_netif_lookup(dev);
if (IS_ERR(netif)) {
rcu_read_unlock();
ret = sel_netif_sids_slow(dev, if_sid, msg_sid);
goto out;
}
sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid);
rcu_read_unlock();
out:
return ret;
}
static void sel_netif_kill(struct net_device *dev)
{
struct sel_netif *netif;
spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(dev);
if (netif)
sel_netif_destroy(netif);
spin_unlock_bh(&sel_netif_lock);
}
static void sel_netif_flush(void)
{
int idx;
spin_lock_bh(&sel_netif_lock);
for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
struct sel_netif *netif;
list_for_each_entry(netif, &sel_netif_hash[idx], list)
sel_netif_destroy(netif);
}
spin_unlock_bh(&sel_netif_lock);
}
static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid,
u16 class, u32 perms, u32 *retained)
{
if (event == AVC_CALLBACK_RESET) {
sel_netif_flush();
synchronize_net();
}
return 0;
}
static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
if (event == NETDEV_DOWN)
sel_netif_kill(dev);
return NOTIFY_DONE;
}
static struct notifier_block sel_netif_netdev_notifier = {
.notifier_call = sel_netif_netdev_notifier_handler,
};
static __init int sel_netif_init(void)
{
int i, err = 0;
if (!selinux_enabled)
goto out;
for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
INIT_LIST_HEAD(&sel_netif_hash[i]);
register_netdevice_notifier(&sel_netif_netdev_notifier);
err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET,
SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
if (err)
panic("avc_add_callback() failed, error %d\n", err);
out:
return err;
}
__initcall(sel_netif_init);

115
security/selinux/netlink.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* Netlink event notifications for SELinux.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/selinux_netlink.h>
static struct sock *selnl;
static int selnl_msglen(int msgtype)
{
int ret = 0;
switch (msgtype) {
case SELNL_MSG_SETENFORCE:
ret = sizeof(struct selnl_msg_setenforce);
break;
case SELNL_MSG_POLICYLOAD:
ret = sizeof(struct selnl_msg_policyload);
break;
default:
BUG();
}
return ret;
}
static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data)
{
switch (msgtype) {
case SELNL_MSG_SETENFORCE: {
struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
memset(msg, 0, len);
msg->val = *((int *)data);
break;
}
case SELNL_MSG_POLICYLOAD: {
struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
memset(msg, 0, len);
msg->seqno = *((u32 *)data);
break;
}
default:
BUG();
}
}
static void selnl_notify(int msgtype, void *data)
{
int len;
unsigned char *tmp;
struct sk_buff *skb;
struct nlmsghdr *nlh;
len = selnl_msglen(msgtype);
skb = alloc_skb(NLMSG_SPACE(len), GFP_USER);
if (!skb)
goto oom;
tmp = skb->tail;
nlh = NLMSG_PUT(skb, 0, 0, msgtype, len);
selnl_add_payload(nlh, len, msgtype, data);
nlh->nlmsg_len = skb->tail - tmp;
NETLINK_CB(skb).dst_group = SELNLGRP_AVC;
netlink_broadcast(selnl, skb, 0, SELNLGRP_AVC, GFP_USER);
out:
return;
nlmsg_failure:
kfree_skb(skb);
oom:
printk(KERN_ERR "SELinux: OOM in %s\n", __FUNCTION__);
goto out;
}
void selnl_notify_setenforce(int val)
{
selnl_notify(SELNL_MSG_SETENFORCE, &val);
}
void selnl_notify_policyload(u32 seqno)
{
selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
}
static int __init selnl_init(void)
{
selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL,
THIS_MODULE);
if (selnl == NULL)
panic("SELinux: Cannot create netlink socket.");
netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
return 0;
}
__initcall(selnl_init);

175
security/selinux/nlmsgtab.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* Netlink message type permission tables, for user generated messages.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
*
* 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.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
#include <linux/netfilter_ipv4/ip_queue.h>
#include <linux/inet_diag.h>
#include <linux/xfrm.h>
#include <linux/audit.h>
#include "flask.h"
#include "av_permissions.h"
struct nlmsg_perm
{
u16 nlmsg_type;
u32 perm;
};
static struct nlmsg_perm nlmsg_route_perms[] =
{
{ RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
};
static struct nlmsg_perm nlmsg_firewall_perms[] =
{
{ IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
{ IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
};
static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
{
{ TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
{ DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
};
static struct nlmsg_perm nlmsg_xfrm_perms[] =
{
{ XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
{ XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ 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[] =
{
{ AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
{ AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ 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 },
};
static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize)
{
int i, err = -EINVAL;
for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
if (nlmsg_type == tab[i].nlmsg_type) {
*perm = tab[i].perm;
err = 0;
break;
}
return err;
}
int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
{
int err = 0;
switch (sclass) {
case SECCLASS_NETLINK_ROUTE_SOCKET:
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
sizeof(nlmsg_route_perms));
break;
case SECCLASS_NETLINK_FIREWALL_SOCKET:
case SECCLASS_NETLINK_IP6FW_SOCKET:
err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms,
sizeof(nlmsg_firewall_perms));
break;
case SECCLASS_NETLINK_TCPDIAG_SOCKET:
err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
sizeof(nlmsg_tcpdiag_perms));
break;
case SECCLASS_NETLINK_XFRM_SOCKET:
err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
sizeof(nlmsg_xfrm_perms));
break;
case SECCLASS_NETLINK_AUDIT_SOCKET:
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,
sizeof(nlmsg_audit_perms));
}
break;
/* No messaging from userspace, or class unknown/unhandled */
default:
err = -ENOENT;
break;
}
return err;
}

1386
security/selinux/selinuxfs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
#
# Makefile for building the SELinux security server as part of the kernel tree.
#
EXTRA_CFLAGS += -Isecurity/selinux/include
obj-y := ss.o
ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o

454
security/selinux/ss/avtab.c Normal file
View File

@@ -0,0 +1,454 @@
/*
* Implementation of the access vector table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
*
* Added conditional policy language extensions
*
* Copyright (C) 2003 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/errno.h>
#include "avtab.h"
#include "policydb.h"
#define AVTAB_HASH(keyp) \
((keyp->target_class + \
(keyp->target_type << 2) + \
(keyp->source_type << 9)) & \
AVTAB_HASH_MASK)
static struct kmem_cache *avtab_node_cachep;
static struct avtab_node*
avtab_insert_node(struct avtab *h, int hvalue,
struct avtab_node * prev, struct avtab_node * cur,
struct avtab_key *key, struct avtab_datum *datum)
{
struct avtab_node * newnode;
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
if (newnode == NULL)
return NULL;
newnode->key = *key;
newnode->datum = *datum;
if (prev) {
newnode->next = prev->next;
prev->next = newnode;
} else {
newnode->next = h->htable[hvalue];
h->htable[hvalue] = newnode;
}
h->nel++;
return newnode;
}
static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
{
int hvalue;
struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
return -EINVAL;
hvalue = AVTAB_HASH(key);
for (prev = NULL, cur = h->htable[hvalue];
cur;
prev = cur, cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
(specified & cur->key.specified))
return -EEXIST;
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type < cur->key.target_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class < cur->key.target_class)
break;
}
newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
if(!newnode)
return -ENOMEM;
return 0;
}
/* Unlike avtab_insert(), this function allow multiple insertions of the same
* key/specified mask into the table, as needed by the conditional avtab.
* It also returns a pointer to the node inserted.
*/
struct avtab_node *
avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum)
{
int hvalue;
struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
return NULL;
hvalue = AVTAB_HASH(key);
for (prev = NULL, cur = h->htable[hvalue];
cur;
prev = cur, cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
(specified & cur->key.specified))
break;
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type < cur->key.target_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class < cur->key.target_class)
break;
}
newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
return newnode;
}
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
{
int hvalue;
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
return NULL;
hvalue = AVTAB_HASH(key);
for (cur = h->htable[hvalue]; cur; cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
(specified & cur->key.specified))
return &cur->datum;
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type < cur->key.target_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class < cur->key.target_class)
break;
}
return NULL;
}
/* This search function returns a node pointer, and can be used in
* conjunction with avtab_search_next_node()
*/
struct avtab_node*
avtab_search_node(struct avtab *h, struct avtab_key *key)
{
int hvalue;
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
return NULL;
hvalue = AVTAB_HASH(key);
for (cur = h->htable[hvalue]; cur; cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
(specified & cur->key.specified))
return cur;
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type < cur->key.target_type)
break;
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class < cur->key.target_class)
break;
}
return NULL;
}
struct avtab_node*
avtab_search_node_next(struct avtab_node *node, int specified)
{
struct avtab_node *cur;
if (!node)
return NULL;
specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
for (cur = node->next; cur; cur = cur->next) {
if (node->key.source_type == cur->key.source_type &&
node->key.target_type == cur->key.target_type &&
node->key.target_class == cur->key.target_class &&
(specified & cur->key.specified))
return cur;
if (node->key.source_type < cur->key.source_type)
break;
if (node->key.source_type == cur->key.source_type &&
node->key.target_type < cur->key.target_type)
break;
if (node->key.source_type == cur->key.source_type &&
node->key.target_type == cur->key.target_type &&
node->key.target_class < cur->key.target_class)
break;
}
return NULL;
}
void avtab_destroy(struct avtab *h)
{
int i;
struct avtab_node *cur, *temp;
if (!h || !h->htable)
return;
for (i = 0; i < AVTAB_SIZE; i++) {
cur = h->htable[i];
while (cur != NULL) {
temp = cur;
cur = cur->next;
kmem_cache_free(avtab_node_cachep, temp);
}
h->htable[i] = NULL;
}
vfree(h->htable);
h->htable = NULL;
}
int avtab_init(struct avtab *h)
{
int i;
h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE);
if (!h->htable)
return -ENOMEM;
for (i = 0; i < AVTAB_SIZE; i++)
h->htable[i] = NULL;
h->nel = 0;
return 0;
}
void avtab_hash_eval(struct avtab *h, char *tag)
{
int i, chain_len, slots_used, max_chain_len;
struct avtab_node *cur;
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVTAB_SIZE; i++) {
cur = h->htable[i];
if (cur) {
slots_used++;
chain_len = 0;
while (cur) {
chain_len++;
cur = cur->next;
}
if (chain_len > max_chain_len)
max_chain_len = chain_len;
}
}
printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest "
"chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE,
max_chain_len);
}
static uint16_t spec_order[] = {
AVTAB_ALLOWED,
AVTAB_AUDITDENY,
AVTAB_AUDITALLOW,
AVTAB_TRANSITION,
AVTAB_CHANGE,
AVTAB_MEMBER
};
int avtab_read_item(void *fp, u32 vers, struct avtab *a,
int (*insertf)(struct avtab *a, struct avtab_key *k,
struct avtab_datum *d, void *p),
void *p)
{
__le16 buf16[4];
u16 enabled;
__le32 buf32[7];
u32 items, items2, val;
struct avtab_key key;
struct avtab_datum datum;
int i, rc;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
if (vers < POLICYDB_VERSION_AVTAB) {
rc = next_entry(buf32, fp, sizeof(u32));
if (rc < 0) {
printk(KERN_ERR "security: avtab: truncated entry\n");
return -1;
}
items2 = le32_to_cpu(buf32[0]);
if (items2 > ARRAY_SIZE(buf32)) {
printk(KERN_ERR "security: avtab: entry overflow\n");
return -1;
}
rc = next_entry(buf32, fp, sizeof(u32)*items2);
if (rc < 0) {
printk(KERN_ERR "security: avtab: truncated entry\n");
return -1;
}
items = 0;
val = le32_to_cpu(buf32[items++]);
key.source_type = (u16)val;
if (key.source_type != val) {
printk("security: avtab: truncated source type\n");
return -1;
}
val = le32_to_cpu(buf32[items++]);
key.target_type = (u16)val;
if (key.target_type != val) {
printk("security: avtab: truncated target type\n");
return -1;
}
val = le32_to_cpu(buf32[items++]);
key.target_class = (u16)val;
if (key.target_class != val) {
printk("security: avtab: truncated target class\n");
return -1;
}
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
printk("security: avtab: null entry\n");
return -1;
}
if ((val & AVTAB_AV) &&
(val & AVTAB_TYPE)) {
printk("security: avtab: entry has both access vectors and types\n");
return -1;
}
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++]);
rc = insertf(a, &key, &datum, p);
if (rc) return rc;
}
}
if (items != items2) {
printk("security: avtab: entry only had %d items, expected %d\n", items2, items);
return -1;
}
return 0;
}
rc = next_entry(buf16, fp, sizeof(u16)*4);
if (rc < 0) {
printk("security: avtab: truncated entry\n");
return -1;
}
items = 0;
key.source_type = le16_to_cpu(buf16[items++]);
key.target_type = le16_to_cpu(buf16[items++]);
key.target_class = le16_to_cpu(buf16[items++]);
key.specified = le16_to_cpu(buf16[items++]);
rc = next_entry(buf32, fp, sizeof(u32));
if (rc < 0) {
printk("security: avtab: truncated entry\n");
return -1;
}
datum.data = le32_to_cpu(*buf32);
return insertf(a, &key, &datum, p);
}
static int avtab_insertf(struct avtab *a, struct avtab_key *k,
struct avtab_datum *d, void *p)
{
return avtab_insert(a, k, d);
}
int avtab_read(struct avtab *a, void *fp, u32 vers)
{
int rc;
__le32 buf[1];
u32 nel, i;
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0) {
printk(KERN_ERR "security: avtab: truncated table\n");
goto bad;
}
nel = le32_to_cpu(buf[0]);
if (!nel) {
printk(KERN_ERR "security: avtab: table is empty\n");
rc = -EINVAL;
goto bad;
}
for (i = 0; i < nel; i++) {
rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL);
if (rc) {
if (rc == -ENOMEM)
printk(KERN_ERR "security: avtab: out of memory\n");
else if (rc == -EEXIST)
printk(KERN_ERR "security: avtab: duplicate entry\n");
else
rc = -EINVAL;
goto bad;
}
}
rc = 0;
out:
return rc;
bad:
avtab_destroy(a);
goto out;
}
void avtab_cache_init(void)
{
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
0, SLAB_PANIC, NULL, NULL);
}
void avtab_cache_destroy(void)
{
kmem_cache_destroy (avtab_node_cachep);
}

View File

@@ -0,0 +1,84 @@
/*
* An access vector table (avtab) is a hash table
* of access vectors and transition types indexed
* by a type pair and a class. An access vector
* table is used to represent the type enforcement
* tables.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
*
* Added conditional policy language extensions
*
* Copyright (C) 2003 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#ifndef _SS_AVTAB_H_
#define _SS_AVTAB_H_
struct avtab_key {
u16 source_type; /* source type */
u16 target_type; /* target type */
u16 target_class; /* target object class */
#define AVTAB_ALLOWED 1
#define AVTAB_AUDITALLOW 2
#define AVTAB_AUDITDENY 4
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
#define AVTAB_TRANSITION 16
#define AVTAB_MEMBER 32
#define AVTAB_CHANGE 64
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
u16 specified; /* what field is specified */
};
struct avtab_datum {
u32 data; /* access vector or type value */
};
struct avtab_node {
struct avtab_key key;
struct avtab_datum datum;
struct avtab_node *next;
};
struct avtab {
struct avtab_node **htable;
u32 nel; /* number of elements */
};
int avtab_init(struct avtab *);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h);
void avtab_hash_eval(struct avtab *h, char *tag);
int avtab_read_item(void *fp, uint32_t vers, struct avtab *a,
int (*insert)(struct avtab *a, struct avtab_key *k,
struct avtab_datum *d, void *p),
void *p);
int avtab_read(struct avtab *a, void *fp, u32 vers);
struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
struct avtab_datum *datum);
struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
void avtab_cache_init(void);
void avtab_cache_destroy(void);
#define AVTAB_HASH_BITS 15
#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS)
#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1)
#define AVTAB_SIZE AVTAB_HASH_BUCKETS
#endif /* _SS_AVTAB_H_ */

View File

@@ -0,0 +1,509 @@
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Frank Mayer <mayerf@tresys.com>
*
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <linux/slab.h>
#include "security.h"
#include "conditional.h"
/*
* cond_evaluate_expr evaluates a conditional expr
* in reverse polish notation. It returns true (1), false (0),
* or undefined (-1). Undefined occurs when the expression
* exceeds the stack depth of COND_EXPR_MAXDEPTH.
*/
static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
{
struct cond_expr *cur;
int s[COND_EXPR_MAXDEPTH];
int sp = -1;
for (cur = expr; cur != NULL; cur = cur->next) {
switch (cur->expr_type) {
case COND_BOOL:
if (sp == (COND_EXPR_MAXDEPTH - 1))
return -1;
sp++;
s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;
break;
case COND_NOT:
if (sp < 0)
return -1;
s[sp] = !s[sp];
break;
case COND_OR:
if (sp < 1)
return -1;
sp--;
s[sp] |= s[sp + 1];
break;
case COND_AND:
if (sp < 1)
return -1;
sp--;
s[sp] &= s[sp + 1];
break;
case COND_XOR:
if (sp < 1)
return -1;
sp--;
s[sp] ^= s[sp + 1];
break;
case COND_EQ:
if (sp < 1)
return -1;
sp--;
s[sp] = (s[sp] == s[sp + 1]);
break;
case COND_NEQ:
if (sp < 1)
return -1;
sp--;
s[sp] = (s[sp] != s[sp + 1]);
break;
default:
return -1;
}
}
return s[0];
}
/*
* evaluate_cond_node evaluates the conditional stored in
* a struct cond_node and if the result is different than the
* current state of the node it sets the rules in the true/false
* list appropriately. If the result of the expression is undefined
* all of the rules are disabled for safety.
*/
int evaluate_cond_node(struct policydb *p, struct cond_node *node)
{
int new_state;
struct cond_av_list* cur;
new_state = cond_evaluate_expr(p, node->expr);
if (new_state != node->cur_state) {
node->cur_state = new_state;
if (new_state == -1)
printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n");
/* turn the rules on or off */
for (cur = node->true_list; cur != NULL; cur = cur->next) {
if (new_state <= 0) {
cur->node->key.specified &= ~AVTAB_ENABLED;
} else {
cur->node->key.specified |= AVTAB_ENABLED;
}
}
for (cur = node->false_list; cur != NULL; cur = cur->next) {
/* -1 or 1 */
if (new_state) {
cur->node->key.specified &= ~AVTAB_ENABLED;
} else {
cur->node->key.specified |= AVTAB_ENABLED;
}
}
}
return 0;
}
int cond_policydb_init(struct policydb *p)
{
p->bool_val_to_struct = NULL;
p->cond_list = NULL;
if (avtab_init(&p->te_cond_avtab))
return -1;
return 0;
}
static void cond_av_list_destroy(struct cond_av_list *list)
{
struct cond_av_list *cur, *next;
for (cur = list; cur != NULL; cur = next) {
next = cur->next;
/* the avtab_ptr_t node is destroy by the avtab */
kfree(cur);
}
}
static void cond_node_destroy(struct cond_node *node)
{
struct cond_expr *cur_expr, *next_expr;
for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) {
next_expr = cur_expr->next;
kfree(cur_expr);
}
cond_av_list_destroy(node->true_list);
cond_av_list_destroy(node->false_list);
kfree(node);
}
static void cond_list_destroy(struct cond_node *list)
{
struct cond_node *next, *cur;
if (list == NULL)
return;
for (cur = list; cur != NULL; cur = next) {
next = cur->next;
cond_node_destroy(cur);
}
}
void cond_policydb_destroy(struct policydb *p)
{
kfree(p->bool_val_to_struct);
avtab_destroy(&p->te_cond_avtab);
cond_list_destroy(p->cond_list);
}
int cond_init_bool_indexes(struct policydb *p)
{
kfree(p->bool_val_to_struct);
p->bool_val_to_struct = (struct cond_bool_datum**)
kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL);
if (!p->bool_val_to_struct)
return -1;
return 0;
}
int cond_destroy_bool(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
int cond_index_bool(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cond_bool_datum *booldatum;
booldatum = datum;
p = datap;
if (!booldatum->value || booldatum->value > p->p_bools.nprim)
return -EINVAL;
p->p_bool_val_to_name[booldatum->value - 1] = key;
p->bool_val_to_struct[booldatum->value -1] = booldatum;
return 0;
}
static int bool_isvalid(struct cond_bool_datum *b)
{
if (!(b->state == 0 || b->state == 1))
return 0;
return 1;
}
int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
u32 len;
int rc;
booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
if (!booldatum)
return -1;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
booldatum->state = le32_to_cpu(buf[1]);
if (!bool_isvalid(booldatum))
goto err;
len = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto err;
rc = next_entry(key, fp, len);
if (rc < 0)
goto err;
key[len] = 0;
if (hashtab_insert(h, key, booldatum))
goto err;
return 0;
err:
cond_destroy_bool(key, booldatum, NULL);
return -1;
}
struct cond_insertf_data
{
struct policydb *p;
struct cond_av_list *other;
struct cond_av_list *head;
struct cond_av_list *tail;
};
static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)
{
struct cond_insertf_data *data = ptr;
struct policydb *p = data->p;
struct cond_av_list *other = data->other, *list, *cur;
struct avtab_node *node_ptr;
u8 found;
/*
* For type rules we have to make certain there aren't any
* conflicting rules by searching the te_avtab and the
* cond_te_avtab.
*/
if (k->specified & AVTAB_TYPE) {
if (avtab_search(&p->te_avtab, k)) {
printk("security: type rule already exists outside of a conditional.");
goto err;
}
/*
* If we are reading the false list other will be a pointer to
* the true list. We can have duplicate entries if there is only
* 1 other entry and it is in our true list.
*
* If we are reading the true list (other == NULL) there shouldn't
* be any other entries.
*/
if (other) {
node_ptr = avtab_search_node(&p->te_cond_avtab, k);
if (node_ptr) {
if (avtab_search_node_next(node_ptr, k->specified)) {
printk("security: too many conflicting type rules.");
goto err;
}
found = 0;
for (cur = other; cur != NULL; cur = cur->next) {
if (cur->node == node_ptr) {
found = 1;
break;
}
}
if (!found) {
printk("security: conflicting type rules.\n");
goto err;
}
}
} else {
if (avtab_search(&p->te_cond_avtab, k)) {
printk("security: conflicting type rules when adding type rule for true.\n");
goto err;
}
}
}
node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
if (!node_ptr) {
printk("security: could not insert rule.");
goto err;
}
list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);
if (!list)
goto err;
list->node = node_ptr;
if (!data->head)
data->head = list;
else
data->tail->next = list;
data->tail = list;
return 0;
err:
cond_av_list_destroy(data->head);
data->head = NULL;
return -1;
}
static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
{
int i, rc;
__le32 buf[1];
u32 len;
struct cond_insertf_data data;
*ret_list = NULL;
len = 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
return -1;
len = le32_to_cpu(buf[0]);
if (len == 0) {
return 0;
}
data.p = p;
data.other = other;
data.head = NULL;
data.tail = NULL;
for (i = 0; i < len; i++) {
rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data);
if (rc)
return rc;
}
*ret_list = data.head;
return 0;
}
static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
{
if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
printk("security: conditional expressions uses unknown operator.\n");
return 0;
}
if (expr->bool > p->p_bools.nprim) {
printk("security: conditional expressions uses unknown bool.\n");
return 0;
}
return 1;
}
static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
{
__le32 buf[2];
u32 len, i;
int rc;
struct cond_expr *expr = NULL, *last = NULL;
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
return -1;
node->cur_state = le32_to_cpu(buf[0]);
len = 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
return -1;
/* expr */
len = le32_to_cpu(buf[0]);
for (i = 0; i < len; i++ ) {
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc < 0)
goto err;
expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);
if (!expr) {
goto err;
}
expr->expr_type = le32_to_cpu(buf[0]);
expr->bool = le32_to_cpu(buf[1]);
if (!expr_isvalid(p, expr)) {
kfree(expr);
goto err;
}
if (i == 0) {
node->expr = expr;
} else {
last->next = expr;
}
last = expr;
}
if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0)
goto err;
if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0)
goto err;
return 0;
err:
cond_node_destroy(node);
return -1;
}
int cond_read_list(struct policydb *p, void *fp)
{
struct cond_node *node, *last = NULL;
__le32 buf[1];
u32 i, len;
int rc;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
return -1;
len = le32_to_cpu(buf[0]);
for (i = 0; i < len; i++) {
node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);
if (!node)
goto err;
if (cond_read_node(p, node, fp) != 0)
goto err;
if (i == 0) {
p->cond_list = node;
} else {
last->next = node;
}
last = node;
}
return 0;
err:
cond_list_destroy(p->cond_list);
p->cond_list = NULL;
return -1;
}
/* Determine whether additional permissions are granted by the conditional
* av table, and if so, add them to the result
*/
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
{
struct avtab_node *node;
if(!ctab || !key || !avd)
return;
for(node = avtab_search_node(ctab, key); node != NULL;
node = avtab_search_node_next(node, key->specified)) {
if ( (u16) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
avd->allowed |= node->datum.data;
if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
/* Since a '0' in an auditdeny mask represents a
* permission we do NOT want to audit (dontaudit), we use
* the '&' operand to ensure that all '0's in the mask
* are retained (much unlike the allow and auditallow cases).
*/
avd->auditdeny &= node->datum.data;
if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
avd->auditallow |= node->datum.data;
}
return;
}

View File

@@ -0,0 +1,77 @@
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Frank Mayer <mayerf@tresys.com>
*
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#ifndef _CONDITIONAL_H_
#define _CONDITIONAL_H_
#include "avtab.h"
#include "symtab.h"
#include "policydb.h"
#define COND_EXPR_MAXDEPTH 10
/*
* A conditional expression is a list of operators and operands
* in reverse polish notation.
*/
struct cond_expr {
#define COND_BOOL 1 /* plain bool */
#define COND_NOT 2 /* !bool */
#define COND_OR 3 /* bool || bool */
#define COND_AND 4 /* bool && bool */
#define COND_XOR 5 /* bool ^ bool */
#define COND_EQ 6 /* bool == bool */
#define COND_NEQ 7 /* bool != bool */
#define COND_LAST 8
__u32 expr_type;
__u32 bool;
struct cond_expr *next;
};
/*
* Each cond_node contains a list of rules to be enabled/disabled
* depending on the current value of the conditional expression. This
* struct is for that list.
*/
struct cond_av_list {
struct avtab_node *node;
struct cond_av_list *next;
};
/*
* A cond node represents a conditional block in a policy. It
* contains a conditional expression, the current state of the expression,
* two lists of rules to enable/disable depending on the value of the
* expression (the true list corresponds to if and the false list corresponds
* to else)..
*/
struct cond_node {
int cur_state;
struct cond_expr *expr;
struct cond_av_list *true_list;
struct cond_av_list *false_list;
struct cond_node *next;
};
int cond_policydb_init(struct policydb* p);
void cond_policydb_destroy(struct policydb* p);
int cond_init_bool_indexes(struct policydb* p);
int cond_destroy_bool(void *key, void *datum, void *p);
int cond_index_bool(void *key, void *datum, void *datap);
int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp);
int cond_read_list(struct policydb *p, void *fp);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
#endif /* _CONDITIONAL_H_ */

View File

@@ -0,0 +1,61 @@
/*
* A constraint is a condition that must be satisfied in
* order for one or more permissions to be granted.
* Constraints are used to impose additional restrictions
* beyond the type-based rules in `te' or the role-based
* transition rules in `rbac'. Constraints are typically
* used to prevent a process from transitioning to a new user
* identity or role unless it is in a privileged type.
* Constraints are likewise typically used to prevent a
* process from labeling an object with a different user
* identity.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_CONSTRAINT_H_
#define _SS_CONSTRAINT_H_
#include "ebitmap.h"
#define CEXPR_MAXDEPTH 5
struct constraint_expr {
#define CEXPR_NOT 1 /* not expr */
#define CEXPR_AND 2 /* expr and expr */
#define CEXPR_OR 3 /* expr or expr */
#define CEXPR_ATTR 4 /* attr op attr */
#define CEXPR_NAMES 5 /* attr op names */
u32 expr_type; /* expression type */
#define CEXPR_USER 1 /* user */
#define CEXPR_ROLE 2 /* role */
#define CEXPR_TYPE 4 /* type */
#define CEXPR_TARGET 8 /* target if set, source otherwise */
#define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */
#define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */
#define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */
#define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */
#define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */
#define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */
#define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */
u32 attr; /* attribute */
#define CEXPR_EQ 1 /* == or eq */
#define CEXPR_NEQ 2 /* != */
#define CEXPR_DOM 3 /* dom */
#define CEXPR_DOMBY 4 /* domby */
#define CEXPR_INCOMP 5 /* incomp */
u32 op; /* operator */
struct ebitmap names; /* names */
struct constraint_expr *next; /* next expression */
};
struct constraint_node {
u32 permissions; /* constrained permissions */
struct constraint_expr *expr; /* constraint on permissions */
struct constraint_node *next; /* next constraint */
};
#endif /* _SS_CONSTRAINT_H_ */

View File

@@ -0,0 +1,130 @@
/*
* A security context is a set of security attributes
* associated with each subject and object controlled
* by the security policy. Security contexts are
* externally represented as variable-length strings
* that can be interpreted by a user or application
* with an understanding of the security policy.
* Internally, the security server uses a simple
* structure. This structure is private to the
* security server and can be changed without affecting
* clients of the security server.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_CONTEXT_H_
#define _SS_CONTEXT_H_
#include "ebitmap.h"
#include "mls_types.h"
#include "security.h"
/*
* A security context consists of an authenticated user
* identity, a role, a type and a MLS range.
*/
struct context {
u32 user;
u32 role;
u32 type;
struct mls_range range;
};
static inline void mls_context_init(struct context *c)
{
memset(&c->range, 0, sizeof(c->range));
}
static inline int mls_context_cpy(struct context *dst, struct context *src)
{
int rc;
if (!selinux_mls_enabled)
return 0;
dst->range.level[0].sens = src->range.level[0].sens;
rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
if (rc)
goto out;
dst->range.level[1].sens = src->range.level[1].sens;
rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat);
if (rc)
ebitmap_destroy(&dst->range.level[0].cat);
out:
return rc;
}
/*
* Sets both levels in the MLS range of 'dst' to the low level of 'src'.
*/
static inline int mls_context_cpy_low(struct context *dst, struct context *src)
{
int rc;
if (!selinux_mls_enabled)
return 0;
dst->range.level[0].sens = src->range.level[0].sens;
rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
if (rc)
goto out;
dst->range.level[1].sens = src->range.level[0].sens;
rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat);
if (rc)
ebitmap_destroy(&dst->range.level[0].cat);
out:
return rc;
}
static inline int mls_context_cmp(struct context *c1, struct context *c2)
{
if (!selinux_mls_enabled)
return 1;
return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) &&
(c1->range.level[1].sens == c2->range.level[1].sens) &&
ebitmap_cmp(&c1->range.level[1].cat,&c2->range.level[1].cat));
}
static inline void mls_context_destroy(struct context *c)
{
if (!selinux_mls_enabled)
return;
ebitmap_destroy(&c->range.level[0].cat);
ebitmap_destroy(&c->range.level[1].cat);
mls_context_init(c);
}
static inline void context_init(struct context *c)
{
memset(c, 0, sizeof(*c));
}
static inline int context_cpy(struct context *dst, struct context *src)
{
dst->user = src->user;
dst->role = src->role;
dst->type = src->type;
return mls_context_cpy(dst, src);
}
static inline void context_destroy(struct context *c)
{
c->user = c->role = c->type = 0;
mls_context_destroy(c);
}
static inline int context_cmp(struct context *c1, struct context *c2)
{
return ((c1->user == c2->user) &&
(c1->role == c2->role) &&
(c1->type == c2->type) &&
mls_context_cmp(c1, c2));
}
#endif /* _SS_CONTEXT_H_ */

View File

@@ -0,0 +1,415 @@
/*
* Implementation of the extensible bitmap type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support to import/export the NetLabel category bitmap
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <net/netlabel.h>
#include "ebitmap.h"
#include "policydb.h"
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2;
if (e1->highbit != e2->highbit)
return 0;
n1 = e1->node;
n2 = e2->node;
while (n1 && n2 &&
(n1->startbit == n2->startbit) &&
(n1->map == n2->map)) {
n1 = n1->next;
n2 = n2->next;
}
if (n1 || n2)
return 0;
return 1;
}
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
{
struct ebitmap_node *n, *new, *prev;
ebitmap_init(dst);
n = src->node;
prev = NULL;
while (n) {
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
ebitmap_destroy(dst);
return -ENOMEM;
}
new->startbit = n->startbit;
new->map = n->map;
new->next = NULL;
if (prev)
prev->next = new;
else
dst->node = new;
prev = new;
n = n->next;
}
dst->highbit = src->highbit;
return 0;
}
#ifdef CONFIG_NETLABEL
/**
* ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap
* @ebmap: the ebitmap to export
* @catmap: the NetLabel category bitmap
*
* Description:
* Export a SELinux extensibile bitmap into a NetLabel category bitmap.
* Returns zero on success, negative values on error.
*
*/
int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap **catmap)
{
struct ebitmap_node *e_iter = ebmap->node;
struct netlbl_lsm_secattr_catmap *c_iter;
u32 cmap_idx;
/* This function is a much simpler because SELinux's MAPTYPE happens
* to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is
* changed from a u64 this function will most likely need to be changed
* as well. It's not ideal but I think the tradeoff in terms of
* neatness and speed is worth it. */
if (e_iter == NULL) {
*catmap = NULL;
return 0;
}
c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (c_iter == NULL)
return -ENOMEM;
*catmap = c_iter;
c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);
while (e_iter != NULL) {
if (e_iter->startbit >=
(c_iter->startbit + NETLBL_CATMAP_SIZE)) {
c_iter->next = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (c_iter->next == NULL)
goto netlbl_export_failure;
c_iter = c_iter->next;
c_iter->startbit = e_iter->startbit &
~(NETLBL_CATMAP_SIZE - 1);
}
cmap_idx = (e_iter->startbit - c_iter->startbit) /
NETLBL_CATMAP_MAPSIZE;
c_iter->bitmap[cmap_idx] = e_iter->map;
e_iter = e_iter->next;
}
return 0;
netlbl_export_failure:
netlbl_secattr_catmap_free(*catmap);
return -ENOMEM;
}
/**
* ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap
* @ebmap: the ebitmap to export
* @catmap: the NetLabel category bitmap
*
* Description:
* Import a NetLabel category bitmap into a SELinux extensibile bitmap.
* Returns zero on success, negative values on error.
*
*/
int ebitmap_netlbl_import(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap *catmap)
{
struct ebitmap_node *e_iter = NULL;
struct ebitmap_node *emap_prev = NULL;
struct netlbl_lsm_secattr_catmap *c_iter = catmap;
u32 c_idx;
/* This function is a much simpler because SELinux's MAPTYPE happens
* to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is
* changed from a u64 this function will most likely need to be changed
* as well. It's not ideal but I think the tradeoff in terms of
* neatness and speed is worth it. */
do {
for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) {
if (c_iter->bitmap[c_idx] == 0)
continue;
e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
if (e_iter == NULL)
goto netlbl_import_failure;
if (emap_prev == NULL)
ebmap->node = e_iter;
else
emap_prev->next = e_iter;
emap_prev = e_iter;
e_iter->startbit = c_iter->startbit +
NETLBL_CATMAP_MAPSIZE * c_idx;
e_iter->map = c_iter->bitmap[c_idx];
}
c_iter = c_iter->next;
} while (c_iter != NULL);
if (e_iter != NULL)
ebmap->highbit = e_iter->startbit + MAPSIZE;
else
ebitmap_destroy(ebmap);
return 0;
netlbl_import_failure:
ebitmap_destroy(ebmap);
return -ENOMEM;
}
#endif /* CONFIG_NETLABEL */
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2;
if (e1->highbit < e2->highbit)
return 0;
n1 = e1->node;
n2 = e2->node;
while (n1 && n2 && (n1->startbit <= n2->startbit)) {
if (n1->startbit < n2->startbit) {
n1 = n1->next;
continue;
}
if ((n1->map & n2->map) != n2->map)
return 0;
n1 = n1->next;
n2 = n2->next;
}
if (n2)
return 0;
return 1;
}
int ebitmap_get_bit(struct ebitmap *e, unsigned long bit)
{
struct ebitmap_node *n;
if (e->highbit < bit)
return 0;
n = e->node;
while (n && (n->startbit <= bit)) {
if ((n->startbit + MAPSIZE) > bit) {
if (n->map & (MAPBIT << (bit - n->startbit)))
return 1;
else
return 0;
}
n = n->next;
}
return 0;
}
int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
{
struct ebitmap_node *n, *prev, *new;
prev = NULL;
n = e->node;
while (n && n->startbit <= bit) {
if ((n->startbit + MAPSIZE) > bit) {
if (value) {
n->map |= (MAPBIT << (bit - n->startbit));
} else {
n->map &= ~(MAPBIT << (bit - n->startbit));
if (!n->map) {
/* drop this node from the bitmap */
if (!n->next) {
/*
* this was the highest map
* within the bitmap
*/
if (prev)
e->highbit = prev->startbit + MAPSIZE;
else
e->highbit = 0;
}
if (prev)
prev->next = n->next;
else
e->node = n->next;
kfree(n);
}
}
return 0;
}
prev = n;
n = n->next;
}
if (!value)
return 0;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->startbit = bit & ~(MAPSIZE - 1);
new->map = (MAPBIT << (bit - new->startbit));
if (!n)
/* this node will be the highest map within the bitmap */
e->highbit = new->startbit + MAPSIZE;
if (prev) {
new->next = prev->next;
prev->next = new;
} else {
new->next = e->node;
e->node = new;
}
return 0;
}
void ebitmap_destroy(struct ebitmap *e)
{
struct ebitmap_node *n, *temp;
if (!e)
return;
n = e->node;
while (n) {
temp = n;
n = n->next;
kfree(temp);
}
e->highbit = 0;
e->node = NULL;
return;
}
int ebitmap_read(struct ebitmap *e, void *fp)
{
int rc;
struct ebitmap_node *n, *l;
__le32 buf[3];
u32 mapsize, count, i;
__le64 map;
ebitmap_init(e);
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
goto out;
mapsize = le32_to_cpu(buf[0]);
e->highbit = le32_to_cpu(buf[1]);
count = le32_to_cpu(buf[2]);
if (mapsize != MAPSIZE) {
printk(KERN_ERR "security: ebitmap: map size %u does not "
"match my size %Zd (high bit was %d)\n", mapsize,
MAPSIZE, e->highbit);
goto bad;
}
if (!e->highbit) {
e->node = NULL;
goto ok;
}
if (e->highbit & (MAPSIZE - 1)) {
printk(KERN_ERR "security: ebitmap: high bit (%d) is not a "
"multiple of the map size (%Zd)\n", e->highbit, MAPSIZE);
goto bad;
}
l = NULL;
for (i = 0; i < count; i++) {
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0) {
printk(KERN_ERR "security: ebitmap: truncated map\n");
goto bad;
}
n = kzalloc(sizeof(*n), GFP_KERNEL);
if (!n) {
printk(KERN_ERR "security: ebitmap: out of memory\n");
rc = -ENOMEM;
goto bad;
}
n->startbit = le32_to_cpu(buf[0]);
if (n->startbit & (MAPSIZE - 1)) {
printk(KERN_ERR "security: ebitmap start bit (%d) is "
"not a multiple of the map size (%Zd)\n",
n->startbit, MAPSIZE);
goto bad_free;
}
if (n->startbit > (e->highbit - MAPSIZE)) {
printk(KERN_ERR "security: ebitmap start bit (%d) is "
"beyond the end of the bitmap (%Zd)\n",
n->startbit, (e->highbit - MAPSIZE));
goto bad_free;
}
rc = next_entry(&map, fp, sizeof(u64));
if (rc < 0) {
printk(KERN_ERR "security: ebitmap: truncated map\n");
goto bad_free;
}
n->map = le64_to_cpu(map);
if (!n->map) {
printk(KERN_ERR "security: ebitmap: null map in "
"ebitmap (startbit %d)\n", n->startbit);
goto bad_free;
}
if (l) {
if (n->startbit <= l->startbit) {
printk(KERN_ERR "security: ebitmap: start "
"bit %d comes after start bit %d\n",
n->startbit, l->startbit);
goto bad_free;
}
l->next = n;
} else
e->node = n;
l = n;
}
ok:
rc = 0;
out:
return rc;
bad_free:
kfree(n);
bad:
if (!rc)
rc = -EINVAL;
ebitmap_destroy(e);
goto out;
}

View File

@@ -0,0 +1,98 @@
/*
* An extensible bitmap is a bitmap that supports an
* arbitrary number of bits. Extensible bitmaps are
* used to represent sets of values, such as types,
* roles, categories, and classes.
*
* Each extensible bitmap is implemented as a linked
* list of bitmap nodes, where each bitmap node has
* an explicitly specified starting bit position within
* the total bitmap.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_EBITMAP_H_
#define _SS_EBITMAP_H_
#include <net/netlabel.h>
#define MAPTYPE u64 /* portion of bitmap in each node */
#define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */
#define MAPBIT 1ULL /* a bit in the node bitmap */
struct ebitmap_node {
u32 startbit; /* starting position in the total bitmap */
MAPTYPE map; /* this node's portion of the bitmap */
struct ebitmap_node *next;
};
struct ebitmap {
struct ebitmap_node *node; /* first node in the bitmap */
u32 highbit; /* highest position in the total bitmap */
};
#define ebitmap_length(e) ((e)->highbit)
#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0)
static inline unsigned int ebitmap_start(struct ebitmap *e,
struct ebitmap_node **n)
{
*n = e->node;
return ebitmap_startbit(e);
}
static inline void ebitmap_init(struct ebitmap *e)
{
memset(e, 0, sizeof(*e));
}
static inline unsigned int ebitmap_next(struct ebitmap_node **n,
unsigned int bit)
{
if ((bit == ((*n)->startbit + MAPSIZE - 1)) &&
(*n)->next) {
*n = (*n)->next;
return (*n)->startbit;
}
return (bit+1);
}
static inline int ebitmap_node_get_bit(struct ebitmap_node * n,
unsigned int bit)
{
if (n->map & (MAPBIT << (bit - n->startbit)))
return 1;
return 0;
}
#define ebitmap_for_each_bit(e, n, bit) \
for (bit = ebitmap_start(e, &n); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
void ebitmap_destroy(struct ebitmap *e);
int ebitmap_read(struct ebitmap *e, void *fp);
#ifdef CONFIG_NETLABEL
int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap **catmap);
int ebitmap_netlbl_import(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap *catmap);
#else
static inline int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap **catmap)
{
return -ENOMEM;
}
static inline int ebitmap_netlbl_import(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap *catmap)
{
return -ENOMEM;
}
#endif
#endif /* _SS_EBITMAP_H_ */

View File

@@ -0,0 +1,165 @@
/*
* Implementation of the hash table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include "hashtab.h"
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 size)
{
struct hashtab *p;
u32 i;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return p;
p->size = size;
p->nel = 0;
p->hash_value = hash_value;
p->keycmp = keycmp;
p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL);
if (p->htable == NULL) {
kfree(p);
return NULL;
}
for (i = 0; i < size; i++)
p->htable[i] = NULL;
return p;
}
int hashtab_insert(struct hashtab *h, void *key, void *datum)
{
u32 hvalue;
struct hashtab_node *prev, *cur, *newnode;
if (!h || h->nel == HASHTAB_MAX_NODES)
return -EINVAL;
hvalue = h->hash_value(h, key);
prev = NULL;
cur = h->htable[hvalue];
while (cur && h->keycmp(h, key, cur->key) > 0) {
prev = cur;
cur = cur->next;
}
if (cur && (h->keycmp(h, key, cur->key) == 0))
return -EEXIST;
newnode = kzalloc(sizeof(*newnode), GFP_KERNEL);
if (newnode == NULL)
return -ENOMEM;
newnode->key = key;
newnode->datum = datum;
if (prev) {
newnode->next = prev->next;
prev->next = newnode;
} else {
newnode->next = h->htable[hvalue];
h->htable[hvalue] = newnode;
}
h->nel++;
return 0;
}
void *hashtab_search(struct hashtab *h, const void *key)
{
u32 hvalue;
struct hashtab_node *cur;
if (!h)
return NULL;
hvalue = h->hash_value(h, key);
cur = h->htable[hvalue];
while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
cur = cur->next;
if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
return NULL;
return cur->datum;
}
void hashtab_destroy(struct hashtab *h)
{
u32 i;
struct hashtab_node *cur, *temp;
if (!h)
return;
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
while (cur != NULL) {
temp = cur;
cur = cur->next;
kfree(temp);
}
h->htable[i] = NULL;
}
kfree(h->htable);
h->htable = NULL;
kfree(h);
}
int hashtab_map(struct hashtab *h,
int (*apply)(void *k, void *d, void *args),
void *args)
{
u32 i;
int ret;
struct hashtab_node *cur;
if (!h)
return 0;
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
while (cur != NULL) {
ret = apply(cur->key, cur->datum, args);
if (ret)
return ret;
cur = cur->next;
}
}
return 0;
}
void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
{
u32 i, chain_len, slots_used, max_chain_len;
struct hashtab_node *cur;
slots_used = 0;
max_chain_len = 0;
for (slots_used = max_chain_len = i = 0; i < h->size; i++) {
cur = h->htable[i];
if (cur) {
slots_used++;
chain_len = 0;
while (cur) {
chain_len++;
cur = cur->next;
}
if (chain_len > max_chain_len)
max_chain_len = chain_len;
}
}
info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
}

View File

@@ -0,0 +1,87 @@
/*
* A hash table (hashtab) maintains associations between
* key values and datum values. The type of the key values
* and the type of the datum values is arbitrary. The
* functions for hash computation and key comparison are
* provided by the creator of the table.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_HASHTAB_H_
#define _SS_HASHTAB_H_
#define HASHTAB_MAX_NODES 0xffffffff
struct hashtab_node {
void *key;
void *datum;
struct hashtab_node *next;
};
struct hashtab {
struct hashtab_node **htable; /* hash table */
u32 size; /* number of slots in hash table */
u32 nel; /* number of elements in hash table */
u32 (*hash_value)(struct hashtab *h, const void *key);
/* hash function */
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2);
/* key comparison function */
};
struct hashtab_info {
u32 slots_used;
u32 max_chain_len;
};
/*
* Creates a new hash table with the specified characteristics.
*
* Returns NULL if insufficent space is available or
* the new hash table otherwise.
*/
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 size);
/*
* Inserts the specified (key, datum) pair into the specified hash table.
*
* Returns -ENOMEM on memory allocation error,
* -EEXIST if there is already an entry with the same key,
* -EINVAL for general errors or
* 0 otherwise.
*/
int hashtab_insert(struct hashtab *h, void *k, void *d);
/*
* Searches for the entry with the specified key in the hash table.
*
* Returns NULL if no entry has the specified key or
* the datum of the entry otherwise.
*/
void *hashtab_search(struct hashtab *h, const void *k);
/*
* Destroys the specified hash table.
*/
void hashtab_destroy(struct hashtab *h);
/*
* Applies the specified apply function to (key,datum,args)
* for each entry in the specified hash table.
*
* The order in which the function is applied to the entries
* is dependent upon the internal structure of the hash table.
*
* If apply returns a non-zero status, then hashtab_map will cease
* iterating through the hash table and will propagate the error
* return to its caller.
*/
int hashtab_map(struct hashtab *h,
int (*apply)(void *k, void *d, void *args),
void *args);
/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
#endif /* _SS_HASHTAB_H */

659
security/selinux/ss/mls.c Normal file
View File

@@ -0,0 +1,659 @@
/*
* Implementation of the multi-level security (MLS) policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
*
* Support for enhanced MLS infrastructure.
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support to import/export the MLS label from NetLabel
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <net/netlabel.h>
#include "sidtab.h"
#include "mls.h"
#include "policydb.h"
#include "services.h"
/*
* Return the length in bytes for the MLS fields of the
* security context string representation of `context'.
*/
int mls_compute_context_len(struct context * context)
{
int i, l, len, range;
struct ebitmap_node *node;
if (!selinux_mls_enabled)
return 0;
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
range = 0;
len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) {
if (range) {
range++;
continue;
}
len += strlen(policydb.p_cat_val_to_name[i]) + 1;
range++;
} else {
if (range > 1)
len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
range = 0;
}
}
/* Handle case where last category is the end of range */
if (range > 1)
len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
if (l == 0) {
if (mls_level_eq(&context->range.level[0],
&context->range.level[1]))
break;
else
len++;
}
}
return len;
}
/*
* Write the security context string representation of
* the MLS fields of `context' into the string `*scontext'.
* Update `*scontext' to point to the end of the MLS fields.
*/
void mls_sid_to_context(struct context *context,
char **scontext)
{
char *scontextp;
int i, l, range, wrote_sep;
struct ebitmap_node *node;
if (!selinux_mls_enabled)
return;
scontextp = *scontext;
*scontextp = ':';
scontextp++;
for (l = 0; l < 2; l++) {
range = 0;
wrote_sep = 0;
strcpy(scontextp,
policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
/* categories */
ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) {
if (range) {
range++;
continue;
}
if (!wrote_sep) {
*scontextp++ = ':';
wrote_sep = 1;
} else
*scontextp++ = ',';
strcpy(scontextp, policydb.p_cat_val_to_name[i]);
scontextp += strlen(policydb.p_cat_val_to_name[i]);
range++;
} else {
if (range > 1) {
if (range > 2)
*scontextp++ = '.';
else
*scontextp++ = ',';
strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
}
range = 0;
}
}
/* Handle case where last category is the end of range */
if (range > 1) {
if (range > 2)
*scontextp++ = '.';
else
*scontextp++ = ',';
strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
}
if (l == 0) {
if (mls_level_eq(&context->range.level[0],
&context->range.level[1]))
break;
else {
*scontextp = '-';
scontextp++;
}
}
}
*scontext = scontextp;
return;
}
/*
* Return 1 if the MLS fields in the security context
* structure `c' are valid. Return 0 otherwise.
*/
int mls_context_isvalid(struct policydb *p, struct context *c)
{
struct level_datum *levdatum;
struct user_datum *usrdatum;
struct ebitmap_node *node;
int i, l;
if (!selinux_mls_enabled)
return 1;
/*
* MLS range validity checks: high must dominate low, low level must
* be valid (category set <-> sensitivity check), and high level must
* be valid (category set <-> sensitivity check)
*/
if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
/* High does not dominate low. */
return 0;
for (l = 0; l < 2; l++) {
if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
return 0;
levdatum = hashtab_search(p->p_levels.table,
p->p_sens_val_to_name[c->range.level[l].sens - 1]);
if (!levdatum)
return 0;
ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) {
if (i > p->p_cats.nprim)
return 0;
if (!ebitmap_get_bit(&levdatum->level->cat, i))
/*
* Category may not be associated with
* sensitivity in low level.
*/
return 0;
}
}
}
if (c->role == OBJECT_R_VAL)
return 1;
/*
* User must be authorized for the MLS range.
*/
if (!c->user || c->user > p->p_users.nprim)
return 0;
usrdatum = p->user_val_to_struct[c->user - 1];
if (!mls_range_contains(usrdatum->range, c->range))
return 0; /* user may not be associated with range */
return 1;
}
/*
* Set the MLS fields in the security context structure
* `context' based on the string representation in
* the string `*scontext'. Update `*scontext' to
* point to the end of the string representation of
* the MLS fields.
*
* This function modifies the string in place, inserting
* NULL characters to terminate the MLS fields.
*
* If a def_sid is provided and no MLS field is present,
* copy the MLS field of the associated default context.
* Used for upgraded to MLS systems where objects may lack
* MLS fields.
*
* Policy read-lock must be held for sidtab lookup.
*
*/
int mls_context_to_sid(char oldc,
char **scontext,
struct context *context,
struct sidtab *s,
u32 def_sid)
{
char delim;
char *scontextp, *p, *rngptr;
struct level_datum *levdatum;
struct cat_datum *catdatum, *rngdatum;
int l, rc = -EINVAL;
if (!selinux_mls_enabled) {
if (def_sid != SECSID_NULL && oldc)
*scontext += strlen(*scontext)+1;
return 0;
}
/*
* No MLS component to the security context, try and map to
* default if provided.
*/
if (!oldc) {
struct context *defcon;
if (def_sid == SECSID_NULL)
goto out;
defcon = sidtab_search(s, def_sid);
if (!defcon)
goto out;
rc = mls_context_cpy(context, defcon);
goto out;
}
/* Extract low sensitivity. */
scontextp = p = *scontext;
while (*p && *p != ':' && *p != '-')
p++;
delim = *p;
if (delim != 0)
*p++ = 0;
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(policydb.p_levels.table, scontextp);
if (!levdatum) {
rc = -EINVAL;
goto out;
}
context->range.level[l].sens = levdatum->level->sens;
if (delim == ':') {
/* Extract category set. */
while (1) {
scontextp = p;
while (*p && *p != ',' && *p != '-')
p++;
delim = *p;
if (delim != 0)
*p++ = 0;
/* Separate into range if exists */
if ((rngptr = strchr(scontextp, '.')) != NULL) {
/* Remove '.' */
*rngptr++ = 0;
}
catdatum = hashtab_search(policydb.p_cats.table,
scontextp);
if (!catdatum) {
rc = -EINVAL;
goto out;
}
rc = ebitmap_set_bit(&context->range.level[l].cat,
catdatum->value - 1, 1);
if (rc)
goto out;
/* If range, set all categories in range */
if (rngptr) {
int i;
rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
if (!rngdatum) {
rc = -EINVAL;
goto out;
}
if (catdatum->value >= rngdatum->value) {
rc = -EINVAL;
goto out;
}
for (i = catdatum->value; i < rngdatum->value; i++) {
rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
if (rc)
goto out;
}
}
if (delim != ',')
break;
}
}
if (delim == '-') {
/* Extract high sensitivity. */
scontextp = p;
while (*p && *p != ':')
p++;
delim = *p;
if (delim != 0)
*p++ = 0;
} else
break;
}
if (l == 0) {
context->range.level[1].sens = context->range.level[0].sens;
rc = ebitmap_cpy(&context->range.level[1].cat,
&context->range.level[0].cat);
if (rc)
goto out;
}
*scontext = ++p;
rc = 0;
out:
return rc;
}
/*
* Set the MLS fields in the security context structure
* `context' based on the string representation in
* the string `str'. This function will allocate temporary memory with the
* given constraints of gfp_mask.
*/
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
{
char *tmpstr, *freestr;
int rc;
if (!selinux_mls_enabled)
return -EINVAL;
/* we need freestr because mls_context_to_sid will change
the value of tmpstr */
tmpstr = freestr = kstrdup(str, gfp_mask);
if (!tmpstr) {
rc = -ENOMEM;
} else {
rc = mls_context_to_sid(':', &tmpstr, context,
NULL, SECSID_NULL);
kfree(freestr);
}
return rc;
}
/*
* Copies the MLS range `range' into `context'.
*/
static inline int mls_range_set(struct context *context,
struct mls_range *range)
{
int l, rc = 0;
/* Copy the MLS range into the context */
for (l = 0; l < 2; l++) {
context->range.level[l].sens = range->level[l].sens;
rc = ebitmap_cpy(&context->range.level[l].cat,
&range->level[l].cat);
if (rc)
break;
}
return rc;
}
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct context *usercon)
{
if (selinux_mls_enabled) {
struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
struct mls_level *user_low = &(user->range.level[0]);
struct mls_level *user_clr = &(user->range.level[1]);
struct mls_level *user_def = &(user->dfltlevel);
struct mls_level *usercon_sen = &(usercon->range.level[0]);
struct mls_level *usercon_clr = &(usercon->range.level[1]);
/* Honor the user's default level if we can */
if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
*usercon_sen = *user_def;
} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
*usercon_sen = *fromcon_sen;
} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
*usercon_sen = *user_low;
} else
return -EINVAL;
/* Lower the clearance of available contexts
if the clearance of "fromcon" is lower than
that of the user's default clearance (but
only if the "fromcon" clearance dominates
the user's computed sensitivity level) */
if (mls_level_dom(user_clr, fromcon_clr)) {
*usercon_clr = *fromcon_clr;
} else if (mls_level_dom(fromcon_clr, user_clr)) {
*usercon_clr = *user_clr;
} else
return -EINVAL;
}
return 0;
}
/*
* Convert the MLS fields in the security context
* structure `c' from the values specified in the
* policy `oldp' to the values specified in the policy `newp'.
*/
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *c)
{
struct level_datum *levdatum;
struct cat_datum *catdatum;
struct ebitmap bitmap;
struct ebitmap_node *node;
int l, i;
if (!selinux_mls_enabled)
return 0;
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
if (!levdatum)
return -EINVAL;
c->range.level[l].sens = levdatum->level->sens;
ebitmap_init(&bitmap);
ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) {
int rc;
catdatum = hashtab_search(newp->p_cats.table,
oldp->p_cat_val_to_name[i]);
if (!catdatum)
return -EINVAL;
rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
if (rc)
return rc;
}
}
ebitmap_destroy(&c->range.level[l].cat);
c->range.level[l].cat = bitmap;
}
return 0;
}
int mls_compute_sid(struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
struct context *newcontext)
{
struct range_trans *rtr;
if (!selinux_mls_enabled)
return 0;
switch (specified) {
case AVTAB_TRANSITION:
/* Look for a range transition rule. */
for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
if (rtr->source_type == scontext->type &&
rtr->target_type == tcontext->type &&
rtr->target_class == tclass) {
/* Set the range from the rule */
return mls_range_set(newcontext,
&rtr->target_range);
}
}
/* Fallthrough */
case AVTAB_CHANGE:
if (tclass == SECCLASS_PROCESS)
/* Use the process MLS attributes. */
return mls_context_cpy(newcontext, scontext);
else
/* Use the process effective MLS attributes. */
return mls_context_cpy_low(newcontext, scontext);
case AVTAB_MEMBER:
/* Only polyinstantiate the MLS attributes if
the type is being polyinstantiated */
if (newcontext->type != tcontext->type) {
/* Use the process effective MLS attributes. */
return mls_context_cpy_low(newcontext, scontext);
} else {
/* Use the related object MLS attributes. */
return mls_context_cpy(newcontext, tcontext);
}
default:
return -EINVAL;
}
return -EINVAL;
}
#ifdef CONFIG_NETLABEL
/**
* mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
* @context: the security context
* @secattr: the NetLabel security attributes
*
* Description:
* Given the security context copy the low MLS sensitivity level into the
* NetLabel MLS sensitivity level field.
*
*/
void mls_export_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!selinux_mls_enabled)
return;
secattr->mls_lvl = context->range.level[0].sens - 1;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
}
/**
* mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
* @context: the security context
* @secattr: the NetLabel security attributes
*
* Description:
* Given the security context and the NetLabel security attributes, copy the
* NetLabel MLS sensitivity level into the context.
*
*/
void mls_import_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!selinux_mls_enabled)
return;
context->range.level[0].sens = secattr->mls_lvl + 1;
context->range.level[1].sens = context->range.level[0].sens;
}
/**
* mls_export_netlbl_cat - Export the MLS categories to NetLabel
* @context: the security context
* @secattr: the NetLabel security attributes
*
* Description:
* Given the security context copy the low MLS categories into the NetLabel
* MLS category field. Returns zero on success, negative values on failure.
*
*/
int mls_export_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
if (!selinux_mls_enabled)
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
&secattr->mls_cat);
if (rc == 0 && secattr->mls_cat != NULL)
secattr->flags |= NETLBL_SECATTR_MLS_CAT;
return rc;
}
/**
* mls_import_netlbl_cat - Import the MLS categories from NetLabel
* @context: the security context
* @secattr: the NetLabel security attributes
*
* Description:
* Copy the NetLabel security attributes into the SELinux context; since the
* NetLabel security attribute only contains a single MLS category use it for
* both the low and high categories of the context. Returns zero on success,
* negative values on failure.
*
*/
int mls_import_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
if (!selinux_mls_enabled)
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
secattr->mls_cat);
if (rc != 0)
goto import_netlbl_cat_failure;
rc = ebitmap_cpy(&context->range.level[1].cat,
&context->range.level[0].cat);
if (rc != 0)
goto import_netlbl_cat_failure;
return 0;
import_netlbl_cat_failure:
ebitmap_destroy(&context->range.level[0].cat);
ebitmap_destroy(&context->range.level[1].cat);
return rc;
}
#endif /* CONFIG_NETLABEL */

85
security/selinux/ss/mls.h Normal file
View File

@@ -0,0 +1,85 @@
/*
* Multi-level security (MLS) policy operations.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
*
* Support for enhanced MLS infrastructure.
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support to import/export the MLS label from NetLabel
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#ifndef _SS_MLS_H_
#define _SS_MLS_H_
#include "context.h"
#include "policydb.h"
int mls_compute_context_len(struct context *context);
void mls_sid_to_context(struct context *context, char **scontext);
int mls_context_isvalid(struct policydb *p, struct context *c);
int mls_context_to_sid(char oldc,
char **scontext,
struct context *context,
struct sidtab *s,
u32 def_sid);
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *context);
int mls_compute_sid(struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
struct context *newcontext);
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct context *usercon);
#ifdef CONFIG_NETLABEL
void mls_export_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr);
void mls_import_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr);
int mls_export_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr);
int mls_import_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr);
#else
static inline void mls_export_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
static inline void mls_import_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
static inline int mls_export_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;
}
static inline int mls_import_netlbl_cat(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;
}
#endif
#endif /* _SS_MLS_H */

View File

@@ -0,0 +1,56 @@
/*
* Type definitions for the multi-level security (MLS) policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
*
* Support for enhanced MLS infrastructure.
*
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
*/
#ifndef _SS_MLS_TYPES_H_
#define _SS_MLS_TYPES_H_
#include "security.h"
struct mls_level {
u32 sens; /* sensitivity */
struct ebitmap cat; /* category set */
};
struct mls_range {
struct mls_level level[2]; /* low == level[0], high == level[1] */
};
static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
{
if (!selinux_mls_enabled)
return 1;
return ((l1->sens == l2->sens) &&
ebitmap_cmp(&l1->cat, &l2->cat));
}
static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
{
if (!selinux_mls_enabled)
return 1;
return ((l1->sens >= l2->sens) &&
ebitmap_contains(&l1->cat, &l2->cat));
}
#define mls_level_incomp(l1, l2) \
(!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1)))
#define mls_level_between(l1, l2, l3) \
(mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1)))
#define mls_range_contains(r1, r2) \
(mls_level_dom(&(r2).level[0], &(r1).level[0]) && \
mls_level_dom(&(r1).level[1], &(r2).level[1]))
#endif /* _SS_MLS_TYPES_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,279 @@
/*
* A policy database (policydb) specifies the
* configuration data for the security policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
*
* Support for enhanced MLS infrastructure.
*
* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
*
* Added conditional policy language extensions
*
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* 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
* the Free Software Foundation, version 2.
*/
#ifndef _SS_POLICYDB_H_
#define _SS_POLICYDB_H_
#include "symtab.h"
#include "avtab.h"
#include "sidtab.h"
#include "context.h"
#include "constraint.h"
/*
* A datum type is defined for each kind of symbol
* in the configuration data: individual permissions,
* common prefixes for access vectors, classes,
* users, roles, types, sensitivities, categories, etc.
*/
/* Permission attributes */
struct perm_datum {
u32 value; /* permission bit + 1 */
};
/* Attributes of a common prefix for access vectors */
struct common_datum {
u32 value; /* internal common value */
struct symtab permissions; /* common permissions */
};
/* Class attributes */
struct class_datum {
u32 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
struct constraint_node *constraints; /* constraints on class permissions */
struct constraint_node *validatetrans; /* special transition rules */
};
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
struct role_trans {
u32 role; /* current role */
u32 type; /* program executable type */
u32 new_role; /* new role */
struct role_trans *next;
};
struct role_allow {
u32 role; /* current role */
u32 new_role; /* new role */
struct role_allow *next;
};
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
unsigned char primary; /* primary name? */
};
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
};
/* Sensitivity attributes */
struct level_datum {
struct mls_level *level; /* sensitivity and associated categories */
unsigned char isalias; /* is this sensitivity an alias for another? */
};
/* Category attributes */
struct cat_datum {
u32 value; /* internal category bit + 1 */
unsigned char isalias; /* is this category an alias for another? */
};
struct range_trans {
u32 source_type;
u32 target_type;
u32 target_class;
struct mls_range target_range;
struct range_trans *next;
};
/* Boolean data type */
struct cond_bool_datum {
__u32 value; /* internal type value */
int state;
};
struct cond_node;
/*
* The configuration data includes security contexts for
* initial SIDs, unlabeled file systems, TCP and UDP port numbers,
* network interfaces, and nodes. This structure stores the
* relevant data for one such entry. Entries of the same kind
* (e.g. all initial SIDs) are linked together into a list.
*/
struct ocontext {
union {
char *name; /* name of initial SID, fs, netif, fstype, path */
struct {
u8 protocol;
u16 low_port;
u16 high_port;
} port; /* TCP or UDP port information */
struct {
u32 addr;
u32 mask;
} node; /* node information */
struct {
u32 addr[4];
u32 mask[4];
} node6; /* IPv6 node information */
} u;
union {
u32 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
u32 sid[2]; /* SID(s) */
struct ocontext *next;
};
struct genfs {
char *fstype;
struct ocontext *head;
struct genfs *next;
};
/* symbol table array indices */
#define SYM_COMMONS 0
#define SYM_CLASSES 1
#define SYM_ROLES 2
#define SYM_TYPES 3
#define SYM_USERS 4
#define SYM_BOOLS 5
#define SYM_LEVELS 6
#define SYM_CATS 7
#define SYM_NUM 8
/* object context array indices */
#define OCON_ISID 0 /* initial SIDs */
#define OCON_FS 1 /* unlabeled file systems */
#define OCON_PORT 2 /* TCP and UDP port numbers */
#define OCON_NETIF 3 /* network interfaces */
#define OCON_NODE 4 /* nodes */
#define OCON_FSUSE 5 /* fs_use */
#define OCON_NODE6 6 /* IPv6 nodes */
#define OCON_NUM 7
/* The policy database */
struct policydb {
/* symbol tables */
struct symtab symtab[SYM_NUM];
#define p_commons symtab[SYM_COMMONS]
#define p_classes symtab[SYM_CLASSES]
#define p_roles symtab[SYM_ROLES]
#define p_types symtab[SYM_TYPES]
#define p_users symtab[SYM_USERS]
#define p_bools symtab[SYM_BOOLS]
#define p_levels symtab[SYM_LEVELS]
#define p_cats symtab[SYM_CATS]
/* symbol names indexed by (value - 1) */
char **sym_val_to_name[SYM_NUM];
#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
#define p_user_val_to_name sym_val_to_name[SYM_USERS]
#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
/* class, role, and user attributes indexed by (value - 1) */
struct class_datum **class_val_to_struct;
struct role_datum **role_val_to_struct;
struct user_datum **user_val_to_struct;
/* type enforcement access vectors and transitions */
struct avtab te_avtab;
/* role transitions */
struct role_trans *role_tr;
/* bools indexed by (value - 1) */
struct cond_bool_datum **bool_val_to_struct;
/* type enforcement conditional access vectors and transitions */
struct avtab te_cond_avtab;
/* linked list indexing te_cond_avtab by conditional */
struct cond_node* cond_list;
/* role allows */
struct role_allow *role_allow;
/* security contexts of initial SIDs, unlabeled file systems,
TCP or UDP port numbers, network interfaces and nodes */
struct ocontext *ocontexts[OCON_NUM];
/* security contexts for files in filesystems that cannot support
a persistent label mapping or use another
fixed labeling behavior. */
struct genfs *genfs;
/* range transitions */
struct range_trans *range_tr;
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map;
unsigned int policyvers;
};
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
extern int policydb_read(struct policydb *p, void *fp);
#define PERM_SYMTAB_SIZE 32
#define POLICYDB_CONFIG_MLS 1
#define OBJECT_R "object_r"
#define OBJECT_R_VAL 1
#define POLICYDB_MAGIC SELINUX_MAGIC
#define POLICYDB_STRING "SE Linux"
struct policy_file {
char *data;
size_t len;
};
static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
{
if (bytes > fp->len)
return -EINVAL;
memcpy(buf, fp->data, bytes);
fp->data += bytes;
fp->len -= bytes;
return 0;
}
#endif /* _SS_POLICYDB_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
/*
* Implementation of the security services.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_SERVICES_H_
#define _SS_SERVICES_H_
#include "policydb.h"
#include "sidtab.h"
extern struct policydb policydb;
#endif /* _SS_SERVICES_H_ */

View File

@@ -0,0 +1,304 @@
/*
* Implementation of the SID table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include "flask.h"
#include "security.h"
#include "sidtab.h"
#define SIDTAB_HASH(sid) \
(sid & SIDTAB_HASH_MASK)
#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
int sidtab_init(struct sidtab *s)
{
int i;
s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
if (!s->htable)
return -ENOMEM;
for (i = 0; i < SIDTAB_SIZE; i++)
s->htable[i] = NULL;
s->nel = 0;
s->next_sid = 1;
s->shutdown = 0;
INIT_SIDTAB_LOCK(s);
return 0;
}
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
{
int hvalue, rc = 0;
struct sidtab_node *prev, *cur, *newnode;
if (!s) {
rc = -ENOMEM;
goto out;
}
hvalue = SIDTAB_HASH(sid);
prev = NULL;
cur = s->htable[hvalue];
while (cur != NULL && sid > cur->sid) {
prev = cur;
cur = cur->next;
}
if (cur && sid == cur->sid) {
rc = -EEXIST;
goto out;
}
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
if (newnode == NULL) {
rc = -ENOMEM;
goto out;
}
newnode->sid = sid;
if (context_cpy(&newnode->context, context)) {
kfree(newnode);
rc = -ENOMEM;
goto out;
}
if (prev) {
newnode->next = prev->next;
wmb();
prev->next = newnode;
} else {
newnode->next = s->htable[hvalue];
wmb();
s->htable[hvalue] = newnode;
}
s->nel++;
if (sid >= s->next_sid)
s->next_sid = sid + 1;
out:
return rc;
}
struct context *sidtab_search(struct sidtab *s, u32 sid)
{
int hvalue;
struct sidtab_node *cur;
if (!s)
return NULL;
hvalue = SIDTAB_HASH(sid);
cur = s->htable[hvalue];
while (cur != NULL && sid > cur->sid)
cur = cur->next;
if (cur == NULL || sid != cur->sid) {
/* Remap invalid SIDs to the unlabeled SID. */
sid = SECINITSID_UNLABELED;
hvalue = SIDTAB_HASH(sid);
cur = s->htable[hvalue];
while (cur != NULL && sid > cur->sid)
cur = cur->next;
if (!cur || sid != cur->sid)
return NULL;
}
return &cur->context;
}
int sidtab_map(struct sidtab *s,
int (*apply) (u32 sid,
struct context *context,
void *args),
void *args)
{
int i, rc = 0;
struct sidtab_node *cur;
if (!s)
goto out;
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
while (cur != NULL) {
rc = apply(cur->sid, &cur->context, args);
if (rc)
goto out;
cur = cur->next;
}
}
out:
return rc;
}
void sidtab_map_remove_on_error(struct sidtab *s,
int (*apply) (u32 sid,
struct context *context,
void *args),
void *args)
{
int i, ret;
struct sidtab_node *last, *cur, *temp;
if (!s)
return;
for (i = 0; i < SIDTAB_SIZE; i++) {
last = NULL;
cur = s->htable[i];
while (cur != NULL) {
ret = apply(cur->sid, &cur->context, args);
if (ret) {
if (last) {
last->next = cur->next;
} else {
s->htable[i] = cur->next;
}
temp = cur;
cur = cur->next;
context_destroy(&temp->context);
kfree(temp);
s->nel--;
} else {
last = cur;
cur = cur->next;
}
}
}
return;
}
static inline u32 sidtab_search_context(struct sidtab *s,
struct context *context)
{
int i;
struct sidtab_node *cur;
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
while (cur != NULL) {
if (context_cmp(&cur->context, context))
return cur->sid;
cur = cur->next;
}
}
return 0;
}
int sidtab_context_to_sid(struct sidtab *s,
struct context *context,
u32 *out_sid)
{
u32 sid;
int ret = 0;
unsigned long flags;
*out_sid = SECSID_NULL;
sid = sidtab_search_context(s, context);
if (!sid) {
SIDTAB_LOCK(s, flags);
/* Rescan now that we hold the lock. */
sid = sidtab_search_context(s, context);
if (sid)
goto unlock_out;
/* No SID exists for the context. Allocate a new one. */
if (s->next_sid == UINT_MAX || s->shutdown) {
ret = -ENOMEM;
goto unlock_out;
}
sid = s->next_sid++;
ret = sidtab_insert(s, sid, context);
if (ret)
s->next_sid--;
unlock_out:
SIDTAB_UNLOCK(s, flags);
}
if (ret)
return ret;
*out_sid = sid;
return 0;
}
void sidtab_hash_eval(struct sidtab *h, char *tag)
{
int i, chain_len, slots_used, max_chain_len;
struct sidtab_node *cur;
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = h->htable[i];
if (cur) {
slots_used++;
chain_len = 0;
while (cur) {
chain_len++;
cur = cur->next;
}
if (chain_len > max_chain_len)
max_chain_len = chain_len;
}
}
printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest "
"chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
max_chain_len);
}
void sidtab_destroy(struct sidtab *s)
{
int i;
struct sidtab_node *cur, *temp;
if (!s)
return;
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
while (cur != NULL) {
temp = cur;
cur = cur->next;
context_destroy(&temp->context);
kfree(temp);
}
s->htable[i] = NULL;
}
kfree(s->htable);
s->htable = NULL;
s->nel = 0;
s->next_sid = 1;
}
void sidtab_set(struct sidtab *dst, struct sidtab *src)
{
unsigned long flags;
SIDTAB_LOCK(src, flags);
dst->htable = src->htable;
dst->nel = src->nel;
dst->next_sid = src->next_sid;
dst->shutdown = 0;
SIDTAB_UNLOCK(src, flags);
}
void sidtab_shutdown(struct sidtab *s)
{
unsigned long flags;
SIDTAB_LOCK(s, flags);
s->shutdown = 1;
SIDTAB_UNLOCK(s, flags);
}

View File

@@ -0,0 +1,59 @@
/*
* A security identifier table (sidtab) is a hash table
* of security context structures indexed by SID value.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_SIDTAB_H_
#define _SS_SIDTAB_H_
#include "context.h"
struct sidtab_node {
u32 sid; /* security identifier */
struct context context; /* security context structure */
struct sidtab_node *next;
};
#define SIDTAB_HASH_BITS 7
#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
struct sidtab {
struct sidtab_node **htable;
unsigned int nel; /* number of elements */
unsigned int next_sid; /* next SID to allocate */
unsigned char shutdown;
spinlock_t lock;
};
int sidtab_init(struct sidtab *s);
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
struct context *sidtab_search(struct sidtab *s, u32 sid);
int sidtab_map(struct sidtab *s,
int (*apply) (u32 sid,
struct context *context,
void *args),
void *args);
void sidtab_map_remove_on_error(struct sidtab *s,
int (*apply) (u32 sid,
struct context *context,
void *args),
void *args);
int sidtab_context_to_sid(struct sidtab *s,
struct context *context,
u32 *sid);
void sidtab_hash_eval(struct sidtab *h, char *tag);
void sidtab_destroy(struct sidtab *s);
void sidtab_set(struct sidtab *dst, struct sidtab *src);
void sidtab_shutdown(struct sidtab *s);
#endif /* _SS_SIDTAB_H_ */

View File

@@ -0,0 +1,44 @@
/*
* Implementation of the symbol table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
#include "symtab.h"
static unsigned int symhash(struct hashtab *h, const void *key)
{
const char *p, *keyp;
unsigned int size;
unsigned int val;
val = 0;
keyp = key;
size = strlen(keyp);
for (p = keyp; (p - keyp) < size; p++)
val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p);
return val & (h->size - 1);
}
static int symcmp(struct hashtab *h, const void *key1, const void *key2)
{
const char *keyp1, *keyp2;
keyp1 = key1;
keyp2 = key2;
return strcmp(keyp1, keyp2);
}
int symtab_init(struct symtab *s, unsigned int size)
{
s->table = hashtab_create(symhash, symcmp, size);
if (!s->table)
return -1;
s->nprim = 0;
return 0;
}

View File

@@ -0,0 +1,23 @@
/*
* A symbol table (symtab) maintains associations between symbol
* strings and datum values. The type of the datum values
* is arbitrary. The symbol table type is implemented
* using the hash table type (hashtab).
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_SYMTAB_H_
#define _SS_SYMTAB_H_
#include "hashtab.h"
struct symtab {
struct hashtab *table; /* hash table (keyed on a string) */
u32 nprim; /* number of primary names in table */
};
int symtab_init(struct symtab *s, unsigned int size);
#endif /* _SS_SYMTAB_H_ */

485
security/selinux/xfrm.c Normal file
View File

@@ -0,0 +1,485 @@
/*
* 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>
*
* Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
*
* Granular IPSec Associations for use in MLS environments.
*
* Copyright (C) 2005 International Business Machines Corporation
* Copyright (C) 2006 Trusted Computer Solutions, Inc.
*
* 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/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 flow can use
* a xfrm policy rule.
*/
int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
{
int rc;
u32 sel_sid;
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;
}
else
/*
* All flows should be treated as polmatch'ing an
* otherwise applicable "non-labeled" policy. This
* would prevent inadvertent "leaks".
*/
return 0;
rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
ASSOCIATION__POLMATCH,
NULL);
if (rc == -EACCES)
rc = -ESRCH;
return rc;
}
/*
* LSM hook implementation that authorizes that a state matches
* the given policy, flow combo.
*/
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
struct flowi *fl)
{
u32 state_sid;
int rc;
if (!xp->security)
if (x->security)
/* unlabeled policy and labeled SA can't match */
return 0;
else
/* unlabeled policy and unlabeled SA match all flows */
return 1;
else
if (!x->security)
/* unlabeled SA and labeled policy can't match */
return 0;
else
if (!selinux_authorizable_xfrm(x))
/* Not a SELinux-labeled SA */
return 0;
state_sid = x->security->ctx_sid;
if (fl->secid != state_sid)
return 0;
rc = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION,
ASSOCIATION__SENDTO,
NULL)? 0:1;
/*
* We don't need a separate SA Vs. policy polmatch check
* since the SA is now of the same label as the flow and
* a flow Vs. policy polmatch check had already happened
* in selinux_xfrm_policy_lookup() above.
*/
return rc;
}
/*
* LSM hook implementation that checks and/or returns the xfrm sid for the
* incoming packet.
*/
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
{
struct sec_path *sp;
*sid = SECSID_NULL;
if (skb == NULL)
return 0;
sp = skb->sp;
if (sp) {
int i, sid_set = 0;
for (i = sp->len-1; i >= 0; i--) {
struct xfrm_state *x = sp->xvec[i];
if (selinux_authorizable_xfrm(x)) {
struct xfrm_sec_ctx *ctx = x->security;
if (!sid_set) {
*sid = ctx->ctx_sid;
sid_set = 1;
if (!ckall)
break;
}
else if (*sid != ctx->ctx_sid)
return -EINVAL;
}
}
}
return 0;
}
/*
* 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, u32 sid)
{
int rc = 0;
struct task_security_struct *tsec = current->security;
struct xfrm_sec_ctx *ctx = NULL;
char *ctx_str = NULL;
u32 str_len;
BUG_ON(uctx && sid);
if (!uctx)
goto not_from_user;
if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
return -EINVAL;
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 context?
*/
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
if (rc)
goto out;
return rc;
not_from_user:
rc = security_sid_to_context(sid, &ctx_str, &str_len);
if (rc)
goto out;
*ctxp = ctx = kmalloc(sizeof(*ctx) +
str_len,
GFP_ATOMIC);
if (!ctx) {
rc = -ENOMEM;
goto out;
}
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
ctx->ctx_sid = sid;
ctx->ctx_len = str_len;
memcpy(ctx->ctx_str,
ctx_str,
str_len);
goto out2;
out:
*ctxp = NULL;
kfree(ctx);
out2:
kfree(ctx_str);
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);
BUG_ON(!uctx);
err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0);
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 authorizes deletion of labeled policies.
*/
int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
{
struct task_security_struct *tsec = current->security;
struct xfrm_sec_ctx *ctx = xp->security;
int rc = 0;
if (ctx)
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
return rc;
}
/*
* 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,
u32 secid)
{
int err;
BUG_ON(!x);
err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
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 implementation that authorizes deletion of labeled SAs.
*/
int selinux_xfrm_state_delete(struct xfrm_state *x)
{
struct task_security_struct *tsec = current->security;
struct xfrm_sec_ctx *ctx = x->security;
int rc = 0;
if (ctx)
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
return rc;
}
/*
* 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,
struct avc_audit_data *ad)
{
int i, rc = 0;
struct sec_path *sp;
u32 sel_sid = SECINITSID_UNLABELED;
sp = skb->sp;
if (sp) {
for (i = 0; i < sp->len; i++) {
struct xfrm_state *x = sp->xvec[i];
if (x && selinux_authorizable_xfrm(x)) {
struct xfrm_sec_ctx *ctx = x->security;
sel_sid = ctx->ctx_sid;
break;
}
}
}
/*
* This check even when there's no association involved is
* intended, according to Trent Jaeger, to make sure a
* process can't engage in non-ipsec communication unless
* explicitly allowed by policy.
*/
rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
ASSOCIATION__RECVFROM, ad);
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 the selinux_xfrm_state_pol_flow_match hook above.
*/
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad, u8 proto)
{
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 out;
}
}
switch (proto) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_COMP:
/*
* We should have already seen this packet once before
* it underwent xfrm(s). No need to subject it to the
* unlabeled check.
*/
goto out;
default:
break;
}
/*
* This check even when there's no association involved is
* intended, according to Trent Jaeger, to make sure a
* process can't engage in non-ipsec communication unless
* explicitly allowed by policy.
*/
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
ASSOCIATION__SENDTO, ad);
out:
return rc;
}