From 0b541c2029c58e7bd7f6e7bdfbf206fe84fb4cc6 Mon Sep 17 00:00:00 2001 From: kernelmethod Date: Mon, 23 May 2022 00:55:16 -0600 Subject: [PATCH 1/5] Add a --with-apparmor build flag Add a new build flag, --with-apparmor, that builds sudo with AppArmor support. Modify the build script for Debian and Ubuntu to enable this flag by default. --- config.h.in | 3 +++ configure.ac | 16 ++++++++++++++++ scripts/mkdep.pl | 2 +- scripts/mkpkg | 21 ++++++++++++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/config.h.in b/config.h.in index ac94b4983..d0be95822 100644 --- a/config.h.in +++ b/config.h.in @@ -51,6 +51,9 @@ /* Define to 1 if you use AIX general authentication. */ #undef HAVE_AIXAUTH +/* Define to 1 to enable AppArmor support. */ +#undef HAVE_APPARMOR + /* Define to 1 if you have the `arc4random' function. */ #undef HAVE_ARC4RANDOM diff --git a/configure.ac b/configure.ac index 541f62066..71325fb5a 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,7 @@ AC_SUBST([BAMAN]) AC_SUBST([LCMAN]) AC_SUBST([PSMAN]) AC_SUBST([SEMAN]) +AC_SUBST([AAMAN]) AC_SUBST([devdir]) AC_SUBST([mansectsu]) AC_SUBST([mansectform]) @@ -251,6 +252,7 @@ BAMAN=0 LCMAN=0 PSMAN=0 SEMAN=0 +AAMAN=0 LIBINTL= LIBCRYPTO= LIBMD= @@ -1483,6 +1485,19 @@ AC_ARG_WITH(selinux, [AS_HELP_STRING([--with-selinux], [enable SELinux support]) ;; esac], [with_selinux=no]) +AC_ARG_WITH(apparmor, [AS_HELP_STRING([--with-apparmor], [enable AppArmor support])], +[case $with_apparmor in + yes) AC_DEFINE(HAVE_APPARMOR) + AAMAN=1 + SUDO_OBJS="${SUDO_OBJS} apparmor.o" + AC_CHECK_LIB(apparmor, aa_change_profile, + [SUDO_LIBS="${SUDO_LIBS} -lapparmor"]) + ;; + no) ;; + *) AC_MSG_ERROR([--with-apparmor does not take an argument.]) + +esac], [with_apparmor=no]) + AC_ARG_ENABLE(sasl, [AS_HELP_STRING([--enable-sasl], [Enable/disable LDAP SASL support])], [ case "$enableval" in @@ -5416,6 +5431,7 @@ AH_TEMPLATE(HAVE_PROJECT_H, [Define to 1 if you have the header file AH_TEMPLATE(HAVE_SECURID, [Define to 1 if you use SecurID for authentication.]) AH_TEMPLATE(HAVE_SELINUX, [Define to 1 to enable SELinux RBAC support.]) AH_TEMPLATE(HAVE_SETKEYCREATECON, [Define to 1 if you have the `setkeycreatecon' function.]) +AH_TEMPLATE(HAVE_APPARMOR, [Define to 1 to enable AppArmor support.]) AH_TEMPLATE(HAVE_SHL_LOAD, [Define to 1 if you have the `shl_load' function.]) AH_TEMPLATE(HAVE_SKEY, [Define to 1 if you use S/Key.]) AH_TEMPLATE(HAVE_SKEYACCESS, [Define to 1 if your S/Key library has skeyaccess().]) diff --git a/scripts/mkdep.pl b/scripts/mkdep.pl index 3d6e6b65e..380439384 100755 --- a/scripts/mkdep.pl +++ b/scripts/mkdep.pl @@ -115,7 +115,7 @@ sub mkdep { # Expand some configure bits $makefile =~ s:\@DEV\@::g; $makefile =~ s:\@COMMON_OBJS\@:aix.lo event_poll.lo event_select.lo:; - $makefile =~ s:\@SUDO_OBJS\@:intercept.pb-c.o openbsd.o preload.o selinux.o sesh.o solaris.o:; + $makefile =~ s:\@SUDO_OBJS\@:intercept.pb-c.o openbsd.o preload.o apparmor.o selinux.o sesh.o solaris.o:; $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo ldap_util.lo ldap_conf.lo solaris_audit.lo sssd.lo:; # XXX - fill in AUTH_OBJS from contents of the auth dir instead $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:; diff --git a/scripts/mkpkg b/scripts/mkpkg index 294a77723..b477c8053 100755 --- a/scripts/mkpkg +++ b/scripts/mkpkg @@ -269,6 +269,23 @@ case "$osversion" in make_opts="${make_opts}${make_opts+ }"'docdir=$(datarootdir)/doc/packages/$(PACKAGE_TARNAME)' ;; deb*|ubu*) + # AppArmor is enabled by default starting in + # Debian: Debian 10 (Buster) + # Ubuntu: Ubuntu 12.04 (Precise Pangolin) + osmajor=`sed -n -e 's/^VERSION_ID=\"\([0-9]*\).*$/\1/p' /etc/os-release` + case "$osversion" in + deb*) + if [ -z $osmajor ] || [ $osmajor -ge 10 ]; then + with_apparmor=true + fi + ;; + ubu*) + if [ -z $osmajor ] || [ $osmajor -ge 14 ]; then + with_apparmor=true + fi + ;; + esac + # Encrypted remote I/O log support. with_openssl=true # Python plugins @@ -295,6 +312,9 @@ case "$osversion" in configure_opts="${configure_opts}${configure_opts+$tab}--with-sssd-lib=/usr/lib/$MULTIARCH" fi fi + if [ X"$with_apparmor" = X"true" ]; then + configure_opts="${configure_opts}${configure_opts+$tab}--with-apparmor" + fi configure_opts="--prefix=/usr --with-all-insults --with-pam @@ -311,7 +331,6 @@ case "$osversion" in --with-sendmail=/usr/sbin/sendmail --mandir=/usr/share/man --libexecdir=/usr/lib - --with-selinux --with-linux-audit $configure_opts" # Use correct libaudit dependency From bd25b85a668a2c37d217afe6c86b74fb6ea20604 Mon Sep 17 00:00:00 2001 From: kernelmethod Date: Mon, 23 May 2022 13:41:42 -0600 Subject: [PATCH 2/5] Add an apparmor_profile sudo setting Define a new sudo setting, `apparmor_profile`, that can be used to pass in an AppArmor profile that should be used to confine commands. If apparmor_profile is specified, sudo will execute the command using the new `apparmor_execve` function, which confines the command under the provided profile before exec'ing it. --- MANIFEST | 1 + include/sudo_debug.h | 1 + src/Makefile.in | 18 +++++++ src/apparmor.c | 111 +++++++++++++++++++++++++++++++++++++++++++ src/parse_args.c | 1 + src/sudo.c | 12 +++++ src/sudo.h | 6 +++ 7 files changed, 150 insertions(+) create mode 100644 src/apparmor.c diff --git a/MANIFEST b/MANIFEST index ccd3aa01e..2fb53d929 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1178,6 +1178,7 @@ scripts/mkpkg scripts/pp scripts/unanon src/Makefile.in +src/apparmor.c src/conversation.c src/copy_file.c src/edit_open.c diff --git a/include/sudo_debug.h b/include/sudo_debug.h index b598aefd0..399602b8f 100644 --- a/include/sudo_debug.h +++ b/include/sudo_debug.h @@ -85,6 +85,7 @@ struct sudo_conf_debug_file_list; #define SUDO_DEBUG_SELINUX (12<<6) /* selinux */ #define SUDO_DEBUG_UTIL (13<<6) /* utility functions */ #define SUDO_DEBUG_UTMP (14<<6) /* utmp file ops */ +#define SUDO_DEBUG_APPARMOR (15<<6) /* AppArmor */ #define SUDO_DEBUG_ALL 0xffff0000 /* all subsystems */ /* Error return for sudo_debug_register(). */ diff --git a/src/Makefile.in b/src/Makefile.in index f6ff40532..9882923ba 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -378,6 +378,24 @@ sudo_noexec.lo: $(srcdir)/sudo_noexec.c $(incdir)/sudo_compat.h \ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/sudo_noexec.c # Autogenerated dependencies, do not modify +apparmor.o: $(srcdir)/apparmor.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/sudo.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/apparmor.c +apparmor.i: $(srcdir)/apparmor.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/sudo.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -E -o $@ $(CPPFLAGS) $< +apparmor.plog: apparmor.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/apparmor.c --i-file $< --output-file $@ check_net_ifs.o: $(srcdir)/regress/net_ifs/check_net_ifs.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_util.h $(top_builddir)/config.h diff --git a/src/apparmor.c b/src/apparmor.c new file mode 100644 index 000000000..5f36f3064 --- /dev/null +++ b/src/apparmor.c @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2022 Will Shand + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#ifdef HAVE_APPARMOR + +# include +# include +# include + +# include "sudo.h" +# include "sudo_debug.h" + +/** + * @brief Check whether AppArmor is enabled. + * + * @return 1 if AppArmor is enabled, 0 otherwise. + */ +int +apparmor_is_enabled(void) +{ + int ret; + FILE *fd; + debug_decl(apparmor_is_enabled, SUDO_DEBUG_APPARMOR); + + /* Check whether AppArmor is enabled by reading + * /sys/module/apparmor/parameters/enabled + * + * When this file exists and its contents are equal to "Y", AppArmor + * is enabled. This is a little more reliable than using + * aa_is_enabled(2), which performs an additional check on securityfs + * that will fail in settings where securityfs isn't available + * (e.g. inside a container). + */ + + fd = fopen("/sys/module/apparmor/parameters/enabled", "r"); + if (fd == NULL) + debug_return_int(0); + + ret = (fgetc(fd) == 'Y'); + + fclose(fd); + debug_return_int(ret); +} + +/** + * @brief Prepare to transition into a new AppArmor profile. + * + * @param new_profile The AppArmor profile to transition into on the + * next exec. + * + * @return 0 on success, and a nonzero value on failure. + */ +int +apparmor_prepare(const char *new_profile) +{ + int ret; + char *mode, *old_profile; + debug_decl(apparmor_prepare, SUDO_DEBUG_APPARMOR); + + /* Determine the current AppArmor confinement status */ + if ((ret = aa_getcon(&old_profile, &mode)) == -1) { + sudo_warn("%s", U_("failed to determine AppArmor confinement")); + old_profile = NULL; + goto done; + } + + /* Tell AppArmor to transition into the new profile on the + * next exec */ + if ((ret = aa_change_onexec(new_profile)) != 0) { + sudo_warn(U_("unable to change AppArmor profile to %s"), new_profile); + goto done; + } + + if (mode == NULL) + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s: changing AppArmor profile: %s -> %s", __func__, + old_profile, new_profile ? new_profile : "?" + ); + else + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s: changing AppArmor profile: %s (%s) -> %s", __func__, + old_profile, mode, new_profile ? new_profile : "?" + ); + +done: + /* The profile string returned by aa_getcon must be free'd, while the + * mode string must _not_ be free'd */ + if (old_profile != NULL) + free(old_profile); + + debug_return_int(ret); +} + +#endif /* HAVE_APPARMOR */ diff --git a/src/parse_args.c b/src/parse_args.c index 9cc320bd7..5c2a62aed 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -82,6 +82,7 @@ static struct sudo_settings sudo_settings[] = { { "cmnd_cwd" }, { "askpass" }, { "intercept_setid" }, + { "apparmor_profile" }, { NULL } }; diff --git a/src/sudo.c b/src/sudo.c index c039cab49..fcf2492d4 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -689,6 +689,9 @@ command_info_to_details(char * const info[], struct command_details *details) for (i = 0; info[i] != NULL; i++) { sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]); switch (info[i][0]) { + case 'a': + SET_STRING("apparmor_profile=", apparmor_profile); + break; case 'c': SET_STRING("chroot=", chroot) SET_STRING("command=", command) @@ -897,6 +900,15 @@ command_info_to_details(char * const info[], struct command_details *details) exit(EXIT_FAILURE); } #endif + +#ifdef HAVE_APPARMOR + if (details->apparmor_profile != NULL && apparmor_is_enabled()) { + i = apparmor_prepare(details->apparmor_profile); + if (i != 0) + exit(EXIT_FAILURE); + } +#endif + debug_return; } diff --git a/src/sudo.h b/src/sudo.h index 2e088de72..2719eb698 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -103,6 +103,7 @@ #define ARG_CWD 24 #define ARG_ASKPASS 25 #define ARG_INTERCEPT_SETID 26 +#define ARG_APPARMOR_PROFILE 27 /* * Flags for tgetpass() @@ -198,6 +199,7 @@ struct command_details { const char *chroot; const char *selinux_role; const char *selinux_type; + const char *apparmor_profile; const char *utmp_user; const char *tty; char **argv; @@ -285,6 +287,10 @@ int selinux_setexeccon(void); void selinux_execve(int fd, const char *path, char *const argv[], char *envp[], int flags); +/* apparmor.c */ +int apparmor_is_enabled(void); +int apparmor_prepare(const char* new_profile); + /* solaris.c */ void set_project(struct passwd *); int os_init_solaris(int argc, char *argv[], char *envp[]); From c20859d55b1edfbc64c705d34165645a7942d161 Mon Sep 17 00:00:00 2001 From: kernelmethod Date: Mon, 23 May 2022 13:16:10 -0600 Subject: [PATCH 3/5] Add an APPARMOR_PROFILE user spec option to sudoers sudoers now supports an APPARMOR_PROFILE option, which can be specified as e.g. alice ALL=(ALL:ALL) APPARMOR_PROFILE=foo ALL The line above says "user alice can run any command as any user/group, under confinement by the AppArmor profile 'foo'." Profiles can be specified in any way that complies with the rules of aa_change_profile(2). For instance, the sudoers configuration alice ALL=(ALL:ALL) APPARMOR_PROFILE=unconfined ALL allows alice to run any command unconfined (i.e., without an AppArmor profile), while alice ALL=(ALL:ALL) APPARMOR_PROFILE=foo//&bar ALL tells sudoers that alice can run any command under the stacked AppArmor profiles 'foo' and 'bar'. The intention of this option is to give sysadmins on Linux distros supporting AppArmor better options for fine-grained access control. Among other things, this option can enforce mandatory access control (MAC) over the operations that a privileged user is able to perform to ensure that they cannot privesc past the boundaries of a specified profile. It can also be used to limit which users are able to get unconfined system access, by enforcing a default AppArmor profile on all users and then specifying 'APPARMOR_PROFILE=unconfined' for a privileged subset of users. --- plugins/sudoers/check.c | 3 +++ plugins/sudoers/cvtsudoers_csv.c | 13 ++++++++++- plugins/sudoers/cvtsudoers_json.c | 13 +++++++++++ plugins/sudoers/cvtsudoers_ldif.c | 12 ++++++++++ plugins/sudoers/cvtsudoers_merge.c | 8 +++++++ plugins/sudoers/def_data.c | 4 ++++ plugins/sudoers/def_data.h | 2 ++ plugins/sudoers/def_data.in | 3 +++ plugins/sudoers/fmtsudoers.c | 4 ++++ plugins/sudoers/gram.y | 19 ++++++++++++++++ plugins/sudoers/parse.c | 22 +++++++++++++++++++ plugins/sudoers/parse.h | 6 +++++ plugins/sudoers/policy.c | 16 ++++++++++++++ plugins/sudoers/regress/fuzz/fuzz_policy.dict | 1 + .../sudoers/regress/fuzz/fuzz_sudoers.dict | 2 ++ plugins/sudoers/sudoers.c | 3 +++ plugins/sudoers/sudoers.h | 4 ++++ plugins/sudoers/toke.l | 8 +++++++ 18 files changed, 142 insertions(+), 1 deletion(-) diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c index 21dc2e4da..6f5d9cc0a 100644 --- a/plugins/sudoers/check.c +++ b/plugins/sudoers/check.c @@ -190,6 +190,9 @@ check_user(int validated, int mode) #ifdef HAVE_SELINUX if (user_role == NULL && user_type == NULL) #endif +#ifdef HAVE_APPARMOR + if (user_apparmor_profile == NULL) +#endif #ifdef HAVE_PRIV_SET if (runas_privs == NULL && runas_limitprivs == NULL) #endif diff --git a/plugins/sudoers/cvtsudoers_csv.c b/plugins/sudoers/cvtsudoers_csv.c index bcfa28d9f..b13861d63 100644 --- a/plugins/sudoers/cvtsudoers_csv.c +++ b/plugins/sudoers/cvtsudoers_csv.c @@ -553,6 +553,14 @@ print_cmndspec_csv(FILE *fp, struct sudoers_parse_tree *parse_tree, } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (cs->apparmor_profile != NULL) { + fprintf(fp, "%sapparmor_profile=%s,", need_comma ? "," : "", + cs->apparmor_profile); + need_comma = true; + } +#endif /* HAVE_APPARMOR */ + #ifdef HAVE_PRIV_SET /* Print Solaris privs/limitprivs */ if (cs->privs != NULL || cs->limitprivs != NULL) { @@ -571,7 +579,7 @@ print_cmndspec_csv(FILE *fp, struct sudoers_parse_tree *parse_tree, /* * Merge adjacent commands with matching tags, runas, SELinux - * role/type and Solaris priv settings. + * role/type, AppArmor profiles and Solaris priv settings. */ for (;;) { /* Does the next entry differ only in the command itself? */ @@ -585,6 +593,9 @@ print_cmndspec_csv(FILE *fp, struct sudoers_parse_tree *parse_tree, #ifdef HAVE_SELINUX || cs->role != next->role || cs->type != next->type #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + || cs->apparmor_profile != next->apparmor_profile +#endif /* HAVE_APPARMOR */ || cs->runchroot != next->runchroot || cs->runcwd != next->runcwd; if (!quoted && !last_one) { diff --git a/plugins/sudoers/cvtsudoers_json.c b/plugins/sudoers/cvtsudoers_json.c index 10386617e..a89ef82b3 100644 --- a/plugins/sudoers/cvtsudoers_json.c +++ b/plugins/sudoers/cvtsudoers_json.c @@ -581,6 +581,9 @@ cmndspec_continues(struct cmndspec *cs, struct cmndspec *next) #ifdef HAVE_SELINUX && cs->role == next->role && cs->type == next->type #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + && cs->apparmor_profile == next->apparmor_profile +#endif /* HAVE_APPARMOR */ && cs->runchroot == next->runchroot && cs->runcwd == next->runcwd; return ret; } @@ -755,6 +758,16 @@ print_cmndspec_json(struct json_container *jsonc, } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (cs->apparmor_profile != NULL) { + sudo_json_open_array(jsonc, "AppArmor_Spec"); + value.type = JSON_STRING; + value.u.string = cs->apparmor_profile; + sudo_json_add_value(jsonc, "apparmor_profile", &value); + sudo_json_close_array(jsonc); + } +#endif /* HAVE_APPARMOR */ + #ifdef HAVE_PRIV_SET /* Print Solaris privs/limitprivs */ if (cs->privs != NULL || cs->limitprivs != NULL) { diff --git a/plugins/sudoers/cvtsudoers_ldif.c b/plugins/sudoers/cvtsudoers_ldif.c index a71890b95..91acfcd86 100644 --- a/plugins/sudoers/cvtsudoers_ldif.c +++ b/plugins/sudoers/cvtsudoers_ldif.c @@ -460,6 +460,18 @@ print_cmndspec_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + /* Print AppArmor profile */ + if (cs->apparmor_profile != NULL) { + if (asprintf(&attr_val, "apparmor_profile=%s", cs->apparmor_profile) == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); + } +#endif /* HAVE_APPARMOR */ + #ifdef HAVE_PRIV_SET /* Print Solaris privs/limitprivs */ if (cs->privs != NULL || cs->limitprivs != NULL) { diff --git a/plugins/sudoers/cvtsudoers_merge.c b/plugins/sudoers/cvtsudoers_merge.c index 5093a0fa5..dde5b9b37 100644 --- a/plugins/sudoers/cvtsudoers_merge.c +++ b/plugins/sudoers/cvtsudoers_merge.c @@ -976,6 +976,14 @@ cmndspec_equivalent(struct cmndspec *cs1, struct cmndspec *cs2, bool check_negat debug_return_bool(false); } #endif +#ifdef HAVE_APPARMOR + if (cs1->apparmor_profile != NULL && cs2->apparmor_profile != NULL) { + if (strcmp(cs1->apparmor_profile, cs2->apparmor_profile) != 0) + debug_return_bool(false); + } else if (cs1->apparmor_profile != cs2->apparmor_profile) { + debug_return_bool(false); + } +#endif #ifdef HAVE_PRIV_SET if (cs1->privs != NULL && cs2->privs != NULL) { if (strcmp(cs1->privs, cs2->privs) != 0) diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c index e5a80e2b8..8849e0942 100644 --- a/plugins/sudoers/def_data.c +++ b/plugins/sudoers/def_data.c @@ -657,6 +657,10 @@ struct sudo_defs_types sudo_defs_table[] = { "passprompt_regex", T_LIST|T_SPACE|T_BOOL, N_("List of regular expressions to use when matching a password prompt"), NULL, + }, { + "apparmor_profile", T_STR, + N_("AppArmor profile to use in the new security context: %s"), + NULL, }, { NULL, 0, NULL } diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h index 4795177bb..754b00075 100644 --- a/plugins/sudoers/def_data.h +++ b/plugins/sudoers/def_data.h @@ -306,6 +306,8 @@ #define def_log_passwords (sudo_defs_table[I_LOG_PASSWORDS].sd_un.flag) #define I_PASSPROMPT_REGEX 152 #define def_passprompt_regex (sudo_defs_table[I_PASSPROMPT_REGEX].sd_un.list) +#define I_APPARMOR_PROFILE 153 +#define def_apparmor_profile (sudo_defs_table[I_APPARMOR_PROFILE].sd_un.str) enum def_tuple { never, diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in index d0cc1780d..4ac670c2c 100644 --- a/plugins/sudoers/def_data.in +++ b/plugins/sudoers/def_data.in @@ -475,3 +475,6 @@ log_passwords passprompt_regex T_LIST|T_SPACE|T_BOOL "List of regular expressions to use when matching a password prompt" +apparmor_profile + T_STR + "AppArmor profile to use in the new security context: %s" diff --git a/plugins/sudoers/fmtsudoers.c b/plugins/sudoers/fmtsudoers.c index e786f6636..c4152efbf 100644 --- a/plugins/sudoers/fmtsudoers.c +++ b/plugins/sudoers/fmtsudoers.c @@ -241,6 +241,10 @@ sudoers_format_cmndspec(struct sudo_lbuf *lbuf, if (cs->type != NULL && FIELD_CHANGED(prev_cs, cs, type)) sudo_lbuf_append(lbuf, "TYPE=%s ", cs->type); #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (cs->apparmor_profile != NULL && FIELD_CHANGED(prev_cs, cs, apparmor_profile)) + sudo_lbuf_append(lbuf, "APPARMOR_PROFILE=%s ", cs->apparmor_profile); +#endif /* HAVE_APPARMOR */ if (cs->runchroot != NULL && FIELD_CHANGED(prev_cs, cs, runchroot)) sudo_lbuf_append(lbuf, "CHROOT=%s ", cs->runchroot); if (cs->runcwd != NULL && FIELD_CHANGED(prev_cs, cs, runcwd)) diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index aafe43afc..72b06efa2 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -144,6 +144,7 @@ static void alias_error(const char *name, int errnum); %token CWD /* working directory for command */ %token TYPE /* SELinux type */ %token ROLE /* SELinux role */ +%token APPARMOR_PROFILE /* AppArmor profile */ %token PRIVS /* Solaris privileges */ %token LIMITPRIVS /* Solaris limit privileges */ %token CMND_TIMEOUT /* command timeout */ @@ -182,6 +183,7 @@ static void alias_error(const char *name, int errnum); %type chrootspec %type rolespec %type typespec +%type apparmor_profilespec %type privsspec %type limitprivsspec %type timeoutspec @@ -534,6 +536,10 @@ cmndspec : runasspec options cmndtag digcmnd { cs->type = $2.type; parser_leak_remove(LEAK_PTR, $2.type); #endif +#ifdef HAVE_APPARMOR + cs->apparmor_profile = $2.apparmor_profile; + parser_leak_remove(LEAK_PTR, $2.apparmor_profile); +#endif #ifdef HAVE_PRIV_SET cs->privs = $2.privs; parser_leak_remove(LEAK_PTR, $2.privs); @@ -688,6 +694,11 @@ typespec : TYPE '=' WORD { } ; +apparmor_profilespec : APPARMOR_PROFILE '=' WORD { + $$ = $3; + } + ; + privsspec : PRIVS '=' WORD { $$ = $3; } @@ -783,6 +794,7 @@ reserved_word : ALL { $$ = "ALL"; } | TYPE { $$ = "TYPE"; } | PRIVS { $$ = "PRIVS"; } | LIMITPRIVS { $$ = "LIMITPRIVS"; } + | APPARMOR_PROFILE { $$ = "APPARMOR_PROFILE"; } ; reserved_alias : reserved_word { @@ -846,6 +858,13 @@ options : /* empty */ { parser_leak_remove(LEAK_PTR, $$.type); free($$.type); $$.type = $2; +#endif + } + | options apparmor_profilespec { +#ifdef HAVE_APPARMOR + parser_leak_remove(LEAK_PTR, $$.apparmor_profile); + free($$.apparmor_profile); + $$.apparmor_profile = $2; #endif } | options privsspec { diff --git a/plugins/sudoers/parse.c b/plugins/sudoers/parse.c index a69b67e52..a56f86c81 100644 --- a/plugins/sudoers/parse.c +++ b/plugins/sudoers/parse.c @@ -254,6 +254,24 @@ apply_cmndspec(struct cmndspec *cs) } } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + /* Set AppArmor profile, if specified */ + if (cs->apparmor_profile != NULL) { + user_apparmor_profile = strdup(cs->apparmor_profile); + if (user_apparmor_profile == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_bool(false); + } + } else { + user_apparmor_profile = def_apparmor_profile; + def_apparmor_profile = NULL; + } + if (user_apparmor_profile != NULL) { + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "user_apparmor_profile -> %s", user_apparmor_profile); + } +#endif #ifdef HAVE_PRIV_SET /* Set Solaris privilege sets */ if (runas_privs == NULL) { @@ -525,6 +543,10 @@ new_long_entry(struct cmndspec *cs, struct cmndspec *prev_cs) if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0)) debug_return_bool(true); #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0)) + debug_return_bool(true); +#endif /* HAVE_APPARMOR */ if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0)) debug_return_bool(true); if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0)) diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index ed15a3440..e276faad0 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -149,6 +149,9 @@ struct command_options { #ifdef HAVE_SELINUX char *role, *type; /* SELinux role and type */ #endif +#ifdef HAVE_APPARMOR + char *apparmor_profile; /* AppArmor profile */ +#endif #ifdef HAVE_PRIV_SET char *privs, *limitprivs; /* Solaris privilege sets */ #endif @@ -233,6 +236,9 @@ struct cmndspec { #ifdef HAVE_SELINUX char *role, *type; /* SELinux role and type */ #endif +#ifdef HAVE_APPARMOR + char *apparmor_profile; /* AppArmor profile */ +#endif #ifdef HAVE_PRIV_SET char *privs, *limitprivs; /* Solaris privilege sets */ #endif diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index fab06017a..e346e3c6a 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -321,6 +321,16 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults) continue; } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (MATCHES(*cur, "apparmor_profile=")) { + CHECK(*cur, "apparmor_profile="); + free(user_apparmor_profile); + user_apparmor_profile = strdup(*cur + sizeof("apparmor_profile=") - 1); + if (user_apparmor_profile == NULL) + goto oom; + continue; + } +#endif /* HAVE_APPARMOR */ #ifdef HAVE_BSD_AUTH_H if (MATCHES(*cur, "bsdauth_type=")) { CHECK(*cur, "bsdauth_type="); @@ -933,6 +943,12 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[], goto oom; } #endif /* HAVE_SELINUX */ +#ifdef HAVE_APPARMOR + if (user_apparmor_profile != NULL) { + if ((command_info[info_len++] = sudo_new_key_val("apparmor_profile", user_apparmor_profile)) == NULL) + goto oom; + } +#endif /* HAVE_APPARMOR */ #ifdef HAVE_PRIV_SET if (runas_privs != NULL) { if ((command_info[info_len++] = sudo_new_key_val("runas_privs", runas_privs)) == NULL) diff --git a/plugins/sudoers/regress/fuzz/fuzz_policy.dict b/plugins/sudoers/regress/fuzz/fuzz_policy.dict index 48678c853..c031eb9ac 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_policy.dict +++ b/plugins/sudoers/regress/fuzz/fuzz_policy.dict @@ -30,6 +30,7 @@ "runas_limitprivs" "selinux_role" "selinux_type" +"apparmor_profile" "bsdauth_type" "network_addrs" "max_groups" diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers.dict b/plugins/sudoers/regress/fuzz/fuzz_sudoers.dict index ea90c49ee..93ef8e7a5 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_sudoers.dict +++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers.dict @@ -55,6 +55,7 @@ "NOTAFTER" "ROLE" "TYPE" +"APPARMOR_PROFILE" "PRIVS" "LIMITPRIVS" @@ -127,6 +128,7 @@ "env_keep" "role" "type" +"apparmor_profile" "env_file" "restricted_env_file" "sudoers_locale" diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 0342c69ca..98a0ee0bb 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -1790,6 +1790,9 @@ sudo_user_free(void) free(user_role); free(user_type); #endif +#ifdef HAVE_APPARMOR + free(user_apparmor_profile); +#endif #ifdef HAVE_PRIV_SET free(runas_privs); free(runas_limitprivs); diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 6c09ccc82..1a57385e9 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -111,6 +111,9 @@ struct sudo_user { char *role; char *type; #endif +#ifdef HAVE_APPARMOR + char *apparmor_profile; +#endif #ifdef HAVE_PRIV_SET char *privs; char *limitprivs; @@ -246,6 +249,7 @@ struct sudo_user { #define runas_gr (sudo_user._runas_gr) #define user_role (sudo_user.role) #define user_type (sudo_user.type) +#define user_apparmor_profile (sudo_user.apparmor_profile) #define user_closefrom (sudo_user.closefrom) #define runas_privs (sudo_user.privs) #define runas_limitprivs (sudo_user.limitprivs) diff --git a/plugins/sudoers/toke.l b/plugins/sudoers/toke.l index 91280739b..3cd4eecb8 100644 --- a/plugins/sudoers/toke.l +++ b/plugins/sudoers/toke.l @@ -679,6 +679,14 @@ ALL { goto got_alias; #endif } +APPARMOR_PROFILE { +#ifdef HAVE_APPARMOR + LEXTRACE("APPARMOR_PROFILE "); + return APPARMOR_PROFILE; +#else + goto got_alias; +#endif + } PRIVS { #ifdef HAVE_PRIV_SET LEXTRACE("PRIVS "); From 58ce33abab12b5bf276d1bdf069f2df69be50816 Mon Sep 17 00:00:00 2001 From: kernelmethod Date: Sun, 22 May 2022 17:11:06 -0600 Subject: [PATCH 4/5] Add libapparmor-dev to the Debian and Ubuntu Dockerfiles Install libapparmor-dev on Debian- and Ubuntu-based Docker images so that they can build sudo with AppArmor support. --- docker/debian/latest/Dockerfile | 1 + docker/debian/testing/Dockerfile | 1 + docker/ubuntu/devel/Dockerfile | 1 + docker/ubuntu/latest/Dockerfile | 1 + docker/ubuntu/rolling/Dockerfile | 1 + 5 files changed, 5 insertions(+) diff --git a/docker/debian/latest/Dockerfile b/docker/debian/latest/Dockerfile index 9e2d5b9a0..80417ffd8 100644 --- a/docker/debian/latest/Dockerfile +++ b/docker/debian/latest/Dockerfile @@ -5,5 +5,6 @@ RUN DEBIAN_FRONTEND=noninteractive TZ=America/Denver apt-get update && \ build-essential curl dpkg-dev ed libldap2-dev libpam0g-dev \ libsasl2-dev libselinux1-dev libsepol1-dev libssl-dev zlib1g-dev \ libaudit-dev libssl-dev python3-dev libpython3-dev libwolfssl-dev \ + libapparmor-dev \ file lsb-release fakeroot pkg-config procps git ssh openssh-client RUN useradd -ms /bin/bash build diff --git a/docker/debian/testing/Dockerfile b/docker/debian/testing/Dockerfile index f8498bb20..34aa72d23 100644 --- a/docker/debian/testing/Dockerfile +++ b/docker/debian/testing/Dockerfile @@ -5,5 +5,6 @@ RUN DEBIAN_FRONTEND=noninteractive TZ=America/Denver apt-get update && \ build-essential curl dpkg-dev ed libldap2-dev libpam0g-dev \ libsasl2-dev libselinux1-dev libsepol1-dev libssl-dev zlib1g-dev \ libaudit-dev libssl-dev python3-dev libpython3-dev libwolfssl-dev \ + libapparmor-dev \ file lsb-release fakeroot pkg-config procps git ssh openssh-client RUN useradd -ms /bin/bash build diff --git a/docker/ubuntu/devel/Dockerfile b/docker/ubuntu/devel/Dockerfile index 5ded20e3b..fffc141b5 100644 --- a/docker/ubuntu/devel/Dockerfile +++ b/docker/ubuntu/devel/Dockerfile @@ -5,5 +5,6 @@ RUN DEBIAN_FRONTEND=noninteractive TZ=America/Denver apt-get update && \ build-essential curl dpkg-dev ed libldap2-dev libpam0g-dev \ libsasl2-dev libselinux1-dev libsepol1-dev libssl-dev zlib1g-dev \ libaudit-dev libssl-dev python3-dev libpython3-dev libwolfssl-dev \ + libapparmor-dev \ file lsb-release fakeroot pkg-config procps git ssh openssh-client RUN useradd -ms /bin/bash build diff --git a/docker/ubuntu/latest/Dockerfile b/docker/ubuntu/latest/Dockerfile index db619f97e..a566c5df9 100644 --- a/docker/ubuntu/latest/Dockerfile +++ b/docker/ubuntu/latest/Dockerfile @@ -5,5 +5,6 @@ RUN DEBIAN_FRONTEND=noninteractive TZ=America/Denver apt-get update && \ build-essential curl dpkg-dev ed libldap2-dev libpam0g-dev \ libsasl2-dev libselinux1-dev libsepol1-dev libssl-dev zlib1g-dev \ libaudit-dev libssl-dev python3-dev libpython3-dev libwolfssl-dev \ + libapparmor-dev \ file lsb-release fakeroot pkg-config procps git ssh openssh-client RUN useradd -ms /bin/bash build diff --git a/docker/ubuntu/rolling/Dockerfile b/docker/ubuntu/rolling/Dockerfile index 6a9aebfc0..2d6a0981d 100644 --- a/docker/ubuntu/rolling/Dockerfile +++ b/docker/ubuntu/rolling/Dockerfile @@ -5,5 +5,6 @@ RUN DEBIAN_FRONTEND=noninteractive TZ=America/Denver apt-get update && \ build-essential curl dpkg-dev ed libldap2-dev libpam0g-dev \ libsasl2-dev libselinux1-dev libsepol1-dev libssl-dev zlib1g-dev \ libaudit-dev libssl-dev python3-dev libpython3-dev libwolfssl-dev \ + libapparmor-dev \ file lsb-release fakeroot pkg-config procps git ssh openssh-client RUN useradd -ms /bin/bash build From d8184fdb6f5299d5a27593bcf4953661dcb6b726 Mon Sep 17 00:00:00 2001 From: kernelmethod Date: Mon, 23 May 2022 00:45:49 -0600 Subject: [PATCH 5/5] Add documentation for AppArmor support - Document the AppArmor userspec option in the sudoers man pages. - Add information about the --with-apparmor build configuration option to INSTALL.md. --- INSTALL.md | 4 +++ docs/sudoers.man.in | 69 +++++++++++++++++++++++++++++++++++++++ docs/sudoers.mdoc.in | 77 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 73d19368d..e6a123138 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -375,6 +375,10 @@ Defaults are listed in brackets after the description. ldap_sasl_interactive_bind_s() function is present in the LDAP libraries. + --with-apparmor + Enable support for the AppArmor Linux Security Module (LSM) on + supported systems. + --with-logincap This adds support for login classes specified in `/etc/login.conf`. It is enabled by default on BSD/OS, Darwin, FreeBSD, OpenBSD, and diff --git a/docs/sudoers.man.in b/docs/sudoers.man.in index d1b5cb576..b2a2e573b 100644 --- a/docs/sudoers.man.in +++ b/docs/sudoers.man.in @@ -1290,6 +1290,8 @@ Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')' SELinux_Spec ::= ('ROLE=role' | 'TYPE=type') .\} +AppArmor_Spec ::= 'APPARMOR_PROFILE=profile' + .if \n(PS \{\ Solaris_Priv_Spec ::= ('PRIVS=privset' | 'LIMITPRIVS=privset') @@ -1503,6 +1505,7 @@ Options may consist of .if \n(SL \{\ SELinux roles and/or types, .\} +AppArmor profiles, .if \n(PS \{\ Solaris privileges sets, .\} @@ -1533,6 +1536,59 @@ A role or type specified on the command line, however, will supersede the values in \fIsudoers\fR. .\} +.SS "AppArmor_Spec" +On systems supporting AppArmor, +\fIsudoers\fR +file entries may optionally specify an AppArmor profile that should be +used to confine a command. +If an AppArmor profile is specified with the command, it will override +any default values specified in +\fIsudoers\fR. +Appropriate profile transition rules must be defined to support the +profile change specified for a user. +.PP +AppArmor profiles can be specified in any way that complies with the +rules of +aa_change_profile(2). +For instance, in the following +\fIsudoers\fR +entry +.nf +.sp +.RS 0n +alice ALL = (root) APPARMOR_PROFILE=my-profile ALL +.RE +.fi +.PP +the user +\fBalice\fR +may run any command as root under confinement by the profile +\(oqmy-profile\(cq. +You can also stack profiles, or allow a user to run commands unconfined by +any profile. E.g., +.nf +.sp +.RS 0n +bob ALL = (root) APPARMOR_PROFILE=foo//&bar /usr/bin/vi +cathy ALL = (root) APPARMOR_PROFILE=unconfined /bin/ls +.RE +.fi +.PP +These +\fIsudoers\fR +entries allow user +\fBbob\fR +to run +\fI/usr/bin/vi\fR +as root under the stacked profiles +\(oqfoo\(cq +and +\(oqbar\(cq, +and user +\fBcathy\fR +to run +\fI/bin/ls\fR +without any confinement at all. .if \n(PS \{\ .SS "Solaris_Priv_Spec" On Solaris systems, @@ -4159,6 +4215,19 @@ which does not create a new PAM session. .PP \fBStrings\fR: .TP 18n +apparmor_profile +The default AppArmor profile to transition into when executing the +command. +The default +\fIapparmor_profile\fR +can be overriden for individual +\fIsudoers\fR +entries by specifying the +\fIAPPARMOR_PROFILE\fR +option. +This option is only available when sudo is built with AppArmor +support. +.TP 18n authfail_message Message that is displayed after a user fails to authenticate. The message may include the diff --git a/docs/sudoers.mdoc.in b/docs/sudoers.mdoc.in index 04661891c..233270076 100644 --- a/docs/sudoers.mdoc.in +++ b/docs/sudoers.mdoc.in @@ -21,6 +21,7 @@ .\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" .nr SL @SEMAN@ +.nr AA @AAMAN@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ @@ -1231,13 +1232,23 @@ Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')' .el Option_Spec ::= (SELinux_Spec | Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec) .\} .el \{\ +.ie \n(AA \{\ +.ie \n(PS Option_Spec ::= (AppArmor_Spec | Solaris_Priv_Spec | Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec) +.el Option_Spec ::= (AppArmor_Spec | Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec) +.\} +.el \{\ .ie \n(PS Option_Spec ::= (Solaris_Priv_Spec | Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec) .el Option_Spec ::= (Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec) .\} +.\} .if \n(SL \{\ SELinux_Spec ::= ('ROLE=role' | 'TYPE=type') +.\} +.if \n(AA \{\ +AppArmor_Spec ::= 'APPARMOR_PROFILE=profile' + .\} .if \n(PS \{\ Solaris_Priv_Spec ::= ('PRIVS=privset' | 'LIMITPRIVS=privset') @@ -1427,6 +1438,9 @@ Options may consist of .if \n(SL \{\ SELinux roles and/or types, .\} +.if \n(AA \{\ +AppArmor profiles, +.\} .if \n(PS \{\ Solaris privileges sets, .\} @@ -1457,6 +1471,55 @@ A role or type specified on the command line, however, will supersede the values in .Em sudoers . .\} +.if \n(AA \{\ +.Ss AppArmor_Spec +On systems supporting AppArmor, +.Em sudoers +file entries may optionally specify an AppArmor profile that should be +used to confine a command. +If an AppArmor profile is specified with the command, it will override +any default values specified in +.Em sudoers . +Appropriate profile transition rules must be defined to support the +profile change specified for a user. +.Pp +AppArmor profiles can be specified in any way that complies with the +rules of +.Xr aa_change_profile 2 . +For instance, in the following +.Em sudoers +entry +.Bd -literal +alice ALL = (root) APPARMOR_PROFILE=my-profile ALL +.Ed +.Pp +the user +.Sy alice +may run any command as root under confinement by the profile +.Ql my-profile . +You can also stack profiles, or allow a user to run commands unconfined by +any profile. E.g., +.Bd -literal +bob ALL = (root) APPARMOR_PROFILE=foo//&bar /usr/bin/vi +cathy ALL = (root) APPARMOR_PROFILE=unconfined /bin/ls +.Ed +.Pp +These +.Em sudoers +entries allow user +.Sy bob +to run +.Pa /usr/bin/vi +as root under the stacked profiles +.Ql foo +and +.Ql bar , +and user +.Sy cathy +to run +.Pa /bin/ls +without any confinement at all. +.\} .if \n(PS \{\ .Ss Solaris_Priv_Spec On Solaris systems, @@ -3929,6 +3992,20 @@ which does not create a new PAM session. .Pp .Sy Strings : .Bl -tag -width 16n +.if \n(AA \{\ +.It apparmor_profile +The default AppArmor profile to transition into when executing the +command. +The default +.Em apparmor_profile +can be overriden for individual +.Em sudoers +entries by specifying the +.Em APPARMOR_PROFILE +option. +This option is only available when sudo is built with AppArmor +support. +.\} .It authfail_message Message that is displayed after a user fails to authenticate. The message may include the