Blame SOURCES/0001-credentials-Invalid-Linux-struct-ucred-means-no-info.patch

2d3b65
From 1485a97d8051b0aa047987f7b0c0bfe4ba4ce55b Mon Sep 17 00:00:00 2001
2d3b65
From: Simon McVittie <smcv@collabora.com>
2d3b65
Date: Fri, 18 Oct 2019 10:55:09 +0100
2d3b65
Subject: [PATCH] credentials: Invalid Linux struct ucred means "no
2d3b65
 information"
2d3b65
2d3b65
On Linux, if getsockopt SO_PEERCRED is used on a TCP socket, one
2d3b65
might expect it to fail with an appropriate error like ENOTSUP or
2d3b65
EPROTONOSUPPORT. However, it appears that in fact it succeeds, but
2d3b65
yields a credentials structure with pid 0, uid -1 and gid -1. These
2d3b65
are not real process, user and group IDs that can be allocated to a
2d3b65
real process (pid 0 needs to be reserved to give kill(0) its documented
2d3b65
special semantics, and similarly uid and gid -1 need to be reserved for
2d3b65
setresuid() and setresgid()) so it is not meaningful to signal them to
2d3b65
high-level API users.
2d3b65
2d3b65
An API user with Linux-specific knowledge can still inspect these fields
2d3b65
via g_credentials_get_native() if desired.
2d3b65
2d3b65
Similarly, if SO_PASSCRED is used to receive a SCM_CREDENTIALS message
2d3b65
on a receiving Unix socket, but the sending socket had not enabled
2d3b65
SO_PASSCRED at the time that the message was sent, it is possible
2d3b65
for it to succeed but yield a credentials structure with pid 0, uid
2d3b65
/proc/sys/kernel/overflowuid and gid /proc/sys/kernel/overflowgid. Even
2d3b65
if we were to read those pseudo-files, we cannot distinguish between
2d3b65
the overflow IDs and a real process that legitimately has the same IDs
2d3b65
(typically they are set to 'nobody' and 'nogroup', which can be used
2d3b65
by a real process), so we detect this situation by noticing that
2d3b65
pid == 0, and to save syscalls we do not read the overflow IDs from
2d3b65
/proc at all.
2d3b65
2d3b65
This results in a small API change: g_credentials_is_same_user() now
2d3b65
returns FALSE if we compare two credentials structures that are both
2d3b65
invalid. This seems like reasonable, conservative behaviour: if we cannot
2d3b65
prove that they are the same user, we should assume they are not.
2d3b65
2d3b65
Signed-off-by: Simon McVittie <smcv@collabora.com>
2d3b65
---
2d3b65
 gio/gcredentials.c | 42 +++++++++++++++++++++++++++++++++++++++---
2d3b65
 1 file changed, 39 insertions(+), 3 deletions(-)
2d3b65
2d3b65
diff --git a/gio/gcredentials.c b/gio/gcredentials.c
2d3b65
index c350e3c88..c4794ded7 100644
2d3b65
--- a/gio/gcredentials.c
2d3b65
+++ b/gio/gcredentials.c
2d3b65
@@ -265,6 +265,35 @@ g_credentials_to_string (GCredentials *credentials)
2d3b65
 
2d3b65
 /* ---------------------------------------------------------------------------------------------------- */
2d3b65
 
2d3b65
+#if G_CREDENTIALS_USE_LINUX_UCRED
2d3b65
+/*
2d3b65
+ * Check whether @native contains invalid data. If getsockopt SO_PEERCRED
2d3b65
+ * is used on a TCP socket, it succeeds but yields a credentials structure
2d3b65
+ * with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
2d3b65
+ * receiving Unix socket when the sending socket did not also enable
2d3b65
+ * SO_PASSCRED, it can succeed but yield a credentials structure with
2d3b65
+ * pid 0, uid /proc/sys/kernel/overflowuid and gid
2d3b65
+ * /proc/sys/kernel/overflowgid.
2d3b65
+ */
2d3b65
+static gboolean
2d3b65
+linux_ucred_check_valid (struct ucred  *native,
2d3b65
+                         GError       **error)
2d3b65
+{
2d3b65
+  if (native->pid == 0
2d3b65
+      || native->uid == -1
2d3b65
+      || native->gid == -1)
2d3b65
+    {
2d3b65
+      g_set_error_literal (error,
2d3b65
+                           G_IO_ERROR,
2d3b65
+                           G_IO_ERROR_INVALID_DATA,
2d3b65
+                           _("GCredentials contains invalid data"));
2d3b65
+      return FALSE;
2d3b65
+    }
2d3b65
+
2d3b65
+  return TRUE;
2d3b65
+}
2d3b65
+#endif
2d3b65
+
2d3b65
 /**
2d3b65
  * g_credentials_is_same_user:
2d3b65
  * @credentials: A #GCredentials.
2d3b65
@@ -294,7 +323,8 @@ g_credentials_is_same_user (GCredentials  *credentials,
2d3b65
 
2d3b65
   ret = FALSE;
2d3b65
 #if G_CREDENTIALS_USE_LINUX_UCRED
2d3b65
-  if (credentials->native.uid == other_credentials->native.uid)
2d3b65
+  if (linux_ucred_check_valid (&credentials->native, NULL)
2d3b65
+      && credentials->native.uid == other_credentials->native.uid)
2d3b65
     ret = TRUE;
2d3b65
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
2d3b65
   if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
2d3b65
@@ -453,7 +483,10 @@ g_credentials_get_unix_user (GCredentials    *credentials,
2d3b65
   g_return_val_if_fail (error == NULL || *error == NULL, -1);
2d3b65
 
2d3b65
 #if G_CREDENTIALS_USE_LINUX_UCRED
2d3b65
-  ret = credentials->native.uid;
2d3b65
+  if (linux_ucred_check_valid (&credentials->native, error))
2d3b65
+    ret = credentials->native.uid;
2d3b65
+  else
2d3b65
+    ret = -1;
2d3b65
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
2d3b65
   ret = credentials->native.cmcred_euid;
2d3b65
 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
2d3b65
@@ -499,7 +532,10 @@ g_credentials_get_unix_pid (GCredentials    *credentials,
2d3b65
   g_return_val_if_fail (error == NULL || *error == NULL, -1);
2d3b65
 
2d3b65
 #if G_CREDENTIALS_USE_LINUX_UCRED
2d3b65
-  ret = credentials->native.pid;
2d3b65
+  if (linux_ucred_check_valid (&credentials->native, error))
2d3b65
+    ret = credentials->native.pid;
2d3b65
+  else
2d3b65
+    ret = -1;
2d3b65
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
2d3b65
   ret = credentials->native.cmcred_pid;
2d3b65
 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
2d3b65
-- 
2d3b65
2.23.0
2d3b65