Blob Blame History Raw
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