From 3486e0f48dfadf1b175e3796f0a65c0da231889b Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 16 Jan 2019 12:30:22 +0100 Subject: [PATCH 1/5] DBusString: extend with checking for starting with words This extracts a few lines of code and adds it as a DBusString function that checks if a DBusString starts with words given with a C string and a word separator. In other words, it checks if: - a DBusString is a given C string, or - a DBusString starts with a given C string and the next character is a given word separator. It is used for matching names to prefixes when checking the policy. Signed-off-by: Adrian Szyndela Change-Id: Ie39d33916863d950dde38d3b8b20c8a539217302 --- bus/policy.c | 12 +++--------- dbus/dbus-string.c | 26 ++++++++++++++++++++++++++ dbus/dbus-string.h | 4 ++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/bus/policy.c b/bus/policy.c index c87bfecf..11d21bf0 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -1341,15 +1341,9 @@ bus_rules_check_can_own (DBusList *rules, } else if (rule->d.own.prefix) { - const char *data; - char next_char; - if (!_dbus_string_starts_with_c_str (service_name, - rule->d.own.service_name)) - continue; - - data = _dbus_string_get_const_data (service_name); - next_char = data[strlen (rule->d.own.service_name)]; - if (next_char != '\0' && next_char != '.') + if (!_dbus_string_starts_with_words_c_str (service_name, + rule->d.own.service_name, + '.')) continue; } diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 52c71c5b..d620067e 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -2237,6 +2237,32 @@ _dbus_string_starts_with_c_str (const DBusString *a, return FALSE; } +/** + * Checks whether a string starts with the given C string, after which it ends or is separated from + * the rest by a given separator character. + * + * @param a the string + * @param c_str the C string + * @param word_separator the separator + * @returns #TRUE if string starts with it + */ +dbus_bool_t +_dbus_string_starts_with_words_c_str (const DBusString *a, + const char *c_str, + char word_separator) +{ + char next_char; + const char *data; + _dbus_assert (c_str != NULL); + + if (!_dbus_string_starts_with_c_str (a, c_str)) + return FALSE; + + data = _dbus_string_get_const_data (a); + next_char = data[strlen (c_str)]; + return next_char == '\0' || next_char == word_separator; +} + /** * Appends a two-character hex digit to a string, where the hex digit * has the value of the given byte. diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index 41e82ab9..7ac32d38 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -338,6 +338,10 @@ dbus_bool_t _dbus_string_starts_with_c_str (const DBusString *a, dbus_bool_t _dbus_string_ends_with_c_str (const DBusString *a, const char *c_str); DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_string_starts_with_words_c_str (const DBusString *a, + const char *c_str, + char word_separator); +DBUS_PRIVATE_EXPORT dbus_bool_t _dbus_string_pop_line (DBusString *source, DBusString *dest); DBUS_PRIVATE_EXPORT From 055ff9e64a65931cbd2b94f24ea46bf3750d0b8e Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 28 Nov 2018 15:15:16 +0100 Subject: [PATCH 2/5] dbus-daemon: add send_destination_prefix support This extends dbus-daemon with support for send_destination_prefix attribute in XML policies. It allows having policy rules for sending to bus names generated within namespaces defined by a prefix. The similar behaviour can be emulated by owning an additional name, not used for addressing messages, as described in https://lists.freedesktop.org/archives/dbus/2017-May/017188.html However, introducing send_destination_prefix creates possibility of communicating intentions in a more direct way, which is easier to understand. Signed-off-by: Adrian Szyndela Change-Id: I0016ad93f1c16b7742fef5f45ebaf01b55694d3c --- bus/config-parser.c | 29 ++++++++++++++++++++++++++--- bus/connection.c | 40 ++++++++++++++++++++++++++++++++++++++++ bus/connection.h | 4 ++++ bus/policy.c | 43 ++++++++++++++++++++++++++++++++++++++++--- bus/policy.h | 1 + 5 files changed, 111 insertions(+), 6 deletions(-) diff --git a/bus/config-parser.c b/bus/config-parser.c index 4e62c342..a47b8a58 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -1298,6 +1298,7 @@ append_rule_from_element (BusConfigParser *parser, const char *send_member; const char *send_error; const char *send_destination; + const char *send_destination_prefix; const char *send_path; const char *send_type; const char *send_requested_reply; @@ -1341,6 +1342,7 @@ append_rule_from_element (BusConfigParser *parser, "send_member", &send_member, "send_error", &send_error, "send_destination", &send_destination, + "send_destination_prefix", &send_destination_prefix, "send_path", &send_path, "send_type", &send_type, "send_broadcast", &send_broadcast, @@ -1364,6 +1366,7 @@ append_rule_from_element (BusConfigParser *parser, return FALSE; any_send_attribute = (send_destination != NULL || + send_destination_prefix != NULL || send_broadcast != NULL || send_path != NULL || send_type != NULL || @@ -1417,7 +1420,8 @@ append_rule_from_element (BusConfigParser *parser, * interface + member * error * - * base send_ can combine with send_destination, send_path, send_type, send_requested_reply, send_broadcast, eavesdrop + * base send_ can combine with send_destination, send_destination_prefix, send_path, send_type, send_requested_reply, send_broadcast, eavesdrop + * send_destination must not occur with send_destination_prefix * base receive_ with receive_sender, receive_path, receive_type, receive_requested_reply, eavesdrop * * user, group, own, own_prefix must occur alone @@ -1456,6 +1460,16 @@ append_rule_from_element (BusConfigParser *parser, return FALSE; } + if ((send_destination != NULL) + (send_destination_prefix != NULL) > 1) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Invalid combination of attributes on element <%s>: " + "send_destination cannot be combined with " + "send_destination_prefix", + element_name); + return FALSE; + } + if ((receive_member != NULL || receive_interface != NULL) && receive_error != NULL) { @@ -1589,7 +1603,16 @@ append_rule_from_element (BusConfigParser *parser, 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_destination); + if (send_destination) + { + rule->d.send.destination = _dbus_strdup (send_destination); + rule->d.send.destination_is_prefix = 0; + } + else if (send_destination_prefix) + { + rule->d.send.destination = _dbus_strdup (send_destination_prefix); + rule->d.send.destination_is_prefix = 1; + } rule->d.send.max_fds = max_fds; rule->d.send.min_fds = min_fds; @@ -1601,7 +1624,7 @@ append_rule_from_element (BusConfigParser *parser, goto nomem; if (send_error && rule->d.send.error == NULL) goto nomem; - if (send_destination && rule->d.send.destination == NULL) + if ((send_destination || send_destination_prefix) && rule->d.send.destination == NULL) goto nomem; } else if (any_receive_attribute) diff --git a/bus/connection.c b/bus/connection.c index 3af980d0..5943841d 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -1446,6 +1446,46 @@ bus_connection_get_n_match_rules (DBusConnection *connection) return d->n_match_rules; } +/** + * Checks whether the connection owns any name with a given prefix, + * regardless of whether the type of ownership is primary or queued. + * + * @note A name matches to a prefix if it is equal to the prefix, + * or if it starts with the prefix followed by a dot. This is the same + * rule as the 'own_prefix' checking rule. + * + * @param connection the connection + * @param name_prefix the prefix + * @returns #TRUE if the connection owns at least one name with the prefix, + * regardless of the type of ownership + */ +dbus_bool_t +bus_connection_is_name_owner_by_prefix (DBusConnection *connection, + const char *name_prefix) +{ + BusConnectionData *d; + DBusList *link; + + d = BUS_CONNECTION_DATA (connection); + _dbus_assert (d != NULL); + + link = _dbus_list_get_first_link (&d->services_owned); + while (link != NULL) + { + BusService *service = link->data; + DBusString str; + + _dbus_string_init_const (&str, bus_service_get_name (service)); + + if (_dbus_string_starts_with_words_c_str (&str, name_prefix, '.')) + return TRUE; + + link = _dbus_list_get_next_link (&d->services_owned, link); + } + + return FALSE; +} + void bus_connection_add_owned_service_link (DBusConnection *connection, DBusList *link) diff --git a/bus/connection.h b/bus/connection.h index 949a2231..912638c0 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -82,6 +82,10 @@ void bus_connection_send_oom_error (DBusConnection *connection, void bus_connection_request_headers (DBusConnection *connection, BusExtraHeaders headers); +/* called by policy.c */ +dbus_bool_t bus_connection_is_name_owner_by_prefix (DBusConnection *connection, + const char *name_prefix); + /* called by signals.c */ dbus_bool_t bus_connection_add_match_rule (DBusConnection *connection, BusMatchRule *rule); diff --git a/bus/policy.c b/bus/policy.c index 11d21bf0..feb1156e 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -1018,7 +1018,7 @@ bus_client_policy_check_can_send (BusClientPolicy *policy, } } - if (rule->d.send.destination != NULL) + if (rule->d.send.destination != NULL && !rule->d.send.destination_is_prefix) { /* receiver can be NULL for messages that are sent to the * message bus itself, we check the strings in that case as @@ -1044,9 +1044,9 @@ bus_client_policy_check_can_send (BusClientPolicy *policy, { DBusString str; BusService *service; - + _dbus_string_init_const (&str, rule->d.send.destination); - + service = bus_registry_lookup (registry, &str); if (service == NULL) { @@ -1064,6 +1064,43 @@ bus_client_policy_check_can_send (BusClientPolicy *policy, } } + if (rule->d.send.destination != NULL && rule->d.send.destination_is_prefix) + { + /* receiver can be NULL - the same as in !send.destination_is_prefix */ + if (receiver == 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; + } + } + else + { + if (!bus_connection_is_name_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; + } + } + } + if (rule->d.send.min_fds > 0 || rule->d.send.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) { diff --git a/bus/policy.h b/bus/policy.h index 308eaf86..df59d090 100644 --- a/bus/policy.h +++ b/bus/policy.h @@ -76,6 +76,7 @@ struct BusPolicyRule unsigned int requested_reply : 1; unsigned int log : 1; unsigned int broadcast : 2; /**< really a BusPolicyTristate */ + unsigned int destination_is_prefix : 1; } send; struct From 7b7c57243482af1a4ace21351b2cdf988676575d Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Thu, 25 Apr 2019 09:35:59 +0200 Subject: [PATCH 3/5] test: send_destination(_prefix) tests This adds tests for mostly "send_destination_prefix" cases and some "send_destination" cases. The general test case is: - addressed recipient is running and owns a name; - a message is sent to the name owner; - the response is checked for allow/deny (method return/error). Each test case is executed both for primary and queued ownership. The tests include: - checking send allow/deny for names and namespaces, including nesting; - checking send allow/deny for neighbour names; - checking send allow/deny for names/namespaces+interface+member. Signed-off-by: Adrian Szyndela Change-Id: If5fcada01601355e7aadefadad79c0b24f8c397f --- test/Makefile.am | 1 + .../send-destination-prefix-rules.conf.in | 121 ++++++++ test/dbus-daemon.c | 269 ++++++++++++++++++ 3 files changed, 391 insertions(+) create mode 100644 test/data/valid-config-files/send-destination-prefix-rules.conf.in diff --git a/test/Makefile.am b/test/Makefile.am index 559590b2..fd16381c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -604,6 +604,7 @@ in_data = \ data/valid-config-files/max-replies-per-connection.conf.in \ data/valid-config-files/multi-user.conf.in \ data/valid-config-files/pending-fd-timeout.conf.in \ + data/valid-config-files/send-destination-prefix-rules.conf.in \ data/valid-config-files/systemd-activation.conf.in \ data/valid-config-files/tmp-session.conf.in \ data/valid-config-files-system/tmp-session-like-system.conf.in \ diff --git a/test/data/valid-config-files/send-destination-prefix-rules.conf.in b/test/data/valid-config-files/send-destination-prefix-rules.conf.in new file mode 100644 index 00000000..304f05c5 --- /dev/null +++ b/test/data/valid-config-files/send-destination-prefix-rules.conf.in @@ -0,0 +1,121 @@ + + + @TEST_LISTEN@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index 8b012b92..80a79421 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -2210,6 +2210,267 @@ test_system_signals (Fixture *f, #endif #endif +static void +take_well_known_name (DBusConnection *conn, + const char *name, + DBusError *error, + int ownership_type) +{ + int ret = dbus_bus_request_name (conn, name, 0, error); + test_assert_no_error (error); + g_assert_cmpint (ret, ==, ownership_type); +} + +static void +drop_well_known_name (DBusConnection *conn, + const char *name, + DBusError *error) +{ + int ret = dbus_bus_release_name (conn, name, error); + test_assert_no_error (error); + g_assert_cmpint (ret, ==, DBUS_RELEASE_NAME_REPLY_RELEASED); +} + +static void +helper_send_destination_prefix_check (Fixture *f, + const char *name, + const char *interface, + const char *member, + dbus_bool_t allowed, + const char *additional_name, + int ownership_type) +{ + DBusMessage *call = NULL; + DBusMessage *reply = NULL; + + take_well_known_name (f->right_conn, name, &f->e, ownership_type); + + if (additional_name) + take_well_known_name (f->right_conn, additional_name, &f->e, ownership_type); + + call = dbus_message_new_method_call (dbus_bus_get_unique_name (f->right_conn), + "/", + interface, + member); + + if (call == NULL) + g_error ("OOM"); + + reply = test_main_context_call_and_wait (f->ctx, f->left_conn, call, + DBUS_TIMEOUT_USE_DEFAULT); + dbus_clear_message (&call); + g_test_message ("reply from %s(%d):%s OK", name, ownership_type, member); + if (allowed) + { + g_test_message ("checking reply from %s for correct method_return", name); + g_assert_cmpint (dbus_message_get_type (reply), ==, + DBUS_MESSAGE_TYPE_METHOD_RETURN); + } + else + { + g_test_message ("checking reply from %s for correct access_denied", name); + g_assert_cmpint (dbus_message_get_type (reply), ==, + DBUS_MESSAGE_TYPE_ERROR); + g_assert_cmpstr (dbus_message_get_error_name (reply), ==, + DBUS_ERROR_ACCESS_DENIED); + } + dbus_clear_message (&reply); + + drop_well_known_name (f->right_conn, name, &f->e); + + if (additional_name) + drop_well_known_name (f->right_conn, additional_name, &f->e); +} + +static void +helper_send_destination_prefix (Fixture *f, + const char *name, + const char *interface, + const char *member, + dbus_bool_t allowed, + const char *additional_name) +{ + /* check with primary ownership */ + helper_send_destination_prefix_check (f, name, interface, member, allowed, additional_name, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + + /* check with queued ownership */ + take_well_known_name (f->left_conn, name, &f->e, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + if (additional_name) + take_well_known_name (f->left_conn, additional_name, &f->e, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + + helper_send_destination_prefix_check (f, name, interface, member, allowed, additional_name, DBUS_REQUEST_NAME_REPLY_IN_QUEUE); + + drop_well_known_name (f->left_conn, name, &f->e); + if (additional_name) + drop_well_known_name (f->left_conn, additional_name, &f->e); +} + +static void +test_send_destination_prefix (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + if (f->skip) + return; + + add_echo_filter (f); + + /* + * Names are constructed with prefix foo.bar.test.dest_prefix followed by some of the tokens: + * - a - allow send_destination for this name + * - d - deny send_destination for this name + * - ap - allow send_destination_prefix for this name + * - dp - deny send_destination_prefix for this name + * - f, f1, f2, f3 - fillers for generating names down the name hierarchy + * - apf, dpf, ao, do - just some neighbour names + * - m - names with 'm' have rules for interface and member + * - apxdp, dpxap - names that have contradicting rules, e.g. for apxdp there are "allow send_destination_prefix" + * rules first, followed by "deny send_destination_prefix" rules + */ + + /* basic checks - base allow */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.apf", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.apf.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + /* basic checks - base deny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dpf", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dpf.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + /* With interface and method in the policy: + * everything is allowed, except foo.bar.a.CallDeny and whole foo.bar.d minus foo.bar.d.CallAllow.*/ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.a", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.a", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.a", "NonExistent", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.d", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.d", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.d", "NonExistent", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m", "foo.bar.none", "NonExistent", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.a", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.a", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.a", "NonExistent", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.d", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.d", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.d", "NonExistent", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.m.f.f.f.f.f", "foo.bar.none", "NonExistent", TRUE, NULL); + /* With interface and method in the policy: + * everything is denied, except foo.bar.d.CallAllow and whole foo.bar.a minus foo.bar.a.CallDeny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.a", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.a", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.a", "NonExistent", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.d", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.d", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.d", "NonExistent", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m", "foo.bar.none", "NonExistent", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.a", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.a", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.a", "NonExistent", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.d", "CallDeny", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.d", "CallAllow", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.d", "NonExistent", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.m.f.f.f.f.f", "foo.bar.none", "NonExistent", FALSE, NULL); + /* multiple names owned - everything is allowed */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.ap.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.ao"); + /* multiple names owned - mixed allow/deny, but denied wins */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ap.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.dp.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.do", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ap.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.do"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.dp.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ao"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ao.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.dp.f.f"); + /* multiple names owned - mixed allow/deny, but allowed wins */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.ao.ao"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao.ao", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f1.ap.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f"); + /* multiple names owned - everything is denied */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.do.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.do.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.dp.f"); + /* holes in default allow */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ao"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ap"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ap.f1.dp.f.f.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap", "com.example.Anything", "Anything", FALSE, "foo.bar.test.dest_prefix.ap.f1.dp.f.f.f.f"); + /* holes in holes in default allow */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.d.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.d.ap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.ap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f1.dp.ap.a", "com.example.Anything", "Anything", TRUE, NULL); + /* redefinitions in default allow */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.ap", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.ap.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.ap.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.a", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.dp.ap.f.a", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.f.f.f.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f2.apxdp.f.f.f.ap.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + /* cancelled definitions in default allow */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.dp", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.dp.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.dp.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.dp.ap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ap.f3.dpxap.ap.dp.a", "com.example.Anything", "Anything", TRUE, NULL); + /* holes in default deny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.a", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.a.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.f.f.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.do"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.do", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f1.ap.f.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.f.f.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.do.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.do.f.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f1.ap.f.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.f.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f.f.f", "com.example.Anything", "Anything", TRUE, "foo.bar.test.dest_prefix.dp.f1.ap.f.f"); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.ao", "com.example.Anything", "Anything", TRUE, NULL); + /* holes in holes in default deny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.a.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.a.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f1.ap.d.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + /* redefinitions in default deny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.dp", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.dp.f.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.dp.a", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.dp.a.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.d.f.f.f", "com.example.Anything", "Anything", TRUE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.ap.dp.f.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.f.f.f.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f2.dpxap.f.f.f.dp.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + /* cancelled definitions in default deny */ + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap.dp", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap.dp.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap.d", "com.example.Anything", "Anything", FALSE, NULL); + helper_send_destination_prefix (f, "foo.bar.test.dest_prefix.dp.f3.apxdp.dp.ap.d.f.f.f.f", "com.example.Anything", "Anything", FALSE, NULL); +} + static void teardown (Fixture *f, gconstpointer context G_GNUC_UNUSED) @@ -2353,6 +2614,11 @@ static Config nearly_system_config = { #endif #endif +static Config send_destination_prefix_config = { + NULL, 1, "valid-config-files/send-destination-prefix-rules.conf", + TEST_USER_ME, SPECIFY_ADDRESS +}; + int main (int argc, char **argv) @@ -2443,6 +2709,9 @@ main (int argc, #endif #endif + g_test_add ("/system-policy/send-destination/prefix", Fixture, &send_destination_prefix_config, + setup, test_send_destination_prefix, teardown); + ret = g_test_run (); dbus_shutdown (); return ret; From 8fcc1f9a8ae1a0160340a55cd4b6a34e329bdb30 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Thu, 25 Apr 2019 15:04:53 +0200 Subject: [PATCH 4/5] test: activation tests for send_destination_prefix This adds a few tests for checking if activation is allowed for names specified within send_destination_prefix namespaces. Signed-off-by: Adrian Szyndela Change-Id: I7a5a66f82fc08ce6cb46e37de2c3dfae24d9ea67 --- test/Makefile.am | 3 ++ ...xDenied.SendPrefixAllowed.internal.service | 4 +++ ....example.SendPrefixDenied.internal.service | 4 +++ .../com.example.SendPrefixDenied.service | 4 +++ .../systemd-activation.conf.in | 2 ++ test/sd-activation.c | 35 ++++++++++++++++++- 6 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/data/systemd-activation/com.example.SendPrefixDenied.SendPrefixAllowed.internal.service create mode 100644 test/data/systemd-activation/com.example.SendPrefixDenied.internal.service create mode 100644 test/data/systemd-activation/com.example.SendPrefixDenied.service diff --git a/test/Makefile.am b/test/Makefile.am index fd16381c..7547ea30 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -688,6 +688,9 @@ static_data = \ data/systemd-activation/com.example.ReceiveDenied.service \ data/systemd-activation/com.example.SendDenied.service \ data/systemd-activation/com.example.SendDeniedByAppArmorName.service \ + data/systemd-activation/com.example.SendPrefixDenied.service \ + data/systemd-activation/com.example.SendPrefixDenied.internal.service \ + data/systemd-activation/com.example.SendPrefixDenied.SendPrefixAllowed.internal.service \ data/systemd-activation/com.example.SystemdActivatable1.service \ data/systemd-activation/com.example.SystemdActivatable2.service \ data/systemd-activation/org.freedesktop.systemd1.service \ diff --git a/test/data/systemd-activation/com.example.SendPrefixDenied.SendPrefixAllowed.internal.service b/test/data/systemd-activation/com.example.SendPrefixDenied.SendPrefixAllowed.internal.service new file mode 100644 index 00000000..5aab8bf2 --- /dev/null +++ b/test/data/systemd-activation/com.example.SendPrefixDenied.SendPrefixAllowed.internal.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SendPrefixDenied.SendPrefixAllowed.internal +Exec=/bin/false SendPrefixDenied.SendPrefixAllowed.internal +SystemdService=dbus-com.example.SendPrefixDenied.SendPrefixAllowed.internal.service diff --git a/test/data/systemd-activation/com.example.SendPrefixDenied.internal.service b/test/data/systemd-activation/com.example.SendPrefixDenied.internal.service new file mode 100644 index 00000000..f9d63356 --- /dev/null +++ b/test/data/systemd-activation/com.example.SendPrefixDenied.internal.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SendPrefixDenied.internal +Exec=/bin/false SendPrefixDenied.internal +SystemdService=dbus-com.example.SendPrefixDenied.internal.service diff --git a/test/data/systemd-activation/com.example.SendPrefixDenied.service b/test/data/systemd-activation/com.example.SendPrefixDenied.service new file mode 100644 index 00000000..3595ce19 --- /dev/null +++ b/test/data/systemd-activation/com.example.SendPrefixDenied.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SendPrefixDenied +Exec=/bin/false SendPrefixDenied +SystemdService=dbus-com.example.SendPrefixDenied.service diff --git a/test/data/valid-config-files/systemd-activation.conf.in b/test/data/valid-config-files/systemd-activation.conf.in index 2c3d2c21..499aaa59 100644 --- a/test/data/valid-config-files/systemd-activation.conf.in +++ b/test/data/valid-config-files/systemd-activation.conf.in @@ -14,6 +14,8 @@ + + diff --git a/test/sd-activation.c b/test/sd-activation.c index b60888ae..e02e3013 100644 --- a/test/sd-activation.c +++ b/test/sd-activation.c @@ -516,6 +516,37 @@ test_activation (Fixture *f, g_error ("OOM"); dbus_connection_send (f->systemd, m, NULL); dbus_message_unref (m); + + /* A fourth activation: for name from send_destination_prefix namespace */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal4"); + if (!dbus_message_set_destination (m, "com.example.SendPrefixDenied.SendPrefixAllowed.internal")) + g_error ("OOM"); + dbus_connection_send (f->caller, m, NULL); + dbus_message_unref (m); + + /* systemd is already ready for it. */ + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + + /* Check ActivationRequest for the required name. */ + /* If it is correct, then it passed through policy checking, and the test is over. */ + do + { + const char *name; + DBusError error; + + dbus_error_init (&error); + dbus_message_get_args (m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); + test_assert_no_error (&error); + g_assert_cmpstr (name, ==, "dbus-com.example.SendPrefixDenied.SendPrefixAllowed.internal.service"); + } while (0); + dbus_message_unref (m); } static void @@ -1031,7 +1062,9 @@ static const Config deny_send_tests[] = { "com.example.SendDeniedByNonexistentAppArmorLabel" }, { "com.example.SendDeniedByAppArmorName" }, #endif - { "com.example.SendDenied" } + { "com.example.SendDenied" }, + { "com.example.SendPrefixDenied" }, + { "com.example.SendPrefixDenied.internal" } }; static const Config deny_receive_tests[] = From dcbab02613d38db4aad5a23ce7ac2505540078d1 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Fri, 26 Apr 2019 11:14:08 +0200 Subject: [PATCH 5/5] doc: described send_destination_prefix in manual This adds a description of send_destination_prefix to the dbus-daemon manual. Signed-off-by: Adrian Szyndela Change-Id: I46e6fa54ee34095c3ac83ec2c06cb91cf5669c7f --- doc/dbus-daemon.1.xml.in | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/dbus-daemon.1.xml.in b/doc/dbus-daemon.1.xml.in index acbc7ded..84698ca7 100644 --- a/doc/dbus-daemon.1.xml.in +++ b/doc/dbus-daemon.1.xml.in @@ -919,6 +919,7 @@ statements, and works just like <deny> but with the inverse meaning.receive_sender="*" similarly matches any message. + + A send_destination_prefix rule opens or closes + the whole namespace for sending. It means that messages may or may not + be sent to the owner of any name matching the prefix, regardless of whether + it is the primary or the queued owner. + In other words, for <allow send_destination_prefix="a.b"/> + rule and names "a.b", "a.b.c", and "a.b.c.d" present on the bus, it works the same as + if three separate rules: <allow send_destination="a.b"/>, + <allow send_destination="a.b.c"/>, and + <allow send_destination="a.b.c.d"/> had been defined. + The rules for matching names are the same as in own_prefix + (see below): a prefix of "a.b" matches names "a.b" or "a.b.c" or "a.b.c.d", but not "a.bc" + or "a.c". The send_destination_prefix attribute cannot be combined + with the send_destination attribute in the same rule. + + Rules with send_broadcast="true" match signal messages with no destination (broadcasts). Rules with