diff options
| author | Thiago Macieira <thiago@kde.org> | 2009-07-16 16:05:16 +0200 | 
|---|---|---|
| committer | Thiago Macieira <thiago@kde.org> | 2009-07-16 16:05:16 +0200 | 
| commit | 37019e9d271390fa8c62c1aae62d30bb9068adaa (patch) | |
| tree | 536d5838b73939a6a885f4ef61de1c18814b9529 | |
| parent | 56f7ce147e82c7eb529ccba634013e97d53b23c0 (diff) | |
| parent | 9f06daccce3f4e75cfac7c97bfb1743affb55cb2 (diff) | |
Merge branch 'fd-passing'
Conflicts:
	dbus/dbus-connection.c
	dbus/dbus-message-util.c
	dbus/dbus-sysdeps-unix.c
55 files changed, 2375 insertions, 354 deletions
| @@ -13,6 +13,12 @@ FILE=dbus-1.pc.in  DIE=0 +if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then +    echo "Activating pre-commit hook." +    cp -av .git/hooks/pre-commit.sample .git/hooks/pre-commit +    chmod -c +x  .git/hooks/pre-commit +fi +  (autoconf --version) < /dev/null > /dev/null 2>&1 || {  	echo  	echo "You must have autoconf installed to compile $PROJECT." @@ -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", @@ -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 007b4e5d..a4a01914 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/desktop-file.c b/bus/desktop-file.c index 754a83c3..9f88d6e7 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;  } diff --git a/bus/dir-watch-inotify.c b/bus/dir-watch-inotify.c index f03e1bd7..ee580d74 100644 --- a/bus/dir-watch-inotify.c +++ b/bus/dir-watch-inotify.c @@ -24,7 +24,6 @@  #include <config.h> -#define _GNU_SOURCE  #include <stdlib.h>  #include <unistd.h>  #include <fcntl.h> diff --git a/bus/dispatch.c b/bus/dispatch.c index ae6971de..a69a929c 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -35,6 +35,11 @@  #include <dbus/dbus-internals.h>  #include <string.h> +#ifdef HAVE_UNIX_FD_PASSING +#include <dbus/dbus-sysdeps-unix.h> +#include <unistd.h> +#endif +  static dbus_bool_t  send_one_message (DBusConnection *connection,                    BusContext     *context, @@ -51,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, @@ -295,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)) @@ -4716,4 +4735,155 @@ 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_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"); + +  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 */ @@ -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); diff --git a/bus/session.conf.in b/bus/session.conf.in index e7229ad5..85395c52 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -45,8 +45,11 @@    <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max -->    <limit name="max_incoming_bytes">1000000000</limit> +  <limit name="max_incoming_unix_fds">250000000</limit>    <limit name="max_outgoing_bytes">1000000000</limit> +  <limit name="max_outgoing_unix_fds">250000000</limit>    <limit name="max_message_size">1000000000</limit> +  <limit name="max_message_unix_fds">4096</limit>    <limit name="service_start_timeout">120000</limit>      <limit name="auth_timeout">240000</limit>    <limit name="max_completed_connections">100000</limit>   diff --git a/bus/test-main.c b/bus/test-main.c index f19d0d55..000bdd5a 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -27,6 +27,7 @@  #include <dbus/dbus-string.h>  #include <dbus/dbus-sysdeps.h>  #include <dbus/dbus-internals.h> +#include <dbus/dbus-message-private.h>  #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]); @@ -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 diff --git a/configure.in b/configure.in index 33faec9f..c011b4a7 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,7 @@ AC_SUBST(DBUS_VERSION)  AC_PROG_CC  AM_PROG_CC_C_O  AC_PROG_CXX +AC_USE_SYSTEM_EXTENSIONS  AC_ISC_POSIX  AC_HEADER_STDC  AC_C_INLINE @@ -349,41 +350,23 @@ if test "x$dbus_cv_va_val_copy" = "xno"; then  fi -#### Atomic integers (checks by Sebastian Wilhelmi for GLib) -AC_MSG_CHECKING([whether to use inline assembler routines for atomic integers]) -have_atomic_inc_cond=0 -if test x"$GCC" = xyes; then -  if test "x$enable_ansi" = "xyes"; then -    AC_MSG_RESULT([no]) -  else -    case $host_cpu in -      i386) -        AC_MSG_RESULT([no]) -        ;; -      i?86) -        case $host_os in -          darwin*) -            AC_MSG_RESULT([darwin]) -            # check at compile-time, so that it is possible to build universal -            # (with multiple architectures at once on the compile line) -            have_atomic_inc_cond="(defined(__i386__) || defined(__x86_64__))" -            ;; -          *) -            AC_MSG_RESULT([i486]) -            have_atomic_inc_cond=1 -            ;; -        esac -        ;; -      *) -        AC_MSG_RESULT([no]) -        ;; -    esac -  fi +#### Atomic integers + +AC_CACHE_CHECK([whether $CC knows __sync_sub_and_fetch()], +  dbus_cv_sync_sub_and_fetch, +  [AC_LINK_IFELSE( +     AC_LANG_PROGRAM([], [[int a = 4; int b = __sync_sub_and_fetch(&a, 4);]]), +     [dbus_cv_sync_sub_and_fetch=yes], +     [dbus_cv_sync_sub_and_fetch=no]) +  ]) + +if test "x$dbus_cv_sync_sub_and_fetch" = "xyes" ; then +   have_sync=1 +else +   have_sync=0  fi -AC_DEFINE_UNQUOTED([DBUS_USE_ATOMIC_INT_486_COND], [$have_atomic_inc_cond], -                   [Always defined; expands to 1 if we should use atomic integer implementation for 486, else 0]) -AC_DEFINE_UNQUOTED(DBUS_HAVE_ATOMIC_INT_COND, [$have_atomic_inc_cond], -                   [Always defined; expands to 1 if we have an atomic integer implementation, else 0]) + +AC_DEFINE_UNQUOTED([DBUS_USE_SYNC], [$have_sync], [Use the gcc __sync extension])  #### Various functions  AC_SEARCH_LIBS(socket,[socket network]) @@ -456,6 +439,8 @@ AC_CHECK_HEADERS(execinfo.h, [AC_CHECK_FUNCS(backtrace)])  AC_CHECK_HEADERS(errno.h) +AC_CHECK_HEADERS(byteswap.h) +  AC_CHECK_HEADERS(unistd.h)  # Add -D_POSIX_PTHREAD_SEMANTICS if on Solaris @@ -536,6 +521,9 @@ AC_CHECK_HEADERS(sys/uio.h, [AC_CHECK_FUNCS(writev)])  dnl needed on darwin for NAME_MAX  AC_CHECK_HEADERS(sys/syslimits.h) +dnl Make it easy to check if we have MSG_NOSIGNAL without actually having to include sys/socket.h +AC_CHECK_DECLS([MSG_NOSIGNAL], [], [], [[ #include <sys/socket.h> ]]) +  dnl check for flavours of varargs macros (test from GLib)  AC_MSG_CHECKING(for ISO C99 varargs macros in C)  AC_TRY_COMPILE([],[ @@ -579,6 +567,8 @@ fi  AC_CHECK_FUNCS(getpeerucred getpeereid) +AC_CHECK_FUNCS(pipe2 accept4) +  #### Abstract sockets  if test x$enable_abstract_sockets = xauto; then @@ -881,6 +871,16 @@ else     AC_MSG_RESULT(no)  fi +# Check for SCM_RIGHTS +AC_MSG_CHECKING([for SCM_RIGHTS]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <sys/socket.h> +#include <sys/un.h> +static int x = SCM_RIGHTS; +]], [[]])], +[ AC_MSG_RESULT([supported]) +  AC_DEFINE([HAVE_UNIX_FD_PASSING], [1], [Supports sending UNIX file descriptors]) ], +[ AC_MSG_RESULT([not supported]) ])  #### Set up final flags  DBUS_CLIENT_CFLAGS= diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index b81c7f25..1b4792de 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -122,7 +122,9 @@ typedef enum {    DBUS_AUTH_COMMAND_REJECTED,    DBUS_AUTH_COMMAND_OK,    DBUS_AUTH_COMMAND_ERROR, -  DBUS_AUTH_COMMAND_UNKNOWN +  DBUS_AUTH_COMMAND_UNKNOWN, +  DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD, +  DBUS_AUTH_COMMAND_AGREE_UNIX_FD  } DBusAuthCommand;  /** @@ -184,6 +186,9 @@ struct DBusAuth    unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */    unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */    unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */ + +  unsigned int unix_fd_possible : 1;  /**< This side could do unix fd passing */ +  unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */  };  /** @@ -223,9 +228,10 @@ static dbus_bool_t send_rejected             (DBusAuth *auth);  static dbus_bool_t send_error                (DBusAuth *auth,                                                const char *message);  static dbus_bool_t send_ok                   (DBusAuth *auth); -static dbus_bool_t send_begin                (DBusAuth *auth, -                                              const DBusString *args_from_ok); +static dbus_bool_t send_begin                (DBusAuth *auth);  static dbus_bool_t send_cancel               (DBusAuth *auth); +static dbus_bool_t send_negotiate_unix_fd    (DBusAuth *auth); +static dbus_bool_t send_agree_unix_fd        (DBusAuth *auth);  /**   * Client states @@ -264,6 +270,9 @@ static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *aut  static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,                                                             DBusAuthCommand   command,                                                             const DBusString *args); +static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth         *auth, +                                                           DBusAuthCommand   command, +                                                           const DBusString *args);  static const DBusAuthStateData client_state_need_send_auth = {    "NeedSendAuth", NULL @@ -277,7 +286,10 @@ static const DBusAuthStateData client_state_waiting_for_ok = {  static const DBusAuthStateData client_state_waiting_for_reject = {    "WaitingForReject", handle_client_state_waiting_for_reject  }; -   +static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = { +  "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd +}; +  /**   * Common terminal states.  Terminal states have handler == NULL.   */ @@ -1522,9 +1534,21 @@ send_ok (DBusAuth *auth)  }  static dbus_bool_t -send_begin (DBusAuth         *auth, -            const DBusString *args_from_ok) +send_begin (DBusAuth         *auth)  { + +  if (!_dbus_string_append (&auth->outgoing, +                            "BEGIN\r\n")) +    return FALSE; + +  goto_state (auth, &common_state_authenticated); +  return TRUE; +} + +static dbus_bool_t +process_ok(DBusAuth *auth, +          const DBusString *args_from_ok) { +    int end_of_hex;    /* "args_from_ok" should be the GUID, whitespace already pulled off the front */ @@ -1549,20 +1573,19 @@ send_begin (DBusAuth         *auth,        return TRUE;      } -  if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) && -      _dbus_string_append (&auth->outgoing, "BEGIN\r\n")) -    { -      _dbus_verbose ("Got GUID '%s' from the server\n", -                     _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); -       -      goto_state (auth, &common_state_authenticated); -      return TRUE; -    } -  else -    { +  if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {        _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);        return FALSE; -    } +  } + +  _dbus_verbose ("Got GUID '%s' from the server\n", +                 _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); + +  if (auth->unix_fd_possible) +    return send_negotiate_unix_fd(auth); + +  _dbus_verbose("Not negotiating unix fd passing, since not possible\n"); +  return send_begin (auth);  }  static dbus_bool_t @@ -1622,6 +1645,33 @@ process_data (DBusAuth             *auth,  }  static dbus_bool_t +send_negotiate_unix_fd (DBusAuth *auth) +{ +  if (!_dbus_string_append (&auth->outgoing, +                            "NEGOTIATE_UNIX_FD\r\n")) +    return FALSE; + +  goto_state (auth, &client_state_waiting_for_agree_unix_fd); +  return TRUE; +} + +static dbus_bool_t +send_agree_unix_fd (DBusAuth *auth) +{ +  _dbus_assert(auth->unix_fd_possible); + +  auth->unix_fd_negotiated = TRUE; +  _dbus_verbose("Agreed to UNIX FD passing\n"); + +  if (!_dbus_string_append (&auth->outgoing, +                            "AGREE_UNIX_FD\r\n")) +    return FALSE; + +  goto_state (auth, &server_state_waiting_for_begin); +  return TRUE; +} + +static dbus_bool_t  handle_auth (DBusAuth *auth, const DBusString *args)  {    if (_dbus_string_get_length (args) == 0) @@ -1712,9 +1762,13 @@ handle_server_state_waiting_for_auth  (DBusAuth         *auth,      case DBUS_AUTH_COMMAND_ERROR:        return send_rejected (auth); +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +      return send_error (auth, "Need to authenticate first"); +      case DBUS_AUTH_COMMAND_REJECTED:      case DBUS_AUTH_COMMAND_OK:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        return send_error (auth, "Unknown command");      } @@ -1741,9 +1795,13 @@ handle_server_state_waiting_for_data  (DBusAuth         *auth,        goto_state (auth, &common_state_need_disconnect);        return TRUE; +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +      return send_error (auth, "Need to authenticate first"); +      case DBUS_AUTH_COMMAND_REJECTED:      case DBUS_AUTH_COMMAND_OK:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        return send_error (auth, "Unknown command");      } @@ -1766,9 +1824,16 @@ handle_server_state_waiting_for_begin (DBusAuth         *auth,        goto_state (auth, &common_state_authenticated);        return TRUE; +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +      if (auth->unix_fd_possible) +        return send_agree_unix_fd(auth); +      else +        return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible"); +      case DBUS_AUTH_COMMAND_REJECTED:      case DBUS_AUTH_COMMAND_OK:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        return send_error (auth, "Unknown command"); @@ -1933,7 +1998,7 @@ handle_client_state_waiting_for_data (DBusAuth         *auth,        return process_rejected (auth, args);      case DBUS_AUTH_COMMAND_OK: -      return send_begin (auth, args); +      return process_ok(auth, args);      case DBUS_AUTH_COMMAND_ERROR:        return send_cancel (auth); @@ -1942,6 +2007,8 @@ handle_client_state_waiting_for_data (DBusAuth         *auth,      case DBUS_AUTH_COMMAND_CANCEL:      case DBUS_AUTH_COMMAND_BEGIN:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        return send_error (auth, "Unknown command");      } @@ -1958,7 +2025,7 @@ handle_client_state_waiting_for_ok (DBusAuth         *auth,        return process_rejected (auth, args);      case DBUS_AUTH_COMMAND_OK: -      return send_begin (auth, args); +      return process_ok(auth, args);      case DBUS_AUTH_COMMAND_DATA:      case DBUS_AUTH_COMMAND_ERROR: @@ -1968,6 +2035,8 @@ handle_client_state_waiting_for_ok (DBusAuth         *auth,      case DBUS_AUTH_COMMAND_CANCEL:      case DBUS_AUTH_COMMAND_BEGIN:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        return send_error (auth, "Unknown command");      } @@ -1990,12 +2059,46 @@ handle_client_state_waiting_for_reject (DBusAuth         *auth,      case DBUS_AUTH_COMMAND_OK:      case DBUS_AUTH_COMMAND_ERROR:      case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:      default:        goto_state (auth, &common_state_need_disconnect);        return TRUE;      }  } +static dbus_bool_t +handle_client_state_waiting_for_agree_unix_fd(DBusAuth         *auth, +                                              DBusAuthCommand   command, +                                              const DBusString *args) +{ +  switch (command) +    { +    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: +      _dbus_assert(auth->unix_fd_possible); +      auth->unix_fd_negotiated = TRUE; +      _dbus_verbose("Sucessfully negotiated UNIX FD passing\n"); +      return send_begin (auth); + +    case DBUS_AUTH_COMMAND_ERROR: +      _dbus_assert(auth->unix_fd_possible); +      auth->unix_fd_negotiated = FALSE; +      _dbus_verbose("Failed to negotiate UNIX FD passing\n"); +      return send_begin (auth); + +    case DBUS_AUTH_COMMAND_OK: +    case DBUS_AUTH_COMMAND_DATA: +    case DBUS_AUTH_COMMAND_REJECTED: +    case DBUS_AUTH_COMMAND_AUTH: +    case DBUS_AUTH_COMMAND_CANCEL: +    case DBUS_AUTH_COMMAND_BEGIN: +    case DBUS_AUTH_COMMAND_UNKNOWN: +    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: +    default: +      return send_error (auth, "Unknown command"); +    } +} +  /**   * Mapping from command name to enum   */ @@ -2005,13 +2108,15 @@ typedef struct {  } DBusAuthCommandName;  static const DBusAuthCommandName auth_command_names[] = { -  { "AUTH",     DBUS_AUTH_COMMAND_AUTH }, -  { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL }, -  { "DATA",     DBUS_AUTH_COMMAND_DATA }, -  { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN }, -  { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, -  { "OK",       DBUS_AUTH_COMMAND_OK }, -  { "ERROR",    DBUS_AUTH_COMMAND_ERROR } +  { "AUTH",              DBUS_AUTH_COMMAND_AUTH }, +  { "CANCEL",            DBUS_AUTH_COMMAND_CANCEL }, +  { "DATA",              DBUS_AUTH_COMMAND_DATA }, +  { "BEGIN",             DBUS_AUTH_COMMAND_BEGIN }, +  { "REJECTED",          DBUS_AUTH_COMMAND_REJECTED }, +  { "OK",                DBUS_AUTH_COMMAND_OK }, +  { "ERROR",             DBUS_AUTH_COMMAND_ERROR }, +  { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD }, +  { "AGREE_UNIX_FD",     DBUS_AUTH_COMMAND_AGREE_UNIX_FD }  };  static DBusAuthCommand @@ -2685,6 +2790,31 @@ _dbus_auth_set_context (DBusAuth               *auth,                                     &auth->context, 0, _dbus_string_get_length (context));  } +/** + * Sets whether unix fd passing is potentially on the transport and + * hence shall be negotiated. + * + * @param auth the auth conversation + * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE + */ +void +_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b) +{ +  auth->unix_fd_possible = b; +} + +/** + * Queries whether unix fd passing was sucessfully negotiated. + * + * @param auth the auth conversion + * @returns #TRUE when unix fd passing was negotiated. + */ +dbus_bool_t +_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth) +{ +  return auth->unix_fd_negotiated; +} +  /** @} */  /* tests in dbus-auth-util.c */ diff --git a/dbus/dbus-auth.h b/dbus/dbus-auth.h index 92150ad6..ae3f3647 100644 --- a/dbus/dbus-auth.h +++ b/dbus/dbus-auth.h @@ -75,6 +75,8 @@ dbus_bool_t   _dbus_auth_set_context         (DBusAuth               *auth,                                                const DBusString       *context);  const char*   _dbus_auth_get_guid_from_server(DBusAuth               *auth); +void          _dbus_auth_set_unix_fd_possible(DBusAuth               *auth, dbus_bool_t b); +dbus_bool_t   _dbus_auth_get_unix_fd_negotiated(DBusAuth             *auth);  DBUS_END_DECLS diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index c933d7d1..a59b1a0c 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -33,6 +33,7 @@  #include "dbus-list.h"  #include "dbus-hash.h"  #include "dbus-message-internal.h" +#include "dbus-message-private.h"  #include "dbus-threads.h"  #include "dbus-protocol.h"  #include "dbus-dataslot.h" @@ -41,6 +42,7 @@  #include "dbus-object-tree.h"  #include "dbus-threads-internal.h"  #include "dbus-bus.h" +#include "dbus-marshal-basic.h"  #ifdef DBUS_DISABLE_CHECKS  #define TOOK_LOCK_CHECK(connection) @@ -223,7 +225,11 @@ struct DBusPreallocatedSend    DBusList *counter_link;     /**< Preallocated link in the resource counter */  }; +#ifdef HAVE_DECL_MSG_NOSIGNAL +static dbus_bool_t _dbus_modify_sigpipe = FALSE; +#else  static dbus_bool_t _dbus_modify_sigpipe = TRUE; +#endif  /**   * Implementation details of DBusConnection. All fields are private. @@ -621,8 +627,8 @@ _dbus_connection_message_sent (DBusConnection *connection,                   connection, connection->n_outgoing);    /* Save this link in the link cache also */ -  _dbus_message_remove_size_counter (message, connection->outgoing_counter, -                                     &link); +  _dbus_message_remove_counter (message, connection->outgoing_counter, +                                &link);    _dbus_list_prepend_link (&connection->link_cache, link);    dbus_message_unref (message); @@ -1929,8 +1935,8 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection       *con    _dbus_list_prepend_link (&connection->outgoing_messages,                             preallocated->queue_link); -  _dbus_message_add_size_counter_link (message, -                                       preallocated->counter_link); +  _dbus_message_add_counter_link (message, +                                  preallocated->counter_link);    dbus_free (preallocated);    preallocated = NULL; @@ -2575,9 +2581,9 @@ free_outgoing_message (void *element,    DBusMessage *message = element;    DBusConnection *connection = data; -  _dbus_message_remove_size_counter (message, -                                     connection->outgoing_counter, -                                     NULL); +  _dbus_message_remove_counter (message, +                                connection->outgoing_counter, +                                NULL);    dbus_message_unref (message);  } @@ -2970,15 +2976,59 @@ dbus_connection_get_server_id (DBusConnection *connection)    char *id;    _dbus_return_val_if_fail (connection != NULL, NULL); -   +    CONNECTION_LOCK (connection);    id = _dbus_strdup (_dbus_transport_get_server_id (connection->transport));    CONNECTION_UNLOCK (connection); -   +    return id;  }  /** + * Tests whether a certain type can be send via the connection. This + * will always return TRUE for all types, with the exception of + * DBUS_TYPE_UNIX_FD. The function will return TRUE for + * DBUS_TYPE_UNIX_FD only on systems that know Unix file descriptors + * and can send them via the chosen transport and when the remote side + * supports this. + * + * This function can be used to do runtime checking for types that + * might be unknown to the specific D-Bus client implementation + * version, i.e. it will return FALSE for all types this + * implementation does not know. + * + * @param connection the connection + * @param type the type to check + * @returns TRUE if the type may be send via the connection + */ +dbus_bool_t +dbus_connection_can_send_type(DBusConnection *connection, +                                  int type) +{ +  _dbus_return_val_if_fail (connection != NULL, FALSE); + +  if (!_dbus_type_is_valid(type)) +    return FALSE; + +  if (type != DBUS_TYPE_UNIX_FD) +    return TRUE; + +#ifdef HAVE_UNIX_FD_PASSING +  { +    dbus_bool_t b; + +    CONNECTION_LOCK(connection); +    b = _dbus_transport_can_pass_unix_fd(connection->transport); +    CONNECTION_UNLOCK(connection); + +    return b; +  } +#endif + +  return FALSE; +} + +/**   * Set whether _exit() should be called when the connection receives a   * disconnect signal. The call to _exit() comes after any handlers for   * the disconnect signal run; handlers can cancel the exit by calling @@ -3078,8 +3128,23 @@ dbus_connection_send_preallocated (DBusConnection       *connection,    _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL ||                          (dbus_message_get_interface (message) != NULL &&                           dbus_message_get_member (message) != NULL)); -   +    CONNECTION_LOCK (connection); + +#ifdef HAVE_UNIX_FD_PASSING + +  if (!_dbus_transport_can_pass_unix_fd(connection->transport) && +      message->n_unix_fds > 0) +    { +      /* Refuse to send fds on a connection that cannot handle +         them. Unfortunately we cannot return a proper error here, so +         the best we can is just return. */ +      CONNECTION_UNLOCK (connection); +      return; +    } + +#endif +    _dbus_connection_send_preallocated_and_unlock (connection,  						 preallocated,  						 message, client_serial); @@ -3143,6 +3208,20 @@ dbus_connection_send (DBusConnection *connection,    CONNECTION_LOCK (connection); +#ifdef HAVE_UNIX_FD_PASSING + +  if (!_dbus_transport_can_pass_unix_fd(connection->transport) && +      message->n_unix_fds > 0) +    { +      /* Refuse to send fds on a connection that cannot handle +         them. Unfortunately we cannot return a proper error here, so +         the best we can is just return. */ +      CONNECTION_UNLOCK (connection); +      return FALSE; +    } + +#endif +    return _dbus_connection_send_and_unlock (connection,  					   message,  					   serial); @@ -3197,12 +3276,16 @@ reply_handler_timeout (void *data)   * you want a very short or very long timeout.  If INT_MAX is passed for   * the timeout, no timeout will be set and the call will block forever.   * - * @warning if the connection is disconnected, the #DBusPendingCall - * will be set to #NULL, so be careful with this. - *  + * @warning if the connection is disconnected or you try to send Unix + * file descriptors on a connection that does not support them, the + * #DBusPendingCall will be set to #NULL, so be careful with this. + *   * @param connection the connection   * @param message the message to send - * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected + * @param pending_return return location for a #DBusPendingCall + * object, or #NULL if connection is disconnected or when you try to + * send Unix file descriptors on a connection that does not support + * them.   * @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout   * @returns #FALSE if no memory, #TRUE otherwise.   * @@ -3226,6 +3309,21 @@ dbus_connection_send_with_reply (DBusConnection     *connection,    CONNECTION_LOCK (connection); +#ifdef HAVE_UNIX_FD_PASSING + +  if (!_dbus_transport_can_pass_unix_fd(connection->transport) && +      message->n_unix_fds > 0) +    { +      /* Refuse to send fds on a connection that cannot handle +         them. Unfortunately we cannot return a proper error here, so +         the best we can do is return TRUE but leave *pending_return +         as NULL. */ +      CONNECTION_UNLOCK (connection); +      return TRUE; +    } + +#endif +     if (!_dbus_connection_get_is_connected_unlocked (connection))      {        CONNECTION_UNLOCK (connection); @@ -3334,12 +3432,26 @@ dbus_connection_send_with_reply_and_block (DBusConnection     *connection,  {    DBusMessage *reply;    DBusPendingCall *pending; -   +    _dbus_return_val_if_fail (connection != NULL, NULL);    _dbus_return_val_if_fail (message != NULL, NULL);    _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL);    _dbus_return_val_if_error_is_set (error, NULL); -   + +#ifdef HAVE_UNIX_FD_PASSING + +  CONNECTION_LOCK (connection); +  if (!_dbus_transport_can_pass_unix_fd(connection->transport) && +      message->n_unix_fds > 0) +    { +      CONNECTION_UNLOCK (connection); +      dbus_set_error(error, DBUS_ERROR_FAILED, "Cannot send file descriptors on this connection."); +      return NULL; +    } +  CONNECTION_UNLOCK (connection); + +#endif +    if (!dbus_connection_send_with_reply (connection, message,                                          &pending, timeout_milliseconds))      { @@ -5861,6 +5973,45 @@ dbus_connection_get_max_message_size (DBusConnection *connection)  }  /** + * Specifies the maximum number of unix fds a message on this + * connection is allowed to receive. Messages with more unix fds will + * result in disconnecting the connection. + * + * @param connection a #DBusConnection + * @param size maximum message unix fds the connection can receive + */ +void +dbus_connection_set_max_message_unix_fds (DBusConnection *connection, +                                          long            n) +{ +  _dbus_return_if_fail (connection != NULL); + +  CONNECTION_LOCK (connection); +  _dbus_transport_set_max_message_unix_fds (connection->transport, +                                            n); +  CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_message_unix_fds(). + * + * @param connection the connection + * @returns the max numer of unix fds of a single message + */ +long +dbus_connection_get_max_message_unix_fds (DBusConnection *connection) +{ +  long res; + +  _dbus_return_val_if_fail (connection != NULL, 0); + +  CONNECTION_LOCK (connection); +  res = _dbus_transport_get_max_message_unix_fds (connection->transport); +  CONNECTION_UNLOCK (connection); +  return res; +} + +/**   * Sets the maximum total number of bytes that can be used for all messages   * received on this connection. Messages count toward the maximum until   * they are finalized. When the maximum is reached, the connection will @@ -5917,6 +6068,48 @@ dbus_connection_get_max_received_size (DBusConnection *connection)  }  /** + * Sets the maximum total number of unix fds that can be used for all messages + * received on this connection. Messages count toward the maximum until + * they are finalized. When the maximum is reached, the connection will + * not read more data until some messages are finalized. + * + * The semantics are analogous to those of dbus_connection_set_max_received_size(). + * + * @param connection the connection + * @param size the maximum size in bytes of all outstanding messages + */ +void +dbus_connection_set_max_received_unix_fds (DBusConnection *connection, +                                           long            n) +{ +  _dbus_return_if_fail (connection != NULL); + +  CONNECTION_LOCK (connection); +  _dbus_transport_set_max_received_unix_fds (connection->transport, +                                             n); +  CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_received_unix_fds(). + * + * @param connection the connection + * @returns the max unix fds of all live messages + */ +long +dbus_connection_get_max_received_unix_fds (DBusConnection *connection) +{ +  long res; + +  _dbus_return_val_if_fail (connection != NULL, 0); + +  CONNECTION_LOCK (connection); +  res = _dbus_transport_get_max_received_unix_fds (connection->transport); +  CONNECTION_UNLOCK (connection); +  return res; +} + +/**   * Gets the approximate size in bytes of all messages in the outgoing   * message queue. The size is approximate in that you shouldn't use   * it to decide how many bytes to read off the network or anything @@ -5932,9 +6125,29 @@ dbus_connection_get_outgoing_size (DBusConnection *connection)    long res;    _dbus_return_val_if_fail (connection != NULL, 0); -   + +  CONNECTION_LOCK (connection); +  res = _dbus_counter_get_size_value (connection->outgoing_counter); +  CONNECTION_UNLOCK (connection); +  return res; +} + +/** + * Gets the approximate number of uni fds of all messages in the + * outgoing message queue. + * + * @param connection the connection + * @returns the number of unix fds that have been queued up but not sent + */ +long +dbus_connection_get_outgoing_unix_fds (DBusConnection *connection) +{ +  long res; + +  _dbus_return_val_if_fail (connection != NULL, 0); +    CONNECTION_LOCK (connection); -  res = _dbus_counter_get_value (connection->outgoing_counter); +  res = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);    CONNECTION_UNLOCK (connection);    return res;  } diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index 29a2f5e2..ede3dcd1 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -180,6 +180,8 @@ dbus_bool_t        dbus_connection_get_is_connected             (DBusConnection  dbus_bool_t        dbus_connection_get_is_authenticated         (DBusConnection             *connection);  dbus_bool_t        dbus_connection_get_is_anonymous             (DBusConnection             *connection);  char*              dbus_connection_get_server_id                (DBusConnection             *connection); +dbus_bool_t        dbus_connection_can_send_type                (DBusConnection             *connection, +                                                                 int                         type);  void               dbus_connection_set_exit_on_disconnect       (DBusConnection             *connection,                                                                   dbus_bool_t                 exit_on_disconnect);  void               dbus_connection_flush                        (DBusConnection             *connection); @@ -279,7 +281,16 @@ long dbus_connection_get_max_message_size  (DBusConnection *connection);  void dbus_connection_set_max_received_size (DBusConnection *connection,                                              long            size);  long dbus_connection_get_max_received_size (DBusConnection *connection); + +void dbus_connection_set_max_message_unix_fds (DBusConnection *connection, +                                               long            n); +long dbus_connection_get_max_message_unix_fds (DBusConnection *connection); +void dbus_connection_set_max_received_unix_fds(DBusConnection *connection, +                                               long            n); +long dbus_connection_get_max_received_unix_fds(DBusConnection *connection); +  long dbus_connection_get_outgoing_size     (DBusConnection *connection); +long dbus_connection_get_outgoing_unix_fds (DBusConnection *connection);  DBusPreallocatedSend* dbus_connection_preallocate_send       (DBusConnection       *connection);  void                  dbus_connection_free_preallocated_send (DBusConnection       *connection, diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 54c73fca..faa24294 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -294,18 +294,23 @@ _DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots);  _DBUS_DECLARE_GLOBAL_LOCK (server_slots);  _DBUS_DECLARE_GLOBAL_LOCK (message_slots);  /* 5-10 */ -_DBUS_DECLARE_GLOBAL_LOCK (atomic);  _DBUS_DECLARE_GLOBAL_LOCK (bus);  _DBUS_DECLARE_GLOBAL_LOCK (bus_datas);  _DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs);  _DBUS_DECLARE_GLOBAL_LOCK (system_users); -/* 10-15 */  _DBUS_DECLARE_GLOBAL_LOCK (message_cache); +/* 10-14 */  _DBUS_DECLARE_GLOBAL_LOCK (shared_connections);  _DBUS_DECLARE_GLOBAL_LOCK (win_fds);  _DBUS_DECLARE_GLOBAL_LOCK (sid_atom_cache);  _DBUS_DECLARE_GLOBAL_LOCK (machine_uuid); + +#if !DBUS_USE_SYNC +_DBUS_DECLARE_GLOBAL_LOCK (atomic);  #define _DBUS_N_GLOBAL_LOCKS (15) +#else +#define _DBUS_N_GLOBAL_LOCKS (14) +#endif  dbus_bool_t _dbus_threads_init_debug (void); diff --git a/dbus/dbus-marshal-basic.c b/dbus/dbus-marshal-basic.c index 52308cbe..00551354 100644 --- a/dbus/dbus-marshal-basic.c +++ b/dbus/dbus-marshal-basic.c @@ -414,6 +414,7 @@ _dbus_marshal_set_basic (DBusString       *str,      case DBUS_TYPE_BOOLEAN:      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32: +    case DBUS_TYPE_UNIX_FD:        pos = _DBUS_ALIGN_VALUE (pos, 4);        set_4_octets (str, pos, vp->u32, byte_order);        if (old_end_pos) @@ -540,6 +541,7 @@ _dbus_marshal_read_basic (const DBusString      *str,      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32:      case DBUS_TYPE_BOOLEAN: +    case DBUS_TYPE_UNIX_FD:        {        volatile dbus_uint32_t *vp = value;        pos = _DBUS_ALIGN_VALUE (pos, 4); @@ -839,6 +841,7 @@ _dbus_marshal_write_basic (DBusString *str,        break;      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32: +    case DBUS_TYPE_UNIX_FD:        return marshal_4_octets (str, insert_at, vp->u32,                                 byte_order, pos_after);        break; @@ -1066,6 +1069,7 @@ _dbus_marshal_write_fixed_multi (DBusString *str,      case DBUS_TYPE_BOOLEAN:      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32: +    case DBUS_TYPE_UNIX_FD:        return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 4, pos_after);        break;      case DBUS_TYPE_INT64: @@ -1114,6 +1118,7 @@ _dbus_marshal_skip_basic (const DBusString      *str,      case DBUS_TYPE_BOOLEAN:      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32: +    case DBUS_TYPE_UNIX_FD:        *pos = _DBUS_ALIGN_VALUE (*pos, 4);        *pos += 4;        break; @@ -1202,6 +1207,7 @@ _dbus_type_get_alignment (int typecode)      case DBUS_TYPE_BOOLEAN:      case DBUS_TYPE_INT32:      case DBUS_TYPE_UINT32: +    case DBUS_TYPE_UNIX_FD:        /* this stuff is 4 since it starts with a length */      case DBUS_TYPE_STRING:      case DBUS_TYPE_OBJECT_PATH: @@ -1256,6 +1262,7 @@ _dbus_type_is_valid (int typecode)      case DBUS_TYPE_STRUCT:      case DBUS_TYPE_DICT_ENTRY:      case DBUS_TYPE_VARIANT: +    case DBUS_TYPE_UNIX_FD:        return TRUE;      default: @@ -1316,6 +1323,8 @@ _dbus_type_to_string (int typecode)        return "begin_dict_entry";      case DBUS_DICT_ENTRY_END_CHAR:        return "end_dict_entry"; +    case DBUS_TYPE_UNIX_FD: +      return "unix_fd";      default:        return "unknown";      } diff --git a/dbus/dbus-marshal-basic.h b/dbus/dbus-marshal-basic.h index 68ced4d2..fd87a80f 100644 --- a/dbus/dbus-marshal-basic.h +++ b/dbus/dbus-marshal-basic.h @@ -26,21 +26,27 @@  #define DBUS_MARSHAL_BASIC_H  #include <config.h> + +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#endif +  #include <dbus/dbus-protocol.h>  #include <dbus/dbus-types.h>  #include <dbus/dbus-arch-deps.h>  #include <dbus/dbus-string.h> -#ifndef PACKAGE -#error "config.h not included here" -#endif -  #ifdef WORDS_BIGENDIAN  #define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN  #else  #define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN  #endif +#ifdef HAVE_BYTESWAP_H +#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) bswap_16(val) +#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) bswap_32(val) +#else /* HAVE_BYTESWAP_H */ +  #define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val)	((dbus_uint16_t) (      \      (dbus_uint16_t) ((dbus_uint16_t) (val) >> 8) |                      \      (dbus_uint16_t) ((dbus_uint16_t) (val) << 8))) @@ -51,8 +57,14 @@      (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >>  8) |     \      (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24))) +#endif /* HAVE_BYTESWAP_H */ +  #ifdef DBUS_HAVE_INT64 +#ifdef HAVE_BYTESWAP_H +#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) bswap_64(val) +#else /* HAVE_BYTESWAP_H */ +  #define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val)	((dbus_uint64_t) (              \        (((dbus_uint64_t) (val) &                                                 \  	(dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) |    \ @@ -72,6 +84,8 @@  	(dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56)))  #endif /* DBUS_HAVE_INT64 */ +#endif /* HAVE_BYTESWAP_H */ +  #define DBUS_UINT16_SWAP_LE_BE(val) (DBUS_UINT16_SWAP_LE_BE_CONSTANT (val))  #define DBUS_INT16_SWAP_LE_BE(val)  ((dbus_int16_t)DBUS_UINT16_SWAP_LE_BE_CONSTANT (val)) diff --git a/dbus/dbus-marshal-byteswap.c b/dbus/dbus-marshal-byteswap.c index 6af5e5e4..c5e92b32 100644 --- a/dbus/dbus-marshal-byteswap.c +++ b/dbus/dbus-marshal-byteswap.c @@ -191,6 +191,11 @@ byteswap_body_helper (DBusTypeReader       *reader,            }            break; +        case DBUS_TYPE_UNIX_FD: +          /* fds can only be passed on a local machine, so byte order must always match */ +          _dbus_assert_not_reached("attempted to byteswap unix fds which makes no sense"); +          break; +          default:            _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature");            break; diff --git a/dbus/dbus-marshal-byteswap.h b/dbus/dbus-marshal-byteswap.h index 764e353d..7c8ea7b6 100644 --- a/dbus/dbus-marshal-byteswap.h +++ b/dbus/dbus-marshal-byteswap.h @@ -28,10 +28,6 @@  #include <dbus/dbus-protocol.h>  #include <dbus/dbus-marshal-recursive.h> -#ifndef PACKAGE -#error "config.h not included here" -#endif -  void _dbus_marshal_byteswap (const DBusString *signature,                               int               signature_start,                               int               old_byte_order, diff --git a/dbus/dbus-marshal-header.c b/dbus/dbus-marshal-header.c index ec98a5ee..d9e9bb51 100644 --- a/dbus/dbus-marshal-header.c +++ b/dbus/dbus-marshal-header.c @@ -81,7 +81,8 @@ _dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = {    { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 },    { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING },    { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING }, -  { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE } +  { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE }, +  { DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 }  };  /** Macro to look up the correct type for a field */ @@ -888,6 +889,10 @@ load_and_validate_field (DBusHeader     *header,          }        break; +    case DBUS_HEADER_FIELD_UNIX_FDS: +      /* Every value makes sense */ +      break; +      case DBUS_HEADER_FIELD_SIGNATURE:        /* SIGNATURE validated generically due to its type */        string_validation_func = NULL; diff --git a/dbus/dbus-marshal-header.h b/dbus/dbus-marshal-header.h index f4038630..0f36efac 100644 --- a/dbus/dbus-marshal-header.h +++ b/dbus/dbus-marshal-header.h @@ -28,10 +28,6 @@  #include <dbus/dbus-marshal-basic.h>  #include <dbus/dbus-marshal-validate.h> -#ifndef PACKAGE -#error "config.h not included here" -#endif -  typedef struct DBusHeader      DBusHeader;  typedef struct DBusHeaderField DBusHeaderField; diff --git a/dbus/dbus-marshal-recursive.h b/dbus/dbus-marshal-recursive.h index cea35350..a743668d 100644 --- a/dbus/dbus-marshal-recursive.h +++ b/dbus/dbus-marshal-recursive.h @@ -28,10 +28,6 @@  #include <dbus/dbus-protocol.h>  #include <dbus/dbus-list.h> -#ifndef PACKAGE -#error "config.h not included here" -#endif -  typedef struct DBusTypeReader      DBusTypeReader;  typedef struct DBusTypeWriter      DBusTypeWriter;  typedef struct DBusTypeReaderClass DBusTypeReaderClass; diff --git a/dbus/dbus-marshal-validate.c b/dbus/dbus-marshal-validate.c index 61fad4de..bbaa9bdf 100644 --- a/dbus/dbus-marshal-validate.c +++ b/dbus/dbus-marshal-validate.c @@ -100,6 +100,7 @@ _dbus_validate_signature_with_reason (const DBusString *type_str,          case DBUS_TYPE_UINT16:          case DBUS_TYPE_INT32:          case DBUS_TYPE_UINT32: +        case DBUS_TYPE_UNIX_FD:          case DBUS_TYPE_INT64:          case DBUS_TYPE_UINT64:          case DBUS_TYPE_DOUBLE: @@ -319,12 +320,13 @@ validate_body_helper (DBusTypeReader       *reader,          case DBUS_TYPE_BYTE:            ++p;            break; -           +          case DBUS_TYPE_BOOLEAN:          case DBUS_TYPE_INT16:          case DBUS_TYPE_UINT16:          case DBUS_TYPE_INT32:          case DBUS_TYPE_UINT32: +        case DBUS_TYPE_UNIX_FD:          case DBUS_TYPE_INT64:          case DBUS_TYPE_UINT64:          case DBUS_TYPE_DOUBLE: diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h index a7d904b2..7a623d9e 100644 --- a/dbus/dbus-marshal-validate.h +++ b/dbus/dbus-marshal-validate.h @@ -26,10 +26,6 @@  #include <config.h> -#ifndef PACKAGE -#error "config.h not included here" -#endif -  /**   * @addtogroup DBusMarshal   * @@ -117,6 +113,7 @@ typedef enum    DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS = 53,    DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY = 54,    DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE = 55, +  DBUS_INVALID_MISSING_UNIX_FDS = 56,    DBUS_VALIDITY_LAST  } DBusValidity; diff --git a/dbus/dbus-md5.c b/dbus/dbus-md5.c index 589c8d2a..8405f360 100644 --- a/dbus/dbus-md5.c +++ b/dbus/dbus-md5.c @@ -451,7 +451,7 @@ _dbus_md5_final (DBusMD5Context   *context,    /* some kind of security paranoia, though it seems pointless     * to me given the nonzeroed stuff flying around     */ -  memset ((void*)context, '\0', sizeof (DBusMD5Context)); +  _DBUS_ZERO(*context);    return TRUE;  } diff --git a/dbus/dbus-memory.h b/dbus/dbus-memory.h index d0e92d1a..aad439b8 100644 --- a/dbus/dbus-memory.h +++ b/dbus/dbus-memory.h @@ -43,8 +43,8 @@ void* dbus_realloc       (void  *memory,                            size_t bytes);  void  dbus_free          (void  *memory); -#define dbus_new(type, count)  ((type*)dbus_malloc (sizeof (type) * (count))); -#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count))); +#define dbus_new(type, count)  ((type*)dbus_malloc (sizeof (type) * (count))) +#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count)))  void dbus_free_string_array (char **str_array); diff --git a/dbus/dbus-message-factory.c b/dbus/dbus-message-factory.c index 7bc539b5..7432cf27 100644 --- a/dbus/dbus-message-factory.c +++ b/dbus/dbus-message-factory.c @@ -949,6 +949,7 @@ static const int typecodes[] = {    DBUS_STRUCT_END_CHAR,    DBUS_DICT_ENTRY_BEGIN_CHAR,    DBUS_DICT_ENTRY_END_CHAR, +  DBUS_TYPE_UNIX_FD,    255 /* random invalid typecode */  }; diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index 7cd88d56..5c616a12 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -34,14 +34,17 @@ typedef struct DBusMessageLoader DBusMessageLoader;  void _dbus_message_get_network_data  (DBusMessage       *message,  				      const DBusString **header,  				      const DBusString **body); +void _dbus_message_get_unix_fds      (DBusMessage *message, +                                      const int **fds, +                                      unsigned *n_fds);  void        _dbus_message_lock                  (DBusMessage  *message);  void        _dbus_message_unlock                (DBusMessage  *message); -dbus_bool_t _dbus_message_add_size_counter      (DBusMessage  *message, +dbus_bool_t _dbus_message_add_counter           (DBusMessage  *message,                                                   DBusCounter  *counter); -void        _dbus_message_add_size_counter_link (DBusMessage  *message, +void        _dbus_message_add_counter_link      (DBusMessage  *message,                                                   DBusList     *link); -void        _dbus_message_remove_size_counter   (DBusMessage  *message, +void        _dbus_message_remove_counter        (DBusMessage  *message,                                                   DBusCounter  *counter,                                                   DBusList    **link_return); @@ -54,6 +57,14 @@ void               _dbus_message_loader_get_buffer            (DBusMessageLoader  void               _dbus_message_loader_return_buffer         (DBusMessageLoader  *loader,                                                                 DBusString         *buffer,                                                                 int                 bytes_read); + +dbus_bool_t        _dbus_message_loader_get_unix_fds          (DBusMessageLoader  *loader, +                                                               int               **fds, +                                                               unsigned           *max_n_fds); +void               _dbus_message_loader_return_unix_fds       (DBusMessageLoader  *loader, +                                                               int                *fds, +                                                               unsigned            n_fds); +  dbus_bool_t        _dbus_message_loader_queue_messages        (DBusMessageLoader  *loader);  DBusMessage*       _dbus_message_loader_peek_message          (DBusMessageLoader  *loader);  DBusMessage*       _dbus_message_loader_pop_message           (DBusMessageLoader  *loader); @@ -67,6 +78,10 @@ void               _dbus_message_loader_set_max_message_size  (DBusMessageLoader                                                                 long                size);  long               _dbus_message_loader_get_max_message_size  (DBusMessageLoader  *loader); +void               _dbus_message_loader_set_max_message_unix_fds(DBusMessageLoader  *loader, +                                                                 long                n); +long               _dbus_message_loader_get_max_message_unix_fds(DBusMessageLoader  *loader); +  DBUS_END_DECLS  #endif /* DBUS_MESSAGE_INTERNAL_H */ diff --git a/dbus/dbus-message-private.h b/dbus/dbus-message-private.h index c8b2ba63..366c53aa 100644 --- a/dbus/dbus-message-private.h +++ b/dbus/dbus-message-private.h @@ -23,6 +23,8 @@  #ifndef DBUS_MESSAGE_PRIVATE_H  #define DBUS_MESSAGE_PRIVATE_H +#include <config.h> +  #include <dbus/dbus-message.h>  #include <dbus/dbus-message-internal.h>  #include <dbus/dbus-string.h> @@ -66,12 +68,21 @@ struct DBusMessageLoader    DBusList *messages;  /**< Complete messages. */    long max_message_size; /**< Maximum size of a message */ +  long max_message_unix_fds; /**< Maximum unix fds in a message */ -  unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ +  DBusValidity corruption_reason; /**< why we were corrupted */    unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ -  DBusValidity corruption_reason; /**< why we were corrupted */ +  unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ + +#ifdef HAVE_UNIX_FD_PASSING +  unsigned int unix_fds_outstanding : 1; /**< Someone is using the unix fd array to read */ + +  int *unix_fds; /**< File descriptors that have been read from the transport but not yet been handed to any message. Array will be allocated at first use. */ +  unsigned n_unix_fds_allocated; /**< Number of file descriptors this array has space for */ +  unsigned n_unix_fds; /**< Number of valid file descriptors in array */ +#endif  }; @@ -100,8 +111,8 @@ struct DBusMessage  #ifndef DBUS_DISABLE_CHECKS    unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */  #endif -   -  DBusList *size_counters;   /**< 0-N DBusCounter used to track message size. */ + +  DBusList *counters;   /**< 0-N DBusCounter used to track message size/unix fds. */    long size_counter_delta;   /**< Size we incremented the size counters by.   */    dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ @@ -111,6 +122,17 @@ struct DBusMessage  #ifndef DBUS_DISABLE_CHECKS    int generation; /**< _dbus_current_generation when message was created */  #endif + +#ifdef HAVE_UNIX_FD_PASSING +  int *unix_fds; +  /**< Unix file descriptors associated with this message. These are +     closed when the message is destroyed, hence make sure to dup() +     them when adding or removing them here. */ +  unsigned n_unix_fds; /**< Number of valid fds in the array */ +  unsigned n_unix_fds_allocated; /**< Allocated size of the array */ + +  long unix_fd_counter_delta; /**< Size we incremented the unix fd counter by */ +#endif  };  dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter, @@ -118,6 +140,9 @@ dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter,                                                  int              first_arg_type,                                                  va_list          var_args); + +void _dbus_check_fdleaks(void); +  /** @} */  DBUS_END_DECLS diff --git a/dbus/dbus-message-util.c b/dbus/dbus-message-util.c index 84471a50..a56fcc49 100644 --- a/dbus/dbus-message-util.c +++ b/dbus/dbus-message-util.c @@ -27,6 +27,17 @@  #include "dbus-message-private.h"  #include "dbus-marshal-recursive.h"  #include "dbus-string.h" +#ifdef HAVE_UNIX_FD_PASSING +#include "dbus-sysdeps-unix.h" +#endif + +#ifdef __linux__ +/* Necessary for the Linux-specific fd leak checking code only */ +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> +#include <errno.h> +#endif  /**   * @addtogroup DBusMessage @@ -126,6 +137,50 @@ check_memleaks (void)      }  } +void +_dbus_check_fdleaks(void) +{ + +#ifdef __linux__ + +  DIR *d; + +  /* This works on Linux only */ + +  if ((d = opendir("/proc/self/fd"))) +    { +      struct dirent *de; + +      while ((de = readdir(d))) +        { +          long l; +          char *e = NULL; +          int fd; + +          if (de->d_name[0] == '.') +            continue; + +          errno = 0; +          l = strtol(de->d_name, &e, 10); +          _dbus_assert(errno == 0 && e && !*e); + +          fd = (int) l; + +          if (fd < 3) +            continue; + +          if (fd == dirfd(d)) +            continue; + +          _dbus_warn("file descriptor %i leaked in %s.\n", fd, __FILE__); +          _dbus_assert_not_reached("fdleaks"); +        } + +      closedir(d); +    } +#endif +} +  static dbus_bool_t  check_have_valid_message (DBusMessageLoader *loader)  { @@ -895,7 +950,7 @@ verify_test_message (DBusMessage *message)  dbus_bool_t  _dbus_message_test (const char *test_data_dir)  { -  DBusMessage *message; +  DBusMessage *message, *message_without_unix_fds;    DBusMessageLoader *loader;    int i;    const char *data; @@ -940,6 +995,9 @@ _dbus_message_test (const char *test_data_dir)    unsigned char v2_BYTE;    dbus_bool_t v_BOOLEAN;    DBusMessageIter iter, array_iter, struct_iter; +#ifdef HAVE_UNIX_FD_PASSING +  int v_UNIX_FD; +#endif    message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",                                            "/org/freedesktop/TestPath", @@ -1058,6 +1116,9 @@ _dbus_message_test (const char *test_data_dir)    v_BOOLEAN = TRUE;    v_BYTE = 42;    v2_BYTE = 24; +#ifdef HAVE_UNIX_FD_PASSING +  v_UNIX_FD = 1; +#endif    dbus_message_append_args (message,                              DBUS_TYPE_INT16, &v_INT16, @@ -1091,6 +1152,7 @@ _dbus_message_test (const char *test_data_dir)                              _DBUS_N_ELEMENTS (our_boolean_array),                              DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v_ARRAY_STRING,                              _DBUS_N_ELEMENTS (our_string_array), +  			    DBUS_TYPE_INVALID);    i = 0; @@ -1125,7 +1187,16 @@ _dbus_message_test (const char *test_data_dir)    sig[i++] = DBUS_TYPE_BOOLEAN;    sig[i++] = DBUS_TYPE_ARRAY;    sig[i++] = DBUS_TYPE_STRING; -  sig[i++] = DBUS_TYPE_INVALID;   + +  message_without_unix_fds = dbus_message_copy(message); +  _dbus_assert(message_without_unix_fds); +#ifdef HAVE_UNIX_FD_PASSING +  dbus_message_append_args (message, +                            DBUS_TYPE_UNIX_FD, &v_UNIX_FD, +			    DBUS_TYPE_INVALID); +  sig[i++] = DBUS_TYPE_UNIX_FD; +#endif +  sig[i++] = DBUS_TYPE_INVALID;    _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); @@ -1202,6 +1273,20 @@ _dbus_message_test (const char *test_data_dir)        _dbus_message_loader_return_buffer (loader, buffer, 1);      } +#ifdef HAVE_UNIX_FD_PASSING +  { +    int *unix_fds; +    unsigned n_unix_fds; +    /* Write unix fd */ +    _dbus_message_loader_get_unix_fds(loader, &unix_fds, &n_unix_fds); +    _dbus_assert(n_unix_fds > 0); +    _dbus_assert(message->n_unix_fds == 1); +    unix_fds[0] = _dbus_dup(message->unix_fds[0], NULL); +    _dbus_assert(unix_fds[0] >= 0); +    _dbus_message_loader_return_unix_fds(loader, unix_fds, 1); +  } +#endif +    dbus_message_unref (message);    /* Now pop back the message */ @@ -1218,7 +1303,14 @@ _dbus_message_test (const char *test_data_dir)    if (dbus_message_get_reply_serial (message) != 5678)      _dbus_assert_not_reached ("reply serial fields differ"); -  verify_test_message (message); +  dbus_message_unref (message); + +  /* ovveride the serial, since it was reset by dbus_message_copy() */ +  dbus_message_set_serial(message_without_unix_fds, 8901); + +  dbus_message_lock (message_without_unix_fds); + +  verify_test_message (message_without_unix_fds);      {        /* Marshal and demarshal the message. */ @@ -1229,7 +1321,7 @@ _dbus_message_test (const char *test_data_dir)        int len = 0;        char garbage_header[DBUS_MINIMUM_HEADER_SIZE] = "xxx"; -      if (!dbus_message_marshal (message, &marshalled, &len)) +      if (!dbus_message_marshal (message_without_unix_fds, &marshalled, &len))          _dbus_assert_not_reached ("failed to marshal message");        _dbus_assert (len != 0); @@ -1268,10 +1360,11 @@ _dbus_message_test (const char *test_data_dir)        _dbus_assert (dbus_message_demarshal_bytes_needed (garbage_header, DBUS_MINIMUM_HEADER_SIZE) == -1);      } -  dbus_message_unref (message); +  dbus_message_unref (message_without_unix_fds);    _dbus_message_loader_unref (loader);    check_memleaks (); +  _dbus_check_fdleaks();    /* Check that we can abandon a container */    message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", @@ -1333,9 +1426,10 @@ _dbus_message_test (const char *test_data_dir)      print_validities_seen (FALSE);      print_validities_seen (TRUE);    } -   +    check_memleaks (); -   +  _dbus_check_fdleaks(); +    /* Now load every message in test_data_dir if we have one */    if (test_data_dir == NULL)      return TRUE; diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index b7b5afca..33732292 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -33,6 +33,10 @@  #include "dbus-memory.h"  #include "dbus-list.h"  #include "dbus-threads-internal.h" +#ifdef HAVE_UNIX_FD_PASSING +#include "dbus-sysdeps-unix.h" +#endif +  #include <string.h>  static void dbus_message_finalize (DBusMessage *message); @@ -160,6 +164,30 @@ _dbus_message_get_network_data (DBusMessage          *message,  }  /** + * Gets the unix fds to be sent over the network for this message. + * This function is guaranteed to always return the same data once a + * message is locked (with dbus_message_lock()). + * + * @param message the message. + * @param fds return location of unix fd array + * @param n_fds return number of entries in array + */ +void _dbus_message_get_unix_fds(DBusMessage *message, +                                const int  **fds, +                                unsigned    *n_fds) +{ +  _dbus_assert (message->locked); + +#ifdef HAVE_UNIX_FD_PASSING +  *fds = message->unix_fds; +  *n_fds = message->n_unix_fds; +#else +  *fds = NULL; +  *n_fds = 0; +#endif +} + +/**   * Sets the serial number of a message.   * This can only be done once on a message.   * @@ -181,20 +209,19 @@ dbus_message_set_serial (DBusMessage   *message,  }  /** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. - * The link contains a counter with its refcount already - * incremented, but the counter itself not incremented. - * Ownership of link and counter refcount is passed to - * the message. + * Adds a counter to be incremented immediately with the size/unix fds + * of this message, and decremented by the size/unix fds of this + * message when this message if finalized.  The link contains a + * counter with its refcount already incremented, but the counter + * itself not incremented.  Ownership of link and counter refcount is + * passed to the message.   *   * @param message the message   * @param link link with counter as data   */  void -_dbus_message_add_size_counter_link (DBusMessage  *message, -                                     DBusList     *link) +_dbus_message_add_counter_link (DBusMessage  *message, +                                DBusList     *link)  {    /* right now we don't recompute the delta when message     * size changes, and that's OK for current purposes @@ -202,35 +229,43 @@ _dbus_message_add_size_counter_link (DBusMessage  *message,     * Do recompute it whenever there are no outstanding counters,     * since it's basically free.     */ -  if (message->size_counters == NULL) +  if (message->counters == NULL)      {        message->size_counter_delta =          _dbus_string_get_length (&message->header.data) +          _dbus_string_get_length (&message->body); +#ifdef HAVE_UNIX_FD_PASSING +      message->unix_fd_counter_delta = message->n_unix_fds; +#endif +  #if 0        _dbus_verbose ("message has size %ld\n",                       message->size_counter_delta);  #endif      } -  _dbus_list_append_link (&message->size_counters, link); +  _dbus_list_append_link (&message->counters, link); + +  _dbus_counter_adjust_size (link->data, message->size_counter_delta); -  _dbus_counter_adjust (link->data, message->size_counter_delta); +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_counter_adjust_unix_fd (link->data, message->unix_fd_counter_delta); +#endif  }  /** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. + * Adds a counter to be incremented immediately with the size/unix fds + * of this message, and decremented by the size/unix fds of this + * message when this message if finalized.   *   * @param message the message   * @param counter the counter   * @returns #FALSE if no memory   */  dbus_bool_t -_dbus_message_add_size_counter (DBusMessage *message, -                                DBusCounter *counter) +_dbus_message_add_counter (DBusMessage *message, +                           DBusCounter *counter)  {    DBusList *link; @@ -239,38 +274,42 @@ _dbus_message_add_size_counter (DBusMessage *message,      return FALSE;    _dbus_counter_ref (counter); -  _dbus_message_add_size_counter_link (message, link); +  _dbus_message_add_counter_link (message, link);    return TRUE;  }  /** - * Removes a counter tracking the size of this message, and decrements - * the counter by the size of this message. + * Removes a counter tracking the size/unix fds of this message, and + * decrements the counter by the size/unix fds of this message.   *   * @param message the message   * @param link_return return the link used   * @param counter the counter   */  void -_dbus_message_remove_size_counter (DBusMessage  *message, -                                   DBusCounter  *counter, -                                   DBusList    **link_return) +_dbus_message_remove_counter (DBusMessage  *message, +                              DBusCounter  *counter, +                              DBusList    **link_return)  {    DBusList *link; -  link = _dbus_list_find_last (&message->size_counters, +  link = _dbus_list_find_last (&message->counters,                                 counter);    _dbus_assert (link != NULL); -  _dbus_list_unlink (&message->size_counters, +  _dbus_list_unlink (&message->counters,                       link);    if (link_return)      *link_return = link;    else      _dbus_list_free_link (link); -  _dbus_counter_adjust (counter, - message->size_counter_delta); +  _dbus_counter_adjust_size (counter, - message->size_counter_delta); + +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); +#endif    _dbus_counter_unref (counter);  } @@ -487,21 +526,51 @@ dbus_message_get_cached (void)    _dbus_assert (message != NULL);    _dbus_assert (message->refcount.value == 0); -  _dbus_assert (message->size_counters == NULL); +  _dbus_assert (message->counters == NULL);    _DBUS_UNLOCK (message_cache);    return message;  } +#ifdef HAVE_UNIX_FD_PASSING +static void +close_unix_fds(int *fds, unsigned *n_fds) +{ +  DBusError e; +  int i; + +  if (*n_fds <= 0) +    return; + +  dbus_error_init(&e); + +  for (i = 0; i < *n_fds; i++) +    { +      if (!_dbus_close(fds[i], &e)) +        { +          _dbus_warn("Failed to close file descriptor: %s\n", e.message); +          dbus_error_free(&e); +        } +    } + +  *n_fds = 0; + +  /* We don't free the array here, in case we can recycle it later */ +} +#endif +  static void -free_size_counter (void *element, -                   void *data) +free_counter (void *element, +              void *data)  {    DBusCounter *counter = element;    DBusMessage *message = data; -  _dbus_counter_adjust (counter, - message->size_counter_delta); +  _dbus_counter_adjust_size (counter, - message->size_counter_delta); +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); +#endif    _dbus_counter_unref (counter);  } @@ -524,9 +593,13 @@ dbus_message_cache_or_finalize (DBusMessage *message)     */    _dbus_data_slot_list_clear (&message->slot_list); -  _dbus_list_foreach (&message->size_counters, -                      free_size_counter, message); -  _dbus_list_clear (&message->size_counters); +  _dbus_list_foreach (&message->counters, +                      free_counter, message); +  _dbus_list_clear (&message->counters); + +#ifdef HAVE_UNIX_FD_PASSING +  close_unix_fds(message->unix_fds, &message->n_unix_fds); +#endif    was_cached = FALSE; @@ -634,6 +707,8 @@ _dbus_message_iter_check (DBusMessageRealIter *iter)   * dbus_message_get_args() is the place to go for complete   * documentation.   * + * @todo This may leak memory and file descriptors if parsing fails. See #21259 + *   * @see dbus_message_get_args   * @param iter the message iter   * @param error error to be filled in @@ -673,7 +748,38 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,            goto out;  	} -      if (dbus_type_is_basic (spec_type)) +      if (spec_type == DBUS_TYPE_UNIX_FD) +        { +#ifdef HAVE_UNIX_FD_PASSING +          DBusBasicValue idx; +          int *pfd, nfd; + +          pfd = va_arg (var_args, int*); +          _dbus_assert(pfd); + +          _dbus_type_reader_read_basic(&real->u.reader, &idx); + +          if (idx.u32 >= real->message->n_unix_fds) +            { +              dbus_set_error (error, DBUS_ERROR_INCONSISTENT_MESSAGE, +                              "Message refers to file descriptor at index %i," +                              "but has only %i descriptors attached.\n", +                              idx.u32, +                              real->message->n_unix_fds); +              goto out; +            } + +          if ((nfd = _dbus_dup(real->message->unix_fds[idx.u32], error)) < 0) +            goto out; + +          *pfd = nfd; +#else +          dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, +                          "Platform does not support file desciptor passing.\n"); +          goto out; +#endif +        } +      else if (dbus_type_is_basic (spec_type))          {            DBusBasicValue *ptr; @@ -707,7 +813,8 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,                goto out;              } -          if (dbus_type_is_fixed (spec_element_type)) +          if (dbus_type_is_fixed (spec_element_type) && +              element_type != DBUS_TYPE_UNIX_FD)              {                ptr = va_arg (var_args, const DBusBasicValue**);                n_elements_p = va_arg (var_args, int*); @@ -936,13 +1043,18 @@ dbus_message_finalize (DBusMessage *message)    /* This calls application callbacks! */    _dbus_data_slot_list_free (&message->slot_list); -  _dbus_list_foreach (&message->size_counters, -                      free_size_counter, message); -  _dbus_list_clear (&message->size_counters); +  _dbus_list_foreach (&message->counters, +                      free_counter, message); +  _dbus_list_clear (&message->counters);    _dbus_header_free (&message->header);    _dbus_string_free (&message->body); +#ifdef HAVE_UNIX_FD_PASSING +  close_unix_fds(message->unix_fds, &message->n_unix_fds); +  dbus_free(message->unix_fds); +#endif +    _dbus_assert (message->refcount.value == 0);    dbus_free (message); @@ -969,6 +1081,11 @@ dbus_message_new_empty_header (void)  #ifndef DBUS_DISABLE_CHECKS        message->generation = _dbus_current_generation;  #endif + +#ifdef HAVE_UNIX_FD_PASSING +      message->unix_fds = NULL; +      message->n_unix_fds_allocated = 0; +#endif      }    message->refcount.value = 1; @@ -977,10 +1094,15 @@ dbus_message_new_empty_header (void)  #ifndef DBUS_DISABLE_CHECKS    message->in_cache = FALSE;  #endif -  message->size_counters = NULL; +  message->counters = NULL;    message->size_counter_delta = 0;    message->changed_stamp = 0; +#ifdef HAVE_UNIX_FD_PASSING +  message->n_unix_fds = 0; +  message->unix_fd_counter_delta = 0; +#endif +    if (!from_cache)      _dbus_data_slot_list_init (&message->slot_list); @@ -1308,8 +1430,10 @@ dbus_message_new_error_printf (DBusMessage *reply_to,   * outgoing message queue and thus not modifiable) the new message   * will not be locked.   * + * @todo This function can't be used in programs that try to recover from OOM errors. + *   * @param message the message - * @returns the new message.or #NULL if not enough memory + * @returns the new message.or #NULL if not enough memory or Unix file descriptors (in case the message to copy includes Unix file descriptors) can be allocated.   */  DBusMessage *  dbus_message_copy (const DBusMessage *message) @@ -1347,11 +1471,36 @@ dbus_message_copy (const DBusMessage *message)  			  &retval->body, 0))      goto failed_copy; +#ifdef HAVE_UNIX_FD_PASSING +  retval->unix_fds = dbus_new(int, message->n_unix_fds); +  if (retval->unix_fds == NULL && message->n_unix_fds > 0) +    goto failed_copy; + +  retval->n_unix_fds_allocated = message->n_unix_fds; + +  for (retval->n_unix_fds = 0; +       retval->n_unix_fds < message->n_unix_fds; +       retval->n_unix_fds++) +    { +      retval->unix_fds[retval->n_unix_fds] = _dbus_dup(message->unix_fds[retval->n_unix_fds], NULL); + +      if (retval->unix_fds[retval->n_unix_fds] < 0) +        goto failed_copy; +    } + +#endif +    return retval;   failed_copy:    _dbus_header_free (&retval->header);    _dbus_string_free (&retval->body); + +#ifdef HAVE_UNIX_FD_PASSING +  close_unix_fds(retval->unix_fds, &retval->n_unix_fds); +  dbus_free(retval->unix_fds); +#endif +    dbus_free (retval);    return NULL; @@ -1429,9 +1578,10 @@ dbus_message_get_type (DBusMessage *message)   * Appends fields to a message given a variable argument list. The   * variable argument list should contain the type of each argument   * followed by the value to append. Appendable types are basic types, - * and arrays of fixed-length basic types. To append variable-length - * basic types, or any more complex value, you have to use an iterator - * rather than this function. + * and arrays of fixed-length basic types (except arrays of Unix file + * descriptors). To append variable-length basic types, or any more + * complex value, you have to use an iterator rather than this + * function.   *   * To append a basic type, specify its type code followed by the   * address of the value. For example: @@ -1446,18 +1596,22 @@ dbus_message_get_type (DBusMessage *message)   *                           DBUS_TYPE_INVALID);   * @endcode   * - * To append an array of fixed-length basic types, pass in the - * DBUS_TYPE_ARRAY typecode, the element typecode, the address of - * the array pointer, and a 32-bit integer giving the number of - * elements in the array. So for example: - * @code - * const dbus_int32_t array[] = { 1, 2, 3 }; - * const dbus_int32_t *v_ARRAY = array; - * dbus_message_append_args (message, - *                           DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, - *                           DBUS_TYPE_INVALID); + * To append an array of fixed-length basic types (except Unix file + * descriptors), pass in the DBUS_TYPE_ARRAY typecode, the element + * typecode, the address of the array pointer, and a 32-bit integer + * giving the number of elements in the array. So for example: @code + * const dbus_int32_t array[] = { 1, 2, 3 }; const dbus_int32_t + * *v_ARRAY = array; dbus_message_append_args (message, + * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, DBUS_TYPE_INVALID);   * @endcode   * + * This function does not support arrays of Unix file descriptors. If + * you need those you need to manually recurse into the array. + * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + *   * @warning in C, given "int array[]", "&array == array" (the   * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree).   * So if you're using an array instead of a pointer you have to create @@ -1555,8 +1709,9 @@ dbus_message_append_args_valist (DBusMessage *message,                                                   buf,                                                   &array))              goto failed; -           -          if (dbus_type_is_fixed (element_type)) + +          if (dbus_type_is_fixed (element_type) && +              element_type != DBUS_TYPE_UNIX_FD)              {                const DBusBasicValue **value;                int n_elements; @@ -1639,7 +1794,16 @@ dbus_message_append_args_valist (DBusMessage *message,   * signature are supported; but these are returned as allocated memory   * and must be freed with dbus_free_string_array(), while the other   * types are returned as const references. To get a string array - * pass in "char ***array_location" and "int *n_elements" + * pass in "char ***array_location" and "int *n_elements". + * + * Similar to dbus_message_get_fixed_array() this function does not + * support arrays of type DBUS_TYPE_UNIX_FD. If you need to parse + * messages with arrays of Unix file descriptors you need to recurse + * into the array manually. + * + * Unix file descriptors that are read with this function will have + * the FD_CLOEXEC flag set. If you need them without this flag set, + * make sure to unset it with fcntl().   *   * The variable argument list should contain the type of the argument   * followed by a pointer to where the value should be stored. The list @@ -1864,10 +2028,10 @@ dbus_message_iter_get_element_type (DBusMessageIter *iter)   * you won't be able to recurse further. There's no array of int32 to   * recurse into.   * - * If a container is an array of fixed-length types, it is much more - * efficient to use dbus_message_iter_get_fixed_array() to get the - * whole array in one shot, rather than individually walking over the - * array elements. + * If a container is an array of fixed-length types (except Unix file + * descriptors), it is much more efficient to use + * dbus_message_iter_get_fixed_array() to get the whole array in one + * shot, rather than individually walking over the array elements.   *   * Be sure you have somehow checked that   * dbus_message_iter_get_arg_type() matches the type you are expecting @@ -1937,17 +2101,24 @@ dbus_message_iter_get_signature (DBusMessageIter *iter)   * and for string a "const char**". The returned value is   * by reference and should not be freed.   * + * This call duplicates Unix file descriptors when reading them. It is + * your job to close them when you don't need them anymore. + * + * Unix file descriptors that are read with this function will have + * the FD_CLOEXEC flag set. If you need them without this flag set, + * make sure to unset it with fcntl(). + *   * Be sure you have somehow checked that   * dbus_message_iter_get_arg_type() matches the type you are   * expecting, or you'll crash when you try to use an integer as a   * string or something.   * - * To read any container type (array, struct, dict) you will need - * to recurse into the container with dbus_message_iter_recurse(). - * If the container is an array of fixed-length values, you can - * get all the array elements at once with - * dbus_message_iter_get_fixed_array(). Otherwise, you have to - * iterate over the container's contents one value at a time. + * To read any container type (array, struct, dict) you will need to + * recurse into the container with dbus_message_iter_recurse().  If + * the container is an array of fixed-length values (except Unix file + * descriptors), you can get all the array elements at once with + * dbus_message_iter_get_fixed_array(). Otherwise, you have to iterate + * over the container's contents one value at a time.   *    * All basic-typed values are guaranteed to fit in 8 bytes. So you can   * write code like this: @@ -1977,8 +2148,30 @@ dbus_message_iter_get_basic (DBusMessageIter  *iter,    _dbus_return_if_fail (_dbus_message_iter_check (real));    _dbus_return_if_fail (value != NULL); -  _dbus_type_reader_read_basic (&real->u.reader, -                                value); +  if (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_UNIX_FD) +    { +#ifdef HAVE_UNIX_FD_PASSING +      DBusBasicValue idx; + +      _dbus_type_reader_read_basic(&real->u.reader, &idx); + +      if (idx.u32 >= real->message->n_unix_fds) { +        /* Hmm, we cannot really signal an error here, so let's make +           sure to return an invalid fd. */ +        *((int*) value) = -1; +        return; +      } + +      *((int*) value) = _dbus_dup(real->message->unix_fds[idx.u32], NULL); +#else +      *((int*) value) = -1; +#endif +    } +  else +    { +      _dbus_type_reader_read_basic (&real->u.reader, +                                    value); +    }  }  /** @@ -2015,6 +2208,10 @@ dbus_message_iter_get_array_len (DBusMessageIter *iter)   * such as integers, bool, double. The returned block will be from the   * current position in the array until the end of the array.   * + * There is one exception here: although DBUS_TYPE_UNIX_FD is + * considered a 'fixed' type arrays of this type may not be read with + * this function. + *   * The message iter should be "in" the array (that is, you recurse into the   * array, and then you call dbus_message_iter_get_fixed_array() on the   * "sub-iterator" created by dbus_message_iter_recurse()). @@ -2051,7 +2248,7 @@ dbus_message_iter_get_fixed_array (DBusMessageIter  *iter,    _dbus_return_if_fail (_dbus_message_iter_check (real));    _dbus_return_if_fail (value != NULL);    _dbus_return_if_fail ((subtype == DBUS_TYPE_INVALID) || -                         dbus_type_is_fixed (subtype)); +                        (dbus_type_is_fixed (subtype) && subtype != DBUS_TYPE_UNIX_FD));    _dbus_type_reader_read_fixed_multi (&real->u.reader,                                        value, n_elements); @@ -2250,6 +2447,40 @@ _dbus_message_iter_append_check (DBusMessageRealIter *iter)  }  #endif /* DBUS_DISABLE_CHECKS */ +#ifdef HAVE_UNIX_FD_PASSING +static int * +expand_fd_array(DBusMessage *m, +                unsigned     n) +{ +  _dbus_assert(m); + +  /* This makes space for adding n new fds to the array and returns a +     pointer to the place were the first fd should be put. */ + +  if (m->n_unix_fds + n > m->n_unix_fds_allocated) +    { +      unsigned k; +      int *p; + +      /* Make twice as much space as necessary */ +      k = (m->n_unix_fds + n) * 2; + +      /* Allocate at least four */ +      if (k < 4) +        k = 4; + +      p = dbus_realloc(m->unix_fds, k * sizeof(int)); +      if (p == NULL) +        return NULL; + +      m->unix_fds = p; +      m->n_unix_fds_allocated = k; +    } + +  return m->unix_fds + m->n_unix_fds; +} +#endif +  /**   * Appends a basic-typed value to the message. The basic types are the   * non-container types such as integer and string. @@ -2257,6 +2488,10 @@ _dbus_message_iter_append_check (DBusMessageRealIter *iter)   * The "value" argument should be the address of a basic-typed value.   * So for string, const char**. For integer, dbus_int32_t*.   * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + *   * @todo If this fails due to lack of memory, the message is hosed and   * you have to start over building the whole message.   * @@ -2281,7 +2516,50 @@ dbus_message_iter_append_basic (DBusMessageIter *iter,    if (!_dbus_message_iter_open_signature (real))      return FALSE; -  ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); +  if (type == DBUS_TYPE_UNIX_FD) +    { +#ifdef HAVE_UNIX_FD_PASSING +      int *fds; +      dbus_uint32_t u; + +      /* First step, include the fd in the fd list of this message */ +      if (!(fds = expand_fd_array(real->message, 1))) +        return FALSE; + +      *fds = _dbus_dup(*(int*) value, NULL); +      if (*fds < 0) +        return FALSE; + +      u = real->message->n_unix_fds; + +      /* Second step, write the index to the fd */ +      if (!(ret = _dbus_type_writer_write_basic (&real->u.writer, DBUS_TYPE_UNIX_FD, &u))) { +        _dbus_close(*fds, NULL); +        return FALSE; +      } + +      real->message->n_unix_fds += 1; +      u += 1; + +      /* Final step, update the header accordingly */ +      ret = _dbus_header_set_field_basic (&real->message->header, +                                          DBUS_HEADER_FIELD_UNIX_FDS, +                                          DBUS_TYPE_UINT32, +                                          &u); + +      /* If any of these operations fail the message is +         hosed. However, no memory or fds should be leaked since what +         has been added to message has been added to the message, and +         can hence be accounted for when the message is being +         freed. */ +#else +      ret = FALSE; +#endif +    } +  else +    { +      ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); +    }    if (!_dbus_message_iter_close_signature (real))      ret = FALSE; @@ -2292,10 +2570,10 @@ dbus_message_iter_append_basic (DBusMessageIter *iter,  /**   * Appends a block of fixed-length values to an array. The   * fixed-length types are all basic types that are not string-like. So - * int32, double, bool, etc. You must call - * dbus_message_iter_open_container() to open an array of values - * before calling this function. You may call this function multiple - * times (and intermixed with calls to + * int32, double, bool, etc. (Unix file descriptors however are not + * supported.) You must call dbus_message_iter_open_container() to + * open an array of values before calling this function. You may call + * this function multiple times (and intermixed with calls to   * dbus_message_iter_append_basic()) for the same array.   *   * The "value" argument should be the address of the array.  So for @@ -2318,6 +2596,10 @@ dbus_message_iter_append_basic (DBusMessageIter *iter,   * @todo If this fails due to lack of memory, the message is hosed and   * you have to start over building the whole message.   * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + *   * @param iter the append iterator   * @param element_type the type of the array elements   * @param value the address of the array @@ -2335,7 +2617,7 @@ dbus_message_iter_append_fixed_array (DBusMessageIter *iter,    _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);    _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); -  _dbus_return_val_if_fail (dbus_type_is_fixed (element_type), FALSE); +  _dbus_return_val_if_fail (dbus_type_is_fixed (element_type) && element_type != DBUS_TYPE_UNIX_FD, FALSE);    _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE);    _dbus_return_val_if_fail (value != NULL, FALSE);    _dbus_return_val_if_fail (n_elements >= 0, FALSE); @@ -3337,6 +3619,20 @@ dbus_set_error_from_message (DBusError   *error,    return TRUE;  } +/** + * Checks whether a message contains unix fds + * + * @param message the message + * @returns #TRUE if the message contains unix fds + */ +dbus_bool_t +dbus_message_contains_unix_fds(DBusMessage *message) +{ +  _dbus_assert(message); + +  return message->n_unix_fds > 0; +} +  /** @} */  /** @@ -3380,6 +3676,12 @@ _dbus_message_loader_new (void)    /* this can be configured by the app, but defaults to the protocol max */    loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH; +  /* We set a very relatively conservative default here since due to how +  SCM_RIGHTS works we need to preallocate an fd array of the maximum +  number of unix fds we want to receive in advance. A +  try-and-reallocate loop is not possible. */ +  loader->max_message_unix_fds = 1024; +    if (!_dbus_string_init (&loader->data))      {        dbus_free (loader); @@ -3390,6 +3692,12 @@ _dbus_message_loader_new (void)    _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN);    _dbus_string_set_length (&loader->data, 0); +#ifdef HAVE_UNIX_FD_PASSING +  loader->unix_fds = NULL; +  loader->n_unix_fds = loader->n_unix_fds_allocated = 0; +  loader->unix_fds_outstanding = FALSE; +#endif +    return loader;  } @@ -3419,6 +3727,10 @@ _dbus_message_loader_unref (DBusMessageLoader *loader)    loader->refcount -= 1;    if (loader->refcount == 0)      { +#ifdef HAVE_UNIX_FD_PASSING +      close_unix_fds(loader->unix_fds, &loader->n_unix_fds); +      dbus_free(loader->unix_fds); +#endif        _dbus_list_foreach (&loader->messages,                            (DBusForeachFunction) dbus_message_unref,                            NULL); @@ -3478,6 +3790,81 @@ _dbus_message_loader_return_buffer (DBusMessageLoader  *loader,    loader->buffer_outstanding = FALSE;  } +/** + * Gets the buffer to use for reading unix fds from the network. + * + * This works similar to _dbus_message_loader_get_buffer() + * + * @param loader the message loader. + * @param fds the array to read fds into + * @param max_n_fds how many fds to read at most + * @return TRUE on success, FALSE on OOM + */ +dbus_bool_t +_dbus_message_loader_get_unix_fds(DBusMessageLoader  *loader, +                                  int               **fds, +                                  unsigned           *max_n_fds) +{ +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_assert (!loader->unix_fds_outstanding); + +  /* Allocate space where we can put the fds we read. We allocate +     space for max_message_unix_fds since this is an +     upper limit how many fds can be received within a single +     message. Since SCM_RIGHTS doesn't allow a reallocate+retry logic +     we are allocating the maximum possible array size right from the +     beginning. This sucks a bit, however unless SCM_RIGHTS is fixed +     there is no better way. */ + +  if (loader->n_unix_fds_allocated < loader->max_message_unix_fds) +    { +      int *a = dbus_realloc(loader->unix_fds, +                            loader->max_message_unix_fds * sizeof(loader->unix_fds[0])); + +      if (!a) +        return FALSE; + +      loader->unix_fds = a; +      loader->n_unix_fds_allocated = loader->max_message_unix_fds; +    } + +  *fds = loader->unix_fds + loader->n_unix_fds; +  *max_n_fds = loader->n_unix_fds_allocated - loader->n_unix_fds; + +  loader->unix_fds_outstanding = TRUE; +  return TRUE; +#else +  _dbus_assert_not_reached("Platform doesn't support unix fd passing"); +#endif +} + +/** + * Returns a buffer obtained from _dbus_message_loader_get_unix_fds(). + * + * This works similar to _dbus_message_loader_return_buffer() + * + * @param loader the message loader. + * @param fds the array fds were read into + * @param max_n_fds how many fds were read + */ + +void +_dbus_message_loader_return_unix_fds(DBusMessageLoader  *loader, +                                     int                *fds, +                                     unsigned            n_fds) +{ +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_assert(loader->unix_fds_outstanding); +  _dbus_assert(loader->unix_fds + loader->n_unix_fds == fds); +  _dbus_assert(loader->n_unix_fds + n_fds <= loader->n_unix_fds_allocated); + +  loader->n_unix_fds += n_fds; +  loader->unix_fds_outstanding = FALSE; +#else +  _dbus_assert_not_reached("Platform doesn't support unix fd passing"); +#endif +} +  /*   * FIXME when we move the header out of the buffer, that memmoves all   * buffered messages. Kind of crappy. @@ -3517,6 +3904,7 @@ load_message (DBusMessageLoader *loader,    const DBusString *type_str;    int type_pos;    DBusValidationMode mode; +  dbus_uint32_t n_unix_fds = 0;    mode = DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED; @@ -3586,6 +3974,59 @@ load_message (DBusMessageLoader *loader,          }      } +  /* 3. COPY OVER UNIX FDS */ +  _dbus_header_get_field_basic(&message->header, +                               DBUS_HEADER_FIELD_UNIX_FDS, +                               DBUS_TYPE_UINT32, +                               &n_unix_fds); + +#ifdef HAVE_UNIX_FD_PASSING + +  if (n_unix_fds > loader->n_unix_fds) +    { +      _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n", +                    n_unix_fds, loader->n_unix_fds); + +      loader->corrupted = TRUE; +      loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS; +      goto failed; +    } + +  /* If this was a recycled message there might still be +     some memory allocated for the fds */ +  dbus_free(message->unix_fds); + +  if (n_unix_fds > 0) +    { +      message->unix_fds = _dbus_memdup(loader->unix_fds, n_unix_fds * sizeof(message->unix_fds[0])); +      if (message->unix_fds == NULL) +        { +          _dbus_verbose ("Failed to allocate file descriptor array\n"); +          oom = TRUE; +          goto failed; +        } + +      message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds; +      loader->n_unix_fds -= n_unix_fds; +      memmove(loader->unix_fds + n_unix_fds, loader->unix_fds, loader->n_unix_fds); +    } +  else +    message->unix_fds = NULL; + +#else + +  if (n_unix_fds > 0) +    { +      _dbus_verbose ("Hmm, message claims to come with file descriptors " +                     "but that's not supported on our platform, disconnecting.\n"); + +      loader->corrupted = TRUE; +      loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS; +      goto failed; +    } + +#endif +    /* 3. COPY OVER BODY AND QUEUE MESSAGE */    if (!_dbus_list_append (&loader->messages, message)) @@ -3815,6 +4256,37 @@ _dbus_message_loader_get_max_message_size (DBusMessageLoader  *loader)    return loader->max_message_size;  } +/** + * Sets the maximum unix fds per message we allow. + * + * @param loader the loader + * @param size the max number of unix fds in a message + */ +void +_dbus_message_loader_set_max_message_unix_fds (DBusMessageLoader  *loader, +                                               long                n) +{ +  if (n > DBUS_MAXIMUM_MESSAGE_UNIX_FDS) +    { +      _dbus_verbose ("clamping requested max message unix_fds %ld to %d\n", +                     n, DBUS_MAXIMUM_MESSAGE_UNIX_FDS); +      n = DBUS_MAXIMUM_MESSAGE_UNIX_FDS; +    } +  loader->max_message_unix_fds = n; +} + +/** + * Gets the maximum allowed number of unix fds per message + * + * @param loader the loader + * @returns max unix fds + */ +long +_dbus_message_loader_get_max_message_unix_fds (DBusMessageLoader  *loader) +{ +  return loader->max_message_unix_fds; +} +  static DBusDataSlotAllocator slot_allocator;  _DBUS_DEFINE_GLOBAL_LOCK (message_slots); diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h index 682e5a05..a89fcd5e 100644 --- a/dbus/dbus-message.h +++ b/dbus/dbus-message.h @@ -159,6 +159,7 @@ dbus_bool_t dbus_message_get_args_valist      (DBusMessage     *message,  					       int              first_arg_type,  					       va_list          var_args); +dbus_bool_t dbus_message_contains_unix_fds    (DBusMessage *message);  dbus_bool_t dbus_message_iter_init             (DBusMessage     *message,                                                  DBusMessageIter *iter); diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index ed718fec..a2284ee5 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -110,6 +110,10 @@ extern "C" {  #define DBUS_TYPE_SIGNATURE     ((int) 'g')  /** #DBUS_TYPE_SIGNATURE as a string literal instead of a int literal */  #define DBUS_TYPE_SIGNATURE_AS_STRING      "g" +/** Type code marking a unix file descriptor */ +#define DBUS_TYPE_UNIX_FD      ((int) 'h') +/** #DBUS_TYPE_UNIX_FD as a string literal instead of a int literal */ +#define DBUS_TYPE_UNIX_FD_AS_STRING        "h"  /* Compound types */  /** Type code marking a D-Bus array type */ @@ -207,6 +211,14 @@ extern "C" {  /** Number of bits you need in an unsigned to store the max message size */  #define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 +/** The maximum total number of unix fds in a message. Similar + * rationale as DBUS_MAXIMUM_MESSAGE_LENGTH. However we divide by four + * given that one fd is an int and hence at least 32 bits. + */ +#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS (DBUS_MAXIMUM_MESSAGE_LENGTH/4) +/** Number of bits you need in an unsigned to store the max message unix fds */ +#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS_BITS (DBUS_MAXIMUM_MESSAGE_LENGTH_BITS-2) +  /** Depth of recursion in the type tree. This is automatically limited   * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array   * of array of array of ... that fit in the max signature.  But that's @@ -276,6 +288,12 @@ extern "C" {   * Header field code for the type signature of a message.   */  #define DBUS_HEADER_FIELD_SIGNATURE      8 +/** + * Header field code for the number of unix file descriptors associated + * with this message. + */ +#define DBUS_HEADER_FIELD_UNIX_FDS       9 +  /**   * Value of the highest-numbered header field code, can be used to determine @@ -283,7 +301,7 @@ extern "C" {   * that unknown codes must be ignored, so check for that before   * indexing the array.   */ -#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE +#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_UNIX_FDS  /** Header format is defined as a signature:   *   byte                            byte order @@ -415,6 +433,9 @@ extern "C" {  #define DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN     "org.freedesktop.DBus.Error.AdtAuditDataUnknown"  /** There's already an object with the requested object path. */  #define DBUS_ERROR_OBJECT_PATH_IN_USE         "org.freedesktop.DBus.Error.ObjectPathInUse" +/** The message meta data does not match the payload. e.g. expected +    number of file descriptors were not sent over the socket this message was received on. */ +#define DBUS_ERROR_INCONSISTENT_MESSAGE       "org.freedesktop.DBus.Error.InconsistentMessage"  /* XML introspection format */ diff --git a/dbus/dbus-resources.c b/dbus/dbus-resources.c index bfe8a089..7c7aabd0 100644 --- a/dbus/dbus-resources.c +++ b/dbus/dbus-resources.c @@ -29,7 +29,7 @@   * @brief DBusCounter and other stuff related to resource limits   *   * Types and functions related to tracking resource limits, - * such as the maximum amount of memory a connection can use + * such as the maximum amount of memory/unix fds a connection can use   * for messages, etc.   */ @@ -53,9 +53,12 @@ struct DBusCounter  {    int refcount;  /**< reference count */ -  long value;    /**< current counter value */ +  long size_value;       /**< current size counter value */ +  long unix_fd_value;    /**< current unix fd counter value */ + +  long notify_size_guard_value;    /**< call notify function when crossing this size value */ +  long notify_unix_fd_guard_value; /**< call notify function when crossing this unix fd value */ -  long notify_guard_value; /**< call notify function when crossing this value */    DBusCounterNotifyFunction notify_function; /**< notify function */    void *notify_data; /**< data for notify function */  }; @@ -83,9 +86,11 @@ _dbus_counter_new (void)      return NULL;    counter->refcount = 1; -  counter->value = 0; +  counter->size_value = 0; +  counter->unix_fd_value = 0; -  counter->notify_guard_value = 0; +  counter->notify_size_guard_value = 0; +  counter->notify_unix_fd_guard_value = 0;    counter->notify_function = NULL;    counter->notify_data = NULL; @@ -129,64 +134,109 @@ _dbus_counter_unref (DBusCounter *counter)  }  /** - * Adjusts the value of the counter by the given + * Adjusts the value of the size counter by the given + * delta which may be positive or negative. + * Calls the notify function from _dbus_counter_set_notify() + * if that function has been specified. + * + * @param counter the counter + * @param delta value to add to the size counter's current value + */ +void +_dbus_counter_adjust_size (DBusCounter *counter, +                           long         delta) +{ +  long old = counter->size_value; + +  counter->size_value += delta; + +#if 0 +  _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n", +                 old, delta, counter->size_value); +#endif + +  if (counter->notify_function != NULL && +      ((old < counter->notify_size_guard_value && +        counter->size_value >= counter->notify_size_guard_value) || +       (old >= counter->notify_size_guard_value && +        counter->size_value < counter->notify_size_guard_value))) +    (* counter->notify_function) (counter, counter->notify_data); +} + +/** + * Adjusts the value of the unix fd counter by the given   * delta which may be positive or negative.   * Calls the notify function from _dbus_counter_set_notify()   * if that function has been specified.   *   * @param counter the counter - * @param delta value to add to the counter's current value + * @param delta value to add to the unix fds counter's current value   */  void -_dbus_counter_adjust (DBusCounter *counter, -                      long         delta) +_dbus_counter_adjust_unix_fd (DBusCounter *counter, +                              long         delta)  { -  long old = counter->value; +  long old = counter->unix_fd_value; -  counter->value += delta; +  counter->unix_fd_value += delta;  #if 0    _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n", -                 old, delta, counter->value); +                 old, delta, counter->unix_fd_value);  #endif    if (counter->notify_function != NULL && -      ((old < counter->notify_guard_value && -        counter->value >= counter->notify_guard_value) || -       (old >= counter->notify_guard_value && -        counter->value < counter->notify_guard_value))) +      ((old < counter->notify_unix_fd_guard_value && +        counter->unix_fd_value >= counter->notify_unix_fd_guard_value) || +       (old >= counter->notify_unix_fd_guard_value && +        counter->unix_fd_value < counter->notify_unix_fd_guard_value)))      (* counter->notify_function) (counter, counter->notify_data);  }  /** - * Gets the current value of the counter. + * Gets the current value of the size counter. + * + * @param counter the counter + * @returns its current size value + */ +long +_dbus_counter_get_size_value (DBusCounter *counter) +{ +  return counter->size_value; +} + +/** + * Gets the current value of the unix fd counter.   *   * @param counter the counter - * @returns its current value + * @returns its current unix fd value   */  long -_dbus_counter_get_value (DBusCounter *counter) +_dbus_counter_get_unix_fd_value (DBusCounter *counter)  { -  return counter->value; +  return counter->unix_fd_value;  }  /**   * Sets the notify function for this counter; the notify function is - * called whenever the counter's value crosses the guard value in + * called whenever the counter's values cross the guard values in   * either direction (moving up, or moving down).   *   * @param counter the counter - * @param guard_value the value we're notified if the counter crosses + * @param size_guard_value the value we're notified if the size counter crosses + * @param unix_fd_guard_value the value we're notified if the unix fd counter crosses   * @param function function to call in order to notify   * @param user_data data to pass to the function   */  void  _dbus_counter_set_notify (DBusCounter               *counter, -                          long                       guard_value, +                          long                       size_guard_value, +                          long                       unix_fd_guard_value,                            DBusCounterNotifyFunction  function,                            void                      *user_data)  { -  counter->notify_guard_value = guard_value; +  counter->notify_size_guard_value = size_guard_value; +  counter->notify_unix_fd_guard_value = unix_fd_guard_value;    counter->notify_function = function;    counter->notify_data = user_data;  } diff --git a/dbus/dbus-resources.h b/dbus/dbus-resources.h index f87a56a3..4763a97f 100644 --- a/dbus/dbus-resources.h +++ b/dbus/dbus-resources.h @@ -37,14 +37,19 @@ typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter,  DBusCounter* _dbus_counter_new       (void);  DBusCounter* _dbus_counter_ref       (DBusCounter *counter);  void         _dbus_counter_unref     (DBusCounter *counter); -void         _dbus_counter_adjust    (DBusCounter *counter, -                                      long         delta); -long         _dbus_counter_get_value (DBusCounter *counter); - -void _dbus_counter_set_notify (DBusCounter               *counter, -                               long                       guard_value, -                               DBusCounterNotifyFunction  function, -                               void                      *user_data); + +void         _dbus_counter_adjust_size       (DBusCounter *counter, +                                              long         delta); +void         _dbus_counter_adjust_unix_fd    (DBusCounter *counter, +                                              long         delta); +long         _dbus_counter_get_size_value    (DBusCounter *counter); +long         _dbus_counter_get_unix_fd_value (DBusCounter *counter); + +void _dbus_counter_set_notify    (DBusCounter               *counter, +                                  long                       size_guard_value, +                                  long                       unix_fd_guard_value, +                                  DBusCounterNotifyFunction  function, +                                  void                      *user_data);  DBUS_END_DECLS diff --git a/dbus/dbus-server-debug-pipe.c b/dbus/dbus-server-debug-pipe.c index fe7c970b..8ba6fb6b 100644 --- a/dbus/dbus-server-debug-pipe.c +++ b/dbus/dbus-server-debug-pipe.c @@ -253,9 +253,6 @@ _dbus_transport_debug_pipe_new (const char     *server_name,        return NULL;      } -  _dbus_fd_set_close_on_exec (client_fd); -  _dbus_fd_set_close_on_exec (server_fd); -      client_transport = _dbus_transport_new_for_socket (client_fd,                                                       NULL, &address);    if (client_transport == NULL) diff --git a/dbus/dbus-server-socket.c b/dbus/dbus-server-socket.c index bd38ce6a..132fdb7c 100644 --- a/dbus/dbus-server-socket.c +++ b/dbus/dbus-server-socket.c @@ -195,8 +195,6 @@ socket_handle_watch (DBusWatch    *watch,          }        else          { -	  _dbus_fd_set_close_on_exec (client_fd);	   -            if (!handle_new_client_fd_and_unlock (server, client_fd))              _dbus_verbose ("Rejected client connection due to lack of memory\n");          } @@ -413,9 +411,6 @@ _dbus_server_new_for_tcp_socket (const char     *host,        goto failed_1;      } -  for (i = 0 ; i < nlisten_fds ; i++) -    _dbus_fd_set_close_on_exec (listen_fds[i]); -    _dbus_string_init_const (&host_str, host);    if (!_dbus_string_append (&address, "tcp:host=") ||        !_dbus_address_append_escaped (&address, &host_str) || diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c index fba9e9d2..07800c8c 100644 --- a/dbus/dbus-server-unix.c +++ b/dbus/dbus-server-unix.c @@ -201,8 +201,7 @@ _dbus_server_new_for_domain_socket (const char     *path,      }    listen_fd = _dbus_listen_unix_socket (path, abstract, error); -  _dbus_fd_set_close_on_exec (listen_fd); -   +    if (listen_fd < 0)      {        _DBUS_ASSERT_ERROR_IS_SET (error); diff --git a/dbus/dbus-sha.c b/dbus/dbus-sha.c index df34cc8c..04e353a0 100644 --- a/dbus/dbus-sha.c +++ b/dbus/dbus-sha.c @@ -465,7 +465,7 @@ _dbus_sha_final (DBusSHAContext   *context,    /* some kind of security paranoia, though it seems pointless     * to me given the nonzeroed stuff flying around     */ -  memset ((void*)context, '\0', sizeof (DBusSHAContext)); +  _DBUS_ZERO(*context);    return TRUE;  } diff --git a/dbus/dbus-signature.c b/dbus/dbus-signature.c index 5c152580..b2d0a627 100644 --- a/dbus/dbus-signature.c +++ b/dbus/dbus-signature.c @@ -355,6 +355,7 @@ dbus_type_is_fixed (int typecode)      case DBUS_TYPE_INT64:      case DBUS_TYPE_UINT64:      case DBUS_TYPE_DOUBLE: +    case DBUS_TYPE_UNIX_FD:        return TRUE;      default:        return FALSE; diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index c08f9278..c3c2fb99 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -21,6 +21,9 @@   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA   *   */ + +#include <config.h> +  #include "dbus-spawn.h"  #include "dbus-sysdeps-unix.h"  #include "dbus-internals.h" @@ -805,9 +808,25 @@ static dbus_bool_t  make_pipe (int         p[2],             DBusError  *error)  { +  int retval; + +#ifdef HAVE_PIPE2 +  dbus_bool_t cloexec_done; + +  retval = pipe2 (p, O_CLOEXEC); +  cloexec_done = retval >= 0; + +  /* Check if kernel seems to be too old to know pipe2(). We assume +     that if pipe2 is available, O_CLOEXEC is too.  */ +  if (retval < 0 && errno == ENOSYS) +#endif +    { +      retval = pipe(p); +    } +    _DBUS_ASSERT_ERROR_IS_CLEAR (error); -   -  if (pipe (p) < 0) + +  if (retval < 0)      {        dbus_set_error (error,  		      DBUS_ERROR_SPAWN_FAILED, @@ -816,6 +835,14 @@ make_pipe (int         p[2],        return FALSE;      } +#ifdef HAVE_PIPE2 +  if (!cloexec_done) +#endif +    { +      _dbus_fd_set_close_on_exec (p[0]); +      _dbus_fd_set_close_on_exec (p[1]); +    } +    return TRUE;  } @@ -1117,15 +1144,9 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,    if (!make_pipe (child_err_report_pipe, error))      goto cleanup_and_fail; -  _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]); -  _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]); -    if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error))      goto cleanup_and_fail; -  _dbus_fd_set_close_on_exec (babysitter_pipe[0]); -  _dbus_fd_set_close_on_exec (babysitter_pipe[1]); -    /* Setting up the babysitter is only useful in the parent,     * but we don't want to run out of memory and fail     * after we've already forked, since then we'd leak diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 2ae7909e..f0f1e706 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -22,7 +22,7 @@   *   */ -#define _GNU_SOURCE  +#include <config.h>  #include "dbus-internals.h"  #include "dbus-sysdeps.h" @@ -94,9 +94,28 @@ _dbus_open_socket (int              *fd_p,                     int               protocol,                     DBusError        *error)  { -  *fd_p = socket (domain, type, protocol); +#ifdef SOCK_CLOEXEC +  dbus_bool_t cloexec_done; + +  *fd_p = socket (domain, type | SOCK_CLOEXEC, protocol); +  cloexec_done = *fd_p >= 0; + +  /* Check if kernel seems to be too old to know SOCK_CLOEXEC */ +  if (*fd_p < 0 && errno == EINVAL) +#endif +    { +      *fd_p = socket (domain, type, protocol); +    } +    if (*fd_p >= 0)      { +#ifdef SOCK_CLOEXEC +      if (!cloexec_done) +#endif +        { +          _dbus_fd_set_close_on_exec(*fd_p); +        } +        _dbus_verbose ("socket fd %d opened\n", *fd_p);        return TRUE;      } @@ -120,6 +139,9 @@ _dbus_open_tcp_socket (int              *fd,  /**   * Opens a UNIX domain socket (as in the socket() call).   * Does not bind the socket. + * + * This will set FD_CLOEXEC for the socket returned + *   * @param fd return location for socket descriptor   * @param error return location for an error   * @returns #FALSE if error is set @@ -179,7 +201,259 @@ _dbus_write_socket (int               fd,                      int               start,                      int               len)  { +#ifdef MSG_NOSIGNAL +  const char *data; +  int bytes_written; + +  data = _dbus_string_get_const_data_len (buffer, start, len); + + again: + +  bytes_written = send (fd, data, len, MSG_NOSIGNAL); + +  if (bytes_written < 0 && errno == EINTR) +    goto again; + +  return bytes_written; + +#else    return _dbus_write (fd, buffer, start, len); +#endif +} + +/** + * Like _dbus_read_socket() but also tries to read unix fds from the + * socket. When there are more fds to read than space in the array + * passed this function will fail with ENOSPC. + * + * @param fd the socket + * @param buffer string to append data to + * @param count max amount of data to read + * @param fds array to place read file descriptors in + * @param n_fds on input space in fds array, on output how many fds actually got read + * @returns number of bytes appended to string + */ +int +_dbus_read_socket_with_unix_fds (int               fd, +                                 DBusString       *buffer, +                                 int               count, +                                 int              *fds, +                                 int              *n_fds) { +#ifndef HAVE_UNIX_FD_PASSING +  int r; + +  if ((r = _dbus_read_socket(fd, buffer, count)) < 0) +    return r; + +  *n_fds = 0; +  return r; + +#else +  int bytes_read; +  int start; +  struct msghdr m; +  struct iovec iov; + +  _dbus_assert (count >= 0); +  _dbus_assert (*n_fds >= 0); + +  start = _dbus_string_get_length (buffer); + +  if (!_dbus_string_lengthen (buffer, count)) +    { +      errno = ENOMEM; +      return -1; +    } + +  _DBUS_ZERO(iov); +  iov.iov_base = _dbus_string_get_data_len (buffer, start, count); +  iov.iov_len = count; + +  _DBUS_ZERO(m); +  m.msg_iov = &iov; +  m.msg_iovlen = 1; + +  /* Hmm, we have no clue how long the control data will actually be +     that is queued for us. The least we can do is assume that the +     caller knows. Hence let's make space for the number of fds that +     we shall read at max plus the cmsg header. */ +  m.msg_controllen = CMSG_SPACE(*n_fds * sizeof(int)); + +  /* It's probably safe to assume that systems with SCM_RIGHTS also +     know alloca() */ +  m.msg_control = alloca(m.msg_controllen); +  memset(m.msg_control, 0, m.msg_controllen); + + again: + +  bytes_read = recvmsg(fd, &m, 0 +#ifdef MSG_CMSG_CLOEXEC +                       |MSG_CMSG_CLOEXEC +#endif +                       ); + +  if (bytes_read < 0) +    { +      if (errno == EINTR) +        goto again; +      else +        { +          /* put length back (note that this doesn't actually realloc anything) */ +          _dbus_string_set_length (buffer, start); +          return -1; +        } +    } +  else +    { +      struct cmsghdr *cm; +      dbus_bool_t found = FALSE; + +      if (m.msg_flags & MSG_CTRUNC) +        { +          /* Hmm, apparently the control data was truncated. The bad +             thing is that we might have completely lost a couple of fds +             without chance to recover them. Hence let's treat this as a +             serious error. */ + +          errno = ENOSPC; +          _dbus_string_set_length (buffer, start); +          return -1; +        } + +      for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) +        if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) +          { +            unsigned i; + +            _dbus_assert(cm->cmsg_len <= CMSG_LEN(*n_fds * sizeof(int))); +            *n_fds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int); + +            memcpy(fds, CMSG_DATA(cm), *n_fds * sizeof(int)); +            found = TRUE; + +            /* Linux doesn't tell us whether MSG_CMSG_CLOEXEC actually +               worked, hence we need to go through this list and set +               CLOEXEC everywhere in any case */ +            for (i = 0; i < *n_fds; i++) +              _dbus_fd_set_close_on_exec(fds[i]); + +            break; +          } + +      if (!found) +        *n_fds = 0; + +      /* put length back (doesn't actually realloc) */ +      _dbus_string_set_length (buffer, start + bytes_read); + +#if 0 +      if (bytes_read > 0) +        _dbus_verbose_bytes_of_string (buffer, start, bytes_read); +#endif + +      return bytes_read; +    } +#endif +} + +int +_dbus_write_socket_with_unix_fds(int               fd, +                                 const DBusString *buffer, +                                 int               start, +                                 int               len, +                                 const int        *fds, +                                 int               n_fds) { + +#ifndef HAVE_UNIX_FD_PASSING + +  if (n_fds > 0) { +    errno = ENOTSUP; +    return -1; +  } + +  return _dbus_write_socket(fd, buffer, start, len); +#else +  return _dbus_write_socket_with_unix_fds_two(fd, buffer, start, len, NULL, 0, 0, fds, n_fds); +#endif +} + +int +_dbus_write_socket_with_unix_fds_two(int               fd, +                                     const DBusString *buffer1, +                                     int               start1, +                                     int               len1, +                                     const DBusString *buffer2, +                                     int               start2, +                                     int               len2, +                                     const int        *fds, +                                     int               n_fds) { + +#ifndef HAVE_UNIX_FD_PASSING + +  if (n_fds > 0) { +    errno = ENOTSUP; +    return -1; +  } + +  return _dbus_write_socket_two(fd, +                                buffer1, start1, len1, +                                buffer2, start2, len2); +#else + +  struct msghdr m; +  struct cmsghdr *cm; +  struct iovec iov[2]; +  int bytes_written; + +  _dbus_assert (len1 >= 0); +  _dbus_assert (len2 >= 0); +  _dbus_assert (n_fds >= 0); + +  _DBUS_ZERO(iov); +  iov[0].iov_base = (char*) _dbus_string_get_const_data_len (buffer1, start1, len1); +  iov[0].iov_len = len1; + +  if (buffer2) +    { +      iov[1].iov_base = (char*) _dbus_string_get_const_data_len (buffer2, start2, len2); +      iov[1].iov_len = len2; +    } + +  _DBUS_ZERO(m); +  m.msg_iov = iov; +  m.msg_iovlen = buffer2 ? 2 : 1; + +  if (n_fds > 0) +    { +      m.msg_controllen = CMSG_SPACE(n_fds * sizeof(int)); +      m.msg_control = alloca(m.msg_controllen); +      memset(m.msg_control, 0, m.msg_controllen); + +      cm = CMSG_FIRSTHDR(&m); +      cm->cmsg_level = SOL_SOCKET; +      cm->cmsg_type = SCM_RIGHTS; +      cm->cmsg_len = CMSG_LEN(n_fds * sizeof(int)); +      memcpy(CMSG_DATA(cm), fds, n_fds * sizeof(int)); +    } + + again: + +  bytes_written = sendmsg (fd, &m, 0 +#ifdef MSG_NOSIGNAL +                           |MSG_NOSIGNAL +#endif +                           ); + +  if (bytes_written < 0 && errno == EINTR) +    goto again; + +#if 0 +  if (bytes_written > 0) +    _dbus_verbose_bytes_of_string (buffer, start, bytes_written); +#endif + +  return bytes_written; +#endif  }  /** @@ -255,8 +529,52 @@ _dbus_write_socket_two (int               fd,                          int               start2,                          int               len2)  { +#ifdef MSG_NOSIGNAL +  struct iovec vectors[2]; +  const char *data1; +  const char *data2; +  int bytes_written; +  struct msghdr m; + +  _dbus_assert (buffer1 != NULL); +  _dbus_assert (start1 >= 0); +  _dbus_assert (start2 >= 0); +  _dbus_assert (len1 >= 0); +  _dbus_assert (len2 >= 0); + +  data1 = _dbus_string_get_const_data_len (buffer1, start1, len1); + +  if (buffer2 != NULL) +    data2 = _dbus_string_get_const_data_len (buffer2, start2, len2); +  else +    { +      data2 = NULL; +      start2 = 0; +      len2 = 0; +    } + +  vectors[0].iov_base = (char*) data1; +  vectors[0].iov_len = len1; +  vectors[1].iov_base = (char*) data2; +  vectors[1].iov_len = len2; + +  _DBUS_ZERO(m); +  m.msg_iov = vectors; +  m.msg_iovlen = data2 ? 2 : 1; + + again: + +  bytes_written = sendmsg (fd, &m, MSG_NOSIGNAL); + +  if (bytes_written < 0 && errno == EINTR) +    goto again; + +  return bytes_written; + +#else    return _dbus_write_two (fd, buffer1, start1, len1,                            buffer2, start2, len2); +#endif  } @@ -474,6 +792,8 @@ _dbus_write_two (int               fd,   * requested (it's possible only on Linux; see "man 7 unix" on Linux).   * On non-Linux abstract socket usage always fails.   * + * This will set FD_CLOEXEC for the socket returned. + *   * @param path the path to UNIX domain socket   * @param abstract #TRUE to use abstract namespace   * @param error return location for error code @@ -610,6 +930,8 @@ _dbus_set_local_creds (int fd, dbus_bool_t on)   * see "man 7 unix" on Linux).   * On non-Linux abstract socket usage always fails.   * + * This will set FD_CLOEXEC for the socket returned + *   * @param path the socket name   * @param abstract #TRUE to use abstract namespace   * @param error return location for errors @@ -746,6 +1068,8 @@ _dbus_listen_unix_socket (const char     *path,   * and port. The connection fd is returned, and is set up as   * nonblocking.   * + * This will set FD_CLOEXEC for the socket returned + *   * @param host the host name to connect to   * @param port the port to connect to   * @param family the address family to listen on, NULL for all @@ -853,6 +1177,8 @@ _dbus_connect_tcp_socket (const char     *host,   * a random free port is used and returned in the port parameter.   * If inaddr_any is specified, the hostname is ignored.   * + * This will set FD_CLOEXEC for the socket returned + *   * @param host the host name to listen on   * @param port the port to listen on, if zero a free port will be used   * @param family the address family to listen on, NULL for all @@ -1056,13 +1382,13 @@ write_credentials_byte (int             server_fd,    iov.iov_base = buf;    iov.iov_len = 1; -  memset (&msg, 0, sizeof (msg)); +  _DBUS_ZERO(msg);    msg.msg_iov = &iov;    msg.msg_iovlen = 1;    msg.msg_control = (caddr_t) &cmsg;    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred)); -  memset (&cmsg, 0, sizeof (cmsg)); +  _DBUS_ZERO(cmsg);    cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));    cmsg.hdr.cmsg_level = SOL_SOCKET;    cmsg.hdr.cmsg_type = SCM_CREDS; @@ -1172,12 +1498,12 @@ _dbus_read_credentials_socket  (int              client_fd,    iov.iov_base = &buf;    iov.iov_len = 1; -  memset (&msg, 0, sizeof (msg)); +  _DBUS_ZERO(msg);    msg.msg_iov = &iov;    msg.msg_iovlen = 1;  #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) -  memset (&cmsg, 0, sizeof (cmsg)); +  _DBUS_ZERO(cmsg);    msg.msg_control = (caddr_t) &cmsg;    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));  #endif @@ -1377,6 +1703,8 @@ _dbus_send_credentials_socket  (int              server_fd,   * Accepts a connection on a listening socket.   * Handles EINTR for you.   * + * This will enable FD_CLOEXEC for the returned socket. + *   * @param listen_fd the listen file descriptor   * @returns the connection fd of the client, or -1 on error   */ @@ -1386,12 +1714,25 @@ _dbus_accept  (int listen_fd)    int client_fd;    struct sockaddr addr;    socklen_t addrlen; +#ifdef HAVE_ACCEPT4 +  dbus_bool_t cloexec_done; +#endif    addrlen = sizeof (addr); -   +   retry: -  client_fd = accept (listen_fd, &addr, &addrlen); -   + +#ifdef HAVE_ACCEPT4 +  /* We assume that if accept4 is available SOCK_CLOEXEC is too */ +  client_fd = accept4 (listen_fd, &addr, &addrlen, SOCK_CLOEXEC); +  cloexec_done = client_fd >= 0; + +  if (client_fd < 0 && errno == ENOSYS) +#endif +    { +      client_fd = accept (listen_fd, &addr, &addrlen); +    } +    if (client_fd < 0)      {        if (errno == EINTR) @@ -1399,7 +1740,14 @@ _dbus_accept  (int listen_fd)      }    _dbus_verbose ("client fd %d accepted\n", client_fd); -   + +#ifdef HAVE_ACCEPT4 +  if (!cloexec_done) +#endif +    { +      _dbus_fd_set_close_on_exec(client_fd); +    } +    return client_fd;  } @@ -1864,23 +2212,8 @@ _dbus_parse_uid (const DBusString      *uid_str,    return TRUE;  } - +#if !DBUS_USE_SYNC  _DBUS_DEFINE_GLOBAL_LOCK (atomic); - -#if DBUS_USE_ATOMIC_INT_486_COND -/* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */ -/* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */ -static inline dbus_int32_t -atomic_exchange_and_add (DBusAtomic            *atomic, -                         volatile dbus_int32_t  val) -{ -  register dbus_int32_t result; - -  __asm__ __volatile__ ("lock; xaddl %0,%1" -                        : "=r" (result), "=m" (atomic->value) -			: "0" (val), "m" (atomic->value)); -  return result; -}  #endif  /** @@ -1888,14 +2221,12 @@ atomic_exchange_and_add (DBusAtomic            *atomic,   *   * @param atomic pointer to the integer to increment   * @returns the value before incrementing - * - * @todo implement arch-specific faster atomic ops   */  dbus_int32_t  _dbus_atomic_inc (DBusAtomic *atomic)  { -#if DBUS_USE_ATOMIC_INT_486_COND -  return atomic_exchange_and_add (atomic, 1); +#if DBUS_USE_SYNC +  return __sync_add_and_fetch(&atomic->value, 1)-1;  #else    dbus_int32_t res;    _DBUS_LOCK (atomic); @@ -1911,14 +2242,12 @@ _dbus_atomic_inc (DBusAtomic *atomic)   *   * @param atomic pointer to the integer to decrement   * @returns the value before decrementing - * - * @todo implement arch-specific faster atomic ops   */  dbus_int32_t  _dbus_atomic_dec (DBusAtomic *atomic)  { -#if DBUS_USE_ATOMIC_INT_486_COND -  return atomic_exchange_and_add (atomic, -1); +#if DBUS_USE_SYNC +  return __sync_sub_and_fetch(&atomic->value, 1)+1;  #else    dbus_int32_t res; @@ -2683,6 +3012,48 @@ _dbus_close (int        fd,  }  /** + * Duplicates a file descriptor. Makes sure the fd returned is >= 3 + * (i.e. avoids stdin/stdout/stderr). Sets O_CLOEXEC. + * + * @param fd the file descriptor to duplicate + * @returns duplicated file descriptor + * */ +int +_dbus_dup(int        fd, +          DBusError *error) +{ +  int new_fd; + +#ifdef F_DUPFD_CLOEXEC +  dbus_bool_t cloexec_done; + +  new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); +  cloexec_done = new_fd >= 0; + +  if (new_fd < 0 && errno == EINVAL) +#endif +    { +      new_fd = fcntl(fd, F_DUPFD, 3); +    } + +  if (new_fd < 0) { + +    dbus_set_error (error, _dbus_error_from_errno (errno), +                    "Could not duplicate fd %d", fd); +    return -1; +  } + +#ifndef F_DUPFD_CLOEXEC +  if (!cloexec_done) +#endif +    { +      _dbus_fd_set_close_on_exec(new_fd); +    } + +  return new_fd; +} + +/**   * Sets a file descriptor to be nonblocking.   *   * @param fd the file descriptor. @@ -2761,6 +3132,8 @@ _dbus_print_backtrace (void)   * Creates a full-duplex pipe (as in socketpair()).   * Sets both ends of the pipe nonblocking.   * + * Marks both file descriptors as close-on-exec + *   * @todo libdbus only uses this for the debug-pipe server, so in   * principle it could be in dbus-sysdeps-util.c, except that   * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the @@ -2780,16 +3153,37 @@ _dbus_full_duplex_pipe (int        *fd1,  {  #ifdef HAVE_SOCKETPAIR    int fds[2]; +  int retval; -  _DBUS_ASSERT_ERROR_IS_CLEAR (error); -   -  if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) +#ifdef SOCK_CLOEXEC +  dbus_bool_t cloexec_done; + +  retval = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds); +  cloexec_done = retval >= 0; + +  if (retval < 0 && errno == EINVAL) +#endif +    { +      retval = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); +    } + +  if (retval < 0)      {        dbus_set_error (error, _dbus_error_from_errno (errno),                        "Could not create full-duplex pipe");        return FALSE;      } +  _DBUS_ASSERT_ERROR_IS_CLEAR (error); + +#ifdef SOCK_CLOEXEC +  if (!cloexec_done) +#endif +    { +      _dbus_fd_set_close_on_exec (fds[0]); +      _dbus_fd_set_close_on_exec (fds[1]); +    } +    if (!blocking &&        (!_dbus_set_fd_nonblocking (fds[0], NULL) ||         !_dbus_set_fd_nonblocking (fds[1], NULL))) @@ -3487,4 +3881,36 @@ _dbus_get_is_errno_eagain_or_ewouldblock (void)    return errno == EAGAIN || errno == EWOULDBLOCK;  } +/** + *  Checks whether file descriptors may be passed via the socket + * + *  @param fd the socket + *  @return TRUE when fd passing over this socket is supported + * + */ +dbus_bool_t +_dbus_socket_can_pass_unix_fd(int fd) { + +#ifdef SCM_RIGHTS +  union { +    struct sockaddr sa; +    struct sockaddr_storage storage; +    struct sockaddr_un un; +  } sa_buf; + +  socklen_t sa_len = sizeof(sa_buf); + +  _DBUS_ZERO(sa_buf); + +  if (getsockname(fd, &sa_buf.sa, &sa_len) < 0) +    return FALSE; + +  return sa_buf.sa.sa_family == AF_UNIX; + +#else +  return FALSE; + +#endif +} +  /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps-unix.h b/dbus/dbus-sysdeps-unix.h index ecd20f67..96b44e29 100644 --- a/dbus/dbus-sysdeps-unix.h +++ b/dbus/dbus-sysdeps-unix.h @@ -44,7 +44,9 @@ DBUS_BEGIN_DECLS  dbus_bool_t   _dbus_close     (int               fd,                   DBusError        *error); -int  +int _dbus_dup   (int               fd, +                 DBusError        *error); +int  _dbus_read      (int               fd,                   DBusString       *buffer,                   int               count); diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 02f31231..4a42c761 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -2616,7 +2616,7 @@ static void dump_backtrace_for_thread(HANDLE hThread)      DPRINTF("Backtrace:\n"); -    memset(&context, 0, sizeof(context)); +    _DBUS_ZERO(context);      context.ContextFlags = CONTEXT_FULL;      SuspendThread(hThread); @@ -2628,7 +2628,7 @@ static void dump_backtrace_for_thread(HANDLE hThread)          return;      } -    memset(&sf, 0, sizeof(sf)); +    _DBUS_ZERO(sf);  #ifdef __i386__      sf.AddrFrame.Offset = context.Ebp; diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 30e7dff2..739a4546 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -153,6 +153,28 @@ int         _dbus_write_socket_two (int               fd,                                      const DBusString *buffer2,                                      int               start2,                                      int               len2); + +int _dbus_read_socket_with_unix_fds      (int               fd, +                                          DBusString       *buffer, +                                          int               count, +                                          int              *fds, +                                          int              *n_fds); +int _dbus_write_socket_with_unix_fds     (int               fd, +                                          const DBusString *buffer, +                                          int               start, +                                          int               len, +                                          const int        *fds, +                                          int               n_fds); +int _dbus_write_socket_with_unix_fds_two (int               fd, +                                          const DBusString *buffer1, +                                          int               start1, +                                          int               len1, +                                          const DBusString *buffer2, +                                          int               start2, +                                          int               len2, +                                          const int        *fds, +                                          int               n_fds); +  int _dbus_connect_tcp_socket  (const char     *host,                                 const char     *port,                                 const char     *family, @@ -192,6 +214,8 @@ dbus_bool_t _dbus_windows_user_is_process_owner (const char        *windows_sid)  dbus_bool_t _dbus_append_keyring_directory_for_credentials (DBusString      *directory,                                                              DBusCredentials *credentials); +dbus_bool_t _dbus_socket_can_pass_unix_fd(int fd); +  /** Opaque type representing an atomically-modifiable integer   * that can be used from multiple threads.   */ diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index c9649646..2e41ed96 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -424,7 +424,9 @@ init_locks (void)      LOCK_ADDR (pending_call_slots),      LOCK_ADDR (server_slots),      LOCK_ADDR (message_slots), +#if !DBUS_USE_SYNC      LOCK_ADDR (atomic), +#endif      LOCK_ADDR (bus),      LOCK_ADDR (bus_datas),      LOCK_ADDR (shutdown_funcs), diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 280bbd3d..a3c8f3e4 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -23,6 +23,8 @@  #ifndef DBUS_TRANSPORT_PROTECTED_H  #define DBUS_TRANSPORT_PROTECTED_H +#include <config.h> +  #include <dbus/dbus-internals.h>  #include <dbus/dbus-errors.h>  #include <dbus/dbus-transport.h> @@ -92,9 +94,9 @@ struct DBusTransport    DBusCredentials *credentials;               /**< Credentials of other end read from the socket */      long max_live_messages_size;                /**< Max total size of received messages. */ +  long max_live_messages_unix_fds;            /**< Max total unix fds of received messages. */ -  DBusCounter *live_messages_size;            /**< Counter for size of all live messages. */ - +  DBusCounter *live_messages;                 /**< Counter for size/unix fds of all live messages. */    char *address;                              /**< Address of the server we are connecting to (#NULL for the server side of a transport) */ @@ -138,6 +140,9 @@ DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry                                                                  DBusTransport    **transport_p,                                                                  DBusError         *error); +#define DBUS_TRANSPORT_CAN_SEND_UNIX_FD(x)      \ +  _dbus_auth_get_unix_fd_negotiated((x)->auth) +  DBUS_END_DECLS  #endif /* DBUS_TRANSPORT_PROTECTED_H */ diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 8be4d135..4b88af7c 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -28,7 +28,6 @@  #include "dbus-watch.h"  #include "dbus-credentials.h" -  /**   * @defgroup DBusTransportSocket DBusTransport implementations for sockets   * @ingroup  DBusInternals @@ -192,7 +191,8 @@ check_read_watch (DBusTransport *transport)    if (_dbus_transport_get_is_authenticated (transport))      need_read_watch = -      _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; +      (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) && +      (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds);    else      {        if (transport->receive_credentials_pending) @@ -551,6 +551,9 @@ do_writing (DBusTransport *transport)        if (_dbus_auth_needs_encoding (transport->auth))          { +          /* Does fd passing even make sense with encoded data? */ +          _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); +            if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0)              {                if (!_dbus_auth_encode_data (transport->auth, @@ -588,27 +591,53 @@ do_writing (DBusTransport *transport)  #if 0            _dbus_verbose ("message is %d bytes\n", -                         total_bytes_to_write);           +                         total_bytes_to_write);  #endif -           -          if (socket_transport->message_bytes_written < header_len) + +#ifdef HAVE_UNIX_FD_PASSING +          if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))              { +              /* Send the fds along with the first byte of the message */ +              const int *unix_fds; +              unsigned n; + +              _dbus_message_get_unix_fds(message, &unix_fds, &n); +                bytes_written = -                _dbus_write_socket_two (socket_transport->fd, -                                        header, -                                        socket_transport->message_bytes_written, -                                        header_len - socket_transport->message_bytes_written, -                                        body, -                                        0, body_len); +                _dbus_write_socket_with_unix_fds_two (socket_transport->fd, +                                                      header, +                                                      socket_transport->message_bytes_written, +                                                      header_len - socket_transport->message_bytes_written, +                                                      body, +                                                      0, body_len, +                                                      unix_fds, +                                                      n); + +              if (bytes_written > 0 && n > 0) +                _dbus_verbose("Wrote %i unix fds\n", n);              }            else +#endif              { -              bytes_written = -                _dbus_write_socket (socket_transport->fd, -                                    body, -                                    (socket_transport->message_bytes_written - header_len), -                                    body_len - -                                    (socket_transport->message_bytes_written - header_len)); +              if (socket_transport->message_bytes_written < header_len) +                { +                  bytes_written = +                    _dbus_write_socket_two (socket_transport->fd, +                                            header, +                                            socket_transport->message_bytes_written, +                                            header_len - socket_transport->message_bytes_written, +                                            body, +                                            0, body_len); +                } +              else +                { +                  bytes_written = +                    _dbus_write_socket (socket_transport->fd, +                                        body, +                                        (socket_transport->message_bytes_written - header_len), +                                        body_len - +                                        (socket_transport->message_bytes_written - header_len)); +                }              }          } @@ -704,6 +733,9 @@ do_reading (DBusTransport *transport)    if (_dbus_auth_needs_decoding (transport->auth))      { +      /* Does fd passing even make sense with encoded data? */ +      _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); +        if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0)          bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming);        else @@ -748,10 +780,37 @@ do_reading (DBusTransport *transport)      {        _dbus_message_loader_get_buffer (transport->loader,                                         &buffer); -       -      bytes_read = _dbus_read_socket (socket_transport->fd, -                                      buffer, socket_transport->max_bytes_read_per_iteration); -       + +#ifdef HAVE_UNIX_FD_PASSING +      if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)) +        { +          int *fds, n_fds; + +          if (!_dbus_message_loader_get_unix_fds(transport->loader, &fds, &n_fds)) +            { +              _dbus_verbose ("Out of memory reading file descriptors\n"); +              _dbus_message_loader_return_buffer (transport->loader, buffer, 0); +              oom = TRUE; +              goto out; +            } + +          bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd, +                                                       buffer, +                                                       socket_transport->max_bytes_read_per_iteration, +                                                       fds, &n_fds); + +          if (bytes_read >= 0 && n_fds > 0) +            _dbus_verbose("Read %i unix fds\n", n_fds); + +          _dbus_message_loader_return_unix_fds(transport->loader, fds, bytes_read < 0 ? 0 : n_fds); +        } +      else +#endif +        { +          bytes_read = _dbus_read_socket (socket_transport->fd, +                                          buffer, socket_transport->max_bytes_read_per_iteration); +        } +        _dbus_message_loader_return_buffer (transport->loader,                                            buffer,                                            bytes_read < 0 ? 0 : bytes_read); @@ -1204,7 +1263,11 @@ _dbus_transport_new_for_socket (int               fd,                                    &socket_vtable,                                    server_guid, address))      goto failed_4; -   + +#ifdef HAVE_UNIX_FD_PASSING +  _dbus_auth_set_unix_fd_possible(socket_transport->base.auth, _dbus_socket_can_pass_unix_fd(fd)); +#endif +    socket_transport->fd = fd;    socket_transport->message_bytes_written = 0; @@ -1282,8 +1345,6 @@ _dbus_transport_new_for_tcp_socket (const char     *host,        return NULL;      } -  _dbus_fd_set_close_on_exec (fd); -      _dbus_verbose ("Successfully connected to tcp socket %s:%s\n",                   host, port); diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index a4452aa1..2f2a3da5 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -85,8 +85,6 @@ _dbus_transport_new_for_domain_socket (const char     *path,        goto failed_0;      } -  _dbus_fd_set_close_on_exec (fd); -      _dbus_verbose ("Successfully connected to unix socket %s\n",                   path); diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index e3ed59e8..3c67621d 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -29,6 +29,8 @@  #include "dbus-auth.h"  #include "dbus-address.h"  #include "dbus-credentials.h" +#include "dbus-message-private.h" +#include "dbus-marshal-header.h"  #ifdef DBUS_BUILD_TESTS  #include "dbus-server-debug-pipe.h"  #endif @@ -55,7 +57,7 @@   */  static void -live_messages_size_notify (DBusCounter *counter, +live_messages_notify (DBusCounter *counter,                             void        *user_data)  {    DBusTransport *transport = user_data; @@ -63,8 +65,10 @@ live_messages_size_notify (DBusCounter *counter,    _dbus_transport_ref (transport);  #if 0 -  _dbus_verbose ("Counter value is now %d\n", -                 (int) _dbus_counter_get_value (counter)); +  _dbus_verbose ("Size counter value is now %d\n", +                 (int) _dbus_counter_get_size_value (counter)); +  _dbus_verbose ("Unix FD counter value is now %d\n", +                 (int) _dbus_counter_get_unix_fd_value (counter));  #endif    /* disable or re-enable the read watch for the transport if @@ -155,7 +159,7 @@ _dbus_transport_init_base (DBusTransport             *transport,    transport->vtable = vtable;    transport->loader = loader;    transport->auth = auth; -  transport->live_messages_size = counter; +  transport->live_messages = counter;    transport->authenticated = FALSE;    transport->disconnected = FALSE;    transport->is_server = (server_guid != NULL); @@ -178,17 +182,22 @@ _dbus_transport_init_base (DBusTransport             *transport,     */    transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63; +  /* On Linux RLIMIT_NOFILE defaults to 1024, so allowing 4096 fds live +     should be more than enough */ +  transport->max_live_messages_unix_fds = 4096; +    /* credentials read from socket if any */    transport->credentials = creds; -   -  _dbus_counter_set_notify (transport->live_messages_size, + +  _dbus_counter_set_notify (transport->live_messages,                              transport->max_live_messages_size, -                            live_messages_size_notify, +                            transport->max_live_messages_unix_fds, +                            live_messages_notify,                              transport);    if (transport->address)      _dbus_verbose ("Initialized transport on address %s\n", transport->address); -   +    return TRUE;  } @@ -212,9 +221,9 @@ _dbus_transport_finalize_base (DBusTransport *transport)    _dbus_message_loader_unref (transport->loader);    _dbus_auth_unref (transport->auth); -  _dbus_counter_set_notify (transport->live_messages_size, -                            0, NULL, NULL); -  _dbus_counter_unref (transport->live_messages_size); +  _dbus_counter_set_notify (transport->live_messages, +                            0, 0, NULL, NULL); +  _dbus_counter_unref (transport->live_messages);    dbus_free (transport->address);    dbus_free (transport->expected_guid);    if (transport->credentials) @@ -803,6 +812,18 @@ _dbus_transport_get_is_anonymous (DBusTransport *transport)  }  /** + * Returns TRUE if the transport supports sending unix fds. + * + * @param transport the transport + * @returns #TRUE if TRUE it is possible to send unix fds across the transport. + */ +dbus_bool_t +_dbus_transport_can_pass_unix_fd(DBusTransport *transport) +{ +  return DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport); +} + +/**   * Gets the address of a transport. It will be   * #NULL for a server-side transport.   * @@ -1059,7 +1080,8 @@ recover_unused_bytes (DBusTransport *transport)  DBusDispatchStatus  _dbus_transport_get_dispatch_status (DBusTransport *transport)  { -  if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size) +  if (_dbus_counter_get_size_value (transport->live_messages) >= transport->max_live_messages_size || +      _dbus_counter_get_unix_fd_value (transport->live_messages) >= transport->max_live_messages_unix_fds)      return DBUS_DISPATCH_COMPLETE; /* complete for now */    if (!_dbus_transport_get_is_authenticated (transport)) @@ -1116,7 +1138,7 @@ _dbus_transport_queue_messages (DBusTransport *transport)        _dbus_verbose ("queueing received message %p\n", message); -      if (!_dbus_message_add_size_counter (message, transport->live_messages_size)) +      if (!_dbus_message_add_counter (message, transport->live_messages))          {            _dbus_message_loader_putback_message_link (transport->loader,                                                       link); @@ -1154,6 +1176,19 @@ _dbus_transport_set_max_message_size (DBusTransport  *transport,  }  /** + * See dbus_connection_set_max_message_unix_fds(). + * + * @param transport the transport + * @param n the max number of unix fds of a single message + */ +void +_dbus_transport_set_max_message_unix_fds (DBusTransport  *transport, +                                          long            n) +{ +  _dbus_message_loader_set_max_message_unix_fds (transport->loader, n); +} + +/**   * See dbus_connection_get_max_message_size().   *   * @param transport the transport @@ -1166,6 +1201,18 @@ _dbus_transport_get_max_message_size (DBusTransport  *transport)  }  /** + * See dbus_connection_get_max_message_unix_fds(). + * + * @param transport the transport + * @returns max message unix fds + */ +long +_dbus_transport_get_max_message_unix_fds (DBusTransport  *transport) +{ +  return _dbus_message_loader_get_max_message_unix_fds (transport->loader); +} + +/**   * See dbus_connection_set_max_received_size().   *   * @param transport the transport @@ -1176,12 +1223,30 @@ _dbus_transport_set_max_received_size (DBusTransport  *transport,                                         long            size)  {    transport->max_live_messages_size = size; -  _dbus_counter_set_notify (transport->live_messages_size, +  _dbus_counter_set_notify (transport->live_messages,                              transport->max_live_messages_size, -                            live_messages_size_notify, +                            transport->max_live_messages_unix_fds, +                            live_messages_notify,                              transport);  } +/** + * See dbus_connection_set_max_received_unix_fds(). + * + * @param transport the transport + * @param n the max unix fds of all incoming messages + */ +void +_dbus_transport_set_max_received_unix_fds (DBusTransport  *transport, +                                           long            n) +{ +  transport->max_live_messages_unix_fds = n; +  _dbus_counter_set_notify (transport->live_messages, +                            transport->max_live_messages_size, +                            transport->max_live_messages_unix_fds, +                            live_messages_notify, +                            transport); +}  /**   * See dbus_connection_get_max_received_size(). @@ -1196,6 +1261,18 @@ _dbus_transport_get_max_received_size (DBusTransport  *transport)  }  /** + * See dbus_connection_set_max_received_unix_fds(). + * + * @param transport the transport + * @returns max unix fds for all live messages + */ +long +_dbus_transport_get_max_received_unix_fds (DBusTransport  *transport) +{ +  return transport->max_live_messages_unix_fds; +} + +/**   * See dbus_connection_get_unix_user().   *   * @param transport the transport diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index f2915f33..0db048a2 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -40,6 +40,8 @@ void               _dbus_transport_disconnect             (DBusTransport  dbus_bool_t        _dbus_transport_get_is_connected       (DBusTransport              *transport);  dbus_bool_t        _dbus_transport_get_is_authenticated   (DBusTransport              *transport);  dbus_bool_t        _dbus_transport_get_is_anonymous       (DBusTransport              *transport); +dbus_bool_t        _dbus_transport_can_pass_unix_fd       (DBusTransport              *transport); +  const char*        _dbus_transport_get_address            (DBusTransport              *transport);  const char*        _dbus_transport_get_server_id          (DBusTransport              *transport);  dbus_bool_t        _dbus_transport_handle_watch           (DBusTransport              *transport, @@ -52,12 +54,21 @@ void               _dbus_transport_do_iteration           (DBusTransport                                                             int                         timeout_milliseconds);  DBusDispatchStatus _dbus_transport_get_dispatch_status    (DBusTransport              *transport);  dbus_bool_t        _dbus_transport_queue_messages         (DBusTransport              *transport); +  void               _dbus_transport_set_max_message_size   (DBusTransport              *transport,                                                             long                        size);  long               _dbus_transport_get_max_message_size   (DBusTransport              *transport);  void               _dbus_transport_set_max_received_size  (DBusTransport              *transport,                                                             long                        size);  long               _dbus_transport_get_max_received_size  (DBusTransport              *transport); + +void               _dbus_transport_set_max_message_unix_fds (DBusTransport              *transport, +                                                             long                        n); +long               _dbus_transport_get_max_message_unix_fds (DBusTransport              *transport); +void               _dbus_transport_set_max_received_unix_fds(DBusTransport              *transport, +                                                             long                        n); +long               _dbus_transport_get_max_received_unix_fds(DBusTransport              *transport); +  dbus_bool_t        _dbus_transport_get_socket_fd          (DBusTransport              *transport,                                                             int                        *fd_p);  dbus_bool_t        _dbus_transport_get_unix_user          (DBusTransport              *transport, diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index 9b22c84a..8d10aade 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -415,6 +415,10 @@                  <entry><literal>DICT_ENTRY</literal></entry>                  <entry>101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}') </entry>                  <entry>Entry in a dict or map (array of key-value pairs)</entry> +              </row><row> +                <entry><literal>UNIX_FD</literal></entry> +                <entry>104 (ASCII 'h')</entry> +                <entry>Unix file descriptor</entry>                </row>              </tbody>            </tgroup> @@ -579,6 +583,15 @@                  <entry>                    8                  </entry> +              </row><row> +                <entry><literal>UNIX_FD</literal></entry> +                <entry>32-bit unsigned integer in the message's byte +                order. The actual file descriptors need to be +                transferred out-of-band via some platform specific +                mechanism. On the wire, values of this type store the index to the +                file descriptor in the array of file descriptors that +                accompany the message.</entry> +                <entry>4</entry>  	      </row>              </tbody>            </tgroup> @@ -999,6 +1012,22 @@                    If omitted, it is assumed to be the                     empty signature "" (i.e. the body must be 0-length).</entry>                  </row> +                <row> +                  <entry><literal>UNIX_FDS</literal></entry> +                  <entry>9</entry> +                  <entry><literal>UINT32</literal></entry> +                  <entry>optional</entry> +                  <entry>The number of Unix file descriptors that +                  accompany the message.  If omitted, it is assumed +                  that no Unix file descriptors accompany the +                  message. The actual file descriptors need to be +                  transferred via platform specific mechanism +                  out-of-band. They must be sent at the same time as +                  part of the message itself. They may not be sent +                  before the first byte of the message itself is +                  transferred or after the last byte of the message +                  itself.</entry> +                </row>                </tbody>              </tgroup>            </informaltable> @@ -1410,6 +1439,7 @@  	  <listitem><para>BEGIN</para></listitem>  	  <listitem><para>DATA <data in hex encoding></para></listitem>  	  <listitem><para>ERROR [human-readable error explanation]</para></listitem> +	  <listitem><para>NEGOTIATE_UNIX_FD</para></listitem>  	</itemizedlist>          From server to client are as follows: @@ -1419,6 +1449,7 @@  	  <listitem><para>OK <GUID in hex></para></listitem>  	  <listitem><para>DATA <data in hex encoding></para></listitem>  	  <listitem><para>ERROR</para></listitem> +	  <listitem><para>AGREE_UNIX_FD</para></listitem>  	</itemizedlist>        </para>        <para> @@ -1480,15 +1511,16 @@          an OK command must be sent to the client.        </para>        <para> -        The first octet received by the client after the \r\n of the OK -        command must be the first octet of the authenticated/encrypted  -        stream of D-Bus messages. -      </para> -      <para>          The first octet received by the server after the \r\n of the BEGIN          command from the client must be the first octet of the          authenticated/encrypted stream of D-Bus messages.        </para> +      <para> +        If BEGIN is received by the server, the first octet received +        by the client after the \r\n of the OK command must be the +        first octet of the authenticated/encrypted stream of D-Bus +        messages. +      </para>      </sect2>      <sect2 id="auth-command-cancel">        <title>CANCEL Command</title> @@ -1542,20 +1574,24 @@      <sect2 id="auth-command-ok">        <title>OK Command</title>        <para> -        The OK command indicates that the client has been authenticated, -        and that further communication will be a stream of D-Bus messages -        (optionally encrypted, as negotiated) rather than this protocol. +        The OK command indicates that the client has been +        authenticated. The client may now proceed with negotiating +        Unix file descriptor passing. To do that it shall send +        NEGOTIATE_UNIX_FD to the server.        </para>        <para> -        The first octet received by the client after the \r\n of the OK -        command must be the first octet of the authenticated/encrypted  -        stream of D-Bus messages. +        Otherwise, the client must respond to the OK command by +        sending a BEGIN command, followed by its stream of messages, +        or by disconnecting.  The server must not accept additional +        commands using this protocol after the BEGIN command has been +        received. Further communication will be a stream of D-Bus +        messages (optionally encrypted, as negotiated) rather than +        this protocol.        </para>        <para> -        The client must respond to the OK command by sending a BEGIN -        command, followed by its stream of messages, or by disconnecting. -        The server must not accept additional commands using this protocol  -        after the OK command has been sent. +        If a client sends BEGIN the first octet received by the client +        after the \r\n of the OK command must be the first octet of +        the authenticated/encrypted stream of D-Bus messages.        </para>        <para>          The OK command has one argument, which is the GUID of the server. @@ -1589,6 +1625,56 @@          negotiate extensions or changes to the D-Bus protocol in the future.        </para>      </sect2> +    <sect2 id="auth-command-negotiate-unix-fd"> +      <title>NEGOTIATE_UNIX_FD Command</title> +      <para> +        The NEGOTIATE_UNIX_FD command indicates that the client +        supports Unix file descriptor passing. This command may only +        be sent after the connection is authenticated, i.e. after OK +        was received by the client. This command may only be sent on +        transports that support Unix file descriptor passing. +      </para> +      <para> +        On receiving NEGOTIATE_UNIX_FD the server must respond with +        either AGREE_UNIX_FD or ERROR. It shall respond the former if +        the transport chosen supports Unix file descriptor passing and +        the server supports this feature. It shall respond the latter +        if the transport does not support Unix file descriptor +        passing, the server does not support this feature, or the +        server decides not to enable file descriptor passing due to +        security or other reasons. +      </para> +    </sect2> +    <sect2 id="auth-command-agree-unix-fd"> +      <title>AGREE_UNIX_FD Command</title> +      <para> +        The AGREE_UNIX_FD command indicates that the server supports +        Unix file descriptor passing. This command may only be sent +        after the connection is authenticated, and the client sent +        NEGOTIATE_UNIX_FD to enable Unix file descriptor passing. This +        command may only be sent on transports that support Unix file +        descriptor passing. +      </para> +      <para> +        On receiving AGREE_UNIX_FD the client must respond with BEGIN, +        followed by its stream of messages, or by disconnecting.  The +        server must not accept additional commands using this protocol +        after the BEGIN command has been received. Further +        communication will be a stream of D-Bus messages (optionally +        encrypted, as negotiated) rather than this protocol. +      </para> +    </sect2> +    <sect2 id="auth-command-future"> +      <title>Future Extensions</title> +      <para> +        Future extensions to the authentication and negotiation +        protocol are possible. For that new commands may be +        introduced. If a client or server receives an unknown command +        it shall respond with ERROR and not consider this fatal. New +        commands may be introduced both before, and after +        authentication, i.e. both before and after the OK command. +      </para> +    </sect2>      <sect2 id="auth-examples">        <title>Authentication examples</title> @@ -1669,6 +1755,30 @@              C: BEGIN            </programlisting>  	</figure> +        <figure> +	  <title>Example of successful magic cookie authentication with successful negotiation of Unix FD passing</title> +	  <programlisting> +            (MAGIC_COOKIE is a made up mechanism) + +            C: AUTH MAGIC_COOKIE 3138363935333137393635383634 +            S: OK 1234deadbeef +            C: NEGOTIATE_UNIX_FD +            S: AGREE_UNIX_FD +            C: BEGIN +          </programlisting> +	</figure> +        <figure> +	  <title>Example of successful magic cookie authentication with unsuccessful negotiation of Unix FD passing</title> +	  <programlisting> +            (MAGIC_COOKIE is a made up mechanism) + +            C: AUTH MAGIC_COOKIE 3138363935333137393635383634 +            S: OK 1234deadbeef +            C: NEGOTIATE_UNIX_FD +            S: ERROR +            C: BEGIN +          </programlisting> +	</figure>        </para>      </sect2>      <sect2 id="auth-states"> @@ -4078,4 +4188,3 @@    </glossary>  </article> - | 
