Merge from apparmor trunk; fixed up conflict due to imported indonesian

.po file.
This commit is contained in:
Steve Beattie 2016-01-25 10:55:41 -08:00
commit 28f072bfb2
181 changed files with 5512 additions and 941 deletions

View file

@ -46,7 +46,10 @@ libraries/libapparmor/ylwrap
libraries/libapparmor/doc/Makefile
libraries/libapparmor/doc/Makefile.in
libraries/libapparmor/doc/*.2
libraries/libapparmor/doc/aa_*.3
libraries/libapparmor/include/Makefile
libraries/libapparmor/include/Makefile.in
libraries/libapparmor/include/sys/Makefile
libraries/libapparmor/include/sys/Makefile.in
libraries/libapparmor/src/.deps
libraries/libapparmor/src/.libs
@ -54,10 +57,16 @@ libraries/libapparmor/src/Makefile
libraries/libapparmor/src/Makefile.in
libraries/libapparmor/src/af_protos.h
libraries/libapparmor/src/change_hat.lo
libraries/libapparmor/src/features.lo
libraries/libapparmor/src/grammar.lo
libraries/libapparmor/src/kernel.lo
libraries/libapparmor/src/kernel_interface.lo
libraries/libapparmor/src/libaalogparse.lo
libraries/libapparmor/src/libimmunix_warning.lo
libraries/libapparmor/src/policy_cache.lo
libraries/libapparmor/src/private.lo
libraries/libapparmor/src/scanner.lo
libraries/libapparmor/src/libapparmor.pc
libraries/libapparmor/src/libapparmor.la
libraries/libapparmor/src/libimmunix.la
libraries/libapparmor/src/grammar.c
@ -74,12 +83,18 @@ libraries/libapparmor/swig/perl/Makefile.PL
libraries/libapparmor/swig/perl/Makefile.in
libraries/libapparmor/swig/perl/Makefile.perl
libraries/libapparmor/swig/perl/Makefile.perle
libraries/libapparmor/swig/perl/MYMETA.json
libraries/libapparmor/swig/perl/MYMETA.yml
libraries/libapparmor/swig/perl/blib
libraries/libapparmor/swig/perl/libapparmor_wrap.c
libraries/libapparmor/swig/perl/pm_to_blib
libraries/libapparmor/swig/python/__init__.py
libraries/libapparmor/swig/python/build/
libraries/libapparmor/swig/python/libapparmor_wrap.c
libraries/libapparmor/swig/python/Makefile
libraries/libapparmor/swig/python/Makefile.in
libraries/libapparmor/swig/python/setup.py
libraries/libapparmor/swig/python/test/Makefile
libraries/libapparmor/swig/python/test/Makefile.in
libraries/libapparmor/swig/ruby/Makefile
libraries/libapparmor/swig/ruby/Makefile.in

View file

@ -11,6 +11,7 @@ include ${COMMONDIR}/Make.rules
DIRS=parser \
profiles \
utils \
binutils \
libraries/libapparmor \
changehat/mod_apparmor \
changehat/pam_apparmor \

10
README
View file

@ -27,6 +27,7 @@ Source Layout
AppArmor consists of several different parts:
binutils/ source for basic utilities written in compiled languages
changehat/ source for using changehat with Apache, PAM and Tomcat
common/ common makefile rules
desktop/ empty
@ -71,6 +72,13 @@ $ make install
generate Ruby bindings to libapparmor.]
Binary Utilities:
$ cd binutils
$ make
$ make check
$ make install
Utilities:
$ cd utils
$ make
@ -104,7 +112,7 @@ $ make check # depends on the parser having been built first
$ make install
[Note that for the parser and the utils, if you only with to build/use
[Note that for the parser, binutils, and utils, if you only wish to build/use
some of the locale languages, you can override the default by passing
the LANGS arguments to make; e.g. make all install "LANGS=en_US fr".]

157
binutils/Makefile Normal file
View file

@ -0,0 +1,157 @@
# ----------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------
NAME=aa-binutils
all:
COMMONDIR=../common/
include $(COMMONDIR)/Make.rules
DESTDIR=/
BINDIR=${DESTDIR}/usr/bin
LOCALEDIR=/usr/share/locale
MANPAGES=aa-enabled.8 aa-exec.8
WARNINGS = -Wall
EXTRA_WARNINGS = -Wsign-compare -Wmissing-field-initializers -Wformat-security -Wunused-parameter
CPP_WARNINGS =
ifndef CFLAGS
CFLAGS = -g -O2 -pipe
ifdef DEBUG
CFLAGS += -pg -D DEBUG
endif
ifdef COVERAGE
CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
endif
endif #CFLAGS
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
#INCLUDEDIR = /usr/src/linux/include
INCLUDEDIR =
ifdef INCLUDEDIR
CFLAGS += -I$(INCLUDEDIR)
endif
# Internationalization support. Define a package and a LOCALEDIR
EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
SRCS = aa_enabled.c
HDRS =
TOOLS = aa-enabled aa-exec
AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread
ifdef USE_SYSTEM
# Using the system libapparmor so Makefile dependencies can't be used
LIBAPPARMOR_A =
INCLUDE_APPARMOR =
APPARMOR_H =
LIBAPPARMOR_LDFLAGS =
else
LIBAPPARMOR_SRC = ../libraries/libapparmor/
LOCAL_LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include
LOCAL_LIBAPPARMOR_LDPATH = $(LIBAPPARMOR_SRC)/src/.libs
LIBAPPARMOR_A = $(LOCAL_LIBAPPARMOR_LDPATH)/libapparmor.a
INCLUDE_APPARMOR = -I$(LOCAL_LIBAPPARMOR_INCLUDE)
APPARMOR_H = $(LOCAL_LIBAPPARMOR_INCLUDE)/sys/apparmor.h
LIBAPPARMOR_LDFLAGS = -L$(LOCAL_LIBAPPARMOR_LDPATH)
endif
EXTRA_CFLAGS += $(INCLUDE_APPARMOR)
LDFLAGS += $(LIBAPPARMOR_LDFLAGS)
ifdef V
VERBOSE = 1
endif
ifndef VERBOSE
VERBOSE = 0
endif
ifeq ($(VERBOSE),1)
BUILD_OUTPUT =
Q =
else
BUILD_OUTPUT = > /dev/null 2>&1
Q = @
endif
export Q VERBOSE BUILD_OUTPUT
po/%.pot: %.c
$(MAKE) -C po $(@F) NAME=$* SOURCES=$*.c
# targets arranged this way so that people who don't want full docs can
# pick specific targets they want.
arch: $(TOOLS)
manpages: $(MANPAGES)
docs: manpages
indep: docs
$(Q)$(MAKE) -C po all
all: arch indep
.PHONY: coverage
coverage:
$(MAKE) clean $(TOOLS) COVERAGE=1
ifndef USE_SYSTEM
$(LIBAPPARMOR_A):
@if [ ! -f $@ ]; then \
echo "error: $@ is missing. Pick one of these possible solutions:" 1>&2; \
echo " 1) Build against the in-tree libapparmor by building it first and then trying again. See the top-level README for help." 1>&2; \
echo " 2) Build against the system libapparmor by adding USE_SYSTEM=1 to your make command." 1>&2;\
return 1; \
fi
endif
aa-enabled: aa_enabled.c $(LIBAPPARMOR_A)
$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB)
aa-exec: aa_exec.c $(LIBAPPARMOR_A)
$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB)
.SILENT: check
.PHONY: check
check: check_pod_files tests
.SILENT: tests
tests: $(TOOLS) $(TESTS)
echo "no tests atm"
.PHONY: install
install: install-indep install-arch
.PHONY: install-arch
install-arch: arch
install -m 755 -d ${BINDIR}
install -m 755 ${TOOLS} ${BINDIR}
.PHONY: install-indep
install-indep:
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
$(MAKE) install_manpages DESTDIR=${DESTDIR}
ifndef VERBOSE
.SILENT: clean
endif
.PHONY: clean
clean: pod_clean
rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno
rm -f gmon.out
rm -f $(TOOLS) $(TESTS)
$(MAKE) -s -C po clean

94
binutils/aa-enabled.pod Normal file
View file

@ -0,0 +1,94 @@
# This publication is intellectual property of Canonical Ltd. Its contents
# can be duplicated, either in part or in whole, provided that a copyright
# label is visibly located on each copy.
#
# All information found in this book has been compiled with utmost
# attention to detail. However, this does not guarantee complete accuracy.
# Neither Canonical Ltd, the authors, nor the translators shall be held
# liable for possible errors or the consequences thereof.
#
# Many of the software and hardware descriptions cited in this book
# are registered trademarks. All trade names are subject to copyright
# restrictions and may be registered trade marks. Canonical Ltd
# essentially adheres to the manufacturer's spelling.
#
# Names of products and trademarks appearing in this book (with or without
# specific notation) are likewise subject to trademark and trade protection
# laws and may thus fall under copyright restrictions.
#
=pod
=head1 NAME
aa-enabled - test whether AppArmor is enabled
=head1 SYNOPSIS
B<aa-enabled> [options]
=head1 DESCRIPTION
B<aa-enabled> is used to determine if AppArmor is enabled.
=head1 OPTIONS
B<aa-enabled> accepts the following arguments:
=over 4
=item -h, --help
Display a brief usage guide.
=item -q, --quiet
Do not output anything to stdout. This option is intended to be used by
scripts that simply want to use the exit code to determine if AppArmor is
enabled.
=back
=head1 EXIT STATUS
Upon exiting, B<aa-enabled> will set its exit status to the following values:
=over 4
=item 0:
if AppArmor is enabled.
=item 1:
if AppArmor is not enabled/loaded.
=item 2:
intentionally not used as an B<aa-enabled> exit status.
=item 3:
if the AppArmor control files aren't available under /sys/kernel/security/.
=item 4:
if B<aa-enabled> doesn't have enough privileges to read the apparmor control files.
=item 64:
if any unexpected error or condition is encountered.
=back
=head1 BUGS
If you find any bugs, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), aa_is_enabled(2), and L<http://wiki.apparmor.net>.
=cut

View file

@ -57,10 +57,6 @@ use the current profile name (likely unconfined).
use profiles in NAMESPACE. This will result in confinement transitioning
to using the new profile namespace.
=item -f FILE, --file=FILE
a file or directory containing profiles to load before confining the program.
=item -i, --immediate
transition to PROFILE before doing executing I<E<lt>commandE<gt>>. This

92
binutils/aa_enabled.c Normal file
View file

@ -0,0 +1,92 @@
/*
* Copyright (C) 2015 Canonical Ltd.
*
* 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.
*/
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#define _(s) gettext(s)
#include <sys/apparmor.h>
void print_help(const char *command)
{
printf(_("%s: [options]\n"
" options:\n"
" -q | --quiet Don't print out any messages\n"
" -h | --help Print help\n"),
command);
exit(1);
}
/* Exit statuses and meanings are documented in the aa-enabled.pod file */
static void exit_with_error(int saved_errno, int quiet)
{
int err;
switch(saved_errno) {
case ENOSYS:
if (!quiet)
printf(_("No - not available on this system.\n"));
exit(1);
case ECANCELED:
if (!quiet)
printf(_("No - disabled at boot.\n"));
exit(1);
case ENOENT:
if (!quiet)
printf(_("Maybe - policy interface not available.\n"));
exit(3);
case EPERM:
case EACCES:
if (!quiet)
printf(_("Maybe - insufficient permissions to determine availability.\n"));
exit(4);
}
if (!quiet)
printf(_("Error - %s\n"), strerror(saved_errno));
exit(64);
}
int main(int argc, char **argv)
{
int enabled;
int quiet = 0;
setlocale(LC_MESSAGES, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc > 2) {
printf(_("unknown or incompatible options\n"));
print_help(argv[0]);
} else if (argc == 2) {
if (strcmp(argv[1], "--quiet") == 0 ||
strcmp(argv[1], "-q") == 0) {
quiet = 1;
} else if (strcmp(argv[1], "--help") == 0 ||
strcmp(argv[1], "-h") == 0) {
print_help(argv[0]);
} else {
printf(_("unknown option '%s'\n"), argv[1]);
print_help(argv[0]);
}
}
enabled = aa_is_enabled();
if (!enabled)
exit_with_error(errno, quiet);
if (!quiet)
printf(_("Yes\n"));
exit(0);
}

218
binutils/aa_exec.c Normal file
View file

@ -0,0 +1,218 @@
/*
* 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 <libintl.h>
#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include <unistd.h>
#define _(s) gettext(s)
static const char *opt_profile = NULL;
static const char *opt_namespace = NULL;
static bool opt_debug = false;
static bool opt_immediate = 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"
" -n NAMESPACE, --namespace=NAMESPACE NAMESPACE to confine <prog> in\n"
" -d, --debug show messages with debugging information\n"
" -i, --immediate change profile immediately instead of at exec\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'},
{"namespace", required_argument, 0, 'n'},
{"immediate", no_argument, 0, 'i'},
{"verbose", no_argument, 0, 'v'},
};
while ((opt = getopt_long(argc, argv, "+dhp:n:iv", 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 'n':
opt_namespace = optarg;
break;
case 'i':
opt_immediate = true;
break;
case 'v':
opt_verbose = true;
break;
default:
usage(argv[0], true);
break;
}
}
if (optind >= argc)
usage(argv[0], true);
return argv + optind;
}
static void build_name(char *name, size_t name_len,
const char *namespace, const char *profile)
{
size_t required_len = 1; /* reserve 1 byte for NUL-terminator */
if (namespace)
required_len += 1 + strlen(namespace) + 3; /* :<NAMESPACE>:// */
if (profile)
required_len += strlen(profile);
if (required_len > name_len)
error("name too long (%zu > %zu)", required_len, name_len);
name[0] = '\0';
if (namespace) {
strcat(name, ":");
strcat(name, namespace);
strcat(name, "://");
}
if (profile)
strcat(name, profile);
}
int main(int argc, char **argv)
{
char name[PATH_MAX];
int rc = 0;
argv = parse_args(argc, argv);
if (opt_namespace || opt_profile)
build_name(name, sizeof(name), opt_namespace, opt_profile);
else
goto exec;
if (opt_immediate) {
verbose("aa_change_profile(\"%s\")", name);
rc = aa_change_profile(name);
debug("%d = aa_change_profile(\"%s\")", rc, name);
} else {
verbose("aa_change_onexec(\"%s\")", name);
rc = aa_change_onexec(name);
debug("%d = aa_change_onexec(\"%s\")", rc, name);
}
if (rc) {
if (errno == ENOENT || errno == EACCES) {
error("%s '%s' does not exist\n",
opt_profile ? "profile" : "namespace", name);
} else if (errno == EINVAL) {
error("AppArmor interface not available");
} else {
error("%m");
}
}
exec:
verbose_print_argv(argv);
execvp(argv[0], argv);
error("Failed to execute \"%s\": %m", argv[0]);
}

19
binutils/po/Makefile Normal file
View file

@ -0,0 +1,19 @@
# ----------------------------------------------------------------------
# Copyright (C) 2015 Canonical Ltd.
#
# 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.
# ----------------------------------------------------------------------
all:
# As translations get added, they will automatically be included, unless
# the lang is explicitly added to DISABLED_LANGS; e.g. DISABLED_LANGS=en es
DISABLED_LANGS=
COMMONDIR=../../common
include $(COMMONDIR)/Make-po.rules
XGETTEXT_ARGS+=--language=C --keyword=_ $(shell if [ -f ${NAME}.pot ] ; then echo -n -j ; fi)

View file

@ -0,0 +1,66 @@
# Copyright (C) 2015 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2015.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2015-11-28 10:23-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../aa_enabled.c:26
#, c-format
msgid ""
"%s: [options]\n"
" options:\n"
" -q | --quiet Don't print out any messages\n"
" -h | --help Print help\n"
msgstr ""
#: ../aa_enabled.c:45
#, c-format
msgid "unknown or incompatible options\n"
msgstr ""
#: ../aa_enabled.c:55
#, c-format
msgid "unknown option '%s'\n"
msgstr ""
#: ../aa_enabled.c:64
#, c-format
msgid "Yes\n"
msgstr ""
#: ../aa_enabled.c:71
#, c-format
msgid "No - not available on this system.\n"
msgstr ""
#: ../aa_enabled.c:74
#, c-format
msgid "No - disabled at boot.\n"
msgstr ""
#: ../aa_enabled.c:77
#, c-format
msgid "Maybe - policy interface not available.\n"
msgstr ""
#: ../aa_enabled.c:81
#, c-format
msgid "Maybe - insufficient permissions to determine availability.\n"
msgstr ""
#: ../aa_enabled.c:84
#, c-format
msgid "Error - '%s'\n"
msgstr ""

View file

@ -16,6 +16,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2016-01-21 05:11+0000\n"
"X-Generator: Launchpad (build 17886)\n"
"Language: id\n"
#: ../aa_enabled.c:26
#, c-format

View file

@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (c) 1999-2008 NOVELL (All rights reserved)
# Copyright 2009-2010 Canonical Ltd.
# Copyright 2009-2015 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -21,7 +21,7 @@
# exist
LOCALEDIR=/usr/share/locale
XGETTEXT_ARGS=--copyright-holder="NOVELL, Inc." --msgid-bugs-address=apparmor@lists.ubuntu.com -d ${NAME}
XGETTEXT_ARGS=--copyright-holder="Canonical Ltd" --msgid-bugs-address=apparmor@lists.ubuntu.com -d ${NAME}
# When making the .pot file, it's expected that the parent Makefile will
# pass in the list of sources in the SOURCES variable

View file

@ -82,7 +82,7 @@ pod_clean:
# =====================
# emits defined capabilities in a simple list, e.g. "CAP_NAME CAP_NAME2"
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | sort)
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | LC_ALL=C sort)
.PHONY: list_capabilities
list_capabilities: /usr/include/linux/capability.h

View file

@ -1 +1 @@
2.9.90
2.10.90

Binary file not shown.

View file

@ -24,27 +24,23 @@
aa_query_label - query access permission associated with a label
aa_query_file_path, aa_query_file_path_len - query access permissions of a file path
aa_query_link_path, aa_query_link_path_len - query access permissions of a link path
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_query_label((uint32_t mask, char *query, size_t size,
int *allowed, int *audited);>
B<int aa_query_label(uint32_t mask, char *query, size_t size, int *allowed, int *audited);>
B<int aa_query_file_path((uint32_t mask, const char *label, size_t label_len,
const char *path, int *allowed, int *audited);>
B<int aa_query_file_path(uint32_t mask, const char *label, size_t label_len, const char *path, int *allowed, int *audited);>
B<int aa_query_file_path_len((uint32_t mask, const char *label,
size_t label_len, const char *path, size_t path_len,
int *allowed, int *audited);>
B<int aa_query_file_path_len(uint32_t mask, const char *label, size_t label_len, const char *path, size_t path_len, int *allowed, int *audited);>
B<int aa_query_link_path_len(const char *label, size_t label_len,
const char *target, size_t target_len,
const char *link, size_t link_len,
int *allowed, int *audited);>
B<int aa_query_link_path(const char *label, const char *target, const char *link, int *allowed, int *audited);>
B<int aa_query_link_path(const char *label, const char *target,
const char *link, int *allowed, int *audited);>
B<int aa_query_link_path_len(const char *label, size_t label_len, const char *target, size_t target_len, const char *link, size_t link_len, int *allowed, int *audited);>
Link with B<-lapparmor> when compiling.

View file

@ -26,9 +26,9 @@ INCLUDES = $(all_includes)
# For more information, see:
# http://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
#
AA_LIB_CURRENT = 3
AA_LIB_REVISION = 1
AA_LIB_AGE = 2
AA_LIB_CURRENT = 4
AA_LIB_REVISION = 0
AA_LIB_AGE = 3
SUFFIXES = .pc.in .pc

View file

@ -166,6 +166,11 @@ aa_record_event_type lookup_aa_event(unsigned int type)
%token TOK_SYSLOG_KERNEL
%token TOK_SYSLOG_USER
%destructor { free($$); } TOK_QUOTED_STRING TOK_ID TOK_MODE TOK_DMESG_STAMP
%destructor { free($$); } TOK_AUDIT_DIGITS TOK_DATE_MONTH TOK_DATE TOK_TIME
%destructor { free($$); } TOK_HEXSTRING TOK_TYPE_OTHER TOK_MSG_REST
%destructor { free($$); } TOK_IP_ADDR
%%
log_message: audit_type
@ -201,7 +206,7 @@ other_audit: TOK_TYPE_OTHER audit_msg TOK_MSG_REST
;
dmesg_type: TOK_DMESG_STAMP TOK_AUDIT TOK_COLON key_type audit_id key_list
{ ret_record->version = AA_RECORD_SYNTAX_V2; }
{ ret_record->version = AA_RECORD_SYNTAX_V2; free($1); }
;
syslog_type:

View file

@ -34,19 +34,25 @@ int main(void)
retstr = hex_to_string("2F746D702F646F6573206E6F74206578697374");
MY_TEST(retstr, "basic allocation");
MY_TEST(strcmp(retstr, "/tmp/does not exist") == 0, "basic dehex 1");
free(retstr);
retstr = hex_to_string("61");
MY_TEST(strcmp(retstr, "a") == 0, "basic dehex 2");
free(retstr);
retstr = hex_to_string("");
MY_TEST(strcmp(retstr, "") == 0, "empty string");
free(retstr);
/* ipproto_to_string() tests */
retstr = ipproto_to_string((unsigned) 99999);
MY_TEST(strcmp(retstr, "unknown(99999)") == 0, "invalid protocol test");
free(retstr);
retstr = ipproto_to_string((unsigned) 6);
MY_TEST(strcmp(retstr, "tcp") == 0, "protocol=tcp");
free(retstr);
return rc;
}

View file

@ -15,5 +15,10 @@ test_multi_multi_LDADD = -L../src/.libs -lapparmor
clean-local:
rm -rf tmp.err.* tmp.out.* site.exp site.bak test_multi/out
rm -f libaalogparse.log libaalogparse.sum
check-local:
if ! test -f libaalogparse.log ; then echo '*** libaalogparse.log not found - is dejagnu installed? ***'; exit 1; fi
if grep ERROR libaalogparse.log ; then exit 1 ; fi
EXTRA_DIST = test_multi/*.in test_multi/*.out test_multi/*.err

View file

@ -0,0 +1 @@
type=AVC msg=audit(1449442292.901:961): apparmor="ALLOWED" operation="change_hat" profile="/usr/sbin/httpd{,2}-prefork" pid=8527 comm="httpd-prefork" target="/usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT"

View file

@ -0,0 +1,11 @@
START
File: testcase_changehat_01.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1449442292.901:961
Operation: change_hat
Profile: /usr/sbin/httpd{,2}-prefork
Command: httpd-prefork
Name2: /usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT
PID: 8527
Epoch: 1449442292
Audit subid: 961

View file

@ -0,0 +1 @@
Jul 25 15:02:00 redacted kernel: [ 296.524447] audit: type=1400 audit(1437850920.403:64): apparmor="ALLOWED" operation="open" profile="/usr/sbin/vsftpd" name="/home/bane/foo" pid=1811 comm="vsftpd" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000

View file

@ -0,0 +1,15 @@
START
File: testcase_syslog_read.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1437850920.403:64
Operation: open
Mask: r
Denied Mask: r
fsuid: 1000
ouid: 1000
Profile: /usr/sbin/vsftpd
Name: /home/bane/foo
Command: vsftpd
PID: 1811
Epoch: 1437850920
Audit subid: 64

View file

@ -103,7 +103,7 @@ capabilities(7))
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ]
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' ) ','
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' ) ','
B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
@ -161,7 +161,7 @@ B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' )
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' )
B<SIGNAL PEER> = 'peer' '=' I<AARE>
@ -231,11 +231,13 @@ B<RLIMIT RULE> = 'set' 'rlimit' [I<RLIMIT> 'E<lt>=' I<RLIMIT VALUE> ]
B<RLIMIT> = ( 'cpu' | 'fsize' | 'data' | 'stack' | 'core' | 'rss' | 'nofile' | 'ofile' | 'as' | 'nproc' | 'memlock' | 'locks' | 'sigpending' | 'msgqueue' | 'nice' | 'rtprio' | 'rttime' )
B<RLIMIT VALUE> = ( I<RLIMIT SIZE> | I<RLIMIT NUMBER> | I<RLIMIT NICE> )
B<RLIMIT VALUE> = ( I<RLIMIT SIZE> | I<RLIMIT NUMBER> | I<RLIMIT TIME> | I<RLIMIT NICE> )
B<RLIMIT SIZE> = I<NUMBER> ( 'K' | 'M' | 'G' ) Only applies to RLIMIT of 'fsize', 'data', 'stack', 'core', 'rss', 'as', 'memlock', 'msgqueue'.
B<RLIMIT NUMBER> = number from 0 to max rlimit value. Only applies ot RLIMIT of 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio', 'cpu'
B<RLIMIT NUMBER> = number from 0 to max rlimit value. Only applies ot RLIMIT of 'ofile', 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio'
B<RLIMIT TIME> = I<NUMBER> ( 'us' | 'microsecond' | 'microseconds' | 'ms' | 'millisecond' | 'milliseconds' | 's' | 'sec' | 'second' | 'seconds' | 'min' | 'minute' | 'minutes' | 'h' | 'hour' | 'hours' | 'd' | 'day' | 'days' | 'week' | 'weeks' ) Only applies to RLIMIT of 'cpu', 'rttime'. RLIMIT 'cpu' only allows units >= 'seconds'.
B<RLIMIT NICE> = a number between -20 and 19. Only applies to RLIMIT of 'nice'
@ -962,6 +964,9 @@ Example AppArmor signal rules:
# Allow us to signal ourselves using the built-in @{profile_name} variable
signal peer=@{profile_name},
# Allow two real-time signals
signal set=(rtmin+0 rtmin+32),
=head2 DBus rules
AppArmor supports DBus mediation. The mediation is performed in conjunction
@ -1227,8 +1232,10 @@ provided AppArmor policy:
@{HOMEDIRS}
@{multiarch}
@{pid}
@{pids}
@{PROC}
@{securityfs}
@{apparmorfs}
@{sys}
@{tid}
@{XDG_DESKTOP_DIR}

View file

@ -278,9 +278,32 @@ the matching stats flag.
Use --help=dump to see a full list of which dump flags are supported
=item -j n, --jobs=n
Set the number of jobs used to compile the specified policy. Where n can
be
# - a specific number of jobs
auto - the # of cpus in the in the system
x# - # * number of cpus
Eg.
-j8 OR --jobs=8 allows for 8 parallel jobs
-jauto OR --jobs=auto sets the jobs to the # of cpus
-jx4 OR --jobs=x4 sets the jobs to # of cpus * 4
-jx1 is equivalent to -jauto
The default value is the number of cpus in the system.
=item --max-jobs n
Set a hard cap on the value that can be specified by the --jobs flag.
It takes the same set of options available to the --jobs option, and
defaults to 8*cpus
=item -O n, --optimize=n
Set the optimization flags used by policy compilation. A sinlge optimization
Set the optimization flags used by policy compilation. A single optimization
flag can be toggled per -O option, but the optimize flag can be passed
multiple times. Turning off some phases of the optimization can make
it so that policy can't complete compilation due to size constraints

View file

@ -96,13 +96,13 @@ ostream &operator<<(ostream &os, Node &node);
/* An abstract node in the syntax tree. */
class Node {
public:
Node(): nullable(false) { child[0] = child[1] = 0; }
Node(Node *left): nullable(false)
Node(): nullable(false), label(0) { child[0] = child[1] = 0; }
Node(Node *left): nullable(false), label(0)
{
child[0] = left;
child[1] = 0;
}
Node(Node *left, Node *right): nullable(false)
Node(Node *left, Node *right): nullable(false), label(0)
{
child[0] = left;
child[1] = right;

View file

@ -103,7 +103,7 @@
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_RBIND | \
MS_UNBINDABLE | MS_RUNBINDABLE | MS_PRIVATE | MS_RPRIVATE | \
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED)
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT))
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT & ~MS_BIND & ~MS_RBIND))
#define MNT_SRC_OPT 1
#define MNT_DST_OPT 2

View file

@ -402,6 +402,9 @@ extern void free_cod_entries(struct cod_entry *list);
extern void __debug_capabilities(uint64_t capset, const char *name);
void debug_cod_entries(struct cod_entry *list);
#define SECONDS_P_MS (1000LL * 1000LL)
long long convert_time_units(long long value, long long base, const char *units);
/* parser_symtab.c */
struct set_value {

View file

@ -57,7 +57,7 @@
* numbers where supported.
*/
uint32_t policy_version = 2;
uint32_t parser_abi_version = 1;
uint32_t parser_abi_version = 2;
uint32_t kernel_abi_version = 5;
int force_complain = 0;

View file

@ -447,7 +447,7 @@ LT_EQUAL <=
}
<RLIMIT_MODE>{
-?{NUMBER}[[:alpha:]]* {
-?{NUMBER} {
yylval.var_val = strdup(yytext);
RETURN_TOKEN(TOK_VALUE);
}
@ -519,7 +519,14 @@ LT_EQUAL <=
#include/.*\r?\n {
/* Don't use PUSH() macro here as we don't want #include echoed out.
* It needs to be handled specially
*/
*/
yy_push_state(INCLUDE);
}
include/{WS} {
/* Don't use PUSH() macro here as we don't want #include echoed out.
* It needs to be handled specially
*/
yy_push_state(INCLUDE);
}

View file

@ -36,8 +36,11 @@
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/apparmor.h>
#include "lib.h"
#include "features.h"
#include "parser.h"
@ -76,6 +79,18 @@ int abort_on_error = 0; /* stop processing profiles if error */
int skip_bad_cache_rebuild = 0;
int mru_skip_cache = 1;
int debug_cache = 0;
/* for jobs_max and jobs
* LONG_MAX : no limit
* 0 : auto = detect system processing cores
* n : use that number of processes/threads to compile policy
*/
#define JOBS_AUTO 0
long jobs_max = -8; /* 8 * cpus */
long jobs = JOBS_AUTO; /* default: number of processor cores */
long njobs = 0;
bool debug_jobs = false;
struct timespec cache_tstamp, mru_policy_tstamp;
static char *apparmorfs = NULL;
@ -84,7 +99,7 @@ static char *cacheloc = NULL;
static aa_features *features = NULL;
/* Make sure to update BOTH the short and long_options */
static const char *short_options = "adf:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:";
static const char *short_options = "ad::f:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:j:";
struct option long_options[] = {
{"add", 0, 0, 'a'},
{"binary", 0, 0, 'B'},
@ -116,7 +131,7 @@ struct option long_options[] = {
{"purge-cache", 0, 0, 130}, /* no short option */
{"create-cache-dir", 0, 0, 131}, /* no short option */
{"cache-loc", 1, 0, 'L'},
{"debug", 0, 0, 'd'},
{"debug", 2, 0, 'd'},
{"dump", 1, 0, 'D'},
{"Dump", 1, 0, 'D'},
{"optimize", 1, 0, 'O'},
@ -126,6 +141,8 @@ struct option long_options[] = {
{"skip-bad-cache-rebuild", 0, 0, 133}, /* no short option */
{"warn", 1, 0, 134}, /* no short option */
{"debug-cache", 0, 0, 135}, /* no short option */
{"jobs", 1, 0, 'j'},
{"max-jobs", 1, 0, 136}, /* no short option */
{NULL, 0, 0, 0},
};
@ -170,11 +187,13 @@ static void display_usage(const char *command)
"-v, --verbose Show profile names as they load\n"
"-Q, --skip-kernel-load Do everything except loading into kernel\n"
"-V, --version Display version info and exit\n"
"-d, --debug Debug apparmor definitions\n"
"-d [n], --debug Debug apparmor definitions OR [n]\n"
"-p, --preprocess Dump preprocessed profile\n"
"-D [n], --dump Dump internal info for debugging\n"
"-O [n], --Optimize Control dfa optimizations\n"
"-h [cmd], --help[=cmd] Display this text or info about cmd\n"
"-j n, --jobs n Set the number of compile threads\n"
"--max-jobs n Hard cap on --jobs. Default 8*cpus\n"
"--abort-on-error Abort processing of profiles on first error\n"
"--skip-bad-cache-rebuild Do not try rebuilding the cache if it is rejected by the kernel\n"
"--warn n Enable warnings (see --help=warn)\n"
@ -268,6 +287,32 @@ static int getopt_long_file(FILE *f, const struct option *longopts,
return 0;
}
static long process_jobs_arg(const char *arg, const char *val) {
char *end;
long n;
if (!val || strcmp(val, "auto") == 0)
n = JOBS_AUTO;
else if (strcmp(val, "max") == 0)
n = LONG_MAX;
else {
bool multiple = false;
if (*val == 'x') {
multiple = true;
val++;
}
n = strtol(val, &end, 0);
if (!(*val && val != end && *end == '\0')) {
PERROR("%s: Invalid option %s=%s%s\n", progname, arg, multiple ? "x" : "", val);
exit(1);
}
if (multiple)
n = -n;
}
return n;
}
/* process a single argment from getopt_long
* Returns: 1 if an action arg, else 0
*/
@ -286,8 +331,17 @@ static int process_arg(int c, char *optarg)
option = OPTION_ADD;
break;
case 'd':
debug++;
skip_read_cache = 1;
if (!optarg) {
debug++;
skip_read_cache = 1;
} else if (strcmp(optarg, "jobs") == 0 ||
strcmp(optarg, "j") == 0) {
debug_jobs = true;
} else {
PERROR("%s: Invalid --debug option '%s'\n",
progname, optarg);
exit(1);
}
break;
case 'h':
if (!optarg) {
@ -470,6 +524,12 @@ static int process_arg(int c, char *optarg)
case 135:
debug_cache = 1;
break;
case 'j':
jobs = process_jobs_arg("-j", optarg);
break;
case 136:
jobs_max = process_jobs_arg("max-jobs", optarg);
break;
default:
display_usage(progname);
exit(1);
@ -803,6 +863,118 @@ out:
return retval;
}
/* Do not call directly, this is a helper for work_sync, which can handle
* single worker cases and cases were the work queue is optimized away
*
* call only if there are work children to wait on
*/
#define work_sync_one(RESULT) \
do { \
int status; \
wait(&status); \
if (WIFEXITED(status)) \
RESULT(WEXITSTATUS(status)); \
else \
RESULT(ECHILD); \
/* TODO: do we need to handle traced */ \
njobs--; \
if (debug_jobs) \
fprintf(stderr, " JOBS SYNC ONE: result %d, jobs left %ld\n", status, njobs); \
} while (0)
#define work_sync(RESULT) \
do { \
if (debug_jobs) \
fprintf(stderr, "JOBS SYNC: jobs left %ld\n", njobs); \
while (njobs) \
work_sync_one(RESULT); \
} while (0)
#define work_spawn(WORK, RESULT) \
do { \
/* what to do to avoid fork() overhead when single threaded \
if (jobs == 1) { \
// no parallel work so avoid fork() overhead \
RESULT(WORK); \
break; \
}*/ \
if (njobs == jobs) { \
/* wait for a child */ \
if (debug_jobs) \
fprintf(stderr, " JOBS SPAWN: waiting (jobs %ld == max %ld) ...\n", njobs, jobs); \
work_sync_one(RESULT); \
} \
\
pid_t child = fork(); \
if (child == 0) { \
/* child - exit work unit with returned value */ \
exit(WORK); \
} else if (child > 0) { \
/* parent */ \
njobs++; \
if (debug_jobs) \
fprintf(stderr, " JOBS SPAWN: created %ld ...\n", njobs); \
} else { \
/* error */ \
if (debug_jobs) \
fprintf(stderr, " JOBS SPAWN: failed error: %d) ...\n", errno); \
RESULT(errno); \
} \
} while (0)
/* sadly C forces us to do this with exit, long_jump or returning error
* from work_spawn and work_sync. We could throw a C++ exception, is it
* worth doing it to avoid the exit here.
*
* atm not all resources maybe cleanedup at exit
*/
int last_error = 0;
void handle_work_result(int retval)
{
if (retval) {
last_error = retval;
if (abort_on_error) {
/* already in abort mode we don't need subsequent
* syncs to do this too
*/
abort_on_error = 0;
work_sync(handle_work_result);
exit(last_error);
}
}
}
static long compute_jobs(long n, long j)
{
if (j == JOBS_AUTO)
j = n;
else if (j < 0)
j = n * j * -1;
return j;
}
static void setup_parallel_compile(void)
{
/* jobs and paralell_max set by default, config or args */
long n = sysconf(_SC_NPROCESSORS_ONLN);
if (n == -1)
/* unable to determine number of processors, default to 1 */
n = 1;
jobs = compute_jobs(n, jobs);
jobs_max = compute_jobs(n, jobs_max);
if (jobs > jobs_max) {
pwarn("%s: Warning capping number of jobs to %ld * # of cpus == '%ld'",
progname, jobs_max, jobs);
jobs = jobs_max;
}
njobs = 0;
if (debug_jobs)
fprintf(stderr, "jobs: %ld\n", jobs);
}
struct dir_cb_data {
aa_kernel_interface *kernel_interface;
const char *dirname; /* name of the parent dir */
@ -820,8 +992,9 @@ static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st,
autofree char *path = NULL;
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
PERROR(_("Out of memory"));
rc = process_profile(option, cb_data->kernel_interface, path,
cb_data->cachedir);
work_spawn(process_profile(option, cb_data->kernel_interface,
path, cb_data->cachedir),
handle_work_result);
}
return rc;
}
@ -837,7 +1010,9 @@ static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st,
autofree char *path = NULL;
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
PERROR(_("Out of memory"));
rc = process_binary(option, cb_data->kernel_interface, path);
work_spawn(process_binary(option, cb_data->kernel_interface,
path),
handle_work_result);
}
return rc;
}
@ -860,8 +1035,8 @@ static void setup_flags(void)
int main(int argc, char *argv[])
{
aa_kernel_interface *kernel_interface = NULL;
aa_policy_cache *policy_cache;
int retval, last_error;
aa_policy_cache *policy_cache = NULL;
int retval;
int i;
int optind;
@ -873,6 +1048,8 @@ int main(int argc, char *argv[])
process_config_file("/etc/apparmor/parser.conf");
optind = process_args(argc, argv);
setup_parallel_compile();
setlocale(LC_MESSAGES, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
@ -964,8 +1141,10 @@ int main(int argc, char *argv[])
void *data);
struct dir_cb_data cb_data;
memset(&cb_data, 0, sizeof(struct dir_cb_data));
cb_data.dirname = profilename;
cb_data.cachedir = cacheloc;
cb_data.kernel_interface = kernel_interface;
cb = binary_input ? binary_dir_cb : profile_dir_cb;
if ((retval = dirat_for_each(AT_FDCWD, profilename,
&cb_data, cb))) {
@ -973,25 +1152,23 @@ int main(int argc, char *argv[])
profilename);
}
} else if (binary_input) {
retval = process_binary(option, kernel_interface,
profilename);
work_spawn(process_binary(option, kernel_interface,
profilename),
handle_work_result);
} else {
retval = process_profile(option, kernel_interface,
profilename, cacheloc);
work_spawn(process_profile(option, kernel_interface,
profilename, cacheloc),
handle_work_result);
}
if (profilename) free(profilename);
profilename = NULL;
if (retval) {
last_error = retval;
if (abort_on_error)
break;
}
}
work_sync(handle_work_result);
if (ofile)
fclose(ofile);
aa_policy_cache_unref(policy_cache);
return last_error;
}

View file

@ -724,7 +724,7 @@ static const char *capnames[] = {
"audit_write",
"audit_control",
"setfcap",
"mac_override"
"mac_override",
"syslog",
};
@ -867,6 +867,54 @@ void print_cond_entry(struct cond_entry *ent)
}
}
struct time_units {
const char *str;
long long value;
};
static struct time_units time_units[] = {
{ "us", 1LL },
{ "microsecond", 1LL },
{ "microseconds", 1LL },
{ "ms", 1000LL },
{ "millisecond", 1000LL },
{ "milliseconds", 1000LL },
{ "s", 1000LL * 1000LL },
{ "sec", SECONDS_P_MS },
{ "second", SECONDS_P_MS },
{ "seconds", SECONDS_P_MS },
{ "min" , 60LL * SECONDS_P_MS },
{ "minute", 60LL * SECONDS_P_MS },
{ "minutes", 60LL * SECONDS_P_MS },
{ "h", 60LL * 60LL * SECONDS_P_MS },
{ "hour", 60LL * 60LL * SECONDS_P_MS },
{ "hours", 60LL * 60LL * SECONDS_P_MS },
{ "d", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "day", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "days", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "week", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "weeks", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ NULL, 0 }
};
long long convert_time_units(long long value, long long base, const char *units)
{
struct time_units *ent;
if (!units)
/* default to base if no units */
return value;
for (ent = time_units; ent->str; ent++) {
if (strcmp(ent->str, units) == 0) {
if (value * ent->value < base)
return -1LL;
return value * ent->value / base;
}
}
return -2LL;
}
#ifdef UNIT_TEST
#include "unit_test.h"
@ -1085,6 +1133,50 @@ int test_processquoted(void)
return rc;
}
#define TIME_TEST(V, B, U, R) \
MY_TEST(convert_time_units((V), (B), U) == (R), \
"convert " #V " with base of " #B ", " #U " units")
int test_convert_time_units()
{
int rc = 0;
TIME_TEST(1LL, 1LL, NULL, 1LL);
TIME_TEST(12345LL, 1LL, NULL, 12345LL);
TIME_TEST(10LL, 10LL, NULL, 10LL);
TIME_TEST(123450LL, 10LL, NULL, 123450LL);
TIME_TEST(12345LL, 1LL, "us", 12345LL);
TIME_TEST(12345LL, 1LL, "microsecond", 12345LL);
TIME_TEST(12345LL, 1LL, "microseconds", 12345LL);
TIME_TEST(12345LL, 1LL, "ms", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "millisecond", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "milliseconds", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "s", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "sec", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "second", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "seconds", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "min", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minute", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minutes", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "h", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hour", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hours", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "d", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "day", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "days", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "week", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
TIME_TEST(12345LL, 1LL, "weeks", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
return rc;
}
int main(void)
{
int rc = 0;
@ -1102,6 +1194,10 @@ int main(void)
if (retval != 0)
rc = retval;
retval = test_convert_time_units();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */

View file

@ -275,12 +275,51 @@ static int process_variables_in_rules(Profile &prof)
return 0;
}
static int process_variables_in_name(Profile &prof)
{
/* this needs to be done before alias expansion, ie. altnames are
* setup
*/
int error = expand_entry_variables(&prof.name);
if (!error && prof.attachment)
error = expand_entry_variables(&prof.attachment);
return error;
}
static std::string escape_re(std::string str)
{
for (size_t i = 0; i < str.length(); i++) {
if (str[i] == '\\') {
/* skip \ and follow char. Skipping \ and first
* char is enough for multichar escape sequence
*/
i++;
continue;
}
if (strchr("{}[]*?", str[i]) != NULL) {
str.insert(i++, "\\");
}
}
return str;
}
int process_profile_variables(Profile *prof)
{
int error = 0, rc;
error = new_set_var(PROFILE_NAME_VARIABLE, prof->get_name(true).c_str());
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
* have the correct name
*/
error = process_variables_in_name(*prof);
if (!error) {
/* escape profile name elements that could be interpreted
* as regular expressions.
*/
error = new_set_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
}
if (!error)
error = process_variables_in_entries(prof->entries);

View file

@ -78,7 +78,6 @@ mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src,
int mode);
mnt_rule *do_pivot_rule(struct cond_entry *old, char *root,
char *transition);
void add_local_entry(Profile *prof);
%}
@ -252,6 +251,7 @@ void add_local_entry(Profile *prof);
%type <val_list> valuelist
%type <boolean> expr
%type <id> id_or_var
%type <id> opt_id_or_var
%type <boolean> opt_subset_flag
%type <boolean> opt_audit_flag
%type <boolean> opt_owner_flag
@ -307,7 +307,10 @@ opt_ns: { /* nothing */ $$ = NULL; }
opt_id: { /* nothing */ $$ = NULL; }
| TOK_ID { $$ = $1; }
profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
opt_id_or_var: { /* nothing */ $$ = NULL; }
| id_or_var { $$ = $1; }
profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
{
Profile *prof = $5;
@ -315,13 +318,17 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
yyerror(_("Memory allocation error."));
}
/* Honor the --namespace-string command line option */
if (profile_ns) {
prof->ns = strdup(profile_ns);
if (!prof->ns)
yyerror(_("Memory allocation error."));
}
prof->name = $1;
prof->attachment = $2;
if ($2 && $2[0] != '/')
/* we don't support variables as part of the profile
* name or attachment atm
*/
yyerror(_("Profile attachment must begin with a '/'."));
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
yyerror(_("Profile attachment must begin with a '/' or variable."));
prof->flags = $3;
if (force_complain && kernel_abi_version == 5)
/* newer abis encode force complain as part of the
@ -351,12 +358,17 @@ profile: opt_profile_flag opt_ns profile_base
if ($3->name[0] != '/' && !($1 || $2))
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
if ($2 && profile_ns) {
pwarn("%s: -n %s overriding policy specified namespace :%s:\n", progname, profile_ns, $2);
if (prof->ns) {
/**
* Print warning if the profile specified a namespace
* different than the one specified with the
* --namespace-string command line option
*/
if ($2 && strcmp(prof->ns, $2)) {
pwarn("%s: -n %s overriding policy specified namespace :%s:\n",
progname, prof->ns, $2);
}
free($2);
prof->ns = strdup(profile_ns);
if (!prof->ns)
yyerror(_("Memory allocation error."));
} else
prof->ns = $2;
if ($1 == 2)
@ -853,7 +865,7 @@ rules: rules cond_rule
$$ = merge_policy($1, $2);
}
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
{
rlim_t value = RLIM_INFINITY;
long long tmp;
@ -866,11 +878,6 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
if (strcmp($6, "infinity") == 0) {
value = RLIM_INFINITY;
} else {
const char *seconds = "seconds";
const char *milliseconds = "ms";
const char *minutes = "minutes";
const char *hours = "hours";
const char *days = "days";
const char *kb = "KB";
const char *mb = "MB";
const char *gb = "GB";
@ -880,34 +887,25 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
case RLIMIT_CPU:
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (*end == '\0' ||
strstr(seconds, end) == seconds) {
value = tmp;
} else if (strstr(minutes, end) == minutes) {
value = tmp * 60;
} else if (strstr(hours, end) == hours) {
value = tmp * 60 * 60;
} else if (strstr(days, end) == days) {
value = tmp * 60 * 60 * 24;
} else {
tmp = convert_time_units(tmp, SECONDS_P_MS, $7);
if (tmp == -1LL)
yyerror("RLIMIT '%s %s' < minimum value of 1s\n", $4, $6);
else if (tmp < 0LL)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
if (!$7)
pwarn(_("RLIMIT 'cpu' no units specified using default units of seconds\n"));
value = tmp;
break;
case RLIMIT_RTTIME:
/* RTTIME is measured in microseconds */
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (*end == '\0') {
value = tmp;
} else if (strstr(milliseconds, end) == milliseconds) {
value = tmp * 1000;
} else if (strstr(seconds, end) == seconds) {
value = tmp * 1000 * 1000;
} else if (strstr(minutes, end) == minutes) {
value = tmp * 1000 * 1000 * 60;
} else {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
tmp = convert_time_units(tmp, 1LL, $7);
if (tmp < 0LL)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!$7)
pwarn(_("RLIMIT 'rttime' no units specified using default units of microseconds\n"));
value = tmp;
break;
case RLIMIT_NOFILE:
case RLIMIT_NPROC:
@ -915,15 +913,15 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
case RLIMIT_SIGPENDING:
#ifdef RLIMIT_RTPRIO
case RLIMIT_RTPRIO:
if (!end || $6 == end || *end != '\0' || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (!end || $6 == end || $7 || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
value = tmp;
break;
#endif
#ifdef RLIMIT_NICE
case RLIMIT_NICE:
if (!end || $6 == end || *end != '\0')
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (!end || $6 == end || $7)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (tmp < -20 || tmp > 19)
yyerror("RLIMIT '%s' out of range (-20 .. 19) %d\n", $4, tmp);
value = tmp + 20;
@ -938,15 +936,17 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
case RLIMIT_MEMLOCK:
case RLIMIT_MSGQUEUE:
if ($6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (strstr(kb, end) == kb) {
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!$7) {
; /* use default of bytes */
} else if (strstr(kb, $7) == kb) {
tmp *= 1024;
} else if (strstr(mb, end) == mb) {
} else if (strstr(mb, $7) == mb) {
tmp *= 1024*1024;
} else if (strstr(gb, end) == gb) {
} else if (strstr(gb, $7) == gb) {
tmp *= 1024*1024*1024;
} else if (*end != '\0') {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
} else {
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7);
}
value = tmp;
break;

View file

@ -18,6 +18,7 @@
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -25,8 +26,6 @@
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <utime.h>
#include "lib.h"
#include "parser.h"
@ -166,12 +165,21 @@ void install_cache(const char *cachetmpname, const char *cachename)
/* Only install the generate cache file if it parsed correctly
and did not have write/close errors */
if (cachetmpname) {
struct timeval t;
struct timespec times[2];
/* set the mtime of the cache file to the most newest mtime
* of policy files used to generate it
*/
TIMESPEC_TO_TIMEVAL(&t, &mru_policy_tstamp);
utimes(cachetmpname, &t);
times[0].tv_sec = 0;
times[0].tv_nsec = UTIME_OMIT;
times[1] = mru_policy_tstamp;
if (utimensat(AT_FDCWD, cachetmpname, times, 0) < 0) {
PERROR("%s: Failed to set the mtime of cache file '%s': %m\n",
progname, cachename);
unlink(cachetmpname);
return;
}
if (rename(cachetmpname, cachename) < 0) {
pwarn("Warning failed to write cache: %s\n", cachename);
unlink(cachetmpname);

View file

@ -94,12 +94,13 @@ aa_log_skipped_msg() {
echo -e "$rc_skipped"
}
_set_status() {
return $1
}
aa_log_end_msg() {
v="-v"
if [ "$1" != '0' ]; then
rc="-v$1"
fi
rc_status $v
_set_status $1
rc_status -v
}
usage() {

View file

@ -1,8 +1,9 @@
#!/usr/bin/env python3
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Canonical Ltd.
# Author: Steve Beattie <steve@nxnw.org>
# Copyright (C) 2013-2015 Canonical Ltd.
# Authors: Steve Beattie <steve@nxnw.org>
# Tyler Hicks <tyhicks@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -12,11 +13,11 @@
# TODO
# - check cache not used if parser in $PATH is newer
# - check cache used/not used if includes are newer/older
# - check cache used for force-complain, disable symlink, etc.
from argparse import ArgumentParser
import os
import platform
import shutil
import time
import tempfile
@ -24,19 +25,24 @@ import unittest
import testlib
ABSTRACTION_CONTENTS = '''
# Simple example abstraction
capability setuid,
'''
ABSTRACTION = 'suid-abstraction'
PROFILE_CONTENTS = '''
# Simple example profile for caching tests
/bin/pingy {
#include <%s>
capability net_raw,
capability setuid,
network inet raw,
/bin/ping mixr,
/etc/modules.conf r,
}
'''
''' % (ABSTRACTION)
PROFILE = 'sbin.pingy'
config = None
@ -51,11 +57,6 @@ class AAParserCachingCommon(testlib.AATestTemplate):
# REPORT ALL THE OUTPUT
self.maxDiff = None
# skip all the things if apparmor securityfs isn't mounted
if not os.path.exists("/sys/kernel/security/apparmor"):
raise unittest.SkipTest("WARNING: /sys/kernel/security/apparmor does not exist. "
"Skipping tests")
self.tmp_dir = tempfile.mkdtemp(prefix='aa-caching-')
os.chmod(self.tmp_dir, 0o755)
@ -63,7 +64,11 @@ class AAParserCachingCommon(testlib.AATestTemplate):
self.cache_dir = os.path.join(self.tmp_dir, 'cache')
os.mkdir(self.cache_dir)
# write our sample profile out
# default path of the output cache file
self.cache_file = os.path.join(self.cache_dir, PROFILE)
# write our sample abstraction and profile out
self.abstraction = testlib.write_file(self.tmp_dir, ABSTRACTION, ABSTRACTION_CONTENTS)
self.profile = testlib.write_file(self.tmp_dir, PROFILE, PROFILE_CONTENTS)
if config.debug:
@ -72,6 +77,9 @@ class AAParserCachingCommon(testlib.AATestTemplate):
self.cmd_prefix = [config.parser, '--base', self.tmp_dir, '--skip-kernel-load']
if not self.is_apparmorfs_mounted():
self.cmd_prefix += ['-M', './features_files/features.all']
def tearDown(self):
'''teardown for each test'''
@ -89,7 +97,17 @@ class AAParserCachingCommon(testlib.AATestTemplate):
self.assertFalse(os.path.exists(path),
'test created file %s, when it was not expected to do so' % path)
def is_apparmorfs_mounted(self):
return os.path.exists("/sys/kernel/security/apparmor")
def require_apparmorfs(self):
# skip the test if apparmor securityfs isn't mounted
if not self.is_apparmorfs_mounted():
raise unittest.SkipTest("WARNING: /sys/kernel/security/apparmor does not exist. Skipping test.")
def compare_features_file(self, features_path, expected=True):
# tests that need this function should call require_apparmorfs() early
# compare features contents
expected_output = testlib.read_features_dir('/sys/kernel/security/apparmor/features')
with open(features_path) as f:
@ -143,6 +161,8 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
def test_features_match_when_caching(self):
'''test features file is written when caching'''
self.require_apparmorfs()
cmd = list(self.cmd_prefix)
cmd.extend(['-q', '--write-cache', '-r', self.profile])
self.run_cmd_check(cmd)
@ -203,21 +223,34 @@ class AAParserCachingTests(AAParserCachingCommon):
def setUp(self):
super(AAParserCachingTests, self).setUp()
# need separation of length timeout between generating profile
# and generating cache entry, as the parser distinguishes
# between ctime, not mtime.
if not 'timeout' in dir(config):
r = testlib.filesystem_time_resolution()
config.timeout = r[1]
time.sleep(config.timeout)
r = testlib.filesystem_time_resolution()
self.mtime_res = r[1]
def _generate_cache_file(self):
cmd = list(self.cmd_prefix)
cmd.extend(['-q', '--write-cache', '-r', self.profile])
self.run_cmd_check(cmd)
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE))
self.assert_path_exists(self.cache_file)
def _assertTimeStampEquals(self, time1, time2):
'''Compare two timestamps to ensure equality'''
# python 3.2 and earlier don't support writing timestamps with
# nanosecond resolution, only microsecond. When comparing
# timestamps in such an environment, loosen the equality bounds
# to compensate
# Reference: https://bugs.python.org/issue12904
(major, minor, _) = platform.python_version_tuple()
if (int(major) < 3) or ((int(major) == 3) and (int(minor) <= 2)):
self.assertAlmostEquals(time1, time2, places=5)
else:
self.assertEquals(time1, time2)
def _set_mtime(self, path, mtime):
atime = os.stat(path).st_atime
os.utime(path, (atime, mtime))
self._assertTimeStampEquals(os.stat(path).st_mtime, mtime)
def test_cache_loaded_when_exists(self):
'''test cache is loaded when it exists, is newer than profile, and features match'''
@ -260,6 +293,8 @@ class AAParserCachingTests(AAParserCachingCommon):
def test_cache_writing_does_not_overwrite_features_when_features_differ(self):
'''test cache writing does not overwrite the features files when it differs and --skip-bad-cache is given'''
self.require_apparmorfs()
features_file = testlib.write_file(self.cache_dir, '.features', 'monkey\n')
cmd = list(self.cmd_prefix)
@ -277,11 +312,13 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '--write-cache', '--skip-bad-cache', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE), expected=False)
self.assert_path_exists(self.cache_file, expected=False)
def test_cache_writing_updates_features(self):
'''test cache writing updates features'''
self.require_apparmorfs()
features_file = testlib.write_file(self.cache_dir, '.features', 'monkey\n')
cmd = list(self.cmd_prefix)
@ -294,18 +331,18 @@ class AAParserCachingTests(AAParserCachingCommon):
'''test cache writing updates cache file'''
cache_file = testlib.write_file(self.cache_dir, PROFILE, 'monkey\n')
orig_size = os.stat(cache_file).st_size
orig_stat = os.stat(cache_file)
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '--write-cache', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
self.assert_path_exists(cache_file)
with open(cache_file, 'rb') as f:
new_size = os.fstat(f.fileno()).st_size
stat = os.stat(cache_file)
# We check sizes here rather than whether the string monkey is
# in cache_contents because of the difficulty coercing cache
# file bytes into strings in python3
self.assertNotEquals(orig_size, new_size, 'Expected cache file to be updated, size is not changed.')
self.assertNotEquals(orig_stat.st_size, stat.st_size, 'Expected cache file to be updated, size is not changed.')
self.assertEquals(os.stat(self.profile).st_mtime, stat.st_mtime)
def test_cache_writing_clears_all_files(self):
'''test cache writing clears all cache files'''
@ -317,27 +354,110 @@ class AAParserCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
self.assert_path_exists(check_file, expected=False)
def test_profile_mtime_preserved(self):
'''test profile mtime is preserved when it is newest'''
expected = 1
self._set_mtime(self.abstraction, 0)
self._set_mtime(self.profile, expected)
self._generate_cache_file()
self.assertEquals(expected, os.stat(self.cache_file).st_mtime)
def test_abstraction_mtime_preserved(self):
'''test abstraction mtime is preserved when it is newest'''
expected = 1000
self._set_mtime(self.profile, 0)
self._set_mtime(self.abstraction, expected)
self._generate_cache_file()
self.assertEquals(expected, os.stat(self.cache_file).st_mtime)
def test_equal_mtimes_preserved(self):
'''test equal profile and abstraction mtimes are preserved'''
expected = 10000 + self.mtime_res
self._set_mtime(self.profile, expected)
self._set_mtime(self.abstraction, expected)
self._generate_cache_file()
self.assertEquals(expected, os.stat(self.cache_file).st_mtime)
def test_profile_newer_skips_cache(self):
'''test cache is skipped if profile is newer'''
self._generate_cache_file()
time.sleep(config.timeout)
testlib.touch(self.profile)
profile_mtime = os.stat(self.cache_file).st_mtime + self.mtime_res
self._set_mtime(self.profile, profile_mtime)
orig_stat = os.stat(self.cache_file)
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
stat = os.stat(self.cache_file)
self.assertEquals(orig_stat.st_size, stat.st_size)
self.assertEquals(orig_stat.st_ino, stat.st_ino)
self.assertEquals(orig_stat.st_mtime, stat.st_mtime)
def test_abstraction_newer_skips_cache(self):
'''test cache is skipped if abstraction is newer'''
self._generate_cache_file()
abstraction_mtime = os.stat(self.cache_file).st_mtime + self.mtime_res
self._set_mtime(self.abstraction, abstraction_mtime)
orig_stat = os.stat(self.cache_file)
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
stat = os.stat(self.cache_file)
self.assertEquals(orig_stat.st_size, stat.st_size)
self.assertEquals(orig_stat.st_ino, stat.st_ino)
self.assertEquals(orig_stat.st_mtime, stat.st_mtime)
def test_profile_newer_rewrites_cache(self):
'''test cache is rewritten if profile is newer'''
self._generate_cache_file()
profile_mtime = os.stat(self.cache_file).st_mtime + self.mtime_res
self._set_mtime(self.profile, profile_mtime)
orig_stat = os.stat(self.cache_file)
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '-r', '-W', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
stat = os.stat(self.cache_file)
self.assertNotEquals(orig_stat.st_ino, stat.st_ino)
self._assertTimeStampEquals(profile_mtime, stat.st_mtime)
def test_abstraction_newer_rewrites_cache(self):
'''test cache is rewritten if abstraction is newer'''
self._generate_cache_file()
abstraction_mtime = os.stat(self.cache_file).st_mtime + self.mtime_res
self._set_mtime(self.abstraction, abstraction_mtime)
orig_stat = os.stat(self.cache_file)
cmd = list(self.cmd_prefix)
cmd.extend(['-v', '-r', '-W', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
stat = os.stat(self.cache_file)
self.assertNotEquals(orig_stat.st_ino, stat.st_ino)
self._assertTimeStampEquals(abstraction_mtime, stat.st_mtime)
def test_parser_newer_uses_cache(self):
'''test cache is not skipped if parser is newer'''
self._generate_cache_file()
time.sleep(config.timeout)
# copy parser
os.mkdir(os.path.join(self.tmp_dir, 'parser'))
new_parser = os.path.join(self.tmp_dir, 'parser', 'apparmor_parser')
shutil.copy(config.parser, new_parser)
self._set_mtime(new_parser, os.stat(self.cache_file).st_mtime + self.mtime_res)
cmd = list(self.cmd_prefix)
cmd[0] = new_parser
@ -379,6 +499,7 @@ class AAParserAltCacheTests(AAParserCachingTests):
self.orig_cache_dir = self.cache_dir
self.cache_dir = alt_cache_dir
self.cache_file = os.path.join(self.cache_dir, PROFILE)
self.cmd_prefix.extend(['--cache-loc', alt_cache_dir])
def tearDown(self):

View file

@ -464,6 +464,77 @@ verify_binary_equality "change_profile == change_profile -> **" \
"/t { change_profile /**, }" \
"/t { change_profile /** -> **, }"
verify_binary_equality "profile name is hname in rule" \
":ns:/hname { signal peer=/hname, }" \
":ns:/hname { signal peer=@{profile_name}, }"
verify_binary_inequality "profile name is NOT fq name in rule" \
":ns:/hname { signal peer=:ns:/hname, }" \
":ns:/hname { signal peer=@{profile_name}, }"
verify_binary_equality "profile name is hname in sub pofile rule" \
":ns:/hname { profile child { signal peer=/hname//child, } }" \
":ns:/hname { profile child { signal peer=@{profile_name}, } }"
verify_binary_inequality "profile name is NOT fq name in sub profile rule" \
":ns:/hname { profile child { signal peer=:ns:/hname//child, } }" \
":ns:/hname { profile child { signal peer=@{profile_name}, } }"
verify_binary_equality "profile name is hname in hat rule" \
":ns:/hname { ^child { signal peer=/hname//child, } }" \
":ns:/hname { ^child { signal peer=@{profile_name}, } }"
verify_binary_inequality "profile name is NOT fq name in hat rule" \
":ns:/hname { ^child { signal peer=:ns:/hname//child, } }" \
":ns:/hname { ^child { signal peer=@{profile_name}, } }"
verify_binary_equality "@{profile_name} is literal in peer" \
"/{a,b} { signal peer=/\{a,b\}, }" \
"/{a,b} { signal peer=@{profile_name}, }"
verify_binary_equality "@{profile_name} is literal in peer with pattern" \
"/{a,b} { signal peer={/\{a,b\},c}, }" \
"/{a,b} { signal peer={@{profile_name},c}, }"
verify_binary_inequality "@{profile_name} is not pattern in peer" \
"/{a,b} { signal peer=/{a,b}, }" \
"/{a,b} { signal peer=@{profile_name}, }"
verify_binary_equality "@{profile_name} is literal in peer with esc sequence" \
"/\\\\a { signal peer=/\\\\a, }" \
"/\\\\a { signal peer=@{profile_name}, }"
verify_binary_equality "@{profile_name} is literal in peer with esc alt sequence" \
"/\\{a,b\\},c { signal peer=/\\{a,b\\},c, }" \
"/\\{a,b\\},c { signal peer=@{profile_name}, }"
# verify rlimit data conversions
verify_binary_equality "set rlimit rttime <= 12 weeks" \
"/t { set rlimit rttime <= 12 weeks, }" \
"/t { set rlimit rttime <= $((12 * 7)) days, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24)) hours, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60)) minutes, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60)) seconds, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000)) ms, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)) us, }" \
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)), }"
verify_binary_equality "set rlimit cpu <= 42 weeks" \
"/t { set rlimit cpu <= 42 weeks, }" \
"/t { set rlimit cpu <= $((42 * 7)) days, }" \
"/t { set rlimit cpu <= $((42 * 7 * 24)) hours, }" \
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60)) minutes, }" \
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)) seconds, }" \
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)), }"
verify_binary_equality "set rlimit memlock <= 2GB" \
"/t { set rlimit memlock <= 2GB, }" \
"/t { set rlimit memlock <= $((2 * 1024)) MB, }" \
"/t { set rlimit memlock <= $((2 * 1024 * 1024)) KB, }" \
"/t { set rlimit memlock <= $((2 * 1024 * 1024 * 1024)) , }" \
if [ $fails -ne 0 -o $errors -ne 0 ]
then
printf "ERRORS: %d\nFAILS: %d\n" $errors $fails 2>&1

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION includes testing - non-existent include should fail
#=EXRESULT FAIL
#
/does/not/exist {
include <does-not-exist/does-not-exist>
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION includes testing - mis-parsing include should fail
#=EXRESULT FAIL
#
/does/not/exist {
include does-not-exist/does-not-exist
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION includes testing - non-existent include should fail
#=EXRESULT FAIL
#
/does/not/exist {
include <does-not-exist/does-not-exist>
include <includes/base>
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION includes testing - non-existent include should fail
#=EXRESULT FAIL
#
/does/not/exist {
include <includes/base>
include <does-not-exist/does-not-exist>
}

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1 @@
THIS WILL NOT PARSE!

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Valid include
#
# if parsed stand-alone,
#=EXRESULT PASS
@{FOO} = /foo /bar

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION includes testing - verify that ignored suffixes are ignored
#=EXRESULT PASS
#
include <include_tests/ignored_suffix>
/does/not/exist {
@{FOO} r,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION includes testing - verify that only suffixes are ignored
#=EXRESULT PASS
#
include <include_tests/ignored_suffix_2>
/does/not/exist {
@{FOO} r,
}

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Valid include
#
# if parsed stand-alone,
#=EXRESULT PASS
@{FOO} = /foo /bar

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION A helper for includes_okay.sd
#
# if parsed standalone,
#=EXRESULT FAIL
#
include <includes/fonts>
/tmp/** r,

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION includes testing - basic include of global and local include
#=EXRESULT PASS
#
/does/not/exist {
#include <includes/base>
#include <include_tests/includes_okay_helper.include>
#include <includes/base>
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION includes testing - test some "odd" locations of includes
#=EXRESULT PASS
#
/does/not/exist {
/does/not/exist mr, #include <includes/base> /bin/true Px,
include <include_tests/includes_okay_helper.include> #include <includes/base>
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION includes testing - basic include of a directory
#=EXRESULT PASS
#
/does/not/exist {
include <includes/base>
include <includes/>
include <includes/base>
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION includes testing - recursive include should fail
#=EXRESULT FAIL
#
/does/not/exist {
include <include_tests/recursive.sd>
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION capability rule outside of a profile
#=EXRESULT FAIL
#
capability,

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION change_profile rule outside of a profile
#=EXRESULT FAIL
#
change_profile -> /bin/foo,

View file

@ -0,0 +1,5 @@
#
#=DESCRIPTION dbus rule outside of a profile
#=EXRESULT FAIL
dbus name=(SomeService),

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple dbus implicit bind acceptance test with deny keyword
#=EXRESULT PASS
profile a_profile {
deny dbus name=(SomeService),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION bare file rule outside of a profile
#=EXRESULT FAIL
#
file,

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION link rule outside of a profile
#=EXRESULT FAIL
#
deny link /alpha/beta -> /tmp/**,

View file

@ -0,0 +1,7 @@
#
#=Description bare file rule
#=EXRESULT PASS
#
/usr/bin/foo {
deny file,
}

View file

@ -0,0 +1,6 @@
#
#=Description mount rule outside of a profile
#=EXRESULT FAIL
#
mount,

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION network rule outside of a profile
#=EXRESULT FAIL
#
network,

View file

@ -0,0 +1,7 @@
#
#=Description ptrace all rule outside of a profile
#=EXRESULT FAIL
#
ptrace,

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION realtime time rlimit test with ambiguous unit 'm' which could mean 'ms' or 'minutes'
#=EXRESULT FAIL
profile rlimit {
set rlimit rttime <= 60m,
}

View file

@ -0,0 +1,5 @@
#
#=DESCRIPTION simple cpu rlimit rule outside of a profile
#=EXRESULT FAIL
set rlimit cpu <= 1024,

View file

@ -1,5 +1,5 @@
#
#=DESCRIPTION simple cpu rlimit test
#=DESCRIPTION simple cpu rlimit test, cpu allows default units
#=EXRESULT PASS
profile rlimit {

View file

@ -1,7 +1,7 @@
#
#=DESCRIPTION simple cpu rlimit test
#=DESCRIPTION simple rttime rlimit allows default units
#=EXRESULT PASS
profile rlimit {
set rlimit cpu <= 12,
set rlimit rttime <= 12,
}

View file

@ -0,0 +1,7 @@
#
#=Description signal rule outside of a profile
#=EXRESULT FAIL
#
signal,

View file

@ -0,0 +1,5 @@
#
#=DESCRIPTION unix accept rule outside of a profile
#=EXRESULT FAIL
unix accept,

View file

@ -0,0 +1,8 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar
/does/not/exist@{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,8 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar baz
/does/not/exist@{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,8 @@
#=DESCRIPTION profiles declared with the profile keyword can begin with var
#=EXRESULT PASS
@{FOO}=bar
profile @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,8 @@
#=DESCRIPTION profiles declared with the profile keyword can begin with var
#=EXRESULT PASS
@{FOO}=bar baz
profile @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,8 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar
profile /does/not /exist{@{FOO},} {
/does/not/exist r,
}

View file

@ -0,0 +1,8 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar baz
profile /does/not /exist@{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,10 @@
#=DESCRIPTION profiles declared with the profile keyword can begin with var
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=bar
profile /does/not/exist @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,10 @@
#=DESCRIPTION profiles declared with the profile keyword can begin with var
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=bar baz
profile /does/not/exist @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION reference variables in name and attachment
#=EXRESULT PASS
@{FOO}=bar
@{BAR}=baz
profile /does/not@{BAR} /exist@{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar baz
@{BAR}=baz
profile /does/not@{BAR} /exist@{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION profiles declared with the profile keyword have var and var attachment
#=EXRESULT PASS
@{FOO}=/bar /baz
@{BAR}=baz foo
profile /does/not/exist@{BAR} @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,11 @@
#=DESCRIPTION profiles declared with the profile keyword can expand var and have var attachment
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=bar baz
@{BAR}=baz foo
profile /does/not/exist@{BAR} @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,11 @@
#=DESCRIPTION reference variables that are the profile name and attachment
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=bar
@{BAR}=baz
profile @{BAR} @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,11 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=/bar /baz
@{BAR}=baz
profile @{BAR} @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,11 @@
#=DESCRIPTION profiles declared with the profile keyword can begin with var
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=bar baz
@{BAR}=baz foo
profile @{BAR} @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in sub profile name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
profile foo@{FOO} {
}
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in sub profile name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
profile @{FOO} {
}
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in hat name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
^foo@{FOO} {
}
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in sub profile name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
profile @{FOO} {
}
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in sub profile name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
profile foo@{FOO} {
}
}

View file

@ -0,0 +1,9 @@
#=DESCRIPTION var in hat name
#=EXRESULT PASS
@{FOO}=bar
profile /does/not/exist {
^@{FOO} {
}
}

View file

@ -0,0 +1,10 @@
#=DESCRIPTION all attachment expansions must start with /
#=EXRESULT FAIL
#=TODO
# This test needs check on @{FOO} attachment having leading / post var expansion
@{FOO}=/bar baz
profile /does/not/exist @{FOO} {
/does/not/exist r,
}

View file

@ -0,0 +1,7 @@
#=DESCRIPTION reference variables in profile name is undefined
#=EXRESULT FAIL
/does/not/exist@{FOO} {
/does/not/exist r,
}

Some files were not shown because too many files have changed in this diff Show more