1162 lines
39 KiB
Diff
1162 lines
39 KiB
Diff
From 8b07b18a44f7e0ebdb65b791d79d588dd90b70b6 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
|
|
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 <sys/stat.h>, <unistd.h>, <selinux/label.h>,
|
|
"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] <GETOPT_QUAL_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 <string.h>.
|
|
(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 <sys/stat.h> and <selinux/label.h>.
|
|
(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 <ldv@strace.io>
|
|
|
|
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 <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
+#include <sys/stat.h>
|
|
+#include <unistd.h>
|
|
#include <selinux/selinux.h>
|
|
+#include <selinux/label.h>
|
|
|
|
+#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 <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
+#include <string.h>
|
|
|
|
#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 <errno.h>
|
|
# include <stdlib.h>
|
|
# include <string.h>
|
|
+# include <sys/stat.h>
|
|
# include <unistd.h>
|
|
# include <selinux/selinux.h>
|
|
+# include <selinux/label.h>
|
|
|
|
# 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 <unistd.h>
|
|
|
|
-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
|
|
|