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/pp
|
||||||
scripts/unanon
|
scripts/unanon
|
||||||
src/Makefile.in
|
src/Makefile.in
|
||||||
|
src/apparmor.c
|
||||||
src/conversation.c
|
src/conversation.c
|
||||||
src/copy_file.c
|
src/copy_file.c
|
||||||
src/edit_open.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_SELINUX (12<<6) /* selinux */
|
||||||
#define SUDO_DEBUG_UTIL (13<<6) /* utility functions */
|
#define SUDO_DEBUG_UTIL (13<<6) /* utility functions */
|
||||||
#define SUDO_DEBUG_UTMP (14<<6) /* utmp file ops */
|
#define SUDO_DEBUG_UTMP (14<<6) /* utmp file ops */
|
||||||
|
#define SUDO_DEBUG_APPARMOR (15<<6) /* AppArmor */
|
||||||
#define SUDO_DEBUG_ALL 0xffff0000 /* all subsystems */
|
#define SUDO_DEBUG_ALL 0xffff0000 /* all subsystems */
|
||||||
|
|
||||||
/* Error return for sudo_debug_register(). */
|
/* 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
|
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/sudo_noexec.c
|
||||||
|
|
||||||
# Autogenerated dependencies, do not modify
|
# 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 \
|
check_net_ifs.o: $(srcdir)/regress/net_ifs/check_net_ifs.c \
|
||||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||||
$(incdir)/sudo_util.h $(top_builddir)/config.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" },
|
{ "cmnd_cwd" },
|
||||||
{ "askpass" },
|
{ "askpass" },
|
||||||
{ "intercept_setid" },
|
{ "intercept_setid" },
|
||||||
|
{ "apparmor_profile" },
|
||||||
{ NULL }
|
{ 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++) {
|
for (i = 0; info[i] != NULL; i++) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]);
|
sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]);
|
||||||
switch (info[i][0]) {
|
switch (info[i][0]) {
|
||||||
|
case 'a':
|
||||||
|
SET_STRING("apparmor_profile=", apparmor_profile);
|
||||||
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
SET_STRING("chroot=", chroot)
|
SET_STRING("chroot=", chroot)
|
||||||
SET_STRING("command=", command)
|
SET_STRING("command=", command)
|
||||||
@@ -897,6 +900,15 @@ command_info_to_details(char * const info[], struct command_details *details)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
debug_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -103,6 +103,7 @@
|
|||||||
#define ARG_CWD 24
|
#define ARG_CWD 24
|
||||||
#define ARG_ASKPASS 25
|
#define ARG_ASKPASS 25
|
||||||
#define ARG_INTERCEPT_SETID 26
|
#define ARG_INTERCEPT_SETID 26
|
||||||
|
#define ARG_APPARMOR_PROFILE 27
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flags for tgetpass()
|
* Flags for tgetpass()
|
||||||
@@ -198,6 +199,7 @@ struct command_details {
|
|||||||
const char *chroot;
|
const char *chroot;
|
||||||
const char *selinux_role;
|
const char *selinux_role;
|
||||||
const char *selinux_type;
|
const char *selinux_type;
|
||||||
|
const char *apparmor_profile;
|
||||||
const char *utmp_user;
|
const char *utmp_user;
|
||||||
const char *tty;
|
const char *tty;
|
||||||
char **argv;
|
char **argv;
|
||||||
@@ -285,6 +287,10 @@ int selinux_setexeccon(void);
|
|||||||
void selinux_execve(int fd, const char *path, char *const argv[],
|
void selinux_execve(int fd, const char *path, char *const argv[],
|
||||||
char *envp[], int flags);
|
char *envp[], int flags);
|
||||||
|
|
||||||
|
/* apparmor.c */
|
||||||
|
int apparmor_is_enabled(void);
|
||||||
|
int apparmor_prepare(const char* new_profile);
|
||||||
|
|
||||||
/* solaris.c */
|
/* solaris.c */
|
||||||
void set_project(struct passwd *);
|
void set_project(struct passwd *);
|
||||||
int os_init_solaris(int argc, char *argv[], char *envp[]);
|
int os_init_solaris(int argc, char *argv[], char *envp[]);
|
||||||
|
Reference in New Issue
Block a user