/dev/null | 0 25-akpm/security/Kconfig | 2 25-akpm/security/Makefile | 6 25-akpm/security/selinux/Kconfig | 34 25-akpm/security/selinux/Makefile | 10 25-akpm/security/selinux/avc.c | 1115 ++++ 25-akpm/security/selinux/hooks.c | 3405 +++++++++++++++ 25-akpm/security/selinux/include/av_inherit.h | 35 25-akpm/security/selinux/include/av_perm_to_string.h | 120 25-akpm/security/selinux/include/av_permissions.h | 550 ++ 25-akpm/security/selinux/include/avc.h | 159 25-akpm/security/selinux/include/avc_ss.h | 27 25-akpm/security/selinux/include/class_to_string.h | 39 25-akpm/security/selinux/include/common_perm_to_string.h | 65 25-akpm/security/selinux/include/flask.h | 71 25-akpm/security/selinux/include/initial_sid_to_string.h | 32 25-akpm/security/selinux/include/objsec.h | 88 25-akpm/security/selinux/include/security.h | 70 25-akpm/security/selinux/selinuxfs.c | 593 ++ 25-akpm/security/selinux/ss/Makefile | 14 25-akpm/security/selinux/ss/avtab.c | 264 + 25-akpm/security/selinux/ss/avtab.h | 68 25-akpm/security/selinux/ss/constraint.h | 54 25-akpm/security/selinux/ss/context.h | 117 25-akpm/security/selinux/ss/ebitmap.c | 332 + 25-akpm/security/selinux/ss/ebitmap.h | 49 25-akpm/security/selinux/ss/global.h | 17 25-akpm/security/selinux/ss/hashtab.c | 277 + 25-akpm/security/selinux/ss/hashtab.h | 125 25-akpm/security/selinux/ss/mls.c | 741 +++ 25-akpm/security/selinux/ss/mls.h | 99 25-akpm/security/selinux/ss/mls_types.h | 58 25-akpm/security/selinux/ss/policydb.c | 1429 ++++++ 25-akpm/security/selinux/ss/policydb.h | 256 + 25-akpm/security/selinux/ss/services.c | 1413 ++++++ 25-akpm/security/selinux/ss/services.h | 21 25-akpm/security/selinux/ss/sidtab.c | 329 + 25-akpm/security/selinux/ss/sidtab.h | 59 25-akpm/security/selinux/ss/symtab.c | 40 25-akpm/security/selinux/ss/symtab.h | 23 40 files changed, 12206 insertions(+) diff -puN security/Kconfig~selinux security/Kconfig --- 25/security/Kconfig~selinux Thu Jul 17 09:38:01 2003 +++ 25-akpm/security/Kconfig Thu Jul 17 09:38:01 2003 @@ -44,5 +44,7 @@ config SECURITY_ROOTPLUG If you are unsure how to answer this question, answer N. +source security/selinux/Kconfig + endmenu diff -puN -L security/Kconfig:1.1.1.2 /dev/null /dev/null diff -puN security/Makefile~selinux security/Makefile --- 25/security/Makefile~selinux Thu Jul 17 09:38:01 2003 +++ 25-akpm/security/Makefile Thu Jul 17 09:38:01 2003 @@ -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 diff -puN -L security/Makefile:1.1.1.1 /dev/null /dev/null diff -puN /dev/null security/selinux/avc.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/avc.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,1115 @@ +/* + * Implementation of the kernel access vector cache (AVC). + * + * Authors: Stephen Smalley, + * James Morris + * + * Copyright (C) 2003 Red Hat, Inc., James Morris + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); + diff -puN /dev/null security/selinux/hooks.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/hooks.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,3405 @@ +/* + * NSA Security-Enhanced Linux (SELinux) security module + * + * This file contains the SELinux hook function implementations. + * + * Authors: Stephen Smalley, + * Chris Vance, + * Wayne Salamon, + * James Morris + * + * Copyright (C) 2001,2002 Networks Associates Technology, Inc. + * Copyright (C) 2003 Red Hat, Inc., James Morris + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for sysctl_local_port_range[] */ +#include /* struct or_callable used in sock_rcv_skb */ +#include +#include +#include +#include +#include +#include /* for network interface checks */ +#include +#include +#include +#include /* for Unix socket types */ +#include /* 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(¤t->fs->count) > 1 || + atomic_read(¤t->files->count) > 1 || + atomic_read(¤t->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(¤t->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); + diff -puN /dev/null security/selinux/include/avc.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/avc.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,159 @@ +/* + * Access vector cache interface for object managers. + * + * Author : Stephen Smalley, + */ +#ifndef _SELINUX_AVC_H_ +#define _SELINUX_AVC_H_ + +#include +#include +#include +#include +#include +#include +#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_ */ + diff -puN /dev/null security/selinux/include/avc_ss.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/avc_ss.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,27 @@ +/* + * Access vector cache interface for the security server. + * + * Author : Stephen Smalley, + */ +#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_ */ + diff -puN /dev/null security/selinux/include/av_inherit.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/av_inherit.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,35 @@ +/* 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 */ diff -puN /dev/null security/selinux/include/av_permissions.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/av_permissions.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,550 @@ +/* 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 */ diff -puN /dev/null security/selinux/include/av_perm_to_string.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/av_perm_to_string.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,120 @@ +/* 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 */ diff -puN /dev/null security/selinux/include/class_to_string.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/class_to_string.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,39 @@ +/* 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", +}; + diff -puN /dev/null security/selinux/include/common_perm_to_string.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/common_perm_to_string.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,65 @@ +/* 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 */ diff -puN /dev/null security/selinux/include/flask.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/flask.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,71 @@ +/* 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 diff -puN /dev/null security/selinux/include/initial_sid_to_string.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/initial_sid_to_string.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,32 @@ +/* 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", +}; + diff -puN /dev/null security/selinux/include/objsec.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/objsec.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,88 @@ +/* + * NSA Security-Enhanced Linux (SELinux) security module + * + * This file contains the SELinux security data structures for kernel objects. + * + * Author(s): Stephen Smalley, + * Chris Vance, + * Wayne Salamon, + * James Morris + * + * Copyright (C) 2001,2002 Networks Associates Technology, Inc. + * Copyright (C) 2003 Red Hat, Inc., James Morris + * + * 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 +#include +#include +#include +#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_ */ diff -puN /dev/null security/selinux/include/security.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/include/security.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,70 @@ +/* + * Security server interface. + * + * Author : Stephen Smalley, + */ +#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_ */ + diff -puN /dev/null security/selinux/Kconfig --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/Kconfig Thu Jul 17 09:38:01 2003 @@ -0,0 +1,34 @@ +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. diff -puN /dev/null security/selinux/Makefile --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/Makefile Thu Jul 17 09:38:01 2003 @@ -0,0 +1,10 @@ +# +# 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 + diff -puN /dev/null security/selinux/selinuxfs.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/selinuxfs.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,593 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); diff -puN /dev/null security/selinux/ss/avtab.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/avtab.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,264 @@ +/* + * Implementation of the access vector table type. + * + * Author : Stephen Smalley, + */ +#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; +} + diff -puN /dev/null security/selinux/ss/avtab.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/avtab.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,68 @@ +/* + * 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, + */ +#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_ */ + diff -puN /dev/null security/selinux/ss/constraint.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/constraint.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,54 @@ +/* + * 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, + */ +#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_ */ diff -puN /dev/null security/selinux/ss/context.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/context.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,117 @@ +/* + * 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, + */ +#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_ */ + diff -puN /dev/null security/selinux/ss/ebitmap.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/ebitmap.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,332 @@ +/* + * Implementation of the extensible bitmap type. + * + * Author : Stephen Smalley, + */ +#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; +} diff -puN /dev/null security/selinux/ss/ebitmap.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/ebitmap.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,49 @@ +/* + * 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, + */ +#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_ */ diff -puN /dev/null security/selinux/ss/global.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/global.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,17 @@ +#ifndef _SS_GLOBAL_H_ +#define _SS_GLOBAL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "flask.h" +#include "avc.h" +#include "avc_ss.h" +#include "security.h" + +#endif /* _SS_GLOBAL_H_ */ diff -puN /dev/null security/selinux/ss/hashtab.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/hashtab.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,277 @@ +/* + * Implementation of the hash table type. + * + * Author : Stephen Smalley, + */ +#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; +} diff -puN /dev/null security/selinux/ss/hashtab.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/hashtab.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,125 @@ +/* + * 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, + */ +#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 */ diff -puN /dev/null security/selinux/ss/Makefile --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/Makefile Thu Jul 17 09:38:01 2003 @@ -0,0 +1,14 @@ +# +# 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 + diff -puN /dev/null security/selinux/ss/mls.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/mls.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,741 @@ +/* + * Implementation of the multi-level security (MLS) policy. + * + * Author : Stephen Smalley, + */ +#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; +} diff -puN /dev/null security/selinux/ss/mls.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/mls.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,99 @@ +/* + * Multi-level security (MLS) policy operations. + * + * Author : Stephen Smalley, + */ +#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 */ + diff -puN /dev/null security/selinux/ss/mls_types.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/mls_types.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,58 @@ +/* + * Type definitions for the multi-level security (MLS) policy. + * + * Author : Stephen Smalley, + */ +#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_ */ diff -puN /dev/null security/selinux/ss/policydb.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/policydb.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,1429 @@ +/* + * Implementation of the policy database. + * + * Author : Stephen Smalley, + */ +#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; +} diff -puN /dev/null security/selinux/ss/policydb.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/policydb.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,256 @@ +/* + * A policy database (policydb) specifies the + * configuration data for the security policy. + * + * Author : Stephen Smalley, + */ +#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_ */ + diff -puN /dev/null security/selinux/ss/services.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/services.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,1413 @@ +/* + * Implementation of the security services. + * + * Authors : Stephen Smalley, + * James Morris + * + * Copyright (C) 2003 Red Hat, Inc., James Morris + * + * 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; +} diff -puN /dev/null security/selinux/ss/services.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/services.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,21 @@ +/* + * Implementation of the security services. + * + * Author : Stephen Smalley, + */ +#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_ */ + diff -puN /dev/null security/selinux/ss/sidtab.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/sidtab.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,329 @@ +/* + * Implementation of the SID table type. + * + * Author : Stephen Smalley, + */ +#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); +} diff -puN /dev/null security/selinux/ss/sidtab.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/sidtab.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,59 @@ +/* + * A security identifier table (sidtab) is a hash table + * of security context structures indexed by SID value. + * + * Author : Stephen Smalley, + */ +#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_ */ + + diff -puN /dev/null security/selinux/ss/symtab.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/symtab.c Thu Jul 17 09:38:01 2003 @@ -0,0 +1,40 @@ +/* + * Implementation of the symbol table type. + * + * Author : Stephen Smalley, + */ +#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; +} + diff -puN /dev/null security/selinux/ss/symtab.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/security/selinux/ss/symtab.h Thu Jul 17 09:38:01 2003 @@ -0,0 +1,23 @@ +/* + * A symbol table (symtab) maintains associations between symbol + * strings and datum values. The type of the datum values + * is arbitrary. The symbol table type is implemented + * using the hash table type (hashtab). + * + * Author : Stephen Smalley, + */ +#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_ */ + + _