apparmor: add a stop mode to apparmor

Add the ability for AppArmor to stop tasks that generate a reject via
sending them a SIG_STOP. This then allows a userspace program to examine
the tasks state before allowing it to continue.

Signed-off-by: John Johansen <john.johansen@canonical.com>
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index d63cfb6..ff13f46 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -212,6 +212,8 @@
 
 	if (sa->aad.type == AUDIT_APPARMOR_KILL)
 		(void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current);
+	else if (STOP_MODE(profile))
+		(void)send_sig_info(SIGSTOP, NULL, sa->tsk ? sa->tsk : current);
 
 	if (sa->aad.type == AUDIT_APPARMOR_ALLOWED)
 		return complain_error(sa->aad.error);
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index f2cf0fb..546b72e 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -29,8 +29,7 @@
 #include "file.h"
 #include "resource.h"
 
-extern const char *profile_mode_names[];
-#define APPARMOR_NAMES_MAX_INDEX 3
+extern const char *profile_mode_names[4];
 
 #define PROFILE_MODE(_profile, _mode)		\
 	((aa_g_profile_mode == (_mode)) ||	\
@@ -38,6 +37,8 @@
 
 #define COMPLAIN_MODE(_profile)	PROFILE_MODE((_profile), APPARMOR_COMPLAIN)
 
+#define STOP_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_STOP)
+
 #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
 
 #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
@@ -52,6 +53,7 @@
 	APPARMOR_ENFORCE,	/* enforce access rules */
 	APPARMOR_COMPLAIN,	/* allow and log access violations */
 	APPARMOR_KILL,		/* kill task on access violation */
+	APPARMOR_STOP,		/* stop task on access violation */
 };
 
 enum profile_flags {
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 23ac013..2986f56 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -917,7 +917,7 @@
 	if (!val)
 		return -EINVAL;
 
-	for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) {
+	for (i = 0; i < sizeof(profile_mode_names); i++) {
 		if (strcmp(val, profile_mode_names[i]) == 0) {
 			aa_g_profile_mode = i;
 			return 0;
@@ -985,6 +985,8 @@
 		aa_info_message("AppArmor initialized: complain mode enabled");
 	else if (aa_g_profile_mode == APPARMOR_KILL)
 		aa_info_message("AppArmor initialized: kill mode enabled");
+	else if (aa_g_profile_mode == APPARMOR_STOP)
+		aa_info_message("AppArmor initialized: stop mode enabled");
 	else
 		aa_info_message("AppArmor initialized");
 
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 44c84b8..75e7ec4 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -97,6 +97,7 @@
 	"enforce",
 	"complain",
 	"kill",
+	"stop",
 };
 
 /**