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