[glib/halfline/debug-metrics: 5/22] credentials: Invalid Linux struct ucred means "no information"




commit c4741e99f543def97477b78bbba5dd88f764f04e
Author: Simon McVittie <smcv collabora com>
Date:   Fri Oct 18 10:55:09 2019 +0100

    credentials: Invalid Linux struct ucred means "no information"
    
    On Linux, if getsockopt SO_PEERCRED is used on a TCP socket, one
    might expect it to fail with an appropriate error like ENOTSUP or
    EPROTONOSUPPORT. However, it appears that in fact it succeeds, but
    yields a credentials structure with pid 0, uid -1 and gid -1. These
    are not real process, user and group IDs that can be allocated to a
    real process (pid 0 needs to be reserved to give kill(0) its documented
    special semantics, and similarly uid and gid -1 need to be reserved for
    setresuid() and setresgid()) so it is not meaningful to signal them to
    high-level API users.
    
    An API user with Linux-specific knowledge can still inspect these fields
    via g_credentials_get_native() if desired.
    
    Similarly, if SO_PASSCRED is used to receive a SCM_CREDENTIALS message
    on a receiving Unix socket, but the sending socket had not enabled
    SO_PASSCRED at the time that the message was sent, it is possible
    for it to succeed but yield a credentials structure with pid 0, uid
    /proc/sys/kernel/overflowuid and gid /proc/sys/kernel/overflowgid. Even
    if we were to read those pseudo-files, we cannot distinguish between
    the overflow IDs and a real process that legitimately has the same IDs
    (typically they are set to 'nobody' and 'nogroup', which can be used
    by a real process), so we detect this situation by noticing that
    pid == 0, and to save syscalls we do not read the overflow IDs from
    /proc at all.
    
    This results in a small API change: g_credentials_is_same_user() now
    returns FALSE if we compare two credentials structures that are both
    invalid. This seems like reasonable, conservative behaviour: if we cannot
    prove that they are the same user, we should assume they are not.
    
    Signed-off-by: Simon McVittie <smcv collabora com>

 gio/gcredentials.c | 42 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 39 insertions(+), 3 deletions(-)
---
diff --git a/gio/gcredentials.c b/gio/gcredentials.c
index 2e050776c..75d333294 100644
--- a/gio/gcredentials.c
+++ b/gio/gcredentials.c
@@ -262,6 +262,35 @@ g_credentials_to_string (GCredentials *credentials)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+#if G_CREDENTIALS_USE_LINUX_UCRED
+/*
+ * Check whether @native contains invalid data. If getsockopt SO_PEERCRED
+ * is used on a TCP socket, it succeeds but yields a credentials structure
+ * with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
+ * receiving Unix socket when the sending socket did not also enable
+ * SO_PASSCRED, it can succeed but yield a credentials structure with
+ * pid 0, uid /proc/sys/kernel/overflowuid and gid
+ * /proc/sys/kernel/overflowgid.
+ */
+static gboolean
+linux_ucred_check_valid (struct ucred  *native,
+                         GError       **error)
+{
+  if (native->pid == 0
+      || native->uid == -1
+      || native->gid == -1)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_DATA,
+                           _("GCredentials contains invalid data"));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+#endif
+
 /**
  * g_credentials_is_same_user:
  * @credentials: A #GCredentials.
@@ -291,7 +320,8 @@ g_credentials_is_same_user (GCredentials  *credentials,
 
   ret = FALSE;
 #if G_CREDENTIALS_USE_LINUX_UCRED
-  if (credentials->native.uid == other_credentials->native.uid)
+  if (linux_ucred_check_valid (&credentials->native, NULL)
+      && credentials->native.uid == other_credentials->native.uid)
     ret = TRUE;
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
   if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
@@ -450,7 +480,10 @@ g_credentials_get_unix_user (GCredentials    *credentials,
   g_return_val_if_fail (error == NULL || *error == NULL, -1);
 
 #if G_CREDENTIALS_USE_LINUX_UCRED
-  ret = credentials->native.uid;
+  if (linux_ucred_check_valid (&credentials->native, error))
+    ret = credentials->native.uid;
+  else
+    ret = -1;
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
   ret = credentials->native.cmcred_euid;
 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
@@ -496,7 +529,10 @@ g_credentials_get_unix_pid (GCredentials    *credentials,
   g_return_val_if_fail (error == NULL || *error == NULL, -1);
 
 #if G_CREDENTIALS_USE_LINUX_UCRED
-  ret = credentials->native.pid;
+  if (linux_ucred_check_valid (&credentials->native, error))
+    ret = credentials->native.pid;
+  else
+    ret = -1;
 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
   ret = credentials->native.cmcred_pid;
 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]