From e80309331ca1dc2b10c0f5057b166e706b252feb Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Wed, 19 Jan 2022 21:03:53 +0100 Subject: [PATCH] Add --secontext=mismatch option support - Add 0168-m4-fix-st_SELINUX-check.patch (v5.15~18 "m4: fix st_SELINUX check") - Add 0169-Implement-displaying-of-expected-context-upon-mismat.patch (v5.16~31 "Implement displaying of expected context upon mismatch") - Add 0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch - Add 0171-tests-secontext-add-secontext-field-getters.patch - Add 0172-tests-linkat-provide-fallback-values-for-secontext-f.patch - Add 0173-tests-secontext-eliminate-separate-secontext_format-.patch - Add 0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch * 0168-m4-fix-st_SELINUX-check.patch: New file. * 0169-Implement-displaying-of-expected-context-upon-mismat.patch: Ditto. * 0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch: Ditto. * 0171-tests-secontext-add-secontext-field-getters.patch: Ditto. * 0172-tests-linkat-provide-fallback-values-for-secontext-f.patch: Ditto. * 0173-tests-secontext-eliminate-separate-secontext_format-.patch: Ditto. * 0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch: Ditto. * strace.spec strace.spec (Release): Bump to 6. (Patch168, Patch169, Patch170, Patch171, Patch172, Patch173, Patch164): New patches. (%prep): Apply them. (%changelog): Mention it. Resolves: #2038965 Signed-off-by: Eugene Syromiatnikov --- 0168-m4-fix-st_SELINUX-check.patch | 66 + ...ying-of-expected-context-upon-mismat.patch | 1161 +++++++++++++++++ ...et-errno-before-SELinux-context-mani.patch | 54 + ...econtext-add-secontext-field-getters.patch | 134 ++ ...vide-fallback-values-for-secontext-f.patch | 71 + ...eliminate-separate-secontext_format-.patch | 31 + ...et-context-to-the-expected-one-if-a-.patch | 76 ++ strace.spec | 23 +- 8 files changed, 1615 insertions(+), 1 deletion(-) create mode 100644 0168-m4-fix-st_SELINUX-check.patch create mode 100644 0169-Implement-displaying-of-expected-context-upon-mismat.patch create mode 100644 0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch create mode 100644 0171-tests-secontext-add-secontext-field-getters.patch create mode 100644 0172-tests-linkat-provide-fallback-values-for-secontext-f.patch create mode 100644 0173-tests-secontext-eliminate-separate-secontext_format-.patch create mode 100644 0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch diff --git a/0168-m4-fix-st_SELINUX-check.patch b/0168-m4-fix-st_SELINUX-check.patch new file mode 100644 index 0000000..b95d55f --- /dev/null +++ b/0168-m4-fix-st_SELINUX-check.patch @@ -0,0 +1,66 @@ +From b8f375c2c8140e759122bca3e3469386d3ba5184 Mon Sep 17 00:00:00 2001 +From: "Dmitry V. Levin" +Date: Mon, 29 Nov 2021 08:00:00 +0000 +Subject: [PATCH 168/174] m4: fix st_SELINUX check + +* m4/st_selinux.m4: Make sure selinux support is enabled only if +all expected functions are provided by libselinux. + +Fixes: v5.12~49 "Implement --secontext[=full] option to display SELinux contexts" +--- + m4/st_selinux.m4 | 36 ++++++++++++++++-------------------- + 1 file changed, 16 insertions(+), 20 deletions(-) + +diff --git a/m4/st_selinux.m4 b/m4/st_selinux.m4 +index da72a48..7b24eba 100644 +--- a/m4/st_selinux.m4 ++++ b/m4/st_selinux.m4 +@@ -34,29 +34,25 @@ AS_IF([test "x$with_libselinux" != xno], + AS_IF([test "x$found_selinux_h" = xyes], + [saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $libselinux_LDFLAGS" +- AC_CHECK_LIB([selinux],[getpidcon], +- [libselinux_LIBS="-lselinux" +- enable_secontext=yes +- ], +- [if test "x$with_libselinux" != xcheck; then +- AC_MSG_FAILURE([failed to find getpidcon in libselinux]) +- fi +- ] +- ) +- AC_CHECK_LIB([selinux],[getfilecon], +- [libselinux_LIBS="-lselinux" +- enable_secontext=yes +- ], +- [if test "x$with_libselinux" != xcheck; then +- AC_MSG_FAILURE([failed to find getfilecon in libselinux]) +- fi +- ] ++ missing= ++ for func in getpidcon getfilecon; do ++ AC_CHECK_LIB([selinux], [$func], [:], ++ [missing="$missing $func"]) ++ done ++ AS_IF([test "x$missing" = x], ++ [libselinux_LIBS="-lselinux" ++ enable_secontext=yes ++ ], ++ [AS_IF([test "x$with_libselinux" != xcheck], ++ [AC_MSG_FAILURE([failed to find in libselinux:$missing])] ++ ) ++ ] + ) + LDFLAGS="$saved_LDFLAGS" + ], +- [if test "x$with_libselinux" != xcheck; then +- AC_MSG_FAILURE([failed to find selinux.h]) +- fi ++ [AS_IF([test "x$with_libselinux" != xcheck], ++ [AC_MSG_FAILURE([failed to find selinux.h])] ++ ) + ] + ) + ] +-- +2.1.4 + diff --git a/0169-Implement-displaying-of-expected-context-upon-mismat.patch b/0169-Implement-displaying-of-expected-context-upon-mismat.patch new file mode 100644 index 0000000..1f495c2 --- /dev/null +++ b/0169-Implement-displaying-of-expected-context-upon-mismat.patch @@ -0,0 +1,1161 @@ +From 8b07b18a44f7e0ebdb65b791d79d588dd90b70b6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Renaud=20M=C3=A9trich?= +Date: Tue, 5 Oct 2021 08:36:22 +0200 +Subject: [PATCH 169/174] Implement displaying of expected context upon + mismatch + +New option to --secontext=... (also available as -e secontext=...) +- mismatch: print expected context on mismatch + +When using 'mismatch', an additional check is made on the context by +reading the context database and comparing the output after stripping +the unwanted part (e.g. stripping nothing in 'full' mode, keeping the +type only in default mode): +- if it differs, prints the expected context after printing '!!' +- if not, don't print anything + +Example with /home/rmetrich/GIT/strace/autom4te.cache/output.3 file: + +---- +$ matchpathcon /home/rmetrich/GIT/strace/autom4te.cache/output.3 +/home/rmetrich/GIT/strace/autom4te.cache/output.3 unconfined_u:object_r:user_home_t:s0 + +$ ls -Z /home/rmetrich/GIT/strace/autom4te.cache/output.3 +system_u:object_r:user_home_t:s0 /home/rmetrich/GIT/strace/autom4te.cache/output.3 +---- + +From above, we see the user part differs ('unconfined_u' vs 'system_u') + +Output in '!full' mode (no diff found on type): + +---- +$ strace --secontext=mismatch -e statx stat /home/rmetrich/GIT/strace/autom4te.cache/output.3 +... statx(AT_FDCWD, "/home/rmetrich/GIT/strace/autom4te.cache/output.3" [user_home_t], ... +---- + +Output in 'full' mode (diff found on user): + +---- +... statx(AT_FDCWD, "/home/rmetrich/GIT/strace/autom4te.cache/output.3" [system_u:object_r:user_home_t:s0!!unconfined_u:object_r:user_home_t:s0], ... +---- + +* NEWS: Mention this change. +* doc/strace.1.in: Document it. +* m4/st_selinux.m4 (st_SELINUX): Check for selabel_open +and selabel_lookup. +* src/filter_qualify.c [ENABLE_SECONTEXT]: Include "secontext.h". +[ENABLE_SECONTEXT] (secontext_set): New variable. +[ENABLE_SECONTEXT] (secontextstr_to_uint, qualify_secontext): New +functions. +(qual_options) [ENABLE_SECONTEXT]: Add "secontext". +* src/secontext.c: Include , , , +"largefile_wrappers.h", "number_set.h", and "xmalloc.h". +(selinux_context, selinux_context_full): Remove. +(getcontext): Use is_number_in_set instead of selinux_context_full. +(selinux_getpidcon): Use is_number_in_set instead of selinux_context. +(get_expected_filecontext): New function. +(selinux_getfdcon, selinux_getfilecon): Use it to print context mismatch +if SECONTEXT_MISMATCH is set in secontext_set. +* src/secontext.h (selinux_context, selinux_context_full): Remove. +(secontext_bits): New enum. +(secontext_set, qualify_secontext, selinux_set_format): New +declarations. +* src/strace.c (SECONTEXT_E_QUAL): New macro. +(usage): Use it, describe --secontext. +(init) [ENABLE_SECONTEXT]: Call qualify_secontext, rename +GETOPT_SECONTEXT to GETOPT_QUAL_SECONTEXT, use is_number_in_set +instead of selinux_context. +(init) [ENABLE_SECONTEXT] (secontext_qual): New variable. +(init) [ENABLE_SECONTEXT] : Use it. +* tests/.gitignore: Add *--secontext_full_mismatch, +*--secontext_full_mismatch.c, *--secontext_mismatch, and +*--secontext_mismatch.c. +* tests/gen_secontext.sh: Generate *--secontext_full_mismatch.c +and *--secontext_mismatch.c. +* tests/gen_tests.in (access--secontext_full_mismatch, +access--secontext_mismatch, chmod--secontext_full_mismatch, +chmod--secontext_mismatch, execve--secontext_full_mismatch, +execve--secontext_mismatch, execveat--secontext_full_mismatch, +execveat--secontext_mismatch, faccessat--secontext_full_mismatch, +faccessat--secontext_mismatch, faccessat-y--secontext_full_mismatch, +faccessat-y--secontext_mismatch, fanotify_mark--secontext_full_mismatch, +fanotify_mark--secontext_mismatch, fchmod--secontext_full_mismatch, +fchmod--secontext_mismatch, fchmod-y--secontext_full_mismatch, +fchmod-y--secontext_mismatch, fchmodat--secontext_full_mismatch, +fchmodat--secontext_mismatch, fchownat--secontext_full_mismatch, +fchownat--secontext_mismatch, file_handle--secontext_full_mismatch, +file_handle--secontext_mismatch, linkat--secontext_full_mismatch, +linkat--secontext_mismatch, open--secontext_full_mismatch, +open--secontext_mismatch, openat--secontext_full_mismatch, +openat--secontext_mismatch): New tests. +* tests/linkat.c: Include . +(main) [PRINT_SECONTEXT_MISMATCH]: Check context mismatch. +* tests/options-syntax.test: Check --secontext and -e secontext syntax. +* tests/secontext.h (secontext_field): New enum. +(secontext_full_file, secontext_short_file): Add "mismatch" argument. +(update_secontext_type): Rename to update_secontext_field, add "field" +argument. +(SECONTEXT_FILE): Conditionalize "mismatch" argument passed to +secontext_full_file and secontext_short_file on +PRINT_SECONTEXT_MISMATCH. +* tests/secontext.c: Include and . +(get_type_from_context, raw_expected_secontext_full_file, +raw_expected_secontext_short_file): New functions. +(raw_secontext_short_file, raw_secontext_short_pid): Use +get_type_from_context. +(secontext_full_file): Add "mismatch" argument, use +raw_expected_secontext_full_file if mismatch is enabled. +(secontext_short_file): Add "mismatch" argument, use +raw_expected_secontext_short_file if mismatch is enabled. +(update_secontext_type): Rename to update_secontext_field, add "field" +argument. + +Co-authored-by: Dmitry V. Levin + +Conflicts: + NEWS + doc/strace.1.in + src/filter_qualify.c + src/strace.c +--- + NEWS | 2 + + doc/strace.1.in | 33 +++++++-- + m4/st_selinux.m4 | 2 +- + src/filter_qualify.c | 29 ++++++++ + src/secontext.c | 114 ++++++++++++++++++++++++++---- + src/secontext.h | 15 +++- + src/strace.c | 49 ++++++++----- + tests/.gitignore | 4 ++ + tests/gen_secontext.sh | 12 +++- + tests/gen_tests.in | 34 ++++++++- + tests/linkat.c | 46 +++++++++++- + tests/options-syntax.test | 14 +++- + tests/secontext.c | 176 ++++++++++++++++++++++++++++++++-------------- + tests/secontext.h | 28 ++++++-- + 14 files changed, 453 insertions(+), 105 deletions(-) + +diff --git a/NEWS b/NEWS +index 969ed11..f7542ea 100644 +--- a/NEWS ++++ b/NEWS +@@ -2,6 +2,8 @@ Noteworthy changes in release 5.13 (2021-07-18) + =============================================== + + * Improvements ++ * Implemented --secontext=mismatch option to find mismatches in SELinux ++ contexts. + * Print netlink data in a more structured way. + * Implemented decoding of NT_PRSTATUS and NT_FPREGSET regsets + of PTRACE_GETREGSET and PTRACE_SETREGSET requests. +diff --git a/doc/strace.1.in b/doc/strace.1.in +index 003e9e5..439243b 100644 +--- a/doc/strace.1.in ++++ b/doc/strace.1.in +@@ -53,7 +53,7 @@ strace \- trace system calls and signals + .OM \-P path + .OM \-p pid + .OP \-\-seccomp\-bpf +-.if '@ENABLE_SECONTEXT_FALSE@'#' .OP \-\-secontext\fR[=full] ++.if '@ENABLE_SECONTEXT_FALSE@'#' .OP \-\-secontext\fR[=\fIformat\fR] + .BR "" { + .OR \-p pid + .BR "" | +@@ -259,6 +259,7 @@ is one of + .BR inject , + .BR status , + .BR quiet " (or " silent " or " silence " or " q ), ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR secontext , + .BR decode\-fds " (or " decode\-fd ), + or + .BR kvm , +@@ -1086,13 +1087,33 @@ and PIDs associated with pidfd file descriptors. + If strace and tracee are in different PID namespaces, print PIDs in + strace's namespace, too. + .if '@ENABLE_SECONTEXT_FALSE@'#' .TP +-.if '@ENABLE_SECONTEXT_FALSE@'#' .BR \-\-secontext "[=full]" ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR \-\-secontext\fR[=\fIformat\fR] ++.if '@ENABLE_SECONTEXT_FALSE@'#' .TQ ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR \-e\ secontext\fR=\fIformat\fR + .if '@ENABLE_SECONTEXT_FALSE@'#' When SELinux is available and is not disabled, + .if '@ENABLE_SECONTEXT_FALSE@'#' print in square brackets SELinux contexts of +-.if '@ENABLE_SECONTEXT_FALSE@'#' processes, files, and descriptors. When +-.if '@ENABLE_SECONTEXT_FALSE@'#' .B full +-.if '@ENABLE_SECONTEXT_FALSE@'#' is specified, print the complete context (user, +-.if '@ENABLE_SECONTEXT_FALSE@'#' role, type and category) instead of just the type. ++.if '@ENABLE_SECONTEXT_FALSE@'#' processes, files, and descriptors. The ++.if '@ENABLE_SECONTEXT_FALSE@'#' .I format ++.if '@ENABLE_SECONTEXT_FALSE@'#' argument is a comma-separated list of items ++.if '@ENABLE_SECONTEXT_FALSE@'#' being one of the following: ++.if '@ENABLE_SECONTEXT_FALSE@'#' .RS ++.if '@ENABLE_SECONTEXT_FALSE@'#' .TP 18 ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR full ++.if '@ENABLE_SECONTEXT_FALSE@'#' Print the full context (user, role, type level ++.if '@ENABLE_SECONTEXT_FALSE@'#' and category). ++.if '@ENABLE_SECONTEXT_FALSE@'#' .TQ ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR mismatch ++.if '@ENABLE_SECONTEXT_FALSE@'#' Also print the context recorded by the SELinux ++.if '@ENABLE_SECONTEXT_FALSE@'#' database in case the current context differs. ++.if '@ENABLE_SECONTEXT_FALSE@'#' The latter is printed after two exclamation marks (!!). ++.if '@ENABLE_SECONTEXT_FALSE@'#' .RE ++.if '@ENABLE_SECONTEXT_FALSE@'#' .IP ++.if '@ENABLE_SECONTEXT_FALSE@'#' The default value for ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR \-\-secontext ++.if '@ENABLE_SECONTEXT_FALSE@'#' is ++.if '@ENABLE_SECONTEXT_FALSE@'#' .BR !full,mismatch ++.if '@ENABLE_SECONTEXT_FALSE@'#' which prints only the type instead of full context ++.if '@ENABLE_SECONTEXT_FALSE@'#' and doesn't check for context mismatches. + .SS Statistics + .TP 12 + .B \-c +diff --git a/m4/st_selinux.m4 b/m4/st_selinux.m4 +index 7b24eba..60e23a9 100644 +--- a/m4/st_selinux.m4 ++++ b/m4/st_selinux.m4 +@@ -35,7 +35,7 @@ AS_IF([test "x$with_libselinux" != xno], + [saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $libselinux_LDFLAGS" + missing= +- for func in getpidcon getfilecon; do ++ for func in getpidcon getfilecon selabel_open selabel_lookup; do + AC_CHECK_LIB([selinux], [$func], [:], + [missing="$missing $func"]) + done +diff --git a/src/filter_qualify.c b/src/filter_qualify.c +index df05496..a5b4fe7 100644 +--- a/src/filter_qualify.c ++++ b/src/filter_qualify.c +@@ -14,6 +14,9 @@ + #include "poke.h" + #include "retval.h" + #include "static_assert.h" ++#ifdef ENABLE_SECONTEXT ++# include "secontext.h" ++#endif + + struct number_set *read_set; + struct number_set *write_set; +@@ -591,6 +594,29 @@ qualify_kvm(const char *const str) + } + } + ++#ifdef ENABLE_SECONTEXT ++struct number_set *secontext_set; ++ ++static int ++secontextstr_to_uint(const char *s) ++{ ++ static const struct xlat_data secontext_strs[] = { ++ { SECONTEXT_FULL, "full" }, ++ { SECONTEXT_MISMATCH, "mismatch" }, ++ }; ++ ++ return (int) find_arg_val(s, secontext_strs, -1ULL, -1ULL); ++} ++ ++void ++qualify_secontext(const char *const str) ++{ ++ if (!secontext_set) ++ secontext_set = alloc_number_set_array(1); ++ qualify_tokens(str, secontext_set, secontextstr_to_uint, "secontext"); ++} ++#endif ++ + static const struct qual_options { + const char *name; + void (*qualify)(const char *); +@@ -622,6 +648,9 @@ static const struct qual_options { + { "kvm", qualify_kvm }, + { "decode-fd", qualify_decode_fd }, + { "decode-fds", qualify_decode_fd }, ++#ifdef ENABLE_SECONTEXT ++ { "secontext", qualify_secontext }, ++#endif + }; + + void +diff --git a/src/secontext.c b/src/secontext.c +index ccf9b34..9a91386 100644 +--- a/src/secontext.c ++++ b/src/secontext.c +@@ -10,14 +10,17 @@ + #include + #include + #include ++#include ++#include + #include ++#include + ++#include "largefile_wrappers.h" ++#include "number_set.h" + #include "secontext.h" ++#include "xmalloc.h" + #include "xstring.h" + +-bool selinux_context = false; +-bool selinux_context_full = false; +- + static int + getcontext(int rc, char **secontext, char **result) + { +@@ -25,7 +28,7 @@ getcontext(int rc, char **secontext, char **result) + return rc; + + *result = NULL; +- if (!selinux_context_full) { ++ if (!is_number_in_set(SECONTEXT_FULL, secontext_set)) { + char *saveptr = NULL; + char *secontext_copy = xstrdup(*secontext); + const char *token; +@@ -59,6 +62,36 @@ getcontext(int rc, char **secontext, char **result) + freecon(*secontext); + return 0; + } ++ ++static int ++get_expected_filecontext(const char *path, char **result) ++{ ++ static struct selabel_handle *hdl; ++ ++ if (!hdl) { ++ static bool disabled; ++ if (disabled) ++ return -1; ++ ++ hdl = selabel_open(SELABEL_CTX_FILE, NULL, 0); ++ if (!hdl) { ++ perror_msg("could not open SELinux database, disabling " ++ "context mismatch checking"); ++ disabled = true; ++ return -1; ++ } ++ } ++ ++ strace_stat_t stb; ++ if (stat_file(path, &stb) < 0) { ++ return -1; ++ } ++ ++ char *secontext; ++ return getcontext(selabel_lookup(hdl, &secontext, path, stb.st_mode), ++ &secontext, result); ++} ++ + /* + * Retrieves the SELinux context of the given PID (extracted from the tcb). + * Memory must be freed. +@@ -67,7 +100,7 @@ getcontext(int rc, char **secontext, char **result) + int + selinux_getpidcon(struct tcb *tcp, char **result) + { +- if (!selinux_context) ++ if (number_set_array_is_empty(secontext_set, 0)) + return -1; + + int proc_pid = 0; +@@ -87,7 +120,7 @@ selinux_getpidcon(struct tcb *tcp, char **result) + int + selinux_getfdcon(pid_t pid, int fd, char **result) + { +- if (!selinux_context || pid <= 0 || fd < 0) ++ if (number_set_array_is_empty(secontext_set, 0) || pid <= 0 || fd < 0) + return -1; + + int proc_pid = 0; +@@ -99,7 +132,33 @@ selinux_getfdcon(pid_t pid, int fd, char **result) + xsprintf(linkpath, "/proc/%u/fd/%u", proc_pid, fd); + + char *secontext; +- return getcontext(getfilecon(linkpath, &secontext), &secontext, result); ++ int rc = getcontext(getfilecon(linkpath, &secontext), &secontext, result); ++ if (rc < 0 || !is_number_in_set(SECONTEXT_MISMATCH, secontext_set)) ++ return rc; ++ ++ /* ++ * We need to resolve the path, because selabel_lookup() doesn't ++ * resolve anything. Using readlink() is sufficient here. ++ */ ++ ++ char buf[PATH_MAX]; ++ ssize_t n = readlink(linkpath, buf, sizeof(buf)); ++ if ((size_t) n >= sizeof(buf)) ++ return 0; ++ buf[n] = '\0'; ++ ++ char *expected; ++ if (get_expected_filecontext(buf, &expected) < 0) ++ return 0; ++ if (strcmp(expected, *result) == 0) { ++ free(expected); ++ return 0; ++ } ++ char *final_result = xasprintf("%s!!%s", *result, expected); ++ free(expected); ++ free(*result); ++ *result = final_result; ++ return 0; + } + + /* +@@ -110,7 +169,7 @@ selinux_getfdcon(pid_t pid, int fd, char **result) + int + selinux_getfilecon(struct tcb *tcp, const char *path, char **result) + { +- if (!selinux_context) ++ if (number_set_array_is_empty(secontext_set, 0)) + return -1; + + int proc_pid = 0; +@@ -118,22 +177,49 @@ selinux_getfilecon(struct tcb *tcp, const char *path, char **result) + if (!proc_pid) + return -1; + +- int ret = -1; ++ int rc = -1; + char fname[PATH_MAX]; + + if (path[0] == '/') +- ret = snprintf(fname, sizeof(fname), "/proc/%u/root%s", ++ rc = snprintf(fname, sizeof(fname), "/proc/%u/root%s", + proc_pid, path); + else if (tcp->last_dirfd == AT_FDCWD) +- ret = snprintf(fname, sizeof(fname), "/proc/%u/cwd/%s", ++ rc = snprintf(fname, sizeof(fname), "/proc/%u/cwd/%s", + proc_pid, path); + else if (tcp->last_dirfd >= 0 ) +- ret = snprintf(fname, sizeof(fname), "/proc/%u/fd/%u/%s", ++ rc = snprintf(fname, sizeof(fname), "/proc/%u/fd/%u/%s", + proc_pid, tcp->last_dirfd, path); + +- if ((unsigned int) ret >= sizeof(fname)) ++ if ((unsigned int) rc >= sizeof(fname)) + return -1; + + char *secontext; +- return getcontext(getfilecon(fname, &secontext), &secontext, result); ++ rc = getcontext(getfilecon(fname, &secontext), &secontext, result); ++ if (rc < 0 || !is_number_in_set(SECONTEXT_MISMATCH, secontext_set)) ++ return rc; ++ ++ /* ++ * We need to fully resolve the path, because selabel_lookup() doesn't ++ * resolve anything. Using realpath() is the only solution here to make ++ * sure the path is canonicalized. ++ */ ++ ++ char *resolved = realpath(fname, NULL); ++ if (!resolved) ++ return 0; ++ ++ char *expected; ++ rc = get_expected_filecontext(resolved, &expected); ++ free(resolved); ++ if (rc < 0) ++ return 0; ++ if (strcmp(expected, *result) == 0) { ++ free(expected); ++ return 0; ++ } ++ char *final_result = xasprintf("%s!!%s", *result, expected); ++ free(expected); ++ free(*result); ++ *result = final_result; ++ return 0; + } +diff --git a/src/secontext.h b/src/secontext.h +index 1ed88c7..39222d0 100644 +--- a/src/secontext.h ++++ b/src/secontext.h +@@ -11,11 +11,22 @@ + + # include "defs.h" + +-extern bool selinux_context; +-extern bool selinux_context_full; ++void qualify_secontext(const char *const str); ++ ++enum secontext_bits { ++ /* Display full context instead of type only */ ++ SECONTEXT_FULL, ++ /* Check for context mismatch */ ++ SECONTEXT_MISMATCH, ++ ++ NUMBER_OF_SECONTEXT_BITS ++}; ++ ++extern struct number_set *secontext_set; + + int selinux_getfdcon(pid_t pid, int fd, char **context); + int selinux_getfilecon(struct tcb *tcp, const char *path, char **context); + int selinux_getpidcon(struct tcb *tcp, char **context); ++void selinux_set_format(const char *optarg); + + #endif /* !STRACE_SECONTEXT_H */ +diff --git a/src/strace.c b/src/strace.c +index fb42fe9..0a18478 100644 +--- a/src/strace.c ++++ b/src/strace.c +@@ -264,9 +264,11 @@ usage(void) + # define K_OPT "" + #endif + #ifdef ENABLE_SECONTEXT +-# define SECONTEXT_OPT "[--secontext[=full]]\n" ++# define SECONTEXT_OPT " [--secontext[=FORMAT]]\n" ++# define SECONTEXT_E_QUAL ", secontext" + #else + # define SECONTEXT_OPT "" ++# define SECONTEXT_E_QUAL "" + #endif + + printf("\ +@@ -282,7 +284,7 @@ Usage: strace [-ACdffhi" K_OPT "qqrtttTvVwxxyyzZ] [-I N] [-b execve] [-e EXPR].. + General:\n\ + -e EXPR a qualifying expression: OPTION=[!]all or OPTION=[!]VAL1[,VAL2]...\n\ + options: trace, abbrev, verbose, raw, signal, read, write, fault,\n\ +- inject, status, quiet, kvm, decode-fds\n\ ++ inject, status, quiet, kvm, decode-fds" SECONTEXT_E_QUAL "\n\ + \n\ + Startup:\n\ + -E VAR=VAL, --env=VAR=VAL\n\ +@@ -358,6 +360,19 @@ Output format:\n\ + path (file path),\n\ + pidfd (associated PID for pidfds),\n\ + socket (protocol-specific information for socket descriptors)\n\ ++" ++#ifdef ENABLE_SECONTEXT ++"\ ++ -e secontext=FORMAT, --secontext[=FORMAT]\n\ ++ print SELinux contexts in square brackets\n\ ++ formats: comma-separated list of all, full, mismatch, none\n\ ++ all: equivalent to full,mismatch\n\ ++ full: print the full context instead of the type only\n\ ++ mismatch: print expected context when actual is not matching\n\ ++ none: equivalent to not specifying the option at all\n\ ++" ++#endif ++"\ + -i, --instruction-pointer\n\ + print instruction pointer at time of syscall\n\ + " +@@ -1991,6 +2006,9 @@ init(int argc, char *argv[]) + static const char tflag_str[] = "format:time"; + static const char ttflag_str[] = "precision:us,format:time"; + static const char tttflag_str[] = "format:unix,precision:us"; ++#ifdef ENABLE_SECONTEXT ++ static const char secontext_qual[] = "!full,mismatch"; ++#endif + + int c, i; + int optF = 0, zflags = 0; +@@ -2054,6 +2072,9 @@ init(int argc, char *argv[]) + qualify_quiet("none"); + qualify_decode_fd("none"); + qualify_signals("all"); ++#ifdef ENABLE_SECONTEXT ++ qualify_secontext("none"); ++#endif + + static const char optstring[] = + "+a:Ab:cCdDe:E:fFhiI:kno:O:p:P:qrs:S:tTu:U:vVwxX:yzZ"; +@@ -2066,9 +2087,6 @@ init(int argc, char *argv[]) + GETOPT_OUTPUT_SEPARATELY, + GETOPT_TS, + GETOPT_PIDNS_TRANSLATION, +-#ifdef ENABLE_SECONTEXT +- GETOPT_SECONTEXT, +-#endif + + GETOPT_QUAL_TRACE, + GETOPT_QUAL_ABBREV, +@@ -2083,6 +2101,9 @@ init(int argc, char *argv[]) + GETOPT_QUAL_KVM, + GETOPT_QUAL_QUIET, + GETOPT_QUAL_DECODE_FD, ++#ifdef ENABLE_SECONTEXT ++ GETOPT_QUAL_SECONTEXT, ++#endif + }; + static const struct option longopts[] = { + { "columns", required_argument, 0, 'a' }, +@@ -2125,9 +2146,6 @@ init(int argc, char *argv[]) + { "failed-only", no_argument, 0, 'Z' }, + { "failing-only", no_argument, 0, 'Z' }, + { "seccomp-bpf", no_argument, 0, GETOPT_SECCOMP }, +-#ifdef ENABLE_SECONTEXT +- { "secontext", optional_argument, 0, GETOPT_SECONTEXT }, +-#endif + + { "trace", required_argument, 0, GETOPT_QUAL_TRACE }, + { "abbrev", required_argument, 0, GETOPT_QUAL_ABBREV }, +@@ -2144,6 +2162,9 @@ init(int argc, char *argv[]) + { "silent", optional_argument, 0, GETOPT_QUAL_QUIET }, + { "silence", optional_argument, 0, GETOPT_QUAL_QUIET }, + { "decode-fds", optional_argument, 0, GETOPT_QUAL_DECODE_FD }, ++#ifdef ENABLE_SECONTEXT ++ { "secontext", optional_argument, 0, GETOPT_QUAL_SECONTEXT }, ++#endif + + { 0, 0, 0, 0 } + }; +@@ -2357,14 +2378,8 @@ init(int argc, char *argv[]) + seccomp_filtering = true; + break; + #ifdef ENABLE_SECONTEXT +- case GETOPT_SECONTEXT: +- selinux_context = true; +- if (optarg) { +- if (!strcmp(optarg, "full")) +- selinux_context_full = true; +- else +- error_opt_arg(c, lopt, optarg); +- } ++ case GETOPT_QUAL_SECONTEXT: ++ qualify_secontext(optarg ? optarg : secontext_qual); + break; + #endif + case GETOPT_QUAL_TRACE: +@@ -2550,7 +2565,7 @@ init(int argc, char *argv[]) + error_msg("-y/--decode-fds has no effect " + "with -c/--summary-only"); + #ifdef ENABLE_SECONTEXT +- if (selinux_context) ++ if (!number_set_array_is_empty(secontext_set, 0)) + error_msg("--secontext has no effect with " + "-c/--summary-only"); + #endif +diff --git a/tests/gen_tests.in b/tests/gen_tests.in +index 8b4e2e9..71e2f17 100644 +--- a/tests/gen_tests.in ++++ b/tests/gen_tests.in +@@ -12,6 +12,8 @@ accept4 -a37 + access -a30 --trace-path=access_sample + access--secontext -a30 --secontext --trace-path=access_sample -e trace=access + access--secontext_full -a30 --secontext=full --trace-path=access_sample -e trace=access ++access--secontext_full_mismatch -a30 --secontext=full,mismatch --trace-path=access_sample -e trace=access ++access--secontext_mismatch -a30 --secontext=mismatch --trace-path=access_sample -e trace=access + acct -a20 + add_key -a30 -s12 + adjtimex -a15 +@@ -27,8 +29,10 @@ bpf-v -a20 -v -e trace=bpf + btrfs +ioctl.test + chdir -a10 + chmod -a28 +-chmod--secontext -a28 --secontext -e trace=chmod +-chmod--secontext_full -a28 --secontext=full -e trace=chmod ++chmod--secontext -a28 -e secontext=!full,mismatch -e trace=chmod ++chmod--secontext_full -a28 -e secontext=full -e trace=chmod ++chmod--secontext_full_mismatch -a28 --secontext=mismatch,full -e trace=chmod ++chmod--secontext_mismatch -a28 --secontext=mismatch -e trace=chmod + chown -a28 + chown32 -a31 + chroot -a13 +@@ -84,16 +88,24 @@ epoll_wait -a26 + erestartsys -a34 -e signal=none -e trace=recvfrom + execve--secontext +execve.test --secontext + execve--secontext_full +execve.test --secontext=full ++execve--secontext_full_mismatch +execve.test --secontext=full,mismatch ++execve--secontext_mismatch +execve.test --secontext=mismatch + execveat + execveat--secontext --secontext --trace=execveat + execveat--secontext_full --secontext=full --trace=execveat ++execveat--secontext_full_mismatch --secontext=full,mismatch --trace=execveat ++execveat--secontext_mismatch --secontext=mismatch --trace=execveat + execveat-v -v -e trace=execveat + faccessat--secontext +faccessat.test -a24 --secontext + faccessat--secontext_full +faccessat.test -a24 --secontext=full ++faccessat--secontext_full_mismatch +faccessat.test -a24 --secontext=full,mismatch ++faccessat--secontext_mismatch +faccessat.test -a24 --secontext=mismatch + faccessat-P -a23 --trace=faccessat -P /dev/full + faccessat-y +faccessat.test -a24 -y + faccessat-y--secontext +faccessat.test -a24 -y --secontext + faccessat-y--secontext_full +faccessat.test -a24 -y --secontext=full ++faccessat-y--secontext_full_mismatch +faccessat.test -a24 -y --secontext=full,mismatch ++faccessat-y--secontext_mismatch +faccessat.test -a24 -y --secontext=mismatch + faccessat-yy +faccessat.test -a24 -yy + faccessat2-P -a27 --trace=faccessat2 -P /dev/full + faccessat2-y +faccessat2.test -a28 -y +@@ -104,6 +116,8 @@ fanotify_init + fanotify_mark -a32 + fanotify_mark--secontext -a32 --secontext -e trace=fanotify_mark + fanotify_mark--secontext_full -a32 --secontext=full -e trace=fanotify_mark ++fanotify_mark--secontext_full_mismatch -a32 --secontext=full,mismatch -e trace=fanotify_mark ++fanotify_mark--secontext_mismatch -a32 --secontext=mismatch -e trace=fanotify_mark + fanotify_mark-Xabbrev -a32 -Xabbrev -e trace=fanotify_mark + fanotify_mark-Xraw -a32 -Xraw -e trace=fanotify_mark + fanotify_mark-Xverbose -a32 -Xverbose -e trace=fanotify_mark +@@ -111,17 +125,25 @@ fchdir -a11 + fchmod -a15 + fchmod--secontext -a15 --secontext -e trace=fchmod + fchmod--secontext_full -a15 --secontext=full -e trace=fchmod ++fchmod--secontext_full_mismatch -a15 --secontext=full,mismatch -e trace=fchmod ++fchmod--secontext_mismatch -a15 --secontext=mismatch -e trace=fchmod + fchmod-y -y -e trace=fchmod + fchmod-y--secontext -a15 -y --secontext -e trace=fchmod + fchmod-y--secontext_full -a15 -y --secontext=full -e trace=fchmod ++fchmod-y--secontext_full_mismatch -a15 -y --secontext=full,mismatch -e trace=fchmod ++fchmod-y--secontext_mismatch -a15 -y --secontext=mismatch -e trace=fchmod + fchmodat + fchmodat--secontext --secontext -e trace=fchmodat + fchmodat--secontext_full --secontext=full -e trace=fchmodat ++fchmodat--secontext_full_mismatch --secontext=full,mismatch -e trace=fchmodat ++fchmodat--secontext_mismatch --secontext=mismatch -e trace=fchmodat + fchown -a16 + fchown32 -a18 + fchownat + fchownat--secontext --secontext -e trace=fchownat + fchownat--secontext_full --secontext=full -e trace=fchownat ++fchownat--secontext_full_mismatch -e secontext=full,mismatch -e trace=fchownat ++fchownat--secontext_mismatch -e secontext=mismatch -e trace=fchownat + fcntl -a8 + fcntl--pidns-translation test_pidns -a8 -e trace=fcntl + fcntl64 -a8 +@@ -130,6 +152,8 @@ fdatasync -a14 + file_handle -e trace=name_to_handle_at,open_by_handle_at + file_handle--secontext --secontext -e trace=name_to_handle_at,open_by_handle_at + file_handle--secontext_full --secontext=full -e trace=name_to_handle_at,open_by_handle_at ++file_handle--secontext_full_mismatch --secontext=full,mismatch -e trace=name_to_handle_at,open_by_handle_at ++file_handle--secontext_mismatch --secontext=mismatch -e trace=name_to_handle_at,open_by_handle_at + filter_seccomp . "${srcdir=.}/filter_seccomp.sh"; test_prog_set --seccomp-bpf -f + filter_seccomp-flag ../$NAME + finit_module -a25 +@@ -383,6 +407,8 @@ link + linkat + linkat--secontext --secontext -e trace=linkat + linkat--secontext_full --secontext=full -e trace=linkat ++linkat--secontext_full_mismatch --secontext=full,mismatch -e trace=linkat ++linkat--secontext_mismatch --secontext=mismatch -e trace=linkat + lookup_dcookie -a27 + lstat -a31 --no-abbrev --trace-path=stat.sample --trace-path=/dev/full + lstat64 -a32 --no-abbrev --trace-path=stat.sample --trace-path=/dev/full +@@ -526,11 +552,15 @@ oldstat -a32 -v -P stat.sample -P /dev/full + open -a30 -P $NAME.sample + open--secontext -a30 -P open.sample --secontext --trace=open + open--secontext_full -a30 -P open.sample --secontext=full --trace=open ++open--secontext_full_mismatch -a30 -P open.sample --secontext=full,mismatch --trace=open ++open--secontext_mismatch -a30 -P open.sample --secontext=mismatch --trace=open + open_tree -a30 -y + open_tree-P -a30 --decode-fds -P /dev/full -e trace=open_tree + openat -a36 -P $NAME.sample + openat--secontext -a36 -P openat.sample -P $PWD/openat.sample --secontext -e trace=openat + openat--secontext_full -a36 -P openat.sample -P $PWD/openat.sample --secontext=full -e trace=openat ++openat--secontext_full_mismatch -a36 -P openat.sample -P $PWD/openat.sample --secontext=full,mismatch -e trace=openat ++openat--secontext_mismatch -a36 -P openat.sample -P $PWD/openat.sample --secontext=mismatch -e trace=openat + openat2 -a35 + openat2-Xabbrev --trace=openat2 -a35 -Xabbrev + openat2-Xraw --trace=openat2 -a32 -Xraw +diff --git a/tests/linkat.c b/tests/linkat.c +index 1d41d3d..1a869e3 100644 +--- a/tests/linkat.c ++++ b/tests/linkat.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "secontext.h" + #include "xmalloc.h" +@@ -88,10 +89,42 @@ main(void) + perror_msg_and_fail("close"); + + free(sample_1_secontext); +- update_secontext_type(sample_1, "default_t"); ++ ++#ifdef PRINT_SECONTEXT_MISMATCH ++ update_secontext_field(sample_1, SECONTEXT_USER, "system_u"); ++ sample_1_secontext = SECONTEXT_FILE(sample_1); ++ ++# ifdef PRINT_SECONTEXT_FULL ++ /* The mismatch should be detected */ ++ if (*sample_1_secontext && strstr(sample_1_secontext, "!!") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ sample_1_secontext); ++ if (*sample_1_secontext && strstr(sample_1_secontext, "system_u") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ sample_1_secontext); ++# else ++ /* The mismatch cannot be detected since it's on user part */ ++ if (*sample_1_secontext && strstr(sample_1_secontext, "!!") != NULL) ++ perror_msg_and_fail("Context mismatch detected: %s", ++ sample_1_secontext); ++# endif ++ ++ free(sample_1_secontext); ++#endif ++ ++ update_secontext_field(sample_1, SECONTEXT_TYPE, "default_t"); + sample_1_secontext = SECONTEXT_FILE(sample_1); + sample_2_secontext = sample_1_secontext; + ++#ifdef PRINT_SECONTEXT_MISMATCH ++ if (*sample_1_secontext && strstr(sample_1_secontext, "!!") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ sample_1_secontext); ++ if (*sample_1_secontext && strstr(sample_1_secontext, "default_t") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ sample_1_secontext); ++#endif ++ + rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0); + printf("%s%s(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n", + my_secontext, "linkat", +@@ -108,8 +141,19 @@ main(void) + + int dfd_old = get_dir_fd("."); + char *cwd = get_fd_path(dfd_old); ++ ++ update_secontext_field(".", SECONTEXT_TYPE, "default_t"); + char *dfd_old_secontext = SECONTEXT_FILE("."); + ++#ifdef PRINT_SECONTEXT_MISMATCH ++ if (*dfd_old_secontext && strstr(dfd_old_secontext, "!!") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ dfd_old_secontext); ++ if (*dfd_old_secontext && strstr(dfd_old_secontext, "default_t") == NULL) ++ perror_msg_and_fail("Context mismatch not detected: %s", ++ dfd_old_secontext); ++#endif ++ + rc = syscall(__NR_linkat, dfd_old, sample_1, -100, sample_2, 0); + /* no context printed for sample_2 since file doesn't exist yet */ + printf("%s%s(%d%s, \"%s\"%s, AT_FDCWD, \"%s\", 0) = %s\n", +diff --git a/tests/options-syntax.test b/tests/options-syntax.test +index 765b2f8..848d297 100755 +--- a/tests/options-syntax.test ++++ b/tests/options-syntax.test +@@ -48,8 +48,18 @@ check_e '-t and --absolute-timestamps cannot be provided simultaneously' -t --ti + check_e '-t and --absolute-timestamps cannot be provided simultaneously' --absolute-timestamps -ttt -p $$ + check_e '-t and --absolute-timestamps cannot be provided simultaneously' -t --timestamps=ns -t -p $$ + check_e '-t and --absolute-timestamps cannot be provided simultaneously' --timestamps=ns -t --absolute-timestamps=unix -p $$ +-[ -z "$compiled_with_secontext" ] || +- check_h "invalid --secontext argument: 'ss'" --secontext=ss ++if [ -n "$compiled_with_secontext" ]; then ++ for opt in '--secontext' '-e secontext'; do ++ check_e "invalid secontext ''" $opt= ++ check_e "invalid secontext 'ss'" $opt=ss ++ check_e "invalid secontext 'ss'" $opt=ss,full,mismatch ++ check_e "invalid secontext 'ss'" $opt=full,ss,mismatch ++ check_e "invalid secontext 'ss'" $opt=full,ss ++ check_e "invalid secontext 'ss'" $opt=full,mismatch,ss ++ check_e "invalid secontext 'ss'" $opt=!full,ss ++ check_e "invalid secontext 'ss'" $opt=!full,mismatch,ss ++ done ++fi + check_h 'PROG [ARGS] must be specified with -D/--daemonize' -D -p $$ + check_h 'PROG [ARGS] must be specified with -D/--daemonize' -DD -p $$ + check_h 'PROG [ARGS] must be specified with -D/--daemonize' -DDD -p $$ +diff --git a/tests/secontext.c b/tests/secontext.c +index 21c6370..848eea9 100644 +--- a/tests/secontext.c ++++ b/tests/secontext.c +@@ -13,8 +13,10 @@ + # include + # include + # include ++# include + # include + # include ++# include + + # include "xmalloc.h" + +@@ -55,6 +57,79 @@ strip_trailing_newlines(char *context) + } + + static char * ++get_type_from_context(const char *full_context) ++{ ++ int saved_errno = errno; ++ ++ if (!full_context) ++ return NULL; ++ ++ char *saveptr = NULL; ++ const char *token; ++ unsigned int i; ++ ++ char *ctx_copy = xstrdup(full_context); ++ char *context = NULL; ++ for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; ++ token; token = strtok_r(NULL, ":", &saveptr), i++) { ++ if (i == 2) { ++ context = xstrdup(token); ++ break; ++ } ++ } ++ if (!context) ++ context = xstrdup(full_context); ++ free(ctx_copy); ++ ++ errno = saved_errno; ++ return context; ++} ++ ++static char * ++raw_expected_secontext_full_file(const char *filename) ++{ ++ int saved_errno = errno; ++ char *secontext; ++ ++ static struct selabel_handle *hdl; ++ if (!hdl) { ++ hdl = selabel_open(SELABEL_CTX_FILE, NULL, 0); ++ if (!hdl) ++ perror_msg_and_skip("selabel_open"); ++ } ++ ++ char *resolved = realpath(filename, NULL); ++ if (!resolved) ++ perror_msg_and_fail("realpath: %s", filename); ++ ++ struct stat statbuf; ++ if (stat(resolved, &statbuf) < 0) ++ perror_msg_and_fail("stat: %s", resolved); ++ ++ if (selabel_lookup(hdl, &secontext, resolved, statbuf.st_mode) < 0) ++ perror_msg_and_skip("selabel_lookup: %s", resolved); ++ free(resolved); ++ ++ char *full_secontext = xstrdup(secontext); ++ freecon(secontext); ++ errno = saved_errno; ++ return full_secontext; ++} ++ ++static char * ++raw_expected_secontext_short_file(const char *filename) ++{ ++ int saved_errno = errno; ++ ++ char *ctx = raw_expected_secontext_full_file(filename); ++ char *type = get_type_from_context(ctx); ++ free(ctx); ++ ++ errno = saved_errno; ++ return type; ++} ++ ++static char * + raw_secontext_full_file(const char *filename) + { + int saved_errno = errno; +@@ -75,29 +150,11 @@ raw_secontext_short_file(const char *filename) + int saved_errno = errno; + + char *ctx = raw_secontext_full_file(filename); +- if (ctx == NULL) +- return ctx; +- +- char *saveptr = NULL; +- const char *token; +- unsigned int i; +- +- char *ctx_copy = xstrdup(ctx); +- char *context = NULL; +- for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; +- token; token = strtok_r(NULL, ":", &saveptr), i++) { +- if (i == 2) { +- context = xstrdup(token); +- break; +- } +- } +- if (context == NULL) +- context = xstrdup(ctx); +- free(ctx_copy); ++ char *type = get_type_from_context(ctx); + free(ctx); + + errno = saved_errno; +- return context; ++ return type; + } + + static char * +@@ -121,35 +178,30 @@ raw_secontext_short_pid(pid_t pid) + int saved_errno = errno; + + char *ctx = raw_secontext_full_pid(pid); +- if (ctx == NULL) +- return ctx; +- +- char *saveptr = NULL; +- const char *token; +- int i; +- +- char *ctx_copy = xstrdup(ctx); +- char *context = NULL; +- for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; +- token; token = strtok_r(NULL, ":", &saveptr), i++) { +- if (i == 2) { +- context = xstrdup(token); +- break; +- } +- } +- if (context == NULL) +- context = xstrdup(ctx); +- free(ctx_copy); ++ char *type = get_type_from_context(ctx); + free(ctx); + + errno = saved_errno; +- return context; ++ return type; + } + + char * +-secontext_full_file(const char *filename) ++secontext_full_file(const char *filename, bool mismatch) + { +- return FORMAT_SPACE_BEFORE(raw_secontext_full_file(filename)); ++ int saved_errno = errno; ++ char *context = raw_secontext_full_file(filename); ++ if (context && mismatch) { ++ char *expected = raw_expected_secontext_full_file(filename); ++ if (expected && strcmp(context, expected)) { ++ char *context_mismatch = ++ xasprintf("%s!!%s", context, expected); ++ free(context); ++ context = context_mismatch; ++ } ++ free(expected); ++ } ++ errno = saved_errno; ++ return FORMAT_SPACE_BEFORE(context); + } + + char * +@@ -159,9 +211,22 @@ secontext_full_pid(pid_t pid) + } + + char * +-secontext_short_file(const char *filename) ++secontext_short_file(const char *filename, bool mismatch) + { +- return FORMAT_SPACE_BEFORE(raw_secontext_short_file(filename)); ++ int saved_errno = errno; ++ char *context = raw_secontext_short_file(filename); ++ if (context && mismatch) { ++ char *expected = raw_expected_secontext_short_file(filename); ++ if (expected && strcmp(context, expected)) { ++ char *context_mismatch = ++ xasprintf("%s!!%s", context, expected); ++ free(context); ++ context = context_mismatch; ++ } ++ free(expected); ++ } ++ errno = saved_errno; ++ return FORMAT_SPACE_BEFORE(context); + } + + char * +@@ -171,31 +236,38 @@ secontext_short_pid(pid_t pid) + } + + void +-update_secontext_type(const char *file, const char *newtype) ++update_secontext_field(const char *file, enum secontext_field field, ++ const char *newvalue) + { ++ int saved_errno = errno; ++ assert(field >= SECONTEXT_USER && field <= SECONTEXT_TYPE); ++ + char *ctx = raw_secontext_full_file(file); + if (ctx == NULL) + return; + + char *saveptr = NULL; + char *token; +- int field; ++ int nfields; + char *split[4]; + +- for (token = strtok_r(ctx, ":", &saveptr), field = 0; +- token; token = strtok_r(NULL, ":", &saveptr), field++) { +- assert(field < 4); +- split[field] = token; ++ for (token = strtok_r(ctx, ":", &saveptr), nfields = 0; ++ token; token = strtok_r(NULL, ":", &saveptr), nfields++) { ++ assert(nfields < 4); ++ split[nfields] = token; + } +- assert(field == 4); ++ assert(nfields == 4); ++ ++ split[field] = (char *)newvalue; + + char *newcontext = xasprintf("%s:%s:%s:%s", split[0], split[1], +- newtype, split[3]); ++ split[2], split[3]); + + (void) setfilecon(file, newcontext); + + free(newcontext); + free(ctx); ++ errno = saved_errno; + } + + #endif /* HAVE_SELINUX_RUNTIME */ +diff --git a/tests/secontext.h b/tests/secontext.h +index c65f53a..1d0251a 100644 +--- a/tests/secontext.h ++++ b/tests/secontext.h +@@ -9,24 +9,39 @@ + #include "xmalloc.h" + #include + +-char *secontext_full_file(const char *) ATTRIBUTE_MALLOC; ++char *secontext_full_file(const char *, bool) ATTRIBUTE_MALLOC; + char *secontext_full_pid(pid_t) ATTRIBUTE_MALLOC; + +-char *secontext_short_file(const char *) ATTRIBUTE_MALLOC; ++char *secontext_short_file(const char *, bool) ATTRIBUTE_MALLOC; + char *secontext_short_pid(pid_t) ATTRIBUTE_MALLOC; + ++enum secontext_field { ++ SECONTEXT_USER, ++ SECONTEXT_ROLE, ++ SECONTEXT_TYPE ++}; ++ + #if defined TEST_SECONTEXT && defined HAVE_SELINUX_RUNTIME + +-void update_secontext_type(const char *file, const char *newtype); ++void update_secontext_field(const char *file, enum secontext_field field, ++ const char *newvalue); + + # ifdef PRINT_SECONTEXT_FULL + +-# define SECONTEXT_FILE(filename) secontext_full_file(filename) ++# ifdef PRINT_SECONTEXT_MISMATCH ++# define SECONTEXT_FILE(filename) secontext_full_file(filename, true) ++# else ++# define SECONTEXT_FILE(filename) secontext_full_file(filename, false) ++# endif + # define SECONTEXT_PID(pid) secontext_full_pid(pid) + + # else + +-# define SECONTEXT_FILE(filename) secontext_short_file(filename) ++# ifdef PRINT_SECONTEXT_MISMATCH ++# define SECONTEXT_FILE(filename) secontext_short_file(filename, true) ++# else ++# define SECONTEXT_FILE(filename) secontext_short_file(filename, false) ++# endif + # define SECONTEXT_PID(pid) secontext_short_pid(pid) + + # endif +@@ -34,7 +49,8 @@ void update_secontext_type(const char *file, const char *newtype); + #else + + static inline void +-update_secontext_type(const char *file, const char *newtype) ++update_secontext_field(const char *file, enum secontext_field field, ++ const char *newvalue) + { + } + +-- +2.1.4 + diff --git a/0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch b/0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch new file mode 100644 index 0000000..c2e1651 --- /dev/null +++ b/0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch @@ -0,0 +1,54 @@ +From f5fd689e40322a7b08a97eb2d26f192610728230 Mon Sep 17 00:00:00 2001 +From: Eugene Syromyatnikov +Date: Tue, 18 Jan 2022 16:10:28 +0100 +Subject: [PATCH 170/174] tests/linkat: reset errno before SELinux context + manipulation + +To avoid printing a stale error information in case of mismatch check +failure. + +* tests/linkat.c: Include . +(main): Add "errno = 0" before update_secontext_field calls. +--- + tests/linkat.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tests/linkat.c b/tests/linkat.c +index 1a869e3..c3e2ee4 100644 +--- a/tests/linkat.c ++++ b/tests/linkat.c +@@ -10,6 +10,7 @@ + #include "tests.h" + #include "scno.h" + ++#include + #include + #include + #include +@@ -91,6 +92,7 @@ main(void) + free(sample_1_secontext); + + #ifdef PRINT_SECONTEXT_MISMATCH ++ errno = 0; + update_secontext_field(sample_1, SECONTEXT_USER, "system_u"); + sample_1_secontext = SECONTEXT_FILE(sample_1); + +@@ -112,6 +114,7 @@ main(void) + free(sample_1_secontext); + #endif + ++ errno = 0; + update_secontext_field(sample_1, SECONTEXT_TYPE, "default_t"); + sample_1_secontext = SECONTEXT_FILE(sample_1); + sample_2_secontext = sample_1_secontext; +@@ -142,6 +145,7 @@ main(void) + int dfd_old = get_dir_fd("."); + char *cwd = get_fd_path(dfd_old); + ++ errno = 0; + update_secontext_field(".", SECONTEXT_TYPE, "default_t"); + char *dfd_old_secontext = SECONTEXT_FILE("."); + +-- +2.1.4 + diff --git a/0171-tests-secontext-add-secontext-field-getters.patch b/0171-tests-secontext-add-secontext-field-getters.patch new file mode 100644 index 0000000..538856b --- /dev/null +++ b/0171-tests-secontext-add-secontext-field-getters.patch @@ -0,0 +1,134 @@ +From 4951286eb634c00c11883b851c91f3a21975eabd Mon Sep 17 00:00:00 2001 +From: Eugene Syromyatnikov +Date: Tue, 18 Jan 2022 18:03:57 +0100 +Subject: [PATCH 171/174] tests/secontext: add secontext field getters + +* tests/secontext.h (get_secontext_field, get_secontext_field_file): New +declarations. +* tests/secontext.c (get_type_from_context): Rename to... +(get_secontext_field): ...this; remove "static" qualifier; add "field" +argument, use it. +(raw_expected_secontext_short_file, raw_secontext_short_pid): Replace +get_type_from_context call with get_secontext_field. +(get_secontext_field_file): New function. +(raw_secontext_short_file): Replace body with get_secontext_field_file +call. +--- + tests/secontext.c | 27 +++++++++++++++------------ + tests/secontext.h | 20 ++++++++++++++++++++ + 2 files changed, 35 insertions(+), 12 deletions(-) + +diff --git a/tests/secontext.c b/tests/secontext.c +index 848eea9..52211ed 100644 +--- a/tests/secontext.c ++++ b/tests/secontext.c +@@ -56,8 +56,8 @@ strip_trailing_newlines(char *context) + return context; + } + +-static char * +-get_type_from_context(const char *full_context) ++char * ++get_secontext_field(const char *full_context, enum secontext_field field) + { + int saved_errno = errno; + +@@ -72,7 +72,7 @@ get_type_from_context(const char *full_context) + char *context = NULL; + for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0; + token; token = strtok_r(NULL, ":", &saveptr), i++) { +- if (i == 2) { ++ if (i == field) { + context = xstrdup(token); + break; + } +@@ -122,7 +122,7 @@ raw_expected_secontext_short_file(const char *filename) + int saved_errno = errno; + + char *ctx = raw_expected_secontext_full_file(filename); +- char *type = get_type_from_context(ctx); ++ char *type = get_secontext_field(ctx, SECONTEXT_TYPE); + free(ctx); + + errno = saved_errno; +@@ -144,20 +144,23 @@ raw_secontext_full_file(const char *filename) + return full_secontext; + } + +-static char * +-raw_secontext_short_file(const char *filename) ++char * ++get_secontext_field_file(const char *file, enum secontext_field field) + { +- int saved_errno = errno; +- +- char *ctx = raw_secontext_full_file(filename); +- char *type = get_type_from_context(ctx); ++ char *ctx = raw_secontext_full_file(file); ++ char *type = get_secontext_field(ctx, field); + free(ctx); + +- errno = saved_errno; + return type; + } + + static char * ++raw_secontext_short_file(const char *filename) ++{ ++ return get_secontext_field_file(filename, SECONTEXT_TYPE); ++} ++ ++static char * + raw_secontext_full_pid(pid_t pid) + { + int saved_errno = errno; +@@ -178,7 +181,7 @@ raw_secontext_short_pid(pid_t pid) + int saved_errno = errno; + + char *ctx = raw_secontext_full_pid(pid); +- char *type = get_type_from_context(ctx); ++ char *type = get_secontext_field(ctx, SECONTEXT_TYPE); + free(ctx); + + errno = saved_errno; +diff --git a/tests/secontext.h b/tests/secontext.h +index 1d0251a..e5571d5 100644 +--- a/tests/secontext.h ++++ b/tests/secontext.h +@@ -23,6 +23,15 @@ enum secontext_field { + + #if defined TEST_SECONTEXT && defined HAVE_SELINUX_RUNTIME + ++/** ++ * Parse a SELinux context string and return a specified field, duplicated ++ * in a separate string. The caller is responsible for freeing the memory ++ * pointed by the returned value. ++ */ ++char *get_secontext_field(const char *full_context, enum secontext_field field); ++ ++char *get_secontext_field_file(const char *file, enum secontext_field field); ++ + void update_secontext_field(const char *file, enum secontext_field field, + const char *newvalue); + +@@ -48,6 +57,17 @@ void update_secontext_field(const char *file, enum secontext_field field, + + #else + ++static inline char * ++get_secontext_field(const char *ctx, enum secontext_field field) ++{ ++ return NULL; ++} ++static inline char * ++get_secontext_field_file(const char *file, enum secontext_field field) ++{ ++ return NULL; ++} ++ + static inline void + update_secontext_field(const char *file, enum secontext_field field, + const char *newvalue) +-- +2.1.4 + diff --git a/0172-tests-linkat-provide-fallback-values-for-secontext-f.patch b/0172-tests-linkat-provide-fallback-values-for-secontext-f.patch new file mode 100644 index 0000000..f35b881 --- /dev/null +++ b/0172-tests-linkat-provide-fallback-values-for-secontext-f.patch @@ -0,0 +1,71 @@ +From 97e2742a7f1e6e113354911d04505ada3bfb5d70 Mon Sep 17 00:00:00 2001 +From: Eugene Syromyatnikov +Date: Tue, 18 Jan 2022 18:04:42 +0100 +Subject: [PATCH 172/174] tests/linkat: provide fallback values for secontext + fields changes + +* tests/linkat.c (mangle_secontext_field): New function. +(main): Replace calls to update_secontext_field +with mangle_secontext_field calls. +--- + tests/linkat.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/tests/linkat.c b/tests/linkat.c +index c3e2ee4..decb736 100644 +--- a/tests/linkat.c ++++ b/tests/linkat.c +@@ -21,6 +21,20 @@ + #include "secontext.h" + #include "xmalloc.h" + ++static void ++mangle_secontext_field(const char *path, enum secontext_field field, ++ const char *new_val, const char *fallback_val) ++{ ++ char *orig = get_secontext_field_file(path, field); ++ if (!orig) ++ return; ++ ++ update_secontext_field(path, field, ++ strcmp(new_val, orig) ? new_val : fallback_val); ++ ++ free(orig); ++} ++ + int + main(void) + { +@@ -93,7 +107,8 @@ main(void) + + #ifdef PRINT_SECONTEXT_MISMATCH + errno = 0; +- update_secontext_field(sample_1, SECONTEXT_USER, "system_u"); ++ mangle_secontext_field(sample_1, SECONTEXT_USER, "system_u", ++ "unconfined_u"); + sample_1_secontext = SECONTEXT_FILE(sample_1); + + # ifdef PRINT_SECONTEXT_FULL +@@ -115,7 +130,8 @@ main(void) + #endif + + errno = 0; +- update_secontext_field(sample_1, SECONTEXT_TYPE, "default_t"); ++ mangle_secontext_field(sample_1, SECONTEXT_TYPE, "default_t", ++ "unconfined_t"); + sample_1_secontext = SECONTEXT_FILE(sample_1); + sample_2_secontext = sample_1_secontext; + +@@ -146,7 +162,8 @@ main(void) + char *cwd = get_fd_path(dfd_old); + + errno = 0; +- update_secontext_field(".", SECONTEXT_TYPE, "default_t"); ++ mangle_secontext_field(".", SECONTEXT_TYPE, "default_t", ++ "unconfined_t"); + char *dfd_old_secontext = SECONTEXT_FILE("."); + + #ifdef PRINT_SECONTEXT_MISMATCH +-- +2.1.4 + diff --git a/0173-tests-secontext-eliminate-separate-secontext_format-.patch b/0173-tests-secontext-eliminate-separate-secontext_format-.patch new file mode 100644 index 0000000..18079f3 --- /dev/null +++ b/0173-tests-secontext-eliminate-separate-secontext_format-.patch @@ -0,0 +1,31 @@ +From 6e8aa3749cb7e11e9a59db996f79f036bf7ef263 Mon Sep 17 00:00:00 2001 +From: Eugene Syromyatnikov +Date: Tue, 18 Jan 2022 18:05:19 +0100 +Subject: [PATCH 173/174] tests/secontext: eliminate separate secontext_format + declaration + +* tests/secontext.c (secontext_format): Remove declaration, supply +the attributes to the definition. +--- + tests/secontext.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/tests/secontext.c b/tests/secontext.c +index 52211ed..ba271c8 100644 +--- a/tests/secontext.c ++++ b/tests/secontext.c +@@ -23,10 +23,7 @@ + # define TEST_SECONTEXT + # include "secontext.h" + +-static char * +-secontext_format(char *context, const char *fmt) +- ATTRIBUTE_FORMAT((printf, 2, 0)) ATTRIBUTE_MALLOC; +- ++ATTRIBUTE_FORMAT((printf, 2, 0)) ATTRIBUTE_MALLOC + static char * + secontext_format(char *context, const char *fmt) + { +-- +2.1.4 + diff --git a/0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch b/0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch new file mode 100644 index 0000000..b39cfc7 --- /dev/null +++ b/0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch @@ -0,0 +1,76 @@ +From 78a81bcfb71ef3d9f6e8b1a32e123fbbc6112a60 Mon Sep 17 00:00:00 2001 +From: Eugene Syromyatnikov +Date: Tue, 18 Jan 2022 18:24:34 +0100 +Subject: [PATCH 174/174] tests/linkat: reset context to the expected one if a + mismatch has been detected + +* tests/secontext.h (reset_secontext_file): New declaration. +* tests/secontext.c (reset_secontext_file): New function. +* tests/linkat.c (main): Check that there is no initial mismatch +in the sample_1 context, reset it otherwise. +--- + tests/linkat.c | 3 +++ + tests/secontext.c | 7 +++++++ + tests/secontext.h | 7 +++++++ + 3 files changed, 17 insertions(+) + +diff --git a/tests/linkat.c b/tests/linkat.c +index decb736..781b85a 100644 +--- a/tests/linkat.c ++++ b/tests/linkat.c +@@ -103,6 +103,9 @@ main(void) + if (close(fd_sample_2)) + perror_msg_and_fail("close"); + ++ if (*sample_1_secontext && strstr(sample_1_secontext, "!!")) ++ reset_secontext_file(sample_1); ++ + free(sample_1_secontext); + + #ifdef PRINT_SECONTEXT_MISMATCH +diff --git a/tests/secontext.c b/tests/secontext.c +index ba271c8..94fadd4 100644 +--- a/tests/secontext.c ++++ b/tests/secontext.c +@@ -235,6 +235,13 @@ secontext_short_pid(pid_t pid) + return FORMAT_SPACE_AFTER(raw_secontext_short_pid(pid)); + } + ++void reset_secontext_file(const char *file) ++{ ++ char *proper_ctx = raw_expected_secontext_full_file(file); ++ (void) setfilecon(file, proper_ctx); ++ free(proper_ctx); ++} ++ + void + update_secontext_field(const char *file, enum secontext_field field, + const char *newvalue) +diff --git a/tests/secontext.h b/tests/secontext.h +index e5571d5..387263e 100644 +--- a/tests/secontext.h ++++ b/tests/secontext.h +@@ -32,6 +32,8 @@ char *get_secontext_field(const char *full_context, enum secontext_field field); + + char *get_secontext_field_file(const char *file, enum secontext_field field); + ++void reset_secontext_file(const char *file); ++ + void update_secontext_field(const char *file, enum secontext_field field, + const char *newvalue); + +@@ -69,6 +71,11 @@ get_secontext_field_file(const char *file, enum secontext_field field) + } + + static inline void ++reset_secontext_file(const char *file) ++{ ++} ++ ++static inline void + update_secontext_field(const char *file, enum secontext_field field, + const char *newvalue) + { +-- +2.1.4 + diff --git a/strace.spec b/strace.spec index 329ce61..e80d649 100644 --- a/strace.spec +++ b/strace.spec @@ -1,7 +1,7 @@ Summary: Tracks and displays system calls associated with a running process Name: strace Version: 5.13 -Release: 5%{?dist} +Release: 6%{?dist} # The test suite is GPLv2+, all the rest is LGPLv2.1+. License: LGPL-2.1+ and GPL-2.0+ # Some distros require Group tag to be present, @@ -57,6 +57,16 @@ Patch154: 0154-tests-call-setsockopt-directly-in-sockopt-timestamp.patch # v5.15~1 "print_ifindex: fix IFNAME_QUOTED_SZ definition" Patch167: 0167-print_ifindex-fix-IFNAME_QUOTED_SZ-definition.patch +# v5.15~18 "m4: fix st_SELINUX check" +Patch168: 0168-m4-fix-st_SELINUX-check.patch +# v5.16~31 "Implement displaying of expected context upon mismatch" +Patch169: 0169-Implement-displaying-of-expected-context-upon-mismat.patch +Patch170: 0170-tests-linkat-reset-errno-before-SELinux-context-mani.patch +Patch171: 0171-tests-secontext-add-secontext-field-getters.patch +Patch172: 0172-tests-linkat-provide-fallback-values-for-secontext-f.patch +Patch173: 0173-tests-secontext-eliminate-separate-secontext_format-.patch +Patch174: 0174-tests-linkat-reset-context-to-the-expected-one-if-a-.patch + # Fallback definitions for make_build/make_install macros %{?!__make: %global __make %_bindir/make} %{?!__install: %global __install %_bindir/install} @@ -85,6 +95,14 @@ received by a process. %patch167 -p1 +%patch168 -p1 +%patch169 -p1 +%patch170 -p1 +%patch171 -p1 +%patch172 -p1 +%patch173 -p1 +%patch174 -p1 + echo -n %version-%release > .tarball-version echo -n 2021 > .year echo -n 2021-07-20 > doc/.strace.1.in.date @@ -141,6 +159,9 @@ echo 'END OF TEST SUITE INFORMATION' %{_mandir}/man1/* %changelog +* Wed Jan 19 2022 Eugene Syromiatnikov - 5.13-6 +- Add --secontext=mismatch option support (#2038965). + * Wed Jan 05 2022 Eugene Syromiatnikov - 5.13-5 - Fix incorrect ifname printing buffer size (#2028166).