diff --git a/arch/i386/kernel/audit.c b/arch/i386/kernel/audit.c
index 28bbc46f75c2696f6ecc6ed73e1f1a5c53c294eb..3b97cff4154991699f241a96f7a3ef6f840bac30 100644
--- a/arch/i386/kernel/audit.c
+++ b/arch/i386/kernel/audit.c
@@ -23,6 +23,22 @@ static unsigned chattr_class[] = {
 ~0U
 };
 
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
 static int __init audit_classes_init(void)
 {
 	audit_register_class(AUDIT_CLASS_WRITE, write_class);
diff --git a/arch/ia64/ia32/audit.c b/arch/ia64/ia32/audit.c
index 798501994bd937b8f2bc6e17321c6f2aa2eb384b..92d7d0c8d93fb238dcf32831dcbfc5719078efd6 100644
--- a/arch/ia64/ia32/audit.c
+++ b/arch/ia64/ia32/audit.c
@@ -19,3 +19,19 @@ unsigned ia32_read_class[] = {
 #include <asm-generic/audit_read.h>
 ~0U
 };
+
+int ia32_classify_syscall(unsigned syscall)
+{
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 1;
+	}
+}
diff --git a/arch/ia64/kernel/audit.c b/arch/ia64/kernel/audit.c
index 99488cdbf5cd06542747d41316859506389094b1..04682555a28c0e81b33feb1492fae2dc9ee05a2c 100644
--- a/arch/ia64/kernel/audit.c
+++ b/arch/ia64/kernel/audit.c
@@ -23,6 +23,25 @@ static unsigned chattr_class[] = {
 ~0U
 };
 
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_IA32_SUPPORT
+	extern int ia32_classify_syscall(unsigned);
+	if (abi == AUDIT_ARCH_I386)
+		return ia32_classify_syscall(syscall);
+#endif
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_execve:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
 static int __init audit_classes_init(void)
 {
 #ifdef CONFIG_IA32_SUPPORT
diff --git a/arch/powerpc/kernel/audit.c b/arch/powerpc/kernel/audit.c
index 24a65e3724e14fd9bd58ae3027570e3fc923079c..7fe5e6300e9a2e2447ef08a5970b53d12743c73f 100644
--- a/arch/powerpc/kernel/audit.c
+++ b/arch/powerpc/kernel/audit.c
@@ -23,6 +23,27 @@ static unsigned chattr_class[] = {
 ~0U
 };
 
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_PPC64
+	extern int ppc32_classify_syscall(unsigned);
+	if (abi == AUDIT_ARCH_PPC)
+		return ppc32_classify_syscall(syscall);
+#endif
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
 static int __init audit_classes_init(void)
 {
 #ifdef CONFIG_PPC64
diff --git a/arch/powerpc/kernel/compat_audit.c b/arch/powerpc/kernel/compat_audit.c
index ddc0a64896a24e886aa12ebfdf14b26125746906..640d4bb29321b5b20a074e5491aeaada69b021e2 100644
--- a/arch/powerpc/kernel/compat_audit.c
+++ b/arch/powerpc/kernel/compat_audit.c
@@ -20,3 +20,19 @@ unsigned ppc32_read_class[] = {
 #include <asm-generic/audit_read.h>
 ~0U
 };
+
+int ppc32_classify_syscall(unsigned syscall)
+{
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 1;
+	}
+}
diff --git a/arch/s390/kernel/audit.c b/arch/s390/kernel/audit.c
index cde57039334cdd50f775a02b8400b5f381b60051..0741d91933906ec1feccc1e335122d7fb244431c 100644
--- a/arch/s390/kernel/audit.c
+++ b/arch/s390/kernel/audit.c
@@ -23,6 +23,27 @@ static unsigned chattr_class[] = {
 ~0U
 };
 
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_COMPAT
+	extern int s390_classify_syscall(unsigned);
+	if (abi == AUDIT_ARCH_S390)
+		return s390_classify_syscall(syscall);
+#endif
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
 static int __init audit_classes_init(void)
 {
 #ifdef CONFIG_COMPAT
diff --git a/arch/s390/kernel/compat_audit.c b/arch/s390/kernel/compat_audit.c
index d9e5f3540d4847a0c6d03893e15e8d6c693e846e..16d9436bfa9103ab1d3464d5f5f02eec65ca76e0 100644
--- a/arch/s390/kernel/compat_audit.c
+++ b/arch/s390/kernel/compat_audit.c
@@ -20,3 +20,19 @@ unsigned s390_read_class[] = {
 #include <asm-generic/audit_read.h>
 ~0U
 };
+
+int s390_classify_syscall(unsigned syscall)
+{
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 1;
+	}
+}
diff --git a/arch/x86_64/ia32/audit.c b/arch/x86_64/ia32/audit.c
index 798501994bd937b8f2bc6e17321c6f2aa2eb384b..92d7d0c8d93fb238dcf32831dcbfc5719078efd6 100644
--- a/arch/x86_64/ia32/audit.c
+++ b/arch/x86_64/ia32/audit.c
@@ -19,3 +19,19 @@ unsigned ia32_read_class[] = {
 #include <asm-generic/audit_read.h>
 ~0U
 };
+
+int ia32_classify_syscall(unsigned syscall)
+{
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_socketcall:
+		return 4;
+	case __NR_execve:
+		return 5;
+	default:
+		return 1;
+	}
+}
diff --git a/arch/x86_64/kernel/audit.c b/arch/x86_64/kernel/audit.c
index 36840acb65133792563e21c2c811bf9426b49820..21f33387bef3a3427d1bab4cfe924a4bc545391d 100644
--- a/arch/x86_64/kernel/audit.c
+++ b/arch/x86_64/kernel/audit.c
@@ -23,6 +23,25 @@ static unsigned chattr_class[] = {
 ~0U
 };
 
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_IA32_EMULATION
+	extern int ia32_classify_syscall(unsigned);
+	if (abi == AUDIT_ARCH_I386)
+		return ia32_classify_syscall(syscall);
+#endif
+	switch(syscall) {
+	case __NR_open:
+		return 2;
+	case __NR_openat:
+		return 3;
+	case __NR_execve:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
 static int __init audit_classes_init(void)
 {
 #ifdef CONFIG_IA32_EMULATION
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 1077362a2efb9594425c98fa839dd5ca72545652..40a6c26294ae9fda282d26f27b8f7c9cac622d30 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -181,6 +181,7 @@
 #define AUDIT_EXIT	103
 #define AUDIT_SUCCESS   104	/* exit >= 0; value ignored */
 #define AUDIT_WATCH	105
+#define AUDIT_PERM	106
 
 #define AUDIT_ARG0      200
 #define AUDIT_ARG1      (AUDIT_ARG0+1)
@@ -256,6 +257,11 @@
 #define AUDIT_ARCH_V850		(EM_V850|__AUDIT_ARCH_LE)
 #define AUDIT_ARCH_X86_64	(EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
 
+#define AUDIT_PERM_EXEC		1
+#define AUDIT_PERM_WRITE	2
+#define AUDIT_PERM_READ		4
+#define AUDIT_PERM_ATTR		8
+
 struct audit_status {
 	__u32		mask;		/* Bit mask for valid entries */
 	__u32		enabled;	/* 1 = enabled, 0 = disabled */
@@ -318,6 +324,7 @@ struct mqstat;
 #define AUDITSC_FAILURE 2
 #define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS )
 extern int __init audit_register_class(int class, unsigned *list);
+extern int audit_classify_syscall(int abi, unsigned syscall);
 #ifdef CONFIG_AUDITSYSCALL
 /* These are defined in auditsc.c */
 				/* Public API */
diff --git a/kernel/audit.h b/kernel/audit.h
index 6aa33b848cf2f0363ba6b8f8f477e9c36f2affc8..a3370232a390eff1560b0f98c1e4ebd8f18948f6 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -104,6 +104,7 @@ static inline int audit_hash_ino(u32 ino)
 	return (ino & (AUDIT_INODE_BUCKETS-1));
 }
 
+extern int audit_match_class(int class, unsigned syscall);
 extern int audit_comparator(const u32 left, const u32 op, const u32 right);
 extern int audit_compare_dname_path(const char *dname, const char *path,
 				    int *dirlen);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index e4cafc11c510aab8ded1d48e0cdefe556eb0322b..a44879b0c72ff422ec5ef25c7931bbbb7da7cc57 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -302,6 +302,15 @@ int __init audit_register_class(int class, unsigned *list)
 	return 0;
 }
 
+int audit_match_class(int class, unsigned syscall)
+{
+	if (unlikely(syscall >= AUDIT_BITMASK_SIZE * sizeof(__u32)))
+		return 0;
+	if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class]))
+		return 0;
+	return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);
+}
+
 /* Common user-space to kernel rule translation. */
 static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
 {
@@ -414,6 +423,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
 		case AUDIT_ARG2:
 		case AUDIT_ARG3:
 			break;
+		case AUDIT_PERM:
+			if (f->val & ~15)
+				goto exit_free;
+			break;
 		case AUDIT_INODE:
 			err = audit_to_inode(&entry->rule, f);
 			if (err)
@@ -568,6 +581,10 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
 			entry->rule.buflen += f->val;
 			entry->rule.filterkey = str;
 			break;
+		case AUDIT_PERM:
+			if (f->val & ~15)
+				goto exit_free;
+			break;
 		default:
 			goto exit_free;
 		}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index efc1b74bebf3bc1da8f41c64c7e2deeeb8ba3d0f..1bd8827a0102809606b8ccab55d7db185cd12fb2 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -209,6 +209,54 @@ struct audit_context {
 #endif
 };
 
+#define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE])
+static inline int open_arg(int flags, int mask)
+{
+	int n = ACC_MODE(flags);
+	if (flags & (O_TRUNC | O_CREAT))
+		n |= AUDIT_PERM_WRITE;
+	return n & mask;
+}
+
+static int audit_match_perm(struct audit_context *ctx, int mask)
+{
+	unsigned n = ctx->major;
+	switch (audit_classify_syscall(ctx->arch, n)) {
+	case 0:	/* native */
+		if ((mask & AUDIT_PERM_WRITE) &&
+		     audit_match_class(AUDIT_CLASS_WRITE, n))
+			return 1;
+		if ((mask & AUDIT_PERM_READ) &&
+		     audit_match_class(AUDIT_CLASS_READ, n))
+			return 1;
+		if ((mask & AUDIT_PERM_ATTR) &&
+		     audit_match_class(AUDIT_CLASS_CHATTR, n))
+			return 1;
+		return 0;
+	case 1: /* 32bit on biarch */
+		if ((mask & AUDIT_PERM_WRITE) &&
+		     audit_match_class(AUDIT_CLASS_WRITE_32, n))
+			return 1;
+		if ((mask & AUDIT_PERM_READ) &&
+		     audit_match_class(AUDIT_CLASS_READ_32, n))
+			return 1;
+		if ((mask & AUDIT_PERM_ATTR) &&
+		     audit_match_class(AUDIT_CLASS_CHATTR_32, n))
+			return 1;
+		return 0;
+	case 2: /* open */
+		return mask & ACC_MODE(ctx->argv[1]);
+	case 3: /* openat */
+		return mask & ACC_MODE(ctx->argv[2]);
+	case 4: /* socketcall */
+		return ((mask & AUDIT_PERM_WRITE) && ctx->argv[0] == SYS_BIND);
+	case 5: /* execve */
+		return mask & AUDIT_PERM_EXEC;
+	default:
+		return 0;
+	}
+}
+
 /* Determine if any context name data matches a rule's watch data */
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
@@ -397,6 +445,9 @@ static int audit_filter_rules(struct task_struct *tsk,
 			/* ignore this field for filtering */
 			result = 1;
 			break;
+		case AUDIT_PERM:
+			result = audit_match_perm(ctx, f->val);
+			break;
 		}
 
 		if (!result)