chiark / gitweb /
core: Add AppArmor profile switching
authorMichael Scherer <misc@zarb.org>
Thu, 20 Feb 2014 15:19:44 +0000 (16:19 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2014 02:44:20 +0000 (03:44 +0100)
This permit to switch to a specific apparmor profile when starting a daemon. This
will result in a non operation if apparmor is disabled.
It also add a new build requirement on libapparmor for using this feature.

12 files changed:
Makefile.am
configure.ac
man/systemd.exec.xml
src/core/build.h
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/shared/exit-status.c
src/shared/exit-status.h

index 56cdb85684919ace3fda03908afc8917e0f7de07..6f8d3b5a29be4d05416ac811367d32c046e618c1 100644 (file)
@@ -1020,6 +1020,7 @@ libsystemd_core_la_CFLAGS = \
        $(AUDIT_CFLAGS) \
        $(CAP_CFLAGS) \
        $(KMOD_CFLAGS) \
+       $(APPARMOR_CFLAGS) \
        $(SECCOMP_CFLAGS) \
        -pthread
 
@@ -1035,6 +1036,7 @@ libsystemd_core_la_LIBADD = \
        $(AUDIT_LIBS) \
        $(CAP_LIBS) \
        $(KMOD_LIBS) \
+       $(APPARMOR_LIBS) \
        $(SECCOMP_LIBS)
 
 if HAVE_SECCOMP
index c03a0e26b839ec732ff8164999c4c52b8a99a50d..80c14493e6e349c22460a1c067dbea9357b80e77 100644 (file)
@@ -385,6 +385,21 @@ if test "x$enable_selinux" != "xno"; then
 fi
 AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
 
+have_apparmor=no
+AC_ARG_ENABLE(apparmor, AS_HELP_STRING([--disable-apparmor], [Disable optional AppArmor support]))
+if test "x$enable_apparmor" != "xno"; then
+        PKG_CHECK_MODULES([APPARMOR], [libapparmor],
+                [AC_DEFINE(HAVE_APPARMOR, 1, [Define if AppArmor is available])
+                 have_apparmor=yes
+                 M4_DEFINES="$M4_DEFINES -DHAVE_APPARMOR"],
+                [have_apparmor=no])
+        if test "x$have_apparmor" = xno -a "x$enable_apparmor" = xyes; then
+                AC_MSG_ERROR([*** AppArmor support requested but libraries not found])
+        fi
+fi
+AM_CONDITIONAL(HAVE_APPARMOR, [test "$have_apparmor" = "yes"])
+
+
 AC_ARG_WITH(debug-shell,
         AS_HELP_STRING([--with-debug-shell=PATH],
                 [Path to debug shell binary]),
@@ -1110,6 +1125,7 @@ AC_MSG_RESULT([
         PAM:                     ${have_pam}
         AUDIT:                   ${have_audit}
         IMA:                     ${have_ima}
+        AppArmor:                ${have_apparmor}
         SELinux:                 ${have_selinux}
         SECCOMP:                 ${have_seccomp}
         SMACK:                   ${have_smack}
index 7dbe05d265123e08411eedacfbbb7dd606551591..19839937c71f2eadf25bc4912c1ed8f2576bf2d2 100644 (file)
                                 for details.</para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><varname>AppArmorProfile=</varname></term>
+
+                                <listitem><para>Take a profile name as argument.
+                                The process executed by the unit will switch to
+                                this profile when started. Profiles must already
+                                be loaded in the kernel, or the unit will fail.
+                                This result in a non operation if AppArmor is not
+                                enabled. If prefixed by <literal>-</literal>, all errors
+                                will be ignored.
+                                </para></listitem>
+                        </varlistentry>
+
                         <varlistentry>
                                 <term><varname>IgnoreSIGPIPE=</varname></term>
 
index c8117edff38fe1bb4e1e4a509ce4b97fb877a580..3d7cd3ea39b77e98aa8776afaf182d00856de2de 100644 (file)
 #define _SELINUX_FEATURE_ "-SELINUX"
 #endif
 
+#ifdef HAVE_APPARMOR
+#define _APPARMOR_FEATURE_ "+APPARMOR"
+#else
+#define _APPARMOR_FEATURE_ "-APPARMOR"
+#endif
+
 #ifdef HAVE_IMA
 #define _IMA_FEATURE_ "+IMA"
 #else
@@ -87,4 +93,4 @@
 #define _SECCOMP_FEATURE_ "-SECCOMP"
 #endif
 
-#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ " " _SECCOMP_FEATURE_
+#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ " " _SECCOMP_FEATURE_ " " _APPARMOR_FEATURE_
index 41dbbab904a6ab7988273137425cc75dba47822a..935c62bdf26b7f7c40e41389cb25b89e177463b3 100644 (file)
@@ -482,6 +482,24 @@ static int property_get_selinux_context(
         return sd_bus_message_append(reply, "(bs)", c->selinux_context_ignore, c->selinux_context);
 }
 
+static int property_get_apparmor_profile(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(c);
+
+        return sd_bus_message_append(reply, "(bs)", c->apparmor_profile_ignore, c->apparmor_profile);
+}
+
 static int property_get_personality(
                 sd_bus *bus,
                 const char *path,
@@ -560,6 +578,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
index f8b7521ff9ceb20b4a8f81b96036d40cb37c2e68..a328fc265f719ec123a0f1cab6b93d0e2808ea2f 100644 (file)
 #include <seccomp.h>
 #endif
 
+#ifdef HAVE_APPARMOR
+#include <sys/apparmor.h>
+#endif
+
 #include "execute.h"
 #include "strv.h"
 #include "macro.h"
@@ -77,6 +81,7 @@
 #include "async.h"
 #include "selinux-util.h"
 #include "errno-list.h"
+#include "apparmor-util.h"
 
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
@@ -1597,6 +1602,16 @@ int exec_spawn(ExecCommand *command,
                                 }
                         }
 #endif
+
+#ifdef HAVE_APPARMOR
+                        if (context->apparmor_profile && use_apparmor()) {
+                                err = aa_change_onexec(context->apparmor_profile);
+                                if (err < 0 && !context->apparmor_profile_ignore) {
+                                        r = EXIT_APPARMOR_PROFILE;
+                                        goto fail_child;
+                                }
+                        }
+#endif
                 }
 
                 err = build_environment(context, n_fds, watchdog_usec, home, username, shell, &our_env);
@@ -1759,6 +1774,9 @@ void exec_context_done(ExecContext *c) {
         free(c->selinux_context);
         c->selinux_context = NULL;
 
+        free(c->apparmor_profile);
+        c->apparmor_profile = NULL;
+
 #ifdef HAVE_SECCOMP
         set_free(c->syscall_filter);
         c->syscall_filter = NULL;
@@ -2188,6 +2206,11 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f,
                         "%sSystemCallErrorNumber: %s\n",
                         prefix, strna(errno_to_name(c->syscall_errno)));
+
+        if (c->apparmor_profile)
+                fprintf(f,
+                        "%sAppArmorProfile: %s%s\n",
+                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
 }
 
 void exec_status_start(ExecStatus *s, pid_t pid) {
index 5c4c0b4abe635393b1d1e8b23b337af5ff16c617..2bfe227145365534ed2b2eef9578c34fdda4bccc 100644 (file)
@@ -141,6 +141,9 @@ struct ExecContext {
         bool selinux_context_ignore;
         char *selinux_context;
 
+        bool apparmor_profile_ignore;
+        char *apparmor_profile;
+
         char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
         unsigned long mount_flags;
 
index e9995bf0c794aa919515e2ea842f6568e959f583..e1bab7a2643be18003ebb80cae1dd4fdb2f85c65 100644 (file)
@@ -90,7 +90,10 @@ $1.IgnoreSIGPIPE,                config_parse_bool,                  0,
 $1.UtmpIdentifier,               config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.utmp_id)
 m4_ifdef(`HAVE_SELINUX',
 `$1.SELinuxContext,              config_parse_exec_selinux_context,  0,                             offsetof($1, exec_context)',
-`$1.SELinuxContext,              config_parse_warn_compat,           0,                             0')'
+`$1.SELinuxContext,              config_parse_warn_compat,           0,                             0')
+m4_ifdef(`HAVE_APPARMOR',
+`$1.AppArmorProfile,              config_parse_exec_apparmor_profile,0,                             offsetof($1, exec_context)',
+`$1.AppArmorProfile,              config_parse_warn_compat,          0,                             0')'
 )m4_dnl
 m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
 `$1.SendSIGKILL,                 config_parse_bool,                  0,                             offsetof($1, kill_context.send_sigkill)
index 27666b937c2fc6a3ff85d0fca0b1aaaa8ddecaea..e74a790e25542cdf155dd3fcb4ed5a8af0849239 100644 (file)
@@ -61,7 +61,7 @@
 #include "seccomp-util.h"
 #endif
 
-#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
+#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
 int config_parse_warn_compat(
                 const char *unit,
                 const char *filename,
@@ -1192,6 +1192,55 @@ int config_parse_exec_selinux_context(
         return 0;
 }
 
+int config_parse_exec_apparmor_profile(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        Unit *u = userdata;
+        bool ignore;
+        char *k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                free(c->apparmor_profile);
+                c->apparmor_profile = NULL;
+                c->apparmor_profile_ignore = false;
+                return 0;
+        }
+
+        if (rvalue[0] == '-') {
+                ignore = true;
+                rvalue++;
+        } else
+                ignore = false;
+
+        r = unit_name_printf(u, rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                return 0;
+        }
+
+        free(c->apparmor_profile);
+        c->apparmor_profile = k;
+        c->apparmor_profile_ignore = ignore;
+
+        return 0;
+}
+
 int config_parse_timer(const char *unit,
                        const char *filename,
                        unsigned line,
@@ -2910,7 +2959,7 @@ void unit_dump_config_items(FILE *f) {
                 const ConfigParserCallback callback;
                 const char *rvalue;
         } table[] = {
-#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
+#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
                 { config_parse_warn_compat,           "NOTSUPPORTED" },
 #endif
                 { config_parse_int,                   "INTEGER" },
index 83ecea5da163c1f6b9edfbecc0fc9c7b867a3db6..4a5ec35cbe40d57b6887cf7308eeddf3e6b13fab 100644 (file)
@@ -89,6 +89,7 @@ int config_parse_job_mode(const char *unit, const char *filename, unsigned line,
 int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 
 /* gperf prototypes */
 const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
index 8b096da7e1b275e4f7130ac1a725dd622bb5ffd0..902f55ac659276c55268f38471cca37587aef4e4 100644 (file)
@@ -136,6 +136,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
 
                 case EXIT_PERSONALITY:
                         return "PERSONALITY";
+
+                case EXIT_APPARMOR_PROFILE:
+                        return "APPARMOR";
                 }
         }
 
index dde5bdda8b0836faf0e855a1a265224607962a43..de379f1aa00836743d9aee0e97c3106a2bea5558 100644 (file)
@@ -69,7 +69,8 @@ typedef enum ExitStatus {
         EXIT_NO_NEW_PRIVILEGES,
         EXIT_SECCOMP,
         EXIT_SELINUX_CONTEXT,
-        EXIT_PERSONALITY  /* 230 */
+        EXIT_PERSONALITY,  /* 230 */
+        EXIT_APPARMOR_PROFILE
 } ExitStatus;
 
 typedef enum ExitStatusLevel {