SELinux: improve performance when AVC misses.
[safe/jmp/linux-2.6] / security / selinux / avc.c
index 9e71a1b..81b3dff 100644 (file)
 #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[] = {
+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_
 };
 
-#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) };
@@ -59,17 +52,21 @@ static const char *class_to_string[] = {
 #undef TE_
 #undef S_
 
-static const struct av_inherit
-{
-    u16 tclass;
-    const char **common_pts;
-    u32 common_base;
-} av_inherit[] = {
+static const struct av_inherit av_inherit[] = {
 #define S_(c, i, b) { c, common_##i##_perm_to_string, b },
 #include "av_inherit.h"
 #undef S_
 };
 
+const struct selinux_class_perm selinux_class_perm = {
+       av_perm_to_string,
+       ARRAY_SIZE(av_perm_to_string),
+       class_to_string,
+       ARRAY_SIZE(class_to_string),
+       av_inherit,
+       ARRAY_SIZE(av_inherit)
+};
+
 #define AVC_CACHE_SLOTS                        512
 #define AVC_DEF_CACHE_THRESHOLD                512
 #define AVC_CACHE_RECLAIM              16
@@ -127,7 +124,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)
 {
@@ -220,6 +217,8 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
                audit_log_format(ab, " tcontext=%s", scontext);
                kfree(scontext);
        }
+
+       BUG_ON(tclass >= ARRAY_SIZE(class_to_string) || !class_to_string[tclass]);
        audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
 }
 
@@ -240,9 +239,9 @@ void __init avc_init(void)
        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, "AVC INITIALIZED\n");
+       audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
 }
 
 int avc_get_hash_stats(char *page)
@@ -335,11 +334,10 @@ 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);
        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);
@@ -490,21 +488,20 @@ out:
 }
 
 static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
-                                      struct in6_addr *addr, u16 port,
+                                      struct in6_addr *addr, __be16 port,
                                       char *name1, char *name2)
 {
        if (!ipv6_addr_any(addr))
-               audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:"
-                                "%04x:%04x:%04x", name1, NIP6(*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,
-                                      u16 port, char *name1, char *name2)
+static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
+                                      __be16 port, char *name1, char *name2)
 {
        if (addr)
-               audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
+               audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr));
        if (port)
                audit_log_format(ab, " %s=%d", name2, ntohs(port));
 }
@@ -532,6 +529,7 @@ void avc_audit(u32 ssid, u32 tsid,
                u16 tclass, u32 requested,
                struct av_decision *avd, int result, struct avc_audit_data *a)
 {
+       struct task_struct *tsk = current;
        struct inode *inode = NULL;
        u32 denied, audited;
        struct audit_buffer *ab;
@@ -549,12 +547,18 @@ void avc_audit(u32 ssid, u32 tsid,
                        return;
        }
 
-       ab = audit_log_start(current->audit_context, AUDIT_KERNEL, 0);
+       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:
@@ -567,11 +571,10 @@ void avc_audit(u32 ssid, u32 tsid,
                        if (a->u.fs.dentry) {
                                struct dentry *dentry = a->u.fs.dentry;
                                if (a->u.fs.mnt) {
-                                       audit_log_d_path(ab, "path=", dentry,
-                                                       a->u.fs.mnt);
+                                       audit_log_d_path(ab, "path=", dentry, a->u.fs.mnt);
                                } else {
-                                       audit_log_format(ab, " name=%s",
-                                                        dentry->d_name.name);
+                                       audit_log_format(ab, " name=");
+                                       audit_log_untrustedstring(ab, dentry->d_name.name);
                                }
                                inode = dentry->d_inode;
                        } else if (a->u.fs.inode) {
@@ -579,13 +582,13 @@ void avc_audit(u32 ssid, u32 tsid,
                                inode = a->u.fs.inode;
                                dentry = d_find_alias(inode);
                                if (dentry) {
-                                       audit_log_format(ab, " name=%s",
-                                                        dentry->d_name.name);
+                                       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",
+                               audit_log_format(ab, " dev=%s ino=%lu",
                                                 inode->i_sb->s_id,
                                                 inode->i_ino);
                        break;
@@ -624,21 +627,18 @@ void avc_audit(u32 ssid, u32 tsid,
                                        u = unix_sk(sk);
                                        if (u->dentry) {
                                                audit_log_d_path(ab, "path=",
-                                                       u->dentry, u->mnt);
+                                                                u->dentry, u->mnt);
                                                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_format(ab,
-                                                       "path=%*.*s", len,
-                                                       len, p);
+                                               audit_log_untrustedstring(ab, p);
                                        else
-                                               audit_log_format(ab,
-                                                       "path=@%*.*s", len-1,
-                                                       len-1, p+1);
+                                               audit_log_hex(ab, p, len);
                                        break;
                                }
                        }
@@ -801,7 +801,7 @@ out:
 int avc_ss_reset(u32 seqno)
 {
        struct avc_callback_node *c;
-       int i, rc = 0;
+       int i, rc = 0, tmprc;
        unsigned long flag;
        struct avc_node *node;
 
@@ -814,15 +814,16 @@ int avc_ss_reset(u32 seqno)
 
        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,
+                                           0, 0, 0, 0, NULL);
+                       /* save the first error encountered for the return
+                          value and continue processing the callbacks */
+                       if (!rc)
+                               rc = tmprc;
                }
        }
 
        avc_latest_notif_update(seqno, 0);
-out:
        return rc;
 }
 
@@ -832,6 +833,7 @@ out:
  * @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,8 +848,9 @@ out:
  * should be released for the auditing.
  */
 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)
 {
        struct avc_node *node;
        struct avc_entry entry, *p_ae;
@@ -874,7 +877,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
        denied = requested & ~(p_ae->avd.allowed);
 
        if (!requested || denied) {
-               if (selinux_enforcing)
+               if (selinux_enforcing || (flags & AVC_STRICT))
                        rc = -EACCES;
                else
                        if (node)
@@ -909,7 +912,12 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
        struct av_decision avd;
        int rc;
 
-       rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
+       rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
        avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
        return rc;
 }
+
+u32 avc_policy_seqno(void)
+{
+       return avc_cache.latest_notif;
+}