2003-03-20 Havoc Pennington <hp@redhat.com>
authorHavoc Pennington <hp@redhat.com>
Fri, 21 Mar 2003 02:38:40 +0000 (02:38 +0000)
committerHavoc Pennington <hp@redhat.com>
Fri, 21 Mar 2003 02:38:40 +0000 (02:38 +0000)
* dbus/dbus-connection.c (dbus_connection_set_unix_user_function):
new function
(dbus_connection_get_unix_user): new function

ChangeLog
bus/bus.c
dbus/dbus-connection.c
dbus/dbus-connection.h
dbus/dbus-sysdeps.c
dbus/dbus-transport-protected.h
dbus/dbus-transport.c
dbus/dbus-transport.h
doc/config-file.txt

index 4688cdb..a96b97f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2003-03-20  Havoc Pennington  <hp@redhat.com>
+
+       * dbus/dbus-connection.c (dbus_connection_set_unix_user_function):
+       new function
+       (dbus_connection_get_unix_user): new function
+
 2003-03-20  Havoc Pennington  <hp@pobox.com>
 
        * bus/connection.c (bus_connection_send_oom_error): assert that
index a1bc662..4b5b23b 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
 #include "connection.h"
 #include "services.h"
 #include "utils.h"
+#include "policy.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
 #include <dbus/dbus-internals.h>
 
 struct BusContext
 {
   int refcount;
-  char *address;  
+  char *address;
   DBusServer *server;
   BusConnections *connections;
   BusActivation *activation;
   BusRegistry *registry;
+  DBusList *default_rules;      /**< Default policy rules */
+  DBusList *override_rules;     /**< Override policy rules */
+  DBusHashTable *rules_by_uid;  /**< per-UID policy rules */
+  DBusHashTable *rules_by_gid;  /**< per-GID policy rules */
 };
 
 static dbus_bool_t
@@ -109,6 +116,14 @@ new_connection_callback (DBusServer     *server,
   /* on OOM, we won't have ref'd the connection so it will die. */
 }
 
+static void
+free_rule_func (void *data)
+{
+  BusPolicyRule *rule = data;
+
+  bus_policy_rule_unref (rule);
+}
+
 BusContext*
 bus_context_new (const char  *address,
                  const char **service_dirs,
@@ -164,6 +179,24 @@ bus_context_new (const char  *address,
       goto failed;
     }
   
+  context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_INT,
+                                                NULL,
+                                                free_rule_func);
+  if (context->rules_by_uid == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+
+  context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_INT,
+                                                NULL,
+                                                free_rule_func);
+  if (context->rules_by_gid == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+  
   dbus_server_set_new_connection_function (context->server,
                                            new_connection_callback,
                                            context, NULL);
@@ -260,6 +293,18 @@ bus_context_unref (BusContext *context)
           dbus_server_unref (context->server);
           context->server = NULL;
         }
+
+      if (context->rules_by_uid)
+        {
+          _dbus_hash_table_unref (context->rules_by_uid);
+          context->rules_by_uid = NULL;
+        }
+
+      if (context->rules_by_gid)
+        {
+          _dbus_hash_table_unref (context->rules_by_gid);
+          context->rules_by_gid = NULL;
+        }
       
       dbus_free (context->address);
       dbus_free (context);
index d70ff71..db62140 100644 (file)
@@ -2211,6 +2211,64 @@ dbus_connection_handle_watch (DBusConnection              *connection,
 }
 
 /**
+ * Gets the UNIX user ID of the connection if any.
+ * Returns #TRUE if the uid is filled in.
+ * Always returns #FALSE on non-UNIX platforms.
+ *
+ * @param connection the connection
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_user (DBusConnection *connection,
+                               unsigned long  *uid)
+{
+  dbus_bool_t result;
+
+  dbus_mutex_lock (connection->mutex);
+  result = _dbus_transport_get_unix_user (connection->transport,
+                                          uid);
+  dbus_mutex_unlock (connection->mutex);
+
+  return result;
+}
+
+/**
+ * Sets a predicate function used to determine whether a given user ID
+ * is allowed to connect. When an incoming connection has
+ * authenticated with a particular user ID, this function is called;
+ * if it returns #TRUE, the connection is allowed to proceed,
+ * otherwise the connection is disconnected.
+ *
+ * If the function is set to #NULL (as it is by default), then
+ * only the same UID as the server process will be allowed to
+ * connect.
+ *
+ * @param connection the connection
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ */
+void
+dbus_connection_set_unix_user_function (DBusConnection             *connection,
+                                        DBusAllowUnixUserFunction   function,
+                                        void                       *data,
+                                        DBusFreeFunction            free_data_function)
+{
+  void *old_data = NULL;
+  DBusFreeFunction old_free_function = NULL;
+
+  dbus_mutex_lock (connection->mutex);
+  _dbus_transport_set_unix_user_function (connection->transport,
+                                          function, data, free_data_function,
+                                          &old_data, &old_free_function);
+  dbus_mutex_unlock (connection->mutex);
+
+  if (old_free_function != NULL)
+    (* old_free_function) (old_data);    
+}
+
+/**
  * Adds a message filter. Filters are handlers that are run on
  * all incoming messages, prior to the normal handlers
  * registered with dbus_connection_register_handler().
index 9b135a5..21a4a15 100644 (file)
@@ -69,13 +69,16 @@ typedef void        (* DBusWatchToggledFunction)   (DBusWatch      *watch,
                                                     void           *data);
 typedef void        (* DBusRemoveWatchFunction)    (DBusWatch      *watch,
                                                     void           *data);
-typedef void        (* DBusWakeupMainFunction)     (void           *data);
 typedef dbus_bool_t (* DBusAddTimeoutFunction)     (DBusTimeout    *timeout,
                                                     void           *data);
 typedef void        (* DBusTimeoutToggledFunction) (DBusTimeout    *timeout,
                                                     void           *data);
 typedef void        (* DBusRemoveTimeoutFunction)  (DBusTimeout    *timeout,
                                                     void           *data);
+typedef void        (* DBusWakeupMainFunction)     (void           *data);
+typedef dbus_bool_t (* DBusAllowUnixUserFunction)  (DBusConnection *connection,
+                                                    unsigned long   uid,
+                                                    void           *data);
 
 DBusConnection*    dbus_connection_open                      (const char                 *address,
                                                               DBusResultCode             *result);
@@ -123,7 +126,12 @@ void               dbus_connection_set_wakeup_main_function  (DBusConnection
 dbus_bool_t        dbus_connection_handle_watch              (DBusConnection             *connection,
                                                               DBusWatch                  *watch,
                                                               unsigned int                condition);
-
+dbus_bool_t        dbus_connection_get_unix_user             (DBusConnection             *connection,
+                                                              unsigned long              *uid);
+void               dbus_connection_set_unix_user_function    (DBusConnection             *connection,
+                                                              DBusAllowUnixUserFunction   function,
+                                                              void                       *data,
+                                                              DBusFreeFunction            free_data_function);
 
 int          dbus_watch_get_fd      (DBusWatch        *watch);
 unsigned int dbus_watch_get_flags   (DBusWatch        *watch);
index d653b86..cd4a82a 100644 (file)
@@ -651,6 +651,14 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
 #endif
 
+  /* The POSIX spec certainly doesn't promise this, but
+   * we need these assertions to fail as soon as we're wrong about
+   * it so we can do the porting fixups
+   */
+  _dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid));
+  _dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid));
+  _dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid));
+  
   credentials->pid = -1;
   credentials->uid = -1;
   credentials->gid = -1;
@@ -1353,6 +1361,14 @@ _dbus_credentials_from_uid_string (const DBusString      *uid_str,
 void
 _dbus_credentials_from_current_process (DBusCredentials *credentials)
 {
+  /* The POSIX spec certainly doesn't promise this, but
+   * we need these assertions to fail as soon as we're wrong about
+   * it so we can do the porting fixups
+   */
+  _dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid));
+  _dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid));
+  _dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid));
+  
   credentials->pid = getpid ();
   credentials->uid = getuid ();
   credentials->gid = getgid ();
index 8ee605c..3166838 100644 (file)
@@ -86,6 +86,11 @@ struct DBusTransport
   long max_live_messages_size;                /**< Max total size of received messages. */
 
   DBusCounter *live_messages_size;            /**< Counter for size of all live messages. */
+
+
+  DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */
+  void *unix_user_data;                         /**< Data for unix_user_function */
+  DBusFreeFunction free_unix_user_data;         /**< Function to free unix_user_data */
   
   unsigned int disconnected : 1;              /**< #TRUE if we are disconnected. */
   unsigned int authenticated : 1;             /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
index 8087f5b..b6ab8c0 100644 (file)
@@ -127,6 +127,10 @@ _dbus_transport_init_base (DBusTransport             *transport,
   transport->receive_credentials_pending = server;
   transport->is_server = server;
 
+  transport->unix_user_function = NULL;
+  transport->unix_user_data = NULL;
+  transport->free_unix_user_data = NULL;
+  
   /* Try to default to something that won't totally hose the system,
    * but doesn't impose too much of a limitation.
    */
@@ -155,6 +159,9 @@ _dbus_transport_finalize_base (DBusTransport *transport)
 {
   if (!transport->disconnected)
     _dbus_transport_disconnect (transport);
+
+  if (transport->free_unix_user_data != NULL)
+    (* transport->free_unix_user_data) (transport->unix_user_data);
   
   _dbus_message_loader_unref (transport->loader);
   _dbus_auth_unref (transport->auth);
@@ -334,6 +341,8 @@ _dbus_transport_get_is_connected (DBusTransport *transport)
  * Returns #TRUE if we have been authenticated.  Will return #TRUE
  * even if the transport is disconnected.
  *
+ * @todo needs to drop connection->mutex when calling the unix_user_function
+ *
  * @param transport the transport
  * @returns whether we're authenticated
  */
@@ -363,23 +372,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
       if (transport->authenticated && transport->is_server)
         {
           DBusCredentials auth_identity;
-          DBusCredentials our_identity;
 
-          _dbus_credentials_from_current_process (&our_identity);
           _dbus_auth_get_identity (transport->auth, &auth_identity);
-          
-          if (!_dbus_credentials_match (&our_identity,
-                                        &auth_identity))
+
+          if (transport->unix_user_function != NULL)
             {
-              _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
-                             auth_identity.uid, our_identity.uid);
-              _dbus_transport_disconnect (transport);
-              return FALSE;
+              /* FIXME we hold the connection lock here and should drop it */
+              if (!(* transport->unix_user_function) (transport->connection,
+                                                      auth_identity.uid,
+                                                      transport->unix_user_data))
+                {
+                  _dbus_verbose ("Client UID %d was rejected, disconnecting\n",
+                                 auth_identity.uid);
+                  _dbus_transport_disconnect (transport);
+                  return FALSE;
+                }
+              else
+                {
+                  _dbus_verbose ("Client UID %d authorized\n", auth_identity.uid);
+                }
             }
           else
             {
-              _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
-                             auth_identity.uid, our_identity.uid);
+              DBusCredentials our_identity;
+              
+              _dbus_credentials_from_current_process (&our_identity);
+              
+              if (!_dbus_credentials_match (&our_identity,
+                                            &auth_identity))
+                {
+                  _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
+                                 auth_identity.uid, our_identity.uid);
+                  _dbus_transport_disconnect (transport);
+                  return FALSE;
+                }
+              else
+                {
+                  _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
+                                 auth_identity.uid, our_identity.uid);
+                }
             }
         }
       
@@ -737,4 +768,62 @@ _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
   return transport->max_live_messages_size;
 }
 
+/**
+ * See dbus_connection_get_unix_user().
+ *
+ * @param transport the transport
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+_dbus_transport_get_unix_user (DBusTransport *transport,
+                               unsigned long *uid)
+{
+  DBusCredentials auth_identity;
+
+  *uid = _DBUS_INT_MAX; /* better than some root or system user in
+                         * case of bugs in the caller. Caller should
+                         * never use this value on purpose, however.
+                         */
+  
+  if (!transport->authenticated)
+    return FALSE;
+  
+  _dbus_auth_get_identity (transport->auth, &auth_identity);
+
+  if (auth_identity.uid >= 0)
+    {
+      *uid = auth_identity.uid;
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * See dbus_connection_set_unix_user_function().
+ *
+ * @param transport the transport
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ * @param old_data the old user data to be freed
+ * @param old_free_data_function old free data function to free it with
+ */
+void
+_dbus_transport_set_unix_user_function (DBusTransport             *transport,
+                                        DBusAllowUnixUserFunction  function,
+                                        void                      *data,
+                                        DBusFreeFunction           free_data_function,
+                                        void                     **old_data,
+                                        DBusFreeFunction          *old_free_data_function)
+{  
+  *old_data = transport->unix_user_data;
+  *old_free_data_function = transport->free_unix_user_data;
+
+  transport->unix_user_function = function;
+  transport->unix_user_data = data;
+  transport->free_unix_user_data = free_data_function;
+}
+
 /** @} */
index 639a7d5..c016412 100644 (file)
@@ -30,31 +30,41 @@ DBUS_BEGIN_DECLS;
 
 typedef struct DBusTransport DBusTransport;
 
-DBusTransport*     _dbus_transport_open                       (const char     *address,
-                                                               DBusResultCode *result);
-void               _dbus_transport_ref                        (DBusTransport  *transport);
-void               _dbus_transport_unref                      (DBusTransport  *transport);
-void               _dbus_transport_disconnect                 (DBusTransport  *transport);
-dbus_bool_t        _dbus_transport_get_is_connected           (DBusTransport  *transport);
-dbus_bool_t        _dbus_transport_get_is_authenticated       (DBusTransport  *transport);
-dbus_bool_t        _dbus_transport_handle_watch               (DBusTransport  *transport,
-                                                               DBusWatch      *watch,
-                                                               unsigned int    condition);
-dbus_bool_t        _dbus_transport_set_connection             (DBusTransport  *transport,
-                                                               DBusConnection *connection);
-void               _dbus_transport_messages_pending           (DBusTransport  *transport,
-                                                               int             queue_length);
-void               _dbus_transport_do_iteration               (DBusTransport  *transport,
-                                                               unsigned int    flags,
-                                                               int             timeout_milliseconds);
-DBusDispatchStatus _dbus_transport_get_dispatch_status        (DBusTransport  *transport);
-dbus_bool_t        _dbus_transport_queue_messages             (DBusTransport  *transport);
-void               _dbus_transport_set_max_message_size       (DBusTransport  *transport,
-                                                               long            size);
-long               _dbus_transport_get_max_message_size       (DBusTransport  *transport);
-void               _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
-                                                               long            size);
-long               _dbus_transport_get_max_live_messages_size (DBusTransport  *transport);
+DBusTransport*     _dbus_transport_open                       (const char                *address,
+                                                               DBusResultCode            *result);
+void               _dbus_transport_ref                        (DBusTransport             *transport);
+void               _dbus_transport_unref                      (DBusTransport             *transport);
+void               _dbus_transport_disconnect                 (DBusTransport             *transport);
+dbus_bool_t        _dbus_transport_get_is_connected           (DBusTransport             *transport);
+dbus_bool_t        _dbus_transport_get_is_authenticated       (DBusTransport             *transport);
+dbus_bool_t        _dbus_transport_handle_watch               (DBusTransport             *transport,
+                                                               DBusWatch                 *watch,
+                                                               unsigned int               condition);
+dbus_bool_t        _dbus_transport_set_connection             (DBusTransport             *transport,
+                                                               DBusConnection            *connection);
+void               _dbus_transport_messages_pending           (DBusTransport             *transport,
+                                                               int                        queue_length);
+void               _dbus_transport_do_iteration               (DBusTransport             *transport,
+                                                               unsigned int               flags,
+                                                               int                        timeout_milliseconds);
+DBusDispatchStatus _dbus_transport_get_dispatch_status        (DBusTransport             *transport);
+dbus_bool_t        _dbus_transport_queue_messages             (DBusTransport             *transport);
+void               _dbus_transport_set_max_message_size       (DBusTransport             *transport,
+                                                               long                       size);
+long               _dbus_transport_get_max_message_size       (DBusTransport             *transport);
+void               _dbus_transport_set_max_live_messages_size (DBusTransport             *transport,
+                                                               long                       size);
+long               _dbus_transport_get_max_live_messages_size (DBusTransport             *transport);
+dbus_bool_t        _dbus_transport_get_unix_user              (DBusTransport             *transport,
+                                                               unsigned long             *uid);
+void               _dbus_transport_set_unix_user_function     (DBusTransport             *transport,
+                                                               DBusAllowUnixUserFunction  function,
+                                                               void                      *data,
+                                                               DBusFreeFunction           free_data_function,
+                                                               void                     **old_data,
+                                                               DBusFreeFunction          *old_free_data_function);
+
+
 
 
 DBUS_END_DECLS;
index c10cd7a..ae58192 100644 (file)
@@ -94,6 +94,8 @@ Elements:
   own="servicename"
   send_to="servicename"
   receive_from="servicename"
+  user="username"
+  group="groupname"
 
     Examples:
        <deny send="org.freedesktop.System.Reboot"/> 
@@ -101,6 +103,8 @@ Elements:
        <deny own="org.freedesktop.System"/>
        <deny send_to="org.freedesktop.System"/>
        <deny receive_from="org.freedesktop.System"/>
+       <deny user="john"/>
+       <deny group="enemies"/>
 
     send_to and receive_from mean that messages may not be sent to 
     or received from the *owner* of the given service, not that 
@@ -108,24 +112,32 @@ Elements:
     a connection owns services A, B, C, and sending to A is denied, 
     sending to B or C will not work either.
 
-    For "servicename" or "messagename" the character "*" can be
-    substituted, meaning "any." Complex globs like "foo.bar.*" aren't
-    allowed for now because they'd be work to implement and maybe 
-    encourage sloppy security anyway.
+    user and group denials mean that the given user or group may 
+    not connect to the message bus.
 
-    FIXME should we allow send/send_to and receive/receive_from 
-    to both be specified, in which case they would be ANDed together?
+    For "servicename" or "messagename" or "username" or "groupname"
+    the character "*" can be substituted, meaning "any." Complex globs
+    like "foo.bar.*" aren't allowed for now because they'd be work to
+    implement and maybe encourage sloppy security anyway.
+
+    It does not make sense to deny a user or group inside a <policy>
+    for a user or group; user/group denials can only be inside
+    context="default" or context="required" policies.
+
+    A single <deny> rule may specify both send and send_to, OR both
+    receive and receive_from. In this case, the denial applies only if
+    both attributes match the message being denied.
     e.g. <deny send="foo.bar" send_to="foo.blah"/> would deny 
     messages of the given name AND to the given service.
 
-    Probably need to see how hard/slow all this will be to implement.
-
  <allow>
   send="messagename"
   receive="messagename"
   own="servicename"
   send_to="servicename"
   receive_from="servicename"
+  user="username"
+  group="groupname"
 
     Makes an exception to previous <deny> statements. Works 
     just like <deny> but with the inverse meaning.