diff options
Diffstat (limited to 'bus/policy.c')
-rw-r--r-- | bus/policy.c | 929 |
1 files changed, 485 insertions, 444 deletions
diff --git a/bus/policy.c b/bus/policy.c index 6f27d1ce..1f3cee8d 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -866,281 +866,296 @@ bus_policy_rule_to_string (BusPolicyRule *rule, return TRUE; } -BusResult -bus_client_policy_check_can_send (DBusConnection *sender, - BusClientPolicy *policy, - BusRegistry *registry, - dbus_bool_t requested_reply, - DBusConnection *addressed_recipient, - DBusConnection *receiver, - DBusMessage *message, - dbus_int32_t *toggles, - dbus_bool_t *log, - const char **privilege_param, - BusDeferredMessage **deferred_message, - char **out_rule) +static dbus_bool_t +check_send_rule (const BusPolicyRule *rule, + BusRegistry *registry, + dbus_bool_t requested_reply, + DBusConnection *receiver, + DBusMessage *message, + BusResult *result, + const char **privilege) { - DBusList *link; - BusResult result; - const char *privilege; - BusPolicyRule *matched_rule = NULL; - - /* policy->rules is in the order the rules appeared - * in the config file, i.e. last rule that applies wins + /* Rule is skipped if it specifies a different + * message name from the message, or a different + * destination from the message */ + if (rule->type != BUS_POLICY_RULE_SEND) + { + _dbus_verbose (" (policy) skipping non-send rule\n"); + return FALSE; + } - _dbus_verbose (" (policy) checking send rules\n"); - *toggles = 0; - - result = BUS_RESULT_FALSE; - link = _dbus_list_get_first_link (&policy->rules); - while (link != NULL) + if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID) { - BusPolicyRule *rule = link->data; + if (dbus_message_get_type (message) != rule->d.send.message_type) + { + _dbus_verbose (" (policy) skipping rule for different message type\n"); + return FALSE; + } + } - link = _dbus_list_get_next_link (&policy->rules, link); - - /* Rule is skipped if it specifies a different - * message name from the message, or a different - * destination from the message + /* If it's a reply, the requested_reply flag kicks in */ + if (dbus_message_get_reply_serial (message) != 0) + { + /* for allow or check requested_reply=true means the rule applies + * only when reply was requested. requested_reply=false means the + * rule always applies */ - - if (rule->type != BUS_POLICY_RULE_SEND) + if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop) { - _dbus_verbose (" (policy) skipping non-send rule\n"); - continue; + _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n", + rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check"); + return FALSE; } - if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID) + /* for deny, requested_reply=false means the rule applies only + * when the reply was not requested. requested_reply=true means the + * rule always applies. + */ + if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.send.requested_reply) { - if (dbus_message_get_type (message) != rule->d.send.message_type) - { - _dbus_verbose (" (policy) skipping rule for different message type\n"); - continue; - } + _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n"); + return FALSE; } + } - /* If it's a reply, the requested_reply flag kicks in */ - if (dbus_message_get_reply_serial (message) != 0) + if (rule->d.send.path != NULL) + { + if (dbus_message_get_path (message) != NULL && + strcmp (dbus_message_get_path (message), + rule->d.send.path) != 0) { - /* for allow or check requested_reply=true means the rule applies - * only when reply was requested. requested_reply=false means the - * rule always applies - */ - if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop) - { - _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n", - rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check"); - continue; - } - - /* for deny, requested_reply=false means the rule applies only - * when the reply was not requested. requested_reply=true means the - * rule always applies. - */ - if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.send.requested_reply) - { - _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n"); - continue; - } + _dbus_verbose (" (policy) skipping rule for different path\n"); + return FALSE; } + } + + if (rule->d.send.interface != NULL) + { + /* The interface is optional in messages. For allow rules, if the message + * has no interface we want to skip the rule (and thus not allow); + * for deny rules, if the message has no interface we want to use the + * rule (and thus deny). Check rules are meant to be used like allow + * rules (they can grant access, but not remove it), so we treat it like + * allow here. + */ + dbus_bool_t no_interface; + + no_interface = dbus_message_get_interface (message) == NULL; - if (rule->d.send.path != NULL) + if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) || + (!no_interface && + strcmp (dbus_message_get_interface (message), + rule->d.send.interface) != 0)) { - if (dbus_message_get_path (message) != NULL && - strcmp (dbus_message_get_path (message), - rule->d.send.path) != 0) - { - _dbus_verbose (" (policy) skipping rule for different path\n"); - continue; - } + _dbus_verbose (" (policy) skipping rule for different interface\n"); + return FALSE; } - - if (rule->d.send.interface != NULL) - { - /* The interface is optional in messages. For allow rules, if the message - * has no interface we want to skip the rule (and thus not allow); - * for deny rules, if the message has no interface we want to use the - * rule (and thus deny). Check rules are meant to be used like allow - * rules (they can grant access, but not remove it), so we treat it like - * allow here. - */ - dbus_bool_t no_interface; + } - no_interface = dbus_message_get_interface (message) == NULL; - - if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) || - (!no_interface && - strcmp (dbus_message_get_interface (message), - rule->d.send.interface) != 0)) - { - _dbus_verbose (" (policy) skipping rule for different interface\n"); - continue; - } + if (rule->d.send.member != NULL) + { + if (dbus_message_get_member (message) != NULL && + strcmp (dbus_message_get_member (message), + rule->d.send.member) != 0) + { + _dbus_verbose (" (policy) skipping rule for different member\n"); + return FALSE; } + } - if (rule->d.send.member != NULL) + if (rule->d.send.error != NULL) + { + if (dbus_message_get_error_name (message) != NULL && + strcmp (dbus_message_get_error_name (message), + rule->d.send.error) != 0) { - if (dbus_message_get_member (message) != NULL && - strcmp (dbus_message_get_member (message), - rule->d.send.member) != 0) - { - _dbus_verbose (" (policy) skipping rule for different member\n"); - continue; - } + _dbus_verbose (" (policy) skipping rule for different error name\n"); + return FALSE; } + } - if (rule->d.send.error != NULL) + if (rule->d.send.broadcast != BUS_POLICY_TRISTATE_ANY) + { + if (dbus_message_get_destination (message) == NULL && + dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) { - if (dbus_message_get_error_name (message) != NULL && - strcmp (dbus_message_get_error_name (message), - rule->d.send.error) != 0) + /* it's a broadcast */ + if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_FALSE) { - _dbus_verbose (" (policy) skipping rule for different error name\n"); - continue; + _dbus_verbose (" (policy) skipping rule because message is a broadcast\n"); + return FALSE; } } + /* else it isn't a broadcast: there is some destination */ + else if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_TRUE) + { + _dbus_verbose (" (policy) skipping rule because message is not a broadcast\n"); + return FALSE; + } + } - if (rule->d.send.broadcast != BUS_POLICY_TRISTATE_ANY) + if (rule->d.send.destination != NULL) + { + if (!rule->d.send.destination_prefix) { - if (dbus_message_get_destination (message) == NULL && - dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) + /* receiver can be NULL for messages that are sent to the + * message bus itself, we check the strings in that case as + * built-in services don't have a DBusConnection but messages + * to them have a destination service name. + * + * Similarly, receiver can be NULL when we're deciding whether + * activation should be allowed; we make the authorization decision + * on the assumption that the activated service will have the + * requested name and no others. + */ + if (receiver == NULL) { - /* it's a broadcast */ - if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_FALSE) + if (!dbus_message_has_destination (message, + rule->d.send.destination)) { - _dbus_verbose (" (policy) skipping rule because message is a broadcast\n"); - continue; + _dbus_verbose (" (policy) skipping rule because message dest is not %s\n", + rule->d.send.destination); + return FALSE; } } - /* else it isn't a broadcast: there is some destination */ - else if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_TRUE) + else { - _dbus_verbose (" (policy) skipping rule because message is not a broadcast\n"); - continue; - } - } + DBusString str; + BusService *service; - if (rule->d.send.destination != NULL) - { - if (!rule->d.send.destination_prefix) - { - /* receiver can be NULL for messages that are sent to the - * message bus itself, we check the strings in that case as - * built-in services don't have a DBusConnection but messages - * to them have a destination service name. - * - * Similarly, receiver can be NULL when we're deciding whether - * activation should be allowed; we make the authorization decision - * on the assumption that the activated service will have the - * requested name and no others. - */ - if (receiver == NULL) + _dbus_string_init_const (&str, rule->d.send.destination); + + service = bus_registry_lookup (registry, &str); + if (service == NULL) { - if (!dbus_message_has_destination (message, - rule->d.send.destination)) - { - _dbus_verbose (" (policy) skipping rule because message dest is not %s\n", - rule->d.send.destination); - continue; - } + _dbus_verbose (" (policy) skipping rule because dest %s doesn't exist\n", + rule->d.send.destination); + return FALSE; } - else + + if (!bus_service_has_owner (service, receiver)) { - DBusString str; - BusService *service; - - _dbus_string_init_const (&str, rule->d.send.destination); - - service = bus_registry_lookup (registry, &str); - if (service == NULL) - { - _dbus_verbose (" (policy) skipping rule because dest %s doesn't exist\n", - rule->d.send.destination); - continue; - } - - if (!bus_service_has_owner (service, receiver)) - { - _dbus_verbose (" (policy) skipping rule because dest %s isn't owned by receiver\n", - rule->d.send.destination); - continue; - } + _dbus_verbose (" (policy) skipping rule because dest %s isn't owned by receiver\n", + rule->d.send.destination); + return FALSE; } } - else if (rule->d.send.destination_prefix) + } + else if (rule->d.send.destination_prefix) + { + /* receiver can be NULL - the same as in !send.destination_prefix */ + if (receiver == NULL) { - /* receiver can be NULL - the same as in !send.destination_prefix */ - if (receiver == NULL) + const char *destination = dbus_message_get_destination (message); + DBusString dest_name; + + if (destination == NULL) { - const char *destination = dbus_message_get_destination (message); - DBusString dest_name; - - if (destination == NULL) - { - _dbus_verbose (" (policy) skipping rule because message has no dest\n"); - continue; - } - - _dbus_string_init_const (&dest_name, destination); - - if (!_dbus_string_starts_with_words_c_str (&dest_name, - rule->d.send.destination, - '.')) - { - _dbus_verbose (" (policy) skipping rule because message dest doesn't start with %s\n", - rule->d.send.destination); - continue; - } + _dbus_verbose (" (policy) skipping rule because message has no dest\n"); + return FALSE; } - else + + _dbus_string_init_const (&dest_name, destination); + + if (!_dbus_string_starts_with_words_c_str (&dest_name, + rule->d.send.destination, + '.')) { - if (!bus_connection_is_service_owner_by_prefix (receiver, - rule->d.send.destination)) - { - _dbus_verbose (" (policy) skipping rule because no dest with prefix %s is owned by receiver\n", - rule->d.send.destination); - continue; - } + _dbus_verbose (" (policy) skipping rule because message dest doesn't start with %s\n", + rule->d.send.destination); + return FALSE; } } - } - - if (rule->d.send.min_fds > 0 || - rule->d.send.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) - { - unsigned int n_fds = _dbus_message_get_n_unix_fds (message); - - if (n_fds < rule->d.send.min_fds || n_fds > rule->d.send.max_fds) + else { - _dbus_verbose (" (policy) skipping rule because message has %u fds " - "and that is outside range [%u,%u]", - n_fds, rule->d.send.min_fds, rule->d.send.max_fds); - continue; + if (!bus_connection_is_service_owner_by_prefix (receiver, + rule->d.send.destination)) + { + _dbus_verbose (" (policy) skipping rule because no dest with prefix %s is owned by receiver\n", + rule->d.send.destination); + return FALSE; + } } } + } - /* Use this rule */ - switch (rule->access) + if (rule->d.send.min_fds > 0 || + rule->d.send.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) + { + unsigned int n_fds = _dbus_message_get_n_unix_fds (message); + + if (n_fds < rule->d.send.min_fds || n_fds > rule->d.send.max_fds) { - case BUS_POLICY_RULE_ACCESS_ALLOW: - result = BUS_RESULT_TRUE; - break; - case BUS_POLICY_RULE_ACCESS_DENY: - result = BUS_RESULT_FALSE; - break; - case BUS_POLICY_RULE_ACCESS_CHECK: - result = BUS_RESULT_LATER; - privilege = rule->privilege; - break; + _dbus_verbose (" (policy) skipping rule because message has %u fds " + "and that is outside range [%u,%u]", + n_fds, rule->d.send.min_fds, rule->d.send.max_fds); + return FALSE; } + } + + /* Use this rule */ + switch (rule->access) + { + case BUS_POLICY_RULE_ACCESS_ALLOW: + *result = BUS_RESULT_TRUE; + break; + case BUS_POLICY_RULE_ACCESS_DENY: + *result = BUS_RESULT_FALSE; + break; + case BUS_POLICY_RULE_ACCESS_CHECK: + *result = BUS_RESULT_LATER; + *privilege = rule->privilege; + break; + } + + return TRUE; +} + +BusResult +bus_client_policy_check_can_send (DBusConnection *sender, + BusClientPolicy *policy, + BusRegistry *registry, + dbus_bool_t requested_reply, + DBusConnection *addressed_recipient, + DBusConnection *receiver, + DBusMessage *message, + dbus_int32_t *toggles, + dbus_bool_t *log, + const char **privilege_param, + BusDeferredMessage **deferred_message, + char **out_rule) +{ + DBusList *link; + BusResult result; + const char *privilege; + BusPolicyRule *matched_rule = NULL; + + /* policy->rules is in the order the rules appeared + * in the config file, i.e. last rule that applies wins + */ + + _dbus_verbose (" (policy) checking send rules\n"); + *toggles = 0; + + result = BUS_RESULT_FALSE; + link = _dbus_list_get_first_link (&policy->rules); + while (link != NULL) + { + const BusPolicyRule *rule = link->data; + + link = _dbus_list_get_next_link (&policy->rules, link); - *log = rule->d.send.log; - (*toggles)++; - matched_rule = rule; + if (check_send_rule (rule, registry, requested_reply, receiver, message, + &result, &privilege)) + { + *log = rule->d.send.log; + (*toggles)++; + matched_rule = (BusPolicyRule *)rule; - _dbus_verbose (" (policy) used rule, result now = %d\n", - result); + _dbus_verbose (" (policy) used rule, result now = %d\n", + result); + } } if (result == BUS_RESULT_LATER) @@ -1169,238 +1184,255 @@ bus_client_policy_check_can_send (DBusConnection *sender, return result; } -/* See docs on what the args mean on bus_context_check_security_policy() - * comment - */ -BusResult -bus_client_policy_check_can_receive (BusClientPolicy *policy, - BusRegistry *registry, - dbus_bool_t requested_reply, - DBusConnection *sender, - DBusConnection *addressed_recipient, - DBusConnection *proposed_recipient, - DBusMessage *message, - dbus_int32_t *toggles, - const char **privilege_param, - BusDeferredMessage **deferred_message, - char **out_rule) +static dbus_bool_t +check_receive_rule (const BusPolicyRule *rule, + BusRegistry *registry, + dbus_bool_t requested_reply, + DBusConnection *sender, + DBusMessage *message, + dbus_bool_t eavesdropping, + BusResult *result, + const char **privilege) { - DBusList *link; - dbus_bool_t eavesdropping; - BusResult result; - const char *privilege; - BusPolicyRule *matched_rule = NULL; + if (rule->type != BUS_POLICY_RULE_RECEIVE) + { + _dbus_verbose (" (policy) skipping non-receive rule\n"); + return FALSE; + } - eavesdropping = - addressed_recipient != proposed_recipient && - dbus_message_get_destination (message) != NULL; - - /* policy->rules is in the order the rules appeared - * in the config file, i.e. last rule that applies wins + if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID) + { + if (dbus_message_get_type (message) != rule->d.receive.message_type) + { + _dbus_verbose (" (policy) skipping rule for different message type\n"); + return FALSE; + } + } + + + /* for allow or check, eavesdrop=false means the rule doesn't apply when + * eavesdropping. eavesdrop=true means the rule always applies */ + if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop) + { + _dbus_verbose (" (policy) skipping %s rule since it doesn't apply to eavesdropping\n", + rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check"); + return FALSE; + } - _dbus_verbose (" (policy) checking receive rules, eavesdropping = %d\n", eavesdropping); - *toggles = 0; - - result = BUS_RESULT_FALSE; - link = _dbus_list_get_first_link (&policy->rules); - while (link != NULL) + /* for deny, eavesdrop=true means the rule applies only when + * eavesdropping; eavesdrop=false means always deny. + */ + if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop) { - BusPolicyRule *rule = link->data; + _dbus_verbose (" (policy) skipping deny rule since it only applies to eavesdropping\n"); + return FALSE; + } - link = _dbus_list_get_next_link (&policy->rules, link); - - if (rule->type != BUS_POLICY_RULE_RECEIVE) + /* If it's a reply, the requested_reply flag kicks in */ + if (dbus_message_get_reply_serial (message) != 0) + { + /* for allow or check requested_reply=true means the rule applies + * only when reply was requested. requested_reply=false means the + * rule always applies + */ + if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop) { - _dbus_verbose (" (policy) skipping non-receive rule\n"); - continue; + _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n", + rule->access == BUS_POLICY_RULE_ACCESS_DENY ? "allow" : "deny"); + return FALSE; } - if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID) + /* for deny, requested_reply=false means the rule applies only + * when the reply was not requested. requested_reply=true means the + * rule always applies. + */ + if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.requested_reply) { - if (dbus_message_get_type (message) != rule->d.receive.message_type) - { - _dbus_verbose (" (policy) skipping rule for different message type\n"); - continue; - } + _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n"); + return FALSE; } + } - - /* for allow or check, eavesdrop=false means the rule doesn't apply when - * eavesdropping. eavesdrop=true means the rule always applies - */ - if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop) + if (rule->d.receive.path != NULL) + { + if (dbus_message_get_path (message) != NULL && + strcmp (dbus_message_get_path (message), + rule->d.receive.path) != 0) { - _dbus_verbose (" (policy) skipping %s rule since it doesn't apply to eavesdropping\n", - rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check"); - continue; + _dbus_verbose (" (policy) skipping rule for different path\n"); + return FALSE; } + } - /* for deny, eavesdrop=true means the rule applies only when - * eavesdropping; eavesdrop=false means always deny. + if (rule->d.receive.interface != NULL) + { + /* The interface is optional in messages. For allow rules, if the message + * has no interface we want to skip the rule (and thus not allow); + * for deny rules, if the message has no interface we want to use the + * rule (and thus deny). Check rules are treated like allow rules. */ - if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop) + dbus_bool_t no_interface; + + no_interface = dbus_message_get_interface (message) == NULL; + + if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) || + (!no_interface && + strcmp (dbus_message_get_interface (message), + rule->d.receive.interface) != 0)) { - _dbus_verbose (" (policy) skipping deny rule since it only applies to eavesdropping\n"); - continue; + _dbus_verbose (" (policy) skipping rule for different interface\n"); + return FALSE; } + } - /* If it's a reply, the requested_reply flag kicks in */ - if (dbus_message_get_reply_serial (message) != 0) + if (rule->d.receive.member != NULL) + { + if (dbus_message_get_member (message) != NULL && + strcmp (dbus_message_get_member (message), + rule->d.receive.member) != 0) { - /* for allow or check requested_reply=true means the rule applies - * only when reply was requested. requested_reply=false means the - * rule always applies - */ - if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop) - { - _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n", - rule->access == BUS_POLICY_RULE_ACCESS_DENY ? "allow" : "deny"); - continue; - } + _dbus_verbose (" (policy) skipping rule for different member\n"); + return FALSE; + } + } - /* for deny, requested_reply=false means the rule applies only - * when the reply was not requested. requested_reply=true means the - * rule always applies. - */ - if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.requested_reply) - { - _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n"); - continue; - } + if (rule->d.receive.error != NULL) + { + if (dbus_message_get_error_name (message) != NULL && + strcmp (dbus_message_get_error_name (message), + rule->d.receive.error) != 0) + { + _dbus_verbose (" (policy) skipping rule for different error name\n"); + return FALSE; } - - if (rule->d.receive.path != NULL) + } + + if (rule->d.receive.origin != NULL) + { + /* sender can be NULL for messages that originate from the + * message bus itself, we check the strings in that case as + * built-in services don't have a DBusConnection but will + * still set the sender on their messages. + */ + if (sender == NULL) { - if (dbus_message_get_path (message) != NULL && - strcmp (dbus_message_get_path (message), - rule->d.receive.path) != 0) + if (!dbus_message_has_sender (message, + rule->d.receive.origin)) { - _dbus_verbose (" (policy) skipping rule for different path\n"); - continue; + _dbus_verbose (" (policy) skipping rule because message sender is not %s\n", + rule->d.receive.origin); + return FALSE; } } - - if (rule->d.receive.interface != NULL) + else { - /* The interface is optional in messages. For allow rules, if the message - * has no interface we want to skip the rule (and thus not allow); - * for deny rules, if the message has no interface we want to use the - * rule (and thus deny). Check rules are treated like allow rules. - */ - dbus_bool_t no_interface; + BusService *service; + DBusString str; - no_interface = dbus_message_get_interface (message) == NULL; + _dbus_string_init_const (&str, rule->d.receive.origin); + + service = bus_registry_lookup (registry, &str); - if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) || - (!no_interface && - strcmp (dbus_message_get_interface (message), - rule->d.receive.interface) != 0)) + if (service == NULL) { - _dbus_verbose (" (policy) skipping rule for different interface\n"); - continue; + _dbus_verbose (" (policy) skipping rule because origin %s doesn't exist\n", + rule->d.receive.origin); + return FALSE; } - } - if (rule->d.receive.member != NULL) - { - if (dbus_message_get_member (message) != NULL && - strcmp (dbus_message_get_member (message), - rule->d.receive.member) != 0) + if (!bus_service_has_owner (service, sender)) { - _dbus_verbose (" (policy) skipping rule for different member\n"); - continue; + _dbus_verbose (" (policy) skipping rule because origin %s isn't owned by sender\n", + rule->d.receive.origin); + return FALSE; } } + } - if (rule->d.receive.error != NULL) + if (rule->d.receive.min_fds > 0 || + rule->d.receive.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) + { + unsigned int n_fds = _dbus_message_get_n_unix_fds (message); + + if (n_fds < rule->d.receive.min_fds || n_fds > rule->d.receive.max_fds) { - if (dbus_message_get_error_name (message) != NULL && - strcmp (dbus_message_get_error_name (message), - rule->d.receive.error) != 0) - { - _dbus_verbose (" (policy) skipping rule for different error name\n"); - continue; - } + _dbus_verbose (" (policy) skipping rule because message has %u fds " + "and that is outside range [%u,%u]", + n_fds, rule->d.receive.min_fds, + rule->d.receive.max_fds); + return FALSE; } - - if (rule->d.receive.origin != NULL) - { - /* sender can be NULL for messages that originate from the - * message bus itself, we check the strings in that case as - * built-in services don't have a DBusConnection but will - * still set the sender on their messages. - */ - if (sender == NULL) - { - if (!dbus_message_has_sender (message, - rule->d.receive.origin)) - { - _dbus_verbose (" (policy) skipping rule because message sender is not %s\n", - rule->d.receive.origin); - continue; - } - } - else - { - BusService *service; - DBusString str; + } - _dbus_string_init_const (&str, rule->d.receive.origin); - - service = bus_registry_lookup (registry, &str); - - if (service == NULL) - { - _dbus_verbose (" (policy) skipping rule because origin %s doesn't exist\n", - rule->d.receive.origin); - continue; - } + /* Use this rule */ + switch (rule->access) + { + case BUS_POLICY_RULE_ACCESS_ALLOW: + *result = BUS_RESULT_TRUE; + break; + case BUS_POLICY_RULE_ACCESS_DENY: + *result = BUS_RESULT_FALSE; + break; + case BUS_POLICY_RULE_ACCESS_CHECK: + *result = BUS_RESULT_LATER; + *privilege = rule->privilege; + break; + } - if (!bus_service_has_owner (service, sender)) - { - _dbus_verbose (" (policy) skipping rule because origin %s isn't owned by sender\n", - rule->d.receive.origin); - continue; - } - } - } + return TRUE; +} - if (rule->d.receive.min_fds > 0 || - rule->d.receive.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) - { - unsigned int n_fds = _dbus_message_get_n_unix_fds (message); +/* See docs on what the args mean on bus_context_check_security_policy() + * comment + */ +BusResult +bus_client_policy_check_can_receive (BusClientPolicy *policy, + BusRegistry *registry, + dbus_bool_t requested_reply, + DBusConnection *sender, + DBusConnection *addressed_recipient, + DBusConnection *proposed_recipient, + DBusMessage *message, + dbus_int32_t *toggles, + const char **privilege_param, + BusDeferredMessage **deferred_message, + char **out_rule) +{ + DBusList *link; + dbus_bool_t eavesdropping; + BusResult result; + const char *privilege; + BusPolicyRule *matched_rule = NULL; - if (n_fds < rule->d.receive.min_fds || n_fds > rule->d.receive.max_fds) - { - _dbus_verbose (" (policy) skipping rule because message has %u fds " - "and that is outside range [%u,%u]", - n_fds, rule->d.receive.min_fds, - rule->d.receive.max_fds); - continue; - } - } + eavesdropping = + addressed_recipient != proposed_recipient && + dbus_message_get_destination (message) != NULL; - /* Use this rule */ - switch (rule->access) - { - case BUS_POLICY_RULE_ACCESS_ALLOW: - result = BUS_RESULT_TRUE; - break; - case BUS_POLICY_RULE_ACCESS_DENY: - result = BUS_RESULT_FALSE; - break; - case BUS_POLICY_RULE_ACCESS_CHECK: - result = BUS_RESULT_LATER; - privilege = rule->privilege; - break; - } + /* 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, eavesdropping = %d\n", eavesdropping); + *toggles = 0; - (*toggles)++; - matched_rule = rule; + result = BUS_RESULT_FALSE; + link = _dbus_list_get_first_link (&policy->rules); + while (link != NULL) + { + const BusPolicyRule *rule = link->data; - _dbus_verbose (" (policy) used rule, result now = %d\n", - result); + link = _dbus_list_get_next_link (&policy->rules, link); + + if (check_receive_rule (rule, registry, requested_reply, sender, + message, eavesdropping, &result, &privilege)) + { + (*toggles)++; + matched_rule = (BusPolicyRule *)rule; + + _dbus_verbose (" (policy) used rule, result now = %d\n", + result); + } } @@ -1430,7 +1462,50 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy, return result; } +static dbus_bool_t +check_own_rule (const BusPolicyRule *rule, + const DBusString *service_name, + BusResult *result, + const char **privilege) +{ + /* Rule is skipped if it specifies a different service name from + * the desired one. + */ + + if (rule->type != BUS_POLICY_RULE_OWN) + return FALSE; + + if (!rule->d.own.prefix && rule->d.own.service_name != NULL) + { + if (!_dbus_string_equal_c_str (service_name, + rule->d.own.service_name)) + return FALSE; + } + else if (rule->d.own.prefix) + { + if (!_dbus_string_starts_with_words_c_str (service_name, + rule->d.own.service_name, + '.')) + return FALSE; + } + /* Use this rule */ + switch (rule->access) + { + case BUS_POLICY_RULE_ACCESS_ALLOW: + *result = BUS_RESULT_TRUE; + break; + case BUS_POLICY_RULE_ACCESS_DENY: + *result = BUS_RESULT_FALSE; + break; + case BUS_POLICY_RULE_ACCESS_CHECK: + *result = BUS_RESULT_LATER; + *privilege = rule->privilege; + break; + } + + return TRUE; +} static BusResult bus_rules_check_can_own (DBusList *rules, @@ -1450,45 +1525,11 @@ bus_rules_check_can_own (DBusList *rules, link = _dbus_list_get_first_link (&rules); while (link != NULL) { - BusPolicyRule *rule = link->data; + const BusPolicyRule *rule = link->data; link = _dbus_list_get_next_link (&rules, link); - /* Rule is skipped if it specifies a different service name from - * the desired one. - */ - - if (rule->type != BUS_POLICY_RULE_OWN) - continue; - - if (!rule->d.own.prefix && rule->d.own.service_name != NULL) - { - if (!_dbus_string_equal_c_str (service_name, - rule->d.own.service_name)) - continue; - } - else if (rule->d.own.prefix) - { - if (!_dbus_string_starts_with_words_c_str (service_name, - rule->d.own.service_name, - '.')) - continue; - } - - /* Use this rule */ - switch (rule->access) - { - case BUS_POLICY_RULE_ACCESS_ALLOW: - result = BUS_RESULT_TRUE; - break; - case BUS_POLICY_RULE_ACCESS_DENY: - result = BUS_RESULT_FALSE; - break; - case BUS_POLICY_RULE_ACCESS_CHECK: - result = BUS_RESULT_LATER; - privilege = rule->privilege; - break; - } + check_own_rule (rule, service_name, &result, &privilege); } if (result == BUS_RESULT_LATER) |