core: Add AppArmor profile switching

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.
This commit is contained in:
Michael Scherer 2014-02-20 16:19:44 +01:00 committed by Lennart Poettering
parent 37f78db2f4
commit eef65bf3ee
12 changed files with 144 additions and 5 deletions

View 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

View 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}

View File

@ -967,6 +967,19 @@
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>

View File

@ -45,6 +45,12 @@
#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_

View 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),

View File

@ -55,6 +55,10 @@
#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) {

View 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;

View 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)

View 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" },

View 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);

View 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";
}
}

View 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 {