diff options
Diffstat (limited to 'security/selinux')
53 files changed, 13469 insertions, 6672 deletions
diff --git a/security/selinux/.gitignore b/security/selinux/.gitignore new file mode 100644 index 00000000000..2e5040a3d48 --- /dev/null +++ b/security/selinux/.gitignore @@ -0,0 +1,2 @@ +av_permissions.h +flask.h diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index b59582b9228..bca1b74a4a2 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -1,13 +1,11 @@ config SECURITY_SELINUX bool "NSA SELinux Support" - depends on SECURITY && NET && INET + depends on SECURITY_NETWORK && AUDIT && NET && INET + select NETWORK_SECMARK default n help This selects NSA Security-Enhanced Linux (SELinux). You will also need a policy configuration and a labeled filesystem. - You can obtain the policy compiler (checkpolicy), the utility for - labeling filesystems (setfiles), and an example policy configuration - from <http://www.nsa.gov/selinux/>. If you are unsure how to answer this question, answer N. config SECURITY_SELINUX_BOOTPARAM @@ -95,3 +93,41 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE via /selinux/checkreqprot if authorized by policy. If you are unsure how to answer this question, answer 1. + +config SECURITY_SELINUX_POLICYDB_VERSION_MAX + bool "NSA SELinux maximum supported policy format version" + depends on SECURITY_SELINUX + default n + help + This option enables the maximum policy format version supported + by SELinux to be set to a particular value. This value is reported + to userspace via /selinux/policyvers and used at policy load time. + It can be adjusted downward to support legacy userland (init) that + does not correctly handle kernels that support newer policy versions. + + Examples: + For the Fedora Core 3 or 4 Linux distributions, enable this option + and set the value via the next option. For Fedora Core 5 and later, + do not enable this option. + + If you are unsure how to answer this question, answer N. + +config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE + int "NSA SELinux maximum supported policy format version value" + depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX + range 15 23 + default 19 + help + This option sets the value for the maximum policy format version + supported by SELinux. + + Examples: + For Fedora Core 3, use 18. + For Fedora Core 4, use 19. + + If you are unsure how to answer this question, look for the + policy format version supported by your policy toolchain, by + running 'checkpolicy -V'. Or look at what policy you have + installed under /etc/selinux/$SELINUXTYPE/policy, where + SELINUXTYPE is defined in your /etc/selinux/config. + diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 06d54d9d20a..ad5cd76ec23 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -2,13 +2,24 @@ # Makefile for building the SELinux module as part of the kernel tree. # -obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/ +obj-$(CONFIG_SECURITY_SELINUX) := selinux.o -selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o - -selinux-$(CONFIG_SECURITY_NETWORK) += netif.o +selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ + netnode.o netport.o exports.o \ + ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ + ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o -EXTRA_CFLAGS += -Isecurity/selinux/include +selinux-$(CONFIG_NETLABEL) += netlabel.o + +ccflags-y := -Isecurity/selinux -Isecurity/selinux/include + +$(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h + +quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h + cmd_flask = scripts/selinux/genheaders/genheaders $(obj)/flask.h $(obj)/av_permissions.h +targets += flask.h av_permissions.h +$(obj)/flask.h: $(src)/include/classmap.h FORCE + $(call if_changed,flask) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 53d6c7bbf56..a18f1fa6440 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -2,16 +2,16 @@ * Implementation of the kernel access vector cache (AVC). * * Authors: Stephen Smalley, <sds@epoch.ncsc.mil> - * James Morris <jmorris@redhat.com> + * James Morris <jmorris@redhat.com> * * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com> - * Replaced the avc_lock spinlock by RCU. + * Replaced the avc_lock spinlock by RCU. * * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * as published by the Free Software Foundation. */ #include <linux/types.h> #include <linux/stddef.h> @@ -31,55 +31,14 @@ #include <net/ipv6.h> #include "avc.h" #include "avc_ss.h" - -static const struct av_perm_to_string -{ - u16 tclass; - u32 value; - const char *name; -} av_perm_to_string[] = { -#define S_(c, v, s) { c, v, s }, -#include "av_perm_to_string.h" -#undef S_ -}; - -#ifdef CONFIG_AUDIT -static const char *class_to_string[] = { -#define S_(s) s, -#include "class_to_string.h" -#undef S_ -}; -#endif - -#define TB_(s) static const char * s [] = { -#define TE_(s) }; -#define S_(s) s, -#include "common_perm_to_string.h" -#undef TB_ -#undef TE_ -#undef S_ - -static const struct av_inherit -{ - u16 tclass; - const char **common_pts; - u32 common_base; -} av_inherit[] = { -#define S_(c, i, b) { c, common_##i##_perm_to_string, b }, -#include "av_inherit.h" -#undef S_ -}; +#include "classmap.h" #define AVC_CACHE_SLOTS 512 #define AVC_DEF_CACHE_THRESHOLD 512 #define AVC_CACHE_RECLAIM 16 #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS -#define avc_cache_stats_incr(field) \ -do { \ - per_cpu(avc_cache_stats, get_cpu()).field++; \ - put_cpu(); \ -} while (0) +#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field) #else #define avc_cache_stats_incr(field) do {} while (0) #endif @@ -89,17 +48,16 @@ struct avc_entry { u32 tsid; u16 tclass; struct av_decision avd; - atomic_t used; /* used recently */ }; struct avc_node { struct avc_entry ae; - struct list_head list; - struct rcu_head rhead; + struct hlist_node list; /* anchored in avc_cache->slots[i] */ + struct rcu_head rhead; }; struct avc_cache { - struct list_head slots[AVC_CACHE_SLOTS]; + struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ atomic_t lru_hint; /* LRU hint for reclaim scan */ atomic_t active_nodes; @@ -107,14 +65,8 @@ struct avc_cache { }; struct avc_callback_node { - int (*callback) (u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, - u32 *out_retained); + int (*callback) (u32 event); u32 events; - u32 ssid; - u32 tsid; - u16 tclass; - u32 perms; struct avc_callback_node *next; }; @@ -127,7 +79,7 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; static struct avc_cache avc_cache; static struct avc_callback_node *avc_callbacks; -static kmem_cache_t *avc_node_cachep; +static struct kmem_cache *avc_node_cachep; static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) { @@ -141,52 +93,28 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) */ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { - const char **common_pts = NULL; - u32 common_base = 0; - int i, i2, perm; + const char **perms; + int i, perm; if (av == 0) { audit_log_format(ab, " null"); return; } - for (i = 0; i < ARRAY_SIZE(av_inherit); i++) { - if (av_inherit[i].tclass == tclass) { - common_pts = av_inherit[i].common_pts; - common_base = av_inherit[i].common_base; - break; - } - } + perms = secclass_map[tclass-1].perms; audit_log_format(ab, " {"); i = 0; perm = 1; - while (perm < common_base) { - if (perm & av) { - audit_log_format(ab, " %s", common_pts[i]); + while (i < (sizeof(av) * 8)) { + if ((perm & av) && perms[i]) { + audit_log_format(ab, " %s", perms[i]); av &= ~perm; } i++; perm <<= 1; } - while (i < sizeof(av) * 8) { - if (perm & av) { - for (i2 = 0; i2 < ARRAY_SIZE(av_perm_to_string); i2++) { - if ((av_perm_to_string[i2].tclass == tclass) && - (av_perm_to_string[i2].value == perm)) - break; - } - if (i2 < ARRAY_SIZE(av_perm_to_string)) { - audit_log_format(ab, " %s", - av_perm_to_string[i2].name); - av &= ~perm; - } - } - i++; - perm <<= 1; - } - if (av) audit_log_format(ab, " 0x%x", av); @@ -205,7 +133,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla char *scontext; u32 scontext_len; - rc = security_sid_to_context(ssid, &scontext, &scontext_len); + rc = security_sid_to_context(ssid, &scontext, &scontext_len); if (rc) audit_log_format(ab, "ssid=%d", ssid); else { @@ -220,7 +148,9 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla audit_log_format(ab, " tcontext=%s", scontext); kfree(scontext); } - audit_log_format(ab, " tclass=%s", class_to_string[tclass]); + + BUG_ON(tclass >= ARRAY_SIZE(secclass_map)); + audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); } /** @@ -233,14 +163,14 @@ void __init avc_init(void) int i; for (i = 0; i < AVC_CACHE_SLOTS; i++) { - INIT_LIST_HEAD(&avc_cache.slots[i]); + INIT_HLIST_HEAD(&avc_cache.slots[i]); spin_lock_init(&avc_cache.slots_lock[i]); } atomic_set(&avc_cache.active_nodes, 0); atomic_set(&avc_cache.lru_hint, 0); avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); } @@ -249,16 +179,18 @@ int avc_get_hash_stats(char *page) { int i, chain_len, max_chain_len, slots_used; struct avc_node *node; + struct hlist_head *head; rcu_read_lock(); slots_used = 0; max_chain_len = 0; for (i = 0; i < AVC_CACHE_SLOTS; i++) { - if (!list_empty(&avc_cache.slots[i])) { + head = &avc_cache.slots[i]; + if (!hlist_empty(head)) { slots_used++; chain_len = 0; - list_for_each_entry_rcu(node, &avc_cache.slots[i], list) + hlist_for_each_entry_rcu(node, head, list) chain_len++; if (chain_len > max_chain_len) max_chain_len = chain_len; @@ -282,7 +214,7 @@ static void avc_node_free(struct rcu_head *rhead) static void avc_node_delete(struct avc_node *node) { - list_del_rcu(&node->list); + hlist_del_rcu(&node->list); call_rcu(&node->rhead, avc_node_free); atomic_dec(&avc_cache.active_nodes); } @@ -296,7 +228,7 @@ static void avc_node_kill(struct avc_node *node) static void avc_node_replace(struct avc_node *new, struct avc_node *old) { - list_replace_rcu(&old->list, &new->list); + hlist_replace_rcu(&old->list, &new->list); call_rcu(&old->rhead, avc_node_free); atomic_dec(&avc_cache.active_nodes); } @@ -306,26 +238,30 @@ static inline int avc_reclaim_node(void) struct avc_node *node; int hvalue, try, ecx; unsigned long flags; + struct hlist_head *head; + spinlock_t *lock; - for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) { + for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) { hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; - if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags)) + if (!spin_trylock_irqsave(lock, flags)) continue; - list_for_each_entry(node, &avc_cache.slots[hvalue], list) { - if (atomic_dec_and_test(&node->ae.used)) { - /* Recently Unused */ - avc_node_delete(node); - avc_cache_stats_incr(reclaims); - ecx++; - if (ecx >= AVC_CACHE_RECLAIM) { - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); - goto out; - } + rcu_read_lock(); + hlist_for_each_entry(node, head, list) { + avc_node_delete(node); + avc_cache_stats_incr(reclaims); + ecx++; + if (ecx >= AVC_CACHE_RECLAIM) { + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flags); + goto out; } } - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flags); } out: return ecx; @@ -335,14 +271,11 @@ static struct avc_node *avc_alloc_node(void) { struct avc_node *node; - node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC); + node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC); if (!node) goto out; - memset(node, 0, sizeof(*node)); - INIT_RCU_HEAD(&node->rhead); - INIT_LIST_HEAD(&node->list); - atomic_set(&node->ae.used, 1); + INIT_HLIST_NODE(&node->list); avc_cache_stats_incr(allocations); if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold) @@ -352,21 +285,23 @@ out: return node; } -static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) +static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) { node->ae.ssid = ssid; node->ae.tsid = tsid; node->ae.tclass = tclass; - memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd)); + memcpy(&node->ae.avd, avd, sizeof(node->ae.avd)); } static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) { struct avc_node *node, *ret = NULL; int hvalue; + struct hlist_head *head; hvalue = avc_hash(ssid, tsid, tclass); - list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) { + head = &avc_cache.slots[hvalue]; + hlist_for_each_entry_rcu(node, head, list) { if (ssid == node->ae.ssid && tclass == node->ae.tclass && tsid == node->ae.tsid) { @@ -375,15 +310,6 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) } } - if (ret == NULL) { - /* cache miss */ - goto out; - } - - /* cache hit */ - if (atomic_read(&ret->ae.used) != 1) - atomic_set(&ret->ae.used, 1); -out: return ret; } @@ -392,31 +318,25 @@ out: * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class - * @requested: requested permissions, interpreted based on @tclass * * Look up an AVC entry that is valid for the - * @requested permissions between the SID pair * (@ssid, @tsid), interpreting the permissions * based on @tclass. If a valid AVC entry exists, - * then this function return the avc_node. + * then this function returns the avc_node. * Otherwise, this function returns NULL. */ -static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested) +static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass) { struct avc_node *node; avc_cache_stats_incr(lookups); node = avc_search_node(ssid, tsid, tclass); - if (node && ((node->ae.avd.decided & requested) == requested)) { - avc_cache_stats_incr(hits); - goto out; - } + if (node) + return node; - node = NULL; avc_cache_stats_incr(misses); -out: - return node; + return NULL; } static int avc_latest_notif_update(int seqno, int is_insert) @@ -428,7 +348,7 @@ static int avc_latest_notif_update(int seqno, int is_insert) spin_lock_irqsave(¬if_lock, flag); if (is_insert) { if (seqno < avc_cache.latest_notif) { - printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n", + printk(KERN_WARNING "SELinux: avc: seqno %d < latest_notif %d\n", seqno, avc_cache.latest_notif); ret = -EAGAIN; } @@ -446,258 +366,144 @@ static int avc_latest_notif_update(int seqno, int is_insert) * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class - * @ae: AVC entry + * @avd: resulting av decision * * 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 + * sequence number @avd->seqno is not less than the latest * revocation notification, then the function copies * the access vectors into a cache entry, returns * avc_node inserted. Otherwise, this function returns NULL. */ -static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) +static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) { struct avc_node *pos, *node = NULL; int hvalue; unsigned long flag; - if (avc_latest_notif_update(ae->avd.seqno, 1)) + if (avc_latest_notif_update(avd->seqno, 1)) goto out; node = avc_alloc_node(); if (node) { + struct hlist_head *head; + spinlock_t *lock; + hvalue = avc_hash(ssid, tsid, tclass); - avc_node_populate(node, ssid, tsid, tclass, ae); + avc_node_populate(node, ssid, tsid, tclass, avd); - spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); - list_for_each_entry(pos, &avc_cache.slots[hvalue], list) { + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; + + spin_lock_irqsave(lock, flag); + hlist_for_each_entry(pos, head, list) { if (pos->ae.ssid == ssid && pos->ae.tsid == tsid && pos->ae.tclass == tclass) { - avc_node_replace(node, pos); + avc_node_replace(node, pos); goto found; } } - list_add_rcu(&node->list, &avc_cache.slots[hvalue]); + hlist_add_head_rcu(&node->list, head); found: - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); + spin_unlock_irqrestore(lock, flag); } out: return node; } -static inline void avc_print_ipv6_addr(struct audit_buffer *ab, - struct in6_addr *addr, __be16 port, - char *name1, char *name2) -{ - if (!ipv6_addr_any(addr)) - audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr)); - if (port) - audit_log_format(ab, " %s=%d", name2, ntohs(port)); -} - -static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr, - __be16 port, char *name1, char *name2) +/** + * avc_audit_pre_callback - SELinux specific information + * will be called by generic audit code + * @ab: the audit buffer + * @a: audit_data + */ +static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) { - if (addr) - audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr)); - if (port) - audit_log_format(ab, " %s=%d", name2, ntohs(port)); + struct common_audit_data *ad = a; + audit_log_format(ab, "avc: %s ", + ad->selinux_audit_data->denied ? "denied" : "granted"); + avc_dump_av(ab, ad->selinux_audit_data->tclass, + ad->selinux_audit_data->audited); + audit_log_format(ab, " for "); } /** - * 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. + * avc_audit_post_callback - SELinux specific information + * will be called by generic audit code + * @ab: the audit buffer + * @a: audit_data */ -void avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, int result, struct avc_audit_data *a) +static void avc_audit_post_callback(struct audit_buffer *ab, void *a) { - struct task_struct *tsk = current; - struct inode *inode = NULL; - u32 denied, audited; - struct audit_buffer *ab; - - denied = requested & ~avd->allowed; - if (denied) { - audited = denied; - if (!(audited & avd->auditdeny)) - return; - } else if (result) { - audited = denied = requested; - } else { - audited = requested; - if (!(audited & avd->auditallow)) - return; + struct common_audit_data *ad = a; + audit_log_format(ab, " "); + avc_dump_query(ab, ad->selinux_audit_data->ssid, + ad->selinux_audit_data->tsid, + ad->selinux_audit_data->tclass); + if (ad->selinux_audit_data->denied) { + audit_log_format(ab, " permissive=%u", + ad->selinux_audit_data->result ? 0 : 1); } +} - ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC); - if (!ab) - return; /* audit_panic has been called */ - audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); - avc_dump_av(ab, tclass,audited); - audit_log_format(ab, " for "); - if (a && a->tsk) - tsk = a->tsk; - if (tsk && tsk->pid) { - audit_log_format(ab, " pid=%d comm=", tsk->pid); - audit_log_untrustedstring(ab, tsk->comm); - } - if (a) { - switch (a->type) { - case AVC_AUDIT_DATA_IPC: - audit_log_format(ab, " key=%d", a->u.ipc_id); - break; - case AVC_AUDIT_DATA_CAP: - audit_log_format(ab, " capability=%d", a->u.cap); - break; - case AVC_AUDIT_DATA_FS: - if (a->u.fs.dentry) { - struct dentry *dentry = a->u.fs.dentry; - if (a->u.fs.mnt) - audit_avc_path(dentry, a->u.fs.mnt); - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, dentry->d_name.name); - inode = dentry->d_inode; - } else if (a->u.fs.inode) { - struct dentry *dentry; - inode = a->u.fs.inode; - dentry = d_find_alias(inode); - if (dentry) { - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, dentry->d_name.name); - dput(dentry); - } - } - if (inode) - audit_log_format(ab, " dev=%s ino=%ld", - inode->i_sb->s_id, - inode->i_ino); - break; - case AVC_AUDIT_DATA_NET: - if (a->u.net.sk) { - struct sock *sk = a->u.net.sk; - struct unix_sock *u; - int len = 0; - char *p = NULL; - - switch (sk->sk_family) { - case AF_INET: { - struct inet_sock *inet = inet_sk(sk); - - avc_print_ipv4_addr(ab, inet->rcv_saddr, - inet->sport, - "laddr", "lport"); - avc_print_ipv4_addr(ab, inet->daddr, - inet->dport, - "faddr", "fport"); - break; - } - case AF_INET6: { - struct inet_sock *inet = inet_sk(sk); - struct ipv6_pinfo *inet6 = inet6_sk(sk); - - avc_print_ipv6_addr(ab, &inet6->rcv_saddr, - inet->sport, - "laddr", "lport"); - avc_print_ipv6_addr(ab, &inet6->daddr, - inet->dport, - "faddr", "fport"); - break; - } - case AF_UNIX: - u = unix_sk(sk); - if (u->dentry) { - audit_avc_path(u->dentry, u->mnt); - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, u->dentry->d_name.name); - break; - } - if (!u->addr) - break; - len = u->addr->len-sizeof(short); - p = &u->addr->name->sun_path[0]; - audit_log_format(ab, " path="); - if (*p) - audit_log_untrustedstring(ab, p); - else - audit_log_hex(ab, p, len); - break; - } - } - - switch (a->u.net.family) { - case AF_INET: - avc_print_ipv4_addr(ab, a->u.net.v4info.saddr, - a->u.net.sport, - "saddr", "src"); - avc_print_ipv4_addr(ab, a->u.net.v4info.daddr, - a->u.net.dport, - "daddr", "dest"); - break; - case AF_INET6: - avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr, - a->u.net.sport, - "saddr", "src"); - avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr, - a->u.net.dport, - "daddr", "dest"); - break; - } - if (a->u.net.netif) - audit_log_format(ab, " netif=%s", - a->u.net.netif); - break; - } +/* This is the slow part of avc audit with big stack footprint */ +noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, + u32 requested, u32 audited, u32 denied, int result, + struct common_audit_data *a, + unsigned flags) +{ + struct common_audit_data stack_data; + struct selinux_audit_data sad; + + if (!a) { + a = &stack_data; + a->type = LSM_AUDIT_DATA_NONE; } - audit_log_format(ab, " "); - avc_dump_query(ab, ssid, tsid, tclass); - audit_log_end(ab); + + /* + * When in a RCU walk do the audit on the RCU retry. This is because + * the collection of the dname in an inode audit message is not RCU + * safe. Note this may drop some audits when the situation changes + * during retry. However this is logically just as if the operation + * happened a little later. + */ + if ((a->type == LSM_AUDIT_DATA_INODE) && + (flags & MAY_NOT_BLOCK)) + return -ECHILD; + + sad.tclass = tclass; + sad.requested = requested; + sad.ssid = ssid; + sad.tsid = tsid; + sad.audited = audited; + sad.denied = denied; + sad.result = result; + + a->selinux_audit_data = &sad; + + common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); + return 0; } /** * 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. + * Register a callback function for events in the set @events. + * 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) +int __init avc_add_callback(int (*callback)(u32 event), u32 events) { struct avc_callback_node *c; int rc = 0; - c = kmalloc(sizeof(*c), GFP_ATOMIC); + c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) { rc = -ENOMEM; goto out; @@ -705,9 +511,6 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, c->callback = callback; c->events = events; - c->ssid = ssid; - c->tsid = tsid; - c->perms = perms; c->next = avc_callbacks; avc_callbacks = c; out: @@ -724,17 +527,21 @@ static inline int avc_sidcmp(u32 x, u32 y) * @event : Updating event * @perms : Permission mask bits * @ssid,@tsid,@tclass : identifier of an AVC entry + * @seqno : sequence number when decision was made * * if a valid AVC entry doesn't exist,this function returns -ENOENT. * if kmalloc() called internal returns NULL, this function returns -ENOMEM. - * otherwise, this function update the AVC entry. The original AVC-entry object + * otherwise, this function updates the AVC entry. The original AVC-entry object * will release later by RCU. */ -static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) +static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, + u32 seqno) { int hvalue, rc = 0; unsigned long flag; struct avc_node *pos, *node, *orig = NULL; + struct hlist_head *head; + spinlock_t *lock; node = avc_alloc_node(); if (!node) { @@ -744,12 +551,17 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) /* Lock the target slot */ hvalue = avc_hash(ssid, tsid, tclass); - spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); - list_for_each_entry(pos, &avc_cache.slots[hvalue], list){ - if ( ssid==pos->ae.ssid && - tsid==pos->ae.tsid && - tclass==pos->ae.tclass ){ + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; + + spin_lock_irqsave(lock, flag); + + hlist_for_each_entry(pos, head, list) { + if (ssid == pos->ae.ssid && + tsid == pos->ae.tsid && + tclass == pos->ae.tclass && + seqno == pos->ae.avd.seqno){ orig = pos; break; } @@ -765,7 +577,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) * Copy and replace original node. */ - avc_node_populate(node, ssid, tsid, tclass, &orig->ae); + avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); switch (event) { case AVC_CALLBACK_GRANT: @@ -790,49 +602,106 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) } avc_node_replace(node, orig); out_unlock: - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); + spin_unlock_irqrestore(lock, flag); out: return rc; } /** - * avc_ss_reset - Flush the cache and revalidate migrated permissions. - * @seqno: policy sequence number + * avc_flush - Flush the cache */ -int avc_ss_reset(u32 seqno) +static void avc_flush(void) { - struct avc_callback_node *c; - int i, rc = 0; - unsigned long flag; + struct hlist_head *head; struct avc_node *node; + spinlock_t *lock; + unsigned long flag; + int i; for (i = 0; i < AVC_CACHE_SLOTS; i++) { - spin_lock_irqsave(&avc_cache.slots_lock[i], flag); - list_for_each_entry(node, &avc_cache.slots[i], list) + head = &avc_cache.slots[i]; + lock = &avc_cache.slots_lock[i]; + + spin_lock_irqsave(lock, flag); + /* + * With preemptable RCU, the outer spinlock does not + * prevent RCU grace periods from ending. + */ + rcu_read_lock(); + hlist_for_each_entry(node, head, list) avc_node_delete(node); - spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag); + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flag); } +} + +/** + * 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 rc = 0, tmprc; + + avc_flush(); for (c = avc_callbacks; c; c = c->next) { if (c->events & AVC_CALLBACK_RESET) { - rc = c->callback(AVC_CALLBACK_RESET, - 0, 0, 0, 0, NULL); - if (rc) - goto out; + tmprc = c->callback(AVC_CALLBACK_RESET); + /* save the first error encountered for the return + value and continue processing the callbacks */ + if (!rc) + rc = tmprc; } } avc_latest_notif_update(seqno, 0); -out: return rc; } +/* + * Slow-path helper function for avc_has_perm_noaudit, + * when the avc_node lookup fails. We get called with + * the RCU read lock held, and need to return with it + * still held, but drop if for the security compute. + * + * Don't inline this, since it's the slow-path and just + * results in a bigger stack frame. + */ +static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd) +{ + rcu_read_unlock(); + security_compute_av(ssid, tsid, tclass, avd); + rcu_read_lock(); + return avc_insert(ssid, tsid, tclass, avd); +} + +static noinline int avc_denied(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + unsigned flags, + struct av_decision *avd) +{ + if (flags & AVC_STRICT) + return -EACCES; + + if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) + return -EACCES; + + avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, + tsid, tclass, avd->seqno); + return 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 + * @flags: AVC_STRICT or 0 * @avd: access vector decisions * * Check the AVC to determine whether the @requested permissions are granted @@ -846,45 +715,32 @@ out: * 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 av_decision *avd) +inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + unsigned flags, + struct av_decision *avd) { struct avc_node *node; - struct avc_entry entry, *p_ae; int rc = 0; u32 denied; + BUG_ON(!requested); + rcu_read_lock(); - node = avc_lookup(ssid, tsid, tclass, requested); - if (!node) { - rcu_read_unlock(); - rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd); - if (rc) - goto out; - rcu_read_lock(); - node = avc_insert(ssid,tsid,tclass,&entry); + node = avc_lookup(ssid, tsid, tclass); + if (unlikely(!node)) { + node = avc_compute_av(ssid, tsid, tclass, avd); + } else { + memcpy(avd, &node->ae.avd, sizeof(*avd)); + avd = &node->ae.avd; } - p_ae = node ? &node->ae : &entry; - - if (avd) - memcpy(avd, &p_ae->avd, sizeof(*avd)); - - denied = requested & ~(p_ae->avd.allowed); - - if (!requested || denied) { - if (selinux_enforcing) - rc = -EACCES; - else - if (node) - avc_update_node(AVC_CALLBACK_GRANT,requested, - ssid,tsid,tclass); - } + denied = requested & ~(avd->allowed); + if (unlikely(denied)) + rc = avc_denied(ssid, tsid, tclass, requested, flags, avd); rcu_read_unlock(); -out: return rc; } @@ -905,12 +761,39 @@ out: * another -errno upon other errors. */ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, - u32 requested, struct avc_audit_data *auditdata) + u32 requested, struct common_audit_data *auditdata) { struct av_decision avd; - int rc; + int rc, rc2; + + rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); - rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd); - avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); + rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); + if (rc2) + return rc2; return rc; } + +u32 avc_policy_seqno(void) +{ + return avc_cache.latest_notif; +} + +void avc_disable(void) +{ + /* + * If you are looking at this because you have realized that we are + * not destroying the avc_node_cachep it might be easy to fix, but + * I don't know the memory barrier semantics well enough to know. It's + * possible that some other task dereferenced security_ops when + * it still pointed to selinux operations. If that is the case it's + * possible that it is about to use the avc and is about to need the + * avc_node_cachep. I know I could wrap the security.c security_ops call + * in an rcu_lock, but seriously, it's not worth it. Instead I just flush + * the cache and get that memory back. + */ + if (avc_node_cachep) { + avc_flush(); + /* kmem_cache_destroy(avc_node_cachep); */ + } +} diff --git a/security/selinux/exports.c b/security/selinux/exports.c new file mode 100644 index 00000000000..e75dd94e2d2 --- /dev/null +++ b/security/selinux/exports.c @@ -0,0 +1,23 @@ +/* + * SELinux services exported to the rest of the kernel. + * + * Author: James Morris <jmorris@redhat.com> + * + * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> + * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/selinux.h> + +#include "security.h" + +bool selinux_is_enabled(void) +{ + return selinux_enabled; +} +EXPORT_SYMBOL_GPL(selinux_is_enabled); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b9f8d9731c3..83d06db34d0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4,25 +4,29 @@ * This file contains the SELinux hook function implementations. * * Authors: Stephen Smalley, <sds@epoch.ncsc.mil> - * Chris Vance, <cvance@nai.com> - * Wayne Salamon, <wsalamon@nai.com> - * James Morris <jmorris@redhat.com> + * Chris Vance, <cvance@nai.com> + * Wayne Salamon, <wsalamon@nai.com> + * James Morris <jmorris@redhat.com> * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. - * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Eric Paris <eparis@redhat.com> * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. - * <dgoeddel@trustedcs.com> + * <dgoeddel@trustedcs.com> + * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P. + * Paul Moore <paul@paul-moore.com> + * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. + * Yuichi Nakamura <ynakam@hitachisoft.jp> * * 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. + * as published by the Free Software Foundation. */ -#include <linux/config.h> -#include <linux/module.h> #include <linux/init.h> +#include <linux/kd.h> #include <linux/kernel.h> -#include <linux/ptrace.h> +#include <linux/tracehook.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/security.h> @@ -33,31 +37,35 @@ #include <linux/mman.h> #include <linux/slab.h> #include <linux/pagemap.h> +#include <linux/proc_fs.h> #include <linux/swap.h> -#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/syscalls.h> +#include <linux/dcache.h> #include <linux/file.h> +#include <linux/fdtable.h> #include <linux/namei.h> #include <linux/mount.h> -#include <linux/ext2_fs.h> -#include <linux/proc_fs.h> -#include <linux/kd.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> #include <linux/tty.h> #include <net/icmp.h> -#include <net/ip.h> /* for sysctl_local_port_range[] */ +#include <net/ip.h> /* for local_port_range[] */ +#include <net/sock.h> #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ -#include <asm/uaccess.h> -#include <asm/semaphore.h> +#include <net/inet_connection_sock.h> +#include <net/net_namespace.h> +#include <net/netlabel.h> +#include <linux/uaccess.h> #include <asm/ioctls.h> +#include <linux/atomic.h> #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/netdevice.h> /* for network interface checks */ -#include <linux/netlink.h> +#include <net/netlink.h> #include <linux/tcp.h> #include <linux/udp.h> +#include <linux/dccp.h> #include <linux/quota.h> #include <linux/un.h> /* for Unix socket types */ #include <net/af_unix.h> /* for Unix socket types */ @@ -66,27 +74,40 @@ #include <net/ipv6.h> #include <linux/hugetlb.h> #include <linux/personality.h> -#include <linux/sysctl.h> #include <linux/audit.h> #include <linux/string.h> +#include <linux/selinux.h> +#include <linux/mutex.h> +#include <linux/posix-timers.h> +#include <linux/syslog.h> +#include <linux/user_namespace.h> +#include <linux/export.h> +#include <linux/msg.h> +#include <linux/shm.h> #include "avc.h" #include "objsec.h" #include "netif.h" +#include "netnode.h" +#include "netport.h" #include "xfrm.h" +#include "netlabel.h" +#include "audit.h" +#include "avc_ss.h" -#define XATTR_SELINUX_SUFFIX "selinux" -#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX +extern struct security_operations *security_ops; -extern unsigned int policydb_loaded_version; -extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); +/* SECMARK reference count */ +static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_SELINUX_DEVELOP -int selinux_enforcing = 0; +int selinux_enforcing; static int __init enforcing_setup(char *str) { - selinux_enforcing = simple_strtol(str,NULL,0); + unsigned long enforcing; + if (!kstrtoul(str, 0, &enforcing)) + selinux_enforcing = enforcing ? 1 : 0; return 1; } __setup("enforcing=", enforcing_setup); @@ -97,114 +118,162 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; static int __init selinux_enabled_setup(char *str) { - selinux_enabled = simple_strtol(str, NULL, 0); + unsigned long enabled; + if (!kstrtoul(str, 0, &enabled)) + selinux_enabled = enabled ? 1 : 0; return 1; } __setup("selinux=", selinux_enabled_setup); +#else +int selinux_enabled = 1; #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; +static struct kmem_cache *sel_inode_cache; -/* Lists of inode and superblock security structures initialized - before the policy was loaded. */ -static LIST_HEAD(superblock_security_head); -static DEFINE_SPINLOCK(sb_security_lock); +/** + * selinux_secmark_enabled - Check to see if SECMARK is currently enabled + * + * Description: + * This function checks the SECMARK reference counter to see if any SECMARK + * targets are currently configured, if the reference counter is greater than + * zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is + * enabled, false (0) if SECMARK is disabled. If the always_check_network + * policy capability is enabled, SECMARK is always considered enabled. + * + */ +static int selinux_secmark_enabled(void) +{ + return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); +} -/* Allocate and free functions for each kind of security blob. */ +/** + * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled + * + * Description: + * This function checks if NetLabel or labeled IPSEC is enabled. Returns true + * (1) if any are enabled or false (0) if neither are enabled. If the + * always_check_network policy capability is enabled, peer labeling + * is always considered enabled. + * + */ +static int selinux_peerlbl_enabled(void) +{ + return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); +} -static int task_alloc_security(struct task_struct *task) +/* + * initialise the security for the init task + */ +static void cred_init_security(void) { + struct cred *cred = (struct cred *) current->real_cred; struct task_security_struct *tsec; tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL); if (!tsec) - return -ENOMEM; + panic("SELinux: Failed to initialize initial task.\n"); - tsec->magic = SELINUX_MAGIC; - tsec->task = task; - tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED; - task->security = tsec; + tsec->osid = tsec->sid = SECINITSID_KERNEL; + cred->security = tsec; +} - return 0; +/* + * get the security ID of a set of credentials + */ +static inline u32 cred_sid(const struct cred *cred) +{ + const struct task_security_struct *tsec; + + tsec = cred->security; + return tsec->sid; } -static void task_free_security(struct task_struct *task) +/* + * get the objective security ID of a task + */ +static inline u32 task_sid(const struct task_struct *task) { - struct task_security_struct *tsec = task->security; + u32 sid; - if (!tsec || tsec->magic != SELINUX_MAGIC) - return; + rcu_read_lock(); + sid = cred_sid(__task_cred(task)); + rcu_read_unlock(); + return sid; +} - task->security = NULL; - kfree(tsec); +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ + const struct task_security_struct *tsec = current_security(); + + return tsec->sid; } +/* Allocate and free functions for each kind of security blob. */ + static int inode_alloc_security(struct inode *inode) { - struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; + u32 sid = current_sid(); - isec = kzalloc(sizeof(struct inode_security_struct), GFP_KERNEL); + isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS); if (!isec) return -ENOMEM; - init_MUTEX(&isec->sem); + mutex_init(&isec->lock); 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; + isec->task_sid = sid; inode->i_security = isec; return 0; } +static void inode_free_rcu(struct rcu_head *head) +{ + struct inode_security_struct *isec; + + isec = container_of(head, struct inode_security_struct, rcu); + kmem_cache_free(sel_inode_cache, isec); +} + static void inode_free_security(struct inode *inode) { struct inode_security_struct *isec = inode->i_security; struct superblock_security_struct *sbsec = inode->i_sb->s_security; - if (!isec || isec->magic != SELINUX_MAGIC) - return; - spin_lock(&sbsec->isec_lock); if (!list_empty(&isec->list)) list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); - inode->i_security = NULL; - kfree(isec); + /* + * The inode may still be referenced in a path walk and + * a call to selinux_inode_permission() can be made + * after inode_free_security() is called. Ideally, the VFS + * wouldn't do this, but fixing that is a much harder + * job. For now, simply free the i_security via RCU, and + * leave the current inode->i_security pointer intact. + * The inode will be freed after the RCU grace period too. + */ + call_rcu(&isec->rcu, inode_free_rcu); } static int file_alloc_security(struct file *file) { - struct task_security_struct *tsec = current->security; struct file_security_struct *fsec; + u32 sid = current_sid(); - fsec = kzalloc(sizeof(struct file_security_struct), GFP_ATOMIC); + fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL); if (!fsec) return -ENOMEM; - 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; - } + fsec->sid = sid; + fsec->fown_sid = sid; file->f_security = fsec; return 0; @@ -213,10 +282,6 @@ static int file_alloc_security(struct file *file) 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); } @@ -229,14 +294,13 @@ static int superblock_alloc_security(struct super_block *sb) if (!sbsec) return -ENOMEM; - init_MUTEX(&sbsec->sem); - INIT_LIST_HEAD(&sbsec->list); + mutex_init(&sbsec->lock); INIT_LIST_HEAD(&sbsec->isec_head); spin_lock_init(&sbsec->isec_lock); - sbsec->magic = SELINUX_MAGIC; sbsec->sb = sb; sbsec->sid = SECINITSID_UNLABELED; sbsec->def_sid = SECINITSID_FILE; + sbsec->mntpoint_sid = SECINITSID_UNLABELED; sb->s_security = sbsec; return 0; @@ -245,64 +309,20 @@ static int superblock_alloc_security(struct super_block *sb) 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); } -#ifdef CONFIG_SECURITY_NETWORK -static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) -{ - struct sk_security_struct *ssec; - - if (family != PF_UNIX) - return 0; - - ssec = kzalloc(sizeof(*ssec), priority); - if (!ssec) - return -ENOMEM; - - ssec->magic = SELINUX_MAGIC; - ssec->sk = sk; - ssec->peer_sid = SECINITSID_UNLABELED; - sk->sk_security = ssec; - - return 0; -} - -static void sk_free_security(struct sock *sk) -{ - struct sk_security_struct *ssec = sk->sk_security; - - if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC) - return; - - sk->sk_security = NULL; - kfree(ssec); -} -#endif /* CONFIG_SECURITY_NETWORK */ - -/* 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[6] = { +static const char *labeling_behaviors[7] = { "uses xattr", "uses transition SIDs", "uses task SIDs", "uses genfs_contexts", "not configured for labeling", "uses mountpoint labeling", + "uses native labeling", }; static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); @@ -313,237 +333,104 @@ static inline int inode_doinit(struct inode *inode) } enum { + Opt_error = -1, Opt_context = 1, Opt_fscontext = 2, - Opt_defcontext = 4, + Opt_defcontext = 3, + Opt_rootcontext = 4, + Opt_labelsupport = 5, + Opt_nextmntopt = 6, }; -static match_table_t tokens = { - {Opt_context, "context=%s"}, - {Opt_fscontext, "fscontext=%s"}, - {Opt_defcontext, "defcontext=%s"}, +#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1) + +static const match_table_t tokens = { + {Opt_context, CONTEXT_STR "%s"}, + {Opt_fscontext, FSCONTEXT_STR "%s"}, + {Opt_defcontext, DEFCONTEXT_STR "%s"}, + {Opt_rootcontext, ROOTCONTEXT_STR "%s"}, + {Opt_labelsupport, LABELSUPP_STR}, + {Opt_error, NULL}, }; #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" -static int try_context_mount(struct super_block *sb, void *data) +static int may_context_mount_sb_relabel(u32 sid, + struct superblock_security_struct *sbsec, + const struct cred *cred) { - char *context = NULL, *defcontext = NULL; - const char *name; - u32 sid; - int alloc = 0, rc = 0, seen = 0; - struct task_security_struct *tsec = current->security; - struct superblock_security_struct *sbsec = sb->s_security; - - if (!data) - goto out; - - name = sb->s_type->name; - - if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { - - /* NFS we understand. */ - if (!strcmp(name, "nfs")) { - struct nfs_mount_data *d = data; - - if (d->version < NFS_MOUNT_VERSION) - goto out; - - if (d->context[0]) { - context = d->context; - seen |= Opt_context; - } - } else - goto out; - - } else { - /* Standard string-based options. */ - char *p, *options = data; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - substring_t args[MAX_OPT_ARGS]; - - if (!*p) - continue; - - token = match_token(p, tokens, args); - - switch (token) { - case Opt_context: - if (seen) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - context = match_strdup(&args[0]); - if (!context) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_context; - break; - - case Opt_fscontext: - if (seen & (Opt_context|Opt_fscontext)) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - context = match_strdup(&args[0]); - if (!context) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_fscontext; - break; - - case Opt_defcontext: - if (sbsec->behavior != SECURITY_FS_USE_XATTR) { - rc = -EINVAL; - printk(KERN_WARNING "SELinux: " - "defcontext option is invalid " - "for this filesystem type\n"); - goto out_free; - } - if (seen & (Opt_context|Opt_defcontext)) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - defcontext = match_strdup(&args[0]); - if (!defcontext) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_defcontext; - break; - - default: - rc = -EINVAL; - printk(KERN_WARNING "SELinux: unknown mount " - "option\n"); - goto out_free; - - } - } - } - - if (!seen) - goto out; - - if (context) { - rc = security_context_to_sid(context, strlen(context), &sid); - if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" - "(%s) failed for (dev %s, type %s) errno=%d\n", - context, sb->s_id, name, rc); - goto out_free; - } - - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); - if (rc) - goto out_free; + const struct task_security_struct *tsec = cred->security; + int rc; - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELTO, NULL); - if (rc) - goto out_free; + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL); + if (rc) + return rc; - sbsec->sid = sid; + rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELTO, NULL); + return rc; +} - if (seen & Opt_context) - sbsec->behavior = SECURITY_FS_USE_MNTPOINT; - } +static int may_context_mount_inode_relabel(u32 sid, + struct superblock_security_struct *sbsec, + const struct cred *cred) +{ + const struct task_security_struct *tsec = cred->security; + int rc; + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL); + if (rc) + return rc; - if (defcontext) { - rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); - if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" - "(%s) failed for (dev %s, type %s) errno=%d\n", - defcontext, sb->s_id, name, rc); - goto out_free; - } + rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__ASSOCIATE, NULL); + return rc; +} - if (sid == sbsec->def_sid) - goto out_free; +static int selinux_is_sblabel_mnt(struct super_block *sb) +{ + struct superblock_security_struct *sbsec = sb->s_security; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); - if (rc) - goto out_free; + if (sbsec->behavior == SECURITY_FS_USE_XATTR || + sbsec->behavior == SECURITY_FS_USE_TRANS || + sbsec->behavior == SECURITY_FS_USE_TASK) + return 1; - rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, NULL); - if (rc) - goto out_free; + /* Special handling for sysfs. Is genfs but also has setxattr handler*/ + if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) + return 1; - sbsec->def_sid = sid; - } + /* + * Special handling for rootfs. Is genfs but supports + * setting SELinux context on in-core inodes. + */ + if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) + return 1; -out_free: - if (alloc) { - kfree(context); - kfree(defcontext); - } -out: - return rc; + return 0; } -static int superblock_doinit(struct super_block *sb, void *data) +static int sb_finish_set_opts(struct super_block *sb) { struct superblock_security_struct *sbsec = sb->s_security; struct dentry *root = sb->s_root; - struct inode *inode = root->d_inode; + struct inode *root_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; - } - - rc = try_context_mount(sb, data); - if (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) { + if (!root_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); + rc = root_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 " @@ -557,23 +444,20 @@ static int superblock_doinit(struct super_block *sb, void *data) } } - if (strcmp(sb->s_type->name, "proc") == 0) - sbsec->proc = 1; - - sbsec->initialized = 1; - - if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) { - printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n", + if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) + printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n", sb->s_id, sb->s_type->name); - } - else { - printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n", + else + printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n", sb->s_id, sb->s_type->name, labeling_behaviors[sbsec->behavior-1]); - } + + sbsec->flags |= SE_SBINITIALIZED; + if (selinux_is_sblabel_mnt(sb)) + sbsec->flags |= SBLABEL_MNT; /* Initialize the root inode. */ - rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root); + rc = inode_doinit_with_dentry(root_inode, root); /* Initialize any other inodes associated with the superblock, e.g. inodes created prior to initial policy load or inodes created @@ -584,12 +468,12 @@ next_inode: if (!list_empty(&sbsec->isec_head)) { struct inode_security_struct *isec = list_entry(sbsec->isec_head.next, - struct inode_security_struct, list); + struct inode_security_struct, list); struct inode *inode = isec->inode; spin_unlock(&sbsec->isec_lock); inode = igrab(inode); if (inode) { - if (!IS_PRIVATE (inode)) + if (!IS_PRIVATE(inode)) inode_doinit(inode); iput(inode); } @@ -599,7 +483,645 @@ next_inode: } spin_unlock(&sbsec->isec_lock); out: - up(&sbsec->sem); + return rc; +} + +/* + * This function should allow an FS to ask what it's mount security + * options were so it can use those later for submounts, displaying + * mount options, or whatever. + */ +static int selinux_get_mnt_opts(const struct super_block *sb, + struct security_mnt_opts *opts) +{ + int rc = 0, i; + struct superblock_security_struct *sbsec = sb->s_security; + char *context = NULL; + u32 len; + char tmp; + + security_init_mnt_opts(opts); + + if (!(sbsec->flags & SE_SBINITIALIZED)) + return -EINVAL; + + if (!ss_initialized) + return -EINVAL; + + /* make sure we always check enough bits to cover the mask */ + BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS)); + + tmp = sbsec->flags & SE_MNTMASK; + /* count the number of mount options for this sb */ + for (i = 0; i < NUM_SEL_MNT_OPTS; i++) { + if (tmp & 0x01) + opts->num_mnt_opts++; + tmp >>= 1; + } + /* Check if the Label support flag is set */ + if (sbsec->flags & SBLABEL_MNT) + opts->num_mnt_opts++; + + opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC); + if (!opts->mnt_opts) { + rc = -ENOMEM; + goto out_free; + } + + opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC); + if (!opts->mnt_opts_flags) { + rc = -ENOMEM; + goto out_free; + } + + i = 0; + if (sbsec->flags & FSCONTEXT_MNT) { + rc = security_sid_to_context(sbsec->sid, &context, &len); + if (rc) + goto out_free; + opts->mnt_opts[i] = context; + opts->mnt_opts_flags[i++] = FSCONTEXT_MNT; + } + if (sbsec->flags & CONTEXT_MNT) { + rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); + if (rc) + goto out_free; + opts->mnt_opts[i] = context; + opts->mnt_opts_flags[i++] = CONTEXT_MNT; + } + if (sbsec->flags & DEFCONTEXT_MNT) { + rc = security_sid_to_context(sbsec->def_sid, &context, &len); + if (rc) + goto out_free; + opts->mnt_opts[i] = context; + opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT; + } + if (sbsec->flags & ROOTCONTEXT_MNT) { + struct inode *root = sbsec->sb->s_root->d_inode; + struct inode_security_struct *isec = root->i_security; + + rc = security_sid_to_context(isec->sid, &context, &len); + if (rc) + goto out_free; + opts->mnt_opts[i] = context; + opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT; + } + if (sbsec->flags & SBLABEL_MNT) { + opts->mnt_opts[i] = NULL; + opts->mnt_opts_flags[i++] = SBLABEL_MNT; + } + + BUG_ON(i != opts->num_mnt_opts); + + return 0; + +out_free: + security_free_mnt_opts(opts); + return rc; +} + +static int bad_option(struct superblock_security_struct *sbsec, char flag, + u32 old_sid, u32 new_sid) +{ + char mnt_flags = sbsec->flags & SE_MNTMASK; + + /* check if the old mount command had the same options */ + if (sbsec->flags & SE_SBINITIALIZED) + if (!(sbsec->flags & flag) || + (old_sid != new_sid)) + return 1; + + /* check if we were passed the same options twice, + * aka someone passed context=a,context=b + */ + if (!(sbsec->flags & SE_SBINITIALIZED)) + if (mnt_flags & flag) + return 1; + return 0; +} + +/* + * Allow filesystems with binary mount data to explicitly set mount point + * labeling information. + */ +static int selinux_set_mnt_opts(struct super_block *sb, + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) +{ + const struct cred *cred = current_cred(); + int rc = 0, i; + struct superblock_security_struct *sbsec = sb->s_security; + const char *name = sb->s_type->name; + struct inode *inode = sbsec->sb->s_root->d_inode; + struct inode_security_struct *root_isec = inode->i_security; + u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; + u32 defcontext_sid = 0; + char **mount_options = opts->mnt_opts; + int *flags = opts->mnt_opts_flags; + int num_opts = opts->num_mnt_opts; + + mutex_lock(&sbsec->lock); + + if (!ss_initialized) { + if (!num_opts) { + /* Defer initialization until selinux_complete_init, + after the initial policy is loaded and the security + server is ready to handle calls. */ + goto out; + } + rc = -EINVAL; + printk(KERN_WARNING "SELinux: Unable to set superblock options " + "before the security server is initialized\n"); + goto out; + } + if (kern_flags && !set_kern_flags) { + /* Specifying internal flags without providing a place to + * place the results is not allowed */ + rc = -EINVAL; + goto out; + } + + /* + * Binary mount data FS will come through this function twice. Once + * from an explicit call and once from the generic calls from the vfs. + * Since the generic VFS calls will not contain any security mount data + * we need to skip the double mount verification. + * + * This does open a hole in which we will not notice if the first + * mount using this sb set explict options and a second mount using + * this sb does not set any security options. (The first options + * will be used for both mounts) + */ + if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) + && (num_opts == 0)) + goto out; + + /* + * parse the mount options, check if they are valid sids. + * also check if someone is trying to mount the same sb more + * than once with different security options. + */ + for (i = 0; i < num_opts; i++) { + u32 sid; + + if (flags[i] == SBLABEL_MNT) + continue; + rc = security_context_to_sid(mount_options[i], + strlen(mount_options[i]), &sid, GFP_KERNEL); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + mount_options[i], sb->s_id, name, rc); + goto out; + } + switch (flags[i]) { + case FSCONTEXT_MNT: + fscontext_sid = sid; + + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, + fscontext_sid)) + goto out_double_mount; + + sbsec->flags |= FSCONTEXT_MNT; + break; + case CONTEXT_MNT: + context_sid = sid; + + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, + context_sid)) + goto out_double_mount; + + sbsec->flags |= CONTEXT_MNT; + break; + case ROOTCONTEXT_MNT: + rootcontext_sid = sid; + + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, + rootcontext_sid)) + goto out_double_mount; + + sbsec->flags |= ROOTCONTEXT_MNT; + + break; + case DEFCONTEXT_MNT: + defcontext_sid = sid; + + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, + defcontext_sid)) + goto out_double_mount; + + sbsec->flags |= DEFCONTEXT_MNT; + + break; + default: + rc = -EINVAL; + goto out; + } + } + + if (sbsec->flags & SE_SBINITIALIZED) { + /* previously mounted with options, but not on this attempt? */ + if ((sbsec->flags & SE_MNTMASK) && !num_opts) + goto out_double_mount; + rc = 0; + goto out; + } + + if (strcmp(sb->s_type->name, "proc") == 0) + sbsec->flags |= SE_SBPROC; + + if (!sbsec->behavior) { + /* + * Determine the labeling behavior to use for this + * filesystem type. + */ + rc = security_fs_use(sb); + if (rc) { + printk(KERN_WARNING + "%s: security_fs_use(%s) returned %d\n", + __func__, sb->s_type->name, rc); + goto out; + } + } + /* sets the context of the superblock for the fs being mounted. */ + if (fscontext_sid) { + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred); + if (rc) + goto out; + + sbsec->sid = fscontext_sid; + } + + /* + * Switch to using mount point labeling behavior. + * sets the label used on all file below the mountpoint, and will set + * the superblock context if not already set. + */ + if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { + sbsec->behavior = SECURITY_FS_USE_NATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } + + if (context_sid) { + if (!fscontext_sid) { + rc = may_context_mount_sb_relabel(context_sid, sbsec, + cred); + if (rc) + goto out; + sbsec->sid = context_sid; + } else { + rc = may_context_mount_inode_relabel(context_sid, sbsec, + cred); + if (rc) + goto out; + } + if (!rootcontext_sid) + rootcontext_sid = context_sid; + + sbsec->mntpoint_sid = context_sid; + sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + } + + if (rootcontext_sid) { + rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, + cred); + if (rc) + goto out; + + root_isec->sid = rootcontext_sid; + root_isec->initialized = 1; + } + + if (defcontext_sid) { + if (sbsec->behavior != SECURITY_FS_USE_XATTR && + sbsec->behavior != SECURITY_FS_USE_NATIVE) { + rc = -EINVAL; + printk(KERN_WARNING "SELinux: defcontext option is " + "invalid for this filesystem type\n"); + goto out; + } + + if (defcontext_sid != sbsec->def_sid) { + rc = may_context_mount_inode_relabel(defcontext_sid, + sbsec, cred); + if (rc) + goto out; + } + + sbsec->def_sid = defcontext_sid; + } + + rc = sb_finish_set_opts(sb); +out: + mutex_unlock(&sbsec->lock); + return rc; +out_double_mount: + rc = -EINVAL; + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different " + "security settings for (dev %s, type %s)\n", sb->s_id, name); + goto out; +} + +static int selinux_cmp_sb_context(const struct super_block *oldsb, + const struct super_block *newsb) +{ + struct superblock_security_struct *old = oldsb->s_security; + struct superblock_security_struct *new = newsb->s_security; + char oldflags = old->flags & SE_MNTMASK; + char newflags = new->flags & SE_MNTMASK; + + if (oldflags != newflags) + goto mismatch; + if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid) + goto mismatch; + if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid) + goto mismatch; + if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) + goto mismatch; + if (oldflags & ROOTCONTEXT_MNT) { + struct inode_security_struct *oldroot = oldsb->s_root->d_inode->i_security; + struct inode_security_struct *newroot = newsb->s_root->d_inode->i_security; + if (oldroot->sid != newroot->sid) + goto mismatch; + } + return 0; +mismatch: + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, " + "different security settings for (dev %s, " + "type %s)\n", newsb->s_id, newsb->s_type->name); + return -EBUSY; +} + +static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, + struct super_block *newsb) +{ + const struct superblock_security_struct *oldsbsec = oldsb->s_security; + struct superblock_security_struct *newsbsec = newsb->s_security; + + int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT); + int set_context = (oldsbsec->flags & CONTEXT_MNT); + int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT); + + /* + * if the parent was able to be mounted it clearly had no special lsm + * mount options. thus we can safely deal with this superblock later + */ + if (!ss_initialized) + return 0; + + /* how can we clone if the old one wasn't set up?? */ + BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); + + /* if fs is reusing a sb, make sure that the contexts match */ + if (newsbsec->flags & SE_SBINITIALIZED) + return selinux_cmp_sb_context(oldsb, newsb); + + mutex_lock(&newsbsec->lock); + + newsbsec->flags = oldsbsec->flags; + + newsbsec->sid = oldsbsec->sid; + newsbsec->def_sid = oldsbsec->def_sid; + newsbsec->behavior = oldsbsec->behavior; + + if (set_context) { + u32 sid = oldsbsec->mntpoint_sid; + + if (!set_fscontext) + newsbsec->sid = sid; + if (!set_rootcontext) { + struct inode *newinode = newsb->s_root->d_inode; + struct inode_security_struct *newisec = newinode->i_security; + newisec->sid = sid; + } + newsbsec->mntpoint_sid = sid; + } + if (set_rootcontext) { + const struct inode *oldinode = oldsb->s_root->d_inode; + const struct inode_security_struct *oldisec = oldinode->i_security; + struct inode *newinode = newsb->s_root->d_inode; + struct inode_security_struct *newisec = newinode->i_security; + + newisec->sid = oldisec->sid; + } + + sb_finish_set_opts(newsb); + mutex_unlock(&newsbsec->lock); + return 0; +} + +static int selinux_parse_opts_str(char *options, + struct security_mnt_opts *opts) +{ + char *p; + char *context = NULL, *defcontext = NULL; + char *fscontext = NULL, *rootcontext = NULL; + int rc, num_mnt_opts = 0; + + opts->num_mnt_opts = 0; + + /* Standard string-based options. */ + while ((p = strsep(&options, "|")) != NULL) { + int token; + substring_t args[MAX_OPT_ARGS]; + + if (!*p) + continue; + + token = match_token(p, tokens, args); + + switch (token) { + case Opt_context: + if (context || defcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + context = match_strdup(&args[0]); + if (!context) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_fscontext: + if (fscontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + fscontext = match_strdup(&args[0]); + if (!fscontext) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_rootcontext: + if (rootcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + rootcontext = match_strdup(&args[0]); + if (!rootcontext) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_defcontext: + if (context || defcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + defcontext = match_strdup(&args[0]); + if (!defcontext) { + rc = -ENOMEM; + goto out_err; + } + break; + case Opt_labelsupport: + break; + default: + rc = -EINVAL; + printk(KERN_WARNING "SELinux: unknown mount option\n"); + goto out_err; + + } + } + + rc = -ENOMEM; + opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC); + if (!opts->mnt_opts) + goto out_err; + + opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC); + if (!opts->mnt_opts_flags) { + kfree(opts->mnt_opts); + goto out_err; + } + + if (fscontext) { + opts->mnt_opts[num_mnt_opts] = fscontext; + opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT; + } + if (context) { + opts->mnt_opts[num_mnt_opts] = context; + opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT; + } + if (rootcontext) { + opts->mnt_opts[num_mnt_opts] = rootcontext; + opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT; + } + if (defcontext) { + opts->mnt_opts[num_mnt_opts] = defcontext; + opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT; + } + + opts->num_mnt_opts = num_mnt_opts; + return 0; + +out_err: + kfree(context); + kfree(defcontext); + kfree(fscontext); + kfree(rootcontext); + return rc; +} +/* + * string mount options parsing and call set the sbsec + */ +static int superblock_doinit(struct super_block *sb, void *data) +{ + int rc = 0; + char *options = data; + struct security_mnt_opts opts; + + security_init_mnt_opts(&opts); + + if (!data) + goto out; + + BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA); + + rc = selinux_parse_opts_str(options, &opts); + if (rc) + goto out_err; + +out: + rc = selinux_set_mnt_opts(sb, &opts, 0, NULL); + +out_err: + security_free_mnt_opts(&opts); + return rc; +} + +static void selinux_write_opts(struct seq_file *m, + struct security_mnt_opts *opts) +{ + int i; + char *prefix; + + for (i = 0; i < opts->num_mnt_opts; i++) { + char *has_comma; + + if (opts->mnt_opts[i]) + has_comma = strchr(opts->mnt_opts[i], ','); + else + has_comma = NULL; + + switch (opts->mnt_opts_flags[i]) { + case CONTEXT_MNT: + prefix = CONTEXT_STR; + break; + case FSCONTEXT_MNT: + prefix = FSCONTEXT_STR; + break; + case ROOTCONTEXT_MNT: + prefix = ROOTCONTEXT_STR; + break; + case DEFCONTEXT_MNT: + prefix = DEFCONTEXT_STR; + break; + case SBLABEL_MNT: + seq_putc(m, ','); + seq_puts(m, LABELSUPP_STR); + continue; + default: + BUG(); + return; + }; + /* we need a comma before each option */ + seq_putc(m, ','); + seq_puts(m, prefix); + if (has_comma) + seq_putc(m, '\"'); + seq_puts(m, opts->mnt_opts[i]); + if (has_comma) + seq_putc(m, '\"'); + } +} + +static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) +{ + struct security_mnt_opts opts; + int rc; + + rc = selinux_get_mnt_opts(sb, &opts); + if (rc) { + /* before policy load we may get EINVAL, don't show anything */ + if (rc == -EINVAL) + rc = 0; + return rc; + } + + selinux_write_opts(m, &opts); + + security_free_mnt_opts(&opts); + return rc; } @@ -661,6 +1183,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_UDP_SOCKET; else return SECCLASS_RAWIP_SOCKET; + case SOCK_DCCP: + return SECCLASS_DCCP_SOCKET; default: return SECCLASS_RAWIP_SOCKET; } @@ -671,7 +1195,7 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_NETLINK_ROUTE_SOCKET; case NETLINK_FIREWALL: return SECCLASS_NETLINK_FIREWALL_SOCKET; - case NETLINK_INET_DIAG: + case NETLINK_SOCK_DIAG: return SECCLASS_NETLINK_TCPDIAG_SOCKET; case NETLINK_NFLOG: return SECCLASS_NETLINK_NFLOG_SOCKET; @@ -694,45 +1218,43 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_PACKET_SOCKET; case PF_KEY: return SECCLASS_KEY_SOCKET; + case PF_APPLETALK: + return SECCLASS_APPLETALK_SOCKET; } return SECCLASS_SOCKET; } #ifdef CONFIG_PROC_FS -static int selinux_proc_get_sid(struct proc_dir_entry *de, +static int selinux_proc_get_sid(struct dentry *dentry, u16 tclass, u32 *sid) { - int buflen, rc; - char *buffer, *path, *end; + int rc; + char *buffer, *path; - buffer = (char*)__get_free_page(GFP_KERNEL); + 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; + path = dentry_path_raw(dentry, buffer, PAGE_SIZE); + if (IS_ERR(path)) + rc = PTR_ERR(path); + else { + /* each process gets a /proc/PID/ entry. Strip off the + * PID part to get a valid selinux labeling. + * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ + while (path[1] >= '0' && path[1] <= '9') { + path[1] = '/'; + path++; + } + rc = security_genfs_sid("proc", path, tclass, sid); } - 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, +static int selinux_proc_get_sid(struct dentry *dentry, u16 tclass, u32 *sid) { @@ -751,18 +1273,16 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent char *context = NULL; unsigned len = 0; int rc = 0; - int hold_sem = 0; if (isec->initialized) goto out; - down(&isec->sem); - hold_sem = 1; + mutex_lock(&isec->lock); if (isec->initialized) - goto out; + goto out_unlock; sbsec = inode->i_sb->s_security; - if (!sbsec->initialized) { + if (!(sbsec->flags & SE_SBINITIALIZED)) { /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security server is ready to handle calls. */ @@ -770,10 +1290,12 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent if (list_empty(&isec->list)) list_add(&isec->list, &sbsec->isec_head); spin_unlock(&sbsec->isec_lock); - goto out; + goto out_unlock; } switch (sbsec->behavior) { + case SECURITY_FS_USE_NATIVE: + break; case SECURITY_FS_USE_XATTR: if (!inode->i_op->getxattr) { isec->sid = sbsec->def_sid; @@ -790,37 +1312,46 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent 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; + /* + * this is can be hit on boot when a file is accessed + * before the policy is loaded. When we load policy we + * may find inodes that have no dentry on the + * sbsec->isec_head list. No reason to complain as these + * will get fixed up the next time we go through + * inode_doinit with a dentry, before these inodes could + * be used again by userspace. + */ + goto out_unlock; } len = INITCONTEXTLEN; - context = kmalloc(len, GFP_KERNEL); + context = kmalloc(len+1, GFP_NOFS); if (!context) { rc = -ENOMEM; dput(dentry); - goto out; + goto out_unlock; } + context[len] = '\0'; rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, context, len); if (rc == -ERANGE) { + kfree(context); + /* 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; + goto out_unlock; } - kfree(context); len = rc; - context = kmalloc(len, GFP_KERNEL); + context = kmalloc(len+1, GFP_NOFS); if (!context) { rc = -ENOMEM; dput(dentry); - goto out; + goto out_unlock; } + context[len] = '\0'; rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, context, len); @@ -828,23 +1359,33 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent dput(dentry); if (rc < 0) { if (rc != -ENODATA) { - printk(KERN_WARNING "%s: getxattr returned " - "%d for dev=%s ino=%ld\n", __FUNCTION__, + printk(KERN_WARNING "SELinux: %s: getxattr returned " + "%d for dev=%s ino=%ld\n", __func__, -rc, inode->i_sb->s_id, inode->i_ino); kfree(context); - goto out; + goto out_unlock; } /* Map ENODATA to the default file SID */ sid = sbsec->def_sid; rc = 0; } else { rc = security_context_to_sid_default(context, rc, &sid, - sbsec->def_sid); + sbsec->def_sid, + GFP_NOFS); 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); + char *dev = inode->i_sb->s_id; + unsigned long ino = inode->i_ino; + + if (rc == -EINVAL) { + if (printk_ratelimit()) + printk(KERN_NOTICE "SELinux: inode=%lu on dev=%s was found to have an invalid " + "context=%s. This indicates you may need to relabel the inode or the " + "filesystem in question.\n", ino, dev, context); + } else { + printk(KERN_WARNING "SELinux: %s: context_to_sid(%s) " + "returned %d for dev=%s ino=%ld\n", + __func__, context, -rc, dev, ino); + } kfree(context); /* Leave with the unlabeled SID */ rc = 0; @@ -863,41 +1404,58 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent /* 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); + rc = security_transition_sid(isec->task_sid, sbsec->sid, + isec->sclass, NULL, &sid); if (rc) - goto out; + goto out_unlock; isec->sid = sid; break; + case SECURITY_FS_USE_MNTPOINT: + isec->sid = sbsec->mntpoint_sid; + break; default: - /* Default to the fs SID. */ + /* Default to the fs superblock 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; - } + if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { + /* We must have a dentry to determine the label on + * procfs inodes */ + 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); + /* + * This can be hit on boot when a file is accessed + * before the policy is loaded. When we load policy we + * may find inodes that have no dentry on the + * sbsec->isec_head list. No reason to complain as + * these will get fixed up the next time we go through + * inode_doinit() with a dentry, before these inodes + * could be used again by userspace. + */ + if (!dentry) + goto out_unlock; + isec->sclass = inode_mode_to_security_class(inode->i_mode); + rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); + dput(dentry); + if (rc) + goto out_unlock; + isec->sid = sid; } break; } isec->initialized = 1; +out_unlock: + mutex_unlock(&isec->lock); out: if (isec->sclass == SECCLASS_FILE) isec->sclass = inode_mode_to_security_class(inode->i_mode); - - if (hold_sem) - up(&isec->sem); return rc; } @@ -928,87 +1486,168 @@ static inline u32 signal_to_av(int sig) return perm; } -/* Check permission betweeen a pair of tasks, e.g. signal checks, - fork check, ptrace check, etc. */ -static int task_has_perm(struct task_struct *tsk1, - struct task_struct *tsk2, +/* + * Check permission between a pair of credentials + * fork check, ptrace check, etc. + */ +static int cred_has_perm(const struct cred *actor, + const struct cred *target, + u32 perms) +{ + u32 asid = cred_sid(actor), tsid = cred_sid(target); + + return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL); +} + +/* + * Check permission between a pair of tasks, e.g. signal checks, + * fork check, ptrace check, etc. + * tsk1 is the actor and tsk2 is the target + * - this uses the default subjective creds of tsk1 + */ +static int task_has_perm(const struct task_struct *tsk1, + const struct task_struct *tsk2, u32 perms) { - struct task_security_struct *tsec1, *tsec2; + const struct task_security_struct *__tsec1, *__tsec2; + u32 sid1, sid2; - tsec1 = tsk1->security; - tsec2 = tsk2->security; - return avc_has_perm(tsec1->sid, tsec2->sid, - SECCLASS_PROCESS, perms, NULL); + rcu_read_lock(); + __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid; + __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid; + rcu_read_unlock(); + return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL); } -/* Check whether a task is allowed to use a capability. */ -static int task_has_capability(struct task_struct *tsk, - int cap) +/* + * Check permission between current and another task, e.g. signal checks, + * fork check, ptrace check, etc. + * current is the actor and tsk2 is the target + * - this uses current's subjective creds + */ +static int current_has_perm(const struct task_struct *tsk, + u32 perms) { - struct task_security_struct *tsec; - struct avc_audit_data ad; + u32 sid, tsid; + + sid = current_sid(); + tsid = task_sid(tsk); + return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL); +} + +#if CAP_LAST_CAP > 63 +#error Fix SELinux to handle capabilities > 63. +#endif - tsec = tsk->security; +/* Check whether a task is allowed to use a capability. */ +static int cred_has_capability(const struct cred *cred, + int cap, int audit) +{ + struct common_audit_data ad; + struct av_decision avd; + u16 sclass; + u32 sid = cred_sid(cred); + u32 av = CAP_TO_MASK(cap); + int rc; - AVC_AUDIT_DATA_INIT(&ad,CAP); - ad.tsk = tsk; + ad.type = LSM_AUDIT_DATA_CAP; ad.u.cap = cap; - return avc_has_perm(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad); + switch (CAP_TO_INDEX(cap)) { + case 0: + sclass = SECCLASS_CAPABILITY; + break; + case 1: + sclass = SECCLASS_CAPABILITY2; + break; + default: + printk(KERN_ERR + "SELinux: out of range capability %d\n", cap); + BUG(); + return -EINVAL; + } + + rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); + if (audit == SECURITY_CAP_AUDIT) { + int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); + if (rc2) + return rc2; + } + return rc; } /* Check whether a task is allowed to use a system operation. */ static int task_has_system(struct task_struct *tsk, u32 perms) { - struct task_security_struct *tsec; - - tsec = tsk->security; + u32 sid = task_sid(tsk); - return avc_has_perm(tsec->sid, SECINITSID_KERNEL, + return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, perms, NULL); } /* Check whether a task has a particular permission to an inode. The 'adp' parameter is optional and allows other audit data to be passed (e.g. the dentry). */ -static int inode_has_perm(struct task_struct *tsk, +static int inode_has_perm(const struct cred *cred, struct inode *inode, u32 perms, - struct avc_audit_data *adp) + struct common_audit_data *adp) { - struct task_security_struct *tsec; struct inode_security_struct *isec; - struct avc_audit_data ad; + u32 sid; - tsec = tsk->security; - isec = inode->i_security; + validate_creds(cred); - if (!adp) { - adp = &ad; - AVC_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.inode = inode; - } + if (unlikely(IS_PRIVATE(inode))) + return 0; + + sid = cred_sid(cred); + isec = inode->i_security; - return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp); + return avc_has_perm(sid, isec->sid, isec->sclass, perms, 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, +static inline int dentry_has_perm(const struct cred *cred, 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, &ad); + struct common_audit_data ad; + + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry; + return inode_has_perm(cred, inode, av, &ad); +} + +/* Same as inode_has_perm, but pass explicit audit data containing + the path to help the auditing code to more easily generate the + pathname if needed. */ +static inline int path_has_perm(const struct cred *cred, + struct path *path, + u32 av) +{ + struct inode *inode = path->dentry->d_inode; + struct common_audit_data ad; + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = *path; + return inode_has_perm(cred, inode, av, &ad); +} + +/* Same as path_has_perm, but uses the inode from the file struct. */ +static inline int file_path_has_perm(const struct cred *cred, + struct file *file, + u32 av) +{ + struct common_audit_data ad; + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + return inode_has_perm(cred, file_inode(file), av, &ad); } /* Check whether a task can use an open file descriptor to @@ -1019,36 +1658,35 @@ static inline int dentry_has_perm(struct task_struct *tsk, 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 int file_has_perm(struct task_struct *tsk, - struct file *file, - u32 av) +static int file_has_perm(const struct cred *cred, + 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; + struct inode *inode = file_inode(file); + struct common_audit_data ad; + u32 sid = cred_sid(cred); int rc; - AVC_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.mnt = mnt; - ad.u.fs.dentry = dentry; + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; - if (tsec->sid != fsec->sid) { - rc = avc_has_perm(tsec->sid, fsec->sid, + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); if (rc) - return rc; + goto out; } /* av is zero if only checking access to the descriptor. */ + rc = 0; if (av) - return inode_has_perm(tsk, inode, av, &ad); + rc = inode_has_perm(cred, inode, av, &ad); - return 0; +out: + return rc; } /* Check whether a task can create a file. */ @@ -1056,36 +1694,36 @@ static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass) { - struct task_security_struct *tsec; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; - u32 newsid; - struct avc_audit_data ad; + u32 sid, newsid; + struct common_audit_data ad; int rc; - tsec = current->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; - AVC_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.dentry = dentry; + sid = tsec->sid; + newsid = tsec->create_sid; + + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry; - rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, &ad); if (rc) return rc; - if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { - newsid = tsec->create_sid; - } else { - rc = security_transition_sid(tsec->sid, dsec->sid, tclass, - &newsid); + if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { + rc = security_transition_sid(sid, dsec->sid, tclass, + &dentry->d_name, &newsid); if (rc) return rc; } - rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad); + rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); if (rc) return rc; @@ -1094,9 +1732,18 @@ static int may_create(struct inode *dir, FILESYSTEM__ASSOCIATE, &ad); } -#define MAY_LINK 0 -#define MAY_UNLINK 1 -#define MAY_RMDIR 2 +/* Check whether a task can create a key. */ +static int may_create_key(u32 ksid, + struct task_struct *ctx) +{ + u32 sid = task_sid(ctx); + + return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); +} + +#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, @@ -1104,22 +1751,21 @@ static int may_link(struct inode *dir, int kind) { - struct task_security_struct *tsec; struct inode_security_struct *dsec, *isec; - struct avc_audit_data ad; + struct common_audit_data ad; + u32 sid = current_sid(); 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; + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry; av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); - rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; @@ -1134,11 +1780,12 @@ static int may_link(struct inode *dir, av = DIR__RMDIR; break; default: - printk(KERN_WARNING "may_link: unrecognized kind %d\n", kind); + printk(KERN_WARNING "SELinux: %s: unrecognized kind %d\n", + __func__, kind); return 0; } - rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad); + rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); return rc; } @@ -1147,48 +1794,47 @@ static inline int may_rename(struct inode *old_dir, 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; + struct common_audit_data ad; + u32 sid = current_sid(); 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.type = LSM_AUDIT_DATA_DENTRY; - ad.u.fs.dentry = old_dentry; - rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR, + ad.u.dentry = old_dentry; + rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) return rc; - rc = avc_has_perm(tsec->sid, old_isec->sid, + rc = avc_has_perm(sid, old_isec->sid, old_isec->sclass, FILE__RENAME, &ad); if (rc) return rc; if (old_is_dir && new_dir != old_dir) { - rc = avc_has_perm(tsec->sid, old_isec->sid, + rc = avc_has_perm(sid, old_isec->sid, old_isec->sclass, DIR__REPARENT, &ad); if (rc) return rc; } - ad.u.fs.dentry = new_dentry; + ad.u.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, &ad); + rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &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, + rc = avc_has_perm(sid, new_isec->sid, new_isec->sclass, (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); if (rc) @@ -1199,18 +1845,16 @@ static inline int may_rename(struct inode *old_dir, } /* Check whether a task can perform a filesystem operation. */ -static int superblock_has_perm(struct task_struct *tsk, +static int superblock_has_perm(const struct cred *cred, struct super_block *sb, u32 perms, - struct avc_audit_data *ad) + struct common_audit_data *ad) { - struct task_security_struct *tsec; struct superblock_security_struct *sbsec; + u32 sid = cred_sid(cred); - tsec = tsk->security; sbsec = sb->s_security; - return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - perms, ad); + return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); } /* Convert a Linux mode and permission mask to an access vector. */ @@ -1218,7 +1862,7 @@ static inline u32 file_mask_to_av(int mode, int mask) { u32 av = 0; - if ((mode & S_IFMT) != S_IFDIR) { + if (!S_ISDIR(mode)) { if (mask & MAY_EXEC) av |= FILE__EXECUTE; if (mask & MAY_READ) @@ -1254,191 +1898,168 @@ static inline u32 file_to_av(struct file *file) else av |= FILE__WRITE; } + if (!av) { + /* + * Special file opened with flags 3 for ioctl-only use. + */ + av = FILE__IOCTL; + } return av; } -/* Set an inode's SID to a specified value. */ -static int inode_security_set_sid(struct inode *inode, u32 sid) +/* + * Convert a file to an access vector and include the correct open + * open permission. + */ +static inline u32 open_file_to_av(struct file *file) { - struct inode_security_struct *isec = inode->i_security; - struct superblock_security_struct *sbsec = inode->i_sb->s_security; + u32 av = file_to_av(file); - if (!sbsec->initialized) { - /* Defer initialization to selinux_complete_init. */ - return 0; - } + if (selinux_policycap_openperm) + av |= FILE__OPEN; - down(&isec->sem); - isec->sclass = inode_mode_to_security_class(inode->i_mode); - isec->sid = sid; - isec->initialized = 1; - up(&isec->sem); - return 0; + return av; } /* Hook functions begin here. */ -static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) +static int selinux_ptrace_access_check(struct task_struct *child, + unsigned int mode) { - struct task_security_struct *psec = parent->security; - struct task_security_struct *csec = child->security; int rc; - rc = secondary_ops->ptrace(parent,child); + rc = cap_ptrace_access_check(child, mode); if (rc) return rc; - rc = task_has_perm(parent, child, PROCESS__PTRACE); - /* Save the SID of the tracing process for later use in apply_creds. */ - if (!rc) - csec->ptrace_sid = psec->sid; - return rc; + if (mode & PTRACE_MODE_READ) { + u32 sid = current_sid(); + u32 csid = task_sid(child); + return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); + } + + return current_has_perm(child, PROCESS__PTRACE); } -static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) +static int selinux_ptrace_traceme(struct task_struct *parent) { - int error; + int rc; - error = task_has_perm(current, target, PROCESS__GETCAP); - if (error) - return error; + rc = cap_ptrace_traceme(parent); + if (rc) + return rc; - return secondary_ops->capget(target, effective, inheritable, permitted); + return task_has_perm(parent, current, PROCESS__PTRACE); } -static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) +static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, + kernel_cap_t *inheritable, kernel_cap_t *permitted) { int error; - error = secondary_ops->capset_check(target, effective, inheritable, permitted); + error = current_has_perm(target, PROCESS__GETCAP); if (error) return error; - return task_has_perm(current, target, PROCESS__SETCAP); -} - -static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) -{ - secondary_ops->capset_set(target, effective, inheritable, permitted); + return cap_capget(target, effective, inheritable, permitted); } -static int selinux_capable(struct task_struct *tsk, int cap) +static int selinux_capset(struct cred *new, const struct cred *old, + const kernel_cap_t *effective, + const kernel_cap_t *inheritable, + const kernel_cap_t *permitted) { - int rc; + int error; - rc = secondary_ops->capable(tsk, cap); - if (rc) - return rc; + error = cap_capset(new, old, + effective, inheritable, permitted); + if (error) + return error; - return task_has_capability(tsk,cap); + return cred_has_perm(old, new, PROCESS__SETCAP); } -static int selinux_sysctl(ctl_table *table, int op) +/* + * (This comment used to live with the selinux_task_setuid hook, + * which was removed). + * + * 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. + */ + +static int selinux_capable(const struct cred *cred, struct user_namespace *ns, + int cap, int audit) { - int error = 0; - u32 av; - struct task_security_struct *tsec; - u32 tsid; int rc; - rc = secondary_ops->sysctl(table, op); + rc = cap_capable(cred, ns, cap, audit); if (rc) return 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); - } 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); - } - - return error; + return cred_has_capability(cred, cap, audit); } static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) { + const struct cred *cred = current_cred(); 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; + case Q_SYNC: + case Q_QUOTAON: + case Q_QUOTAOFF: + case Q_SETINFO: + case Q_SETQUOTA: + rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL); + break; + case Q_GETFMT: + case Q_GETINFO: + case Q_GETQUOTA: + rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL); + break; + default: + rc = 0; /* let the kernel handle invalid cmds */ + break; } return rc; } static int selinux_quota_on(struct dentry *dentry) { - return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); + const struct cred *cred = current_cred(); + + return dentry_has_perm(cred, dentry, 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 */ - case 10: /* Return size of the log buffer */ - 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; + case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ + case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ + rc = task_has_system(current, SYSTEM__SYSLOG_READ); + break; + case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ + case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ + /* Set level of messages printed to console */ + case SYSLOG_ACTION_CONSOLE_LEVEL: + rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); + break; + case SYSLOG_ACTION_CLOSE: /* Close log */ + case SYSLOG_ACTION_OPEN: /* Open log */ + case SYSLOG_ACTION_READ: /* Read from log */ + case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */ + case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */ + default: + rc = task_has_system(current, SYSTEM__SYSLOG_MOD); + break; } return rc; } @@ -1448,358 +2069,303 @@ static int selinux_syslog(int type) * mapping. 0 means there is enough memory for the allocation to * succeed and -ENOMEM implies there is not. * - * Note that secondary_ops->capable and task_has_perm_noaudit return 0 - * if the capability is granted, but __vm_enough_memory requires 1 if - * the capability is granted. - * * Do not audit the selinux permission check, as this is applied to all * processes that allocate mappings. */ -static int selinux_vm_enough_memory(long pages) +static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) { int rc, cap_sys_admin = 0; - struct task_security_struct *tsec = current->security; - - rc = secondary_ops->capable(current, CAP_SYS_ADMIN); - if (rc == 0) - rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, - CAP_TO_MASK(CAP_SYS_ADMIN), - NULL); + rc = selinux_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN, + SECURITY_CAP_NOAUDIT); if (rc == 0) cap_sys_admin = 1; - return __vm_enough_memory(pages, cap_sys_admin); + return __vm_enough_memory(mm, pages, cap_sys_admin); } /* binprm security operations */ -static int selinux_bprm_alloc_security(struct linux_binprm *bprm) -{ - struct bprm_security_struct *bsec; - - bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); - if (!bsec) - return -ENOMEM; - - bsec->magic = SELINUX_MAGIC; - bsec->bprm = bprm; - bsec->sid = SECINITSID_UNLABELED; - bsec->set = 0; - - bprm->security = bsec; - return 0; -} - -static int selinux_bprm_set_security(struct linux_binprm *bprm) +static int selinux_bprm_set_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct inode *inode = bprm->file->f_dentry->d_inode; + const struct task_security_struct *old_tsec; + struct task_security_struct *new_tsec; struct inode_security_struct *isec; - struct bprm_security_struct *bsec; - u32 newsid; - struct avc_audit_data ad; + struct common_audit_data ad; + struct inode *inode = file_inode(bprm->file); int rc; - rc = secondary_ops->bprm_set_security(bprm); + rc = cap_bprm_set_creds(bprm); if (rc) return rc; - bsec = bprm->security; - - if (bsec->set) + /* SELinux context only depends on initial program or script and not + * the script interpreter */ + if (bprm->cred_prepared) return 0; - tsec = current->security; + old_tsec = current_security(); + new_tsec = bprm->cred->security; isec = inode->i_security; /* Default to the current task SID. */ - bsec->sid = tsec->sid; + new_tsec->sid = old_tsec->sid; + new_tsec->osid = old_tsec->sid; - /* Reset create SID on execve. */ - tsec->create_sid = 0; + /* Reset fs, key, and sock SIDs on execve. */ + new_tsec->create_sid = 0; + new_tsec->keycreate_sid = 0; + new_tsec->sockcreate_sid = 0; - if (tsec->exec_sid) { - newsid = tsec->exec_sid; + if (old_tsec->exec_sid) { + new_tsec->sid = old_tsec->exec_sid; /* Reset exec SID on execve. */ - tsec->exec_sid = 0; + new_tsec->exec_sid = 0; + + /* + * Minimize confusion: if no_new_privs or nosuid and a + * transition is explicitly requested, then fail the exec. + */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) + return -EPERM; + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + return -EACCES; } else { /* Check for a default transition on this program. */ - rc = security_transition_sid(tsec->sid, isec->sid, - SECCLASS_PROCESS, &newsid); + rc = security_transition_sid(old_tsec->sid, isec->sid, + SECCLASS_PROCESS, NULL, + &new_tsec->sid); 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; + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = bprm->file->f_path; - if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) - newsid = tsec->sid; + if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || + (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) + new_tsec->sid = old_tsec->sid; - if (tsec->sid == newsid) { - rc = avc_has_perm(tsec->sid, isec->sid, + if (new_tsec->sid == old_tsec->sid) { + rc = avc_has_perm(old_tsec->sid, isec->sid, SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); if (rc) return rc; } else { /* Check permissions for the transition. */ - rc = avc_has_perm(tsec->sid, newsid, + rc = avc_has_perm(old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); if (rc) return rc; - rc = avc_has_perm(newsid, isec->sid, + rc = avc_has_perm(new_tsec->sid, isec->sid, SECCLASS_FILE, FILE__ENTRYPOINT, &ad); if (rc) return rc; - /* Clear any possibly unsafe personality bits on exec: */ - current->personality &= ~PER_CLEAR_ON_SETID; + /* Check for shared state */ + if (bprm->unsafe & LSM_UNSAFE_SHARE) { + rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + SECCLASS_PROCESS, PROCESS__SHARE, + NULL); + if (rc) + return -EPERM; + } + + /* Make sure that anyone attempting to ptrace over a task that + * changes its SID has the appropriate permit */ + if (bprm->unsafe & + (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { + struct task_struct *tracer; + struct task_security_struct *sec; + u32 ptsid = 0; + + rcu_read_lock(); + tracer = ptrace_parent(current); + if (likely(tracer != NULL)) { + sec = __task_cred(tracer)->security; + ptsid = sec->sid; + } + rcu_read_unlock(); - /* Set the security field to the new SID. */ - bsec->sid = newsid; + if (ptsid != 0) { + rc = avc_has_perm(ptsid, new_tsec->sid, + SECCLASS_PROCESS, + PROCESS__PTRACE, NULL); + if (rc) + return -EPERM; + } + } + + /* Clear any possibly unsafe personality bits on exec: */ + bprm->per_clear |= PER_CLEAR_ON_SETID; } - bsec->set = 1; return 0; } -static int selinux_bprm_check_security (struct linux_binprm *bprm) +static int selinux_bprm_secureexec(struct linux_binprm *bprm) { - return secondary_ops->bprm_check_security(bprm); -} - - -static int selinux_bprm_secureexec (struct linux_binprm *bprm) -{ - struct task_security_struct *tsec = current->security; + const struct task_security_struct *tsec = current_security(); + u32 sid, osid; int atsecure = 0; - if (tsec->osid != tsec->sid) { + sid = tsec->sid; + osid = tsec->osid; + + if (osid != 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); + atsecure = avc_has_perm(osid, sid, + SECCLASS_PROCESS, + PROCESS__NOATSECURE, NULL); } - return (atsecure || secondary_ops->bprm_secureexec(bprm)); + return (atsecure || cap_bprm_secureexec(bprm)); } -static void selinux_bprm_free_security(struct linux_binprm *bprm) +static int match_file(const void *p, struct file *file, unsigned fd) { - kfree(bprm->security); - bprm->security = NULL; + return file_has_perm(p, file, file_to_av(file)) ? fd + 1 : 0; } -extern struct vfsmount *selinuxfs_mount; -extern struct dentry *selinux_null; - /* Derived from fs/exec.c:flush_old_files. */ -static inline void flush_unauthorized_files(struct files_struct * files) +static inline void flush_unauthorized_files(const struct cred *cred, + struct files_struct *files) { - struct avc_audit_data ad; struct file *file, *devnull = NULL; - struct tty_struct *tty = current->signal->tty; - struct fdtable *fdt; - long j = -1; + struct tty_struct *tty; + int drop_tty = 0; + unsigned n; + tty = get_current_tty(); if (tty) { - file_list_lock(); - file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list); - if (file) { + spin_lock(&tty_files_lock); + if (!list_empty(&tty->tty_files)) { + struct tty_file_private *file_priv; + /* Revalidate access to controlling tty. - Use inode_has_perm on the tty inode directly rather - than using file_has_perm, as this particular open - file may belong to another process and we are only - interested in the inode-based check here. */ - struct inode *inode = file->f_dentry->d_inode; - if (inode_has_perm(current, inode, - FILE__READ | FILE__WRITE, NULL)) { - /* Reset controlling tty. */ - current->signal->tty = NULL; - current->signal->tty_old_pgrp = 0; - } + Use file_path_has_perm on the tty path directly + rather than using file_has_perm, as this particular + open file may belong to another process and we are + only interested in the inode-based check here. */ + file_priv = list_first_entry(&tty->tty_files, + struct tty_file_private, list); + file = file_priv->file; + if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE)) + drop_tty = 1; } - file_list_unlock(); + spin_unlock(&tty_files_lock); + tty_kref_put(tty); } + /* Reset controlling tty. */ + if (drop_tty) + no_tty(); /* Revalidate access to inherited open files. */ + n = iterate_fd(files, 0, match_file, cred); + if (!n) /* none found? */ + return; - AVC_AUDIT_DATA_INIT(&ad,FS); - - spin_lock(&files->file_lock); - for (;;) { - unsigned long set, i; - int fd; - - j++; - i = j * __NFDBITS; - fdt = files_fdtable(files); - if (i >= fdt->max_fds || i >= fdt->max_fdset) - break; - set = fdt->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); - fd = get_unused_fd(); - if (fd != i) { - if (fd >= 0) - put_unused_fd(fd); - fput(file); - continue; - } - if (devnull) { - get_file(devnull); - } else { - devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); - if (!devnull) { - put_unused_fd(fd); - fput(file); - continue; - } - } - fd_install(fd, devnull); - } - fput(file); - } - } - spin_lock(&files->file_lock); - - } - spin_unlock(&files->file_lock); + devnull = dentry_open(&selinux_null, O_RDWR, cred); + if (IS_ERR(devnull)) + devnull = NULL; + /* replace all the matching ones with this */ + do { + replace_fd(n - 1, devnull, 0); + } while ((n = iterate_fd(files, n, match_file, cred)) != 0); + if (devnull) + fput(devnull); } -static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) +/* + * Prepare a process for imminent new credential changes due to exec + */ +static void selinux_bprm_committing_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct bprm_security_struct *bsec; - u32 sid; - int rc; - - secondary_ops->bprm_apply_creds(bprm, unsafe); - - tsec = current->security; - - bsec = bprm->security; - sid = bsec->sid; + struct task_security_struct *new_tsec; + struct rlimit *rlim, *initrlim; + int rc, i; - tsec->osid = tsec->sid; - bsec->unsafe = 0; - if (tsec->sid != sid) { - /* Check for shared state. If not ok, leave SID - unchanged and kill. */ - if (unsafe & LSM_UNSAFE_SHARE) { - rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, - PROCESS__SHARE, NULL); - if (rc) { - bsec->unsafe = 1; - return; - } - } + new_tsec = bprm->cred->security; + if (new_tsec->sid == new_tsec->osid) + return; - /* Check for ptracing, and update the task SID if ok. - Otherwise, leave SID unchanged and kill. */ - if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - rc = avc_has_perm(tsec->ptrace_sid, sid, - SECCLASS_PROCESS, PROCESS__PTRACE, - NULL); - if (rc) { - bsec->unsafe = 1; - return; - } + /* Close files for which the new task SID is not authorized. */ + flush_unauthorized_files(bprm->cred, current->files); + + /* Always clear parent death signal on SID transitions. */ + current->pdeath_signal = 0; + + /* Check whether the new SID can inherit resource limits from the old + * SID. If not, reset all soft limits to the lower of the current + * task's hard limit and the init task's soft limit. + * + * Note that the setting of hard limits (even to lower them) can be + * controlled by the setrlimit check. The inclusion of the init task's + * soft limit into the computation is to avoid resetting soft limits + * higher than the default soft limit for cases where the default is + * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK. + */ + rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, + PROCESS__RLIMITINH, NULL); + if (rc) { + /* protect against do_prlimit() */ + task_lock(current); + for (i = 0; i < RLIM_NLIMITS; i++) { + rlim = current->signal->rlim + i; + initrlim = init_task.signal->rlim + i; + rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); } - tsec->sid = sid; + task_unlock(current); + update_rlimit_cpu(current, rlimit(RLIMIT_CPU)); } } /* - * called after apply_creds without the task lock held + * Clean up the process immediately after the installation of new credentials + * due to exec */ -static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm) +static void selinux_bprm_committed_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct rlimit *rlim, *initrlim; + const struct task_security_struct *tsec = current_security(); struct itimerval itimer; - struct bprm_security_struct *bsec; + u32 osid, sid; int rc, i; - tsec = current->security; - bsec = bprm->security; + osid = tsec->osid; + sid = tsec->sid; - if (bsec->unsafe) { - force_sig_specific(SIGKILL, current); - return; - } - if (tsec->osid == tsec->sid) + if (sid == osid) return; - /* Close files for which the new task SID is not authorized. */ - flush_unauthorized_files(current->files); - - /* Check whether the new SID can inherit signal state - from the old SID. If not, clear itimers to avoid - subsequent signal generation and flush and unblock - signals. This must occur _after_ the task SID has - been updated so that any kill done after the flush - will be checked against the new SID. */ - rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, - PROCESS__SIGINH, NULL); + /* Check whether the new SID can inherit signal state from the old SID. + * If not, clear itimers to avoid subsequent signal generation and + * flush and unblock signals. + * + * This must occur _after_ the task SID has been updated so that any + * kill done after the flush will be checked against the new SID. + */ + rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); if (rc) { memset(&itimer, 0, sizeof itimer); for (i = 0; i < 3; i++) do_setitimer(i, &itimer, NULL); - flush_signals(current); spin_lock_irq(¤t->sighand->siglock); - flush_signal_handlers(current, 1); - sigemptyset(¤t->blocked); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - /* Check whether the new SID can inherit resource limits - from the old SID. If not, reset all soft limits to - the lower of the current task's hard limit and the init - task's soft limit. Note that the setting of hard limits - (even to lower them) can be controlled by the setrlimit - check. The inclusion of the init task's soft limit into - the computation is to avoid resetting soft limits higher - than the default soft limit for cases where the default - is lower than the hard limit, e.g. RLIMIT_CORE or - RLIMIT_STACK.*/ - rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, - PROCESS__RLIMITINH, NULL); - if (rc) { - for (i = 0; i < RLIM_NLIMITS; i++) { - rlim = current->signal->rlim + i; - initrlim = init_task.signal->rlim+i; - rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur); - } - if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) { - /* - * This will cause RLIMIT_CPU calculations - * to be refigured. - */ - current->it_prof_expires = jiffies_to_cputime(1); + if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) { + __flush_signals(current); + flush_signal_handlers(current, 1); + sigemptyset(¤t->blocked); } + spin_unlock_irq(¤t->sighand->siglock); } - /* 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->signal->wait_chldexit); + /* Wake up the parent if it is waiting so that it can recheck + * wait permission to the new task SID. */ + read_lock(&tasklist_lock); + __wake_up_parent(current, current->real_parent); + read_unlock(&tasklist_lock); } /* superblock security operations */ @@ -1824,9 +2390,11 @@ static inline int match_prefix(char *prefix, int plen, char *option, int olen) static inline int selinux_option(char *option, int len) { - return (match_prefix("context=", sizeof("context=")-1, option, len) || - match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || - match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); + return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) || + match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) || + match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) || + match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) || + match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len)); } static inline void take_option(char **to, char *from, int *first, int len) @@ -1834,28 +2402,43 @@ static inline void take_option(char **to, char *from, int *first, int len) if (!*first) { **to = ','; *to += 1; - } - else + } else *first = 0; memcpy(*to, from, len); *to += len; } -static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy) +static inline void take_selinux_option(char **to, char *from, int *first, + int len) +{ + int current_size = 0; + + if (!*first) { + **to = '|'; + *to += 1; + } else + *first = 0; + + while (current_size < len) { + if (*from != '"') { + **to = *from; + *to += 1; + } + from += 1; + current_size += 1; + } +} + +static int selinux_sb_copy_data(char *orig, char *copy) { int fnosec, fsec, rc = 0; char *in_save, *in_curr, *in_end; char *sec_curr, *nosec_save, *nosec; + int open_quote = 0; in_curr = orig; sec_curr = copy; - /* Binary mount data: just copy */ - if (type->fs_flags & FS_BINARY_MOUNTDATA) { - copy_page(sec_curr, in_curr); - goto out; - } - nosec = (char *)get_zeroed_page(GFP_KERNEL); if (!nosec) { rc = -ENOMEM; @@ -1867,11 +2450,14 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void in_save = in_end = orig; do { - if (*in_end == ',' || *in_end == '\0') { + if (*in_end == '"') + open_quote = !open_quote; + if ((*in_end == ',' && open_quote == 0) || + *in_end == '\0') { int len = in_end - in_curr; if (selinux_option(in_curr, len)) - take_option(&sec_curr, in_curr, &fsec, len); + take_selinux_option(&sec_curr, in_curr, &fsec, len); else take_option(&nosec, in_curr, &fnosec, len); @@ -1885,59 +2471,142 @@ out: return rc; } -static int selinux_sb_kern_mount(struct super_block *sb, void *data) +static int selinux_sb_remount(struct super_block *sb, void *data) +{ + int rc, i, *flags; + struct security_mnt_opts opts; + char *secdata, **mount_options; + struct superblock_security_struct *sbsec = sb->s_security; + + if (!(sbsec->flags & SE_SBINITIALIZED)) + return 0; + + if (!data) + return 0; + + if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) + return 0; + + security_init_mnt_opts(&opts); + secdata = alloc_secdata(); + if (!secdata) + return -ENOMEM; + rc = selinux_sb_copy_data(data, secdata); + if (rc) + goto out_free_secdata; + + rc = selinux_parse_opts_str(secdata, &opts); + if (rc) + goto out_free_secdata; + + mount_options = opts.mnt_opts; + flags = opts.mnt_opts_flags; + + for (i = 0; i < opts.num_mnt_opts; i++) { + u32 sid; + size_t len; + + if (flags[i] == SBLABEL_MNT) + continue; + len = strlen(mount_options[i]); + rc = security_context_to_sid(mount_options[i], len, &sid, + GFP_KERNEL); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + mount_options[i], sb->s_id, sb->s_type->name, rc); + goto out_free_opts; + } + rc = -EINVAL; + switch (flags[i]) { + case FSCONTEXT_MNT: + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid)) + goto out_bad_option; + break; + case CONTEXT_MNT: + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid)) + goto out_bad_option; + break; + case ROOTCONTEXT_MNT: { + struct inode_security_struct *root_isec; + root_isec = sb->s_root->d_inode->i_security; + + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) + goto out_bad_option; + break; + } + case DEFCONTEXT_MNT: + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid)) + goto out_bad_option; + break; + default: + goto out_free_opts; + } + } + + rc = 0; +out_free_opts: + security_free_mnt_opts(&opts); +out_free_secdata: + free_secdata(secdata); + return rc; +out_bad_option: + printk(KERN_WARNING "SELinux: unable to change security options " + "during remount (dev %s, type=%s)\n", sb->s_id, + sb->s_type->name); + goto out_free_opts; +} + +static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) { - struct avc_audit_data ad; + const struct cred *cred = current_cred(); + struct common_audit_data ad; int rc; rc = superblock_doinit(sb, data); 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); + /* Allow all mounts performed by the kernel */ + if (flags & MS_KERNMOUNT) + return 0; + + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = sb->s_root; + return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); } -static int selinux_sb_statfs(struct super_block *sb) +static int selinux_sb_statfs(struct dentry *dentry) { - struct avc_audit_data ad; + const struct cred *cred = current_cred(); + struct common_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); + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry->d_sb->s_root; + return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } -static int selinux_mount(char * dev_name, - struct nameidata *nd, - char * type, - unsigned long flags, - void * data) +static int selinux_mount(const char *dev_name, + struct path *path, + const char *type, + unsigned long flags, + void *data) { - int rc; - - rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data); - if (rc) - return rc; + const struct cred *cred = current_cred(); if (flags & MS_REMOUNT) - return superblock_has_perm(current, nd->mnt->mnt_sb, - FILESYSTEM__REMOUNT, NULL); + return superblock_has_perm(cred, path->dentry->d_sb, + FILESYSTEM__REMOUNT, NULL); else - return dentry_has_perm(current, nd->mnt, nd->dentry, - FILE__MOUNTON); + return path_has_perm(cred, path, FILE__MOUNTON); } static int selinux_umount(struct vfsmount *mnt, int flags) { - int rc; - - rc = secondary_ops->sb_umount(mnt, flags); - if (rc) - return rc; + const struct cred *cred = current_cred(); - return superblock_has_perm(current,mnt->mnt_sb, - FILESYSTEM__UNMOUNT,NULL); + return superblock_has_perm(cred, mnt->mnt_sb, + FILESYSTEM__UNMOUNT, NULL); } /* inode security operations */ @@ -1952,57 +2621,93 @@ static void selinux_inode_free_security(struct inode *inode) inode_free_security(inode); } -static int selinux_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, - size_t *len) +static int selinux_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) { + const struct cred *cred = current_cred(); struct task_security_struct *tsec; struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; - struct inode_security_struct *isec; - u32 newsid, clen; + struct inode *dir = dentry->d_parent->d_inode; + u32 newsid; int rc; - char *namep = NULL, *context; - tsec = current->security; + tsec = cred->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; - isec = inode->i_security; if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { newsid = tsec->create_sid; } else { rc = security_transition_sid(tsec->sid, dsec->sid, - inode_mode_to_security_class(inode->i_mode), + inode_mode_to_security_class(mode), + name, &newsid); if (rc) { + printk(KERN_WARNING + "%s: security_transition_sid failed, rc=%d\n", + __func__, -rc); + return rc; + } + } + + return security_sid_to_context(newsid, (char **)ctx, ctxlen); +} + +static int selinux_inode_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr, + const char **name, + void **value, size_t *len) +{ + const struct task_security_struct *tsec = current_security(); + struct inode_security_struct *dsec; + struct superblock_security_struct *sbsec; + u32 sid, newsid, clen; + int rc; + char *context; + + dsec = dir->i_security; + sbsec = dir->i_sb->s_security; + + sid = tsec->sid; + newsid = tsec->create_sid; + + if ((sbsec->flags & SE_SBINITIALIZED) && + (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) + newsid = sbsec->mntpoint_sid; + else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { + rc = security_transition_sid(sid, dsec->sid, + inode_mode_to_security_class(inode->i_mode), + qstr, &newsid); + if (rc) { printk(KERN_WARNING "%s: " "security_transition_sid failed, rc=%d (dev=%s " "ino=%ld)\n", - __FUNCTION__, + __func__, -rc, inode->i_sb->s_id, inode->i_ino); return rc; } } - inode_security_set_sid(inode, newsid); + /* Possibly defer initialization to selinux_complete_init. */ + if (sbsec->flags & SE_SBINITIALIZED) { + struct inode_security_struct *isec = inode->i_security; + isec->sclass = inode_mode_to_security_class(inode->i_mode); + isec->sid = newsid; + isec->initialized = 1; + } - if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) + if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; - if (name) { - namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL); - if (!namep) - return -ENOMEM; - *name = namep; - } + if (name) + *name = XATTR_SELINUX_SUFFIX; if (value && len) { - rc = security_sid_to_context(newsid, &context, &clen); - if (rc) { - kfree(namep); + rc = security_sid_to_context_force(newsid, &context, &clen); + if (rc) return rc; - } *value = context; *len = clen; } @@ -2010,28 +2715,18 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, return 0; } -static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) +static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode) { return may_create(dir, dentry, SECCLASS_FILE); } 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 int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) { - int rc; - - rc = secondary_ops->inode_unlink(dir, dentry); - if (rc) - return rc; return may_link(dir, dentry, MAY_UNLINK); } @@ -2040,7 +2735,7 @@ static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const return may_create(dir, dentry, SECCLASS_LNK_FILE); } -static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask) +static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask) { return may_create(dir, dentry, SECCLASS_DIR); } @@ -2050,129 +2745,214 @@ 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) +static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int rc; - - rc = secondary_ops->inode_mknod(dir, dentry, mode, dev); - if (rc) - return rc; - return may_create(dir, dentry, inode_mode_to_security_class(mode)); } static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry) + struct inode *new_inode, struct dentry *new_dentry) { return may_rename(old_inode, old_dentry, new_inode, new_dentry); } static int selinux_inode_readlink(struct dentry *dentry) { - return dentry_has_perm(current, NULL, dentry, FILE__READ); + const struct cred *cred = current_cred(); + + return dentry_has_perm(cred, dentry, FILE__READ); } static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata) { - int rc; + const struct cred *cred = current_cred(); - rc = secondary_ops->inode_follow_link(dentry,nameidata); - if (rc) - return rc; - return dentry_has_perm(current, NULL, dentry, FILE__READ); + return dentry_has_perm(cred, dentry, FILE__READ); } -static int selinux_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static noinline int audit_inode_permission(struct inode *inode, + u32 perms, u32 audited, u32 denied, + int result, + unsigned flags) { + struct common_audit_data ad; + struct inode_security_struct *isec = inode->i_security; int rc; - rc = secondary_ops->inode_permission(inode, mask, nd); + ad.type = LSM_AUDIT_DATA_INODE; + ad.u.inode = inode; + + rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, + audited, denied, result, &ad, flags); if (rc) return rc; + return 0; +} - if (!mask) { - /* No permission to check. Existence test. */ +static int selinux_inode_permission(struct inode *inode, int mask) +{ + const struct cred *cred = current_cred(); + u32 perms; + bool from_access; + unsigned flags = mask & MAY_NOT_BLOCK; + struct inode_security_struct *isec; + u32 sid; + struct av_decision avd; + int rc, rc2; + u32 audited, denied; + + from_access = mask & MAY_ACCESS; + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); + + /* No permission to check. Existence test. */ + if (!mask) return 0; - } - return inode_has_perm(current, inode, - file_mask_to_av(inode->i_mode, mask), NULL); + validate_creds(cred); + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + perms = file_mask_to_av(inode->i_mode, mask); + + sid = cred_sid(cred); + isec = inode->i_security; + + rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); + audited = avc_audit_required(perms, &avd, rc, + from_access ? FILE__AUDIT_ACCESS : 0, + &denied); + if (likely(!audited)) + return rc; + + rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags); + if (rc2) + return rc2; + return rc; } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) { - int rc; + const struct cred *cred = current_cred(); + unsigned int ia_valid = iattr->ia_valid; + __u32 av = FILE__WRITE; - rc = secondary_ops->inode_setattr(dentry, iattr); - if (rc) - return rc; + /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */ + if (ia_valid & ATTR_FORCE) { + ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE | + ATTR_FORCE); + if (!ia_valid) + return 0; + } - if (iattr->ia_valid & ATTR_FORCE) - return 0; + if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | + ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) + return dentry_has_perm(cred, dentry, FILE__SETATTR); - 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); + if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)) + av |= FILE__OPEN; - return dentry_has_perm(current, NULL, dentry, FILE__WRITE); + return dentry_has_perm(cred, dentry, av); } static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) { - return dentry_has_perm(current, mnt, dentry, FILE__GETATTR); + const struct cred *cred = current_cred(); + struct path path; + + path.dentry = dentry; + path.mnt = mnt; + + return path_has_perm(cred, &path, FILE__GETATTR); } -static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags) +static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) { - 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; + const struct cred *cred = current_cred(); - if (strcmp(name, XATTR_NAME_SELINUX)) { - if (!strncmp(name, XATTR_SECURITY_PREFIX, - sizeof XATTR_SECURITY_PREFIX - 1) && - !capable(CAP_SYS_ADMIN)) { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof XATTR_SECURITY_PREFIX - 1)) { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) + return -EPERM; + } else if (!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); } + /* Not an attribute we recognize, so just check the + ordinary setattr permission. */ + return dentry_has_perm(cred, dentry, FILE__SETATTR); +} + +static int selinux_inode_setxattr(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; + struct superblock_security_struct *sbsec; + struct common_audit_data ad; + u32 newsid, sid = current_sid(); + int rc = 0; + + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); + sbsec = inode->i_sb->s_security; - if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) + if (!(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!inode_owner_or_capable(inode)) return -EPERM; - AVC_AUDIT_DATA_INIT(&ad,FS); - ad.u.fs.dentry = dentry; + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry; - rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, + rc = avc_has_perm(sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); if (rc) return rc; - rc = security_context_to_sid(value, size, &newsid); + rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); + if (rc == -EINVAL) { + if (!capable(CAP_MAC_ADMIN)) { + struct audit_buffer *ab; + size_t audit_size; + const char *str; + + /* We strip a nul only if it is at the end, otherwise the + * context contains a nul and we should audit that */ + if (value) { + str = value; + if (str[size - 1] == '\0') + audit_size = size - 1; + else + audit_size = size; + } else { + str = ""; + audit_size = 0; + } + ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + audit_log_format(ab, "op=setxattr invalid_context="); + audit_log_n_untrustedstring(ab, value, audit_size); + audit_log_end(ab); + + return rc; + } + rc = security_context_to_sid_force(value, size, &newsid); + } if (rc) return rc; - rc = avc_has_perm(tsec->sid, newsid, isec->sclass, + rc = avc_has_perm(sid, newsid, isec->sclass, FILE__RELABELTO, &ad); if (rc) return rc; - rc = security_validate_transition(isec->sid, newsid, tsec->sid, - isec->sclass); + rc = security_validate_transition(isec->sid, newsid, sid, + isec->sclass); if (rc) return rc; @@ -2183,8 +2963,9 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value &ad); } -static void selinux_inode_post_setxattr(struct dentry *dentry, char *name, - void *value, size_t size, int flags) +static void selinux_inode_post_setxattr(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; @@ -2196,43 +2977,39 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, char *name, return; } - rc = security_context_to_sid(value, size, &newsid); + rc = security_context_to_sid_force(value, size, &newsid); if (rc) { - printk(KERN_WARNING "%s: unable to obtain SID for context " - "%s, rc=%d\n", __FUNCTION__, (char*)value, -rc); + printk(KERN_ERR "SELinux: unable to map context to SID" + "for (%s, %lu), rc=%d\n", + inode->i_sb->s_id, inode->i_ino, -rc); return; } + isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; + isec->initialized = 1; + return; } -static int selinux_inode_getxattr (struct dentry *dentry, char *name) +static int selinux_inode_getxattr(struct dentry *dentry, const char *name) { - return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + const struct cred *cred = current_cred(); + + return dentry_has_perm(cred, dentry, FILE__GETATTR); } -static int selinux_inode_listxattr (struct dentry *dentry) +static int selinux_inode_listxattr(struct dentry *dentry) { - return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + const struct cred *cred = current_cred(); + + return dentry_has_perm(cred, dentry, FILE__GETATTR); } -static int selinux_inode_removexattr (struct dentry *dentry, char *name) +static int selinux_inode_removexattr(struct dentry *dentry, const 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); - } + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); /* No one is allowed to remove a SELinux security label. You can change the label, but all data must be labeled. */ @@ -2240,57 +3017,50 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) } /* - * Copy the in-core inode security context value to the user. If the - * getxattr() prior to this succeeded, check to see if we need to - * canonicalize the value to be finally returned to the user. + * Copy the inode security context value to the user. * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) { + u32 size; + int error; + char *context = NULL; struct inode_security_struct *isec = inode->i_security; - char *context; - unsigned len; - int rc; - if (strcmp(name, XATTR_SELINUX_SUFFIX)) { - rc = -EOPNOTSUPP; - goto out; - } - - rc = security_sid_to_context(isec->sid, &context, &len); - if (rc) - goto out; - - /* Probe for required buffer size */ - if (!buffer || !size) { - rc = len; - goto out_free; - } - - if (size < len) { - rc = -ERANGE; - goto out_free; - } + if (strcmp(name, XATTR_SELINUX_SUFFIX)) + return -EOPNOTSUPP; - if (err > 0) { - if ((len == err) && !(memcmp(context, buffer, len))) { - /* Don't need to canonicalize value */ - rc = err; - goto out_free; - } - memset(buffer, 0, size); + /* + * If the caller has CAP_MAC_ADMIN, then get the raw context + * value even if it is not defined by current policy; otherwise, + * use the in-core value under current policy. + * Use the non-auditing forms of the permission checks since + * getxattr may be called by unprivileged processes commonly + * and lack of permission just means that we fall back to the + * in-core context value, not a denial. + */ + error = selinux_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN, + SECURITY_CAP_NOAUDIT); + if (!error) + error = security_sid_to_context_force(isec->sid, &context, + &size); + else + error = security_sid_to_context(isec->sid, &context, &size); + if (error) + return error; + error = size; + if (alloc) { + *buffer = context; + goto out_nofree; } - memcpy(buffer, context, len); - rc = len; -out_free: kfree(context); -out: - return rc; +out_nofree: + return error; } static int selinux_inode_setsecurity(struct inode *inode, const char *name, - const void *value, size_t size, int flags) + const void *value, size_t size, int flags) { struct inode_security_struct *isec = inode->i_security; u32 newsid; @@ -2302,11 +3072,13 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (!value || !size) return -EACCES; - rc = security_context_to_sid((void*)value, size, &newsid); + rc = security_context_to_sid((void *)value, size, &newsid, GFP_KERNEL); if (rc) return rc; + isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; + isec->initialized = 1; return 0; } @@ -2318,25 +3090,46 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t return len; } +static void selinux_inode_getsecid(const struct inode *inode, u32 *secid) +{ + struct inode_security_struct *isec = inode->i_security; + *secid = isec->sid; +} + /* file security operations */ -static int selinux_file_permission(struct file *file, int mask) +static int selinux_revalidate_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; - } + const struct cred *cred = current_cred(); + struct inode *inode = file_inode(file); /* 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, + return file_has_perm(cred, file, file_mask_to_av(inode->i_mode, mask)); } +static int selinux_file_permission(struct file *file, int mask) +{ + struct inode *inode = file_inode(file); + struct file_security_struct *fsec = file->f_security; + struct inode_security_struct *isec = inode->i_security; + u32 sid = current_sid(); + + if (!mask) + /* No permission to check. Existence test. */ + return 0; + + if (sid == fsec->sid && fsec->isid == isec->sid && + fsec->pseqno == avc_policy_seqno()) + /* No change since file_open check. */ + return 0; + + return selinux_revalidate_file_permission(file, mask); +} + static int selinux_file_alloc_security(struct file *file) { return file_alloc_security(file); @@ -2350,63 +3143,68 @@ static void selinux_file_free_security(struct file *file) static int selinux_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + const struct cred *cred = current_cred(); 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; + case FIONREAD: + /* fall through */ + case FIBMAP: + /* fall through */ + case FIGETBSZ: + /* fall through */ + case FS_IOC_GETFLAGS: + /* fall through */ + case FS_IOC_GETVERSION: + error = file_has_perm(cred, file, FILE__GETATTR); + break; - /* sys_ioctl() checks */ - case FIONBIO: - /* fall through */ - case FIOASYNC: - error = file_has_perm(current, file, 0); - break; + case FS_IOC_SETFLAGS: + /* fall through */ + case FS_IOC_SETVERSION: + error = file_has_perm(cred, file, FILE__SETATTR); + break; - case KDSKBENT: - case KDSKBSENT: - error = task_has_capability(current,CAP_SYS_TTY_CONFIG); - break; + /* sys_ioctl() checks */ + case FIONBIO: + /* fall through */ + case FIOASYNC: + error = file_has_perm(cred, file, 0); + break; - /* default case assumes that the command will go - * to the file's ioctl() function. - */ - default: - error = file_has_perm(current, file, FILE__IOCTL); + case KDSKBENT: + case KDSKBSENT: + error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, + SECURITY_CAP_AUDIT); + break; + /* default case assumes that the command will go + * to the file's ioctl() function. + */ + default: + error = file_has_perm(cred, file, FILE__IOCTL); } return error; } +static int default_noexec; + static int file_map_prot_check(struct file *file, unsigned long prot, int shared) { -#ifndef CONFIG_PPC32 - if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) { + const struct cred *cred = current_cred(); + int rc = 0; + + if (default_noexec && + (prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) { /* * We are making executable an anonymous mapping or a * private file mapping that will also be writable. * This has an additional check. */ - int rc = task_has_perm(current, current, PROCESS__EXECMEM); + rc = cred_has_perm(cred, cred, PROCESS__EXECMEM); if (rc) - return rc; + goto error; } -#endif if (file) { /* read access is always possible with a mapping */ @@ -2419,20 +3217,34 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared if (prot & PROT_EXEC) av |= FILE__EXECUTE; - return file_has_perm(current, file, av); + return file_has_perm(cred, file, av); } - return 0; + +error: + return rc; } -static int selinux_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) +static int selinux_mmap_addr(unsigned long addr) { int rc; - rc = secondary_ops->file_mmap(file, reqprot, prot, flags); + /* do DAC check on address space usage */ + rc = cap_mmap_addr(addr); if (rc) return rc; + if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { + u32 sid = current_sid(); + rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, + MEMPROTECT__MMAP_ZERO, NULL); + } + + return rc; +} + +static int selinux_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) +{ if (selinux_checkreqprot) prot = reqprot; @@ -2444,97 +3256,80 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { - int rc; - - rc = secondary_ops->file_mprotect(vma, reqprot, prot); - if (rc) - return rc; + const struct cred *cred = current_cred(); if (selinux_checkreqprot) prot = reqprot; -#ifndef CONFIG_PPC32 - if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXECUTABLE) && - (vma->vm_start >= vma->vm_mm->start_brk && - vma->vm_end <= vma->vm_mm->brk)) { - /* - * We are making an executable mapping in the brk region. - * This has an additional execheap check. - */ - rc = task_has_perm(current, current, PROCESS__EXECHEAP); - if (rc) - return rc; - } - if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) { - /* - * We are making executable a file mapping that has - * had some COW done. Since pages might have been written, - * check ability to execute the possibly modified content. - * This typically should only occur for text relocations. - */ - int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD); - if (rc) - return rc; - } - if (!vma->vm_file && (prot & PROT_EXEC) && - vma->vm_start <= vma->vm_mm->start_stack && - vma->vm_end >= vma->vm_mm->start_stack) { - /* Attempt to make the process stack executable. - * This has an additional execstack check. - */ - rc = task_has_perm(current, current, PROCESS__EXECSTACK); + if (default_noexec && + (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { + int rc = 0; + if (vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk) { + rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); + } else if (!vma->vm_file && + vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) { + rc = current_has_perm(current, PROCESS__EXECSTACK); + } else if (vma->vm_file && vma->anon_vma) { + /* + * We are making executable a file mapping that has + * had some COW done. Since pages might have been + * written, check ability to execute the possibly + * modified content. This typically should only + * occur for text relocations. + */ + rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); + } if (rc) return rc; } -#endif return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); } static int selinux_file_lock(struct file *file, unsigned int cmd) { - return file_has_perm(current, file, FILE__LOCK); + const struct cred *cred = current_cred(); + + return file_has_perm(cred, file, FILE__LOCK); } static int selinux_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { + const struct cred *cred = current_cred(); 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); + case F_SETFL: + if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) { + err = file_has_perm(cred, file, FILE__WRITE); break; - case F_GETLK: - case F_SETLK: - case F_SETLKW: + } + /* fall through */ + case F_SETOWN: + case F_SETSIG: + case F_GETFL: + case F_GETOWN: + case F_GETSIG: + case F_GETOWNER_UIDS: + /* Just check FD__USE permission */ + err = file_has_perm(cred, file, 0); + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + case F_OFD_GETLK: + case F_OFD_SETLK: + case F_OFD_SETLKW: #if BITS_PER_LONG == 32 - case F_GETLK64: - case F_SETLK64: - case F_SETLKW64: + case F_GETLK64: + case F_SETLK64: + case F_SETLKW64: #endif - if (!file->f_dentry || !file->f_dentry->d_inode) { - err = -EINVAL; - break; - } - err = file_has_perm(current, file, FILE__LOCK); - break; + err = file_has_perm(cred, file, FILE__LOCK); + break; } return err; @@ -2542,12 +3337,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, 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; + fsec->fown_sid = current_sid(); return 0; } @@ -2555,15 +3348,14 @@ static int selinux_file_set_fowner(struct file *file) static int selinux_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { - struct file *file; + struct file *file; + u32 sid = task_sid(tsk); 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)); + file = container_of(fown, struct file, f_owner); - tsec = tsk->security; fsec = file->f_security; if (!signum) @@ -2571,216 +3363,294 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, else perm = signal_to_av(signum); - return avc_has_perm(fsec->fown_sid, tsec->sid, + return avc_has_perm(fsec->fown_sid, sid, SECCLASS_PROCESS, perm, NULL); } static int selinux_file_receive(struct file *file) { - return file_has_perm(current, file, file_to_av(file)); + const struct cred *cred = current_cred(); + + return file_has_perm(cred, file, file_to_av(file)); +} + +static int selinux_file_open(struct file *file, const struct cred *cred) +{ + struct file_security_struct *fsec; + struct inode_security_struct *isec; + + fsec = file->f_security; + isec = file_inode(file)->i_security; + /* + * Save inode label and policy sequence number + * at open-time so that selinux_file_permission + * can determine whether revalidation is necessary. + * Task label is already saved in the file security + * struct as its SID. + */ + fsec->isid = isec->sid; + fsec->pseqno = avc_policy_seqno(); + /* + * Since the inode label or policy seqno may have changed + * between the selinux_inode_permission check and the saving + * of state above, recheck that access is still permitted. + * Otherwise, access might never be revalidated against the + * new inode label or new policy. + * This check is not redundant - do not remove. + */ + return file_path_has_perm(cred, file, open_file_to_av(file)); } /* task security operations */ static int selinux_task_create(unsigned long clone_flags) { - int rc; + return current_has_perm(current, PROCESS__FORK); +} - rc = secondary_ops->task_create(clone_flags); - if (rc) - return rc; +/* + * allocate the SELinux part of blank credentials + */ +static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ + struct task_security_struct *tsec; - return task_has_perm(current, current, PROCESS__FORK); + tsec = kzalloc(sizeof(struct task_security_struct), gfp); + if (!tsec) + return -ENOMEM; + + cred->security = tsec; + return 0; } -static int selinux_task_alloc_security(struct task_struct *tsk) +/* + * detach and free the LSM part of a set of credentials + */ +static void selinux_cred_free(struct cred *cred) { - struct task_security_struct *tsec1, *tsec2; - int rc; + struct task_security_struct *tsec = cred->security; - tsec1 = current->security; - - rc = task_alloc_security(tsk); - if (rc) - return rc; - tsec2 = tsk->security; + /* + * cred->security == NULL if security_cred_alloc_blank() or + * security_prepare_creds() returned an error. + */ + BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE); + cred->security = (void *) 0x7UL; + kfree(tsec); +} - tsec2->osid = tsec1->osid; - tsec2->sid = tsec1->sid; +/* + * prepare a new set of credentials for modification + */ +static int selinux_cred_prepare(struct cred *new, const struct cred *old, + gfp_t gfp) +{ + const struct task_security_struct *old_tsec; + struct task_security_struct *tsec; - /* Retain the exec and create SIDs across fork */ - tsec2->exec_sid = tsec1->exec_sid; - tsec2->create_sid = tsec1->create_sid; + old_tsec = old->security; - /* Retain ptracer SID across fork, if any. - This will be reset by the ptrace hook upon any - subsequent ptrace_attach operations. */ - tsec2->ptrace_sid = tsec1->ptrace_sid; + tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp); + if (!tsec) + return -ENOMEM; + new->security = tsec; return 0; } -static void selinux_task_free_security(struct task_struct *tsk) +/* + * transfer the SELinux data to a blank set of creds + */ +static void selinux_cred_transfer(struct cred *new, const struct cred *old) { - task_free_security(tsk); + const struct task_security_struct *old_tsec = old->security; + struct task_security_struct *tsec = new->security; + + *tsec = *old_tsec; } -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; +/* + * set the security data for a kernel service + * - all the creation contexts are set to unlabelled + */ +static int selinux_kernel_act_as(struct cred *new, u32 secid) +{ + struct task_security_struct *tsec = new->security; + u32 sid = current_sid(); + int ret; + + ret = avc_has_perm(sid, secid, + SECCLASS_KERNEL_SERVICE, + KERNEL_SERVICE__USE_AS_OVERRIDE, + NULL); + if (ret == 0) { + tsec->sid = secid; + tsec->create_sid = 0; + tsec->keycreate_sid = 0; + tsec->sockcreate_sid = 0; + } + return ret; } -static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) +/* + * set the file creation context in a security record to the same as the + * objective context of the specified inode + */ +static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) { - return secondary_ops->task_post_setuid(id0,id1,id2,flags); + struct inode_security_struct *isec = inode->i_security; + struct task_security_struct *tsec = new->security; + u32 sid = current_sid(); + int ret; + + ret = avc_has_perm(sid, isec->sid, + SECCLASS_KERNEL_SERVICE, + KERNEL_SERVICE__CREATE_FILES_AS, + NULL); + + if (ret == 0) + tsec->create_sid = isec->sid; + return ret; } -static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags) +static int selinux_kernel_module_request(char *kmod_name) { - /* See the comment for setuid above. */ - return 0; + u32 sid; + struct common_audit_data ad; + + sid = task_sid(current); + + ad.type = LSM_AUDIT_DATA_KMOD; + ad.u.kmod_name = kmod_name; + + return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, + SYSTEM__MODULE_REQUEST, &ad); } static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { - return task_has_perm(current, p, PROCESS__SETPGID); + return current_has_perm(p, PROCESS__SETPGID); } static int selinux_task_getpgid(struct task_struct *p) { - return task_has_perm(current, p, PROCESS__GETPGID); + return current_has_perm(p, PROCESS__GETPGID); } static int selinux_task_getsid(struct task_struct *p) { - return task_has_perm(current, p, PROCESS__GETSESSION); + return current_has_perm(p, PROCESS__GETSESSION); } -static int selinux_task_setgroups(struct group_info *group_info) +static void selinux_task_getsecid(struct task_struct *p, u32 *secid) { - /* See the comment for setuid above. */ - return 0; + *secid = task_sid(p); } static int selinux_task_setnice(struct task_struct *p, int nice) { int rc; - rc = secondary_ops->task_setnice(p, nice); + rc = cap_task_setnice(p, nice); if (rc) return rc; - return task_has_perm(current,p, PROCESS__SETSCHED); + return current_has_perm(p, PROCESS__SETSCHED); } -static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) +static int selinux_task_setioprio(struct task_struct *p, int ioprio) { - struct rlimit *old_rlim = current->signal->rlim + resource; int rc; - rc = secondary_ops->task_setrlimit(resource, new_rlim); + rc = cap_task_setioprio(p, ioprio); if (rc) return rc; + return current_has_perm(p, PROCESS__SETSCHED); +} + +static int selinux_task_getioprio(struct task_struct *p) +{ + return current_has_perm(p, PROCESS__GETSCHED); +} + +static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, + struct rlimit *new_rlim) +{ + struct rlimit *old_rlim = p->signal->rlim + resource; + /* Control the ability to change the hard limit (whether lowering or raising it), so that the hard limit can later be used as a safe reset point for the soft limit - upon context transitions. See selinux_bprm_apply_creds. */ + upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) - return task_has_perm(current, current, PROCESS__SETRLIMIT); + return current_has_perm(p, PROCESS__SETRLIMIT); return 0; } -static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp) +static int selinux_task_setscheduler(struct task_struct *p) { - return task_has_perm(current, p, PROCESS__SETSCHED); + int rc; + + rc = cap_task_setscheduler(p); + if (rc) + return rc; + + return current_has_perm(p, PROCESS__SETSCHED); } static int selinux_task_getscheduler(struct task_struct *p) { - return task_has_perm(current, p, PROCESS__GETSCHED); + return current_has_perm(p, PROCESS__GETSCHED); } -static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) +static int selinux_task_movememory(struct task_struct *p) +{ + return current_has_perm(p, PROCESS__SETSCHED); +} + +static int selinux_task_kill(struct task_struct *p, struct siginfo *info, + int sig, u32 secid) { u32 perm; int rc; - rc = secondary_ops->task_kill(p, info, sig); - if (rc) - return rc; - - if (info != SEND_SIG_NOINFO && (is_si_special(info) || 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; + if (secid) + rc = avc_has_perm(secid, task_sid(p), + SECCLASS_PROCESS, perm, NULL); + else + rc = current_has_perm(p, perm); + return rc; } 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; + return task_has_perm(p, current, PROCESS__SIGCHLD); } 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; + u32 sid = task_sid(p); - isec->sid = tsec->sid; + isec->sid = sid; isec->initialized = 1; - return; } -#ifdef CONFIG_SECURITY_NETWORK - /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv4(struct sk_buff *skb, + struct common_audit_data *ad, u8 *proto) { int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; - offset = skb->nh.raw - skb->data; + offset = skb_network_offset(skb); ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); if (ih == NULL) goto out; @@ -2789,46 +3659,65 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad if (ihlen < sizeof(_iph)) goto out; - ad->u.net.v4info.saddr = ih->saddr; - ad->u.net.v4info.daddr = ih->daddr; + ad->u.net->v4info.saddr = ih->saddr; + ad->u.net->v4info.daddr = ih->daddr; ret = 0; + if (proto) + *proto = ih->protocol; + switch (ih->protocol) { - case IPPROTO_TCP: { - struct tcphdr _tcph, *th; + case IPPROTO_TCP: { + struct tcphdr _tcph, *th; - if (ntohs(ih->frag_off) & IP_OFFSET) - break; + if (ntohs(ih->frag_off) & IP_OFFSET) + break; offset += ihlen; th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; - } - - case IPPROTO_UDP: { - struct udphdr _udph, *uh; - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - + } + + case IPPROTO_UDP: { + struct udphdr _udph, *uh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + offset += ihlen; - uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); if (uh == NULL) - break; + break; + + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; + break; + } + + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; - break; - } + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; + break; + } - default: - break; - } + default: + break; + } out: return ret; } @@ -2836,37 +3725,42 @@ out: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv6(struct sk_buff *skb, + struct common_audit_data *ad, u8 *proto) { u8 nexthdr; int ret = -EINVAL, offset; struct ipv6hdr _ipv6h, *ip6; + __be16 frag_off; - offset = skb->nh.raw - skb->data; + offset = skb_network_offset(skb); ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); if (ip6 == NULL) goto out; - ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr); - ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr); + ad->u.net->v6info.saddr = ip6->saddr; + ad->u.net->v6info.daddr = ip6->daddr; ret = 0; nexthdr = ip6->nexthdr; offset += sizeof(_ipv6h); - offset = ipv6_skip_exthdr(skb, offset, &nexthdr); + offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); if (offset < 0) goto out; + if (proto) + *proto = nexthdr; + switch (nexthdr) { case IPPROTO_TCP: { - struct tcphdr _tcph, *th; + struct tcphdr _tcph, *th; th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); if (th == NULL) break; - ad->u.net.sport = th->source; - ad->u.net.dport = th->dest; + ad->u.net->sport = th->source; + ad->u.net->dport = th->dest; break; } @@ -2877,8 +3771,20 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad if (uh == NULL) break; - ad->u.net.sport = uh->source; - ad->u.net.dport = uh->dest; + ad->u.net->sport = uh->source; + ad->u.net->dport = uh->dest; + break; + } + + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net->sport = dh->dccph_sport; + ad->u.net->dport = dh->dccph_dport; break; } @@ -2892,107 +3798,202 @@ out: #endif /* IPV6 */ -static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, - char **addrp, int *len, int src) +static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad, + char **_addrp, int src, u8 *proto) { - int ret = 0; + char *addrp; + int ret; - switch (ad->u.net.family) { + switch (ad->u.net->family) { case PF_INET: - ret = selinux_parse_skb_ipv4(skb, ad); - if (ret || !addrp) - break; - *len = 4; - *addrp = (char *)(src ? &ad->u.net.v4info.saddr : - &ad->u.net.v4info.daddr); - break; + ret = selinux_parse_skb_ipv4(skb, ad, proto); + if (ret) + goto parse_error; + addrp = (char *)(src ? &ad->u.net->v4info.saddr : + &ad->u.net->v4info.daddr); + goto okay; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case PF_INET6: - ret = selinux_parse_skb_ipv6(skb, ad); - if (ret || !addrp) - break; - *len = 16; - *addrp = (char *)(src ? &ad->u.net.v6info.saddr : - &ad->u.net.v6info.daddr); - break; + ret = selinux_parse_skb_ipv6(skb, ad, proto); + if (ret) + goto parse_error; + addrp = (char *)(src ? &ad->u.net->v6info.saddr : + &ad->u.net->v6info.daddr); + goto okay; #endif /* IPV6 */ default: - break; + addrp = NULL; + goto okay; } +parse_error: + printk(KERN_WARNING + "SELinux: failure in selinux_parse_skb()," + " unable to parse packet\n"); return ret; + +okay: + if (_addrp) + *_addrp = addrp; + return 0; } -/* socket security operations */ -static int socket_has_perm(struct task_struct *task, struct socket *sock, - u32 perms) +/** + * selinux_skb_peerlbl_sid - Determine the peer label of a packet + * @skb: the packet + * @family: protocol family + * @sid: the packet's peer label SID + * + * Description: + * Check the various different forms of network peer labeling and determine + * the peer label/SID for the packet; most of the magic actually occurs in + * the security server function security_net_peersid_cmp(). The function + * returns zero if the value in @sid is valid (although it may be SECSID_NULL) + * or -EACCES if @sid is invalid due to inconsistencies with the different + * peer labels. + * + */ +static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) { - struct inode_security_struct *isec; - struct task_security_struct *tsec; - struct avc_audit_data ad; - int err = 0; + int err; + u32 xfrm_sid; + u32 nlbl_sid; + u32 nlbl_type; - tsec = task->security; - isec = SOCK_INODE(sock)->i_security; + err = selinux_xfrm_skb_sid(skb, &xfrm_sid); + if (unlikely(err)) + return -EACCES; + err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); + if (unlikely(err)) + return -EACCES; - if (isec->sid == SECINITSID_KERNEL) - goto out; + err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); + if (unlikely(err)) { + printk(KERN_WARNING + "SELinux: failure in selinux_skb_peerlbl_sid()," + " unable to determine packet's peer label\n"); + return -EACCES; + } - AVC_AUDIT_DATA_INIT(&ad,NET); - ad.u.net.sk = sock->sk; - err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); + return 0; +} + +/** + * selinux_conn_sid - Determine the child socket label for a connection + * @sk_sid: the parent socket's SID + * @skb_sid: the packet's SID + * @conn_sid: the resulting connection SID + * + * If @skb_sid is valid then the user:role:type information from @sk_sid is + * combined with the MLS information from @skb_sid in order to create + * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy + * of @sk_sid. Returns zero on success, negative values on failure. + * + */ +static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) +{ + int err = 0; + + if (skb_sid != SECSID_NULL) + err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); + else + *conn_sid = sk_sid; -out: return err; } +/* socket security operations */ + +static int socket_sockcreate_sid(const struct task_security_struct *tsec, + u16 secclass, u32 *socksid) +{ + if (tsec->sockcreate_sid > SECSID_NULL) { + *socksid = tsec->sockcreate_sid; + return 0; + } + + return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL, + socksid); +} + +static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + u32 tsid = task_sid(task); + + if (sksec->sid == SECINITSID_KERNEL) + return 0; + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sk = sk; + + return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); +} + static int selinux_socket_create(int family, int type, int protocol, int kern) { - int err = 0; - struct task_security_struct *tsec; + const struct task_security_struct *tsec = current_security(); + u32 newsid; + u16 secclass; + int rc; if (kern) - goto out; + return 0; - tsec = current->security; - err = avc_has_perm(tsec->sid, tsec->sid, - socket_type_to_security_class(family, type, - protocol), SOCKET__CREATE, NULL); + secclass = socket_type_to_security_class(family, type, protocol); + rc = socket_sockcreate_sid(tsec, secclass, &newsid); + if (rc) + return rc; -out: - return err; + return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); } -static void selinux_socket_post_create(struct socket *sock, int family, - int type, int protocol, int kern) +static int selinux_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) { - struct inode_security_struct *isec; - struct task_security_struct *tsec; - - isec = SOCK_INODE(sock)->i_security; + const struct task_security_struct *tsec = current_security(); + struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct sk_security_struct *sksec; + int err = 0; - tsec = current->security; isec->sclass = socket_type_to_security_class(family, type, protocol); - isec->sid = kern ? SECINITSID_KERNEL : tsec->sid; + + if (kern) + isec->sid = SECINITSID_KERNEL; + else { + err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid)); + if (err) + return err; + } + isec->initialized = 1; - return; + if (sock->sk) { + sksec = sock->sk->sk_security; + sksec->sid = isec->sid; + sksec->sclass = isec->sclass; + err = selinux_netlbl_socket_post_create(sock->sk, family); + } + + return err; } /* 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) { + struct sock *sk = sock->sk; u16 family; int err; - err = socket_has_perm(current, sock, SOCKET__BIND); + err = sock_has_perm(current, sk, SOCKET__BIND); if (err) goto out; @@ -3001,78 +4002,83 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in * Multiple address binding for SCTP is not supported yet: we just * check the first address now. */ - family = sock->sk->sk_family; + family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; - struct inode_security_struct *isec; - struct task_security_struct *tsec; - struct avc_audit_data ad; + struct sk_security_struct *sksec = sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - struct sock *sk = sock->sk; - u32 sid, node_perm, addrlen; - - tsec = current->security; - isec = SOCK_INODE(sock)->i_security; + u32 sid, node_perm; if (family == PF_INET) { addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); - addrlen = sizeof(addr4->sin_addr.s_addr); addrp = (char *)&addr4->sin_addr.s_addr; } else { addr6 = (struct sockaddr_in6 *)address; snum = ntohs(addr6->sin6_port); - addrlen = sizeof(addr6->sin6_addr.s6_addr); addrp = (char *)&addr6->sin6_addr.s6_addr; } - 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) - goto out; - AVC_AUDIT_DATA_INIT(&ad,NET); - ad.u.net.sport = htons(snum); - ad.u.net.family = family; - err = avc_has_perm(isec->sid, sid, - isec->sclass, - SOCKET__NAME_BIND, &ad); - if (err) - goto out; + if (snum) { + int low, high; + + inet_get_local_port_range(sock_net(sk), &low, &high); + + if (snum < max(PROT_SOCK, low) || snum > high) { + err = sel_netport_sid(sk->sk_protocol, + snum, &sid); + if (err) + goto out; + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family; + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, + SOCKET__NAME_BIND, &ad); + if (err) + goto out; + } } - - switch(isec->sclass) { + + switch (sksec->sclass) { case SECCLASS_TCP_SOCKET: node_perm = TCP_SOCKET__NODE_BIND; break; - + case SECCLASS_UDP_SOCKET: node_perm = UDP_SOCKET__NODE_BIND; break; - + + case SECCLASS_DCCP_SOCKET: + node_perm = DCCP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; } - - err = security_node_sid(family, addrp, addrlen, &sid); + + err = sel_netnode_sid(addrp, family, &sid); if (err) goto out; - - AVC_AUDIT_DATA_INIT(&ad,NET); - ad.u.net.sport = htons(snum); - ad.u.net.family = family; + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family; if (family == PF_INET) - ad.u.net.v4info.saddr = addr4->sin_addr.s_addr; + ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else - ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); + ad.u.net->v6info.saddr = addr6->sin6_addr; - err = avc_has_perm(isec->sid, sid, - isec->sclass, node_perm, &ad); + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, node_perm, &ad); if (err) goto out; } @@ -3082,24 +4088,25 @@ out: static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { - struct inode_security_struct *isec; + struct sock *sk = sock->sk; + struct sk_security_struct *sksec = sk->sk_security; int err; - err = socket_has_perm(current, sock, SOCKET__CONNECT); + err = sock_has_perm(current, sk, SOCKET__CONNECT); if (err) return err; /* - * If a TCP socket, check name_connect permission for the port. + * If a TCP or DCCP socket, check name_connect permission for the port. */ - isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET) { - struct sock *sk = sock->sk; - struct avc_audit_data ad; + if (sksec->sclass == SECCLASS_TCP_SOCKET || + sksec->sclass == SECCLASS_DCCP_SOCKET) { + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - u32 sid; + u32 sid, perm; if (sk->sk_family == PF_INET) { addr4 = (struct sockaddr_in *)address; @@ -3113,27 +4120,31 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, snum = ntohs(addr6->sin6_port); } - err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, snum, &sid); + err = sel_netport_sid(sk->sk_protocol, snum, &sid); if (err) goto out; - AVC_AUDIT_DATA_INIT(&ad,NET); - ad.u.net.dport = htons(snum); - ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, - TCP_SOCKET__NAME_CONNECT, &ad); + perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? + TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->dport = htons(snum); + ad.u.net->family = sk->sk_family; + err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); if (err) goto out; } + err = selinux_netlbl_socket_connect(sk, address); + out: return err; } static int selinux_socket_listen(struct socket *sock, int backlog) { - return socket_has_perm(current, sock, SOCKET__LISTEN); + return sock_has_perm(current, sock->sk, SOCKET__LISTEN); } static int selinux_socket_accept(struct socket *sock, struct socket *newsock) @@ -3142,7 +4153,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) struct inode_security_struct *isec; struct inode_security_struct *newisec; - err = socket_has_perm(current, sock, SOCKET__ACCEPT); + err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); if (err) return err; @@ -3157,227 +4168,242 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) } static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, - int size) + int size) { - return socket_has_perm(current, sock, SOCKET__WRITE); + return sock_has_perm(current, sock->sk, SOCKET__WRITE); } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags) { - return socket_has_perm(current, sock, SOCKET__READ); + return sock_has_perm(current, sock->sk, SOCKET__READ); } static int selinux_socket_getsockname(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } static int selinux_socket_getpeername(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } -static int selinux_socket_setsockopt(struct socket *sock,int level,int optname) +static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) { - return socket_has_perm(current, sock, SOCKET__SETOPT); + int err; + + err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); + if (err) + return err; + + return selinux_netlbl_socket_setsockopt(sock, level, optname); } static int selinux_socket_getsockopt(struct socket *sock, int level, int optname) { - return socket_has_perm(current, sock, SOCKET__GETOPT); + return sock_has_perm(current, sock->sk, SOCKET__GETOPT); } static int selinux_socket_shutdown(struct socket *sock, int how) { - return socket_has_perm(current, sock, SOCKET__SHUTDOWN); + return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); } -static int selinux_socket_unix_stream_connect(struct socket *sock, - struct socket *other, +static int selinux_socket_unix_stream_connect(struct sock *sock, + struct sock *other, struct sock *newsk) { - struct sk_security_struct *ssec; - struct inode_security_struct *isec; - struct inode_security_struct *other_isec; - struct avc_audit_data ad; + struct sk_security_struct *sksec_sock = sock->sk_security; + struct sk_security_struct *sksec_other = other->sk_security; + struct sk_security_struct *sksec_new = newsk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; int err; - err = secondary_ops->unix_stream_connect(sock, other, newsk); + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sk = other; + + err = avc_has_perm(sksec_sock->sid, sksec_other->sid, + sksec_other->sclass, + UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) return 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, &ad); + /* server child socket */ + sksec_new->peer_sid = sksec_sock->sid; + err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, + &sksec_new->sid); if (err) return err; /* connecting socket */ - ssec = sock->sk->sk_security; - ssec->peer_sid = other_isec->sid; - - /* server child socket */ - ssec = newsk->sk_security; - ssec->peer_sid = isec->sid; - + sksec_sock->peer_sid = sksec_new->sid; + 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; + struct sk_security_struct *ssec = sock->sk->sk_security; + struct sk_security_struct *osec = other->sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sk = other->sk; + + return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, + &ad); +} + +static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, + u32 peer_sid, + struct common_audit_data *ad) +{ int err; + u32 if_sid; + u32 node_sid; - isec = SOCK_INODE(sock)->i_security; - other_isec = SOCK_INODE(other)->i_security; + err = sel_netif_sid(ifindex, &if_sid); + if (err) + return err; + err = avc_has_perm(peer_sid, if_sid, + SECCLASS_NETIF, NETIF__INGRESS, ad); + if (err) + return err; + + err = sel_netnode_sid(addrp, family, &node_sid); + if (err) + return err; + return avc_has_perm(peer_sid, node_sid, + SECCLASS_NODE, NODE__RECVFROM, ad); +} - AVC_AUDIT_DATA_INIT(&ad,NET); - ad.u.net.sk = other->sk; +static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, + u16 family) +{ + int err = 0; + struct sk_security_struct *sksec = sk->sk_security; + u32 sk_sid = sksec->sid; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + char *addrp; - err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, SOCKET__SENDTO, &ad); + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->netif = skb->skb_iif; + ad.u.net->family = family; + err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) return err; - return 0; + if (selinux_secmark_enabled()) { + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + return err; + } + + err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); + if (err) + return err; + err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); + + return err; } static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { - u16 family; + int err; + struct sk_security_struct *sksec = sk->sk_security; + u16 family = sk->sk_family; + u32 sk_sid = sksec->sid; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; char *addrp; - int len, err = 0; - u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; - u32 sock_sid = 0; - u16 sock_class = 0; - struct socket *sock; - struct net_device *dev; - struct avc_audit_data ad; + u8 secmark_active; + u8 peerlbl_active; - family = sk->sk_family; if (family != PF_INET && family != PF_INET6) - goto out; + return 0; /* Handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) family = PF_INET; - read_lock_bh(&sk->sk_callback_lock); - sock = sk->sk_socket; - if (sock) { - struct inode *inode; - inode = SOCK_INODE(sock); - if (inode) { - struct inode_security_struct *isec; - isec = inode->i_security; - sock_sid = isec->sid; - sock_class = isec->sclass; - } - } - read_unlock_bh(&sk->sk_callback_lock); - if (!sock_sid) - goto out; - - dev = skb->dev; - if (!dev) - goto out; + /* If any sort of compatibility mode is enabled then handoff processing + * to the selinux_sock_rcv_skb_compat() function to deal with the + * special handling. We do this in an attempt to keep this function + * as fast and as clean as possible. */ + if (!selinux_policycap_netpeer) + return selinux_sock_rcv_skb_compat(sk, skb, family); - err = sel_netif_sids(dev, &if_sid, NULL); - if (err) - goto out; - - switch (sock_class) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_RECV; - node_perm = NODE__UDP_RECV; - recv_perm = UDP_SOCKET__RECV_MSG; - break; - - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_RECV; - node_perm = NODE__TCP_RECV; - recv_perm = TCP_SOCKET__RECV_MSG; - break; - - default: - netif_perm = NETIF__RAWIP_RECV; - node_perm = NODE__RAWIP_RECV; - break; - } - - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); - if (err) - goto out; + secmark_active = selinux_secmark_enabled(); + peerlbl_active = selinux_peerlbl_enabled(); + if (!secmark_active && !peerlbl_active) + return 0; - err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->netif = skb->skb_iif; + ad.u.net->family = family; + err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) - goto out; - - /* Fixme: this lookup is inefficient */ - err = security_node_sid(family, addrp, len, &node_sid); - if (err) - goto out; - - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); - if (err) - goto out; + return err; - if (recv_perm) { - u32 port_sid; + if (peerlbl_active) { + u32 peer_sid; - /* Fixme: make this more efficient */ - err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, ntohs(ad.u.net.sport), - &port_sid); + err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); if (err) - goto out; - - err = avc_has_perm(sock_sid, port_sid, - sock_class, recv_perm, &ad); + return err; + err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, + peer_sid, &ad); + if (err) { + selinux_netlbl_err(skb, err, 0); + return err; + } + err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, + PEER__RECV, &ad); + if (err) { + selinux_netlbl_err(skb, err, 0); + return err; + } } - if (!err) - err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); + if (secmark_active) { + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + return err; + } -out: return err; } -static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, + int __user *optlen, unsigned len) { int err = 0; char *scontext; u32 scontext_len; - struct sk_security_struct *ssec; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sock->sk->sk_security; + u32 peer_sid = SECSID_NULL; - isec = SOCK_INODE(sock)->i_security; - if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { - err = -ENOPROTOOPT; - goto out; - } + if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + sksec->sclass == SECCLASS_TCP_SOCKET) + peer_sid = sksec->peer_sid; + if (peer_sid == SECSID_NULL) + return -ENOPROTOOPT; - ssec = sock->sk->sk_security; - - err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); + err = security_sid_to_context(peer_sid, &scontext, &scontext_len); if (err) - goto out; + return err; if (scontext_len > len) { err = -ERANGE; @@ -3390,38 +4416,253 @@ static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, out_len: if (put_user(scontext_len, optlen)) err = -EFAULT; - kfree(scontext); -out: return err; } +static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) +{ + u32 peer_secid = SECSID_NULL; + u16 family; + + if (skb && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + else if (skb && skb->protocol == htons(ETH_P_IPV6)) + family = PF_INET6; + else if (sock) + family = sock->sk->sk_family; + else + goto out; + + if (sock && family == PF_UNIX) + selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid); + else if (skb) + selinux_skb_peerlbl_sid(skb, family, &peer_secid); + +out: + *secid = peer_secid; + if (peer_secid == SECSID_NULL) + return -EINVAL; + return 0; +} + static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { - return sk_alloc_security(sk, family, priority); + struct sk_security_struct *sksec; + + sksec = kzalloc(sizeof(*sksec), priority); + if (!sksec) + return -ENOMEM; + + sksec->peer_sid = SECINITSID_UNLABELED; + sksec->sid = SECINITSID_UNLABELED; + selinux_netlbl_sk_security_reset(sksec); + sk->sk_security = sksec; + + return 0; } static void selinux_sk_free_security(struct sock *sk) { - sk_free_security(sk); + struct sk_security_struct *sksec = sk->sk_security; + + sk->sk_security = NULL; + selinux_netlbl_sk_security_free(sksec); + kfree(sksec); } -static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir) +static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) { - struct inode_security_struct *isec; - u32 sock_sid = SECINITSID_ANY_SOCKET; + struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *newsksec = newsk->sk_security; + + newsksec->sid = sksec->sid; + newsksec->peer_sid = sksec->peer_sid; + newsksec->sclass = sksec->sclass; + + selinux_netlbl_sk_security_reset(newsksec); +} +static void selinux_sk_getsecid(struct sock *sk, u32 *secid) +{ if (!sk) - return selinux_no_sk_sid(fl); + *secid = SECINITSID_ANY_SOCKET; + else { + struct sk_security_struct *sksec = sk->sk_security; + + *secid = sksec->sid; + } +} + +static void selinux_sock_graft(struct sock *sk, struct socket *parent) +{ + struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; + struct sk_security_struct *sksec = sk->sk_security; + + if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || + sk->sk_family == PF_UNIX) + isec->sid = sksec->sid; + sksec->sclass = isec->sclass; +} + +static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct sk_security_struct *sksec = sk->sk_security; + int err; + u16 family = req->rsk_ops->family; + u32 connsid; + u32 peersid; + + err = selinux_skb_peerlbl_sid(skb, family, &peersid); + if (err) + return err; + err = selinux_conn_sid(sksec->sid, peersid, &connsid); + if (err) + return err; + req->secid = connsid; + req->peer_secid = peersid; + + return selinux_netlbl_inet_conn_request(req, family); +} + +static void selinux_inet_csk_clone(struct sock *newsk, + const struct request_sock *req) +{ + struct sk_security_struct *newsksec = newsk->sk_security; + + newsksec->sid = req->secid; + newsksec->peer_sid = req->peer_secid; + /* NOTE: Ideally, we should also get the isec->sid for the + new socket in sync, but we don't have the isec available yet. + So we will wait until sock_graft to do it, by which + time it will have been created and available. */ - read_lock_bh(&sk->sk_callback_lock); - isec = get_sock_isec(sk); + /* We don't need to take any sort of lock here as we are the only + * thread with access to newsksec */ + selinux_netlbl_inet_csk_clone(newsk, req->rsk_ops->family); +} + +static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) +{ + u16 family = sk->sk_family; + struct sk_security_struct *sksec = sk->sk_security; + + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + + selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); +} - if (isec) - sock_sid = isec->sid; +static void selinux_skb_owned_by(struct sk_buff *skb, struct sock *sk) +{ + skb_set_owner_w(skb, sk); +} - read_unlock_bh(&sk->sk_callback_lock); - return sock_sid; +static int selinux_secmark_relabel_packet(u32 sid) +{ + const struct task_security_struct *__tsec; + u32 tsid; + + __tsec = current_security(); + tsid = __tsec->sid; + + return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL); +} + +static void selinux_secmark_refcount_inc(void) +{ + atomic_inc(&selinux_secmark_refcount); +} + +static void selinux_secmark_refcount_dec(void) +{ + atomic_dec(&selinux_secmark_refcount); +} + +static void selinux_req_classify_flow(const struct request_sock *req, + struct flowi *fl) +{ + fl->flowi_secid = req->secid; +} + +static int selinux_tun_dev_alloc_security(void **security) +{ + struct tun_security_struct *tunsec; + + tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL); + if (!tunsec) + return -ENOMEM; + tunsec->sid = current_sid(); + + *security = tunsec; + return 0; +} + +static void selinux_tun_dev_free_security(void *security) +{ + kfree(security); +} + +static int selinux_tun_dev_create(void) +{ + u32 sid = current_sid(); + + /* we aren't taking into account the "sockcreate" SID since the socket + * that is being created here is not a socket in the traditional sense, + * instead it is a private sock, accessible only to the kernel, and + * representing a wide range of network traffic spanning multiple + * connections unlike traditional sockets - check the TUN driver to + * get a better understanding of why this socket is special */ + + return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, + NULL); +} + +static int selinux_tun_dev_attach_queue(void *security) +{ + struct tun_security_struct *tunsec = security; + + return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, + TUN_SOCKET__ATTACH_QUEUE, NULL); +} + +static int selinux_tun_dev_attach(struct sock *sk, void *security) +{ + struct tun_security_struct *tunsec = security; + struct sk_security_struct *sksec = sk->sk_security; + + /* we don't currently perform any NetLabel based labeling here and it + * isn't clear that we would want to do so anyway; while we could apply + * labeling without the support of the TUN user the resulting labeled + * traffic from the other end of the connection would almost certainly + * cause confusion to the TUN user that had no idea network labeling + * protocols were being used */ + + sksec->sid = tunsec->sid; + sksec->sclass = SECCLASS_TUN_SOCKET; + + return 0; +} + +static int selinux_tun_dev_open(void *security) +{ + struct tun_security_struct *tunsec = security; + u32 sid = current_sid(); + int err; + + err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET, + TUN_SOCKET__RELABELFROM, NULL); + if (err) + return err; + err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, + TUN_SOCKET__RELABELTO, NULL); + if (err) + return err; + tunsec->sid = sid; + + return 0; } static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) @@ -3429,23 +4670,22 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) int err = 0; u32 perm; struct nlmsghdr *nlh; - struct socket *sock = sk->sk_socket; - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; - - if (skb->len < NLMSG_SPACE(0)) { + struct sk_security_struct *sksec = sk->sk_security; + + if (skb->len < NLMSG_HDRLEN) { err = -EINVAL; goto out; } - nlh = (struct nlmsghdr *)skb->data; - - err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); + nlh = nlmsg_hdr(skb); + + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, "SELinux: unrecognized netlink message" " type=%hu for sclass=%hu\n", - nlh->nlmsg_type, isec->sclass); - if (!selinux_enforcing) + nlh->nlmsg_type, sksec->sclass); + if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } @@ -3455,201 +4695,352 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) goto out; } - err = socket_has_perm(current, sock, perm); + err = sock_has_perm(current, sk, perm); out: return err; } #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *), - u16 family) +static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, + u16 family) { + int err; char *addrp; - int len, err = NF_ACCEPT; - u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; + u32 peer_sid; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + u8 secmark_active; + u8 netlbl_active; + u8 peerlbl_active; + + if (!selinux_policycap_netpeer) + return NF_ACCEPT; + + secmark_active = selinux_secmark_enabled(); + netlbl_active = netlbl_enabled(); + peerlbl_active = selinux_peerlbl_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; + + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) + return NF_DROP; + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) + return NF_DROP; + + if (peerlbl_active) { + err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, + peer_sid, &ad); + if (err) { + selinux_netlbl_err(skb, err, 1); + return NF_DROP; + } + } + + if (secmark_active) + if (avc_has_perm(peer_sid, skb->secmark, + SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) + return NF_DROP; + + if (netlbl_active) + /* we do this in the FORWARD path and not the POST_ROUTING + * path because we want to make sure we apply the necessary + * labeling before IPsec is applied so we can leverage AH + * protection */ + if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_forward(skb, in->ifindex, PF_INET); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_forward(skb, in->ifindex, PF_INET6); +} +#endif /* IPV6 */ + +static unsigned int selinux_ip_output(struct sk_buff *skb, + u16 family) +{ struct sock *sk; - struct socket *sock; - struct inode *inode; - struct sk_buff *skb = *pskb; - struct inode_security_struct *isec; - struct avc_audit_data ad; - struct net_device *dev = (struct net_device *)out; - + u32 sid; + + if (!netlbl_enabled()) + return NF_ACCEPT; + + /* we do this in the LOCAL_OUT path and not the POST_ROUTING path + * because we want to make sure we apply the necessary labeling + * before IPsec is applied so we can leverage AH protection */ sk = skb->sk; - if (!sk) - goto out; - - sock = sk->sk_socket; - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; + if (sk) { + struct sk_security_struct *sksec; + + if (sk->sk_state == TCP_LISTEN) + /* if the socket is the listening state then this + * packet is a SYN-ACK packet which means it needs to + * be labeled based on the connection/request_sock and + * not the parent socket. unfortunately, we can't + * lookup the request_sock yet as it isn't queued on + * the parent socket until after the SYN-ACK is sent. + * the "solution" is to simply pass the packet as-is + * as any IP option based labeling should be copied + * from the initial connection request (in the IP + * layer). it is far from ideal, but until we get a + * security label in the packet itself this is the + * best we can do. */ + return NF_ACCEPT; + + /* standard practice, label using the parent socket */ + sksec = sk->sk_security; + sid = sksec->sid; + } else + sid = SECINITSID_KERNEL; + if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_output(skb, PF_INET); +} + +static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, + int ifindex, + u16 family) +{ + struct sock *sk = skb->sk; + struct sk_security_struct *sksec; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + char *addrp; + u8 proto; - err = sel_netif_sids(dev, &if_sid, NULL); - if (err) - goto out; + if (sk == NULL) + return NF_ACCEPT; + sksec = sk->sk_security; - isec = inode->i_security; - - switch (isec->sclass) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_SEND; - node_perm = NODE__UDP_SEND; - send_perm = UDP_SOCKET__SEND_MSG; - break; - - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_SEND; - node_perm = NODE__TCP_SEND; - send_perm = TCP_SOCKET__SEND_MSG; - break; - - default: - netif_perm = NETIF__RAWIP_SEND; - node_perm = NODE__RAWIP_SEND; - break; - } + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) + return NF_DROP; + if (selinux_secmark_enabled()) + if (avc_has_perm(sksec->sid, skb->secmark, + SECCLASS_PACKET, PACKET__SEND, &ad)) + return NF_DROP_ERR(-ECONNREFUSED); - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; + if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) + return NF_DROP_ERR(-ECONNREFUSED); - err = selinux_parse_skb(skb, &ad, &addrp, - &len, 0) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; + return NF_ACCEPT; +} - err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, - netif_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; - - /* Fixme: this lookup is inefficient */ - err = security_node_sid(family, addrp, len, - &node_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; - - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, - node_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; +static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, + u16 family) +{ + u32 secmark_perm; + u32 peer_sid; + struct sock *sk; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + char *addrp; + u8 secmark_active; + u8 peerlbl_active; - if (send_perm) { - u32 port_sid; - - /* Fixme: make this more efficient */ - err = security_port_sid(sk->sk_family, - sk->sk_type, - sk->sk_protocol, - ntohs(ad.u.net.dport), - &port_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; + /* If any sort of compatibility mode is enabled then handoff processing + * to the selinux_ip_postroute_compat() function to deal with the + * special handling. We do this in an attempt to keep this function + * as fast and as clean as possible. */ + if (!selinux_policycap_netpeer) + return selinux_ip_postroute_compat(skb, ifindex, family); - err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, &ad) ? NF_DROP : NF_ACCEPT; - } + secmark_active = selinux_secmark_enabled(); + peerlbl_active = selinux_peerlbl_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; + sk = skb->sk; - err = selinux_xfrm_postroute_last(isec->sid, skb); +#ifdef CONFIG_XFRM + /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec + * packet transformation so allow the packet to pass without any checks + * since we'll have another chance to perform access control checks + * when the packet is on it's final way out. + * NOTE: there appear to be some IPv6 multicast cases where skb->dst + * is NULL, in this case go ahead and apply access control. + * NOTE: if this is a local socket (skb->sk != NULL) that is in the + * TCP listening state we cannot wait until the XFRM processing + * is done as we will miss out on the SA label if we do; + * unfortunately, this means more work, but it is only once per + * connection. */ + if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && + !(sk != NULL && sk->sk_state == TCP_LISTEN)) + return NF_ACCEPT; +#endif -out: - return err; + if (sk == NULL) { + /* Without an associated socket the packet is either coming + * from the kernel or it is being forwarded; check the packet + * to determine which and if the packet is being forwarded + * query the packet directly to determine the security label. */ + if (skb->skb_iif) { + secmark_perm = PACKET__FORWARD_OUT; + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) + return NF_DROP; + } else { + secmark_perm = PACKET__SEND; + peer_sid = SECINITSID_KERNEL; + } + } else if (sk->sk_state == TCP_LISTEN) { + /* Locally generated packet but the associated socket is in the + * listening state which means this is a SYN-ACK packet. In + * this particular case the correct security label is assigned + * to the connection/request_sock but unfortunately we can't + * query the request_sock as it isn't queued on the parent + * socket until after the SYN-ACK packet is sent; the only + * viable choice is to regenerate the label like we do in + * selinux_inet_conn_request(). See also selinux_ip_output() + * for similar problems. */ + u32 skb_sid; + struct sk_security_struct *sksec = sk->sk_security; + if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) + return NF_DROP; + /* At this point, if the returned skb peerlbl is SECSID_NULL + * and the packet has been through at least one XFRM + * transformation then we must be dealing with the "final" + * form of labeled IPsec packet; since we've already applied + * all of our access controls on this packet we can safely + * pass the packet. */ + if (skb_sid == SECSID_NULL) { + switch (family) { + case PF_INET: + if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) + return NF_ACCEPT; + break; + case PF_INET6: + if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) + return NF_ACCEPT; + default: + return NF_DROP_ERR(-ECONNREFUSED); + } + } + if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) + return NF_DROP; + secmark_perm = PACKET__SEND; + } else { + /* Locally generated packet, fetch the security label from the + * associated socket. */ + struct sk_security_struct *sksec = sk->sk_security; + peer_sid = sksec->sid; + secmark_perm = PACKET__SEND; + } + + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->netif = ifindex; + ad.u.net->family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) + return NF_DROP; + + if (secmark_active) + if (avc_has_perm(peer_sid, skb->secmark, + SECCLASS_PACKET, secmark_perm, &ad)) + return NF_DROP_ERR(-ECONNREFUSED); + + if (peerlbl_active) { + u32 if_sid; + u32 node_sid; + + if (sel_netif_sid(ifindex, &if_sid)) + return NF_DROP; + if (avc_has_perm(peer_sid, if_sid, + SECCLASS_NETIF, NETIF__EGRESS, &ad)) + return NF_DROP_ERR(-ECONNREFUSED); + + if (sel_netnode_sid(addrp, family, &node_sid)) + return NF_DROP; + if (avc_has_perm(peer_sid, node_sid, + SECCLASS_NODE, NODE__SENDTO, &ad)) + return NF_DROP_ERR(-ECONNREFUSED); + } + + return NF_ACCEPT; } -static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET); + return selinux_ip_postroute(skb, out->ifindex, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6); + return selinux_ip_postroute(skb, out->ifindex, PF_INET6); } - #endif /* IPV6 */ #endif /* CONFIG_NETFILTER */ -#else - -static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) -{ - return 0; -} - -#endif /* CONFIG_SECURITY_NETWORK */ - static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) { - struct task_security_struct *tsec; - struct av_decision avd; int err; - err = secondary_ops->netlink_send(sk, skb); + err = cap_netlink_send(sk, skb); if (err) return err; - tsec = current->security; - - avd.allowed = 0; - avc_has_perm_noaudit(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, ~0, &avd); - cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed); - - if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) - err = selinux_nlmsg_perm(sk, skb); - - return err; -} - -static int selinux_netlink_recv(struct sk_buff *skb) -{ - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) - return -EPERM; - return 0; + return selinux_nlmsg_perm(sk, skb); } 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; + u32 sid; isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); if (!isec) return -ENOMEM; - isec->magic = SELINUX_MAGIC; + sid = task_sid(task); isec->sclass = sclass; - isec->ipc_perm = perm; - if (tsec) { - isec->sid = tsec->sid; - } else { - isec->sid = SECINITSID_UNLABELED; - } + isec->sid = sid; perm->security = isec; return 0; @@ -3658,9 +5049,6 @@ static int ipc_alloc_security(struct task_struct *task, 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); } @@ -3673,8 +5061,6 @@ static int msg_msg_alloc_security(struct msg_msg *msg) if (!msec) return -ENOMEM; - msec->magic = SELINUX_MAGIC; - msec->msg = msg; msec->sid = SECINITSID_UNLABELED; msg->security = msec; @@ -3684,8 +5070,6 @@ static int msg_msg_alloc_security(struct msg_msg *msg) 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); @@ -3694,17 +5078,16 @@ static void msg_msg_free_security(struct msg_msg *msg) static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, u32 perms) { - struct task_security_struct *tsec; struct ipc_security_struct *isec; - struct avc_audit_data ad; + struct common_audit_data ad; + u32 sid = current_sid(); - tsec = current->security; isec = ipc_perms->security; - AVC_AUDIT_DATA_INIT(&ad, IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = ipc_perms->key; - return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); + return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); } static int selinux_msg_msg_alloc_security(struct msg_msg *msg) @@ -3720,22 +5103,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *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; + struct common_audit_data ad; + u32 sid = current_sid(); 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; + ad.type = LSM_AUDIT_DATA_IPC; + ad.u.ipc_id = msq->q_perm.key; - rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, MSGQ__CREATE, &ad); if (rc) { ipc_free_security(&msq->q_perm); @@ -3751,17 +5133,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq) 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; + struct common_audit_data ad; + u32 sid = current_sid(); - tsec = current->security; isec = msq->q_perm.security; - AVC_AUDIT_DATA_INIT(&ad, IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->q_perm.key; - return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, + return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, MSGQ__ASSOCIATE, &ad); } @@ -3770,7 +5151,7 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) int err; int perms; - switch(cmd) { + switch (cmd) { case IPC_INFO: case MSG_INFO: /* No specific object, just general system-wide information. */ @@ -3795,13 +5176,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) 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; + struct common_audit_data ad; + u32 sid = current_sid(); int rc; - tsec = current->security; isec = msq->q_perm.security; msec = msg->security; @@ -3813,28 +5193,26 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, * 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); + rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG, + NULL, &msec->sid); if (rc) return rc; } - AVC_AUDIT_DATA_INIT(&ad, IPC); + ad.type = LSM_AUDIT_DATA_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, + rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, MSGQ__WRITE, &ad); if (!rc) /* Can this process send the message */ - rc = avc_has_perm(tsec->sid, msec->sid, - SECCLASS_MSG, MSG__SEND, &ad); + rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG, + MSG__SEND, &ad); if (!rc) /* Can the message be put in the queue? */ - rc = avc_has_perm(msec->sid, isec->sid, - SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad); + rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ, + MSGQ__ENQUEUE, &ad); return rc; } @@ -3843,23 +5221,22 @@ 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; + struct common_audit_data ad; + u32 sid = task_sid(target); 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; + ad.type = LSM_AUDIT_DATA_IPC; + ad.u.ipc_id = msq->q_perm.key; - rc = avc_has_perm(tsec->sid, isec->sid, + rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, MSGQ__READ, &ad); if (!rc) - rc = avc_has_perm(tsec->sid, msec->sid, + rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG, MSG__RECEIVE, &ad); return rc; } @@ -3867,22 +5244,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, /* 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; + struct common_audit_data ad; + u32 sid = current_sid(); 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; + ad.type = LSM_AUDIT_DATA_IPC; + ad.u.ipc_id = shp->shm_perm.key; - rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, + rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM, SHM__CREATE, &ad); if (rc) { ipc_free_security(&shp->shm_perm); @@ -3898,17 +5274,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp) 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; + struct common_audit_data ad; + u32 sid = current_sid(); - tsec = current->security; isec = shp->shm_perm.security; - AVC_AUDIT_DATA_INIT(&ad, IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->shm_perm.key; - return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, + return avc_has_perm(sid, isec->sid, SECCLASS_SHM, SHM__ASSOCIATE, &ad); } @@ -3918,7 +5293,7 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd) int perms; int err; - switch(cmd) { + switch (cmd) { case IPC_INFO: case SHM_INFO: /* No specific object, just general system-wide information. */ @@ -3949,11 +5324,6 @@ static int selinux_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg) { u32 perms; - int rc; - - rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg); - if (rc) - return rc; if (shmflg & SHM_RDONLY) perms = SHM__READ; @@ -3966,22 +5336,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp, /* 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; + struct common_audit_data ad; + u32 sid = current_sid(); 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; + ad.type = LSM_AUDIT_DATA_IPC; + ad.u.ipc_id = sma->sem_perm.key; - rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, + rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM, SEM__CREATE, &ad); if (rc) { ipc_free_security(&sma->sem_perm); @@ -3997,17 +5366,16 @@ static void selinux_sem_free_security(struct sem_array *sma) 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; + struct common_audit_data ad; + u32 sid = current_sid(); - tsec = current->security; isec = sma->sem_perm.security; - AVC_AUDIT_DATA_INIT(&ad, IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->sem_perm.key; - return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, + return avc_has_perm(sid, isec->sid, SECCLASS_SEM, SEM__ASSOCIATE, &ad); } @@ -4017,7 +5385,7 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd) int err; u32 perms; - switch(cmd) { + switch (cmd) { case IPC_INFO: case SEM_INFO: /* No specific object, just general system-wide information. */ @@ -4082,93 +5450,71 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) return ipc_has_perm(ipcp, av); } -/* module stacking operations */ -static 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; -} - -static int selinux_unregister_security (const char *name, struct security_operations *ops) +static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) { - 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; + struct ipc_security_struct *isec = ipcp->security; + *secid = isec->sid; } -static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode) +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) + char *name, char **value) { - struct task_security_struct *tsec; - u32 sid, len; - char *context; + const struct task_security_struct *__tsec; + u32 sid; int error; + unsigned len; if (current != p) { - error = task_has_perm(current, p, PROCESS__GETATTR); + error = current_has_perm(p, PROCESS__GETATTR); if (error) return error; } - if (!size) - return -ERANGE; - - tsec = p->security; + rcu_read_lock(); + __tsec = __task_cred(p)->security; if (!strcmp(name, "current")) - sid = tsec->sid; + sid = __tsec->sid; else if (!strcmp(name, "prev")) - sid = tsec->osid; + sid = __tsec->osid; else if (!strcmp(name, "exec")) - sid = tsec->exec_sid; + sid = __tsec->exec_sid; else if (!strcmp(name, "fscreate")) - sid = tsec->create_sid; + sid = __tsec->create_sid; + else if (!strcmp(name, "keycreate")) + sid = __tsec->keycreate_sid; + else if (!strcmp(name, "sockcreate")) + sid = __tsec->sockcreate_sid; else - return -EINVAL; + goto invalid; + rcu_read_unlock(); if (!sid) return 0; - error = security_sid_to_context(sid, &context, &len); + error = security_sid_to_context(sid, value, &len); if (error) return error; - if (len > size) { - kfree(context); - return -ERANGE; - } - memcpy(value, context, len); - kfree(context); return len; + +invalid: + rcu_read_unlock(); + return -EINVAL; } static int selinux_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - u32 sid = 0; + struct task_struct *tracer; + struct cred *new; + u32 sid = 0, ptsid; int error; char *str = value; @@ -4184,11 +5530,15 @@ static int selinux_setprocattr(struct task_struct *p, * above restriction is ever removed. */ if (!strcmp(name, "exec")) - error = task_has_perm(current, p, PROCESS__SETEXEC); + error = current_has_perm(p, PROCESS__SETEXEC); else if (!strcmp(name, "fscreate")) - error = task_has_perm(current, p, PROCESS__SETFSCREATE); + error = current_has_perm(p, PROCESS__SETFSCREATE); + else if (!strcmp(name, "keycreate")) + error = current_has_perm(p, PROCESS__SETKEYCREATE); + else if (!strcmp(name, "sockcreate")) + error = current_has_perm(p, PROCESS__SETSOCKCREATE); else if (!strcmp(name, "current")) - error = task_has_perm(current, p, PROCESS__SETCURRENT); + error = current_has_perm(p, PROCESS__SETCURRENT); else error = -EINVAL; if (error) @@ -4200,79 +5550,224 @@ static int selinux_setprocattr(struct task_struct *p, str[size-1] = 0; size--; } - error = security_context_to_sid(value, size, &sid); + error = security_context_to_sid(value, size, &sid, GFP_KERNEL); + if (error == -EINVAL && !strcmp(name, "fscreate")) { + if (!capable(CAP_MAC_ADMIN)) { + struct audit_buffer *ab; + size_t audit_size; + + /* We strip a nul only if it is at the end, otherwise the + * context contains a nul and we should audit that */ + if (str[size - 1] == '\0') + audit_size = size - 1; + else + audit_size = size; + ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + audit_log_format(ab, "op=fscreate invalid_context="); + audit_log_n_untrustedstring(ab, value, audit_size); + audit_log_end(ab); + + return error; + } + error = security_context_to_sid_force(value, size, + &sid); + } if (error) return error; } + new = prepare_creds(); + if (!new) + return -ENOMEM; + /* 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 + operation. See selinux_bprm_set_creds 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 = new->security; + if (!strcmp(name, "exec")) { tsec->exec_sid = sid; - else if (!strcmp(name, "fscreate")) + } else if (!strcmp(name, "fscreate")) { tsec->create_sid = sid; - else if (!strcmp(name, "current")) { - struct av_decision avd; - + } else if (!strcmp(name, "keycreate")) { + error = may_create_key(sid, p); + if (error) + goto abort_change; + tsec->keycreate_sid = sid; + } else if (!strcmp(name, "sockcreate")) { + tsec->sockcreate_sid = sid; + } else if (!strcmp(name, "current")) { + error = -EINVAL; if (sid == 0) - return -EINVAL; + goto abort_change; /* Only allow single threaded processes to change context */ - if (atomic_read(&p->mm->mm_users) != 1) { - struct task_struct *g, *t; - struct mm_struct *mm = p->mm; - read_lock(&tasklist_lock); - do_each_thread(g, t) - if (t->mm == mm && t != p) { - read_unlock(&tasklist_lock); - return -EPERM; - } - while_each_thread(g, t); - read_unlock(&tasklist_lock); - } + error = -EPERM; + if (!current_is_single_threaded()) { + error = security_bounded_transition(tsec->sid, sid); + if (error) + goto abort_change; + } /* Check permissions for the transition. */ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, - PROCESS__DYNTRANSITION, NULL); + PROCESS__DYNTRANSITION, NULL); if (error) - return error; + goto abort_change; /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ - task_lock(p); - if (p->ptrace & PT_PTRACED) { - error = avc_has_perm_noaudit(tsec->ptrace_sid, sid, - SECCLASS_PROCESS, - PROCESS__PTRACE, &avd); - if (!error) - tsec->sid = sid; - task_unlock(p); - avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS, - PROCESS__PTRACE, &avd, error, NULL); + ptsid = 0; + rcu_read_lock(); + tracer = ptrace_parent(p); + if (tracer) + ptsid = task_sid(tracer); + rcu_read_unlock(); + + if (tracer) { + error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, + PROCESS__PTRACE, NULL); if (error) - return error; - } else { - tsec->sid = sid; - task_unlock(p); + goto abort_change; } + + tsec->sid = sid; + } else { + error = -EINVAL; + goto abort_change; } - else - return -EINVAL; + commit_creds(new); return size; + +abort_change: + abort_creds(new); + return error; +} + +static int selinux_ismaclabel(const char *name) +{ + return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0); +} + +static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ + return security_sid_to_context(secid, secdata, seclen); +} + +static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) +{ + return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL); +} + +static void selinux_release_secctx(char *secdata, u32 seclen) +{ + kfree(secdata); +} + +/* + * called with inode->i_mutex locked + */ +static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) +{ + return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0); } +/* + * called with inode->i_mutex locked + */ +static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) +{ + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0); +} + +static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) +{ + int len = 0; + len = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX, + ctx, true); + if (len < 0) + return len; + *ctxlen = len; + return 0; +} +#ifdef CONFIG_KEYS + +static int selinux_key_alloc(struct key *k, const struct cred *cred, + unsigned long flags) +{ + const struct task_security_struct *tsec; + struct key_security_struct *ksec; + + ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL); + if (!ksec) + return -ENOMEM; + + tsec = cred->security; + if (tsec->keycreate_sid) + ksec->sid = tsec->keycreate_sid; + else + ksec->sid = tsec->sid; + + k->security = ksec; + return 0; +} + +static void selinux_key_free(struct key *k) +{ + struct key_security_struct *ksec = k->security; + + k->security = NULL; + kfree(ksec); +} + +static int selinux_key_permission(key_ref_t key_ref, + const struct cred *cred, + unsigned perm) +{ + struct key *key; + struct key_security_struct *ksec; + u32 sid; + + /* if no specific permissions are requested, we skip the + permission check. No serious, additional covert channels + appear to be created. */ + if (perm == 0) + return 0; + + sid = cred_sid(cred); + + key = key_ref_to_ptr(key_ref); + ksec = key->security; + + return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL); +} + +static int selinux_key_getsecurity(struct key *key, char **_buffer) +{ + struct key_security_struct *ksec = key->security; + char *context = NULL; + unsigned len; + int rc; + + rc = security_sid_to_context(ksec->sid, &context, &len); + if (!rc) + rc = len; + *_buffer = context; + return rc; +} + +#endif + static struct security_operations selinux_ops = { - .ptrace = selinux_ptrace, + .name = "selinux", + + .ptrace_access_check = selinux_ptrace_access_check, + .ptrace_traceme = selinux_ptrace_traceme, .capget = selinux_capget, - .capset_check = selinux_capset_check, - .capset_set = selinux_capset_set, - .sysctl = selinux_sysctl, + .capset = selinux_capset, .capable = selinux_capable, .quotactl = selinux_quotactl, .quota_on = selinux_quota_on, @@ -4280,23 +5775,26 @@ static struct security_operations selinux_ops = { .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_apply_creds = selinux_bprm_apply_creds, - .bprm_post_apply_creds = selinux_bprm_post_apply_creds, - .bprm_set_security = selinux_bprm_set_security, - .bprm_check_security = selinux_bprm_check_security, + + .bprm_set_creds = selinux_bprm_set_creds, + .bprm_committing_creds = selinux_bprm_committing_creds, + .bprm_committed_creds = selinux_bprm_committed_creds, .bprm_secureexec = selinux_bprm_secureexec, .sb_alloc_security = selinux_sb_alloc_security, .sb_free_security = selinux_sb_free_security, .sb_copy_data = selinux_sb_copy_data, - .sb_kern_mount = selinux_sb_kern_mount, + .sb_remount = selinux_sb_remount, + .sb_kern_mount = selinux_sb_kern_mount, + .sb_show_options = selinux_sb_show_options, .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, .sb_umount = selinux_umount, + .sb_set_mnt_opts = selinux_set_mnt_opts, + .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, + .sb_parse_opts_str = selinux_parse_opts_str, + + .dentry_init_security = selinux_dentry_init_security, .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, @@ -4319,15 +5817,17 @@ static struct security_operations selinux_ops = { .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, + .inode_getsecurity = selinux_inode_getsecurity, + .inode_setsecurity = selinux_inode_setsecurity, + .inode_listsecurity = selinux_inode_listsecurity, + .inode_getsecid = selinux_inode_getsecid, .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, + .mmap_file = selinux_mmap_file, + .mmap_addr = selinux_mmap_addr, .file_mprotect = selinux_file_mprotect, .file_lock = selinux_file_lock, .file_fcntl = selinux_file_fcntl, @@ -4335,27 +5835,33 @@ static struct security_operations selinux_ops = { .file_send_sigiotask = selinux_file_send_sigiotask, .file_receive = selinux_file_receive, + .file_open = selinux_file_open, + .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, + .cred_alloc_blank = selinux_cred_alloc_blank, + .cred_free = selinux_cred_free, + .cred_prepare = selinux_cred_prepare, + .cred_transfer = selinux_cred_transfer, + .kernel_act_as = selinux_kernel_act_as, + .kernel_create_files_as = selinux_kernel_create_files_as, + .kernel_module_request = selinux_kernel_module_request, .task_setpgid = selinux_task_setpgid, .task_getpgid = selinux_task_getpgid, - .task_getsid = selinux_task_getsid, - .task_setgroups = selinux_task_setgroups, + .task_getsid = selinux_task_getsid, + .task_getsecid = selinux_task_getsecid, .task_setnice = selinux_task_setnice, + .task_setioprio = selinux_task_setioprio, + .task_getioprio = selinux_task_getioprio, .task_setrlimit = selinux_task_setrlimit, .task_setscheduler = selinux_task_setscheduler, .task_getscheduler = selinux_task_getscheduler, + .task_movememory = selinux_task_movememory, .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, + .task_to_inode = selinux_task_to_inode, .ipc_permission = selinux_ipc_permission, + .ipc_getsecid = selinux_ipc_getsecid, .msg_msg_alloc_security = selinux_msg_msg_alloc_security, .msg_msg_free_security = selinux_msg_msg_free_security, @@ -4373,22 +5879,26 @@ static struct security_operations selinux_ops = { .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_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, - .d_instantiate = selinux_d_instantiate, + .getprocattr = selinux_getprocattr, + .setprocattr = selinux_setprocattr, - .getprocattr = selinux_getprocattr, - .setprocattr = selinux_setprocattr, + .ismaclabel = selinux_ismaclabel, + .secid_to_secctx = selinux_secid_to_secctx, + .secctx_to_secid = selinux_secctx_to_secid, + .release_secctx = selinux_release_secctx, + .inode_notifysecctx = selinux_inode_notifysecctx, + .inode_setsecctx = selinux_inode_setsecctx, + .inode_getsecctx = selinux_inode_getsecctx, -#ifdef CONFIG_SECURITY_NETWORK - .unix_stream_connect = selinux_socket_unix_stream_connect, + .unix_stream_connect = selinux_socket_unix_stream_connect, .unix_may_send = selinux_socket_unix_may_send, .socket_create = selinux_socket_create, @@ -4405,25 +5915,63 @@ static struct security_operations selinux_ops = { .socket_setsockopt = selinux_socket_setsockopt, .socket_shutdown = selinux_socket_shutdown, .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, - .socket_getpeersec = selinux_socket_getpeersec, + .socket_getpeersec_stream = selinux_socket_getpeersec_stream, + .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram, .sk_alloc_security = selinux_sk_alloc_security, .sk_free_security = selinux_sk_free_security, - .sk_getsid = selinux_sk_getsid_security, -#endif + .sk_clone_security = selinux_sk_clone_security, + .sk_getsecid = selinux_sk_getsecid, + .sock_graft = selinux_sock_graft, + .inet_conn_request = selinux_inet_conn_request, + .inet_csk_clone = selinux_inet_csk_clone, + .inet_conn_established = selinux_inet_conn_established, + .secmark_relabel_packet = selinux_secmark_relabel_packet, + .secmark_refcount_inc = selinux_secmark_refcount_inc, + .secmark_refcount_dec = selinux_secmark_refcount_dec, + .req_classify_flow = selinux_req_classify_flow, + .tun_dev_alloc_security = selinux_tun_dev_alloc_security, + .tun_dev_free_security = selinux_tun_dev_free_security, + .tun_dev_create = selinux_tun_dev_create, + .tun_dev_attach_queue = selinux_tun_dev_attach_queue, + .tun_dev_attach = selinux_tun_dev_attach, + .tun_dev_open = selinux_tun_dev_open, + .skb_owned_by = selinux_skb_owned_by, #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc, .xfrm_policy_clone_security = selinux_xfrm_policy_clone, .xfrm_policy_free_security = selinux_xfrm_policy_free, - .xfrm_state_alloc_security = selinux_xfrm_state_alloc, + .xfrm_policy_delete_security = selinux_xfrm_policy_delete, + .xfrm_state_alloc = selinux_xfrm_state_alloc, + .xfrm_state_alloc_acquire = selinux_xfrm_state_alloc_acquire, .xfrm_state_free_security = selinux_xfrm_state_free, - .xfrm_policy_lookup = selinux_xfrm_policy_lookup, + .xfrm_state_delete_security = selinux_xfrm_state_delete, + .xfrm_policy_lookup = selinux_xfrm_policy_lookup, + .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match, + .xfrm_decode_session = selinux_xfrm_decode_session, +#endif + +#ifdef CONFIG_KEYS + .key_alloc = selinux_key_alloc, + .key_free = selinux_key_free, + .key_permission = selinux_key_permission, + .key_getsecurity = selinux_key_getsecurity, +#endif + +#ifdef CONFIG_AUDIT + .audit_rule_init = selinux_audit_rule_init, + .audit_rule_known = selinux_audit_rule_known, + .audit_rule_match = selinux_audit_rule_match, + .audit_rule_free = selinux_audit_rule_free, #endif }; static __init int selinux_init(void) { - struct task_security_struct *tsec; + if (!security_module_enable(&selinux_ops)) { + selinux_enabled = 0; + return 0; + } if (!selinux_enabled) { printk(KERN_INFO "SELinux: Disabled at boot.\n"); @@ -4433,78 +5981,87 @@ static __init int selinux_init(void) 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; + cred_init_security(); + + default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC); + sel_inode_cache = kmem_cache_create("selinux_inode_security", + sizeof(struct inode_security_struct), + 0, SLAB_PANIC, NULL); avc_init(); - original_ops = secondary_ops = security_ops; - if (!secondary_ops) - panic ("SELinux: No initial security operations\n"); - if (register_security (&selinux_ops)) + 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"); - } + if (selinux_enforcing) + printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); + else + printk(KERN_DEBUG "SELinux: Starting in permissive mode\n"); + return 0; } +static void delayed_superblock_init(struct super_block *sb, void *unused) +{ + superblock_doinit(sb, NULL); +} + void selinux_complete_init(void) { - printk(KERN_INFO "SELinux: Completing initialization.\n"); + printk(KERN_DEBUG "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, NULL); - drop_super(sb); - spin_lock(&sb_security_lock); - list_del_init(&sbsec->list); - goto next_sb; - } - spin_unlock(&sb_security_lock); + printk(KERN_DEBUG "SELinux: Setting up existing superblocks.\n"); + iterate_supers(delayed_superblock_init, NULL); } /* SELinux requires early initialization in order to label all processes and objects when they are created. */ security_initcall(selinux_init); -#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER) - -static struct nf_hook_ops selinux_ipv4_op = { - .hook = selinux_ipv4_postroute_last, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_SELINUX_LAST, +#if defined(CONFIG_NETFILTER) + +static struct nf_hook_ops selinux_ipv4_ops[] = { + { + .hook = selinux_ipv4_postroute, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_SELINUX_LAST, + }, + { + .hook = selinux_ipv4_forward, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_SELINUX_FIRST, + }, + { + .hook = selinux_ipv4_output, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_SELINUX_FIRST, + } }; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static struct nf_hook_ops selinux_ipv6_op = { - .hook = selinux_ipv6_postroute_last, - .owner = THIS_MODULE, - .pf = PF_INET6, - .hooknum = NF_IP6_POST_ROUTING, - .priority = NF_IP6_PRI_SELINUX_LAST, +static struct nf_hook_ops selinux_ipv6_ops[] = { + { + .hook = selinux_ipv6_postroute, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP6_PRI_SELINUX_LAST, + }, + { + .hook = selinux_ipv6_forward, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP6_PRI_SELINUX_FIRST, + } }; #endif /* IPV6 */ @@ -4515,19 +6072,17 @@ static int __init selinux_nf_ip_init(void) if (!selinux_enabled) goto out; - - printk(KERN_INFO "SELinux: Registering netfilter hooks\n"); - - err = nf_register_hook(&selinux_ipv4_op); - if (err) - panic("SELinux: nf_register_hook for IPv4: error %d\n", err); -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); - err = nf_register_hook(&selinux_ipv6_op); + err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); if (err) - panic("SELinux: nf_register_hook for IPv6: error %d\n", err); + panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); + if (err) + panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); #endif /* IPV6 */ out: @@ -4539,29 +6094,28 @@ __initcall(selinux_nf_ip_init); #ifdef CONFIG_SECURITY_SELINUX_DISABLE static void selinux_nf_ip_exit(void) { - printk(KERN_INFO "SELinux: Unregistering netfilter hooks\n"); + printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); - nf_unregister_hook(&selinux_ipv4_op); + nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - nf_unregister_hook(&selinux_ipv6_op); + nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); #endif /* IPV6 */ } #endif -#else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ +#else /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE #define selinux_nf_ip_exit() #endif -#endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ +#endif /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE +static int selinux_disabled; + int selinux_disable(void) { - extern void exit_sel_fs(void); - static int selinux_disabled = 0; - if (ss_initialized) { /* Not permitted after initial policy load. */ return -EINVAL; @@ -4575,9 +6129,12 @@ int selinux_disable(void) printk(KERN_INFO "SELinux: Disabled at runtime.\n"); selinux_disabled = 1; + selinux_enabled = 0; - /* Reset security_ops to the secondary module, dummy or capability. */ - security_ops = secondary_ops; + reset_security_ops(); + + /* Try to destroy the avc node cache */ + avc_disable(); /* Unregister netfilter hooks. */ selinux_nf_ip_exit(); @@ -4588,5 +6145,3 @@ int selinux_disable(void) return 0; } #endif - - diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h new file mode 100644 index 00000000000..1bdf973433c --- /dev/null +++ b/security/selinux/include/audit.h @@ -0,0 +1,65 @@ +/* + * SELinux support for the Audit LSM hooks + * + * Most of below header was moved from include/linux/selinux.h which + * is released under below copyrights: + * + * Author: James Morris <jmorris@redhat.com> + * + * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> + * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ + +#ifndef _SELINUX_AUDIT_H +#define _SELINUX_AUDIT_H + +/** + * selinux_audit_rule_init - alloc/init an selinux audit rule structure. + * @field: the field this rule refers to + * @op: the operater the rule uses + * @rulestr: the text "target" of the rule + * @rule: pointer to the new rule structure returned via this + * + * Returns 0 if successful, -errno if not. On success, the rule structure + * will be allocated internally. The caller must free this structure with + * selinux_audit_rule_free() after use. + */ +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **rule); + +/** + * selinux_audit_rule_free - free an selinux audit rule structure. + * @rule: pointer to the audit rule to be freed + * + * This will free all memory associated with the given rule. + * If @rule is NULL, no operation is performed. + */ +void selinux_audit_rule_free(void *rule); + +/** + * selinux_audit_rule_match - determine if a context ID matches a rule. + * @sid: the context ID to check + * @field: the field this rule refers to + * @op: the operater the rule uses + * @rule: pointer to the audit rule to check against + * @actx: the audit context (can be NULL) associated with the check + * + * Returns 1 if the context id matches the rule, 0 if it does not, and + * -errno on failure. + */ +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule, + struct audit_context *actx); + +/** + * selinux_audit_rule_known - check to see if rule contains selinux fields. + * @rule: rule to be checked + * Returns 1 if there are selinux fields specified in the rule, 0 otherwise. + */ +int selinux_audit_rule_known(struct audit_krule *krule); + +#endif /* _SELINUX_AUDIT_H */ + diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h deleted file mode 100644 index b0e6b12931c..00000000000 --- a/security/selinux/include/av_inherit.h +++ /dev/null @@ -1,31 +0,0 @@ -/* This file is automatically generated. Do not edit. */ - S_(SECCLASS_DIR, file, 0x00020000UL) - S_(SECCLASS_FILE, file, 0x00020000UL) - S_(SECCLASS_LNK_FILE, file, 0x00020000UL) - S_(SECCLASS_CHR_FILE, file, 0x00020000UL) - S_(SECCLASS_BLK_FILE, file, 0x00020000UL) - S_(SECCLASS_SOCK_FILE, file, 0x00020000UL) - S_(SECCLASS_FIFO_FILE, file, 0x00020000UL) - S_(SECCLASS_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_TCP_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_UDP_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_RAWIP_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_PACKET_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_IPC, ipc, 0x00000200UL) - S_(SECCLASS_SEM, ipc, 0x00000200UL) - S_(SECCLASS_MSGQ, ipc, 0x00000200UL) - S_(SECCLASS_SHM, ipc, 0x00000200UL) - S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_NFLOG_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_XFRM_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_SELINUX_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_AUDIT_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL) diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h deleted file mode 100644 index 591e98d9315..00000000000 --- a/security/selinux/include/av_perm_to_string.h +++ /dev/null @@ -1,241 +0,0 @@ -/* This file is automatically generated. Do not edit. */ - S_(SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod") - S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget") - S_(SECCLASS_DIR, DIR__ADD_NAME, "add_name") - S_(SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name") - S_(SECCLASS_DIR, DIR__REPARENT, "reparent") - S_(SECCLASS_DIR, DIR__SEARCH, "search") - S_(SECCLASS_DIR, DIR__RMDIR, "rmdir") - S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans") - S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint") - S_(SECCLASS_FILE, FILE__EXECMOD, "execmod") - S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans") - S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint") - S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod") - S_(SECCLASS_FD, FD__USE, "use") - S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto") - S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn") - S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom") - S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NODE_BIND, "node_bind") - S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NAME_CONNECT, "name_connect") - S_(SECCLASS_UDP_SOCKET, UDP_SOCKET__NODE_BIND, "node_bind") - S_(SECCLASS_RAWIP_SOCKET, RAWIP_SOCKET__NODE_BIND, "node_bind") - S_(SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv") - S_(SECCLASS_NODE, NODE__TCP_SEND, "tcp_send") - S_(SECCLASS_NODE, NODE__UDP_RECV, "udp_recv") - S_(SECCLASS_NODE, NODE__UDP_SEND, "udp_send") - S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv") - S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send") - S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest") - S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv") - S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send") - S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv") - S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send") - S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv") - S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send") - S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto") - S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn") - S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom") - S_(SECCLASS_PROCESS, PROCESS__FORK, "fork") - S_(SECCLASS_PROCESS, PROCESS__TRANSITION, "transition") - S_(SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld") - S_(SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill") - S_(SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop") - S_(SECCLASS_PROCESS, PROCESS__SIGNULL, "signull") - S_(SECCLASS_PROCESS, PROCESS__SIGNAL, "signal") - S_(SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace") - S_(SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched") - S_(SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched") - S_(SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession") - S_(SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid") - S_(SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid") - S_(SECCLASS_PROCESS, PROCESS__GETCAP, "getcap") - S_(SECCLASS_PROCESS, PROCESS__SETCAP, "setcap") - S_(SECCLASS_PROCESS, PROCESS__SHARE, "share") - S_(SECCLASS_PROCESS, PROCESS__GETATTR, "getattr") - S_(SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec") - S_(SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate") - S_(SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure") - S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh") - S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit") - S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh") - S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition") - S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent") - S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem") - S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack") - S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap") - S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue") - S_(SECCLASS_MSG, MSG__SEND, "send") - S_(SECCLASS_MSG, MSG__RECEIVE, "receive") - S_(SECCLASS_SHM, SHM__LOCK, "lock") - S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av") - S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create") - S_(SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member") - S_(SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context") - S_(SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy") - S_(SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel") - S_(SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user") - S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce") - S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool") - S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam") - S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot") - S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info") - S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read") - S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod") - S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console") - S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown") - S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override") - S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search") - S_(SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner") - S_(SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid") - S_(SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill") - S_(SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid") - S_(SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid") - S_(SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap") - S_(SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable") - S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service") - S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast") - S_(SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin") - S_(SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw") - S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock") - S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time") - S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config") - S_(SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod") - S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease") - S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_WRITE, "audit_write") - S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_CONTROL, "audit_control") - S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd") - S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn") - S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh") - S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok") - S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab") - S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create") - S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy") - S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw") - S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy") - S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr") - S_(SECCLASS_GC, GC__CREATE, "create") - S_(SECCLASS_GC, GC__FREE, "free") - S_(SECCLASS_GC, GC__GETATTR, "getattr") - S_(SECCLASS_GC, GC__SETATTR, "setattr") - S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild") - S_(SECCLASS_WINDOW, WINDOW__CREATE, "create") - S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy") - S_(SECCLASS_WINDOW, WINDOW__MAP, "map") - S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap") - S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack") - S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist") - S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop") - S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop") - S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr") - S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr") - S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus") - S_(SECCLASS_WINDOW, WINDOW__MOVE, "move") - S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection") - S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent") - S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife") - S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate") - S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent") - S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion") - S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent") - S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent") - S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent") - S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent") - S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest") - S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent") - S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent") - S_(SECCLASS_FONT, FONT__LOAD, "load") - S_(SECCLASS_FONT, FONT__FREE, "free") - S_(SECCLASS_FONT, FONT__GETATTR, "getattr") - S_(SECCLASS_FONT, FONT__USE, "use") - S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create") - S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free") - S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install") - S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall") - S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list") - S_(SECCLASS_COLORMAP, COLORMAP__READ, "read") - S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store") - S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr") - S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr") - S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create") - S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free") - S_(SECCLASS_PROPERTY, PROPERTY__READ, "read") - S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write") - S_(SECCLASS_CURSOR, CURSOR__CREATE, "create") - S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph") - S_(SECCLASS_CURSOR, CURSOR__FREE, "free") - S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign") - S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr") - S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill") - S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup") - S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr") - S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr") - S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus") - S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer") - S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab") - S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab") - S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab") - S_(SECCLASS_XINPUT, XINPUT__BELL, "bell") - S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion") - S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput") - S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver") - S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist") - S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist") - S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath") - S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath") - S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr") - S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab") - S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab") - S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query") - S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use") - S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec") - S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp") - S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect") - S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap") - S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec") - S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec") - S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay") - S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv") - S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read") - S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write") - S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc") - S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg") - S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd") - S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp") - S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost") - S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat") - S_(SECCLASS_NSCD, NSCD__ADMIN, "admin") - S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd") - S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp") - S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost") - S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto") - S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom") - S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h deleted file mode 100644 index d7f02edf393..00000000000 --- a/security/selinux/include/av_permissions.h +++ /dev/null @@ -1,935 +0,0 @@ -/* This file is automatically generated. Do not edit. */ -#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__IOCTL 0x00000001UL -#define DIR__READ 0x00000002UL -#define DIR__WRITE 0x00000004UL -#define DIR__CREATE 0x00000008UL -#define DIR__GETATTR 0x00000010UL -#define DIR__SETATTR 0x00000020UL -#define DIR__LOCK 0x00000040UL -#define DIR__RELABELFROM 0x00000080UL -#define DIR__RELABELTO 0x00000100UL -#define DIR__APPEND 0x00000200UL -#define DIR__UNLINK 0x00000400UL -#define DIR__LINK 0x00000800UL -#define DIR__RENAME 0x00001000UL -#define DIR__EXECUTE 0x00002000UL -#define DIR__SWAPON 0x00004000UL -#define DIR__QUOTAON 0x00008000UL -#define DIR__MOUNTON 0x00010000UL - -#define DIR__ADD_NAME 0x00020000UL -#define DIR__REMOVE_NAME 0x00040000UL -#define DIR__REPARENT 0x00080000UL -#define DIR__SEARCH 0x00100000UL -#define DIR__RMDIR 0x00200000UL - -#define FILE__IOCTL 0x00000001UL -#define FILE__READ 0x00000002UL -#define FILE__WRITE 0x00000004UL -#define FILE__CREATE 0x00000008UL -#define FILE__GETATTR 0x00000010UL -#define FILE__SETATTR 0x00000020UL -#define FILE__LOCK 0x00000040UL -#define FILE__RELABELFROM 0x00000080UL -#define FILE__RELABELTO 0x00000100UL -#define FILE__APPEND 0x00000200UL -#define FILE__UNLINK 0x00000400UL -#define FILE__LINK 0x00000800UL -#define FILE__RENAME 0x00001000UL -#define FILE__EXECUTE 0x00002000UL -#define FILE__SWAPON 0x00004000UL -#define FILE__QUOTAON 0x00008000UL -#define FILE__MOUNTON 0x00010000UL - -#define FILE__EXECUTE_NO_TRANS 0x00020000UL -#define FILE__ENTRYPOINT 0x00040000UL -#define FILE__EXECMOD 0x00080000UL - -#define LNK_FILE__IOCTL 0x00000001UL -#define LNK_FILE__READ 0x00000002UL -#define LNK_FILE__WRITE 0x00000004UL -#define LNK_FILE__CREATE 0x00000008UL -#define LNK_FILE__GETATTR 0x00000010UL -#define LNK_FILE__SETATTR 0x00000020UL -#define LNK_FILE__LOCK 0x00000040UL -#define LNK_FILE__RELABELFROM 0x00000080UL -#define LNK_FILE__RELABELTO 0x00000100UL -#define LNK_FILE__APPEND 0x00000200UL -#define LNK_FILE__UNLINK 0x00000400UL -#define LNK_FILE__LINK 0x00000800UL -#define LNK_FILE__RENAME 0x00001000UL -#define LNK_FILE__EXECUTE 0x00002000UL -#define LNK_FILE__SWAPON 0x00004000UL -#define LNK_FILE__QUOTAON 0x00008000UL -#define LNK_FILE__MOUNTON 0x00010000UL - -#define CHR_FILE__IOCTL 0x00000001UL -#define CHR_FILE__READ 0x00000002UL -#define CHR_FILE__WRITE 0x00000004UL -#define CHR_FILE__CREATE 0x00000008UL -#define CHR_FILE__GETATTR 0x00000010UL -#define CHR_FILE__SETATTR 0x00000020UL -#define CHR_FILE__LOCK 0x00000040UL -#define CHR_FILE__RELABELFROM 0x00000080UL -#define CHR_FILE__RELABELTO 0x00000100UL -#define CHR_FILE__APPEND 0x00000200UL -#define CHR_FILE__UNLINK 0x00000400UL -#define CHR_FILE__LINK 0x00000800UL -#define CHR_FILE__RENAME 0x00001000UL -#define CHR_FILE__EXECUTE 0x00002000UL -#define CHR_FILE__SWAPON 0x00004000UL -#define CHR_FILE__QUOTAON 0x00008000UL -#define CHR_FILE__MOUNTON 0x00010000UL - -#define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL -#define CHR_FILE__ENTRYPOINT 0x00040000UL -#define CHR_FILE__EXECMOD 0x00080000UL - -#define BLK_FILE__IOCTL 0x00000001UL -#define BLK_FILE__READ 0x00000002UL -#define BLK_FILE__WRITE 0x00000004UL -#define BLK_FILE__CREATE 0x00000008UL -#define BLK_FILE__GETATTR 0x00000010UL -#define BLK_FILE__SETATTR 0x00000020UL -#define BLK_FILE__LOCK 0x00000040UL -#define BLK_FILE__RELABELFROM 0x00000080UL -#define BLK_FILE__RELABELTO 0x00000100UL -#define BLK_FILE__APPEND 0x00000200UL -#define BLK_FILE__UNLINK 0x00000400UL -#define BLK_FILE__LINK 0x00000800UL -#define BLK_FILE__RENAME 0x00001000UL -#define BLK_FILE__EXECUTE 0x00002000UL -#define BLK_FILE__SWAPON 0x00004000UL -#define BLK_FILE__QUOTAON 0x00008000UL -#define BLK_FILE__MOUNTON 0x00010000UL - -#define SOCK_FILE__IOCTL 0x00000001UL -#define SOCK_FILE__READ 0x00000002UL -#define SOCK_FILE__WRITE 0x00000004UL -#define SOCK_FILE__CREATE 0x00000008UL -#define SOCK_FILE__GETATTR 0x00000010UL -#define SOCK_FILE__SETATTR 0x00000020UL -#define SOCK_FILE__LOCK 0x00000040UL -#define SOCK_FILE__RELABELFROM 0x00000080UL -#define SOCK_FILE__RELABELTO 0x00000100UL -#define SOCK_FILE__APPEND 0x00000200UL -#define SOCK_FILE__UNLINK 0x00000400UL -#define SOCK_FILE__LINK 0x00000800UL -#define SOCK_FILE__RENAME 0x00001000UL -#define SOCK_FILE__EXECUTE 0x00002000UL -#define SOCK_FILE__SWAPON 0x00004000UL -#define SOCK_FILE__QUOTAON 0x00008000UL -#define SOCK_FILE__MOUNTON 0x00010000UL - -#define FIFO_FILE__IOCTL 0x00000001UL -#define FIFO_FILE__READ 0x00000002UL -#define FIFO_FILE__WRITE 0x00000004UL -#define FIFO_FILE__CREATE 0x00000008UL -#define FIFO_FILE__GETATTR 0x00000010UL -#define FIFO_FILE__SETATTR 0x00000020UL -#define FIFO_FILE__LOCK 0x00000040UL -#define FIFO_FILE__RELABELFROM 0x00000080UL -#define FIFO_FILE__RELABELTO 0x00000100UL -#define FIFO_FILE__APPEND 0x00000200UL -#define FIFO_FILE__UNLINK 0x00000400UL -#define FIFO_FILE__LINK 0x00000800UL -#define FIFO_FILE__RENAME 0x00001000UL -#define FIFO_FILE__EXECUTE 0x00002000UL -#define FIFO_FILE__SWAPON 0x00004000UL -#define FIFO_FILE__QUOTAON 0x00008000UL -#define FIFO_FILE__MOUNTON 0x00010000UL - -#define FD__USE 0x00000001UL - -#define SOCKET__IOCTL 0x00000001UL -#define SOCKET__READ 0x00000002UL -#define SOCKET__WRITE 0x00000004UL -#define SOCKET__CREATE 0x00000008UL -#define SOCKET__GETATTR 0x00000010UL -#define SOCKET__SETATTR 0x00000020UL -#define SOCKET__LOCK 0x00000040UL -#define SOCKET__RELABELFROM 0x00000080UL -#define SOCKET__RELABELTO 0x00000100UL -#define SOCKET__APPEND 0x00000200UL -#define SOCKET__BIND 0x00000400UL -#define SOCKET__CONNECT 0x00000800UL -#define SOCKET__LISTEN 0x00001000UL -#define SOCKET__ACCEPT 0x00002000UL -#define SOCKET__GETOPT 0x00004000UL -#define SOCKET__SETOPT 0x00008000UL -#define SOCKET__SHUTDOWN 0x00010000UL -#define SOCKET__RECVFROM 0x00020000UL -#define SOCKET__SENDTO 0x00040000UL -#define SOCKET__RECV_MSG 0x00080000UL -#define SOCKET__SEND_MSG 0x00100000UL -#define SOCKET__NAME_BIND 0x00200000UL - -#define TCP_SOCKET__IOCTL 0x00000001UL -#define TCP_SOCKET__READ 0x00000002UL -#define TCP_SOCKET__WRITE 0x00000004UL -#define TCP_SOCKET__CREATE 0x00000008UL -#define TCP_SOCKET__GETATTR 0x00000010UL -#define TCP_SOCKET__SETATTR 0x00000020UL -#define TCP_SOCKET__LOCK 0x00000040UL -#define TCP_SOCKET__RELABELFROM 0x00000080UL -#define TCP_SOCKET__RELABELTO 0x00000100UL -#define TCP_SOCKET__APPEND 0x00000200UL -#define TCP_SOCKET__BIND 0x00000400UL -#define TCP_SOCKET__CONNECT 0x00000800UL -#define TCP_SOCKET__LISTEN 0x00001000UL -#define TCP_SOCKET__ACCEPT 0x00002000UL -#define TCP_SOCKET__GETOPT 0x00004000UL -#define TCP_SOCKET__SETOPT 0x00008000UL -#define TCP_SOCKET__SHUTDOWN 0x00010000UL -#define TCP_SOCKET__RECVFROM 0x00020000UL -#define TCP_SOCKET__SENDTO 0x00040000UL -#define TCP_SOCKET__RECV_MSG 0x00080000UL -#define TCP_SOCKET__SEND_MSG 0x00100000UL -#define TCP_SOCKET__NAME_BIND 0x00200000UL - -#define TCP_SOCKET__CONNECTTO 0x00400000UL -#define TCP_SOCKET__NEWCONN 0x00800000UL -#define TCP_SOCKET__ACCEPTFROM 0x01000000UL -#define TCP_SOCKET__NODE_BIND 0x02000000UL -#define TCP_SOCKET__NAME_CONNECT 0x04000000UL - -#define UDP_SOCKET__IOCTL 0x00000001UL -#define UDP_SOCKET__READ 0x00000002UL -#define UDP_SOCKET__WRITE 0x00000004UL -#define UDP_SOCKET__CREATE 0x00000008UL -#define UDP_SOCKET__GETATTR 0x00000010UL -#define UDP_SOCKET__SETATTR 0x00000020UL -#define UDP_SOCKET__LOCK 0x00000040UL -#define UDP_SOCKET__RELABELFROM 0x00000080UL -#define UDP_SOCKET__RELABELTO 0x00000100UL -#define UDP_SOCKET__APPEND 0x00000200UL -#define UDP_SOCKET__BIND 0x00000400UL -#define UDP_SOCKET__CONNECT 0x00000800UL -#define UDP_SOCKET__LISTEN 0x00001000UL -#define UDP_SOCKET__ACCEPT 0x00002000UL -#define UDP_SOCKET__GETOPT 0x00004000UL -#define UDP_SOCKET__SETOPT 0x00008000UL -#define UDP_SOCKET__SHUTDOWN 0x00010000UL -#define UDP_SOCKET__RECVFROM 0x00020000UL -#define UDP_SOCKET__SENDTO 0x00040000UL -#define UDP_SOCKET__RECV_MSG 0x00080000UL -#define UDP_SOCKET__SEND_MSG 0x00100000UL -#define UDP_SOCKET__NAME_BIND 0x00200000UL - -#define UDP_SOCKET__NODE_BIND 0x00400000UL - -#define RAWIP_SOCKET__IOCTL 0x00000001UL -#define RAWIP_SOCKET__READ 0x00000002UL -#define RAWIP_SOCKET__WRITE 0x00000004UL -#define RAWIP_SOCKET__CREATE 0x00000008UL -#define RAWIP_SOCKET__GETATTR 0x00000010UL -#define RAWIP_SOCKET__SETATTR 0x00000020UL -#define RAWIP_SOCKET__LOCK 0x00000040UL -#define RAWIP_SOCKET__RELABELFROM 0x00000080UL -#define RAWIP_SOCKET__RELABELTO 0x00000100UL -#define RAWIP_SOCKET__APPEND 0x00000200UL -#define RAWIP_SOCKET__BIND 0x00000400UL -#define RAWIP_SOCKET__CONNECT 0x00000800UL -#define RAWIP_SOCKET__LISTEN 0x00001000UL -#define RAWIP_SOCKET__ACCEPT 0x00002000UL -#define RAWIP_SOCKET__GETOPT 0x00004000UL -#define RAWIP_SOCKET__SETOPT 0x00008000UL -#define RAWIP_SOCKET__SHUTDOWN 0x00010000UL -#define RAWIP_SOCKET__RECVFROM 0x00020000UL -#define RAWIP_SOCKET__SENDTO 0x00040000UL -#define RAWIP_SOCKET__RECV_MSG 0x00080000UL -#define RAWIP_SOCKET__SEND_MSG 0x00100000UL -#define RAWIP_SOCKET__NAME_BIND 0x00200000UL - -#define RAWIP_SOCKET__NODE_BIND 0x00400000UL - -#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__IOCTL 0x00000001UL -#define NETLINK_SOCKET__READ 0x00000002UL -#define NETLINK_SOCKET__WRITE 0x00000004UL -#define NETLINK_SOCKET__CREATE 0x00000008UL -#define NETLINK_SOCKET__GETATTR 0x00000010UL -#define NETLINK_SOCKET__SETATTR 0x00000020UL -#define NETLINK_SOCKET__LOCK 0x00000040UL -#define NETLINK_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_SOCKET__APPEND 0x00000200UL -#define NETLINK_SOCKET__BIND 0x00000400UL -#define NETLINK_SOCKET__CONNECT 0x00000800UL -#define NETLINK_SOCKET__LISTEN 0x00001000UL -#define NETLINK_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_SOCKET__GETOPT 0x00004000UL -#define NETLINK_SOCKET__SETOPT 0x00008000UL -#define NETLINK_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_SOCKET__SENDTO 0x00040000UL -#define NETLINK_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_SOCKET__NAME_BIND 0x00200000UL - -#define PACKET_SOCKET__IOCTL 0x00000001UL -#define PACKET_SOCKET__READ 0x00000002UL -#define PACKET_SOCKET__WRITE 0x00000004UL -#define PACKET_SOCKET__CREATE 0x00000008UL -#define PACKET_SOCKET__GETATTR 0x00000010UL -#define PACKET_SOCKET__SETATTR 0x00000020UL -#define PACKET_SOCKET__LOCK 0x00000040UL -#define PACKET_SOCKET__RELABELFROM 0x00000080UL -#define PACKET_SOCKET__RELABELTO 0x00000100UL -#define PACKET_SOCKET__APPEND 0x00000200UL -#define PACKET_SOCKET__BIND 0x00000400UL -#define PACKET_SOCKET__CONNECT 0x00000800UL -#define PACKET_SOCKET__LISTEN 0x00001000UL -#define PACKET_SOCKET__ACCEPT 0x00002000UL -#define PACKET_SOCKET__GETOPT 0x00004000UL -#define PACKET_SOCKET__SETOPT 0x00008000UL -#define PACKET_SOCKET__SHUTDOWN 0x00010000UL -#define PACKET_SOCKET__RECVFROM 0x00020000UL -#define PACKET_SOCKET__SENDTO 0x00040000UL -#define PACKET_SOCKET__RECV_MSG 0x00080000UL -#define PACKET_SOCKET__SEND_MSG 0x00100000UL -#define PACKET_SOCKET__NAME_BIND 0x00200000UL - -#define KEY_SOCKET__IOCTL 0x00000001UL -#define KEY_SOCKET__READ 0x00000002UL -#define KEY_SOCKET__WRITE 0x00000004UL -#define KEY_SOCKET__CREATE 0x00000008UL -#define KEY_SOCKET__GETATTR 0x00000010UL -#define KEY_SOCKET__SETATTR 0x00000020UL -#define KEY_SOCKET__LOCK 0x00000040UL -#define KEY_SOCKET__RELABELFROM 0x00000080UL -#define KEY_SOCKET__RELABELTO 0x00000100UL -#define KEY_SOCKET__APPEND 0x00000200UL -#define KEY_SOCKET__BIND 0x00000400UL -#define KEY_SOCKET__CONNECT 0x00000800UL -#define KEY_SOCKET__LISTEN 0x00001000UL -#define KEY_SOCKET__ACCEPT 0x00002000UL -#define KEY_SOCKET__GETOPT 0x00004000UL -#define KEY_SOCKET__SETOPT 0x00008000UL -#define KEY_SOCKET__SHUTDOWN 0x00010000UL -#define KEY_SOCKET__RECVFROM 0x00020000UL -#define KEY_SOCKET__SENDTO 0x00040000UL -#define KEY_SOCKET__RECV_MSG 0x00080000UL -#define KEY_SOCKET__SEND_MSG 0x00100000UL -#define KEY_SOCKET__NAME_BIND 0x00200000UL - -#define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL -#define UNIX_STREAM_SOCKET__READ 0x00000002UL -#define UNIX_STREAM_SOCKET__WRITE 0x00000004UL -#define UNIX_STREAM_SOCKET__CREATE 0x00000008UL -#define UNIX_STREAM_SOCKET__GETATTR 0x00000010UL -#define UNIX_STREAM_SOCKET__SETATTR 0x00000020UL -#define UNIX_STREAM_SOCKET__LOCK 0x00000040UL -#define UNIX_STREAM_SOCKET__RELABELFROM 0x00000080UL -#define UNIX_STREAM_SOCKET__RELABELTO 0x00000100UL -#define UNIX_STREAM_SOCKET__APPEND 0x00000200UL -#define UNIX_STREAM_SOCKET__BIND 0x00000400UL -#define UNIX_STREAM_SOCKET__CONNECT 0x00000800UL -#define UNIX_STREAM_SOCKET__LISTEN 0x00001000UL -#define UNIX_STREAM_SOCKET__ACCEPT 0x00002000UL -#define UNIX_STREAM_SOCKET__GETOPT 0x00004000UL -#define UNIX_STREAM_SOCKET__SETOPT 0x00008000UL -#define UNIX_STREAM_SOCKET__SHUTDOWN 0x00010000UL -#define UNIX_STREAM_SOCKET__RECVFROM 0x00020000UL -#define UNIX_STREAM_SOCKET__SENDTO 0x00040000UL -#define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL -#define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL -#define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL - -#define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL -#define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL -#define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL - -#define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL -#define UNIX_DGRAM_SOCKET__READ 0x00000002UL -#define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL -#define UNIX_DGRAM_SOCKET__CREATE 0x00000008UL -#define UNIX_DGRAM_SOCKET__GETATTR 0x00000010UL -#define UNIX_DGRAM_SOCKET__SETATTR 0x00000020UL -#define UNIX_DGRAM_SOCKET__LOCK 0x00000040UL -#define UNIX_DGRAM_SOCKET__RELABELFROM 0x00000080UL -#define UNIX_DGRAM_SOCKET__RELABELTO 0x00000100UL -#define UNIX_DGRAM_SOCKET__APPEND 0x00000200UL -#define UNIX_DGRAM_SOCKET__BIND 0x00000400UL -#define UNIX_DGRAM_SOCKET__CONNECT 0x00000800UL -#define UNIX_DGRAM_SOCKET__LISTEN 0x00001000UL -#define UNIX_DGRAM_SOCKET__ACCEPT 0x00002000UL -#define UNIX_DGRAM_SOCKET__GETOPT 0x00004000UL -#define UNIX_DGRAM_SOCKET__SETOPT 0x00008000UL -#define UNIX_DGRAM_SOCKET__SHUTDOWN 0x00010000UL -#define UNIX_DGRAM_SOCKET__RECVFROM 0x00020000UL -#define UNIX_DGRAM_SOCKET__SENDTO 0x00040000UL -#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL -#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL -#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL - -#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 PROCESS__SIGINH 0x00100000UL -#define PROCESS__SETRLIMIT 0x00200000UL -#define PROCESS__RLIMITINH 0x00400000UL -#define PROCESS__DYNTRANSITION 0x00800000UL -#define PROCESS__SETCURRENT 0x01000000UL -#define PROCESS__EXECMEM 0x02000000UL -#define PROCESS__EXECSTACK 0x04000000UL -#define PROCESS__EXECHEAP 0x08000000UL - -#define IPC__CREATE 0x00000001UL -#define IPC__DESTROY 0x00000002UL -#define IPC__GETATTR 0x00000004UL -#define IPC__SETATTR 0x00000008UL -#define IPC__READ 0x00000010UL -#define IPC__WRITE 0x00000020UL -#define IPC__ASSOCIATE 0x00000040UL -#define IPC__UNIX_READ 0x00000080UL -#define IPC__UNIX_WRITE 0x00000100UL - -#define SEM__CREATE 0x00000001UL -#define SEM__DESTROY 0x00000002UL -#define SEM__GETATTR 0x00000004UL -#define SEM__SETATTR 0x00000008UL -#define SEM__READ 0x00000010UL -#define SEM__WRITE 0x00000020UL -#define SEM__ASSOCIATE 0x00000040UL -#define SEM__UNIX_READ 0x00000080UL -#define SEM__UNIX_WRITE 0x00000100UL - -#define MSGQ__CREATE 0x00000001UL -#define MSGQ__DESTROY 0x00000002UL -#define MSGQ__GETATTR 0x00000004UL -#define MSGQ__SETATTR 0x00000008UL -#define MSGQ__READ 0x00000010UL -#define MSGQ__WRITE 0x00000020UL -#define MSGQ__ASSOCIATE 0x00000040UL -#define MSGQ__UNIX_READ 0x00000080UL -#define MSGQ__UNIX_WRITE 0x00000100UL - -#define MSGQ__ENQUEUE 0x00000200UL - -#define MSG__SEND 0x00000001UL -#define MSG__RECEIVE 0x00000002UL - -#define SHM__CREATE 0x00000001UL -#define SHM__DESTROY 0x00000002UL -#define SHM__GETATTR 0x00000004UL -#define SHM__SETATTR 0x00000008UL -#define SHM__READ 0x00000010UL -#define SHM__WRITE 0x00000020UL -#define SHM__ASSOCIATE 0x00000040UL -#define SHM__UNIX_READ 0x00000080UL -#define SHM__UNIX_WRITE 0x00000100UL - -#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 SECURITY__SETBOOL 0x00000100UL -#define SECURITY__SETSECPARAM 0x00000200UL -#define SECURITY__SETCHECKREQPROT 0x00000400UL - -#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 CAPABILITY__AUDIT_WRITE 0x20000000UL -#define CAPABILITY__AUDIT_CONTROL 0x40000000UL - -#define PASSWD__PASSWD 0x00000001UL -#define PASSWD__CHFN 0x00000002UL -#define PASSWD__CHSH 0x00000004UL -#define PASSWD__ROOTOK 0x00000008UL -#define PASSWD__CRONTAB 0x00000010UL - -#define DRAWABLE__CREATE 0x00000001UL -#define DRAWABLE__DESTROY 0x00000002UL -#define DRAWABLE__DRAW 0x00000004UL -#define DRAWABLE__COPY 0x00000008UL -#define DRAWABLE__GETATTR 0x00000010UL - -#define GC__CREATE 0x00000001UL -#define GC__FREE 0x00000002UL -#define GC__GETATTR 0x00000004UL -#define GC__SETATTR 0x00000008UL - -#define WINDOW__ADDCHILD 0x00000001UL -#define WINDOW__CREATE 0x00000002UL -#define WINDOW__DESTROY 0x00000004UL -#define WINDOW__MAP 0x00000008UL -#define WINDOW__UNMAP 0x00000010UL -#define WINDOW__CHSTACK 0x00000020UL -#define WINDOW__CHPROPLIST 0x00000040UL -#define WINDOW__CHPROP 0x00000080UL -#define WINDOW__LISTPROP 0x00000100UL -#define WINDOW__GETATTR 0x00000200UL -#define WINDOW__SETATTR 0x00000400UL -#define WINDOW__SETFOCUS 0x00000800UL -#define WINDOW__MOVE 0x00001000UL -#define WINDOW__CHSELECTION 0x00002000UL -#define WINDOW__CHPARENT 0x00004000UL -#define WINDOW__CTRLLIFE 0x00008000UL -#define WINDOW__ENUMERATE 0x00010000UL -#define WINDOW__TRANSPARENT 0x00020000UL -#define WINDOW__MOUSEMOTION 0x00040000UL -#define WINDOW__CLIENTCOMEVENT 0x00080000UL -#define WINDOW__INPUTEVENT 0x00100000UL -#define WINDOW__DRAWEVENT 0x00200000UL -#define WINDOW__WINDOWCHANGEEVENT 0x00400000UL -#define WINDOW__WINDOWCHANGEREQUEST 0x00800000UL -#define WINDOW__SERVERCHANGEEVENT 0x01000000UL -#define WINDOW__EXTENSIONEVENT 0x02000000UL - -#define FONT__LOAD 0x00000001UL -#define FONT__FREE 0x00000002UL -#define FONT__GETATTR 0x00000004UL -#define FONT__USE 0x00000008UL - -#define COLORMAP__CREATE 0x00000001UL -#define COLORMAP__FREE 0x00000002UL -#define COLORMAP__INSTALL 0x00000004UL -#define COLORMAP__UNINSTALL 0x00000008UL -#define COLORMAP__LIST 0x00000010UL -#define COLORMAP__READ 0x00000020UL -#define COLORMAP__STORE 0x00000040UL -#define COLORMAP__GETATTR 0x00000080UL -#define COLORMAP__SETATTR 0x00000100UL - -#define PROPERTY__CREATE 0x00000001UL -#define PROPERTY__FREE 0x00000002UL -#define PROPERTY__READ 0x00000004UL -#define PROPERTY__WRITE 0x00000008UL - -#define CURSOR__CREATE 0x00000001UL -#define CURSOR__CREATEGLYPH 0x00000002UL -#define CURSOR__FREE 0x00000004UL -#define CURSOR__ASSIGN 0x00000008UL -#define CURSOR__SETATTR 0x00000010UL - -#define XCLIENT__KILL 0x00000001UL - -#define XINPUT__LOOKUP 0x00000001UL -#define XINPUT__GETATTR 0x00000002UL -#define XINPUT__SETATTR 0x00000004UL -#define XINPUT__SETFOCUS 0x00000008UL -#define XINPUT__WARPPOINTER 0x00000010UL -#define XINPUT__ACTIVEGRAB 0x00000020UL -#define XINPUT__PASSIVEGRAB 0x00000040UL -#define XINPUT__UNGRAB 0x00000080UL -#define XINPUT__BELL 0x00000100UL -#define XINPUT__MOUSEMOTION 0x00000200UL -#define XINPUT__RELABELINPUT 0x00000400UL - -#define XSERVER__SCREENSAVER 0x00000001UL -#define XSERVER__GETHOSTLIST 0x00000002UL -#define XSERVER__SETHOSTLIST 0x00000004UL -#define XSERVER__GETFONTPATH 0x00000008UL -#define XSERVER__SETFONTPATH 0x00000010UL -#define XSERVER__GETATTR 0x00000020UL -#define XSERVER__GRAB 0x00000040UL -#define XSERVER__UNGRAB 0x00000080UL - -#define XEXTENSION__QUERY 0x00000001UL -#define XEXTENSION__USE 0x00000002UL - -#define PAX__PAGEEXEC 0x00000001UL -#define PAX__EMUTRAMP 0x00000002UL -#define PAX__MPROTECT 0x00000004UL -#define PAX__RANDMMAP 0x00000008UL -#define PAX__RANDEXEC 0x00000010UL -#define PAX__SEGMEXEC 0x00000020UL - -#define NETLINK_ROUTE_SOCKET__IOCTL 0x00000001UL -#define NETLINK_ROUTE_SOCKET__READ 0x00000002UL -#define NETLINK_ROUTE_SOCKET__WRITE 0x00000004UL -#define NETLINK_ROUTE_SOCKET__CREATE 0x00000008UL -#define NETLINK_ROUTE_SOCKET__GETATTR 0x00000010UL -#define NETLINK_ROUTE_SOCKET__SETATTR 0x00000020UL -#define NETLINK_ROUTE_SOCKET__LOCK 0x00000040UL -#define NETLINK_ROUTE_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_ROUTE_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_ROUTE_SOCKET__APPEND 0x00000200UL -#define NETLINK_ROUTE_SOCKET__BIND 0x00000400UL -#define NETLINK_ROUTE_SOCKET__CONNECT 0x00000800UL -#define NETLINK_ROUTE_SOCKET__LISTEN 0x00001000UL -#define NETLINK_ROUTE_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_ROUTE_SOCKET__GETOPT 0x00004000UL -#define NETLINK_ROUTE_SOCKET__SETOPT 0x00008000UL -#define NETLINK_ROUTE_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_ROUTE_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_ROUTE_SOCKET__SENDTO 0x00040000UL -#define NETLINK_ROUTE_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_ROUTE_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_ROUTE_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_ROUTE_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_ROUTE_SOCKET__NLMSG_WRITE 0x00800000UL - -#define NETLINK_FIREWALL_SOCKET__IOCTL 0x00000001UL -#define NETLINK_FIREWALL_SOCKET__READ 0x00000002UL -#define NETLINK_FIREWALL_SOCKET__WRITE 0x00000004UL -#define NETLINK_FIREWALL_SOCKET__CREATE 0x00000008UL -#define NETLINK_FIREWALL_SOCKET__GETATTR 0x00000010UL -#define NETLINK_FIREWALL_SOCKET__SETATTR 0x00000020UL -#define NETLINK_FIREWALL_SOCKET__LOCK 0x00000040UL -#define NETLINK_FIREWALL_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_FIREWALL_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_FIREWALL_SOCKET__APPEND 0x00000200UL -#define NETLINK_FIREWALL_SOCKET__BIND 0x00000400UL -#define NETLINK_FIREWALL_SOCKET__CONNECT 0x00000800UL -#define NETLINK_FIREWALL_SOCKET__LISTEN 0x00001000UL -#define NETLINK_FIREWALL_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_FIREWALL_SOCKET__GETOPT 0x00004000UL -#define NETLINK_FIREWALL_SOCKET__SETOPT 0x00008000UL -#define NETLINK_FIREWALL_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_FIREWALL_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_FIREWALL_SOCKET__SENDTO 0x00040000UL -#define NETLINK_FIREWALL_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_FIREWALL_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_FIREWALL_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_FIREWALL_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE 0x00800000UL - -#define NETLINK_TCPDIAG_SOCKET__IOCTL 0x00000001UL -#define NETLINK_TCPDIAG_SOCKET__READ 0x00000002UL -#define NETLINK_TCPDIAG_SOCKET__WRITE 0x00000004UL -#define NETLINK_TCPDIAG_SOCKET__CREATE 0x00000008UL -#define NETLINK_TCPDIAG_SOCKET__GETATTR 0x00000010UL -#define NETLINK_TCPDIAG_SOCKET__SETATTR 0x00000020UL -#define NETLINK_TCPDIAG_SOCKET__LOCK 0x00000040UL -#define NETLINK_TCPDIAG_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_TCPDIAG_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_TCPDIAG_SOCKET__APPEND 0x00000200UL -#define NETLINK_TCPDIAG_SOCKET__BIND 0x00000400UL -#define NETLINK_TCPDIAG_SOCKET__CONNECT 0x00000800UL -#define NETLINK_TCPDIAG_SOCKET__LISTEN 0x00001000UL -#define NETLINK_TCPDIAG_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_TCPDIAG_SOCKET__GETOPT 0x00004000UL -#define NETLINK_TCPDIAG_SOCKET__SETOPT 0x00008000UL -#define NETLINK_TCPDIAG_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_TCPDIAG_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_TCPDIAG_SOCKET__SENDTO 0x00040000UL -#define NETLINK_TCPDIAG_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_TCPDIAG_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_TCPDIAG_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_TCPDIAG_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE 0x00800000UL - -#define NETLINK_NFLOG_SOCKET__IOCTL 0x00000001UL -#define NETLINK_NFLOG_SOCKET__READ 0x00000002UL -#define NETLINK_NFLOG_SOCKET__WRITE 0x00000004UL -#define NETLINK_NFLOG_SOCKET__CREATE 0x00000008UL -#define NETLINK_NFLOG_SOCKET__GETATTR 0x00000010UL -#define NETLINK_NFLOG_SOCKET__SETATTR 0x00000020UL -#define NETLINK_NFLOG_SOCKET__LOCK 0x00000040UL -#define NETLINK_NFLOG_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_NFLOG_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_NFLOG_SOCKET__APPEND 0x00000200UL -#define NETLINK_NFLOG_SOCKET__BIND 0x00000400UL -#define NETLINK_NFLOG_SOCKET__CONNECT 0x00000800UL -#define NETLINK_NFLOG_SOCKET__LISTEN 0x00001000UL -#define NETLINK_NFLOG_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_NFLOG_SOCKET__GETOPT 0x00004000UL -#define NETLINK_NFLOG_SOCKET__SETOPT 0x00008000UL -#define NETLINK_NFLOG_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_NFLOG_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_NFLOG_SOCKET__SENDTO 0x00040000UL -#define NETLINK_NFLOG_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_NFLOG_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_NFLOG_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_XFRM_SOCKET__IOCTL 0x00000001UL -#define NETLINK_XFRM_SOCKET__READ 0x00000002UL -#define NETLINK_XFRM_SOCKET__WRITE 0x00000004UL -#define NETLINK_XFRM_SOCKET__CREATE 0x00000008UL -#define NETLINK_XFRM_SOCKET__GETATTR 0x00000010UL -#define NETLINK_XFRM_SOCKET__SETATTR 0x00000020UL -#define NETLINK_XFRM_SOCKET__LOCK 0x00000040UL -#define NETLINK_XFRM_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_XFRM_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_XFRM_SOCKET__APPEND 0x00000200UL -#define NETLINK_XFRM_SOCKET__BIND 0x00000400UL -#define NETLINK_XFRM_SOCKET__CONNECT 0x00000800UL -#define NETLINK_XFRM_SOCKET__LISTEN 0x00001000UL -#define NETLINK_XFRM_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_XFRM_SOCKET__GETOPT 0x00004000UL -#define NETLINK_XFRM_SOCKET__SETOPT 0x00008000UL -#define NETLINK_XFRM_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_XFRM_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_XFRM_SOCKET__SENDTO 0x00040000UL -#define NETLINK_XFRM_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_XFRM_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_XFRM_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_XFRM_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_XFRM_SOCKET__NLMSG_WRITE 0x00800000UL - -#define NETLINK_SELINUX_SOCKET__IOCTL 0x00000001UL -#define NETLINK_SELINUX_SOCKET__READ 0x00000002UL -#define NETLINK_SELINUX_SOCKET__WRITE 0x00000004UL -#define NETLINK_SELINUX_SOCKET__CREATE 0x00000008UL -#define NETLINK_SELINUX_SOCKET__GETATTR 0x00000010UL -#define NETLINK_SELINUX_SOCKET__SETATTR 0x00000020UL -#define NETLINK_SELINUX_SOCKET__LOCK 0x00000040UL -#define NETLINK_SELINUX_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_SELINUX_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_SELINUX_SOCKET__APPEND 0x00000200UL -#define NETLINK_SELINUX_SOCKET__BIND 0x00000400UL -#define NETLINK_SELINUX_SOCKET__CONNECT 0x00000800UL -#define NETLINK_SELINUX_SOCKET__LISTEN 0x00001000UL -#define NETLINK_SELINUX_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_SELINUX_SOCKET__GETOPT 0x00004000UL -#define NETLINK_SELINUX_SOCKET__SETOPT 0x00008000UL -#define NETLINK_SELINUX_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_SELINUX_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_SELINUX_SOCKET__SENDTO 0x00040000UL -#define NETLINK_SELINUX_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_SELINUX_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_SELINUX_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_AUDIT_SOCKET__IOCTL 0x00000001UL -#define NETLINK_AUDIT_SOCKET__READ 0x00000002UL -#define NETLINK_AUDIT_SOCKET__WRITE 0x00000004UL -#define NETLINK_AUDIT_SOCKET__CREATE 0x00000008UL -#define NETLINK_AUDIT_SOCKET__GETATTR 0x00000010UL -#define NETLINK_AUDIT_SOCKET__SETATTR 0x00000020UL -#define NETLINK_AUDIT_SOCKET__LOCK 0x00000040UL -#define NETLINK_AUDIT_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_AUDIT_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_AUDIT_SOCKET__APPEND 0x00000200UL -#define NETLINK_AUDIT_SOCKET__BIND 0x00000400UL -#define NETLINK_AUDIT_SOCKET__CONNECT 0x00000800UL -#define NETLINK_AUDIT_SOCKET__LISTEN 0x00001000UL -#define NETLINK_AUDIT_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_AUDIT_SOCKET__GETOPT 0x00004000UL -#define NETLINK_AUDIT_SOCKET__SETOPT 0x00008000UL -#define NETLINK_AUDIT_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_AUDIT_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_AUDIT_SOCKET__SENDTO 0x00040000UL -#define NETLINK_AUDIT_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_AUDIT_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_AUDIT_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_AUDIT_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL -#define NETLINK_AUDIT_SOCKET__NLMSG_RELAY 0x01000000UL -#define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV 0x02000000UL - -#define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL -#define NETLINK_IP6FW_SOCKET__READ 0x00000002UL -#define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL -#define NETLINK_IP6FW_SOCKET__CREATE 0x00000008UL -#define NETLINK_IP6FW_SOCKET__GETATTR 0x00000010UL -#define NETLINK_IP6FW_SOCKET__SETATTR 0x00000020UL -#define NETLINK_IP6FW_SOCKET__LOCK 0x00000040UL -#define NETLINK_IP6FW_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_IP6FW_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_IP6FW_SOCKET__APPEND 0x00000200UL -#define NETLINK_IP6FW_SOCKET__BIND 0x00000400UL -#define NETLINK_IP6FW_SOCKET__CONNECT 0x00000800UL -#define NETLINK_IP6FW_SOCKET__LISTEN 0x00001000UL -#define NETLINK_IP6FW_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_IP6FW_SOCKET__GETOPT 0x00004000UL -#define NETLINK_IP6FW_SOCKET__SETOPT 0x00008000UL -#define NETLINK_IP6FW_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_IP6FW_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_IP6FW_SOCKET__SENDTO 0x00040000UL -#define NETLINK_IP6FW_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_IP6FW_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_IP6FW_SOCKET__NAME_BIND 0x00200000UL - -#define NETLINK_IP6FW_SOCKET__NLMSG_READ 0x00400000UL -#define NETLINK_IP6FW_SOCKET__NLMSG_WRITE 0x00800000UL - -#define NETLINK_DNRT_SOCKET__IOCTL 0x00000001UL -#define NETLINK_DNRT_SOCKET__READ 0x00000002UL -#define NETLINK_DNRT_SOCKET__WRITE 0x00000004UL -#define NETLINK_DNRT_SOCKET__CREATE 0x00000008UL -#define NETLINK_DNRT_SOCKET__GETATTR 0x00000010UL -#define NETLINK_DNRT_SOCKET__SETATTR 0x00000020UL -#define NETLINK_DNRT_SOCKET__LOCK 0x00000040UL -#define NETLINK_DNRT_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_DNRT_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_DNRT_SOCKET__APPEND 0x00000200UL -#define NETLINK_DNRT_SOCKET__BIND 0x00000400UL -#define NETLINK_DNRT_SOCKET__CONNECT 0x00000800UL -#define NETLINK_DNRT_SOCKET__LISTEN 0x00001000UL -#define NETLINK_DNRT_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_DNRT_SOCKET__GETOPT 0x00004000UL -#define NETLINK_DNRT_SOCKET__SETOPT 0x00008000UL -#define NETLINK_DNRT_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_DNRT_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_DNRT_SOCKET__SENDTO 0x00040000UL -#define NETLINK_DNRT_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_DNRT_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_DNRT_SOCKET__NAME_BIND 0x00200000UL - -#define DBUS__ACQUIRE_SVC 0x00000001UL -#define DBUS__SEND_MSG 0x00000002UL - -#define NSCD__GETPWD 0x00000001UL -#define NSCD__GETGRP 0x00000002UL -#define NSCD__GETHOST 0x00000004UL -#define NSCD__GETSTAT 0x00000008UL -#define NSCD__ADMIN 0x00000010UL -#define NSCD__SHMEMPWD 0x00000020UL -#define NSCD__SHMEMGRP 0x00000040UL -#define NSCD__SHMEMHOST 0x00000080UL - -#define ASSOCIATION__SENDTO 0x00000001UL -#define ASSOCIATION__RECVFROM 0x00000002UL -#define ASSOCIATION__SETCONTEXT 0x00000004UL - -#define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__WRITE 0x00000004UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__CREATE 0x00000008UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__GETATTR 0x00000010UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__SETATTR 0x00000020UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__LOCK 0x00000040UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__RELABELFROM 0x00000080UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__RELABELTO 0x00000100UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__APPEND 0x00000200UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__BIND 0x00000400UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__CONNECT 0x00000800UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__LISTEN 0x00001000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__ACCEPT 0x00002000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__GETOPT 0x00004000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__SETOPT 0x00008000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__SHUTDOWN 0x00010000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__RECVFROM 0x00020000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__SENDTO 0x00040000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__RECV_MSG 0x00080000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__SEND_MSG 0x00100000UL -#define NETLINK_KOBJECT_UEVENT_SOCKET__NAME_BIND 0x00200000UL - diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 960ef18ddc4..ddf8eec03f2 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -12,8 +12,9 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/init.h> +#include <linux/audit.h> +#include <linux/lsm_audit.h> #include <linux/in6.h> -#include <asm/system.h> #include "flask.h" #include "av_permissions.h" #include "security.h" @@ -30,62 +31,15 @@ extern int selinux_enforcing; struct avc_entry; struct task_struct; -struct vfsmount; -struct dentry; struct inode; struct sock; struct sk_buff; -/* Auxiliary data to use in generating the audit record. */ -struct avc_audit_data { - char type; -#define AVC_AUDIT_DATA_FS 1 -#define AVC_AUDIT_DATA_NET 2 -#define AVC_AUDIT_DATA_CAP 3 -#define AVC_AUDIT_DATA_IPC 4 - struct task_struct *tsk; - union { - struct { - struct vfsmount *mnt; - struct dentry *dentry; - struct inode *inode; - } fs; - struct { - char *netif; - struct sock *sk; - u16 family; - u16 dport; - u16 sport; - union { - struct { - u32 daddr; - u32 saddr; - } v4; - struct { - struct in6_addr daddr; - struct in6_addr saddr; - } v6; - } fam; - } net; - int cap; - int ipc_id; - } u; -}; - -#define v4info fam.v4 -#define v6info fam.v6 - -/* Initialize an AVC audit data structure. */ -#define AVC_AUDIT_DATA_INIT(_d,_t) \ - { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } - /* * AVC statistics */ -struct avc_cache_stats -{ +struct avc_cache_stats { unsigned int lookups; - unsigned int hits; unsigned int misses; unsigned int allocations; unsigned int reclaims; @@ -93,22 +47,111 @@ struct avc_cache_stats }; /* + * We only need this data after we have decided to send an audit message. + */ +struct selinux_audit_data { + u32 ssid; + u32 tsid; + u16 tclass; + u32 requested; + u32 audited; + u32 denied; + int result; +}; + +/* * AVC operations */ void __init avc_init(void); -void avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, int result, struct avc_audit_data *auditdata); - +static inline u32 avc_audit_required(u32 requested, + struct av_decision *avd, + int result, + u32 auditdeny, + u32 *deniedp) +{ + u32 denied, audited; + denied = requested & ~avd->allowed; + if (unlikely(denied)) { + audited = denied & avd->auditdeny; + /* + * auditdeny is TRICKY! Setting a bit in + * this field means that ANY denials should NOT be audited if + * the policy contains an explicit dontaudit rule for that + * permission. Take notice that this is unrelated to the + * actual permissions that were denied. As an example lets + * assume: + * + * denied == READ + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) + * auditdeny & ACCESS == 1 + * + * We will NOT audit the denial even though the denied + * permission was READ and the auditdeny checks were for + * ACCESS + */ + if (auditdeny && !(auditdeny & avd->auditdeny)) + audited = 0; + } else if (result) + audited = denied = requested; + else + audited = requested & avd->auditallow; + *deniedp = denied; + return audited; +} + +int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, + u32 requested, u32 audited, u32 denied, int result, + struct common_audit_data *a, + unsigned flags); + +/** + * 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 + * @flags: VFS walk flags + * + * 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. + */ +static inline int avc_audit(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct av_decision *avd, + int result, + struct common_audit_data *a) +{ + u32 audited, denied; + audited = avc_audit_required(requested, avd, result, 0, &denied); + if (likely(!audited)) + return 0; + return slow_avc_audit(ssid, tsid, tclass, + requested, audited, denied, result, + a, 0); +} + +#define AVC_STRICT 1 /* Ignore permissive mode. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd); + u16 tclass, u32 requested, + unsigned flags, + struct av_decision *avd); int avc_has_perm(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct avc_audit_data *auditdata); + u16 tclass, u32 requested, + struct common_audit_data *auditdata); + +u32 avc_policy_seqno(void); #define AVC_CALLBACK_GRANT 1 #define AVC_CALLBACK_TRY_REVOKE 2 @@ -119,16 +162,15 @@ int avc_has_perm(u32 ssid, u32 tsid, #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); +int avc_add_callback(int (*callback)(u32 event), u32 events); /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); extern unsigned int avc_cache_threshold; +/* Attempt to free avc node cache */ +void avc_disable(void); + #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats); #endif diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index 450a2831e2e..d5c328452df 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -10,5 +10,19 @@ int avc_ss_reset(u32 seqno); +/* Class/perm mapping support */ +struct security_class_mapping { + const char *name; + const char *perms[sizeof(u32) * 8 + 1]; +}; + +extern struct security_class_mapping secclass_map[]; + +/* + * The security server must be initialized before + * any labeling or access decisions can be provided. + */ +extern int ss_initialized; + #endif /* _SELINUX_AVC_SS_H_ */ diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h deleted file mode 100644 index 77b2c5996f3..00000000000 --- a/security/selinux/include/class_to_string.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is automatically generated. Do not edit. */ -/* - * Security object class definitions - */ - S_("null") - S_("security") - S_("process") - S_("system") - S_("capability") - S_("filesystem") - S_("file") - S_("dir") - S_("fd") - S_("lnk_file") - S_("chr_file") - S_("blk_file") - S_("sock_file") - S_("fifo_file") - S_("socket") - S_("tcp_socket") - S_("udp_socket") - S_("rawip_socket") - S_("node") - S_("netif") - S_("netlink_socket") - S_("packet_socket") - S_("key_socket") - S_("unix_stream_socket") - S_("unix_dgram_socket") - S_("sem") - S_("msg") - S_("msgq") - S_("shm") - S_("ipc") - S_("passwd") - S_("drawable") - S_("window") - S_("gc") - S_("font") - S_("colormap") - S_("property") - S_("cursor") - S_("xclient") - S_("xinput") - S_("xserver") - S_("xextension") - S_("pax") - S_("netlink_route_socket") - S_("netlink_firewall_socket") - S_("netlink_tcpdiag_socket") - S_("netlink_nflog_socket") - S_("netlink_xfrm_socket") - S_("netlink_selinux_socket") - S_("netlink_audit_socket") - S_("netlink_ip6fw_socket") - S_("netlink_dnrt_socket") - S_("dbus") - S_("nscd") - S_("association") - S_("netlink_kobject_uevent_socket") diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h new file mode 100644 index 00000000000..be491a74c1e --- /dev/null +++ b/security/selinux/include/classmap.h @@ -0,0 +1,155 @@ +#define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \ + "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" + +#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ + "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ + "open", "execmod" + +#define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ + "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ + "sendto", "recv_msg", "send_msg", "name_bind" + +#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \ + "write", "associate", "unix_read", "unix_write" + +/* + * Note: The name for any socket class should be suffixed by "socket", + * and doesn't contain more than one substr of "socket". + */ +struct security_class_mapping secclass_map[] = { + { "security", + { "compute_av", "compute_create", "compute_member", + "check_context", "load_policy", "compute_relabel", + "compute_user", "setenforce", "setbool", "setsecparam", + "setcheckreqprot", "read_policy", NULL } }, + { "process", + { "fork", "transition", "sigchld", "sigkill", + "sigstop", "signull", "signal", "ptrace", "getsched", "setsched", + "getsession", "getpgid", "setpgid", "getcap", "setcap", "share", + "getattr", "setexec", "setfscreate", "noatsecure", "siginh", + "setrlimit", "rlimitinh", "dyntransition", "setcurrent", + "execmem", "execstack", "execheap", "setkeycreate", + "setsockcreate", NULL } }, + { "system", + { "ipc_info", "syslog_read", "syslog_mod", + "syslog_console", "module_request", NULL } }, + { "capability", + { "chown", "dac_override", "dac_read_search", + "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", + "linux_immutable", "net_bind_service", "net_broadcast", + "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", + "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", + "sys_boot", "sys_nice", "sys_resource", "sys_time", + "sys_tty_config", "mknod", "lease", "audit_write", + "audit_control", "setfcap", NULL } }, + { "filesystem", + { "mount", "remount", "unmount", "getattr", + "relabelfrom", "relabelto", "transition", "associate", "quotamod", + "quotaget", NULL } }, + { "file", + { COMMON_FILE_PERMS, + "execute_no_trans", "entrypoint", NULL } }, + { "dir", + { COMMON_FILE_PERMS, "add_name", "remove_name", + "reparent", "search", "rmdir", NULL } }, + { "fd", { "use", NULL } }, + { "lnk_file", + { COMMON_FILE_PERMS, NULL } }, + { "chr_file", + { COMMON_FILE_PERMS, NULL } }, + { "blk_file", + { COMMON_FILE_PERMS, NULL } }, + { "sock_file", + { COMMON_FILE_PERMS, NULL } }, + { "fifo_file", + { COMMON_FILE_PERMS, NULL } }, + { "socket", + { COMMON_SOCK_PERMS, NULL } }, + { "tcp_socket", + { COMMON_SOCK_PERMS, + "connectto", "newconn", "acceptfrom", "node_bind", "name_connect", + NULL } }, + { "udp_socket", + { COMMON_SOCK_PERMS, + "node_bind", NULL } }, + { "rawip_socket", + { COMMON_SOCK_PERMS, + "node_bind", NULL } }, + { "node", + { "tcp_recv", "tcp_send", "udp_recv", "udp_send", + "rawip_recv", "rawip_send", "enforce_dest", + "dccp_recv", "dccp_send", "recvfrom", "sendto", NULL } }, + { "netif", + { "tcp_recv", "tcp_send", "udp_recv", "udp_send", + "rawip_recv", "rawip_send", "dccp_recv", "dccp_send", + "ingress", "egress", NULL } }, + { "netlink_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "packet_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "key_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "unix_stream_socket", + { COMMON_SOCK_PERMS, "connectto", "newconn", "acceptfrom", NULL + } }, + { "unix_dgram_socket", + { COMMON_SOCK_PERMS, NULL + } }, + { "sem", + { COMMON_IPC_PERMS, NULL } }, + { "msg", { "send", "receive", NULL } }, + { "msgq", + { COMMON_IPC_PERMS, "enqueue", NULL } }, + { "shm", + { COMMON_IPC_PERMS, "lock", NULL } }, + { "ipc", + { COMMON_IPC_PERMS, NULL } }, + { "netlink_route_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", NULL } }, + { "netlink_firewall_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", NULL } }, + { "netlink_tcpdiag_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", NULL } }, + { "netlink_nflog_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "netlink_xfrm_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", NULL } }, + { "netlink_selinux_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "netlink_audit_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", "nlmsg_relay", "nlmsg_readpriv", + "nlmsg_tty_audit", NULL } }, + { "netlink_ip6fw_socket", + { COMMON_SOCK_PERMS, + "nlmsg_read", "nlmsg_write", NULL } }, + { "netlink_dnrt_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "association", + { "sendto", "recvfrom", "setcontext", "polmatch", NULL } }, + { "netlink_kobject_uevent_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "appletalk_socket", + { COMMON_SOCK_PERMS, NULL } }, + { "packet", + { "send", "recv", "relabelto", "forward_in", "forward_out", NULL } }, + { "key", + { "view", "read", "write", "search", "link", "setattr", "create", + NULL } }, + { "dccp_socket", + { COMMON_SOCK_PERMS, + "node_bind", "name_connect", NULL } }, + { "memprotect", { "mmap_zero", NULL } }, + { "peer", { "recv", NULL } }, + { "capability2", + { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", + "audit_read", NULL } }, + { "kernel_service", { "use_as_override", "create_files_as", NULL } }, + { "tun_socket", + { COMMON_SOCK_PERMS, "attach_queue", NULL } }, + { NULL } + }; diff --git a/security/selinux/include/common_perm_to_string.h b/security/selinux/include/common_perm_to_string.h deleted file mode 100644 index ce5b6e2fe9d..00000000000 --- a/security/selinux/include/common_perm_to_string.h +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is automatically generated. Do not edit. */ -TB_(common_file_perm_to_string) - S_("ioctl") - S_("read") - S_("write") - S_("create") - S_("getattr") - S_("setattr") - S_("lock") - S_("relabelfrom") - S_("relabelto") - S_("append") - S_("unlink") - S_("link") - S_("rename") - S_("execute") - S_("swapon") - S_("quotaon") - S_("mounton") -TE_(common_file_perm_to_string) - -TB_(common_socket_perm_to_string) - S_("ioctl") - S_("read") - S_("write") - S_("create") - S_("getattr") - S_("setattr") - S_("lock") - S_("relabelfrom") - S_("relabelto") - S_("append") - S_("bind") - S_("connect") - S_("listen") - S_("accept") - S_("getopt") - S_("setopt") - S_("shutdown") - S_("recvfrom") - S_("sendto") - S_("recv_msg") - S_("send_msg") - S_("name_bind") -TE_(common_socket_perm_to_string) - -TB_(common_ipc_perm_to_string) - S_("create") - S_("destroy") - S_("getattr") - S_("setattr") - S_("read") - S_("write") - S_("associate") - S_("unix_read") - S_("unix_write") -TE_(common_ipc_perm_to_string) - diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h deleted file mode 100644 index eb9f50823f6..00000000000 --- a/security/selinux/include/flask.h +++ /dev/null @@ -1,97 +0,0 @@ -/* This file is automatically generated. Do not edit. */ -#ifndef _SELINUX_FLASK_H_ -#define _SELINUX_FLASK_H_ - -/* - * Security object class definitions - */ -#define SECCLASS_SECURITY 1 -#define SECCLASS_PROCESS 2 -#define SECCLASS_SYSTEM 3 -#define SECCLASS_CAPABILITY 4 -#define SECCLASS_FILESYSTEM 5 -#define SECCLASS_FILE 6 -#define SECCLASS_DIR 7 -#define SECCLASS_FD 8 -#define SECCLASS_LNK_FILE 9 -#define SECCLASS_CHR_FILE 10 -#define SECCLASS_BLK_FILE 11 -#define SECCLASS_SOCK_FILE 12 -#define SECCLASS_FIFO_FILE 13 -#define SECCLASS_SOCKET 14 -#define SECCLASS_TCP_SOCKET 15 -#define SECCLASS_UDP_SOCKET 16 -#define SECCLASS_RAWIP_SOCKET 17 -#define SECCLASS_NODE 18 -#define SECCLASS_NETIF 19 -#define SECCLASS_NETLINK_SOCKET 20 -#define SECCLASS_PACKET_SOCKET 21 -#define SECCLASS_KEY_SOCKET 22 -#define SECCLASS_UNIX_STREAM_SOCKET 23 -#define SECCLASS_UNIX_DGRAM_SOCKET 24 -#define SECCLASS_SEM 25 -#define SECCLASS_MSG 26 -#define SECCLASS_MSGQ 27 -#define SECCLASS_SHM 28 -#define SECCLASS_IPC 29 -#define SECCLASS_PASSWD 30 -#define SECCLASS_DRAWABLE 31 -#define SECCLASS_WINDOW 32 -#define SECCLASS_GC 33 -#define SECCLASS_FONT 34 -#define SECCLASS_COLORMAP 35 -#define SECCLASS_PROPERTY 36 -#define SECCLASS_CURSOR 37 -#define SECCLASS_XCLIENT 38 -#define SECCLASS_XINPUT 39 -#define SECCLASS_XSERVER 40 -#define SECCLASS_XEXTENSION 41 -#define SECCLASS_PAX 42 -#define SECCLASS_NETLINK_ROUTE_SOCKET 43 -#define SECCLASS_NETLINK_FIREWALL_SOCKET 44 -#define SECCLASS_NETLINK_TCPDIAG_SOCKET 45 -#define SECCLASS_NETLINK_NFLOG_SOCKET 46 -#define SECCLASS_NETLINK_XFRM_SOCKET 47 -#define SECCLASS_NETLINK_SELINUX_SOCKET 48 -#define SECCLASS_NETLINK_AUDIT_SOCKET 49 -#define SECCLASS_NETLINK_IP6FW_SOCKET 50 -#define SECCLASS_NETLINK_DNRT_SOCKET 51 -#define SECCLASS_DBUS 52 -#define SECCLASS_NSCD 53 -#define SECCLASS_ASSOCIATION 54 -#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55 - -/* - * Security identifier indices for initial entities - */ -#define SECINITSID_KERNEL 1 -#define SECINITSID_SECURITY 2 -#define SECINITSID_UNLABELED 3 -#define SECINITSID_FS 4 -#define SECINITSID_FILE 5 -#define SECINITSID_FILE_LABELS 6 -#define SECINITSID_INIT 7 -#define SECINITSID_ANY_SOCKET 8 -#define SECINITSID_PORT 9 -#define SECINITSID_NETIF 10 -#define SECINITSID_NETMSG 11 -#define SECINITSID_NODE 12 -#define SECINITSID_IGMP_PACKET 13 -#define SECINITSID_ICMP_SOCKET 14 -#define SECINITSID_TCP_SOCKET 15 -#define SECINITSID_SYSCTL_MODPROBE 16 -#define SECINITSID_SYSCTL 17 -#define SECINITSID_SYSCTL_FS 18 -#define SECINITSID_SYSCTL_KERNEL 19 -#define SECINITSID_SYSCTL_NET 20 -#define SECINITSID_SYSCTL_NET_UNIX 21 -#define SECINITSID_SYSCTL_VM 22 -#define SECINITSID_SYSCTL_DEV 23 -#define SECINITSID_KMOD 24 -#define SECINITSID_POLICY 25 -#define SECINITSID_SCMP_PACKET 26 -#define SECINITSID_DEVNULL 27 - -#define SECINITSID_NUM 27 - -#endif diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h index d4fac82793a..a59b64e3fd0 100644 --- a/security/selinux/include/initial_sid_to_string.h +++ b/security/selinux/include/initial_sid_to_string.h @@ -1,5 +1,5 @@ /* This file is automatically generated. Do not edit. */ -static char *initial_sid_to_string[] = +static const char *initial_sid_to_string[] = { "null", "kernel", diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 8bd6f9992d2..43d507242b4 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -7,6 +7,8 @@ * Author: James Morris <jmorris@redhat.com> * * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Paul Moore <paul@paul-moore.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,7 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ -int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid); +int sel_netif_sid(int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h new file mode 100644 index 00000000000..8c59b8f150e --- /dev/null +++ b/security/selinux/include/netlabel.h @@ -0,0 +1,149 @@ +/* + * SELinux interface to the NetLabel subsystem + * + * Author: Paul Moore <paul@paul-moore.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _SELINUX_NETLABEL_H_ +#define _SELINUX_NETLABEL_H_ + +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/net.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/request_sock.h> + +#include "avc.h" +#include "objsec.h" + +#ifdef CONFIG_NETLABEL +void selinux_netlbl_cache_invalidate(void); + +void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway); + +void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec); +void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec); + +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, + u32 *type, + u32 *sid); +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid); + +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family); +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family); +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family); +int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + u16 family, + struct common_audit_data *ad); +int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname); +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr); + +#else +static inline void selinux_netlbl_cache_invalidate(void) +{ + return; +} + +static inline void selinux_netlbl_err(struct sk_buff *skb, + int error, + int gateway) +{ + return; +} + +static inline void selinux_netlbl_sk_security_free( + struct sk_security_struct *sksec) +{ + return; +} + +static inline void selinux_netlbl_sk_security_reset( + struct sk_security_struct *sksec) +{ + return; +} + +static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, + u32 *type, + u32 *sid) +{ + *type = NETLBL_NLTYPE_NONE; + *sid = SECSID_NULL; + return 0; +} +static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid) +{ + return 0; +} + +static inline int selinux_netlbl_conn_setsid(struct sock *sk, + struct sockaddr *addr) +{ + return 0; +} + +static inline int selinux_netlbl_inet_conn_request(struct request_sock *req, + u16 family) +{ + return 0; +} +static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family) +{ + return; +} +static inline int selinux_netlbl_socket_post_create(struct sock *sk, + u16 family) +{ + return 0; +} +static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + u16 family, + struct common_audit_data *ad) +{ + return 0; +} +static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname) +{ + return 0; +} +static inline int selinux_netlbl_socket_connect(struct sock *sk, + struct sockaddr *addr) +{ + return 0; +} +#endif /* CONFIG_NETLABEL */ + +#endif diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h new file mode 100644 index 00000000000..df7a5ed6c69 --- /dev/null +++ b/security/selinux/include/netnode.h @@ -0,0 +1,32 @@ +/* + * Network node table + * + * SELinux must keep a mapping of network nodes to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead since most of these queries happen on + * a per-packet basis. + * + * Author: Paul Moore <paul@paul-moore.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _SELINUX_NETNODE_H +#define _SELINUX_NETNODE_H + +int sel_netnode_sid(void *addr, u16 family, u32 *sid); + +#endif diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h new file mode 100644 index 00000000000..4d965b83d73 --- /dev/null +++ b/security/selinux/include/netport.h @@ -0,0 +1,31 @@ +/* + * Network port table + * + * SELinux must keep a mapping of network ports to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead. + * + * Author: Paul Moore <paul@paul-moore.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _SELINUX_NETPORT_H +#define _SELINUX_NETPORT_H + +int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid); + +#endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 887937c8134..078e553f52f 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -4,16 +4,16 @@ * This file contains the SELinux security data structures for kernel objects. * * Author(s): Stephen Smalley, <sds@epoch.ncsc.mil> - * Chris Vance, <cvance@nai.com> - * Wayne Salamon, <wsalamon@nai.com> - * James Morris <jmorris@redhat.com> + * Chris Vance, <cvance@nai.com> + * Wayne Salamon, <wsalamon@nai.com> + * James Morris <jmorris@redhat.com> * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * as published by the Free Software Foundation. */ #ifndef _SELINUX_OBJSEC_H_ #define _SELINUX_OBJSEC_H_ @@ -23,88 +23,102 @@ #include <linux/fs.h> #include <linux/binfmts.h> #include <linux/in.h> +#include <linux/spinlock.h> #include "flask.h" #include "avc.h" struct task_security_struct { - 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 */ - u32 ptrace_sid; /* SID of ptrace parent */ + u32 osid; /* SID prior to last execve */ + u32 sid; /* current SID */ + u32 exec_sid; /* exec SID */ + u32 create_sid; /* fscreate SID */ + u32 keycreate_sid; /* keycreate SID */ + u32 sockcreate_sid; /* fscreate SID */ }; 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 */ - unsigned char initialized; /* initialization flag */ - struct semaphore sem; - unsigned char inherit; /* inherit SID from parent entry */ + struct inode *inode; /* back pointer to inode object */ + union { + struct list_head list; /* list of inode_security_struct */ + struct rcu_head rcu; /* for freeing the inode_security_struct */ + }; + u32 task_sid; /* SID of creating task */ + u32 sid; /* SID of this object */ + u16 sclass; /* security class of this object */ + unsigned char initialized; /* initialization flag */ + struct mutex lock; }; 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) */ + u32 sid; /* SID of open file description */ + u32 fown_sid; /* SID of file owner (for SIGIO) */ + u32 isid; /* SID of inode at the time of file open */ + u32 pseqno; /* Policy seqno at the time of file open */ }; 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 */ + struct super_block *sb; /* back pointer to sb object */ + u32 sid; /* SID of file system superblock */ u32 def_sid; /* default SID for labeling */ - unsigned int behavior; /* labeling behavior */ - unsigned char initialized; /* initialization flag */ - unsigned char proc; /* proc fs */ - struct semaphore sem; + u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */ + unsigned short behavior; /* labeling behavior */ + unsigned short flags; /* which mount options were specified */ + struct mutex lock; struct list_head isec_head; spinlock_t isec_lock; }; struct msg_security_struct { - unsigned long magic; /* magic number for this module */ - struct msg_msg *msg; /* back pointer */ - u32 sid; /* SID of message */ + u32 sid; /* SID of message */ }; 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 */ + u32 sid; /* SID of IPC resource */ }; -struct bprm_security_struct { - unsigned long magic; /* magic number for this module */ - struct linux_binprm *bprm; /* back pointer to bprm object */ - u32 sid; /* SID for transformed process */ - unsigned char set; +struct netif_security_struct { + int ifindex; /* device index */ + u32 sid; /* SID for this interface */ +}; - /* - * unsafe is used to share failure information from bprm_apply_creds() - * to bprm_post_apply_creds(). - */ - char unsafe; +struct netnode_security_struct { + union { + __be32 ipv4; /* IPv4 node address */ + struct in6_addr ipv6; /* IPv6 node address */ + } addr; + u32 sid; /* SID for this node */ + u16 family; /* address family */ }; -struct netif_security_struct { - struct net_device *dev; /* back pointer */ - u32 if_sid; /* SID for this interface */ - u32 msg_sid; /* default SID for messages received on this interface */ +struct netport_security_struct { + u32 sid; /* SID for this node */ + u16 port; /* port number */ + u8 protocol; /* transport protocol */ }; struct sk_security_struct { - unsigned long magic; /* magic number for this module */ - struct sock *sk; /* back pointer to sk object */ +#ifdef CONFIG_NETLABEL + enum { /* NetLabel state */ + NLBL_UNSET = 0, + NLBL_REQUIRE, + NLBL_LABELED, + NLBL_REQSKB, + NLBL_CONNLABELED, + } nlbl_state; + struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */ +#endif + u32 sid; /* SID of this object */ u32 peer_sid; /* SID of peer */ + u16 sclass; /* sock security class */ +}; + +struct tun_security_struct { + u32 sid; /* SID for the tun device sockets */ +}; + +struct key_security_struct { + u32 sid; /* SID of key */ }; extern unsigned int selinux_checkreqprot; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 5f016c98056..ce7852cf526 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -8,14 +8,15 @@ #ifndef _SELINUX_SECURITY_H_ #define _SELINUX_SECURITY_H_ +#include <linux/dcache.h> +#include <linux/magic.h> +#include <linux/types.h> #include "flask.h" #define SECSID_NULL 0x00000000 /* unspecified SID */ #define SECSID_WILD 0xffffffff /* wildcard SID */ #define SECCLASS_NULL 0x0000 /* no class */ -#define SELINUX_MAGIC 0xf97cff8c - /* Identify specific policy version changes */ #define POLICYDB_VERSION_BASE 15 #define POLICYDB_VERSION_BOOL 16 @@ -24,35 +25,102 @@ #define POLICYDB_VERSION_VALIDATETRANS 19 #define POLICYDB_VERSION_MLS 19 #define POLICYDB_VERSION_AVTAB 20 +#define POLICYDB_VERSION_RANGETRANS 21 +#define POLICYDB_VERSION_POLCAP 22 +#define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 +#define POLICYDB_VERSION_FILENAME_TRANS 25 +#define POLICYDB_VERSION_ROLETRANS 26 +#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 +#define POLICYDB_VERSION_DEFAULT_TYPE 28 +#define POLICYDB_VERSION_CONSTRAINT_NAMES 29 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_AVTAB - -#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM -extern int selinux_enabled; +#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX +#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define selinux_enabled 1 +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES #endif -extern int selinux_mls_enabled; +/* Mask for just the mount related flags */ +#define SE_MNTMASK 0x0f +/* Super block security struct flags for mount options */ +/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */ +#define CONTEXT_MNT 0x01 +#define FSCONTEXT_MNT 0x02 +#define ROOTCONTEXT_MNT 0x04 +#define DEFCONTEXT_MNT 0x08 +#define SBLABEL_MNT 0x10 +/* Non-mount related flags */ +#define SE_SBINITIALIZED 0x0100 +#define SE_SBPROC 0x0200 + +#define CONTEXT_STR "context=" +#define FSCONTEXT_STR "fscontext=" +#define ROOTCONTEXT_STR "rootcontext=" +#define DEFCONTEXT_STR "defcontext=" +#define LABELSUPP_STR "seclabel" + +struct netlbl_lsm_secattr; -int security_load_policy(void * data, size_t len); +extern int selinux_enabled; +/* Policy capabilities */ +enum { + POLICYDB_CAPABILITY_NETPEER, + POLICYDB_CAPABILITY_OPENPERM, + POLICYDB_CAPABILITY_REDHAT1, + POLICYDB_CAPABILITY_ALWAYSNETWORK, + __POLICYDB_CAPABILITY_MAX +}; +#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) + +extern int selinux_policycap_netpeer; +extern int selinux_policycap_openperm; +extern int selinux_policycap_alwaysnetwork; + +/* + * type_datum properties + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 + +/* limitation of boundary depth */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 + +int security_mls_enabled(void); + +int security_load_policy(void *data, size_t len); +int security_read_policy(void **data, size_t *len); +size_t security_policydb_len(void); + +int security_policycap_supported(unsigned int req_cap); + +#define SEL_VEC_MAX 32 struct av_decision { u32 allowed; - u32 decided; u32 auditallow; u32 auditdeny; u32 seqno; + u32 flags; }; -int security_compute_av(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd); +/* definitions of av_decision.flags */ +#define AVD_FLAGS_PERMISSIVE 0x0001 -int security_transition_sid(u32 ssid, u32 tsid, - u16 tclass, u32 *out_sid); +void security_compute_av(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd); + +void security_compute_av_user(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd); + +int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, + const struct qstr *qstr, u32 *out_sid); + +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid); int security_member_sid(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid); @@ -63,25 +131,42 @@ int security_change_sid(u32 ssid, u32 tsid, 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_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len); + +int security_context_to_sid(const char *scontext, u32 scontext_len, + u32 *out_sid, gfp_t gfp); + +int security_context_to_sid_default(const char *scontext, u32 scontext_len, + u32 *out_sid, u32 def_sid, gfp_t gfp_flags); -int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *out_sid, u32 def_sid); +int security_context_to_sid_force(const char *scontext, u32 scontext_len, + u32 *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_port_sid(u8 protocol, u16 port, u32 *out_sid); -int security_netif_sid(char *name, u32 *if_sid, - u32 *msg_sid); +int security_netif_sid(char *name, u32 *if_sid); int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid); int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 tclass); + u16 tclass); + +int security_bounded_transition(u32 oldsid, u32 newsid); + +int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); + +int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, + u32 xfrm_sid, + u32 *peer_sid); + +int security_get_classes(char ***classes, int *nclasses); +int security_get_permissions(char *class, char ***perms, int *nperms); +int security_get_reject_unknown(void); +int security_get_allow_unknown(void); #define SECURITY_FS_USE_XATTR 1 /* use xattr */ #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ @@ -89,12 +174,64 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, #define SECURITY_FS_USE_GENFS 4 /* use the genfs support */ #define SECURITY_FS_USE_NONE 5 /* no labeling support */ #define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */ +#define SECURITY_FS_USE_NATIVE 7 /* use native label support */ +#define SECURITY_FS_USE_MAX 7 /* Highest SECURITY_FS_USE_XXX */ -int security_fs_use(const char *fstype, unsigned int *behavior, - u32 *sid); +int security_fs_use(struct super_block *sb); int security_genfs_sid(const char *fstype, char *name, u16 sclass, u32 *sid); +#ifdef CONFIG_NETLABEL +int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, + u32 *sid); + +int security_netlbl_sid_to_secattr(u32 sid, + struct netlbl_lsm_secattr *secattr); +#else +static inline int security_netlbl_secattr_to_sid( + struct netlbl_lsm_secattr *secattr, + u32 *sid) +{ + return -EIDRM; +} + +static inline int security_netlbl_sid_to_secattr(u32 sid, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOENT; +} +#endif /* CONFIG_NETLABEL */ + +const char *security_get_initial_sid_context(u32 sid); + +/* + * status notifier using mmap interface + */ +extern struct page *selinux_kernel_status_page(void); + +#define SELINUX_KERNEL_STATUS_VERSION 1 +struct selinux_kernel_status { + u32 version; /* version number of thie structure */ + u32 sequence; /* sequence number of seqlock logic */ + u32 enforcing; /* current setting of enforcing mode */ + u32 policyload; /* times of policy reloaded */ + u32 deny_unknown; /* current setting of deny_unknown */ + /* + * The version > 0 supports above members. + */ +} __attribute__((packed)); + +extern void selinux_status_update_setenforce(int enforcing); +extern void selinux_status_update_policyload(int seqno); +extern void selinux_complete_init(void); +extern int selinux_disable(void); +extern void exit_sel_fs(void); +extern struct path selinux_null; +extern struct vfsmount *selinuxfs_mount; +extern void selnl_notify_setenforce(int val); +extern void selnl_notify_policyload(u32 seqno); +extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); + #endif /* _SELINUX_SECURITY_H_ */ diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 8e87996c6dd..1450f85b946 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -2,52 +2,91 @@ * SELinux support for the XFRM LSM hooks * * Author : Trent Jaeger, <jaegert@us.ibm.com> + * Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.com> */ #ifndef _SELINUX_XFRM_H_ #define _SELINUX_XFRM_H_ -int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx); -int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new); -void selinux_xfrm_policy_free(struct xfrm_policy *xp); -int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx); +#include <net/flow.h> + +int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, + struct xfrm_user_sec_ctx *uctx, + gfp_t gfp); +int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx, + struct xfrm_sec_ctx **new_ctxp); +void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx); +int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx); +int selinux_xfrm_state_alloc(struct xfrm_state *x, + struct xfrm_user_sec_ctx *uctx); +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, + struct xfrm_sec_ctx *polsec, u32 secid); void selinux_xfrm_state_free(struct xfrm_state *x); -int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir); +int selinux_xfrm_state_delete(struct xfrm_state *x); +int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir); +int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, + struct xfrm_policy *xp, + const struct flowi *fl); -/* - * Extract the security blob from the sock (it's actually on the socket) - */ -static inline struct inode_security_struct *get_sock_isec(struct sock *sk) -{ - if (!sk->sk_socket) - return NULL; +#ifdef CONFIG_SECURITY_NETWORK_XFRM +extern atomic_t selinux_xfrm_refcount; - return SOCK_INODE(sk->sk_socket)->i_security; +static inline int selinux_xfrm_enabled(void) +{ + return (atomic_read(&selinux_xfrm_refcount) > 0); } +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad); +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad, u8 proto); +int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid); -static inline u32 selinux_no_sk_sid(struct flowi *fl) +static inline void selinux_xfrm_notify_policyload(void) { - /* NOTE: no sock occurs on ICMP reply, forwards, ... */ - /* icmp_reply: authorize as kernel packet */ - if (fl && fl->proto == IPPROTO_ICMP) { - return SECINITSID_KERNEL; + struct net *net; + + rtnl_lock(); + for_each_net(net) { + atomic_inc(&net->xfrm.flow_cache_genid); + rt_genid_bump_all(net); } + rtnl_unlock(); +} +#else +static inline int selinux_xfrm_enabled(void) +{ + return 0; +} - return SECINITSID_ANY_SOCKET; +static inline int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad) +{ + return 0; } -#ifdef CONFIG_SECURITY_NETWORK_XFRM -int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb); -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb); -#else -static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) +static inline int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad, + u8 proto) +{ + return 0; +} + +static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, + int ckall) { + *sid = SECSID_NULL; return 0; } -static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) +static inline void selinux_xfrm_notify_policyload(void) +{ +} + +static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) { - return NF_ACCEPT; + *sid = SECSID_NULL; + return 0; } #endif diff --git a/security/selinux/netif.c b/security/selinux/netif.c index b10c34e8a74..694e9e43855 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -7,6 +7,8 @@ * Author: James Morris <jmorris@redhat.com> * * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Paul Moore <paul@paul-moore.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -14,12 +16,14 @@ */ #include <linux/init.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/notifier.h> #include <linux/netdevice.h> #include <linux/rcupdate.h> +#include <net/net_namespace.h> #include "security.h" #include "objsec.h" @@ -28,16 +32,7 @@ #define SEL_NETIF_HASH_SIZE 64 #define SEL_NETIF_HASH_MAX 1024 -#undef DEBUG - -#ifdef DEBUG -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -struct sel_netif -{ +struct sel_netif { struct list_head list; struct netif_security_struct nsec; struct rcu_head rcu_head; @@ -48,179 +43,216 @@ static LIST_HEAD(sel_netif_list); static DEFINE_SPINLOCK(sel_netif_lock); static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; -static inline u32 sel_netif_hasfn(struct net_device *dev) +/** + * sel_netif_hashfn - Hashing function for the interface table + * @ifindex: the network interface + * + * Description: + * This is the hashing function for the network interface table, it returns the + * bucket number for the given interface. + * + */ +static inline u32 sel_netif_hashfn(int ifindex) { - return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1)); + return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); } -/* - * All of the devices should normally fit in the hash, so we optimize - * for that case. +/** + * sel_netif_find - Search for an interface record + * @ifindex: the network interface + * + * Description: + * Search the network interface table and return the record matching @ifindex. + * If an entry can not be found in the table return NULL. + * */ -static inline struct sel_netif *sel_netif_find(struct net_device *dev) +static inline struct sel_netif *sel_netif_find(int ifindex) { - struct list_head *pos; - int idx = sel_netif_hasfn(dev); + int idx = sel_netif_hashfn(ifindex); + struct sel_netif *netif; - __list_for_each_rcu(pos, &sel_netif_hash[idx]) { - struct sel_netif *netif = list_entry(pos, - struct sel_netif, list); - if (likely(netif->nsec.dev == dev)) + list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) + /* all of the devices should normally fit in the hash, so we + * optimize for that case */ + if (likely(netif->nsec.ifindex == ifindex)) return netif; - } + return NULL; } +/** + * sel_netif_insert - Insert a new interface into the table + * @netif: the new interface record + * + * Description: + * Add a new interface record to the network interface hash table. Returns + * zero on success, negative values on failure. + * + */ static int sel_netif_insert(struct sel_netif *netif) { - int idx, ret = 0; - - if (sel_netif_total >= SEL_NETIF_HASH_MAX) { - ret = -ENOSPC; - goto out; - } - - idx = sel_netif_hasfn(netif->nsec.dev); + int idx; + + if (sel_netif_total >= SEL_NETIF_HASH_MAX) + return -ENOSPC; + + idx = sel_netif_hashfn(netif->nsec.ifindex); list_add_rcu(&netif->list, &sel_netif_hash[idx]); sel_netif_total++; -out: - return ret; -} - -static void sel_netif_free(struct rcu_head *p) -{ - struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); - DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); - kfree(netif); + return 0; } +/** + * sel_netif_destroy - Remove an interface record from the table + * @netif: the existing interface record + * + * Description: + * Remove an existing interface record from the network interface table. + * + */ static void sel_netif_destroy(struct sel_netif *netif) { - DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); - list_del_rcu(&netif->list); sel_netif_total--; - call_rcu(&netif->rcu_head, sel_netif_free); + kfree_rcu(netif, rcu_head); } -static struct sel_netif *sel_netif_lookup(struct net_device *dev) +/** + * sel_netif_sid_slow - Lookup the SID of a network interface using the policy + * @ifindex: the network interface + * @sid: interface SID + * + * Description: + * This function determines the SID of a network interface by quering the + * security policy. The result is added to the network interface table to + * speedup future queries. Returns zero on success, negative values on + * failure. + * + */ +static int sel_netif_sid_slow(int ifindex, u32 *sid) { int ret; - struct sel_netif *netif, *new; - struct netif_security_struct *nsec; - - netif = sel_netif_find(dev); - if (likely(netif != NULL)) - goto out; - - new = kzalloc(sizeof(*new), GFP_ATOMIC); - if (!new) { - netif = ERR_PTR(-ENOMEM); - goto out; + struct sel_netif *netif; + struct sel_netif *new = NULL; + struct net_device *dev; + + /* NOTE: we always use init's network namespace since we don't + * currently support containers */ + + dev = dev_get_by_index(&init_net, ifindex); + if (unlikely(dev == NULL)) { + printk(KERN_WARNING + "SELinux: failure in sel_netif_sid_slow()," + " invalid network interface (%d)\n", ifindex); + return -ENOENT; } - - nsec = &new->nsec; - ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid); - if (ret < 0) { - kfree(new); - netif = ERR_PTR(ret); + spin_lock_bh(&sel_netif_lock); + netif = sel_netif_find(ifindex); + if (netif != NULL) { + *sid = netif->nsec.sid; + ret = 0; goto out; } - - nsec->dev = dev; - - spin_lock_bh(&sel_netif_lock); - - netif = sel_netif_find(dev); - if (netif) { - spin_unlock_bh(&sel_netif_lock); - kfree(new); + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (new == NULL) { + ret = -ENOMEM; goto out; } - + ret = security_netif_sid(dev->name, &new->nsec.sid); + if (ret != 0) + goto out; + new->nsec.ifindex = ifindex; ret = sel_netif_insert(new); - spin_unlock_bh(&sel_netif_lock); - - if (ret) { - kfree(new); - netif = ERR_PTR(ret); + if (ret != 0) goto out; - } + *sid = new->nsec.sid; - netif = new; - - DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name, - nsec->if_sid, nsec->msg_sid); out: - return netif; -} - -static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out) -{ - if (if_sid_out) - *if_sid_out = if_sid_in; - if (msg_sid_out) - *msg_sid_out = msg_sid_in; -} - -static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid) -{ - int ret = 0; - u32 tmp_if_sid, tmp_msg_sid; - - ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid); - if (!ret) - sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid); + spin_unlock_bh(&sel_netif_lock); + dev_put(dev); + if (unlikely(ret)) { + printk(KERN_WARNING + "SELinux: failure in sel_netif_sid_slow()," + " unable to determine network interface label (%d)\n", + ifindex); + kfree(new); + } return ret; } -int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid) +/** + * sel_netif_sid - Lookup the SID of a network interface + * @ifindex: the network interface + * @sid: interface SID + * + * Description: + * This function determines the SID of a network interface using the fastest + * method possible. First the interface table is queried, but if an entry + * can't be found then the policy is queried and the result is added to the + * table to speedup future queries. Returns zero on success, negative values + * on failure. + * + */ +int sel_netif_sid(int ifindex, u32 *sid) { - int ret = 0; struct sel_netif *netif; rcu_read_lock(); - netif = sel_netif_lookup(dev); - if (IS_ERR(netif)) { + netif = sel_netif_find(ifindex); + if (likely(netif != NULL)) { + *sid = netif->nsec.sid; rcu_read_unlock(); - ret = sel_netif_sids_slow(dev, if_sid, msg_sid); - goto out; + return 0; } - sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid); rcu_read_unlock(); -out: - return ret; + + return sel_netif_sid_slow(ifindex, sid); } -static void sel_netif_kill(struct net_device *dev) +/** + * sel_netif_kill - Remove an entry from the network interface table + * @ifindex: the network interface + * + * Description: + * This function removes the entry matching @ifindex from the network interface + * table if it exists. + * + */ +static void sel_netif_kill(int ifindex) { struct sel_netif *netif; + rcu_read_lock(); spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(dev); + netif = sel_netif_find(ifindex); if (netif) sel_netif_destroy(netif); spin_unlock_bh(&sel_netif_lock); + rcu_read_unlock(); } +/** + * sel_netif_flush - Flush the entire network interface table + * + * Description: + * Remove all entries from the network interface table. + * + */ static void sel_netif_flush(void) { int idx; + struct sel_netif *netif; spin_lock_bh(&sel_netif_lock); - for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) { - struct sel_netif *netif; - + for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) list_for_each_entry(netif, &sel_netif_hash[idx], list) sel_netif_destroy(netif); - } spin_unlock_bh(&sel_netif_lock); } -static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid, - u16 class, u32 perms, u32 *retained) +static int sel_netif_avc_callback(u32 event) { if (event == AVC_CALLBACK_RESET) { sel_netif_flush(); @@ -230,12 +262,15 @@ static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid, } static int sel_netif_netdev_notifier_handler(struct notifier_block *this, - unsigned long event, void *ptr) + unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + + if (dev_net(dev) != &init_net) + return NOTIFY_DONE; if (event == NETDEV_DOWN) - sel_netif_kill(dev); + sel_netif_kill(dev->ifindex); return NOTIFY_DONE; } @@ -246,22 +281,20 @@ static struct notifier_block sel_netif_netdev_notifier = { static __init int sel_netif_init(void) { - int i, err = 0; - + int i, err; + if (!selinux_enabled) - goto out; + return 0; for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) INIT_LIST_HEAD(&sel_netif_hash[i]); register_netdevice_notifier(&sel_netif_netdev_notifier); - - err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + + err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET); if (err) panic("avc_add_callback() failed, error %d\n", err); -out: return err; } diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c new file mode 100644 index 00000000000..0364120d1ec --- /dev/null +++ b/security/selinux/netlabel.c @@ -0,0 +1,497 @@ +/* + * SELinux NetLabel Support + * + * This file provides the necessary glue to tie NetLabel into the SELinux + * subsystem. + * + * Author: Paul Moore <paul@paul-moore.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/spinlock.h> +#include <linux/rcupdate.h> +#include <linux/gfp.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/sock.h> +#include <net/netlabel.h> +#include <net/ip.h> +#include <net/ipv6.h> + +#include "objsec.h" +#include "security.h" +#include "netlabel.h" + +/** + * selinux_netlbl_sidlookup_cached - Cache a SID lookup + * @skb: the packet + * @secattr: the NetLabel security attributes + * @sid: the SID + * + * Description: + * Query the SELinux security server to lookup the correct SID for the given + * security attributes. If the query is successful, cache the result to speed + * up future lookups. Returns zero on success, negative values on failure. + * + */ +static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, + struct netlbl_lsm_secattr *secattr, + u32 *sid) +{ + int rc; + + rc = security_netlbl_secattr_to_sid(secattr, sid); + if (rc == 0 && + (secattr->flags & NETLBL_SECATTR_CACHEABLE) && + (secattr->flags & NETLBL_SECATTR_CACHE)) + netlbl_cache_add(skb, secattr); + + return rc; +} + +/** + * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr + * @sk: the socket + * + * Description: + * Generate the NetLabel security attributes for a socket, making full use of + * the socket's attribute cache. Returns a pointer to the security attributes + * on success, NULL on failure. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk) +{ + int rc; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; + + if (sksec->nlbl_secattr != NULL) + return sksec->nlbl_secattr; + + secattr = netlbl_secattr_alloc(GFP_ATOMIC); + if (secattr == NULL) + return NULL; + rc = security_netlbl_sid_to_secattr(sksec->sid, secattr); + if (rc != 0) { + netlbl_secattr_free(secattr); + return NULL; + } + sksec->nlbl_secattr = secattr; + + return secattr; +} + +/** + * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr + * @sk: the socket + * @sid: the SID + * + * Query the socket's cached secattr and if the SID matches the cached value + * return the cache, otherwise return NULL. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr( + const struct sock *sk, + u32 sid) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr; + + if (secattr == NULL) + return NULL; + + if ((secattr->flags & NETLBL_SECATTR_SECID) && + (secattr->attr.secid == sid)) + return secattr; + + return NULL; +} + +/** + * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache + * + * Description: + * Invalidate the NetLabel security attribute mapping cache. + * + */ +void selinux_netlbl_cache_invalidate(void) +{ + netlbl_cache_invalidate(); +} + +/** + * selinux_netlbl_err - Handle a NetLabel packet error + * @skb: the packet + * @error: the error code + * @gateway: true if host is acting as a gateway, false otherwise + * + * Description: + * When a packet is dropped due to a call to avc_has_perm() pass the error + * code to the NetLabel subsystem so any protocol specific processing can be + * done. This is safe to call even if you are unsure if NetLabel labeling is + * present on the packet, NetLabel is smart enough to only act when it should. + * + */ +void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway) +{ + netlbl_skbuff_err(skb, error, gateway); +} + +/** + * selinux_netlbl_sk_security_free - Free the NetLabel fields + * @sksec: the sk_security_struct + * + * Description: + * Free all of the memory in the NetLabel fields of a sk_security_struct. + * + */ +void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec) +{ + if (sksec->nlbl_secattr != NULL) + netlbl_secattr_free(sksec->nlbl_secattr); +} + +/** + * selinux_netlbl_sk_security_reset - Reset the NetLabel fields + * @sksec: the sk_security_struct + * @family: the socket family + * + * Description: + * Called when the NetLabel state of a sk_security_struct needs to be reset. + * The caller is responsible for all the NetLabel sk_security_struct locking. + * + */ +void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec) +{ + sksec->nlbl_state = NLBL_UNSET; +} + +/** + * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel + * @skb: the packet + * @family: protocol family + * @type: NetLabel labeling protocol type + * @sid: the SID + * + * Description: + * Call the NetLabel mechanism to get the security attributes of the given + * packet and use those attributes to determine the correct context/SID to + * assign to the packet. Returns zero on success, negative values on failure. + * + */ +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, + u32 *type, + u32 *sid) +{ + int rc; + struct netlbl_lsm_secattr secattr; + + if (!netlbl_enabled()) { + *sid = SECSID_NULL; + return 0; + } + + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid); + else + *sid = SECSID_NULL; + *type = secattr.type; + netlbl_secattr_destroy(&secattr); + + return rc; +} + +/** + * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid + * @skb: the packet + * @family: protocol family + * @sid: the SID + * + * Description + * Call the NetLabel mechanism to set the label of a packet using @sid. + * Returns zero on success, negative values on failure. + * + */ +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid) +{ + int rc; + struct netlbl_lsm_secattr secattr_storage; + struct netlbl_lsm_secattr *secattr = NULL; + struct sock *sk; + + /* if this is a locally generated packet check to see if it is already + * being labeled by it's parent socket, if it is just exit */ + sk = skb->sk; + if (sk != NULL) { + struct sk_security_struct *sksec = sk->sk_security; + if (sksec->nlbl_state != NLBL_REQSKB) + return 0; + secattr = selinux_netlbl_sock_getattr(sk, sid); + } + if (secattr == NULL) { + secattr = &secattr_storage; + netlbl_secattr_init(secattr); + rc = security_netlbl_sid_to_secattr(sid, secattr); + if (rc != 0) + goto skbuff_setsid_return; + } + + rc = netlbl_skbuff_setattr(skb, family, secattr); + +skbuff_setsid_return: + if (secattr == &secattr_storage) + netlbl_secattr_destroy(secattr); + return rc; +} + +/** + * selinux_netlbl_inet_conn_request - Label an incoming stream connection + * @req: incoming connection request socket + * + * Description: + * A new incoming connection request is represented by @req, we need to label + * the new request_sock here and the stack will ensure the on-the-wire label + * will get preserved when a full sock is created once the connection handshake + * is complete. Returns zero on success, negative values on failure. + * + */ +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family) +{ + int rc; + struct netlbl_lsm_secattr secattr; + + if (family != PF_INET) + return 0; + + netlbl_secattr_init(&secattr); + rc = security_netlbl_sid_to_secattr(req->secid, &secattr); + if (rc != 0) + goto inet_conn_request_return; + rc = netlbl_req_setattr(req, &secattr); +inet_conn_request_return: + netlbl_secattr_destroy(&secattr); + return rc; +} + +/** + * selinux_netlbl_inet_csk_clone - Initialize the newly created sock + * @sk: the new sock + * + * Description: + * A new connection has been established using @sk, we've already labeled the + * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but + * we need to set the NetLabel state here since we now have a sock structure. + * + */ +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family) +{ + struct sk_security_struct *sksec = sk->sk_security; + + if (family == PF_INET) + sksec->nlbl_state = NLBL_LABELED; + else + sksec->nlbl_state = NLBL_UNSET; +} + +/** + * selinux_netlbl_socket_post_create - Label a socket using NetLabel + * @sock: the socket to label + * @family: protocol family + * + * Description: + * Attempt to label a socket using the NetLabel mechanism using the given + * SID. Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) +{ + int rc; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; + + if (family != PF_INET) + return 0; + + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) + return -ENOMEM; + rc = netlbl_sock_setattr(sk, family, secattr); + switch (rc) { + case 0: + sksec->nlbl_state = NLBL_LABELED; + break; + case -EDESTADDRREQ: + sksec->nlbl_state = NLBL_REQSKB; + rc = 0; + break; + } + + return rc; +} + +/** + * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel + * @sksec: the sock's sk_security_struct + * @skb: the packet + * @family: protocol family + * @ad: the audit data + * + * Description: + * Fetch the NetLabel security attributes from @skb and perform an access check + * against the receiving socket. Returns zero on success, negative values on + * error. + * + */ +int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, + struct sk_buff *skb, + u16 family, + struct common_audit_data *ad) +{ + int rc; + u32 nlbl_sid; + u32 perm; + struct netlbl_lsm_secattr secattr; + + if (!netlbl_enabled()) + return 0; + + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid); + else + nlbl_sid = SECINITSID_UNLABELED; + netlbl_secattr_destroy(&secattr); + if (rc != 0) + return rc; + + switch (sksec->sclass) { + case SECCLASS_UDP_SOCKET: + perm = UDP_SOCKET__RECVFROM; + break; + case SECCLASS_TCP_SOCKET: + perm = TCP_SOCKET__RECVFROM; + break; + default: + perm = RAWIP_SOCKET__RECVFROM; + } + + rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad); + if (rc == 0) + return 0; + + if (nlbl_sid != SECINITSID_UNLABELED) + netlbl_skbuff_err(skb, rc, 0); + return rc; +} + +/** + * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel + * @sock: the socket + * @level: the socket level or protocol + * @optname: the socket option name + * + * Description: + * Check the setsockopt() call and if the user is trying to replace the IP + * options on a socket and a NetLabel is in place for the socket deny the + * access; otherwise allow the access. Returns zero when the access is + * allowed, -EACCES when denied, and other negative values on error. + * + */ +int selinux_netlbl_socket_setsockopt(struct socket *sock, + int level, + int optname) +{ + int rc = 0; + struct sock *sk = sock->sk; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr secattr; + + if (level == IPPROTO_IP && optname == IP_OPTIONS && + (sksec->nlbl_state == NLBL_LABELED || + sksec->nlbl_state == NLBL_CONNLABELED)) { + netlbl_secattr_init(&secattr); + lock_sock(sk); + /* call the netlabel function directly as we want to see the + * on-the-wire label that is assigned via the socket's options + * and not the cached netlabel/lsm attributes */ + rc = netlbl_sock_getattr(sk, &secattr); + release_sock(sk); + if (rc == 0) + rc = -EACCES; + else if (rc == -ENOMSG) + rc = 0; + netlbl_secattr_destroy(&secattr); + } + + return rc; +} + +/** + * selinux_netlbl_socket_connect - Label a client-side socket on connect + * @sk: the socket to label + * @addr: the destination address + * + * Description: + * Attempt to label a connected socket with NetLabel using the given address. + * Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) +{ + int rc; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; + + if (sksec->nlbl_state != NLBL_REQSKB && + sksec->nlbl_state != NLBL_CONNLABELED) + return 0; + + lock_sock(sk); + + /* connected sockets are allowed to disconnect when the address family + * is set to AF_UNSPEC, if that is what is happening we want to reset + * the socket */ + if (addr->sa_family == AF_UNSPEC) { + netlbl_sock_delattr(sk); + sksec->nlbl_state = NLBL_REQSKB; + rc = 0; + goto socket_connect_return; + } + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) { + rc = -ENOMEM; + goto socket_connect_return; + } + rc = netlbl_conn_setattr(sk, addr, secattr); + if (rc == 0) + sksec->nlbl_state = NLBL_CONNLABELED; + +socket_connect_return: + release_sock(sk); + return rc; +} diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index e203883406d..828fb6a4e94 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c @@ -11,28 +11,32 @@ */ #include <linux/init.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/stddef.h> #include <linux/kernel.h> -#include <linux/list.h> +#include <linux/export.h> #include <linux/skbuff.h> -#include <linux/netlink.h> #include <linux/selinux_netlink.h> +#include <net/net_namespace.h> +#include <net/netlink.h> + +#include "security.h" static struct sock *selnl; static int selnl_msglen(int msgtype) { int ret = 0; - + switch (msgtype) { case SELNL_MSG_SETENFORCE: ret = sizeof(struct selnl_msg_setenforce); break; - + case SELNL_MSG_POLICYLOAD: ret = sizeof(struct selnl_msg_policyload); break; - + default: BUG(); } @@ -43,16 +47,16 @@ static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void * { switch (msgtype) { case SELNL_MSG_SETENFORCE: { - struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); - + struct selnl_msg_setenforce *msg = nlmsg_data(nlh); + memset(msg, 0, len); msg->val = *((int *)data); break; } - + case SELNL_MSG_POLICYLOAD: { - struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); - + struct selnl_msg_policyload *msg = nlmsg_data(nlh); + memset(msg, 0, len); msg->seqno = *((u32 *)data); break; @@ -66,29 +70,31 @@ static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void * static void selnl_notify(int msgtype, void *data) { int len; - unsigned char *tmp; + sk_buff_data_t tmp; struct sk_buff *skb; struct nlmsghdr *nlh; - + len = selnl_msglen(msgtype); - - skb = alloc_skb(NLMSG_SPACE(len), GFP_USER); + + skb = nlmsg_new(len, GFP_USER); if (!skb) goto oom; tmp = skb->tail; - nlh = NLMSG_PUT(skb, 0, 0, msgtype, len); + nlh = nlmsg_put(skb, 0, 0, msgtype, len, 0); + if (!nlh) + goto out_kfree_skb; selnl_add_payload(nlh, len, msgtype, data); nlh->nlmsg_len = skb->tail - tmp; NETLINK_CB(skb).dst_group = SELNLGRP_AVC; netlink_broadcast(selnl, skb, 0, SELNLGRP_AVC, GFP_USER); out: return; - -nlmsg_failure: + +out_kfree_skb: kfree_skb(skb); oom: - printk(KERN_ERR "SELinux: OOM in %s\n", __FUNCTION__); + printk(KERN_ERR "SELinux: OOM in %s\n", __func__); goto out; } @@ -104,11 +110,14 @@ void selnl_notify_policyload(u32 seqno) static int __init selnl_init(void) { - selnl = netlink_kernel_create(NETLINK_SELINUX, SELNLGRP_MAX, NULL, - THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .groups = SELNLGRP_MAX, + .flags = NL_CFG_F_NONROOT_RECV, + }; + + selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg); if (selnl == NULL) panic("SELinux: Cannot create netlink socket."); - netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); return 0; } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c new file mode 100644 index 00000000000..03a72c32afd --- /dev/null +++ b/security/selinux/netnode.c @@ -0,0 +1,332 @@ +/* + * Network node table + * + * SELinux must keep a mapping of network nodes to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead since most of these queries happen on + * a per-packet basis. + * + * Author: Paul Moore <paul@paul-moore.com> + * + * This code is heavily based on the "netif" concept originally developed by + * James Morris <jmorris@redhat.com> + * (see security/selinux/netif.c for more information) + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/types.h> +#include <linux/rcupdate.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ip.h> +#include <net/ipv6.h> + +#include "netnode.h" +#include "objsec.h" + +#define SEL_NETNODE_HASH_SIZE 256 +#define SEL_NETNODE_HASH_BKT_LIMIT 16 + +struct sel_netnode_bkt { + unsigned int size; + struct list_head list; +}; + +struct sel_netnode { + struct netnode_security_struct nsec; + + struct list_head list; + struct rcu_head rcu; +}; + +/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason + * for this is that I suspect most users will not make heavy use of both + * address families at the same time so one table will usually end up wasted, + * if this becomes a problem we can always add a hash table for each address + * family later */ + +static LIST_HEAD(sel_netnode_list); +static DEFINE_SPINLOCK(sel_netnode_lock); +static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE]; + +/** + * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table + * @addr: IPv4 address + * + * Description: + * This is the IPv4 hashing function for the node interface table, it returns + * the bucket number for the given IP address. + * + */ +static unsigned int sel_netnode_hashfn_ipv4(__be32 addr) +{ + /* at some point we should determine if the mismatch in byte order + * affects the hash function dramatically */ + return (addr & (SEL_NETNODE_HASH_SIZE - 1)); +} + +/** + * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table + * @addr: IPv6 address + * + * Description: + * This is the IPv6 hashing function for the node interface table, it returns + * the bucket number for the given IP address. + * + */ +static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr) +{ + /* just hash the least significant 32 bits to keep things fast (they + * are the most likely to be different anyway), we can revisit this + * later if needed */ + return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1)); +} + +/** + * sel_netnode_find - Search for a node record + * @addr: IP address + * @family: address family + * + * Description: + * Search the network node table and return the record matching @addr. If an + * entry can not be found in the table return NULL. + * + */ +static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) +{ + unsigned int idx; + struct sel_netnode *node; + + switch (family) { + case PF_INET: + idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr); + break; + case PF_INET6: + idx = sel_netnode_hashfn_ipv6(addr); + break; + default: + BUG(); + return NULL; + } + + list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list) + if (node->nsec.family == family) + switch (family) { + case PF_INET: + if (node->nsec.addr.ipv4 == *(__be32 *)addr) + return node; + break; + case PF_INET6: + if (ipv6_addr_equal(&node->nsec.addr.ipv6, + addr)) + return node; + break; + } + + return NULL; +} + +/** + * sel_netnode_insert - Insert a new node into the table + * @node: the new node record + * + * Description: + * Add a new node record to the network address hash table. + * + */ +static void sel_netnode_insert(struct sel_netnode *node) +{ + unsigned int idx; + + switch (node->nsec.family) { + case PF_INET: + idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4); + break; + case PF_INET6: + idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6); + break; + default: + BUG(); + return; + } + + /* we need to impose a limit on the growth of the hash table so check + * this bucket to make sure it is within the specified bounds */ + list_add_rcu(&node->list, &sel_netnode_hash[idx].list); + if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) { + struct sel_netnode *tail; + tail = list_entry( + rcu_dereference_protected(sel_netnode_hash[idx].list.prev, + lockdep_is_held(&sel_netnode_lock)), + struct sel_netnode, list); + list_del_rcu(&tail->list); + kfree_rcu(tail, rcu); + } else + sel_netnode_hash[idx].size++; +} + +/** + * sel_netnode_sid_slow - Lookup the SID of a network address using the policy + * @addr: the IP address + * @family: the address family + * @sid: node SID + * + * Description: + * This function determines the SID of a network address by quering the + * security policy. The result is added to the network address table to + * speedup future queries. Returns zero on success, negative values on + * failure. + * + */ +static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) +{ + int ret = -ENOMEM; + struct sel_netnode *node; + struct sel_netnode *new = NULL; + + spin_lock_bh(&sel_netnode_lock); + node = sel_netnode_find(addr, family); + if (node != NULL) { + *sid = node->nsec.sid; + spin_unlock_bh(&sel_netnode_lock); + return 0; + } + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (new == NULL) + goto out; + switch (family) { + case PF_INET: + ret = security_node_sid(PF_INET, + addr, sizeof(struct in_addr), sid); + new->nsec.addr.ipv4 = *(__be32 *)addr; + break; + case PF_INET6: + ret = security_node_sid(PF_INET6, + addr, sizeof(struct in6_addr), sid); + new->nsec.addr.ipv6 = *(struct in6_addr *)addr; + break; + default: + BUG(); + ret = -EINVAL; + } + if (ret != 0) + goto out; + + new->nsec.family = family; + new->nsec.sid = *sid; + sel_netnode_insert(new); + +out: + spin_unlock_bh(&sel_netnode_lock); + if (unlikely(ret)) { + printk(KERN_WARNING + "SELinux: failure in sel_netnode_sid_slow()," + " unable to determine network node label\n"); + kfree(new); + } + return ret; +} + +/** + * sel_netnode_sid - Lookup the SID of a network address + * @addr: the IP address + * @family: the address family + * @sid: node SID + * + * Description: + * This function determines the SID of a network address using the fastest + * method possible. First the address table is queried, but if an entry + * can't be found then the policy is queried and the result is added to the + * table to speedup future queries. Returns zero on success, negative values + * on failure. + * + */ +int sel_netnode_sid(void *addr, u16 family, u32 *sid) +{ + struct sel_netnode *node; + + rcu_read_lock(); + node = sel_netnode_find(addr, family); + if (node != NULL) { + *sid = node->nsec.sid; + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + + return sel_netnode_sid_slow(addr, family, sid); +} + +/** + * sel_netnode_flush - Flush the entire network address table + * + * Description: + * Remove all entries from the network address table. + * + */ +static void sel_netnode_flush(void) +{ + unsigned int idx; + struct sel_netnode *node, *node_tmp; + + spin_lock_bh(&sel_netnode_lock); + for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) { + list_for_each_entry_safe(node, node_tmp, + &sel_netnode_hash[idx].list, list) { + list_del_rcu(&node->list); + kfree_rcu(node, rcu); + } + sel_netnode_hash[idx].size = 0; + } + spin_unlock_bh(&sel_netnode_lock); +} + +static int sel_netnode_avc_callback(u32 event) +{ + if (event == AVC_CALLBACK_RESET) { + sel_netnode_flush(); + synchronize_net(); + } + return 0; +} + +static __init int sel_netnode_init(void) +{ + int iter; + int ret; + + if (!selinux_enabled) + return 0; + + for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) { + INIT_LIST_HEAD(&sel_netnode_hash[iter].list); + sel_netnode_hash[iter].size = 0; + } + + ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET); + if (ret != 0) + panic("avc_add_callback() failed, error %d\n", ret); + + return ret; +} + +__initcall(sel_netnode_init); diff --git a/security/selinux/netport.c b/security/selinux/netport.c new file mode 100644 index 00000000000..d35379781c2 --- /dev/null +++ b/security/selinux/netport.c @@ -0,0 +1,266 @@ +/* + * Network port table + * + * SELinux must keep a mapping of network ports to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead. + * + * Author: Paul Moore <paul@paul-moore.com> + * + * This code is heavily based on the "netif" concept originally developed by + * James Morris <jmorris@redhat.com> + * (see security/selinux/netif.c for more information) + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/types.h> +#include <linux/rcupdate.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ip.h> +#include <net/ipv6.h> + +#include "netport.h" +#include "objsec.h" + +#define SEL_NETPORT_HASH_SIZE 256 +#define SEL_NETPORT_HASH_BKT_LIMIT 16 + +struct sel_netport_bkt { + int size; + struct list_head list; +}; + +struct sel_netport { + struct netport_security_struct psec; + + struct list_head list; + struct rcu_head rcu; +}; + +/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason + * for this is that I suspect most users will not make heavy use of both + * address families at the same time so one table will usually end up wasted, + * if this becomes a problem we can always add a hash table for each address + * family later */ + +static LIST_HEAD(sel_netport_list); +static DEFINE_SPINLOCK(sel_netport_lock); +static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE]; + +/** + * sel_netport_hashfn - Hashing function for the port table + * @pnum: port number + * + * Description: + * This is the hashing function for the port table, it returns the bucket + * number for the given port. + * + */ +static unsigned int sel_netport_hashfn(u16 pnum) +{ + return (pnum & (SEL_NETPORT_HASH_SIZE - 1)); +} + +/** + * sel_netport_find - Search for a port record + * @protocol: protocol + * @port: pnum + * + * Description: + * Search the network port table and return the matching record. If an entry + * can not be found in the table return NULL. + * + */ +static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) +{ + unsigned int idx; + struct sel_netport *port; + + idx = sel_netport_hashfn(pnum); + list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) + if (port->psec.port == pnum && port->psec.protocol == protocol) + return port; + + return NULL; +} + +/** + * sel_netport_insert - Insert a new port into the table + * @port: the new port record + * + * Description: + * Add a new port record to the network address hash table. + * + */ +static void sel_netport_insert(struct sel_netport *port) +{ + unsigned int idx; + + /* we need to impose a limit on the growth of the hash table so check + * this bucket to make sure it is within the specified bounds */ + idx = sel_netport_hashfn(port->psec.port); + list_add_rcu(&port->list, &sel_netport_hash[idx].list); + if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { + struct sel_netport *tail; + tail = list_entry( + rcu_dereference_protected( + sel_netport_hash[idx].list.prev, + lockdep_is_held(&sel_netport_lock)), + struct sel_netport, list); + list_del_rcu(&tail->list); + kfree_rcu(tail, rcu); + } else + sel_netport_hash[idx].size++; +} + +/** + * sel_netport_sid_slow - Lookup the SID of a network address using the policy + * @protocol: protocol + * @pnum: port + * @sid: port SID + * + * Description: + * This function determines the SID of a network port by quering the security + * policy. The result is added to the network port table to speedup future + * queries. Returns zero on success, negative values on failure. + * + */ +static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) +{ + int ret = -ENOMEM; + struct sel_netport *port; + struct sel_netport *new = NULL; + + spin_lock_bh(&sel_netport_lock); + port = sel_netport_find(protocol, pnum); + if (port != NULL) { + *sid = port->psec.sid; + spin_unlock_bh(&sel_netport_lock); + return 0; + } + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (new == NULL) + goto out; + ret = security_port_sid(protocol, pnum, sid); + if (ret != 0) + goto out; + + new->psec.port = pnum; + new->psec.protocol = protocol; + new->psec.sid = *sid; + sel_netport_insert(new); + +out: + spin_unlock_bh(&sel_netport_lock); + if (unlikely(ret)) { + printk(KERN_WARNING + "SELinux: failure in sel_netport_sid_slow()," + " unable to determine network port label\n"); + kfree(new); + } + return ret; +} + +/** + * sel_netport_sid - Lookup the SID of a network port + * @protocol: protocol + * @pnum: port + * @sid: port SID + * + * Description: + * This function determines the SID of a network port using the fastest method + * possible. First the port table is queried, but if an entry can't be found + * then the policy is queried and the result is added to the table to speedup + * future queries. Returns zero on success, negative values on failure. + * + */ +int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) +{ + struct sel_netport *port; + + rcu_read_lock(); + port = sel_netport_find(protocol, pnum); + if (port != NULL) { + *sid = port->psec.sid; + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + + return sel_netport_sid_slow(protocol, pnum, sid); +} + +/** + * sel_netport_flush - Flush the entire network port table + * + * Description: + * Remove all entries from the network address table. + * + */ +static void sel_netport_flush(void) +{ + unsigned int idx; + struct sel_netport *port, *port_tmp; + + spin_lock_bh(&sel_netport_lock); + for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { + list_for_each_entry_safe(port, port_tmp, + &sel_netport_hash[idx].list, list) { + list_del_rcu(&port->list); + kfree_rcu(port, rcu); + } + sel_netport_hash[idx].size = 0; + } + spin_unlock_bh(&sel_netport_lock); +} + +static int sel_netport_avc_callback(u32 event) +{ + if (event == AVC_CALLBACK_RESET) { + sel_netport_flush(); + synchronize_net(); + } + return 0; +} + +static __init int sel_netport_init(void) +{ + int iter; + int ret; + + if (!selinux_enabled) + return 0; + + for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) { + INIT_LIST_HEAD(&sel_netport_hash[iter].list); + sel_netport_hash[iter].size = 0; + } + + ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET); + if (ret != 0) + panic("avc_add_callback() failed, error %d\n", ret); + + return ret; +} + +__initcall(sel_netport_init); diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 69b9329b205..2df7b900e25 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -11,20 +11,19 @@ */ #include <linux/types.h> #include <linux/kernel.h> -#include <linux/skbuff.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <linux/if.h> -#include <linux/netfilter_ipv4/ip_queue.h> #include <linux/inet_diag.h> #include <linux/xfrm.h> #include <linux/audit.h> +#include <linux/sock_diag.h> #include "flask.h" #include "av_permissions.h" +#include "security.h" -struct nlmsg_perm -{ +struct nlmsg_perm { u16 nlmsg_type; u32 perm; }; @@ -60,23 +59,27 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, -}; - -static struct nlmsg_perm nlmsg_firewall_perms[] = -{ - { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, - { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, + { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_xfrm_perms[] = @@ -88,8 +91,15 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] = { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ }, { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_audit_perms[] = @@ -99,8 +109,17 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, + { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT }, + { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, }; @@ -128,12 +147,6 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) sizeof(nlmsg_route_perms)); break; - case SECCLASS_NETLINK_FIREWALL_SOCKET: - case SECCLASS_NETLINK_IP6FW_SOCKET: - err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms, - sizeof(nlmsg_firewall_perms)); - break; - case SECCLASS_NETLINK_TCPDIAG_SOCKET: err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms, sizeof(nlmsg_tcpdiag_perms)); @@ -145,8 +158,10 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) break; case SECCLASS_NETLINK_AUDIT_SOCKET: - if (nlmsg_type >= AUDIT_FIRST_USER_MSG && - nlmsg_type <= AUDIT_LAST_USER_MSG) { + if ((nlmsg_type >= AUDIT_FIRST_USER_MSG && + nlmsg_type <= AUDIT_LAST_USER_MSG) || + (nlmsg_type >= AUDIT_FIRST_USER_MSG2 && + nlmsg_type <= AUDIT_LAST_USER_MSG2)) { *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY; } else { err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index b5fa02d17b1..c71737f6d1c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1,28 +1,35 @@ /* Updated: Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/pagemap.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/fs.h> +#include <linux/mutex.h> #include <linux/init.h> #include <linux/string.h> #include <linux/security.h> #include <linux/major.h> #include <linux/seq_file.h> #include <linux/percpu.h> -#include <asm/uaccess.h> -#include <asm/semaphore.h> +#include <linux/audit.h> +#include <linux/uaccess.h> +#include <linux/kobject.h> +#include <linux/ctype.h> /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ @@ -34,36 +41,58 @@ #include "objsec.h" #include "conditional.h" +/* Policy capability filenames */ +static char *policycap_names[] = { + "network_peer_controls", + "open_perms", + "redhat1", + "always_check_network" +}; + unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; static int __init checkreqprot_setup(char *str) { - selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; + unsigned long checkreqprot; + if (!kstrtoul(str, 0, &checkreqprot)) + selinux_checkreqprot = checkreqprot ? 1 : 0; return 1; } __setup("checkreqprot=", checkreqprot_setup); - -static DECLARE_MUTEX(sel_sem); +static DEFINE_MUTEX(sel_mutex); /* global data for booleans */ -static struct dentry *bool_dir = NULL; -static int bool_num = 0; -static int *bool_pending_values = NULL; +static struct dentry *bool_dir; +static int bool_num; +static char **bool_pending_names; +static int *bool_pending_values; -extern void selnl_notify_setenforce(int val); +/* global data for classes */ +static struct dentry *class_dir; +static unsigned long last_class_ino; + +static char policy_opened; + +/* global data for policy capabilities */ +static struct dentry *policycap_dir; /* Check whether a task is allowed to use a security operation. */ static int task_has_security(struct task_struct *tsk, u32 perms) { - struct task_security_struct *tsec; - - tsec = tsk->security; + const struct task_security_struct *tsec; + u32 sid = 0; + + rcu_read_lock(); + tsec = __task_cred(tsk)->security; + if (tsec) + sid = tsec->sid; + rcu_read_unlock(); if (!tsec) return -EACCES; - return avc_has_perm(tsec->sid, SECINITSID_SECURITY, + return avc_has_perm(sid, SECINITSID_SECURITY, SECCLASS_SECURITY, perms, NULL); } @@ -80,11 +109,24 @@ enum sel_inos { SEL_COMMIT_BOOLS, /* commit new boolean values */ SEL_MLS, /* return if MLS policy is enabled */ SEL_DISABLE, /* disable SELinux until next reboot */ - SEL_AVC, /* AVC management directory */ SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ + SEL_COMPAT_NET, /* whether to use old compat network packet controls */ + SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ + SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ + SEL_STATUS, /* export current status using mmap() */ + SEL_POLICY, /* allow userspace to read the in kernel policy */ + SEL_INO_NEXT, /* The next inode number to use */ }; +static unsigned long sel_last_ino = SEL_INO_NEXT - 1; + +#define SEL_INITCON_INO_OFFSET 0x01000000 +#define SEL_BOOL_INO_OFFSET 0x02000000 +#define SEL_CLASS_INO_OFFSET 0x04000000 +#define SEL_POLICYCAP_INO_OFFSET 0x08000000 +#define SEL_INO_MASK 0x00ffffff + #define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -97,23 +139,28 @@ static ssize_t sel_read_enforce(struct file *filp, char __user *buf, } #ifdef CONFIG_SECURITY_SELINUX_DEVELOP -static ssize_t sel_write_enforce(struct file * file, const char __user * buf, +static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page; + char *page = NULL; ssize_t length; int new_value; + length = -ENOMEM; if (count >= PAGE_SIZE) - return -ENOMEM; - if (*ppos != 0) { - /* No partial writes. */ - return -EINVAL; - } - page = (char*)get_zeroed_page(GFP_KERNEL); + goto out; + + /* No partial writes. */ + length = EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) - return -ENOMEM; + goto out; + length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -126,10 +173,16 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, length = task_has_security(current, SECURITY__SETENFORCE); if (length) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "enforcing=%d old_enforcing=%d auid=%u ses=%u", + new_value, selinux_enforcing, + from_kuid(&init_user_ns, audit_get_loginuid(current)), + audit_get_sessionid(current)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); selnl_notify_setenforce(selinux_enforcing); + selinux_status_update_setenforce(selinux_enforcing); } length = count; out: @@ -140,30 +193,106 @@ out: #define sel_write_enforce NULL #endif -static struct file_operations sel_enforce_ops = { +static const struct file_operations sel_enforce_ops = { .read = sel_read_enforce, .write = sel_write_enforce, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[TMPBUFLEN]; + ssize_t length; + ino_t ino = file_inode(filp)->i_ino; + int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? + security_get_reject_unknown() : !security_get_allow_unknown(); + + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static const struct file_operations sel_handle_unknown_ops = { + .read = sel_read_handle_unknown, + .llseek = generic_file_llseek, +}; + +static int sel_open_handle_status(struct inode *inode, struct file *filp) +{ + struct page *status = selinux_kernel_status_page(); + + if (!status) + return -ENOMEM; + + filp->private_data = status; + + return 0; +} + +static ssize_t sel_read_handle_status(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct page *status = filp->private_data; + + BUG_ON(!status); + + return simple_read_from_buffer(buf, count, ppos, + page_address(status), + sizeof(struct selinux_kernel_status)); +} + +static int sel_mmap_handle_status(struct file *filp, + struct vm_area_struct *vma) +{ + struct page *status = filp->private_data; + unsigned long size = vma->vm_end - vma->vm_start; + + BUG_ON(!status); + + /* only allows one page from the head */ + if (vma->vm_pgoff > 0 || size != PAGE_SIZE) + return -EIO; + /* disallow writable mapping */ + if (vma->vm_flags & VM_WRITE) + return -EPERM; + /* disallow mprotect() turns it into writable */ + vma->vm_flags &= ~VM_MAYWRITE; + + return remap_pfn_range(vma, vma->vm_start, + page_to_pfn(status), + size, vma->vm_page_prot); +} + +static const struct file_operations sel_handle_status_ops = { + .open = sel_open_handle_status, + .read = sel_read_handle_status, + .mmap = sel_mmap_handle_status, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_DISABLE -static ssize_t sel_write_disable(struct file * file, const char __user * buf, +static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page; + char *page = NULL; ssize_t length; int new_value; - extern int selinux_disable(void); + length = -ENOMEM; if (count >= PAGE_SIZE) - return -ENOMEM; - if (*ppos != 0) { - /* No partial writes. */ - return -EINVAL; - } - page = (char*)get_zeroed_page(GFP_KERNEL); + goto out; + + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) - return -ENOMEM; + goto out; + length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -174,8 +303,12 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, if (new_value) { length = selinux_disable(); - if (length < 0) + if (length) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "selinux=0 auid=%u ses=%u", + from_kuid(&init_user_ns, audit_get_loginuid(current)), + audit_get_sessionid(current)); } length = count; @@ -187,12 +320,13 @@ out: #define sel_write_disable NULL #endif -static struct file_operations sel_disable_ops = { +static const struct file_operations sel_disable_ops = { .write = sel_write_disable, + .llseek = generic_file_llseek, }; static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { char tmpbuf[TMPBUFLEN]; ssize_t length; @@ -201,12 +335,19 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static struct file_operations sel_policyvers_ops = { +static const struct file_operations sel_policyvers_ops = { .read = sel_read_policyvers, + .llseek = generic_file_llseek, }; /* declaration for sel_write_load */ static int sel_make_bools(void); +static int sel_make_classes(void); +static int sel_make_policycap(void); + +/* declaration for sel_make_class_dirs */ +static struct dentry *sel_make_dir(struct dentry *dir, const char *name, + unsigned long *ino); static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -214,39 +355,178 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, char tmpbuf[TMPBUFLEN]; ssize_t length; - length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", + security_mls_enabled()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static struct file_operations sel_mls_ops = { +static const struct file_operations sel_mls_ops = { .read = sel_read_mls, + .llseek = generic_file_llseek, }; -static ssize_t sel_write_load(struct file * file, const char __user * buf, - size_t count, loff_t *ppos) +struct policy_load_memory { + size_t len; + void *data; +}; +static int sel_open_policy(struct inode *inode, struct file *filp) { + struct policy_load_memory *plm = NULL; + int rc; + + BUG_ON(filp->private_data); + + mutex_lock(&sel_mutex); + + rc = task_has_security(current, SECURITY__READ_POLICY); + if (rc) + goto err; + + rc = -EBUSY; + if (policy_opened) + goto err; + + rc = -ENOMEM; + plm = kzalloc(sizeof(*plm), GFP_KERNEL); + if (!plm) + goto err; + + if (i_size_read(inode) != security_policydb_len()) { + mutex_lock(&inode->i_mutex); + i_size_write(inode, security_policydb_len()); + mutex_unlock(&inode->i_mutex); + } + + rc = security_read_policy(&plm->data, &plm->len); + if (rc) + goto err; + + policy_opened = 1; + + filp->private_data = plm; + + mutex_unlock(&sel_mutex); + + return 0; +err: + mutex_unlock(&sel_mutex); + + if (plm) + vfree(plm->data); + kfree(plm); + return rc; +} + +static int sel_release_policy(struct inode *inode, struct file *filp) +{ + struct policy_load_memory *plm = filp->private_data; + + BUG_ON(!plm); + + policy_opened = 0; + + vfree(plm->data); + kfree(plm); + + return 0; +} + +static ssize_t sel_read_policy(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct policy_load_memory *plm = filp->private_data; int ret; + + mutex_lock(&sel_mutex); + + ret = task_has_security(current, SECURITY__READ_POLICY); + if (ret) + goto out; + + ret = simple_read_from_buffer(buf, count, ppos, plm->data, plm->len); +out: + mutex_unlock(&sel_mutex); + return ret; +} + +static int sel_mmap_policy_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct policy_load_memory *plm = vma->vm_file->private_data; + unsigned long offset; + struct page *page; + + if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) + return VM_FAULT_SIGBUS; + + offset = vmf->pgoff << PAGE_SHIFT; + if (offset >= roundup(plm->len, PAGE_SIZE)) + return VM_FAULT_SIGBUS; + + page = vmalloc_to_page(plm->data + offset); + get_page(page); + + vmf->page = page; + + return 0; +} + +static struct vm_operations_struct sel_mmap_policy_ops = { + .fault = sel_mmap_policy_fault, + .page_mkwrite = sel_mmap_policy_fault, +}; + +static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) { + /* do not allow mprotect to make mapping writable */ + vma->vm_flags &= ~VM_MAYWRITE; + + if (vma->vm_flags & VM_WRITE) + return -EACCES; + } + + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = &sel_mmap_policy_ops; + + return 0; +} + +static const struct file_operations sel_policy_ops = { + .open = sel_open_policy, + .read = sel_read_policy, + .mmap = sel_mmap_policy, + .release = sel_release_policy, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_write_load(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) + +{ ssize_t length; void *data = NULL; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__LOAD_POLICY); if (length) goto out; - if (*ppos != 0) { - /* No partial writes. */ - length = -EINVAL; + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) goto out; - } - if ((count > 64 * 1024 * 1024) - || (data = vmalloc(count)) == NULL) { - length = -ENOMEM; + length = -EFBIG; + if (count > 64 * 1024 * 1024) + goto out; + + length = -ENOMEM; + data = vmalloc(count); + if (!data) goto out; - } length = -EFAULT; if (copy_from_user(data, buf, count) != 0) @@ -256,43 +536,58 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, if (length) goto out; - ret = sel_make_bools(); - if (ret) - length = ret; - else - length = count; + length = sel_make_bools(); + if (length) + goto out1; + + length = sel_make_classes(); + if (length) + goto out1; + + length = sel_make_policycap(); + if (length) + goto out1; + + length = count; + +out1: + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, + "policy loaded auid=%u ses=%u", + from_kuid(&init_user_ns, audit_get_loginuid(current)), + audit_get_sessionid(current)); out: - up(&sel_sem); + mutex_unlock(&sel_mutex); vfree(data); return length; } -static struct file_operations sel_load_ops = { +static const struct file_operations sel_load_ops = { .write = sel_write_load, + .llseek = generic_file_llseek, }; -static ssize_t sel_write_context(struct file * file, char *buf, size_t size) +static ssize_t sel_write_context(struct file *file, char *buf, size_t size) { - char *canon; + char *canon = NULL; u32 sid, len; ssize_t length; length = task_has_security(current, SECURITY__CHECK_CONTEXT); if (length) - return length; + goto out; - length = security_context_to_sid(buf, size, &sid); - if (length < 0) - return length; + length = security_context_to_sid(buf, size, &sid, GFP_KERNEL); + if (length) + goto out; length = security_sid_to_context(sid, &canon, &len); - if (length < 0) - return length; + if (length) + goto out; + length = -ERANGE; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); - length = -ERANGE; + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); goto out; } @@ -313,26 +608,31 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf, +static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page; + char *page = NULL; ssize_t length; unsigned int new_value; length = task_has_security(current, SECURITY__SETCHECKREQPROT); if (length) - return length; + goto out; + length = -ENOMEM; if (count >= PAGE_SIZE) - return -ENOMEM; - if (*ppos != 0) { - /* No partial writes. */ - return -EINVAL; - } - page = (char*)get_zeroed_page(GFP_KERNEL); + goto out; + + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) - return -ENOMEM; + goto out; + length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -347,19 +647,20 @@ out: free_page((unsigned long) page); return length; } -static struct file_operations sel_checkreqprot_ops = { +static const struct file_operations sel_checkreqprot_ops = { .read = sel_read_checkreqprot, .write = sel_write_checkreqprot, + .llseek = generic_file_llseek, }; /* * 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 sel_write_member(struct file * file, char *buf, size_t size); +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 sel_write_member(struct file *file, char *buf, size_t size); static ssize_t (*write_op[])(struct file *, char *, size_t) = { [SEL_ACCESS] = sel_write_access, @@ -372,7 +673,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { - ino_t ino = file->f_dentry->d_inode->i_ino; + ino_t ino = file_inode(file)->i_ino; char *data; ssize_t rv; @@ -383,18 +684,19 @@ static ssize_t selinux_transaction_write(struct file *file, const char __user *b if (IS_ERR(data)) return PTR_ERR(data); - rv = write_op[ino](file, data, size); - if (rv>0) { + rv = write_op[ino](file, data, size); + if (rv > 0) { simple_transaction_set(file, rv); rv = size; } return rv; } -static struct file_operations transaction_ops = { +static const struct file_operations transaction_ops = { .write = selinux_transaction_write, .read = simple_transaction_read, .release = simple_transaction_release, + .llseek = generic_file_llseek, }; /* @@ -403,175 +705,218 @@ static struct file_operations transaction_ops = { * and the length returned. Otherwise return 0 or and -error. */ -static ssize_t sel_write_access(struct file * file, char *buf, size_t size) +static ssize_t sel_write_access(struct file *file, char *buf, size_t size) { - char *scon, *tcon; + char *scon = NULL, *tcon = NULL; 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; + goto out; length = -ENOMEM; - scon = kzalloc(size+1, GFP_KERNEL); + scon = kzalloc(size + 1, GFP_KERNEL); if (!scon) - return length; + goto out; - tcon = kzalloc(size+1, GFP_KERNEL); + length = -ENOMEM; + tcon = kzalloc(size + 1, GFP_KERNEL); if (!tcon) goto out; length = -EINVAL; - if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) - goto out2; + if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) + goto out; - 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_context_to_sid(scon, strlen(scon) + 1, &ssid, + GFP_KERNEL); + if (length) + goto out; - length = security_compute_av(ssid, tsid, tclass, req, &avd); - if (length < 0) - goto out2; + length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, + GFP_KERNEL); + if (length) + goto out; + + security_compute_av_user(ssid, tsid, tclass, &avd); length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, - "%x %x %x %x %u", - avd.allowed, avd.decided, + "%x %x %x %x %u %x", + avd.allowed, 0xffffffff, avd.auditallow, avd.auditdeny, - avd.seqno); -out2: - kfree(tcon); + avd.seqno, avd.flags); out: + kfree(tcon); kfree(scon); return length; } -static ssize_t sel_write_create(struct file * file, char *buf, size_t size) +static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { - char *scon, *tcon; + char *scon = NULL, *tcon = NULL; + char *namebuf = NULL, *objname = NULL; u32 ssid, tsid, newsid; u16 tclass; ssize_t length; - char *newcon; + char *newcon = NULL; u32 len; + int nargs; length = task_has_security(current, SECURITY__COMPUTE_CREATE); if (length) - return length; + goto out; length = -ENOMEM; - scon = kzalloc(size+1, GFP_KERNEL); + scon = kzalloc(size + 1, GFP_KERNEL); if (!scon) - return length; + goto out; - tcon = kzalloc(size+1, GFP_KERNEL); + length = -ENOMEM; + tcon = kzalloc(size + 1, GFP_KERNEL); if (!tcon) goto out; + length = -ENOMEM; + namebuf = kzalloc(size + 1, GFP_KERNEL); + if (!namebuf) + goto out; + length = -EINVAL; - if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) - goto out2; + nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf); + if (nargs < 3 || nargs > 4) + goto out; + if (nargs == 4) { + /* + * If and when the name of new object to be queried contains + * either whitespace or multibyte characters, they shall be + * encoded based on the percentage-encoding rule. + * If not encoded, the sscanf logic picks up only left-half + * of the supplied name; splitted by a whitespace unexpectedly. + */ + char *r, *w; + int c1, c2; + + r = w = namebuf; + do { + c1 = *r++; + if (c1 == '+') + c1 = ' '; + else if (c1 == '%') { + c1 = hex_to_bin(*r++); + if (c1 < 0) + goto out; + c2 = hex_to_bin(*r++); + if (c2 < 0) + goto out; + c1 = (c1 << 4) | c2; + } + *w++ = c1; + } while (c1 != '\0'); + + objname = namebuf; + } + + length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, + GFP_KERNEL); + if (length) + goto out; - 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_context_to_sid(tcon, strlen(tcon) + 1, &tsid, + GFP_KERNEL); + if (length) + goto out; - length = security_transition_sid(ssid, tsid, tclass, &newsid); - if (length < 0) - goto out2; + length = security_transition_sid_user(ssid, tsid, tclass, + objname, &newsid); + if (length) + goto out; length = security_sid_to_context(newsid, &newcon, &len); - if (length < 0) - goto out2; + if (length) + goto out; + length = -ERANGE; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); - length = -ERANGE; - goto out3; + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); + goto out; } memcpy(buf, newcon, len); length = len; -out3: +out: kfree(newcon); -out2: + kfree(namebuf); kfree(tcon); -out: kfree(scon); return length; } -static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size) +static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) { - char *scon, *tcon; + char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; ssize_t length; - char *newcon; + char *newcon = NULL; u32 len; length = task_has_security(current, SECURITY__COMPUTE_RELABEL); if (length) - return length; + goto out; length = -ENOMEM; - scon = kzalloc(size+1, GFP_KERNEL); + scon = kzalloc(size + 1, GFP_KERNEL); if (!scon) - return length; + goto out; - tcon = kzalloc(size+1, GFP_KERNEL); + length = -ENOMEM; + tcon = kzalloc(size + 1, GFP_KERNEL); if (!tcon) goto out; length = -EINVAL; if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) - goto out2; + goto out; + + length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, + GFP_KERNEL); + if (length) + goto out; - 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_context_to_sid(tcon, strlen(tcon) + 1, &tsid, + GFP_KERNEL); + if (length) + goto out; length = security_change_sid(ssid, tsid, tclass, &newsid); - if (length < 0) - goto out2; + if (length) + goto out; length = security_sid_to_context(newsid, &newcon, &len); - if (length < 0) - goto out2; + if (length) + goto out; - if (len > SIMPLE_TRANSACTION_LIMIT) { - length = -ERANGE; - goto out3; - } + length = -ERANGE; + if (len > SIMPLE_TRANSACTION_LIMIT) + goto out; memcpy(buf, newcon, len); length = len; -out3: +out: kfree(newcon); -out2: kfree(tcon); -out: kfree(scon); return length; } -static ssize_t sel_write_user(struct file * file, char *buf, size_t size) +static ssize_t sel_write_user(struct file *file, char *buf, size_t size) { - char *con, *user, *ptr; - u32 sid, *sids; + char *con = NULL, *user = NULL, *ptr; + u32 sid, *sids = NULL; ssize_t length; char *newcon; int i, rc; @@ -579,28 +924,29 @@ static ssize_t sel_write_user(struct file * file, char *buf, size_t size) length = task_has_security(current, SECURITY__COMPUTE_USER); if (length) - return length; + goto out; length = -ENOMEM; - con = kzalloc(size+1, GFP_KERNEL); + con = kzalloc(size + 1, GFP_KERNEL); if (!con) - return length; + goto out; - user = kzalloc(size+1, GFP_KERNEL); + length = -ENOMEM; + user = kzalloc(size + 1, GFP_KERNEL); if (!user) goto out; length = -EINVAL; if (sscanf(buf, "%s %s", con, user) != 2) - goto out2; + goto out; - length = security_context_to_sid(con, strlen(con)+1, &sid); - if (length < 0) - goto out2; + length = security_context_to_sid(con, strlen(con) + 1, &sid, GFP_KERNEL); + if (length) + goto out; length = security_get_user_sids(sid, user, &sids, &nsids); - if (length < 0) - goto out2; + if (length) + goto out; length = sprintf(buf, "%u", nsids) + 1; ptr = buf + length; @@ -608,82 +954,82 @@ static ssize_t sel_write_user(struct file * file, char *buf, size_t size) rc = security_sid_to_context(sids[i], &newcon, &len); if (rc) { length = rc; - goto out3; + goto out; } if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { kfree(newcon); length = -ERANGE; - goto out3; + goto out; } memcpy(ptr, newcon, len); kfree(newcon); ptr += len; length += len; } -out3: +out: kfree(sids); -out2: kfree(user); -out: kfree(con); return length; } -static ssize_t sel_write_member(struct file * file, char *buf, size_t size) +static ssize_t sel_write_member(struct file *file, char *buf, size_t size) { - char *scon, *tcon; + char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; ssize_t length; - char *newcon; + char *newcon = NULL; u32 len; length = task_has_security(current, SECURITY__COMPUTE_MEMBER); if (length) - return length; + goto out; length = -ENOMEM; - scon = kzalloc(size+1, GFP_KERNEL); + scon = kzalloc(size + 1, GFP_KERNEL); if (!scon) - return length; + goto out; - tcon = kzalloc(size+1, GFP_KERNEL); + length = -ENOMEM; + tcon = kzalloc(size + 1, GFP_KERNEL); if (!tcon) goto out; length = -EINVAL; if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) - goto out2; + goto out; + + length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, + GFP_KERNEL); + if (length) + goto out; - 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_context_to_sid(tcon, strlen(tcon) + 1, &tsid, + GFP_KERNEL); + if (length) + goto out; length = security_member_sid(ssid, tsid, tclass, &newsid); - if (length < 0) - goto out2; + if (length) + goto out; length = security_sid_to_context(newsid, &newcon, &len); - if (length < 0) - goto out2; + if (length) + goto out; + length = -ERANGE; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); - length = -ERANGE; - goto out3; + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); + goto out; } memcpy(buf, newcon, len); length = len; -out3: +out: kfree(newcon); -out2: kfree(tcon); -out: kfree(scon); return length; } @@ -694,74 +1040,43 @@ static struct inode *sel_make_inode(struct super_block *sb, int mode) if (ret) { ret->i_mode = mode; - ret->i_uid = ret->i_gid = 0; - ret->i_blksize = PAGE_CACHE_SIZE; - ret->i_blocks = 0; ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; } return ret; } -#define BOOL_INO_OFFSET 30 - static ssize_t sel_read_bool(struct file *filep, char __user *buf, size_t count, loff_t *ppos) { char *page = NULL; ssize_t length; - ssize_t end; ssize_t ret; int cur_enforcing; - struct inode *inode; + unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; + const char *name = filep->f_path.dentry->d_name.name; - down(&sel_sem); + mutex_lock(&sel_mutex); - ret = -EFAULT; - - /* check to see if this file has been deleted */ - if (!filep->f_op) + ret = -EINVAL; + if (index >= bool_num || strcmp(name, bool_pending_names[index])) goto out; - if (count > PAGE_SIZE) { - ret = -EINVAL; - goto out; - } - if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) { - ret = -ENOMEM; + ret = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) goto out; - } - inode = filep->f_dentry->d_inode; - cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET); + cur_enforcing = security_get_bool_value(index); if (cur_enforcing < 0) { ret = cur_enforcing; goto out; } - length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, - bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]); - if (length < 0) { - ret = length; - goto out; - } - - if (*ppos >= length) { - ret = 0; - goto out; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) { - ret = -EFAULT; - goto out; - } - *ppos = end; - ret = count; + bool_pending_values[index]); + ret = simple_read_from_buffer(buf, count, ppos, page, length); out: - up(&sel_sem); - if (page) - free_page((unsigned long)page); + mutex_unlock(&sel_mutex); + free_page((unsigned long)page); return ret; } @@ -769,34 +1084,36 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, size_t count, loff_t *ppos) { char *page = NULL; - ssize_t length = -EFAULT; + ssize_t length; int new_value; - struct inode *inode; + unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; + const char *name = filep->f_path.dentry->d_name.name; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__SETBOOL); if (length) goto out; - /* check to see if this file has been deleted */ - if (!filep->f_op) + length = -EINVAL; + if (index >= bool_num || strcmp(name, bool_pending_names[index])) goto out; - if (count >= PAGE_SIZE) { - length = -ENOMEM; + length = -ENOMEM; + if (count >= PAGE_SIZE) goto out; - } - if (*ppos != 0) { - /* No partial writes. */ + + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) goto out; - } - page = (char*)get_zeroed_page(GFP_KERNEL); - if (!page) { - length = -ENOMEM; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) goto out; - } + length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -807,20 +1124,19 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, if (new_value) new_value = 1; - inode = filep->f_dentry->d_inode; - bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value; + bool_pending_values[index] = new_value; length = count; out: - up(&sel_sem); - if (page) - free_page((unsigned long) page); + mutex_unlock(&sel_mutex); + free_page((unsigned long) page); return length; } -static struct file_operations sel_bool_ops = { - .read = sel_read_bool, - .write = sel_write_bool, +static const struct file_operations sel_bool_ops = { + .read = sel_read_bool, + .write = sel_write_bool, + .llseek = generic_file_llseek, }; static ssize_t sel_commit_bools_write(struct file *filep, @@ -828,33 +1144,30 @@ static ssize_t sel_commit_bools_write(struct file *filep, size_t count, loff_t *ppos) { char *page = NULL; - ssize_t length = -EFAULT; + ssize_t length; int new_value; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__SETBOOL); if (length) goto out; - /* check to see if this file has been deleted */ - if (!filep->f_op) + length = -ENOMEM; + if (count >= PAGE_SIZE) goto out; - if (count >= PAGE_SIZE) { - length = -ENOMEM; - goto out; - } - if (*ppos != 0) { - /* No partial writes. */ + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) goto out; - } - page = (char*)get_zeroed_page(GFP_KERNEL); - if (!page) { - length = -ENOMEM; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) goto out; - } + length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -862,67 +1175,57 @@ static ssize_t sel_commit_bools_write(struct file *filep, if (sscanf(page, "%d", &new_value) != 1) goto out; - if (new_value && bool_pending_values) { - security_set_bools(bool_num, bool_pending_values); - } + length = 0; + if (new_value && bool_pending_values) + length = security_set_bools(bool_num, bool_pending_values); - length = count; + if (!length) + length = count; out: - up(&sel_sem); - if (page) - free_page((unsigned long) page); + mutex_unlock(&sel_mutex); + free_page((unsigned long) page); return length; } -static struct file_operations sel_commit_bools_ops = { - .write = sel_commit_bools_write, +static const struct file_operations sel_commit_bools_ops = { + .write = sel_commit_bools_write, + .llseek = generic_file_llseek, }; -/* delete booleans - partial revoke() from - * fs/proc/generic.c proc_kill_inodes */ -static void sel_remove_bools(struct dentry *de) +static void sel_remove_entries(struct dentry *de) { - struct list_head *p, *node; - struct super_block *sb = de->d_sb; + struct list_head *node; - spin_lock(&dcache_lock); + spin_lock(&de->d_lock); node = de->d_subdirs.next; while (node != &de->d_subdirs) { struct dentry *d = list_entry(node, struct dentry, d_u.d_child); + + spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); list_del_init(node); if (d->d_inode) { - d = dget_locked(d); - spin_unlock(&dcache_lock); + dget_dlock(d); + spin_unlock(&de->d_lock); + spin_unlock(&d->d_lock); d_delete(d); simple_unlink(de->d_inode, d); dput(d); - spin_lock(&dcache_lock); - } + spin_lock(&de->d_lock); + } else + spin_unlock(&d->d_lock); node = de->d_subdirs.next; } - spin_unlock(&dcache_lock); - - file_list_lock(); - list_for_each(p, &sb->s_files) { - struct file * filp = list_entry(p, struct file, f_u.fu_list); - struct dentry * dentry = filp->f_dentry; - - if (dentry->d_parent != de) { - continue; - } - filp->f_op = NULL; - } - file_list_unlock(); + spin_unlock(&de->d_lock); } #define BOOL_DIR_NAME "booleans" static int sel_make_bools(void) { - int i, ret = 0; + int i, ret; ssize_t len; struct dentry *dentry = NULL; struct dentry *dir = bool_dir; @@ -934,67 +1237,75 @@ static int sel_make_bools(void) u32 sid; /* remove any existing files */ + for (i = 0; i < bool_num; i++) + kfree(bool_pending_names[i]); + kfree(bool_pending_names); kfree(bool_pending_values); + bool_num = 0; + bool_pending_names = NULL; bool_pending_values = NULL; - sel_remove_bools(dir); + sel_remove_entries(dir); - if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) - return -ENOMEM; + ret = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; ret = security_get_bools(&num, &names, &values); - if (ret != 0) + if (ret) goto out; for (i = 0; i < num; i++) { + ret = -ENOMEM; dentry = d_alloc_name(dir, names[i]); - if (!dentry) { - ret = -ENOMEM; - goto err; - } + if (!dentry) + goto out; + + ret = -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); - if (!inode) { - ret = -ENOMEM; - goto err; - } + if (!inode) + goto out; + ret = -ENAMETOOLONG; len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); - if (len < 0) { - ret = -EINVAL; - goto err; - } else if (len >= PAGE_SIZE) { - ret = -ENAMETOOLONG; - goto err; - } - isec = (struct inode_security_struct*)inode->i_security; - if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid))) - goto err; + if (len >= PAGE_SIZE) + goto out; + + isec = (struct inode_security_struct *)inode->i_security; + ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); + if (ret) + goto out; + isec->sid = sid; isec->initialized = 1; inode->i_fop = &sel_bool_ops; - inode->i_ino = i + BOOL_INO_OFFSET; + inode->i_ino = i|SEL_BOOL_INO_OFFSET; d_add(dentry, inode); } bool_num = num; + bool_pending_names = names; bool_pending_values = values; + + free_page((unsigned long)page); + return 0; out: free_page((unsigned long)page); + if (names) { for (i = 0; i < num; i++) kfree(names[i]); kfree(names); } - return ret; -err: kfree(values); - d_genocide(dir); - ret = -ENOMEM; - goto out; + sel_remove_entries(dir); + + return ret; } #define NULL_FILE_NAME "null" -struct dentry *selinux_null = NULL; +struct path selinux_null; static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -1006,52 +1317,46 @@ static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static ssize_t sel_write_avc_cache_threshold(struct file * file, - const char __user * buf, +static ssize_t sel_write_avc_cache_threshold(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) { - char *page; + char *page = NULL; ssize_t ret; int new_value; - if (count >= PAGE_SIZE) { - ret = -ENOMEM; + ret = task_has_security(current, SECURITY__SETSECPARAM); + if (ret) goto out; - } - if (*ppos != 0) { - /* No partial writes. */ - ret = -EINVAL; + ret = -ENOMEM; + if (count >= PAGE_SIZE) goto out; - } - page = (char*)get_zeroed_page(GFP_KERNEL); - if (!page) { - ret = -ENOMEM; + /* No partial writes. */ + ret = -EINVAL; + if (*ppos != 0) goto out; - } - if (copy_from_user(page, buf, count)) { - ret = -EFAULT; - goto out_free; - } + ret = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; - if (sscanf(page, "%u", &new_value) != 1) { - ret = -EINVAL; + ret = -EFAULT; + if (copy_from_user(page, buf, count)) goto out; - } - if (new_value != avc_cache_threshold) { - ret = task_has_security(current, SECURITY__SETSECPARAM); - if (ret) - goto out_free; - avc_cache_threshold = new_value; - } + ret = -EINVAL; + if (sscanf(page, "%u", &new_value) != 1) + goto out; + + avc_cache_threshold = new_value; + ret = count; -out_free: - free_page((unsigned long)page); out: + free_page((unsigned long)page); return ret; } @@ -1059,28 +1364,29 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { char *page; - ssize_t ret = 0; + ssize_t length; page = (char *)__get_free_page(GFP_KERNEL); - if (!page) { - ret = -ENOMEM; - goto out; - } - ret = avc_get_hash_stats(page); - if (ret >= 0) - ret = simple_read_from_buffer(buf, count, ppos, page, ret); + if (!page) + return -ENOMEM; + + length = avc_get_hash_stats(page); + if (length >= 0) + length = simple_read_from_buffer(buf, count, ppos, page, length); free_page((unsigned long)page); -out: - return ret; + + return length; } -static struct file_operations sel_avc_cache_threshold_ops = { +static const struct file_operations sel_avc_cache_threshold_ops = { .read = sel_read_avc_cache_threshold, .write = sel_write_avc_cache_threshold, + .llseek = generic_file_llseek, }; -static struct file_operations sel_avc_hash_stats_ops = { +static const struct file_operations sel_avc_hash_stats_ops = { .read = sel_read_avc_hash_stats, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS @@ -1088,7 +1394,7 @@ static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) { int cpu; - for (cpu = *idx; cpu < NR_CPUS; ++cpu) { + for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *idx = cpu + 1; @@ -1119,17 +1425,21 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) if (v == SEQ_START_TOKEN) seq_printf(seq, "lookups hits misses allocations reclaims " "frees\n"); - else - seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups, - st->hits, st->misses, st->allocations, + else { + unsigned int lookups = st->lookups; + unsigned int misses = st->misses; + unsigned int hits = lookups - misses; + seq_printf(seq, "%u %u %u %u %u %u\n", lookups, + hits, misses, st->allocations, st->reclaims, st->frees); + } return 0; } static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) { } -static struct seq_operations sel_avc_cache_stats_seq_ops = { +static const struct seq_operations sel_avc_cache_stats_seq_ops = { .start = sel_avc_stats_seq_start, .next = sel_avc_stats_seq_next, .show = sel_avc_stats_seq_show, @@ -1141,7 +1451,7 @@ static int sel_open_avc_cache_stats(struct inode *inode, struct file *file) return seq_open(file, &sel_avc_cache_stats_seq_ops); } -static struct file_operations sel_avc_cache_stats_ops = { +static const struct file_operations sel_avc_cache_stats_ops = { .open = sel_open_avc_cache_stats, .read = seq_read, .llseek = seq_lseek, @@ -1151,7 +1461,7 @@ static struct file_operations sel_avc_cache_stats_ops = { static int sel_make_avc_files(struct dentry *dir) { - int i, ret = 0; + int i; static struct tree_descr files[] = { { "cache_threshold", &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, @@ -1166,44 +1476,319 @@ static int sel_make_avc_files(struct dentry *dir) struct dentry *dentry; dentry = d_alloc_name(dir, files[i].name); - if (!dentry) { - ret = -ENOMEM; - goto err; - } + if (!dentry) + return -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); - if (!inode) { - ret = -ENOMEM; - goto err; - } + if (!inode) + return -ENOMEM; + inode->i_fop = files[i].ops; + inode->i_ino = ++sel_last_ino; d_add(dentry, inode); } -out: + + return 0; +} + +static ssize_t sel_read_initcon(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + char *con; + u32 sid, len; + ssize_t ret; + + sid = file_inode(file)->i_ino&SEL_INO_MASK; + ret = security_sid_to_context(sid, &con, &len); + if (ret) + return ret; + + ret = simple_read_from_buffer(buf, count, ppos, con, len); + kfree(con); return ret; -err: - d_genocide(dir); - goto out; } -static int sel_make_dir(struct super_block *sb, struct dentry *dentry) +static const struct file_operations sel_initcon_ops = { + .read = sel_read_initcon, + .llseek = generic_file_llseek, +}; + +static int sel_make_initcon_files(struct dentry *dir) +{ + int i; + + for (i = 1; i <= SECINITSID_NUM; i++) { + struct inode *inode; + struct dentry *dentry; + dentry = d_alloc_name(dir, security_get_initial_sid_context(i)); + if (!dentry) + return -ENOMEM; + + inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); + if (!inode) + return -ENOMEM; + + inode->i_fop = &sel_initcon_ops; + inode->i_ino = i|SEL_INITCON_INO_OFFSET; + d_add(dentry, inode); + } + + return 0; +} + +static inline unsigned long sel_class_to_ino(u16 class) +{ + return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; +} + +static inline u16 sel_ino_to_class(unsigned long ino) +{ + return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1); +} + +static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) +{ + return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; +} + +static inline u32 sel_ino_to_perm(unsigned long ino) +{ + return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); +} + +static ssize_t sel_read_class(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long ino = file_inode(file)->i_ino; + char res[TMPBUFLEN]; + ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_class(ino)); + return simple_read_from_buffer(buf, count, ppos, res, len); +} + +static const struct file_operations sel_class_ops = { + .read = sel_read_class, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_read_perm(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long ino = file_inode(file)->i_ino; + char res[TMPBUFLEN]; + ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino)); + return simple_read_from_buffer(buf, count, ppos, res, len); +} + +static const struct file_operations sel_perm_ops = { + .read = sel_read_perm, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_read_policycap(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int value; + char tmpbuf[TMPBUFLEN]; + ssize_t length; + unsigned long i_ino = file_inode(file)->i_ino; + + value = security_policycap_supported(i_ino & SEL_INO_MASK); + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); + + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static const struct file_operations sel_policycap_ops = { + .read = sel_read_policycap, + .llseek = generic_file_llseek, +}; + +static int sel_make_perm_files(char *objclass, int classvalue, + struct dentry *dir) +{ + int i, rc, nperms; + char **perms; + + rc = security_get_permissions(objclass, &perms, &nperms); + if (rc) + return rc; + + for (i = 0; i < nperms; i++) { + struct inode *inode; + struct dentry *dentry; + + rc = -ENOMEM; + dentry = d_alloc_name(dir, perms[i]); + if (!dentry) + goto out; + + rc = -ENOMEM; + inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); + if (!inode) + goto out; + + inode->i_fop = &sel_perm_ops; + /* i+1 since perm values are 1-indexed */ + inode->i_ino = sel_perm_to_ino(classvalue, i + 1); + d_add(dentry, inode); + } + rc = 0; +out: + for (i = 0; i < nperms; i++) + kfree(perms[i]); + kfree(perms); + return rc; +} + +static int sel_make_class_dir_entries(char *classname, int index, + struct dentry *dir) +{ + struct dentry *dentry = NULL; + struct inode *inode = NULL; + int rc; + + dentry = d_alloc_name(dir, "index"); + if (!dentry) + return -ENOMEM; + + inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); + if (!inode) + return -ENOMEM; + + inode->i_fop = &sel_class_ops; + inode->i_ino = sel_class_to_ino(index); + d_add(dentry, inode); + + dentry = sel_make_dir(dir, "perms", &last_class_ino); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + rc = sel_make_perm_files(classname, index, dentry); + + return rc; +} + +static void sel_remove_classes(void) +{ + struct list_head *class_node; + + list_for_each(class_node, &class_dir->d_subdirs) { + struct dentry *class_subdir = list_entry(class_node, + struct dentry, d_u.d_child); + struct list_head *class_subdir_node; + + list_for_each(class_subdir_node, &class_subdir->d_subdirs) { + struct dentry *d = list_entry(class_subdir_node, + struct dentry, d_u.d_child); + + if (d->d_inode) + if (d->d_inode->i_mode & S_IFDIR) + sel_remove_entries(d); + } + + sel_remove_entries(class_subdir); + } + + sel_remove_entries(class_dir); +} + +static int sel_make_classes(void) +{ + int rc, nclasses, i; + char **classes; + + /* delete any existing entries */ + sel_remove_classes(); + + rc = security_get_classes(&classes, &nclasses); + if (rc) + return rc; + + /* +2 since classes are 1-indexed */ + last_class_ino = sel_class_to_ino(nclasses + 2); + + for (i = 0; i < nclasses; i++) { + struct dentry *class_name_dir; + + class_name_dir = sel_make_dir(class_dir, classes[i], + &last_class_ino); + if (IS_ERR(class_name_dir)) { + rc = PTR_ERR(class_name_dir); + goto out; + } + + /* i+1 since class values are 1-indexed */ + rc = sel_make_class_dir_entries(classes[i], i + 1, + class_name_dir); + if (rc) + goto out; + } + rc = 0; +out: + for (i = 0; i < nclasses; i++) + kfree(classes[i]); + kfree(classes); + return rc; +} + +static int sel_make_policycap(void) { - int ret = 0; + unsigned int iter; + struct dentry *dentry = NULL; + struct inode *inode = NULL; + + sel_remove_entries(policycap_dir); + + for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { + if (iter < ARRAY_SIZE(policycap_names)) + dentry = d_alloc_name(policycap_dir, + policycap_names[iter]); + else + dentry = d_alloc_name(policycap_dir, "unknown"); + + if (dentry == NULL) + return -ENOMEM; + + inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO); + if (inode == NULL) + return -ENOMEM; + + inode->i_fop = &sel_policycap_ops; + inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; + d_add(dentry, inode); + } + + return 0; +} + +static struct dentry *sel_make_dir(struct dentry *dir, const char *name, + unsigned long *ino) +{ + struct dentry *dentry = d_alloc_name(dir, name); struct inode *inode; - inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); + if (!dentry) + return ERR_PTR(-ENOMEM); + + inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO); if (!inode) { - ret = -ENOMEM; - goto out; + dput(dentry); + return ERR_PTR(-ENOMEM); } + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; + inode->i_ino = ++(*ino); + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); d_add(dentry, inode); -out: - return ret; + /* bump link count on parent directory, too */ + inc_nlink(dir->d_inode); + + return dentry; } -static int sel_fill_super(struct super_block * sb, void * data, int silent) +static int sel_fill_super(struct super_block *sb, void *data, int silent) { int ret; struct dentry *dentry; @@ -1224,75 +1809,97 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, + [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, + [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, + [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, + [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); if (ret) - return ret; + goto err; - dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME); - if (!dentry) - return -ENOMEM; - - inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); - if (!inode) - goto out; - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - d_add(dentry, inode); - bool_dir = dentry; - ret = sel_make_bools(); - if (ret) - goto out; + bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &sel_last_ino); + if (IS_ERR(bool_dir)) { + ret = PTR_ERR(bool_dir); + bool_dir = NULL; + goto err; + } + ret = -ENOMEM; dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); if (!dentry) - return -ENOMEM; + goto err; + ret = -ENOMEM; inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); if (!inode) - goto out; - isec = (struct inode_security_struct*)inode->i_security; + goto err; + + inode->i_ino = ++sel_last_ino; + isec = (struct inode_security_struct *)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; isec->initialized = 1; init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); d_add(dentry, inode); - selinux_null = dentry; + selinux_null.dentry = dentry; - dentry = d_alloc_name(sb->s_root, "avc"); - if (!dentry) - return -ENOMEM; + dentry = sel_make_dir(sb->s_root, "avc", &sel_last_ino); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err; + } - ret = sel_make_dir(sb, dentry); + ret = sel_make_avc_files(dentry); if (ret) - goto out; + goto err; - ret = sel_make_avc_files(dentry); + dentry = sel_make_dir(sb->s_root, "initial_contexts", &sel_last_ino); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err; + } + + ret = sel_make_initcon_files(dentry); if (ret) - goto out; + goto err; + class_dir = sel_make_dir(sb->s_root, "class", &sel_last_ino); + if (IS_ERR(class_dir)) { + ret = PTR_ERR(class_dir); + class_dir = NULL; + goto err; + } + + policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities", &sel_last_ino); + if (IS_ERR(policycap_dir)) { + ret = PTR_ERR(policycap_dir); + policycap_dir = NULL; + goto err; + } return 0; -out: - dput(dentry); - printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); - return -ENOMEM; +err: + printk(KERN_ERR "SELinux: %s: failed while creating inodes\n", + __func__); + return ret; } -static struct super_block *sel_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static struct dentry *sel_mount(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); + return mount_single(fs_type, flags, data, sel_fill_super); } static struct file_system_type sel_fs_type = { .name = "selinuxfs", - .get_sb = sel_get_sb, + .mount = sel_mount, .kill_sb = kill_litter_super, }; struct vfsmount *selinuxfs_mount; +static struct kobject *selinuxfs_kobj; static int __init init_sel_fs(void) { @@ -1300,15 +1907,24 @@ static int __init init_sel_fs(void) if (!selinux_enabled) return 0; + + selinuxfs_kobj = kobject_create_and_add("selinux", fs_kobj); + if (!selinuxfs_kobj) + return -ENOMEM; + err = register_filesystem(&sel_fs_type); - if (!err) { - selinuxfs_mount = kern_mount(&sel_fs_type); - if (IS_ERR(selinuxfs_mount)) { - printk(KERN_ERR "selinuxfs: could not mount!\n"); - err = PTR_ERR(selinuxfs_mount); - selinuxfs_mount = NULL; - } + if (err) { + kobject_put(selinuxfs_kobj); + return err; + } + + selinux_null.mnt = selinuxfs_mount = kern_mount(&sel_fs_type); + if (IS_ERR(selinuxfs_mount)) { + printk(KERN_ERR "selinuxfs: could not mount!\n"); + err = PTR_ERR(selinuxfs_mount); + selinuxfs_mount = NULL; } + return err; } @@ -1317,6 +1933,8 @@ __initcall(init_sel_fs); #ifdef CONFIG_SECURITY_SELINUX_DISABLE void exit_sel_fs(void) { + kobject_put(selinuxfs_kobj); + kern_unmount(selinuxfs_mount); unregister_filesystem(&sel_fs_type); } #endif diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile deleted file mode 100644 index bad78779b9b..00000000000 --- a/security/selinux/ss/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for building the SELinux security server as part of the kernel tree. -# - -EXTRA_CFLAGS += -Isecurity/selinux/include -obj-y := ss.o - -ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o - diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index d049c7acbc8..a3dd9faa19c 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -6,40 +6,40 @@ /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * * Copyright (C) 2003 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/errno.h> - #include "avtab.h" #include "policydb.h" -#define AVTAB_HASH(keyp) \ -((keyp->target_class + \ - (keyp->target_type << 2) + \ - (keyp->source_type << 9)) & \ - AVTAB_HASH_MASK) +static struct kmem_cache *avtab_node_cachep; -static kmem_cache_t *avtab_node_cachep; +static inline int avtab_hash(struct avtab_key *keyp, u16 mask) +{ + return ((keyp->target_class + (keyp->target_type << 2) + + (keyp->source_type << 9)) & mask); +} static struct avtab_node* avtab_insert_node(struct avtab *h, int hvalue, - struct avtab_node * prev, struct avtab_node * cur, + struct avtab_node *prev, struct avtab_node *cur, struct avtab_key *key, struct avtab_datum *datum) { - struct avtab_node * newnode; - newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); + struct avtab_node *newnode; + newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); if (newnode == NULL) return NULL; - memset(newnode, 0, sizeof(struct avtab_node)); newnode->key = *key; newnode->datum = *datum; if (prev) { @@ -60,10 +60,10 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat struct avtab_node *prev, *cur, *newnode; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return -EINVAL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { @@ -84,7 +84,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat } newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); - if(!newnode) + if (!newnode) return -ENOMEM; return 0; @@ -95,15 +95,15 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat * It also returns a pointer to the node inserted. */ struct avtab_node * -avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum) +avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) { int hvalue; - struct avtab_node *prev, *cur, *newnode; + struct avtab_node *prev, *cur; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { @@ -122,9 +122,7 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da key->target_class < cur->key.target_class) break; } - newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); - - return newnode; + return avtab_insert_node(h, hvalue, prev, cur, key, datum); } struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) @@ -133,10 +131,10 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) struct avtab_node *cur; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (cur = h->htable[hvalue]; cur; cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && @@ -168,10 +166,10 @@ avtab_search_node(struct avtab *h, struct avtab_key *key) struct avtab_node *cur; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (cur = h->htable[hvalue]; cur; cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && @@ -229,41 +227,72 @@ void avtab_destroy(struct avtab *h) if (!h || !h->htable) return; - for (i = 0; i < AVTAB_SIZE; i++) { + for (i = 0; i < h->nslot; i++) { cur = h->htable[i]; - while (cur != NULL) { + while (cur) { temp = cur; cur = cur->next; kmem_cache_free(avtab_node_cachep, temp); } h->htable[i] = NULL; } - vfree(h->htable); + kfree(h->htable); h->htable = NULL; + h->nslot = 0; + h->mask = 0; } - int avtab_init(struct avtab *h) { - int i; + h->htable = NULL; + h->nel = 0; + return 0; +} - h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE); +int avtab_alloc(struct avtab *h, u32 nrules) +{ + u16 mask = 0; + u32 shift = 0; + u32 work = nrules; + u32 nslot = 0; + + if (nrules == 0) + goto avtab_alloc_out; + + while (work) { + work = work >> 1; + shift++; + } + if (shift > 2) + shift = shift - 2; + nslot = 1 << shift; + if (nslot > MAX_AVTAB_HASH_BUCKETS) + nslot = MAX_AVTAB_HASH_BUCKETS; + mask = nslot - 1; + + h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL); if (!h->htable) return -ENOMEM; - for (i = 0; i < AVTAB_SIZE; i++) - h->htable[i] = NULL; + + avtab_alloc_out: h->nel = 0; + h->nslot = nslot; + h->mask = mask; + printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n", + h->nslot, nrules); return 0; } void avtab_hash_eval(struct avtab *h, char *tag) { int i, chain_len, slots_used, max_chain_len; + unsigned long long chain2_len_sum; struct avtab_node *cur; slots_used = 0; max_chain_len = 0; - for (i = 0; i < AVTAB_SIZE; i++) { + chain2_len_sum = 0; + for (i = 0; i < h->nslot; i++) { cur = h->htable[i]; if (cur) { slots_used++; @@ -275,12 +304,14 @@ void avtab_hash_eval(struct avtab *h, char *tag) if (chain_len > max_chain_len) max_chain_len = chain_len; + chain2_len_sum += 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); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d sum of chain length^2 %llu\n", + tag, h->nel, slots_used, h->nslot, max_chain_len, + chain2_len_sum); } static uint16_t spec_order[] = { @@ -292,71 +323,72 @@ static uint16_t spec_order[] = { AVTAB_MEMBER }; -int avtab_read_item(void *fp, u32 vers, struct avtab *a, - int (*insertf)(struct avtab *a, struct avtab_key *k, +int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + int (*insertf)(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *p), void *p) { __le16 buf16[4]; u16 enabled; __le32 buf32[7]; - u32 items, items2, val; + u32 items, items2, val, vers = pol->policyvers; struct avtab_key key; struct avtab_datum datum; int i, rc; + unsigned set; memset(&key, 0, sizeof(struct avtab_key)); memset(&datum, 0, sizeof(struct avtab_datum)); if (vers < POLICYDB_VERSION_AVTAB) { rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items2 = le32_to_cpu(buf32[0]); if (items2 > ARRAY_SIZE(buf32)) { - printk(KERN_ERR "security: avtab: entry overflow\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: entry overflow\n"); + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)*items2); - if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items = 0; val = le32_to_cpu(buf32[items++]); key.source_type = (u16)val; if (key.source_type != val) { - printk("security: avtab: truncated source type\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated source type\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_type = (u16)val; if (key.target_type != val) { - printk("security: avtab: truncated target type\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated target type\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_class = (u16)val; if (key.target_class != val) { - printk("security: avtab: truncated target class\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated target class\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; if (!(val & (AVTAB_AV | AVTAB_TYPE))) { - printk("security: avtab: null entry\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: null entry\n"); + return -EINVAL; } if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { - printk("security: avtab: entry has both access vectors and types\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(spec_order); i++) { @@ -364,21 +396,22 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, key.specified = spec_order[i] | enabled; datum.data = le32_to_cpu(buf32[items++]); rc = insertf(a, &key, &datum, p); - if (rc) return rc; + if (rc) + return rc; } } if (items != items2) { - printk("security: avtab: entry only had %d items, expected %d\n", items2, items); - return -1; + printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); + return -EINVAL; } return 0; } rc = next_entry(buf16, fp, sizeof(u16)*4); - if (rc < 0) { - printk("security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items = 0; @@ -387,12 +420,34 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, key.target_class = le16_to_cpu(buf16[items++]); key.specified = le16_to_cpu(buf16[items++]); + if (!policydb_type_isvalid(pol, key.source_type) || + !policydb_type_isvalid(pol, key.target_type) || + !policydb_class_isvalid(pol, key.target_class)) { + printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); + return -EINVAL; + } + + set = 0; + for (i = 0; i < ARRAY_SIZE(spec_order); i++) { + if (key.specified & spec_order[i]) + set++; + } + if (!set || set > 1) { + printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); + return -EINVAL; + } + rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { - printk("security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } datum.data = le32_to_cpu(*buf32); + if ((key.specified & AVTAB_TYPE) && + !policydb_type_isvalid(pol, datum.data)) { + printk(KERN_ERR "SELinux: avtab: invalid type\n"); + return -EINVAL; + } return insertf(a, &key, &datum, p); } @@ -402,7 +457,7 @@ static int avtab_insertf(struct avtab *a, struct avtab_key *k, return avtab_insert(a, k, d); } -int avtab_read(struct avtab *a, void *fp, u32 vers) +int avtab_read(struct avtab *a, void *fp, struct policydb *pol) { int rc; __le32 buf[1]; @@ -411,24 +466,28 @@ int avtab_read(struct avtab *a, void *fp, u32 vers) rc = next_entry(buf, fp, sizeof(u32)); if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated table\n"); + printk(KERN_ERR "SELinux: avtab: truncated table\n"); goto bad; } nel = le32_to_cpu(buf[0]); if (!nel) { - printk(KERN_ERR "security: avtab: table is empty\n"); + printk(KERN_ERR "SELinux: avtab: table is empty\n"); rc = -EINVAL; goto bad; } + + rc = avtab_alloc(a, nel); + if (rc) + goto bad; + for (i = 0; i < nel; i++) { - rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL); + rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); if (rc) { if (rc == -ENOMEM) - printk(KERN_ERR "security: avtab: out of memory\n"); + printk(KERN_ERR "SELinux: avtab: out of memory\n"); else if (rc == -EEXIST) - printk(KERN_ERR "security: avtab: duplicate entry\n"); - else - rc = -EINVAL; + printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); + goto bad; } } @@ -442,14 +501,56 @@ bad: goto out; } +int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) +{ + __le16 buf16[4]; + __le32 buf32[1]; + int rc; + + buf16[0] = cpu_to_le16(cur->key.source_type); + buf16[1] = cpu_to_le16(cur->key.target_type); + buf16[2] = cpu_to_le16(cur->key.target_class); + buf16[3] = cpu_to_le16(cur->key.specified); + rc = put_entry(buf16, sizeof(u16), 4, fp); + if (rc) + return rc; + buf32[0] = cpu_to_le32(cur->datum.data); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + return rc; + return 0; +} + +int avtab_write(struct policydb *p, struct avtab *a, void *fp) +{ + unsigned int i; + int rc = 0; + struct avtab_node *cur; + __le32 buf[1]; + + buf[0] = cpu_to_le32(a->nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + rc = avtab_write_item(p, cur, fp); + if (rc) + return rc; + } + } + + return rc; +} void avtab_cache_init(void) { avtab_node_cachep = kmem_cache_create("avtab_node", sizeof(struct avtab_node), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); } void avtab_cache_destroy(void) { - kmem_cache_destroy (avtab_node_cachep); + kmem_cache_destroy(avtab_node_cachep); } diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 0a90d939af9..63ce2f9e441 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -14,8 +14,11 @@ * * Copyright (C) 2003 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ #ifndef _SS_AVTAB_H_ #define _SS_AVTAB_H_ @@ -24,16 +27,16 @@ struct avtab_key { u16 source_type; /* source type */ u16 target_type; /* target type */ u16 target_class; /* target object class */ -#define AVTAB_ALLOWED 1 -#define AVTAB_AUDITALLOW 2 -#define AVTAB_AUDITDENY 4 -#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) -#define AVTAB_TRANSITION 16 -#define AVTAB_MEMBER 32 -#define AVTAB_CHANGE 64 -#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) -#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ -#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ +#define AVTAB_ALLOWED 0x0001 +#define AVTAB_AUDITALLOW 0x0002 +#define AVTAB_AUDITDENY 0x0004 +#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) +#define AVTAB_TRANSITION 0x0010 +#define AVTAB_MEMBER 0x0020 +#define AVTAB_CHANGE 0x0040 +#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) +#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ +#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ u16 specified; /* what field is specified */ }; @@ -50,19 +53,26 @@ struct avtab_node { struct avtab { struct avtab_node **htable; u32 nel; /* number of elements */ + u32 nslot; /* number of hash slots */ + u16 mask; /* mask to compute hash func */ + }; int avtab_init(struct avtab *); +int avtab_alloc(struct avtab *, u32); struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k); void avtab_destroy(struct avtab *h); void avtab_hash_eval(struct avtab *h, char *tag); -int avtab_read_item(void *fp, uint32_t vers, struct avtab *a, +struct policydb; +int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, int (*insert)(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *p), void *p); -int avtab_read(struct avtab *a, void *fp, u32 vers); +int avtab_read(struct avtab *a, void *fp, struct policydb *pol); +int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp); +int avtab_write(struct policydb *p, struct avtab *a, void *fp); struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum); @@ -74,11 +84,8 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified void avtab_cache_init(void); void avtab_cache_destroy(void); -#define AVTAB_HASH_BITS 15 -#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS) -#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1) - -#define AVTAB_SIZE AVTAB_HASH_BUCKETS +#define MAX_AVTAB_HASH_BITS 11 +#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) #endif /* _SS_AVTAB_H_ */ diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index d2737edba54..377d148e715 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -1,9 +1,9 @@ /* Authors: Karl MacMillan <kmacmillan@tresys.com> - * Frank Mayer <mayerf@tresys.com> + * Frank Mayer <mayerf@tresys.com> * * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/spinlock.h> -#include <asm/semaphore.h> #include <linux/slab.h> #include "security.h" @@ -30,7 +29,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) int s[COND_EXPR_MAXDEPTH]; int sp = -1; - for (cur = expr; cur != NULL; cur = cur->next) { + for (cur = expr; cur; cur = cur->next) { switch (cur->expr_type) { case COND_BOOL: if (sp == (COND_EXPR_MAXDEPTH - 1)) @@ -90,29 +89,27 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) int evaluate_cond_node(struct policydb *p, struct cond_node *node) { int new_state; - struct cond_av_list* cur; + struct cond_av_list *cur; new_state = cond_evaluate_expr(p, node->expr); if (new_state != node->cur_state) { node->cur_state = new_state; if (new_state == -1) - printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n"); + printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n"); /* turn the rules on or off */ - for (cur = node->true_list; cur != NULL; cur = cur->next) { - if (new_state <= 0) { + for (cur = node->true_list; cur; cur = cur->next) { + if (new_state <= 0) cur->node->key.specified &= ~AVTAB_ENABLED; - } else { + else cur->node->key.specified |= AVTAB_ENABLED; - } } - for (cur = node->false_list; cur != NULL; cur = cur->next) { + for (cur = node->false_list; cur; cur = cur->next) { /* -1 or 1 */ - if (new_state) { + if (new_state) cur->node->key.specified &= ~AVTAB_ENABLED; - } else { + else cur->node->key.specified |= AVTAB_ENABLED; - } } } return 0; @@ -120,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) int cond_policydb_init(struct policydb *p) { + int rc; + p->bool_val_to_struct = NULL; p->cond_list = NULL; - if (avtab_init(&p->te_cond_avtab)) - return -1; + + rc = avtab_init(&p->te_cond_avtab); + if (rc) + return rc; return 0; } @@ -131,7 +132,7 @@ int cond_policydb_init(struct policydb *p) static void cond_av_list_destroy(struct cond_av_list *list) { struct cond_av_list *cur, *next; - for (cur = list; cur != NULL; cur = next) { + for (cur = list; cur; cur = next) { next = cur->next; /* the avtab_ptr_t node is destroy by the avtab */ kfree(cur); @@ -142,7 +143,7 @@ static void cond_node_destroy(struct cond_node *node) { struct cond_expr *cur_expr, *next_expr; - for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) { + for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) { next_expr = cur_expr->next; kfree(cur_expr); } @@ -158,7 +159,7 @@ static void cond_list_destroy(struct cond_node *list) if (list == NULL) return; - for (cur = list; cur != NULL; cur = next) { + for (cur = list; cur; cur = next) { next = cur->next; cond_node_destroy(cur); } @@ -174,10 +175,10 @@ void cond_policydb_destroy(struct policydb *p) int cond_init_bool_indexes(struct policydb *p) { kfree(p->bool_val_to_struct); - p->bool_val_to_struct = (struct cond_bool_datum**) - kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL); + p->bool_val_to_struct = + kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL); if (!p->bool_val_to_struct) - return -1; + return -ENOMEM; return 0; } @@ -192,6 +193,7 @@ int cond_index_bool(void *key, void *datum, void *datap) { struct policydb *p; struct cond_bool_datum *booldatum; + struct flex_array *fa; booldatum = datum; p = datap; @@ -199,8 +201,11 @@ int cond_index_bool(void *key, void *datum, void *datap) if (!booldatum->value || booldatum->value > p->p_bools.nprim) return -EINVAL; - p->p_bool_val_to_name[booldatum->value - 1] = key; - p->bool_val_to_struct[booldatum->value -1] = booldatum; + fa = p->sym_val_to_name[SYM_BOOLS]; + if (flex_array_put_ptr(fa, booldatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); + p->bool_val_to_struct[booldatum->value - 1] = booldatum; return 0; } @@ -222,38 +227,40 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); if (!booldatum) - return -1; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto err; booldatum->value = le32_to_cpu(buf[0]); booldatum->state = le32_to_cpu(buf[1]); + rc = -EINVAL; if (!bool_isvalid(booldatum)) goto err; len = le32_to_cpu(buf[2]); + rc = -ENOMEM; key = kmalloc(len + 1, GFP_KERNEL); if (!key) goto err; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto err; - key[len] = 0; - if (hashtab_insert(h, key, booldatum)) + key[len] = '\0'; + rc = hashtab_insert(h, key, booldatum); + if (rc) goto err; return 0; err: cond_destroy_bool(key, booldatum, NULL); - return -1; + return rc; } -struct cond_insertf_data -{ +struct cond_insertf_data { struct policydb *p; struct cond_av_list *other; struct cond_av_list *head; @@ -267,7 +274,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum struct cond_av_list *other = data->other, *list, *cur; struct avtab_node *node_ptr; u8 found; - + int rc = -EINVAL; /* * For type rules we have to make certain there aren't any @@ -276,7 +283,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum */ if (k->specified & AVTAB_TYPE) { if (avtab_search(&p->te_avtab, k)) { - printk("security: type rule already exists outside of a conditional."); + printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n"); goto err; } /* @@ -291,24 +298,24 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_search_node(&p->te_cond_avtab, k); if (node_ptr) { if (avtab_search_node_next(node_ptr, k->specified)) { - printk("security: too many conflicting type rules."); + printk(KERN_ERR "SELinux: too many conflicting type rules.\n"); goto err; } found = 0; - for (cur = other; cur != NULL; cur = cur->next) { + for (cur = other; cur; cur = cur->next) { if (cur->node == node_ptr) { found = 1; break; } } if (!found) { - printk("security: conflicting type rules.\n"); + printk(KERN_ERR "SELinux: conflicting type rules.\n"); goto err; } } } else { if (avtab_search(&p->te_cond_avtab, k)) { - printk("security: conflicting type rules when adding type rule for true.\n"); + printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n"); goto err; } } @@ -316,13 +323,16 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if (!node_ptr) { - printk("security: could not insert rule."); + printk(KERN_ERR "SELinux: could not insert rule.\n"); + rc = -ENOMEM; goto err; } list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); - if (!list) + if (!list) { + rc = -ENOMEM; goto err; + } list->node = node_ptr; if (!data->head) @@ -335,7 +345,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum err: cond_av_list_destroy(data->head); data->head = NULL; - return -1; + return rc; } static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) @@ -349,23 +359,22 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); - if (len == 0) { + if (len == 0) return 0; - } data.p = p; data.other = other; data.head = NULL; data.tail = NULL; for (i = 0; i < len; i++) { - rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data); + rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, + &data); if (rc) return rc; - } *ret_list = data.head; @@ -375,12 +384,12 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * static int expr_isvalid(struct policydb *p, struct cond_expr *expr) { if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { - printk("security: conditional expressions uses unknown operator.\n"); + printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n"); return 0; } if (expr->bool > p->p_bools.nprim) { - printk("security: conditional expressions uses unknown bool.\n"); + printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n"); return 0; } return 1; @@ -394,53 +403,55 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) struct cond_expr *expr = NULL, *last = NULL; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; node->cur_state = le32_to_cpu(buf[0]); len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; /* expr */ len = le32_to_cpu(buf[0]); - for (i = 0; i < len; i++ ) { + for (i = 0; i < len; i++) { rc = next_entry(buf, fp, sizeof(u32) * 2); - if (rc < 0) + if (rc) goto err; + rc = -ENOMEM; expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); - if (!expr) { + if (!expr) goto err; - } expr->expr_type = le32_to_cpu(buf[0]); expr->bool = le32_to_cpu(buf[1]); if (!expr_isvalid(p, expr)) { + rc = -EINVAL; kfree(expr); goto err; } - if (i == 0) { + if (i == 0) node->expr = expr; - } else { + else last->next = expr; - } last = expr; } - if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) + rc = cond_read_av_list(p, fp, &node->true_list, NULL); + if (rc) goto err; - if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) + rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); + if (rc) goto err; return 0; err: cond_node_destroy(node); - return -1; + return rc; } int cond_read_list(struct policydb *p, void *fp) @@ -451,33 +462,161 @@ int cond_read_list(struct policydb *p, void *fp) int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); + rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); + if (rc) + goto err; + for (i = 0; i < len; i++) { + rc = -ENOMEM; node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); if (!node) goto err; - if (cond_read_node(p, node, fp) != 0) + rc = cond_read_node(p, node, fp); + if (rc) goto err; - if (i == 0) { + if (i == 0) p->cond_list = node; - } else { + else last->next = node; - } last = node; } return 0; err: cond_list_destroy(p->cond_list); p->cond_list = NULL; - return -1; + return rc; +} + +int cond_write_bool(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct cond_bool_datum *booldatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[3]; + u32 len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(booldatum->value); + buf[1] = cpu_to_le32(booldatum->state); + buf[2] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + return 0; +} + +/* + * cond_write_cond_av_list doesn't write out the av_list nodes. + * Instead it writes out the key/value pairs from the avtab. This + * is necessary because there is no way to uniquely identifying rules + * in the avtab so it is not possible to associate individual rules + * in the avtab with a conditional without saving them as part of + * the conditional. This means that the avtab with the conditional + * rules will not be saved but will be rebuilt on policy load. + */ +static int cond_write_av_list(struct policydb *p, + struct cond_av_list *list, struct policy_file *fp) +{ + __le32 buf[1]; + struct cond_av_list *cur_list; + u32 len; + int rc; + + len = 0; + for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) + len++; + + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + if (len == 0) + return 0; + + for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) { + rc = avtab_write_item(p, cur_list->node, fp); + if (rc) + return rc; + } + + return 0; } +static int cond_write_node(struct policydb *p, struct cond_node *node, + struct policy_file *fp) +{ + struct cond_expr *cur_expr; + __le32 buf[2]; + int rc; + u32 len = 0; + + buf[0] = cpu_to_le32(node->cur_state); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) + len++; + + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) { + buf[0] = cpu_to_le32(cur_expr->expr_type); + buf[1] = cpu_to_le32(cur_expr->bool); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + } + + rc = cond_write_av_list(p, node->true_list, fp); + if (rc) + return rc; + rc = cond_write_av_list(p, node->false_list, fp); + if (rc) + return rc; + + return 0; +} + +int cond_write_list(struct policydb *p, struct cond_node *list, void *fp) +{ + struct cond_node *cur; + u32 len; + __le32 buf[1]; + int rc; + + len = 0; + for (cur = list; cur != NULL; cur = cur->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur = list; cur != NULL; cur = cur->next) { + rc = cond_write_node(p, cur, fp); + if (rc) + return rc; + } + + return 0; +} /* Determine whether additional permissions are granted by the conditional * av table, and if so, add them to the result */ @@ -485,24 +624,24 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi { struct avtab_node *node; - if(!ctab || !key || !avd) + if (!ctab || !key || !avd) return; - for(node = avtab_search_node(ctab, key); node != NULL; + for (node = avtab_search_node(ctab, key); node; node = avtab_search_node_next(node, key->specified)) { - if ( (u16) (AVTAB_ALLOWED|AVTAB_ENABLED) == - (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) + if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) avd->allowed |= node->datum.data; - if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) == - (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) + if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) /* Since a '0' in an auditdeny mask represents a * permission we do NOT want to audit (dontaudit), we use * the '&' operand to ensure that all '0's in the mask * are retained (much unlike the allow and auditallow cases). */ avd->auditdeny &= node->datum.data; - if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) == - (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) + if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) avd->auditallow |= node->datum.data; } return; diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index f3a1fc6e5d6..4d1f8746650 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -13,6 +13,7 @@ #include "avtab.h" #include "symtab.h" #include "policydb.h" +#include "../include/conditional.h" #define COND_EXPR_MAXDEPTH 10 @@ -28,7 +29,7 @@ struct cond_expr { #define COND_XOR 5 /* bool ^ bool */ #define COND_EQ 6 /* bool == bool */ #define COND_NEQ 7 /* bool != bool */ -#define COND_LAST 8 +#define COND_LAST COND_NEQ __u32 expr_type; __u32 bool; struct cond_expr *next; @@ -59,16 +60,18 @@ struct cond_node { struct cond_node *next; }; -int cond_policydb_init(struct policydb* p); -void cond_policydb_destroy(struct policydb* p); +int cond_policydb_init(struct policydb *p); +void cond_policydb_destroy(struct policydb *p); -int cond_init_bool_indexes(struct policydb* p); +int cond_init_bool_indexes(struct policydb *p); int cond_destroy_bool(void *key, void *datum, void *p); int cond_index_bool(void *key, void *datum, void *datap); int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp); int cond_read_list(struct policydb *p, void *fp); +int cond_write_bool(void *key, void *datum, void *ptr); +int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 149dda731fd..96fd947c494 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr { u32 op; /* operator */ struct ebitmap names; /* names */ + struct type_set *type_names; struct constraint_expr *next; /* next expression */ }; diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 0562bacb7b9..212e3479a0d 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -27,7 +27,9 @@ struct context { u32 user; u32 role; u32 type; + u32 len; /* length of string in bytes */ struct mls_range range; + char *str; /* string representation if context cannot be mapped. */ }; static inline void mls_context_init(struct context *c) @@ -39,14 +41,51 @@ static inline int mls_context_cpy(struct context *dst, struct context *src) { int rc; - if (!selinux_mls_enabled) - return 0; + dst->range.level[0].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + +/* + * Sets both levels in the MLS range of 'dst' to the low level of 'src'. + */ +static inline int mls_context_cpy_low(struct context *dst, struct context *src) +{ + int rc; dst->range.level[0].sens = src->range.level[0].sens; rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); if (rc) goto out; + dst->range.level[1].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + +/* + * Sets both levels in the MLS range of 'dst' to the high level of 'src'. + */ +static inline int mls_context_cpy_high(struct context *dst, struct context *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].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) @@ -57,20 +96,14 @@ out: static inline int mls_context_cmp(struct context *c1, struct context *c2) { - if (!selinux_mls_enabled) - return 1; - return ((c1->range.level[0].sens == c2->range.level[0].sens) && - ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) && + 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)); + ebitmap_cmp(&c1->range.level[1].cat, &c2->range.level[1].cat)); } static inline void mls_context_destroy(struct context *c) { - if (!selinux_mls_enabled) - return; - ebitmap_destroy(&c->range.level[0].cat); ebitmap_destroy(&c->range.level[1].cat); mls_context_init(c); @@ -83,20 +116,43 @@ static inline void context_init(struct context *c) static inline int context_cpy(struct context *dst, struct context *src) { + int rc; + dst->user = src->user; dst->role = src->role; dst->type = src->type; - return mls_context_cpy(dst, src); + if (src->str) { + dst->str = kstrdup(src->str, GFP_ATOMIC); + if (!dst->str) + return -ENOMEM; + dst->len = src->len; + } else { + dst->str = NULL; + dst->len = 0; + } + rc = mls_context_cpy(dst, src); + if (rc) { + kfree(dst->str); + return rc; + } + return 0; } static inline void context_destroy(struct context *c) { c->user = c->role = c->type = 0; + kfree(c->str); + c->str = NULL; + c->len = 0; mls_context_destroy(c); } static inline int context_cmp(struct context *c1, struct context *c2) { + if (c1->len && c2->len) + return (c1->len == c2->len && !strcmp(c1->str, c2->str)); + if (c1->len || c2->len) + return 0; return ((c1->user == c2->user) && (c1->role == c2->role) && (c1->type == c2->type) && diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 47024a6e184..820313a04d4 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -3,12 +3,27 @@ * * Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the NetLabel category bitmap + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + */ +/* + * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> + * Applied standard bit operations to improve bitmap scanning. + */ + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "ebitmap.h" #include "policydb.h" +#define BITS_PER_U64 (sizeof(u64) * 8) + int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) { struct ebitmap_node *n1, *n2; @@ -20,7 +35,7 @@ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) n2 = e2->node; while (n1 && n2 && (n1->startbit == n2->startbit) && - (n1->map == n2->map)) { + !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) { n1 = n1->next; n2 = n2->next; } @@ -45,7 +60,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return -ENOMEM; } new->startbit = n->startbit; - new->map = n->map; + memcpy(new->maps, n->maps, EBITMAP_SIZE / 8); new->next = NULL; if (prev) prev->next = new; @@ -59,22 +74,180 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) +#ifdef CONFIG_NETLABEL +/** + * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap + * + * Description: + * Export a SELinux extensibile bitmap into a NetLabel category bitmap. + * Returns zero on success, negative values on error. + * + */ +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + struct ebitmap_node *e_iter = ebmap->node; + struct netlbl_lsm_secattr_catmap *c_iter; + u32 cmap_idx, cmap_sft; + int i; + + /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, + * however, it is not always compatible with an array of unsigned long + * in ebitmap_node. + * In addition, you should pay attention the following implementation + * assumes unsigned long has a width equal with or less than 64-bit. + */ + + if (e_iter == NULL) { + *catmap = NULL; + return 0; + } + + c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter == NULL) + return -ENOMEM; + *catmap = c_iter; + c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + + while (e_iter) { + for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { + unsigned int delta, e_startbit, c_endbit; + + e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE; + c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE; + if (e_startbit >= c_endbit) { + c_iter->next + = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter->next == NULL) + goto netlbl_export_failure; + c_iter = c_iter->next; + c_iter->startbit + = e_startbit & ~(NETLBL_CATMAP_SIZE - 1); + } + delta = e_startbit - c_iter->startbit; + cmap_idx = delta / NETLBL_CATMAP_MAPSIZE; + cmap_sft = delta % NETLBL_CATMAP_MAPSIZE; + c_iter->bitmap[cmap_idx] + |= e_iter->maps[i] << cmap_sft; + } + e_iter = e_iter->next; + } + + return 0; + +netlbl_export_failure: + netlbl_secattr_catmap_free(*catmap); + return -ENOMEM; +} + +/** + * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap + * @ebmap: the ebitmap to import + * @catmap: the NetLabel category bitmap + * + * Description: + * Import a NetLabel category bitmap into a SELinux extensibile bitmap. + * Returns zero on success, negative values on error. + * + */ +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + struct ebitmap_node *e_iter = NULL; + struct ebitmap_node *emap_prev = NULL; + struct netlbl_lsm_secattr_catmap *c_iter = catmap; + u32 c_idx, c_pos, e_idx, e_sft; + + /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, + * however, it is not always compatible with an array of unsigned long + * in ebitmap_node. + * In addition, you should pay attention the following implementation + * assumes unsigned long has a width equal with or less than 64-bit. + */ + + do { + for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { + unsigned int delta; + u64 map = c_iter->bitmap[c_idx]; + + if (!map) + continue; + + c_pos = c_iter->startbit + + c_idx * NETLBL_CATMAP_MAPSIZE; + if (!e_iter + || c_pos >= e_iter->startbit + EBITMAP_SIZE) { + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (!e_iter) + goto netlbl_import_failure; + e_iter->startbit + = c_pos - (c_pos % EBITMAP_SIZE); + if (emap_prev == NULL) + ebmap->node = e_iter; + else + emap_prev->next = e_iter; + emap_prev = e_iter; + } + delta = c_pos - e_iter->startbit; + e_idx = delta / EBITMAP_UNIT_SIZE; + e_sft = delta % EBITMAP_UNIT_SIZE; + while (map) { + e_iter->maps[e_idx++] |= map & (-1UL); + map = EBITMAP_SHIFT_UNIT_SIZE(map); + } + } + c_iter = c_iter->next; + } while (c_iter); + if (e_iter != NULL) + ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; + else + ebitmap_destroy(ebmap); + + return 0; + +netlbl_import_failure: + ebitmap_destroy(ebmap); + return -ENOMEM; +} +#endif /* CONFIG_NETLABEL */ + +/* + * Check to see if all the bits set in e2 are also set in e1. Optionally, + * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed + * last_e2bit. + */ +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit) { struct ebitmap_node *n1, *n2; + int i; 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; + for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; ) + i--; /* Skip trailing NULL map entries */ + if (last_e2bit && (i >= 0)) { + u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE + + __fls(n2->maps[i]); + if (lastsetbit > last_e2bit) + return 0; + } + + while (i >= 0) { + if ((n1->maps[i] & n2->maps[i]) != n2->maps[i]) + return 0; + i--; + } n1 = n1->next; n2 = n2->next; @@ -95,12 +268,8 @@ int ebitmap_get_bit(struct ebitmap *e, unsigned long bit) 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; - } + if ((n->startbit + EBITMAP_SIZE) > bit) + return ebitmap_node_get_bit(n, bit); n = n->next; } @@ -114,31 +283,35 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) prev = NULL; n = e->node; while (n && n->startbit <= bit) { - if ((n->startbit + MAPSIZE) > bit) { + if ((n->startbit + EBITMAP_SIZE) > bit) { if (value) { - n->map |= (MAPBIT << (bit - n->startbit)); + ebitmap_node_set_bit(n, bit); } 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; - } + unsigned int s; + + ebitmap_node_clr_bit(n, bit); + + s = find_first_bit(n->maps, EBITMAP_SIZE); + if (s < EBITMAP_SIZE) + return 0; + + /* drop this node from the bitmap */ + if (!n->next) { + /* + * this was the highest map + * within the bitmap + */ if (prev) - prev->next = n->next; + e->highbit = prev->startbit + + EBITMAP_SIZE; else - e->node = n->next; - - kfree(n); + e->highbit = 0; } + if (prev) + prev->next = n->next; + else + e->node = n->next; + kfree(n); } return 0; } @@ -153,12 +326,12 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) if (!new) return -ENOMEM; - new->startbit = bit & ~(MAPSIZE - 1); - new->map = (MAPBIT << (bit - new->startbit)); + new->startbit = bit - (bit % EBITMAP_SIZE); + ebitmap_node_set_bit(new, bit); if (!n) /* this node will be the highest map within the bitmap */ - e->highbit = new->startbit + MAPSIZE; + e->highbit = new->startbit + EBITMAP_SIZE; if (prev) { new->next = prev->next; @@ -192,11 +365,11 @@ void ebitmap_destroy(struct ebitmap *e) int ebitmap_read(struct ebitmap *e, void *fp) { - int rc; - struct ebitmap_node *n, *l; + struct ebitmap_node *n = NULL; + u32 mapunit, count, startbit, index; + u64 map; __le32 buf[3]; - u32 mapsize, count, i; - __le64 map; + int rc, i; ebitmap_init(e); @@ -204,88 +377,165 @@ int ebitmap_read(struct ebitmap *e, void *fp) if (rc < 0) goto out; - mapsize = le32_to_cpu(buf[0]); + mapunit = le32_to_cpu(buf[0]); e->highbit = le32_to_cpu(buf[1]); count = le32_to_cpu(buf[2]); - if (mapsize != MAPSIZE) { - printk(KERN_ERR "security: ebitmap: map size %u does not " - "match my size %Zd (high bit was %d)\n", mapsize, - MAPSIZE, e->highbit); + if (mapunit != BITS_PER_U64) { + printk(KERN_ERR "SELinux: ebitmap: map size %u does not " + "match my size %Zd (high bit was %d)\n", + mapunit, BITS_PER_U64, e->highbit); goto bad; } + + /* round up e->highbit */ + e->highbit += EBITMAP_SIZE - 1; + e->highbit -= (e->highbit % EBITMAP_SIZE); + if (!e->highbit) { e->node = NULL; goto ok; } - if (e->highbit & (MAPSIZE - 1)) { - printk(KERN_ERR "security: ebitmap: high bit (%d) is not a " - "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE); - goto bad; - } - l = NULL; + for (i = 0; i < count; i++) { - rc = next_entry(buf, fp, sizeof(u32)); + rc = next_entry(&startbit, fp, sizeof(u32)); if (rc < 0) { - printk(KERN_ERR "security: ebitmap: truncated map\n"); + printk(KERN_ERR "SELinux: ebitmap: truncated map\n"); goto bad; } - n = kzalloc(sizeof(*n), GFP_KERNEL); - if (!n) { - printk(KERN_ERR "security: ebitmap: out of memory\n"); - rc = -ENOMEM; + startbit = le32_to_cpu(startbit); + + if (startbit & (mapunit - 1)) { + printk(KERN_ERR "SELinux: ebitmap start bit (%d) is " + "not a multiple of the map unit size (%u)\n", + startbit, mapunit); goto bad; } - - n->startbit = le32_to_cpu(buf[0]); - - if (n->startbit & (MAPSIZE - 1)) { - printk(KERN_ERR "security: ebitmap start bit (%d) is " - "not a multiple of the map size (%Zd)\n", - n->startbit, MAPSIZE); - goto bad_free; + if (startbit > e->highbit - mapunit) { + printk(KERN_ERR "SELinux: ebitmap start bit (%d) is " + "beyond the end of the bitmap (%u)\n", + startbit, (e->highbit - mapunit)); + goto bad; } - if (n->startbit > (e->highbit - MAPSIZE)) { - printk(KERN_ERR "security: ebitmap start bit (%d) is " - "beyond the end of the bitmap (%Zd)\n", - n->startbit, (e->highbit - MAPSIZE)); - goto bad_free; + + if (!n || startbit >= n->startbit + EBITMAP_SIZE) { + struct ebitmap_node *tmp; + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + printk(KERN_ERR + "SELinux: ebitmap: out of memory\n"); + rc = -ENOMEM; + goto bad; + } + /* round down */ + tmp->startbit = startbit - (startbit % EBITMAP_SIZE); + if (n) + n->next = tmp; + else + e->node = tmp; + n = tmp; + } else if (startbit <= n->startbit) { + printk(KERN_ERR "SELinux: ebitmap: start bit %d" + " comes after start bit %d\n", + startbit, n->startbit); + goto bad; } + rc = next_entry(&map, fp, sizeof(u64)); if (rc < 0) { - printk(KERN_ERR "security: ebitmap: truncated map\n"); - goto bad_free; + printk(KERN_ERR "SELinux: ebitmap: truncated map\n"); + goto bad; } - n->map = le64_to_cpu(map); + 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; + index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE; + while (map) { + n->maps[index++] = map & (-1UL); + map = EBITMAP_SHIFT_UNIT_SIZE(map); } - if (l) { - if (n->startbit <= l->startbit) { - printk(KERN_ERR "security: ebitmap: start " - "bit %d comes after start bit %d\n", - n->startbit, l->startbit); - goto bad_free; - } - l->next = n; - } else - e->node = n; - - l = n; } - ok: rc = 0; out: return rc; -bad_free: - kfree(n); bad: if (!rc) rc = -EINVAL; ebitmap_destroy(e); goto out; } + +int ebitmap_write(struct ebitmap *e, void *fp) +{ + struct ebitmap_node *n; + u32 count; + __le32 buf[3]; + u64 map; + int bit, last_bit, last_startbit, rc; + + buf[0] = cpu_to_le32(BITS_PER_U64); + + count = 0; + last_bit = 0; + last_startbit = -1; + ebitmap_for_each_positive_bit(e, n, bit) { + if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + count++; + last_startbit = rounddown(bit, BITS_PER_U64); + } + last_bit = roundup(bit + 1, BITS_PER_U64); + } + buf[1] = cpu_to_le32(last_bit); + buf[2] = cpu_to_le32(count); + + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + map = 0; + last_startbit = INT_MIN; + ebitmap_for_each_positive_bit(e, n, bit) { + if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + __le64 buf64[1]; + + /* this is the very first bit */ + if (!map) { + last_startbit = rounddown(bit, BITS_PER_U64); + map = (u64)1 << (bit - last_startbit); + continue; + } + + /* write the last node */ + buf[0] = cpu_to_le32(last_startbit); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + buf64[0] = cpu_to_le64(map); + rc = put_entry(buf64, sizeof(u64), 1, fp); + if (rc) + return rc; + + /* set up for the next node */ + map = 0; + last_startbit = rounddown(bit, BITS_PER_U64); + } + map |= (u64)1 << (bit - last_startbit); + } + /* write the last node */ + if (map) { + __le64 buf64[1]; + + /* write the last node */ + buf[0] = cpu_to_le32(last_startbit); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + buf64[0] = cpu_to_le64(map); + rc = put_entry(buf64, sizeof(u64), 1, fp); + if (rc) + return rc; + } + return 0; +} diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 8bf41055a6c..712c8a7b8e8 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -14,14 +14,26 @@ #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 */ +#include <net/netlabel.h> + +#ifdef CONFIG_64BIT +#define EBITMAP_NODE_SIZE 64 +#else +#define EBITMAP_NODE_SIZE 32 +#endif + +#define EBITMAP_UNIT_NUMS ((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\ + / sizeof(unsigned long)) +#define EBITMAP_UNIT_SIZE BITS_PER_LONG +#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE) +#define EBITMAP_BIT 1ULL +#define EBITMAP_SHIFT_UNIT_SIZE(x) \ + (((x) >> EBITMAP_UNIT_SIZE / 2) >> EBITMAP_UNIT_SIZE / 2) struct ebitmap_node { - u32 startbit; /* starting position in the total bitmap */ - MAPTYPE map; /* this node's portion of the bitmap */ struct ebitmap_node *next; + unsigned long maps[EBITMAP_UNIT_NUMS]; + u32 startbit; }; struct ebitmap { @@ -30,13 +42,18 @@ struct ebitmap { }; #define ebitmap_length(e) ((e)->highbit) -#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0) -static inline unsigned int ebitmap_start(struct ebitmap *e, - struct ebitmap_node **n) +static inline unsigned int ebitmap_start_positive(struct ebitmap *e, + struct ebitmap_node **n) { - *n = e->node; - return ebitmap_startbit(e); + unsigned int ofs; + + for (*n = e->node; *n; *n = (*n)->next) { + ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); + if (ofs < EBITMAP_SIZE) + return (*n)->startbit + ofs; + } + return ebitmap_length(e); } static inline void ebitmap_init(struct ebitmap *e) @@ -44,35 +61,91 @@ static inline void ebitmap_init(struct ebitmap *e) memset(e, 0, sizeof(*e)); } -static inline unsigned int ebitmap_next(struct ebitmap_node **n, - unsigned int bit) +static inline unsigned int ebitmap_next_positive(struct ebitmap *e, + struct ebitmap_node **n, + unsigned int bit) { - if ((bit == ((*n)->startbit + MAPSIZE - 1)) && - (*n)->next) { - *n = (*n)->next; - return (*n)->startbit; - } + unsigned int ofs; + + ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1); + if (ofs < EBITMAP_SIZE) + return ofs + (*n)->startbit; - return (bit+1); + for (*n = (*n)->next; *n; *n = (*n)->next) { + ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); + if (ofs < EBITMAP_SIZE) + return ofs + (*n)->startbit; + } + return ebitmap_length(e); } -static inline int ebitmap_node_get_bit(struct ebitmap_node * n, +#define EBITMAP_NODE_INDEX(node, bit) \ + (((bit) - (node)->startbit) / EBITMAP_UNIT_SIZE) +#define EBITMAP_NODE_OFFSET(node, bit) \ + (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE) + +static inline int ebitmap_node_get_bit(struct ebitmap_node *n, unsigned int bit) { - if (n->map & (MAPBIT << (bit - n->startbit))) + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + if ((n->maps[index] & (EBITMAP_BIT << ofs))) return 1; return 0; } -#define ebitmap_for_each_bit(e, n, bit) \ - for (bit = ebitmap_start(e, &n); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \ +static inline void ebitmap_node_set_bit(struct ebitmap_node *n, + unsigned int bit) +{ + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + n->maps[index] |= (EBITMAP_BIT << ofs); +} + +static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, + unsigned int bit) +{ + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + n->maps[index] &= ~(EBITMAP_BIT << ofs); +} + +#define ebitmap_for_each_positive_bit(e, n, bit) \ + for (bit = ebitmap_start_positive(e, &n); \ + bit < ebitmap_length(e); \ + bit = ebitmap_next_positive(e, &n, bit)) \ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit); 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); +int ebitmap_write(struct ebitmap *e, void *fp); + +#ifdef CONFIG_NETLABEL +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap); +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap); +#else +static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + return -ENOMEM; +} +static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + return -ENOMEM; +} +#endif #endif /* _SS_EBITMAP_H_ */ diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 24e5ec95763..2cc49614984 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -6,11 +6,12 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/sched.h> #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 *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), + u32 size) { struct hashtab *p; u32 i; @@ -40,6 +41,8 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) u32 hvalue; struct hashtab_node *prev, *cur, *newnode; + cond_resched(); + if (!h || h->nel == HASHTAB_MAX_NODES) return -EINVAL; @@ -71,7 +74,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) return 0; } -void *hashtab_search(struct hashtab *h, void *key) +void *hashtab_search(struct hashtab *h, const void *key) { u32 hvalue; struct hashtab_node *cur; @@ -81,7 +84,7 @@ void *hashtab_search(struct hashtab *h, void *key) hvalue = h->hash_value(h, key); cur = h->htable[hvalue]; - while (cur != NULL && h->keycmp(h, key, cur->key) > 0) + while (cur && h->keycmp(h, key, cur->key) > 0) cur = cur->next; if (cur == NULL || (h->keycmp(h, key, cur->key) != 0)) @@ -100,7 +103,7 @@ void hashtab_destroy(struct hashtab *h) for (i = 0; i < h->size; i++) { cur = h->htable[i]; - while (cur != NULL) { + while (cur) { temp = cur; cur = cur->next; kfree(temp); @@ -127,7 +130,7 @@ int hashtab_map(struct hashtab *h, for (i = 0; i < h->size; i++) { cur = h->htable[i]; - while (cur != NULL) { + while (cur) { ret = apply(cur->key, cur->datum, args); if (ret) return ret; diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 4cc85816a71..953872cd84a 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -22,9 +22,9 @@ 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); + u32 (*hash_value)(struct hashtab *h, const void *key); /* hash function */ - int (*keycmp)(struct hashtab *h, void *key1, void *key2); + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); /* key comparison function */ }; @@ -39,9 +39,9 @@ struct hashtab_info { * 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); +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), + u32 size); /* * Inserts the specified (key, datum) pair into the specified hash table. @@ -49,7 +49,7 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), * Returns -ENOMEM on memory allocation error, * -EEXIST if there is already an entry with the same key, * -EINVAL for general errors or - * 0 otherwise. + 0 otherwise. */ int hashtab_insert(struct hashtab *h, void *k, void *d); @@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d); * Returns NULL if no entry has the specified key or * the datum of the entry otherwise. */ -void *hashtab_search(struct hashtab *h, void *k); +void *hashtab_search(struct hashtab *h, const void *k); /* * Destroys the specified hash table. diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 640d0bfdbc6..d307b37ddc2 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -8,13 +8,21 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the MLS label from NetLabel + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "sidtab.h" #include "mls.h" #include "policydb.h" @@ -24,41 +32,45 @@ * 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 mls_compute_context_len(struct context *context) { - int i, l, len, range; + int i, l, len, head, prev; + char *nm; + struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; len = 1; /* for the beginning ":" */ for (l = 0; l < 2; l++) { - range = 0; - len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); - - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (range) { - range++; - continue; - } + int index_sens = context->range.level[l].sens; + len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1)); - len += strlen(policydb.p_cat_val_to_name[i]) + 1; - range++; - } else { - if (range > 1) - len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; - range = 0; + /* categories */ + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { + if (i - prev > 1) { + /* one or more negative bits are skipped */ + if (head != prev) { + nm = sym_name(&policydb, SYM_CATS, prev); + len += strlen(nm) + 1; + } + nm = sym_name(&policydb, SYM_CATS, i); + len += strlen(nm) + 1; + head = i; } + prev = i; + } + if (prev != head) { + nm = sym_name(&policydb, SYM_CATS, prev); + len += strlen(nm) + 1; } - /* Handle case where last category is the end of range */ - if (range > 1) - len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; - if (l == 0) { if (mls_level_eq(&context->range.level[0], - &context->range.level[1])) + &context->range.level[1])) break; else len++; @@ -74,13 +86,14 @@ int mls_compute_context_len(struct context * context) * Update `*scontext' to point to the end of the MLS fields. */ void mls_sid_to_context(struct context *context, - char **scontext) + char **scontext) { - char *scontextp; - int i, l, range, wrote_sep; + char *scontextp, *nm; + int i, l, head, prev; + struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return; scontextp = *scontext; @@ -89,61 +102,54 @@ void mls_sid_to_context(struct context *context, scontextp++; for (l = 0; l < 2; l++) { - range = 0; - wrote_sep = 0; - strcpy(scontextp, - policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); - scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); + strcpy(scontextp, sym_name(&policydb, SYM_LEVELS, + context->range.level[l].sens - 1)); + scontextp += strlen(scontextp); /* categories */ - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (range) { - range++; - continue; - } - - if (!wrote_sep) { - *scontextp++ = ':'; - wrote_sep = 1; - } else - *scontextp++ = ','; - strcpy(scontextp, policydb.p_cat_val_to_name[i]); - scontextp += strlen(policydb.p_cat_val_to_name[i]); - range++; - } else { - if (range > 1) { - if (range > 2) + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { + if (i - prev > 1) { + /* one or more negative bits are skipped */ + if (prev != head) { + if (prev - head > 1) *scontextp++ = '.'; else *scontextp++ = ','; - - strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = sym_name(&policydb, SYM_CATS, prev); + strcpy(scontextp, nm); + scontextp += strlen(nm); } - range = 0; + if (prev < 0) + *scontextp++ = ':'; + else + *scontextp++ = ','; + nm = sym_name(&policydb, SYM_CATS, i); + strcpy(scontextp, nm); + scontextp += strlen(nm); + head = i; } + prev = i; } - /* Handle case where last category is the end of range */ - if (range > 1) { - if (range > 2) + if (prev != head) { + if (prev - head > 1) *scontextp++ = '.'; else *scontextp++ = ','; - - strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = sym_name(&policydb, SYM_CATS, prev); + strcpy(scontextp, nm); + scontextp += strlen(nm); } if (l == 0) { if (mls_level_eq(&context->range.level[0], - &context->range.level[1])) + &context->range.level[1])) break; - else { - *scontextp = '-'; - scontextp++; - } + else + *scontextp++ = '-'; } } @@ -151,51 +157,47 @@ void mls_sid_to_context(struct context *context, return; } +int mls_level_isvalid(struct policydb *p, struct mls_level *l) +{ + struct level_datum *levdatum; + + if (!l->sens || l->sens > p->p_levels.nprim) + return 0; + levdatum = hashtab_search(p->p_levels.table, + sym_name(p, SYM_LEVELS, l->sens - 1)); + if (!levdatum) + return 0; + + /* + * Return 1 iff all the bits set in l->cat are also be set in + * levdatum->level->cat and no bit in l->cat is larger than + * p->p_cats.nprim. + */ + return ebitmap_contains(&levdatum->level->cat, &l->cat, + p->p_cats.nprim); +} + +int mls_range_isvalid(struct policydb *p, struct mls_range *r) +{ + return (mls_level_isvalid(p, &r->level[0]) && + mls_level_isvalid(p, &r->level[1]) && + mls_level_dom(&r->level[1], &r->level[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) { - struct level_datum *levdatum; struct user_datum *usrdatum; - struct ebitmap_node *node; - int i, l; - if (!selinux_mls_enabled) + if (!p->mls_enabled) return 1; - /* - * MLS range validity checks: high must dominate low, low level must - * be valid (category set <-> sensitivity check), and high level must - * be valid (category set <-> sensitivity check) - */ - if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) - /* High does not dominate low. */ + if (!mls_range_isvalid(p, &c->range)) return 0; - for (l = 0; l < 2; l++) { - if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) - return 0; - levdatum = hashtab_search(p->p_levels.table, - p->p_sens_val_to_name[c->range.level[l].sens - 1]); - if (!levdatum) - return 0; - - ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (i > p->p_cats.nprim) - return 0; - if (!ebitmap_get_bit(&levdatum->level->cat, i)) - /* - * Category may not be associated with - * sensitivity in low level. - */ - return 0; - } - } - } - if (c->role == OBJECT_R_VAL) return 1; @@ -212,26 +214,6 @@ int mls_context_isvalid(struct policydb *p, struct context *c) } /* - * 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; -} - -/* * Set the MLS fields in the security context structure * `context' based on the string representation in * the string `*scontext'. Update `*scontext' to @@ -249,7 +231,8 @@ static inline int mls_copy_context(struct context *dst, * Policy read-lock must be held for sidtab lookup. * */ -int mls_context_to_sid(char oldc, +int mls_context_to_sid(struct policydb *pol, + char oldc, char **scontext, struct context *context, struct sidtab *s, @@ -262,9 +245,9 @@ int mls_context_to_sid(char oldc, struct cat_datum *catdatum, *rngdatum; int l, rc = -EINVAL; - if (!selinux_mls_enabled) { + if (!pol->mls_enabled) { if (def_sid != SECSID_NULL && oldc) - *scontext += strlen(*scontext); + *scontext += strlen(*scontext) + 1; return 0; } @@ -282,7 +265,7 @@ int mls_context_to_sid(char oldc, if (!defcon) goto out; - rc = mls_copy_context(context, defcon); + rc = mls_context_cpy(context, defcon); goto out; } @@ -292,11 +275,11 @@ int mls_context_to_sid(char oldc, p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; for (l = 0; l < 2; l++) { - levdatum = hashtab_search(policydb.p_levels.table, scontextp); + levdatum = hashtab_search(pol->p_levels.table, scontextp); if (!levdatum) { rc = -EINVAL; goto out; @@ -311,24 +294,25 @@ int mls_context_to_sid(char oldc, while (*p && *p != ',' && *p != '-') p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; /* Separate into range if exists */ - if ((rngptr = strchr(scontextp, '.')) != NULL) { + rngptr = strchr(scontextp, '.'); + if (rngptr != NULL) { /* Remove '.' */ - *rngptr++ = 0; + *rngptr++ = '\0'; } - catdatum = hashtab_search(policydb.p_cats.table, - scontextp); + catdatum = hashtab_search(pol->p_cats.table, + scontextp); if (!catdatum) { rc = -EINVAL; goto out; } rc = ebitmap_set_bit(&context->range.level[l].cat, - catdatum->value - 1, 1); + catdatum->value - 1, 1); if (rc) goto out; @@ -336,7 +320,7 @@ int mls_context_to_sid(char oldc, if (rngptr) { int i; - rngdatum = hashtab_search(policydb.p_cats.table, rngptr); + rngdatum = hashtab_search(pol->p_cats.table, rngptr); if (!rngdatum) { rc = -EINVAL; goto out; @@ -365,8 +349,8 @@ int mls_context_to_sid(char oldc, p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; } else break; } @@ -385,20 +369,28 @@ out: } /* - * Copies the effective MLS range from `src' into `dst'. + * Set the MLS fields in the security context structure + * `context' based on the string representation in + * the string `str'. This function will allocate temporary memory with the + * given constraints of gfp_mask. */ -static inline int mls_scopy_context(struct context *dst, - struct context *src) +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) { - int l, rc = 0; + char *tmpstr, *freestr; + int rc; - /* Copy the MLS range from the source context */ - for (l = 0; l < 2; l++) { - dst->range.level[l].sens = src->range.level[0].sens; - rc = ebitmap_cpy(&dst->range.level[l].cat, - &src->range.level[0].cat); - if (rc) - break; + if (!policydb.mls_enabled) + return -EINVAL; + + /* we need freestr because mls_context_to_sid will change + the value of tmpstr */ + tmpstr = freestr = kstrdup(str, gfp_mask); + if (!tmpstr) { + rc = -ENOMEM; + } else { + rc = mls_context_to_sid(&policydb, ':', &tmpstr, context, + NULL, SECSID_NULL); + kfree(freestr); } return rc; @@ -407,8 +399,8 @@ static inline int mls_scopy_context(struct context *dst, /* * Copies the MLS range `range' into `context'. */ -static inline int mls_range_set(struct context *context, - struct mls_range *range) +int mls_range_set(struct context *context, + struct mls_range *range) { int l, rc = 0; @@ -425,9 +417,9 @@ static inline int mls_range_set(struct context *context, } int mls_setup_user_range(struct context *fromcon, struct user_datum *user, - struct context *usercon) + struct context *usercon) { - if (selinux_mls_enabled) { + if (policydb.mls_enabled) { struct mls_level *fromcon_sen = &(fromcon->range.level[0]); struct mls_level *fromcon_clr = &(fromcon->range.level[1]); struct mls_level *user_low = &(user->range.level[0]); @@ -437,13 +429,13 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct mls_level *usercon_clr = &(usercon->range.level[1]); /* Honor the user's default level if we can */ - if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { + if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) *usercon_sen = *user_def; - } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { + else if (mls_level_between(fromcon_sen, user_def, user_clr)) *usercon_sen = *fromcon_sen; - } else if (mls_level_between(fromcon_clr, user_low, user_def)) { + else if (mls_level_between(fromcon_clr, user_low, user_def)) *usercon_sen = *user_low; - } else + else return -EINVAL; /* Lower the clearance of available contexts @@ -451,11 +443,11 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user, that of the user's default clearance (but only if the "fromcon" clearance dominates the user's computed sensitivity level) */ - if (mls_level_dom(user_clr, fromcon_clr)) { + if (mls_level_dom(user_clr, fromcon_clr)) *usercon_clr = *fromcon_clr; - } else if (mls_level_dom(fromcon_clr, user_clr)) { + else if (mls_level_dom(fromcon_clr, user_clr)) *usercon_clr = *user_clr; - } else + else return -EINVAL; } @@ -477,30 +469,31 @@ int mls_convert_context(struct policydb *oldp, struct ebitmap_node *node; int l, i; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; for (l = 0; l < 2; l++) { levdatum = hashtab_search(newp->p_levels.table, - oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); + sym_name(oldp, SYM_LEVELS, + c->range.level[l].sens - 1)); if (!levdatum) return -EINVAL; c->range.level[l].sens = levdatum->level->sens; ebitmap_init(&bitmap); - ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - int rc; - - catdatum = hashtab_search(newp->p_cats.table, - oldp->p_cat_val_to_name[i]); - if (!catdatum) - return -EINVAL; - rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); - if (rc) - return rc; - } + ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { + int rc; + + catdatum = hashtab_search(newp->p_cats.table, + sym_name(oldp, SYM_CATS, i)); + if (!catdatum) + return -EINVAL; + rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); + if (rc) + return rc; + + cond_resched(); } ebitmap_destroy(&c->range.level[l].cat); c->range.level[l].cat = bitmap; @@ -513,47 +506,167 @@ int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, - struct context *newcontext) + struct context *newcontext, + bool sock) { - if (!selinux_mls_enabled) + struct range_trans rtr; + struct mls_range *r; + struct class_datum *cladatum; + int default_range = 0; + + if (!policydb.mls_enabled) return 0; switch (specified) { case AVTAB_TRANSITION: - if (tclass == SECCLASS_PROCESS) { - struct range_trans *rangetr; - /* Look for a range transition rule. */ - for (rangetr = policydb.range_tr; rangetr; - rangetr = rangetr->next) { - if (rangetr->dom == scontext->type && - rangetr->type == tcontext->type) { - /* Set the range from the rule */ - return mls_range_set(newcontext, - &rangetr->range); - } - } + /* Look for a range transition rule. */ + rtr.source_type = scontext->type; + rtr.target_type = tcontext->type; + rtr.target_class = tclass; + r = hashtab_search(policydb.range_tr, &rtr); + if (r) + return mls_range_set(newcontext, r); + + if (tclass && tclass <= policydb.p_classes.nprim) { + cladatum = policydb.class_val_to_struct[tclass - 1]; + if (cladatum) + default_range = cladatum->default_range; } + + switch (default_range) { + case DEFAULT_SOURCE_LOW: + return mls_context_cpy_low(newcontext, scontext); + case DEFAULT_SOURCE_HIGH: + return mls_context_cpy_high(newcontext, scontext); + case DEFAULT_SOURCE_LOW_HIGH: + return mls_context_cpy(newcontext, scontext); + case DEFAULT_TARGET_LOW: + return mls_context_cpy_low(newcontext, tcontext); + case DEFAULT_TARGET_HIGH: + return mls_context_cpy_high(newcontext, tcontext); + case DEFAULT_TARGET_LOW_HIGH: + return mls_context_cpy(newcontext, tcontext); + } + /* Fallthrough */ case AVTAB_CHANGE: - if (tclass == SECCLASS_PROCESS) + if ((tclass == policydb.process_class) || (sock == true)) /* Use the process MLS attributes. */ - return mls_copy_context(newcontext, scontext); + return mls_context_cpy(newcontext, scontext); else /* Use the process effective MLS attributes. */ - return mls_scopy_context(newcontext, scontext); + return mls_context_cpy_low(newcontext, scontext); case AVTAB_MEMBER: - /* Only polyinstantiate the MLS attributes if - the type is being polyinstantiated */ - if (newcontext->type != tcontext->type) { - /* Use the process effective MLS attributes. */ - return mls_scopy_context(newcontext, scontext); - } else { - /* Use the related object MLS attributes. */ - return mls_copy_context(newcontext, tcontext); - } - default: - return -EINVAL; + /* Use the process effective MLS attributes. */ + return mls_context_cpy_low(newcontext, scontext); + + /* fall through */ } return -EINVAL; } +#ifdef CONFIG_NETLABEL +/** + * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context copy the low MLS sensitivity level into the + * NetLabel MLS sensitivity level field. + * + */ +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + if (!policydb.mls_enabled) + return; + + secattr->attr.mls.lvl = context->range.level[0].sens - 1; + secattr->flags |= NETLBL_SECATTR_MLS_LVL; +} + +/** + * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context and the NetLabel security attributes, copy the + * NetLabel MLS sensitivity level into the context. + * + */ +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + if (!policydb.mls_enabled) + return; + + context->range.level[0].sens = secattr->attr.mls.lvl + 1; + context->range.level[1].sens = context->range.level[0].sens; +} + +/** + * mls_export_netlbl_cat - Export the MLS categories to NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context copy the low MLS categories into the NetLabel + * MLS category field. Returns zero on success, negative values on failure. + * + */ +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + int rc; + + if (!policydb.mls_enabled) + return 0; + + rc = ebitmap_netlbl_export(&context->range.level[0].cat, + &secattr->attr.mls.cat); + if (rc == 0 && secattr->attr.mls.cat != NULL) + secattr->flags |= NETLBL_SECATTR_MLS_CAT; + + return rc; +} + +/** + * mls_import_netlbl_cat - Import the MLS categories from NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Copy the NetLabel security attributes into the SELinux context; since the + * NetLabel security attribute only contains a single MLS category use it for + * both the low and high categories of the context. Returns zero on success, + * negative values on failure. + * + */ +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + int rc; + + if (!policydb.mls_enabled) + return 0; + + rc = ebitmap_netlbl_import(&context->range.level[0].cat, + secattr->attr.mls.cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + rc = ebitmap_cpy(&context->range.level[1].cat, + &context->range.level[0].cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + return 0; + +import_netlbl_cat_failure: + ebitmap_destroy(&context->range.level[0].cat); + ebitmap_destroy(&context->range.level[1].cat); + return rc; +} +#endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 03de697c805..e4369e3e636 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -8,7 +8,14 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the MLS label from NetLabel + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ #ifndef _SS_MLS_H_ @@ -20,13 +27,20 @@ int mls_compute_context_len(struct context *context); void mls_sid_to_context(struct context *context, char **scontext); int mls_context_isvalid(struct policydb *p, struct context *c); +int mls_range_isvalid(struct policydb *p, struct mls_range *r); +int mls_level_isvalid(struct policydb *p, struct mls_level *l); -int mls_context_to_sid(char oldc, - char **scontext, +int mls_context_to_sid(struct policydb *p, + char oldc, + char **scontext, struct context *context, struct sidtab *s, u32 def_sid); +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask); + +int mls_range_set(struct context *context, struct mls_range *range); + int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *context); @@ -35,10 +49,43 @@ int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, - struct context *newcontext); + struct context *newcontext, + bool sock); int mls_setup_user_range(struct context *fromcon, struct user_datum *user, - struct context *usercon); + struct context *usercon); + +#ifdef CONFIG_NETLABEL +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +#else +static inline void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +static inline int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +#endif #endif /* _SS_MLS_H */ diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 0c692d58d48..e9364877413 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -15,6 +15,7 @@ #define _SS_MLS_TYPES_H_ #include "security.h" +#include "ebitmap.h" struct mls_level { u32 sens; /* sensitivity */ @@ -27,20 +28,14 @@ struct mls_range { static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens == l2->sens) && - ebitmap_cmp(&l1->cat, &l2->cat)); + ebitmap_cmp(&l1->cat, &l2->cat)); } static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens >= l2->sens) && - ebitmap_contains(&l1->cat, &l2->cat)); + ebitmap_contains(&l1->cat, &l2->cat, 0)); } #define mls_level_incomp(l1, l2) \ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 0111990ba83..9c5cdc2caae 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -11,29 +11,38 @@ * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> +#include <linux/flex_array.h> #include "security.h" #include "policydb.h" #include "conditional.h" #include "mls.h" +#include "services.h" #define _DEBUG_HASHES #ifdef DEBUG_HASHES -static char *symtab_name[SYM_NUM] = { +static const char *symtab_name[SYM_NUM] = { "common prefixes", "classes", "roles", @@ -45,8 +54,6 @@ static char *symtab_name[SYM_NUM] = { }; #endif -int selinux_mls_enabled = 0; - static unsigned int symtab_sizes[SYM_NUM] = { 2, 32, @@ -67,34 +74,79 @@ struct policydb_compat_info { /* These need to be updated if SYM_NUM or OCON_NUM changes */ static struct policydb_compat_info policydb_compat[] = { { - .version = POLICYDB_VERSION_BASE, - .sym_num = SYM_NUM - 3, - .ocon_num = OCON_NUM - 1, + .version = POLICYDB_VERSION_BASE, + .sym_num = SYM_NUM - 3, + .ocon_num = OCON_NUM - 1, + }, + { + .version = POLICYDB_VERSION_BOOL, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM - 1, + }, + { + .version = POLICYDB_VERSION_IPV6, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_NLCLASS, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_BOOL, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM - 1, + .version = POLICYDB_VERSION_MLS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_IPV6, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_AVTAB, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_NLCLASS, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_RANGETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_MLS, - .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_POLCAP, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_AVTAB, - .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_PERMISSIVE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_FILENAME_TRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_ROLETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_DEFAULT_TYPE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_CONSTRAINT_NAMES, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, }; @@ -121,33 +173,92 @@ static int roles_init(struct policydb *p) int rc; struct role_datum *role; + rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); - if (!role) { - rc = -ENOMEM; + if (!role) goto out; - } + + rc = -EINVAL; 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); + if (role->value != OBJECT_R_VAL) + goto out; + + rc = -ENOMEM; + key = kstrdup(OBJECT_R, GFP_KERNEL); + if (!key) + goto out; + rc = hashtab_insert(p->p_roles.table, key, role); if (rc) - goto out_free_key; -out: - return rc; + goto out; -out_free_key: + return 0; +out: kfree(key); -out_free_role: kfree(role); - goto out; + return rc; +} + +static u32 filenametr_hash(struct hashtab *h, const void *k) +{ + const struct filename_trans *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->stype ^ ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash & (h->size - 1); +} + +static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct filename_trans *ft1 = k1; + const struct filename_trans *ft2 = k2; + int v; + + v = ft1->stype - ft2->stype; + if (v) + return v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); + +} + +static u32 rangetr_hash(struct hashtab *h, const void *k) +{ + const struct range_trans *key = k; + return (key->source_type + (key->target_type << 3) + + (key->target_class << 5)) & (h->size - 1); +} + +static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct range_trans *key1 = k1, *key2 = k2; + int v; + + v = key1->source_type - key2->source_type; + if (v) + return v; + + v = key1->target_type - key2->target_type; + if (v) + return v; + + v = key1->target_class - key2->target_class; + + return v; } /* @@ -162,31 +273,40 @@ static int policydb_init(struct policydb *p) for (i = 0; i < SYM_NUM; i++) { rc = symtab_init(&p->symtab[i], symtab_sizes[i]); if (rc) - goto out_free_symtab; + goto out; } rc = avtab_init(&p->te_avtab); if (rc) - goto out_free_symtab; + goto out; rc = roles_init(p); if (rc) - goto out_free_avtab; + goto out; rc = cond_policydb_init(p); if (rc) - goto out_free_avtab; + goto out; -out: - return rc; + p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); + if (!p->filename_trans) + goto out; -out_free_avtab: - avtab_destroy(&p->te_avtab); + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); + if (!p->range_tr) + goto out; -out_free_symtab: + ebitmap_init(&p->filename_trans_ttypes); + ebitmap_init(&p->policycaps); + ebitmap_init(&p->permissive_map); + + return 0; +out: + hashtab_destroy(p->filename_trans); + hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) hashtab_destroy(p->symtab[i].table); - goto out; + return rc; } /* @@ -203,12 +323,17 @@ static int common_index(void *key, void *datum, void *datap) { struct policydb *p; struct common_datum *comdatum; + struct flex_array *fa; comdatum = datum; p = datap; if (!comdatum->value || comdatum->value > p->p_commons.nprim) return -EINVAL; - p->p_common_val_to_name[comdatum->value - 1] = key; + + fa = p->sym_val_to_name[SYM_COMMONS]; + if (flex_array_put_ptr(fa, comdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); return 0; } @@ -216,12 +341,16 @@ static int class_index(void *key, void *datum, void *datap) { struct policydb *p; struct class_datum *cladatum; + struct flex_array *fa; cladatum = datum; p = datap; if (!cladatum->value || cladatum->value > p->p_classes.nprim) return -EINVAL; - p->p_class_val_to_name[cladatum->value - 1] = key; + fa = p->sym_val_to_name[SYM_CLASSES]; + if (flex_array_put_ptr(fa, cladatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->class_val_to_struct[cladatum->value - 1] = cladatum; return 0; } @@ -230,12 +359,19 @@ static int role_index(void *key, void *datum, void *datap) { struct policydb *p; struct role_datum *role; + struct flex_array *fa; role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; - p->p_role_val_to_name[role->value - 1] = key; + + fa = p->sym_val_to_name[SYM_ROLES]; + if (flex_array_put_ptr(fa, role->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->role_val_to_struct[role->value - 1] = role; return 0; } @@ -244,14 +380,25 @@ static int type_index(void *key, void *datum, void *datap) { struct policydb *p; struct type_datum *typdatum; + struct flex_array *fa; typdatum = datum; p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; - p->p_type_val_to_name[typdatum->value - 1] = key; + fa = p->sym_val_to_name[SYM_TYPES]; + if (flex_array_put_ptr(fa, typdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); + + fa = p->type_val_to_struct_array; + if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -261,12 +408,19 @@ static int user_index(void *key, void *datum, void *datap) { struct policydb *p; struct user_datum *usrdatum; + struct flex_array *fa; usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; - p->p_user_val_to_name[usrdatum->value - 1] = key; + + fa = p->sym_val_to_name[SYM_USERS]; + if (flex_array_put_ptr(fa, usrdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->user_val_to_struct[usrdatum->value - 1] = usrdatum; return 0; } @@ -275,6 +429,7 @@ static int sens_index(void *key, void *datum, void *datap) { struct policydb *p; struct level_datum *levdatum; + struct flex_array *fa; levdatum = datum; p = datap; @@ -283,7 +438,10 @@ static int sens_index(void *key, void *datum, void *datap) if (!levdatum->level->sens || levdatum->level->sens > p->p_levels.nprim) return -EINVAL; - p->p_sens_val_to_name[levdatum->level->sens - 1] = key; + fa = p->sym_val_to_name[SYM_LEVELS]; + if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -293,6 +451,7 @@ static int cat_index(void *key, void *datum, void *datap) { struct policydb *p; struct cat_datum *catdatum; + struct flex_array *fa; catdatum = datum; p = datap; @@ -300,7 +459,10 @@ static int cat_index(void *key, void *datum, void *datap) if (!catdatum->isalias) { if (!catdatum->value || catdatum->value > p->p_cats.nprim) return -EINVAL; - p->p_cat_val_to_name[catdatum->value - 1] = key; + fa = p->sym_val_to_name[SYM_CATS]; + if (flex_array_put_ptr(fa, catdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -318,61 +480,28 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_index, }; -/* - * 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. - */ -static int policydb_index_classes(struct policydb *p) +#ifdef DEBUG_HASHES +static void hash_eval(struct hashtab *h, const char *hash_name) { - int rc; + struct hashtab_info info; - 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; + hashtab_stat(h, &info); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d\n", hash_name, h->nel, + info.slots_used, h->size, info.max_chain_len); } -#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; + for (i = 0; i < SYM_NUM; i++) + hash_eval(s[i].table, symtab_name[i]); +} - 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); - } +#else +static inline void hash_eval(struct hashtab *h, char *hash_name) +{ } #endif @@ -382,18 +511,18 @@ static void symtab_hash_eval(struct symtab *s) * * Caller must clean up on failure. */ -static int policydb_index_others(struct policydb *p) +static int policydb_index(struct policydb *p) { - int i, rc = 0; + int i, rc; - printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools", + printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools", p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); - if (selinux_mls_enabled) + if (p->mls_enabled) printk(", %d sens, %d cats", p->p_levels.nprim, p->p_cats.nprim); printk("\n"); - printk(KERN_INFO "security: %d classes, %d rules\n", + printk(KERN_DEBUG "SELinux: %d classes, %d rules\n", p->p_classes.nprim, p->te_avtab.nel); #ifdef DEBUG_HASHES @@ -401,39 +530,63 @@ static int policydb_index_others(struct policydb *p) symtab_hash_eval(p->symtab); #endif + rc = -ENOMEM; + p->class_val_to_struct = + kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), + GFP_KERNEL); + if (!p->class_val_to_struct) + goto out; + + rc = -ENOMEM; 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; + GFP_KERNEL); + if (!p->role_val_to_struct) goto out; - } + rc = -ENOMEM; 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; + GFP_KERNEL); + if (!p->user_val_to_struct) goto out; - } - if (cond_init_bool_indexes(p)) { - rc = -ENOMEM; + /* Yes, I want the sizeof the pointer, not the structure */ + rc = -ENOMEM; + p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_val_to_struct_array) 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; + rc = flex_array_prealloc(p->type_val_to_struct_array, 0, + p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); + if (rc) + goto out; + + rc = cond_init_bool_indexes(p); + if (rc) + goto out; + + for (i = 0; i < SYM_NUM; i++) { + rc = -ENOMEM; + p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *), + p->symtab[i].nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->sym_val_to_name[i]) goto out; - } + + rc = flex_array_prealloc(p->sym_val_to_name[i], + 0, p->symtab[i].nprim, + GFP_KERNEL | __GFP_ZERO); + if (rc) + goto out; + rc = hashtab_map(p->symtab[i].table, index_f[i], p); if (rc) goto out; } - + rc = 0; out: return rc; } @@ -456,52 +609,66 @@ 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, NULL); - hashtab_destroy(comdatum->permissions.table); + if (datum) { + comdatum = datum; + hashtab_map(comdatum->permissions.table, perm_destroy, NULL); + hashtab_destroy(comdatum->permissions.table); + } kfree(datum); return 0; } -static int class_destroy(void *key, void *datum, void *p) +static void constraint_expr_destroy(struct constraint_expr *expr) +{ + if (expr) { + ebitmap_destroy(&expr->names); + if (expr->type_names) { + ebitmap_destroy(&expr->type_names->types); + ebitmap_destroy(&expr->type_names->negset); + kfree(expr->type_names); + } + kfree(expr); + } +} + +static int cls_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, NULL); - 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); + if (datum) { + cladatum = datum; + hashtab_map(cladatum->permissions.table, perm_destroy, NULL); + hashtab_destroy(cladatum->permissions.table); + constraint = cladatum->constraints; + while (constraint) { + e = constraint->expr; + while (e) { + etmp = e; + e = e->next; + constraint_expr_destroy(etmp); + } + ctemp = constraint; + constraint = constraint->next; + kfree(ctemp); } - ctemp = constraint; - constraint = constraint->next; - kfree(ctemp); - } - - constraint = cladatum->validatetrans; - while (constraint) { - e = constraint->expr; - while (e) { - ebitmap_destroy(&e->names); - etmp = e; - e = e->next; - kfree(etmp); + + constraint = cladatum->validatetrans; + while (constraint) { + e = constraint->expr; + while (e) { + etmp = e; + e = e->next; + constraint_expr_destroy(etmp); + } + ctemp = constraint; + constraint = constraint->next; + kfree(ctemp); } - ctemp = constraint; - constraint = constraint->next; - kfree(ctemp); + kfree(cladatum->comkey); } - - kfree(cladatum->comkey); kfree(datum); return 0; } @@ -511,9 +678,11 @@ 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); + if (datum) { + role = datum; + ebitmap_destroy(&role->dominates); + ebitmap_destroy(&role->types); + } kfree(datum); return 0; } @@ -530,11 +699,13 @@ static int user_destroy(void *key, void *datum, void *p) struct user_datum *usrdatum; kfree(key); - usrdatum = datum; - ebitmap_destroy(&usrdatum->roles); - ebitmap_destroy(&usrdatum->range.level[0].cat); - ebitmap_destroy(&usrdatum->range.level[1].cat); - ebitmap_destroy(&usrdatum->dfltlevel.cat); + if (datum) { + usrdatum = datum; + ebitmap_destroy(&usrdatum->roles); + ebitmap_destroy(&usrdatum->range.level[0].cat); + ebitmap_destroy(&usrdatum->range.level[1].cat); + ebitmap_destroy(&usrdatum->dfltlevel.cat); + } kfree(datum); return 0; } @@ -544,9 +715,11 @@ static int sens_destroy(void *key, void *datum, void *p) struct level_datum *levdatum; kfree(key); - levdatum = datum; - ebitmap_destroy(&levdatum->level->cat); - kfree(levdatum->level); + if (datum) { + levdatum = datum; + ebitmap_destroy(&levdatum->level->cat); + kfree(levdatum->level); + } kfree(datum); return 0; } @@ -561,7 +734,7 @@ static int cat_destroy(void *key, void *datum, void *p) static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = { common_destroy, - class_destroy, + cls_destroy, role_destroy, type_destroy, user_destroy, @@ -570,8 +743,32 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_destroy, }; +static int filenametr_destroy(void *key, void *datum, void *p) +{ + struct filename_trans *ft = key; + kfree(ft->name); + kfree(key); + kfree(datum); + cond_resched(); + return 0; +} + +static int range_tr_destroy(void *key, void *datum, void *p) +{ + struct mls_range *rt = datum; + kfree(key); + ebitmap_destroy(&rt->level[0].cat); + ebitmap_destroy(&rt->level[1].cat); + kfree(datum); + cond_resched(); + return 0; +} + static void ocontext_destroy(struct ocontext *c, int i) { + if (!c) + return; + context_destroy(&c->context[0]); context_destroy(&c->context[1]); if (i == OCON_ISID || i == OCON_FS || @@ -590,70 +787,90 @@ void policydb_destroy(struct policydb *p) int i; struct role_allow *ra, *lra = NULL; struct role_trans *tr, *ltr = NULL; - struct range_trans *rt, *lrt = NULL; for (i = 0; i < SYM_NUM; i++) { + cond_resched(); hashtab_map(p->symtab[i].table, destroy_f[i], NULL); hashtab_destroy(p->symtab[i].table); } - for (i = 0; i < SYM_NUM; i++) - kfree(p->sym_val_to_name[i]); + for (i = 0; i < SYM_NUM; i++) { + if (p->sym_val_to_name[i]) + flex_array_free(p->sym_val_to_name[i]); + } kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + if (p->type_val_to_struct_array) + flex_array_free(p->type_val_to_struct_array); avtab_destroy(&p->te_avtab); for (i = 0; i < OCON_NUM; i++) { + cond_resched(); c = p->ocontexts[i]; while (c) { ctmp = c; c = c->next; - ocontext_destroy(ctmp,i); + ocontext_destroy(ctmp, i); } + p->ocontexts[i] = NULL; } g = p->genfs; while (g) { + cond_resched(); kfree(g->fstype); c = g->head; while (c) { ctmp = c; c = c->next; - ocontext_destroy(ctmp,OCON_FSUSE); + ocontext_destroy(ctmp, OCON_FSUSE); } gtmp = g; g = g->next; kfree(gtmp); } + p->genfs = NULL; cond_policydb_destroy(p); for (tr = p->role_tr; tr; tr = tr->next) { + cond_resched(); kfree(ltr); ltr = tr; } kfree(ltr); - for (ra = p->role_allow; ra; ra = ra -> next) { + for (ra = p->role_allow; ra; ra = ra->next) { + cond_resched(); kfree(lra); lra = ra; } kfree(lra); - for (rt = p->range_tr; rt; rt = rt -> next) { - kfree(lrt); - lrt = rt; - } - kfree(lrt); + hashtab_map(p->filename_trans, filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans); + + hashtab_map(p->range_tr, range_tr_destroy, NULL); + hashtab_destroy(p->range_tr); - if (p->type_attr_map) { - for (i = 0; i < p->p_types.nprim; i++) - ebitmap_destroy(&p->type_attr_map[i]); + if (p->type_attr_map_array) { + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e; + + e = flex_array_get(p->type_attr_map_array, i); + if (!e) + continue; + ebitmap_destroy(e); + } + flex_array_free(p->type_attr_map_array); } - kfree(p->type_attr_map); + + ebitmap_destroy(&p->filename_trans_ttypes); + ebitmap_destroy(&p->policycaps); + ebitmap_destroy(&p->permissive_map); return; } @@ -669,29 +886,52 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s) rc = sidtab_init(s); if (rc) { - printk(KERN_ERR "security: out of memory on SID table init\n"); + printk(KERN_ERR "SELinux: out of memory on SID table init\n"); goto out; } head = p->ocontexts[OCON_ISID]; for (c = head; c; c = c->next) { + rc = -EINVAL; if (!c->context[0].user) { - printk(KERN_ERR "security: SID %s was never " - "defined.\n", c->u.name); - rc = -EINVAL; + printk(KERN_ERR "SELinux: SID %s was never defined.\n", + c->u.name); 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; + + rc = sidtab_insert(s, c->sid[0], &c->context[0]); + if (rc) { + printk(KERN_ERR "SELinux: unable to load initial SID %s.\n", + c->u.name); goto out; } } + rc = 0; out: return rc; } +int policydb_class_isvalid(struct policydb *p, unsigned int class) +{ + if (!class || class > p->p_classes.nprim) + return 0; + return 1; +} + +int policydb_role_isvalid(struct policydb *p, unsigned int role) +{ + if (!role || role > p->p_roles.nprim) + return 0; + return 1; +} + +int policydb_type_isvalid(struct policydb *p, unsigned int type) +{ + if (!type || type > p->p_types.nprim) + return 0; + return 1; +} + /* * Return 1 if the fields in the security context * structure `c' are valid. Return 0 otherwise. @@ -715,8 +955,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) * Role must be authorized for the type. */ role = p->role_val_to_struct[c->role - 1]; - if (!ebitmap_get_bit(&role->types, - c->type - 1)) + if (!ebitmap_get_bit(&role->types, c->type - 1)) /* role may not be associated with type */ return 0; @@ -727,8 +966,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) if (!usrdatum) return 0; - if (!ebitmap_get_bit(&usrdatum->roles, - c->role - 1)) + if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1)) /* user may not be associated with role */ return 0; } @@ -750,20 +988,22 @@ static int mls_read_range_helper(struct mls_range *r, void *fp) int rc; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto out; + rc = -EINVAL; items = le32_to_cpu(buf[0]); if (items > ARRAY_SIZE(buf)) { - printk(KERN_ERR "security: mls: range overflow\n"); - rc = -EINVAL; + printk(KERN_ERR "SELinux: mls: range overflow\n"); goto out; } + rc = next_entry(buf, fp, sizeof(u32) * items); - if (rc < 0) { - printk(KERN_ERR "security: mls: truncated range\n"); + if (rc) { + printk(KERN_ERR "SELinux: 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]); @@ -772,31 +1012,28 @@ static int mls_read_range_helper(struct mls_range *r, void *fp) rc = ebitmap_read(&r->level[0].cat, fp); if (rc) { - printk(KERN_ERR "security: mls: error reading low " - "categories\n"); + printk(KERN_ERR "SELinux: 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"); + printk(KERN_ERR "SELinux: 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"); + printk(KERN_ERR "SELinux: mls: out of memory\n"); goto bad_high; } } - rc = 0; -out: - return rc; + return 0; bad_high: ebitmap_destroy(&r->level[0].cat); - goto out; +out: + return rc; } /* @@ -811,27 +1048,28 @@ static int context_read_and_validate(struct context *c, int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) { - printk(KERN_ERR "security: context truncated\n"); + if (rc) { + printk(KERN_ERR "SELinux: context truncated\n"); goto out; } c->user = le32_to_cpu(buf[0]); c->role = le32_to_cpu(buf[1]); c->type = le32_to_cpu(buf[2]); if (p->policyvers >= POLICYDB_VERSION_MLS) { - if (mls_read_range_helper(&c->range, fp)) { - printk(KERN_ERR "security: error reading MLS range of " - "context\n"); - rc = -EINVAL; + rc = mls_read_range_helper(&c->range, fp); + if (rc) { + printk(KERN_ERR "SELinux: error reading MLS range of context\n"); goto out; } } + rc = -EINVAL; if (!policydb_context_isvalid(p, c)) { - printk(KERN_ERR "security: invalid security context\n"); + printk(KERN_ERR "SELinux: invalid security context\n"); context_destroy(c); - rc = -EINVAL; + goto out; } + rc = 0; out: return rc; } @@ -850,37 +1088,36 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; + rc = -ENOMEM; perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL); - if (!perdatum) { - rc = -ENOMEM; - goto out; - } + if (!perdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); perdatum->value = le32_to_cpu(buf[1]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, perdatum); if (rc) goto bad; -out: - return rc; + + return 0; bad: perm_destroy(key, perdatum, NULL); - goto out; + return rc; } static int common_read(struct policydb *p, struct hashtab *h, void *fp) @@ -891,14 +1128,13 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, nel; int i, rc; + rc = -ENOMEM; comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL); - if (!comdatum) { - rc = -ENOMEM; - goto out; - } + if (!comdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); @@ -910,15 +1146,15 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) comdatum->permissions.nprim = le32_to_cpu(buf[2]); nel = le32_to_cpu(buf[3]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; for (i = 0; i < nel; i++) { rc = perm_read(p, comdatum->permissions.table, fp); @@ -929,15 +1165,40 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, comdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: common_destroy(key, comdatum, NULL); - goto out; + return rc; } -static int read_cons_helper(struct constraint_node **nodep, int ncons, - int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ + ebitmap_init(&t->types); + ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ + __le32 buf[1]; + int rc; + + if (ebitmap_read(&t->types, fp)) + return -EINVAL; + if (ebitmap_read(&t->negset, fp)) + return -EINVAL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + return -EINVAL; + t->flags = le32_to_cpu(buf[0]); + + return 0; +} + + +static int read_cons_helper(struct policydb *p, + struct constraint_node **nodep, + int ncons, int allowxtarget, void *fp) { struct constraint_node *c, *lc; struct constraint_expr *e, *le; @@ -951,14 +1212,13 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (!c) return -ENOMEM; - if (lc) { + if (lc) lc->next = c; - } else { + else *nodep = c; - } rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) + if (rc) return rc; c->permissions = le32_to_cpu(buf[0]); nexpr = le32_to_cpu(buf[1]); @@ -969,14 +1229,13 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (!e) return -ENOMEM; - if (le) { + if (le) le->next = e; - } else { + else c->expr = e; - } rc = next_entry(buf, fp, (sizeof(u32) * 3)); - if (rc < 0) + if (rc) return rc; e->expr_type = le32_to_cpu(buf[0]); e->attr = le32_to_cpu(buf[1]); @@ -1004,8 +1263,21 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (depth == (CEXPR_MAXDEPTH - 1)) return -EINVAL; depth++; - if (ebitmap_read(&e->names, fp)) - return -EINVAL; + rc = ebitmap_read(&e->names, fp); + if (rc) + return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + e->type_names = kzalloc(sizeof + (*e->type_names), + GFP_KERNEL); + if (!e->type_names) + return -ENOMEM; + type_set_init(e->type_names); + rc = type_set_read(e->type_names, fp); + if (rc) + return rc; + } break; default: return -EINVAL; @@ -1028,14 +1300,13 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, len2, ncons, nel; int i, rc; + rc = -ENOMEM; cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL); - if (!cladatum) { - rc = -ENOMEM; - goto out; - } + if (!cladatum) + goto bad; rc = next_entry(buf, fp, sizeof(u32)*6); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); @@ -1050,33 +1321,30 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) ncons = le32_to_cpu(buf[5]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; if (len2) { - cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL); - if (!cladatum->comkey) { - rc = -ENOMEM; + rc = -ENOMEM; + cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL); + if (!cladatum->comkey) goto bad; - } rc = next_entry(cladatum->comkey, fp, len2); - if (rc < 0) + if (rc) goto bad; - cladatum->comkey[len2] = 0; + cladatum->comkey[len2] = '\0'; - cladatum->comdatum = hashtab_search(p->p_commons.table, - cladatum->comkey); + rc = -EINVAL; + 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; + printk(KERN_ERR "SELinux: unknown common %s\n", cladatum->comkey); goto bad; } } @@ -1086,63 +1354,83 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; } - rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) { /* grab the validatetrans rules */ rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; ncons = le32_to_cpu(buf[0]); - rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); + rc = read_cons_helper(p, &cladatum->validatetrans, + ncons, 1, fp); + if (rc) + goto bad; + } + + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + rc = next_entry(buf, fp, sizeof(u32) * 3); + if (rc) + goto bad; + + cladatum->default_user = le32_to_cpu(buf[0]); + cladatum->default_role = le32_to_cpu(buf[1]); + cladatum->default_range = le32_to_cpu(buf[2]); + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + rc = next_entry(buf, fp, sizeof(u32) * 1); if (rc) goto bad; + cladatum->default_type = le32_to_cpu(buf[0]); } rc = hashtab_insert(h, key, cladatum); if (rc) goto bad; - rc = 0; -out: - return rc; + return 0; bad: - class_destroy(key, cladatum, NULL); - goto out; + cls_destroy(key, cladatum, NULL); + return rc; } static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; + rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); - if (!role) { - rc = -ENOMEM; - goto out; - } + if (!role) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = ebitmap_read(&role->dominates, fp); if (rc) @@ -1153,10 +1441,10 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; if (strcmp(key, OBJECT_R) == 0) { + rc = -EINVAL; if (role->value != OBJECT_R_VAL) { - printk(KERN_ERR "Role %s has wrong value %d\n", + printk(KERN_ERR "SELinux: Role %s has wrong value %d\n", OBJECT_R, role->value); - rc = -EINVAL; goto bad; } rc = 0; @@ -1166,53 +1454,63 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, role); if (rc) goto bad; -out: - return rc; + return 0; bad: role_destroy(key, role, NULL); - goto out; + return rc; } static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; - typdatum = kzalloc(sizeof(*typdatum),GFP_KERNEL); - if (!typdatum) { - rc = -ENOMEM; - return rc; - } + rc = -ENOMEM; + typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); + if (!typdatum) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 prop = le32_to_cpu(buf[2]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; - goto bad; + if (prop & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->attribute = 1; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); } + + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) + goto bad; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, typdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: type_destroy(key, typdatum, NULL); - goto out; + return rc; } @@ -1228,53 +1526,53 @@ static int mls_read_level(struct mls_level *lp, void *fp) memset(lp, 0, sizeof(*lp)); rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) { - printk(KERN_ERR "security: mls: truncated level\n"); - goto bad; + if (rc) { + printk(KERN_ERR "SELinux: mls: truncated level\n"); + return rc; } lp->sens = le32_to_cpu(buf[0]); - if (ebitmap_read(&lp->cat, fp)) { - printk(KERN_ERR "security: mls: error reading level " - "categories\n"); - goto bad; + rc = ebitmap_read(&lp->cat, fp); + if (rc) { + printk(KERN_ERR "SELinux: mls: error reading level categories\n"); + return rc; } return 0; - -bad: - return -EINVAL; } static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; + rc = -ENOMEM; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); - if (!usrdatum) { - rc = -ENOMEM; - goto out; - } + if (!usrdatum) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = ebitmap_read(&usrdatum->roles, fp); if (rc) @@ -1292,11 +1590,10 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, usrdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: user_destroy(key, usrdatum, NULL); - goto out; + return rc; } static int sens_read(struct policydb *p, struct hashtab *h, void *fp) @@ -1307,47 +1604,43 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; + rc = -ENOMEM; levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC); - if (!levdatum) { - rc = -ENOMEM; - goto out; - } + if (!levdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); levdatum->isalias = le32_to_cpu(buf[1]); - key = kmalloc(len + 1,GFP_ATOMIC); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_ATOMIC); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; + rc = -ENOMEM; levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); - if (!levdatum->level) { - rc = -ENOMEM; + if (!levdatum->level) goto bad; - } - if (mls_read_level(levdatum->level, fp)) { - rc = -EINVAL; + + rc = mls_read_level(levdatum->level, fp); + if (rc) goto bad; - } rc = hashtab_insert(h, key, levdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: sens_destroy(key, levdatum, NULL); - goto out; + return rc; } static int cat_read(struct policydb *p, struct hashtab *h, void *fp) @@ -1358,39 +1651,35 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; + rc = -ENOMEM; catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC); - if (!catdatum) { - rc = -ENOMEM; - goto out; - } + if (!catdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); catdatum->value = le32_to_cpu(buf[1]); catdatum->isalias = le32_to_cpu(buf[2]); - key = kmalloc(len + 1,GFP_ATOMIC); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_ATOMIC); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, catdatum); if (rc) goto bad; -out: - return rc; - + return 0; bad: cat_destroy(key, catdatum, NULL); - goto out; + return rc; } static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) = @@ -1405,7 +1694,588 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; -extern int ss_initialized; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: user %s: " + "too deep or looped boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + sym_name(p, SYM_USERS, user->value - 1), + sym_name(p, SYM_ROLES, bit), + sym_name(p, SYM_USERS, upper->value - 1)); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: role %s: " + "too deep or looped bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + sym_name(p, SYM_ROLES, role->value - 1), + sym_name(p, SYM_TYPES, bit), + sym_name(p, SYM_ROLES, upper->value - 1)); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper; + struct policydb *p = datap; + int depth = 0; + + upper = datum; + while (upper->bounds) { + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = flex_array_get_ptr(p->type_val_to_struct_array, + upper->bounds - 1); + BUG_ON(!upper); + + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + sym_name(p, SYM_TYPES, upper->value - 1)); + return -EINVAL; + } + } + + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + +u16 string_to_security_class(struct policydb *p, const char *name) +{ + struct class_datum *cladatum; + + cladatum = hashtab_search(p->p_classes.table, name); + if (!cladatum) + return 0; + + return cladatum->value; +} + +u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) +{ + struct class_datum *cladatum; + struct perm_datum *perdatum = NULL; + struct common_datum *comdatum; + + if (!tclass || tclass > p->p_classes.nprim) + return 0; + + cladatum = p->class_val_to_struct[tclass-1]; + comdatum = cladatum->comdatum; + if (comdatum) + perdatum = hashtab_search(comdatum->permissions.table, + name); + if (!perdatum) + perdatum = hashtab_search(cladatum->permissions.table, + name); + if (!perdatum) + return 0; + + return 1U << (perdatum->value-1); +} + +static int range_read(struct policydb *p, void *fp) +{ + struct range_trans *rt = NULL; + struct mls_range *r = NULL; + int i, rc; + __le32 buf[2]; + u32 nel; + + if (p->policyvers < POLICYDB_VERSION_MLS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel = le32_to_cpu(buf[0]); + for (i = 0; i < nel; i++) { + rc = -ENOMEM; + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + goto out; + + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc) + goto out; + + rt->source_type = le32_to_cpu(buf[0]); + rt->target_type = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + rt->target_class = le32_to_cpu(buf[0]); + } else + rt->target_class = p->process_class; + + rc = -EINVAL; + if (!policydb_type_isvalid(p, rt->source_type) || + !policydb_type_isvalid(p, rt->target_type) || + !policydb_class_isvalid(p, rt->target_class)) + goto out; + + rc = -ENOMEM; + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + goto out; + + rc = mls_read_range_helper(r, fp); + if (rc) + goto out; + + rc = -EINVAL; + if (!mls_range_isvalid(p, r)) { + printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); + goto out; + } + + rc = hashtab_insert(p->range_tr, rt, r); + if (rc) + goto out; + + rt = NULL; + r = NULL; + } + hash_eval(p->range_tr, "rangetr"); + rc = 0; +out: + kfree(rt); + kfree(r); + return rc; +} + +static int filename_trans_read(struct policydb *p, void *fp) +{ + struct filename_trans *ft; + struct filename_trans_datum *otype; + char *name; + u32 nel, len; + __le32 buf[4]; + int rc, i; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + return rc; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + ft = NULL; + otype = NULL; + name = NULL; + + rc = -ENOMEM; + ft = kzalloc(sizeof(*ft), GFP_KERNEL); + if (!ft) + goto out; + + rc = -ENOMEM; + otype = kmalloc(sizeof(*otype), GFP_KERNEL); + if (!otype) + goto out; + + /* length of the path component string */ + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + goto out; + + ft->name = name; + + /* path component string */ + rc = next_entry(name, fp, len); + if (rc) + goto out; + name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32) * 4); + if (rc) + goto out; + + ft->stype = le32_to_cpu(buf[0]); + ft->ttype = le32_to_cpu(buf[1]); + ft->tclass = le32_to_cpu(buf[2]); + + otype->otype = le32_to_cpu(buf[3]); + + rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); + if (rc) + goto out; + + rc = hashtab_insert(p->filename_trans, ft, otype); + if (rc) { + /* + * Do not return -EEXIST to the caller, or the system + * will not boot. + */ + if (rc != -EEXIST) + goto out; + /* But free memory to avoid memory leak. */ + kfree(ft); + kfree(name); + kfree(otype); + } + } + hash_eval(p->filename_trans, "filenametr"); + return 0; +out: + kfree(ft); + kfree(name); + kfree(otype); + + return rc; +} + +static int genfs_read(struct policydb *p, void *fp) +{ + int i, j, rc; + u32 nel, nel2, len, len2; + __le32 buf[1]; + struct ocontext *l, *c; + struct ocontext *newc = NULL; + struct genfs *genfs_p, *genfs; + struct genfs *newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); + if (!newgenfs) + goto out; + + rc = -ENOMEM; + newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); + if (!newgenfs->fstype) + goto out; + + rc = next_entry(newgenfs->fstype, fp, len); + if (rc) + goto out; + + newgenfs->fstype[len] = 0; + + for (genfs_p = NULL, genfs = p->genfs; genfs; + genfs_p = genfs, genfs = genfs->next) { + rc = -EINVAL; + if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { + printk(KERN_ERR "SELinux: dup genfs fstype %s\n", + newgenfs->fstype); + goto out; + } + if (strcmp(newgenfs->fstype, genfs->fstype) < 0) + break; + } + newgenfs->next = genfs; + if (genfs_p) + genfs_p->next = newgenfs; + else + p->genfs = newgenfs; + genfs = newgenfs; + newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel2 = le32_to_cpu(buf[0]); + for (j = 0; j < nel2; j++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newc = kzalloc(sizeof(*newc), GFP_KERNEL); + if (!newc) + goto out; + + rc = -ENOMEM; + newc->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!newc->u.name) + goto out; + + rc = next_entry(newc->u.name, fp, len); + if (rc) + goto out; + newc->u.name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + newc->v.sclass = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&newc->context[0], p, fp); + if (rc) + goto out; + + for (l = NULL, c = genfs->head; c; + l = c, c = c->next) { + rc = -EINVAL; + if (!strcmp(newc->u.name, c->u.name) && + (!c->v.sclass || !newc->v.sclass || + newc->v.sclass == c->v.sclass)) { + printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", + genfs->fstype, c->u.name); + goto out; + } + len = strlen(newc->u.name); + len2 = strlen(c->u.name); + if (len > len2) + break; + } + + newc->next = c; + if (l) + l->next = newc; + else + genfs->head = newc; + newc = NULL; + } + } + rc = 0; +out: + if (newgenfs) + kfree(newgenfs->fstype); + kfree(newgenfs); + ocontext_destroy(newc, OCON_FSUSE); + + return rc; +} + +static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + int i, j, rc; + u32 nel, len; + __le32 buf[3]; + struct ocontext *l, *c; + u32 nodebuf[8]; + + for (i = 0; i < info->ocon_num; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + l = NULL; + for (j = 0; j < nel; j++) { + rc = -ENOMEM; + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + goto out; + if (l) + l->next = c; + else + p->ocontexts[i] = c; + l = c; + + switch (i) { + case OCON_ISID: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + c->sid[0] = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FS: + case OCON_NETIF: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + rc = context_read_and_validate(&c->context[1], p, fp); + if (rc) + goto out; + break; + case OCON_PORT: + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc) + goto out; + 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 out; + break; + case OCON_NODE: + rc = next_entry(nodebuf, fp, sizeof(u32) * 2); + if (rc) + goto out; + c->u.node.addr = nodebuf[0]; /* network order */ + c->u.node.mask = nodebuf[1]; /* network order */ + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FSUSE: + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc) + goto out; + + rc = -EINVAL; + c->v.behavior = le32_to_cpu(buf[0]); + /* Determined at runtime, not in policy DB. */ + if (c->v.behavior == SECURITY_FS_USE_MNTPOINT) + goto out; + if (c->v.behavior > SECURITY_FS_USE_MAX) + goto out; + + rc = -ENOMEM; + len = le32_to_cpu(buf[1]); + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE6: { + int k; + + rc = next_entry(nodebuf, fp, sizeof(u32) * 8); + if (rc) + goto out; + for (k = 0; k < 4; k++) + c->u.node6.addr[k] = nodebuf[k]; + for (k = 0; k < 4; k++) + c->u.node6.mask[k] = nodebuf[k+4]; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + } + } + } + } + rc = 0; +out: + return rc; +} /* * Read the configuration data from a policy database binary @@ -1415,56 +2285,58 @@ 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; - __le32 buf[8]; - u32 len, len2, config, nprim, nel, nel2; + __le32 buf[4]; + u32 len, nprim, nel; + char *policydb_str; struct policydb_compat_info *info; - struct range_trans *rt, *lrt; - - config = 0; rc = policydb_init(p); if (rc) - goto out; + return rc; /* Read the magic number and string length. */ - rc = next_entry(buf, fp, sizeof(u32)* 2); - if (rc < 0) + rc = next_entry(buf, fp, sizeof(u32) * 2); + if (rc) goto bad; + rc = -EINVAL; if (le32_to_cpu(buf[0]) != POLICYDB_MAGIC) { - printk(KERN_ERR "security: policydb magic number 0x%x does " + printk(KERN_ERR "SELinux: policydb magic number 0x%x does " "not match expected magic number 0x%x\n", le32_to_cpu(buf[0]), POLICYDB_MAGIC); goto bad; } + rc = -EINVAL; len = le32_to_cpu(buf[1]); if (len != strlen(POLICYDB_STRING)) { - printk(KERN_ERR "security: policydb string length %d does not " + printk(KERN_ERR "SELinux: policydb string length %d does not " "match expected length %Zu\n", len, strlen(POLICYDB_STRING)); goto bad; } - policydb_str = kmalloc(len + 1,GFP_KERNEL); + + rc = -ENOMEM; + policydb_str = kmalloc(len + 1, GFP_KERNEL); if (!policydb_str) { - printk(KERN_ERR "security: unable to allocate memory for policydb " + printk(KERN_ERR "SELinux: unable to allocate memory for policydb " "string of length %d\n", len); - rc = -ENOMEM; goto bad; } + rc = next_entry(policydb_str, fp, len); - if (rc < 0) { - printk(KERN_ERR "security: truncated policydb string identifier\n"); + if (rc) { + printk(KERN_ERR "SELinux: truncated policydb string identifier\n"); kfree(policydb_str); goto bad; } - policydb_str[len] = 0; + + rc = -EINVAL; + policydb_str[len] = '\0'; if (strcmp(policydb_str, POLICYDB_STRING)) { - printk(KERN_ERR "security: policydb string %s does not match " + printk(KERN_ERR "SELinux: policydb string %s does not match " "my string %s\n", policydb_str, POLICYDB_STRING); kfree(policydb_str); goto bad; @@ -1473,52 +2345,59 @@ int policydb_read(struct policydb *p, void *fp) kfree(policydb_str); policydb_str = NULL; - /* Read the version, config, and table sizes. */ + /* Read the version and table sizes. */ rc = next_entry(buf, fp, sizeof(u32)*4); - if (rc < 0) + if (rc) goto bad; + rc = -EINVAL; p->policyvers = le32_to_cpu(buf[0]); if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX) { - printk(KERN_ERR "security: policydb version %d does not match " - "my version range %d-%d\n", - le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); - goto bad; + printk(KERN_ERR "SELinux: policydb version %d does not match " + "my version range %d-%d\n", + le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); + goto bad; } if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) { - if (ss_initialized && !selinux_mls_enabled) { - printk(KERN_ERR "Cannot switch between non-MLS and MLS " - "policies\n"); - goto bad; - } - selinux_mls_enabled = 1; - config |= POLICYDB_CONFIG_MLS; + p->mls_enabled = 1; + rc = -EINVAL; if (p->policyvers < POLICYDB_VERSION_MLS) { - printk(KERN_ERR "security policydb version %d (MLS) " - "not backwards compatible\n", p->policyvers); + printk(KERN_ERR "SELinux: security policydb version %d " + "(MLS) not backwards compatible\n", + p->policyvers); goto bad; } - } else { - if (ss_initialized && selinux_mls_enabled) { - printk(KERN_ERR "Cannot switch between MLS and non-MLS " - "policies\n"); + } + p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN); + p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN); + + if (p->policyvers >= POLICYDB_VERSION_POLCAP) { + rc = ebitmap_read(&p->policycaps, fp); + if (rc) + goto bad; + } + + if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE) { + rc = ebitmap_read(&p->permissive_map, fp); + if (rc) goto bad; - } } + rc = -EINVAL; info = policydb_lookup_compat(p->policyvers); if (!info) { - printk(KERN_ERR "security: unable to find policy compat info " + printk(KERN_ERR "SELinux: unable to find policy compat info " "for version %d\n", p->policyvers); goto bad; } + rc = -EINVAL; if (le32_to_cpu(buf[2]) != info->sym_num || le32_to_cpu(buf[3]) != info->ocon_num) { - printk(KERN_ERR "security: policydb table sizes (%d,%d) do " + printk(KERN_ERR "SELinux: policydb table sizes (%d,%d) do " "not match mine (%d,%d)\n", le32_to_cpu(buf[2]), le32_to_cpu(buf[3]), info->sym_num, info->ocon_num); @@ -1527,7 +2406,7 @@ int policydb_read(struct policydb *p, void *fp) for (i = 0; i < info->sym_num; i++) { rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) + if (rc) goto bad; nprim = le32_to_cpu(buf[0]); nel = le32_to_cpu(buf[1]); @@ -1540,7 +2419,12 @@ int policydb_read(struct policydb *p, void *fp) p->symtab[i].nprim = nprim; } - rc = avtab_read(&p->te_avtab, fp, p->policyvers); + rc = -EINVAL; + p->process_class = string_to_security_class(p, "process"); + if (!p->process_class) + goto bad; + + rc = avtab_read(&p->te_avtab, fp, p); if (rc) goto bad; @@ -1551,326 +2435,1081 @@ int policydb_read(struct policydb *p, void *fp) } rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; nel = le32_to_cpu(buf[0]); ltr = NULL; for (i = 0; i < nel; i++) { + rc = -ENOMEM; tr = kzalloc(sizeof(*tr), GFP_KERNEL); - if (!tr) { - rc = -ENOMEM; + if (!tr) goto bad; - } - if (ltr) { + if (ltr) ltr->next = tr; - } else { + else p->role_tr = tr; - } rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) + if (rc) goto bad; + + rc = -EINVAL; tr->role = le32_to_cpu(buf[0]); tr->type = le32_to_cpu(buf[1]); tr->new_role = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto bad; + tr->tclass = le32_to_cpu(buf[0]); + } else + tr->tclass = p->process_class; + + if (!policydb_role_isvalid(p, tr->role) || + !policydb_type_isvalid(p, tr->type) || + !policydb_class_isvalid(p, tr->tclass) || + !policydb_role_isvalid(p, tr->new_role)) + goto bad; ltr = tr; } rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; nel = le32_to_cpu(buf[0]); lra = NULL; for (i = 0; i < nel; i++) { + rc = -ENOMEM; ra = kzalloc(sizeof(*ra), GFP_KERNEL); - if (!ra) { - rc = -ENOMEM; + if (!ra) goto bad; - } - if (lra) { + if (lra) lra->next = ra; - } else { + else p->role_allow = ra; - } rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) + if (rc) goto bad; + + rc = -EINVAL; ra->role = le32_to_cpu(buf[0]); ra->new_role = le32_to_cpu(buf[1]); + if (!policydb_role_isvalid(p, ra->role) || + !policydb_role_isvalid(p, ra->new_role)) + goto bad; lra = ra; } - rc = policydb_index_classes(p); + rc = filename_trans_read(p, fp); if (rc) goto bad; - rc = policydb_index_others(p); + rc = policydb_index(p); if (rc) goto bad; - for (i = 0; i < info->ocon_num; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - l = NULL; - for (j = 0; j < nel; j++) { - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - rc = -ENOMEM; + rc = -EINVAL; + p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition"); + p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition"); + if (!p->process_trans_perms) + goto bad; + + rc = ocontext_read(p, info, fp); + if (rc) + goto bad; + + rc = genfs_read(p, fp); + if (rc) + goto bad; + + rc = range_read(p, fp); + if (rc) + goto bad; + + rc = -ENOMEM; + p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_attr_map_array) + goto bad; + + /* preallocate so we don't have to worry about the put ever failing */ + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (rc) + goto bad; + + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + ebitmap_init(e); + if (p->policyvers >= POLICYDB_VERSION_AVTAB) { + rc = ebitmap_read(e, fp); + if (rc) goto bad; + } + /* add the type itself as the degenerate case */ + rc = ebitmap_set_bit(e, i, 1); + if (rc) + goto bad; + } + + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + + rc = 0; +out: + return rc; +bad: + policydb_destroy(p); + goto out; +} + +/* + * Write a MLS level structure to a policydb binary + * representation file. + */ +static int mls_write_level(struct mls_level *l, void *fp) +{ + __le32 buf[1]; + int rc; + + buf[0] = cpu_to_le32(l->sens); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = ebitmap_write(&l->cat, fp); + if (rc) + return rc; + + return 0; +} + +/* + * Write a MLS range structure to a policydb binary + * representation file. + */ +static int mls_write_range_helper(struct mls_range *r, void *fp) +{ + __le32 buf[3]; + size_t items; + int rc, eq; + + eq = mls_level_eq(&r->level[1], &r->level[0]); + + if (eq) + items = 2; + else + items = 3; + buf[0] = cpu_to_le32(items-1); + buf[1] = cpu_to_le32(r->level[0].sens); + if (!eq) + buf[2] = cpu_to_le32(r->level[1].sens); + + BUG_ON(items > (sizeof(buf)/sizeof(buf[0]))); + + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = ebitmap_write(&r->level[0].cat, fp); + if (rc) + return rc; + if (!eq) { + rc = ebitmap_write(&r->level[1].cat, fp); + if (rc) + return rc; + } + + return 0; +} + +static int sens_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct level_datum *levdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[2]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(levdatum->isalias); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = mls_write_level(levdatum->level, fp); + if (rc) + return rc; + + return 0; +} + +static int cat_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct cat_datum *catdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[3]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(catdatum->value); + buf[2] = cpu_to_le32(catdatum->isalias); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int role_trans_write(struct policydb *p, void *fp) +{ + struct role_trans *r = p->role_tr; + struct role_trans *tr; + u32 buf[3]; + size_t nel; + int rc; + + nel = 0; + for (tr = r; tr; tr = tr->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (tr = r; tr; tr = tr->next) { + buf[0] = cpu_to_le32(tr->role); + buf[1] = cpu_to_le32(tr->type); + buf[2] = cpu_to_le32(tr->new_role); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + buf[0] = cpu_to_le32(tr->tclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + } + } + + return 0; +} + +static int role_allow_write(struct role_allow *r, void *fp) +{ + struct role_allow *ra; + u32 buf[2]; + size_t nel; + int rc; + + nel = 0; + for (ra = r; ra; ra = ra->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (ra = r; ra; ra = ra->next) { + buf[0] = cpu_to_le32(ra->role); + buf[1] = cpu_to_le32(ra->new_role); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + } + return 0; +} + +/* + * Write a security context structure + * to a policydb binary representation file. + */ +static int context_write(struct policydb *p, struct context *c, + void *fp) +{ + int rc; + __le32 buf[3]; + + buf[0] = cpu_to_le32(c->user); + buf[1] = cpu_to_le32(c->role); + buf[2] = cpu_to_le32(c->type); + + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + rc = mls_write_range_helper(&c->range, fp); + if (rc) + return rc; + + return 0; +} + +/* + * The following *_write functions are used to + * write the symbol data to a policy database + * binary representation file. + */ + +static int perm_write(void *vkey, void *datum, void *fp) +{ + char *key = vkey; + struct perm_datum *perdatum = datum; + __le32 buf[2]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(perdatum->value); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int common_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct common_datum *comdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[4]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(comdatum->value); + buf[2] = cpu_to_le32(comdatum->permissions.nprim); + buf[3] = cpu_to_le32(comdatum->permissions.table->nel); + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = hashtab_map(comdatum->permissions.table, perm_write, fp); + if (rc) + return rc; + + return 0; +} + +static int type_set_write(struct type_set *t, void *fp) +{ + int rc; + __le32 buf[1]; + + if (ebitmap_write(&t->types, fp)) + return -EINVAL; + if (ebitmap_write(&t->negset, fp)) + return -EINVAL; + + buf[0] = cpu_to_le32(t->flags); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return -EINVAL; + + return 0; +} + +static int write_cons_helper(struct policydb *p, struct constraint_node *node, + void *fp) +{ + struct constraint_node *c; + struct constraint_expr *e; + __le32 buf[3]; + u32 nel; + int rc; + + for (c = node; c; c = c->next) { + nel = 0; + for (e = c->expr; e; e = e->next) + nel++; + buf[0] = cpu_to_le32(c->permissions); + buf[1] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + for (e = c->expr; e; e = e->next) { + buf[0] = cpu_to_le32(e->expr_type); + buf[1] = cpu_to_le32(e->attr); + buf[2] = cpu_to_le32(e->op); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + switch (e->expr_type) { + case CEXPR_NAMES: + rc = ebitmap_write(&e->names, fp); + if (rc) + return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + rc = type_set_write(e->type_names, fp); + if (rc) + return rc; + } + break; + default: + break; } - if (l) { - l->next = c; - } else { - p->ocontexts[i] = c; - } - l = c; - rc = -EINVAL; + } + } + + return 0; +} + +static int class_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct class_datum *cladatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + struct constraint_node *c; + __le32 buf[6]; + u32 ncons; + size_t len, len2; + int rc; + + len = strlen(key); + if (cladatum->comkey) + len2 = strlen(cladatum->comkey); + else + len2 = 0; + + ncons = 0; + for (c = cladatum->constraints; c; c = c->next) + ncons++; + + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(len2); + buf[2] = cpu_to_le32(cladatum->value); + buf[3] = cpu_to_le32(cladatum->permissions.nprim); + if (cladatum->permissions.table) + buf[4] = cpu_to_le32(cladatum->permissions.table->nel); + else + buf[4] = 0; + buf[5] = cpu_to_le32(ncons); + rc = put_entry(buf, sizeof(u32), 6, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + if (cladatum->comkey) { + rc = put_entry(cladatum->comkey, 1, len2, fp); + if (rc) + return rc; + } + + rc = hashtab_map(cladatum->permissions.table, perm_write, fp); + if (rc) + return rc; + + rc = write_cons_helper(p, cladatum->constraints, fp); + if (rc) + return rc; + + /* write out the validatetrans rule */ + ncons = 0; + for (c = cladatum->validatetrans; c; c = c->next) + ncons++; + + buf[0] = cpu_to_le32(ncons); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = write_cons_helper(p, cladatum->validatetrans, fp); + if (rc) + return rc; + + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + buf[0] = cpu_to_le32(cladatum->default_user); + buf[1] = cpu_to_le32(cladatum->default_role); + buf[2] = cpu_to_le32(cladatum->default_range); + + rc = put_entry(buf, sizeof(uint32_t), 3, fp); + if (rc) + return rc; + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + buf[0] = cpu_to_le32(cladatum->default_type); + rc = put_entry(buf, sizeof(uint32_t), 1, fp); + if (rc) + return rc; + } + + return 0; +} + +static int role_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct role_datum *role = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + __le32 buf[3]; + size_t items, len; + int rc; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(role->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + buf[items++] = cpu_to_le32(role->bounds); + + BUG_ON(items > (sizeof(buf)/sizeof(buf[0]))); + + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = ebitmap_write(&role->dominates, fp); + if (rc) + return rc; + + rc = ebitmap_write(&role->types, fp); + if (rc) + return rc; + + return 0; +} + +static int type_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct type_datum *typdatum = datum; + struct policy_data *pd = ptr; + struct policydb *p = pd->p; + void *fp = pd->fp; + __le32 buf[4]; + int rc; + size_t items, len; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(typdatum->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 properties = 0; + + if (typdatum->primary) + properties |= TYPEDATUM_PROPERTY_PRIMARY; + + if (typdatum->attribute) + properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; + + buf[items++] = cpu_to_le32(properties); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + } + BUG_ON(items > (sizeof(buf) / sizeof(buf[0]))); + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int user_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct user_datum *usrdatum = datum; + struct policy_data *pd = ptr; + struct policydb *p = pd->p; + void *fp = pd->fp; + __le32 buf[3]; + size_t items, len; + int rc; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(usrdatum->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + buf[items++] = cpu_to_le32(usrdatum->bounds); + BUG_ON(items > (sizeof(buf) / sizeof(buf[0]))); + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = ebitmap_write(&usrdatum->roles, fp); + if (rc) + return rc; + + rc = mls_write_range_helper(&usrdatum->range, fp); + if (rc) + return rc; + + rc = mls_write_level(&usrdatum->dfltlevel, fp); + if (rc) + return rc; + + return 0; +} + +static int (*write_f[SYM_NUM]) (void *key, void *datum, + void *datap) = +{ + common_write, + class_write, + role_write, + type_write, + user_write, + cond_write_bool, + sens_write, + cat_write, +}; + +static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + unsigned int i, j, rc; + size_t nel, len; + __le32 buf[3]; + u32 nodebuf[8]; + struct ocontext *c; + for (i = 0; i < info->ocon_num; i++) { + nel = 0; + for (c = p->ocontexts[i]; c; c = c->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (c = p->ocontexts[i]; c; c = c->next) { switch (i) { case OCON_ISID: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - c->sid[0] = le32_to_cpu(buf[0]); - rc = context_read_and_validate(&c->context[0], p, fp); + buf[0] = cpu_to_le32(c->sid[0]); + rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) - goto bad; + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; case OCON_FS: case OCON_NETIF: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - c->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); + len = strlen(c->u.name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) - goto bad; - rc = context_read_and_validate(&c->context[1], p, fp); + return rc; + rc = put_entry(c->u.name, 1, len, fp); if (rc) - goto bad; + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; + rc = context_write(p, &c->context[1], fp); + if (rc) + return rc; break; case OCON_PORT: - rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) - 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); + buf[0] = cpu_to_le32(c->u.port.protocol); + buf[1] = cpu_to_le32(c->u.port.low_port); + buf[2] = cpu_to_le32(c->u.port.high_port); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); if (rc) - goto bad; + return rc; break; case OCON_NODE: - rc = next_entry(buf, fp, sizeof(u32)* 2); - if (rc < 0) - 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); + nodebuf[0] = c->u.node.addr; /* network order */ + nodebuf[1] = c->u.node.mask; /* network order */ + rc = put_entry(nodebuf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); if (rc) - goto bad; + return rc; break; case OCON_FSUSE: - rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) - goto bad; - c->v.behavior = le32_to_cpu(buf[0]); - if (c->v.behavior > SECURITY_FS_USE_NONE) - goto bad; - len = le32_to_cpu(buf[1]); - c->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); + buf[0] = cpu_to_le32(c->v.behavior); + len = strlen(c->u.name); + buf[1] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 2, fp); if (rc) - goto bad; + return rc; + rc = put_entry(c->u.name, 1, len, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; - case OCON_NODE6: { - int k; - - rc = next_entry(buf, fp, sizeof(u32) * 8); - if (rc < 0) - goto bad; - for (k = 0; k < 4; k++) - c->u.node6.addr[k] = le32_to_cpu(buf[k]); - for (k = 0; k < 4; k++) - c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); - if (context_read_and_validate(&c->context[0], p, fp)) - goto bad; + case OCON_NODE6: + for (j = 0; j < 4; j++) + nodebuf[j] = c->u.node6.addr[j]; /* network order */ + for (j = 0; j < 4; j++) + nodebuf[j + 4] = c->u.node6.mask[j]; /* network order */ + rc = put_entry(nodebuf, sizeof(u32), 8, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; } - } } } + return 0; +} - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - genfs_p = NULL; - rc = -EINVAL; - for (i = 0; i < nel; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); - if (!newgenfs) { - rc = -ENOMEM; - goto bad; - } +static int genfs_write(struct policydb *p, void *fp) +{ + struct genfs *genfs; + struct ocontext *c; + size_t len; + __le32 buf[1]; + int rc; - newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL); - if (!newgenfs->fstype) { - rc = -ENOMEM; - kfree(newgenfs); - goto bad; - } - rc = next_entry(newgenfs->fstype, fp, len); - if (rc < 0) { - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - 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); - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - if (strcmp(newgenfs->fstype, genfs->fstype) < 0) - break; + len = 0; + for (genfs = p->genfs; genfs; genfs = genfs->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (genfs = p->genfs; genfs; genfs = genfs->next) { + len = strlen(genfs->fstype); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = put_entry(genfs->fstype, 1, len, fp); + if (rc) + return rc; + len = 0; + for (c = genfs->head; c; c = c->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (c = genfs->head; c; c = c->next) { + len = strlen(c->u.name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = put_entry(c->u.name, 1, len, fp); + if (rc) + return rc; + buf[0] = cpu_to_le32(c->v.sclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; } - newgenfs->next = genfs; - if (genfs_p) - genfs_p->next = newgenfs; - else - p->genfs = newgenfs; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel2 = le32_to_cpu(buf[0]); - for (j = 0; j < nel2; j++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); + } + return 0; +} - newc = kzalloc(sizeof(*newc), GFP_KERNEL); - if (!newc) { - rc = -ENOMEM; - goto bad; - } +static int hashtab_cnt(void *key, void *data, void *ptr) +{ + int *cnt = ptr; + *cnt = *cnt + 1; - newc->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!newc->u.name) { - rc = -ENOMEM; - goto bad_newc; - } - rc = next_entry(newc->u.name, fp, len); - if (rc < 0) - goto bad_newc; - newc->u.name[len] = 0; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad_newc; - newc->v.sclass = le32_to_cpu(buf[0]); - if (context_read_and_validate(&newc->context[0], p, fp)) - goto bad_newc; - 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_newc; - } - len = strlen(newc->u.name); - len2 = strlen(c->u.name); - if (len > len2) - break; - } + return 0; +} - newc->next = c; - if (l) - l->next = newc; - else - newgenfs->head = newc; - } +static int range_write_helper(void *key, void *data, void *ptr) +{ + __le32 buf[2]; + struct range_trans *rt = key; + struct mls_range *r = data; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + int rc; + + buf[0] = cpu_to_le32(rt->source_type); + buf[1] = cpu_to_le32(rt->target_type); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + buf[0] = cpu_to_le32(rt->target_class); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; } + rc = mls_write_range_helper(r, fp); + if (rc) + return rc; - if (p->policyvers >= POLICYDB_VERSION_MLS) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - lrt = NULL; - for (i = 0; i < nel; i++) { - rt = kzalloc(sizeof(*rt), GFP_KERNEL); - if (!rt) { - rc = -ENOMEM; - goto bad; - } - if (lrt) - lrt->next = rt; - else - p->range_tr = rt; - rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) - goto bad; - rt->dom = le32_to_cpu(buf[0]); - rt->type = le32_to_cpu(buf[1]); - rc = mls_read_range_helper(&rt->range, fp); - if (rc) - goto bad; - lrt = rt; - } + return 0; +} + +static int range_write(struct policydb *p, void *fp) +{ + __le32 buf[1]; + int rc, nel; + struct policy_data pd; + + pd.p = p; + pd.fp = fp; + + /* count the number of entries in the hashtab */ + nel = 0; + rc = hashtab_map(p->range_tr, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + /* actually write all of the entries */ + rc = hashtab_map(p->range_tr, range_write_helper, &pd); + if (rc) + return rc; + + return 0; +} + +static int filename_write_helper(void *key, void *data, void *ptr) +{ + __le32 buf[4]; + struct filename_trans *ft = key; + struct filename_trans_datum *otype = data; + void *fp = ptr; + int rc; + u32 len; + + len = strlen(ft->name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = put_entry(ft->name, sizeof(char), len, fp); + if (rc) + return rc; + + buf[0] = cpu_to_le32(ft->stype); + buf[1] = cpu_to_le32(ft->ttype); + buf[2] = cpu_to_le32(ft->tclass); + buf[3] = cpu_to_le32(otype->otype); + + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + return 0; +} + +static int filename_trans_write(struct policydb *p, void *fp) +{ + u32 nel; + __le32 buf[1]; + int rc; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + nel = 0; + rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = hashtab_map(p->filename_trans, filename_write_helper, fp); + if (rc) + return rc; + + return 0; +} + +/* + * Write the configuration data in a policy database + * structure to a policy database binary representation + * file. + */ +int policydb_write(struct policydb *p, void *fp) +{ + unsigned int i, num_syms; + int rc; + __le32 buf[4]; + u32 config; + size_t len; + struct policydb_compat_info *info; + + /* + * refuse to write policy older than compressed avtab + * to simplify the writer. There are other tests dropped + * since we assume this throughout the writer code. Be + * careful if you ever try to remove this restriction + */ + if (p->policyvers < POLICYDB_VERSION_AVTAB) { + printk(KERN_ERR "SELinux: refusing to write policy version %d." + " Because it is less than version %d\n", p->policyvers, + POLICYDB_VERSION_AVTAB); + return -EINVAL; } - p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); - if (!p->type_attr_map) - goto bad; + config = 0; + if (p->mls_enabled) + config |= POLICYDB_CONFIG_MLS; + + if (p->reject_unknown) + config |= REJECT_UNKNOWN; + if (p->allow_unknown) + config |= ALLOW_UNKNOWN; + + /* Write the magic number and string identifiers. */ + buf[0] = cpu_to_le32(POLICYDB_MAGIC); + len = strlen(POLICYDB_STRING); + buf[1] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = put_entry(POLICYDB_STRING, 1, len, fp); + if (rc) + return rc; + + /* Write the version, config, and table sizes. */ + info = policydb_lookup_compat(p->policyvers); + if (!info) { + printk(KERN_ERR "SELinux: compatibility lookup failed for policy " + "version %d", p->policyvers); + return -EINVAL; + } + + buf[0] = cpu_to_le32(p->policyvers); + buf[1] = cpu_to_le32(config); + buf[2] = cpu_to_le32(info->sym_num); + buf[3] = cpu_to_le32(info->ocon_num); + + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + if (p->policyvers >= POLICYDB_VERSION_POLCAP) { + rc = ebitmap_write(&p->policycaps, fp); + if (rc) + return rc; + } + + if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE) { + rc = ebitmap_write(&p->permissive_map, fp); + if (rc) + return rc; + } + + num_syms = info->sym_num; + for (i = 0; i < num_syms; i++) { + struct policy_data pd; + + pd.fp = fp; + pd.p = p; + + buf[0] = cpu_to_le32(p->symtab[i].nprim); + buf[1] = cpu_to_le32(p->symtab[i].table->nel); + + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = hashtab_map(p->symtab[i].table, write_f[i], &pd); + if (rc) + return rc; + } + + rc = avtab_write(p, &p->te_avtab, fp); + if (rc) + return rc; + + rc = cond_write_list(p, p->cond_list, fp); + if (rc) + return rc; + + rc = role_trans_write(p, fp); + if (rc) + return rc; + + rc = role_allow_write(p->role_allow, fp); + if (rc) + return rc; + + rc = filename_trans_write(p, fp); + if (rc) + return rc; + + rc = ocontext_write(p, info, fp); + if (rc) + return rc; + + rc = genfs_write(p, fp); + if (rc) + return rc; + + rc = range_write(p, fp); + if (rc) + return rc; for (i = 0; i < p->p_types.nprim; i++) { - ebitmap_init(&p->type_attr_map[i]); - if (p->policyvers >= POLICYDB_VERSION_AVTAB) { - if (ebitmap_read(&p->type_attr_map[i], fp)) - goto bad; - } - /* add the type itself as the degenerate case */ - if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) - goto bad; + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + rc = ebitmap_write(e, fp); + if (rc) + return rc; } - rc = 0; -out: - return rc; -bad_newc: - ocontext_destroy(newc,OCON_FSUSE); -bad: - if (!rc) - rc = -EINVAL; - policydb_destroy(p); - goto out; + return 0; } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index b1340711f72..725d5945a97 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -12,21 +12,25 @@ * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #ifndef _SS_POLICYDB_H_ #define _SS_POLICYDB_H_ +#include <linux/flex_array.h> + #include "symtab.h" #include "avtab.h" #include "sidtab.h" +#include "ebitmap.h" +#include "mls_types.h" #include "context.h" #include "constraint.h" @@ -56,22 +60,49 @@ struct class_datum { struct symtab permissions; /* class-specific permission symbol table */ struct constraint_node *constraints; /* constraints on class permissions */ struct constraint_node *validatetrans; /* special transition rules */ +/* Options how a new object user, role, and type should be decided */ +#define DEFAULT_SOURCE 1 +#define DEFAULT_TARGET 2 + char default_user; + char default_role; + char default_type; +/* Options how a new object range should be decided */ +#define DEFAULT_SOURCE_LOW 1 +#define DEFAULT_SOURCE_HIGH 2 +#define DEFAULT_SOURCE_LOW_HIGH 3 +#define DEFAULT_TARGET_LOW 4 +#define DEFAULT_TARGET_HIGH 5 +#define DEFAULT_TARGET_LOW_HIGH 6 + char default_range; }; /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ 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 type; /* program executable type, or new object type */ + u32 tclass; /* process class, or new object class */ u32 new_role; /* new role */ struct role_trans *next; }; +struct filename_trans { + u32 stype; /* current process */ + u32 ttype; /* parent dir context */ + u16 tclass; /* class of new object */ + const char *name; /* last path component */ +}; + +struct filename_trans_datum { + u32 otype; /* expected of new object */ +}; + struct role_allow { u32 role; /* current role */ u32 new_role; /* new role */ @@ -81,12 +112,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -106,10 +140,9 @@ struct cat_datum { }; struct range_trans { - u32 dom; /* current process domain */ - u32 type; /* program executable type */ - struct mls_range range; /* new range */ - struct range_trans *next; + u32 source_type; + u32 target_type; + u32 target_class; }; /* Boolean data type */ @@ -121,6 +154,17 @@ struct cond_bool_datum { struct cond_node; /* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { + struct ebitmap types; + struct ebitmap negset; + u32 flags; +}; + +/* * 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 @@ -182,6 +226,8 @@ struct genfs { /* The policy database */ struct policydb { + int mls_enabled; + /* symbol tables */ struct symtab symtab[SYM_NUM]; #define p_commons symtab[SYM_COMMONS] @@ -194,20 +240,13 @@ struct policydb { #define p_cats symtab[SYM_CATS] /* symbol names indexed by (value - 1) */ - char **sym_val_to_name[SYM_NUM]; -#define p_common_val_to_name sym_val_to_name[SYM_COMMONS] -#define p_class_val_to_name sym_val_to_name[SYM_CLASSES] -#define p_role_val_to_name sym_val_to_name[SYM_ROLES] -#define p_type_val_to_name sym_val_to_name[SYM_TYPES] -#define p_user_val_to_name sym_val_to_name[SYM_USERS] -#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS] -#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS] -#define p_cat_val_to_name sym_val_to_name[SYM_CATS] + struct flex_array *sym_val_to_name[SYM_NUM]; /* 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; + struct flex_array *type_val_to_struct_array; /* type enforcement access vectors and transitions */ struct avtab te_avtab; @@ -215,12 +254,18 @@ struct policydb { /* role transitions */ struct role_trans *role_tr; + /* file transitions with the last path component */ + /* quickly exclude lookups when parent ttype has no rules */ + struct ebitmap filename_trans_ttypes; + /* actual set of filename_trans rules */ + struct hashtab *filename_trans; + /* bools indexed by (value - 1) */ struct cond_bool_datum **bool_val_to_struct; /* type enforcement conditional access vectors and transitions */ struct avtab te_cond_avtab; /* linked list indexing te_cond_avtab by conditional */ - struct cond_node* cond_list; + struct cond_node *cond_list; /* role allows */ struct role_allow *role_allow; @@ -229,29 +274,50 @@ struct policydb { TCP or UDP port numbers, network interfaces and nodes */ struct ocontext *ocontexts[OCON_NUM]; - /* security contexts for files in filesystems that cannot support + /* security contexts for files in filesystems that cannot support a persistent label mapping or use another fixed labeling behavior. */ - struct genfs *genfs; + struct genfs *genfs; - /* range transitions */ - struct range_trans *range_tr; + /* range transitions table (range_trans_key -> mls_range) */ + struct hashtab *range_tr; /* type -> attribute reverse mapping */ - struct ebitmap *type_attr_map; + struct flex_array *type_attr_map_array; + + struct ebitmap policycaps; + + struct ebitmap permissive_map; + + /* length of this policy when it was loaded */ + size_t len; unsigned int policyvers; + + unsigned int reject_unknown : 1; + unsigned int allow_unknown : 1; + + u16 process_class; + u32 process_trans_perms; }; 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_class_isvalid(struct policydb *p, unsigned int class); +extern int policydb_type_isvalid(struct policydb *p, unsigned int type); +extern int policydb_role_isvalid(struct policydb *p, unsigned int role); extern int policydb_read(struct policydb *p, void *fp); +extern int policydb_write(struct policydb *p, void *fp); #define PERM_SYMTAB_SIZE 32 #define POLICYDB_CONFIG_MLS 1 +/* the config flags related to unknown classes/perms are bits 2 and 3 */ +#define REJECT_UNKNOWN 0x00000002 +#define ALLOW_UNKNOWN 0x00000004 + #define OBJECT_R "object_r" #define OBJECT_R_VAL 1 @@ -263,6 +329,11 @@ struct policy_file { size_t len; }; +struct policy_data { + struct policydb *p; + void *fp; +}; + static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) { if (bytes > fp->len) @@ -274,5 +345,26 @@ static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) return 0; } +static inline int put_entry(const void *buf, size_t bytes, int num, struct policy_file *fp) +{ + size_t len = bytes * num; + + memcpy(fp->data, buf, len); + fp->data += len; + fp->len -= len; + + return 0; +} + +static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr) +{ + struct flex_array *fa = p->sym_val_to_name[sym_num]; + + return flex_array_get_ptr(fa, element_nr); +} + +extern u16 string_to_security_class(struct policydb *p, const char *name); +extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name); + #endif /* _SS_POLICYDB_H_ */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8a764928ff4..4bca49414a4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2,32 +2,58 @@ * Implementation of the security services. * * Authors : Stephen Smalley, <sds@epoch.ncsc.mil> - * James Morris <jmorris@redhat.com> + * James Morris <jmorris@redhat.com> * * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> * * Support for enhanced MLS infrastructure. + * Support for context based audit filters. * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. - * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support for NetLabel + * Added support for the policy capability bitmap + * + * Updated: Chad Sellers <csellers@tresys.com> + * + * Added validation of kernel classes and permissions + * + * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * Added support for bounds domain and audit messaged on masked permissions + * + * Updated: Guido Trentalancia <guido@trentalancia.com> + * + * Added support for runtime switching of the policy type + * + * Copyright (C) 2008, 2009 NEC Corporation + * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/spinlock.h> +#include <linux/rcupdate.h> #include <linux/errno.h> #include <linux/in.h> #include <linux/sched.h> #include <linux/audit.h> -#include <asm/semaphore.h> +#include <linux/mutex.h> +#include <linux/selinux.h> +#include <linux/flex_array.h> +#include <linux/vmalloc.h> +#include <net/netlabel.h> + #include "flask.h" #include "avc.h" #include "avc_ss.h" @@ -38,23 +64,21 @@ #include "services.h" #include "conditional.h" #include "mls.h" +#include "objsec.h" +#include "netlabel.h" +#include "xfrm.h" +#include "ebitmap.h" +#include "audit.h" -extern void selnl_notify_policyload(u32 seqno); -unsigned int policydb_loaded_version; +int selinux_policycap_netpeer; +int selinux_policycap_openperm; +int selinux_policycap_alwaysnetwork; static DEFINE_RWLOCK(policy_rwlock); -#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) static struct sidtab sidtab; struct policydb policydb; -int ss_initialized = 0; +int ss_initialized; /* * The largest sequence number that has been used when @@ -62,12 +86,177 @@ int ss_initialized = 0; * The sequence number only changes when a policy change * occurs. */ -static u32 latest_granting = 0; +static u32 latest_granting; /* Forward declaration. */ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd); + +struct selinux_mapping { + u16 value; /* policy value */ + unsigned num_perms; + u32 perms[sizeof(u32) * 8]; +}; + +static struct selinux_mapping *current_mapping; +static u16 current_mapping_size; + +static int selinux_set_mapping(struct policydb *pol, + struct security_class_mapping *map, + struct selinux_mapping **out_map_p, + u16 *out_map_size) +{ + struct selinux_mapping *out_map = NULL; + size_t size = sizeof(struct selinux_mapping); + u16 i, j; + unsigned k; + bool print_unknown_handle = false; + + /* Find number of classes in the input mapping */ + if (!map) + return -EINVAL; + i = 0; + while (map[i].name) + i++; + + /* Allocate space for the class records, plus one for class zero */ + out_map = kcalloc(++i, size, GFP_ATOMIC); + if (!out_map) + return -ENOMEM; + + /* Store the raw class and permission values */ + j = 0; + while (map[j].name) { + struct security_class_mapping *p_in = map + (j++); + struct selinux_mapping *p_out = out_map + j; + + /* An empty class string skips ahead */ + if (!strcmp(p_in->name, "")) { + p_out->num_perms = 0; + continue; + } + + p_out->value = string_to_security_class(pol, p_in->name); + if (!p_out->value) { + printk(KERN_INFO + "SELinux: Class %s not defined in policy.\n", + p_in->name); + if (pol->reject_unknown) + goto err; + p_out->num_perms = 0; + print_unknown_handle = true; + continue; + } + + k = 0; + while (p_in->perms && p_in->perms[k]) { + /* An empty permission string skips ahead */ + if (!*p_in->perms[k]) { + k++; + continue; + } + p_out->perms[k] = string_to_av_perm(pol, p_out->value, + p_in->perms[k]); + if (!p_out->perms[k]) { + printk(KERN_INFO + "SELinux: Permission %s in class %s not defined in policy.\n", + p_in->perms[k], p_in->name); + if (pol->reject_unknown) + goto err; + print_unknown_handle = true; + } + + k++; + } + p_out->num_perms = k; + } + + if (print_unknown_handle) + printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", + pol->allow_unknown ? "allowed" : "denied"); + + *out_map_p = out_map; + *out_map_size = i; + return 0; +err: + kfree(out_map); + return -EINVAL; +} + +/* + * Get real, policy values from mapped values + */ + +static u16 unmap_class(u16 tclass) +{ + if (tclass < current_mapping_size) + return current_mapping[tclass].value; + + return tclass; +} + +/* + * Get kernel value for class from its policy value + */ +static u16 map_class(u16 pol_value) +{ + u16 i; + + for (i = 1; i < current_mapping_size; i++) { + if (current_mapping[i].value == pol_value) + return i; + } + + return SECCLASS_NULL; +} + +static void map_decision(u16 tclass, struct av_decision *avd, + int allow_unknown) +{ + if (tclass < current_mapping_size) { + unsigned i, n = current_mapping[tclass].num_perms; + u32 result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->allowed & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + avd->allowed = result; + + for (i = 0, result = 0; i < n; i++) + if (avd->auditallow & current_mapping[tclass].perms[i]) + result |= 1<<i; + avd->auditallow = result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->auditdeny & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (!allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + /* + * In case the kernel has a bug and requests a permission + * between num_perms and the maximum permission number, we + * should audit that denial + */ + for (; i < (sizeof(u32)*8); i++) + result |= 1<<i; + avd->auditdeny = result; + } +} + +int security_mls_enabled(void) +{ + return policydb.mls_enabled; +} + /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -101,15 +290,15 @@ static int constraint_expr_eval(struct context *scontext, case CEXPR_AND: BUG_ON(sp < 1); sp--; - s[sp] &= s[sp+1]; + s[sp] &= s[sp + 1]; break; case CEXPR_OR: BUG_ON(sp < 1); sp--; - s[sp] |= s[sp+1]; + s[sp] |= s[sp + 1]; break; case CEXPR_ATTR: - if (sp == (CEXPR_MAXDEPTH-1)) + if (sp == (CEXPR_MAXDEPTH - 1)) return 0; switch (e->attr) { case CEXPR_USER: @@ -135,10 +324,10 @@ static int constraint_expr_eval(struct context *scontext, val1 - 1); continue; case CEXPR_INCOMP: - s[++sp] = ( !ebitmap_get_bit(&r1->dominates, - val2 - 1) && - !ebitmap_get_bit(&r2->dominates, - val1 - 1) ); + s[++sp] = (!ebitmap_get_bit(&r1->dominates, + val2 - 1) && + !ebitmap_get_bit(&r2->dominates, + val1 - 1)); continue; default: break; @@ -254,15 +443,184 @@ mls_ops: } /* - * Compute access vectors based on a context structure pair for - * the permissions in a particular class. + * security_dump_masked_av - dumps masked permissions during + * security_compute_av due to RBAC, MLS/Constraint and Type bounds. + */ +static int dump_masked_av_helper(void *k, void *d, void *args) +{ + struct perm_datum *pdatum = d; + char **permission_names = args; + + BUG_ON(pdatum->value < 1 || pdatum->value > 32); + + permission_names[pdatum->value - 1] = (char *)k; + + return 0; +} + +static void security_dump_masked_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 permissions, + const char *reason) +{ + struct common_datum *common_dat; + struct class_datum *tclass_dat; + struct audit_buffer *ab; + char *tclass_name; + char *scontext_name = NULL; + char *tcontext_name = NULL; + char *permission_names[32]; + int index; + u32 length; + bool need_comma = false; + + if (!permissions) + return; + + tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1); + tclass_dat = policydb.class_val_to_struct[tclass - 1]; + common_dat = tclass_dat->comdatum; + + /* init permission_names */ + if (common_dat && + hashtab_map(common_dat->permissions.table, + dump_masked_av_helper, permission_names) < 0) + goto out; + + if (hashtab_map(tclass_dat->permissions.table, + dump_masked_av_helper, permission_names) < 0) + goto out; + + /* get scontext/tcontext in text form */ + if (context_struct_to_string(scontext, + &scontext_name, &length) < 0) + goto out; + + if (context_struct_to_string(tcontext, + &tcontext_name, &length) < 0) + goto out; + + /* audit a message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + goto out; + + audit_log_format(ab, "op=security_compute_av reason=%s " + "scontext=%s tcontext=%s tclass=%s perms=", + reason, scontext_name, tcontext_name, tclass_name); + + for (index = 0; index < 32; index++) { + u32 mask = (1 << index); + + if ((mask & permissions) == 0) + continue; + + audit_log_format(ab, "%s%s", + need_comma ? "," : "", + permission_names[index] + ? permission_names[index] : "????"); + need_comma = true; + } + audit_log_end(ab); +out: + /* release scontext/tcontext */ + kfree(tcontext_name); + kfree(scontext_name); + + return; +} + +/* + * security_boundary_permission - drops violated permissions + * on boundary constraint. */ -static int context_struct_compute_av(struct context *scontext, +static void type_attribute_bounds_av(struct context *scontext, struct context *tcontext, u16 tclass, - u32 requested, struct av_decision *avd) { + struct context lo_scontext; + struct context lo_tcontext; + struct av_decision lo_avd; + struct type_datum *source; + struct type_datum *target; + u32 masked = 0; + + source = flex_array_get_ptr(policydb.type_val_to_struct_array, + scontext->type - 1); + BUG_ON(!source); + + target = flex_array_get_ptr(policydb.type_val_to_struct_array, + tcontext->type - 1); + BUG_ON(!target); + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; + + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; + + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + /* + * lo_scontext and lo_tcontext are already + * set up. + */ + + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* audit masked permissions */ + security_dump_masked_av(scontext, tcontext, + tclass, masked, "bounds"); + } +} + +/* + * Compute access vectors based on a context structure pair for + * the permissions in a particular class. + */ +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd) +{ struct constraint_node *constraint; struct role_allow *ra; struct avtab_key avkey; @@ -272,32 +630,17 @@ static int context_struct_compute_av(struct context *scontext, struct ebitmap_node *snode, *tnode; unsigned int i, j; - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; - - 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 (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (printk_ratelimit()) + printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); + return; + } + + tclass_datum = policydb.class_val_to_struct[tclass - 1]; /* * If a specific type enforcement rule was defined for @@ -305,18 +648,16 @@ static int context_struct_compute_av(struct context *scontext, */ avkey.target_class = tclass; avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; - ebitmap_for_each_bit(sattr, snode, i) { - if (!ebitmap_node_get_bit(snode, i)) - continue; - ebitmap_for_each_bit(tattr, tnode, j) { - if (!ebitmap_node_get_bit(tnode, j)) - continue; + sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); + BUG_ON(!tattr); + ebitmap_for_each_positive_bit(sattr, snode, i) { + ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; avkey.target_type = j + 1; for (node = avtab_search_node(&policydb.te_avtab, &avkey); - node != NULL; + node; node = avtab_search_node_next(node, avkey.specified)) { if (node->key.specified == AVTAB_ALLOWED) avd->allowed |= node->datum.data; @@ -341,7 +682,7 @@ static int context_struct_compute_av(struct context *scontext, if ((constraint->permissions & (avd->allowed)) && !constraint_expr_eval(scontext, tcontext, NULL, constraint->expr)) { - avd->allowed = (avd->allowed) & ~(constraint->permissions); + avd->allowed &= ~(constraint->permissions); } constraint = constraint->next; } @@ -351,8 +692,8 @@ static int context_struct_compute_av(struct context *scontext, * role is changing, then check the (current_role, new_role) * pair. */ - if (tclass == SECCLASS_PROCESS && - (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) && + if (tclass == policydb.process_class && + (avd->allowed & policydb.process_trans_perms) && scontext->role != tcontext->role) { for (ra = policydb.role_allow; ra; ra = ra->next) { if (scontext->role == ra->role && @@ -360,31 +701,36 @@ static int context_struct_compute_av(struct context *scontext, break; } if (!ra) - avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION | - PROCESS__DYNTRANSITION); + avd->allowed &= ~policydb.process_trans_perms; } - return 0; + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(scontext, tcontext, + tclass, avd); } static int security_validtrans_handle_fail(struct context *ocontext, - struct context *ncontext, - struct context *tcontext, - u16 tclass) + struct context *ncontext, + struct context *tcontext, + u16 tclass) { char *o = NULL, *n = NULL, *t = NULL; u32 olen, nlen, tlen; - if (context_struct_to_string(ocontext, &o, &olen) < 0) + if (context_struct_to_string(ocontext, &o, &olen)) goto out; - if (context_struct_to_string(ncontext, &n, &nlen) < 0) + if (context_struct_to_string(ncontext, &n, &nlen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen) < 0) + if (context_struct_to_string(tcontext, &t, &tlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "security_validate_transition: denied for" - " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", - o, n, t, policydb.p_class_val_to_name[tclass-1]); + "security_validate_transition: denied for" + " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", + o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); out: kfree(o); kfree(n); @@ -396,34 +742,26 @@ out: } int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 tclass) + u16 orig_tclass) { struct context *ocontext; struct context *ncontext; struct context *tcontext; struct class_datum *tclass_datum; struct constraint_node *constraint; + u16 tclass; int rc = 0; if (!ss_initialized) return 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; + tclass = unmap_class(orig_tclass); if (!tclass || tclass > policydb.p_classes.nprim) { - printk(KERN_ERR "security_validate_transition: " - "unrecognized class %d\n", tclass); + printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", + __func__, tclass); rc = -EINVAL; goto out; } @@ -431,24 +769,24 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, ocontext = sidtab_search(&sidtab, oldsid); if (!ocontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", oldsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, oldsid); rc = -EINVAL; goto out; } ncontext = sidtab_search(&sidtab, newsid); if (!ncontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", newsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, newsid); rc = -EINVAL; goto out; } tcontext = sidtab_search(&sidtab, tasksid); if (!tcontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", tasksid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tasksid); rc = -EINVAL; goto out; } @@ -456,72 +794,212 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, constraint = tclass_datum->validatetrans; while (constraint) { if (!constraint_expr_eval(ocontext, ncontext, tcontext, - constraint->expr)) { + constraint->expr)) { rc = security_validtrans_handle_fail(ocontext, ncontext, - tcontext, tclass); + tcontext, tclass); goto out; } constraint = constraint->next; } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + return rc; +} + +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + rc = -EINVAL; + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + rc = 0; + /* type/domain unchanged */ + if (old_context->type == new_context->type) + goto out; + + index = new_context->type; + while (true) { + type = flex_array_get_ptr(policydb.type_val_to_struct_array, + index - 1); + BUG_ON(!type); + + /* not bounded anymore */ + rc = -EPERM; + if (!type->bounds) + break; + + /* @newsid is bounded by @oldsid */ + rc = 0; + if (type->bounds == old_context->type) + break; + + index = type->bounds; + } + + if (rc) { + char *old_name = NULL; + char *new_name = NULL; + u32 length; + + if (!context_struct_to_string(old_context, + &old_name, &length) && + !context_struct_to_string(new_context, + &new_name, &length)) { + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "op=security_bounded_transition " + "result=denied " + "oldcontext=%s newcontext=%s", + old_name, new_name); + } + kfree(new_name); + kfree(old_name); + } +out: + read_unlock(&policy_rwlock); + return rc; } +static void avd_init(struct av_decision *avd) +{ + avd->allowed = 0; + avd->auditallow = 0; + avd->auditdeny = 0xffffffff; + avd->seqno = latest_granting; + avd->flags = 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) +void security_compute_av(u32 ssid, + u32 tsid, + u16 orig_tclass, + struct av_decision *avd) { + u16 tclass; struct context *scontext = NULL, *tcontext = NULL; - int rc = 0; - if (!ss_initialized) { - avd->allowed = 0xffffffff; - avd->decided = 0xffffffff; - avd->auditallow = 0; - avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; - return 0; + read_lock(&policy_rwlock); + avd_init(avd); + if (!ss_initialized) + goto allow; + + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + goto out; + } + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + goto out; } - POLICY_RDLOCK; + tclass = unmap_class(orig_tclass); + if (unlikely(orig_tclass && !tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + context_struct_compute_av(scontext, tcontext, tclass, avd); + map_decision(orig_tclass, avd, policydb.allow_unknown); +out: + read_unlock(&policy_rwlock); + return; +allow: + avd->allowed = 0xffffffff; + goto out; +} + +void security_compute_av_user(u32 ssid, + u32 tsid, + u16 tclass, + struct av_decision *avd) +{ + struct context *scontext = NULL, *tcontext = NULL; + + read_lock(&policy_rwlock); + avd_init(avd); + if (!ss_initialized) + goto allow; scontext = sidtab_search(&sidtab, ssid); if (!scontext) { - printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", - ssid); - rc = -EINVAL; + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); goto out; } + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + tcontext = sidtab_search(&sidtab, tsid); if (!tcontext) { - printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", - tsid); - rc = -EINVAL; + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); goto out; } - rc = context_struct_compute_av(scontext, tcontext, tclass, - requested, avd); -out: - POLICY_RDUNLOCK; - return rc; + if (unlikely(!tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + + context_struct_compute_av(scontext, tcontext, tclass, avd); + out: + read_unlock(&policy_rwlock); + return; +allow: + avd->allowed = 0xffffffff; + goto out; } /* @@ -535,32 +1013,45 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 { char *scontextp; - *scontext = NULL; + if (scontext) + *scontext = NULL; *scontext_len = 0; + if (context->len) { + *scontext_len = context->len; + if (scontext) { + *scontext = kstrdup(context->str, GFP_ATOMIC); + if (!(*scontext)) + return -ENOMEM; + } + return 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 += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1; + *scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1; + *scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1; *scontext_len += mls_compute_context_len(context); + if (!scontext) + return 0; + /* Allocate space for the context; caller must free this space. */ scontextp = kmalloc(*scontext_len, GFP_ATOMIC); - if (!scontextp) { + 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]); + sym_name(&policydb, SYM_USERS, context->user - 1), + sym_name(&policydb, SYM_ROLES, context->role - 1), + sym_name(&policydb, SYM_TYPES, context->type - 1)); + scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + + 1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + + 1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)); mls_sid_to_context(context, &scontextp); @@ -571,98 +1062,105 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 #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) +const char *security_get_initial_sid_context(u32 sid) +{ + if (unlikely(sid > SECINITSID_NUM)) + return NULL; + return initial_sid_to_string[sid]; +} + +static int security_sid_to_context_core(u32 sid, char **scontext, + u32 *scontext_len, int force) { struct context *context; int rc = 0; + if (scontext) + *scontext = NULL; + *scontext_len = 0; + if (!ss_initialized) { if (sid <= SECINITSID_NUM) { char *scontextp; *scontext_len = strlen(initial_sid_to_string[sid]) + 1; - scontextp = kmalloc(*scontext_len,GFP_ATOMIC); + if (!scontext) + goto out; + scontextp = kmalloc(*scontext_len, GFP_ATOMIC); + if (!scontextp) { + rc = -ENOMEM; + goto out; + } 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); + printk(KERN_ERR "SELinux: %s: called before initial " + "load_policy on unknown SID %d\n", __func__, sid); rc = -EINVAL; goto out; } - POLICY_RDLOCK; - context = sidtab_search(&sidtab, sid); + read_lock(&policy_rwlock); + if (force) + context = sidtab_search_force(&sidtab, sid); + else + context = sidtab_search(&sidtab, sid); if (!context) { - printk(KERN_ERR "security_sid_to_context: unrecognized SID " - "%d\n", sid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, sid); rc = -EINVAL; goto out_unlock; } rc = context_struct_to_string(context, scontext, scontext_len); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); out: return rc; } -static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid) +/** + * 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) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 0); +} + +int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 1); +} + +/* + * Caveat: Mutates scontext. + */ +static int string_to_context_struct(struct policydb *pol, + struct sidtab *sidtabp, + char *scontext, + u32 scontext_len, + struct context *ctx, + u32 def_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; - } - } - *sid = SECINITSID_KERNEL; - 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; + context_init(ctx); /* Parse the security context. */ rc = -EINVAL; - scontextp = (char *) scontext2; + scontextp = (char *) scontext; /* Extract the user. */ p = scontextp; @@ -670,15 +1168,15 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - usrdatum = hashtab_search(policydb.p_users.table, scontextp); + usrdatum = hashtab_search(pol->p_users.table, scontextp); if (!usrdatum) - goto out_unlock; + goto out; - context.user = usrdatum->value; + ctx->user = usrdatum->value; /* Extract role. */ scontextp = p; @@ -686,14 +1184,14 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - role = hashtab_search(policydb.p_roles.table, scontextp); + role = hashtab_search(pol->p_roles.table, scontextp); if (!role) - goto out_unlock; - context.role = role->value; + goto out; + ctx->role = role->value; /* Extract type. */ scontextp = p; @@ -702,33 +1200,87 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s oldc = *p; *p++ = 0; - typdatum = hashtab_search(policydb.p_types.table, scontextp); - if (!typdatum) - goto out_unlock; + typdatum = hashtab_search(pol->p_types.table, scontextp); + if (!typdatum || typdatum->attribute) + goto out; - context.type = typdatum->value; + ctx->type = typdatum->value; - rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid); + rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); if (rc) - goto out_unlock; + goto out; - if ((p - scontext2) < scontext_len) { - rc = -EINVAL; - goto out_unlock; - } + rc = -EINVAL; + if ((p - scontext) < scontext_len) + goto out; /* Check the validity of the new context. */ - if (!policydb_context_isvalid(&policydb, &context)) { - rc = -EINVAL; - goto out_unlock; + if (!policydb_context_isvalid(pol, ctx)) + goto out; + rc = 0; +out: + if (rc) + context_destroy(ctx); + return rc; +} + +static int security_context_to_sid_core(const char *scontext, u32 scontext_len, + u32 *sid, u32 def_sid, gfp_t gfp_flags, + int force) +{ + char *scontext2, *str = NULL; + struct context context; + int rc = 0; + + /* An empty security context is never valid. */ + if (!scontext_len) + return -EINVAL; + + if (!ss_initialized) { + int i; + + for (i = 1; i < SECINITSID_NUM; i++) { + if (!strcmp(initial_sid_to_string[i], scontext)) { + *sid = i; + return 0; + } + } + *sid = SECINITSID_KERNEL; + return 0; + } + *sid = SECSID_NULL; + + /* Copy the string so that we can modify the copy as we parse it. */ + scontext2 = kmalloc(scontext_len + 1, gfp_flags); + if (!scontext2) + return -ENOMEM; + memcpy(scontext2, scontext, scontext_len); + scontext2[scontext_len] = 0; + + if (force) { + /* Save another copy for storing in uninterpreted form */ + rc = -ENOMEM; + str = kstrdup(scontext2, gfp_flags); + if (!str) + goto out; } - /* Obtain the new sid. */ + + read_lock(&policy_rwlock); + rc = string_to_context_struct(&policydb, &sidtab, scontext2, + scontext_len, &context, def_sid); + if (rc == -EINVAL && force) { + context.str = str; + context.len = scontext_len; + str = NULL; + } else if (rc) + goto out_unlock; rc = sidtab_context_to_sid(&sidtab, &context, sid); -out_unlock: - POLICY_RDUNLOCK; context_destroy(&context); - kfree(scontext2); +out_unlock: + read_unlock(&policy_rwlock); out: + kfree(scontext2); + kfree(str); return rc; } @@ -737,16 +1289,18 @@ out: * @scontext: security context * @scontext_len: length in bytes * @sid: security identifier, SID + * @gfp: context for the allocation * * 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) +int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, + gfp_t gfp) { return security_context_to_sid_core(scontext, scontext_len, - sid, SECSID_NULL); + sid, SECSID_NULL, gfp, 0); } /** @@ -756,20 +1310,29 @@ int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid) * @scontext: security context * @scontext_len: length in bytes * @sid: security identifier, SID - * @def_sid: default SID to assign on errror + * @def_sid: default SID to assign on error * * Obtains a SID associated with the security context that * has the string representation specified by @scontext. * The default SID is passed to the MLS layer to be used to allow * kernel labeling of the MLS field if the MLS field is not present * (for upgrading to MLS without full relabel). + * Implicitly forces adding of the context even if it cannot be mapped yet. * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ -int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid) +int security_context_to_sid_default(const char *scontext, u32 scontext_len, + u32 *sid, u32 def_sid, gfp_t gfp_flags) +{ + return security_context_to_sid_core(scontext, scontext_len, + sid, def_sid, gfp_flags, 1); +} + +int security_context_to_sid_force(const char *scontext, u32 scontext_len, + u32 *sid) { return security_context_to_sid_core(scontext, scontext_len, - sid, def_sid); + sid, SECSID_NULL, GFP_KERNEL, 1); } static int compute_sid_handle_invalid_context( @@ -781,18 +1344,18 @@ static int compute_sid_handle_invalid_context( char *s = NULL, *t = NULL, *n = NULL; u32 slen, tlen, nlen; - if (context_struct_to_string(scontext, &s, &slen) < 0) + if (context_struct_to_string(scontext, &s, &slen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen) < 0) + if (context_struct_to_string(tcontext, &t, &tlen)) goto out; - if (context_struct_to_string(newcontext, &n, &nlen) < 0) + if (context_struct_to_string(newcontext, &n, &nlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "security_compute_sid: invalid context %s" " for scontext=%s" " tcontext=%s" " tclass=%s", - n, s, t, policydb.p_class_val_to_name[tclass-1]); + n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); out: kfree(s); kfree(t); @@ -802,22 +1365,52 @@ out: return -EACCES; } +static void filename_compute_type(struct policydb *p, struct context *newcontext, + u32 stype, u32 ttype, u16 tclass, + const char *objname) +{ + struct filename_trans ft; + struct filename_trans_datum *otype; + + /* + * Most filename trans rules are going to live in specific directories + * like /dev or /var/run. This bitmap will quickly skip rule searches + * if the ttype does not contain any rules. + */ + if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) + return; + + ft.stype = stype; + ft.ttype = ttype; + ft.tclass = tclass; + ft.name = objname; + + otype = hashtab_search(p->filename_trans, &ft); + if (otype) + newcontext->type = otype->otype; +} + static int security_compute_sid(u32 ssid, u32 tsid, - u16 tclass, + u16 orig_tclass, u32 specified, - u32 *out_sid) + const char *objname, + u32 *out_sid, + bool kern) { + struct class_datum *cladatum = NULL; struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; struct avtab_key avkey; struct avtab_datum *avdatum; struct avtab_node *node; + u16 tclass; int rc = 0; + bool sock; if (!ss_initialized) { - switch (tclass) { - case SECCLASS_PROCESS: + switch (orig_tclass) { + case SECCLASS_PROCESS: /* kernel value */ *out_sid = ssid; break; default: @@ -827,31 +1420,47 @@ static int security_compute_sid(u32 ssid, goto out; } - POLICY_RDLOCK; + context_init(&newcontext); + + read_lock(&policy_rwlock); + + if (kern) { + tclass = unmap_class(orig_tclass); + sock = security_is_socket_class(orig_tclass); + } else { + tclass = orig_tclass; + sock = security_is_socket_class(map_class(tclass)); + } scontext = sidtab_search(&sidtab, ssid); if (!scontext) { - printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", - ssid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); rc = -EINVAL; goto out_unlock; } tcontext = sidtab_search(&sidtab, tsid); if (!tcontext) { - printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", - tsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); rc = -EINVAL; goto out_unlock; } - context_init(&newcontext); + if (tclass && tclass <= policydb.p_classes.nprim) + cladatum = policydb.class_val_to_struct[tclass - 1]; /* Set the user identity. */ switch (specified) { case AVTAB_TRANSITION: case AVTAB_CHANGE: - /* Use the process user identity. */ - newcontext.user = scontext->user; + if (cladatum && cladatum->default_user == DEFAULT_TARGET) { + newcontext.user = tcontext->user; + } else { + /* notice this gets both DEFAULT_SOURCE and unset */ + /* Use the process user identity. */ + newcontext.user = scontext->user; + } break; case AVTAB_MEMBER: /* Use the related object owner. */ @@ -859,18 +1468,31 @@ static int security_compute_sid(u32 ssid, break; } - /* Set the role and type to default values. */ - switch (tclass) { - case SECCLASS_PROCESS: - /* Use the current role and type of process. */ + /* Set the role to default values. */ + if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { newcontext.role = scontext->role; + } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { + newcontext.role = tcontext->role; + } else { + if ((tclass == policydb.process_class) || (sock == true)) + newcontext.role = scontext->role; + else + newcontext.role = OBJECT_R_VAL; + } + + /* Set the type to default values. */ + if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { newcontext.type = scontext->type; - break; - default: - /* Use the well-defined object role. */ - newcontext.role = OBJECT_R_VAL; - /* Use the type of the related object. */ + } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; + } else { + if ((tclass == policydb.process_class) || (sock == true)) { + /* Use the type of process. */ + newcontext.type = scontext->type; + } else { + /* Use the type of the related object. */ + newcontext.type = tcontext->type; + } } /* Look for a type transition/member/change rule. */ @@ -881,9 +1503,9 @@ static int security_compute_sid(u32 ssid, avdatum = avtab_search(&policydb.te_avtab, &avkey); /* If no permanent rule, also check for enabled conditional rules */ - if(!avdatum) { + if (!avdatum) { node = avtab_search_node(&policydb.te_cond_avtab, &avkey); - for (; node != NULL; node = avtab_search_node_next(node, specified)) { + for (; node; node = avtab_search_node_next(node, specified)) { if (node->key.specified & AVTAB_ENABLED) { avdatum = &node->datum; break; @@ -896,29 +1518,29 @@ static int security_compute_sid(u32 ssid, newcontext.type = avdatum->data; } + /* if we have a objname this is a file trans check so check those rules */ + if (objname) + filename_compute_type(&policydb, &newcontext, scontext->type, + tcontext->type, tclass, objname); + /* 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 (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) && + (roletr->tclass == tclass)) { + /* Use the role transition rule. */ + newcontext.role = roletr->new_role; + break; } } - break; - default: - break; } /* Set the MLS attributes. This is done last because it may allocate memory. */ - rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext); + rc = mls_compute_sid(scontext, tcontext, tclass, specified, + &newcontext, sock); if (rc) goto out_unlock; @@ -934,7 +1556,7 @@ static int security_compute_sid(u32 ssid, /* Obtain the sid for the context. */ rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); context_destroy(&newcontext); out: return rc; @@ -953,12 +1575,18 @@ out: * 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) +int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, + const struct qstr *qstr, u32 *out_sid) +{ + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + qstr ? qstr->name : NULL, out_sid, true); +} + +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + objname, out_sid, false); } /** @@ -979,7 +1607,8 @@ int security_member_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, NULL, + out_sid, false); } /** @@ -1000,90 +1629,8 @@ int security_change_sid(u32 ssid, 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; + return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL, + out_sid, false); } /* Clone the SID into the new SID table. */ @@ -1093,24 +1640,25 @@ static int clone_sid(u32 sid, { struct sidtab *s = arg; - return sidtab_insert(s, sid, context); + if (sid > SECINITSID_NUM) + return sidtab_insert(s, sid, context); + else + return 0; } static inline int convert_context_handle_invalid_context(struct context *context) { - int rc = 0; + char *s; + u32 len; - if (selinux_enforcing) { - rc = -EINVAL; - } else { - char *s; - u32 len; + if (selinux_enforcing) + return -EINVAL; - context_struct_to_string(context, &s, &len); - printk(KERN_ERR "security: context %s is invalid\n", s); + if (!context_struct_to_string(context, &s, &len)) { + printk(KERN_WARNING "SELinux: Context %s would be invalid if enforcing\n", s); kfree(s); } - return rc; + return 0; } struct convert_context_args { @@ -1131,48 +1679,112 @@ static int convert_context(u32 key, { struct convert_context_args *args; struct context oldc; + struct ocontext *oc; + struct mls_range *range; struct role_datum *role; struct type_datum *typdatum; struct user_datum *usrdatum; char *s; u32 len; - int rc; + int rc = 0; + + if (key <= SECINITSID_NUM) + goto out; args = p; + if (c->str) { + struct context ctx; + + rc = -ENOMEM; + s = kstrdup(c->str, GFP_KERNEL); + if (!s) + goto out; + + rc = string_to_context_struct(args->newp, NULL, s, + c->len, &ctx, SECSID_NULL); + kfree(s); + if (!rc) { + printk(KERN_INFO "SELinux: Context %s became valid (mapped).\n", + c->str); + /* Replace string with mapped representation. */ + kfree(c->str); + memcpy(c, &ctx, sizeof(*c)); + goto out; + } else if (rc == -EINVAL) { + /* Retain string representation for later mapping. */ + rc = 0; + goto out; + } else { + /* Other error condition, e.g. ENOMEM. */ + printk(KERN_ERR "SELinux: Unable to map context %s, rc = %d.\n", + c->str, -rc); + goto out; + } + } + rc = context_cpy(&oldc, c); if (rc) goto out; - rc = -EINVAL; - /* Convert the user. */ + rc = -EINVAL; usrdatum = hashtab_search(args->newp->p_users.table, - args->oldp->p_user_val_to_name[c->user - 1]); - if (!usrdatum) { + sym_name(args->oldp, SYM_USERS, c->user - 1)); + if (!usrdatum) goto bad; - } c->user = usrdatum->value; /* Convert the role. */ + rc = -EINVAL; role = hashtab_search(args->newp->p_roles.table, - args->oldp->p_role_val_to_name[c->role - 1]); - if (!role) { + sym_name(args->oldp, SYM_ROLES, c->role - 1)); + if (!role) goto bad; - } c->role = role->value; /* Convert the type. */ + rc = -EINVAL; typdatum = hashtab_search(args->newp->p_types.table, - args->oldp->p_type_val_to_name[c->type - 1]); - if (!typdatum) { + sym_name(args->oldp, SYM_TYPES, c->type - 1)); + if (!typdatum) goto bad; - } c->type = typdatum->value; - rc = mls_convert_context(args->oldp, args->newp, c); - if (rc) - goto bad; + /* Convert the MLS fields if dealing with MLS policies */ + if (args->oldp->mls_enabled && args->newp->mls_enabled) { + rc = mls_convert_context(args->oldp, args->newp, c); + if (rc) + goto bad; + } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) { + /* + * Switching between MLS and non-MLS policy: + * free any storage used by the MLS fields in the + * context for all existing entries in the sidtab. + */ + mls_context_destroy(c); + } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { + /* + * Switching between non-MLS and MLS policy: + * ensure that the MLS fields of the context for all + * existing entries in the sidtab are filled in with a + * suitable default value, likely taken from one of the + * initial SIDs. + */ + oc = args->newp->ocontexts[OCON_ISID]; + while (oc && oc->sid[0] != SECINITSID_UNLABELED) + oc = oc->next; + rc = -EINVAL; + if (!oc) { + printk(KERN_ERR "SELinux: unable to look up" + " the initial SIDs list\n"); + goto bad; + } + range = &oc->context[0].range; + rc = mls_range_set(c, range); + if (rc) + goto bad; + } /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, c)) { @@ -1182,17 +1794,36 @@ static int convert_context(u32 key, } context_destroy(&oldc); + + rc = 0; out: return rc; bad: - context_struct_to_string(&oldc, &s, &len); + /* Map old representation to string and save it. */ + rc = context_struct_to_string(&oldc, &s, &len); + if (rc) + return rc; context_destroy(&oldc); - printk(KERN_ERR "security: invalidating context %s\n", s); - kfree(s); + context_destroy(c); + c->str = s; + c->len = len; + printk(KERN_INFO "SELinux: Context %s became invalid (unmapped).\n", + c->str); + rc = 0; goto out; } -extern void selinux_complete_init(void); +static void security_load_policycaps(void) +{ + selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_NETPEER); + selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_OPENPERM); + selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_ALWAYSNETWORK); +} + +static int security_preserve_bools(struct policydb *p); /** * security_load_policy - Load a security policy configuration. @@ -1206,118 +1837,174 @@ extern void selinux_complete_init(void); */ int security_load_policy(void *data, size_t len) { - struct policydb oldpolicydb, newpolicydb; + struct policydb *oldpolicydb, *newpolicydb; struct sidtab oldsidtab, newsidtab; + struct selinux_mapping *oldmap, *map = NULL; struct convert_context_args args; u32 seqno; + u16 map_size; int rc = 0; struct policy_file file = { data, len }, *fp = &file; - LOAD_LOCK; + oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); + if (!oldpolicydb) { + rc = -ENOMEM; + goto out; + } + newpolicydb = oldpolicydb + 1; if (!ss_initialized) { avtab_cache_init(); - if (policydb_read(&policydb, fp)) { - LOAD_UNLOCK; + rc = policydb_read(&policydb, fp); + if (rc) { avtab_cache_destroy(); - return -EINVAL; + goto out; } - if (policydb_load_isids(&policydb, &sidtab)) { - LOAD_UNLOCK; + + policydb.len = len; + rc = selinux_set_mapping(&policydb, secclass_map, + ¤t_mapping, + ¤t_mapping_size); + if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); - return -EINVAL; + goto out; + } + + rc = policydb_load_isids(&policydb, &sidtab); + if (rc) { + policydb_destroy(&policydb); + avtab_cache_destroy(); + goto out; } - policydb_loaded_version = policydb.policyvers; + + security_load_policycaps(); ss_initialized = 1; seqno = ++latest_granting; - LOAD_UNLOCK; selinux_complete_init(); avc_ss_reset(seqno); selnl_notify_policyload(seqno); - return 0; + selinux_status_update_policyload(seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); + goto out; } #if 0 sidtab_hash_eval(&sidtab, "sids"); #endif - if (policydb_read(&newpolicydb, fp)) { - LOAD_UNLOCK; - return -EINVAL; + rc = policydb_read(newpolicydb, fp); + if (rc) + goto out; + + newpolicydb->len = len; + /* If switching between different policy types, log MLS status */ + if (policydb.mls_enabled && !newpolicydb->mls_enabled) + printk(KERN_INFO "SELinux: Disabling MLS support...\n"); + else if (!policydb.mls_enabled && newpolicydb->mls_enabled) + printk(KERN_INFO "SELinux: Enabling MLS support...\n"); + + rc = policydb_load_isids(newpolicydb, &newsidtab); + if (rc) { + printk(KERN_ERR "SELinux: unable to load the initial SIDs\n"); + policydb_destroy(newpolicydb); + goto out; } - sidtab_init(&newsidtab); + rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size); + if (rc) + goto err; - /* 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; + rc = security_preserve_bools(newpolicydb); + if (rc) { + printk(KERN_ERR "SELinux: unable to preserve booleans\n"); goto err; } /* Clone the SID table. */ sidtab_shutdown(&sidtab); - if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { - rc = -ENOMEM; + + rc = sidtab_map(&sidtab, clone_sid, &newsidtab); + if (rc) goto err; - } - /* Convert the internal representations of contexts - in the new SID table and remove invalid SIDs. */ + /* + * Convert the internal representations of contexts + * in the new SID table. + */ args.oldp = &policydb; - args.newp = &newpolicydb; - sidtab_map_remove_on_error(&newsidtab, convert_context, &args); + args.newp = newpolicydb; + rc = sidtab_map(&newsidtab, convert_context, &args); + if (rc) { + printk(KERN_ERR "SELinux: unable to convert the internal" + " representation of contexts in the new SID" + " table\n"); + goto err; + } /* Save the old policydb and SID table to free later. */ - memcpy(&oldpolicydb, &policydb, sizeof policydb); + memcpy(oldpolicydb, &policydb, sizeof(policydb)); sidtab_set(&oldsidtab, &sidtab); /* Install the new policydb and SID table. */ - POLICY_WRLOCK; - memcpy(&policydb, &newpolicydb, sizeof policydb); + write_lock_irq(&policy_rwlock); + memcpy(&policydb, newpolicydb, sizeof(policydb)); sidtab_set(&sidtab, &newsidtab); + security_load_policycaps(); + oldmap = current_mapping; + current_mapping = map; + current_mapping_size = map_size; seqno = ++latest_granting; - policydb_loaded_version = policydb.policyvers; - POLICY_WRUNLOCK; - LOAD_UNLOCK; + write_unlock_irq(&policy_rwlock); /* Free the old policydb and SID table. */ - policydb_destroy(&oldpolicydb); + policydb_destroy(oldpolicydb); sidtab_destroy(&oldsidtab); + kfree(oldmap); avc_ss_reset(seqno); selnl_notify_policyload(seqno); + selinux_status_update_policyload(seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); - return 0; + rc = 0; + goto out; err: - LOAD_UNLOCK; + kfree(map); sidtab_destroy(&newsidtab); - policydb_destroy(&newpolicydb); + policydb_destroy(newpolicydb); + +out: + kfree(oldpolicydb); return rc; +} + +size_t security_policydb_len(void) +{ + size_t len; + + read_lock(&policy_rwlock); + len = policydb.len; + read_unlock(&policy_rwlock); + return len; } /** * 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) +int security_port_sid(u8 protocol, u16 port, u32 *out_sid) { struct ocontext *c; int rc = 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_PORT]; while (c) { @@ -1342,7 +2029,7 @@ int security_port_sid(u16 domain, } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1350,16 +2037,13 @@ out: * 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 security_netif_sid(char *name, u32 *if_sid) { int rc = 0; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_NETIF]; while (c) { @@ -1382,14 +2066,11 @@ int security_netif_sid(char *name, goto out; } *if_sid = c->sid[0]; - *msg_sid = c->sid[1]; - } else { + } else *if_sid = SECINITSID_NETIF; - *msg_sid = SECINITSID_NETMSG; - } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1397,8 +2078,8 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) { int i, fail = 0; - for(i = 0; i < 4; i++) - if(addr[i] != (input[i] & mask[i])) { + for (i = 0; i < 4; i++) + if (addr[i] != (input[i] & mask[i])) { fail = 1; break; } @@ -1418,19 +2099,18 @@ int security_node_sid(u16 domain, u32 addrlen, u32 *out_sid) { - int rc = 0; + int rc; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); switch (domain) { case AF_INET: { u32 addr; - if (addrlen != sizeof(u32)) { - rc = -EINVAL; + rc = -EINVAL; + if (addrlen != sizeof(u32)) goto out; - } addr = *((u32 *)addrp); @@ -1444,10 +2124,9 @@ int security_node_sid(u16 domain, } case AF_INET6: - if (addrlen != sizeof(u64) * 2) { - rc = -EINVAL; + rc = -EINVAL; + if (addrlen != sizeof(u64) * 2) goto out; - } c = policydb.ocontexts[OCON_NODE6]; while (c) { if (match_ipv6_addrmask(addrp, c->u.node6.addr, @@ -1458,6 +2137,7 @@ int security_node_sid(u16 domain, break; default: + rc = 0; *out_sid = SECINITSID_NODE; goto out; } @@ -1475,8 +2155,9 @@ int security_node_sid(u16 domain, *out_sid = SECINITSID_NODE; } + rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1497,80 +2178,65 @@ out: */ int security_get_user_sids(u32 fromsid, - char *username, + char *username, u32 **sids, u32 *nel) { struct context *fromcon, usercon; - u32 *mysids, *mysids2, sid; + u32 *mysids = NULL, *mysids2, sid; u32 mynel = 0, maxnel = SIDS_NEL; struct user_datum *user; struct role_datum *role; - struct av_decision avd; struct ebitmap_node *rnode, *tnode; int rc = 0, i, j; - if (!ss_initialized) { - *sids = NULL; - *nel = 0; + *sids = NULL; + *nel = 0; + + if (!ss_initialized) goto out; - } - POLICY_RDLOCK; + read_lock(&policy_rwlock); + context_init(&usercon); + + rc = -EINVAL; fromcon = sidtab_search(&sidtab, fromsid); - if (!fromcon) { - rc = -EINVAL; + if (!fromcon) goto out_unlock; - } + rc = -EINVAL; user = hashtab_search(policydb.p_users.table, username); - if (!user) { - rc = -EINVAL; + if (!user) goto out_unlock; - } + usercon.user = user->value; + rc = -ENOMEM; mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC); - if (!mysids) { - rc = -ENOMEM; + if (!mysids) goto out_unlock; - } - ebitmap_for_each_bit(&user->roles, rnode, i) { - if (!ebitmap_node_get_bit(rnode, i)) - continue; + ebitmap_for_each_positive_bit(&user->roles, rnode, i) { role = policydb.role_val_to_struct[i]; - usercon.role = i+1; - ebitmap_for_each_bit(&role->types, tnode, j) { - if (!ebitmap_node_get_bit(tnode, j)) - continue; - usercon.type = j+1; + usercon.role = i + 1; + ebitmap_for_each_positive_bit(&role->types, tnode, j) { + usercon.type = j + 1; if (mls_setup_user_range(fromcon, user, &usercon)) continue; - 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); + if (rc) goto out_unlock; - } if (mynel < maxnel) { mysids[mynel++] = sid; } else { + rc = -ENOMEM; maxnel += SIDS_NEL; mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); - if (!mysids2) { - rc = -ENOMEM; - kfree(mysids); + if (!mysids2) goto out_unlock; - } memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); kfree(mysids); mysids = mysids2; @@ -1578,12 +2244,34 @@ int security_get_user_sids(u32 fromsid, } } } - - *sids = mysids; - *nel = mynel; - + rc = 0; out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + if (rc || !mynel) { + kfree(mysids); + goto out; + } + + rc = -ENOMEM; + mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); + if (!mysids2) { + kfree(mysids); + goto out; + } + for (i = 0, j = 0; i < mynel; i++) { + struct av_decision dummy_avd; + rc = avc_has_perm_noaudit(fromsid, mysids[i], + SECCLASS_PROCESS, /* kernel value */ + PROCESS__TRANSITION, AVC_STRICT, + &dummy_avd); + if (!rc) + mysids2[j++] = mysids[i]; + cond_resched(); + } + rc = 0; + kfree(mysids); + *sids = mysids2; + *nel = j; out: return rc; } @@ -1600,16 +2288,23 @@ out: * transition SIDs or task SIDs. */ int security_genfs_sid(const char *fstype, - char *path, - u16 sclass, + char *path, + u16 orig_sclass, u32 *sid) { int len; + u16 sclass; struct genfs *genfs; struct ocontext *c; - int rc = 0, cmp = 0; + int rc, cmp = 0; + + while (path[0] == '/' && path[1] == '/') + path++; - POLICY_RDLOCK; + read_lock(&policy_rwlock); + + sclass = unmap_class(orig_sclass); + *sid = SECINITSID_UNLABELED; for (genfs = policydb.genfs; genfs; genfs = genfs->next) { cmp = strcmp(fstype, genfs->fstype); @@ -1617,11 +2312,9 @@ int security_genfs_sid(const char *fstype, break; } - if (!genfs || cmp) { - *sid = SECINITSID_UNLABELED; - rc = -ENOENT; + rc = -ENOENT; + if (!genfs || cmp) goto out; - } for (c = genfs->head; c; c = c->next) { len = strlen(c->u.name); @@ -1630,41 +2323,35 @@ int security_genfs_sid(const char *fstype, break; } - if (!c) { - *sid = SECINITSID_UNLABELED; - rc = -ENOENT; + rc = -ENOENT; + if (!c) goto out; - } if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, - &c->context[0], - &c->sid[0]); + rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } *sid = c->sid[0]; + rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } /** * security_fs_use - Determine how to handle labeling for a filesystem. - * @fstype: filesystem type - * @behavior: labeling behavior - * @sid: SID for filesystem (superblock) + * @sb: superblock in question */ -int security_fs_use( - const char *fstype, - unsigned int *behavior, - u32 *sid) +int security_fs_use(struct super_block *sb) { int rc = 0; struct ocontext *c; + struct superblock_security_struct *sbsec = sb->s_security; + const char *fstype = sb->s_type->name; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_FSUSE]; while (c) { @@ -1674,65 +2361,69 @@ int security_fs_use( } if (c) { - *behavior = c->v.behavior; + sbsec->behavior = c->v.behavior; if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, - &c->context[0], + rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } - *sid = c->sid[0]; + sbsec->sid = c->sid[0]; } else { - rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); + rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid); if (rc) { - *behavior = SECURITY_FS_USE_NONE; + sbsec->behavior = SECURITY_FS_USE_NONE; rc = 0; } else { - *behavior = SECURITY_FS_USE_GENFS; + sbsec->behavior = SECURITY_FS_USE_GENFS; } } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } int security_get_bools(int *len, char ***names, int **values) { - int i, rc = -ENOMEM; + int i, rc; - POLICY_RDLOCK; + read_lock(&policy_rwlock); *names = NULL; *values = NULL; + rc = 0; *len = policydb.p_bools.nprim; - if (!*len) { - rc = 0; + if (!*len) goto out; - } - *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC); + rc = -ENOMEM; + *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC); if (!*names) goto err; - *values = kcalloc(*len, sizeof(int), GFP_ATOMIC); + rc = -ENOMEM; + *values = kcalloc(*len, sizeof(int), GFP_ATOMIC); if (!*values) goto err; for (i = 0; i < *len; i++) { size_t name_len; + (*values)[i] = policydb.bool_val_to_struct[i]->state; - name_len = strlen(policydb.p_bool_val_to_name[i]) + 1; - (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC); + name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1; + + rc = -ENOMEM; + (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC); if (!(*names)[i]) goto err; - strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len); + + strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len); (*names)[i][name_len - 1] = 0; } rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; err: if (*names) { @@ -1746,64 +2437,826 @@ err: int security_set_bools(int len, int *values) { - int i, rc = 0; + int i, rc; int lenp, seqno = 0; struct cond_node *cur; - POLICY_WRLOCK; + write_lock_irq(&policy_rwlock); + rc = -EFAULT; lenp = policydb.p_bools.nprim; - if (len != lenp) { - rc = -EFAULT; + if (len != lenp) goto out; - } - printk(KERN_INFO "security: committed booleans { "); for (i = 0; i < len; i++) { - if (values[i]) { + if (!!values[i] != policydb.bool_val_to_struct[i]->state) { + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_MAC_CONFIG_CHANGE, + "bool=%s val=%d old_val=%d auid=%u ses=%u", + sym_name(&policydb, SYM_BOOLS, i), + !!values[i], + policydb.bool_val_to_struct[i]->state, + from_kuid(&init_user_ns, audit_get_loginuid(current)), + audit_get_sessionid(current)); + } + if (values[i]) policydb.bool_val_to_struct[i]->state = 1; - } else { + else policydb.bool_val_to_struct[i]->state = 0; - } - if (i != 0) - printk(", "); - printk("%s:%d", policydb.p_bool_val_to_name[i], - policydb.bool_val_to_struct[i]->state); } - printk(" }\n"); - for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { + for (cur = policydb.cond_list; cur; cur = cur->next) { rc = evaluate_cond_node(&policydb, cur); if (rc) goto out; } seqno = ++latest_granting; - + rc = 0; out: - POLICY_WRUNLOCK; + write_unlock_irq(&policy_rwlock); if (!rc) { avc_ss_reset(seqno); selnl_notify_policyload(seqno); + selinux_status_update_policyload(seqno); + selinux_xfrm_notify_policyload(); } return rc; } int security_get_bool_value(int bool) { - int rc = 0; + int rc; int len; - POLICY_RDLOCK; + read_lock(&policy_rwlock); + rc = -EFAULT; len = policydb.p_bools.nprim; - if (bool >= len) { - rc = -EFAULT; + if (bool >= len) goto out; - } rc = policydb.bool_val_to_struct[bool]->state; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + return rc; +} + +static int security_preserve_bools(struct policydb *p) +{ + int rc, nbools = 0, *bvalues = NULL, i; + char **bnames = NULL; + struct cond_bool_datum *booldatum; + struct cond_node *cur; + + rc = security_get_bools(&nbools, &bnames, &bvalues); + if (rc) + goto out; + for (i = 0; i < nbools; i++) { + booldatum = hashtab_search(p->p_bools.table, bnames[i]); + if (booldatum) + booldatum->state = bvalues[i]; + } + for (cur = p->cond_list; cur; cur = cur->next) { + rc = evaluate_cond_node(p, cur); + if (rc) + goto out; + } + +out: + if (bnames) { + for (i = 0; i < nbools; i++) + kfree(bnames[i]); + } + kfree(bnames); + kfree(bvalues); + return rc; +} + +/* + * security_sid_mls_copy() - computes a new sid based on the given + * sid and the mls portion of mls_sid. + */ +int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) +{ + struct context *context1; + struct context *context2; + struct context newcon; + char *s; + u32 len; + int rc; + + rc = 0; + if (!ss_initialized || !policydb.mls_enabled) { + *new_sid = sid; + goto out; + } + + context_init(&newcon); + + read_lock(&policy_rwlock); + + rc = -EINVAL; + context1 = sidtab_search(&sidtab, sid); + if (!context1) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, sid); + goto out_unlock; + } + + rc = -EINVAL; + context2 = sidtab_search(&sidtab, mls_sid); + if (!context2) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, mls_sid); + goto out_unlock; + } + + newcon.user = context1->user; + newcon.role = context1->role; + newcon.type = context1->type; + rc = mls_context_cpy(&newcon, context2); + if (rc) + goto out_unlock; + + /* Check the validity of the new context. */ + if (!policydb_context_isvalid(&policydb, &newcon)) { + rc = convert_context_handle_invalid_context(&newcon); + if (rc) { + if (!context_struct_to_string(&newcon, &s, &len)) { + audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, + "security_sid_mls_copy: invalid context %s", s); + kfree(s); + } + goto out_unlock; + } + } + + rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid); +out_unlock: + read_unlock(&policy_rwlock); + context_destroy(&newcon); +out: + return rc; +} + +/** + * security_net_peersid_resolve - Compare and resolve two network peer SIDs + * @nlbl_sid: NetLabel SID + * @nlbl_type: NetLabel labeling protocol type + * @xfrm_sid: XFRM SID + * + * Description: + * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be + * resolved into a single SID it is returned via @peer_sid and the function + * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function + * returns a negative value. A table summarizing the behavior is below: + * + * | function return | @sid + * ------------------------------+-----------------+----------------- + * no peer labels | 0 | SECSID_NULL + * single peer label | 0 | <peer_label> + * multiple, consistent labels | 0 | <peer_label> + * multiple, inconsistent labels | -<errno> | SECSID_NULL + * + */ +int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, + u32 xfrm_sid, + u32 *peer_sid) +{ + int rc; + struct context *nlbl_ctx; + struct context *xfrm_ctx; + + *peer_sid = SECSID_NULL; + + /* handle the common (which also happens to be the set of easy) cases + * right away, these two if statements catch everything involving a + * single or absent peer SID/label */ + if (xfrm_sid == SECSID_NULL) { + *peer_sid = nlbl_sid; + return 0; + } + /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label + * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label + * is present */ + if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) { + *peer_sid = xfrm_sid; + return 0; + } + + /* we don't need to check ss_initialized here since the only way both + * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the + * security server was initialized and ss_initialized was true */ + if (!policydb.mls_enabled) + return 0; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + nlbl_ctx = sidtab_search(&sidtab, nlbl_sid); + if (!nlbl_ctx) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, nlbl_sid); + goto out; + } + rc = -EINVAL; + xfrm_ctx = sidtab_search(&sidtab, xfrm_sid); + if (!xfrm_ctx) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, xfrm_sid); + goto out; + } + rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES); + if (rc) + goto out; + + /* at present NetLabel SIDs/labels really only carry MLS + * information so if the MLS portion of the NetLabel SID + * matches the MLS portion of the labeled XFRM SID/label + * then pass along the XFRM SID as it is the most + * expressive */ + *peer_sid = xfrm_sid; +out: + read_unlock(&policy_rwlock); + return rc; +} + +static int get_classes_callback(void *k, void *d, void *args) +{ + struct class_datum *datum = d; + char *name = k, **classes = args; + int value = datum->value - 1; + + classes[value] = kstrdup(name, GFP_ATOMIC); + if (!classes[value]) + return -ENOMEM; + + return 0; +} + +int security_get_classes(char ***classes, int *nclasses) +{ + int rc; + + read_lock(&policy_rwlock); + + rc = -ENOMEM; + *nclasses = policydb.p_classes.nprim; + *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC); + if (!*classes) + goto out; + + rc = hashtab_map(policydb.p_classes.table, get_classes_callback, + *classes); + if (rc) { + int i; + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + } + +out: + read_unlock(&policy_rwlock); return rc; } + +static int get_permissions_callback(void *k, void *d, void *args) +{ + struct perm_datum *datum = d; + char *name = k, **perms = args; + int value = datum->value - 1; + + perms[value] = kstrdup(name, GFP_ATOMIC); + if (!perms[value]) + return -ENOMEM; + + return 0; +} + +int security_get_permissions(char *class, char ***perms, int *nperms) +{ + int rc, i; + struct class_datum *match; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + match = hashtab_search(policydb.p_classes.table, class); + if (!match) { + printk(KERN_ERR "SELinux: %s: unrecognized class %s\n", + __func__, class); + goto out; + } + + rc = -ENOMEM; + *nperms = match->permissions.nprim; + *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC); + if (!*perms) + goto out; + + if (match->comdatum) { + rc = hashtab_map(match->comdatum->permissions.table, + get_permissions_callback, *perms); + if (rc) + goto err; + } + + rc = hashtab_map(match->permissions.table, get_permissions_callback, + *perms); + if (rc) + goto err; + +out: + read_unlock(&policy_rwlock); + return rc; + +err: + read_unlock(&policy_rwlock); + for (i = 0; i < *nperms; i++) + kfree((*perms)[i]); + kfree(*perms); + return rc; +} + +int security_get_reject_unknown(void) +{ + return policydb.reject_unknown; +} + +int security_get_allow_unknown(void) +{ + return policydb.allow_unknown; +} + +/** + * security_policycap_supported - Check for a specific policy capability + * @req_cap: capability + * + * Description: + * This function queries the currently loaded policy to see if it supports the + * capability specified by @req_cap. Returns true (1) if the capability is + * supported, false (0) if it isn't supported. + * + */ +int security_policycap_supported(unsigned int req_cap) +{ + int rc; + + read_lock(&policy_rwlock); + rc = ebitmap_get_bit(&policydb.policycaps, req_cap); + read_unlock(&policy_rwlock); + + return rc; +} + +struct selinux_audit_rule { + u32 au_seqno; + struct context au_ctxt; +}; + +void selinux_audit_rule_free(void *vrule) +{ + struct selinux_audit_rule *rule = vrule; + + if (rule) { + context_destroy(&rule->au_ctxt); + kfree(rule); + } +} + +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) +{ + struct selinux_audit_rule *tmprule; + struct role_datum *roledatum; + struct type_datum *typedatum; + struct user_datum *userdatum; + struct selinux_audit_rule **rule = (struct selinux_audit_rule **)vrule; + int rc = 0; + + *rule = NULL; + + if (!ss_initialized) + return -EOPNOTSUPP; + + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + /* only 'equals' and 'not equals' fit user, role, and type */ + if (op != Audit_equal && op != Audit_not_equal) + return -EINVAL; + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + /* we do not allow a range, indicated by the presence of '-' */ + if (strchr(rulestr, '-')) + return -EINVAL; + break; + default: + /* only the above fields are valid */ + return -EINVAL; + } + + tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); + if (!tmprule) + return -ENOMEM; + + context_init(&tmprule->au_ctxt); + + read_lock(&policy_rwlock); + + tmprule->au_seqno = latest_granting; + + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: + rc = -EINVAL; + userdatum = hashtab_search(policydb.p_users.table, rulestr); + if (!userdatum) + goto out; + tmprule->au_ctxt.user = userdatum->value; + break; + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: + rc = -EINVAL; + roledatum = hashtab_search(policydb.p_roles.table, rulestr); + if (!roledatum) + goto out; + tmprule->au_ctxt.role = roledatum->value; + break; + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: + rc = -EINVAL; + typedatum = hashtab_search(policydb.p_types.table, rulestr); + if (!typedatum) + goto out; + tmprule->au_ctxt.type = typedatum->value; + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC); + if (rc) + goto out; + break; + } + rc = 0; +out: + read_unlock(&policy_rwlock); + + if (rc) { + selinux_audit_rule_free(tmprule); + tmprule = NULL; + } + + *rule = tmprule; + + return rc; +} + +/* Check to see if the rule contains any selinux fields */ +int selinux_audit_rule_known(struct audit_krule *rule) +{ + int i; + + for (i = 0; i < rule->field_count; i++) { + struct audit_field *f = &rule->fields[i]; + switch (f->type) { + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + return 1; + } + } + + return 0; +} + +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, + struct audit_context *actx) +{ + struct context *ctxt; + struct mls_level *level; + struct selinux_audit_rule *rule = vrule; + int match = 0; + + if (unlikely(!rule)) { + WARN_ONCE(1, "selinux_audit_rule_match: missing rule\n"); + return -ENOENT; + } + + read_lock(&policy_rwlock); + + if (rule->au_seqno < latest_granting) { + match = -ESTALE; + goto out; + } + + ctxt = sidtab_search(&sidtab, sid); + if (unlikely(!ctxt)) { + WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", + sid); + match = -ENOENT; + goto out; + } + + /* a field/op pair that is not caught here will simply fall through + without a match */ + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: + switch (op) { + case Audit_equal: + match = (ctxt->user == rule->au_ctxt.user); + break; + case Audit_not_equal: + match = (ctxt->user != rule->au_ctxt.user); + break; + } + break; + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: + switch (op) { + case Audit_equal: + match = (ctxt->role == rule->au_ctxt.role); + break; + case Audit_not_equal: + match = (ctxt->role != rule->au_ctxt.role); + break; + } + break; + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: + switch (op) { + case Audit_equal: + match = (ctxt->type == rule->au_ctxt.type); + break; + case Audit_not_equal: + match = (ctxt->type != rule->au_ctxt.type); + break; + } + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + level = ((field == AUDIT_SUBJ_SEN || + field == AUDIT_OBJ_LEV_LOW) ? + &ctxt->range.level[0] : &ctxt->range.level[1]); + switch (op) { + case Audit_equal: + match = mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_not_equal: + match = !mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_lt: + match = (mls_level_dom(&rule->au_ctxt.range.level[0], + level) && + !mls_level_eq(&rule->au_ctxt.range.level[0], + level)); + break; + case Audit_le: + match = mls_level_dom(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_gt: + match = (mls_level_dom(level, + &rule->au_ctxt.range.level[0]) && + !mls_level_eq(level, + &rule->au_ctxt.range.level[0])); + break; + case Audit_ge: + match = mls_level_dom(level, + &rule->au_ctxt.range.level[0]); + break; + } + } + +out: + read_unlock(&policy_rwlock); + return match; +} + +static int (*aurule_callback)(void) = audit_update_lsm_rules; + +static int aurule_avc_callback(u32 event) +{ + int err = 0; + + if (event == AVC_CALLBACK_RESET && aurule_callback) + err = aurule_callback(); + return err; +} + +static int __init aurule_init(void) +{ + int err; + + err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET); + if (err) + panic("avc_add_callback() failed, error %d\n", err); + + return err; +} +__initcall(aurule_init); + +#ifdef CONFIG_NETLABEL +/** + * security_netlbl_cache_add - Add an entry to the NetLabel cache + * @secattr: the NetLabel packet security attributes + * @sid: the SELinux SID + * + * Description: + * Attempt to cache the context in @ctx, which was derived from the packet in + * @skb, in the NetLabel subsystem cache. This function assumes @secattr has + * already been initialized. + * + */ +static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, + u32 sid) +{ + u32 *sid_cache; + + sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC); + if (sid_cache == NULL) + return; + secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); + if (secattr->cache == NULL) { + kfree(sid_cache); + return; + } + + *sid_cache = sid; + secattr->cache->free = kfree; + secattr->cache->data = sid_cache; + secattr->flags |= NETLBL_SECATTR_CACHE; +} + +/** + * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID + * @secattr: the NetLabel packet security attributes + * @sid: the SELinux SID + * + * Description: + * Convert the given NetLabel security attributes in @secattr into a + * SELinux SID. If the @secattr field does not contain a full SELinux + * SID/context then use SECINITSID_NETMSG as the foundation. If possible the + * 'cache' field of @secattr is set and the CACHE flag is set; this is to + * allow the @secattr to be used by NetLabel to cache the secattr to SID + * conversion for future lookups. Returns zero on success, negative values on + * failure. + * + */ +int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, + u32 *sid) +{ + int rc; + struct context *ctx; + struct context ctx_new; + + if (!ss_initialized) { + *sid = SECSID_NULL; + return 0; + } + + read_lock(&policy_rwlock); + + if (secattr->flags & NETLBL_SECATTR_CACHE) + *sid = *(u32 *)secattr->cache->data; + else if (secattr->flags & NETLBL_SECATTR_SECID) + *sid = secattr->attr.secid; + else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { + rc = -EIDRM; + ctx = sidtab_search(&sidtab, SECINITSID_NETMSG); + if (ctx == NULL) + goto out; + + context_init(&ctx_new); + ctx_new.user = ctx->user; + ctx_new.role = ctx->role; + ctx_new.type = ctx->type; + mls_import_netlbl_lvl(&ctx_new, secattr); + if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { + rc = ebitmap_netlbl_import(&ctx_new.range.level[0].cat, + secattr->attr.mls.cat); + if (rc) + goto out; + memcpy(&ctx_new.range.level[1].cat, + &ctx_new.range.level[0].cat, + sizeof(ctx_new.range.level[0].cat)); + } + rc = -EIDRM; + if (!mls_context_isvalid(&policydb, &ctx_new)) + goto out_free; + + rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid); + if (rc) + goto out_free; + + security_netlbl_cache_add(secattr, *sid); + + ebitmap_destroy(&ctx_new.range.level[0].cat); + } else + *sid = SECSID_NULL; + + read_unlock(&policy_rwlock); + return 0; +out_free: + ebitmap_destroy(&ctx_new.range.level[0].cat); +out: + read_unlock(&policy_rwlock); + return rc; +} + +/** + * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr + * @sid: the SELinux SID + * @secattr: the NetLabel packet security attributes + * + * Description: + * Convert the given SELinux SID in @sid into a NetLabel security attribute. + * Returns zero on success, negative values on failure. + * + */ +int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) +{ + int rc; + struct context *ctx; + + if (!ss_initialized) + return 0; + + read_lock(&policy_rwlock); + + rc = -ENOENT; + ctx = sidtab_search(&sidtab, sid); + if (ctx == NULL) + goto out; + + rc = -ENOMEM; + secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1), + GFP_ATOMIC); + if (secattr->domain == NULL) + goto out; + + secattr->attr.secid = sid; + secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID; + mls_export_netlbl_lvl(ctx, secattr); + rc = mls_export_netlbl_cat(ctx, secattr); +out: + read_unlock(&policy_rwlock); + return rc; +} +#endif /* CONFIG_NETLABEL */ + +/** + * security_read_policy - read the policy. + * @data: binary policy data + * @len: length of data in bytes + * + */ +int security_read_policy(void **data, size_t *len) +{ + int rc; + struct policy_file fp; + + if (!ss_initialized) + return -EINVAL; + + *len = security_policydb_len(); + + *data = vmalloc_user(*len); + if (!*data) + return -ENOMEM; + + fp.data = *data; + fp.len = *len; + + read_lock(&policy_rwlock); + rc = policydb_write(&policydb, &fp); + read_unlock(&policy_rwlock); + + if (rc) + return rc; + + *len = (unsigned long)fp.data - (unsigned long)*data; + return 0; + +} diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 871c33bd074..5840a35155f 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -7,7 +7,6 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/errno.h> -#include <linux/sched.h> #include "flask.h" #include "security.h" #include "sidtab.h" @@ -15,10 +14,6 @@ #define SIDTAB_HASH(sid) \ (sid & SIDTAB_HASH_MASK) -#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) -#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) -#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) - int sidtab_init(struct sidtab *s) { int i; @@ -31,7 +26,7 @@ int sidtab_init(struct sidtab *s) s->nel = 0; s->next_sid = 1; s->shutdown = 0; - INIT_SIDTAB_LOCK(s); + spin_lock_init(&s->lock); return 0; } @@ -48,7 +43,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) hvalue = SIDTAB_HASH(sid); prev = NULL; cur = s->htable[hvalue]; - while (cur != NULL && sid > cur->sid) { + while (cur && sid > cur->sid) { prev = cur; cur = cur->next; } @@ -87,7 +82,7 @@ out: return rc; } -struct context *sidtab_search(struct sidtab *s, u32 sid) +static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) { int hvalue; struct sidtab_node *cur; @@ -97,15 +92,18 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) hvalue = SIDTAB_HASH(sid); cur = s->htable[hvalue]; - while (cur != NULL && sid > cur->sid) + while (cur && sid > cur->sid) cur = cur->next; - if (cur == NULL || sid != cur->sid) { + if (force && cur && sid == cur->sid && cur->context.len) + return &cur->context; + + if (cur == NULL || sid != cur->sid || cur->context.len) { /* Remap invalid SIDs to the unlabeled SID. */ sid = SECINITSID_UNLABELED; hvalue = SIDTAB_HASH(sid); cur = s->htable[hvalue]; - while (cur != NULL && sid > cur->sid) + while (cur && sid > cur->sid) cur = cur->next; if (!cur || sid != cur->sid) return NULL; @@ -114,6 +112,16 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) return &cur->context; } +struct context *sidtab_search(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 0); +} + +struct context *sidtab_search_force(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 1); +} + int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, struct context *context, @@ -128,7 +136,7 @@ int sidtab_map(struct sidtab *s, for (i = 0; i < SIDTAB_SIZE; i++) { cur = s->htable[i]; - while (cur != NULL) { + while (cur) { rc = apply(cur->sid, &cur->context, args); if (rc) goto out; @@ -139,43 +147,15 @@ out: return rc; } -void sidtab_map_remove_on_error(struct sidtab *s, - int (*apply) (u32 sid, - struct context *context, - void *args), - void *args) +static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) { - int i, ret; - struct sidtab_node *last, *cur, *temp; + BUG_ON(loc >= SIDTAB_CACHE_LEN); - 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; - } - } + while (loc > 0) { + s->cache[loc] = s->cache[loc - 1]; + loc--; } - - return; + s->cache[0] = n; } static inline u32 sidtab_search_context(struct sidtab *s, @@ -186,15 +166,34 @@ static inline u32 sidtab_search_context(struct sidtab *s, for (i = 0; i < SIDTAB_SIZE; i++) { cur = s->htable[i]; - while (cur != NULL) { - if (context_cmp(&cur->context, context)) + while (cur) { + if (context_cmp(&cur->context, context)) { + sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1); return cur->sid; + } cur = cur->next; } } return 0; } +static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context) +{ + int i; + struct sidtab_node *node; + + for (i = 0; i < SIDTAB_CACHE_LEN; i++) { + node = s->cache[i]; + if (unlikely(!node)) + return 0; + if (context_cmp(&node->context, context)) { + sidtab_update_cache(s, node, i); + return node->sid; + } + } + return 0; +} + int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *out_sid) @@ -205,9 +204,11 @@ int sidtab_context_to_sid(struct sidtab *s, *out_sid = SECSID_NULL; - sid = sidtab_search_context(s, context); + sid = sidtab_search_cache(s, context); + if (!sid) + sid = sidtab_search_context(s, context); if (!sid) { - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); /* Rescan now that we hold the lock. */ sid = sidtab_search_context(s, context); if (sid) @@ -218,11 +219,15 @@ int sidtab_context_to_sid(struct sidtab *s, goto unlock_out; } sid = s->next_sid++; + if (context->len) + printk(KERN_INFO + "SELinux: Context %s is not valid (left unmapped).\n", + context->str); ret = sidtab_insert(s, sid, context); if (ret) s->next_sid--; unlock_out: - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } if (ret) @@ -254,7 +259,7 @@ void sidtab_hash_eval(struct sidtab *h, char *tag) } } - printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " + printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest " "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, max_chain_len); } @@ -269,7 +274,7 @@ void sidtab_destroy(struct sidtab *s) for (i = 0; i < SIDTAB_SIZE; i++) { cur = s->htable[i]; - while (cur != NULL) { + while (cur) { temp = cur; cur = cur->next; context_destroy(&temp->context); @@ -286,20 +291,23 @@ void sidtab_destroy(struct sidtab *s) void sidtab_set(struct sidtab *dst, struct sidtab *src) { unsigned long flags; + int i; - SIDTAB_LOCK(src, flags); + spin_lock_irqsave(&src->lock, flags); dst->htable = src->htable; dst->nel = src->nel; dst->next_sid = src->next_sid; dst->shutdown = 0; - SIDTAB_UNLOCK(src, flags); + for (i = 0; i < SIDTAB_CACHE_LEN; i++) + dst->cache[i] = NULL; + spin_unlock_irqrestore(&src->lock, flags); } void sidtab_shutdown(struct sidtab *s) { unsigned long flags; - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); s->shutdown = 1; - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 2fe9dfa3eb3..84dc154d938 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -26,12 +26,15 @@ struct sidtab { unsigned int nel; /* number of elements */ unsigned int next_sid; /* next SID to allocate */ unsigned char shutdown; +#define SIDTAB_CACHE_LEN 3 + struct sidtab_node *cache[SIDTAB_CACHE_LEN]; 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); +struct context *sidtab_search_force(struct sidtab *s, u32 sid); int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, @@ -39,12 +42,6 @@ int sidtab_map(struct sidtab *s, 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); diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c new file mode 100644 index 00000000000..d982365f9d1 --- /dev/null +++ b/security/selinux/ss/status.c @@ -0,0 +1,126 @@ +/* + * mmap based event notifications for SELinux + * + * Author: KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * Copyright (C) 2010 NEC corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include "avc.h" +#include "services.h" + +/* + * The selinux_status_page shall be exposed to userspace applications + * using mmap interface on /selinux/status. + * It enables to notify applications a few events that will cause reset + * of userspace access vector without context switching. + * + * The selinux_kernel_status structure on the head of status page is + * protected from concurrent accesses using seqlock logic, so userspace + * application should reference the status page according to the seqlock + * logic. + * + * Typically, application checks status->sequence at the head of access + * control routine. If it is odd-number, kernel is updating the status, + * so please wait for a moment. If it is changed from the last sequence + * number, it means something happen, so application will reset userspace + * avc, if needed. + * In most cases, application shall confirm the kernel status is not + * changed without any system call invocations. + */ +static struct page *selinux_status_page; +static DEFINE_MUTEX(selinux_status_lock); + +/* + * selinux_kernel_status_page + * + * It returns a reference to selinux_status_page. If the status page is + * not allocated yet, it also tries to allocate it at the first time. + */ +struct page *selinux_kernel_status_page(void) +{ + struct selinux_kernel_status *status; + struct page *result = NULL; + + mutex_lock(&selinux_status_lock); + if (!selinux_status_page) { + selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO); + + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->version = SELINUX_KERNEL_STATUS_VERSION; + status->sequence = 0; + status->enforcing = selinux_enforcing; + /* + * NOTE: the next policyload event shall set + * a positive value on the status->policyload, + * although it may not be 1, but never zero. + * So, application can know it was updated. + */ + status->policyload = 0; + status->deny_unknown = !security_get_allow_unknown(); + } + } + result = selinux_status_page; + mutex_unlock(&selinux_status_lock); + + return result; +} + +/* + * selinux_status_update_setenforce + * + * It updates status of the current enforcing/permissive mode. + */ +void selinux_status_update_setenforce(int enforcing) +{ + struct selinux_kernel_status *status; + + mutex_lock(&selinux_status_lock); + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->sequence++; + smp_wmb(); + + status->enforcing = enforcing; + + smp_wmb(); + status->sequence++; + } + mutex_unlock(&selinux_status_lock); +} + +/* + * selinux_status_update_policyload + * + * It updates status of the times of policy reloaded, and current + * setting of deny_unknown. + */ +void selinux_status_update_policyload(int seqno) +{ + struct selinux_kernel_status *status; + + mutex_lock(&selinux_status_lock); + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->sequence++; + smp_wmb(); + + status->policyload = seqno; + status->deny_unknown = !security_get_allow_unknown(); + + smp_wmb(); + status->sequence++; + } + mutex_unlock(&selinux_status_lock); +} diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index 24a10d36d3b..160326ee99e 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -4,14 +4,13 @@ * Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ #include <linux/kernel.h> -#include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> #include "symtab.h" -static unsigned int symhash(struct hashtab *h, void *key) +static unsigned int symhash(struct hashtab *h, const void *key) { - char *p, *keyp; + const char *p, *keyp; unsigned int size; unsigned int val; @@ -23,9 +22,9 @@ static unsigned int symhash(struct hashtab *h, void *key) return val & (h->size - 1); } -static int symcmp(struct hashtab *h, void *key1, void *key2) +static int symcmp(struct hashtab *h, const void *key1, const void *key2) { - char *keyp1, *keyp2; + const char *keyp1, *keyp2; keyp1 = key1; keyp2 = key2; @@ -37,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) { s->table = hashtab_create(symhash, symcmp, size); if (!s->table) - return -1; + return -ENOMEM; s->nprim = 0; return 0; } diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index b2af7ca496c..98b042630a9 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -6,7 +6,12 @@ * Authors: Serge Hallyn <sergeh@us.ibm.com> * Trent Jaeger <jaegert@us.ibm.com> * + * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com> + * + * Granular IPSec Associations for use in MLS environments. + * * Copyright (C) 2005 International Business Machines Corporation + * Copyright (C) 2006 Trusted Computer Solutions, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -26,8 +31,6 @@ * 2. Emulating a reasonable SO_PEERSEC across machines * 3. Testing addition of sk_policy's with security context via setsockopt */ -#include <linux/config.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/security.h> @@ -35,6 +38,7 @@ #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> +#include <linux/slab.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/skbuff.h> @@ -42,15 +46,17 @@ #include <net/xfrm.h> #include <net/checksum.h> #include <net/udp.h> -#include <asm/semaphore.h> +#include <linux/atomic.h> #include "avc.h" #include "objsec.h" #include "xfrm.h" +/* Labeled XFRM instance counter */ +atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0); /* - * Returns true if an LSM/SELinux context + * Returns true if the context is an LSM/SELinux context. */ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx) { @@ -60,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx) } /* - * Returns true if the xfrm contains a security blob for SELinux + * Returns true if the xfrm contains a security blob for SELinux. */ static inline int selinux_authorizable_xfrm(struct xfrm_state *x) { @@ -68,150 +74,308 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x) } /* - * LSM hook implementation that authorizes that a socket can be used - * with the corresponding xfrm_sec_ctx and direction. + * Allocates a xfrm_sec_state and populates it using the supplied security + * xfrm_user_sec_ctx context. */ -int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) +static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, + struct xfrm_user_sec_ctx *uctx, + gfp_t gfp) { - int rc = 0; - u32 sel_sid = SECINITSID_UNLABELED; - struct xfrm_sec_ctx *ctx; + int rc; + const struct task_security_struct *tsec = current_security(); + struct xfrm_sec_ctx *ctx = NULL; + u32 str_len; + + if (ctxp == NULL || uctx == NULL || + uctx->ctx_doi != XFRM_SC_DOI_LSM || + uctx->ctx_alg != XFRM_SC_ALG_SELINUX) + return -EINVAL; + + str_len = uctx->ctx_len; + if (str_len >= PAGE_SIZE) + return -ENOMEM; - /* Context sid is either set to label or ANY_ASSOC */ - if ((ctx = xp->security)) { - if (!selinux_authorizable_ctx(ctx)) - return -EINVAL; + ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp); + if (!ctx) + return -ENOMEM; - sel_sid = ctx->ctx_sid; - } + ctx->ctx_doi = XFRM_SC_DOI_LSM; + ctx->ctx_alg = XFRM_SC_ALG_SELINUX; + ctx->ctx_len = str_len; + memcpy(ctx->ctx_str, &uctx[1], str_len); + ctx->ctx_str[str_len] = '\0'; + rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp); + if (rc) + goto err; + + rc = avc_has_perm(tsec->sid, ctx->ctx_sid, + SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); + if (rc) + goto err; - rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION, - ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM : - ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO : - (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))), - NULL); + *ctxp = ctx; + atomic_inc(&selinux_xfrm_refcount); + return 0; +err: + kfree(ctx); return rc; } /* - * Security blob allocation for xfrm_policy and xfrm_state - * CTX does not have a meaningful value on input + * Free the xfrm_sec_ctx structure. */ -static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx) +static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) { - int rc = 0; - struct task_security_struct *tsec = current->security; - struct xfrm_sec_ctx *ctx; + if (!ctx) + return; - BUG_ON(!uctx); - BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX); + atomic_dec(&selinux_xfrm_refcount); + kfree(ctx); +} - if (uctx->ctx_len >= PAGE_SIZE) - return -ENOMEM; +/* + * Authorize the deletion of a labeled SA or policy rule. + */ +static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) +{ + const struct task_security_struct *tsec = current_security(); + + if (!ctx) + return 0; - *ctxp = ctx = kmalloc(sizeof(*ctx) + - uctx->ctx_len, - GFP_KERNEL); + return avc_has_perm(tsec->sid, ctx->ctx_sid, + SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, + NULL); +} +/* + * LSM hook implementation that authorizes that a flow can use a xfrm policy + * rule. + */ +int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +{ + int rc; + + /* All flows should be treated as polmatch'ing an otherwise applicable + * "non-labeled" policy. This would prevent inadvertent "leaks". */ if (!ctx) - return -ENOMEM; + return 0; - ctx->ctx_doi = uctx->ctx_doi; - ctx->ctx_len = uctx->ctx_len; - ctx->ctx_alg = uctx->ctx_alg; + /* Context sid is either set to label or ANY_ASSOC */ + if (!selinux_authorizable_ctx(ctx)) + return -EINVAL; - memcpy(ctx->ctx_str, - uctx+1, - ctx->ctx_len); - rc = security_context_to_sid(ctx->ctx_str, - ctx->ctx_len, - &ctx->ctx_sid); + rc = avc_has_perm(fl_secid, ctx->ctx_sid, + SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); + return (rc == -EACCES ? -ESRCH : rc); +} - if (rc) - goto out; +/* + * LSM hook implementation that authorizes that a state matches + * the given policy, flow combo. + */ +int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, + struct xfrm_policy *xp, + const struct flowi *fl) +{ + u32 state_sid; + + if (!xp->security) + if (x->security) + /* unlabeled policy and labeled SA can't match */ + return 0; + else + /* unlabeled policy and unlabeled SA match all flows */ + return 1; + else + if (!x->security) + /* unlabeled SA and labeled policy can't match */ + return 0; + else + if (!selinux_authorizable_xfrm(x)) + /* Not a SELinux-labeled SA */ + return 0; + + state_sid = x->security->ctx_sid; + + if (fl->flowi_secid != state_sid) + return 0; + + /* We don't need a separate SA Vs. policy polmatch check since the SA + * is now of the same label as the flow and a flow Vs. policy polmatch + * check had already happened in selinux_xfrm_policy_lookup() above. */ + return (avc_has_perm(fl->flowi_secid, state_sid, + SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, + NULL) ? 0 : 1); +} - /* - * Does the subject have permission to set security or permission to - * do the relabel? - * Must be permitted to relabel from default socket type (process type) - * to specified context - */ - rc = avc_has_perm(tsec->sid, ctx->ctx_sid, - SECCLASS_ASSOCIATION, - ASSOCIATION__SETCONTEXT, NULL); - if (rc) - goto out; +static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct xfrm_state *x; - return rc; + if (dst == NULL) + return SECSID_NULL; + x = dst->xfrm; + if (x == NULL || !selinux_authorizable_xfrm(x)) + return SECSID_NULL; + + return x->security->ctx_sid; +} + +static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, + u32 *sid, int ckall) +{ + u32 sid_session = SECSID_NULL; + struct sec_path *sp = skb->sp; + + if (sp) { + int i; + + for (i = sp->len - 1; i >= 0; i--) { + struct xfrm_state *x = sp->xvec[i]; + if (selinux_authorizable_xfrm(x)) { + struct xfrm_sec_ctx *ctx = x->security; + + if (sid_session == SECSID_NULL) { + sid_session = ctx->ctx_sid; + if (!ckall) + goto out; + } else if (sid_session != ctx->ctx_sid) { + *sid = SECSID_NULL; + return -EINVAL; + } + } + } + } out: - *ctxp = NULL; - kfree(ctx); - return rc; + *sid = sid_session; + return 0; } /* - * LSM hook implementation that allocs and transfers uctx spec to - * xfrm_policy. + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet. */ -int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx) +int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) { - int err; + if (skb == NULL) { + *sid = SECSID_NULL; + return 0; + } + return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); +} - BUG_ON(!xp); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) +{ + int rc; - err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx); - return err; -} + rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); + if (rc == 0 && *sid == SECSID_NULL) + *sid = selinux_xfrm_skb_sid_egress(skb); + return rc; +} /* - * LSM hook implementation that copies security data structure from old to - * new for policy cloning. + * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy. */ -int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new) +int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, + struct xfrm_user_sec_ctx *uctx, + gfp_t gfp) { - struct xfrm_sec_ctx *old_ctx, *new_ctx; + return selinux_xfrm_alloc_user(ctxp, uctx, gfp); +} - old_ctx = old->security; +/* + * LSM hook implementation that copies security data structure from old to new + * for policy cloning. + */ +int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx, + struct xfrm_sec_ctx **new_ctxp) +{ + struct xfrm_sec_ctx *new_ctx; - if (old_ctx) { - new_ctx = new->security = kmalloc(sizeof(*new_ctx) + - old_ctx->ctx_len, - GFP_KERNEL); + if (!old_ctx) + return 0; - if (!new_ctx) - return -ENOMEM; + new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len, + GFP_ATOMIC); + if (!new_ctx) + return -ENOMEM; + atomic_inc(&selinux_xfrm_refcount); + *new_ctxp = new_ctx; - memcpy(new_ctx, old_ctx, sizeof(*new_ctx)); - memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len); - } return 0; } /* - * LSM hook implementation that frees xfrm_policy security information. + * LSM hook implementation that frees xfrm_sec_ctx security information. + */ +void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx) +{ + selinux_xfrm_free(ctx); +} + +/* + * LSM hook implementation that authorizes deletion of labeled policies. */ -void selinux_xfrm_policy_free(struct xfrm_policy *xp) +int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx) { - struct xfrm_sec_ctx *ctx = xp->security; - if (ctx) - kfree(ctx); + return selinux_xfrm_delete(ctx); } /* - * LSM hook implementation that allocs and transfers sec_ctx spec to - * xfrm_state. + * LSM hook implementation that allocates a xfrm_sec_state, populates it using + * the supplied security context, and assigns it to the xfrm_state. */ -int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx) +int selinux_xfrm_state_alloc(struct xfrm_state *x, + struct xfrm_user_sec_ctx *uctx) { - int err; + return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL); +} + +/* + * LSM hook implementation that allocates a xfrm_sec_state and populates based + * on a secid. + */ +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, + struct xfrm_sec_ctx *polsec, u32 secid) +{ + int rc; + struct xfrm_sec_ctx *ctx; + char *ctx_str = NULL; + int str_len; - BUG_ON(!x); + if (!polsec) + return 0; - err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx); - return err; + if (secid == 0) + return -EINVAL; + + rc = security_sid_to_context(secid, &ctx_str, &str_len); + if (rc) + return rc; + + ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); + if (!ctx) { + rc = -ENOMEM; + goto out; + } + + ctx->ctx_doi = XFRM_SC_DOI_LSM; + ctx->ctx_alg = XFRM_SC_ALG_SELINUX; + ctx->ctx_sid = secid; + ctx->ctx_len = str_len; + memcpy(ctx->ctx_str, ctx_str, str_len); + + x->security = ctx; + atomic_inc(&selinux_xfrm_refcount); +out: + kfree(ctx_str); + return rc; } /* @@ -219,9 +383,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct */ void selinux_xfrm_state_free(struct xfrm_state *x) { - struct xfrm_sec_ctx *ctx = x->security; - if (ctx) - kfree(ctx); + selinux_xfrm_free(x->security); +} + +/* + * LSM hook implementation that authorizes deletion of labeled SAs. + */ +int selinux_xfrm_state_delete(struct xfrm_state *x) +{ + return selinux_xfrm_delete(x->security); } /* @@ -231,39 +401,30 @@ void selinux_xfrm_state_free(struct xfrm_state *x) * we need to check for unlabelled access since this may not have * gone thru the IPSec process. */ -int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad) { - int i, rc = 0; - struct sec_path *sp; - - sp = skb->sp; + int i; + struct sec_path *sp = skb->sp; + u32 peer_sid = SECINITSID_UNLABELED; if (sp) { - /* - * __xfrm_policy_check does not approve unless xfrm_policy_ok - * says that spi's match for policy and the socket. - * - * Only need to verify the existence of an authorizable sp. - */ for (i = 0; i < sp->len; i++) { - struct xfrm_state *x = sp->x[i].xvec; + struct xfrm_state *x = sp->xvec[i]; - if (x && selinux_authorizable_xfrm(x)) - goto accept; + if (x && selinux_authorizable_xfrm(x)) { + struct xfrm_sec_ctx *ctx = x->security; + peer_sid = ctx->ctx_sid; + break; + } } } - /* check SELinux sock for unlabelled access */ - rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, - ASSOCIATION__RECVFROM, NULL); - if (rc) - goto drop; - -accept: - return 0; - -drop: - return rc; + /* This check even when there's no association involved is intended, + * according to Trent Jaeger, to make sure a process can't engage in + * non-IPsec communication unless explicitly allowed by policy. */ + return avc_has_perm(sk_sid, peer_sid, + SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad); } /* @@ -271,35 +432,40 @@ drop: * If we have no security association, then we need to determine * whether the socket is allowed to send to an unlabelled destination. * If we do have a authorizable security association, then it has already been - * checked in xfrm_policy_lookup hook. + * checked in the selinux_xfrm_state_pol_flow_match hook above. */ -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, + struct common_audit_data *ad, u8 proto) { struct dst_entry *dst; - int rc = 0; - dst = skb->dst; + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_COMP: + /* We should have already seen this packet once before it + * underwent xfrm(s). No need to subject it to the unlabeled + * check. */ + return 0; + default: + break; + } + dst = skb_dst(skb); if (dst) { - struct dst_entry *dst_test; + struct dst_entry *iter; - for (dst_test = dst; dst_test != 0; - dst_test = dst_test->child) { - struct xfrm_state *x = dst_test->xfrm; + for (iter = dst; iter != NULL; iter = iter->child) { + struct xfrm_state *x = iter->xfrm; if (x && selinux_authorizable_xfrm(x)) - goto accept; + return 0; } } - rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, - ASSOCIATION__SENDTO, NULL); - if (rc) - goto drop; - -accept: - return NF_ACCEPT; - -drop: - return NF_DROP; + /* This check even when there's no association involved is intended, + * according to Trent Jaeger, to make sure a process can't engage in + * non-IPsec communication unless explicitly allowed by policy. */ + return avc_has_perm(sk_sid, SECINITSID_UNLABELED, + SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad); } |
