From 004f01fa451b0341e7ea69ce7f08a1c4690f759a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:07:01 +0200 Subject: unix-fd: add test for passing unix fds This adds a full test for passing multiple fds across a D-Bus connection. --- bus/dispatch.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bus/test-main.c | 10 ++++ bus/test.h | 4 +- 3 files changed, 164 insertions(+), 1 deletion(-) (limited to 'bus') diff --git a/bus/dispatch.c b/bus/dispatch.c index 209bde3e..ca9a8433 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -35,6 +35,11 @@ #include #include +#ifdef HAVE_UNIX_FD_PASSING +#include +#include +#endif + static dbus_bool_t send_one_message (DBusConnection *connection, BusContext *context, @@ -4716,4 +4721,150 @@ bus_dispatch_sha1_test (const DBusString *test_data_dir) return TRUE; } +#ifdef HAVE_UNIX_FD_PASSING + +dbus_bool_t +bus_unix_fds_passing_test(const DBusString *test_data_dir) +{ + BusContext *context; + DBusConnection *foo, *bar; + DBusError error; + DBusMessage *m; + dbus_bool_t b; + int one[2], two[2], x, y, z; + char r; + + dbus_error_init (&error); + + context = bus_context_new_test (test_data_dir, "valid-config-files/debug-allow-all.conf"); + if (context == NULL) + _dbus_assert_not_reached ("could not alloc context"); + + foo = dbus_connection_open_private ("debug-pipe:name=test-server", &error); + if (foo == NULL) + _dbus_assert_not_reached ("could not alloc connection"); + + if (!bus_setup_debug_client (foo)) + _dbus_assert_not_reached ("could not set up connection"); + + spin_connection_until_authenticated (context, foo); + + 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_private ("debug-pipe:name=test-server", &error); + if (bar == NULL) + _dbus_assert_not_reached ("could not alloc connection"); + + if (!bus_setup_debug_client (bar)) + _dbus_assert_not_reached ("could not set up connection"); + + spin_connection_until_authenticated (context, bar); + + 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"); + + if (!(m = dbus_message_new_signal("/", "a.b.c", "d"))) + _dbus_assert_not_reached ("could not alloc message"); + + if (!(_dbus_full_duplex_pipe(one, one+1, TRUE, &error))) + _dbus_assert_not_reached("Failed to allocate pipe #1"); + + if (!(_dbus_full_duplex_pipe(two, two+1, TRUE, &error))) + _dbus_assert_not_reached("Failed to allocate pipe #2"); + + if (!dbus_message_append_args(m, + DBUS_TYPE_UNIX_FD, one, + DBUS_TYPE_UNIX_FD, two, + DBUS_TYPE_UNIX_FD, two, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached("Failed to attach fds."); + + if (!_dbus_close(one[0], &error)) + _dbus_assert_not_reached("Failed to close pipe #1 "); + if (!_dbus_close(two[0], &error)) + _dbus_assert_not_reached("Failed to close pipe #2 "); + + + if (!dbus_connection_send (foo, m, NULL)) + _dbus_assert_not_reached("Failed to send fds"); + + dbus_message_unref(m); + + bus_test_run_clients_loop (SEND_PENDING (foo)); + + bus_test_run_everything (context); + + block_connection_until_message_from_bus (context, foo, "unix fd reception on foo"); + + if (!(m = pop_message_waiting_for_memory (foo))) + _dbus_assert_not_reached("Failed to receive msg"); + + if (!dbus_message_is_signal(m, "a.b.c", "d")) + _dbus_assert_not_reached("bogus message received"); + + dbus_message_unref(m); + + block_connection_until_message_from_bus (context, bar, "unix fd reception on bar"); + + if (!(m = pop_message_waiting_for_memory (bar))) + _dbus_assert_not_reached("Failed to receive msg"); + + if (!dbus_message_is_signal(m, "a.b.c", "d")) + _dbus_assert_not_reached("bogus message received"); + + if (!dbus_message_get_args(m, + &error, + DBUS_TYPE_UNIX_FD, &x, + DBUS_TYPE_UNIX_FD, &y, + DBUS_TYPE_UNIX_FD, &z, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached("Failed to parse fds."); + + dbus_message_unref(m); + + if (write(x, "X", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #1"); + if (write(y, "Y", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #2"); + if (write(z, "Z", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #2/2nd fd"); + + if (!_dbus_close(x, &error)) + _dbus_assert_not_reached("Failed to close pipe #1/other side "); + if (!_dbus_close(y, &error)) + _dbus_assert_not_reached("Failed to close pipe #2/other side "); + if (!_dbus_close(z, &error)) + _dbus_assert_not_reached("Failed to close pipe #2/other size 2nd fd "); + + if (read(one[1], &r, 1) != 1 || r != 'X') + _dbus_assert_not_reached("Failed to read value from pipe."); + if (read(two[1], &r, 1) != 1 || r != 'Y') + _dbus_assert_not_reached("Failed to read value from pipe."); + if (read(two[1], &r, 1) != 1 || r != 'Z') + _dbus_assert_not_reached("Failed to read value from pipe."); + + if (!_dbus_close(one[1], &error)) + _dbus_assert_not_reached("Failed to close pipe #1 "); + if (!_dbus_close(two[1], &error)) + _dbus_assert_not_reached("Failed to close pipe #2 "); + + _dbus_verbose ("Disconnecting foo\n"); + kill_client_connection_unchecked (foo); + + _dbus_verbose ("Disconnecting bar\n"); + kill_client_connection_unchecked (bar); + + bus_context_unref (context); + + return TRUE; +} +#endif + #endif /* DBUS_BUILD_TESTS */ diff --git a/bus/test-main.c b/bus/test-main.c index 994550e2..2583f9a8 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "selinux.h" #ifdef DBUS_BUILD_TESTS @@ -69,6 +70,7 @@ test_post_hook (void) if (_dbus_getenv ("DBUS_TEST_SELINUX")) bus_selinux_shutdown (); check_memleaks (progname); + _dbus_check_fdleaks(); } int @@ -138,6 +140,14 @@ main (int argc, char **argv) die ("service reload"); test_post_hook (); +#ifdef HAVE_UNIX_FD_PASSING + test_pre_hook (); + printf ("%s: Running unix fd passing test\n", argv[0]); + if (!bus_unix_fds_passing_test (&test_data_dir)) + die ("unix fd passing"); + test_post_hook (); +#endif + printf ("%s: Success\n", argv[0]); diff --git a/bus/test.h b/bus/test.h index 5aab0ea9..6d36b9c4 100644 --- a/bus/test.h +++ b/bus/test.h @@ -51,7 +51,9 @@ void bus_test_run_everything (BusContext *context); BusContext* bus_context_new_test (const DBusString *test_data_dir, const char *filename); - +#ifdef HAVE_UNIX_FD_PASSING +dbus_bool_t bus_unix_fds_passing_test (const DBusString *test_data_dir); +#endif #endif -- cgit From 44f3a1465a5a3f6d2e022409a960443441662629 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:22:51 +0200 Subject: cloexec: set FD_CLOEXEC for all full duplex pipes All users of full duplex pipes enable FD_CLOEXEC later anyway so let's just do it as part of _dbus_full_duplex_pipe. By side effect this allows to make use of SOCK_CLOEXEC which fixes a race when forking/execing from a different thread at the same time as we ar in this function. --- bus/main.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'bus') diff --git a/bus/main.c b/bus/main.c index 51538fe7..d86825a5 100644 --- a/bus/main.c +++ b/bus/main.c @@ -208,9 +208,6 @@ setup_reload_pipe (DBusLoop *loop) exit (1); } - _dbus_fd_set_close_on_exec (reload_pipe[0]); - _dbus_fd_set_close_on_exec (reload_pipe[1]); - watch = _dbus_watch_new (reload_pipe[RELOAD_READ_END], DBUS_WATCH_READABLE, TRUE, handle_reload_watch, NULL, NULL); -- cgit From 18b08180aa5a4417fa1d6d268a1aad894e8a4549 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:43:31 +0200 Subject: build-system: define _GNU_SOURCE centrally Instead of having everyone define _GNU_SOURCE and similar macros seperately, simply do so centrally by using AC_USE_SYSTEM_EXTENSIONS --- bus/dir-watch-inotify.c | 1 - 1 file changed, 1 deletion(-) (limited to 'bus') diff --git a/bus/dir-watch-inotify.c b/bus/dir-watch-inotify.c index 44ec9cf0..12fdbb07 100644 --- a/bus/dir-watch-inotify.c +++ b/bus/dir-watch-inotify.c @@ -24,7 +24,6 @@ #include -#define _GNU_SOURCE #include #include #include -- cgit From c200e0304d6f53a0fd47f524386b02b27c0c45f6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Apr 2009 04:38:27 +0200 Subject: auth: add fd passing negotiation support This adds two new directives to the auth protocol: NEGOTIATE_UNIX_FD is sent by the client after the authentication was sucessful, i.e. OK was received. AGREE_UNIX_FD is then sent by the server if it can do unix fd passing as well. ERROR is returned when the server cannot or is unwilling to do unix fd passing. This should be compatible with existing D-Bus implementations which will naturally return ERROR on NEGOTIATE_UNIX_FD. --- bus/dispatch.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'bus') diff --git a/bus/dispatch.c b/bus/dispatch.c index ca9a8433..8ed88dad 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -4791,6 +4791,11 @@ bus_unix_fds_passing_test(const DBusString *test_data_dir) if (!_dbus_close(two[0], &error)) _dbus_assert_not_reached("Failed to close pipe #2 "); + if (!(dbus_connection_can_send_type(foo, DBUS_TYPE_UNIX_FD))) + _dbus_assert_not_reached("Connection cannot do fd passing"); + + if (!(dbus_connection_can_send_type(bar, DBUS_TYPE_UNIX_FD))) + _dbus_assert_not_reached("Connection cannot do fd passing"); if (!dbus_connection_send (foo, m, NULL)) _dbus_assert_not_reached("Failed to send fds"); -- cgit From cbf0874f018c6ddd831de26b1ac5bcf7b517e2da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 May 2009 00:01:50 +0200 Subject: memset: replace memset() by _DBUS_ZERO where applicable --- bus/desktop-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bus') diff --git a/bus/desktop-file.c b/bus/desktop-file.c index 2ba77292..e925b2ea 100644 --- a/bus/desktop-file.c +++ b/bus/desktop-file.c @@ -330,7 +330,7 @@ new_line (BusDesktopFileParser *parser) line = §ion->lines[section->n_lines++]; - memset (line, 0, sizeof (BusDesktopFileLine)); + _DBUS_ZERO(*line); return line; } -- cgit From 64ad8449679c53fefd20baea88fa593f226d59b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 May 2009 01:33:17 +0200 Subject: bus: make use of new unix fd limits Create configuration settings and enforce message unix fd limits the same way we do for allocated message memory. --- bus/bus.c | 10 ++++++++-- bus/bus.h | 3 +++ bus/config-parser.c | 27 +++++++++++++++++++++++++++ bus/dbus-daemon.1.in | 5 +++++ bus/session.conf.in | 3 +++ 5 files changed, 46 insertions(+), 2 deletions(-) (limited to 'bus') diff --git a/bus/bus.c b/bus/bus.c index 1412ea28..129b2e6c 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -190,6 +190,12 @@ new_connection_callback (DBusServer *server, dbus_connection_set_max_message_size (new_connection, context->limits.max_message_size); + + dbus_connection_set_max_received_unix_fds (new_connection, + context->limits.max_incoming_unix_fds); + + dbus_connection_set_max_message_unix_fds (new_connection, + context->limits.max_message_unix_fds); dbus_connection_set_allow_anonymous (new_connection, context->allow_anonymous); @@ -1471,8 +1477,8 @@ bus_context_check_security_policy (BusContext *context, /* See if limits on size have been exceeded */ if (proposed_recipient && - dbus_connection_get_outgoing_size (proposed_recipient) > - context->limits.max_outgoing_bytes) + ((dbus_connection_get_outgoing_size (proposed_recipient) > context->limits.max_outgoing_bytes) || + (dbus_connection_get_outgoing_unix_fds (proposed_recipient) > context->limits.max_outgoing_unix_fds))) { dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, "The destination service \"%s\" has a full message queue", diff --git a/bus/bus.h b/bus/bus.h index 74bdb821..aba17043 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -47,8 +47,11 @@ typedef struct BusMatchRule BusMatchRule; typedef struct { long max_incoming_bytes; /**< How many incoming message bytes for a single connection */ + long max_incoming_unix_fds; /**< How many incoming message unix fds for a single connection */ long max_outgoing_bytes; /**< How many outgoing bytes can be queued for a single connection */ + long max_outgoing_unix_fds; /**< How many outgoing unix fds can be queued for a single connection */ long max_message_size; /**< Max size of a single message in bytes */ + long max_message_unix_fds; /**< Max number of unix fds of a single message*/ int activation_timeout; /**< How long to wait for an activation to time out */ int auth_timeout; /**< How long to wait for an authentication to time out */ int max_completed_connections; /**< Max number of authorized connections */ diff --git a/bus/config-parser.c b/bus/config-parser.c index c3e8fba1..784c8315 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -404,6 +404,15 @@ bus_config_parser_new (const DBusString *basedir, parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 127; parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 127; parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32; + + /* We set relatively conservative values here since due to the + way SCM_RIGHTS works we need to preallocate an array for the + maximum number of file descriptors we can receive. Picking a + high value here thus translates directly to more memory + allocation. */ + parser->limits.max_incoming_unix_fds = 1024*4; + parser->limits.max_outgoing_unix_fds = 1024*4; + parser->limits.max_message_unix_fds = 1024; /* Making this long means the user has to wait longer for an error * message if something screws up, but making it too short means @@ -1828,16 +1837,31 @@ set_limit (BusConfigParser *parser, must_be_positive = TRUE; parser->limits.max_incoming_bytes = value; } + else if (strcmp (name, "max_incoming_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_incoming_unix_fds = value; + } else if (strcmp (name, "max_outgoing_bytes") == 0) { must_be_positive = TRUE; parser->limits.max_outgoing_bytes = value; } + else if (strcmp (name, "max_outgoing_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_outgoing_unix_fds = value; + } else if (strcmp (name, "max_message_size") == 0) { must_be_positive = TRUE; parser->limits.max_message_size = value; } + else if (strcmp (name, "max_message_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_message_unix_fds = value; + } else if (strcmp (name, "service_start_timeout") == 0) { must_be_positive = TRUE; @@ -2979,8 +3003,11 @@ limits_equal (const BusLimits *a, { return (a->max_incoming_bytes == b->max_incoming_bytes + || a->max_incoming_unix_fds == b->max_incoming_unix_fds || a->max_outgoing_bytes == b->max_outgoing_bytes + || a->max_outgoing_unix_fds == b->max_outgoing_unix_fds || a->max_message_size == b->max_message_size + || a->max_message_unix_fds == b->max_message_unix_fds || a->activation_timeout == b->activation_timeout || a->auth_timeout == b->auth_timeout || a->max_completed_connections == b->max_completed_connections diff --git a/bus/dbus-daemon.1.in b/bus/dbus-daemon.1.in index 4b55ac29..8d518136 100644 --- a/bus/dbus-daemon.1.in +++ b/bus/dbus-daemon.1.in @@ -365,10 +365,15 @@ Available limit names are: .nf "max_incoming_bytes" : total size in bytes of messages incoming from a single connection + "max_incoming_unix_fds" : total number of unix fds of messages + incoming from a single connection "max_outgoing_bytes" : total size in bytes of messages queued up for a single connection + "max_outgoing_unix_fds" : total number of unix fds of messages + queued up for a single connection "max_message_size" : max size of a single message in bytes + "max_message_unix_fds" : max unix fds of a single message "service_start_timeout" : milliseconds (thousandths) until a started service has to connect "auth_timeout" : milliseconds (thousandths) a diff --git a/bus/session.conf.in b/bus/session.conf.in index 794eb8da..aed320eb 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -45,8 +45,11 @@ 1000000000 + 250000000 1000000000 + 250000000 1000000000 + 4096 120000 240000 100000 -- cgit From 89f70b19495e9c3dadbbb7b51ce1629fa22ea3af Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 May 2009 01:00:52 +0200 Subject: bus: don't forward messages with unix fds on connections that don't support it This simply verifies that we forward unix fds only on connection that support it. We willr eturn an error if a client attempts to send a message with unix fds to another client that cannot do it. --- bus/dispatch.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'bus') diff --git a/bus/dispatch.c b/bus/dispatch.c index 8ed88dad..fa5874d2 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -56,6 +56,10 @@ send_one_message (DBusConnection *connection, message, NULL)) return TRUE; /* silently don't send it */ + + if (dbus_message_contains_unix_fds(message) && + !dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD)) + return TRUE; /* silently don't send it */ if (!bus_transaction_send (transaction, connection, @@ -300,6 +304,16 @@ bus_dispatch (DBusConnection *connection, addressed_recipient, message, &error)) goto out; + + if (dbus_message_contains_unix_fds(message) && + !dbus_connection_can_send_type(addressed_recipient, DBUS_TYPE_UNIX_FD)) + { + dbus_set_error(&error, + DBUS_ERROR_NOT_SUPPORTED, + "Tried to send message with Unix file descriptors" + "to a client that doesn't support that."); + goto out; + } /* Dispatch the message */ if (!bus_transaction_send (transaction, addressed_recipient, message)) -- cgit