mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 14:25:52 +00:00
Compare commits
170 Commits
v4.0.0-alp
...
v4.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
b264bb62c9 | ||
|
35287c8e1c | ||
|
119e3f38f9 | ||
|
dd0d145a19 | ||
|
f6ad1cbe1e | ||
|
8a5e7227db | ||
|
79ee3eb180 | ||
|
052dd987b3 | ||
|
7e25be7b0b | ||
|
ddefe11a40 | ||
|
b83cf90b82 | ||
|
e3cb9e1669 | ||
|
924b335dfc | ||
|
a2da64304f | ||
|
bb53886211 | ||
|
746f76d3e1 | ||
|
35f25a251b | ||
|
c09f58a364 | ||
|
909e330fd0 | ||
|
88907253e4 | ||
|
124e73ec93 | ||
|
4b1bc85022 | ||
|
c4f649da92 | ||
|
108f489f2b | ||
|
4d2172e82e | ||
|
95d9ba8d8b | ||
|
420d3395fc | ||
|
4608d32628 | ||
|
2394ca82c5 | ||
|
105b50502b | ||
|
105bdfdeb9 | ||
|
f9d38c9b2c | ||
|
3ea050eb30 | ||
|
b49822a48d | ||
|
32bba24468 | ||
|
fd25954c56 | ||
|
ca3afe1691 | ||
|
2577fbf077 | ||
|
2b8cf1be80 | ||
|
89a9f76733 | ||
|
4621af8ead | ||
|
668f5af436 | ||
|
48d475036a | ||
|
0e378f52a8 | ||
|
15337db4af | ||
|
ce3c97df0f | ||
|
789cda2f08 | ||
|
6add80d83f | ||
|
3e28d0a254 | ||
|
e63c1e3a76 | ||
|
b03abbd75f | ||
|
6e81104bbf | ||
|
6cc3a3642d | ||
|
166ebdb3bc | ||
|
253eace573 | ||
|
c2487f017f | ||
|
dc73f0fc0b | ||
|
3578b07aeb | ||
|
28e67c7ba8 | ||
|
c8a2dc34d9 | ||
|
350f9cf3dd | ||
|
f889f9f434 | ||
|
5ca2ea3621 | ||
|
2c5bc5a09b | ||
|
dc821ef762 | ||
|
31c9cf6845 | ||
|
66484687e8 | ||
|
dfb6f90aee | ||
|
fcd46063fd | ||
|
f10e106a08 | ||
|
d778fbef57 | ||
|
70809fc716 | ||
|
15d8e21945 | ||
|
3ee47af402 | ||
|
eb6fa02251 | ||
|
923cbcf3be | ||
|
78a2c9f5f3 | ||
|
81bc26c934 | ||
|
71e28e9357 | ||
|
7c684f9d22 | ||
|
6c01b90c13 | ||
|
832bb8f417 | ||
|
e0bc90f5cf | ||
|
6a96067938 | ||
|
dcad01ccc3 | ||
|
2594d936ad | ||
|
9bba464d93 | ||
|
96b1aa549b | ||
|
dcc719c69c | ||
|
6304d372bf | ||
|
54915dabc4 | ||
|
d55a1e6d5d | ||
|
6580331625 | ||
|
1e7f63415a | ||
|
d4dff5ce4e | ||
|
86c05357cf | ||
|
f17bd59904 | ||
|
d38c7b22ce | ||
|
942202da17 | ||
|
5c34655f4a | ||
|
57ba373213 | ||
|
5d9d4483fb | ||
|
8b95030665 | ||
|
58a89284d5 | ||
|
28f336cb91 | ||
|
61fc6805a9 | ||
|
f2f24884c3 | ||
|
5d8347bc26 | ||
|
0c595ac801 | ||
|
1d5f90efcd | ||
|
4f51c93f9d | ||
|
4d1c17b426 | ||
|
918a15e244 | ||
|
fc8c7722a1 | ||
|
7eff621fc7 | ||
|
659a187687 | ||
|
cee501349e | ||
|
27de7ea0c2 | ||
|
bfd72c93be | ||
|
6ac0e0236b | ||
|
151bf26bb9 | ||
|
26903320fd | ||
|
884adcc58f | ||
|
f5be84acdc | ||
|
5d940b2a47 | ||
|
583d116871 | ||
|
e361644d5a | ||
|
4e758a838d | ||
|
197d00d21a | ||
|
a9c5388f69 | ||
|
75ca0e7919 | ||
|
fb5f59024c | ||
|
a71ac76e6d | ||
|
2be9c431ca | ||
|
05de4b82e7 | ||
|
820f1fb5f2 | ||
|
65905b0c55 | ||
|
1945ecbf19 | ||
|
11976c42e3 | ||
|
327588f019 | ||
|
84e22b4cca | ||
|
248625ae00 | ||
|
9ab72ffc7c | ||
|
9be09aa909 | ||
|
2bf35277a0 | ||
|
9db134223c | ||
|
ef56e60e06 | ||
|
96965c3da2 | ||
|
a9494f5523 | ||
|
57985480ca | ||
|
f10467556c | ||
|
30707be87f | ||
|
f61fd42061 | ||
|
847ab59e1c | ||
|
427b58a4b6 | ||
|
aa20721be1 | ||
|
5957aa49f5 | ||
|
e133a9fc68 | ||
|
32307601a0 | ||
|
5b139521aa | ||
|
2d7bd40606 | ||
|
24806f6f61 | ||
|
fbe68f0078 | ||
|
74265e8ded | ||
|
41df2ca366 | ||
|
adf19138d5 | ||
|
1758b66c9d | ||
|
dd9b7b358f | ||
|
b45c10d4de | ||
|
c175e414c8 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -29,6 +29,7 @@ parser/parser_yacc.h
|
||||
parser/pod2htm*.tmp
|
||||
parser/af_rule.o
|
||||
parser/af_unix.o
|
||||
parser/all_rule.o
|
||||
parser/common_optarg.o
|
||||
parser/dbus.o
|
||||
parser/default_features.o
|
||||
@@ -263,7 +264,10 @@ tests/regression/apparmor/link_subset
|
||||
tests/regression/apparmor/mkdir
|
||||
tests/regression/apparmor/mmap
|
||||
tests/regression/apparmor/mount
|
||||
tests/regression/apparmor/move_mount
|
||||
tests/regression/apparmor/named_pipe
|
||||
tests/regression/apparmor/net_finegrained_rcv
|
||||
tests/regression/apparmor/net_finegrained_snd
|
||||
tests/regression/apparmor/net_raw
|
||||
tests/regression/apparmor/open
|
||||
tests/regression/apparmor/openat
|
||||
|
@@ -17,7 +17,7 @@ stages:
|
||||
- uname -a
|
||||
|
||||
.install-c-build-deps: &install-c-build-deps
|
||||
- apt-get install --no-install-recommends -y build-essential apache2-dev autoconf automake bison dejagnu flex libpam-dev libtool pkg-config python3-all-dev python3-setuptools ruby-dev swig zlib1g-dev
|
||||
- apt-get install --no-install-recommends -y build-essential apache2-dev autoconf autoconf-archive automake bison dejagnu flex libpam-dev libtool pkg-config python3-all-dev python3-setuptools ruby-dev swig zlib1g-dev
|
||||
|
||||
build-all:
|
||||
stage: build
|
||||
|
@@ -181,6 +181,9 @@ $ make check # depends on the parser having been built first
|
||||
$ make install
|
||||
```
|
||||
|
||||
Note that the empty local/* profile sniplets no longer get created by default.
|
||||
If you want them, run `make local` before running `make check`.
|
||||
|
||||
[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".]
|
||||
|
@@ -115,6 +115,7 @@ static void free_processes(struct process *processes, size_t n) {
|
||||
#define SHOW_PROCESSES 2
|
||||
|
||||
static int verbose = 1;
|
||||
static bool quiet = false;
|
||||
int opt_show = SHOW_PROFILES | SHOW_PROCESSES;
|
||||
bool opt_json = false;
|
||||
bool opt_pretty = false;
|
||||
@@ -127,15 +128,21 @@ const char *opt_exe = ".*";
|
||||
const char *profile_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined"};
|
||||
const char *process_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined", "mixed"};
|
||||
|
||||
#define dprintf(...) \
|
||||
#define eprintf(...) \
|
||||
do { \
|
||||
if (!quiet) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dprintf(...) \
|
||||
do { \
|
||||
if (verbose) \
|
||||
if (verbose && !opt_json) \
|
||||
printf(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dfprintf(...) \
|
||||
do { \
|
||||
if (verbose) \
|
||||
if (verbose && !opt_json) \
|
||||
fprintf(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
@@ -149,14 +156,14 @@ static int open_profiles(FILE **fp)
|
||||
|
||||
ret = stat("/sys/module/apparmor", &st);
|
||||
if (ret != 0) {
|
||||
dfprintf(stderr, "apparmor not present.\n");
|
||||
eprintf("apparmor not present.\n");
|
||||
return AA_EXIT_DISABLED;
|
||||
}
|
||||
dprintf("apparmor module is loaded.\n");
|
||||
|
||||
ret = aa_find_mountpoint(&apparmorfs);
|
||||
if (ret == -1) {
|
||||
dfprintf(stderr, "apparmor filesystem is not mounted.\n");
|
||||
eprintf("apparmor filesystem is not mounted.\n");
|
||||
return AA_EXIT_NO_CONTROL;
|
||||
}
|
||||
|
||||
@@ -169,9 +176,9 @@ static int open_profiles(FILE **fp)
|
||||
*fp = fopen(apparmor_profiles, "r");
|
||||
if (*fp == NULL) {
|
||||
if (errno == EACCES) {
|
||||
dfprintf(stderr, "You do not have enough privilege to read the profile set.\n");
|
||||
eprintf("You do not have enough privilege to read the profile set.\n");
|
||||
} else {
|
||||
dfprintf(stderr, "Could not open %s: %s", apparmor_profiles, strerror(errno));
|
||||
eprintf("Could not open %s: %s", apparmor_profiles, strerror(errno));
|
||||
}
|
||||
return AA_EXIT_NO_PERM;
|
||||
}
|
||||
@@ -201,7 +208,7 @@ static int get_profiles(FILE *fp, struct profile **profiles, size_t *n) {
|
||||
char *tmpname = aa_splitcon(line, &status);
|
||||
|
||||
if (!tmpname) {
|
||||
dfprintf(stderr, "Error: failed profile name split of '%s'.\n", line);
|
||||
eprintf("Error: failed profile name split of '%s'.\n", line);
|
||||
// skip this entry and keep processing
|
||||
// else would be AA_EXIT_INTERNAL_ERROR;
|
||||
continue;
|
||||
@@ -344,7 +351,7 @@ static int get_processes(struct profile *profiles,
|
||||
continue;
|
||||
} else if (rc == -1 ||
|
||||
asprintf(&exe, "/proc/%s/exe", entry->d_name) == -1) {
|
||||
fprintf(stderr, "ERROR: Failed to allocate memory\n");
|
||||
eprintf("ERROR: Failed to allocate memory\n");
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto exit;
|
||||
} else if (mode) {
|
||||
@@ -367,7 +374,7 @@ static int get_processes(struct profile *profiles,
|
||||
// ensure enough space for NUL terminator
|
||||
real_exe = calloc(PATH_MAX + 1, sizeof(char));
|
||||
if (real_exe == NULL) {
|
||||
fprintf(stderr, "ERROR: Failed to allocate memory\n");
|
||||
eprintf("ERROR: Failed to allocate memory\n");
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
@@ -575,7 +582,7 @@ static int detailed_profiles(FILE *outf, filters_t *filters, bool json,
|
||||
*/
|
||||
subfilters.mode = &mode_filter;
|
||||
if (regcomp(&mode_filter, profile_statuses[i], REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile sub filter '%s'\n",
|
||||
eprintf("Error: failed to compile sub filter '%s'\n",
|
||||
profile_statuses[i]);
|
||||
return AA_EXIT_INTERNAL_ERROR;
|
||||
}
|
||||
@@ -641,7 +648,7 @@ static int detailed_processes(FILE *outf, filters_t *filters, bool json,
|
||||
*/
|
||||
subfilters.mode = &mode_filter;
|
||||
if (regcomp(&mode_filter, process_statuses[i], REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile sub filter '%s'\n",
|
||||
eprintf("Error: failed to compile sub filter '%s'\n",
|
||||
profile_statuses[i]);
|
||||
return AA_EXIT_INTERNAL_ERROR;
|
||||
}
|
||||
@@ -765,8 +772,9 @@ static int print_usage(const char *command, bool error)
|
||||
" --json displays multiple data points in machine-readable JSON format\n"
|
||||
" --pretty-json same data as --json, formatted for human consumption as well\n"
|
||||
" --verbose (default) displays data points about loaded policy set\n"
|
||||
" -h [(legacy|filter)] this message, or info on the specified option\n"
|
||||
" --help[=(legacy|filter)] this message, or info on the specified option\n",
|
||||
" --quiet don't output error messages\n"
|
||||
" -h[(legacy|filters)] this message, or info on the specified option\n"
|
||||
" --help[=(legacy|filters)] this message, or info on the specified option\n",
|
||||
command);
|
||||
|
||||
exit(status);
|
||||
@@ -792,6 +800,7 @@ static int print_usage(const char *command, bool error)
|
||||
#define ARG_EXE 143
|
||||
#define ARG_PROMPT 144
|
||||
#define ARG_VERBOSE 'v'
|
||||
#define ARG_QUIET 'q'
|
||||
#define ARG_HELP 'h'
|
||||
|
||||
static int parse_args(int argc, char **argv)
|
||||
@@ -809,6 +818,7 @@ static int parse_args(int argc, char **argv)
|
||||
{"json", no_argument, 0, ARG_JSON},
|
||||
{"pretty-json", no_argument, 0, ARG_PRETTY},
|
||||
{"verbose", no_argument, 0, ARG_VERBOSE},
|
||||
{"quiet", no_argument, 0, ARG_QUIET},
|
||||
{"help", 2, 0, ARG_HELP},
|
||||
{"count", no_argument, 0, ARG_COUNT},
|
||||
{"show", 1, 0, ARG_SHOW},
|
||||
@@ -820,7 +830,7 @@ static int parse_args(int argc, char **argv)
|
||||
};
|
||||
|
||||
// Using exit here is temporary
|
||||
while ((opt = getopt_long(argc, argv, "+vh", long_opts, NULL)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "+vh::", long_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case ARG_ENABLED:
|
||||
exit(aa_is_enabled() == 1 ? 0 : AA_EXIT_DISABLED);
|
||||
@@ -830,6 +840,9 @@ static int parse_args(int argc, char **argv)
|
||||
/* default opt_mode */
|
||||
/* default opt_show */
|
||||
break;
|
||||
case ARG_QUIET:
|
||||
quiet = true;
|
||||
break;
|
||||
case ARG_HELP:
|
||||
if (!optarg) {
|
||||
print_usage(argv[0], false);
|
||||
@@ -838,7 +851,7 @@ static int parse_args(int argc, char **argv)
|
||||
} else if (strcmp(optarg, "filters") == 0) {
|
||||
usage_filters();
|
||||
} else {
|
||||
dfprintf(stderr, "Error: Invalid --help option '%s'.\n", optarg);
|
||||
eprintf("Error: Invalid --help option '%s'.\n", optarg);
|
||||
print_usage(argv[0], true);
|
||||
break;
|
||||
}
|
||||
@@ -906,7 +919,7 @@ static int parse_args(int argc, char **argv)
|
||||
} else if (strcmp(optarg, "processes") == 0) {
|
||||
opt_show = SHOW_PROCESSES;
|
||||
} else {
|
||||
dfprintf(stderr, "Error: Invalid --show option '%s'.\n", optarg);
|
||||
eprintf("Error: Invalid --show option '%s'.\n", optarg);
|
||||
print_usage(argv[0], true);
|
||||
break;
|
||||
}
|
||||
@@ -928,7 +941,7 @@ static int parse_args(int argc, char **argv)
|
||||
break;
|
||||
|
||||
default:
|
||||
dfprintf(stderr, "Error: Invalid command.\n");
|
||||
eprintf("Error: Invalid command.\n");
|
||||
print_usage(argv[0], true);
|
||||
break;
|
||||
}
|
||||
@@ -953,7 +966,7 @@ int main(int argc, char **argv)
|
||||
if (argc > 1) {
|
||||
int pos = parse_args(argc, argv);
|
||||
if (pos < argc) {
|
||||
dfprintf(stderr, "Error: Unknown options.\n");
|
||||
eprintf("Error: Unknown options.\n");
|
||||
print_usage(progname, true);
|
||||
}
|
||||
} else {
|
||||
@@ -965,24 +978,24 @@ int main(int argc, char **argv)
|
||||
|
||||
init_filters(&filters, &filter_set);
|
||||
if (regcomp(filters.mode, opt_mode, REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile mode filter '%s'\n",
|
||||
eprintf("Error: failed to compile mode filter '%s'\n",
|
||||
opt_mode);
|
||||
return AA_EXIT_INTERNAL_ERROR;
|
||||
}
|
||||
if (regcomp(filters.profile, opt_profiles, REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile profiles filter '%s'\n",
|
||||
eprintf("Error: failed to compile profiles filter '%s'\n",
|
||||
opt_profiles);
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
if (regcomp(filters.pid, opt_pid, REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile ps filter '%s'\n",
|
||||
eprintf("Error: failed to compile ps filter '%s'\n",
|
||||
opt_pid);
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
if (regcomp(filters.exe, opt_exe, REG_NOSUB) != 0) {
|
||||
dfprintf(stderr, "Error: failed to compile exe filter '%s'\n",
|
||||
eprintf("Error: failed to compile exe filter '%s'\n",
|
||||
opt_exe);
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto out;
|
||||
@@ -997,7 +1010,7 @@ int main(int argc, char **argv)
|
||||
outf_save = outf;
|
||||
outf = open_memstream(&buffer, &buffer_size);
|
||||
if (!outf) {
|
||||
dfprintf(stderr, "Failed to open memstream: %m\n");
|
||||
eprintf("Failed to open memstream: %m\n");
|
||||
return AA_EXIT_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -1008,7 +1021,7 @@ int main(int argc, char **argv)
|
||||
*/
|
||||
ret = get_profiles(fp, &profiles, &nprofiles);
|
||||
if (ret != 0) {
|
||||
dfprintf(stderr, "Failed to get profiles: %d....\n", ret);
|
||||
eprintf("Failed to get profiles: %d....\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1045,7 @@ int main(int argc, char **argv)
|
||||
|
||||
ret = get_processes(profiles, nprofiles, &processes, &nprocesses);
|
||||
if (ret != 0) {
|
||||
dfprintf(stderr, "Failed to get processes: %d....\n", ret);
|
||||
eprintf("Failed to get processes: %d....\n", ret);
|
||||
} else if (opt_count) {
|
||||
ret = simple_filtered_process_count(outf, &filters,
|
||||
processes, nprocesses);
|
||||
@@ -1058,14 +1071,14 @@ int main(int argc, char **argv)
|
||||
outf = outf_save;
|
||||
json = cJSON_Parse(buffer);
|
||||
if (!json) {
|
||||
dfprintf(stderr, "Failed to parse json output");
|
||||
eprintf("Failed to parse json output");
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pretty = cJSON_Print(json);
|
||||
if (!pretty) {
|
||||
dfprintf(stderr, "Failed to print pretty json");
|
||||
eprintf("Failed to print pretty json");
|
||||
ret = AA_EXIT_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
4.0.0~alpha2
|
||||
4.0.0~beta1
|
||||
|
@@ -92,6 +92,13 @@ if test "$ac_cv_prog_cc_c99" = "no"; then
|
||||
AC_MSG_ERROR([C99 mode is required to build libapparmor])
|
||||
fi
|
||||
|
||||
EXTRA_CFLAGS="-Wall $(EXTRA_WARNINGS) -fPIC"
|
||||
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
|
||||
AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],
|
||||
[EXTRA_CFLAGS="$EXTRA_CFLAGS -flto-partition=none"]
|
||||
,)
|
||||
AC_SUBST([AM_CFLAGS], ["$EXTRA_CFLAGS"])
|
||||
|
||||
AC_OUTPUT(
|
||||
Makefile
|
||||
doc/Makefile
|
||||
|
@@ -116,6 +116,14 @@ The specified I<file/task> does not exist or is not visible.
|
||||
|
||||
The confinement data is too large to fit in the supplied buffer.
|
||||
|
||||
=item B<ENOPROTOOPT>
|
||||
|
||||
The kernel doesn't support the SO_PEERLABEL option in sockets. This happens
|
||||
mainly when the kernel lacks 'fine grained unix mediation' support. It also
|
||||
can happen on LSM stacking kernels where another LSM has claimed this
|
||||
interface and decides to return this error, although this is really a
|
||||
corner case.
|
||||
|
||||
=back
|
||||
|
||||
=head1 NOTES
|
||||
|
@@ -109,12 +109,12 @@ To immediately stack a profile named "profile_a", as performed with
|
||||
aa_stack_profile("profile_a"), the equivalent of this shell command can be
|
||||
used:
|
||||
|
||||
$ echo -n "stackprofile profile_a" > /proc/self/attr/current
|
||||
$ echo -n "stack profile_a" > /proc/self/attr/current
|
||||
|
||||
To stack a profile named "profile_a" at the next exec, as performed with
|
||||
aa_stack_onexec("profile_a"), the equivalent of this shell command can be used:
|
||||
|
||||
$ echo -n "stackexec profile_a" > /proc/self/attr/exec
|
||||
$ echo -n "stack profile_a" > /proc/self/attr/exec
|
||||
|
||||
These raw AppArmor filesystem operations must only be used when using
|
||||
libapparmor is not a viable option.
|
||||
@@ -184,6 +184,7 @@ with apparmor_parser(8):
|
||||
/etc/passwd r,
|
||||
|
||||
# Needed for aa_stack_profile()
|
||||
change-profile -> &i_cant_be_trusted_anymore,
|
||||
/usr/lib/libapparmor*.so* mr,
|
||||
/proc/[0-9]*/attr/current w,
|
||||
}
|
||||
|
@@ -33,9 +33,9 @@ INCLUDES = $(all_includes)
|
||||
# After changing the AA_LIB_* variables, also update EXPECTED_SO_NAME.
|
||||
|
||||
AA_LIB_CURRENT = 18
|
||||
AA_LIB_REVISION = 0
|
||||
AA_LIB_REVISION = 1
|
||||
AA_LIB_AGE = 17
|
||||
EXPECTED_SO_NAME = libapparmor.so.1.17.0
|
||||
EXPECTED_SO_NAME = libapparmor.so.1.17.1
|
||||
|
||||
SUFFIXES = .pc.in .pc
|
||||
|
||||
@@ -45,7 +45,6 @@ include $(COMMONDIR)/Make.rules
|
||||
BUILT_SOURCES = grammar.h scanner.h af_protos.h
|
||||
AM_LFLAGS = -v
|
||||
AM_YFLAGS = -d -p aalogparse_
|
||||
AM_CFLAGS = -Wall $(EXTRA_WARNINGS) -fPIC -flto-partition=none
|
||||
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
|
||||
scanner.h: scanner.l
|
||||
$(LEX) -v $<
|
||||
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1709108389.303:12383): apparmor="DENIED" operation="mount" class="mount" info="failed mntpnt match" error=-13 profile="/home/user/test/testmount" name="/tmp/foo/" pid=14155 comm="testmount" flags="ro, remount"
|
@@ -0,0 +1,15 @@
|
||||
START
|
||||
File: testcase_remount_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1709108389.303:12383
|
||||
Operation: mount
|
||||
Profile: /home/user/test/testmount
|
||||
Name: /tmp/foo/
|
||||
Command: testmount
|
||||
Info: failed mntpnt match
|
||||
ErrorCode: 13
|
||||
PID: 14155
|
||||
Flags: ro, remount
|
||||
Class: mount
|
||||
Epoch: 1709108389
|
||||
Audit subid: 12383
|
@@ -0,0 +1,4 @@
|
||||
/home/user/test/testmount {
|
||||
mount options=(remount, ro) -> /tmp/foo/,
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1709025786.045:43147): apparmor="DENIED" operation="umount" class="mount" profile="/home/user/test/testmount" name="/mnt/a/" pid=26697 comm="testmount"
|
@@ -0,0 +1,12 @@
|
||||
START
|
||||
File: testcase_umount_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1709025786.045:43147
|
||||
Operation: umount
|
||||
Profile: /home/user/test/testmount
|
||||
Name: /mnt/a/
|
||||
Command: testmount
|
||||
PID: 26697
|
||||
Class: mount
|
||||
Epoch: 1709025786
|
||||
Audit subid: 43147
|
@@ -0,0 +1,4 @@
|
||||
/home/user/test/testmount {
|
||||
umount /mnt/a/,
|
||||
|
||||
}
|
@@ -70,7 +70,10 @@ CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
endif #CFLAGS
|
||||
|
||||
CFLAGS += -flto-partition=none
|
||||
HAVE_FLTO_PARTITION_NONE:=$(shell ${CC} -E -flto-partition=none /dev/null 1>/dev/null 2>&1 && echo true)
|
||||
ifeq ($(HAVE_FLTO_PARTITION_NONE),true)
|
||||
CFLAGS += -flto-partition=none
|
||||
endif
|
||||
|
||||
EXTRA_CXXFLAGS = ${CFLAGS} ${CPPFLAGS} ${CXX_WARNINGS} -std=gnu++0x
|
||||
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
|
||||
@@ -99,15 +102,15 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
||||
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
|
||||
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||
parser_alias.c common_optarg.c lib.c network.c \
|
||||
parser_alias.c common_optarg.c lib.c network.cc \
|
||||
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
|
||||
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
|
||||
mqueue.cc io_uring.cc
|
||||
mqueue.cc io_uring.cc all_rule.cc
|
||||
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
|
||||
file_cache.h immunix.h lib.h mount.h network.h parser.h \
|
||||
parser_include.h parser_version.h policy_cache.h policydb.h \
|
||||
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
|
||||
common_flags.h
|
||||
common_flags.h bignum.h all_rule.h
|
||||
|
||||
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
|
||||
GENERATED_HDRS = af_names.h generated_af_names.h \
|
||||
@@ -295,7 +298,7 @@ signal.o: signal.cc $(HDRS)
|
||||
ptrace.o: ptrace.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
network.o: network.c $(HDRS)
|
||||
network.o: network.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
default_features.o: default_features.c $(HDRS)
|
||||
@@ -322,6 +325,9 @@ mqueue.o: mqueue.cc $(HDRS)
|
||||
io_uring.o: io_uring.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
all_rule.o: all_rule.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
parser_version.h: Makefile
|
||||
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
||||
@mv -f .ver $@
|
||||
@@ -373,13 +379,8 @@ tests: apparmor_parser ${TESTS}
|
||||
$(AAREOBJECT): FORCE
|
||||
$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
|
||||
|
||||
.PHONY: install-rhel4
|
||||
install-rhel4: install-redhat
|
||||
|
||||
.PHONY: install-redhat
|
||||
install-redhat:
|
||||
install -m 755 -d $(DESTDIR)/etc/init.d
|
||||
install -m 755 rc.apparmor.$(subst install-,,$@) $(DESTDIR)/etc/init.d/apparmor
|
||||
install-redhat: install-systemd
|
||||
|
||||
.PHONY: install-suse
|
||||
install-suse: install-systemd
|
||||
@@ -410,9 +411,9 @@ DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
|
||||
if [ "$$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
|
||||
echo suse ;\
|
||||
elif [ "$$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
|
||||
echo rhel4 ;\
|
||||
echo redhat ;\
|
||||
elif [ "$$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
|
||||
echo rhel4 ;\
|
||||
echo redhat ;\
|
||||
else \
|
||||
echo unknown ;\
|
||||
fi ;\
|
||||
|
@@ -108,6 +108,9 @@ unix_rule::unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode
|
||||
perms = AA_VALID_NET_PERMS;
|
||||
audit = audit_p;
|
||||
rule_mode = rule_mode_p;
|
||||
/* if this constructor is used, then there's already a
|
||||
* downgraded network_rule in profile */
|
||||
downgrade = false;
|
||||
}
|
||||
|
||||
unix_rule::unix_rule(perms_t perms_p, struct cond_entry *conds,
|
||||
@@ -190,7 +193,7 @@ static void writeu16(std::ostringstream &o, int v)
|
||||
void unix_rule::downgrade_rule(Profile &prof) {
|
||||
perms_t mask = (perms_t) -1;
|
||||
|
||||
if (!prof.net.allow && !prof.alloc_net_table())
|
||||
if (!prof.net.allow && !prof.net.alloc_net_table())
|
||||
yyerror(_("Memory allocation error."));
|
||||
if (sock_type_n != -1)
|
||||
mask = 1 << sock_type_n;
|
||||
@@ -198,6 +201,11 @@ void unix_rule::downgrade_rule(Profile &prof) {
|
||||
prof.net.allow[AF_UNIX] |= mask;
|
||||
if (audit == AUDIT_FORCE)
|
||||
prof.net.audit[AF_UNIX] |= mask;
|
||||
const char *error;
|
||||
network_rule *netv8 = new network_rule(perms, AF_UNIX, sock_type_n);
|
||||
if(!netv8->add_prefix({audit, rule_mode, owner}, error))
|
||||
yyerror(error);
|
||||
prof.rule_ents.push_back(netv8);
|
||||
} else {
|
||||
/* deny rules have to be dropped because the downgrade makes
|
||||
* the rule less specific meaning it will make the profile more
|
||||
@@ -317,7 +325,8 @@ int unix_rule::gen_policy_re(Profile &prof)
|
||||
* older kernels and be enforced to the best of the old network
|
||||
* rules ability
|
||||
*/
|
||||
downgrade_rule(prof);
|
||||
if (downgrade)
|
||||
downgrade_rule(prof);
|
||||
if (!features_supports_unix) {
|
||||
if (features_supports_network || features_supports_networkv8) {
|
||||
/* only warn if we are building against a kernel
|
||||
|
@@ -36,6 +36,7 @@ class unix_rule: public af_rule {
|
||||
public:
|
||||
char *addr;
|
||||
char *peer_addr;
|
||||
bool downgrade = true;
|
||||
|
||||
unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode_p);
|
||||
unix_rule(perms_t perms, struct cond_entry *conds,
|
||||
|
130
parser/all_rule.cc
Normal file
130
parser/all_rule.cc
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2023
|
||||
* 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 Canonical Ltd.
|
||||
*/
|
||||
|
||||
#include "profile.h"
|
||||
#include "all_rule.h"
|
||||
#include "af_unix.h"
|
||||
#include "dbus.h"
|
||||
#include "io_uring.h"
|
||||
#include "mqueue.h"
|
||||
#include "ptrace.h"
|
||||
#include "signal.h"
|
||||
#include "userns.h"
|
||||
#include "mount.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
|
||||
void all_rule::add_implied_rules(Profile &prof)
|
||||
{
|
||||
prefix_rule_t *rule;
|
||||
const prefixes *prefix = this;
|
||||
|
||||
rule = new unix_rule(0, audit, rule_mode);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new dbus_rule(0, NULL, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new io_uring_rule(0, NULL, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new mqueue_rule(0, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new ptrace_rule(0, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new signal_rule(0, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new userns_rule(0, NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new mnt_rule(NULL, NULL, NULL, NULL, 0);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_DUMMY_REMOUNT);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_MAY_UMOUNT);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_MAY_PIVOTROOT);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
rule = new network_rule(0, (struct cond_entry *)NULL, (struct cond_entry *)NULL);
|
||||
(void) rule->add_prefix(*prefix);
|
||||
prof.rule_ents.push_back(rule);
|
||||
|
||||
/* rules that have not been converted to use rule.h */
|
||||
|
||||
//file
|
||||
{
|
||||
const char *error;
|
||||
struct cod_entry *entry;
|
||||
char *path = strdup("/{**,}");
|
||||
int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
|
||||
(AA_MAY_EXEC));
|
||||
if (rule_mode != RULE_DENY)
|
||||
perms |= AA_EXEC_INHERIT;
|
||||
/* duplicate to other permission set */
|
||||
perms |= perms << AA_OTHER_SHIFT;
|
||||
if (!path)
|
||||
yyerror(_("Memory allocation error."));
|
||||
entry = new_entry(path, perms, NULL);
|
||||
if (!entry_add_prefix(entry, *prefix, error)) {
|
||||
yyerror(_("%s"), error);
|
||||
}
|
||||
add_entry_to_policy(&prof, entry);
|
||||
}
|
||||
|
||||
// caps
|
||||
{
|
||||
if (prefix->owner)
|
||||
yyerror(_("owner prefix not allowed on capability rules"));
|
||||
|
||||
if (rule_mode == RULE_DENY && audit == AUDIT_FORCE) {
|
||||
prof.caps.deny |= 0xffffffffffffffff;
|
||||
} else if (rule_mode == RULE_DENY) {
|
||||
prof.caps.deny |= 0xffffffffffffffff;
|
||||
prof.caps.quiet |= 0xffffffffffffffff;
|
||||
} else {
|
||||
prof.caps.allow |= 0xffffffffffffffff;
|
||||
if (audit != AUDIT_UNSPECIFIED)
|
||||
prof.caps.audit |= 0xffffffffffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rlimit
|
||||
}
|
70
parser/all_rule.h
Normal file
70
parser/all_rule.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023
|
||||
* 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 Canonical Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_ALL_H
|
||||
#define __AA_ALL_H
|
||||
|
||||
#include "rule.h"
|
||||
|
||||
#define AA_IO_URING_OVERRIDE_CREDS AA_MAY_APPEND
|
||||
#define AA_IO_URING_SQPOLL AA_MAY_CREATE
|
||||
|
||||
#define AA_VALID_IO_URING_PERMS (AA_IO_URING_OVERRIDE_CREDS | \
|
||||
AA_IO_URING_SQPOLL)
|
||||
|
||||
class all_rule: public prefix_rule_t {
|
||||
void move_conditionals(struct cond_entry *conds);
|
||||
public:
|
||||
char *label;
|
||||
|
||||
all_rule(void): prefix_rule_t(RULE_TYPE_ALL) { }
|
||||
|
||||
virtual bool valid_prefix(const prefixes &p, const char *&error) {
|
||||
if (p.owner) {
|
||||
error = _("owner prefix not allowed on all rules");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
int expand_variables(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual ostream &dump(ostream &os) {
|
||||
prefix_rule_t::dump(os);
|
||||
|
||||
os << "all";
|
||||
|
||||
return os;
|
||||
}
|
||||
virtual bool is_mergeable(void) { return true; }
|
||||
virtual int cmp(rule_t const &rhs) const
|
||||
{
|
||||
return prefix_rule_t::cmp(rhs);
|
||||
};
|
||||
|
||||
virtual void add_implied_rules(Profile &prof);
|
||||
|
||||
virtual int gen_policy_re(Profile &prof unused) { return RULE_OK; };
|
||||
|
||||
protected:
|
||||
virtual void warn_once(const char *name unused, const char *msg unused) { };
|
||||
virtual void warn_once(const char *name unused) { };
|
||||
};
|
||||
|
||||
#endif /* __AA_ALL_H */
|
@@ -115,9 +115,9 @@ B<PROFILE FLAG CONDS> = [ 'flags=' ] '(' comma or white space separated list of
|
||||
|
||||
B<PROFILE FLAGS> = I<PROFILE MODE> | I<AUDIT_MODE> | 'mediate_deleted'
|
||||
| 'attach_disconnected' | 'attach_disconneced.path='I<ABS PATH> | 'chroot_relative'
|
||||
| 'debug'
|
||||
| 'debug' | 'interruptible' | 'kill.signal='I<SIGNAL>
|
||||
|
||||
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'unconfined' | 'prompt'
|
||||
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'default_allow' | 'unconfined' | 'prompt'
|
||||
|
||||
B<AUDIT MODE> = 'audit'
|
||||
|
||||
@@ -125,7 +125,7 @@ B<RULES> = [ ( I<LINE RULES> | I<COMMA RULES> ',' | I<BLOCK RULES> )
|
||||
|
||||
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
|
||||
|
||||
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> | I<MQUEUE RULE> )
|
||||
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> | I<MQUEUE RULE> | I<IO_URING RULE> | I<USERNS RULE> | I<ALL RULE>)
|
||||
|
||||
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
|
||||
|
||||
@@ -192,6 +192,16 @@ B<MQUEUE LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
|
||||
|
||||
B<MQUEUE NAME> = I<AARE>
|
||||
|
||||
B<USERNS RULE> = [ I<QUALIFIERS> ] 'userns' [ I<USERNS ACCESS PERMISSIONS> ]
|
||||
|
||||
B<USERNS ACCESS PERMISSIONS> = ( 'create' )
|
||||
|
||||
B<IO_URING RULE> = [ I<QUALIFIERS> ] 'io_uring' [ I<IO_URING ACCESS PERMISSIONS> [ I<IO_URING LABEL> ]
|
||||
|
||||
B<IO_URING ACCESS PERMISSIONS> = ( 'sqpoll' | 'override_creds' )
|
||||
|
||||
B<IO_URING LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
|
||||
|
||||
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
|
||||
|
||||
B<SOURCE FILEGLOB> = I<FILEGLOB>
|
||||
@@ -220,9 +230,9 @@ B<SIGNAL ACCESS> = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' | 'receive' )
|
||||
|
||||
B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
|
||||
|
||||
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
|
||||
B<SIGNAL LIST> = Comma or space separated list of I<SIGNAL>s
|
||||
|
||||
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> = ( '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>
|
||||
|
||||
@@ -336,6 +346,8 @@ B<EXEC_MODE> = ( 'safe' | 'unsafe' )
|
||||
|
||||
B<EXEC COND> = I<FILEGLOB>
|
||||
|
||||
B<ALL RULE> = 'all'
|
||||
|
||||
=back
|
||||
|
||||
All resources and programs need a full path. There may be any number of
|
||||
@@ -454,12 +466,36 @@ a signal to kill it.
|
||||
permission the action will be allowed, but the violation will be logged
|
||||
with a tag of the access being B<ALLOWED>.
|
||||
|
||||
=item B<default_allow> This mode changes the default behavior of
|
||||
apparmor from default deny to default allow. When default_allow is
|
||||
specified the resulting profile will allow operations that the profile
|
||||
does not have a rule for. This mode is similar to I<unconfined> but
|
||||
allows for allow and deny rules, specifying audit, and domain
|
||||
transitions. Profiles in this mode may be be reported as being in
|
||||
I<enforce> mode or I<allow> mode when introspected from the kernel.
|
||||
|
||||
Note: default_allow is similar and for many profiles will be equivalent
|
||||
to specifying an I<allow all,> rule in the profile. The default_allow
|
||||
flag does not provide all the same option that the I<allow all,> rule
|
||||
provides.
|
||||
|
||||
=item B<unconfined> This mode allows a task confined by the profile to
|
||||
behave as though they are I<unconfined>. This mode allow for an
|
||||
unconfined behavior that can be later changed to confinement by using
|
||||
profile replacement. This mode is should not be used under regular
|
||||
deployment but can be useful during debugging and some system
|
||||
initialization scenarios.
|
||||
behave as though it is I<unconfined>. The unconfined behavior can be
|
||||
later changed to confinement by using profile replacement. This mode
|
||||
should not be used under regular deployment but can be useful during
|
||||
debugging and some system initialization scenarios.
|
||||
|
||||
This mode is similar to default_allow and may be emulated by
|
||||
default_allow in kernels that no longer support a true unconfined
|
||||
mode. It does not generally allow for specifying deny rules, or allow
|
||||
rules that override the default behavior, except in a few custom
|
||||
kernels where unconfined restricts a few operations. It relies on
|
||||
special customized behavior of the unconfined profile in the kernel
|
||||
and as such should only be used for debugging.
|
||||
|
||||
Note: true unconfined is being phased out, with unconfined becoming a
|
||||
replaceable profile. As such unconfined mode will be emulated by a
|
||||
special profile compiled with the default_allow flag in newer kernels.
|
||||
|
||||
=item B<prompt> This mode allows task mediation to send an up call to
|
||||
userspace to ask for a decision when there isn't a rule covering the
|
||||
@@ -506,6 +542,11 @@ flags to control what messages will be output. Its effect is kernel
|
||||
dependent, and it should never appear in policy except when trying
|
||||
to debug kernel or policy problems.
|
||||
|
||||
=item B<interruptible> Enables interrupts for prompt upcall to userspace.
|
||||
|
||||
=item B<kill.signal>=I<SIGNAL> This changes the signal that will be
|
||||
sent by AppArmor when in kill mode or a kill rule has been violated.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Access Modes
|
||||
@@ -1133,6 +1174,89 @@ Example AppArmor Message Queue rules:
|
||||
# Allow create permission for a SYSV queue of label foo
|
||||
mqueue create label=foo 123,
|
||||
|
||||
=head2 User Namespace Rules
|
||||
|
||||
User namespaces are part of many sandboxing and containerization
|
||||
solutions. They provide a way for a non-system root process to be
|
||||
root within the container. Unfortunately this opens up attack surface
|
||||
in the kernel and has been part of several exploit chains. As such
|
||||
AppArmor can be used to restrict the creation of user namespaces to
|
||||
select processes.
|
||||
|
||||
User namespace permission are implied when a rule does not explicitly
|
||||
state an access list. The rule becomes more restrictive as further
|
||||
information is specified.
|
||||
|
||||
Note: user namespace creation may be restricted so that it is not
|
||||
available to unprivieged unconfined processes. If this is the case any
|
||||
process trying to create user namespaces will require a profile that
|
||||
allows the necessary permissions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<create>
|
||||
|
||||
Allow creation of user namespaces.
|
||||
|
||||
=back
|
||||
|
||||
Example userns rules:
|
||||
|
||||
=over 4
|
||||
|
||||
# Allow all userns perms
|
||||
userns,
|
||||
|
||||
# Allow creation of a userns
|
||||
userns create,
|
||||
|
||||
=back
|
||||
|
||||
=head2 IO_URing Rules
|
||||
|
||||
AppArmor supports mediation of the new Linux high speed IO interface.
|
||||
There is limited mediation at this time to just a few permissions at
|
||||
the moment.
|
||||
|
||||
IO Uring permission are implied when a rule does not explicitly state
|
||||
an access list. The rule becomes more restrictive as further
|
||||
information is specified.
|
||||
|
||||
Note: io_uring access may be restricted so that it is not available to
|
||||
unprivileged unconfined processes. If this is the case any process
|
||||
trying to use io_uring will require a profile that allows the
|
||||
necessary io_uring permissions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<sqpoll>
|
||||
|
||||
All the task confined by the profile to spawn a io_uring polling
|
||||
thread.
|
||||
|
||||
=item B<override_creds>
|
||||
|
||||
Grants the task confined by the profile to override (change) its
|
||||
credentials to the specified label, when executing an io_uring
|
||||
operation.
|
||||
|
||||
=back
|
||||
|
||||
Example IO_URING rules:
|
||||
|
||||
=over 4
|
||||
|
||||
# Allow io_uring operations
|
||||
io_ring,
|
||||
|
||||
# Allow creation of a polling thread
|
||||
io_uring sqpoll,
|
||||
|
||||
# Allow task to override credentials during io_uring operation
|
||||
io_uring override_creds label=new_creds,
|
||||
|
||||
=back
|
||||
|
||||
=head2 Pivot Root Rules
|
||||
|
||||
AppArmor mediates changing of the root filesystem through the pivot_root(2)
|
||||
@@ -1506,6 +1630,26 @@ Not all kernels support B<safe> mode and the parser will downgrade rules to
|
||||
B<unsafe> mode in that situation. If no exec mode is specified, the default is
|
||||
B<safe> mode in kernels that support it.
|
||||
|
||||
=head2 all rule
|
||||
|
||||
The all rule is used to add a generic rule for all supported rule types.
|
||||
This is useful when policy wants to define a black list instead of
|
||||
white list, but can also be useful to add an access qualifier to all
|
||||
rules.
|
||||
|
||||
Eg. Black list
|
||||
|
||||
allow all,
|
||||
# begin blacklist
|
||||
deny file,
|
||||
deny unix,
|
||||
|
||||
|
||||
Eg. Adding audit qualifier
|
||||
|
||||
audit access all,
|
||||
|
||||
|
||||
=head2 rlimit rules
|
||||
|
||||
AppArmor can set and control the resource limits associated with a
|
||||
|
@@ -6,6 +6,8 @@ After=systemd-journald-audit.socket
|
||||
# profile cache: /var/cache/apparmor/ and /usr/share/apparmor/cache/
|
||||
After=var.mount var-cache.mount usr.mount usr-share.mount
|
||||
ConditionSecurity=apparmor
|
||||
Documentation=man:apparmor(7)
|
||||
Documentation=https://gitlab.com/apparmor/apparmor/wikis/home/
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
@@ -299,11 +299,11 @@ Enable various warnings during policy compilation. A single warn flag
|
||||
can be specified per --warn option, but the --warn flag can be passed
|
||||
multiple times.
|
||||
|
||||
apparmor_parser --warn=rules-not-enforced ...
|
||||
apparmor_parser --warn=rule-not-enforced ...
|
||||
|
||||
A specific warning can be disabled by prepending I<no>- to the flag
|
||||
|
||||
apparmor_parser --warn=no-rules-not-enforced ...
|
||||
apparmor_parser --warn=no-rule-not-enforced ...
|
||||
|
||||
Use --help=warn to see a full list of which warn flags are supported.
|
||||
|
||||
|
233
parser/bignum.h
Normal file
233
parser/bignum.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2023
|
||||
* 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 Canonical Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_BIGNUM_H
|
||||
#define __AA_BIGNUM_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
class bignum
|
||||
{
|
||||
public:
|
||||
std::vector<uint8_t> data;
|
||||
uint8_t base;
|
||||
bool negative = false;
|
||||
bignum () : base(0) {}
|
||||
|
||||
bignum (unsigned long val) {
|
||||
if (val == 0)
|
||||
data.push_back(val);
|
||||
else {
|
||||
while(val > 0) {
|
||||
data.push_back(val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
}
|
||||
base = 10;
|
||||
}
|
||||
|
||||
bignum (const char *val) {
|
||||
while (*val) {
|
||||
data.push_back(*val - 48);
|
||||
val++;
|
||||
}
|
||||
std::reverse(data.begin(), data.end());
|
||||
base = 10;
|
||||
}
|
||||
|
||||
bignum (const uint8_t val[16]) {
|
||||
size_t i;
|
||||
bool flag = true;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (flag && (val[i] & 0xF0) >> 4 != 0)
|
||||
flag = false;
|
||||
if (!flag)
|
||||
data.push_back((val[i] & 0xF0) >> 4);
|
||||
if (flag && (val[i] & 0x0F) != 0)
|
||||
flag = false;
|
||||
if (!flag)
|
||||
data.push_back(val[i] & 0x0F);
|
||||
}
|
||||
std::reverse(data.begin(), data.end());
|
||||
base = 16;
|
||||
}
|
||||
|
||||
bignum operator+(const bignum &brhs) const {
|
||||
bignum b1 = this->size() < brhs.size() ? *this : brhs;
|
||||
bignum b2 = this->size() < brhs.size() ? brhs : *this;
|
||||
bignum result;
|
||||
result.base = this->base;
|
||||
uint8_t carryover = 0;
|
||||
uint8_t sum;
|
||||
size_t i;
|
||||
for (i = 0; i < b1.size(); i++) {
|
||||
sum = b1[i] + b2[i] + carryover;
|
||||
if (sum > base - 1)
|
||||
carryover = 1;
|
||||
else
|
||||
carryover = 0;
|
||||
result.data.push_back(sum % base);
|
||||
}
|
||||
for (; i < b2.size(); i++) {
|
||||
sum = b2[i] + carryover;
|
||||
if (sum > base - 1)
|
||||
carryover = 1;
|
||||
else
|
||||
carryover = 0;
|
||||
result.data.push_back(sum % base);
|
||||
}
|
||||
if (carryover != 0)
|
||||
result.data.push_back(carryover);
|
||||
return result;
|
||||
}
|
||||
|
||||
bignum operator-(const bignum &brhs) const {
|
||||
bignum b1 = this->size() < brhs.size() ? *this : brhs;
|
||||
bignum b2 = this->size() < brhs.size() ? brhs : *this;
|
||||
bignum result;
|
||||
result.negative = *this < brhs;
|
||||
result.base = this->base;
|
||||
int8_t borrow = 0;
|
||||
int8_t sub;
|
||||
size_t i;
|
||||
for (i = 0; i < b1.size(); i++) {
|
||||
sub = b2[i] - b1[i] - borrow;
|
||||
if (sub < 0) {
|
||||
sub += base;
|
||||
borrow = 1;
|
||||
} else
|
||||
borrow = 0;
|
||||
result.data.push_back(sub);
|
||||
}
|
||||
for (; i < b2.size(); i++) {
|
||||
sub = b2[i] - borrow;
|
||||
if (sub < 0) {
|
||||
sub += base;
|
||||
borrow = 1;
|
||||
} else
|
||||
borrow = 0;
|
||||
result.data.push_back(sub);
|
||||
}
|
||||
if (borrow) {
|
||||
int8_t tmp = result.data[result.size() - 1] -= base;
|
||||
tmp *= -1;
|
||||
result.data[result.size() - 1] = tmp;
|
||||
}
|
||||
while (result.size() > 1 && result.data[result.size() - 1] == 0)
|
||||
result.data.pop_back();
|
||||
|
||||
return result;
|
||||
}
|
||||
bool operator>=(const bignum &rhs) const {
|
||||
return cmp_bignum(this->data, rhs.data) >= 0;
|
||||
}
|
||||
bool operator<=(const bignum &rhs) const {
|
||||
return cmp_bignum(this->data, rhs.data) <= 0;
|
||||
}
|
||||
bool operator>(const bignum &rhs) const {
|
||||
return cmp_bignum(this->data, rhs.data) > 0;
|
||||
}
|
||||
bool operator<(const bignum &rhs) const {
|
||||
return cmp_bignum(this->data, rhs.data) < 0;
|
||||
}
|
||||
int operator[](int index) const {
|
||||
return this->data[index];
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &os, bignum &bn);
|
||||
size_t size() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/*
|
||||
returns:
|
||||
- 0, if the lhs and rhs are equal;
|
||||
- a negative value if lhs is less than rhs;
|
||||
- a positive value if lhs is greater than rhs.
|
||||
*/
|
||||
int cmp_bignum(std::vector<uint8_t> lhs, std::vector<uint8_t> rhs) const
|
||||
{
|
||||
if (lhs.size() > rhs.size())
|
||||
return 1;
|
||||
else if (lhs.size() < rhs.size())
|
||||
return -1;
|
||||
else {
|
||||
/* assumes the digits are stored in reverse order */
|
||||
std::reverse(lhs.begin(), lhs.end());
|
||||
std::reverse(rhs.begin(), rhs.end());
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
if (lhs[i] > rhs[i])
|
||||
return 1;
|
||||
if (lhs[i] < rhs[i])
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bignum lower_bound_regex(bignum val)
|
||||
{
|
||||
/* single digit numbers reduce to 0 */
|
||||
if (val.size() == 1) {
|
||||
val.data[0] = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
for (auto& j : val.data) {
|
||||
uint8_t tmp = j;
|
||||
j = 0;
|
||||
if (tmp != val.base - 1) {
|
||||
break;
|
||||
}
|
||||
if (&j == &val.data[val.size()-2]) {
|
||||
val.data[val.size()-1] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
static bignum upper_bound_regex(bignum val)
|
||||
{
|
||||
for (auto& j : val.data) {
|
||||
uint8_t tmp = j;
|
||||
j = val.base - 1;
|
||||
if (tmp != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, bignum &bn)
|
||||
{
|
||||
std::stringstream ss;
|
||||
bignum tmp = bn;
|
||||
std::reverse(tmp.data.begin(), tmp.data.end());
|
||||
for (auto i : tmp.data)
|
||||
ss << std::hex << (int) i;
|
||||
os << ss.str();
|
||||
return os;
|
||||
};
|
||||
|
||||
#endif /* __AA_BIGNUM_H */
|
@@ -12,7 +12,7 @@
|
||||
* 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 or Canonical Ltd.
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#include "common_optarg.h"
|
||||
|
@@ -12,7 +12,7 @@
|
||||
* 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 or Canonical Ltd.
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_IO_URING_H
|
||||
|
@@ -72,6 +72,7 @@ static inline Chars* insert_char_range(Chars* cset, transchar a, transchar b)
|
||||
* parsing succeeds!
|
||||
*/
|
||||
%destructor { $$->release(); } expr terms0 terms qterm term
|
||||
%destructor { delete $$; } charset cset_chars
|
||||
|
||||
%%
|
||||
|
||||
|
396
parser/network.c
396
parser/network.c
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/apparmor.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "parser_yacc.h"
|
||||
#include "network.h"
|
||||
|
||||
|
||||
int parse_net_perms(const char *str_mode, perms_t *mode, int fail)
|
||||
{
|
||||
return parse_X_perms("net", AA_VALID_NET_PERMS, str_mode, mode, fail);
|
||||
}
|
||||
|
||||
/* Bleah C++ doesn't have non-trivial designated initializers so we just
|
||||
* have to make sure these are in order. This means we are more brittle
|
||||
* but there isn't much we can do.
|
||||
*/
|
||||
struct sock_type_map {
|
||||
const char *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct sock_type_map sock_types[] = {
|
||||
{ "none", 0 },
|
||||
{ "stream", SOCK_STREAM },
|
||||
{ "dgram", SOCK_DGRAM },
|
||||
{ "raw", SOCK_RAW },
|
||||
{ "rdm", SOCK_RDM },
|
||||
{ "seqpacket", SOCK_SEQPACKET },
|
||||
{ "dccp", SOCK_DCCP },
|
||||
{ "invalid", -1 },
|
||||
{ "invalid", -1 },
|
||||
{ "invalid", -1 },
|
||||
{ "packet", SOCK_PACKET },
|
||||
{ NULL, -1 },
|
||||
/*
|
||||
* See comment above
|
||||
*/
|
||||
};
|
||||
|
||||
int net_find_type_val(const char *type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; sock_types[i].name; i++) {
|
||||
if (strcmp(sock_types[i].name, type) == 0)
|
||||
return sock_types[i].value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *net_find_type_name(int type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; sock_types[i].name; i++) {
|
||||
if (sock_types[i].value == type)
|
||||
return sock_types[i].name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: currently just treating as a bit mask this will have to change
|
||||
* set up a table of mappings, there can be several mappings for a
|
||||
* given match.
|
||||
* currently the mapping does not set the protocol for stream/dgram to
|
||||
* anything other than 0.
|
||||
* network inet tcp -> network inet stream 0 instead of
|
||||
* network inet raw tcp.
|
||||
* some entries are just provided for completeness at this time
|
||||
*/
|
||||
/* values stolen from /etc/protocols - needs to change */
|
||||
#define RAW_TCP 6
|
||||
#define RAW_UDP 17
|
||||
#define RAW_ICMP 1
|
||||
#define RAW_ICMPv6 58
|
||||
|
||||
/* used by af_name.h to auto generate table entries for "name", AF_NAME
|
||||
* pair */
|
||||
#define AA_GEN_NET_ENT(name, AF) \
|
||||
{name, AF, "stream", SOCK_STREAM, "", 0xffffff}, \
|
||||
{name, AF, "dgram", SOCK_DGRAM, "", 0xffffff}, \
|
||||
{name, AF, "seqpacket", SOCK_SEQPACKET, "", 0xffffff}, \
|
||||
{name, AF, "rdm", SOCK_RDM, "", 0xffffff}, \
|
||||
{name, AF, "raw", SOCK_RAW, "", 0xffffff}, \
|
||||
{name, AF, "packet", SOCK_PACKET, "", 0xffffff},
|
||||
/*FIXME: missing {name, AF, "dccp", SOCK_DCCP, "", 0xfffffff}, */
|
||||
|
||||
static struct network_tuple network_mappings[] = {
|
||||
/* basic types */
|
||||
#include "af_names.h"
|
||||
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
|
||||
/* mapped types */
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"tcp", 1 << RAW_TCP},
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"udp", 1 << RAW_UDP},
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"icmp", 1 << RAW_ICMP},
|
||||
{"inet", AF_INET, "tcp", SOCK_STREAM,
|
||||
"", 0xffffffff}, /* should we give raw tcp too? */
|
||||
{"inet", AF_INET, "udp", SOCK_DGRAM,
|
||||
"", 0xffffffff}, /* should these be open masks? */
|
||||
{"inet", AF_INET, "icmp", SOCK_RAW,
|
||||
"", 1 << RAW_ICMP},
|
||||
{"inet6", AF_INET6, "tcp", SOCK_STREAM,
|
||||
"", 0xffffffff},
|
||||
{"inet6", AF_INET6, "udp", SOCK_DGRAM,
|
||||
"", 0xffffffff},
|
||||
/* what do we do with icmp on inet6?
|
||||
{"inet6", AF_INET, "icmp", SOCK_RAW, 0},
|
||||
{"inet6", AF_INET, "icmpv6", SOCK_RAW, 0},
|
||||
*/
|
||||
/* terminate */
|
||||
{NULL, 0, NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
|
||||
* tables with sizes > AF_MAX correctly. This could happen when the
|
||||
* parser was built against newer kernel headers and then used to load
|
||||
* policy on an older kernel. This could happen during upgrades or
|
||||
* in multi-kernel boot systems.
|
||||
*
|
||||
* Try to detect the running kernel version and use that to determine
|
||||
* AF_MAX
|
||||
*/
|
||||
#define PROC_VERSION "/proc/sys/kernel/osrelease"
|
||||
static size_t kernel_af_max(void) {
|
||||
char buffer[32];
|
||||
int major;
|
||||
autoclose int fd = -1;
|
||||
int res;
|
||||
|
||||
if (!net_af_max_override) {
|
||||
return 0;
|
||||
}
|
||||
/* the override parameter is specifying the max value */
|
||||
if (net_af_max_override > 0)
|
||||
return net_af_max_override;
|
||||
|
||||
fd = open(PROC_VERSION, O_RDONLY);
|
||||
if (fd == -1)
|
||||
/* fall back to default provided during build */
|
||||
return 0;
|
||||
res = read(fd, &buffer, sizeof(buffer) - 1);
|
||||
if (res <= 0)
|
||||
return 0;
|
||||
buffer[res] = '\0';
|
||||
res = sscanf(buffer, "2.6.%d", &major);
|
||||
if (res != 1)
|
||||
return 0;
|
||||
|
||||
switch(major) {
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
return 34;
|
||||
case 27:
|
||||
return 35;
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
return 36;
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
return 37;
|
||||
case 36:
|
||||
case 37:
|
||||
return 38;
|
||||
/* kernels .38 and later should handle this correctly so no
|
||||
* static mapping needed
|
||||
*/
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Yuck. We grab AF_* values to define above from linux/socket.h because
|
||||
* they are more accurate than sys/socket.h for what the kernel actually
|
||||
* supports. However, we can't just include linux/socket.h directly,
|
||||
* because the AF_* definitions are protected with an ifdef KERNEL
|
||||
* wrapper, but we don't want to define that because that can cause
|
||||
* other redefinitions from glibc. However, because the kernel may have
|
||||
* more definitions than glibc, we need make sure AF_MAX reflects this,
|
||||
* hence the wrapping function.
|
||||
*/
|
||||
size_t get_af_max() {
|
||||
size_t af_max;
|
||||
/* HACK: declare that version without "create" had a static AF_MAX */
|
||||
if (!perms_create && !net_af_max_override)
|
||||
net_af_max_override = -1;
|
||||
|
||||
#if AA_AF_MAX > AF_MAX
|
||||
af_max = AA_AF_MAX;
|
||||
#else
|
||||
af_max = AF_MAX;
|
||||
#endif
|
||||
|
||||
/* HACK: some kernels didn't handle network tables from parsers
|
||||
* compiled against newer kernel headers as they are larger than
|
||||
* the running kernel expected. If net_override is defined check
|
||||
* to see if there is a static max specified for that kernel
|
||||
*/
|
||||
if (net_af_max_override) {
|
||||
size_t max = kernel_af_max();
|
||||
if (max && max < af_max)
|
||||
return max;
|
||||
}
|
||||
|
||||
return af_max;
|
||||
}
|
||||
struct aa_network_entry *new_network_ent(unsigned int family,
|
||||
unsigned int type,
|
||||
unsigned int protocol)
|
||||
{
|
||||
struct aa_network_entry *new_entry;
|
||||
new_entry = (struct aa_network_entry *) calloc(1, sizeof(struct aa_network_entry));
|
||||
if (new_entry) {
|
||||
new_entry->family = family;
|
||||
new_entry->type = type;
|
||||
new_entry->protocol = protocol;
|
||||
new_entry->next = NULL;
|
||||
}
|
||||
return new_entry;
|
||||
}
|
||||
|
||||
|
||||
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
|
||||
const char *family,
|
||||
const char *type,
|
||||
const char *protocol)
|
||||
{
|
||||
if (!map)
|
||||
map = network_mappings;
|
||||
else
|
||||
/* assumes it points to last entry returned */
|
||||
map++;
|
||||
|
||||
for (; map->family_name; map++) {
|
||||
if (family) {
|
||||
PDEBUG("Checking family %s\n", map->family_name);
|
||||
if (strcmp(family, map->family_name) != 0)
|
||||
continue;
|
||||
PDEBUG("Found family %s\n", family);
|
||||
}
|
||||
if (type) {
|
||||
PDEBUG("Checking type %s\n", map->type_name);
|
||||
if (strcmp(type, map->type_name) != 0)
|
||||
continue;
|
||||
PDEBUG("Found type %s\n", type);
|
||||
}
|
||||
if (protocol) {
|
||||
/* allows the proto to be the "type", ie. tcp implies
|
||||
* stream */
|
||||
if (!type) {
|
||||
PDEBUG("Checking protocol type %s\n", map->type_name);
|
||||
if (strcmp(protocol, map->type_name) == 0)
|
||||
goto match;
|
||||
}
|
||||
PDEBUG("Checking type %s protocol %s\n", map->type_name, map->protocol_name);
|
||||
if (strcmp(protocol, map->protocol_name) != 0)
|
||||
continue;
|
||||
/* fixme should we allow specifying protocol by #
|
||||
* without needing the protocol mapping? */
|
||||
}
|
||||
|
||||
/* if we get this far we have a match */
|
||||
match:
|
||||
return map;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct aa_network_entry *network_entry(const char *family, const char *type,
|
||||
const char *protocol)
|
||||
{
|
||||
struct aa_network_entry *new_entry, *entry = NULL;
|
||||
const struct network_tuple *mapping = NULL;
|
||||
|
||||
while ((mapping = net_find_mapping(mapping, family, type, protocol))) {
|
||||
new_entry = new_network_ent(mapping->family, mapping->type,
|
||||
mapping->protocol);
|
||||
if (!new_entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
new_entry->next = entry;
|
||||
entry = new_entry;
|
||||
}
|
||||
|
||||
return entry;
|
||||
};
|
||||
|
||||
#define ALL_TYPES 0x43e
|
||||
|
||||
const char *net_find_af_name(unsigned int af)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (af < 0 || af > get_af_max())
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
|
||||
if (network_mappings[i].family == af)
|
||||
return network_mappings[i].family_name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void __debug_network(unsigned int *array, const char *name)
|
||||
{
|
||||
unsigned int count = sizeof(sock_types)/sizeof(sock_types[0]);
|
||||
unsigned int mask = ~((1 << count) -1);
|
||||
unsigned int i, j;
|
||||
int none = 1;
|
||||
size_t af_max = get_af_max();
|
||||
|
||||
for (i = AF_UNSPEC; i < af_max; i++)
|
||||
if (array[i]) {
|
||||
none = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (none)
|
||||
return;
|
||||
|
||||
printf("%s: ", name);
|
||||
|
||||
/* This can only be set by an unqualified network rule */
|
||||
if (array[AF_UNSPEC]) {
|
||||
printf("<all>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < af_max; i++) {
|
||||
if (array[i]) {
|
||||
const char *fam = net_find_af_name(i);
|
||||
if (fam)
|
||||
printf("%s ", fam);
|
||||
else
|
||||
printf("#%u ", i);
|
||||
|
||||
/* All types/protocols */
|
||||
if (array[i] == 0xffffffff || array[i] == ALL_TYPES)
|
||||
continue;
|
||||
|
||||
printf("{ ");
|
||||
|
||||
for (j = 0; j < count; j++) {
|
||||
const char *type;
|
||||
if (array[i] & (1 << j)) {
|
||||
type = sock_types[j].name;
|
||||
if (type)
|
||||
printf("%s ", type);
|
||||
else
|
||||
printf("#%u ", j);
|
||||
}
|
||||
}
|
||||
if (array[i] & mask)
|
||||
printf("#%x ", array[i] & mask);
|
||||
|
||||
printf("} ");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
783
parser/network.cc
Normal file
783
parser/network.cc
Normal file
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
* 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 <iomanip>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "network.h"
|
||||
|
||||
#define ALL_TYPES 0x43e
|
||||
|
||||
int parse_net_perms(const char *str_mode, perms_t *mode, int fail)
|
||||
{
|
||||
return parse_X_perms("net", AA_VALID_NET_PERMS, str_mode, mode, fail);
|
||||
}
|
||||
|
||||
/* Bleah C++ doesn't have non-trivial designated initializers so we just
|
||||
* have to make sure these are in order. This means we are more brittle
|
||||
* but there isn't much we can do.
|
||||
*/
|
||||
struct sock_type_map {
|
||||
const char *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct sock_type_map sock_types[] = {
|
||||
{ "none", 0 },
|
||||
{ "stream", SOCK_STREAM },
|
||||
{ "dgram", SOCK_DGRAM },
|
||||
{ "raw", SOCK_RAW },
|
||||
{ "rdm", SOCK_RDM },
|
||||
{ "seqpacket", SOCK_SEQPACKET },
|
||||
{ "dccp", SOCK_DCCP },
|
||||
{ "invalid", -1 },
|
||||
{ "invalid", -1 },
|
||||
{ "invalid", -1 },
|
||||
{ "packet", SOCK_PACKET },
|
||||
{ NULL, -1 },
|
||||
/*
|
||||
* See comment above
|
||||
*/
|
||||
};
|
||||
|
||||
int net_find_type_val(const char *type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; sock_types[i].name; i++) {
|
||||
if (strcmp(sock_types[i].name, type) == 0)
|
||||
return sock_types[i].value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *net_find_type_name(int type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; sock_types[i].name; i++) {
|
||||
if (sock_types[i].value == type)
|
||||
return sock_types[i].name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: currently just treating as a bit mask this will have to change
|
||||
* set up a table of mappings, there can be several mappings for a
|
||||
* given match.
|
||||
* currently the mapping does not set the protocol for stream/dgram to
|
||||
* anything other than 0.
|
||||
* network inet tcp -> network inet stream 0 instead of
|
||||
* network inet raw tcp.
|
||||
* some entries are just provided for completeness at this time
|
||||
*/
|
||||
/* values stolen from /etc/protocols - needs to change */
|
||||
#define RAW_TCP 6
|
||||
#define RAW_UDP 17
|
||||
#define RAW_ICMP 1
|
||||
#define RAW_ICMPv6 58
|
||||
|
||||
/* used by af_name.h to auto generate table entries for "name", AF_NAME
|
||||
* pair */
|
||||
#define AA_GEN_NET_ENT(name, AF) \
|
||||
{name, AF, "stream", SOCK_STREAM, "", 0xffffff}, \
|
||||
{name, AF, "dgram", SOCK_DGRAM, "", 0xffffff}, \
|
||||
{name, AF, "seqpacket", SOCK_SEQPACKET, "", 0xffffff}, \
|
||||
{name, AF, "rdm", SOCK_RDM, "", 0xffffff}, \
|
||||
{name, AF, "raw", SOCK_RAW, "", 0xffffff}, \
|
||||
{name, AF, "packet", SOCK_PACKET, "", 0xffffff},
|
||||
/*FIXME: missing {name, AF, "dccp", SOCK_DCCP, "", 0xfffffff}, */
|
||||
|
||||
static struct network_tuple network_mappings[] = {
|
||||
/* basic types */
|
||||
#include "af_names.h"
|
||||
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
|
||||
/* mapped types */
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"tcp", 1 << RAW_TCP},
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"udp", 1 << RAW_UDP},
|
||||
{"inet", AF_INET, "raw", SOCK_RAW,
|
||||
"icmp", 1 << RAW_ICMP},
|
||||
{"inet", AF_INET, "tcp", SOCK_STREAM,
|
||||
"", 0xffffffff}, /* should we give raw tcp too? */
|
||||
{"inet", AF_INET, "udp", SOCK_DGRAM,
|
||||
"", 0xffffffff}, /* should these be open masks? */
|
||||
{"inet", AF_INET, "icmp", SOCK_RAW,
|
||||
"", 1 << RAW_ICMP},
|
||||
{"inet6", AF_INET6, "tcp", SOCK_STREAM,
|
||||
"", 0xffffffff},
|
||||
{"inet6", AF_INET6, "udp", SOCK_DGRAM,
|
||||
"", 0xffffffff},
|
||||
/* what do we do with icmp on inet6?
|
||||
{"inet6", AF_INET, "icmp", SOCK_RAW, 0},
|
||||
{"inet6", AF_INET, "icmpv6", SOCK_RAW, 0},
|
||||
*/
|
||||
/* terminate */
|
||||
{NULL, 0, NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
|
||||
* tables with sizes > AF_MAX correctly. This could happen when the
|
||||
* parser was built against newer kernel headers and then used to load
|
||||
* policy on an older kernel. This could happen during upgrades or
|
||||
* in multi-kernel boot systems.
|
||||
*
|
||||
* Try to detect the running kernel version and use that to determine
|
||||
* AF_MAX
|
||||
*/
|
||||
#define PROC_VERSION "/proc/sys/kernel/osrelease"
|
||||
static size_t kernel_af_max(void) {
|
||||
char buffer[32];
|
||||
int major;
|
||||
autoclose int fd = -1;
|
||||
int res;
|
||||
|
||||
if (!net_af_max_override) {
|
||||
return 0;
|
||||
}
|
||||
/* the override parameter is specifying the max value */
|
||||
if (net_af_max_override > 0)
|
||||
return net_af_max_override;
|
||||
|
||||
fd = open(PROC_VERSION, O_RDONLY);
|
||||
if (fd == -1)
|
||||
/* fall back to default provided during build */
|
||||
return 0;
|
||||
res = read(fd, &buffer, sizeof(buffer) - 1);
|
||||
if (res <= 0)
|
||||
return 0;
|
||||
buffer[res] = '\0';
|
||||
res = sscanf(buffer, "2.6.%d", &major);
|
||||
if (res != 1)
|
||||
return 0;
|
||||
|
||||
switch(major) {
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
return 34;
|
||||
case 27:
|
||||
return 35;
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
return 36;
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
return 37;
|
||||
case 36:
|
||||
case 37:
|
||||
return 38;
|
||||
/* kernels .38 and later should handle this correctly so no
|
||||
* static mapping needed
|
||||
*/
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Yuck. We grab AF_* values to define above from linux/socket.h because
|
||||
* they are more accurate than sys/socket.h for what the kernel actually
|
||||
* supports. However, we can't just include linux/socket.h directly,
|
||||
* because the AF_* definitions are protected with an ifdef KERNEL
|
||||
* wrapper, but we don't want to define that because that can cause
|
||||
* other redefinitions from glibc. However, because the kernel may have
|
||||
* more definitions than glibc, we need make sure AF_MAX reflects this,
|
||||
* hence the wrapping function.
|
||||
*/
|
||||
size_t get_af_max() {
|
||||
size_t af_max;
|
||||
/* HACK: declare that version without "create" had a static AF_MAX */
|
||||
if (!perms_create && !net_af_max_override)
|
||||
net_af_max_override = -1;
|
||||
|
||||
#if AA_AF_MAX > AF_MAX
|
||||
af_max = AA_AF_MAX;
|
||||
#else
|
||||
af_max = AF_MAX;
|
||||
#endif
|
||||
|
||||
/* HACK: some kernels didn't handle network tables from parsers
|
||||
* compiled against newer kernel headers as they are larger than
|
||||
* the running kernel expected. If net_override is defined check
|
||||
* to see if there is a static max specified for that kernel
|
||||
*/
|
||||
if (net_af_max_override) {
|
||||
size_t max = kernel_af_max();
|
||||
if (max && max < af_max)
|
||||
return max;
|
||||
}
|
||||
|
||||
return af_max;
|
||||
}
|
||||
|
||||
const char *net_find_af_name(unsigned int af)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (af < 0 || af > get_af_max())
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
|
||||
if (network_mappings[i].family == af)
|
||||
return network_mappings[i].family_name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
|
||||
const char *family,
|
||||
const char *type,
|
||||
const char *protocol)
|
||||
{
|
||||
if (!map)
|
||||
map = network_mappings;
|
||||
else
|
||||
/* assumes it points to last entry returned */
|
||||
map++;
|
||||
|
||||
for (; map->family_name; map++) {
|
||||
if (family) {
|
||||
PDEBUG("Checking family %s\n", map->family_name);
|
||||
if (strcmp(family, map->family_name) != 0)
|
||||
continue;
|
||||
PDEBUG("Found family %s\n", family);
|
||||
}
|
||||
if (type) {
|
||||
PDEBUG("Checking type %s\n", map->type_name);
|
||||
if (strcmp(type, map->type_name) != 0)
|
||||
continue;
|
||||
PDEBUG("Found type %s\n", type);
|
||||
}
|
||||
if (protocol) {
|
||||
/* allows the proto to be the "type", ie. tcp implies
|
||||
* stream */
|
||||
if (!type) {
|
||||
PDEBUG("Checking protocol type %s\n", map->type_name);
|
||||
if (strcmp(protocol, map->type_name) == 0)
|
||||
goto match;
|
||||
}
|
||||
PDEBUG("Checking type %s protocol %s\n", map->type_name, map->protocol_name);
|
||||
if (strcmp(protocol, map->protocol_name) != 0)
|
||||
continue;
|
||||
/* fixme should we allow specifying protocol by #
|
||||
* without needing the protocol mapping? */
|
||||
}
|
||||
|
||||
/* if we get this far we have a match */
|
||||
match:
|
||||
return map;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool parse_ipv4_address(const char *input, struct ip_address *result)
|
||||
{
|
||||
struct in_addr addr;
|
||||
if (inet_pton(AF_INET, input, &addr) == 1) {
|
||||
result->family = AF_INET;
|
||||
result->address.address_v4 = addr.s_addr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_ipv6_address(const char *input, struct ip_address *result)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
if (inet_pton(AF_INET6, input, &addr) == 1) {
|
||||
result->family = AF_INET6;
|
||||
memcpy(result->address.address_v6, addr.s6_addr, 16);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_ip(const char *ip, struct ip_address *result)
|
||||
{
|
||||
return parse_ipv6_address(ip, result) ||
|
||||
parse_ipv4_address(ip, result);
|
||||
}
|
||||
|
||||
bool parse_port_number(const char *port_entry, uint16_t *port) {
|
||||
char *eptr;
|
||||
unsigned long port_tmp = strtoul(port_entry, &eptr, 10);
|
||||
|
||||
if (port_tmp >= 0 && port_entry != eptr &&
|
||||
*eptr == '\0' && port_tmp <= UINT16_MAX) {
|
||||
*port = port_tmp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool network_rule::parse_port(ip_conds &entry)
|
||||
{
|
||||
entry.is_port = true;
|
||||
return parse_port_number(entry.sport, &entry.port);
|
||||
}
|
||||
|
||||
bool network_rule::parse_address(ip_conds &entry)
|
||||
{
|
||||
entry.is_ip = true;
|
||||
return parse_ip(entry.sip, &entry.ip);
|
||||
}
|
||||
|
||||
void network_rule::move_conditionals(struct cond_entry *conds, ip_conds &ip_cond)
|
||||
{
|
||||
struct cond_entry *cond_ent;
|
||||
|
||||
list_for_each(conds, cond_ent) {
|
||||
/* for now disallow keyword 'in' (list) */
|
||||
if (!cond_ent->eq)
|
||||
yyerror("keyword \"in\" is not allowed in network rules\n");
|
||||
if (strcmp(cond_ent->name, "ip") == 0) {
|
||||
move_conditional_value("network", &ip_cond.sip, cond_ent);
|
||||
if (!parse_address(ip_cond))
|
||||
yyerror("network invalid ip='%s'\n", ip_cond.sip);
|
||||
} else if (strcmp(cond_ent->name, "port") == 0) {
|
||||
move_conditional_value("network", &ip_cond.sport, cond_ent);
|
||||
if (!parse_port(ip_cond))
|
||||
yyerror("network invalid port='%s'\n", ip_cond.sport);
|
||||
} else {
|
||||
yyerror("invalid network rule conditional \"%s\"\n",
|
||||
cond_ent->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void network_rule::set_netperm(unsigned int family, unsigned int type)
|
||||
{
|
||||
if (type > SOCK_PACKET) {
|
||||
/* setting mask instead of a bit */
|
||||
network_perms[family] |= type;
|
||||
} else
|
||||
network_perms[family] |= 1 << type;
|
||||
}
|
||||
|
||||
network_rule::network_rule(perms_t perms_p, struct cond_entry *conds,
|
||||
struct cond_entry *peer_conds):
|
||||
dedup_perms_rule_t(AA_CLASS_NETV8)
|
||||
{
|
||||
size_t family_index;
|
||||
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
|
||||
network_map[family_index].push_back({ family_index, 0xFFFFFFFF, 0xFFFFFFFF });
|
||||
set_netperm(family_index, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
move_conditionals(conds, local);
|
||||
move_conditionals(peer_conds, peer);
|
||||
free_cond_list(conds);
|
||||
free_cond_list(peer_conds);
|
||||
|
||||
if (perms_p) {
|
||||
perms = perms_p;
|
||||
if (perms & ~AA_VALID_NET_PERMS)
|
||||
yyerror("perms contains invalid permissions for network rules\n");
|
||||
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
||||
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
||||
} else {
|
||||
perms = AA_VALID_NET_PERMS;
|
||||
}
|
||||
}
|
||||
|
||||
network_rule::network_rule(perms_t perms_p, const char *family, const char *type,
|
||||
const char *protocol, struct cond_entry *conds,
|
||||
struct cond_entry *peer_conds):
|
||||
dedup_perms_rule_t(AA_CLASS_NETV8)
|
||||
{
|
||||
const struct network_tuple *mapping = NULL;
|
||||
while ((mapping = net_find_mapping(mapping, family, type, protocol))) {
|
||||
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
|
||||
set_netperm(mapping->family, mapping->type);
|
||||
}
|
||||
|
||||
if (type == NULL && network_map.empty()) {
|
||||
while ((mapping = net_find_mapping(mapping, type, family, protocol))) {
|
||||
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
|
||||
set_netperm(mapping->family, mapping->type);
|
||||
}
|
||||
}
|
||||
|
||||
if (network_map.empty())
|
||||
yyerror(_("Invalid network entry."));
|
||||
|
||||
move_conditionals(conds, local);
|
||||
move_conditionals(peer_conds, peer);
|
||||
free_cond_list(conds);
|
||||
free_cond_list(peer_conds);
|
||||
|
||||
if (perms_p) {
|
||||
perms = perms_p;
|
||||
if (perms & ~AA_VALID_NET_PERMS)
|
||||
yyerror("perms contains invalid permissions for network rules\n");
|
||||
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
||||
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
||||
} else {
|
||||
perms = AA_VALID_NET_PERMS;
|
||||
}
|
||||
}
|
||||
|
||||
network_rule::network_rule(perms_t perms_p, unsigned int family, unsigned int type):
|
||||
dedup_perms_rule_t(AA_CLASS_NETV8)
|
||||
{
|
||||
network_map[family].push_back({ family, type, 0xFFFFFFFF });
|
||||
set_netperm(family, type);
|
||||
|
||||
if (perms_p) {
|
||||
perms = perms_p;
|
||||
if (perms & ~AA_VALID_NET_PERMS)
|
||||
yyerror("perms contains invalid permissions for network rules\n");
|
||||
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
||||
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
||||
} else {
|
||||
perms = AA_VALID_NET_PERMS;
|
||||
}
|
||||
}
|
||||
|
||||
ostream &network_rule::dump(ostream &os)
|
||||
{
|
||||
class_rule_t::dump(os);
|
||||
|
||||
unsigned int count = sizeof(sock_types)/sizeof(sock_types[0]);
|
||||
unsigned int mask = ~((1 << count) -1);
|
||||
unsigned int j;
|
||||
|
||||
/* This can only be set by an unqualified network rule */
|
||||
if (network_map.find(AF_UNSPEC) != network_map.end()) {
|
||||
os << ",\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
for (const auto& perm : network_perms) {
|
||||
unsigned int family = perm.first;
|
||||
unsigned int type = perm.second;
|
||||
|
||||
const char *family_name = net_find_af_name(family);
|
||||
if (family_name)
|
||||
os << " " << family_name;
|
||||
else
|
||||
os << " #" << family;
|
||||
|
||||
/* All types/protocols */
|
||||
if (type == 0xffffffff || type == ALL_TYPES)
|
||||
continue;
|
||||
|
||||
printf(" {");
|
||||
|
||||
for (j = 0; j < count; j++) {
|
||||
const char *type_name;
|
||||
if (type & (1 << j)) {
|
||||
type_name = sock_types[j].name;
|
||||
if (type_name)
|
||||
os << " " << type_name;
|
||||
else
|
||||
os << " #" << j;
|
||||
}
|
||||
}
|
||||
if (type & mask)
|
||||
os << " #" << std::hex << (type & mask);
|
||||
|
||||
printf(" }");
|
||||
}
|
||||
|
||||
os << ",\n";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
int network_rule::expand_variables(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void network_rule::warn_once(const char *name)
|
||||
{
|
||||
rule_t::warn_once(name, "network rules not enforced");
|
||||
}
|
||||
|
||||
std::string gen_ip_cond(const struct ip_address ip)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
int i;
|
||||
if (ip.family == AF_INET) {
|
||||
/* add a byte containing the size of the following ip */
|
||||
oss << "\\x04";
|
||||
|
||||
u8 *byte = (u8 *) &ip.address.address_v4; /* in network byte order */
|
||||
for (i = 0; i < 4; i++)
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned int>(byte[i]);
|
||||
} else {
|
||||
/* add a byte containing the size of the following ip */
|
||||
oss << "\\x10";
|
||||
for (i = 0; i < 16; ++i)
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned int>(ip.address.address_v6[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string gen_port_cond(uint16_t port)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if (port > 0) {
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((port & 0xff00) >> 8);
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (port & 0xff);
|
||||
} else {
|
||||
oss << "..";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void network_rule::gen_ip_conds(std::ostringstream &oss, ip_conds entry, bool is_peer, bool is_cmd)
|
||||
{
|
||||
/* encode protocol */
|
||||
if (!is_cmd) {
|
||||
if (entry.is_ip) {
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((entry.ip.family & 0xff00) >> 8);
|
||||
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (entry.ip.family & 0xff);
|
||||
} else {
|
||||
oss << "..";
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.is_port) {
|
||||
/* encode port type (privileged - 1, remote - 2, unprivileged - 0) */
|
||||
if (!is_peer && perms & AA_NET_BIND && entry.port < IPPORT_RESERVED)
|
||||
oss << "\\x01";
|
||||
else if (is_peer)
|
||||
oss << "\\x02";
|
||||
else
|
||||
oss << "\\x00";
|
||||
|
||||
oss << gen_port_cond(entry.port);
|
||||
} else {
|
||||
/* port type + port number */
|
||||
if (!is_cmd)
|
||||
oss << ".";
|
||||
oss << "..";
|
||||
}
|
||||
|
||||
if (entry.is_ip) {
|
||||
oss << gen_ip_cond(entry.ip);
|
||||
} else {
|
||||
/* encode 0 to indicate there's no ip (ip size) */
|
||||
oss << "\\x00";
|
||||
}
|
||||
|
||||
oss << "\\-x01"; /* oob separator */
|
||||
oss << default_match_pattern; /* label - not used for now */
|
||||
oss << "\\x00"; /* null transition */
|
||||
}
|
||||
|
||||
bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mask) {
|
||||
std::ostringstream buffer;
|
||||
std::string buf;
|
||||
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NETV8;
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((family & 0xff00) >> 8);
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (family & 0xff);
|
||||
if (type_mask > 0xffff) {
|
||||
buffer << "..";
|
||||
} else {
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((type_mask & 0xff00) >> 8);
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
|
||||
}
|
||||
|
||||
if (!features_supports_inet) {
|
||||
buf = buffer.str();
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(AA_VALID_NET_PERMS),
|
||||
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(AA_VALID_NET_PERMS) : 0,
|
||||
parseopts))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (perms & AA_PEER_NET_PERMS) {
|
||||
gen_ip_conds(buffer, peer, true, false);
|
||||
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_ADDR;
|
||||
|
||||
gen_ip_conds(buffer, local, false, true);
|
||||
|
||||
buf = buffer.str();
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms),
|
||||
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
||||
parseopts))
|
||||
return false;
|
||||
}
|
||||
if ((perms & AA_NET_LISTEN) || (perms & AA_NET_OPT)) {
|
||||
gen_ip_conds(buffer, local, false, false);
|
||||
|
||||
if (perms & AA_NET_LISTEN) {
|
||||
std::ostringstream cmd_buffer;
|
||||
cmd_buffer << buffer.str();
|
||||
cmd_buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_LISTEN;
|
||||
/* length of queue allowed - not used for now */
|
||||
cmd_buffer << "..";
|
||||
buf = cmd_buffer.str();
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms),
|
||||
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
||||
parseopts))
|
||||
return false;
|
||||
}
|
||||
if (perms & AA_NET_OPT) {
|
||||
std::ostringstream cmd_buffer;
|
||||
cmd_buffer << buffer.str();
|
||||
cmd_buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_OPT;
|
||||
/* level - not used for now */
|
||||
cmd_buffer << "..";
|
||||
/* socket mapping - not used for now */
|
||||
cmd_buffer << "..";
|
||||
buf = cmd_buffer.str();
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms),
|
||||
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
||||
parseopts))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int network_rule::gen_policy_re(Profile &prof)
|
||||
{
|
||||
std::ostringstream buffer;
|
||||
std::string buf;
|
||||
|
||||
if (!features_supports_networkv8) {
|
||||
warn_once(prof.name);
|
||||
return RULE_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
for (const auto& perm : network_perms) {
|
||||
unsigned int family = perm.first;
|
||||
unsigned int type = perm.second;
|
||||
|
||||
if (type > 0xffff) {
|
||||
if (!gen_net_rule(prof, family, type))
|
||||
goto fail;
|
||||
} else {
|
||||
int t;
|
||||
/* generate rules for types that are set */
|
||||
for (t = 0; t < 16; t++) {
|
||||
if (type & (1 << t)) {
|
||||
if (!gen_net_rule(prof, family, t))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return RULE_OK;
|
||||
|
||||
fail:
|
||||
return RULE_ERROR;
|
||||
|
||||
}
|
||||
|
||||
/* initialize static members */
|
||||
unsigned int *network_rule::allow = NULL;
|
||||
unsigned int *network_rule::audit = NULL;
|
||||
unsigned int *network_rule::deny = NULL;
|
||||
unsigned int *network_rule::quiet = NULL;
|
||||
|
||||
bool network_rule::alloc_net_table()
|
||||
{
|
||||
if (allow)
|
||||
return true;
|
||||
allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
if (!allow || !audit || !deny || !quiet)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* update is required because at the point of the creation of the
|
||||
* network_rule object, we don't have owner, rule_mode, or audit
|
||||
* set.
|
||||
*/
|
||||
void network_rule::update_compat_net(void)
|
||||
{
|
||||
if (!alloc_net_table())
|
||||
yyerror(_("Memory allocation error."));
|
||||
|
||||
for (auto& nm: network_map) {
|
||||
for (auto& entry : nm.second) {
|
||||
if (entry.type > SOCK_PACKET) {
|
||||
/* setting mask instead of a bit */
|
||||
if (rule_mode == RULE_DENY) {
|
||||
deny[entry.family] |= entry.type;
|
||||
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
|
||||
quiet[entry.family] |= entry.type;
|
||||
} else {
|
||||
allow[entry.family] |= entry.type;
|
||||
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
|
||||
audit[entry.family] |= entry.type;
|
||||
}
|
||||
} else {
|
||||
if (rule_mode == RULE_DENY) {
|
||||
deny[entry.family] |= 1 << entry.type;
|
||||
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
|
||||
quiet[entry.family] |= 1 << entry.type;
|
||||
} else {
|
||||
allow[entry.family] |= 1 << entry.type;
|
||||
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
|
||||
audit[entry.family] |= 1 << entry.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int cmp_network_map(std::unordered_map<unsigned int, perms_t> lhs,
|
||||
std::unordered_map<unsigned int, perms_t> rhs)
|
||||
{
|
||||
int res;
|
||||
size_t family_index;
|
||||
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
|
||||
res = lhs[family_index] - rhs[family_index];
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int network_rule::cmp(rule_t const &rhs) const
|
||||
{
|
||||
int res = dedup_perms_rule_t::cmp(rhs);
|
||||
if (res)
|
||||
return res;
|
||||
network_rule const &nrhs = rule_cast<network_rule const &>(rhs);
|
||||
return cmp_network_map(network_perms, nrhs.network_perms);
|
||||
};
|
152
parser/network.h
152
parser/network.h
@@ -29,6 +29,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "parser.h"
|
||||
#include "rule.h"
|
||||
@@ -73,6 +75,10 @@
|
||||
#define AA_PEER_NET_PERMS (AA_VALID_NET_PERMS & (~AA_LOCAL_NET_PERMS | \
|
||||
AA_NET_ACCEPT))
|
||||
|
||||
#define CMD_ADDR 1
|
||||
#define CMD_LISTEN 2
|
||||
#define CMD_OPT 4
|
||||
|
||||
struct network_tuple {
|
||||
const char *family_name;
|
||||
unsigned int family;
|
||||
@@ -82,13 +88,10 @@ struct network_tuple {
|
||||
unsigned int protocol;
|
||||
};
|
||||
|
||||
/* supported AF protocols */
|
||||
struct aa_network_entry {
|
||||
unsigned int family;
|
||||
long unsigned int family;
|
||||
unsigned int type;
|
||||
unsigned int protocol;
|
||||
|
||||
struct aa_network_entry *next;
|
||||
};
|
||||
|
||||
static inline uint32_t map_perms(uint32_t mask)
|
||||
@@ -99,45 +102,114 @@ static inline uint32_t map_perms(uint32_t mask)
|
||||
((mask & (AA_NET_SETOPT | AA_NET_GETOPT)) >> 5); /* 5 + (AA_OTHER_SHIFT - 24) */
|
||||
};
|
||||
|
||||
|
||||
int parse_net_perms(const char *str_mode, perms_t *perms, int fail);
|
||||
extern struct aa_network_entry *new_network_ent(unsigned int family,
|
||||
unsigned int type,
|
||||
unsigned int protocol);
|
||||
extern struct aa_network_entry *network_entry(const char *family,
|
||||
const char *type,
|
||||
const char *protocol);
|
||||
extern size_t get_af_max(void);
|
||||
|
||||
void __debug_network(unsigned int *array, const char *name);
|
||||
|
||||
struct network {
|
||||
unsigned int *allow; /* array of type masks
|
||||
* indexed by AF_FAMILY */
|
||||
unsigned int *audit;
|
||||
unsigned int *deny;
|
||||
unsigned int *quiet;
|
||||
|
||||
network(void) { allow = audit = deny = quiet = NULL; }
|
||||
|
||||
void dump(void) {
|
||||
if (allow)
|
||||
__debug_network(allow, "Network");
|
||||
if (audit)
|
||||
__debug_network(audit, "Audit Net");
|
||||
if (deny)
|
||||
__debug_network(deny, "Deny Net");
|
||||
if (quiet)
|
||||
__debug_network(quiet, "Quiet Net");
|
||||
}
|
||||
};
|
||||
|
||||
size_t get_af_max();
|
||||
int net_find_type_val(const char *type);
|
||||
const char *net_find_type_name(int type);
|
||||
const char *net_find_af_name(unsigned int af);
|
||||
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
|
||||
const char *family,
|
||||
const char *type,
|
||||
const char *protocol);
|
||||
|
||||
struct ip_address {
|
||||
union {
|
||||
uint8_t address_v6[16];
|
||||
uint32_t address_v4;
|
||||
} address;
|
||||
uint16_t family;
|
||||
};
|
||||
|
||||
class ip_conds {
|
||||
public:
|
||||
char *sip = NULL;
|
||||
char *sport = NULL;
|
||||
|
||||
bool is_ip = false;
|
||||
bool is_port = false;
|
||||
|
||||
uint16_t port;
|
||||
struct ip_address ip;
|
||||
|
||||
void free_conds() {
|
||||
if (sip)
|
||||
free(sip);
|
||||
if (sport)
|
||||
free(sport);
|
||||
}
|
||||
};
|
||||
|
||||
class network_rule: public dedup_perms_rule_t {
|
||||
void move_conditionals(struct cond_entry *conds, ip_conds &ip_cond);
|
||||
public:
|
||||
std::unordered_map<unsigned int, std::vector<struct aa_network_entry>> network_map;
|
||||
std::unordered_map<unsigned int, perms_t> network_perms;
|
||||
|
||||
ip_conds peer;
|
||||
ip_conds local;
|
||||
|
||||
bool has_local_conds(void) { return local.sip || local.sport; }
|
||||
bool has_peer_conds(void) { return peer.sip || peer.sport; }
|
||||
/* empty constructor used only for the profile to access
|
||||
* static elements to maintain compatibility with
|
||||
* AA_CLASS_NET */
|
||||
network_rule(): dedup_perms_rule_t(AA_CLASS_NETV8) { }
|
||||
network_rule(perms_t perms_p, struct cond_entry *conds,
|
||||
struct cond_entry *peer_conds);
|
||||
network_rule(perms_t perms_p, const char *family, const char *type,
|
||||
const char *protocol, struct cond_entry *conds,
|
||||
struct cond_entry *peer_conds);
|
||||
network_rule(perms_t perms_p, unsigned int family, unsigned int type);
|
||||
virtual ~network_rule()
|
||||
{
|
||||
peer.free_conds();
|
||||
local.free_conds();
|
||||
if (allow) {
|
||||
free(allow);
|
||||
allow = NULL;
|
||||
}
|
||||
if (audit) {
|
||||
free(audit);
|
||||
audit = NULL;
|
||||
}
|
||||
if (deny) {
|
||||
free(deny);
|
||||
deny = NULL;
|
||||
}
|
||||
if (quiet) {
|
||||
free(quiet);
|
||||
quiet = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
void gen_ip_conds(std::ostringstream &oss, ip_conds entry, bool is_peer, bool is_cmd);
|
||||
bool gen_net_rule(Profile &prof, u16 family, unsigned int type_mask);
|
||||
void set_netperm(unsigned int family, unsigned int type);
|
||||
void update_compat_net(void);
|
||||
bool parse_address(ip_conds &entry);
|
||||
bool parse_port(ip_conds &entry);
|
||||
|
||||
virtual bool valid_prefix(const prefixes &p, const char *&error) {
|
||||
if (p.owner) {
|
||||
error = _("owner prefix not allowed on network rules");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
virtual ostream &dump(ostream &os);
|
||||
virtual int expand_variables(void);
|
||||
virtual int gen_policy_re(Profile &prof);
|
||||
|
||||
virtual bool is_mergeable(void) { return true; }
|
||||
virtual int cmp(rule_t const &rhs) const;
|
||||
|
||||
/* array of type masks indexed by AF_FAMILY */
|
||||
/* allow, audit, deny and quiet are used for compatibility with AA_CLASS_NET */
|
||||
static unsigned int *allow;
|
||||
static unsigned int *audit;
|
||||
static unsigned int *deny;
|
||||
static unsigned int *quiet;
|
||||
|
||||
bool alloc_net_table(void);
|
||||
|
||||
protected:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
||||
#endif /* __AA_NETWORK_H */
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
#include "rule.h"
|
||||
#include "bignum.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -340,6 +341,7 @@ extern int kernel_load;
|
||||
extern int kernel_supports_setload;
|
||||
extern int features_supports_network;
|
||||
extern int features_supports_networkv8;
|
||||
extern int features_supports_inet;
|
||||
extern int kernel_supports_policydb;
|
||||
extern int kernel_supports_diff_encode;
|
||||
extern int features_supports_mount;
|
||||
@@ -353,6 +355,8 @@ extern int features_supports_userns;
|
||||
extern int features_supports_posix_mqueue;
|
||||
extern int features_supports_sysv_mqueue;
|
||||
extern int features_supports_io_uring;
|
||||
extern int features_supports_flag_interruptible;
|
||||
extern int features_supports_flag_signal;
|
||||
extern int kernel_supports_oob;
|
||||
extern int conf_verbose;
|
||||
extern int conf_quiet;
|
||||
@@ -409,6 +413,7 @@ extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
||||
extern int build_list_val_expr(std::string& buffer, struct value_list *list);
|
||||
extern int convert_entry(std::string& buffer, char *entry);
|
||||
extern int clear_and_convert_entry(std::string& buffer, char *entry);
|
||||
extern int convert_range(std::string& buffer, bignum start, bignum end);
|
||||
extern int process_regex(Profile *prof);
|
||||
extern int post_process_entry(struct cod_entry *entry);
|
||||
|
||||
@@ -457,6 +462,9 @@ extern bool strcomp (const char *lhs, const char *rhs);
|
||||
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
|
||||
extern void free_cod_entries(struct cod_entry *list);
|
||||
void debug_cod_entries(struct cod_entry *list);
|
||||
bool check_x_qualifier(struct cod_entry *entry, const char *&error);
|
||||
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error);
|
||||
|
||||
|
||||
#define SECONDS_P_MS (1000LL * 1000LL)
|
||||
long long convert_time_units(long long value, long long base, const char *units);
|
||||
|
@@ -69,6 +69,7 @@ int kernel_load = 1;
|
||||
int kernel_supports_setload = 0; /* kernel supports atomic set loads */
|
||||
int features_supports_network = 0; /* kernel supports network rules */
|
||||
int features_supports_networkv8 = 0; /* kernel supports 4.17 network rules */
|
||||
int features_supports_inet = 0; /* kernel supports inet network rules */
|
||||
int features_supports_unix = 0; /* kernel supports unix socket rules */
|
||||
int kernel_supports_policydb = 0; /* kernel supports new policydb */
|
||||
int features_supports_mount = 0; /* kernel supports mount rules */
|
||||
@@ -82,6 +83,8 @@ int features_supports_userns = 0; /* kernel supports user namespace */
|
||||
int features_supports_posix_mqueue = 0; /* kernel supports mqueue rules */
|
||||
int features_supports_sysv_mqueue = 0; /* kernel supports mqueue rules */
|
||||
int features_supports_io_uring = 0; /* kernel supports io_uring rules */
|
||||
int features_supports_flag_interruptible = 0;
|
||||
int features_supports_flag_signal = 0;
|
||||
int kernel_supports_oob = 0; /* out of band transitions */
|
||||
int conf_verbose = 0;
|
||||
int conf_quiet = 0;
|
||||
|
@@ -276,7 +276,7 @@ static inline void sd_write_aligned_blob(std::ostringstream &buf, void *b, int b
|
||||
buf.write((const char *) b, b_size);
|
||||
}
|
||||
|
||||
static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char *name)
|
||||
static void sd_write_strn(std::ostringstream &buf, const char *b, int size, const char *name)
|
||||
{
|
||||
sd_write_name(buf, name);
|
||||
sd_write8(buf, SD_STRING);
|
||||
@@ -284,7 +284,7 @@ static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char
|
||||
buf.write(b, size);
|
||||
}
|
||||
|
||||
static inline void sd_write_string(std::ostringstream &buf, char *b, const char *name)
|
||||
static inline void sd_write_string(std::ostringstream &buf, const char *b, const char *name)
|
||||
{
|
||||
sd_write_strn(buf, b, strlen(b) + 1, name);
|
||||
}
|
||||
@@ -403,11 +403,7 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
|
||||
sd_write_struct(buf, "profile");
|
||||
if (flattened) {
|
||||
assert(profile->parent);
|
||||
autofree char *name = (char *) malloc(3 + strlen(profile->name) + strlen(profile->parent->name));
|
||||
if (!name)
|
||||
return;
|
||||
sprintf(name, "%s//%s", profile->parent->name, profile->name);
|
||||
sd_write_string(buf, name, NULL);
|
||||
sd_write_string(buf, profile->get_name(false).c_str(), NULL);
|
||||
} else {
|
||||
sd_write_string(buf, profile->name, NULL);
|
||||
}
|
||||
@@ -426,6 +422,10 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
|
||||
"disconnected");
|
||||
}
|
||||
|
||||
if (profile->flags.signal && features_supports_flag_signal) {
|
||||
sd_write_name(buf, "kill");
|
||||
sd_write_uint32(buf, profile->flags.signal);
|
||||
}
|
||||
sd_write_struct(buf, "flags");
|
||||
/* used to be flags.debug, but that's no longer supported */
|
||||
sd_write_uint32(buf, profile->flags.flags);
|
||||
|
@@ -378,7 +378,7 @@ GT >
|
||||
yyterminate();
|
||||
}
|
||||
|
||||
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
|
||||
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE,NETWORK_MODE>{
|
||||
(peer|xattrs)/{WS}*={WS}*\( {
|
||||
/* we match to the = in the lexer so that we can switch scanner
|
||||
* state. By the time the parser see the = it may be too late
|
||||
@@ -517,12 +517,6 @@ GT >
|
||||
}
|
||||
}
|
||||
|
||||
<NETWORK_MODE>{
|
||||
{IDS} {
|
||||
yylval.id = strdup(yytext);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
}
|
||||
}
|
||||
|
||||
<CHANGE_PROFILE_MODE>{
|
||||
safe { RETURN_TOKEN(TOK_SAFE); }
|
||||
@@ -558,7 +552,7 @@ GT >
|
||||
{LT_EQUAL} { RETURN_TOKEN(TOK_LE); }
|
||||
}
|
||||
|
||||
<UNIX_MODE>{
|
||||
<UNIX_MODE,NETWORK_MODE>{
|
||||
listen { RETURN_TOKEN(TOK_LISTEN); }
|
||||
accept { RETURN_TOKEN(TOK_ACCEPT); }
|
||||
connect { RETURN_TOKEN(TOK_CONNECT); }
|
||||
@@ -567,7 +561,7 @@ GT >
|
||||
shutdown { RETURN_TOKEN(TOK_SHUTDOWN); }
|
||||
}
|
||||
|
||||
<UNIX_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||
<UNIX_MODE,USERNS_MODE,MQUEUE_MODE,NETWORK_MODE>{
|
||||
create { RETURN_TOKEN(TOK_CREATE); }
|
||||
}
|
||||
|
||||
@@ -576,12 +570,12 @@ GT >
|
||||
delete { RETURN_TOKEN(TOK_DELETE); }
|
||||
}
|
||||
|
||||
<UNIX_MODE,MQUEUE_MODE>{
|
||||
<UNIX_MODE,MQUEUE_MODE,NETWORK_MODE>{
|
||||
getattr { RETURN_TOKEN(TOK_GETATTR); }
|
||||
setattr { RETURN_TOKEN(TOK_SETATTR); }
|
||||
}
|
||||
|
||||
<DBUS_MODE,UNIX_MODE>{
|
||||
<DBUS_MODE,UNIX_MODE,NETWORK_MODE>{
|
||||
bind { RETURN_TOKEN(TOK_BIND); }
|
||||
}
|
||||
|
||||
@@ -589,7 +583,7 @@ GT >
|
||||
eavesdrop { RETURN_TOKEN(TOK_EAVESDROP); }
|
||||
}
|
||||
|
||||
<DBUS_MODE,SIGNAL_MODE,UNIX_MODE>{
|
||||
<DBUS_MODE,SIGNAL_MODE,UNIX_MODE,NETWORK_MODE>{
|
||||
send { RETURN_TOKEN(TOK_SEND); }
|
||||
receive { RETURN_TOKEN(TOK_RECEIVE); }
|
||||
}
|
||||
@@ -600,7 +594,7 @@ GT >
|
||||
tracedby { RETURN_TOKEN(TOK_TRACEDBY); }
|
||||
}
|
||||
|
||||
<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
|
||||
<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE,NETWORK_MODE>{
|
||||
read { RETURN_TOKEN(TOK_READ); }
|
||||
write { RETURN_TOKEN(TOK_WRITE); }
|
||||
{OPEN_PAREN} {
|
||||
@@ -621,7 +615,7 @@ GT >
|
||||
sqpoll { RETURN_TOKEN(TOK_SQPOLL); }
|
||||
}
|
||||
|
||||
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE,IOURING_MODE>{
|
||||
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE,IOURING_MODE,NETWORK_MODE>{
|
||||
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
@@ -658,6 +652,10 @@ include/{WS} {
|
||||
yy_push_state(INCLUDE);
|
||||
}
|
||||
|
||||
all/({WS}|[^[:alnum:]_]) {
|
||||
RETURN_TOKEN(TOK_ALL);
|
||||
}
|
||||
|
||||
#.*\r?\n { /* normal comment */
|
||||
DUMP_AND_DEBUG("comment(%d): %s\n", current_lineno, yytext);
|
||||
current_lineno++;
|
||||
|
@@ -919,6 +919,9 @@ void set_supported_features()
|
||||
features_supports_networkv8 = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"network_v8");
|
||||
features_supports_inet = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"network/af_inet");
|
||||
features_supports_unix = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"network/af_unix");
|
||||
@@ -951,6 +954,12 @@ void set_supported_features()
|
||||
features_supports_io_uring = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"io_uring");
|
||||
features_supports_flag_interruptible = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"policy/profile/interruptible");
|
||||
features_supports_flag_signal = features_intersect(kernel_features,
|
||||
policy_features,
|
||||
"policy/profile/kill.signal");
|
||||
}
|
||||
|
||||
static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path)
|
||||
|
@@ -129,6 +129,7 @@ static struct keyword_table keyword_table[] = {
|
||||
{"io_uring", TOK_IO_URING},
|
||||
{"override_creds", TOK_OVERRIDE_CREDS},
|
||||
{"sqpoll", TOK_SQPOLL},
|
||||
{"all", TOK_ALL},
|
||||
|
||||
/* terminate */
|
||||
{NULL, 0}
|
||||
@@ -1086,6 +1087,47 @@ void debug_cod_entries(struct cod_entry *list)
|
||||
}
|
||||
}
|
||||
|
||||
bool check_x_qualifier(struct cod_entry *entry, const char *&error)
|
||||
{
|
||||
if (entry->perms & AA_EXEC_BITS) {
|
||||
if ((entry->rule_mode == RULE_DENY) &&
|
||||
(entry->perms & ALL_AA_EXEC_TYPE)) {
|
||||
error = _("Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'");
|
||||
return false;
|
||||
} else if ((entry->rule_mode != RULE_DENY) &&
|
||||
!(entry->perms & ALL_AA_EXEC_TYPE)) {
|
||||
error = _("Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// cod_entry version of ->add_prefix here just as file rules aren't converted yet
|
||||
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error)
|
||||
{
|
||||
/* modifiers aren't correctly stored for cod_entries yet so
|
||||
* we can't conflict on them easily. Leave that until conversion
|
||||
* to rule_t
|
||||
*/
|
||||
/* apply rule mode */
|
||||
entry->rule_mode = p.rule_mode;
|
||||
|
||||
/* apply owner/other */
|
||||
if (p.owner == 1)
|
||||
entry->perms &= (AA_USER_PERMS | AA_SHARED_PERMS);
|
||||
else if (p.owner == 2)
|
||||
entry->perms &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
|
||||
|
||||
/* implied audit modifier */
|
||||
if (p.audit == AUDIT_FORCE && (entry->rule_mode != RULE_DENY))
|
||||
entry->audit = AUDIT_FORCE;
|
||||
else if (p.audit != AUDIT_FORCE && (entry->rule_mode == RULE_DENY))
|
||||
entry->audit = AUDIT_FORCE;
|
||||
|
||||
return check_x_qualifier(entry, error);
|
||||
}
|
||||
|
||||
// these need to move to stl
|
||||
int ordered_cmp_value_list(value_list *lhs, value_list *rhs)
|
||||
{
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
#include "parser_yacc.h"
|
||||
#include "network.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
#ifdef DEBUG
|
||||
|
@@ -845,6 +845,116 @@ int clear_and_convert_entry(std::string& buffer, char *entry)
|
||||
return convert_entry(buffer, entry);
|
||||
}
|
||||
|
||||
static std::vector<std::pair<bignum, bignum>> regex_range_generator(bignum start, bignum end)
|
||||
{
|
||||
std::vector<std::pair<bignum, bignum>> forward;
|
||||
std::vector<std::pair<bignum, bignum>> reverse;
|
||||
bignum next, prev;
|
||||
|
||||
while (start <= end) {
|
||||
next = bignum::upper_bound_regex(start);
|
||||
if (next > end)
|
||||
break;
|
||||
|
||||
forward.emplace_back(start, next);
|
||||
start = next + 1;
|
||||
}
|
||||
|
||||
while (!end.negative && end >= start) {
|
||||
prev = bignum::lower_bound_regex(end);
|
||||
if (prev < start || prev.negative)
|
||||
break;
|
||||
|
||||
reverse.emplace_back(prev, end);
|
||||
end = prev - 1;
|
||||
}
|
||||
|
||||
if (!end.negative && start <= end) {
|
||||
forward.emplace_back(start, end);
|
||||
}
|
||||
|
||||
forward.insert(forward.end(), reverse.rbegin(), reverse.rend());
|
||||
return forward;
|
||||
}
|
||||
|
||||
static std::string generate_regex_range(bignum start, bignum end)
|
||||
{
|
||||
std::ostringstream result;
|
||||
std::vector<std::pair<bignum, bignum>> regex_range;
|
||||
int j;
|
||||
regex_range = regex_range_generator(std::move(start), std::move(end));
|
||||
for (auto &i: regex_range) {
|
||||
bignum sstart = i.first;
|
||||
bignum send = i.second;
|
||||
if (sstart.base == 16) {
|
||||
for (j = (size_t) sstart.size(); j < 32; j++)
|
||||
result << '0';
|
||||
}
|
||||
for (j = sstart.size() - 1; j >= 0; j--) {
|
||||
result << std::nouppercase;
|
||||
if (sstart[j] == send[j]) {
|
||||
if (sstart[j] >= 10)
|
||||
result << '[';
|
||||
result << std::hex << sstart[j];
|
||||
if (sstart[j] >= 10)
|
||||
result << std::uppercase << std::hex << sstart[j] << ']';
|
||||
} else {
|
||||
if (sstart[j] < 10 && send[j] >= 10) {
|
||||
result << '[';
|
||||
result << std::hex << sstart[j];
|
||||
if (sstart[j] < 9) {
|
||||
result << '-';
|
||||
result << '9';
|
||||
}
|
||||
if (send[j] > 10) {
|
||||
result << 'a';
|
||||
result << '-';
|
||||
}
|
||||
result << std::hex << send[j];
|
||||
if (send[j] > 10) {
|
||||
result << 'A';
|
||||
result << '-';
|
||||
}
|
||||
result << std::uppercase << std::hex << send[j];
|
||||
result << ']';
|
||||
} else {
|
||||
result << '[';
|
||||
result << std::hex << sstart[j];
|
||||
result << '-';
|
||||
result << std::hex << send[j];
|
||||
if (sstart[j] >= 10) {
|
||||
result << std::uppercase << std::hex << sstart[j];
|
||||
result << '-';
|
||||
result << std::uppercase << std::hex << send[j];
|
||||
}
|
||||
result << ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (&i != ®ex_range.back())
|
||||
result << ",";
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
int convert_range(std::string& buffer, bignum start, bignum end)
|
||||
{
|
||||
pattern_t ptype;
|
||||
int pos;
|
||||
|
||||
std::string regex_range = generate_regex_range(std::move(start), std::move(end));
|
||||
|
||||
if (!regex_range.empty()) {
|
||||
ptype = convert_aaregex_to_pcre(regex_range.c_str(), 0, glob_default, buffer, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
} else {
|
||||
buffer.append(default_match_pattern);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int post_process_policydb_ents(Profile *prof)
|
||||
{
|
||||
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
|
||||
@@ -857,80 +967,6 @@ int post_process_policydb_ents(Profile *prof)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask,
|
||||
bool audit, bool deny) {
|
||||
std::ostringstream buffer;
|
||||
std::string buf;
|
||||
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NETV8;
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((family & 0xff00) >> 8);
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (family & 0xff);
|
||||
if (type_mask > 0xffff) {
|
||||
buffer << "..";
|
||||
} else {
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((type_mask & 0xff00) >> 8);
|
||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
|
||||
}
|
||||
buf = buffer.str();
|
||||
if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS),
|
||||
audit ? map_perms(AA_VALID_NET_PERMS) : 0,
|
||||
parseopts))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_af_rules(Profile *prof, u16 family, unsigned int type_mask,
|
||||
unsigned int audit_mask, bool deny)
|
||||
{
|
||||
if (type_mask > 0xffff && audit_mask > 0xffff) {
|
||||
/* instead of generating multiple rules wild card type */
|
||||
return gen_net_rule(prof, family, type_mask, audit_mask, deny);
|
||||
} else {
|
||||
int t;
|
||||
/* generate rules for types that are set */
|
||||
for (t = 0; t < 16; t++) {
|
||||
if (type_mask & (1 << t)) {
|
||||
if (!gen_net_rule(prof, family, t,
|
||||
audit_mask & (1 << t),
|
||||
deny))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool post_process_policydb_net(Profile *prof)
|
||||
{
|
||||
u16 af;
|
||||
|
||||
/* no network rules defined so we don't have generate them */
|
||||
if (!prof->net.allow)
|
||||
return true;
|
||||
|
||||
/* generate rules if the af has something set */
|
||||
for (af = AF_UNSPEC; af < get_af_max(); af++) {
|
||||
if (prof->net.allow[af] ||
|
||||
prof->net.deny[af] ||
|
||||
prof->net.audit[af] ||
|
||||
prof->net.quiet[af]) {
|
||||
if (!gen_af_rules(prof, af, prof->net.allow[af],
|
||||
prof->net.audit[af],
|
||||
false))
|
||||
return false;
|
||||
if (!gen_af_rules(prof, af, prof->net.deny[af],
|
||||
prof->net.quiet[af],
|
||||
true))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAKE_STR(X) #X
|
||||
#define CLASS_STR(X) "\\d" MAKE_STR(X)
|
||||
#define MAKE_SUB_STR(X) "\\000" MAKE_STR(X)
|
||||
@@ -959,9 +995,6 @@ int process_profile_policydb(Profile *prof)
|
||||
|
||||
if (!post_process_policydb_ents(prof))
|
||||
goto out;
|
||||
/* TODO: move to network class */
|
||||
if (features_supports_networkv8 && !post_process_policydb_net(prof))
|
||||
goto out;
|
||||
|
||||
/* insert entries to show indicate what compiler/policy expects
|
||||
* to be supported
|
||||
|
@@ -70,8 +70,6 @@ mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src,
|
||||
mnt_rule *do_pivot_rule(struct cond_entry *old, char *root,
|
||||
char *transition);
|
||||
static void abi_features(char *filename, bool search);
|
||||
bool add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error);
|
||||
bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
|
||||
|
||||
%}
|
||||
|
||||
@@ -149,6 +147,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
|
||||
%token TOK_IO_URING
|
||||
%token TOK_OVERRIDE_CREDS
|
||||
%token TOK_SQPOLL
|
||||
%token TOK_ALL
|
||||
|
||||
/* rlimits */
|
||||
%token TOK_RLIMIT
|
||||
@@ -187,13 +186,15 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
|
||||
#include "userns.h"
|
||||
#include "mqueue.h"
|
||||
#include "io_uring.h"
|
||||
#include "network.h"
|
||||
#include "all_rule.h"
|
||||
}
|
||||
|
||||
%union {
|
||||
char *id;
|
||||
char *flag_id;
|
||||
char *mode;
|
||||
struct aa_network_entry *network_entry;
|
||||
network_rule *network_entry;
|
||||
Profile *prof;
|
||||
struct cod_net_entry *net_entry;
|
||||
struct cod_entry *user_entry;
|
||||
@@ -206,6 +207,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
|
||||
userns_rule *userns_entry;
|
||||
mqueue_rule *mqueue_entry;
|
||||
io_uring_rule *io_uring_entry;
|
||||
all_rule *all_entry;
|
||||
prefix_rule_t *prefix_entry;
|
||||
|
||||
flagvals flags;
|
||||
@@ -302,6 +304,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
|
||||
%type <fperms> io_uring_perms
|
||||
%type <fperms> opt_io_uring_perm
|
||||
%type <io_uring_entry> io_uring_rule
|
||||
%type <all_entry> all_rule
|
||||
%%
|
||||
|
||||
|
||||
@@ -575,8 +578,9 @@ valuelist: valuelist TOK_VALUE
|
||||
}
|
||||
|
||||
flags: { /* nothing */
|
||||
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0, NULL };
|
||||
flagvals fv;
|
||||
|
||||
fv.init();
|
||||
$$ = fv;
|
||||
};
|
||||
|
||||
@@ -596,27 +600,7 @@ flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
|
||||
|
||||
flagvals: flagvals flagval
|
||||
{
|
||||
if (merge_profile_mode($1.mode, $2.mode) == MODE_CONFLICT)
|
||||
yyerror(_("Profile flag '%s' conflicts with '%s'"),
|
||||
profile_mode_table[$1.mode],
|
||||
profile_mode_table[$2.mode]);
|
||||
$1.mode = merge_profile_mode($1.mode, $2.mode);
|
||||
$1.audit = $1.audit || $2.audit;
|
||||
$1.path = $1.path | $2.path;
|
||||
if (($1.path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
|
||||
(PATH_CHROOT_REL | PATH_NS_REL))
|
||||
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
|
||||
|
||||
if (($1.path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
|
||||
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
|
||||
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
|
||||
if (($1.path & (PATH_ATTACH | PATH_NO_ATTACH)) ==
|
||||
(PATH_ATTACH | PATH_NO_ATTACH))
|
||||
yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected"));
|
||||
if (($1.path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) ==
|
||||
(PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH))
|
||||
yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach"));
|
||||
|
||||
$1.merge($2);
|
||||
$$ = $1;
|
||||
};
|
||||
|
||||
@@ -627,39 +611,9 @@ flagvals: flagval
|
||||
|
||||
flagval: TOK_VALUE
|
||||
{
|
||||
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0, NULL };
|
||||
enum profile_mode mode;
|
||||
flagvals fv;
|
||||
|
||||
if (strcmp($1, "debug") == 0) {
|
||||
/* DEBUG2 is left for internal compiler use atm */
|
||||
fv.flags |= FLAG_DEBUG1;
|
||||
} else if ((mode = str_to_mode($1))) {
|
||||
fv.mode = mode;
|
||||
} else if (strcmp($1, "audit") == 0) {
|
||||
fv.audit = 1;
|
||||
} else if (strcmp($1, "chroot_relative") == 0) {
|
||||
fv.path |= PATH_CHROOT_REL;
|
||||
} else if (strcmp($1, "namespace_relative") == 0) {
|
||||
fv.path |= PATH_NS_REL;
|
||||
} else if (strcmp($1, "mediate_deleted") == 0) {
|
||||
fv.path |= PATH_MEDIATE_DELETED;
|
||||
} else if (strcmp($1, "delegate_deleted") == 0) {
|
||||
fv.path |= PATH_DELEGATE_DELETED;
|
||||
} else if (strcmp($1, "attach_disconnected") == 0) {
|
||||
fv.path |= PATH_ATTACH;
|
||||
} else if (strcmp($1, "no_attach_disconnected") == 0) {
|
||||
fv.path |= PATH_NO_ATTACH;
|
||||
} else if (strcmp($1, "chroot_attach") == 0) {
|
||||
fv.path |= PATH_CHROOT_NSATTACH;
|
||||
} else if (strcmp($1, "chroot_no_attach") == 0) {
|
||||
fv.path |= PATH_CHROOT_NO_ATTACH;
|
||||
} else if (strncmp($1, "attach_disconnected.path=", 25) == 0) {
|
||||
/* TODO: make this a proper parse */
|
||||
fv.path |= PATH_ATTACH;
|
||||
fv.disconnected_path = strdup($1 + 25);
|
||||
} else {
|
||||
yyerror(_("Invalid profile flag: %s."), $1);
|
||||
}
|
||||
fv.init($1);
|
||||
free($1);
|
||||
$$ = fv;
|
||||
};
|
||||
@@ -704,7 +658,7 @@ rules: rules opt_prefix rule
|
||||
PDEBUG("rules rule: (%s)\n", $3->name);
|
||||
if (!$3)
|
||||
yyerror(_("Assert: `rule' returned NULL."));
|
||||
if (!add_prefix($3, $2, error)) {
|
||||
if (!entry_add_prefix($3, $2, error)) {
|
||||
yyerror(_("%s"), error);
|
||||
}
|
||||
add_entry_to_policy($1, $3);
|
||||
@@ -725,7 +679,7 @@ rules: rules opt_prefix block
|
||||
list_for_each_safe($3->entries, entry, tmp) {
|
||||
const char *error;
|
||||
entry->next = NULL;
|
||||
if (!add_prefix(entry, $2, error)) {
|
||||
if (!entry_add_prefix(entry, $2, error)) {
|
||||
yyerror(_("%s"), error);
|
||||
}
|
||||
/* transfer rule for now, TODO keep block and just
|
||||
@@ -742,51 +696,23 @@ rules: rules opt_prefix block
|
||||
|
||||
rules: rules opt_prefix network_rule
|
||||
{
|
||||
struct aa_network_entry *entry, *tmp;
|
||||
const char *error;
|
||||
if (!$3->add_prefix($2, error))
|
||||
yyerror(error);
|
||||
/* class members need to be updated after prefix is added */
|
||||
$3->update_compat_net();
|
||||
|
||||
PDEBUG("Matched: network rule\n");
|
||||
if ($2.owner)
|
||||
yyerror(_("owner prefix not allowed"));
|
||||
if (!$3)
|
||||
yyerror(_("Assert: `network_rule' return invalid protocol."));
|
||||
if (!$1->alloc_net_table())
|
||||
yyerror(_("Memory allocation error."));
|
||||
list_for_each_safe($3, entry, tmp) {
|
||||
|
||||
/* map to extended mediation, let rule backend do
|
||||
* downgrade if needed
|
||||
*/
|
||||
if (entry->family == AF_UNIX) {
|
||||
unix_rule *rule = new unix_rule(entry->type, $2.audit, $2.rule_mode);
|
||||
auto nm_af_unix = $3->network_map.find(AF_UNIX);
|
||||
if (nm_af_unix != $3->network_map.end()) {
|
||||
for (auto& entry : nm_af_unix->second) {
|
||||
unix_rule *rule = new unix_rule(entry.type,
|
||||
$2.audit, $2.rule_mode);
|
||||
if (!rule)
|
||||
yyerror(_("Memory allocation error."));
|
||||
$1->rule_ents.push_back(rule);
|
||||
}
|
||||
if (entry->type > SOCK_PACKET) {
|
||||
/* setting mask instead of a bit */
|
||||
if ($2.rule_mode == RULE_DENY) {
|
||||
$1->net.deny[entry->family] |= entry->type;
|
||||
if ($2.audit != AUDIT_FORCE)
|
||||
$1->net.quiet[entry->family] |= entry->type;
|
||||
} else {
|
||||
$1->net.allow[entry->family] |= entry->type;
|
||||
if ($2.audit == AUDIT_FORCE)
|
||||
$1->net.audit[entry->family] |= entry->type;
|
||||
}
|
||||
} else {
|
||||
if ($2.rule_mode == RULE_DENY) {
|
||||
$1->net.deny[entry->family] |= 1 << entry->type;
|
||||
if ($2.audit != AUDIT_FORCE)
|
||||
$1->net.quiet[entry->family] |= 1 << entry->type;
|
||||
} else {
|
||||
$1->net.allow[entry->family] |= 1 << entry->type;
|
||||
if ($2.audit == AUDIT_FORCE)
|
||||
$1->net.audit[entry->family] |= 1 << entry->type;
|
||||
}
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
|
||||
$1->rule_ents.push_back($3);
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
@@ -798,6 +724,7 @@ prefix_rule : mnt_rule { $$ = $1; }
|
||||
| userns_rule { $$ = $1; }
|
||||
| mqueue_rule { $$ = $1; }
|
||||
| io_uring_rule { $$ = $1; }
|
||||
| all_rule { $$ = $1; }
|
||||
|
||||
rules: rules opt_prefix prefix_rule
|
||||
{
|
||||
@@ -1156,45 +1083,48 @@ link_rule: TOK_LINK opt_subset_flag id_or_var TOK_ARROW id_or_var TOK_END_OF_RUL
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
network_rule: TOK_NETWORK TOK_END_OF_RULE
|
||||
network_rule: TOK_NETWORK opt_net_perm opt_conds opt_cond_list TOK_END_OF_RULE
|
||||
{
|
||||
size_t family;
|
||||
struct aa_network_entry *new_entry, *entry = NULL;
|
||||
for (family = AF_UNSPEC; family < get_af_max(); family++) {
|
||||
new_entry = new_network_ent(family, 0xffffffff,
|
||||
0xffffffff);
|
||||
if (!new_entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
new_entry->next = entry;
|
||||
entry = new_entry;
|
||||
network_rule *entry;
|
||||
|
||||
if ($4.name) {
|
||||
if (strcmp($4.name, "peer") != 0)
|
||||
yyerror(_("network rule: invalid conditional group %s=()"), $4.name);
|
||||
free($4.name);
|
||||
}
|
||||
entry = new network_rule($2, $3, $4.list);
|
||||
$$ = entry;
|
||||
}
|
||||
|
||||
network_rule: TOK_NETWORK TOK_ID TOK_END_OF_RULE
|
||||
network_rule: TOK_NETWORK opt_net_perm TOK_ID opt_conds opt_cond_list TOK_END_OF_RULE
|
||||
{
|
||||
struct aa_network_entry *entry;
|
||||
entry = network_entry($2, NULL, NULL);
|
||||
if (!entry)
|
||||
/* test for short circuiting of family */
|
||||
entry = network_entry(NULL, $2, NULL);
|
||||
if (!entry)
|
||||
yyerror(_("Invalid network entry."));
|
||||
free($2);
|
||||
$$ = entry;
|
||||
}
|
||||
network_rule *entry;
|
||||
|
||||
network_rule: TOK_NETWORK TOK_ID TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct aa_network_entry *entry;
|
||||
entry = network_entry($2, $3, NULL);
|
||||
if (!entry)
|
||||
yyerror(_("Invalid network entry."));
|
||||
free($2);
|
||||
if ($5.name) {
|
||||
if (strcmp($5.name, "peer") != 0)
|
||||
yyerror(_("network rule: invalid conditional group %s=()"), $5.name);
|
||||
free($5.name);
|
||||
}
|
||||
entry = new network_rule($2, $3, NULL, NULL, $4, $5.list);
|
||||
free($3);
|
||||
$$ = entry;
|
||||
}
|
||||
|
||||
network_rule: TOK_NETWORK opt_net_perm TOK_ID TOK_ID opt_conds opt_cond_list TOK_END_OF_RULE
|
||||
{
|
||||
network_rule *entry;
|
||||
|
||||
if ($6.name) {
|
||||
if (strcmp($6.name, "peer") != 0)
|
||||
yyerror(_("network rule: invalid conditional group %s=()"), $6.name);
|
||||
free($6.name);
|
||||
}
|
||||
entry = new network_rule($2, $3, $4, NULL, $5, $6.list);
|
||||
free($3);
|
||||
free($4);
|
||||
$$ = entry;
|
||||
}
|
||||
|
||||
cond: TOK_CONDID
|
||||
{
|
||||
struct cond_entry *ent;
|
||||
@@ -1605,6 +1535,14 @@ io_uring_rule: TOK_IO_URING opt_io_uring_perm opt_conds opt_cond_list TOK_END_OF
|
||||
$$ = ent;
|
||||
}
|
||||
|
||||
all_rule: TOK_ALL TOK_END_OF_RULE
|
||||
{
|
||||
all_rule *ent = new all_rule();
|
||||
if (!ent)
|
||||
yyerror(_("Memory allocation error."));
|
||||
$$ = ent;
|
||||
}
|
||||
|
||||
hat_start: TOK_CARET {}
|
||||
| TOK_HAT {}
|
||||
|
||||
@@ -1867,43 +1805,3 @@ static void abi_features(char *filename, bool search)
|
||||
|
||||
};
|
||||
|
||||
bool check_x_qualifier(struct cod_entry *entry, const char *&error)
|
||||
{
|
||||
if (entry->perms & AA_EXEC_BITS) {
|
||||
if ((entry->rule_mode == RULE_DENY) &&
|
||||
(entry->perms & ALL_AA_EXEC_TYPE)) {
|
||||
error = _("Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'");
|
||||
return false;
|
||||
} else if ((entry->rule_mode != RULE_DENY) &&
|
||||
!(entry->perms & ALL_AA_EXEC_TYPE)) {
|
||||
error = _("Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// cod_entry version of ->add_prefix here just as file rules aren't converted yet
|
||||
bool add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error)
|
||||
{
|
||||
/* modifiers aren't correctly stored for cod_entries yet so
|
||||
* we can't conflict on them easily. Leave that until conversion
|
||||
* to rule_t
|
||||
*/
|
||||
/* apply rule mode */
|
||||
entry->rule_mode = p.rule_mode;
|
||||
|
||||
/* apply owner/other */
|
||||
if (p.owner == 1)
|
||||
entry->perms &= (AA_USER_PERMS | AA_SHARED_PERMS);
|
||||
else if (p.owner == 2)
|
||||
entry->perms &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
|
||||
|
||||
/* implied audit modifier */
|
||||
if (p.audit == AUDIT_FORCE && (entry->rule_mode != RULE_DENY))
|
||||
entry->audit = AUDIT_FORCE;
|
||||
else if (p.audit != AUDIT_FORCE && (entry->rule_mode == RULE_DENY))
|
||||
entry->audit = AUDIT_FORCE;
|
||||
|
||||
return check_x_qualifier(entry, error);
|
||||
}
|
||||
|
@@ -27,7 +27,9 @@ const char *profile_mode_table[] = {
|
||||
"complain",
|
||||
"kill",
|
||||
"unconfined",
|
||||
"prompt"
|
||||
"prompt",
|
||||
"default_allow",
|
||||
"conflict" /* should not ever be displayed */
|
||||
};
|
||||
|
||||
bool deref_profileptr_lt::operator()(Profile * const &lhs, Profile * const &rhs) const
|
||||
@@ -71,20 +73,6 @@ void ProfileList::dump_profile_names(bool children)
|
||||
}
|
||||
}
|
||||
|
||||
bool Profile::alloc_net_table()
|
||||
{
|
||||
if (net.allow)
|
||||
return true;
|
||||
net.allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
net.audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
net.deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
net.quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
||||
if (!net.allow || !net.audit || !net.deny || !net.quiet)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Profile::~Profile()
|
||||
{
|
||||
hat_table.clear();
|
||||
@@ -114,14 +102,6 @@ Profile::~Profile()
|
||||
for (int i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++)
|
||||
if (exec_table[i])
|
||||
free(exec_table[i]);
|
||||
if (net.allow)
|
||||
free(net.allow);
|
||||
if (net.audit)
|
||||
free(net.audit);
|
||||
if (net.deny)
|
||||
free(net.deny);
|
||||
if (net.quiet)
|
||||
free(net.quiet);
|
||||
}
|
||||
|
||||
static bool comp (rule_t *lhs, rule_t *rhs)
|
||||
@@ -347,6 +327,19 @@ static int profile_add_hat_rules(Profile *prof)
|
||||
|
||||
void Profile::post_parse_profile(void)
|
||||
{
|
||||
/* semantic check stuff that can't be done in parse, like flags */
|
||||
if (flags.flags & FLAG_INTERRUPTIBLE) {
|
||||
if (!features_supports_flag_interruptible) {
|
||||
warn_once(name, "flag interruptible not supported. Ignoring");
|
||||
/* TODO: don't clear in parse data, only at encode */
|
||||
flags.flags &= ~FLAG_INTERRUPTIBLE;
|
||||
}
|
||||
}
|
||||
if (flags.signal) {
|
||||
if (!features_supports_flag_signal) {
|
||||
warn_once(name, "kill.signal not supported. Ignoring");
|
||||
}
|
||||
}
|
||||
post_process_file_entries(this);
|
||||
post_process_rule_entries(this);
|
||||
}
|
||||
@@ -355,6 +348,12 @@ void Profile::add_implied_rules(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
|
||||
if ((*i)->skip())
|
||||
continue;
|
||||
(*i)->add_implied_rules(*this);
|
||||
}
|
||||
|
||||
error = profile_add_hat_rules(this);
|
||||
if (error) {
|
||||
PERROR(_("ERROR adding hat access rule for profile %s\n"),
|
||||
@@ -363,3 +362,9 @@ void Profile::add_implied_rules(void)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* do we want to warn once/profile or just once per compile?? */
|
||||
void Profile::warn_once(const char *name, const char *msg)
|
||||
{
|
||||
common_warn_once(name, msg, &warned_name);
|
||||
}
|
||||
|
131
parser/profile.h
131
parser/profile.h
@@ -23,6 +23,7 @@
|
||||
#include "rule.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
#include "network.h"
|
||||
#include "signal.h"
|
||||
|
||||
class Profile;
|
||||
|
||||
@@ -63,9 +64,10 @@ enum profile_mode {
|
||||
MODE_KILL = 3,
|
||||
MODE_UNCONFINED = 4,
|
||||
MODE_PROMPT = 5,
|
||||
MODE_CONFLICT = 6 /* greater than MODE_LAST */
|
||||
MODE_DEFAULT_ALLOW = 6,
|
||||
MODE_CONFLICT = 7 /* greater than MODE_LAST */
|
||||
};
|
||||
#define MODE_LAST MODE_PROMPT
|
||||
#define MODE_LAST MODE_DEFAULT_ALLOW
|
||||
|
||||
static inline enum profile_mode operator++(enum profile_mode &mode)
|
||||
{
|
||||
@@ -84,6 +86,9 @@ static inline enum profile_mode merge_profile_mode(enum profile_mode l, enum pro
|
||||
|
||||
static inline uint32_t profile_mode_packed(enum profile_mode mode)
|
||||
{
|
||||
/* until dominance is fixed use unconfined mode for default_allow */
|
||||
if (mode == MODE_DEFAULT_ALLOW)
|
||||
mode = MODE_UNCONFINED;
|
||||
/* kernel doesn't have an unspecified mode everything
|
||||
* shifts down by 1
|
||||
*/
|
||||
@@ -114,7 +119,9 @@ static inline enum profile_mode str_to_mode(const char *str)
|
||||
#define FLAG_HAT 1
|
||||
#define FLAG_DEBUG1 2
|
||||
#define FLAG_DEBUG2 4
|
||||
#define FLAG_INTERRUPTIBLE 8
|
||||
|
||||
/* sigh, used in parse union so needs trivial constructors. */
|
||||
class flagvals {
|
||||
public:
|
||||
int flags;
|
||||
@@ -122,6 +129,61 @@ public:
|
||||
int audit;
|
||||
int path;
|
||||
char *disconnected_path;
|
||||
int signal;
|
||||
|
||||
// stupid not constructor constructors
|
||||
void init(void)
|
||||
{
|
||||
flags = 0;
|
||||
mode = MODE_UNSPECIFIED;
|
||||
audit = 0;
|
||||
path = 0;
|
||||
disconnected_path = NULL;
|
||||
signal = 0;
|
||||
}
|
||||
void init(const char *str)
|
||||
{
|
||||
init();
|
||||
enum profile_mode pmode = str_to_mode(str);
|
||||
|
||||
if (strcmp(str, "debug") == 0) {
|
||||
/* DEBUG2 is left for internal compiler use atm */
|
||||
flags |= FLAG_DEBUG1;
|
||||
} else if (pmode) {
|
||||
mode = pmode;
|
||||
} else if (strcmp(str, "audit") == 0) {
|
||||
audit = 1;
|
||||
} else if (strcmp(str, "chroot_relative") == 0) {
|
||||
path |= PATH_CHROOT_REL;
|
||||
} else if (strcmp(str, "namespace_relative") == 0) {
|
||||
path |= PATH_NS_REL;
|
||||
} else if (strcmp(str, "mediate_deleted") == 0) {
|
||||
path |= PATH_MEDIATE_DELETED;
|
||||
} else if (strcmp(str, "delegate_deleted") == 0) {
|
||||
path |= PATH_DELEGATE_DELETED;
|
||||
} else if (strcmp(str, "attach_disconnected") == 0) {
|
||||
path |= PATH_ATTACH;
|
||||
} else if (strcmp(str, "no_attach_disconnected") == 0) {
|
||||
path |= PATH_NO_ATTACH;
|
||||
} else if (strcmp(str, "chroot_attach") == 0) {
|
||||
path |= PATH_CHROOT_NSATTACH;
|
||||
} else if (strcmp(str, "chroot_no_attach") == 0) {
|
||||
path |= PATH_CHROOT_NO_ATTACH;
|
||||
} else if (strncmp(str, "attach_disconnected.path=", 25) == 0) {
|
||||
/* TODO: make this a proper parse */
|
||||
path |= PATH_ATTACH;
|
||||
disconnected_path = strdup(str + 25);
|
||||
} else if (strncmp(str, "kill.signal=", 12) == 0) {
|
||||
/* TODO: make this a proper parse */
|
||||
signal = find_signal_mapping(str + 12);
|
||||
if (signal == -1)
|
||||
yyerror("unknown signal specified for kill.signal=\'%s\'\n", str + 12);
|
||||
} else if (strcmp(str, "interruptible") == 0) {
|
||||
flags |= FLAG_INTERRUPTIBLE;
|
||||
} else {
|
||||
yyerror(_("Invalid profile flag: %s."), str);
|
||||
}
|
||||
}
|
||||
|
||||
ostream &dump(ostream &os)
|
||||
{
|
||||
@@ -135,6 +197,8 @@ public:
|
||||
|
||||
if (disconnected_path)
|
||||
os << ", attach_disconnected.path=" << disconnected_path;
|
||||
if (signal)
|
||||
os << ", kill.signal=" << signal;
|
||||
os << "\n";
|
||||
|
||||
return os;
|
||||
@@ -148,6 +212,58 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
/* warning for now disconnected_path is just passed on (not copied),
|
||||
* or leaked on error. It is not freed here, It is freed when the
|
||||
* profile destroys it self.
|
||||
*/
|
||||
void merge(const flagvals &rhs)
|
||||
{
|
||||
if (merge_profile_mode(mode, rhs.mode) == MODE_CONFLICT)
|
||||
yyerror(_("Profile flag '%s' conflicts with '%s'"),
|
||||
profile_mode_table[mode],
|
||||
profile_mode_table[rhs.mode]);
|
||||
mode = merge_profile_mode(mode, rhs.mode);
|
||||
audit = audit || rhs.audit;
|
||||
path = path | rhs.path;
|
||||
if ((path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
|
||||
(PATH_CHROOT_REL | PATH_NS_REL))
|
||||
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
|
||||
|
||||
if ((path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
|
||||
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
|
||||
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
|
||||
if ((path & (PATH_ATTACH | PATH_NO_ATTACH)) ==
|
||||
(PATH_ATTACH | PATH_NO_ATTACH))
|
||||
yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected"));
|
||||
if ((path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) ==
|
||||
(PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH))
|
||||
yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach"));
|
||||
|
||||
if (rhs.disconnected_path) {
|
||||
if (disconnected_path) {
|
||||
if (strcmp(disconnected_path, rhs.disconnected_path) != 0) {
|
||||
yyerror(_("Profile flag attach_disconnected set to conflicting values: '%s' and '%s'"), disconnected_path, rhs.disconnected_path);
|
||||
}
|
||||
// same ignore rhs.disconnect_path
|
||||
} else {
|
||||
disconnected_path = rhs.disconnected_path;
|
||||
}
|
||||
}
|
||||
if (rhs.signal) {
|
||||
if (signal) {
|
||||
if (signal != rhs.signal) {
|
||||
yyerror(_("Profile flag kill.signal set to conflicting values: '%d' and '%d'"), signal, rhs.signal);
|
||||
}
|
||||
// same so do nothing
|
||||
} else {
|
||||
signal = rhs.signal;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we move to dupping disconnected_path will need to have
|
||||
* an assignment and copy constructor and a destructor
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
struct capabilities {
|
||||
@@ -199,7 +315,7 @@ public:
|
||||
|
||||
flagvals flags;
|
||||
struct capabilities caps;
|
||||
struct network net;
|
||||
network_rule net;
|
||||
|
||||
struct aa_rlimits rlimits;
|
||||
|
||||
@@ -225,7 +341,7 @@ public:
|
||||
|
||||
parent = NULL;
|
||||
|
||||
flags = { 0, MODE_UNSPECIFIED, 0, 0, NULL };
|
||||
flags.init();
|
||||
rlimits = {0, {}};
|
||||
|
||||
std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);
|
||||
@@ -272,7 +388,6 @@ public:
|
||||
|
||||
flags.dump(cerr);
|
||||
caps.dump();
|
||||
net.dump();
|
||||
|
||||
if (entries)
|
||||
debug_cod_entries(entries);
|
||||
@@ -285,8 +400,6 @@ public:
|
||||
hat_table.dump();
|
||||
}
|
||||
|
||||
bool alloc_net_table();
|
||||
|
||||
std::string hname(void)
|
||||
{
|
||||
if (!parent)
|
||||
@@ -319,6 +432,10 @@ public:
|
||||
|
||||
void post_parse_profile(void);
|
||||
void add_implied_rules(void);
|
||||
|
||||
protected:
|
||||
const char *warned_name = NULL;
|
||||
virtual void warn_once(const char *name, const char *msg);
|
||||
};
|
||||
|
||||
|
||||
|
@@ -105,11 +105,12 @@ is_container_with_internal_policy() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# LXD and LXC set up AppArmor namespaces starting with "lxd-" and
|
||||
# "lxc-", respectively. Return non-zero for all other namespace
|
||||
# identifiers.
|
||||
# LXD, Incus and LXC set up AppArmor namespaces starting with "lxd-",
|
||||
# "incus-" and "lxc-", respectively. Return non-zero for all other
|
||||
# namespace identifiers.
|
||||
read -r ns_name < "$ns_name_path"
|
||||
if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
|
||||
[ "${ns_name#incus-*}" = "$ns_name" ] && \
|
||||
[ "${ns_name#lxc-*}" = "$ns_name" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
@@ -35,8 +35,9 @@ class Profile;
|
||||
#define RULE_TYPE_RULE 0
|
||||
#define RULE_TYPE_PREFIX 1
|
||||
#define RULE_TYPE_PERMS 2
|
||||
#define RULE_TYPE_ALL 3
|
||||
// RULE_TYPE_CLASS needs to be last because various class follow it
|
||||
#define RULE_TYPE_CLASS 3
|
||||
#define RULE_TYPE_CLASS 4
|
||||
|
||||
// rule_cast should only be used after a comparison of rule_type to ensure
|
||||
// that it is valid. Change to dynamic_cast for debugging
|
||||
@@ -289,6 +290,10 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
virtual bool add_prefix(const prefixes &p) {
|
||||
const char *err;
|
||||
return add_prefix(p, err);
|
||||
}
|
||||
|
||||
int cmp(prefixes const &rhs) const {
|
||||
return prefixes::cmp(rhs);
|
||||
|
@@ -121,7 +121,7 @@ int parse_signal_perms(const char *str_perms, perms_t *perms, int fail)
|
||||
return parse_X_perms("signal", AA_VALID_SIGNAL_PERMS, str_perms, perms, fail);
|
||||
}
|
||||
|
||||
static int find_signal_mapping(const char *sig)
|
||||
int find_signal_mapping(const char *sig)
|
||||
{
|
||||
if (strncmp("rtmin+", sig, 6) == 0) {
|
||||
char *end;
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
typedef set<int> Signals;
|
||||
|
||||
int find_signal_mapping(const char *sig);
|
||||
int parse_signal_perms(const char *str_perms, perms_t *perms, int fail);
|
||||
|
||||
class signal_rule: public perms_rule_t {
|
||||
|
8
parser/tst/simple_tests/all/bad_01.sd
Normal file
8
parser/tst/simple_tests/all/bad_01.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic ptrace all rule
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
all read readby trace tracedby ,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/bad_02.sd
Normal file
8
parser/tst/simple_tests/all/bad_02.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic ptrace all rule
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
owner all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_01.sd
Normal file
8
parser/tst/simple_tests/all/ok_01.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_02.sd
Normal file
8
parser/tst/simple_tests/all/ok_02.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
audit all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_03.sd
Normal file
8
parser/tst/simple_tests/all/ok_03.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
allow all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_04.sd
Normal file
8
parser/tst/simple_tests/all/ok_04.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
deny all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_05.sd
Normal file
8
parser/tst/simple_tests/all/ok_05.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
audit deny all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/all/ok_06.sd
Normal file
8
parser/tst/simple_tests/all/ok_06.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=Description basic all rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
audit allow all,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_10.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_10.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=127.0.0.1 port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_11.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_11.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=127.0.0.1 port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_12.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_12.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=[invalid] port=80),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_13.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_13.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=::1 port=-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_14.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_14.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=::1 port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_15.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_15.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=::1 port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_16.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_16.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_17.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_17.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(port=-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_18.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_18.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_19.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_19.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.39-192.168.0.4),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_20.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_20.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.39-invalid),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_21.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_21.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.39-::58c2),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_22.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_22.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=2001:1884:d02e:2759:d30:f166:71c9:288f-192.168.0.39),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_23.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_23.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=80-192.168.0.39),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_24.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_24.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(port=80-65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_25.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_25.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(port=443-80),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_26.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_26.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=invalid/80),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_27.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_27.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.1/-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_28.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_28.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.1/invalid),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_29.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_29.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=192.168.0.1/33),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_30.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_30.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=2001:1884:d02e:2759:d30:f166:71c9:288f/-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_31.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_31.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=2001:1884:d02e:2759:d30:f166:71c9:288f/invalid),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_32.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_32.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network subnet test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network peer=(ip=2001:1884:d02e:2759:d30:f166:71c9:288f/129),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_33.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_33.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=127.0.0.1 port=test,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_34.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_34.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=127.0.0.1 port=test peer=(ip=127.0.0.1 port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_35.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_35.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=127.0.0.1 port=65536,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_36.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_36.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=127.0.0.1 port=65536 peer=(ip=127.0.0.1 port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_37.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_37.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=[invalid] port=80,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_38.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_38.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=[invalid] port=80 peer=(ip=[invalid] port=80),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_39.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_39.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=-1,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_40.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_40.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=-1 peer=(ip=::1 port=-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_41.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_41.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=test,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_42.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_42.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip - port conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=test peer=(ip=::1 port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_43.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_43.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=65536,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_44.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_44.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=::1 port=65536 peer=(ip=::1 port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_45.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_45.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=65536,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_46.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_46.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=65536 peer=(port=65536),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_47.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_47.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=-1,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_48.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_48.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=-1 peer=(port=-1),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_49.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_49.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=test,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_5.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_5.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network ip conditional test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=10,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_50.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_50.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network port range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network port=test peer=(port=test),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_51.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_51.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=192.168.0.39-192.168.0.4,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_52.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_52.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=192.168.0.39-192.168.0.4 peer=(ip=192.168.0.39-192.168.0.4),
|
||||
|
||||
}
|
8
parser/tst/simple_tests/network/network_bad_53.sd
Normal file
8
parser/tst/simple_tests/network/network_bad_53.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION invalid network range test
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network ip=192.168.0.39-invalid,
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user