]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
sysdeps: Improve error reporting for looking up a user
authorSimon McVittie <smcv@collabora.com>
Thu, 29 Jun 2023 16:28:40 +0000 (17:28 +0100)
committerSimon McVittie <smcv@collabora.com>
Mon, 21 Aug 2023 13:49:31 +0000 (13:49 +0000)
Our implementation always assumed that both code paths set errno, but
according to their API documentation, getpwnam_r and getpwuid_r actually
don't: they *return* a code from the same pseudo-enum as errno. They
also return 0 (but with a NULL struct passwd) if the user is not found,
which these APIs don't count as an error (but we do).

Similarly, in the legacy getpwnam/getpwuid code path, it is unspecified
whether looking up a nonexistent user will set errno or not.

Having retrieved an errno-like error code, we might as well use it in
the human-readable message and not just the machine-readable code,
because the human-readable message is what ends up in the system log.

Helps: https://gitlab.freedesktop.org/dbus/dbus/-/issues/343
Signed-off-by: Simon McVittie <smcv@collabora.com>
dbus/dbus-sysdeps-unix.c

index d08516ab5c797fc3c41a4206fe2229f96ddfe9c3..8438587f8296d378c6bb686eb8377aa7b01938bd 100644 (file)
@@ -2730,8 +2730,8 @@ fill_user_info (DBusUserInfo       *info,
   {
     struct passwd *p;
     char *buf = NULL;
-#ifdef HAVE_GETPWNAM_R
     int result;
+#ifdef HAVE_GETPWNAM_R
     size_t buflen;
     struct passwd p_str;
 
@@ -2774,16 +2774,32 @@ fill_user_info (DBusUserInfo       *info,
           }
       }
 
+    /* There are three possibilities:
+     * - an error: result is a nonzero error code, p should be NULL
+     * - name or uid not found: result is 0, p is NULL
+     * - success: result is 0, p should be &p_str
+     *
+     * Ensure that in all failure cases, p is set to NULL, matching the
+     * getpwuid/getpwnam interface. */
     if (result != 0 || p != &p_str)
       p = NULL;
+
 #else /* ! HAVE_GETPWNAM_R */
     /* I guess we're screwed on thread safety here */
 #warning getpwnam_r() not available, please report this to the dbus maintainers with details of your OS
 
+    /* It is unspecified whether "failed to find" counts as an error,
+     * or whether it's reported as p == NULL without touching errno.
+     * Reset errno so we can distinguish. */
+    errno = 0;
+
     if (uid != DBUS_UID_UNSET)
       p = getpwuid (uid);
     else
       p = getpwnam (username_c);
+
+    /* Always initialized, but only meaningful if p is NULL */
+    result = errno;
 #endif  /* ! HAVE_GETPWNAM_R */
 
     if (p != NULL)
@@ -2798,15 +2814,21 @@ fill_user_info (DBusUserInfo       *info,
     else
       {
         DBusError local_error = DBUS_ERROR_INIT;
+        const char *error_str;
+
+        if (result == 0)
+          error_str = "not found";
+        else
+          error_str = _dbus_strerror (result);
 
         if (uid != DBUS_UID_UNSET)
-          dbus_set_error (&local_error, _dbus_error_from_errno (errno),
-                          "User ID " DBUS_UID_FORMAT " unknown or no memory to allocate password entry",
-                          uid);
+          dbus_set_error (&local_error, _dbus_error_from_errno (result),
+                          "Looking up user ID " DBUS_UID_FORMAT ": %s",
+                          uid, error_str);
         else
-          dbus_set_error (&local_error, _dbus_error_from_errno (errno),
-                          "User \"%s\" unknown or no memory to allocate password entry\n",
-                          username_c ? username_c : "???");
+          dbus_set_error (&local_error, _dbus_error_from_errno (result),
+                          "Looking up user \"%s\": %s",
+                          username_c ? username_c : "???", error_str);
 
         _dbus_verbose ("%s", local_error.message);
         dbus_move_error (&local_error, error);