Create a simple aa-exec implementation, written in C, matching the --help, --debug, --verbose, and --profile options present in the current Perl implementation.
The build system is updated to honor the USE_SYSTEM make variable which allows aa-exec to be linked against the system libapparmor rather than the libapparmor built in-tree. For now, the C based implementation is built as the aa-exec-c binary until it is feature equivalent with the Perl version. Signed-off-by: Tyler Hicks <tyhi...@canonical.com> --- utils/Makefile | 42 ++++++++++++++- utils/aa_exec.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 utils/aa_exec.c diff --git a/utils/Makefile b/utils/Makefile index 4762262..711626b 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -43,7 +43,46 @@ PYPREFIX=/usr PYFLAKES=pyflakes po/${NAME}.pot: ${TOOLS} ${PYMODULES} - $(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${PYMODULES}" + $(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${PYMODULES} aa-exec.c" + +ifdef USE_SYSTEM + LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \ + pkg-config --silence-errors --libs libapparmor ; \ + elif ldconfig -p | grep -q libapparmor\.so$$ ; then \ + echo -lapparmor ; \ + fi ) + ifeq ($(strip $(LIBAPPARMOR)),) + ERROR_MESSAGE = $(error ${nl}\ +************************************************************************${nl}\ +Unable to find libapparmor installed on this system; either${nl}\ +install libapparmor devel packages, set the LIBAPPARMOR variable${nl}\ +manually, or build against in-tree libapparmor.${nl}\ +************************************************************************${nl}) + endif # LIBAPPARMOR not set + LDLIBS += $(LIBAPPARMOR) +else # !USE_SYSTEM + # use in-tree versions + LIBAPPARMOR_SRC := ../libraries/libapparmor/ + LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include + LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/ + ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),) + ERROR_MESSAGE = $(error ${nl}\ +************************************************************************${nl}\ +$(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against${nl}\ +the in-tree libapparmor by building it first and then trying again${nl}\ +(see the top-level README for help) or build against the system${nl}\ +libapparmor by adding USE_SYSTEM=1 to your make command.${nl}\ +************************************************************************${nl}) + endif + + CFLAGS += -L$(LIBAPPARMOR_PATH) -I$(LIBAPPARMOR_INCLUDE) + LDLIBS += -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread +endif # USE_SYSTEM + +CFLAGS += -g -O0 -Wall -Wstrict-prototypes + +aa-exec-c: aa_exec.c + ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ ${LDLIBS} .PHONY: install install: ${MANPAGES} ${HTMLMANPAGES} @@ -71,6 +110,7 @@ clean: pod_clean rm -rf staging/ build/ rm -f apparmor/*.pyc apparmor/rule/*.pyc rm -rf apparmor/__pycache__/ apparmor/rule/__pycache__/ + rm -f aa-exec-c # ${CAPABILITIES} is defined in common/Make.rules .PHONY: check_severity_db diff --git a/utils/aa_exec.c b/utils/aa_exec.c new file mode 100644 index 0000000..12f54d1 --- /dev/null +++ b/utils/aa_exec.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015 + * Canonical, Ltd. (All rights reserved) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License 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, contact Novell, Inc. or Canonical + * Ltd. + */ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <sys/apparmor.h> +#include <unistd.h> + +static const char *opt_profile = NULL; +static bool opt_debug = false; +static bool opt_verbose = false; + +static void usage(const char *name, bool error) +{ + FILE *stream = stdout; + int status = EXIT_SUCCESS; + + if (error) { + stream = stderr; + status = EXIT_FAILURE; + } + + fprintf(stream, + "USAGE: %s [OPTIONS] <prog> <args>\n" + "\n" + "Confine <prog> with the specified PROFILE.\n" + "\n" + "OPTIONS:\n" + " -p PROFILE, --profile=PROFILE PROFILE to confine <prog> with\n" + " -d, --debug show messages with debugging information\n" + " -v, --verbose show messages with stats\n" + " -h, --help display this help\n" + "\n", name); + exit(status); +} + +#define error(fmt, args...) _error("aa-exec: ERROR: " fmt "\n", ## args) +static void _error(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + exit(EXIT_FAILURE); +} + +#define debug(fmt, args...) _debug("aa-exec: DEBUG: " fmt "\n", ## args) +static void _debug(const char *fmt, ...) +{ + va_list args; + + if (!opt_debug) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +#define verbose(fmt, args...) _verbose(fmt "\n", ## args) +static void _verbose(const char *fmt, ...) +{ + va_list args; + + if (!opt_verbose) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static void verbose_print_argv(char **argv) +{ + if (!opt_verbose) + return; + + fprintf(stderr, "exec"); + for (; *argv; argv++) + fprintf(stderr, " %s", *argv); + fprintf(stderr, "\n"); +} + +static char **parse_args(int argc, char **argv) +{ + int opt; + struct option long_opts[] = { + {"debug", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"profile", required_argument, 0, 'p'}, + {"verbose", no_argument, 0, 'v'}, + }; + + while ((opt = getopt_long(argc, argv, "+dhp:v", long_opts, NULL)) != -1) { + switch (opt) { + case 'd': + opt_debug = true; + break; + case 'h': + usage(argv[0], false); + break; + case 'p': + opt_profile = optarg; + break; + case 'v': + opt_verbose = true; + break; + default: + usage(argv[0], true); + break; + } + } + + if (optind >= argc) + usage(argv[0], true); + + return argv + optind; +} + +int main(int argc, char **argv) +{ + int rc = 0; + + argv = parse_args(argc, argv); + + if (opt_profile) { + verbose("aa_change_onexec(\"%s\")", opt_profile); + rc = aa_change_onexec(opt_profile); + debug("%d = aa_change_onexec(\"%s\")", rc, opt_profile); + } + + if (rc) { + if (errno == ENOENT || errno == EACCES) { + error("profile '%s' does not exist", opt_profile); + } else if (errno == EINVAL) { + error("AppArmor interface not available"); + } else { + error("%m"); + } + } + + verbose_print_argv(argv); + execvp(argv[0], argv); + error("Failed to execute \"%s\": %m", argv[0]); +} -- 2.5.0 -- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor