diff options
| -rw-r--r-- | ChangeLog | 35 | ||||
| -rw-r--r-- | bus/Makefile.am | 2 | ||||
| -rw-r--r-- | bus/bus.c | 65 | ||||
| -rw-r--r-- | bus/bus.h | 71 | ||||
| -rw-r--r-- | bus/config-parser.c | 102 | ||||
| -rw-r--r-- | bus/connection.c | 123 | ||||
| -rw-r--r-- | bus/connection.h | 13 | ||||
| -rw-r--r-- | bus/dbus-daemon-1.1.in | 49 | ||||
| -rw-r--r-- | bus/dispatch.c | 312 | ||||
| -rw-r--r-- | bus/dispatch.h | 3 | ||||
| -rw-r--r-- | bus/driver.c | 164 | ||||
| -rw-r--r-- | bus/policy.c | 39 | ||||
| -rw-r--r-- | bus/policy.h | 3 | ||||
| -rw-r--r-- | bus/session.conf.in | 5 | ||||
| -rw-r--r-- | bus/signals.c | 774 | ||||
| -rw-r--r-- | bus/signals.h | 83 | ||||
| -rw-r--r-- | bus/system.conf.in | 4 | ||||
| -rw-r--r-- | bus/test-main.c | 6 | ||||
| -rw-r--r-- | bus/test.h | 1 | ||||
| -rw-r--r-- | dbus/dbus-errors.h | 13 | ||||
| -rw-r--r-- | dbus/dbus-mainloop.c | 2 | ||||
| -rw-r--r-- | dbus/dbus-protocol.h | 1 | ||||
| -rw-r--r-- | dbus/dbus-transport.c | 2 | ||||
| -rw-r--r-- | doc/TODO | 9 | ||||
| -rw-r--r-- | glib/dbus-gproxy.c | 2 | ||||
| -rw-r--r-- | test/data/valid-config-files/many-rules.conf | 16 | ||||
| -rw-r--r-- | test/data/valid-config-files/system.d/test.conf | 2 | ||||
| -rw-r--r-- | tools/dbus-send.c | 2 | 
28 files changed, 1697 insertions, 206 deletions
| @@ -1,3 +1,38 @@ +2003-09-21  Havoc Pennington  <hp@pobox.com> + +	Get matching rules mostly working in the bus; only actually +	parsing the rule text remains. However, the client side of +	"signal connections" hasn't been started, this patch is only the +	bus side. +	 +	* dbus/dispatch.c: fix for the matching rules changes +	 +	* bus/driver.c (bus_driver_handle_remove_match) +	(bus_driver_handle_add_match): send an ack reply from these +	method calls + +	* glib/dbus-gproxy.c (dbus_gproxy_begin_call): fix order of +	arguments, reported by Seth Nickell + +	* bus/config-parser.c (append_rule_from_element): support +	eavesdrop=true|false attribute on policies so match rules  +	can be prevented from snooping on the system bus. + +	* bus/dbus-daemon-1.1.in: consistently use terminology "sender" +	and "destination" in attribute names; fix some docs bugs;  +	add eavesdrop=true|false attribute + +	* bus/driver.c (bus_driver_handle_add_match) +	(bus_driver_handle_remove_match): handle AddMatch, RemoveMatch +	messages + +	* dbus/dbus-protocol.h (DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST): get +	rid of broadcast service concept, signals are just always broadcast + +	* bus/signals.c, bus/dispatch.c, bus/connection.c, bus/bus.c: +	mostly implement matching rules stuff (currently only exposed as signal +	connections) +  2003-09-21  Mark McLoughlin  <mark@skynet.ie>  	* doc/dbus-specification.sgml: Change the header field name diff --git a/bus/Makefile.am b/bus/Makefile.am index 27735077..bc728801 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -44,6 +44,8 @@ BUS_SOURCES=					\  	policy.h				\  	services.c				\  	services.h				\ +	signals.c				\ +	signals.h				\  	test.c					\  	test.h					\  	utils.c					\ @@ -28,6 +28,7 @@  #include "utils.h"  #include "policy.h"  #include "config-parser.h" +#include "signals.h"  #include <dbus/dbus-list.h>  #include <dbus/dbus-hash.h>  #include <dbus/dbus-internals.h> @@ -44,6 +45,7 @@ struct BusContext    BusActivation *activation;    BusRegistry *registry;    BusPolicy *policy; +  BusMatchmaker *matchmaker;    DBusUserDatabase *user_database;    BusLimits limits;  }; @@ -505,6 +507,13 @@ bus_context_new (const DBusString *config_file,        goto failed;      } +  context->matchmaker = bus_matchmaker_new (); +  if (context->matchmaker == NULL) +    { +      BUS_SET_OOM (error); +      goto failed; +    } +      context->policy = bus_config_parser_steal_policy (parser);    _dbus_assert (context->policy != NULL); @@ -715,6 +724,12 @@ bus_context_unref (BusContext *context)            _dbus_loop_unref (context->loop);            context->loop = NULL;          } + +      if (context->matchmaker) +        { +          bus_matchmaker_unref (context->matchmaker); +          context->matchmaker = NULL; +        }        dbus_free (context->type);        dbus_free (context->address); @@ -771,6 +786,12 @@ bus_context_get_activation (BusContext  *context)    return context->activation;  } +BusMatchmaker* +bus_context_get_matchmaker (BusContext  *context) +{ +  return context->matchmaker; +} +  DBusLoop*  bus_context_get_loop (BusContext *context)  { @@ -845,18 +866,33 @@ bus_context_get_max_services_per_connection (BusContext *context)    return context->limits.max_services_per_connection;  } +int +bus_context_get_max_match_rules_per_connection (BusContext *context) +{ +  return context->limits.max_match_rules_per_connection; +} +  dbus_bool_t  bus_context_check_security_policy (BusContext     *context,                                     DBusConnection *sender, -                                   DBusConnection *recipient, +                                   DBusConnection *addressed_recipient, +                                   DBusConnection *proposed_recipient,                                     DBusMessage    *message,                                     DBusError      *error)  {    BusClientPolicy *sender_policy;    BusClientPolicy *recipient_policy; -  /* NULL sender/receiver means the bus driver */ +  /* NULL sender, proposed_recipient means the bus driver.  NULL +   * addressed_recipient means the message didn't specify an explicit +   * target. If proposed_recipient is NULL, then addressed_recipient +   * is also NULL but is implicitly the bus driver. +   */ +  _dbus_assert (proposed_recipient == NULL || +                (dbus_message_get_destination (message) == NULL || +                 addressed_recipient != NULL)); +      if (sender != NULL)      {        if (bus_connection_is_active (sender)) @@ -869,7 +905,7 @@ bus_context_check_security_policy (BusContext     *context,            /* Policy for inactive connections is that they can only send             * the hello message to the bus driver             */ -          if (recipient == NULL && +          if (proposed_recipient == NULL &&                dbus_message_is_method_call (message,                                             DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,                                             "Hello")) @@ -897,15 +933,15 @@ bus_context_check_security_policy (BusContext     *context,    _dbus_assert ((sender != NULL && sender_policy != NULL) ||                  (sender == NULL && sender_policy == NULL)); -  if (recipient != NULL) +  if (proposed_recipient != NULL)      {        /* only the bus driver can send to an inactive recipient (as it         * owns no services, so other apps can't address it). Inactive         * recipients can receive any message.         */ -      if (bus_connection_is_active (recipient)) +      if (bus_connection_is_active (proposed_recipient))          { -          recipient_policy = bus_connection_get_policy (recipient); +          recipient_policy = bus_connection_get_policy (proposed_recipient);            _dbus_assert (recipient_policy != NULL);          }        else if (sender == NULL) @@ -922,13 +958,13 @@ bus_context_check_security_policy (BusContext     *context,    else      recipient_policy = NULL; -  _dbus_assert ((recipient != NULL && recipient_policy != NULL) || -                (recipient != NULL && sender == NULL && recipient_policy == NULL) || -                (recipient == NULL && recipient_policy == NULL)); +  _dbus_assert ((proposed_recipient != NULL && recipient_policy != NULL) || +                (proposed_recipient != NULL && sender == NULL && recipient_policy == NULL) || +                (proposed_recipient == NULL && recipient_policy == NULL));    if (sender_policy &&        !bus_client_policy_check_can_send (sender_policy, -                                         context->registry, recipient, +                                         context->registry, proposed_recipient,                                           message))      {        const char *dest = dbus_message_get_destination (message); @@ -951,6 +987,7 @@ bus_context_check_security_policy (BusContext     *context,    if (recipient_policy &&        !bus_client_policy_check_can_receive (recipient_policy,                                              context->registry, sender, +                                            addressed_recipient, proposed_recipient,                                              message))      {        const char *dest = dbus_message_get_destination (message); @@ -971,14 +1008,16 @@ bus_context_check_security_policy (BusContext     *context,      }    /* See if limits on size have been exceeded */ -  if (recipient && -      dbus_connection_get_outgoing_size (recipient) > +  if (proposed_recipient && +      dbus_connection_get_outgoing_size (proposed_recipient) >        context->limits.max_outgoing_bytes)      {        const char *dest = dbus_message_get_destination (message);        dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,                        "The destination service \"%s\" has a full message queue", -                      dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS); +                      dest ? dest : (proposed_recipient ? +                                     bus_connection_get_name (proposed_recipient) :  +                                     DBUS_SERVICE_ORG_FREEDESKTOP_DBUS));        _dbus_verbose ("security policy disallowing message due to full message queue\n");        return FALSE;      } @@ -40,7 +40,8 @@ typedef struct BusPolicyRule    BusPolicyRule;  typedef struct BusRegistry      BusRegistry;  typedef struct BusService       BusService;  typedef struct BusTransaction   BusTransaction; - +typedef struct BusMatchmaker    BusMatchmaker; +typedef struct BusMatchRule     BusMatchRule;  typedef struct  { @@ -54,40 +55,44 @@ typedef struct    int max_connections_per_user;     /**< Max number of connections auth'd as same user */    int max_pending_activations;      /**< Max number of pending activations for the entire bus */    int max_services_per_connection;  /**< Max number of owned services for a single connection */ +  int max_match_rules_per_connection; /**< Max number of match rules for a single connection */  } BusLimits; -BusContext*       bus_context_new                             (const DBusString *config_file, -                                                               dbus_bool_t       force_fork, -                                                               int               print_addr_fd, -                                                               int               print_pid_fd, -                                                               DBusError        *error); -void              bus_context_shutdown                        (BusContext       *context); -void              bus_context_ref                             (BusContext       *context); -void              bus_context_unref                           (BusContext       *context); -const char*       bus_context_get_type                        (BusContext       *context); -const char*       bus_context_get_address                     (BusContext       *context); -BusRegistry*      bus_context_get_registry                    (BusContext       *context); -BusConnections*   bus_context_get_connections                 (BusContext       *context); -BusActivation*    bus_context_get_activation                  (BusContext       *context); -DBusLoop*         bus_context_get_loop                        (BusContext       *context); -DBusUserDatabase* bus_context_get_user_database               (BusContext       *context); -dbus_bool_t       bus_context_allow_user                      (BusContext       *context, -                                                               unsigned long     uid); -BusClientPolicy*  bus_context_create_client_policy            (BusContext       *context, -                                                               DBusConnection   *connection, -                                                               DBusError        *error); +BusContext*       bus_context_new                                (const DBusString *config_file, +                                                                  dbus_bool_t       force_fork, +                                                                  int               print_addr_fd, +                                                                  int               print_pid_fd, +                                                                  DBusError        *error); +void              bus_context_shutdown                           (BusContext       *context); +void              bus_context_ref                                (BusContext       *context); +void              bus_context_unref                              (BusContext       *context); +const char*       bus_context_get_type                           (BusContext       *context); +const char*       bus_context_get_address                        (BusContext       *context); +BusRegistry*      bus_context_get_registry                       (BusContext       *context); +BusConnections*   bus_context_get_connections                    (BusContext       *context); +BusActivation*    bus_context_get_activation                     (BusContext       *context); +BusMatchmaker*    bus_context_get_matchmaker                     (BusContext       *context); +DBusLoop*         bus_context_get_loop                           (BusContext       *context); +DBusUserDatabase* bus_context_get_user_database                  (BusContext       *context); +dbus_bool_t       bus_context_allow_user                         (BusContext       *context, +                                                                  unsigned long     uid); +BusClientPolicy*  bus_context_create_client_policy               (BusContext       *context, +                                                                  DBusConnection   *connection, +                                                                  DBusError        *error); +int               bus_context_get_activation_timeout             (BusContext       *context); +int               bus_context_get_auth_timeout                   (BusContext       *context); +int               bus_context_get_max_completed_connections      (BusContext       *context); +int               bus_context_get_max_incomplete_connections     (BusContext       *context); +int               bus_context_get_max_connections_per_user       (BusContext       *context); +int               bus_context_get_max_pending_activations        (BusContext       *context); +int               bus_context_get_max_services_per_connection    (BusContext       *context); +int               bus_context_get_max_match_rules_per_connection (BusContext       *context); +dbus_bool_t       bus_context_check_security_policy              (BusContext       *context, +                                                                  DBusConnection   *sender, +                                                                  DBusConnection   *addressed_recipient, +                                                                  DBusConnection   *proposed_recipient, +                                                                  DBusMessage      *message, +                                                                  DBusError        *error); -int               bus_context_get_activation_timeout          (BusContext       *context); -int               bus_context_get_auth_timeout                (BusContext       *context); -int               bus_context_get_max_completed_connections   (BusContext       *context); -int               bus_context_get_max_incomplete_connections  (BusContext       *context); -int               bus_context_get_max_connections_per_user    (BusContext       *context); -int               bus_context_get_max_pending_activations     (BusContext       *context); -int               bus_context_get_max_services_per_connection (BusContext       *context); -dbus_bool_t       bus_context_check_security_policy           (BusContext       *context, -                                                               DBusConnection   *sender, -                                                               DBusConnection   *recipient, -                                                               DBusMessage      *message, -                                                               DBusError        *error);  #endif /* BUS_BUS_H */ diff --git a/bus/config-parser.c b/bus/config-parser.c index cbc239f9..7b9b962d 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -334,6 +334,8 @@ bus_config_parser_new (const DBusString *basedir,    parser->limits.max_pending_activations = 256;    parser->limits.max_services_per_connection = 256; + +  parser->limits.max_match_rules_per_connection = 128;    parser->refcount = 1; @@ -837,15 +839,16 @@ append_rule_from_element (BusConfigParser   *parser,    const char *send_interface;    const char *send_member;    const char *send_error; -  const char *send_service; +  const char *send_destination;    const char *send_path;    const char *send_type;    const char *receive_interface;    const char *receive_member;    const char *receive_error; -  const char *receive_service; +  const char *receive_sender;    const char *receive_path;    const char *receive_type; +  const char *eavesdrop;    const char *own;    const char *user;    const char *group; @@ -858,25 +861,26 @@ append_rule_from_element (BusConfigParser   *parser,                            "send_interface", &send_interface,                            "send_member", &send_member,                            "send_error", &send_error, -                          "send_service", &send_service, +                          "send_destination", &send_destination,                            "send_path", &send_path,                            "send_type", &send_type,                            "receive_interface", &receive_interface,                            "receive_member", &receive_member,                            "receive_error", &receive_error, -                          "receive_service", &receive_service, +                          "receive_sender", &receive_sender,                            "receive_path", &receive_path,                            "receive_type", &receive_type, +                          "eavesdrop", &eavesdrop,                            "own", &own,                            "user", &user,                            "group", &group,                            NULL))      return FALSE; -  if (!(send_interface || send_member || send_error || send_service || +  if (!(send_interface || send_member || send_error || send_destination ||          send_type || send_path || -        receive_interface || receive_member || receive_error || receive_service || -        receive_type || receive_path || +        receive_interface || receive_member || receive_error || receive_sender || +        receive_type || receive_path || eavesdrop ||          own || user || group))      {        dbus_set_error (error, DBUS_ERROR_FAILED, @@ -902,17 +906,20 @@ append_rule_from_element (BusConfigParser   *parser,     *     interface + member     *     error     *  -   *   base send_ can combine with send_service, send_path, send_type -   *   base receive_ with receive_service, receive_path, receive_type +   *   base send_ can combine with send_destination, send_path, send_type +   *   base receive_ with receive_sender, receive_path, receive_type, eavesdrop     *     *   user, group, own must occur alone +   * +   * Pretty sure the below stuff is broken, FIXME think about it more.     */    if (((send_interface && send_error) ||         (send_interface && receive_interface) ||         (send_interface && receive_member) ||         (send_interface && receive_error) || -       (send_interface && receive_service) || +       (send_interface && receive_sender) || +       (send_interface && eavesdrop) ||         (send_interface && own) ||         (send_interface && user) ||         (send_interface && group)) || @@ -921,7 +928,8 @@ append_rule_from_element (BusConfigParser   *parser,         (send_member && receive_interface) ||         (send_member && receive_member) ||         (send_member && receive_error) || -       (send_member && receive_service) || +       (send_member && receive_sender) || +       (send_member && eavesdrop) ||         (send_member && own) ||         (send_member && user) ||         (send_member && group)) || @@ -929,23 +937,26 @@ append_rule_from_element (BusConfigParser   *parser,        ((send_error && receive_interface) ||         (send_error && receive_member) ||         (send_error && receive_error) || -       (send_error && receive_service) || +       (send_error && receive_sender) || +       (send_error && eavesdrop) ||         (send_error && own) ||         (send_error && user) ||         (send_error && group)) || -      ((send_service && receive_interface) || -       (send_service && receive_member) || -       (send_service && receive_error) || -       (send_service && receive_service) || -       (send_service && own) || -       (send_service && user) || -       (send_service && group)) || +      ((send_destination && receive_interface) || +       (send_destination && receive_member) || +       (send_destination && receive_error) || +       (send_destination && receive_sender) || +       (send_destination && eavesdrop) || +       (send_destination && own) || +       (send_destination && user) || +       (send_destination && group)) ||        ((send_type && receive_interface) ||         (send_type && receive_member) ||         (send_type && receive_error) || -       (send_type && receive_service) || +       (send_type && receive_sender) || +       (send_type && eavesdrop) ||         (send_type && own) ||         (send_type && user) ||         (send_type && group)) || @@ -953,7 +964,8 @@ append_rule_from_element (BusConfigParser   *parser,        ((send_path && receive_interface) ||         (send_path && receive_member) ||         (send_path && receive_error) || -       (send_path && receive_service) || +       (send_path && receive_sender) || +       (send_path && eavesdrop) ||         (send_path && own) ||         (send_path && user) ||         (send_path && group)) || @@ -967,19 +979,22 @@ append_rule_from_element (BusConfigParser   *parser,         (receive_member && own) ||         (receive_member && user) ||         (receive_member && group)) || - +              ((receive_error && own) ||         (receive_error && user) ||         (receive_error && group)) || +      ((eavesdrop && own) || +       (eavesdrop && user) || +       (eavesdrop && group)) || +              ((own && user) ||         (own && group)) ||        ((user && group)))      {        dbus_set_error (error, DBUS_ERROR_FAILED, -                      "Invalid combination of attributes on element <%s>, " -                      "only send_foo/send_service or receive_foo/receive_service may be paired", +                      "Invalid combination of attributes on element <%s>",                        element_name);        return FALSE;      } @@ -991,7 +1006,7 @@ append_rule_from_element (BusConfigParser   *parser,     */  #define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0') -  if (send_interface || send_member || send_error || send_service || +  if (send_interface || send_member || send_error || send_destination ||        send_path || send_type)      {        int message_type; @@ -1002,8 +1017,8 @@ append_rule_from_element (BusConfigParser   *parser,          send_member = NULL;        if (IS_WILDCARD (send_error))          send_error = NULL; -      if (IS_WILDCARD (send_service)) -        send_service = NULL; +      if (IS_WILDCARD (send_destination)) +        send_destination = NULL;        if (IS_WILDCARD (send_path))          send_path = NULL;        if (IS_WILDCARD (send_type)) @@ -1025,13 +1040,13 @@ append_rule_from_element (BusConfigParser   *parser,        rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);         if (rule == NULL)          goto nomem; - +              rule->d.send.message_type = message_type;        rule->d.send.path = _dbus_strdup (send_path);        rule->d.send.interface = _dbus_strdup (send_interface);        rule->d.send.member = _dbus_strdup (send_member);        rule->d.send.error = _dbus_strdup (send_error); -      rule->d.send.destination = _dbus_strdup (send_service); +      rule->d.send.destination = _dbus_strdup (send_destination);        if (send_path && rule->d.send.path == NULL)          goto nomem;        if (send_interface && rule->d.send.interface == NULL) @@ -1040,11 +1055,11 @@ append_rule_from_element (BusConfigParser   *parser,          goto nomem;        if (send_error && rule->d.send.error == NULL)          goto nomem; -      if (send_service && rule->d.send.destination == NULL) +      if (send_destination && rule->d.send.destination == NULL)          goto nomem;      } -  else if (receive_interface || receive_member || receive_error || receive_service || -           receive_path || receive_type) +  else if (receive_interface || receive_member || receive_error || receive_sender || +           receive_path || receive_type || eavesdrop)      {        int message_type; @@ -1054,14 +1069,13 @@ append_rule_from_element (BusConfigParser   *parser,          receive_member = NULL;        if (IS_WILDCARD (receive_error))          receive_error = NULL; -      if (IS_WILDCARD (receive_service)) -        receive_service = NULL; +      if (IS_WILDCARD (receive_sender)) +        receive_sender = NULL;        if (IS_WILDCARD (receive_path))          receive_path = NULL;        if (IS_WILDCARD (receive_type))          receive_type = NULL; -        message_type = DBUS_MESSAGE_TYPE_INVALID;        if (receive_type != NULL)          { @@ -1074,17 +1088,31 @@ append_rule_from_element (BusConfigParser   *parser,                return FALSE;              }          } + + +      if (eavesdrop && +          !(strcmp (eavesdrop, "true") == 0 || +            strcmp (eavesdrop, "false") == 0)) +        { +          dbus_set_error (error, DBUS_ERROR_FAILED, +                          "Bad value \"%s\" for eavesdrop attribute, must be true or false", +                          eavesdrop); +          return FALSE; +        }        rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow);         if (rule == NULL)          goto nomem; +      if (eavesdrop) +        rule->d.receive.eavesdrop = (strcmp (eavesdrop, "true") == 0); +              rule->d.receive.message_type = message_type;        rule->d.receive.path = _dbus_strdup (receive_path);        rule->d.receive.interface = _dbus_strdup (receive_interface);        rule->d.receive.member = _dbus_strdup (receive_member);        rule->d.receive.error = _dbus_strdup (receive_error); -      rule->d.receive.origin = _dbus_strdup (receive_service); +      rule->d.receive.origin = _dbus_strdup (receive_sender);        if (receive_path && rule->d.receive.path == NULL)          goto nomem;        if (receive_interface && rule->d.receive.interface == NULL) @@ -1093,7 +1121,7 @@ append_rule_from_element (BusConfigParser   *parser,          goto nomem;        if (receive_error && rule->d.receive.error == NULL)          goto nomem; -      if (receive_service && rule->d.receive.origin == NULL) +      if (receive_sender && rule->d.receive.origin == NULL)          goto nomem;      }    else if (own) diff --git a/bus/connection.c b/bus/connection.c index f6ce4a29..a824576c 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -25,6 +25,7 @@  #include "policy.h"  #include "services.h"  #include "utils.h" +#include "signals.h"  #include <dbus/dbus-list.h>  #include <dbus/dbus-hash.h>  #include <dbus/dbus-timeout.h> @@ -41,6 +42,7 @@ struct BusConnections    BusContext *context;    DBusHashTable *completed_by_user; /**< Number of completed connections for each UID */    DBusTimeout *expire_timeout; /**< Timeout for expiring incomplete connections. */ +  int stamp;            /**< Incrementing number */  };  static dbus_int32_t connection_data_slot = -1; @@ -52,6 +54,8 @@ typedef struct    DBusConnection *connection;    DBusList *services_owned;    int n_services_owned; +  DBusList *match_rules; +  int n_match_rules;    char *name;    DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */    DBusMessage *oom_message; @@ -60,6 +64,7 @@ typedef struct    long connection_tv_sec;  /**< Time when we connected (seconds component) */    long connection_tv_usec; /**< Time when we connected (microsec component) */ +  int stamp;               /**< connections->stamp last time we were traversed */  } BusConnectionData;  static dbus_bool_t expire_incomplete_timeout (void *data); @@ -140,12 +145,20 @@ bus_connection_disconnected (DBusConnection *connection)  {    BusConnectionData *d;    BusService *service; - +  BusMatchmaker *matchmaker; +      d = BUS_CONNECTION_DATA (connection);    _dbus_assert (d != NULL);    _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n",                   d->name ? d->name : "(inactive)"); + +  /* Delete our match rules */ +  if (d->n_match_rules > 0) +    { +      matchmaker = bus_context_get_matchmaker (d->connections->context); +      bus_matchmaker_disconnected (matchmaker, connection); +    }    /* Drop any service ownership. FIXME Unfortunately, this requires     * memory allocation and there doesn't seem to be a good way to @@ -881,6 +894,40 @@ bus_connections_get_context (BusConnections *connections)    return connections->context;  } +/* + * This is used to avoid covering the same connection twice when + * traversing connections. Note that it assumes we will + * bus_connection_mark_stamp() each connection at least once per + * INT_MAX increments of the global stamp, or wraparound would break + * things. + */ +void +bus_connections_increment_stamp (BusConnections *connections) +{ +  connections->stamp += 1; +} + +/* Mark connection with current stamp, return TRUE if it + * didn't already have that stamp + */ +dbus_bool_t +bus_connection_mark_stamp (DBusConnection *connection) +{ +  BusConnectionData *d; +   +  d = BUS_CONNECTION_DATA (connection); +   +  _dbus_assert (d != NULL); + +  if (d->stamp == d->connections->stamp) +    return FALSE; +  else +    { +      d->stamp = d->connections->stamp; +      return TRUE; +    } +} +  BusContext*  bus_connection_get_context (DBusConnection *connection)  { @@ -929,6 +976,18 @@ bus_connection_get_activation (DBusConnection *connection)    return bus_context_get_activation (d->connections->context);  } +BusMatchmaker* +bus_connection_get_matchmaker (DBusConnection *connection) +{ +  BusConnectionData *d; + +  d = BUS_CONNECTION_DATA (connection); + +  _dbus_assert (d != NULL); + +  return bus_context_get_matchmaker (d->connections->context); +} +  /**   * Checks whether the connection is registered with the message bus.   * @@ -1025,6 +1084,62 @@ bus_connection_send_oom_error (DBusConnection *connection,  }  void +bus_connection_add_match_rule_link (DBusConnection *connection, +                                    DBusList       *link) +{ +  BusConnectionData *d; + +  d = BUS_CONNECTION_DATA (connection); +  _dbus_assert (d != NULL); + +  _dbus_list_append_link (&d->match_rules, link); + +  d->n_match_rules += 1; +} + +dbus_bool_t +bus_connection_add_match_rule (DBusConnection *connection, +                               BusMatchRule   *rule) +{ +    DBusList *link; + +  link = _dbus_list_alloc_link (rule); + +  if (link == NULL) +    return FALSE; + +  bus_connection_add_match_rule_link (connection, link); + +  return TRUE; +} + +void +bus_connection_remove_match_rule (DBusConnection *connection, +                                  BusMatchRule   *rule) +{ +  BusConnectionData *d; + +  d = BUS_CONNECTION_DATA (connection); +  _dbus_assert (d != NULL); + +  _dbus_list_remove_last (&d->match_rules, rule); + +  d->n_match_rules -= 1; +  _dbus_assert (d->n_match_rules >= 0); +} + +int +bus_connection_get_n_match_rules (DBusConnection *connection) +{ +  BusConnectionData *d; + +  d = BUS_CONNECTION_DATA (connection); +  _dbus_assert (d != NULL); +   +  return d->n_match_rules; +} + +void  bus_connection_add_owned_service_link (DBusConnection *connection,                                         DBusList       *link)  { @@ -1092,6 +1207,8 @@ bus_connection_complete (DBusConnection   *connection,    _dbus_assert (d != NULL);    _dbus_assert (d->name == NULL);    _dbus_assert (d->policy == NULL); + +  _dbus_assert (!bus_connection_is_active (connection));    if (!_dbus_string_copy_data (name, &d->name))      { @@ -1147,6 +1264,8 @@ bus_connection_complete (DBusConnection   *connection,    /* See if we can remove the timeout */    bus_connections_expire_incomplete (d->connections); + +  _dbus_assert (bus_connection_is_active (connection));    return TRUE;  } @@ -1327,7 +1446,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,     * eat it; the driver doesn't care about getting a reply.     */    if (!bus_context_check_security_policy (bus_transaction_get_context (transaction), -                                          NULL, connection, message, NULL)) +                                          NULL, connection, connection, message, NULL))      return TRUE;    return bus_transaction_send (transaction, connection, message); diff --git a/bus/connection.h b/bus/connection.h index 92c93267..d9fd727e 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -44,16 +44,18 @@ void            bus_connections_foreach_active    (BusConnections                                                     BusConnectionForeachFunction  function,                                                     void                         *data);  BusContext*     bus_connections_get_context       (BusConnections               *connections); +void            bus_connections_increment_stamp   (BusConnections               *connections);  BusContext*     bus_connection_get_context        (DBusConnection               *connection);  BusConnections* bus_connection_get_connections    (DBusConnection               *connection);  BusRegistry*    bus_connection_get_registry       (DBusConnection               *connection);  BusActivation*  bus_connection_get_activation     (DBusConnection               *connection); +BusMatchmaker*  bus_connection_get_matchmaker     (DBusConnection               *connection);  dbus_bool_t     bus_connections_check_limits      (BusConnections               *connections,                                                     DBusConnection               *requesting_completion,                                                     DBusError                    *error);  void            bus_connections_expire_incomplete (BusConnections               *connections); - +dbus_bool_t     bus_connection_mark_stamp         (DBusConnection               *connection);  dbus_bool_t bus_connection_is_active (DBusConnection *connection);  const char *bus_connection_get_name  (DBusConnection *connection); @@ -62,6 +64,15 @@ dbus_bool_t bus_connection_preallocate_oom_error (DBusConnection *connection);  void        bus_connection_send_oom_error        (DBusConnection *connection,                                                    DBusMessage    *in_reply_to); +/* called by signals.c */ +dbus_bool_t bus_connection_add_match_rule      (DBusConnection *connection, +                                                BusMatchRule   *rule); +void        bus_connection_add_match_rule_link (DBusConnection *connection, +                                                DBusList       *link); +void        bus_connection_remove_match_rule   (DBusConnection *connection, +                                                BusMatchRule   *rule); +int         bus_connection_get_n_match_rules   (DBusConnection *connection); +  /* called by services.c */  dbus_bool_t bus_connection_add_owned_service      (DBusConnection *connection, diff --git a/bus/dbus-daemon-1.1.in b/bus/dbus-daemon-1.1.in index ec915edd..b272a62e 100644 --- a/bus/dbus-daemon-1.1.in +++ b/bus/dbus-daemon-1.1.in @@ -328,26 +328,32 @@ in the config file.  .TP  .I "<deny>" +.I "<allow>" + +.PP +A <deny> element appears below a <policy> element and prohibits some +action. The <allow> element makes an exception to previous <deny> +statements, and works just like <deny> but with the inverse meaning.  .PP -A <deny> element appears below a <policy> element and prohibits  -some action. The possible attributes of a <deny> element are: +The possible attributes of these elements are:  .nf     send_interface="interface_name"     send_member="method_or_signal_name"      send_error="error_name"  -   send_service="service_name"  -   send_type="method_call|method_return|signal|error"  +   send_destination="service_name"  +   send_type="method_call" | "method_return" | "signal" | "error"      send_path="/path/name"     receive_interface="interface_name"     receive_member="method_or_signal_name"      receive_error="error_name"  -   receive_service="service_name"  -   receive_type="method_call|method_return|signal|error"  +   receive_sender="service_name"  +   receive_type="method_call" | "method_return" | "signal" | "error"     receive_path="/path/name" +    +   eavesdrop="true" | "false" -   receive="messagename"     own="servicename"     user="username"     group="groupname" @@ -359,8 +365,8 @@ Examples:     <deny send_interface="org.freedesktop.System" send_member="Reboot"/>      <deny receive_interface="org.freedesktop.System" receive_member="Reboot"/>     <deny own="org.freedesktop.System"/> -   <deny send_service="org.freedesktop.System"/> -   <deny receive_service="org.freedesktop.System"/> +   <deny send_destination="org.freedesktop.System"/> +   <deny receive_sender="org.freedesktop.System"/>     <deny user="john"/>     <deny group="enemies"/>  .fi @@ -371,7 +377,7 @@ particular action. If it matches, the action is denied (unless later  rules in the config file allow it).  .PP -send_service and receive_service rules mean that messages may not be +send_destination and receive_sender rules mean that messages may not be  sent to or received from the *owner* of the given service, not that  they may not be sent *to that service name*. That is, if a connection  owns services A, B, C, and sending to A is denied, sending to B or C @@ -382,6 +388,22 @@ The other send_* and receive_* attributes are purely textual/by-value  matches against the given field in the message header.  .PP +"Eavesdropping" occurs when an application receives a message that +was explicitly addressed to a service the application does not own. +Eavesdropping thus only applies to messages that are addressed to +services (i.e. it does not apply to signals). + +.PP +For <allow>, eavesdrop="true" indicates that the rule matches even  +when eavesdropping. eavesdrop="false" is the default and means that  +the rule only allows messages to go to their specified recipient. +For <deny>, eavesdrop="true" indicates that the rule matches  +only when eavesdropping. eavesdrop="false" is the default for <deny> +also, but here it means that the rule applies always, even when  +not eavesdropping. The eavesdrop attribute can only be combined with +receive rules (with receive_* attributes). + +.PP  user and group denials mean that the given user or group may   not connect to the message bus. @@ -413,13 +435,6 @@ received" are evaluated separately.  Be careful with send_interface/receive_interface, because the   interface field in messages is optional. -.TP -.I "<allow>" - -.PP -Makes an exception to previous <deny> statements. Works  -just like <deny> but with the inverse meaning. -  .SH AUTHOR  See http://www.freedesktop.org/software/dbus/doc/AUTHORS diff --git a/bus/dispatch.c b/bus/dispatch.c index 7bdda0d4..606c68ef 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -28,36 +28,33 @@  #include "services.h"  #include "utils.h"  #include "bus.h" +#include "signals.h"  #include "test.h"  #include <dbus/dbus-internals.h>  #include <string.h> -typedef struct -{ -  BusContext     *context; -  DBusConnection *sender; -  DBusMessage    *message; -  BusTransaction *transaction; -  DBusError      *error; -} SendMessageData; -  static dbus_bool_t -send_one_message (DBusConnection *connection, void *data) +send_one_message (DBusConnection *connection, +                  BusContext     *context, +                  DBusConnection *sender, +                  DBusConnection *addressed_recipient, +                  DBusMessage    *message, +                  BusTransaction *transaction, +                  DBusError      *error)  { -  SendMessageData *d = data; - -  if (!bus_context_check_security_policy (d->context, -                                          d->sender, +  if (!bus_context_check_security_policy (context, +                                          sender, +                                          addressed_recipient,                                            connection, -                                          d->message, +                                          message,                                            NULL))      return TRUE; /* silently don't send it */ -  if (!bus_transaction_send (d->transaction, +  if (!bus_transaction_send (transaction,                               connection, -                             d->message)) +                             message))      { -      BUS_SET_OOM (d->error); +      BUS_SET_OOM (error);        return FALSE;      } @@ -65,30 +62,60 @@ send_one_message (DBusConnection *connection, void *data)  }  dbus_bool_t -bus_dispatch_broadcast_message (BusTransaction *transaction, -                                DBusConnection *sender, -                                DBusMessage    *message, -                                DBusError      *error) +bus_dispatch_matches (BusTransaction *transaction, +                      DBusConnection *sender, +                      DBusConnection *addressed_recipient, +                      DBusMessage    *message, +                      DBusError      *error)  {    DBusError tmp_error; -  SendMessageData d;    BusConnections *connections; +  DBusList *recipients; +  BusMatchmaker *matchmaker; +  DBusList *link; +  BusContext *context;    _DBUS_ASSERT_ERROR_IS_CLEAR (error); -   + +  /* sender and recipient can both be NULL for the bus driver, +   * or for signals with no particular recipient +   */ + +  _dbus_assert (sender == NULL || bus_connection_is_active (sender));    _dbus_assert (dbus_message_get_sender (message) != NULL);    connections = bus_transaction_get_connections (transaction);    dbus_error_init (&tmp_error); -  d.sender = sender; -  d.context = bus_transaction_get_context (transaction); -  d.message = message; -  d.transaction = transaction; -  d.error = &tmp_error; -   -  bus_connections_foreach_active (connections, send_one_message, &d); +  context = bus_transaction_get_context (transaction); +  matchmaker = bus_context_get_matchmaker (context); + +  recipients = NULL; +  if (!bus_matchmaker_get_recipients (matchmaker, +                                      bus_context_get_connections (context), +                                      sender, addressed_recipient, message, +                                      &recipients)) +    { +      BUS_SET_OOM (error); +      return FALSE; +    } + +  link = _dbus_list_get_first_link (&recipients); +  while (link != NULL) +    { +      DBusConnection *dest; + +      dest = link->data; + +      if (!send_one_message (dest, context, sender, addressed_recipient, +                             message, transaction, &tmp_error)) +        break; + +      link = _dbus_list_get_next_link (&recipients, link); +    } +  _dbus_list_clear (&recipients); +      if (dbus_error_is_set (&tmp_error))      {        dbus_move_error (&tmp_error, error); @@ -107,10 +134,12 @@ bus_dispatch (DBusConnection *connection,    BusTransaction *transaction;    BusContext *context;    DBusHandlerResult result; - +  DBusConnection *addressed_recipient; +      result = DBUS_HANDLER_RESULT_HANDLED;    transaction = NULL; +  addressed_recipient = NULL;    dbus_error_init (&error);    context = bus_connection_get_context (connection); @@ -143,26 +172,31 @@ bus_dispatch (DBusConnection *connection,    }  #endif /* DBUS_ENABLE_VERBOSE_MODE */ -  /* If service_name is NULL, this is a message to the bus daemon, not -   * intended to actually go "on the bus"; e.g. a peer-to-peer +  /* If service_name is NULL, if it's a signal we send it to all +   * connections with a match rule. If it's not a signal, it goes to +   * the bus daemon but doesn't go "on the bus"; e.g. a peer-to-peer     * ping. Handle these immediately, especially disconnection     * messages. There are no security policy checks on these.     */    if (service_name == NULL) -    {       +    {        if (dbus_message_is_signal (message,                                    DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,                                    "Disconnected")) -        bus_connection_disconnected (connection); +        { +          bus_connection_disconnected (connection); +          goto out; +        } -      /* DBusConnection also handles some of these automatically, we leave -       * it to do so. -       */ -      result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -      goto out; +      if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) +        { +          /* DBusConnection also handles some of these automatically, we leave +           * it to do so. +           */ +          result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +          goto out; +        }      } - -  _dbus_assert (service_name != NULL); /* this message is intended for bus routing */    /* Create our transaction */    transaction = bus_transaction_new (context); @@ -191,11 +225,12 @@ bus_dispatch (DBusConnection *connection,         */        service_name = dbus_message_get_destination (message);      } - -  if (strcmp (service_name, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) == 0) /* to bus driver */ +   +  if (service_name && +      strcmp (service_name, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) == 0) /* to bus driver */      {        if (!bus_context_check_security_policy (context, -                                              connection, NULL, message, &error)) +                                              connection, NULL, NULL, message, &error))          {            _dbus_verbose ("Security policy rejected message\n");            goto out; @@ -209,22 +244,16 @@ bus_dispatch (DBusConnection *connection,      {        _dbus_verbose ("Received message from non-registered client. Disconnecting.\n");        dbus_connection_disconnect (connection); +      goto out;      } -  /* FIXME what if we un-special-case this service and just have a flag -   * on services that all service owners will get messages to it, not just -   * the primary owner. -   */ -  else if (strcmp (service_name, DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST) == 0) /* spam! */ -    { -      if (!bus_dispatch_broadcast_message (transaction, connection, message, &error)) -        goto out; -    } -  else  /* route to named service */ +  else if (service_name != NULL) /* route to named service */      {        DBusString service_string;        BusService *service;        BusRegistry *registry; +      _dbus_assert (service_name != NULL); +              registry = bus_connection_get_registry (connection);        _dbus_string_init_const (&service_string, service_name); @@ -239,24 +268,30 @@ bus_dispatch (DBusConnection *connection,            goto out;          }        else -        { -          DBusConnection *recipient; -           -          recipient = bus_service_get_primary_owner (service); -          _dbus_assert (recipient != NULL); +        {           +          addressed_recipient = bus_service_get_primary_owner (service); +          _dbus_assert (addressed_recipient != NULL);            if (!bus_context_check_security_policy (context, -                                                  connection, recipient, message, &error)) +                                                  connection, addressed_recipient, +                                                  addressed_recipient, +                                                  message, &error))              goto out;            /* Dispatch the message */ -          if (!bus_transaction_send (transaction, recipient, message)) +          if (!bus_transaction_send (transaction, addressed_recipient, message))              {                BUS_SET_OOM (&error);                goto out;              }          }      } + +  /* Now match the messages against any match rules, which will send +   * out signals and such. addressed_recipient may == NULL. +   */ +  if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error)) +    goto out;   out:    if (dbus_error_is_set (&error)) @@ -673,7 +708,7 @@ check_hello_message (BusContext     *context,                       DBusConnection *connection)  {    DBusMessage *message; -  dbus_int32_t serial; +  dbus_uint32_t serial;    dbus_bool_t retval;    DBusError error;    char *name; @@ -684,6 +719,8 @@ check_hello_message (BusContext     *context,    name = NULL;    acquired = NULL;    message = NULL; + +  _dbus_verbose ("check_hello_message for %p\n", connection);    message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,                                            DBUS_PATH_ORG_FREEDESKTOP_DBUS, @@ -791,7 +828,7 @@ check_hello_message (BusContext     *context,        while (!dbus_bus_set_base_service (connection, name))          _dbus_wait_for_memory (); -      scd.skip_connection = NULL; +      scd.skip_connection = connection; /* we haven't done AddMatch so won't get it ourselves */        scd.failed = FALSE;        scd.expected_service_name = name;        bus_test_clients_foreach (check_service_created_foreach, @@ -861,6 +898,126 @@ check_hello_message (BusContext     *context,   * but the correct thing may include OOM errors.   */  static dbus_bool_t +check_add_match_all (BusContext     *context, +                     DBusConnection *connection) +{ +  DBusMessage *message; +  dbus_bool_t retval; +  dbus_uint32_t serial; +  DBusError error; + +  retval = FALSE; +  dbus_error_init (&error); +  message = NULL; + +  _dbus_verbose ("check_add_match_all for %p\n", connection); +   +  message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS, +                                          DBUS_PATH_ORG_FREEDESKTOP_DBUS, +                                          DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, +                                          "AddMatch"); + +  if (message == NULL) +    return TRUE; + +  if (!dbus_message_append_args (message, DBUS_TYPE_STRING, "", /* FIXME */ +                                 DBUS_TYPE_INVALID)) +    { +      dbus_message_unref (message); +      return TRUE; +    } +   +  if (!dbus_connection_send (connection, message, &serial)) +    { +      dbus_message_unref (message); +      return TRUE; +    } + +  dbus_message_unref (message); +  message = NULL; + +  /* send our message */ +  bus_test_run_clients_loop (TRUE); + +  dbus_connection_ref (connection); /* because we may get disconnected */ +  block_connection_until_message_from_bus (context, connection); + +  if (!dbus_connection_get_is_connected (connection)) +    { +      _dbus_verbose ("connection was disconnected\n"); +       +      dbus_connection_unref (connection); +       +      return TRUE; +    } + +  dbus_connection_unref (connection); +   +  message = pop_message_waiting_for_memory (connection); +  if (message == NULL) +    { +      _dbus_warn ("Did not receive a reply to %s %d on %p\n", +                  "AddMatch", serial, connection); +      goto out; +    } + +  verbose_message_received (connection, message); + +  if (!dbus_message_has_sender (message, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS)) +    { +      _dbus_warn ("Message has wrong sender %s\n", +                  dbus_message_get_sender (message) ? +                  dbus_message_get_sender (message) : "(none)"); +      goto out; +    } +   +  if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) +    { +      if (dbus_message_is_error (message, +                                 DBUS_ERROR_NO_MEMORY)) +        { +          ; /* good, this is a valid response */ +        } +      else +        { +          warn_unexpected (connection, message, "not this error"); + +          goto out; +        } +    } +  else +    { +      if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) +        { +          ; /* good, expected */ +          _dbus_assert (dbus_message_get_reply_serial (message) == serial); +        } +      else +        { +          warn_unexpected (connection, message, "method return for AddMatch"); + +          goto out; +        } +    } + +  if (!check_no_leftovers (context)) +    goto out; +   +  retval = TRUE; +   + out: +  dbus_error_free (&error); +   +  if (message) +    dbus_message_unref (message); +   +  return retval; +} + +/* returns TRUE if the correct thing happens, + * but the correct thing may include OOM errors. + */ +static dbus_bool_t  check_hello_connection (BusContext *context)  {    DBusConnection *connection; @@ -885,7 +1042,7 @@ check_hello_connection (BusContext *context)    if (!check_hello_message (context, connection))      return FALSE; - +      if (dbus_bus_get_base_service (connection) == NULL)      {        /* We didn't successfully register, so we can't @@ -895,6 +1052,9 @@ check_hello_connection (BusContext *context)      }    else      { +      if (!check_add_match_all (context, connection)) +        return FALSE; +              kill_client_connection (context, connection);      } @@ -911,7 +1071,7 @@ check_nonexistent_service_activation (BusContext     *context,                                        DBusConnection *connection)  {    DBusMessage *message; -  dbus_int32_t serial; +  dbus_uint32_t serial;    dbus_bool_t retval;    DBusError error; @@ -1293,7 +1453,7 @@ check_send_exit_to_service (BusContext     *context,  {    dbus_bool_t got_error;    DBusMessage *message; -  dbus_int32_t serial; +  dbus_uint32_t serial;    dbus_bool_t retval;    _dbus_verbose ("Sending exit message to the test service\n"); @@ -1462,7 +1622,7 @@ check_existent_service_activation (BusContext     *context,                                     DBusConnection *connection)  {    DBusMessage *message; -  dbus_int32_t serial; +  dbus_uint32_t serial;    dbus_bool_t retval;    DBusError error;    char *base_service; @@ -1676,7 +1836,7 @@ check_segfault_service_activation (BusContext     *context,                                     DBusConnection *connection)  {    DBusMessage *message; -  dbus_int32_t serial; +  dbus_uint32_t serial;    dbus_bool_t retval;    DBusError error; @@ -1877,6 +2037,9 @@ bus_dispatch_test (const DBusString *test_data_dir)    if (!check_hello_message (context, foo))      _dbus_assert_not_reached ("hello message failed"); + +  if (!check_add_match_all (context, foo)) +    _dbus_assert_not_reached ("AddMatch message failed");    bar = dbus_connection_open ("debug-pipe:name=test-server", &error);    if (bar == NULL) @@ -1887,6 +2050,9 @@ bus_dispatch_test (const DBusString *test_data_dir)    if (!check_hello_message (context, bar))      _dbus_assert_not_reached ("hello message failed"); + +  if (!check_add_match_all (context, bar)) +    _dbus_assert_not_reached ("AddMatch message failed");    baz = dbus_connection_open ("debug-pipe:name=test-server", &error);    if (baz == NULL) @@ -1898,6 +2064,9 @@ bus_dispatch_test (const DBusString *test_data_dir)    if (!check_hello_message (context, baz))      _dbus_assert_not_reached ("hello message failed"); +  if (!check_add_match_all (context, baz)) +    _dbus_assert_not_reached ("AddMatch message failed"); +      if (!check_no_leftovers (context))      {        _dbus_warn ("Messages were left over after setting up initial connections"); @@ -1954,6 +2123,9 @@ bus_dispatch_sha1_test (const DBusString *test_data_dir)    if (!check_hello_message (context, foo))      _dbus_assert_not_reached ("hello message failed"); +  if (!check_add_match_all (context, foo)) +    _dbus_assert_not_reached ("addmatch message failed"); +      if (!check_no_leftovers (context))      {        _dbus_warn ("Messages were left over after setting up initial SHA-1 connection\n"); diff --git a/bus/dispatch.h b/bus/dispatch.h index 18f74529..d8107b1a 100644 --- a/bus/dispatch.h +++ b/bus/dispatch.h @@ -29,8 +29,9 @@  dbus_bool_t bus_dispatch_add_connection    (DBusConnection *connection);  void        bus_dispatch_remove_connection (DBusConnection *connection); -dbus_bool_t bus_dispatch_broadcast_message (BusTransaction *transaction, +dbus_bool_t bus_dispatch_matches           (BusTransaction *transaction,                                              DBusConnection *sender, +                                            DBusConnection *recipient,                                              DBusMessage    *message,                                              DBusError      *error); diff --git a/bus/driver.c b/bus/driver.c index 61bfe1c5..791fcd69 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -27,6 +27,7 @@  #include "driver.h"  #include "dispatch.h"  #include "services.h" +#include "signals.h"  #include "utils.h"  #include <dbus/dbus-string.h>  #include <dbus/dbus-internals.h> @@ -69,7 +70,7 @@ bus_driver_send_service_deleted (const char     *service_name,        return FALSE;      } -  retval = bus_dispatch_broadcast_message (transaction, NULL, message, error); +  retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);    dbus_message_unref (message);    return retval; @@ -111,7 +112,7 @@ bus_driver_send_service_created (const char     *service_name,        return FALSE;      } -  retval = bus_dispatch_broadcast_message (transaction, NULL, message, error); +  retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);    dbus_message_unref (message);    return retval; @@ -331,6 +332,7 @@ bus_driver_handle_hello (DBusConnection *connection,    bus_service_set_prohibit_replacement (service, TRUE); +  _dbus_assert (bus_connection_is_active (connection));    retval = TRUE;   out_0: @@ -600,6 +602,160 @@ bus_driver_handle_activate_service (DBusConnection *connection,    return retval;  } +static dbus_bool_t +send_ack_reply (DBusConnection *connection, +                BusTransaction *transaction, +                DBusMessage    *message, +                DBusError      *error) +{ +  DBusMessage *reply; + +  reply = dbus_message_new_method_return (message); +  if (reply == NULL) +    { +      BUS_SET_OOM (error); +      return FALSE; +    } + +  if (!bus_transaction_send_from_driver (transaction, connection, reply)) +    { +      BUS_SET_OOM (error); +      dbus_message_unref (reply); +      return FALSE; +    } + +  dbus_message_unref (reply); +   +  return TRUE; +} + +static dbus_bool_t +bus_driver_handle_add_match (DBusConnection *connection, +                             BusTransaction *transaction, +                             DBusMessage    *message, +                             DBusError      *error) +{ +  BusMatchRule *rule; +  char *text; +  DBusString str; +  BusMatchmaker *matchmaker; +   +  _DBUS_ASSERT_ERROR_IS_CLEAR (error); + +  text = NULL; +  rule = NULL; + +  if (bus_connection_get_n_match_rules (connection) >= +      bus_context_get_max_match_rules_per_connection (bus_transaction_get_context (transaction))) +    { +      dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, +                      "Connection \"%s\" is not allowed to add more match rules " +                      "(increase limits in configuration file if required)", +                      bus_connection_is_active (connection) ? +                      bus_connection_get_name (connection) : +                      "(inactive)"); +      goto failed; +    } +   +  if (!dbus_message_get_args (message, error, +                              DBUS_TYPE_STRING, &text, +                              DBUS_TYPE_INVALID)) +    { +      _dbus_verbose ("No memory to get arguments to AddMatch\n"); +      goto failed; +    } + +  _dbus_string_init_const (&str, text); + +  rule = bus_match_rule_parse (connection, &str, error); +  if (rule == NULL) +    goto failed; + +  matchmaker = bus_connection_get_matchmaker (connection); + +  if (!bus_matchmaker_add_rule (matchmaker, rule)) +    { +      BUS_SET_OOM (error); +      goto failed; +    } + +  if (!send_ack_reply (connection, transaction, +                       message, error)) +    { +      bus_matchmaker_remove_rule (matchmaker, rule); +      goto failed; +    } +   +  bus_match_rule_unref (rule); +  dbus_free (text); +   +  return TRUE; + + failed: +  _DBUS_ASSERT_ERROR_IS_SET (error); +  if (rule) +    bus_match_rule_unref (rule); +  if (text) +    dbus_free (text); +  return FALSE; +} + +static dbus_bool_t +bus_driver_handle_remove_match (DBusConnection *connection, +                                BusTransaction *transaction, +                                DBusMessage    *message, +                                DBusError      *error) +{ +  BusMatchRule *rule; +  char *text; +  DBusString str; +  BusMatchmaker *matchmaker; +   +  _DBUS_ASSERT_ERROR_IS_CLEAR (error); + +  text = NULL; +  rule = NULL; +   +  if (!dbus_message_get_args (message, error, +                              DBUS_TYPE_STRING, &text, +                              DBUS_TYPE_INVALID)) +    { +      _dbus_verbose ("No memory to get arguments to RemoveMatch\n"); +      goto failed; +    } + +  _dbus_string_init_const (&str, text); + +  rule = bus_match_rule_parse (connection, &str, error); +  if (rule == NULL) +    goto failed; + +  /* Send the ack before we remove the rule, since the ack is undone +   * on transaction cancel, but rule removal isn't. +   */ +  if (!send_ack_reply (connection, transaction, +                       message, error)) +    goto failed; +   +  matchmaker = bus_connection_get_matchmaker (connection); + +  if (!bus_matchmaker_remove_rule_by_value (matchmaker, rule, error)) +    goto failed; + +  bus_match_rule_unref (rule); +  dbus_free (text); +   +  return TRUE; + + failed: +  _DBUS_ASSERT_ERROR_IS_SET (error); +  if (rule) +    bus_match_rule_unref (rule); +  if (text) +    dbus_free (text); +  return FALSE; +} +  /* For speed it might be useful to sort this in order of   * frequency of use (but doesn't matter with only a few items   * anyhow) @@ -616,7 +772,9 @@ struct    { "ActivateService", bus_driver_handle_activate_service },    { "Hello", bus_driver_handle_hello },    { "ServiceExists", bus_driver_handle_service_exists }, -  { "ListServices", bus_driver_handle_list_services } +  { "ListServices", bus_driver_handle_list_services }, +  { "AddMatch", bus_driver_handle_add_match }, +  { "RemoveMatch", bus_driver_handle_remove_match }  };  dbus_bool_t diff --git a/bus/policy.c b/bus/policy.c index 21d0b02e..2d462fb6 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -917,16 +917,33 @@ dbus_bool_t  bus_client_policy_check_can_receive (BusClientPolicy *policy,                                       BusRegistry     *registry,                                       DBusConnection  *sender, +                                     DBusConnection  *addressed_recipient, +                                     DBusConnection  *proposed_recipient,                                       DBusMessage     *message)  {    DBusList *link;    dbus_bool_t allowed; +  dbus_bool_t eavesdropping; +   +  /* NULL sender, proposed_recipient means the bus driver.  NULL +   * addressed_recipient means the message didn't specify an explicit +   * target. If proposed_recipient is NULL, then addressed_recipient +   * is also NULL but is implicitly the bus driver. +   */ + +  _dbus_assert (proposed_recipient == NULL || +                (dbus_message_get_destination (message) == NULL || +                 addressed_recipient != NULL)); +   +  eavesdropping = +    (proposed_recipient == NULL || /* explicitly to bus driver */ +     (addressed_recipient && addressed_recipient != proposed_recipient)); /* explicitly to a different recipient */    /* policy->rules is in the order the rules appeared     * in the config file, i.e. last rule that applies wins     */ -  _dbus_verbose ("  (policy) checking receive rules\n"); +  _dbus_verbose ("  (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);    allowed = FALSE;    link = _dbus_list_get_first_link (&policy->rules); @@ -950,6 +967,24 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,                continue;              }          } + +      /* for allow, eavesdrop=false means the rule doesn't apply when +       * eavesdropping. eavesdrop=true means always allow. +       */ +      if (eavesdropping && rule->allow && !rule->d.receive.eavesdrop) +        { +          _dbus_verbose ("  (policy) skipping allow rule since it doesn't apply to eavesdropping\n"); +          continue; +        } + +      /* for deny, eavesdrop=true means the rule applies only when +       * eavesdropping; eavesdrop=false means always deny. +       */ +      if (!eavesdropping && !rule->allow && rule->d.receive.eavesdrop) +        { +          _dbus_verbose ("  (policy) skipping deny rule since it only applies to eavesdropping\n"); +          continue; +        }        if (rule->d.receive.path != NULL)          { @@ -1036,7 +1071,7 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,                  }              }          } - +              /* Use this rule */        allowed = rule->allow; diff --git a/bus/policy.h b/bus/policy.h index 5824816c..63981cc0 100644 --- a/bus/policy.h +++ b/bus/policy.h @@ -74,6 +74,7 @@ struct BusPolicyRule        char *member;        char *error;        char *origin; +      unsigned int eavesdrop : 1;      } receive;      struct @@ -134,6 +135,8 @@ dbus_bool_t      bus_client_policy_check_can_send    (BusClientPolicy  *policy,  dbus_bool_t      bus_client_policy_check_can_receive (BusClientPolicy  *policy,                                                        BusRegistry      *registry,                                                        DBusConnection   *sender, +                                                      DBusConnection   *addressed_recipient, +                                                      DBusConnection   *proposed_recipient,                                                        DBusMessage      *message);  dbus_bool_t      bus_client_policy_check_can_own     (BusClientPolicy  *policy,                                                        DBusConnection   *connection, diff --git a/bus/session.conf.in b/bus/session.conf.in index 09dd250e..45945688 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -14,10 +14,9 @@    <policy context="default">      <!-- Allow everything --> -    <allow send_interface="*"/> -    <allow receive_interface="*"/> +    <allow eavesdrop="true"/>      <allow own="*"/> -    <allow user="*"/>   +    <allow user="*"/>    </policy>    <!-- This is included last so local configuration can override what's  diff --git a/bus/signals.c b/bus/signals.c new file mode 100644 index 00000000..db7b0665 --- /dev/null +++ b/bus/signals.c @@ -0,0 +1,774 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* signals.c  Bus signal connection implementation + * + * Copyright (C) 2003  Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ +#include "signals.h" +#include "services.h" +#include "utils.h" + +struct BusMatchRule +{ +  int refcount;       /**< reference count */ + +  DBusConnection *matches_go_to; /**< Owner of the rule */ + +  unsigned int flags; /**< BusMatchFlags */ + +  int   message_type; +  char *interface; +  char *member; +  char *sender; +  char *destination; +  char *path; +}; + +BusMatchRule* +bus_match_rule_new (DBusConnection *matches_go_to) +{ +  BusMatchRule *rule; + +  rule = dbus_new0 (BusMatchRule, 1); +  if (rule == NULL) +    return NULL; + +  rule->refcount = 1; +  rule->matches_go_to = matches_go_to; + +  return rule; +} + +void +bus_match_rule_ref (BusMatchRule *rule) +{ +  _dbus_assert (rule->refcount > 0); + +  rule->refcount += 1; +} + +void +bus_match_rule_unref (BusMatchRule *rule) +{ +  _dbus_assert (rule->refcount > 0); + +  rule->refcount -= 1; +  if (rule->refcount == 0) +    { +      dbus_free (rule->interface); +      dbus_free (rule->member); +      dbus_free (rule->sender); +      dbus_free (rule->destination); +      dbus_free (rule->path); +      dbus_free (rule); +    } +} + +#ifdef DBUS_ENABLE_VERBOSE_MODE +static char* +match_rule_to_string (BusMatchRule *rule) +{ +  DBusString str; +  char *ret; +   +  if (!_dbus_string_init (&str)) +    { +      char *s; +      while ((s = _dbus_strdup ("nomem")) == NULL) +        ; /* only OK for debug spew... */ +      return s; +    } +   +  if (rule->flags & BUS_MATCH_MESSAGE_TYPE) +    { +      /* FIXME make type readable */ +      if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type)) +        goto nomem; +    } + +  if (rule->flags & BUS_MATCH_INTERFACE) +    { +      if (_dbus_string_get_length (&str) > 0) +        { +          if (!_dbus_string_append (&str, ",")) +            goto nomem; +        } +       +      if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface)) +        goto nomem; +    } + +  if (rule->flags & BUS_MATCH_MEMBER) +    { +      if (_dbus_string_get_length (&str) > 0) +        { +          if (!_dbus_string_append (&str, ",")) +            goto nomem; +        } +       +      if (!_dbus_string_append_printf (&str, "member='%s'", rule->member)) +        goto nomem; +    } + +  if (rule->flags & BUS_MATCH_PATH) +    { +      if (_dbus_string_get_length (&str) > 0) +        { +          if (!_dbus_string_append (&str, ",")) +            goto nomem; +        } +       +      if (!_dbus_string_append_printf (&str, "path='%s'", rule->path)) +        goto nomem; +    } + +  if (rule->flags & BUS_MATCH_SENDER) +    { +      if (_dbus_string_get_length (&str) > 0) +        { +          if (!_dbus_string_append (&str, ",")) +            goto nomem; +        } +       +      if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender)) +        goto nomem; +    } + +  if (rule->flags & BUS_MATCH_DESTINATION) +    { +      if (_dbus_string_get_length (&str) > 0) +        { +          if (!_dbus_string_append (&str, ",")) +            goto nomem; +        } +       +      if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination)) +        goto nomem; +    } +   +  if (!_dbus_string_steal_data (&str, &ret)) +    goto nomem; + +  _dbus_string_free (&str); +  return ret; +   + nomem: +  _dbus_string_free (&str); +  { +    char *s; +    while ((s = _dbus_strdup ("nomem")) == NULL) +      ;  /* only OK for debug spew... */ +    return s; +  } +} +#endif /* DBUS_ENABLE_VERBOSE_MODE */ + +dbus_bool_t +bus_match_rule_set_message_type (BusMatchRule *rule, +                                 int           type) +{ +  rule->flags |= BUS_MATCH_MESSAGE_TYPE; + +  rule->message_type = type; + +  return TRUE; +} + +dbus_bool_t +bus_match_rule_set_interface (BusMatchRule *rule, +                              const char   *interface) +{ +  char *new; + +  _dbus_assert (interface != NULL); + +  new = _dbus_strdup (interface); +  if (new == NULL) +    return FALSE; + +  rule->flags |= BUS_MATCH_INTERFACE; +  dbus_free (rule->interface); +  rule->interface = new; + +  return TRUE; +} + +dbus_bool_t +bus_match_rule_set_member (BusMatchRule *rule, +                           const char   *member) +{ +  char *new; + +  _dbus_assert (member != NULL); + +  new = _dbus_strdup (member); +  if (new == NULL) +    return FALSE; + +  rule->flags |= BUS_MATCH_MEMBER; +  dbus_free (rule->member); +  rule->member = new; + +  return TRUE; +} + +dbus_bool_t +bus_match_rule_set_sender (BusMatchRule *rule, +                           const char   *sender) +{ +  char *new; + +  _dbus_assert (sender != NULL); + +  new = _dbus_strdup (sender); +  if (new == NULL) +    return FALSE; + +  rule->flags |= BUS_MATCH_SENDER; +  dbus_free (rule->sender); +  rule->sender = new; + +  return TRUE; +} + +dbus_bool_t +bus_match_rule_set_destination (BusMatchRule *rule, +                                const char   *destination) +{ +  char *new; + +  _dbus_assert (destination != NULL); + +  new = _dbus_strdup (destination); +  if (new == NULL) +    return FALSE; + +  rule->flags |= BUS_MATCH_DESTINATION; +  dbus_free (rule->destination); +  rule->destination = new; + +  return TRUE; +} + +dbus_bool_t +bus_match_rule_set_path (BusMatchRule *rule, +                         const char   *path) +{ +  char *new; + +  _dbus_assert (path != NULL); + +  new = _dbus_strdup (path); +  if (new == NULL) +    return FALSE; + +  rule->flags |= BUS_MATCH_PATH; +  dbus_free (rule->path); +  rule->path = new; + +  return TRUE; +} + +/* + * The format is comma-separated with strings quoted with single quotes + * as for the shell (to escape a literal single quote, use '\''). + * + * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo', + * path='/bar/foo',destination=':452345-34' + * + */ +BusMatchRule* +bus_match_rule_parse (DBusConnection   *matches_go_to, +                      const DBusString *rule_text, +                      DBusError        *error) +{ +  BusMatchRule *rule; + +  rule = bus_match_rule_new (matches_go_to); +  if (rule == NULL) +    goto oom; + +  /* FIXME implement for real */ +   +  if (!bus_match_rule_set_message_type (rule, +                                        DBUS_MESSAGE_TYPE_SIGNAL)) +    goto oom; +   +  return rule; +   + oom: +  if (rule) +    bus_match_rule_unref (rule); +  BUS_SET_OOM (error); +  return NULL; +} + +struct BusMatchmaker +{ +  int refcount; + +  DBusList *all_rules; +}; + +BusMatchmaker* +bus_matchmaker_new (void) +{ +  BusMatchmaker *matchmaker; + +  matchmaker = dbus_new0 (BusMatchmaker, 1); +  if (matchmaker == NULL) +    return NULL; + +  matchmaker->refcount = 1; +   +  return matchmaker; +} + +void +bus_matchmaker_ref (BusMatchmaker *matchmaker) +{ +  _dbus_assert (matchmaker->refcount > 0); + +  matchmaker->refcount += 1; +} + +void +bus_matchmaker_unref (BusMatchmaker *matchmaker) +{ +  _dbus_assert (matchmaker->refcount > 0); + +  matchmaker->refcount -= 1; +  if (matchmaker->refcount == 0) +    { +      while (matchmaker->all_rules != NULL) +        { +          BusMatchRule *rule; + +          rule = matchmaker->all_rules->data; +          bus_match_rule_unref (rule); +          _dbus_list_remove_link (&matchmaker->all_rules, +                                  matchmaker->all_rules); +        } + +      dbus_free (matchmaker); +    } +} + +/* The rule can't be modified after it's added. */ +dbus_bool_t +bus_matchmaker_add_rule (BusMatchmaker   *matchmaker, +                         BusMatchRule    *rule) +{ +  _dbus_assert (bus_connection_is_active (rule->matches_go_to)); + +  if (!_dbus_list_append (&matchmaker->all_rules, rule)) +    return FALSE; + +  if (!bus_connection_add_match_rule (rule->matches_go_to, rule)) +    { +      _dbus_list_remove_last (&matchmaker->all_rules, rule); +      return FALSE; +    } +   +  bus_match_rule_ref (rule); + +#ifdef DBUS_ENABLE_VERBOSE_MODE +  { +    char *s = match_rule_to_string (rule); + +    _dbus_verbose ("Added match rule %s to connection %p\n", +                   s, rule->matches_go_to); +    dbus_free (s); +  } +#endif +   +  return TRUE; +} + +static dbus_bool_t +match_rule_equal (BusMatchRule *a, +                  BusMatchRule *b) +{ +  if (a->flags != b->flags) +    return FALSE; + +  if ((a->flags & BUS_MATCH_MESSAGE_TYPE) && +      a->message_type != b->message_type) +    return FALSE; + +  if ((a->flags & BUS_MATCH_MEMBER) && +      strcmp (a->member, b->member) != 0) +    return FALSE; + +  if ((a->flags & BUS_MATCH_PATH) && +      strcmp (a->path, b->path) != 0) +    return FALSE; +   +  if ((a->flags & BUS_MATCH_INTERFACE) && +      strcmp (a->interface, b->interface) != 0) +    return FALSE; + +  if ((a->flags & BUS_MATCH_SENDER) && +      strcmp (a->sender, b->sender) != 0) +    return FALSE; + +  if ((a->flags & BUS_MATCH_DESTINATION) && +      strcmp (a->destination, b->destination) != 0) +    return FALSE; + +  return TRUE; +} + +static void +bus_matchmaker_remove_rule_link (BusMatchmaker   *matchmaker, +                                 DBusList        *link) +{ +  BusMatchRule *rule = link->data; +   +  bus_connection_remove_match_rule (rule->matches_go_to, rule); +  _dbus_list_remove_link (&matchmaker->all_rules, link); + +#ifdef DBUS_ENABLE_VERBOSE_MODE +  { +    char *s = match_rule_to_string (rule); + +    _dbus_verbose ("Removed match rule %s for connection %p\n", +                   s, rule->matches_go_to); +    dbus_free (s); +  } +#endif +   +  bus_match_rule_unref (rule);   +} + +void +bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker, +                            BusMatchRule    *rule) +{ +  bus_connection_remove_match_rule (rule->matches_go_to, rule); +  _dbus_list_remove (&matchmaker->all_rules, rule); + +#ifdef DBUS_ENABLE_VERBOSE_MODE +  { +    char *s = match_rule_to_string (rule); + +    _dbus_verbose ("Removed match rule %s for connection %p\n", +                   s, rule->matches_go_to); +    dbus_free (s); +  } +#endif +   +  bus_match_rule_unref (rule); +} + +/* Remove a single rule which is equal to the given rule by value */ +dbus_bool_t +bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker, +                                     BusMatchRule    *value, +                                     DBusError       *error) +{ +  /* FIXME this is an unoptimized linear scan */ + +  DBusList *link; + +  /* we traverse backward because bus_connection_remove_match_rule() +   * removes the most-recently-added rule +   */ +  link = _dbus_list_get_last_link (&matchmaker->all_rules); +  while (link != NULL) +    { +      BusMatchRule *rule; +      DBusList *prev; + +      rule = link->data; +      prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link); + +      if (match_rule_equal (rule, value)) +        { +          bus_matchmaker_remove_rule_link (matchmaker, link); +          break; +        } + +      link = prev; +    } + +  if (link == NULL) +    { +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND, +                      "The given match rule wasn't found and can't be removed"); +      return FALSE; +    } + +  return TRUE; +} + +void +bus_matchmaker_disconnected (BusMatchmaker   *matchmaker, +                             DBusConnection  *disconnected) +{ +  DBusList *link; + +  /* FIXME +   * +   * This scans all match rules on the bus. We could avoid that +   * for the rules belonging to the connection, since we keep +   * a list of those; but for the rules that just refer to +   * the connection we'd need to do something more elaborate. +   *  +   */ +   +  _dbus_assert (bus_connection_is_active (disconnected)); + +  link = _dbus_list_get_first_link (&matchmaker->all_rules); +  while (link != NULL) +    { +      BusMatchRule *rule; +      DBusList *next; + +      rule = link->data; +      next = _dbus_list_get_next_link (&matchmaker->all_rules, link); + +      if (rule->matches_go_to == disconnected) +        { +          bus_matchmaker_remove_rule_link (matchmaker, link); +        } +      else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') || +               ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':')) +        { +          /* The rule matches to/from a base service, see if it's the +           * one being disconnected, since we know this service name +           * will never be recycled. +           */ +          const char *name; + +          name = bus_connection_get_name (disconnected); +          _dbus_assert (name != NULL); /* because we're an active connection */ + +          if (((rule->flags & BUS_MATCH_SENDER) && +               strcmp (rule->sender, name) == 0) || +              ((rule->flags & BUS_MATCH_DESTINATION) && +               strcmp (rule->destination, name) == 0)) +            { +              bus_matchmaker_remove_rule_link (matchmaker, link); +            } +        } + +      link = next; +    } +} + +static dbus_bool_t +connection_is_primary_owner (DBusConnection *connection, +                             const char     *service_name) +{ +  BusService *service; +  DBusString str; +  BusRegistry *registry; + +  registry = bus_connection_get_registry (connection); + +  _dbus_string_init_const (&str, service_name); +  service = bus_registry_lookup (registry, &str); + +  if (service == NULL) +    return FALSE; /* Service doesn't exist so connection can't own it. */ + +  return bus_service_get_primary_owner (service) == connection; +} + +static dbus_bool_t +match_rule_matches (BusMatchRule    *rule, +                    BusConnections  *connections, +                    DBusConnection  *sender, +                    DBusConnection  *addressed_recipient, +                    DBusMessage     *message) +{ +  /* All features of the match rule are AND'd together, +   * so FALSE if any of them don't match. +   */ + +  if (rule->flags & BUS_MATCH_MESSAGE_TYPE) +    { +      _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID); + +      if (rule->message_type != dbus_message_get_type (message)) +        return FALSE; +    } + +  if (rule->flags & BUS_MATCH_INTERFACE) +    { +      const char *iface; + +      _dbus_assert (rule->interface != NULL); + +      iface = dbus_message_get_interface (message); +      if (iface == NULL) +        return FALSE; + +      if (strcmp (iface, rule->interface) != 0) +        return FALSE; +    } + +  if (rule->flags & BUS_MATCH_MEMBER) +    { +      const char *member; + +      _dbus_assert (rule->member != NULL); + +      member = dbus_message_get_member (message); +      if (member == NULL) +        return FALSE; + +      if (strcmp (member, rule->member) != 0) +        return FALSE; +    } + +  if (rule->flags & BUS_MATCH_SENDER) +    { +      _dbus_assert (rule->sender != NULL); + +      if (!connection_is_primary_owner (sender, rule->sender)) +        return FALSE; +    } + +  if (rule->flags & BUS_MATCH_DESTINATION) +    { +      const char *destination; + +      _dbus_assert (rule->destination != NULL); + +      if (addressed_recipient == NULL) +        return FALSE; + +      destination = dbus_message_get_destination (message); +      if (destination == NULL) +        return FALSE; + +      if (!connection_is_primary_owner (addressed_recipient, rule->destination)) +        return FALSE; +    } + +  if (rule->flags & BUS_MATCH_PATH) +    { +      const char *path; + +      _dbus_assert (rule->path != NULL); + +      path = dbus_message_get_path (message); +      if (path == NULL) +        return FALSE; + +      if (strcmp (path, rule->path) != 0) +        return FALSE; +    } + +  return TRUE; +} + +dbus_bool_t +bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker, +                               BusConnections  *connections, +                               DBusConnection  *sender, +                               DBusConnection  *addressed_recipient, +                               DBusMessage     *message, +                               DBusList       **recipients_p) +{ +  /* FIXME for now this is a wholly unoptimized linear search */ + +  DBusList *link; + +  _dbus_assert (*recipients_p == NULL); + +  /* This avoids sending same message to the same connection twice. +   * Purpose of the stamp instead of a bool is to avoid iterating over +   * all connections resetting the bool each time. +   */ +  bus_connections_increment_stamp (connections); + +  /* addressed_recipient is already receiving the message, don't add to list. +   * NULL addressed_recipient means either bus driver, or this is a signal +   * and thus lacks a specific addressed_recipient. +   */ +  if (addressed_recipient != NULL) +    bus_connection_mark_stamp (addressed_recipient); + +  link = _dbus_list_get_first_link (&matchmaker->all_rules); +  while (link != NULL) +    { +      BusMatchRule *rule; + +      rule = link->data; + +#ifdef DBUS_ENABLE_VERBOSE_MODE +      { +        char *s = match_rule_to_string (rule); +         +        _dbus_verbose ("Checking whether message matches rule %s for connection %p\n", +                       s, rule->matches_go_to); +        dbus_free (s); +      } +#endif +       +      if (match_rule_matches (rule, connections, +                              sender, addressed_recipient, message)) +        { +          _dbus_verbose ("Rule matched\n"); +           +          /* Append to the list if we haven't already */ +          if (bus_connection_mark_stamp (rule->matches_go_to)) +            { +              if (!_dbus_list_append (recipients_p, rule->matches_go_to)) +                goto nomem; +            } +#ifdef DBUS_ENABLE_VERBOSE_MODE +          else +            { +              _dbus_verbose ("Connection already receiving this message, so not adding again\n"); +            } +#endif /* DBUS_ENABLE_VERBOSE_MODE */ +        } + +      link = _dbus_list_get_next_link (&matchmaker->all_rules, link); +    } + +  return TRUE; + + nomem: +  _dbus_list_clear (recipients_p); +  return FALSE; +} + +#ifdef DBUS_BUILD_TESTS +#include "test.h" + +dbus_bool_t +bus_signals_test (const DBusString *test_data_dir) +{ +  BusMatchmaker *matchmaker; + +  matchmaker = bus_matchmaker_new (); +  bus_matchmaker_ref (matchmaker); +  bus_matchmaker_unref (matchmaker); +  bus_matchmaker_unref (matchmaker); +   +  return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ + diff --git a/bus/signals.h b/bus/signals.h new file mode 100644 index 00000000..fab018ae --- /dev/null +++ b/bus/signals.h @@ -0,0 +1,83 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* signals.h  Bus signal connection implementation + * + * Copyright (C) 2003  Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#ifndef BUS_SIGNALS_H +#define BUS_SIGNALS_H + +#include <dbus/dbus.h> +#include <dbus/dbus-string.h> +#include <dbus/dbus-sysdeps.h> +#include "connection.h" + +typedef enum +{ +  BUS_MATCH_MESSAGE_TYPE = 1 << 0, +  BUS_MATCH_INTERFACE    = 1 << 1, +  BUS_MATCH_MEMBER       = 1 << 2, +  BUS_MATCH_SENDER       = 1 << 3, +  BUS_MATCH_DESTINATION  = 1 << 4, +  BUS_MATCH_PATH         = 1 << 5 +} BusMatchFlags; + +BusMatchRule* bus_match_rule_new   (DBusConnection *matches_go_to); +void          bus_match_rule_ref   (BusMatchRule   *rule); +void          bus_match_rule_unref (BusMatchRule   *rule); + +dbus_bool_t bus_match_rule_set_message_type (BusMatchRule *rule, +                                             int           type); +dbus_bool_t bus_match_rule_set_interface    (BusMatchRule *rule, +                                             const char   *interface); +dbus_bool_t bus_match_rule_set_member       (BusMatchRule *rule, +                                             const char   *member); +dbus_bool_t bus_match_rule_set_sender       (BusMatchRule *rule, +                                             const char   *sender); +dbus_bool_t bus_match_rule_set_destination  (BusMatchRule *rule, +                                             const char   *destination); +dbus_bool_t bus_match_rule_set_path         (BusMatchRule *rule, +                                             const char   *path); + +BusMatchRule* bus_match_rule_parse (DBusConnection   *matches_go_to, +                                    const DBusString *rule_text, +                                    DBusError        *error); + +BusMatchmaker* bus_matchmaker_new   (void); +void           bus_matchmaker_ref   (BusMatchmaker *matchmaker); +void           bus_matchmaker_unref (BusMatchmaker *matchmaker); + +dbus_bool_t bus_matchmaker_add_rule             (BusMatchmaker   *matchmaker, +                                                 BusMatchRule    *rule); +dbus_bool_t bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker, +                                                 BusMatchRule    *value, +                                                 DBusError       *error); +void        bus_matchmaker_remove_rule          (BusMatchmaker   *matchmaker, +                                                 BusMatchRule    *rule); +void        bus_matchmaker_disconnected         (BusMatchmaker   *matchmaker, +                                                 DBusConnection  *disconnected); +dbus_bool_t bus_matchmaker_get_recipients       (BusMatchmaker   *matchmaker, +                                                 BusConnections  *connections, +                                                 DBusConnection  *sender, +                                                 DBusConnection  *addressed_recipient, +                                                 DBusMessage     *message, +                                                 DBusList       **recipients_p); + +#endif /* BUS_SIGNALS_H */ diff --git a/bus/system.conf.in b/bus/system.conf.in index 96513a75..122ea1b6 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -42,8 +42,8 @@      <!-- Allow anyone to talk to the message bus -->      <!-- FIXME I think currently these allow rules are always implicit            even if they aren't in here --> -    <allow send_service="org.freedesktop.DBus"/> -    <allow receive_service="org.freedesktop.DBus"/> +    <allow send_destination="org.freedesktop.DBus"/> +    <allow receive_sender="org.freedesktop.DBus"/>    </policy>    <!-- Config files are placed here that among other things, punch  diff --git a/bus/test-main.c b/bus/test-main.c index c433075f..3f280d4e 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -89,6 +89,12 @@ main (int argc, char **argv)    check_memleaks (argv[0]); +  printf ("%s: Running signals test\n", argv[0]); +  if (!bus_signals_test (&test_data_dir)) +    die ("signals"); + +  check_memleaks (argv[0]); +      printf ("%s: Running SHA1 connection test\n", argv[0]);    if (!bus_dispatch_sha1_test (&test_data_dir))      die ("sha1"); @@ -36,6 +36,7 @@ dbus_bool_t bus_dispatch_test         (const DBusString             *test_data_d  dbus_bool_t bus_dispatch_sha1_test    (const DBusString             *test_data_dir);  dbus_bool_t bus_policy_test           (const DBusString             *test_data_dir);  dbus_bool_t bus_config_parser_test    (const DBusString             *test_data_dir); +dbus_bool_t bus_signals_test          (const DBusString             *test_data_dir);  dbus_bool_t bus_setup_debug_client    (DBusConnection               *connection);  void        bus_test_clients_foreach  (BusConnectionForeachFunction  function,                                         void                         *data); diff --git a/dbus/dbus-errors.h b/dbus/dbus-errors.h index ad4801c9..f229188a 100644 --- a/dbus/dbus-errors.h +++ b/dbus/dbus-errors.h @@ -53,13 +53,8 @@ struct DBusError  };  #define DBUS_ERROR_FAILED                     "org.freedesktop.DBus.Error.Failed" -#define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Activate.ServiceNotFound" -#define DBUS_ERROR_SPAWN_EXEC_FAILED          "org.freedesktop.DBus.Error.Spawn.ExecFailed" -#define DBUS_ERROR_SPAWN_FORK_FAILED          "org.freedesktop.DBus.Error.Spawn.ForkFailed" -#define DBUS_ERROR_SPAWN_CHILD_EXITED         "org.freedesktop.DBus.Error.Spawn.ChildExited" -#define DBUS_ERROR_SPAWN_CHILD_SIGNALED       "org.freedesktop.DBus.Error.Spawn.ChildSignaled" -#define DBUS_ERROR_SPAWN_FAILED               "org.freedesktop.DBus.Error.Spawn.Failed"  #define DBUS_ERROR_NO_MEMORY                  "org.freedesktop.DBus.Error.NoMemory" +#define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Error.ServiceNotFound"  #define DBUS_ERROR_SERVICE_DOES_NOT_EXIST     "org.freedesktop.DBus.Error.ServiceDoesNotExist"  #define DBUS_ERROR_NO_REPLY                   "org.freedesktop.DBus.Error.NoReply"  #define DBUS_ERROR_IO_ERROR                   "org.freedesktop.DBus.Error.IOError" @@ -77,6 +72,12 @@ struct DBusError  #define DBUS_ERROR_FILE_NOT_FOUND             "org.freedesktop.DBus.Error.FileNotFound"  #define DBUS_ERROR_UNKNOWN_METHOD             "org.freedesktop.DBus.Error.UnknownMethod"  #define DBUS_ERROR_TIMED_OUT                  "org.freedesktop.DBus.Error.TimedOut" +#define DBUS_ERROR_MATCH_RULE_NOT_FOUND       "org.freedesktop.DBus.Error.MatchRuleNotFound" +#define DBUS_ERROR_SPAWN_EXEC_FAILED          "org.freedesktop.DBus.Error.Spawn.ExecFailed" +#define DBUS_ERROR_SPAWN_FORK_FAILED          "org.freedesktop.DBus.Error.Spawn.ForkFailed" +#define DBUS_ERROR_SPAWN_CHILD_EXITED         "org.freedesktop.DBus.Error.Spawn.ChildExited" +#define DBUS_ERROR_SPAWN_CHILD_SIGNALED       "org.freedesktop.DBus.Error.Spawn.ChildSignaled" +#define DBUS_ERROR_SPAWN_FAILED               "org.freedesktop.DBus.Error.Spawn.Failed"  void        dbus_error_init      (DBusError       *error);  void        dbus_error_free      (DBusError       *error); diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c index f3b68272..6da5318c 100644 --- a/dbus/dbus-mainloop.c +++ b/dbus/dbus-mainloop.c @@ -28,7 +28,7 @@  #include <dbus/dbus-list.h>  #include <dbus/dbus-sysdeps.h> -#define MAINLOOP_SPEW 1 +#define MAINLOOP_SPEW 0  struct DBusLoop  { diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index 329609dc..a0cf54ef 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -84,7 +84,6 @@ extern "C" {  /* Services */  #define DBUS_SERVICE_ORG_FREEDESKTOP_DBUS      "org.freedesktop.DBus" -#define DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST "org.freedesktop.Broadcast"  /* Paths */  #define DBUS_PATH_ORG_FREEDESKTOP_DBUS  "/org/freedesktop/DBus" diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 59ec6ea1..4625cf25 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -813,7 +813,9 @@ _dbus_transport_queue_messages (DBusTransport *transport)  {    DBusDispatchStatus status; +#if 0    _dbus_verbose ("_dbus_transport_queue_messages()\n"); +#endif    /* Queue any messages */    while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS) @@ -80,12 +80,15 @@   - the invalid messages in the test suite are all useless because      they are invalid for the wrong reasons due to protocol changes - - Nuke the org.freedesktop.Broadcast service; instead,  -   just broadcast messages of type signal -   - I don't want to introduce DBusObject, but refcounting and object     data could still be factored out into an internal "base class"      perhaps. + - modify the auth protocol to also support other initial-handshake +   type of information + + - document the auth protocol as a set of states and transitions, and +   then reimplement it in those terms +   - Header fields names are required to be aligned on a 4 byte boundary     at the moment. No alignment should be neccessary. diff --git a/glib/dbus-gproxy.c b/glib/dbus-gproxy.c index 59d86a31..b77e7d30 100644 --- a/glib/dbus-gproxy.c +++ b/glib/dbus-gproxy.c @@ -188,8 +188,8 @@ dbus_gproxy_begin_call (DBusGProxy *proxy,    LOCK_PROXY (proxy);    message = dbus_message_new_method_call (proxy->service, -                                          proxy->interface,                                            proxy->path, +                                          proxy->interface,                                            method);    if (message == NULL)      goto oom; diff --git a/test/data/valid-config-files/many-rules.conf b/test/data/valid-config-files/many-rules.conf index 57ea5ec9..f68430e5 100644 --- a/test/data/valid-config-files/many-rules.conf +++ b/test/data/valid-config-files/many-rules.conf @@ -13,16 +13,16 @@      <deny receive_interface="org.freedesktop.System" receive_member="Reboot"/>      <deny send_path="/foo/bar/SystemObjectThing" send_member="Reboot"/>       <deny own="org.freedesktop.System"/> -    <deny send_service="org.freedesktop.System"/> -    <deny receive_service="org.freedesktop.System"/> +    <deny send_destination="org.freedesktop.System"/> +    <deny receive_sender="org.freedesktop.System"/>      <deny user="root"/>      <deny group="root"/>      <allow send_type="error"/>      <allow send_type="method_call"/>      <allow send_type="method_return"/>      <allow send_type="signal"/> -    <deny send_service="org.freedesktop.Bar" send_interface="org.freedesktop.Foo"/> -    <deny send_service="org.freedesktop.Bar" send_interface="org.freedesktop.Foo" send_type="method_call"/> +    <deny send_destination="org.freedesktop.Bar" send_interface="org.freedesktop.Foo"/> +    <deny send_destination="org.freedesktop.Bar" send_interface="org.freedesktop.Foo" send_type="method_call"/>    </policy>    <policy context="mandatory"> @@ -31,16 +31,16 @@      <deny receive_interface="org.freedesktop.System" receive_member="Reboot"/>      <deny send_path="/foo/bar/SystemObjectThing" send_member="Reboot"/>       <deny own="org.freedesktop.System"/> -    <deny send_service="org.freedesktop.System"/> -    <deny receive_service="org.freedesktop.System"/> +    <deny send_destination="org.freedesktop.System"/> +    <deny receive_sender="org.freedesktop.System"/>      <deny user="root"/>      <deny group="root"/>      <allow send_type="error"/>      <allow send_type="method_call"/>      <allow send_type="method_return"/>      <allow send_type="signal"/> -    <deny send_service="org.freedesktop.Bar" send_interface="org.freedesktop.Foo"/> -    <deny send_service="org.freedesktop.Bar" send_interface="org.freedesktop.Foo" send_type="method_call"/> +    <deny send_destination="org.freedesktop.Bar" send_interface="org.freedesktop.Foo"/> +    <deny send_destination="org.freedesktop.Bar" send_interface="org.freedesktop.Foo" send_type="method_call"/>    </policy>    <limit name="max_incoming_bytes">5000</limit>    diff --git a/test/data/valid-config-files/system.d/test.conf b/test/data/valid-config-files/system.d/test.conf index 10a79847..5b60a1fc 100644 --- a/test/data/valid-config-files/system.d/test.conf +++ b/test/data/valid-config-files/system.d/test.conf @@ -15,6 +15,6 @@    <!-- Allow any connection to receive the message, but          only if the message is sent by the owner of FooService -->    <policy context="default"> -    <allow receive_interface="org.foo.FooBroadcastInterface" receive_service="org.foo.FooService"/> +    <allow receive_interface="org.foo.FooBroadcastInterface" receive_sender="org.foo.FooService"/>    </policy>  </busconfig> diff --git a/tools/dbus-send.c b/tools/dbus-send.c index 67abe066..06a87adb 100644 --- a/tools/dbus-send.c +++ b/tools/dbus-send.c @@ -44,7 +44,7 @@ main (int argc, char *argv[])    DBusMessageIter iter;    int i;    DBusBusType type = DBUS_BUS_SESSION; -  const char *dest = DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST; +  const char *dest = NULL;    const char *name = NULL;    const char *path = NULL;    int message_type = DBUS_MESSAGE_TYPE_SIGNAL; | 
