Merge branch 'for-linus2' of git://git.kernel.dk/linux-2.6-block
[safe/jmp/linux-2.6] / security / selinux / avc.c
index b2ab608..7f1a304 100644 (file)
 #include <net/ipv6.h>
 #include "avc.h"
 #include "avc_ss.h"
-
-static const struct av_perm_to_string av_perm_to_string[] = {
-#define S_(c, v, s) { c, v, s },
-#include "av_perm_to_string.h"
-#undef S_
-};
-
-static const char *class_to_string[] = {
-#define S_(s) s,
-#include "class_to_string.h"
-#undef S_
-};
-
-#define TB_(s) static const char *s[] = {
-#define TE_(s) };
-#define S_(s) s,
-#include "common_perm_to_string.h"
-#undef TB_
-#undef TE_
-#undef S_
-
-static const struct av_inherit av_inherit[] = {
-#define S_(c, i, b) {  .tclass = c,\
-                       .common_pts = common_##i##_perm_to_string,\
-                       .common_base =  b },
-#include "av_inherit.h"
-#undef S_
-};
-
-const struct selinux_class_perm selinux_class_perm = {
-       .av_perm_to_string = av_perm_to_string,
-       .av_pts_len = ARRAY_SIZE(av_perm_to_string),
-       .class_to_string = class_to_string,
-       .cts_len = ARRAY_SIZE(class_to_string),
-       .av_inherit = av_inherit,
-       .av_inherit_len = ARRAY_SIZE(av_inherit)
-};
+#include "classmap.h"
 
 #define AVC_CACHE_SLOTS                        512
 #define AVC_DEF_CACHE_THRESHOLD                512
@@ -137,54 +101,30 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
  * @tclass: target security class
  * @av: access vector
  */
-void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+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);
 
@@ -219,8 +159,8 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
                kfree(scontext);
        }
 
-       BUG_ON(tclass >= ARRAY_SIZE(class_to_string) || !class_to_string[tclass]);
-       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);
 }
 
 /**
@@ -397,7 +337,7 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
  * Look up an AVC entry that is valid for the
  * (@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)
@@ -492,23 +432,35 @@ out:
        return node;
 }
 
-static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
-                                      struct in6_addr *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 (!ipv6_addr_any(addr))
-               audit_log_format(ab, " %s=%pI6", name1, 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 ");
 }
 
-static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
-                                      __be16 port, char *name1, char *name2)
+/**
+ * avc_audit_post_callback - SELinux specific information
+ * will be called by generic audit code
+ * @ab: the audit buffer
+ * @a: audit_data
+ */
+static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
 {
-       if (addr)
-               audit_log_format(ab, " %s=%pI4", name1, &addr);
-       if (port)
-               audit_log_format(ab, " %s=%d", name2, ntohs(port));
+       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);
 }
 
 /**
@@ -532,163 +484,32 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
  */
 void avc_audit(u32 ssid, u32 tsid,
               u16 tclass, u32 requested,
-              struct av_decision *avd, int result, struct avc_audit_data *a)
+              struct av_decision *avd, int result, struct common_audit_data *a)
 {
-       struct task_struct *tsk = current;
-       struct inode *inode = NULL;
+       struct common_audit_data stack_data;
        u32 denied, audited;
-       struct audit_buffer *ab;
-
        denied = requested & ~avd->allowed;
-       if (denied) {
-               audited = denied;
-               if (!(audited & avd->auditdeny))
-                       return;
-       } else if (result) {
+       if (denied)
+               audited = denied & avd->auditdeny;
+       else if (result)
                audited = denied = requested;
-       } else {
-               audited = requested;
-               if (!(audited & avd->auditallow))
-                       return;
-       }
-
-       ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
-       if (!ab)
-               return;         /* audit_panic has been called */
-       audit_log_format(ab, "avc:  %s ", denied ? "denied" : "granted");
-       avc_dump_av(ab, tclass, audited);
-       audit_log_format(ab, " for ");
-       if (a && a->tsk)
-               tsk = a->tsk;
-       if (tsk && tsk->pid) {
-               audit_log_format(ab, " pid=%d comm=", tsk->pid);
-               audit_log_untrustedstring(ab, tsk->comm);
-       }
-       if (a) {
-               switch (a->type) {
-               case AVC_AUDIT_DATA_IPC:
-                       audit_log_format(ab, " key=%d", a->u.ipc_id);
-                       break;
-               case AVC_AUDIT_DATA_CAP:
-                       audit_log_format(ab, " capability=%d", a->u.cap);
-                       break;
-               case AVC_AUDIT_DATA_FS:
-                       if (a->u.fs.path.dentry) {
-                               struct dentry *dentry = a->u.fs.path.dentry;
-                               if (a->u.fs.path.mnt) {
-                                       audit_log_d_path(ab, "path=",
-                                                        &a->u.fs.path);
-                               } else {
-                                       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=%lu",
-                                                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) {
-                                               struct path path = {
-                                                       .dentry = u->dentry,
-                                                       .mnt = u->mnt
-                                               };
-                                               audit_log_d_path(ab, "path=",
-                                                                &path);
-                                               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_n_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 > 0) {
-                               struct net_device *dev;
-
-                               /* NOTE: we always use init's namespace */
-                               dev = dev_get_by_index(&init_net,
-                                                      a->u.net.netif);
-                               if (dev) {
-                                       audit_log_format(ab, " netif=%s",
-                                                        dev->name);
-                                       dev_put(dev);
-                               }
-                       }
-                       break;
-               }
+       else
+               audited = requested & avd->auditallow;
+       if (!audited)
+               return;
+       if (!a) {
+               a = &stack_data;
+               COMMON_AUDIT_DATA_INIT(a, NONE);
        }
-       audit_log_format(ab, " ");
-       avc_dump_query(ab, ssid, tsid, tclass);
-       audit_log_end(ab);
+       a->selinux_audit_data.tclass = tclass;
+       a->selinux_audit_data.requested = requested;
+       a->selinux_audit_data.ssid = ssid;
+       a->selinux_audit_data.tsid = tsid;
+       a->selinux_audit_data.audited = audited;
+       a->selinux_audit_data.denied = denied;
+       a->lsm_pre_audit = avc_audit_pre_callback;
+       a->lsm_post_audit = avc_audit_post_callback;
+       common_lsm_audit(a);
 }
 
 /**
@@ -701,7 +522,7 @@ void avc_audit(u32 ssid, u32 tsid,
  * @perms: permissions
  *
  * Register a callback function for events in the set @events
- * related to the SID pair (@ssid, @tsid) and
+ * related to the SID pair (@ssid, @tsid) 
  * and the permissions @perms, interpreting
  * @perms based on @tclass.  Returns %0 on success or
  * -%ENOMEM if insufficient memory exists to add the callback.
@@ -746,7 +567,7 @@ static inline int avc_sidcmp(u32 x, u32 y)
  *
  * 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,
@@ -824,18 +645,16 @@ out:
 }
 
 /**
- * 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, tmprc;
-       unsigned long flag;
-       struct avc_node *node;
        struct hlist_head *head;
        struct hlist_node *next;
+       struct avc_node *node;
        spinlock_t *lock;
+       unsigned long flag;
+       int i;
 
        for (i = 0; i < AVC_CACHE_SLOTS; i++) {
                head = &avc_cache.slots[i];
@@ -852,6 +671,18 @@ int avc_ss_reset(u32 seqno)
                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) {
@@ -911,9 +742,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                else
                        avd = &avd_entry;
 
-               rc = security_compute_av(ssid, tsid, tclass, requested, avd);
-               if (rc)
-                       goto out;
+               security_compute_av(ssid, tsid, tclass, avd);
                rcu_read_lock();
                node = avc_insert(ssid, tsid, tclass, avd);
        } else {
@@ -935,7 +764,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
        }
 
        rcu_read_unlock();
-out:
        return rc;
 }
 
@@ -956,7 +784,7 @@ 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;
@@ -970,3 +798,22 @@ 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); */
+       }
+}