diff options
| -rw-r--r-- | ChangeLog | 6 | ||||
| -rw-r--r-- | bus/bus.c | 47 | ||||
| -rw-r--r-- | dbus/dbus-connection.c | 58 | ||||
| -rw-r--r-- | dbus/dbus-connection.h | 12 | ||||
| -rw-r--r-- | dbus/dbus-sysdeps.c | 16 | ||||
| -rw-r--r-- | dbus/dbus-transport-protected.h | 5 | ||||
| -rw-r--r-- | dbus/dbus-transport.c | 111 | ||||
| -rw-r--r-- | dbus/dbus-transport.h | 60 | ||||
| -rw-r--r-- | doc/config-file.txt | 28 | 
9 files changed, 296 insertions, 47 deletions
@@ -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 @@ -27,16 +27,23 @@  #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); diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index d70ff716..db621405 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -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(). diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index 9b135a5d..21a4a15a 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -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); diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index d653b868..cd4a82a5 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -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 (); diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 8ee605c0..31668389 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -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 */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 8087f5b0..b6ab8c0a 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -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; +} +  /** @} */ diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 639a7d50..c016412f 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -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; diff --git a/doc/config-file.txt b/doc/config-file.txt index c10cd7ad..ae581924 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -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.  | 
