#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
* @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);
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);
}
/**
* 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)
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);
}
/**
*/
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);
}
/**
* @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.
*
* 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,
}
/**
- * 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];
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) {
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 {
}
rcu_read_unlock();
-out:
return rc;
}
* 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;
{
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); */
+ }
+}