Proposing SELinux support in strace

Dmitry V. Levin ldv at altlinux.org
Fri Mar 19 01:41:24 UTC 2021


On Fri, Mar 19, 2021 at 02:18:35AM +0300, Dmitry V. Levin wrote:
> On Thu, Mar 18, 2021 at 04:55:13PM +0100, Renaud Métrich wrote:
> > Hello, I've just realized that the limitation on the mount namespace is 
> > a real blocker in real world:
> > 
> > it appears that on systemd systems, all systemd services making use of 
> > PrivateTmp get their own namespace (due to having their own /var/tmp), 
> > which prevents the --secontext option from working for all files.
> 
> The very minimal fix that's readily available is for printing selinux
> context associated with descriptors.  Since it can be obtained right from
> procfs without falling back to path names, it should not be affected by
> any differences in mountns or rootfs between strace and its tracee.

Assuming this works, let's see what could be done with file names:
- if the filename starts with "/", we can construct a new name as
  /proc/pid/root/filename and getfilecon it;
- otherwise, if last_dirfd == AT_FDCWD, we can construct a new name as
  /proc/pid/cwd/filename and getfilecon it;
- otherwise, we can construct a new name as
  /proc/pid/fd/last_dirfd/filename and getfilecon it.

Could you check whether this approach would work good enough, please?

diff --git a/doc/strace.1.in b/doc/strace.1.in
index e6ad7a9c0..15d8e2531 100644
--- a/doc/strace.1.in
+++ b/doc/strace.1.in
@@ -1093,11 +1093,6 @@ strace's namespace, too.
 .if '@USE_SELINUX_FALSE@'#' .B full
 .if '@USE_SELINUX_FALSE@'#' is specified, print the complete context (user,
 .if '@USE_SELINUX_FALSE@'#' role, type and category) instead of just the type.
-.if '@USE_SELINUX_FALSE@'#' The current implementation has limitations, in
-.if '@USE_SELINUX_FALSE@'#' particular, SELinux contexts cannot be printed for
-.if '@USE_SELINUX_FALSE@'#' file names when
-.if '@USE_SELINUX_FALSE@'#' .B strace
-.if '@USE_SELINUX_FALSE@'#' and the tracee belong to different mount namespaces.
 .SS Statistics
 .TP 12
 .B \-c
diff --git a/src/selinux.c b/src/selinux.c
index 0f1edfe28..b62d40324 100644
--- a/src/selinux.c
+++ b/src/selinux.c
@@ -103,52 +103,27 @@ selinux_getfilecon(struct tcb *tcp, const char *path, char **result)
 	if (!selinux_context)
 		return -1;
 
-	/*
-	 * Current limitation: we cannot query the path if we are in different
-	 * mount namespaces.
-	 */
-	if (get_mnt_ns(tcp) != get_our_mnt_ns())
+	int proc_pid = 0;
+	translate_pid(NULL, tcp->pid, PT_TID, &proc_pid);
+	if (!proc_pid)
 		return -1;
 
-	char *secontext;
+	int ret = -1;
+	char fname[PATH_MAX];
 
 	if (path[0] == '/')
-		return getcontext(getfilecon(path, &secontext), &secontext, result);
-
-	char resolved[PATH_MAX + 1];
-
-	/*
-	 * If we have a relative pathname and 'last_dirfd' == AT_FDCWD, we need
-	 * to prepend by the CWD.
-	 */
-	if (tcp->last_dirfd == AT_FDCWD) {
-		int proc_pid = 0;
-		translate_pid(NULL, tcp->pid, PT_TID, &proc_pid);
-		if (!proc_pid)
-			return -1;
-
-		char linkpath[sizeof("/proc/%u/cwd") + sizeof(int)*3];
-		xsprintf(linkpath, "/proc/%u/cwd", proc_pid);
-
-		ssize_t n = readlink(linkpath, resolved, sizeof(resolved) - 1);
-		/*
-		 * NB: if buffer is too small, readlink doesn't fail,
-		 * it returns truncated result.
-		 */
-		if (n == sizeof(resolved) - 1)
-			return -1;
-		resolved[n] = '\0';
-	} else {
-		if (getfdpath_pid(tcp->pid, tcp->last_dirfd,
-				  resolved, sizeof(resolved)) < 0)
-			return -1;
-	}
-
-	if (resolved[0] != '/')
+		ret = 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",
+			       proc_pid, path);
+	else if (tcp->last_dirfd >= 0 )
+		ret = snprintf(fname, sizeof(fname), "/proc/%u/fd/%u/%s",
+			       proc_pid, tcp->last_dirfd, path);
+
+	if ((unsigned int) ret >= sizeof(fname))
 		return -1;
 
-	char pathtoresolve[2 * PATH_MAX + 2];
-	xsprintf(pathtoresolve, "%s/%s", resolved, path);
-
-	return getcontext(getfilecon(pathtoresolve, &secontext), &secontext, result);
+	char *secontext;
+	return getcontext(getfilecon(fname, &secontext), &secontext, result);
 }
diff --git a/tests/fchmod--secontext.c b/tests/fchmod--secontext.c
index 13434544c..3b6681153 100644
--- a/tests/fchmod--secontext.c
+++ b/tests/fchmod--secontext.c
@@ -37,19 +37,20 @@ main(void)
 	if (fname_realpath == NULL)
 		perror_msg_and_fail("realpath");
 
+	const char *fname_context = SELINUX_FILECONTEXT(fname);
 	long rc = syscall(__NR_fchmod, fd, 0600);
 	printf("%sfchmod(%d<%s>%s, 0600) = %s\n",
 	       SELINUX_MYCONTEXT(),
-	       fd, fname_realpath, SELINUX_FILECONTEXT(fname),
+	       fd, fname_realpath, fname_context,
 	       sprintrc(rc));
 
 	if (unlink(fname))
 		perror_msg_and_fail("unlink");
 
 	rc = syscall(__NR_fchmod, fd, 0600);
-	printf("%sfchmod(%d<%s (deleted)>, 0600) = %s\n",
+	printf("%sfchmod(%d<%s (deleted)>%s, 0600) = %s\n",
 	       SELINUX_MYCONTEXT(),
-	       fd, fname_realpath,
+	       fd, fname_realpath, fname_context,
 	       sprintrc(rc));
 
 	puts("+++ exited with 0 +++");

-- 
ldv


More information about the Strace-devel mailing list