Commit 7bbf0e05 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] selinux merge

From Stephen Smalley <sds@epoch.ncsc.mil>

This has been in -mm for a few weeks and James Morris has been
regression testing each release.
parent ad55c575
......@@ -44,5 +44,7 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
endmenu
......@@ -2,6 +2,8 @@
# Makefile for the kernel security code
#
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
obj-y += capability.o
......@@ -9,5 +11,9 @@ endif
# Object file lists
obj-$(CONFIG_SECURITY) += security.o dummy.o
# Must precede capability.o in order to stack properly.
ifeq ($(CONFIG_SECURITY_SELINUX),y)
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
endif
obj-$(CONFIG_SECURITY_CAPABILITIES) += capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
config SECURITY_SELINUX
bool "NSA SELinux Support"
depends on SECURITY
default n
help
This enables 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_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_MLS
bool "NSA SELinux MLS policy (EXPERIMENTAL)"
depends on SECURITY_SELINUX && EXPERIMENTAL
default n
help
This enables the NSA SELinux Multi-Level Security (MLS) policy in
addition to the default RBAC/TE policy. This policy is
experimental and has not been configured for use. Unless you
specifically want to experiment with MLS, say N.
#
# Makefile for building the SELinux module as part of the kernel tree.
#
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
selinux-objs := avc.o hooks.o selinuxfs.o
EXTRA_CFLAGS += -Isecurity/selinux/include
/*
* Implementation of the kernel access vector cache (AVC).
*
* Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
* 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/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include "avc.h"
#include "avc_ss.h"
#include "class_to_string.h"
#include "common_perm_to_string.h"
#include "av_inherit.h"
#include "av_perm_to_string.h"
#include "objsec.h"
#define AVC_CACHE_SLOTS 512
#define AVC_CACHE_MAXNODES 410
struct avc_entry {
u32 ssid;
u32 tsid;
u16 tclass;
struct av_decision avd;
int used; /* used recently */
};
struct avc_node {
struct avc_entry ae;
struct avc_node *next;
};
struct avc_cache {
struct avc_node *slots[AVC_CACHE_SLOTS];
u32 lru_hint; /* LRU hint for reclaim scan */
u32 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;
};
static spinlock_t avc_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t avc_log_lock = SPIN_LOCK_UNLOCKED;
static struct avc_node *avc_node_freelist = NULL;
static struct avc_cache avc_cache;
static char *avc_audit_buffer = NULL;
static unsigned avc_cache_stats[AVC_NSTATS];
static struct avc_callback_node *avc_callbacks = NULL;
static unsigned int avc_log_level = 4; /* default: KERN_WARNING */
static char avc_level_string[4] = "< >";
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
*/
void avc_dump_av(u16 tclass, u32 av)
{
char **common_pts = 0;
u32 common_base = 0;
int i, i2, perm;
if (av == 0) {
printk(" 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;
}
}
printk(" {");
i = 0;
perm = 1;
while (perm < common_base) {
if (perm & av)
printk(" %s", common_pts[i]);
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))
printk(" %s", av_perm_to_string[i2].name);
}
i++;
perm <<= 1;
}
printk(" }");
}
/**
* 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
*/
void avc_dump_query(u32 ssid, u32 tsid, u16 tclass)
{
int rc;
char *scontext;
u32 scontext_len;
rc = security_sid_to_context(ssid, &scontext, &scontext_len);
if (rc)
printk("ssid=%d", ssid);
else {
printk("scontext=%s", scontext);
kfree(scontext);
}
rc = security_sid_to_context(tsid, &scontext, &scontext_len);
if (rc)
printk(" tsid=%d", tsid);
else {
printk(" tcontext=%s", scontext);
kfree(scontext);
}
printk(" tclass=%s", class_to_string[tclass]);
}
/**
* avc_init - Initialize the AVC.
*
* Initialize the access vector cache.
*/
void avc_init(void)
{
struct avc_node *new;
int i;
for (i = 0; i < AVC_NSTATS; i++)
avc_cache_stats[i] = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++)
avc_cache.slots[i] = 0;
avc_cache.lru_hint = 0;
avc_cache.active_nodes = 0;
avc_cache.latest_notif = 0;
for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
printk(KERN_WARNING "avc: only able to allocate "
"%d entries\n", i);
break;
}
memset(new, 0, sizeof(*new));
new->next = avc_node_freelist;
avc_node_freelist = new;
}
avc_audit_buffer = (char *)__get_free_page(GFP_ATOMIC);
if (!avc_audit_buffer)
panic("AVC: unable to allocate audit buffer\n");
avc_level_string[1] = '0' + avc_log_level;
}
#if 0
static void avc_hash_eval(char *tag)
{
int i, chain_len, max_chain_len, slots_used;
struct avc_node *node;
unsigned long flags;
spin_lock_irqsave(&avc_lock,flags);
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
node = avc_cache.slots[i];
if (node) {
slots_used++;
chain_len = 0;
while (node) {
chain_len++;
node = node->next;
}
if (chain_len > max_chain_len)
max_chain_len = chain_len;
}
}
spin_unlock_irqrestore(&avc_lock,flags);
printk(KERN_INFO "\n");
printk(KERN_INFO "%s avc: %d entries and %d/%d buckets used, longest "
"chain length %d\n", tag, avc_cache.active_nodes, slots_used,
AVC_CACHE_SLOTS, max_chain_len);
}
#else
static inline void avc_hash_eval(char *tag)
{ }
#endif
static inline struct avc_node *avc_reclaim_node(void)
{
struct avc_node *prev, *cur;
int hvalue, try;
hvalue = avc_cache.lru_hint;
for (try = 0; try < 2; try++) {
do {
prev = NULL;
cur = avc_cache.slots[hvalue];
while (cur) {
if (!cur->ae.used)
goto found;
cur->ae.used = 0;
prev = cur;
cur = cur->next;
}
hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
} while (hvalue != avc_cache.lru_hint);
}
panic("avc_reclaim_node");
found:
avc_cache.lru_hint = hvalue;
if (prev == NULL)
avc_cache.slots[hvalue] = cur->next;
else
prev->next = cur->next;
return cur;
}
static inline struct avc_node *avc_claim_node(u32 ssid,
u32 tsid, u16 tclass)
{
struct avc_node *new;
int hvalue;
hvalue = avc_hash(ssid, tsid, tclass);
if (avc_node_freelist) {
new = avc_node_freelist;
avc_node_freelist = avc_node_freelist->next;
avc_cache.active_nodes++;
} else {
new = avc_reclaim_node();
if (!new)
goto out;
}
new->ae.used = 1;
new->ae.ssid = ssid;
new->ae.tsid = tsid;
new->ae.tclass = tclass;
new->next = avc_cache.slots[hvalue];
avc_cache.slots[hvalue] = new;
out:
return new;
}
static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid,
u16 tclass, int *probes)
{
struct avc_node *cur;
int hvalue;
int tprobes = 1;
hvalue = avc_hash(ssid, tsid, tclass);
cur = avc_cache.slots[hvalue];
while (cur != NULL &&
(ssid != cur->ae.ssid ||
tclass != cur->ae.tclass ||
tsid != cur->ae.tsid)) {
tprobes++;
cur = cur->next;
}
if (cur == NULL) {
/* cache miss */
goto out;
}
/* cache hit */
if (probes)
*probes = tprobes;
cur->ae.used = 1;
out:
return cur;
}
/**
* 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
* @aeref: AVC entry reference
*
* 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 updates @aeref to refer to the
* entry and returns %0. Otherwise, this function
* returns -%ENOENT.
*/
int avc_lookup(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct avc_entry_ref *aeref)
{
struct avc_node *node;
int probes, rc = 0;
avc_cache_stats_incr(AVC_CAV_LOOKUPS);
node = avc_search_node(ssid, tsid, tclass,&probes);
if (node && ((node->ae.avd.decided & requested) == requested)) {
avc_cache_stats_incr(AVC_CAV_HITS);
avc_cache_stats_add(AVC_CAV_PROBES,probes);
aeref->ae = &node->ae;
goto out;
}
avc_cache_stats_incr(AVC_CAV_MISSES);
rc = -ENOENT;
out:
return rc;
}
/**
* avc_insert - Insert an AVC entry.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @ae: AVC entry
* @aeref: AVC entry reference
*
* 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, updates
* @aeref to refer to the entry, and returns %0.
* Otherwise, this function returns -%EAGAIN.
*/
int avc_insert(u32 ssid, u32 tsid, u16 tclass,
struct avc_entry *ae, struct avc_entry_ref *aeref)
{
struct avc_node *node;
int rc = 0;
if (ae->avd.seqno < avc_cache.latest_notif) {
printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n",
ae->avd.seqno, avc_cache.latest_notif);
rc = -EAGAIN;
goto out;
}
node = avc_claim_node(ssid, tsid, tclass);
if (!node) {
rc = -ENOMEM;
goto out;
}
node->ae.avd.allowed = ae->avd.allowed;
node->ae.avd.decided = ae->avd.decided;
node->ae.avd.auditallow = ae->avd.auditallow;
node->ae.avd.auditdeny = ae->avd.auditdeny;
node->ae.avd.seqno = ae->avd.seqno;
aeref->ae = &node->ae;
out:
return rc;
}
static inline void avc_print_ipv4_addr(u32 addr, u16 port, char *name1, char *name2)
{
if (addr)
printk(" %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
if (port)
printk(" %s=%d", name2, ntohs(port));
}
/*
* Copied from net/core/utils.c:net_ratelimit and modified for
* use by the AVC audit facility.
*/
#define AVC_MSG_COST 5*HZ
#define AVC_MSG_BURST 10*5*HZ
/*
* This enforces a rate limit: not more than one kernel message
* every 5secs to make a denial-of-service attack impossible.
*/
static int avc_ratelimit(void)
{
static spinlock_t ratelimit_lock = SPIN_LOCK_UNLOCKED;
static unsigned long toks = 10*5*HZ;
static unsigned long last_msg;
static int missed, rc = 0;
unsigned long flags;
unsigned long now = jiffies;
spin_lock_irqsave(&ratelimit_lock, flags);
toks += now - last_msg;
last_msg = now;
if (toks > AVC_MSG_BURST)
toks = AVC_MSG_BURST;
if (toks >= AVC_MSG_COST) {
int lost = missed;
missed = 0;
toks -= AVC_MSG_COST;
spin_unlock_irqrestore(&ratelimit_lock, flags);
if (lost)
printk(KERN_WARNING "AVC: %d messages suppressed.\n",
lost);
rc = 1;
goto out;
}
missed++;
spin_unlock_irqrestore(&ratelimit_lock, flags);
out:
return rc;
}
static inline int check_avc_ratelimit(void)
{
if (selinux_enforcing)
return avc_ratelimit();
else {
/* If permissive, then never suppress messages. */
return 1;
}
}
/**
* 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;
char *p;
u32 denied, audited;
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;
}
if (!check_avc_ratelimit())
return;
/* prevent overlapping printks */
spin_lock_irq(&avc_log_lock);
printk("%s\n", avc_level_string);
printk("%savc: %s ", avc_level_string, denied ? "denied" : "granted");
avc_dump_av(tclass,audited);
printk(" for ");
if (a && a->tsk)
tsk = a->tsk;
if (tsk && tsk->pid) {
struct mm_struct *mm;
struct vm_area_struct *vma;
printk(" pid=%d", tsk->pid);
if (tsk == current)
mm = current->mm;
else
mm = get_task_mm(tsk);
if (mm) {
if (down_read_trylock(&mm->mmap_sem)) {
vma = mm->mmap;
while (vma) {
if ((vma->vm_flags & VM_EXECUTABLE) &&
vma->vm_file) {
p = d_path(vma->vm_file->f_dentry,
vma->vm_file->f_vfsmnt,
avc_audit_buffer,
PAGE_SIZE);
printk(" exe=%s", p);
break;
}
vma = vma->vm_next;
}
up_read(&mm->mmap_sem);
}
if (tsk != current)
mmput(mm);
} else {
printk(" comm=%s", tsk->comm);
}
}
if (a) {
switch (a->type) {
case AVC_AUDIT_DATA_IPC:
printk(" key=%d", a->u.ipc_id);
break;
case AVC_AUDIT_DATA_CAP:
printk(" capability=%d", a->u.cap);
break;
case AVC_AUDIT_DATA_FS:
if (a->u.fs.dentry) {
if (a->u.fs.mnt) {
p = d_path(a->u.fs.dentry,
a->u.fs.mnt,
avc_audit_buffer,
PAGE_SIZE);
if (p)
printk(" path=%s", p);
}
inode = a->u.fs.dentry->d_inode;
} else if (a->u.fs.inode) {
inode = a->u.fs.inode;
}
if (inode)
printk(" 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;
struct inet_opt *inet;
switch (sk->sk_family) {
case AF_INET:
inet = inet_sk(sk);
avc_print_ipv4_addr(inet->rcv_saddr,
inet->sport,
"laddr", "lport");
avc_print_ipv4_addr(inet->daddr,
inet->dport,
"faddr", "fport");
break;
case AF_UNIX:
u = unix_sk(sk);
if (u->dentry) {
p = d_path(u->dentry,
u->mnt,
avc_audit_buffer,
PAGE_SIZE);
printk(" path=%s", p);
} else if (u->addr) {
p = avc_audit_buffer;
memcpy(p,
u->addr->name->sun_path,
u->addr->len-sizeof(short));
if (*p == 0) {
*p = '@';
p += u->addr->len-sizeof(short);
*p = 0;
}
printk(" path=%s",
avc_audit_buffer);
}
break;
}
}
if (a->u.net.daddr) {
printk(" daddr=%d.%d.%d.%d",
NIPQUAD(a->u.net.daddr));
if (a->u.net.port)
printk(" dest=%d", a->u.net.port);
} else if (a->u.net.port)
printk(" port=%d", a->u.net.port);
if (a->u.net.skb) {
struct sk_buff *skb = a->u.net.skb;
if ((skb->protocol == htons(ETH_P_IP)) &&
skb->nh.iph) {
u16 source = 0, dest = 0;
u8 protocol = skb->nh.iph->protocol;
if (protocol == IPPROTO_TCP &&
skb->h.th) {
source = skb->h.th->source;
dest = skb->h.th->dest;
}
if (protocol == IPPROTO_UDP &&
skb->h.uh) {
source = skb->h.uh->source;
dest = skb->h.uh->dest;
}
avc_print_ipv4_addr(skb->nh.iph->saddr,
source,
"saddr", "source");
avc_print_ipv4_addr(skb->nh.iph->daddr,
dest,
"daddr", "dest");
}
}
if (a->u.net.netif)
printk(" netif=%s", a->u.net.netif);
break;
}
}
printk(" ");
avc_dump_query(ssid, tsid, tclass);
printk("\n");
spin_unlock_irq(&avc_log_lock);
}
/**
* 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);
}
static inline void avc_update_node(u32 event, struct avc_node *node, u32 perms)
{
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;
}
}
static int avc_update_cache(u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms)
{
struct avc_node *node;
int i;
unsigned long flags;
spin_lock_irqsave(&avc_lock,flags);
if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
/* apply to all matching nodes */
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
for (node = avc_cache.slots[i]; node;
node = node->next) {
if (avc_sidcmp(ssid, node->ae.ssid) &&
avc_sidcmp(tsid, node->ae.tsid) &&
tclass == node->ae.tclass) {
avc_update_node(event,node,perms);
}
}
}
} else {
/* apply to one node */
node = avc_search_node(ssid, tsid, tclass, 0);
if (node) {
avc_update_node(event,node,perms);
}
}
spin_unlock_irqrestore(&avc_lock,flags);
return 0;
}
static int avc_control(u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms,
u32 seqno, u32 *out_retained)
{
struct avc_callback_node *c;
u32 tretained = 0, cretained = 0;
int rc = 0;
unsigned long flags;
/*
* try_revoke only removes permissions from the cache
* state if they are not retained by the object manager.
* Hence, try_revoke must wait until after the callbacks have
* been invoked to update the cache state.
*/
if (event != AVC_CALLBACK_TRY_REVOKE)
avc_update_cache(event,ssid,tsid,tclass,perms);
for (c = avc_callbacks; c; c = c->next)
{
if ((c->events & event) &&
avc_sidcmp(c->ssid, ssid) &&
avc_sidcmp(c->tsid, tsid) &&
c->tclass == tclass &&
(c->perms & perms)) {
cretained = 0;
rc = c->callback(event, ssid, tsid, tclass,
(c->perms & perms),
&cretained);
if (rc)
goto out;
tretained |= cretained;
}
}
if (event == AVC_CALLBACK_TRY_REVOKE) {
/* revoke any unretained permissions */
perms &= ~tretained;
avc_update_cache(event,ssid,tsid,tclass,perms);
*out_retained = tretained;
}
spin_lock_irqsave(&avc_lock,flags);
if (seqno > avc_cache.latest_notif)
avc_cache.latest_notif = seqno;
spin_unlock_irqrestore(&avc_lock,flags);
out:
return rc;
}
/**
* avc_ss_grant - Grant previously denied permissions.
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions to grant
* @seqno: policy sequence number
*/
int avc_ss_grant(u32 ssid, u32 tsid, u16 tclass,
u32 perms, u32 seqno)
{
return avc_control(AVC_CALLBACK_GRANT,
ssid, tsid, tclass, perms, seqno, 0);
}
/**
* avc_ss_try_revoke - Try to revoke previously granted permissions.
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions to grant
* @seqno: policy sequence number
* @out_retained: subset of @perms that are retained
*
* Try to revoke previously granted permissions, but
* only if they are not retained as migrated permissions.
* Return the subset of permissions that are retained via @out_retained.
*/
int avc_ss_try_revoke(u32 ssid, u32 tsid, u16 tclass,
u32 perms, u32 seqno, u32 *out_retained)
{
return avc_control(AVC_CALLBACK_TRY_REVOKE,
ssid, tsid, tclass, perms, seqno, out_retained);
}
/**
* avc_ss_revoke - Revoke previously granted permissions.
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions to grant
* @seqno: policy sequence number
*
* Revoke previously granted permissions, even if
* they are retained as migrated permissions.
*/
int avc_ss_revoke(u32 ssid, u32 tsid, u16 tclass,
u32 perms, u32 seqno)
{
return avc_control(AVC_CALLBACK_REVOKE,
ssid, tsid, tclass, perms, seqno, 0);
}
/**
* 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;
struct avc_node *node, *tmp;
unsigned long flags;
avc_hash_eval("reset");
spin_lock_irqsave(&avc_lock,flags);
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
node = avc_cache.slots[i];
while (node) {
tmp = node;
node = node->next;
tmp->ae.ssid = tmp->ae.tsid = SECSID_NULL;
tmp->ae.tclass = SECCLASS_NULL;
tmp->ae.avd.allowed = tmp->ae.avd.decided = 0;
tmp->ae.avd.auditallow = tmp->ae.avd.auditdeny = 0;
tmp->ae.used = 0;
tmp->next = avc_node_freelist;
avc_node_freelist = tmp;
avc_cache.active_nodes--;
}
avc_cache.slots[i] = 0;
}
avc_cache.lru_hint = 0;
spin_unlock_irqrestore(&avc_lock,flags);
for (i = 0; i < AVC_NSTATS; i++)
avc_cache_stats[i] = 0;
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
rc = c->callback(AVC_CALLBACK_RESET,
0, 0, 0, 0, 0);
if (rc)
goto out;
}
}
spin_lock_irqsave(&avc_lock,flags);
if (seqno > avc_cache.latest_notif)
avc_cache.latest_notif = seqno;
spin_unlock_irqrestore(&avc_lock,flags);
out:
return rc;
}
/**
* avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions to grant
* @seqno: policy sequence number
* @enable: enable flag.
*/
int avc_ss_set_auditallow(u32 ssid, u32 tsid, u16 tclass,
u32 perms, u32 seqno, u32 enable)
{
if (enable)
return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
ssid, tsid, tclass, perms, seqno, 0);
else
return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
ssid, tsid, tclass, perms, seqno, 0);
}
/**
* avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
* @ssid: source security identifier or %SECSID_WILD
* @tsid: target security identifier or %SECSID_WILD
* @tclass: target security class
* @perms: permissions to grant
* @seqno: policy sequence number
* @enable: enable flag.
*/
int avc_ss_set_auditdeny(u32 ssid, u32 tsid, u16 tclass,
u32 perms, u32 seqno, u32 enable)
{
if (enable)
return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
ssid, tsid, tclass, perms, seqno, 0);
else
return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
ssid, tsid, tclass, perms, seqno, 0);
}
/**
* 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
* @aeref: AVC entry reference
* @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. Update @aeref to refer to an AVC
* entry with the resulting decisions, and 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,
struct avc_entry_ref *aeref, struct av_decision *avd)
{
struct avc_entry *ae;
int rc = 0;
unsigned long flags;
struct avc_entry entry;
u32 denied;
struct avc_entry_ref ref;
if (!aeref) {
avc_entry_ref_init(&ref);
aeref = &ref;
}
spin_lock_irqsave(&avc_lock, flags);
avc_cache_stats_incr(AVC_ENTRY_LOOKUPS);
ae = aeref->ae;
if (ae) {
if (ae->ssid == ssid &&
ae->tsid == tsid &&
ae->tclass == tclass &&
((ae->avd.decided & requested) == requested)) {
avc_cache_stats_incr(AVC_ENTRY_HITS);
ae->used = 1;
} else {
avc_cache_stats_incr(AVC_ENTRY_DISCARDS);
ae = 0;
}
}
if (!ae) {
avc_cache_stats_incr(AVC_ENTRY_MISSES);
rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
if (rc) {
spin_unlock_irqrestore(&avc_lock,flags);
rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd);
if (rc)
goto out;
spin_lock_irqsave(&avc_lock, flags);
rc = avc_insert(ssid,tsid,tclass,&entry,aeref);
if (rc) {
spin_unlock_irqrestore(&avc_lock,flags);
goto out;
}
}
ae = aeref->ae;
}
if (avd)
memcpy(avd, &ae->avd, sizeof(*avd));
denied = requested & ~(ae->avd.allowed);
if (!requested || denied) {
if (selinux_enforcing) {
spin_unlock_irqrestore(&avc_lock,flags);
rc = -EACCES;
goto out;
} else {
ae->avd.allowed |= requested;
spin_unlock_irqrestore(&avc_lock,flags);
goto out;
}
}
spin_unlock_irqrestore(&avc_lock,flags);
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
* @aeref: AVC entry reference
* @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. Update @aeref to refer to an AVC
* entry with the resulting decisions. 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_entry_ref *aeref,
struct avc_audit_data *auditdata)
{
struct av_decision avd;
int rc;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
static int __init avc_log_level_setup(char *str)
{
avc_log_level = simple_strtol(str, NULL, 0);
if (avc_log_level > 7)
avc_log_level = 7;
return 1;
}
__setup("avc_log_level=", avc_log_level_setup);
/*
* NSA Security-Enhanced Linux (SELinux) security module
*
* This file contains the SELinux hook function implementations.
*
* Authors: 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.
*/
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/xattr.h>
#include <linux/capability.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/file.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/ext2_fs.h>
#include <linux/proc_fs.h>
#include <linux/kd.h>
#include <net/icmp.h>
#include <net/ip.h> /* for sysctl_local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/ioctls.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h> /* for network interface checks */
#include <linux/netlink.h>
#include <linux/tcp.h>
#include <linux/quota.h>
#include <linux/un.h> /* for Unix socket types */
#include <net/af_unix.h> /* for Unix socket types */
#include "avc.h"
#include "objsec.h"
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
static int __init enforcing_setup(char *str)
{
selinux_enforcing = simple_strtol(str,NULL,0);
return 1;
}
__setup("enforcing=", enforcing_setup);
#endif
/* Original (dummy) security module. */
static struct security_operations *original_ops = NULL;
/* Minimal support for a secondary security module,
just to allow the use of the dummy or capability modules.
The owlsm module can alternatively be used as a secondary
module as long as CONFIG_OWLSM_FD is not enabled. */
static struct security_operations *secondary_ops = NULL;
/* Lists of inode and superblock security structures initialized
before the policy was loaded. */
static LIST_HEAD(inode_security_head);
static spinlock_t inode_security_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(superblock_security_head);
static spinlock_t sb_security_lock = SPIN_LOCK_UNLOCKED;
/* Allocate and free functions for each kind of security blob. */
static int task_alloc_security(struct task_struct *task)
{
struct task_security_struct *tsec;
tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
return -ENOMEM;
memset(tsec, 0, sizeof(struct task_security_struct));
tsec->magic = SELINUX_MAGIC;
tsec->task = task;
tsec->osid = tsec->sid = SECINITSID_UNLABELED;
task->security = tsec;
return 0;
}
static void task_free_security(struct task_struct *task)
{
struct task_security_struct *tsec = task->security;
if (!tsec || tsec->magic != SELINUX_MAGIC)
return;
task->security = NULL;
kfree(tsec);
}
static int inode_alloc_security(struct inode *inode)
{
struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec;
isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
memset(isec, 0, sizeof(struct inode_security_struct));
init_MUTEX(&isec->sem);
INIT_LIST_HEAD(&isec->list);
isec->magic = SELINUX_MAGIC;
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
if (tsec && tsec->magic == SELINUX_MAGIC)
isec->task_sid = tsec->sid;
else
isec->task_sid = SECINITSID_UNLABELED;
inode->i_security = isec;
return 0;
}
static void inode_free_security(struct inode *inode)
{
struct inode_security_struct *isec = inode->i_security;
if (!isec || isec->magic != SELINUX_MAGIC)
return;
spin_lock(&inode_security_lock);
if (!list_empty(&isec->list))
list_del_init(&isec->list);
spin_unlock(&inode_security_lock);
inode->i_security = NULL;
kfree(isec);
}
static int file_alloc_security(struct file *file)
{
struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec;
fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
if (!fsec)
return -ENOMEM;
memset(fsec, 0, sizeof(struct file_security_struct));
fsec->magic = SELINUX_MAGIC;
fsec->file = file;
if (tsec && tsec->magic == SELINUX_MAGIC) {
fsec->sid = tsec->sid;
fsec->fown_sid = tsec->sid;
} else {
fsec->sid = SECINITSID_UNLABELED;
fsec->fown_sid = SECINITSID_UNLABELED;
}
file->f_security = fsec;
return 0;
}
static void file_free_security(struct file *file)
{
struct file_security_struct *fsec = file->f_security;
if (!fsec || fsec->magic != SELINUX_MAGIC)
return;
file->f_security = NULL;
kfree(fsec);
}
static int superblock_alloc_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec;
sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
if (!sbsec)
return -ENOMEM;
memset(sbsec, 0, sizeof(struct superblock_security_struct));
init_MUTEX(&sbsec->sem);
INIT_LIST_HEAD(&sbsec->list);
sbsec->magic = SELINUX_MAGIC;
sbsec->sb = sb;
sbsec->sid = SECINITSID_UNLABELED;
sb->s_security = sbsec;
return 0;
}
static void superblock_free_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
if (!sbsec || sbsec->magic != SELINUX_MAGIC)
return;
spin_lock(&sb_security_lock);
if (!list_empty(&sbsec->list))
list_del_init(&sbsec->list);
spin_unlock(&sb_security_lock);
sb->s_security = NULL;
kfree(sbsec);
}
/* The security server must be initialized before
any labeling or access decisions can be provided. */
extern int ss_initialized;
/* The file system's label must be initialized prior to use. */
static char *labeling_behaviors[5] = {
"uses xattr",
"uses transition SIDs",
"uses task SIDs",
"uses genfs_contexts",
"not configured for labeling"
};
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
static inline int inode_doinit(struct inode *inode)
{
return inode_doinit_with_dentry(inode, NULL);
}
static int superblock_doinit(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
struct dentry *root = sb->s_root;
struct inode *inode = root->d_inode;
int rc = 0;
down(&sbsec->sem);
if (sbsec->initialized)
goto out;
if (!ss_initialized) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
spin_lock(&sb_security_lock);
if (list_empty(&sbsec->list))
list_add(&sbsec->list, &superblock_security_head);
spin_unlock(&sb_security_lock);
goto out;
}
/* Determine the labeling behavior to use for this filesystem type. */
rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
__FUNCTION__, sb->s_type->name, rc);
goto out;
}
if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
/* Make sure that the xattr handler exists and that no
error other than -ENODATA is returned by getxattr on
the root directory. -ENODATA is ok, as this may be
the first boot of the SELinux kernel before we have
assigned xattr values to the filesystem. */
if (!inode->i_op->getxattr) {
printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
"xattr support\n", sb->s_id, sb->s_type->name);
rc = -EOPNOTSUPP;
goto out;
}
rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
if (rc < 0 && rc != -ENODATA) {
if (rc == -EOPNOTSUPP)
printk(KERN_WARNING "SELinux: (dev %s, type "
"%s) has no security xattr handler\n",
sb->s_id, sb->s_type->name);
else
printk(KERN_WARNING "SELinux: (dev %s, type "
"%s) getxattr errno %d\n", sb->s_id,
sb->s_type->name, -rc);
goto out;
}
}
if (strcmp(sb->s_type->name, "proc") == 0)
sbsec->proc = 1;
sbsec->initialized = 1;
printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n",
sb->s_id, sb->s_type->name,
labeling_behaviors[sbsec->behavior-1]);
/* Initialize the root inode. */
rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
out:
up(&sbsec->sem);
return rc;
}
static inline u16 inode_mode_to_security_class(umode_t mode)
{
switch (mode & S_IFMT) {
case S_IFSOCK:
return SECCLASS_SOCK_FILE;
case S_IFLNK:
return SECCLASS_LNK_FILE;
case S_IFREG:
return SECCLASS_FILE;
case S_IFBLK:
return SECCLASS_BLK_FILE;
case S_IFDIR:
return SECCLASS_DIR;
case S_IFCHR:
return SECCLASS_CHR_FILE;
case S_IFIFO:
return SECCLASS_FIFO_FILE;
}
return SECCLASS_FILE;
}
static inline u16 socket_type_to_security_class(int family, int type)
{
switch (family) {
case PF_UNIX:
switch (type) {
case SOCK_STREAM:
return SECCLASS_UNIX_STREAM_SOCKET;
case SOCK_DGRAM:
return SECCLASS_UNIX_DGRAM_SOCKET;
}
case PF_INET:
case PF_INET6:
switch (type) {
case SOCK_STREAM:
return SECCLASS_TCP_SOCKET;
case SOCK_DGRAM:
return SECCLASS_UDP_SOCKET;
case SOCK_RAW:
return SECCLASS_RAWIP_SOCKET;
}
case PF_NETLINK:
return SECCLASS_NETLINK_SOCKET;
case PF_PACKET:
return SECCLASS_PACKET_SOCKET;
case PF_KEY:
return SECCLASS_KEY_SOCKET;
}
return SECCLASS_SOCKET;
}
#ifdef CONFIG_PROC_FS
static int selinux_proc_get_sid(struct proc_dir_entry *de,
u16 tclass,
u32 *sid)
{
int buflen, rc;
char *buffer, *path, *end;
buffer = (char*)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;
buflen = PAGE_SIZE;
end = buffer+buflen;
*--end = '\0';
buflen--;
path = end-1;
*path = '/';
while (de && de != de->parent) {
buflen -= de->namelen + 1;
if (buflen < 0)
break;
end -= de->namelen;
memcpy(end, de->name, de->namelen);
*--end = '/';
path = end;
de = de->parent;
}
rc = security_genfs_sid("proc", path, tclass, sid);
free_page((unsigned long)buffer);
return rc;
}
#else
static int selinux_proc_get_sid(struct proc_dir_entry *de,
u16 tclass,
u32 *sid)
{
return -EINVAL;
}
#endif
/* The inode's security attributes must be initialized before first use. */
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
{
struct superblock_security_struct *sbsec = NULL;
struct inode_security_struct *isec = inode->i_security;
u32 sid;
struct dentry *dentry;
#define INITCONTEXTLEN 255
char *context = NULL;
unsigned len = 0;
int rc = 0;
int hold_sem = 0;
if (isec->initialized)
goto out;
down(&isec->sem);
hold_sem = 1;
if (isec->initialized)
goto out;
sbsec = inode->i_sb->s_security;
if (!sbsec || !sbsec->initialized) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
spin_lock(&inode_security_lock);
if (list_empty(&isec->list))
list_add(&isec->list, &inode_security_head);
spin_unlock(&inode_security_lock);
goto out;
}
switch (sbsec->behavior) {
case SECURITY_FS_USE_XATTR:
if (!inode->i_op->getxattr) {
isec->sid = SECINITSID_FILE;
break;
}
/* Need a dentry, since the xattr API requires one.
Life would be simpler if we could just pass the inode. */
if (opt_dentry) {
/* Called from d_instantiate or d_splice_alias. */
dentry = dget(opt_dentry);
} else {
/* Called from selinux_complete_init, try to find a dentry. */
dentry = d_find_alias(inode);
}
if (!dentry) {
printk(KERN_WARNING "%s: no dentry for dev=%s "
"ino=%ld\n", __FUNCTION__, inode->i_sb->s_id,
inode->i_ino);
goto out;
}
len = INITCONTEXTLEN;
context = kmalloc(len, GFP_KERNEL);
if (!context) {
rc = -ENOMEM;
dput(dentry);
goto out;
}
rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
context, len);
if (rc == -ERANGE) {
/* Need a larger buffer. Query for the right size. */
rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
NULL, 0);
if (rc < 0) {
dput(dentry);
goto out;
}
kfree(context);
len = rc;
context = kmalloc(len, GFP_KERNEL);
if (!context) {
rc = -ENOMEM;
dput(dentry);
goto out;
}
rc = inode->i_op->getxattr(dentry,
XATTR_NAME_SELINUX,
context, len);
}
dput(dentry);
if (rc < 0) {
if (rc != -ENODATA) {
printk(KERN_WARNING "%s: getxattr returned "
"%d for dev=%s ino=%ld\n", __FUNCTION__,
-rc, inode->i_sb->s_id, inode->i_ino);
kfree(context);
goto out;
}
/* Map ENODATA to the default file SID */
sid = SECINITSID_FILE;
rc = 0;
} else {
rc = security_context_to_sid(context, rc, &sid);
if (rc) {
printk(KERN_WARNING "%s: context_to_sid(%s) "
"returned %d for dev=%s ino=%ld\n",
__FUNCTION__, context, -rc,
inode->i_sb->s_id, inode->i_ino);
kfree(context);
goto out;
}
}
kfree(context);
isec->sid = sid;
break;
case SECURITY_FS_USE_TASK:
isec->sid = isec->task_sid;
break;
case SECURITY_FS_USE_TRANS:
/* Default to the fs SID. */
isec->sid = sbsec->sid;
/* Try to obtain a transition SID. */
isec->sclass = inode_mode_to_security_class(inode->i_mode);
rc = security_transition_sid(isec->task_sid,
sbsec->sid,
isec->sclass,
&sid);
if (rc)
goto out;
isec->sid = sid;
break;
default:
/* Default to the fs SID. */
isec->sid = sbsec->sid;
if (sbsec->proc) {
struct proc_inode *proci = PROC_I(inode);
if (proci->pde) {
isec->sclass = inode_mode_to_security_class(inode->i_mode);
rc = selinux_proc_get_sid(proci->pde,
isec->sclass,
&sid);
if (rc)
goto out;
isec->sid = sid;
}
}
break;
}
isec->initialized = 1;
out:
if (inode->i_sock) {
struct socket *sock = SOCKET_I(inode);
if (sock->sk) {
isec->sclass = socket_type_to_security_class(sock->sk->sk_family,
sock->sk->sk_type);
} else {
isec->sclass = SECCLASS_SOCKET;
}
} else {
isec->sclass = inode_mode_to_security_class(inode->i_mode);
}
if (hold_sem)
up(&isec->sem);
return rc;
}
/* Convert a Linux signal to an access vector. */
static inline u32 signal_to_av(int sig)
{
u32 perm = 0;
switch (sig) {
case SIGCHLD:
/* Commonly granted from child to parent. */
perm = PROCESS__SIGCHLD;
break;
case SIGKILL:
/* Cannot be caught or ignored */
perm = PROCESS__SIGKILL;
break;
case SIGSTOP:
/* Cannot be caught or ignored */
perm = PROCESS__SIGSTOP;
break;
default:
/* All other signals. */
perm = PROCESS__SIGNAL;
break;
}
return perm;
}
/* Check permission betweeen a pair of tasks, e.g. signal checks,
fork check, ptrace check, etc. */
int task_has_perm(struct task_struct *tsk1,
struct task_struct *tsk2,
u32 perms)
{
struct task_security_struct *tsec1, *tsec2;
tsec1 = tsk1->security;
tsec2 = tsk2->security;
return avc_has_perm(tsec1->sid, tsec2->sid,
SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);
}
/* Check whether a task is allowed to use a capability. */
int task_has_capability(struct task_struct *tsk,
int cap)
{
struct task_security_struct *tsec;
struct avc_audit_data ad;
tsec = tsk->security;
AVC_AUDIT_DATA_INIT(&ad,CAP);
ad.tsk = tsk;
ad.u.cap = cap;
return avc_has_perm(tsec->sid, tsec->sid,
SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad);
}
/* Check whether a task is allowed to use a system operation. */
int task_has_system(struct task_struct *tsk,
u32 perms)
{
struct task_security_struct *tsec;
tsec = tsk->security;
return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL, NULL);
}
/* Check whether a task has a particular permission to an inode.
The 'aeref' parameter is optional and allows other AVC
entry references to be passed (e.g. the one in the struct file).
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
int inode_has_perm(struct task_struct *tsk,
struct inode *inode,
u32 perms,
struct avc_entry_ref *aeref,
struct avc_audit_data *adp)
{
struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
tsec = tsk->security;
isec = inode->i_security;
if (!adp) {
adp = &ad;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.inode = inode;
}
return avc_has_perm(tsec->sid, isec->sid, isec->sclass,
perms, aeref ? aeref : &isec->avcr, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
the dentry to help the auditing code to more easily generate the
pathname if needed. */
static inline int dentry_has_perm(struct task_struct *tsk,
struct vfsmount *mnt,
struct dentry *dentry,
u32 av)
{
struct inode *inode = dentry->d_inode;
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;
return inode_has_perm(tsk, inode, av, NULL, &ad);
}
/* Check whether a task can use an open file descriptor to
access an inode in a given way. Check access to the
descriptor itself, and then use dentry_has_perm to
check a particular permission to the file.
Access to the descriptor is implicitly granted if it
has the same SID as the process. If av is zero, then
access to the file is not checked, e.g. for cases
where only the descriptor is affected like seek. */
static inline int file_has_perm(struct task_struct *tsk,
struct file *file,
u32 av)
{
struct task_security_struct *tsec = tsk->security;
struct file_security_struct *fsec = file->f_security;
struct vfsmount *mnt = file->f_vfsmnt;
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
struct avc_audit_data ad;
int rc;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;
if (tsec->sid != fsec->sid) {
rc = avc_has_perm(tsec->sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&fsec->avcr, &ad);
if (rc)
return rc;
}
/* av is zero if only checking access to the descriptor. */
if (av)
return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad);
return 0;
}
/* Check whether a task can create a file. */
static int may_create(struct inode *dir,
struct dentry *dentry,
u16 tclass)
{
struct task_security_struct *tsec;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 newsid;
struct avc_audit_data ad;
int rc;
tsec = current->security;
dsec = dir->i_security;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
&dsec->avcr, &ad);
if (rc)
return rc;
if (tsec->create_sid) {
newsid = tsec->create_sid;
} else {
rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
&newsid);
if (rc)
return rc;
}
rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, NULL, &ad);
if (rc)
return rc;
sbsec = dir->i_sb->s_security;
return avc_has_perm(newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE, NULL, &ad);
}
#define MAY_LINK 0
#define MAY_UNLINK 1
#define MAY_RMDIR 2
/* Check whether a task can link, unlink, or rmdir a file/directory. */
static int may_link(struct inode *dir,
struct dentry *dentry,
int kind)
{
struct task_security_struct *tsec;
struct inode_security_struct *dsec, *isec;
struct avc_audit_data ad;
u32 av;
int rc;
tsec = current->security;
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
av, &dsec->avcr, &ad);
if (rc)
return rc;
switch (kind) {
case MAY_LINK:
av = FILE__LINK;
break;
case MAY_UNLINK:
av = FILE__UNLINK;
break;
case MAY_RMDIR:
av = DIR__RMDIR;
break;
default:
printk(KERN_WARNING "may_link: unrecognized kind %d\n", kind);
return 0;
}
rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
av, &isec->avcr, &ad);
return rc;
}
static inline int may_rename(struct inode *old_dir,
struct dentry *old_dentry,
struct inode *new_dir,
struct dentry *new_dentry)
{
struct task_security_struct *tsec;
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
struct avc_audit_data ad;
u32 av;
int old_is_dir, new_is_dir;
int rc;
tsec = current->security;
old_dsec = old_dir->i_security;
old_isec = old_dentry->d_inode->i_security;
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
new_dsec = new_dir->i_security;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = old_dentry;
rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
DIR__REMOVE_NAME | DIR__SEARCH,
&old_dsec->avcr, &ad);
if (rc)
return rc;
rc = avc_has_perm(tsec->sid, old_isec->sid,
old_isec->sclass,
FILE__RENAME,
&old_isec->avcr, &ad);
if (rc)
return rc;
if (old_is_dir && new_dir != old_dir) {
rc = avc_has_perm(tsec->sid, old_isec->sid,
old_isec->sclass,
DIR__REPARENT,
&old_isec->avcr, &ad);
if (rc)
return rc;
}
ad.u.fs.dentry = new_dentry;
av = DIR__ADD_NAME | DIR__SEARCH;
if (new_dentry->d_inode)
av |= DIR__REMOVE_NAME;
rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR,
av,&new_dsec->avcr, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
new_isec = new_dentry->d_inode->i_security;
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
rc = avc_has_perm(tsec->sid, new_isec->sid,
new_isec->sclass,
(new_is_dir ? DIR__RMDIR : FILE__UNLINK),
&new_isec->avcr, &ad);
if (rc)
return rc;
}
return 0;
}
/* Check whether a task can perform a filesystem operation. */
int superblock_has_perm(struct task_struct *tsk,
struct super_block *sb,
u32 perms,
struct avc_audit_data *ad)
{
struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;
tsec = tsk->security;
sbsec = sb->s_security;
return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
perms, NULL, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
static inline u32 file_mask_to_av(int mode, int mask)
{
u32 av = 0;
if ((mode & S_IFMT) != S_IFDIR) {
if (mask & MAY_EXEC)
av |= FILE__EXECUTE;
if (mask & MAY_READ)
av |= FILE__READ;
if (mask & MAY_APPEND)
av |= FILE__APPEND;
else if (mask & MAY_WRITE)
av |= FILE__WRITE;
} else {
if (mask & MAY_EXEC)
av |= DIR__SEARCH;
if (mask & MAY_WRITE)
av |= DIR__WRITE;
if (mask & MAY_READ)
av |= DIR__READ;
}
return av;
}
/* Convert a Linux file to an access vector. */
static inline u32 file_to_av(struct file *file)
{
u32 av = 0;
if (file->f_mode & FMODE_READ)
av |= FILE__READ;
if (file->f_mode & FMODE_WRITE) {
if (file->f_flags & O_APPEND)
av |= FILE__APPEND;
else
av |= FILE__WRITE;
}
return av;
}
/* Set an inode's SID to a specified value. */
int inode_security_set_sid(struct inode *inode, u32 sid)
{
struct inode_security_struct *isec = inode->i_security;
down(&isec->sem);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = sid;
isec->initialized = 1;
up(&isec->sem);
return 0;
}
/* Set the security attributes on a newly created file. */
static int post_create(struct inode *dir,
struct dentry *dentry)
{
struct task_security_struct *tsec;
struct inode *inode;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 newsid;
char *context;
unsigned int len;
int rc;
tsec = current->security;
dsec = dir->i_security;
inode = dentry->d_inode;
if (!inode) {
/* Some file system types (e.g. NFS) may not instantiate
a dentry for all create operations (e.g. symlink),
so we have to check to see if the inode is non-NULL. */
printk(KERN_WARNING "post_create: no inode, dir (dev=%s, "
"ino=%ld)\n", dir->i_sb->s_id, dir->i_ino);
return 0;
}
if (tsec->create_sid) {
newsid = tsec->create_sid;
} else {
rc = security_transition_sid(tsec->sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
if (rc) {
printk(KERN_WARNING "post_create: "
"security_transition_sid failed, rc=%d (dev=%s "
"ino=%ld)\n",
-rc, inode->i_sb->s_id, inode->i_ino);
return rc;
}
}
rc = inode_security_set_sid(inode, newsid);
if (rc) {
printk(KERN_WARNING "post_create: inode_security_set_sid "
"failed, rc=%d (dev=%s ino=%ld)\n",
-rc, inode->i_sb->s_id, inode->i_ino);
return rc;
}
sbsec = dir->i_sb->s_security;
if (!sbsec)
return 0;
if (sbsec->behavior == SECURITY_FS_USE_XATTR &&
inode->i_op->setxattr) {
/* Use extended attributes. */
rc = security_sid_to_context(newsid, &context, &len);
if (rc) {
printk(KERN_WARNING "post_create: sid_to_context "
"failed, rc=%d (dev=%s ino=%ld)\n",
-rc, inode->i_sb->s_id, inode->i_ino);
return rc;
}
down(&inode->i_sem);
rc = inode->i_op->setxattr(dentry,
XATTR_NAME_SELINUX,
context, len, 0);
up(&inode->i_sem);
kfree(context);
if (rc < 0) {
printk(KERN_WARNING "post_create: setxattr failed, "
"rc=%d (dev=%s ino=%ld)\n",
-rc, inode->i_sb->s_id, inode->i_ino);
return rc;
}
}
return 0;
}
/* Hook functions begin here. */
static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{
int rc;
rc = secondary_ops->ptrace(parent,child);
if (rc)
return rc;
return task_has_perm(parent, child, PROCESS__PTRACE);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
int error;
error = task_has_perm(current, target, PROCESS__GETCAP);
if (error)
return error;
return secondary_ops->capget(target, effective, inheritable, permitted);
}
static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
int error;
error = task_has_perm(current, target, PROCESS__SETCAP);
if (error)
return error;
return secondary_ops->capset_check(target, effective, inheritable, permitted);
}
static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
int error;
error = task_has_perm(current, target, PROCESS__SETCAP);
if (error)
return;
return secondary_ops->capset_set(target, effective, inheritable, permitted);
}
static int selinux_capable(struct task_struct *tsk, int cap)
{
int rc;
rc = secondary_ops->capable(tsk, cap);
if (rc)
return rc;
return task_has_capability(tsk,cap);
}
static int selinux_sysctl(ctl_table *table, int op)
{
int error = 0;
u32 av;
struct task_security_struct *tsec;
u32 tsid;
int rc;
tsec = current->security;
rc = selinux_proc_get_sid(table->de, (op == 001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
if (rc) {
/* Default to the well-defined sysctl SID. */
tsid = SECINITSID_SYSCTL;
}
/* The op values are "defined" in sysctl.c, thereby creating
* a bad coupling between this module and sysctl.c */
if(op == 001) {
error = avc_has_perm(tsec->sid, tsid,
SECCLASS_DIR, DIR__SEARCH, NULL, NULL);
} else {
av = 0;
if (op & 004)
av |= FILE__READ;
if (op & 002)
av |= FILE__WRITE;
if (av)
error = avc_has_perm(tsec->sid, tsid,
SECCLASS_FILE, av, NULL, NULL);
}
return error;
}
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
{
int rc = 0;
if (!sb)
return 0;
switch (cmds) {
case Q_SYNC:
case Q_QUOTAON:
case Q_QUOTAOFF:
case Q_SETINFO:
case Q_SETQUOTA:
rc = superblock_has_perm(current,
sb,
FILESYSTEM__QUOTAMOD, NULL);
break;
case Q_GETFMT:
case Q_GETINFO:
case Q_GETQUOTA:
rc = superblock_has_perm(current,
sb,
FILESYSTEM__QUOTAGET, NULL);
break;
default:
rc = 0; /* let the kernel handle invalid cmds */
break;
}
return rc;
}
static int selinux_quota_on(struct file *f)
{
return file_has_perm(current, f, FILE__QUOTAON);;
}
static int selinux_syslog(int type)
{
int rc;
rc = secondary_ops->syslog(type);
if (rc)
return rc;
switch (type) {
case 3: /* Read last kernel messages */
rc = task_has_system(current, SYSTEM__SYSLOG_READ);
break;
case 6: /* Disable logging to console */
case 7: /* Enable logging to console */
case 8: /* Set level of messages printed to console */
rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
break;
case 0: /* Close log */
case 1: /* Open log */
case 2: /* Read from log */
case 4: /* Read/clear last kernel messages */
case 5: /* Clear ring buffer */
default:
rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
break;
}
return rc;
}
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
* We currently support three overcommit policies, which are set via the
* vm.overcommit_memory sysctl. See Documentation/vm/overcommit-acounting
*
* Strict overcommit modes added 2002 Feb 26 by Alan Cox.
* Additional code 2002 Jul 20 by Robert Love.
*/
static int selinux_vm_enough_memory(long pages)
{
unsigned long free, allowed;
int rc;
struct task_security_struct *tsec = current->security;
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == 1)
return 0;
if (sysctl_overcommit_memory == 0) {
free = get_page_cache_size();
free += nr_free_pages();
free += nr_swap_pages;
/*
* Any slabs which are created with the
* SLAB_RECLAIM_ACCOUNT flag claim to have contents
* which are reclaimable, under pressure. The dentry
* cache and most inode caches should fall into this
*/
free += atomic_read(&slab_reclaim_pages);
/*
* Leave the last 3% for privileged processes.
* Don't audit the check, as it is applied to all processes
* that allocate mappings.
*/
rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
if (!rc) {
rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
SECCLASS_CAPABILITY,
CAP_TO_MASK(CAP_SYS_ADMIN),
NULL, NULL);
}
if (rc)
free -= free / 32;
if (free > pages)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
allowed = totalram_pages * sysctl_overcommit_ratio / 100;
allowed += total_swap_pages;
if (atomic_read(&vm_committed_space) < allowed)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
static int selinux_netlink_send(struct sk_buff *skb)
{
if (capable(CAP_NET_ADMIN))
cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
else
NETLINK_CB(skb).eff_cap = 0;
return 0;
}
static int selinux_netlink_recv(struct sk_buff *skb)
{
if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
return -EPERM;
return 0;
}
/* binprm security operations */
static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
{
int rc;
/* Make sure that the secondary module doesn't use the
bprm->security field, since we do not yet support chaining
of multiple security structures on the field. Neither
the dummy nor the capability module use the field. The owlsm
module uses the field if CONFIG_OWLSM_FD is enabled. */
rc = secondary_ops->bprm_alloc_security(bprm);
if (rc)
return rc;
if (bprm->security) {
printk(KERN_WARNING "%s: no support yet for chaining on the "
"security field by secondary modules.\n", __FUNCTION__);
/* Release the secondary module's security object. */
secondary_ops->bprm_free_security(bprm);
/* Unregister the secondary module to prevent problems
with subsequent binprm hooks. This will revert to the
original (dummy) module for the secondary operations. */
rc = security_ops->unregister_security("unknown", secondary_ops);
if (rc)
return rc;
printk(KERN_WARNING "%s: Unregistered the secondary security "
"module.\n", __FUNCTION__);
}
bprm->security = NULL;
return 0;
}
static int selinux_bprm_set_security(struct linux_binprm *bprm)
{
struct task_security_struct *tsec;
struct inode *inode = bprm->file->f_dentry->d_inode;
struct inode_security_struct *isec;
u32 newsid;
struct avc_audit_data ad;
int rc;
rc = secondary_ops->bprm_set_security(bprm);
if (rc)
return rc;
if (bprm->sh_bang || bprm->security)
/* The security field should already be set properly. */
return 0;
tsec = current->security;
isec = inode->i_security;
/* Default to the current task SID. */
bprm->security = (void *)tsec->sid;
/* Reset create SID on execve. */
tsec->create_sid = 0;
if (tsec->exec_sid) {
newsid = tsec->exec_sid;
/* Reset exec SID on execve. */
tsec->exec_sid = 0;
} else {
/* Check for a default transition on this program. */
rc = security_transition_sid(tsec->sid, isec->sid,
SECCLASS_PROCESS, &newsid);
if (rc)
return rc;
}
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.mnt = bprm->file->f_vfsmnt;
ad.u.fs.dentry = bprm->file->f_dentry;
if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
newsid = tsec->sid;
if (tsec->sid == newsid) {
rc = avc_has_perm(tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS,
&isec->avcr, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
rc = avc_has_perm(tsec->sid, newsid,
SECCLASS_PROCESS, PROCESS__TRANSITION,
NULL,
&ad);
if (rc)
return rc;
rc = avc_has_perm(newsid, isec->sid,
SECCLASS_FILE, FILE__ENTRYPOINT,
&isec->avcr, &ad);
if (rc)
return rc;
/* Set the security field to the new SID. */
bprm->security = (void*) newsid;
}
return 0;
}
static int selinux_bprm_check_security (struct linux_binprm *bprm)
{
return 0;
}
static int selinux_bprm_secureexec (struct linux_binprm *bprm)
{
struct task_security_struct *tsec = current->security;
int atsecure = 0;
if (tsec->osid != tsec->sid) {
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
atsecure = avc_has_perm(tsec->osid, tsec->sid,
SECCLASS_PROCESS,
PROCESS__NOATSECURE, NULL, NULL);
}
/* Note that we must include the legacy uid/gid test below
to retain it, as the new userland will simply use the
value passed by AT_SECURE to decide whether to enable
secure mode. */
return ( atsecure || current->euid != current->uid ||
current->egid != current->gid);
}
static void selinux_bprm_free_security(struct linux_binprm *bprm)
{
/* Nothing to do - not dynamically allocated. */
return;
}
/* Derived from fs/exec.c:flush_old_files. */
static inline void flush_unauthorized_files(struct files_struct * files)
{
struct avc_audit_data ad;
struct file *file;
long j = -1;
AVC_AUDIT_DATA_INIT(&ad,FS);
spin_lock(&files->file_lock);
for (;;) {
unsigned long set, i;
j++;
i = j * __NFDBITS;
if (i >= files->max_fds || i >= files->max_fdset)
break;
set = files->open_fds->fds_bits[j];
if (!set)
continue;
spin_unlock(&files->file_lock);
for ( ; set ; i++,set >>= 1) {
if (set & 1) {
file = fget(i);
if (!file)
continue;
if (file_has_perm(current,
file,
file_to_av(file)))
sys_close(i);
fput(file);
}
}
spin_lock(&files->file_lock);
}
spin_unlock(&files->file_lock);
}
static void selinux_bprm_compute_creds(struct linux_binprm *bprm)
{
struct task_security_struct *tsec, *psec;
u32 sid;
struct av_decision avd;
int rc;
secondary_ops->bprm_compute_creds(bprm);
tsec = current->security;
sid = (u32)bprm->security;
if (!sid)
sid = tsec->sid;
tsec->osid = tsec->sid;
if (tsec->sid != sid) {
/* Check for shared state. If not ok, leave SID
unchanged and kill. */
if ((atomic_read(&current->fs->count) > 1 ||
atomic_read(&current->files->count) > 1 ||
atomic_read(&current->sighand->count) > 1)) {
rc = avc_has_perm(tsec->sid, sid,
SECCLASS_PROCESS, PROCESS__SHARE,
NULL, NULL);
if (rc) {
force_sig_specific(SIGKILL, current);
return;
}
}
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and kill. */
task_lock(current);
if (current->ptrace & PT_PTRACED) {
psec = current->parent->security;
rc = avc_has_perm_noaudit(psec->sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE,
NULL, &avd);
if (!rc)
tsec->sid = sid;
task_unlock(current);
avc_audit(psec->sid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, rc, NULL);
if (rc) {
force_sig_specific(SIGKILL, current);
return;
}
} else {
tsec->sid = sid;
task_unlock(current);
}
/* Close files for which the new task SID is not authorized. */
flush_unauthorized_files(current->files);
/* Wake up the parent if it is waiting so that it can
recheck wait permission to the new task SID. */
wake_up_interruptible(&current->parent->wait_chldexit);
}
}
/* superblock security operations */
static int selinux_sb_alloc_security(struct super_block *sb)
{
return superblock_alloc_security(sb);
}
static void selinux_sb_free_security(struct super_block *sb)
{
superblock_free_security(sb);
}
static int selinux_sb_kern_mount(struct super_block *sb)
{
struct avc_audit_data ad;
int rc;
rc = superblock_doinit(sb);
if (rc)
return rc;
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = sb->s_root;
return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
}
static int selinux_sb_statfs(struct super_block *sb)
{
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = sb->s_root;
return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad);
}
static int selinux_mount(char * dev_name,
struct nameidata *nd,
char * type,
unsigned long flags,
void * data)
{
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
else
return dentry_has_perm(current, nd->mnt, nd->dentry,
FILE__MOUNTON);
}
static int selinux_umount(struct vfsmount *mnt, int flags)
{
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
}
/* inode security operations */
static int selinux_inode_alloc_security(struct inode *inode)
{
return inode_alloc_security(inode);
}
static void selinux_inode_free_security(struct inode *inode)
{
inode_free_security(inode);
}
static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
{
return may_create(dir, dentry, SECCLASS_FILE);
}
static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask)
{
post_create(dir, dentry);
}
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
int rc;
rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
if (rc)
return rc;
return may_link(dir, old_dentry, MAY_LINK);
}
static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry)
{
return;
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
return may_link(dir, dentry, MAY_UNLINK);
}
static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
{
return may_create(dir, dentry, SECCLASS_LNK_FILE);
}
static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name)
{
post_create(dir, dentry);
}
static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
{
return may_create(dir, dentry, SECCLASS_DIR);
}
static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask)
{
post_create(dir, dentry);
}
static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
return may_link(dir, dentry, MAY_RMDIR);
}
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
post_create(dir, dentry);
}
static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
return;
}
static int selinux_inode_readlink(struct dentry *dentry)
{
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
int rc;
rc = secondary_ops->inode_follow_link(dentry,nameidata);
if (rc)
return rc;
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask)
{
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
return inode_has_perm(current, inode,
file_mask_to_av(inode->i_mode, mask), NULL, NULL);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET))
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
}
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
}
static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
{
struct task_security_struct *tsec = current->security;
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
struct avc_audit_data ad;
u32 newsid;
int rc = 0;
if (strcmp(name, XATTR_NAME_SELINUX)) {
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof XATTR_SECURITY_PREFIX - 1) &&
!capable(CAP_SYS_ADMIN)) {
/* A different attribute in the security namespace.
Restrict to administrator. */
return -EPERM;
}
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
}
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = dentry;
rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
FILE__RELABELFROM,
&isec->avcr, &ad);
if (rc)
return rc;
rc = security_context_to_sid(value, size, &newsid);
if (rc)
return rc;
rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
FILE__RELABELTO, NULL, &ad);
if (rc)
return rc;
sbsec = inode->i_sb->s_security;
if (!sbsec)
return 0;
return avc_has_perm(newsid,
sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE,
NULL,
&ad);
}
static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
u32 newsid;
int rc;
if (strcmp(name, XATTR_NAME_SELINUX)) {
/* Not an attribute we recognize, so nothing to do. */
return;
}
rc = security_context_to_sid(value, size, &newsid);
if (rc) {
printk(KERN_WARNING "%s: unable to obtain SID for context "
"%s, rc=%d\n", __FUNCTION__, (char*)value, -rc);
return;
}
isec->sid = newsid;
return;
}
static int selinux_inode_getxattr (struct dentry *dentry, char *name)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_listxattr (struct dentry *dentry)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_removexattr (struct dentry *dentry, char *name)
{
if (strcmp(name, XATTR_NAME_SELINUX)) {
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof XATTR_SECURITY_PREFIX - 1) &&
!capable(CAP_SYS_ADMIN)) {
/* A different attribute in the security namespace.
Restrict to administrator. */
return -EPERM;
}
/* Not an attribute we recognize, so just check the
ordinary setattr permission. Might want a separate
permission for removexattr. */
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
}
/* No one is allowed to remove a SELinux security label.
You can change the label, but all data must be labeled. */
return -EACCES;
}
static int selinux_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size)
{
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
char *context;
unsigned len;
int rc;
/* Permission check handled by selinux_inode_getxattr hook.*/
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
return rc;
if (!buffer || !size) {
kfree(context);
return len;
}
if (size < len) {
kfree(context);
return -ERANGE;
}
memcpy(buffer, context, len);
kfree(context);
return len;
}
static int selinux_inode_setsecurity(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
u32 newsid;
int rc;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
if (!value || !size)
return -EACCES;
rc = security_context_to_sid((void*)value, size, &newsid);
if (rc)
return rc;
isec->sid = newsid;
return 0;
}
static int selinux_inode_listsecurity(struct dentry *dentry, char *buffer)
{
const int len = sizeof(XATTR_NAME_SELINUX);
if (buffer)
memcpy(buffer, XATTR_NAME_SELINUX, len);
return len;
}
/* file security operations */
static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file->f_dentry->d_inode;
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
/* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
return file_has_perm(current, file,
file_mask_to_av(inode->i_mode, mask));
}
static int selinux_file_alloc_security(struct file *file)
{
return file_alloc_security(file);
}
static void selinux_file_free_security(struct file *file)
{
file_free_security(file);
}
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int error = 0;
switch (cmd) {
case FIONREAD:
/* fall through */
case FIBMAP:
/* fall through */
case FIGETBSZ:
/* fall through */
case EXT2_IOC_GETFLAGS:
/* fall through */
case EXT2_IOC_GETVERSION:
error = file_has_perm(current, file, FILE__GETATTR);
break;
case EXT2_IOC_SETFLAGS:
/* fall through */
case EXT2_IOC_SETVERSION:
error = file_has_perm(current, file, FILE__SETATTR);
break;
/* sys_ioctl() checks */
case FIONBIO:
/* fall through */
case FIOASYNC:
error = file_has_perm(current, file, 0);
break;
case KDSKBENT:
case KDSKBSENT:
if (!capable(CAP_SYS_TTY_CONFIG))
error = -EPERM;
break;
/* default case assumes that the command will go
* to the file's ioctl() function.
*/
default:
error = file_has_perm(current, file, FILE__IOCTL);
}
return error;
}
static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
{
u32 av;
if (file) {
/* read access is always possible with a mapping */
av = FILE__READ;
/* write access only matters if the mapping is shared */
if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE))
av |= FILE__WRITE;
if (prot & PROT_EXEC)
av |= FILE__EXECUTE;
return file_has_perm(current, file, av);
}
return 0;
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
{
return file_has_perm(current, file, FILE__LOCK);
}
static int selinux_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int err = 0;
switch (cmd) {
case F_SETFL:
if (!file->f_dentry || !file->f_dentry->d_inode) {
err = -EINVAL;
break;
}
if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
err = file_has_perm(current, file,FILE__WRITE);
break;
}
/* fall through */
case F_SETOWN:
case F_SETSIG:
case F_GETFL:
case F_GETOWN:
case F_GETSIG:
/* Just check FD__USE permission */
err = file_has_perm(current, file, 0);
break;
case F_GETLK:
case F_SETLK:
case F_SETLKW:
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
if (!file->f_dentry || !file->f_dentry->d_inode) {
err = -EINVAL;
break;
}
err = file_has_perm(current, file, FILE__LOCK);
break;
}
return err;
}
static int selinux_file_set_fowner(struct file *file)
{
struct task_security_struct *tsec;
struct file_security_struct *fsec;
tsec = current->security;
fsec = file->f_security;
fsec->fown_sid = tsec->sid;
return 0;
}
static int selinux_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown,
int fd, int reason)
{
struct file *file;
u32 perm;
struct task_security_struct *tsec;
struct file_security_struct *fsec;
/* struct fown_struct is never outside the context of a struct file */
file = (struct file *)((long)fown - offsetof(struct file,f_owner));
tsec = tsk->security;
fsec = file->f_security;
if (!fown->signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
else
perm = signal_to_av(fown->signum);
return avc_has_perm(fsec->fown_sid, tsec->sid,
SECCLASS_PROCESS, perm, NULL, NULL);
}
static int selinux_file_receive(struct file *file)
{
return file_has_perm(current, file, file_to_av(file));
}
/* task security operations */
static int selinux_task_create(unsigned long clone_flags)
{
return task_has_perm(current, current, PROCESS__FORK);
}
static int selinux_task_alloc_security(struct task_struct *tsk)
{
struct task_security_struct *tsec1, *tsec2;
int rc;
tsec1 = current->security;
rc = task_alloc_security(tsk);
if (rc)
return rc;
tsec2 = tsk->security;
tsec2->osid = tsec1->osid;
tsec2->sid = tsec1->sid;
/* Retain the exec and create SIDs across fork */
tsec2->exec_sid = tsec1->exec_sid;
tsec2->create_sid = tsec1->create_sid;
return 0;
}
static void selinux_task_free_security(struct task_struct *tsk)
{
task_free_security(tsk);
}
static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{
/* Since setuid only affects the current process, and
since the SELinux controls are not based on the Linux
identity attributes, SELinux does not need to control
this operation. However, SELinux does control the use
of the CAP_SETUID and CAP_SETGID capabilities using the
capable hook. */
return 0;
}
static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{
return secondary_ops->task_post_setuid(id0,id1,id2,flags);
}
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
{
/* See the comment for setuid above. */
return 0;
}
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return task_has_perm(current, p, PROCESS__SETPGID);
}
static int selinux_task_getpgid(struct task_struct *p)
{
return task_has_perm(current, p, PROCESS__GETPGID);
}
static int selinux_task_getsid(struct task_struct *p)
{
return task_has_perm(current, p, PROCESS__GETSESSION);
}
static int selinux_task_setgroups(int gidsetsize, gid_t *grouplist)
{
/* See the comment for setuid above. */
return 0;
}
static int selinux_task_setnice(struct task_struct *p, int nice)
{
return task_has_perm(current,p, PROCESS__SETSCHED);
}
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
/* SELinux does not currently provide a process
resource limit policy based on security contexts.
It does control the use of the CAP_SYS_RESOURCE capability
using the capable hook. */
return 0;
}
static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
{
struct task_security_struct *tsec1, *tsec2;
tsec1 = current->security;
tsec2 = p->security;
/* No auditing from the setscheduler hook, since the runqueue lock
is held and the system will deadlock if we try to log an audit
message. */
return avc_has_perm_noaudit(tsec1->sid, tsec2->sid,
SECCLASS_PROCESS, PROCESS__SETSCHED,
&tsec2->avcr, NULL);
}
static int selinux_task_getscheduler(struct task_struct *p)
{
return task_has_perm(current, p, PROCESS__GETSCHED);
}
static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
{
u32 perm;
if (info && ((unsigned long)info == 1 ||
(unsigned long)info == 2 || SI_FROMKERNEL(info)))
return 0;
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
return task_has_perm(current, p, perm);
}
static int selinux_task_prctl(int option,
unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
unsigned long arg5)
{
/* The current prctl operations do not appear to require
any SELinux controls since they merely observe or modify
the state of the current process. */
return 0;
}
static int selinux_task_wait(struct task_struct *p)
{
u32 perm;
perm = signal_to_av(p->exit_signal);
return task_has_perm(p, current, perm);
}
static void selinux_task_reparent_to_init(struct task_struct *p)
{
struct task_security_struct *tsec;
secondary_ops->task_reparent_to_init(p);
tsec = p->security;
tsec->osid = tsec->sid;
tsec->sid = SECINITSID_KERNEL;
return;
}
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
struct task_security_struct *tsec = p->security;
struct inode_security_struct *isec = inode->i_security;
isec->sid = tsec->sid;
isec->initialized = 1;
return;
}
#ifdef CONFIG_SECURITY_NETWORK
/* socket security operations */
static int socket_has_perm(struct task_struct *task, struct socket *sock,
u32 perms)
{
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct avc_audit_data ad;
int err;
tsec = task->security;
isec = SOCK_INODE(sock)->i_security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
perms, &isec->avcr, &ad);
return err;
}
static int selinux_socket_create(int family, int type, int protocol)
{
int err;
struct task_security_struct *tsec;
tsec = current->security;
err = avc_has_perm(tsec->sid, tsec->sid,
socket_type_to_security_class(family, type),
SOCKET__CREATE, NULL, NULL);
return err;
}
static void selinux_socket_post_create(struct socket *sock, int family, int type, int protocol)
{
int err;
struct inode_security_struct *isec;
struct task_security_struct *tsec;
err = inode_doinit(SOCK_INODE(sock));
if (err < 0)
return;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
isec->sclass = socket_type_to_security_class(family, type);
isec->sid = tsec->sid;
return;
}
/* Range of port numbers used to automatically bind.
Need to determine whether we should perform a name_bind
permission check between the socket and the port number. */
#define ip_local_port_range_0 sysctl_local_port_range[0]
#define ip_local_port_range_1 sysctl_local_port_range[1]
static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{
int err;
err = socket_has_perm(current, sock, SOCKET__BIND);
if (err)
return err;
/*
* If PF_INET, check name_bind permission for the port.
*/
if (sock->sk->sk_family == PF_INET) {
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct avc_audit_data ad;
struct sockaddr_in *addr = (struct sockaddr_in *)address;
unsigned short snum = ntohs(addr->sin_port);
struct sock *sk = sock->sk;
u32 sid;
tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
snum > ip_local_port_range_1)) {
err = security_port_sid(sk->sk_family, sk->sk_type,
sk->sk_protocol, snum, &sid);
if (err)
return err;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.port = snum;
err = avc_has_perm(isec->sid, sid,
isec->sclass,
SOCKET__NAME_BIND, NULL, &ad);
if (err)
return err;
}
}
return 0;
}
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
int err;
struct sock *sk = sock->sk;
struct avc_audit_data ad;
struct task_security_struct *tsec;
struct inode_security_struct *isec;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__CONNECT, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_listen(struct socket *sock, int backlog)
{
int err;
struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__LISTEN, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
{
int err;
struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct inode_security_struct *newisec;
struct avc_audit_data ad;
tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__ACCEPT, &isec->avcr, &ad);
if (err)
return err;
err = inode_doinit(SOCK_INODE(newsock));
if (err < 0)
return err;
newisec = SOCK_INODE(newsock)->i_security;
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;
return 0;
}
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
struct sock *sk;
int err;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
sk = sock->sk;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__WRITE, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags)
{
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct avc_audit_data ad;
int err;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__READ, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_getsockname(struct socket *sock)
{
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct avc_audit_data ad;
int err;
tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__GETATTR, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_getpeername(struct socket *sock)
{
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct avc_audit_data ad;
int err;
tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
SOCKET__GETATTR, &isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
{
return socket_has_perm(current, sock, SOCKET__SETOPT);
}
static int selinux_socket_getsockopt(struct socket *sock, int level,
int optname)
{
return socket_has_perm(current, sock, SOCKET__GETOPT);
}
static int selinux_socket_shutdown(struct socket *sock, int how)
{
return socket_has_perm(current, sock, SOCKET__SHUTDOWN);
}
static int selinux_socket_unix_stream_connect(struct socket *sock,
struct socket *other,
struct sock *newsk)
{
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
struct avc_audit_data ad;
int err;
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
isec->sclass,
UNIX_STREAM_SOCKET__CONNECTTO,
&other_isec->avcr, &ad);
if (err)
return err;
return 0;
}
static int selinux_socket_unix_may_send(struct socket *sock,
struct socket *other)
{
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
struct avc_audit_data ad;
int err;
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
isec->sclass,
SOCKET__SENDTO,
&other_isec->avcr, &ad);
if (err)
return err;
return 0;
}
#endif
static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
{
struct task_security_struct *tsec = task->security;
struct ipc_security_struct *isec;
isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
memset(isec, 0, sizeof(struct ipc_security_struct));
isec->magic = SELINUX_MAGIC;
isec->sclass = sclass;
isec->ipc_perm = perm;
if (tsec) {
isec->sid = tsec->sid;
} else {
isec->sid = SECINITSID_UNLABELED;
}
perm->security = isec;
return 0;
}
static void ipc_free_security(struct kern_ipc_perm *perm)
{
struct ipc_security_struct *isec = perm->security;
if (!isec || isec->magic != SELINUX_MAGIC)
return;
perm->security = NULL;
kfree(isec);
}
static int msg_msg_alloc_security(struct msg_msg *msg)
{
struct msg_security_struct *msec;
msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
if (!msec)
return -ENOMEM;
memset(msec, 0, sizeof(struct msg_security_struct));
msec->magic = SELINUX_MAGIC;
msec->msg = msg;
msec->sid = SECINITSID_UNLABELED;
msg->security = msec;
return 0;
}
static void msg_msg_free_security(struct msg_msg *msg)
{
struct msg_security_struct *msec = msg->security;
if (!msec || msec->magic != SELINUX_MAGIC)
return;
msg->security = NULL;
kfree(msec);
}
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
u16 sclass, u32 perms)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
tsec = current->security;
isec = ipc_perms->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
return avc_has_perm(tsec->sid, isec->sid, sclass,
perms, &isec->avcr, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
{
return msg_msg_alloc_security(msg);
}
static void selinux_msg_msg_free_security(struct msg_msg *msg)
{
return msg_msg_free_security(msg);
}
/* message queue security operations */
static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
if (rc)
return rc;
tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &isec->avcr, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
return rc;
}
return 0;
}
static void selinux_msg_queue_free_security(struct msg_queue *msq)
{
ipc_free_security(&msq->q_perm);
}
static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ASSOCIATE, &isec->avcr, &ad);
}
static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
{
int err;
int perms;
switch(cmd) {
case IPC_INFO:
case MSG_INFO:
/* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO);
case IPC_STAT:
case MSG_STAT:
perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
break;
case IPC_SET:
perms = MSGQ__SETATTR;
break;
case IPC_RMID:
perms = MSGQ__DESTROY;
break;
default:
return 0;
}
err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms);
return err;
}
static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
int rc;
tsec = current->security;
isec = msq->q_perm.security;
msec = msg->security;
/*
* First time through, need to assign label to the message
*/
if (msec->sid == SECINITSID_UNLABELED) {
/*
* Compute new sid based on current process and
* message queue this message will be stored in
*/
rc = security_transition_sid(tsec->sid,
isec->sid,
SECCLASS_MSG,
&msec->sid);
if (rc)
return rc;
}
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
/* Can this process write to the queue? */
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
MSGQ__WRITE, &isec->avcr, &ad);
if (!rc)
/* Can this process send the message */
rc = avc_has_perm(tsec->sid, msec->sid,
SECCLASS_MSG, MSG__SEND,
&msec->avcr, &ad);
if (!rc)
/* Can the message be put in the queue? */
rc = avc_has_perm(msec->sid, isec->sid,
SECCLASS_MSGQ, MSGQ__ENQUEUE,
&isec->avcr, &ad);
return rc;
}
static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
struct task_struct *target,
long type, int mode)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
int rc;
tsec = target->security;
isec = msq->q_perm.security;
msec = msg->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid,
SECCLASS_MSGQ, MSGQ__READ,
&isec->avcr, &ad);
if (!rc)
rc = avc_has_perm(tsec->sid, msec->sid,
SECCLASS_MSG, MSG__RECEIVE,
&msec->avcr, &ad);
return rc;
}
/* Shared Memory security operations */
static int selinux_shm_alloc_security(struct shmid_kernel *shp)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
if (rc)
return rc;
tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &isec->avcr, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
return rc;
}
return 0;
}
static void selinux_shm_free_security(struct shmid_kernel *shp)
{
ipc_free_security(&shp->shm_perm);
}
static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
SHM__ASSOCIATE, &isec->avcr, &ad);
}
/* Note, at this point, shp is locked down */
static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
{
int perms;
int err;
switch(cmd) {
case IPC_INFO:
case SHM_INFO:
/* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO);
case IPC_STAT:
case SHM_STAT:
perms = SHM__GETATTR | SHM__ASSOCIATE;
break;
case IPC_SET:
perms = SHM__SETATTR;
break;
case SHM_LOCK:
case SHM_UNLOCK:
perms = SHM__LOCK;
break;
case IPC_RMID:
perms = SHM__DESTROY;
break;
default:
return 0;
}
err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
return err;
}
static int selinux_shm_shmat(struct shmid_kernel *shp,
char *shmaddr, int shmflg)
{
u32 perms;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
else
perms = SHM__READ | SHM__WRITE;
return ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
}
/* Semaphore security operations */
static int selinux_sem_alloc_security(struct sem_array *sma)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
if (rc)
return rc;
tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &isec->avcr, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
return rc;
}
return 0;
}
static void selinux_sem_free_security(struct sem_array *sma)
{
ipc_free_security(&sma->sem_perm);
}
static int selinux_sem_associate(struct sem_array *sma, int semflg)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
SEM__ASSOCIATE, &isec->avcr, &ad);
}
/* Note, at this point, sma is locked down */
static int selinux_sem_semctl(struct sem_array *sma, int cmd)
{
int err;
u32 perms;
switch(cmd) {
case IPC_INFO:
case SEM_INFO:
/* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO);
case GETPID:
case GETNCNT:
case GETZCNT:
perms = SEM__GETATTR;
break;
case GETVAL:
case GETALL:
perms = SEM__READ;
break;
case SETVAL:
case SETALL:
perms = SEM__WRITE;
break;
case IPC_RMID:
perms = SEM__DESTROY;
break;
case IPC_SET:
perms = SEM__SETATTR;
break;
case IPC_STAT:
case SEM_STAT:
perms = SEM__GETATTR | SEM__ASSOCIATE;
break;
default:
return 0;
}
err = ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
return err;
}
static int selinux_sem_semop(struct sem_array *sma,
struct sembuf *sops, unsigned nsops, int alter)
{
u32 perms;
if (alter)
perms = SEM__READ | SEM__WRITE;
else
perms = SEM__READ;
return ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
}
static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{
struct ipc_security_struct *isec = ipcp->security;
u16 sclass = SECCLASS_IPC;
u32 av = 0;
if (isec && isec->magic == SELINUX_MAGIC)
sclass = isec->sclass;
av = 0;
if (flag & S_IRUGO)
av |= IPC__UNIX_READ;
if (flag & S_IWUGO)
av |= IPC__UNIX_WRITE;
if (av == 0)
return 0;
return ipc_has_perm(ipcp, sclass, av);
}
/* module stacking operations */
int selinux_register_security (const char *name, struct security_operations *ops)
{
if (secondary_ops != original_ops) {
printk(KERN_INFO "%s: There is already a secondary security "
"module registered.\n", __FUNCTION__);
return -EINVAL;
}
secondary_ops = ops;
printk(KERN_INFO "%s: Registering secondary module %s\n",
__FUNCTION__,
name);
return 0;
}
int selinux_unregister_security (const char *name, struct security_operations *ops)
{
if (ops != secondary_ops) {
printk (KERN_INFO "%s: trying to unregister a security module "
"that is not registered.\n", __FUNCTION__);
return -EINVAL;
}
secondary_ops = original_ops;
return 0;
}
static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
{
if (inode)
inode_doinit_with_dentry(inode, dentry);
}
static int selinux_getprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
u32 sid;
char *context;
size_t len;
int error;
if (current != p) {
error = task_has_perm(current, p, PROCESS__GETATTR);
if (error)
return error;
}
if (!size)
return -ERANGE;
tsec = p->security;
if (!strcmp(name, "current"))
sid = tsec->sid;
else if (!strcmp(name, "prev"))
sid = tsec->osid;
else if (!strcmp(name, "exec"))
sid = tsec->exec_sid;
else if (!strcmp(name, "fscreate"))
sid = tsec->create_sid;
else
return -EINVAL;
if (!sid)
return 0;
error = security_sid_to_context(sid, &context, &len);
if (error)
return error;
if (len > size) {
kfree(context);
return -ERANGE;
}
memcpy(value, context, len);
kfree(context);
return len;
}
static int selinux_setprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
u32 sid = 0;
int error;
if (current != p || !strcmp(name, "current")) {
/* SELinux only allows a process to change its own
security attributes, and it only allows the process
current SID to change via exec. */
return -EACCES;
}
/*
* Basic control over ability to set these attributes at all.
* current == p, but we'll pass them separately in case the
* above restriction is ever removed.
*/
if (!strcmp(name, "exec"))
error = task_has_perm(current, p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
error = task_has_perm(current, p, PROCESS__SETFSCREATE);
else
error = -EINVAL;
if (error)
return error;
/* Obtain a SID for the context, if one was specified. */
if (size) {
int error;
error = security_context_to_sid(value, size, &sid);
if (error)
return error;
}
/* Permission checking based on the specified context is
performed during the actual operation (execve,
open/mkdir/...), when we know the full context of the
operation. See selinux_bprm_set_security for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
tsec = p->security;
if (!strcmp(name, "exec"))
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
tsec->create_sid = sid;
else
return -EINVAL;
return size;
}
struct security_operations selinux_ops = {
.ptrace = selinux_ptrace,
.capget = selinux_capget,
.capset_check = selinux_capset_check,
.capset_set = selinux_capset_set,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
.quota_on = selinux_quota_on,
.syslog = selinux_syslog,
.vm_enough_memory = selinux_vm_enough_memory,
.netlink_send = selinux_netlink_send,
.netlink_recv = selinux_netlink_recv,
.bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security,
.bprm_compute_creds = selinux_bprm_compute_creds,
.bprm_set_security = selinux_bprm_set_security,
.bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,
.sb_alloc_security = selinux_sb_alloc_security,
.sb_free_security = selinux_sb_free_security,
.sb_kern_mount = selinux_sb_kern_mount,
.sb_statfs = selinux_sb_statfs,
.sb_mount = selinux_mount,
.sb_umount = selinux_umount,
.inode_alloc_security = selinux_inode_alloc_security,
.inode_free_security = selinux_inode_free_security,
.inode_create = selinux_inode_create,
.inode_post_create = selinux_inode_post_create,
.inode_link = selinux_inode_link,
.inode_post_link = selinux_inode_post_link,
.inode_unlink = selinux_inode_unlink,
.inode_symlink = selinux_inode_symlink,
.inode_post_symlink = selinux_inode_post_symlink,
.inode_mkdir = selinux_inode_mkdir,
.inode_post_mkdir = selinux_inode_post_mkdir,
.inode_rmdir = selinux_inode_rmdir,
.inode_mknod = selinux_inode_mknod,
.inode_post_mknod = selinux_inode_post_mknod,
.inode_rename = selinux_inode_rename,
.inode_post_rename = selinux_inode_post_rename,
.inode_readlink = selinux_inode_readlink,
.inode_follow_link = selinux_inode_follow_link,
.inode_permission = selinux_inode_permission,
.inode_setattr = selinux_inode_setattr,
.inode_getattr = selinux_inode_getattr,
.inode_setxattr = selinux_inode_setxattr,
.inode_post_setxattr = selinux_inode_post_setxattr,
.inode_getxattr = selinux_inode_getxattr,
.inode_listxattr = selinux_inode_listxattr,
.inode_removexattr = selinux_inode_removexattr,
.inode_getsecurity = selinux_inode_getsecurity,
.inode_setsecurity = selinux_inode_setsecurity,
.inode_listsecurity = selinux_inode_listsecurity,
.file_permission = selinux_file_permission,
.file_alloc_security = selinux_file_alloc_security,
.file_free_security = selinux_file_free_security,
.file_ioctl = selinux_file_ioctl,
.file_mmap = selinux_file_mmap,
.file_mprotect = selinux_file_mprotect,
.file_lock = selinux_file_lock,
.file_fcntl = selinux_file_fcntl,
.file_set_fowner = selinux_file_set_fowner,
.file_send_sigiotask = selinux_file_send_sigiotask,
.file_receive = selinux_file_receive,
.task_create = selinux_task_create,
.task_alloc_security = selinux_task_alloc_security,
.task_free_security = selinux_task_free_security,
.task_setuid = selinux_task_setuid,
.task_post_setuid = selinux_task_post_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
.task_getsid = selinux_task_getsid,
.task_setgroups = selinux_task_setgroups,
.task_setnice = selinux_task_setnice,
.task_setrlimit = selinux_task_setrlimit,
.task_setscheduler = selinux_task_setscheduler,
.task_getscheduler = selinux_task_getscheduler,
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
.task_prctl = selinux_task_prctl,
.task_reparent_to_init = selinux_task_reparent_to_init,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
.msg_msg_free_security = selinux_msg_msg_free_security,
.msg_queue_alloc_security = selinux_msg_queue_alloc_security,
.msg_queue_free_security = selinux_msg_queue_free_security,
.msg_queue_associate = selinux_msg_queue_associate,
.msg_queue_msgctl = selinux_msg_queue_msgctl,
.msg_queue_msgsnd = selinux_msg_queue_msgsnd,
.msg_queue_msgrcv = selinux_msg_queue_msgrcv,
.shm_alloc_security = selinux_shm_alloc_security,
.shm_free_security = selinux_shm_free_security,
.shm_associate = selinux_shm_associate,
.shm_shmctl = selinux_shm_shmctl,
.shm_shmat = selinux_shm_shmat,
.sem_alloc_security = selinux_sem_alloc_security,
.sem_free_security = selinux_sem_free_security,
.sem_associate = selinux_sem_associate,
.sem_semctl = selinux_sem_semctl,
.sem_semop = selinux_sem_semop,
.register_security = selinux_register_security,
.unregister_security = selinux_unregister_security,
.d_instantiate = selinux_d_instantiate,
.getprocattr = selinux_getprocattr,
.setprocattr = selinux_setprocattr,
#ifdef CONFIG_SECURITY_NETWORK
.unix_stream_connect = selinux_socket_unix_stream_connect,
.unix_may_send = selinux_socket_unix_may_send,
.socket_create = selinux_socket_create,
.socket_post_create = selinux_socket_post_create,
.socket_bind = selinux_socket_bind,
.socket_connect = selinux_socket_connect,
.socket_listen = selinux_socket_listen,
.socket_accept = selinux_socket_accept,
.socket_sendmsg = selinux_socket_sendmsg,
.socket_recvmsg = selinux_socket_recvmsg,
.socket_getsockname = selinux_socket_getsockname,
.socket_getpeername = selinux_socket_getpeername,
.socket_getsockopt = selinux_socket_getsockopt,
.socket_setsockopt = selinux_socket_setsockopt,
.socket_shutdown = selinux_socket_shutdown,
#endif
};
__init int selinux_init(void)
{
struct task_security_struct *tsec;
printk(KERN_INFO "SELinux: Initializing.\n");
/* Set the security state for the initial task. */
if (task_alloc_security(current))
panic("SELinux: Failed to initialize initial task.\n");
tsec = current->security;
tsec->osid = tsec->sid = SECINITSID_KERNEL;
avc_init();
original_ops = secondary_ops = security_ops;
if (!secondary_ops)
panic ("SELinux: No initial security operations\n");
if (register_security (&selinux_ops))
panic("SELinux: Unable to register with kernel.\n");
if (selinux_enforcing) {
printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
} else {
printk(KERN_INFO "SELinux: Starting in permissive mode\n");
}
return 0;
}
void selinux_complete_init(void)
{
printk(KERN_INFO "SELinux: Completing initialization.\n");
/* Set up any superblocks initialized prior to the policy load. */
printk(KERN_INFO "SELinux: Setting up existing superblocks.\n");
spin_lock(&sb_security_lock);
next_sb:
if (!list_empty(&superblock_security_head)) {
struct superblock_security_struct *sbsec =
list_entry(superblock_security_head.next,
struct superblock_security_struct,
list);
struct super_block *sb = sbsec->sb;
spin_lock(&sb_lock);
sb->s_count++;
spin_unlock(&sb_lock);
spin_unlock(&sb_security_lock);
down_read(&sb->s_umount);
if (sb->s_root)
superblock_doinit(sb);
drop_super(sb);
spin_lock(&sb_security_lock);
list_del_init(&sbsec->list);
goto next_sb;
}
spin_unlock(&sb_security_lock);
/* Set up any inodes initialized prior to the policy load. */
printk(KERN_INFO "SELinux: Setting up existing inodes.\n");
spin_lock(&inode_security_lock);
next_inode:
if (!list_empty(&inode_security_head)) {
struct inode_security_struct *isec =
list_entry(inode_security_head.next,
struct inode_security_struct, list);
struct inode *inode = isec->inode;
spin_unlock(&inode_security_lock);
inode = igrab(inode);
if (inode) {
inode_doinit(inode);
iput(inode);
}
spin_lock(&inode_security_lock);
list_del_init(&isec->list);
goto next_inode;
}
spin_unlock(&inode_security_lock);
}
/* SELinux requires early initialization in order to label
all processes and objects when they are created. */
security_initcall(selinux_init);
/* This file is automatically generated. Do not edit. */
/* FLASK */
struct av_inherit
{
u16 tclass;
char **common_pts;
u32 common_base;
};
static struct av_inherit av_inherit[] = {
{ SECCLASS_DIR, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_LNK_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_CHR_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_BLK_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_SOCK_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_FIFO_FILE, common_file_perm_to_string, 0x00020000UL },
{ SECCLASS_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_TCP_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_UDP_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_RAWIP_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_NETLINK_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_PACKET_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_KEY_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_UNIX_STREAM_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_UNIX_DGRAM_SOCKET, common_socket_perm_to_string, 0x00400000UL },
{ SECCLASS_IPC, common_ipc_perm_to_string, 0x00000200UL },
{ SECCLASS_SEM, common_ipc_perm_to_string, 0x00000200UL },
{ SECCLASS_MSGQ, common_ipc_perm_to_string, 0x00000200UL },
{ SECCLASS_SHM, common_ipc_perm_to_string, 0x00000200UL },
};
/* FLASK */
/* This file is automatically generated. Do not edit. */
/* FLASK */
struct av_perm_to_string
{
u16 tclass;
u32 value;
char *name;
};
static struct av_perm_to_string av_perm_to_string[] = {
{ SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod" },
{ SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget" },
{ SECCLASS_DIR, DIR__ADD_NAME, "add_name" },
{ SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name" },
{ SECCLASS_DIR, DIR__REPARENT, "reparent" },
{ SECCLASS_DIR, DIR__SEARCH, "search" },
{ SECCLASS_DIR, DIR__RMDIR, "rmdir" },
{ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans" },
{ SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint" },
{ SECCLASS_FD, FD__USE, "use" },
{ SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto" },
{ SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn" },
{ SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom" },
{ SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv" },
{ SECCLASS_NODE, NODE__TCP_SEND, "tcp_send" },
{ SECCLASS_NODE, NODE__UDP_RECV, "udp_recv" },
{ SECCLASS_NODE, NODE__UDP_SEND, "udp_send" },
{ SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv" },
{ SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send" },
{ SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest" },
{ SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv" },
{ SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send" },
{ SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv" },
{ SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send" },
{ SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv" },
{ SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send" },
{ SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto" },
{ SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn" },
{ SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom" },
{ SECCLASS_PROCESS, PROCESS__FORK, "fork" },
{ SECCLASS_PROCESS, PROCESS__TRANSITION, "transition" },
{ SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld" },
{ SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill" },
{ SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop" },
{ SECCLASS_PROCESS, PROCESS__SIGNULL, "signull" },
{ SECCLASS_PROCESS, PROCESS__SIGNAL, "signal" },
{ SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace" },
{ SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched" },
{ SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched" },
{ SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession" },
{ SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid" },
{ SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid" },
{ SECCLASS_PROCESS, PROCESS__GETCAP, "getcap" },
{ SECCLASS_PROCESS, PROCESS__SETCAP, "setcap" },
{ SECCLASS_PROCESS, PROCESS__SHARE, "share" },
{ SECCLASS_PROCESS, PROCESS__GETATTR, "getattr" },
{ SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec" },
{ SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate" },
{ SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure" },
{ SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue" },
{ SECCLASS_MSG, MSG__SEND, "send" },
{ SECCLASS_MSG, MSG__RECEIVE, "receive" },
{ SECCLASS_SHM, SHM__LOCK, "lock" },
{ SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av" },
{ SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create" },
{ SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member" },
{ SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context" },
{ SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy" },
{ SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel" },
{ SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user" },
{ SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce" },
{ SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info" },
{ SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read" },
{ SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod" },
{ SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console" },
{ SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown" },
{ SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override" },
{ SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search" },
{ SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner" },
{ SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid" },
{ SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill" },
{ SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid" },
{ SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid" },
{ SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap" },
{ SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable" },
{ SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service" },
{ SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast" },
{ SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin" },
{ SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw" },
{ SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock" },
{ SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time" },
{ SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config" },
{ SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod" },
{ SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease" },
{ SECCLASS_PASSWD, PASSWD__PASSWD, "passwd" },
{ SECCLASS_PASSWD, PASSWD__CHFN, "chfn" },
{ SECCLASS_PASSWD, PASSWD__CHSH, "chsh" },
};
/* FLASK */
/* This file is automatically generated. Do not edit. */
/* FLASK */
#define COMMON_FILE__IOCTL 0x00000001UL
#define COMMON_FILE__READ 0x00000002UL
#define COMMON_FILE__WRITE 0x00000004UL
#define COMMON_FILE__CREATE 0x00000008UL
#define COMMON_FILE__GETATTR 0x00000010UL
#define COMMON_FILE__SETATTR 0x00000020UL
#define COMMON_FILE__LOCK 0x00000040UL
#define COMMON_FILE__RELABELFROM 0x00000080UL
#define COMMON_FILE__RELABELTO 0x00000100UL
#define COMMON_FILE__APPEND 0x00000200UL
#define COMMON_FILE__UNLINK 0x00000400UL
#define COMMON_FILE__LINK 0x00000800UL
#define COMMON_FILE__RENAME 0x00001000UL
#define COMMON_FILE__EXECUTE 0x00002000UL
#define COMMON_FILE__SWAPON 0x00004000UL
#define COMMON_FILE__QUOTAON 0x00008000UL
#define COMMON_FILE__MOUNTON 0x00010000UL
#define COMMON_SOCKET__IOCTL 0x00000001UL
#define COMMON_SOCKET__READ 0x00000002UL
#define COMMON_SOCKET__WRITE 0x00000004UL
#define COMMON_SOCKET__CREATE 0x00000008UL
#define COMMON_SOCKET__GETATTR 0x00000010UL
#define COMMON_SOCKET__SETATTR 0x00000020UL
#define COMMON_SOCKET__LOCK 0x00000040UL
#define COMMON_SOCKET__RELABELFROM 0x00000080UL
#define COMMON_SOCKET__RELABELTO 0x00000100UL
#define COMMON_SOCKET__APPEND 0x00000200UL
#define COMMON_SOCKET__BIND 0x00000400UL
#define COMMON_SOCKET__CONNECT 0x00000800UL
#define COMMON_SOCKET__LISTEN 0x00001000UL
#define COMMON_SOCKET__ACCEPT 0x00002000UL
#define COMMON_SOCKET__GETOPT 0x00004000UL
#define COMMON_SOCKET__SETOPT 0x00008000UL
#define COMMON_SOCKET__SHUTDOWN 0x00010000UL
#define COMMON_SOCKET__RECVFROM 0x00020000UL
#define COMMON_SOCKET__SENDTO 0x00040000UL
#define COMMON_SOCKET__RECV_MSG 0x00080000UL
#define COMMON_SOCKET__SEND_MSG 0x00100000UL
#define COMMON_SOCKET__NAME_BIND 0x00200000UL
#define COMMON_IPC__CREATE 0x00000001UL
#define COMMON_IPC__DESTROY 0x00000002UL
#define COMMON_IPC__GETATTR 0x00000004UL
#define COMMON_IPC__SETATTR 0x00000008UL
#define COMMON_IPC__READ 0x00000010UL
#define COMMON_IPC__WRITE 0x00000020UL
#define COMMON_IPC__ASSOCIATE 0x00000040UL
#define COMMON_IPC__UNIX_READ 0x00000080UL
#define COMMON_IPC__UNIX_WRITE 0x00000100UL
#define FILESYSTEM__MOUNT 0x00000001UL
#define FILESYSTEM__REMOUNT 0x00000002UL
#define FILESYSTEM__UNMOUNT 0x00000004UL
#define FILESYSTEM__GETATTR 0x00000008UL
#define FILESYSTEM__RELABELFROM 0x00000010UL
#define FILESYSTEM__RELABELTO 0x00000020UL
#define FILESYSTEM__TRANSITION 0x00000040UL
#define FILESYSTEM__ASSOCIATE 0x00000080UL
#define FILESYSTEM__QUOTAMOD 0x00000100UL
#define FILESYSTEM__QUOTAGET 0x00000200UL
#define DIR__EXECUTE 0x00002000UL
#define DIR__UNLINK 0x00000400UL
#define DIR__SETATTR 0x00000020UL
#define DIR__QUOTAON 0x00008000UL
#define DIR__RELABELFROM 0x00000080UL
#define DIR__LINK 0x00000800UL
#define DIR__WRITE 0x00000004UL
#define DIR__IOCTL 0x00000001UL
#define DIR__RELABELTO 0x00000100UL
#define DIR__READ 0x00000002UL
#define DIR__RENAME 0x00001000UL
#define DIR__APPEND 0x00000200UL
#define DIR__LOCK 0x00000040UL
#define DIR__SWAPON 0x00004000UL
#define DIR__GETATTR 0x00000010UL
#define DIR__MOUNTON 0x00010000UL
#define DIR__CREATE 0x00000008UL
#define DIR__ADD_NAME 0x00020000UL
#define DIR__REMOVE_NAME 0x00040000UL
#define DIR__REPARENT 0x00080000UL
#define DIR__SEARCH 0x00100000UL
#define DIR__RMDIR 0x00200000UL
#define FILE__EXECUTE 0x00002000UL
#define FILE__UNLINK 0x00000400UL
#define FILE__SETATTR 0x00000020UL
#define FILE__QUOTAON 0x00008000UL
#define FILE__RELABELFROM 0x00000080UL
#define FILE__LINK 0x00000800UL
#define FILE__WRITE 0x00000004UL
#define FILE__IOCTL 0x00000001UL
#define FILE__RELABELTO 0x00000100UL
#define FILE__READ 0x00000002UL
#define FILE__RENAME 0x00001000UL
#define FILE__APPEND 0x00000200UL
#define FILE__LOCK 0x00000040UL
#define FILE__SWAPON 0x00004000UL
#define FILE__GETATTR 0x00000010UL
#define FILE__MOUNTON 0x00010000UL
#define FILE__CREATE 0x00000008UL
#define FILE__EXECUTE_NO_TRANS 0x00020000UL
#define FILE__ENTRYPOINT 0x00040000UL
#define LNK_FILE__EXECUTE 0x00002000UL
#define LNK_FILE__UNLINK 0x00000400UL
#define LNK_FILE__SETATTR 0x00000020UL
#define LNK_FILE__QUOTAON 0x00008000UL
#define LNK_FILE__RELABELFROM 0x00000080UL
#define LNK_FILE__LINK 0x00000800UL
#define LNK_FILE__WRITE 0x00000004UL
#define LNK_FILE__IOCTL 0x00000001UL
#define LNK_FILE__RELABELTO 0x00000100UL
#define LNK_FILE__READ 0x00000002UL
#define LNK_FILE__RENAME 0x00001000UL
#define LNK_FILE__APPEND 0x00000200UL
#define LNK_FILE__LOCK 0x00000040UL
#define LNK_FILE__SWAPON 0x00004000UL
#define LNK_FILE__GETATTR 0x00000010UL
#define LNK_FILE__MOUNTON 0x00010000UL
#define LNK_FILE__CREATE 0x00000008UL
#define CHR_FILE__EXECUTE 0x00002000UL
#define CHR_FILE__UNLINK 0x00000400UL
#define CHR_FILE__SETATTR 0x00000020UL
#define CHR_FILE__QUOTAON 0x00008000UL
#define CHR_FILE__RELABELFROM 0x00000080UL
#define CHR_FILE__LINK 0x00000800UL
#define CHR_FILE__WRITE 0x00000004UL
#define CHR_FILE__IOCTL 0x00000001UL
#define CHR_FILE__RELABELTO 0x00000100UL
#define CHR_FILE__READ 0x00000002UL
#define CHR_FILE__RENAME 0x00001000UL
#define CHR_FILE__APPEND 0x00000200UL
#define CHR_FILE__LOCK 0x00000040UL
#define CHR_FILE__SWAPON 0x00004000UL
#define CHR_FILE__GETATTR 0x00000010UL
#define CHR_FILE__MOUNTON 0x00010000UL
#define CHR_FILE__CREATE 0x00000008UL
#define BLK_FILE__EXECUTE 0x00002000UL
#define BLK_FILE__UNLINK 0x00000400UL
#define BLK_FILE__SETATTR 0x00000020UL
#define BLK_FILE__QUOTAON 0x00008000UL
#define BLK_FILE__RELABELFROM 0x00000080UL
#define BLK_FILE__LINK 0x00000800UL
#define BLK_FILE__WRITE 0x00000004UL
#define BLK_FILE__IOCTL 0x00000001UL
#define BLK_FILE__RELABELTO 0x00000100UL
#define BLK_FILE__READ 0x00000002UL
#define BLK_FILE__RENAME 0x00001000UL
#define BLK_FILE__APPEND 0x00000200UL
#define BLK_FILE__LOCK 0x00000040UL
#define BLK_FILE__SWAPON 0x00004000UL
#define BLK_FILE__GETATTR 0x00000010UL
#define BLK_FILE__MOUNTON 0x00010000UL
#define BLK_FILE__CREATE 0x00000008UL
#define SOCK_FILE__EXECUTE 0x00002000UL
#define SOCK_FILE__UNLINK 0x00000400UL
#define SOCK_FILE__SETATTR 0x00000020UL
#define SOCK_FILE__QUOTAON 0x00008000UL
#define SOCK_FILE__RELABELFROM 0x00000080UL
#define SOCK_FILE__LINK 0x00000800UL
#define SOCK_FILE__WRITE 0x00000004UL
#define SOCK_FILE__IOCTL 0x00000001UL
#define SOCK_FILE__RELABELTO 0x00000100UL
#define SOCK_FILE__READ 0x00000002UL
#define SOCK_FILE__RENAME 0x00001000UL
#define SOCK_FILE__APPEND 0x00000200UL
#define SOCK_FILE__LOCK 0x00000040UL
#define SOCK_FILE__SWAPON 0x00004000UL
#define SOCK_FILE__GETATTR 0x00000010UL
#define SOCK_FILE__MOUNTON 0x00010000UL
#define SOCK_FILE__CREATE 0x00000008UL
#define FIFO_FILE__EXECUTE 0x00002000UL
#define FIFO_FILE__UNLINK 0x00000400UL
#define FIFO_FILE__SETATTR 0x00000020UL
#define FIFO_FILE__QUOTAON 0x00008000UL
#define FIFO_FILE__RELABELFROM 0x00000080UL
#define FIFO_FILE__LINK 0x00000800UL
#define FIFO_FILE__WRITE 0x00000004UL
#define FIFO_FILE__IOCTL 0x00000001UL
#define FIFO_FILE__RELABELTO 0x00000100UL
#define FIFO_FILE__READ 0x00000002UL
#define FIFO_FILE__RENAME 0x00001000UL
#define FIFO_FILE__APPEND 0x00000200UL
#define FIFO_FILE__LOCK 0x00000040UL
#define FIFO_FILE__SWAPON 0x00004000UL
#define FIFO_FILE__GETATTR 0x00000010UL
#define FIFO_FILE__MOUNTON 0x00010000UL
#define FIFO_FILE__CREATE 0x00000008UL
#define FD__USE 0x00000001UL
#define SOCKET__RELABELTO 0x00000100UL
#define SOCKET__RECV_MSG 0x00080000UL
#define SOCKET__RELABELFROM 0x00000080UL
#define SOCKET__SETOPT 0x00008000UL
#define SOCKET__APPEND 0x00000200UL
#define SOCKET__SETATTR 0x00000020UL
#define SOCKET__SENDTO 0x00040000UL
#define SOCKET__GETOPT 0x00004000UL
#define SOCKET__READ 0x00000002UL
#define SOCKET__SHUTDOWN 0x00010000UL
#define SOCKET__LISTEN 0x00001000UL
#define SOCKET__BIND 0x00000400UL
#define SOCKET__WRITE 0x00000004UL
#define SOCKET__ACCEPT 0x00002000UL
#define SOCKET__CONNECT 0x00000800UL
#define SOCKET__LOCK 0x00000040UL
#define SOCKET__IOCTL 0x00000001UL
#define SOCKET__CREATE 0x00000008UL
#define SOCKET__NAME_BIND 0x00200000UL
#define SOCKET__SEND_MSG 0x00100000UL
#define SOCKET__RECVFROM 0x00020000UL
#define SOCKET__GETATTR 0x00000010UL
#define TCP_SOCKET__RELABELTO 0x00000100UL
#define TCP_SOCKET__RECV_MSG 0x00080000UL
#define TCP_SOCKET__RELABELFROM 0x00000080UL
#define TCP_SOCKET__SETOPT 0x00008000UL
#define TCP_SOCKET__APPEND 0x00000200UL
#define TCP_SOCKET__SETATTR 0x00000020UL
#define TCP_SOCKET__SENDTO 0x00040000UL
#define TCP_SOCKET__GETOPT 0x00004000UL
#define TCP_SOCKET__READ 0x00000002UL
#define TCP_SOCKET__SHUTDOWN 0x00010000UL
#define TCP_SOCKET__LISTEN 0x00001000UL
#define TCP_SOCKET__BIND 0x00000400UL
#define TCP_SOCKET__WRITE 0x00000004UL
#define TCP_SOCKET__ACCEPT 0x00002000UL
#define TCP_SOCKET__CONNECT 0x00000800UL
#define TCP_SOCKET__LOCK 0x00000040UL
#define TCP_SOCKET__IOCTL 0x00000001UL
#define TCP_SOCKET__CREATE 0x00000008UL
#define TCP_SOCKET__NAME_BIND 0x00200000UL
#define TCP_SOCKET__SEND_MSG 0x00100000UL
#define TCP_SOCKET__RECVFROM 0x00020000UL
#define TCP_SOCKET__GETATTR 0x00000010UL
#define TCP_SOCKET__CONNECTTO 0x00400000UL
#define TCP_SOCKET__NEWCONN 0x00800000UL
#define TCP_SOCKET__ACCEPTFROM 0x01000000UL
#define UDP_SOCKET__RELABELTO 0x00000100UL
#define UDP_SOCKET__RECV_MSG 0x00080000UL
#define UDP_SOCKET__RELABELFROM 0x00000080UL
#define UDP_SOCKET__SETOPT 0x00008000UL
#define UDP_SOCKET__APPEND 0x00000200UL
#define UDP_SOCKET__SETATTR 0x00000020UL
#define UDP_SOCKET__SENDTO 0x00040000UL
#define UDP_SOCKET__GETOPT 0x00004000UL
#define UDP_SOCKET__READ 0x00000002UL
#define UDP_SOCKET__SHUTDOWN 0x00010000UL
#define UDP_SOCKET__LISTEN 0x00001000UL
#define UDP_SOCKET__BIND 0x00000400UL
#define UDP_SOCKET__WRITE 0x00000004UL
#define UDP_SOCKET__ACCEPT 0x00002000UL
#define UDP_SOCKET__CONNECT 0x00000800UL
#define UDP_SOCKET__LOCK 0x00000040UL
#define UDP_SOCKET__IOCTL 0x00000001UL
#define UDP_SOCKET__CREATE 0x00000008UL
#define UDP_SOCKET__NAME_BIND 0x00200000UL
#define UDP_SOCKET__SEND_MSG 0x00100000UL
#define UDP_SOCKET__RECVFROM 0x00020000UL
#define UDP_SOCKET__GETATTR 0x00000010UL
#define RAWIP_SOCKET__RELABELTO 0x00000100UL
#define RAWIP_SOCKET__RECV_MSG 0x00080000UL
#define RAWIP_SOCKET__RELABELFROM 0x00000080UL
#define RAWIP_SOCKET__SETOPT 0x00008000UL
#define RAWIP_SOCKET__APPEND 0x00000200UL
#define RAWIP_SOCKET__SETATTR 0x00000020UL
#define RAWIP_SOCKET__SENDTO 0x00040000UL
#define RAWIP_SOCKET__GETOPT 0x00004000UL
#define RAWIP_SOCKET__READ 0x00000002UL
#define RAWIP_SOCKET__SHUTDOWN 0x00010000UL
#define RAWIP_SOCKET__LISTEN 0x00001000UL
#define RAWIP_SOCKET__BIND 0x00000400UL
#define RAWIP_SOCKET__WRITE 0x00000004UL
#define RAWIP_SOCKET__ACCEPT 0x00002000UL
#define RAWIP_SOCKET__CONNECT 0x00000800UL
#define RAWIP_SOCKET__LOCK 0x00000040UL
#define RAWIP_SOCKET__IOCTL 0x00000001UL
#define RAWIP_SOCKET__CREATE 0x00000008UL
#define RAWIP_SOCKET__NAME_BIND 0x00200000UL
#define RAWIP_SOCKET__SEND_MSG 0x00100000UL
#define RAWIP_SOCKET__RECVFROM 0x00020000UL
#define RAWIP_SOCKET__GETATTR 0x00000010UL
#define NODE__TCP_RECV 0x00000001UL
#define NODE__TCP_SEND 0x00000002UL
#define NODE__UDP_RECV 0x00000004UL
#define NODE__UDP_SEND 0x00000008UL
#define NODE__RAWIP_RECV 0x00000010UL
#define NODE__RAWIP_SEND 0x00000020UL
#define NODE__ENFORCE_DEST 0x00000040UL
#define NETIF__TCP_RECV 0x00000001UL
#define NETIF__TCP_SEND 0x00000002UL
#define NETIF__UDP_RECV 0x00000004UL
#define NETIF__UDP_SEND 0x00000008UL
#define NETIF__RAWIP_RECV 0x00000010UL
#define NETIF__RAWIP_SEND 0x00000020UL
#define NETLINK_SOCKET__RELABELTO 0x00000100UL
#define NETLINK_SOCKET__RECV_MSG 0x00080000UL
#define NETLINK_SOCKET__RELABELFROM 0x00000080UL
#define NETLINK_SOCKET__SETOPT 0x00008000UL
#define NETLINK_SOCKET__APPEND 0x00000200UL
#define NETLINK_SOCKET__SETATTR 0x00000020UL
#define NETLINK_SOCKET__SENDTO 0x00040000UL
#define NETLINK_SOCKET__GETOPT 0x00004000UL
#define NETLINK_SOCKET__READ 0x00000002UL
#define NETLINK_SOCKET__SHUTDOWN 0x00010000UL
#define NETLINK_SOCKET__LISTEN 0x00001000UL
#define NETLINK_SOCKET__BIND 0x00000400UL
#define NETLINK_SOCKET__WRITE 0x00000004UL
#define NETLINK_SOCKET__ACCEPT 0x00002000UL
#define NETLINK_SOCKET__CONNECT 0x00000800UL
#define NETLINK_SOCKET__LOCK 0x00000040UL
#define NETLINK_SOCKET__IOCTL 0x00000001UL
#define NETLINK_SOCKET__CREATE 0x00000008UL
#define NETLINK_SOCKET__NAME_BIND 0x00200000UL
#define NETLINK_SOCKET__SEND_MSG 0x00100000UL
#define NETLINK_SOCKET__RECVFROM 0x00020000UL
#define NETLINK_SOCKET__GETATTR 0x00000010UL
#define PACKET_SOCKET__RELABELTO 0x00000100UL
#define PACKET_SOCKET__RECV_MSG 0x00080000UL
#define PACKET_SOCKET__RELABELFROM 0x00000080UL
#define PACKET_SOCKET__SETOPT 0x00008000UL
#define PACKET_SOCKET__APPEND 0x00000200UL
#define PACKET_SOCKET__SETATTR 0x00000020UL
#define PACKET_SOCKET__SENDTO 0x00040000UL
#define PACKET_SOCKET__GETOPT 0x00004000UL
#define PACKET_SOCKET__READ 0x00000002UL
#define PACKET_SOCKET__SHUTDOWN 0x00010000UL
#define PACKET_SOCKET__LISTEN 0x00001000UL
#define PACKET_SOCKET__BIND 0x00000400UL
#define PACKET_SOCKET__WRITE 0x00000004UL
#define PACKET_SOCKET__ACCEPT 0x00002000UL
#define PACKET_SOCKET__CONNECT 0x00000800UL
#define PACKET_SOCKET__LOCK 0x00000040UL
#define PACKET_SOCKET__IOCTL 0x00000001UL
#define PACKET_SOCKET__CREATE 0x00000008UL
#define PACKET_SOCKET__NAME_BIND 0x00200000UL
#define PACKET_SOCKET__SEND_MSG 0x00100000UL
#define PACKET_SOCKET__RECVFROM 0x00020000UL
#define PACKET_SOCKET__GETATTR 0x00000010UL
#define KEY_SOCKET__RELABELTO 0x00000100UL
#define KEY_SOCKET__RECV_MSG 0x00080000UL
#define KEY_SOCKET__RELABELFROM 0x00000080UL
#define KEY_SOCKET__SETOPT 0x00008000UL
#define KEY_SOCKET__APPEND 0x00000200UL
#define KEY_SOCKET__SETATTR 0x00000020UL
#define KEY_SOCKET__SENDTO 0x00040000UL
#define KEY_SOCKET__GETOPT 0x00004000UL
#define KEY_SOCKET__READ 0x00000002UL
#define KEY_SOCKET__SHUTDOWN 0x00010000UL
#define KEY_SOCKET__LISTEN 0x00001000UL
#define KEY_SOCKET__BIND 0x00000400UL
#define KEY_SOCKET__WRITE 0x00000004UL
#define KEY_SOCKET__ACCEPT 0x00002000UL
#define KEY_SOCKET__CONNECT 0x00000800UL
#define KEY_SOCKET__LOCK 0x00000040UL
#define KEY_SOCKET__IOCTL 0x00000001UL
#define KEY_SOCKET__CREATE 0x00000008UL
#define KEY_SOCKET__NAME_BIND 0x00200000UL
#define KEY_SOCKET__SEND_MSG 0x00100000UL
#define KEY_SOCKET__RECVFROM 0x00020000UL
#define KEY_SOCKET__GETATTR 0x00000010UL
#define UNIX_STREAM_SOCKET__RELABELTO 0x00000100UL
#define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL
#define UNIX_STREAM_SOCKET__RELABELFROM 0x00000080UL
#define UNIX_STREAM_SOCKET__SETOPT 0x00008000UL
#define UNIX_STREAM_SOCKET__APPEND 0x00000200UL
#define UNIX_STREAM_SOCKET__SETATTR 0x00000020UL
#define UNIX_STREAM_SOCKET__SENDTO 0x00040000UL
#define UNIX_STREAM_SOCKET__GETOPT 0x00004000UL
#define UNIX_STREAM_SOCKET__READ 0x00000002UL
#define UNIX_STREAM_SOCKET__SHUTDOWN 0x00010000UL
#define UNIX_STREAM_SOCKET__LISTEN 0x00001000UL
#define UNIX_STREAM_SOCKET__BIND 0x00000400UL
#define UNIX_STREAM_SOCKET__WRITE 0x00000004UL
#define UNIX_STREAM_SOCKET__ACCEPT 0x00002000UL
#define UNIX_STREAM_SOCKET__CONNECT 0x00000800UL
#define UNIX_STREAM_SOCKET__LOCK 0x00000040UL
#define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL
#define UNIX_STREAM_SOCKET__CREATE 0x00000008UL
#define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL
#define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL
#define UNIX_STREAM_SOCKET__RECVFROM 0x00020000UL
#define UNIX_STREAM_SOCKET__GETATTR 0x00000010UL
#define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL
#define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL
#define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL
#define UNIX_DGRAM_SOCKET__RELABELTO 0x00000100UL
#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
#define UNIX_DGRAM_SOCKET__RELABELFROM 0x00000080UL
#define UNIX_DGRAM_SOCKET__SETOPT 0x00008000UL
#define UNIX_DGRAM_SOCKET__APPEND 0x00000200UL
#define UNIX_DGRAM_SOCKET__SETATTR 0x00000020UL
#define UNIX_DGRAM_SOCKET__SENDTO 0x00040000UL
#define UNIX_DGRAM_SOCKET__GETOPT 0x00004000UL
#define UNIX_DGRAM_SOCKET__READ 0x00000002UL
#define UNIX_DGRAM_SOCKET__SHUTDOWN 0x00010000UL
#define UNIX_DGRAM_SOCKET__LISTEN 0x00001000UL
#define UNIX_DGRAM_SOCKET__BIND 0x00000400UL
#define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL
#define UNIX_DGRAM_SOCKET__ACCEPT 0x00002000UL
#define UNIX_DGRAM_SOCKET__CONNECT 0x00000800UL
#define UNIX_DGRAM_SOCKET__LOCK 0x00000040UL
#define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL
#define UNIX_DGRAM_SOCKET__CREATE 0x00000008UL
#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
#define UNIX_DGRAM_SOCKET__RECVFROM 0x00020000UL
#define UNIX_DGRAM_SOCKET__GETATTR 0x00000010UL
#define PROCESS__FORK 0x00000001UL
#define PROCESS__TRANSITION 0x00000002UL
#define PROCESS__SIGCHLD 0x00000004UL
#define PROCESS__SIGKILL 0x00000008UL
#define PROCESS__SIGSTOP 0x00000010UL
#define PROCESS__SIGNULL 0x00000020UL
#define PROCESS__SIGNAL 0x00000040UL
#define PROCESS__PTRACE 0x00000080UL
#define PROCESS__GETSCHED 0x00000100UL
#define PROCESS__SETSCHED 0x00000200UL
#define PROCESS__GETSESSION 0x00000400UL
#define PROCESS__GETPGID 0x00000800UL
#define PROCESS__SETPGID 0x00001000UL
#define PROCESS__GETCAP 0x00002000UL
#define PROCESS__SETCAP 0x00004000UL
#define PROCESS__SHARE 0x00008000UL
#define PROCESS__GETATTR 0x00010000UL
#define PROCESS__SETEXEC 0x00020000UL
#define PROCESS__SETFSCREATE 0x00040000UL
#define PROCESS__NOATSECURE 0x00080000UL
#define IPC__SETATTR 0x00000008UL
#define IPC__READ 0x00000010UL
#define IPC__ASSOCIATE 0x00000040UL
#define IPC__DESTROY 0x00000002UL
#define IPC__UNIX_WRITE 0x00000100UL
#define IPC__CREATE 0x00000001UL
#define IPC__UNIX_READ 0x00000080UL
#define IPC__GETATTR 0x00000004UL
#define IPC__WRITE 0x00000020UL
#define SEM__SETATTR 0x00000008UL
#define SEM__READ 0x00000010UL
#define SEM__ASSOCIATE 0x00000040UL
#define SEM__DESTROY 0x00000002UL
#define SEM__UNIX_WRITE 0x00000100UL
#define SEM__CREATE 0x00000001UL
#define SEM__UNIX_READ 0x00000080UL
#define SEM__GETATTR 0x00000004UL
#define SEM__WRITE 0x00000020UL
#define MSGQ__SETATTR 0x00000008UL
#define MSGQ__READ 0x00000010UL
#define MSGQ__ASSOCIATE 0x00000040UL
#define MSGQ__DESTROY 0x00000002UL
#define MSGQ__UNIX_WRITE 0x00000100UL
#define MSGQ__CREATE 0x00000001UL
#define MSGQ__UNIX_READ 0x00000080UL
#define MSGQ__GETATTR 0x00000004UL
#define MSGQ__WRITE 0x00000020UL
#define MSGQ__ENQUEUE 0x00000200UL
#define MSG__SEND 0x00000001UL
#define MSG__RECEIVE 0x00000002UL
#define SHM__SETATTR 0x00000008UL
#define SHM__READ 0x00000010UL
#define SHM__ASSOCIATE 0x00000040UL
#define SHM__DESTROY 0x00000002UL
#define SHM__UNIX_WRITE 0x00000100UL
#define SHM__CREATE 0x00000001UL
#define SHM__UNIX_READ 0x00000080UL
#define SHM__GETATTR 0x00000004UL
#define SHM__WRITE 0x00000020UL
#define SHM__LOCK 0x00000200UL
#define SECURITY__COMPUTE_AV 0x00000001UL
#define SECURITY__COMPUTE_CREATE 0x00000002UL
#define SECURITY__COMPUTE_MEMBER 0x00000004UL
#define SECURITY__CHECK_CONTEXT 0x00000008UL
#define SECURITY__LOAD_POLICY 0x00000010UL
#define SECURITY__COMPUTE_RELABEL 0x00000020UL
#define SECURITY__COMPUTE_USER 0x00000040UL
#define SECURITY__SETENFORCE 0x00000080UL
#define SYSTEM__IPC_INFO 0x00000001UL
#define SYSTEM__SYSLOG_READ 0x00000002UL
#define SYSTEM__SYSLOG_MOD 0x00000004UL
#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
#define CAPABILITY__CHOWN 0x00000001UL
#define CAPABILITY__DAC_OVERRIDE 0x00000002UL
#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
#define CAPABILITY__FOWNER 0x00000008UL
#define CAPABILITY__FSETID 0x00000010UL
#define CAPABILITY__KILL 0x00000020UL
#define CAPABILITY__SETGID 0x00000040UL
#define CAPABILITY__SETUID 0x00000080UL
#define CAPABILITY__SETPCAP 0x00000100UL
#define CAPABILITY__LINUX_IMMUTABLE 0x00000200UL
#define CAPABILITY__NET_BIND_SERVICE 0x00000400UL
#define CAPABILITY__NET_BROADCAST 0x00000800UL
#define CAPABILITY__NET_ADMIN 0x00001000UL
#define CAPABILITY__NET_RAW 0x00002000UL
#define CAPABILITY__IPC_LOCK 0x00004000UL
#define CAPABILITY__IPC_OWNER 0x00008000UL
#define CAPABILITY__SYS_MODULE 0x00010000UL
#define CAPABILITY__SYS_RAWIO 0x00020000UL
#define CAPABILITY__SYS_CHROOT 0x00040000UL
#define CAPABILITY__SYS_PTRACE 0x00080000UL
#define CAPABILITY__SYS_PACCT 0x00100000UL
#define CAPABILITY__SYS_ADMIN 0x00200000UL
#define CAPABILITY__SYS_BOOT 0x00400000UL
#define CAPABILITY__SYS_NICE 0x00800000UL
#define CAPABILITY__SYS_RESOURCE 0x01000000UL
#define CAPABILITY__SYS_TIME 0x02000000UL
#define CAPABILITY__SYS_TTY_CONFIG 0x04000000UL
#define CAPABILITY__MKNOD 0x08000000UL
#define CAPABILITY__LEASE 0x10000000UL
#define PASSWD__PASSWD 0x00000001UL
#define PASSWD__CHFN 0x00000002UL
#define PASSWD__CHSH 0x00000004UL
/* FLASK */
/*
* 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 <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;
/*
* A reference to an AVC entry.
*/
struct avc_entry_ref {
struct avc_entry *ae;
};
/* Initialize an AVC entry reference before first use. */
static inline void avc_entry_ref_init(struct avc_entry_ref *h)
{
h->ae = NULL;
}
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 sk_buff *skb;
struct sock *sk;
u16 port;
u32 daddr;
} net;
int cap;
int ipc_id;
} u;
};
/* 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
*/
#define AVC_ENTRY_LOOKUPS 0
#define AVC_ENTRY_HITS 1
#define AVC_ENTRY_MISSES 2
#define AVC_ENTRY_DISCARDS 3
#define AVC_CAV_LOOKUPS 4
#define AVC_CAV_HITS 5
#define AVC_CAV_PROBES 6
#define AVC_CAV_MISSES 7
#define AVC_NSTATS 8
extern unsigned avc_cache_stats[AVC_NSTATS];
#ifdef AVC_CACHE_STATS
static inline void avc_cache_stats_incr(int type)
{
avc_cache_stats[type]++;
}
static inline void avc_cache_stats_add(int type, unsigned val)
{
avc_cache_stats[type] += val;
}
#else
static inline void avc_cache_stats_incr(int type)
{ }
static inline void avc_cache_stats_add(int type, unsigned val)
{ }
#endif
/*
* AVC display support
*/
void avc_dump_av(u16 tclass, u32 av);
void avc_dump_query(u32 ssid, u32 tsid, u16 tclass);
void avc_dump_cache(char *tag);
/*
* AVC operations
*/
void avc_init(void);
int avc_lookup(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct avc_entry_ref *aeref);
int avc_insert(u32 ssid, u32 tsid, u16 tclass,
struct avc_entry *ae, struct avc_entry_ref *out_aeref);
void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *auditdata);
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct avc_entry_ref *aeref, struct av_decision *avd);
int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct avc_entry_ref *aeref, 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);
#endif /* _SELINUX_AVC_H_ */
/*
* 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_grant(u32 ssid, u32 tsid, u16 tclass, u32 perms, u32 seqno);
int avc_ss_try_revoke(u32 ssid, u32 tsid, u16 tclass, u32 perms, u32 seqno,
u32 *out_retained);
int avc_ss_revoke(u32 ssid, u32 tsid, u16 tclass, u32 perms, u32 seqno);
int avc_ss_reset(u32 seqno);
int avc_ss_set_auditallow(u32 ssid, u32 tsid, u16 tclass, u32 perms,
u32 seqno, u32 enable);
int avc_ss_set_auditdeny(u32 ssid, u32 tsid, u16 tclass, u32 perms,
u32 seqno, u32 enable);
#endif /* _SELINUX_AVC_SS_H_ */
/* This file is automatically generated. Do not edit. */
/*
* Security object class definitions
*/
static char *class_to_string[] =
{
"null",
"security",
"process",
"system",
"capability",
"filesystem",
"file",
"dir",
"fd",
"lnk_file",
"chr_file",
"blk_file",
"sock_file",
"fifo_file",
"socket",
"tcp_socket",
"udp_socket",
"rawip_socket",
"node",
"netif",
"netlink_socket",
"packet_socket",
"key_socket",
"unix_stream_socket",
"unix_dgram_socket",
"sem",
"msg",
"msgq",
"shm",
"ipc",
"passwd",
};
/* This file is automatically generated. Do not edit. */
/* FLASK */
static char *common_file_perm_to_string[] =
{
"ioctl",
"read",
"write",
"create",
"getattr",
"setattr",
"lock",
"relabelfrom",
"relabelto",
"append",
"unlink",
"link",
"rename",
"execute",
"swapon",
"quotaon",
"mounton",
};
static char *common_socket_perm_to_string[] =
{
"ioctl",
"read",
"write",
"create",
"getattr",
"setattr",
"lock",
"relabelfrom",
"relabelto",
"append",
"bind",
"connect",
"listen",
"accept",
"getopt",
"setopt",
"shutdown",
"recvfrom",
"sendto",
"recv_msg",
"send_msg",
"name_bind",
};
static char *common_ipc_perm_to_string[] =
{
"create",
"destroy",
"getattr",
"setattr",
"read",
"write",
"associate",
"unix_read",
"unix_write",
};
/* FLASK */
/* 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
/*
* 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_NUM 26
#endif
/* 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",
};
/*
* 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/in.h>
#include "flask.h"
#include "avc.h"
struct task_security_struct {
unsigned long magic; /* magic number for this module */
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 */
struct avc_entry_ref avcr; /* reference to process permissions */
};
struct inode_security_struct {
unsigned long magic; /* magic number for this module */
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 */
struct avc_entry_ref avcr; /* reference to object permissions */
unsigned char initialized; /* initialization flag */
struct semaphore sem;
unsigned char inherit; /* inherit SID from parent entry */
};
struct file_security_struct {
unsigned long magic; /* magic number for this module */
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 avc_entry_ref avcr; /* reference to fd permissions */
struct avc_entry_ref inode_avcr; /* reference to object permissions */
};
struct superblock_security_struct {
unsigned long magic; /* magic number for this module */
struct super_block *sb; /* back pointer to sb object */
struct list_head list; /* list of superblock_security_struct */
u32 sid; /* SID of file system */
unsigned int behavior; /* labeling behavior */
unsigned char initialized; /* initialization flag */
unsigned char proc; /* proc fs */
struct semaphore sem;
};
struct msg_security_struct {
unsigned long magic; /* magic number for this module */
struct msg_msg *msg; /* back pointer */
u32 sid; /* SID of message */
struct avc_entry_ref avcr; /* reference to permissions */
};
struct ipc_security_struct {
unsigned long magic; /* magic number for this module */
struct kern_ipc_perm *ipc_perm; /* back pointer */
u16 sclass; /* security class of this object */
u32 sid; /* SID of IPC resource */
struct avc_entry_ref avcr; /* reference to permissions */
};
extern int inode_security_set_sid(struct inode *inode, u32 sid);
#endif /* _SELINUX_OBJSEC_H_ */
/*
* 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
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_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);
#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 */
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_ */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/security.h>
#include <asm/uaccess.h>
/* selinuxfs pseudo filesystem for exporting the security policy API.
Based on the proc code and the fs/nfsd/nfsctl.c code. */
#include "flask.h"
#include "avc.h"
#include "avc_ss.h"
#include "security.h"
#include "objsec.h"
/* Check whether a task is allowed to use a security operation. */
int task_has_security(struct task_struct *tsk,
u32 perms)
{
struct task_security_struct *tsec;
tsec = tsk->security;
return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
SECCLASS_SECURITY, perms, NULL, NULL);
}
enum sel_inos {
SEL_ROOT_INO = 2,
SEL_LOAD, /* load policy */
SEL_ENFORCE, /* get or set enforcing status */
SEL_CONTEXT, /* validate context */
SEL_ACCESS, /* compute access decision */
SEL_CREATE, /* compute create labeling decision */
SEL_RELABEL, /* compute relabeling decision */
SEL_USER /* compute reachable user contexts */
};
static ssize_t sel_read_enforce(struct file *filp, char *buf,
size_t count, loff_t *ppos)
{
char *page;
ssize_t length;
ssize_t end;
if (count < 0 || count > PAGE_SIZE)
return -EINVAL;
if (!(page = (char*)__get_free_page(GFP_KERNEL)))
return -ENOMEM;
memset(page, 0, PAGE_SIZE);
length = snprintf(page, PAGE_SIZE, "%d", selinux_enforcing);
if (length < 0) {
free_page((unsigned long)page);
return length;
}
if (*ppos >= length) {
free_page((unsigned long)page);
return 0;
}
if (count + *ppos > length)
count = length - *ppos;
end = count + *ppos;
if (copy_to_user(buf, (char *) page + *ppos, count)) {
count = -EFAULT;
goto out;
}
*ppos = end;
out:
free_page((unsigned long)page);
return count;
}
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
static ssize_t sel_write_enforce(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
char *page;
ssize_t length;
int new_value;
if (count < 0 || count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
/* No partial writes. */
return -EINVAL;
}
page = (char*)__get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
memset(page, 0, PAGE_SIZE);
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
goto out;
if (new_value != selinux_enforcing) {
length = task_has_security(current, SECURITY__SETENFORCE);
if (length)
goto out;
selinux_enforcing = new_value;
if (selinux_enforcing)
avc_ss_reset(0);
}
length = count;
out:
free_page((unsigned long) page);
return length;
}
#else
#define sel_write_enforce NULL
#endif
static struct file_operations sel_enforce_ops = {
.read = sel_read_enforce,
.write = sel_write_enforce,
};
static ssize_t sel_write_load(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
ssize_t length;
void *data;
length = task_has_security(current, SECURITY__LOAD_POLICY);
if (length)
return length;
if (*ppos != 0) {
/* No partial writes. */
return -EINVAL;
}
if ((count < 0) || (count > 64 * 1024 * 1024) || (data = vmalloc(count)) == NULL)
return -ENOMEM;
length = -EFAULT;
if (copy_from_user(data, buf, count) != 0)
goto out;
length = security_load_policy(data, count);
if (length)
goto out;
length = count;
out:
vfree(data);
return length;
}
static struct file_operations sel_load_ops = {
.write = sel_write_load,
};
static ssize_t sel_write_context(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
char *page;
u32 sid;
ssize_t length;
length = task_has_security(current, SECURITY__CHECK_CONTEXT);
if (length)
return length;
if (count < 0 || count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
/* No partial writes. */
return -EINVAL;
}
page = (char*)__get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
memset(page, 0, PAGE_SIZE);
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
length = security_context_to_sid(page, count, &sid);
if (length < 0)
goto out;
length = count;
out:
free_page((unsigned long) page);
return length;
}
static struct file_operations sel_context_ops = {
.write = sel_write_context,
};
/*
* Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
*/
static ssize_t sel_write_access(struct file * file, char *buf, size_t size);
static ssize_t sel_write_create(struct file * file, char *buf, size_t size);
static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size);
static ssize_t sel_write_user(struct file * file, char *buf, size_t size);
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[SEL_ACCESS] = sel_write_access,
[SEL_CREATE] = sel_write_create,
[SEL_RELABEL] = sel_write_relabel,
[SEL_USER] = sel_write_user,
};
/* an argresp is stored in an allocated page and holds the
* size of the argument or response, along with its content
*/
struct argresp {
ssize_t size;
char data[0];
};
#define PAYLOAD_SIZE (PAGE_SIZE - sizeof(struct argresp))
/*
* transaction based IO methods.
* The file expects a single write which triggers the transaction, and then
* possibly a read which collects the result - which is stored in a
* file-local buffer.
*/
static ssize_t TA_write(struct file *file, const char *buf, size_t size, loff_t *pos)
{
ino_t ino = file->f_dentry->d_inode->i_ino;
struct argresp *ar;
ssize_t rv = 0;
if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
return -EINVAL;
if (file->private_data)
return -EINVAL; /* only one write allowed per open */
if (size > PAYLOAD_SIZE - 1) /* allow one byte for null terminator */
return -EFBIG;
ar = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ar)
return -ENOMEM;
memset(ar, 0, PAGE_SIZE); /* clear buffer, particularly last byte */
ar->size = 0;
down(&file->f_dentry->d_inode->i_sem);
if (file->private_data)
rv = -EINVAL;
else
file->private_data = ar;
up(&file->f_dentry->d_inode->i_sem);
if (rv) {
kfree(ar);
return rv;
}
if (copy_from_user(ar->data, buf, size))
return -EFAULT;
rv = write_op[ino](file, ar->data, size);
if (rv>0) {
ar->size = rv;
rv = size;
}
return rv;
}
static ssize_t TA_read(struct file *file, char *buf, size_t size, loff_t *pos)
{
struct argresp *ar;
ssize_t rv = 0;
if (file->private_data == NULL)
rv = TA_write(file, buf, 0, pos);
if (rv < 0)
return rv;
ar = file->private_data;
if (!ar)
return 0;
if (*pos >= ar->size)
return 0;
if (*pos + size > ar->size)
size = ar->size - *pos;
if (copy_to_user(buf, ar->data + *pos, size))
return -EFAULT;
*pos += size;
return size;
}
static int TA_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static int TA_release(struct inode *inode, struct file *file)
{
void *p = file->private_data;
file->private_data = NULL;
kfree(p);
return 0;
}
static struct file_operations transaction_ops = {
.write = TA_write,
.read = TA_read,
.open = TA_open,
.release = TA_release,
};
/*
* payload - write methods
* If the method has a response, the response should be put in buf,
* and the length returned. Otherwise return 0 or and -error.
*/
static ssize_t sel_write_access(struct file * file, char *buf, size_t size)
{
char *scon, *tcon;
u32 ssid, tsid;
u16 tclass;
u32 req;
struct av_decision avd;
ssize_t length;
length = task_has_security(current, SECURITY__COMPUTE_AV);
if (length)
return length;
length = -ENOMEM;
scon = kmalloc(size+1, GFP_KERNEL);
if (!scon)
return length;
memset(scon, 0, size+1);
tcon = kmalloc(size+1, GFP_KERNEL);
if (!tcon)
goto out;
memset(tcon, 0, size+1);
length = -EINVAL;
if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
goto out2;
length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
if (length < 0)
goto out2;
length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
if (length < 0)
goto out2;
length = security_compute_av(ssid, tsid, tclass, req, &avd);
if (length < 0)
goto out2;
length = snprintf(buf, PAYLOAD_SIZE, "%x %x %x %x %u",
avd.allowed, avd.decided,
avd.auditallow, avd.auditdeny,
avd.seqno);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
static ssize_t sel_write_create(struct file * file, char *buf, size_t size)
{
char *scon, *tcon;
u32 ssid, tsid, newsid;
u16 tclass;
ssize_t length;
char *newcon;
u32 len;
length = task_has_security(current, SECURITY__COMPUTE_CREATE);
if (length)
return length;
length = -ENOMEM;
scon = kmalloc(size+1, GFP_KERNEL);
if (!scon)
return length;
memset(scon, 0, size+1);
tcon = kmalloc(size+1, GFP_KERNEL);
if (!tcon)
goto out;
memset(tcon, 0, size+1);
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
if (length < 0)
goto out2;
length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
if (length < 0)
goto out2;
length = security_transition_sid(ssid, tsid, tclass, &newsid);
if (length < 0)
goto out2;
length = security_sid_to_context(newsid, &newcon, &len);
if (length < 0)
goto out2;
if (len > PAYLOAD_SIZE) {
printk(KERN_ERR "%s: context size (%u) exceeds payload "
"max\n", __FUNCTION__, len);
length = -ERANGE;
goto out3;
}
memcpy(buf, newcon, len);
length = len;
out3:
kfree(newcon);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size)
{
char *scon, *tcon;
u32 ssid, tsid, newsid;
u16 tclass;
ssize_t length;
char *newcon;
u32 len;
length = task_has_security(current, SECURITY__COMPUTE_RELABEL);
if (length)
return length;
length = -ENOMEM;
scon = kmalloc(size+1, GFP_KERNEL);
if (!scon)
return length;
memset(scon, 0, size+1);
tcon = kmalloc(size+1, GFP_KERNEL);
if (!tcon)
goto out;
memset(tcon, 0, size+1);
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
if (length < 0)
goto out2;
length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
if (length < 0)
goto out2;
length = security_change_sid(ssid, tsid, tclass, &newsid);
if (length < 0)
goto out2;
length = security_sid_to_context(newsid, &newcon, &len);
if (length < 0)
goto out2;
if (len > PAYLOAD_SIZE) {
length = -ERANGE;
goto out3;
}
memcpy(buf, newcon, len);
length = len;
out3:
kfree(newcon);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
static ssize_t sel_write_user(struct file * file, char *buf, size_t size)
{
char *con, *user, *ptr;
u32 sid, *sids;
ssize_t length;
char *newcon;
int i, rc;
u32 len, nsids;
length = task_has_security(current, SECURITY__COMPUTE_USER);
if (length)
return length;
length = -ENOMEM;
con = kmalloc(size+1, GFP_KERNEL);
if (!con)
return length;
memset(con, 0, size+1);
user = kmalloc(size+1, GFP_KERNEL);
if (!user)
goto out;
memset(user, 0, size+1);
length = -EINVAL;
if (sscanf(buf, "%s %s", con, user) != 2)
goto out2;
length = security_context_to_sid(con, strlen(con)+1, &sid);
if (length < 0)
goto out2;
length = security_get_user_sids(sid, user, &sids, &nsids);
if (length < 0)
goto out2;
length = sprintf(buf, "%u", nsids) + 1;
ptr = buf + length;
for (i = 0; i < nsids; i++) {
rc = security_sid_to_context(sids[i], &newcon, &len);
if (rc) {
length = rc;
goto out3;
}
if ((length + len) >= PAYLOAD_SIZE) {
kfree(newcon);
length = -ERANGE;
goto out3;
}
memcpy(ptr, newcon, len);
kfree(newcon);
ptr += len;
length += len;
}
out3:
kfree(sids);
out2:
kfree(user);
out:
kfree(con);
return length;
}
static int sel_fill_super(struct super_block * sb, void * data, int silent)
{
static struct tree_descr selinux_files[] = {
[SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
[SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUSR|S_IWUSR},
[SEL_CONTEXT] = {"context", &sel_context_ops, S_IRUGO|S_IWUGO},
[SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
[SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},
[SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO},
[SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO},
/* last one */ {""}
};
return simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
}
static struct super_block *sel_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, sel_fill_super);
}
static struct file_system_type sel_fs_type = {
.name = "selinuxfs",
.get_sb = sel_get_sb,
.kill_sb = kill_litter_super,
};
static int __init init_sel_fs(void)
{
return register_filesystem(&sel_fs_type);
}
__initcall(init_sel_fs);
#
# Makefile for building the SELinux security server as part of the kernel tree.
#
EXTRA_CFLAGS += -Isecurity/selinux/include -include security/selinux/ss/global.h
obj-y := ss.o
ss-objs := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o
ifeq ($(CONFIG_SECURITY_SELINUX_MLS),y)
ss-objs += mls.o
endif
/*
* Implementation of the access vector table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "avtab.h"
#include "policydb.h"
#define AVTAB_HASH(keyp) \
((keyp->target_class + \
(keyp->target_type << 2) + \
(keyp->source_type << 9)) & \
AVTAB_HASH_MASK)
int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
{
int hvalue;
struct avtab_node *prev, *cur, *newnode;
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 &&
(datum->specified & cur->datum.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 = kmalloc(sizeof(*newnode), GFP_KERNEL);
if (newnode == NULL)
return -ENOMEM;
memset(newnode, 0, sizeof(*newnode));
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;
}
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int specified)
{
int hvalue;
struct avtab_node *cur;
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->datum.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;
}
void avtab_destroy(struct avtab *h)
{
int i;
struct avtab_node *cur, *temp;
if (!h)
return;
for (i = 0; i < AVTAB_SIZE; i++) {
cur = h->htable[i];
while (cur != NULL) {
temp = cur;
cur = cur->next;
kfree(temp);
}
h->htable[i] = NULL;
}
kfree(h->htable);
}
int avtab_map(struct avtab *h,
int (*apply) (struct avtab_key *k,
struct avtab_datum *d,
void *args),
void *args)
{
int i, ret;
struct avtab_node *cur;
if (!h)
return 0;
for (i = 0; i < AVTAB_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;
}
int avtab_init(struct avtab *h)
{
int i;
h->htable = kmalloc(sizeof(*(h->htable)) * AVTAB_SIZE, GFP_KERNEL);
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_INFO "%s: %d entries and %d/%d buckets used, longest "
"chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE,
max_chain_len);
}
int avtab_read(struct avtab *a, void *fp, u32 config)
{
int i, rc = -EINVAL;
struct avtab_key avkey;
struct avtab_datum avdatum;
u32 *buf;
u32 nel, items, items2;
buf = next_entry(fp, sizeof(u32));
if (!buf) {
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");
goto bad;
}
for (i = 0; i < nel; i++) {
memset(&avkey, 0, sizeof(avkey));
memset(&avdatum, 0, sizeof(avdatum));
buf = next_entry(fp, sizeof(u32));
if (!buf) {
printk(KERN_ERR "security: avtab: truncated entry\n");
goto bad;
}
items2 = le32_to_cpu(buf[0]);
buf = next_entry(fp, sizeof(u32)*items2);
if (!buf) {
printk(KERN_ERR "security: avtab: truncated entry\n");
goto bad;
}
items = 0;
avkey.source_type = le32_to_cpu(buf[items++]);
avkey.target_type = le32_to_cpu(buf[items++]);
avkey.target_class = le32_to_cpu(buf[items++]);
avdatum.specified = le32_to_cpu(buf[items++]);
if (!(avdatum.specified & (AVTAB_AV | AVTAB_TYPE))) {
printk(KERN_ERR "security: avtab: null entry\n");
goto bad;
}
if ((avdatum.specified & AVTAB_AV) &&
(avdatum.specified & AVTAB_TYPE)) {
printk(KERN_ERR "security: avtab: entry has both "
"access vectors and types\n");
goto bad;
}
if (avdatum.specified & AVTAB_AV) {
if (avdatum.specified & AVTAB_ALLOWED)
avtab_allowed(&avdatum) = le32_to_cpu(buf[items++]);
if (avdatum.specified & AVTAB_AUDITDENY)
avtab_auditdeny(&avdatum) = le32_to_cpu(buf[items++]);
if (avdatum.specified & AVTAB_AUDITALLOW)
avtab_auditallow(&avdatum) = le32_to_cpu(buf[items++]);
} else {
if (avdatum.specified & AVTAB_TRANSITION)
avtab_transition(&avdatum) = le32_to_cpu(buf[items++]);
if (avdatum.specified & AVTAB_CHANGE)
avtab_change(&avdatum) = le32_to_cpu(buf[items++]);
if (avdatum.specified & AVTAB_MEMBER)
avtab_member(&avdatum) = le32_to_cpu(buf[items++]);
}
if (items != items2) {
printk(KERN_ERR "security: avtab: entry only had %d "
"items, expected %d\n", items2, items);
goto bad;
}
rc = avtab_insert(a, &avkey, &avdatum);
if (rc) {
if (rc == -ENOMEM)
printk(KERN_ERR "security: avtab: out of memory\n");
if (rc == -EEXIST)
printk(KERN_ERR "security: avtab: duplicate entry\n");
goto bad;
}
}
rc = 0;
out:
return rc;
bad:
avtab_destroy(a);
goto out;
}
/*
* 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>
*/
#ifndef _SS_AVTAB_H_
#define _SS_AVTAB_H_
struct avtab_key {
u32 source_type; /* source type */
u32 target_type; /* target type */
u32 target_class; /* target object class */
};
struct avtab_datum {
#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)
u32 specified; /* what fields are specified */
u32 data[3]; /* access vectors or types */
#define avtab_allowed(x) (x)->data[0]
#define avtab_auditdeny(x) (x)->data[1]
#define avtab_auditallow(x) (x)->data[2]
#define avtab_transition(x) (x)->data[0]
#define avtab_change(x) (x)->data[1]
#define avtab_member(x) (x)->data[2]
};
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 *);
int avtab_insert(struct avtab *h, struct avtab_key *k, struct avtab_datum *d);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k, int specified);
void avtab_destroy(struct avtab *h);
int avtab_map(struct avtab *h,
int (*apply) (struct avtab_key *k,
struct avtab_datum *d,
void *args),
void *args);
void avtab_hash_eval(struct avtab *h, char *tag);
int avtab_read(struct avtab *a, void *fp, u32 config);
#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_ */
/*
* 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 */
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_ */
/*
* 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"
/*
* 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;
#ifdef CONFIG_SECURITY_SELINUX_MLS
struct mls_range range;
#endif
};
#ifdef CONFIG_SECURITY_SELINUX_MLS
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;
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;
}
static inline int mls_context_cmp(struct context *c1, struct context *c2)
{
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)
{
ebitmap_destroy(&c->range.level[0].cat);
ebitmap_destroy(&c->range.level[1].cat);
mls_context_init(c);
}
#else
static inline void mls_context_init(struct context *c)
{ }
static inline int mls_context_cpy(struct context *dst, struct context *src)
{ return 0; }
static inline int mls_context_cmp(struct context *c1, struct context *c2)
{ return 1; }
static inline void mls_context_destroy(struct context *c)
{ }
#endif
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_ */
/*
* Implementation of the extensible bitmap type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "ebitmap.h"
#include "policydb.h"
int ebitmap_or(struct ebitmap *dst, struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2, *new, *prev;
ebitmap_init(dst);
n1 = e1->node;
n2 = e2->node;
prev = 0;
while (n1 || n2) {
new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
ebitmap_destroy(dst);
return -ENOMEM;
}
memset(new, 0, sizeof(*new));
if (n1 && n2 && n1->startbit == n2->startbit) {
new->startbit = n1->startbit;
new->map = n1->map | n2->map;
n1 = n1->next;
n2 = n2->next;
} else if (!n2 || (n1 && n1->startbit < n2->startbit)) {
new->startbit = n1->startbit;
new->map = n1->map;
n1 = n1->next;
} else {
new->startbit = n2->startbit;
new->map = n2->map;
n2 = n2->next;
}
new->next = 0;
if (prev)
prev->next = new;
else
dst->node = new;
prev = new;
}
dst->highbit = (e1->highbit > e2->highbit) ? e1->highbit : e2->highbit;
return 0;
}
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 = 0;
while (n) {
new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
ebitmap_destroy(dst);
return -ENOMEM;
}
memset(new, 0, sizeof(*new));
new->startbit = n->startbit;
new->map = n->map;
new->next = 0;
if (prev)
prev->next = new;
else
dst->node = new;
prev = new;
n = n->next;
}
dst->highbit = src->highbit;
return 0;
}
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 = 0;
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 = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new)
return -ENOMEM;
memset(new, 0, sizeof(*new));
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 = 0;
return;
}
int ebitmap_read(struct ebitmap *e, void *fp)
{
int rc = -EINVAL;
struct ebitmap_node *n, *l;
u32 *buf, mapsize, count, i;
u64 map;
ebitmap_init(e);
buf = next_entry(fp, sizeof(u32)*3);
if (!buf)
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 %d does not "
"match my size %d (high bit was %d)\n", mapsize,
MAPSIZE, e->highbit);
goto out;
}
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 (%d)\n", e->highbit, MAPSIZE);
goto bad;
}
l = NULL;
for (i = 0; i < count; i++) {
buf = next_entry(fp, sizeof(u32));
if (!buf) {
printk(KERN_ERR "security: ebitmap: truncated map\n");
goto bad;
}
n = kmalloc(sizeof(*n), GFP_KERNEL);
if (!n) {
printk(KERN_ERR "security: ebitmap: out of memory\n");
rc = -ENOMEM;
goto bad;
}
memset(n, 0, sizeof(*n));
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 (%d)\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 (%d)\n",
n->startbit, (e->highbit - MAPSIZE));
goto bad_free;
}
buf = next_entry(fp, sizeof(u64));
if (!buf) {
printk(KERN_ERR "security: ebitmap: truncated map\n");
goto bad_free;
}
memcpy(&map, buf, sizeof(u64));
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:
ebitmap_destroy(e);
goto out;
}
/*
* 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_
#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 void ebitmap_init(struct ebitmap *e)
{
memset(e, 0, sizeof(*e));
}
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_or(struct ebitmap *dst, 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);
#endif /* _SS_EBITMAP_H_ */
#ifndef _SS_GLOBAL_H_
#define _SS_GLOBAL_H_
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/in.h>
#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include "flask.h"
#include "avc.h"
#include "avc_ss.h"
#include "security.h"
#endif /* _SS_GLOBAL_H_ */
/*
* Implementation of the hash table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "hashtab.h"
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key),
int (*keycmp)(struct hashtab *h, void *key1, void *key2),
u32 size)
{
struct hashtab *p;
u32 i;
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return p;
memset(p, 0, sizeof(*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 = kmalloc(sizeof(*newnode), GFP_KERNEL);
if (newnode == NULL)
return -ENOMEM;
memset(newnode, 0, sizeof(*newnode));
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;
}
int hashtab_remove(struct hashtab *h, void *key,
void (*destroy)(void *k, void *d, void *args),
void *args)
{
u32 hvalue;
struct hashtab_node *cur, *last;
if (!h)
return -EINVAL;
hvalue = h->hash_value(h, key);
last = NULL;
cur = h->htable[hvalue];
while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
last = cur;
cur = cur->next;
}
if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
return -ENOENT;
if (last == NULL)
h->htable[hvalue] = cur->next;
else
last->next = cur->next;
if (destroy)
destroy(cur->key, cur->datum, args);
kfree(cur);
h->nel--;
return 0;
}
int hashtab_replace(struct hashtab *h, void *key, void *datum,
void (*destroy)(void *k, void *d, void *args),
void *args)
{
u32 hvalue;
struct hashtab_node *prev, *cur, *newnode;
if (!h)
return -EINVAL;
hvalue = h->hash_value(h, key);
prev = NULL;
cur = h->htable[hvalue];
while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
prev = cur;
cur = cur->next;
}
if (cur && (h->keycmp(h, key, cur->key) == 0)) {
if (destroy)
destroy(cur->key, cur->datum, args);
cur->key = key;
cur->datum = datum;
} else {
newnode = kmalloc(sizeof(*newnode), GFP_KERNEL);
if (newnode == NULL)
return -ENOMEM;
memset(newnode, 0, sizeof(*newnode));
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;
}
}
return 0;
}
void *hashtab_search(struct hashtab *h, 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_map_remove_on_error(struct hashtab *h,
int (*apply)(void *k, void *d, void *args),
void (*destroy)(void *k, void *d, void *args),
void *args)
{
u32 i;
int ret;
struct hashtab_node *last, *cur, *temp;
if (!h)
return;
for (i = 0; i < h->size; i++) {
last = NULL;
cur = h->htable[i];
while (cur != NULL) {
ret = apply(cur->key, cur->datum, args);
if (ret) {
if (last)
last->next = cur->next;
else
h->htable[i] = cur->next;
temp = cur;
cur = cur->next;
if (destroy)
destroy(temp->key, temp->datum, args);
kfree(temp);
h->nel--;
} else {
last = cur;
cur = cur->next;
}
}
}
return;
}
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;
}
/*
* 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, void *key);
/* hash function */
int (*keycmp)(struct hashtab *h, void *key1, 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, void *key),
int (*keycmp)(struct hashtab *h, void *key1, 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);
/*
* Removes the entry with the specified key from the hash table.
* Applies the specified destroy function to (key,datum,args) for
* the entry.
*
* Returns -ENOENT if no entry has the specified key,
* -EINVAL for general errors or
*0 otherwise.
*/
int hashtab_remove(struct hashtab *h, void *k,
void (*destroy)(void *k, void *d, void *args),
void *args);
/*
* Insert or replace the specified (key, datum) pair in the specified
* hash table. If an entry for the specified key already exists,
* then the specified destroy function is applied to (key,datum,args)
* for the entry prior to replacing the entry's contents.
*
* Returns -ENOMEM if insufficient space is available,
* -EINVAL for general errors or
* 0 otherwise.
*/
int hashtab_replace(struct hashtab *h, void *k, void *d,
void (*destroy)(void *k, void *d, void *args),
void *args);
/*
* 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, 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);
/*
* Same as hashtab_map, except that if apply returns a non-zero status,
* then the (key,datum) pair will be removed from the hashtab and the
* destroy function will be applied to (key,datum,args).
*/
void hashtab_map_remove_on_error(struct hashtab *h,
int (*apply)(void *k, void *d, void *args),
void (*destroy)(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 */
/*
* Implementation of the multi-level security (MLS) policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "mls.h"
#include "policydb.h"
#include "services.h"
/*
* Remove any permissions from `allowed' that are
* denied by the MLS policy.
*/
void mls_compute_av(struct context *scontext,
struct context *tcontext,
struct class_datum *tclass,
u32 *allowed)
{
unsigned int rel[2];
int l;
for (l = 0; l < 2; l++)
rel[l] = mls_level_relation(scontext->range.level[l],
tcontext->range.level[l]);
if (rel[1] != MLS_RELATION_EQ) {
if (rel[1] != MLS_RELATION_DOM &&
!ebitmap_get_bit(&policydb.trustedreaders, scontext->type - 1) &&
!ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) {
/* read(s,t) = (s.high >= t.high) = False */
*allowed = (*allowed) & ~(tclass->mlsperms.read);
}
if (rel[1] != MLS_RELATION_DOMBY &&
!ebitmap_get_bit(&policydb.trustedreaders, tcontext->type - 1) &&
!ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) {
/* readby(s,t) = read(t,s) = False */
*allowed = (*allowed) & ~(tclass->mlsperms.readby);
}
}
if (((rel[0] != MLS_RELATION_DOMBY && rel[0] != MLS_RELATION_EQ) ||
((!mls_level_eq(tcontext->range.level[0],
tcontext->range.level[1])) &&
(rel[1] != MLS_RELATION_DOM && rel[1] != MLS_RELATION_EQ))) &&
!ebitmap_get_bit(&policydb.trustedwriters, scontext->type - 1) &&
!ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) {
/*
* write(s,t) = ((s.low <= t.low = t.high) or (s.low
* <= t.low <= t.high <= s.high)) = False
*/
*allowed = (*allowed) & ~(tclass->mlsperms.write);
}
if (((rel[0] != MLS_RELATION_DOM && rel[0] != MLS_RELATION_EQ) ||
((!mls_level_eq(scontext->range.level[0],
scontext->range.level[1])) &&
(rel[1] != MLS_RELATION_DOMBY && rel[1] != MLS_RELATION_EQ))) &&
!ebitmap_get_bit(&policydb.trustedwriters, tcontext->type - 1) &&
!ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) {
/* writeby(s,t) = write(t,s) = False */
*allowed = (*allowed) & ~(tclass->mlsperms.writeby);
}
}
/*
* 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;
len = 0;
for (l = 0; l < 2; l++) {
len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]) + 1;
for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++)
if (ebitmap_get_bit(&context->range.level[l].cat, i - 1))
len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
if (mls_level_relation(context->range.level[0], context->range.level[1])
== MLS_RELATION_EQ)
break;
}
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.
*/
int mls_sid_to_context(struct context *context,
char **scontext)
{
char *scontextp;
int i, l;
scontextp = *scontext;
for (l = 0; l < 2; l++) {
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]);
*scontextp = ':';
scontextp++;
for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++)
if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
*scontextp = ',';
scontextp++;
}
if (mls_level_relation(context->range.level[0], context->range.level[1])
!= MLS_RELATION_EQ) {
scontextp--;
sprintf(scontextp, "-");
scontextp++;
} else {
break;
}
}
*scontext = scontextp;
return 0;
}
/*
* 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)
{
unsigned int relation;
struct level_datum *levdatum;
struct user_datum *usrdatum;
struct mls_range_list *rnode;
int i, l;
/*
* 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)
*/
relation = mls_level_relation(c->range.level[1],
c->range.level[0]);
if (!(relation & (MLS_RELATION_DOM | MLS_RELATION_EQ)))
/* 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;
for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
if (i > p->p_cats.nprim)
return 0;
if (!ebitmap_get_bit(&levdatum->level->cat, i - 1))
/*
* 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];
for (rnode = usrdatum->ranges; rnode; rnode = rnode->next) {
if (mls_range_contains(rnode->range, c->range))
break;
}
if (!rnode)
/* user may not be associated with range */
return 0;
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.
*/
int mls_context_to_sid(char oldc,
char **scontext,
struct context *context)
{
char delim;
char *scontextp, *p;
struct level_datum *levdatum;
struct cat_datum *catdatum;
int l, rc = -EINVAL;
if (!oldc) {
/* No MLS component to the security context. Try
to use a default 'unclassified' value. */
levdatum = hashtab_search(policydb.p_levels.table,
"unclassified");
if (!levdatum)
goto out;
context->range.level[0].sens = levdatum->level->sens;
context->range.level[1].sens = context->range.level[0].sens;
rc = 0;
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)
goto out;
context->range.level[l].sens = levdatum->level->sens;
if (delim == ':') {
/* Extract low category set. */
while (1) {
scontextp = p;
while (*p && *p != ',' && *p != '-')
p++;
delim = *p;
if (delim != 0)
*p++ = 0;
catdatum = hashtab_search(policydb.p_cats.table,
scontextp);
if (!catdatum)
goto out;
rc = ebitmap_set_bit(&context->range.level[l].cat,
catdatum->value - 1, 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;
}
/*
* Copies the MLS range from `src' into `dst'.
*/
static inline int mls_copy_context(struct context *dst,
struct context *src)
{
int l, rc = 0;
/* Copy the MLS range from the source context */
for (l = 0; l < 2; l++) {
dst->range.level[l].sens = src->range.level[l].sens;
rc = ebitmap_cpy(&dst->range.level[l].cat,
&src->range.level[l].cat);
if (rc)
break;
}
return rc;
}
/*
* 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;
int l, i;
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);
for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
int rc;
catdatum = hashtab_search(newp->p_cats.table,
oldp->p_cat_val_to_name[i - 1]);
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)
{
switch (specified) {
case AVTAB_TRANSITION:
case AVTAB_CHANGE:
/* Use the process MLS attributes. */
return mls_copy_context(newcontext, scontext);
case AVTAB_MEMBER:
/* Only polyinstantiate the MLS attributes if
the type is being polyinstantiated */
if (newcontext->type != tcontext->type) {
/* Use the process MLS attributes. */
return mls_copy_context(newcontext, scontext);
} else {
/* Use the related object MLS attributes. */
return mls_copy_context(newcontext, tcontext);
}
default:
return -EINVAL;
}
return -EINVAL;
}
void mls_user_destroy(struct user_datum *usrdatum)
{
struct mls_range_list *rnode, *rtmp;
rnode = usrdatum->ranges;
while (rnode) {
rtmp = rnode;
rnode = rnode->next;
ebitmap_destroy(&rtmp->range.level[0].cat);
ebitmap_destroy(&rtmp->range.level[1].cat);
kfree(rtmp);
}
}
int mls_read_perm(struct perm_datum *perdatum, void *fp)
{
u32 *buf;
buf = next_entry(fp, sizeof(u32));
if (!buf)
return -EINVAL;
perdatum->base_perms = le32_to_cpu(buf[0]);
return 0;
}
/*
* Read a MLS level structure from a policydb binary
* representation file.
*/
struct mls_level *mls_read_level(void *fp)
{
struct mls_level *l;
u32 *buf;
l = kmalloc(sizeof(*l), GFP_ATOMIC);
if (!l) {
printk(KERN_ERR "security: mls: out of memory\n");
return NULL;
}
memset(l, 0, sizeof(*l));
buf = next_entry(fp, sizeof(u32));
if (!buf) {
printk(KERN_ERR "security: mls: truncated level\n");
goto bad;
}
l->sens = cpu_to_le32(buf[0]);
if (ebitmap_read(&l->cat, fp)) {
printk(KERN_ERR "security: mls: error reading level "
"categories\n");
goto bad;
}
return l;
bad:
kfree(l);
return NULL;
}
/*
* Read a MLS range structure from a policydb binary
* representation file.
*/
static int mls_read_range_helper(struct mls_range *r, void *fp)
{
u32 *buf;
int items, rc = -EINVAL;
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto out;
items = le32_to_cpu(buf[0]);
buf = next_entry(fp, sizeof(u32) * items);
if (!buf) {
printk(KERN_ERR "security: mls: truncated range\n");
goto out;
}
r->level[0].sens = le32_to_cpu(buf[0]);
if (items > 1) {
r->level[1].sens = le32_to_cpu(buf[1]);
} else {
r->level[1].sens = r->level[0].sens;
}
rc = ebitmap_read(&r->level[0].cat, fp);
if (rc) {
printk(KERN_ERR "security: mls: error reading low "
"categories\n");
goto out;
}
if (items > 1) {
rc = ebitmap_read(&r->level[1].cat, fp);
if (rc) {
printk(KERN_ERR "security: mls: error reading high "
"categories\n");
goto bad_high;
}
} else {
rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat);
if (rc) {
printk(KERN_ERR "security: mls: out of memory\n");
goto bad_high;
}
}
rc = 0;
out:
return rc;
bad_high:
ebitmap_destroy(&r->level[0].cat);
goto out;
}
int mls_read_range(struct context *c, void *fp)
{
return mls_read_range_helper(&c->range, fp);
}
/*
* Read a MLS perms structure from a policydb binary
* representation file.
*/
int mls_read_class(struct class_datum *cladatum, void *fp)
{
struct mls_perms *p = &cladatum->mlsperms;
u32 *buf;
buf = next_entry(fp, sizeof(u32)*4);
if (!buf) {
printk(KERN_ERR "security: mls: truncated mls permissions\n");
return -EINVAL;
}
p->read = le32_to_cpu(buf[0]);
p->readby = le32_to_cpu(buf[1]);
p->write = le32_to_cpu(buf[2]);
p->writeby = le32_to_cpu(buf[3]);
return 0;
}
int mls_read_user(struct user_datum *usrdatum, void *fp)
{
struct mls_range_list *r, *l;
int rc = 0;
u32 nel, i;
u32 *buf;
buf = next_entry(fp, sizeof(u32));
if (!buf) {
rc = -EINVAL;
goto out;
}
nel = le32_to_cpu(buf[0]);
l = NULL;
for (i = 0; i < nel; i++) {
r = kmalloc(sizeof(*r), GFP_ATOMIC);
if (!r) {
rc = -ENOMEM;
goto out;
}
memset(r, 0, sizeof(*r));
rc = mls_read_range_helper(&r->range, fp);
if (rc)
goto out;
if (l)
l->next = r;
else
usrdatum->ranges = r;
l = r;
}
out:
return rc;
}
int mls_read_nlevels(struct policydb *p, void *fp)
{
u32 *buf;
buf = next_entry(fp, sizeof(u32));
if (!buf)
return -EINVAL;
p->nlevels = le32_to_cpu(buf[0]);
return 0;
}
int mls_read_trusted(struct policydb *p, void *fp)
{
int rc = 0;
rc = ebitmap_read(&p->trustedreaders, fp);
if (rc)
goto out;
rc = ebitmap_read(&p->trustedwriters, fp);
if (rc)
goto out;
rc = ebitmap_read(&p->trustedobjects, fp);
out:
return rc;
}
int sens_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct level_datum *levdatum;
levdatum = datum;
p = datap;
if (!levdatum->isalias)
p->p_sens_val_to_name[levdatum->level->sens - 1] = key;
return 0;
}
int cat_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cat_datum *catdatum;
catdatum = datum;
p = datap;
if (!catdatum->isalias)
p->p_cat_val_to_name[catdatum->value - 1] = key;
return 0;
}
int sens_destroy(void *key, void *datum, void *p)
{
struct level_datum *levdatum;
kfree(key);
levdatum = datum;
if (!levdatum->isalias) {
ebitmap_destroy(&levdatum->level->cat);
kfree(levdatum->level);
}
kfree(datum);
return 0;
}
int cat_destroy(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
int sens_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct level_datum *levdatum;
int rc;
u32 *buf, len;
levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC);
if (!levdatum) {
rc = -ENOMEM;
goto out;
}
memset(levdatum, 0, sizeof(*levdatum));
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
levdatum->isalias = le32_to_cpu(buf[1]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_ATOMIC);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
levdatum->level = mls_read_level(fp);
if (!levdatum->level) {
rc = -EINVAL;
goto bad;
}
rc = hashtab_insert(h, key, levdatum);
if (rc)
goto bad;
out:
return rc;
bad:
sens_destroy(key, levdatum, NULL);
goto out;
}
int cat_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct cat_datum *catdatum;
int rc;
u32 *buf, len;
catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC);
if (!catdatum) {
rc = -ENOMEM;
goto out;
}
memset(catdatum, 0, sizeof(*catdatum));
buf = next_entry(fp, sizeof(u32)*3);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
catdatum->isalias = le32_to_cpu(buf[2]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_ATOMIC);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
rc = hashtab_insert(h, key, catdatum);
if (rc)
goto bad;
out:
return rc;
bad:
cat_destroy(key, catdatum, NULL);
goto out;
}
/*
* Multi-level security (MLS) policy operations.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_MLS_H_
#define _SS_MLS_H_
#include "context.h"
#include "policydb.h"
#ifdef CONFIG_SECURITY_SELINUX_MLS
void mls_compute_av(struct context *scontext,
struct context *tcontext,
struct class_datum *tclass,
u32 *allowed);
int mls_compute_context_len(struct context *context);
int 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);
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 sens_index(void *key, void *datum, void *datap);
int cat_index(void *key, void *datum, void *datap);
int sens_destroy(void *key, void *datum, void *p);
int cat_destroy(void *key, void *datum, void *p);
int sens_read(struct policydb *p, struct hashtab *h, void *fp);
int cat_read(struct policydb *p, struct hashtab *h, void *fp);
#define mls_for_user_ranges(user, usercon) { \
struct mls_range_list *__ranges; \
for (__ranges = user->ranges; __ranges; __ranges = __ranges->next) { \
usercon.range = __ranges->range;
#define mls_end_user_ranges } }
#define mls_symtab_names , "levels", "categories"
#define mls_symtab_sizes , 16, 16
#define mls_index_f ,sens_index, cat_index
#define mls_destroy_f ,sens_destroy, cat_destroy
#define mls_read_f ,sens_read, cat_read
#define mls_write_f ,sens_write, cat_write
#define mls_policydb_index_others(p) printk(", %d levels", p->nlevels);
#define mls_set_config(config) config |= POLICYDB_CONFIG_MLS
void mls_user_destroy(struct user_datum *usrdatum);
int mls_read_range(struct context *c, void *fp);
int mls_read_perm(struct perm_datum *perdatum, void *fp);
int mls_read_class(struct class_datum *cladatum, void *fp);
int mls_read_user(struct user_datum *usrdatum, void *fp);
int mls_read_nlevels(struct policydb *p, void *fp);
int mls_read_trusted(struct policydb *p, void *fp);
#else
#define mls_compute_av(scontext, tcontext, tclass_datum, allowed)
#define mls_compute_context_len(context) 0
#define mls_sid_to_context(context, scontextpp)
#define mls_context_isvalid(p, c) 1
#define mls_context_to_sid(oldc, context_str, context) 0
#define mls_convert_context(oldp, newp, c) 0
#define mls_compute_sid(scontext, tcontext, tclass, specified, newcontextp) 0
#define mls_for_user_ranges(user, usercon)
#define mls_end_user_ranges
#define mls_symtab_names
#define mls_symtab_sizes
#define mls_index_f
#define mls_destroy_f
#define mls_read_f
#define mls_write_f
#define mls_policydb_index_others(p)
#define mls_set_config(config)
#define mls_user_destroy(usrdatum)
#define mls_read_range(c, fp) 0
#define mls_read_perm(p, fp) 0
#define mls_read_class(c, fp) 0
#define mls_read_user(u, fp) 0
#define mls_read_nlevels(p, fp) 0
#define mls_read_trusted(p, fp) 0
#endif
#endif /* _SS_MLS_H */
/*
* Type definitions for the multi-level security (MLS) policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#ifndef _SS_MLS_TYPES_H_
#define _SS_MLS_TYPES_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] */
};
struct mls_range_list {
struct mls_range range;
struct mls_range_list *next;
};
#define MLS_RELATION_DOM 1 /* source dominates */
#define MLS_RELATION_DOMBY 2 /* target dominates */
#define MLS_RELATION_EQ 4 /* source and target are equivalent */
#define MLS_RELATION_INCOMP 8 /* source and target are incomparable */
#define mls_level_eq(l1,l2) \
(((l1).sens == (l2).sens) && ebitmap_cmp(&(l1).cat,&(l2).cat))
#define mls_level_relation(l1,l2) ( \
(((l1).sens == (l2).sens) && ebitmap_cmp(&(l1).cat,&(l2).cat)) ? \
MLS_RELATION_EQ : \
(((l1).sens >= (l2).sens) && ebitmap_contains(&(l1).cat, &(l2).cat)) ? \
MLS_RELATION_DOM : \
(((l2).sens >= (l1).sens) && ebitmap_contains(&(l2).cat, &(l1).cat)) ? \
MLS_RELATION_DOMBY : \
MLS_RELATION_INCOMP )
#define mls_range_contains(r1,r2) \
((mls_level_relation((r1).level[0], (r2).level[0]) & \
(MLS_RELATION_EQ | MLS_RELATION_DOMBY)) && \
(mls_level_relation((r1).level[1], (r2).level[1]) & \
(MLS_RELATION_EQ | MLS_RELATION_DOM)))
/*
* Every access vector permission is mapped to a set of MLS base
* permissions, based on the flow properties of the corresponding
* operation.
*/
struct mls_perms {
u32 read; /* permissions that map to `read' */
u32 readby; /* permissions that map to `readby' */
u32 write; /* permissions that map to `write' */
u32 writeby; /* permissions that map to `writeby' */
};
#endif /* _SS_MLS_TYPES_H_ */
/*
* Implementation of the policy database.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "policydb.h"
#include "mls.h"
#define _DEBUG_HASHES
#ifdef DEBUG_HASHES
static char *symtab_name[SYM_NUM] = {
"common prefixes",
"classes",
"roles",
"types",
"users"
mls_symtab_names
};
#endif
static unsigned int symtab_sizes[SYM_NUM] = {
2,
32,
16,
512,
128
mls_symtab_sizes
};
/*
* Initialize the role table.
*/
int roles_init(struct policydb *p)
{
char *key = 0;
int rc;
struct role_datum *role;
role = kmalloc(sizeof(*role), GFP_KERNEL);
if (!role) {
rc = -ENOMEM;
goto out;
}
memset(role, 0, sizeof(*role));
role->value = ++p->p_roles.nprim;
if (role->value != OBJECT_R_VAL) {
rc = -EINVAL;
goto out_free_role;
}
key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto out_free_role;
}
strcpy(key, OBJECT_R);
rc = hashtab_insert(p->p_roles.table, key, role);
if (rc)
goto out_free_key;
out:
return rc;
out_free_key:
kfree(key);
out_free_role:
kfree(role);
goto out;
}
/*
* Initialize a policy database structure.
*/
int policydb_init(struct policydb *p)
{
int i, rc;
memset(p, 0, sizeof(*p));
for (i = 0; i < SYM_NUM; i++) {
rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
if (rc)
goto out_free_symtab;
}
rc = avtab_init(&p->te_avtab);
if (rc)
goto out_free_symtab;
rc = roles_init(p);
if (rc)
goto out_free_avtab;
out:
return rc;
out_free_avtab:
avtab_destroy(&p->te_avtab);
out_free_symtab:
for (i = 0; i < SYM_NUM; i++)
hashtab_destroy(p->symtab[i].table);
goto out;
}
/*
* The following *_index functions are used to
* define the val_to_name and val_to_struct arrays
* in a policy database structure. The val_to_name
* arrays are used when converting security context
* structures into string representations. The
* val_to_struct arrays are used when the attributes
* of a class, role, or user are needed.
*/
static int common_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct common_datum *comdatum;
comdatum = datum;
p = datap;
p->p_common_val_to_name[comdatum->value - 1] = key;
return 0;
}
static int class_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct class_datum *cladatum;
cladatum = datum;
p = datap;
p->p_class_val_to_name[cladatum->value - 1] = key;
p->class_val_to_struct[cladatum->value - 1] = cladatum;
return 0;
}
static int role_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct role_datum *role;
role = datum;
p = datap;
p->p_role_val_to_name[role->value - 1] = key;
p->role_val_to_struct[role->value - 1] = role;
return 0;
}
static int type_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct type_datum *typdatum;
typdatum = datum;
p = datap;
if (typdatum->primary)
p->p_type_val_to_name[typdatum->value - 1] = key;
return 0;
}
static int user_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct user_datum *usrdatum;
usrdatum = datum;
p = datap;
p->p_user_val_to_name[usrdatum->value - 1] = key;
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
return 0;
}
static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
{
common_index,
class_index,
role_index,
type_index,
user_index
mls_index_f
};
/*
* Define the common val_to_name array and the class
* val_to_name and val_to_struct arrays in a policy
* database structure.
*
* Caller must clean up upon failure.
*/
int policydb_index_classes(struct policydb *p)
{
int rc;
p->p_common_val_to_name =
kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL);
if (!p->p_common_val_to_name) {
rc = -ENOMEM;
goto out;
}
rc = hashtab_map(p->p_commons.table, common_index, p);
if (rc)
goto out;
p->class_val_to_struct =
kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL);
if (!p->class_val_to_struct) {
rc = -ENOMEM;
goto out;
}
p->p_class_val_to_name =
kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL);
if (!p->p_class_val_to_name) {
rc = -ENOMEM;
goto out;
}
rc = hashtab_map(p->p_classes.table, class_index, p);
out:
return rc;
}
#ifdef DEBUG_HASHES
static void symtab_hash_eval(struct symtab *s)
{
int i;
for (i = 0; i < SYM_NUM; i++) {
struct hashtab *h = s[i].table;
struct hashtab_info info;
hashtab_stat(h, &info);
printk(KERN_INFO "%s: %d entries and %d/%d buckets used, "
"longest chain length %d\n", symtab_name[i], h->nel,
info.slots_used, h->size, info.max_chain_len);
}
}
#endif
/*
* Define the other val_to_name and val_to_struct arrays
* in a policy database structure.
*
* Caller must clean up on failure.
*/
int policydb_index_others(struct policydb *p)
{
int i, rc = 0;
printk(KERN_INFO "security: %d users, %d roles, %d types",
p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim);
mls_policydb_index_others(p);
printk("\n");
printk(KERN_INFO "security: %d classes, %d rules\n",
p->p_classes.nprim, p->te_avtab.nel);
#ifdef DEBUG_HASHES
avtab_hash_eval(&p->te_avtab, "rules");
symtab_hash_eval(p->symtab);
#endif
p->role_val_to_struct =
kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
GFP_KERNEL);
if (!p->role_val_to_struct) {
rc = -ENOMEM;
goto out;
}
p->user_val_to_struct =
kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
GFP_KERNEL);
if (!p->user_val_to_struct) {
rc = -ENOMEM;
goto out;
}
for (i = SYM_ROLES; i < SYM_NUM; i++) {
p->sym_val_to_name[i] =
kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL);
if (!p->sym_val_to_name[i]) {
rc = -ENOMEM;
goto out;
}
rc = hashtab_map(p->symtab[i].table, index_f[i], p);
if (rc)
goto out;
}
out:
return rc;
}
/*
* The following *_destroy functions are used to
* free any memory allocated for each kind of
* symbol data in the policy database.
*/
static int perm_destroy(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
static int common_destroy(void *key, void *datum, void *p)
{
struct common_datum *comdatum;
kfree(key);
comdatum = datum;
hashtab_map(comdatum->permissions.table, perm_destroy, 0);
hashtab_destroy(comdatum->permissions.table);
kfree(datum);
return 0;
}
static int class_destroy(void *key, void *datum, void *p)
{
struct class_datum *cladatum;
struct constraint_node *constraint, *ctemp;
struct constraint_expr *e, *etmp;
kfree(key);
cladatum = datum;
hashtab_map(cladatum->permissions.table, perm_destroy, 0);
hashtab_destroy(cladatum->permissions.table);
constraint = cladatum->constraints;
while (constraint) {
e = constraint->expr;
while (e) {
ebitmap_destroy(&e->names);
etmp = e;
e = e->next;
kfree(etmp);
}
ctemp = constraint;
constraint = constraint->next;
kfree(ctemp);
}
kfree(cladatum->comkey);
kfree(datum);
return 0;
}
static int role_destroy(void *key, void *datum, void *p)
{
struct role_datum *role;
kfree(key);
role = datum;
ebitmap_destroy(&role->dominates);
ebitmap_destroy(&role->types);
kfree(datum);
return 0;
}
static int type_destroy(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
static int user_destroy(void *key, void *datum, void *p)
{
struct user_datum *usrdatum;
kfree(key);
usrdatum = datum;
ebitmap_destroy(&usrdatum->roles);
mls_user_destroy(usrdatum);
kfree(datum);
return 0;
}
static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
{
common_destroy,
class_destroy,
role_destroy,
type_destroy,
user_destroy
mls_destroy_f
};
/*
* Free any memory allocated by a policy database structure.
*/
void policydb_destroy(struct policydb *p)
{
struct ocontext *c, *ctmp;
struct genfs *g, *gtmp;
int i;
for (i = 0; i < SYM_NUM; i++) {
hashtab_map(p->symtab[i].table, destroy_f[i], 0);
hashtab_destroy(p->symtab[i].table);
}
for (i = 0; i < SYM_NUM; i++) {
if (p->sym_val_to_name[i])
kfree(p->sym_val_to_name[i]);
}
if (p->class_val_to_struct)
kfree(p->class_val_to_struct);
if (p->role_val_to_struct)
kfree(p->role_val_to_struct);
if (p->user_val_to_struct)
kfree(p->user_val_to_struct);
avtab_destroy(&p->te_avtab);
for (i = 0; i < OCON_NUM; i++) {
c = p->ocontexts[i];
while (c) {
ctmp = c;
c = c->next;
context_destroy(&ctmp->context[0]);
context_destroy(&ctmp->context[1]);
if (i == OCON_ISID || i == OCON_FS ||
i == OCON_NETIF || i == OCON_FSUSE)
kfree(ctmp->u.name);
kfree(ctmp);
}
}
g = p->genfs;
while (g) {
kfree(g->fstype);
c = g->head;
while (c) {
ctmp = c;
c = c->next;
context_destroy(&ctmp->context[0]);
kfree(ctmp->u.name);
kfree(ctmp);
}
gtmp = g;
g = g->next;
kfree(gtmp);
}
return;
}
/*
* Load the initial SIDs specified in a policy database
* structure into a SID table.
*/
int policydb_load_isids(struct policydb *p, struct sidtab *s)
{
struct ocontext *head, *c;
int rc;
rc = sidtab_init(s);
if (rc) {
printk(KERN_ERR "security: out of memory on SID table init\n");
goto out;
}
head = p->ocontexts[OCON_ISID];
for (c = head; c; c = c->next) {
if (!c->context[0].user) {
printk(KERN_ERR "security: SID %s was never "
"defined.\n", c->u.name);
rc = -EINVAL;
goto out;
}
if (sidtab_insert(s, c->sid[0], &c->context[0])) {
printk(KERN_ERR "security: unable to load initial "
"SID %s.\n", c->u.name);
rc = -EINVAL;
goto out;
}
}
out:
return rc;
}
/*
* Return 1 if the fields in the security context
* structure `c' are valid. Return 0 otherwise.
*/
int policydb_context_isvalid(struct policydb *p, struct context *c)
{
struct role_datum *role;
struct user_datum *usrdatum;
/*
* Role must be authorized for the type.
*/
if (!c->role || c->role > p->p_roles.nprim)
return 0;
if (c->role != OBJECT_R_VAL) {
role = p->role_val_to_struct[c->role - 1];
if (!ebitmap_get_bit(&role->types,
c->type - 1))
/* role may not be associated with type */
return 0;
/*
* User must be authorized for the role.
*/
if (!c->user || c->user > p->p_users.nprim)
return 0;
usrdatum = p->user_val_to_struct[c->user - 1];
if (!usrdatum)
return 0;
if (!ebitmap_get_bit(&usrdatum->roles,
c->role - 1))
/* user may not be associated with role */
return 0;
}
if (!mls_context_isvalid(p, c))
return 0;
return 1;
}
/*
* Read and validate a security context structure
* from a policydb binary representation file.
*/
static int context_read_and_validate(struct context *c,
struct policydb *p,
void *fp)
{
u32 *buf;
int rc = 0;
buf = next_entry(fp, sizeof(u32)*3);
if (!buf) {
printk(KERN_ERR "security: context truncated\n");
rc = -EINVAL;
goto out;
}
c->user = le32_to_cpu(buf[0]);
c->role = le32_to_cpu(buf[1]);
c->type = le32_to_cpu(buf[2]);
if (mls_read_range(c, fp)) {
printk(KERN_ERR "security: error reading MLS range of "
"context\n");
rc = -EINVAL;
goto out;
}
if (!policydb_context_isvalid(p, c)) {
printk(KERN_ERR "security: invalid security context\n");
context_destroy(c);
rc = -EINVAL;
}
out:
return rc;
}
/*
* The following *_read functions are used to
* read the symbol data from a policy database
* binary representation file.
*/
static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct perm_datum *perdatum;
int rc;
u32 *buf, len;
perdatum = kmalloc(sizeof(*perdatum), GFP_KERNEL);
if (!perdatum) {
rc = -ENOMEM;
goto out;
}
memset(perdatum, 0, sizeof(*perdatum));
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
rc = mls_read_perm(perdatum, fp);
if (rc)
goto bad;
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
rc = hashtab_insert(h, key, perdatum);
if (rc)
goto bad;
out:
return rc;
bad:
perm_destroy(key, perdatum, NULL);
goto out;
}
static int common_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct common_datum *comdatum;
u32 *buf, len, nel;
int i, rc;
comdatum = kmalloc(sizeof(*comdatum), GFP_KERNEL);
if (!comdatum) {
rc = -ENOMEM;
goto out;
}
memset(comdatum, 0, sizeof(*comdatum));
buf = next_entry(fp, sizeof(u32)*4);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[3]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
for (i = 0; i < nel; i++) {
rc = perm_read(p, comdatum->permissions.table, fp);
if (rc)
goto bad;
}
rc = hashtab_insert(h, key, comdatum);
if (rc)
goto bad;
out:
return rc;
bad:
common_destroy(key, comdatum, NULL);
goto out;
}
static int class_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct class_datum *cladatum;
struct constraint_node *c, *lc;
struct constraint_expr *e, *le;
u32 *buf, len, len2, ncons, nexpr, nel;
int i, j, depth, rc;
cladatum = kmalloc(sizeof(*cladatum), GFP_KERNEL);
if (!cladatum) {
rc = -ENOMEM;
goto bad;
}
memset(cladatum, 0, sizeof(*cladatum));
buf = next_entry(fp, sizeof(u32)*6);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
cladatum->value = le32_to_cpu(buf[2]);
rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
nel = le32_to_cpu(buf[4]);
ncons = le32_to_cpu(buf[5]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
if (len2) {
cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL);
if (!cladatum->comkey) {
rc = -ENOMEM;
goto bad;
}
buf = next_entry(fp, len2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
memcpy(cladatum->comkey, buf, len2);
cladatum->comkey[len2] = 0;
cladatum->comdatum = hashtab_search(p->p_commons.table,
cladatum->comkey);
if (!cladatum->comdatum) {
printk(KERN_ERR "security: unknown common %s\n",
cladatum->comkey);
rc = -EINVAL;
goto bad;
}
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, cladatum->permissions.table, fp);
if (rc)
goto bad;
}
lc = NULL;
rc = -EINVAL;
for (i = 0; i < ncons; i++) {
c = kmalloc(sizeof(*c), GFP_KERNEL);
if (!c) {
rc = -ENOMEM;
goto bad;
}
memset(c, 0, sizeof(*c));
buf = next_entry(fp, sizeof(u32)*2);
if (!buf)
goto bad;
c->permissions = le32_to_cpu(buf[0]);
nexpr = le32_to_cpu(buf[1]);
le = NULL;
depth = -1;
for (j = 0; j < nexpr; j++) {
e = kmalloc(sizeof(*e), GFP_KERNEL);
if (!e) {
rc = -ENOMEM;
goto bad;
}
memset(e, 0, sizeof(*e));
buf = next_entry(fp, sizeof(u32)*3);
if (!buf) {
kfree(e);
goto bad;
}
e->expr_type = le32_to_cpu(buf[0]);
e->attr = le32_to_cpu(buf[1]);
e->op = le32_to_cpu(buf[2]);
switch (e->expr_type) {
case CEXPR_NOT:
if (depth < 0) {
kfree(e);
goto bad;
}
break;
case CEXPR_AND:
case CEXPR_OR:
if (depth < 1) {
kfree(e);
goto bad;
}
depth--;
break;
case CEXPR_ATTR:
if (depth == (CEXPR_MAXDEPTH-1)) {
kfree(e);
goto bad;
}
depth++;
break;
case CEXPR_NAMES:
if (depth == (CEXPR_MAXDEPTH-1)) {
kfree(e);
goto bad;
}
depth++;
if (ebitmap_read(&e->names, fp)) {
kfree(e);
goto bad;
}
break;
default:
kfree(e);
goto bad;
break;
}
if (le) {
le->next = e;
} else {
c->expr = e;
}
le = e;
}
if (depth != 0)
goto bad;
if (lc) {
lc->next = c;
} else {
cladatum->constraints = c;
}
lc = c;
}
rc = mls_read_class(cladatum, fp);
if (rc)
goto bad;
rc = hashtab_insert(h, key, cladatum);
if (rc)
goto bad;
out:
return rc;
bad:
class_destroy(key, cladatum, NULL);
goto out;
}
static int role_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct role_datum *role;
int rc;
u32 *buf, len;
role = kmalloc(sizeof(*role), GFP_KERNEL);
if (!role) {
rc = -ENOMEM;
goto out;
}
memset(role, 0, sizeof(*role));
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
role->value = le32_to_cpu(buf[1]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
rc = ebitmap_read(&role->dominates, fp);
if (rc)
goto bad;
rc = ebitmap_read(&role->types, fp);
if (rc)
goto bad;
if (strcmp(key, OBJECT_R) == 0) {
if (role->value != OBJECT_R_VAL) {
printk(KERN_ERR "Role %s has wrong value %d\n",
OBJECT_R, role->value);
rc = -EINVAL;
goto bad;
}
rc = 0;
goto bad;
}
rc = hashtab_insert(h, key, role);
if (rc)
goto bad;
out:
return rc;
bad:
role_destroy(key, role, NULL);
goto out;
}
static int type_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct type_datum *typdatum;
int rc;
u32 *buf, len;
typdatum = kmalloc(sizeof(*typdatum),GFP_KERNEL);
if (!typdatum) {
rc = -ENOMEM;
return rc;
}
memset(typdatum, 0, sizeof(*typdatum));
buf = next_entry(fp, sizeof(u32)*3);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
typdatum->value = le32_to_cpu(buf[1]);
typdatum->primary = le32_to_cpu(buf[2]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
rc = hashtab_insert(h, key, typdatum);
if (rc)
goto bad;
out:
return rc;
bad:
type_destroy(key, typdatum, NULL);
goto out;
}
static int user_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = 0;
struct user_datum *usrdatum;
int rc;
u32 *buf, len;
usrdatum = kmalloc(sizeof(*usrdatum), GFP_KERNEL);
if (!usrdatum) {
rc = -ENOMEM;
goto out;
}
memset(usrdatum, 0, sizeof(*usrdatum));
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
len = le32_to_cpu(buf[0]);
usrdatum->value = le32_to_cpu(buf[1]);
buf = next_entry(fp, len);
if (!buf) {
rc = -EINVAL;
goto bad;
}
key = kmalloc(len + 1,GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto bad;
}
memcpy(key, buf, len);
key[len] = 0;
rc = ebitmap_read(&usrdatum->roles, fp);
if (rc)
goto bad;
rc = mls_read_user(usrdatum, fp);
if (rc)
goto bad;
rc = hashtab_insert(h, key, usrdatum);
if (rc)
goto bad;
out:
return rc;
bad:
user_destroy(key, usrdatum, NULL);
goto out;
}
static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
{
common_read,
class_read,
role_read,
type_read,
user_read
mls_read_f
};
#define mls_config(x) \
((x) & POLICYDB_CONFIG_MLS) ? "mls" : "no_mls"
/*
* Read the configuration data from a policy database binary
* representation file into a policy database structure.
*/
int policydb_read(struct policydb *p, void *fp)
{
struct role_allow *ra, *lra;
struct role_trans *tr, *ltr;
struct ocontext *l, *c, *newc;
struct genfs *genfs_p, *genfs, *newgenfs;
int i, j, rc;
u32 *buf, len, len2, config, nprim, nel, nel2;
char *policydb_str;
config = 0;
mls_set_config(config);
rc = policydb_init(p);
if (rc)
goto out;
rc = -EINVAL;
/* Read the magic number and string length. */
buf = next_entry(fp, sizeof(u32)* 2);
if (!buf)
goto bad;
for (i = 0; i < 2; i++)
buf[i] = le32_to_cpu(buf[i]);
if (buf[0] != POLICYDB_MAGIC) {
printk(KERN_ERR "security: policydb magic number 0x%x does "
"not match expected magic number 0x%x\n",
buf[0], POLICYDB_MAGIC);
goto bad;
}
len = buf[1];
if (len != strlen(POLICYDB_STRING)) {
printk(KERN_ERR "security: policydb string length %d does not "
"match expected length %d\n",
len, strlen(POLICYDB_STRING));
goto bad;
}
buf = next_entry(fp, len);
if (!buf) {
printk(KERN_ERR "security: truncated policydb string identifier\n");
goto bad;
}
policydb_str = kmalloc(len + 1,GFP_KERNEL);
if (!policydb_str) {
printk(KERN_ERR "security: unable to allocate memory for policydb "
"string of length %d\n", len);
rc = -ENOMEM;
goto bad;
}
memcpy(policydb_str, buf, len);
policydb_str[len] = 0;
if (strcmp(policydb_str, POLICYDB_STRING)) {
printk(KERN_ERR "security: policydb string %s does not match "
"my string %s\n", policydb_str, POLICYDB_STRING);
kfree(policydb_str);
goto bad;
}
/* Done with policydb_str. */
kfree(policydb_str);
policydb_str = NULL;
/* Read the version, config, and table sizes. */
buf = next_entry(fp, sizeof(u32)*4);
if (!buf)
goto bad;
for (i = 0; i < 4; i++)
buf[i] = le32_to_cpu(buf[i]);
if (buf[0] != POLICYDB_VERSION) {
printk(KERN_ERR "security: policydb version %d does not match "
"my version %d\n", buf[0], POLICYDB_VERSION);
goto bad;
}
if (buf[1] != config) {
printk(KERN_ERR "security: policydb configuration (%s) does "
"not match my configuration (%s)\n",
mls_config(buf[1]),
mls_config(config));
goto bad;
}
if (buf[2] != SYM_NUM || buf[3] != OCON_NUM) {
printk(KERN_ERR "security: policydb table sizes (%d,%d) do "
"not match mine (%d,%d)\n",
buf[2], buf[3], SYM_NUM, OCON_NUM);
goto bad;
}
rc = mls_read_nlevels(p, fp);
if (rc)
goto bad;
for (i = 0; i < SYM_NUM; i++) {
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
nprim = le32_to_cpu(buf[0]);
nel = le32_to_cpu(buf[1]);
for (j = 0; j < nel; j++) {
rc = read_f[i](p, p->symtab[i].table, fp);
if (rc)
goto bad;
}
p->symtab[i].nprim = nprim;
}
rc = avtab_read(&p->te_avtab, fp, config);
if (rc)
goto bad;
buf = next_entry(fp, sizeof(u32));
if (!buf) {
rc = -EINVAL;
goto bad;
}
nel = le32_to_cpu(buf[0]);
ltr = NULL;
for (i = 0; i < nel; i++) {
tr = kmalloc(sizeof(*tr), GFP_KERNEL);
if (!tr) {
rc = -ENOMEM;
goto bad;
}
memset(tr, 0, sizeof(*tr));
if (ltr) {
ltr->next = tr;
} else {
p->role_tr = tr;
}
buf = next_entry(fp, sizeof(u32)*3);
if (!buf) {
rc = -EINVAL;
goto bad;
}
tr->role = le32_to_cpu(buf[0]);
tr->type = le32_to_cpu(buf[1]);
tr->new_role = le32_to_cpu(buf[2]);
ltr = tr;
}
buf = next_entry(fp, sizeof(u32));
if (!buf) {
rc = -EINVAL;
goto bad;
}
nel = le32_to_cpu(buf[0]);
lra = NULL;
for (i = 0; i < nel; i++) {
ra = kmalloc(sizeof(*ra), GFP_KERNEL);
if (!ra) {
rc = -ENOMEM;
goto bad;
}
memset(ra, 0, sizeof(*ra));
if (lra) {
lra->next = ra;
} else {
p->role_allow = ra;
}
buf = next_entry(fp, sizeof(u32)*2);
if (!buf) {
rc = -EINVAL;
goto bad;
}
ra->role = le32_to_cpu(buf[0]);
ra->new_role = le32_to_cpu(buf[1]);
lra = ra;
}
rc = policydb_index_classes(p);
if (rc)
goto bad;
rc = policydb_index_others(p);
if (rc)
goto bad;
for (i = 0; i < OCON_NUM; i++) {
buf = next_entry(fp, sizeof(u32));
if (!buf) {
rc = -EINVAL;
goto bad;
}
nel = le32_to_cpu(buf[0]);
l = NULL;
for (j = 0; j < nel; j++) {
c = kmalloc(sizeof(*c), GFP_KERNEL);
if (!c) {
rc = -ENOMEM;
goto bad;
}
memset(c, 0, sizeof(*c));
if (l) {
l->next = c;
} else {
p->ocontexts[i] = c;
}
l = c;
rc = -EINVAL;
switch (i) {
case OCON_ISID:
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
c->sid[0] = le32_to_cpu(buf[0]);
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
break;
case OCON_FS:
case OCON_NETIF:
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
len = le32_to_cpu(buf[0]);
buf = next_entry(fp, len);
if (!buf)
goto bad;
c->u.name = kmalloc(len + 1,GFP_KERNEL);
if (!c->u.name) {
rc = -ENOMEM;
goto bad;
}
memcpy(c->u.name, buf, len);
c->u.name[len] = 0;
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
rc = context_read_and_validate(&c->context[1], p, fp);
if (rc)
goto bad;
break;
case OCON_PORT:
buf = next_entry(fp, sizeof(u32)*3);
if (!buf)
goto bad;
c->u.port.protocol = le32_to_cpu(buf[0]);
c->u.port.low_port = le32_to_cpu(buf[1]);
c->u.port.high_port = le32_to_cpu(buf[2]);
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
break;
case OCON_NODE:
buf = next_entry(fp, sizeof(u32)* 2);
if (!buf)
goto bad;
c->u.node.addr = le32_to_cpu(buf[0]);
c->u.node.mask = le32_to_cpu(buf[1]);
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
break;
case OCON_FSUSE:
buf = next_entry(fp, sizeof(u32)*2);
if (!buf)
goto bad;
c->v.behavior = le32_to_cpu(buf[0]);
len = le32_to_cpu(buf[1]);
buf = next_entry(fp, len);
if (!buf)
goto bad;
c->u.name = kmalloc(len + 1,GFP_KERNEL);
if (!c->u.name) {
rc = -ENOMEM;
goto bad;
}
memcpy(c->u.name, buf, len);
c->u.name[len] = 0;
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
break;
}
}
}
buf = next_entry(fp, sizeof(u32));
if (!buf) {
rc = -EINVAL;
goto bad;
}
nel = le32_to_cpu(buf[0]);
genfs_p = NULL;
rc = -EINVAL;
for (i = 0; i < nel; i++) {
newgenfs = kmalloc(sizeof(*newgenfs), GFP_KERNEL);
if (!newgenfs) {
rc = -ENOMEM;
goto bad;
}
memset(newgenfs, 0, sizeof(*newgenfs));
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
len = le32_to_cpu(buf[0]);
buf = next_entry(fp, len);
if (!buf)
goto bad;
newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL);
if (!newgenfs->fstype) {
rc = -ENOMEM;
goto bad;
}
memcpy(newgenfs->fstype, buf, len);
newgenfs->fstype[len] = 0;
for (genfs_p = NULL, genfs = p->genfs; genfs;
genfs_p = genfs, genfs = genfs->next) {
if (strcmp(newgenfs->fstype, genfs->fstype) == 0) {
printk(KERN_ERR "security: dup genfs "
"fstype %s\n", newgenfs->fstype);
goto bad;
}
if (strcmp(newgenfs->fstype, genfs->fstype) < 0)
break;
}
newgenfs->next = genfs;
if (genfs_p)
genfs_p->next = newgenfs;
else
p->genfs = newgenfs;
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
nel2 = le32_to_cpu(buf[0]);
for (j = 0; j < nel2; j++) {
newc = kmalloc(sizeof(*newc), GFP_KERNEL);
if (!newc) {
rc = -ENOMEM;
goto bad;
}
memset(newc, 0, sizeof(*newc));
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
len = le32_to_cpu(buf[0]);
buf = next_entry(fp, len);
if (!buf)
goto bad;
newc->u.name = kmalloc(len + 1,GFP_KERNEL);
if (!newc->u.name) {
rc = -ENOMEM;
goto bad;
}
memcpy(newc->u.name, buf, len);
newc->u.name[len] = 0;
buf = next_entry(fp, sizeof(u32));
if (!buf)
goto bad;
newc->v.sclass = le32_to_cpu(buf[0]);
if (context_read_and_validate(&newc->context[0], p, fp))
goto bad;
for (l = NULL, c = newgenfs->head; c;
l = c, c = c->next) {
if (!strcmp(newc->u.name, c->u.name) &&
(!c->v.sclass || !newc->v.sclass ||
newc->v.sclass == c->v.sclass)) {
printk(KERN_ERR "security: dup genfs "
"entry (%s,%s)\n",
newgenfs->fstype, c->u.name);
goto bad;
}
len = strlen(newc->u.name);
len2 = strlen(c->u.name);
if (len > len2)
break;
}
newc->next = c;
if (l)
l->next = newc;
else
newgenfs->head = newc;
}
}
rc = mls_read_trusted(p, fp);
if (rc)
goto bad;
out:
return rc;
bad:
policydb_destroy(p);
goto out;
}
/*
* A policy database (policydb) specifies the
* configuration data for the security policy.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#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 */
#ifdef CONFIG_SECURITY_SELINUX_MLS
#define MLS_BASE_READ 1 /* MLS base permission `read' */
#define MLS_BASE_WRITE 2 /* MLS base permission `write' */
#define MLS_BASE_READBY 4 /* MLS base permission `readby' */
#define MLS_BASE_WRITEBY 8 /* MLS base permission `writeby' */
u32 base_perms; /* MLS base permission mask */
#endif
};
/* 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 */
#ifdef CONFIG_SECURITY_SELINUX_MLS
struct mls_perms mlsperms; /* MLS base permission masks */
#endif
};
/* 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 */
#ifdef CONFIG_SECURITY_SELINUX_MLS
struct mls_range_list *ranges; /* list of authorized MLS ranges for user */
#endif
};
#ifdef CONFIG_SECURITY_SELINUX_MLS
/* 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? */
};
#endif
/*
* 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 */
} 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
#ifdef CONFIG_SECURITY_SELINUX_MLS
#define SYM_LEVELS 5
#define SYM_CATS 6
#define SYM_NUM 7
#else
#define SYM_NUM 5
#endif
/* 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_NUM 6
/* 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_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_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;
/* 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;
#ifdef CONFIG_SECURITY_SELINUX_MLS
/* number of legitimate MLS levels */
u32 nlevels;
struct ebitmap trustedreaders;
struct ebitmap trustedwriters;
struct ebitmap trustedobjects;
#endif
};
extern int policydb_init(struct policydb *p);
extern int policydb_index_classes(struct policydb *p);
extern int policydb_index_others(struct policydb *p);
extern int constraint_expr_destroy(struct constraint_expr *expr);
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_VERSION 15
#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 void *next_entry(struct policy_file *fp, size_t bytes)
{
void *buf;
if (bytes > fp->len)
return NULL;
buf = fp->data;
fp->data += bytes;
fp->len -= bytes;
return buf;
}
#endif /* _SS_POLICYDB_H_ */
/*
* Implementation of the security services.
*
* Authors : Stephen Smalley, <sds@epoch.ncsc.mil>
* 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 "context.h"
#include "policydb.h"
#include "sidtab.h"
#include "services.h"
#include "mls.h"
static rwlock_t policy_rwlock = RW_LOCK_UNLOCKED;
#define POLICY_RDLOCK read_lock(&policy_rwlock)
#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
static DECLARE_MUTEX(load_sem);
#define LOAD_LOCK down(&load_sem)
#define LOAD_UNLOCK up(&load_sem)
struct sidtab sidtab;
struct policydb policydb;
int ss_initialized = 0;
/*
* The largest sequence number that has been used when
* providing an access decision to the access vector cache.
* The sequence number only changes when a policy change
* occurs.
*/
static u32 latest_granting = 0;
/*
* Return the boolean value of a constraint expression
* when it is applied to the specified source and target
* security contexts.
*/
static int constraint_expr_eval(struct context *scontext,
struct context *tcontext,
struct constraint_expr *cexpr)
{
u32 val1, val2;
struct context *c;
struct role_datum *r1, *r2;
struct constraint_expr *e;
int s[CEXPR_MAXDEPTH];
int sp = -1;
for (e = cexpr; e; e = e->next) {
switch (e->expr_type) {
case CEXPR_NOT:
BUG_ON(sp < 0);
s[sp] = !s[sp];
break;
case CEXPR_AND:
BUG_ON(sp < 1);
sp--;
s[sp] &= s[sp+1];
break;
case CEXPR_OR:
BUG_ON(sp < 1);
sp--;
s[sp] |= s[sp+1];
break;
case CEXPR_ATTR:
if (sp == (CEXPR_MAXDEPTH-1))
return 0;
switch (e->attr) {
case CEXPR_USER:
val1 = scontext->user;
val2 = tcontext->user;
break;
case CEXPR_TYPE:
val1 = scontext->type;
val2 = tcontext->type;
break;
case CEXPR_ROLE:
val1 = scontext->role;
val2 = tcontext->role;
r1 = policydb.role_val_to_struct[val1 - 1];
r2 = policydb.role_val_to_struct[val2 - 1];
switch (e->op) {
case CEXPR_DOM:
s[++sp] = ebitmap_get_bit(&r1->dominates,
val2 - 1);
continue;
case CEXPR_DOMBY:
s[++sp] = ebitmap_get_bit(&r2->dominates,
val1 - 1);
continue;
case CEXPR_INCOMP:
s[++sp] = ( !ebitmap_get_bit(&r1->dominates,
val2 - 1) &&
!ebitmap_get_bit(&r2->dominates,
val1 - 1) );
continue;
default:
break;
}
break;
default:
BUG();
return 0;
}
switch (e->op) {
case CEXPR_EQ:
s[++sp] = (val1 == val2);
break;
case CEXPR_NEQ:
s[++sp] = (val1 != val2);
break;
default:
BUG();
return 0;
}
break;
case CEXPR_NAMES:
if (sp == (CEXPR_MAXDEPTH-1))
return 0;
c = scontext;
if (e->attr & CEXPR_TARGET)
c = tcontext;
if (e->attr & CEXPR_USER)
val1 = c->user;
else if (e->attr & CEXPR_ROLE)
val1 = c->role;
else if (e->attr & CEXPR_TYPE)
val1 = c->type;
else {
BUG();
return 0;
}
switch (e->op) {
case CEXPR_EQ:
s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
break;
case CEXPR_NEQ:
s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
break;
default:
BUG();
return 0;
}
break;
default:
BUG();
return 0;
}
}
BUG_ON(sp != 0);
return s[0];
}
/*
* Compute access vectors based on a context structure pair for
* the permissions in a particular class.
*/
static int context_struct_compute_av(struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 requested,
struct av_decision *avd)
{
struct constraint_node *constraint;
struct role_allow *ra;
struct avtab_key avkey;
struct avtab_datum *avdatum;
struct class_datum *tclass_datum;
if (!tclass || tclass > policydb.p_classes.nprim) {
printk(KERN_ERR "security_compute_av: unrecognized class %d\n",
tclass);
return -EINVAL;
}
tclass_datum = policydb.class_val_to_struct[tclass - 1];
/*
* Initialize the access vectors to the default values.
*/
avd->allowed = 0;
avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
/*
* If a specific type enforcement rule was defined for
* this permission check, then use it.
*/
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV);
if (avdatum) {
if (avdatum->specified & AVTAB_ALLOWED)
avd->allowed = avtab_allowed(avdatum);
if (avdatum->specified & AVTAB_AUDITDENY)
avd->auditdeny = avtab_auditdeny(avdatum);
if (avdatum->specified & AVTAB_AUDITALLOW)
avd->auditallow = avtab_auditallow(avdatum);
}
/*
* Remove any permissions prohibited by the MLS policy.
*/
mls_compute_av(scontext, tcontext, tclass_datum, &avd->allowed);
/*
* Remove any permissions prohibited by a constraint.
*/
constraint = tclass_datum->constraints;
while (constraint) {
if ((constraint->permissions & (avd->allowed)) &&
!constraint_expr_eval(scontext, tcontext,
constraint->expr)) {
avd->allowed = (avd->allowed) & ~(constraint->permissions);
}
constraint = constraint->next;
}
/*
* If checking process transition permission and the
* role is changing, then check the (current_role, new_role)
* pair.
*/
if (tclass == SECCLASS_PROCESS &&
avd->allowed && PROCESS__TRANSITION &&
scontext->role != tcontext->role) {
for (ra = policydb.role_allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
tcontext->role == ra->new_role)
break;
}
if (!ra)
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION);
}
return 0;
}
/**
* security_compute_av - Compute access vector decisions.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions
* @avd: access vector decisions
*
* Compute a set of access vector decisions based on the
* SID pair (@ssid, @tsid) for the permissions in @tclass.
* Return -%EINVAL if any of the parameters are invalid or %0
* if the access vector decisions were computed successfully.
*/
int security_compute_av(u32 ssid,
u32 tsid,
u16 tclass,
u32 requested,
struct av_decision *avd)
{
struct context *scontext = 0, *tcontext = 0;
int rc = 0;
if (!ss_initialized) {
avd->allowed = requested;
avd->decided = requested;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
return 0;
}
POLICY_RDLOCK;
scontext = sidtab_search(&sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out;
}
tcontext = sidtab_search(&sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
goto out;
}
rc = context_struct_compute_av(scontext, tcontext, tclass,
requested, avd);
out:
POLICY_RDUNLOCK;
return rc;
}
/*
* Write the security context string representation of
* the context structure `context' into a dynamically
* allocated string of the correct size. Set `*scontext'
* to point to this string and set `*scontext_len' to
* the length of the string.
*/
int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
{
char *scontextp;
*scontext = 0;
*scontext_len = 0;
/* Compute the size of the context. */
*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
*scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
*scontext_len += mls_compute_context_len(context);
/* Allocate space for the context; caller must free this space. */
scontextp = kmalloc(*scontext_len+1,GFP_ATOMIC);
if (!scontextp) {
return -ENOMEM;
}
*scontext = scontextp;
/*
* Copy the user name, role name and type name into the context.
*/
sprintf(scontextp, "%s:%s:%s:",
policydb.p_user_val_to_name[context->user - 1],
policydb.p_role_val_to_name[context->role - 1],
policydb.p_type_val_to_name[context->type - 1]);
scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
1 + strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
mls_sid_to_context(context, &scontextp);
scontextp--;
*scontextp = 0;
return 0;
}
#include "initial_sid_to_string.h"
/**
* security_sid_to_context - Obtain a context for a given SID.
* @sid: security identifier, SID
* @scontext: security context
* @scontext_len: length in bytes
*
* Write the string representation of the context associated with @sid
* into a dynamically allocated string of the correct size. Set @scontext
* to point to this string and set @scontext_len to the length of the string.
*/
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
{
struct context *context;
int rc = 0;
if (!ss_initialized) {
if (sid <= SECINITSID_NUM) {
char *scontextp;
*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
scontextp = kmalloc(*scontext_len,GFP_KERNEL);
strcpy(scontextp, initial_sid_to_string[sid]);
*scontext = scontextp;
goto out;
}
printk(KERN_ERR "security_sid_to_context: called before initial "
"load_policy on unknown SID %d\n", sid);
rc = -EINVAL;
goto out;
}
POLICY_RDLOCK;
context = sidtab_search(&sidtab, sid);
if (!context) {
printk(KERN_ERR "security_sid_to_context: unrecognized SID "
"%d\n", sid);
rc = -EINVAL;
goto out_unlock;
}
rc = context_struct_to_string(context, scontext, scontext_len);
out_unlock:
POLICY_RDUNLOCK;
out:
return rc;
}
/**
* security_context_to_sid - Obtain a SID for a given security context.
* @scontext: security context
* @scontext_len: length in bytes
* @sid: security identifier, SID
*
* Obtains a SID associated with the security context that
* has the string representation specified by @scontext.
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
* memory is available, or 0 on success.
*/
int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
{
char *scontext2;
struct context context;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *scontextp, *p, oldc;
int rc = 0;
if (!ss_initialized) {
int i;
for (i = 1; i < SECINITSID_NUM; i++) {
if (!strcmp(initial_sid_to_string[i], scontext)) {
*sid = i;
goto out;
}
}
printk(KERN_ERR "security_context_to_sid: called before "
"initial load_policy on unknown context %s\n", scontext);
rc = -EINVAL;
goto out;
}
*sid = SECSID_NULL;
/* Copy the string so that we can modify the copy as we parse it.
The string should already by null terminated, but we append a
null suffix to the copy to avoid problems with the existing
attr package, which doesn't view the null terminator as part
of the attribute value. */
scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
if (!scontext2) {
rc = -ENOMEM;
goto out;
}
memcpy(scontext2, scontext, scontext_len);
scontext2[scontext_len] = 0;
context_init(&context);
*sid = SECSID_NULL;
POLICY_RDLOCK;
/* Parse the security context. */
rc = -EINVAL;
scontextp = (char *) scontext2;
/* Extract the user. */
p = scontextp;
while (*p && *p != ':')
p++;
if (*p == 0)
goto out_unlock;
*p++ = 0;
usrdatum = hashtab_search(policydb.p_users.table, scontextp);
if (!usrdatum)
goto out_unlock;
context.user = usrdatum->value;
/* Extract role. */
scontextp = p;
while (*p && *p != ':')
p++;
if (*p == 0)
goto out_unlock;
*p++ = 0;
role = hashtab_search(policydb.p_roles.table, scontextp);
if (!role)
goto out_unlock;
context.role = role->value;
/* Extract type. */
scontextp = p;
while (*p && *p != ':')
p++;
oldc = *p;
*p++ = 0;
typdatum = hashtab_search(policydb.p_types.table, scontextp);
if (!typdatum)
goto out_unlock;
context.type = typdatum->value;
rc = mls_context_to_sid(oldc, &p, &context);
if (rc)
goto out_unlock;
/* Check the validity of the new context. */
if (!policydb_context_isvalid(&policydb, &context)) {
rc = -EINVAL;
goto out_unlock;
}
/* Obtain the new sid. */
rc = sidtab_context_to_sid(&sidtab, &context, sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&context);
kfree(scontext2);
out:
return rc;
}
static inline int compute_sid_handle_invalid_context(
struct context *scontext,
struct context *tcontext,
u16 tclass,
struct context *newcontext)
{
int rc = 0;
if (selinux_enforcing) {
rc = -EACCES;
} else {
char *s, *t, *n;
u32 slen, tlen, nlen;
context_struct_to_string(scontext, &s, &slen);
context_struct_to_string(tcontext, &t, &tlen);
context_struct_to_string(newcontext, &n, &nlen);
printk(KERN_ERR "security_compute_sid: invalid context %s", n);
printk(" for scontext=%s", s);
printk(" tcontext=%s", t);
printk(" tclass=%s\n", policydb.p_class_val_to_name[tclass-1]);
kfree(s);
kfree(t);
kfree(n);
}
return rc;
}
static int security_compute_sid(u32 ssid,
u32 tsid,
u16 tclass,
u32 specified,
u32 *out_sid)
{
struct context *scontext = 0, *tcontext = 0, newcontext;
struct role_trans *roletr = 0;
struct avtab_key avkey;
struct avtab_datum *avdatum;
unsigned int type_change = 0;
int rc = 0;
if (!ss_initialized) {
switch (tclass) {
case SECCLASS_PROCESS:
*out_sid = ssid;
break;
default:
*out_sid = tsid;
break;
}
goto out;
}
POLICY_RDLOCK;
scontext = sidtab_search(&sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out_unlock;
}
tcontext = sidtab_search(&sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
goto out_unlock;
}
context_init(&newcontext);
/* Set the user identity. */
switch (specified) {
case AVTAB_TRANSITION:
case AVTAB_CHANGE:
/* Use the process user identity. */
newcontext.user = scontext->user;
break;
case AVTAB_MEMBER:
/* Use the related object owner. */
newcontext.user = tcontext->user;
break;
}
/* Set the role and type to default values. */
switch (tclass) {
case SECCLASS_PROCESS:
/* Use the current role and type of process. */
newcontext.role = scontext->role;
newcontext.type = scontext->type;
break;
default:
/* Use the well-defined object role. */
newcontext.role = OBJECT_R_VAL;
/* Use the type of the related object. */
newcontext.type = tcontext->type;
}
/* Look for a type transition/member/change rule. */
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE);
type_change = (avdatum && (avdatum->specified & specified));
if (type_change) {
/* Use the type from the type transition/member/change rule. */
switch (specified) {
case AVTAB_TRANSITION:
newcontext.type = avtab_transition(avdatum);
break;
case AVTAB_MEMBER:
newcontext.type = avtab_member(avdatum);
break;
case AVTAB_CHANGE:
newcontext.type = avtab_change(avdatum);
break;
}
}
/* Check for class-specific changes. */
switch (tclass) {
case SECCLASS_PROCESS:
if (specified & AVTAB_TRANSITION) {
/* Look for a role transition rule. */
for (roletr = policydb.role_tr; roletr;
roletr = roletr->next) {
if (roletr->role == scontext->role &&
roletr->type == tcontext->type) {
/* Use the role transition rule. */
newcontext.role = roletr->new_role;
break;
}
}
}
if (!type_change && !roletr) {
/* No change in process role or type. */
*out_sid = ssid;
goto out_unlock;
}
break;
default:
if (!type_change &&
(newcontext.user == tcontext->user) &&
mls_context_cmp(scontext, tcontext)) {
/* No change in object type, owner,
or MLS attributes. */
*out_sid = tsid;
goto out_unlock;
}
break;
}
/* Set the MLS attributes.
This is done last because it may allocate memory. */
rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext);
if (rc)
goto out_unlock;
/* Check the validity of the context. */
if (!policydb_context_isvalid(&policydb, &newcontext)) {
rc = compute_sid_handle_invalid_context(scontext,
tcontext,
tclass,
&newcontext);
if (rc)
goto out_unlock;
}
/* Obtain the sid for the context. */
rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&newcontext);
out:
return rc;
}
/**
* security_transition_sid - Compute the SID for a new subject/object.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @out_sid: security identifier for new subject/object
*
* Compute a SID to use for labeling a new subject or object in the
* class @tclass based on a SID pair (@ssid, @tsid).
* Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
* if insufficient memory is available, or %0 if the new SID was
* computed successfully.
*/
int security_transition_sid(u32 ssid,
u32 tsid,
u16 tclass,
u32 *out_sid)
{
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
}
/**
* security_member_sid - Compute the SID for member selection.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @out_sid: security identifier for selected member
*
* Compute a SID to use when selecting a member of a polyinstantiated
* object of class @tclass based on a SID pair (@ssid, @tsid).
* Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
* if insufficient memory is available, or %0 if the SID was
* computed successfully.
*/
int security_member_sid(u32 ssid,
u32 tsid,
u16 tclass,
u32 *out_sid)
{
return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid);
}
/**
* security_change_sid - Compute the SID for object relabeling.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @out_sid: security identifier for selected member
*
* Compute a SID to use for relabeling an object of class @tclass
* based on a SID pair (@ssid, @tsid).
* Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
* if insufficient memory is available, or %0 if the SID was
* computed successfully.
*/
int security_change_sid(u32 ssid,
u32 tsid,
u16 tclass,
u32 *out_sid)
{
return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid);
}
/*
* Verify that each permission that is defined under the
* existing policy is still defined with the same value
* in the new policy.
*/
static int validate_perm(void *key, void *datum, void *p)
{
struct hashtab *h;
struct perm_datum *perdatum, *perdatum2;
int rc = 0;
h = p;
perdatum = datum;
perdatum2 = hashtab_search(h, key);
if (!perdatum2) {
printk(KERN_ERR "security: permission %s disappeared",
(char *)key);
rc = -ENOENT;
goto out;
}
if (perdatum->value != perdatum2->value) {
printk(KERN_ERR "security: the value of permission %s changed",
(char *)key);
rc = -EINVAL;
}
out:
return rc;
}
/*
* Verify that each class that is defined under the
* existing policy is still defined with the same
* attributes in the new policy.
*/
static int validate_class(void *key, void *datum, void *p)
{
struct policydb *newp;
struct class_datum *cladatum, *cladatum2;
int rc;
newp = p;
cladatum = datum;
cladatum2 = hashtab_search(newp->p_classes.table, key);
if (!cladatum2) {
printk(KERN_ERR "security: class %s disappeared\n",
(char *)key);
rc = -ENOENT;
goto out;
}
if (cladatum->value != cladatum2->value) {
printk(KERN_ERR "security: the value of class %s changed\n",
(char *)key);
rc = -EINVAL;
goto out;
}
if ((cladatum->comdatum && !cladatum2->comdatum) ||
(!cladatum->comdatum && cladatum2->comdatum)) {
printk(KERN_ERR "security: the inherits clause for the access "
"vector definition for class %s changed\n", (char *)key);
rc = -EINVAL;
goto out;
}
if (cladatum->comdatum) {
rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm,
cladatum2->comdatum->permissions.table);
if (rc) {
printk(" in the access vector definition for class "
"%s\n", (char *)key);
goto out;
}
}
rc = hashtab_map(cladatum->permissions.table, validate_perm,
cladatum2->permissions.table);
if (rc)
printk(" in access vector definition for class %s\n",
(char *)key);
out:
return rc;
}
/* Clone the SID into the new SID table. */
static int clone_sid(u32 sid,
struct context *context,
void *arg)
{
struct sidtab *s = arg;
return sidtab_insert(s, sid, context);
}
static inline int convert_context_handle_invalid_context(struct context *context)
{
int rc = 0;
if (selinux_enforcing) {
rc = -EINVAL;
} else {
char *s;
u32 len;
context_struct_to_string(context, &s, &len);
printk(KERN_ERR "security: context %s is invalid\n", s);
kfree(s);
}
return rc;
}
struct convert_context_args {
struct policydb *oldp;
struct policydb *newp;
};
/*
* Convert the values in the security context
* structure `c' from the values specified
* in the policy `p->oldp' to the values specified
* in the policy `p->newp'. Verify that the
* context is valid under the new policy.
*/
static int convert_context(u32 key,
struct context *c,
void *p)
{
struct convert_context_args *args;
struct context oldc;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *s;
u32 len;
int rc = -EINVAL;
args = p;
rc = context_cpy(&oldc, c);
if (rc)
goto out;
/* Convert the user. */
usrdatum = hashtab_search(args->newp->p_users.table,
args->oldp->p_user_val_to_name[c->user - 1]);
if (!usrdatum) {
goto bad;
}
c->user = usrdatum->value;
/* Convert the role. */
role = hashtab_search(args->newp->p_roles.table,
args->oldp->p_role_val_to_name[c->role - 1]);
if (!role) {
goto bad;
}
c->role = role->value;
/* Convert the type. */
typdatum = hashtab_search(args->newp->p_types.table,
args->oldp->p_type_val_to_name[c->type - 1]);
if (!typdatum) {
goto bad;
}
c->type = typdatum->value;
rc = mls_convert_context(args->oldp, args->newp, c);
if (rc)
goto bad;
/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
rc = convert_context_handle_invalid_context(&oldc);
if (rc)
goto bad;
}
context_destroy(&oldc);
out:
return rc;
bad:
context_struct_to_string(&oldc, &s, &len);
context_destroy(&oldc);
printk(KERN_ERR "security: invalidating context %s\n", s);
kfree(s);
goto out;
}
extern void selinux_complete_init(void);
/**
* security_load_policy - Load a security policy configuration.
* @data: binary policy data
* @len: length of data in bytes
*
* Load a new set of security policy configuration data,
* validate it and convert the SID table as necessary.
* This function will flush the access vector cache after
* loading the new policy.
*/
int security_load_policy(void *data, size_t len)
{
struct policydb oldpolicydb, newpolicydb;
struct sidtab oldsidtab, newsidtab;
struct convert_context_args args;
u32 seqno;
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;
LOAD_LOCK;
if (!ss_initialized) {
if (policydb_read(&policydb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}
if (policydb_load_isids(&policydb, &sidtab)) {
LOAD_UNLOCK;
policydb_destroy(&policydb);
return -EINVAL;
}
ss_initialized = 1;
LOAD_UNLOCK;
selinux_complete_init();
return 0;
}
#if 0
sidtab_hash_eval(&sidtab, "sids");
#endif
if (policydb_read(&newpolicydb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}
sidtab_init(&newsidtab);
/* Verify that the existing classes did not change. */
if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
printk(KERN_ERR "security: the definition of an existing "
"class changed\n");
rc = -EINVAL;
goto err;
}
/* Clone the SID table. */
sidtab_shutdown(&sidtab);
if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
rc = -ENOMEM;
goto err;
}
/* Convert the internal representations of contexts
in the new SID table and remove invalid SIDs. */
args.oldp = &policydb;
args.newp = &newpolicydb;
sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
/* Save the old policydb and SID table to free later. */
memcpy(&oldpolicydb, &policydb, sizeof policydb);
sidtab_set(&oldsidtab, &sidtab);
/* Install the new policydb and SID table. */
POLICY_WRLOCK;
memcpy(&policydb, &newpolicydb, sizeof policydb);
sidtab_set(&sidtab, &newsidtab);
seqno = ++latest_granting;
POLICY_WRUNLOCK;
LOAD_UNLOCK;
/* Free the old policydb and SID table. */
policydb_destroy(&oldpolicydb);
sidtab_destroy(&oldsidtab);
avc_ss_reset(seqno);
return 0;
err:
LOAD_UNLOCK;
sidtab_destroy(&newsidtab);
policydb_destroy(&newpolicydb);
return rc;
}
/**
* security_port_sid - Obtain the SID for a port.
* @domain: communication domain aka address family
* @type: socket type
* @protocol: protocol number
* @port: port number
* @out_sid: security identifier
*/
int security_port_sid(u16 domain,
u16 type,
u8 protocol,
u16 port,
u32 *out_sid)
{
struct ocontext *c;
int rc = 0;
POLICY_RDLOCK;
c = policydb.ocontexts[OCON_PORT];
while (c) {
if (c->u.port.protocol == protocol &&
c->u.port.low_port <= port &&
c->u.port.high_port >= port)
break;
c = c->next;
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*out_sid = c->sid[0];
} else {
*out_sid = SECINITSID_PORT;
}
out:
POLICY_RDUNLOCK;
return rc;
}
/**
* security_netif_sid - Obtain the SID for a network interface.
* @name: interface name
* @if_sid: interface SID
* @msg_sid: default SID for received packets
*/
int security_netif_sid(char *name,
u32 *if_sid,
u32 *msg_sid)
{
int rc = 0;
struct ocontext *c;
POLICY_RDLOCK;
c = policydb.ocontexts[OCON_NETIF];
while (c) {
if (strcmp(name, c->u.name) == 0)
break;
c = c->next;
}
if (c) {
if (!c->sid[0] || !c->sid[1]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
rc = sidtab_context_to_sid(&sidtab,
&c->context[1],
&c->sid[1]);
if (rc)
goto out;
}
*if_sid = c->sid[0];
*msg_sid = c->sid[1];
} else {
*if_sid = SECINITSID_NETIF;
*msg_sid = SECINITSID_NETMSG;
}
out:
POLICY_RDUNLOCK;
return rc;
}
/**
* security_node_sid - Obtain the SID for a node (host).
* @domain: communication domain aka address family
* @addrp: address
* @addrlen: address length in bytes
* @out_sid: security identifier
*/
int security_node_sid(u16 domain,
void *addrp,
u32 addrlen,
u32 *out_sid)
{
int rc = 0;
u32 addr;
struct ocontext *c;
POLICY_RDLOCK;
if (domain != AF_INET || addrlen != sizeof(u32)) {
*out_sid = SECINITSID_NODE;
goto out;
}
addr = *((u32 *)addrp);
c = policydb.ocontexts[OCON_NODE];
while (c) {
if (c->u.node.addr == (addr & c->u.node.mask))
break;
c = c->next;
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*out_sid = c->sid[0];
} else {
*out_sid = SECINITSID_NODE;
}
out:
POLICY_RDUNLOCK;
return rc;
}
#define SIDS_NEL 25
/**
* security_get_user_sids - Obtain reachable SIDs for a user.
* @fromsid: starting SID
* @username: username
* @sids: array of reachable SIDs for user
* @nel: number of elements in @sids
*
* Generate the set of SIDs for legal security contexts
* for a given user that can be reached by @fromsid.
* Set *@sids to point to a dynamically allocated
* array containing the set of SIDs. Set *@nel to the
* number of elements in the array.
*/
int security_get_user_sids(u32 fromsid,
char *username,
u32 **sids,
u32 *nel)
{
struct context *fromcon, usercon;
u32 *mysids, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
struct av_decision avd;
int rc = 0, i, j;
if (!ss_initialized) {
*sids = NULL;
*nel = 0;
goto out;
}
POLICY_RDLOCK;
fromcon = sidtab_search(&sidtab, fromsid);
if (!fromcon) {
rc = -EINVAL;
goto out_unlock;
}
user = hashtab_search(policydb.p_users.table, username);
if (!user) {
rc = -EINVAL;
goto out_unlock;
}
usercon.user = user->value;
mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC);
if (!mysids) {
rc = -ENOMEM;
goto out_unlock;
}
memset(mysids, 0, maxnel*sizeof(*mysids));
for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) {
if (!ebitmap_get_bit(&user->roles, i))
continue;
role = policydb.role_val_to_struct[i];
usercon.role = i+1;
for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) {
if (!ebitmap_get_bit(&role->types, j))
continue;
usercon.type = j+1;
if (usercon.type == fromcon->type)
continue;
mls_for_user_ranges(user,usercon) {
rc = context_struct_compute_av(fromcon, &usercon,
SECCLASS_PROCESS,
PROCESS__TRANSITION,
&avd);
if (rc || !(avd.allowed & PROCESS__TRANSITION))
continue;
rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
if (rc) {
kfree(mysids);
goto out_unlock;
}
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
maxnel += SIDS_NEL;
mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
goto out_unlock;
}
memset(mysids2, 0, maxnel*sizeof(*mysids2));
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
kfree(mysids);
mysids = mysids2;
mysids[mynel++] = sid;
}
}
mls_end_user_ranges;
}
}
*sids = mysids;
*nel = mynel;
out_unlock:
POLICY_RDUNLOCK;
out:
return rc;
}
/**
* security_genfs_sid - Obtain a SID for a file in a filesystem
* @fstype: filesystem type
* @path: path from root of mount
* @sclass: file security class
* @sid: SID for path
*
* Obtain a SID to use for a file in a filesystem that
* cannot support xattr or use a fixed labeling behavior like
* transition SIDs or task SIDs.
*/
int security_genfs_sid(const char *fstype,
char *path,
u16 sclass,
u32 *sid)
{
int len;
struct genfs *genfs;
struct ocontext *c;
int rc = 0, cmp = 0;
POLICY_RDLOCK;
for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
if (cmp <= 0)
break;
}
if (!genfs || cmp) {
*sid = SECINITSID_UNLABELED;
rc = -ENOENT;
goto out;
}
for (c = genfs->head; c; c = c->next) {
len = strlen(c->u.name);
if ((!c->v.sclass || sclass == c->v.sclass) &&
(strncmp(c->u.name, path, len) == 0))
break;
}
if (!c) {
*sid = SECINITSID_UNLABELED;
rc = -ENOENT;
goto out;
}
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*sid = c->sid[0];
out:
POLICY_RDUNLOCK;
return rc;
}
/**
* security_fs_use - Determine how to handle labeling for a filesystem.
* @fstype: filesystem type
* @behavior: labeling behavior
* @sid: SID for filesystem (superblock)
*/
int security_fs_use(
const char *fstype,
unsigned int *behavior,
u32 *sid)
{
int rc = 0;
struct ocontext *c;
POLICY_RDLOCK;
c = policydb.ocontexts[OCON_FSUSE];
while (c) {
if (strcmp(fstype, c->u.name) == 0)
break;
c = c->next;
}
if (c) {
*behavior = c->v.behavior;
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*sid = c->sid[0];
} else {
rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
if (rc) {
*behavior = SECURITY_FS_USE_NONE;
rc = 0;
} else {
*behavior = SECURITY_FS_USE_GENFS;
}
}
out:
POLICY_RDUNLOCK;
return rc;
}
/*
* 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"
/*
* The security server uses two global data structures
* when providing its services: the SID table (sidtab)
* and the policy database (policydb).
*/
extern struct sidtab sidtab;
extern struct policydb policydb;
#endif /* _SS_SERVICES_H_ */
/*
* Implementation of the SID table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#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) spin_lock_irq(&s->lock)
#define SIDTAB_UNLOCK(s) spin_unlock_irq(&s->lock)
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;
}
int sidtab_remove(struct sidtab *s, u32 sid)
{
int hvalue, rc = 0;
struct sidtab_node *cur, *last;
if (!s) {
rc = -ENOENT;
goto out;
}
hvalue = SIDTAB_HASH(sid);
last = NULL;
cur = s->htable[hvalue];
while (cur != NULL && sid > cur->sid) {
last = cur;
cur = cur->next;
}
if (cur == NULL || sid != cur->sid) {
rc = -ENOENT;
goto out;
}
if (last == NULL)
s->htable[hvalue] = cur->next;
else
last->next = cur->next;
context_destroy(&cur->context);
kfree(cur);
s->nel--;
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;
*out_sid = SECSID_NULL;
sid = sidtab_search_context(s, context);
if (!sid) {
SIDTAB_LOCK(s);
/* 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);
}
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_INFO "%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)
{
SIDTAB_LOCK(src);
dst->htable = src->htable;
dst->nel = src->nel;
dst->next_sid = src->next_sid;
dst->shutdown = 0;
SIDTAB_UNLOCK(src);
}
void sidtab_shutdown(struct sidtab *s)
{
SIDTAB_LOCK(s);
s->shutdown = 1;
SIDTAB_UNLOCK(s);
}
/*
* 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_ */
/*
* Implementation of the symbol table type.
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include "symtab.h"
static unsigned int symhash(struct hashtab *h, void *key)
{
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, void *key1, void *key2)
{
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;
}
/*
* 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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment