Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
99
security/Kconfig
Normal file
99
security/Kconfig
Normal 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
18
security/Makefile
Normal 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
101
security/capability.c
Normal 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
345
security/commoncap.c
Normal 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
1132
security/dummy.c
Normal file
File diff suppressed because it is too large
Load Diff
345
security/inode.c
Normal file
345
security/inode.c
Normal 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
16
security/keys/Makefile
Normal 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
86
security/keys/compat.c
Normal 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
164
security/keys/internal.h
Normal 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
1032
security/keys/key.c
Normal file
File diff suppressed because it is too large
Load Diff
1142
security/keys/keyctl.c
Normal file
1142
security/keys/keyctl.c
Normal file
File diff suppressed because it is too large
Load Diff
974
security/keys/keyring.c
Normal file
974
security/keys/keyring.c
Normal 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
107
security/keys/permission.c
Normal 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
263
security/keys/proc.c
Normal 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;
|
||||
|
||||
}
|
||||
765
security/keys/process_keys.c
Normal file
765
security/keys/process_keys.c
Normal 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
522
security/keys/request_key.c
Normal 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);
|
||||
268
security/keys/request_key_auth.c
Normal file
268
security/keys/request_key_auth.c
Normal 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(¤t->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,
|
||||
¤t->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(¤t->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(¤t->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() */
|
||||
218
security/keys/user_defined.c
Normal file
218
security/keys/user_defined.c
Normal 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
141
security/root_plug.c
Normal 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
179
security/security.c
Normal 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
163
security/selinux/Kconfig
Normal 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
12
security/selinux/Makefile
Normal 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
915
security/selinux/avc.c
Normal 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(¬if_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(¬if_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;
|
||||
}
|
||||
87
security/selinux/exports.c
Normal file
87
security/selinux/exports.c
Normal 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
5051
security/selinux/hooks.c
Normal file
File diff suppressed because it is too large
Load Diff
33
security/selinux/include/av_inherit.h
Normal file
33
security/selinux/include/av_inherit.h
Normal 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)
|
||||
262
security/selinux/include/av_perm_to_string.h
Normal file
262
security/selinux/include/av_perm_to_string.h
Normal 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")
|
||||
1004
security/selinux/include/av_permissions.h
Normal file
1004
security/selinux/include/av_permissions.h
Normal file
File diff suppressed because it is too large
Load Diff
139
security/selinux/include/avc.h
Normal file
139
security/selinux/include/avc.h
Normal 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_ */
|
||||
|
||||
38
security/selinux/include/avc_ss.h
Normal file
38
security/selinux/include/avc_ss.h
Normal 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_ */
|
||||
|
||||
65
security/selinux/include/class_to_string.h
Normal file
65
security/selinux/include/class_to_string.h
Normal 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")
|
||||
58
security/selinux/include/common_perm_to_string.h
Normal file
58
security/selinux/include/common_perm_to_string.h
Normal 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)
|
||||
|
||||
22
security/selinux/include/conditional.h
Normal file
22
security/selinux/include/conditional.h
Normal 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
|
||||
102
security/selinux/include/flask.h
Normal file
102
security/selinux/include/flask.h
Normal 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
|
||||
33
security/selinux/include/initial_sid_to_string.h
Normal file
33
security/selinux/include/initial_sid_to_string.h
Normal 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",
|
||||
};
|
||||
|
||||
21
security/selinux/include/netif.h
Normal file
21
security/selinux/include/netif.h
Normal 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_ */
|
||||
|
||||
123
security/selinux/include/objsec.h
Normal file
123
security/selinux/include/objsec.h
Normal 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_ */
|
||||
106
security/selinux/include/security.h
Normal file
106
security/selinux/include/security.h
Normal 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_ */
|
||||
|
||||
124
security/selinux/include/selinux_netlabel.h
Normal file
124
security/selinux/include/selinux_netlabel.h
Normal 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
|
||||
75
security/selinux/include/xfrm.h
Normal file
75
security/selinux/include/xfrm.h
Normal 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
269
security/selinux/netif.c
Normal 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
115
security/selinux/netlink.c
Normal 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
175
security/selinux/nlmsgtab.c
Normal 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
1386
security/selinux/selinuxfs.c
Normal file
File diff suppressed because it is too large
Load Diff
9
security/selinux/ss/Makefile
Normal file
9
security/selinux/ss/Makefile
Normal 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
454
security/selinux/ss/avtab.c
Normal 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);
|
||||
}
|
||||
84
security/selinux/ss/avtab.h
Normal file
84
security/selinux/ss/avtab.h
Normal 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_ */
|
||||
|
||||
509
security/selinux/ss/conditional.c
Normal file
509
security/selinux/ss/conditional.c
Normal 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;
|
||||
}
|
||||
77
security/selinux/ss/conditional.h
Normal file
77
security/selinux/ss/conditional.h
Normal 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_ */
|
||||
61
security/selinux/ss/constraint.h
Normal file
61
security/selinux/ss/constraint.h
Normal 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_ */
|
||||
130
security/selinux/ss/context.h
Normal file
130
security/selinux/ss/context.h
Normal 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_ */
|
||||
|
||||
415
security/selinux/ss/ebitmap.c
Normal file
415
security/selinux/ss/ebitmap.c
Normal 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;
|
||||
}
|
||||
98
security/selinux/ss/ebitmap.h
Normal file
98
security/selinux/ss/ebitmap.h
Normal 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_ */
|
||||
165
security/selinux/ss/hashtab.c
Normal file
165
security/selinux/ss/hashtab.c
Normal 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;
|
||||
}
|
||||
87
security/selinux/ss/hashtab.h
Normal file
87
security/selinux/ss/hashtab.h
Normal 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
659
security/selinux/ss/mls.c
Normal 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
85
security/selinux/ss/mls.h
Normal 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 */
|
||||
|
||||
56
security/selinux/ss/mls_types.h
Normal file
56
security/selinux/ss/mls_types.h
Normal 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_ */
|
||||
1899
security/selinux/ss/policydb.c
Normal file
1899
security/selinux/ss/policydb.c
Normal file
File diff suppressed because it is too large
Load Diff
279
security/selinux/ss/policydb.h
Normal file
279
security/selinux/ss/policydb.h
Normal 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_ */
|
||||
|
||||
2775
security/selinux/ss/services.c
Normal file
2775
security/selinux/ss/services.c
Normal file
File diff suppressed because it is too large
Load Diff
15
security/selinux/ss/services.h
Normal file
15
security/selinux/ss/services.h
Normal 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_ */
|
||||
|
||||
304
security/selinux/ss/sidtab.c
Normal file
304
security/selinux/ss/sidtab.c
Normal 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);
|
||||
}
|
||||
59
security/selinux/ss/sidtab.h
Normal file
59
security/selinux/ss/sidtab.h
Normal 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_ */
|
||||
|
||||
|
||||
44
security/selinux/ss/symtab.c
Normal file
44
security/selinux/ss/symtab.c
Normal 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;
|
||||
}
|
||||
|
||||
23
security/selinux/ss/symtab.h
Normal file
23
security/selinux/ss/symtab.h
Normal 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
485
security/selinux/xfrm.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user