diff --git a/src/Makefile.am b/src/Makefile.am
index 020334e..dcfe2e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,7 +59,9 @@ snap_confine_SOURCES = \
mountinfo.c \
mountinfo.h \
ns-support.c \
- ns-support.h
+ ns-support.h \
+ apparmor-support.c \
+ apparmor-support.h
snap_confine_CFLAGS = -Wall -Werror $(AM_CFLAGS)
snap_confine_LDFLAGS = $(AM_LDFLAGS)
diff --git a/src/apparmor-support.c b/src/apparmor-support.c
new file mode 100644
index 0000000..e325e7a
--- /dev/null
+++ b/src/apparmor-support.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "apparmor-support.h"
+
+#include
+#include
+#ifdef HAVE_APPARMOR
+#include
+#endif // ifdef HAVE_APPARMOR
+
+#include "cleanup-funcs.h"
+#include "utils.h"
+
+// NOTE: Those constants map exactly what apparmor is returning and cannot be
+// changed without breaking apparmor functionality.
+#define SC_AA_ENFORCE_STR "enforce"
+#define SC_AA_COMPLAIN_STR "complain"
+#define SC_AA_MIXED_STR "mixed"
+#define SC_AA_UNCONFINED_STR "unconfined"
+
+void sc_init_apparmor_support(struct sc_apparmor *apparmor)
+{
+#ifdef HAVE_APPARMOR
+ // Use aa_is_enabled() to see if apparmor is available in the kernel and
+ // enabled at boot time. If it isn't log a diagnostic message and assume
+ // we're not confined.
+ if (aa_is_enabled() != true) {
+ switch (errno) {
+ case ENOSYS:
+ debug
+ ("apparmor extensions to the system are not available");
+ break;
+ case ECANCELED:
+ debug
+ ("apparmor is available on the system but has been disabled at boot");
+ break;
+ case ENOENT:
+ debug
+ ("apparmor is available but the interface but the interface is not available");
+ case EPERM:
+ // NOTE: fall-through
+ case EACCES:
+ debug
+ ("insufficient permissions to determine if apparmor is enabled");
+ break;
+ default:
+ debug("apparmor is not enabled: %s", strerror(errno));
+ break;
+ }
+ apparmor->is_confined = false;
+ apparmor->mode = SC_AA_NOT_APPLICABLE;
+ return;
+ }
+ // Use aa_getcon() to check the label of the current process and
+ // confinement type. Note that the returned label must be released with
+ // free() but the mode is a constant string that must not be freed.
+ char *label __attribute__ ((cleanup(sc_cleanup_string))) = NULL;
+ char *mode = NULL;
+ if (aa_getcon(&label, &mode) < 0) {
+ die("cannot query current apparmor profile");
+ }
+ // The label has a special value "unconfined" that is applied to all
+ // processes without a dedicated profile. If that label is used then the
+ // current process is not confined. All other labels imply confinement.
+ if (label != NULL && strcmp(label, SC_AA_UNCONFINED_STR) == 0) {
+ apparmor->is_confined = false;
+ } else {
+ apparmor->is_confined = true;
+ }
+ // There are several possible results for the confinement type (mode) that
+ // are checked for below.
+ if (mode != NULL && strcmp(mode, SC_AA_COMPLAIN_STR) == 0) {
+ apparmor->mode = SC_AA_COMPLAIN;
+ } else if (mode != NULL && strcmp(mode, SC_AA_ENFORCE_STR) == 0) {
+ apparmor->mode = SC_AA_ENFORCE;
+ } else if (mode != NULL && strcmp(mode, SC_AA_MIXED_STR) == 0) {
+ apparmor->mode = SC_AA_MIXED;
+ } else {
+ apparmor->mode = SC_AA_INVALID;
+ }
+#else
+ apparmor->mode = SC_AA_NOT_APPLICABLE;
+ apparmor->is_confined = false;
+#endif // ifdef HAVE_APPARMOR
+}
+
+void
+sc_maybe_aa_change_onexec(struct sc_apparmor *apparmor, const char *profile)
+{
+#ifdef HAVE_APPARMOR
+ debug("requesting changing of apparmor profile on next exec to %s",
+ profile);
+ if (aa_change_onexec(profile) < 0) {
+ if (secure_getenv("SNAPPY_LAUNCHER_INSIDE_TESTS") == NULL) {
+ die("cannot change profile for the next exec call");
+ }
+ }
+#endif // ifdef HAVE_APPARMOR
+}
+
+void
+sc_maybe_aa_change_hat(struct sc_apparmor *apparmor,
+ const char *subprofile, unsigned long magic_token)
+{
+#ifdef HAVE_APPARMOR
+ if (apparmor->is_confined) {
+ debug("changing apparmor hat to %s", subprofile);
+ if (aa_change_hat(subprofile, magic_token) < 0) {
+ die("cannot change apparmor hat");
+ }
+ }
+#endif // ifdef HAVE_APPARMOR
+}
diff --git a/src/apparmor-support.h b/src/apparmor-support.h
new file mode 100644
index 0000000..b90f285
--- /dev/null
+++ b/src/apparmor-support.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef SNAP_CONFINE_APPARMOR_SUPPORT_H
+#define SNAP_CONFINE_APPARMOR_SUPPORT_H
+
+#include
+
+/**
+ * Type of apparmor confinement.
+ **/
+enum sc_apparmor_mode {
+ // The enforcement mode was not recognized.
+ SC_AA_INVALID = -1,
+ // The enforcement mode is not applicable because apparmor is disabled.
+ SC_AA_NOT_APPLICABLE = 0,
+ // The enforcement mode is "enforcing"
+ SC_AA_ENFORCE = 1,
+ // The enforcement mode is "complain"
+ SC_AA_COMPLAIN,
+ // The enforcement mode is "mixed"
+ SC_AA_MIXED,
+};
+
+/**
+ * Data required to manage apparmor wrapper.
+ **/
+struct sc_apparmor {
+ // The mode of enforcement. In addition to the two apparmor defined modes
+ // can be also SC_AA_INVALID (unknown mode reported by apparmor) and
+ // SC_AA_NOT_APPLICABLE (when we're not linked with apparmor).
+ enum sc_apparmor_mode mode;
+ // Flag indicating that the current process is confined.
+ bool is_confined;
+};
+
+/**
+ * Initialize apparmor support.
+ *
+ * This operation should be done even when apparmor support is disabled at
+ * compile time. Internally the supplied structure is initialized based on the
+ * information returned from aa_getcon(2) or if apparmor is disabled at compile
+ * time, with built-in constants.
+ *
+ * The main action performed here is to check if snap-confine is currently
+ * confined, this information is used later in sc_maybe_change_apparmor_hat()
+ *
+ * As with many functions in the snap-confine tree, all errors result in
+ * process termination.
+ **/
+void sc_init_apparmor_support(struct sc_apparmor *apparmor);
+
+/**
+ * Maybe call aa_change_onexec(2)
+ *
+ * This function does nothing when apparmor support is not enabled at compile
+ * time. If apparmor is enabled then profile change request is attempted.
+ *
+ * As with many functions in the snap-confine tree, all errors result in
+ * process termination. As an exception, when SNAPPY_LAUNCHER_INSIDE_TESTS
+ * environment variable is set then the process is not terminated.
+ **/
+void
+sc_maybe_aa_change_onexec(struct sc_apparmor *apparmor, const char *profile);
+
+/**
+ * Maybe call aa_change_hat(2)
+ *
+ * This function does nothing when apparmor support is not enabled at compile
+ * time. If apparmor is enabled then hat change is attempted.
+ *
+ * As with many functions in the snap-confine tree, all errors result in
+ * process termination.
+ **/
+void
+sc_maybe_aa_change_hat(struct sc_apparmor *apparmor,
+ const char *subprofile, unsigned long magic_token);
+
+#endif