GetConnectionCredentials: add
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Thu, 22 Aug 2013 17:21:58 +0000 (18:21 +0100)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Thu, 22 Aug 2013 17:21:58 +0000 (18:21 +0100)
The initial set of credentials is just UnixUserID and ProcessID.
The rest can follow when someone is sufficiently interested to actually
test them.

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=54445
Reviewed-by: Ralf Habacker <ralf.habacker@freenet.de>
[rename a function that Ralf found unclear -smcv]
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
bus/driver.c
doc/dbus-specification.xml
test/dbus-daemon.c

index 01e56fb..23197e4 100644 (file)
@@ -33,6 +33,7 @@
 #include "stats.h"
 #include "utils.h"
 
+#include <dbus/dbus-asv-util.h>
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-message.h>
@@ -1524,6 +1525,80 @@ bus_driver_handle_get_connection_selinux_security_context (DBusConnection *conne
 }
 
 static dbus_bool_t
+bus_driver_handle_get_connection_credentials (DBusConnection *connection,
+                                              BusTransaction *transaction,
+                                              DBusMessage    *message,
+                                              DBusError      *error)
+{
+  DBusConnection *conn;
+  DBusMessage *reply;
+  DBusMessageIter reply_iter;
+  DBusMessageIter array_iter;
+  unsigned long ulong_val;
+  const char *service;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  reply = NULL;
+
+  conn = bus_driver_get_conn_helper (connection, message, "credentials",
+                                     &service, error);
+
+  if (conn == NULL)
+    goto failed;
+
+  reply = _dbus_asv_new_method_return (message, &reply_iter, &array_iter);
+  if (reply == NULL)
+    goto oom;
+
+  /* we can't represent > 32-bit pids; if your system needs them, please
+   * add ProcessID64 to the spec or something */
+  if (dbus_connection_get_unix_process_id (conn, &ulong_val) &&
+      ulong_val <= _DBUS_UINT32_MAX)
+    {
+      if (!_dbus_asv_add_uint32 (&array_iter, "ProcessID", ulong_val))
+        goto oom;
+    }
+
+  /* we can't represent > 32-bit uids; if your system needs them, please
+   * add UnixUserID64 to the spec or something */
+  if (dbus_connection_get_unix_user (conn, &ulong_val) &&
+      ulong_val <= _DBUS_UINT32_MAX)
+    {
+      if (!_dbus_asv_add_uint32 (&array_iter, "UnixUserID", ulong_val))
+        goto oom;
+    }
+
+  if (!_dbus_asv_close (&reply_iter, &array_iter))
+    goto oom;
+
+  if (! bus_transaction_send_from_driver (transaction, connection, reply))
+    {
+      /* this time we don't want to close the iterator again, so just
+       * get rid of the message */
+      dbus_message_unref (reply);
+      reply = NULL;
+      goto oom;
+    }
+
+  return TRUE;
+
+ oom:
+  BUS_SET_OOM (error);
+
+ failed:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+
+  if (reply)
+    {
+      _dbus_asv_abandon (&reply_iter, &array_iter);
+      dbus_message_unref (reply);
+    }
+
+  return FALSE;
+}
+
+static dbus_bool_t
 bus_driver_handle_reload_config (DBusConnection *connection,
                                 BusTransaction *transaction,
                                 DBusMessage    *message,
@@ -1703,6 +1778,8 @@ static const MessageHandler dbus_message_handlers[] = {
     "",
     DBUS_TYPE_STRING_AS_STRING,
     bus_driver_handle_get_id },
+  { "GetConnectionCredentials", "s", "a{sv}",
+    bus_driver_handle_get_connection_credentials },
   { NULL, NULL, NULL, NULL }
 };
 
index 324dfd4..f5d8a34 100644 (file)
        </para>
       </sect3>
 
+      <sect3 id="bus-messages-get-connection-credentials">
+        <title><literal>org.freedesktop.DBus.GetConnectionCredentials</literal></title>
+        <para>
+          As a method:
+          <programlisting>
+            DICT&lt;STRING,VARIANT&gt; GetConnectionCredentials (in STRING bus_name)
+          </programlisting>
+          Message arguments:
+          <informaltable>
+            <tgroup cols="3">
+              <thead>
+                <row>
+                  <entry>Argument</entry>
+                  <entry>Type</entry>
+                  <entry>Description</entry>
+                </row>
+              </thead>
+              <tbody>
+                <row>
+                  <entry>0</entry>
+                  <entry>STRING</entry>
+                  <entry>Unique or well-known bus name of the connection to
+                    query, such as <literal>:12.34</literal> or
+                    <literal>com.example.tea</literal></entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        Reply arguments:
+        <informaltable>
+          <tgroup cols="3">
+            <thead>
+              <row>
+                <entry>Argument</entry>
+                <entry>Type</entry>
+                <entry>Description</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry>0</entry>
+                <entry>DICT&lt;STRING,VARIANT&gt;</entry>
+                <entry>Credentials</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </informaltable>
+      </para>
+
+      <para>
+        Returns as many credentials as possible for the process connected to
+        the server. If unable to determine certain credentials (for instance,
+        because the process is not on the same machine as the bus daemon,
+        or because this version of the bus daemon does not support a
+        particular security framework), or if the values of those credentials
+        cannot be represented as documented here, then those credentials
+        are omitted.
+      </para>
+
+      <para>
+        Keys in the returned dictionary not containing "." are defined
+        by this specification. Bus daemon implementors supporting
+        credentials frameworks not mentioned in this document should either
+        contribute patches to this specification, or use keys containing
+        "." and starting with a reversed domain name.
+        <informaltable>
+          <tgroup cols="3">
+            <thead>
+              <row>
+                <entry>Key</entry>
+                <entry>Value type</entry>
+                <entry>Value</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry>UnixUserID</entry>
+                <entry>UINT32</entry>
+                <entry>The numeric Unix user ID, as defined by POSIX</entry>
+              </row>
+              <row>
+                <entry>ProcessID</entry>
+                <entry>UINT32</entry>
+                <entry>The numeric process ID, on platforms that have
+                  this concept. On Unix, this is the process ID defined by
+                  POSIX.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </informaltable>
+       </para>
+
+        <para>
+          This method was added in D-Bus 1.7 to reduce the round-trips
+          required to list a process's credentials. In older versions, calling
+          this method will fail: applications should recover by using the
+          separate methods such as
+          <xref linkend="bus-messages-get-connection-unix-user"/>
+          instead.
+        </para>
+      </sect3>
+
       <sect3 id="bus-messages-add-match">
         <title><literal>org.freedesktop.DBus.AddMatch</literal></title>
         <para>
index cc87153..69b6791 100644 (file)
@@ -39,6 +39,7 @@
 #else
 # include <signal.h>
 # include <unistd.h>
+# include <sys/types.h>
 #endif
 
 typedef struct {
@@ -310,6 +311,131 @@ test_echo (Fixture *f,
 }
 
 static void
+pending_call_store_reply (DBusPendingCall *pc,
+    void *data)
+{
+  DBusMessage **message_p = data;
+
+  *message_p = dbus_pending_call_steal_reply (pc);
+  g_assert (*message_p != NULL);
+}
+
+static void
+test_creds (Fixture *f,
+    gconstpointer context)
+{
+  const char *unique = dbus_bus_get_unique_name (f->left_conn);
+  DBusMessage *m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+      DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionCredentials");
+  DBusPendingCall *pc;
+  DBusMessageIter args_iter;
+  DBusMessageIter arr_iter;
+  DBusMessageIter pair_iter;
+  DBusMessageIter var_iter;
+  enum {
+      SEEN_UNIX_USER = 1,
+      SEEN_PID = 2,
+      SEEN_WINDOWS_SID = 4
+  } seen = 0;
+
+  if (m == NULL)
+    g_error ("OOM");
+
+  if (!dbus_message_append_args (m,
+        DBUS_TYPE_STRING, &unique,
+        DBUS_TYPE_INVALID))
+    g_error ("OOM");
+
+  if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
+                                        DBUS_TIMEOUT_USE_DEFAULT) ||
+      pc == NULL)
+    g_error ("OOM");
+
+  dbus_message_unref (m);
+  m = NULL;
+
+  if (dbus_pending_call_get_completed (pc))
+    pending_call_store_reply (pc, &m);
+  else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply,
+                                          &m, NULL))
+    g_error ("OOM");
+
+  while (m == NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_assert_cmpstr (dbus_message_get_signature (m), ==, "a{sv}");
+
+  dbus_message_iter_init (m, &args_iter);
+  g_assert_cmpuint (dbus_message_iter_get_arg_type (&args_iter), ==,
+      DBUS_TYPE_ARRAY);
+  g_assert_cmpuint (dbus_message_iter_get_element_type (&args_iter), ==,
+      DBUS_TYPE_DICT_ENTRY);
+  dbus_message_iter_recurse (&args_iter, &arr_iter);
+
+  while (dbus_message_iter_get_arg_type (&arr_iter) != DBUS_TYPE_INVALID)
+    {
+      const char *name;
+
+      dbus_message_iter_recurse (&arr_iter, &pair_iter);
+      g_assert_cmpuint (dbus_message_iter_get_arg_type (&pair_iter), ==,
+          DBUS_TYPE_STRING);
+      dbus_message_iter_get_basic (&pair_iter, &name);
+      dbus_message_iter_next (&pair_iter);
+      g_assert_cmpuint (dbus_message_iter_get_arg_type (&pair_iter), ==,
+          DBUS_TYPE_VARIANT);
+      dbus_message_iter_recurse (&pair_iter, &var_iter);
+
+      if (g_strcmp0 (name, "UnixUserID") == 0)
+        {
+#ifdef G_OS_UNIX
+          guint32 u32;
+
+          g_assert (!(seen & SEEN_UNIX_USER));
+          g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
+              DBUS_TYPE_UINT32);
+          dbus_message_iter_get_basic (&var_iter, &u32);
+          g_message ("%s of this process is %u", name, u32);
+          g_assert_cmpuint (u32, ==, geteuid ());
+          seen |= SEEN_UNIX_USER;
+#else
+          g_assert_not_reached ();
+#endif
+        }
+      else if (g_strcmp0 (name, "ProcessID") == 0)
+        {
+          guint32 u32;
+
+          g_assert (!(seen & SEEN_PID));
+          g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
+              DBUS_TYPE_UINT32);
+          dbus_message_iter_get_basic (&var_iter, &u32);
+          g_message ("%s of this process is %u", name, u32);
+#ifdef G_OS_UNIX
+          g_assert_cmpuint (u32, ==, getpid ());
+#elif defined(G_OS_WIN32)
+          g_assert_cmpuint (u32, ==, GetCurrentProcessId ());
+#else
+          g_assert_not_reached ();
+#endif
+          seen |= SEEN_PID;
+        }
+
+      dbus_message_iter_next (&arr_iter);
+    }
+
+#ifdef G_OS_UNIX
+  g_assert (seen & SEEN_UNIX_USER);
+  g_assert (seen & SEEN_PID);
+#endif
+
+#ifdef G_OS_WIN32
+  /* FIXME: when implemented:
+  g_assert (seen & SEEN_WINDOWS_SID);
+   */
+#endif
+}
+
+static void
 teardown (Fixture *f,
     gconstpointer context G_GNUC_UNUSED)
 {
@@ -363,6 +489,7 @@ main (int argc,
   g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown);
   g_test_add ("/echo/limited", Fixture, &limited_config,
       setup, test_echo, teardown);
+  g_test_add ("/creds", Fixture, NULL, setup, test_creds, teardown);
 
   return g_test_run ();
 }