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.
This commit is contained in:
1
MANIFEST
1
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
|
||||
|
@@ -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(). */
|
||||
|
@@ -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
|
||||
|
111
src/apparmor.c
Normal file
111
src/apparmor.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2022 Will Shand <wss2ec@virginia.edu>
|
||||
*
|
||||
* 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 <config.h>
|
||||
|
||||
#ifdef HAVE_APPARMOR
|
||||
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/apparmor.h>
|
||||
|
||||
# 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 */
|
@@ -82,6 +82,7 @@ static struct sudo_settings sudo_settings[] = {
|
||||
{ "cmnd_cwd" },
|
||||
{ "askpass" },
|
||||
{ "intercept_setid" },
|
||||
{ "apparmor_profile" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
12
src/sudo.c
12
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;
|
||||
}
|
||||
|
||||
|
@@ -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[]);
|
||||
|
Reference in New Issue
Block a user