Blob Blame History Raw
From 26213aa0e0d8dca5f36cc23f6942525224cbe9f5 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 19 Jun 2012 12:02:24 -0400
Subject: [PATCH 1/3] util: CVE-2012-2737: validate SetIconFile caller over
 bus

The AccountsService SetIconFile call associates an icon
with a user.

SetIconFile allows users to have icons visible at the login
screen that don't necessarily originate in globally
readable or always available locations. This is accomplished
by copying the originating icon to the local disk in /var.

Since AccountsService runs with with root privileges, the
implemention of the SetIconFile method queries the uid of
the method caller, forks, switches to that uid and performs
the image copy as if it were the user.

Unfortunately, the uid lookup peformed is done "just in time"
instead of looking at peer credentials from the time the call
was initiated. There is a race condition that means a caller
could invoke the method call, quickly exec a setuid binary, and
then cause the copy to be performed as the uid of the setuid
process.

This commit changes the uid lookup logic to query the system
bus daemon for the peer credentials that were cached from the
caller at the time of the initial connection.
---
 src/util.c |   37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/src/util.c b/src/util.c
index 66ddd98..1ce375b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -251,22 +251,37 @@ get_user_groups (const gchar  *user,


 gboolean
-get_caller_uid (GDBusMethodInvocation *context, gint *uid)
+get_caller_uid (GDBusMethodInvocation *context,
+                gint                  *uid)
 {
-        PolkitSubject *subject;
-        PolkitSubject *process;
+        GVariant      *reply;
+        GError        *error;
+
+        error = NULL;
+        reply = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (context),
+                                             "org.freedesktop.DBus",
+                                             "/org/freedesktop/DBus",
+                                             "org.freedesktop.DBus",
+                                             "GetConnectionUnixUser",
+                                             g_variant_new ("(s)",
+                                                            g_dbus_method_invocation_get_sender (context)),
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             &error);
+
+        if (reply == NULL) {
+                g_warning ("Could not talk to message bus to find uid of sender %s: %s",
+                           g_dbus_method_invocation_get_sender (context),
+                           error->message);
+                g_error_free (error);

-        subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (context));
-        process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, NULL);
-        if (!process) {
-                g_object_unref (subject);
                 return FALSE;
         }

-        *uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (process));
-
-        g_object_unref (subject);
-        g_object_unref (process);
+        g_variant_get (reply, "(u)", uid);
+        g_variant_unref (reply);

         return TRUE;
 }
--
1.7.10.2


From bd51aa4cdac380f55d607f4ffdf2ab3c00d08721 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 19 Jun 2012 14:02:42 -0400
Subject: [PATCH 2/3] user: CVE-2012-2737: verify caller through bus in more
 cases

The previous commit changed the SetIconFile call to identify
the uid of the calling process via cached peer credentials
stored by the bus daemon.

This commit fixes other similar cases where we try to figure
out process identity on our own instead of through the bus
daemon.
---
 src/user.c |   78 ++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 42 insertions(+), 36 deletions(-)

diff --git a/src/user.c b/src/user.c
index 55c238d..9713ecd 100644
--- a/src/user.c
+++ b/src/user.c
@@ -552,35 +552,21 @@ user_change_real_name_authorized_cb (Daemon                *daemon,
         accounts_user_complete_set_real_name (ACCOUNTS_USER (user), context);
 }

-static uid_t
-method_invocation_get_uid (GDBusMethodInvocation *context)
-{
-  const gchar *sender;
-  PolkitSubject *busname;
-  PolkitSubject *process;
-  uid_t uid;
-
-  sender = g_dbus_method_invocation_get_sender (context);
-  busname = polkit_system_bus_name_new (sender);
-  process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (busname), NULL, NULL);
-  uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (process));
-  g_object_unref (busname);
-  g_object_unref (process);
-
-  return uid;
-}
-
 static gboolean
 user_set_real_name (AccountsUser          *auser,
                     GDBusMethodInvocation *context,
                     const gchar           *real_name)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
@@ -692,11 +678,15 @@ user_set_email (AccountsUser          *auser,
                 const gchar           *email)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
@@ -744,11 +734,15 @@ user_set_language (AccountsUser          *auser,
                    const gchar           *language)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
@@ -794,11 +788,15 @@ user_set_x_session (AccountsUser          *auser,
                     const gchar           *x_session)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
@@ -844,11 +842,15 @@ user_set_location (AccountsUser          *auser,
                    const gchar           *location)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
@@ -1163,11 +1165,15 @@ user_set_icon_file (AccountsUser          *auser,
                     const gchar           *filename)
 {
         User *user = (User*)auser;
-        uid_t uid;
+        int uid;
         const gchar *action_id;

-        uid = method_invocation_get_uid (context);
-        if (user->uid == uid)
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (user->uid == (uid_t) uid)
                 action_id = "org.freedesktop.accounts.change-own-user-data";
         else
                 action_id = "org.freedesktop.accounts.user-administration";
--
1.7.10.2


From 4c5b12e363410e490e776e4b4a86dcce157a543d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 19 Jun 2012 14:34:18 -0400
Subject: [PATCH 3/3] util: CVE-2012-2737: drop _polkit_subject_get_cmdline

_polkit_subject_get_cmdline is a function copy and pasted
from the polkit code that returns the command line, uid, and
pid of a particular polkit subject.  It's used for helping to
generate log entries that detail what processes are invoking methods
on the accounts service.

It's also used, on older kernels, for setting up the the loginuid
of subprocesses that are run on behalf of AccountsService clients,
so the audit trail leads back to the user initiating a request.

_polkit_subject_get_cmdline directly looks up the uid of the caller,
instead of querying the system bus.  As such, it's vulnerable to
the same race condition discussed in the previous two commits.

This commit guts _polkit_subject_get_cmdline, keeping only the part
that reads /proc/pid/cmdline. We now get the uid and pid from the
bus daemon.
---
 src/util.c |  135 ++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 76 insertions(+), 59 deletions(-)

diff --git a/src/util.c b/src/util.c
index 1ce375b..adc559a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -34,11 +34,9 @@

 #include "util.h"

-
 static gchar *
-_polkit_subject_get_cmdline (PolkitSubject *subject, gint *pid, gint *uid)
+get_cmdline_of_pid (GPid pid)
 {
-  PolkitSubject *process;
   gchar *ret;
   gchar *filename;
   gchar *contents;
@@ -46,43 +44,7 @@ _polkit_subject_get_cmdline (PolkitSubject *subject, gint *pid, gint *uid)
   GError *error;
   guint n;

-  g_return_val_if_fail (subject != NULL, NULL);
-
-  error = NULL;
-
-  ret = NULL;
-  process = NULL;
-  filename = NULL;
-  contents = NULL;
-
-  if (POLKIT_IS_UNIX_PROCESS (subject))
-   {
-      process = g_object_ref (subject);
-    }
-  else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
-    {
-      process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject),
-                                                         NULL,
-                                                         &error);
-      if (process == NULL)
-        {
-          g_warning ("Error getting process for system bus name `%s': %s",
-                     polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
-                     error->message);
-          g_error_free (error);
-          goto out;
-        }
-    }
-  else
-    {
-      g_warning ("Unknown subject type passed to guess_program_name()");
-      goto out;
-    }
-
-  *pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
-  *uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (process));
-
-  filename = g_strdup_printf ("/proc/%d/cmdline", *pid);
+  filename = g_strdup_printf ("/proc/%d/cmdline", (int) pid);

   if (!g_file_get_contents (filename,
                             &contents,
@@ -108,11 +70,49 @@ _polkit_subject_get_cmdline (PolkitSubject *subject, gint *pid, gint *uid)
  out:
   g_free (filename);
   g_free (contents);
-  if (process != NULL)
-    g_object_unref (process);
+
   return ret;
 }

+static gboolean
+get_caller_pid (GDBusMethodInvocation *context,
+                GPid                  *pid)
+{
+        GVariant      *reply;
+        GError        *error;
+        guint32        pid_as_int;
+
+        error = NULL;
+        reply = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (context),
+                                             "org.freedesktop.DBus",
+                                             "/org/freedesktop/DBus",
+                                             "org.freedesktop.DBus",
+                                             "GetConnectionUnixProcessID",
+                                             g_variant_new ("(s)",
+                                                            g_dbus_method_invocation_get_sender (context)),
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             &error);
+
+        if (reply == NULL) {
+                g_warning ("Could not talk to message bus to find uid of sender %s: %s",
+                           g_dbus_method_invocation_get_sender (context),
+                           error->message);
+                g_error_free (error);
+
+                return FALSE;
+        }
+
+        g_variant_get (reply, "(u)", &pid_as_int);
+        *pid = pid_as_int;
+
+        g_variant_unref (reply);
+
+        return TRUE;
+}
+
 void
 sys_log (GDBusMethodInvocation *context,
          const gchar           *format,
@@ -127,21 +127,36 @@ sys_log (GDBusMethodInvocation *context,

         if (context) {
                 PolkitSubject *subject;
-                gchar *cmdline;
+                gchar *cmdline = NULL;
                 gchar *id;
-                gint pid = 0;
-                gint uid = 0;
+                GPid pid = 0;
+                gint uid = -1;
                 gchar *tmp;

                 subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (context));
                 id = polkit_subject_to_string (subject);
-                cmdline = _polkit_subject_get_cmdline (subject, &pid, &uid);

-                if (cmdline == NULL) {
-                        tmp = g_strdup_printf ("request by %s: %s", id, msg);
+                if (get_caller_pid (context, &pid)) {
+                        cmdline = get_cmdline_of_pid (pid);
+                } else {
+                        pid = 0;
+                        cmdline = NULL;
                 }
-                else {
-                        tmp = g_strdup_printf ("request by %s [%s pid:%d uid:%d]: %s", id, cmdline, pid, uid, msg);
+
+                if (cmdline != NULL) {
+                        if (get_caller_uid (context, &uid)) {
+                                tmp = g_strdup_printf ("request by %s [%s pid:%d uid:%d]: %s", id, cmdline, (int) pid, uid, msg);
+                        } else {
+                                tmp = g_strdup_printf ("request by %s [%s pid:%d]: %s", id, cmdline, (int) pid, msg);
+                        }
+                } else {
+                        if (get_caller_uid (context, &uid) && pid != 0) {
+                                tmp = g_strdup_printf ("request by %s [pid:%d uid:%d]: %s", id, (int) pid, uid, msg);
+                        } else if (pid != 0) {
+                                tmp = g_strdup_printf ("request by %s [pid:%d]: %s", id, (int) pid, msg);
+                        } else {
+                                tmp = g_strdup_printf ("request by %s: %s", id, msg);
+                        }
                 }

                 g_free (msg);
@@ -160,20 +175,22 @@ sys_log (GDBusMethodInvocation *context,
 static void
 get_caller_loginuid (GDBusMethodInvocation *context, gchar *loginuid, gint size)
 {
-        PolkitSubject *subject;
-        gchar *cmdline;
-        gint pid;
+        GPid pid;
         gint uid;
         gchar *path;
         gchar *buf;

-        subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (context));
-        cmdline = _polkit_subject_get_cmdline (subject, &pid, &uid);
-        g_free (cmdline);
-        g_object_unref (subject);
+        if (!get_caller_uid (context, &uid)) {
+                uid = getuid ();
+        }
+
+        if (get_caller_pid (context, &pid)) {
+                path = g_strdup_printf ("/proc/%d/loginuid", (int) pid);
+        } else {
+                path = NULL;
+        }

-        path = g_strdup_printf ("/proc/%d/loginuid", pid);
-        if (g_file_get_contents (path, &buf, NULL, NULL)) {
+        if (path != NULL && g_file_get_contents (path, &buf, NULL, NULL)) {
                 strncpy (loginuid, buf, size);
                 g_free (buf);
         }
--
1.7.10.2