summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dbus/dbus-marshal-validate.h1
-rw-r--r--dbus/dbus-message-internal.h15
-rw-r--r--dbus/dbus-message-private.h29
-rw-r--r--dbus/dbus-message-util.c108
-rw-r--r--dbus/dbus-message.c434
-rw-r--r--dbus/dbus-transport-protected.h6
-rw-r--r--dbus/dbus-transport-socket.c104
-rw-r--r--dbus/dbus-transport.c24
-rw-r--r--dbus/dbus-transport.h2
9 files changed, 680 insertions, 43 deletions
diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h
index 29419991..bf68ecd6 100644
--- a/dbus/dbus-marshal-validate.h
+++ b/dbus/dbus-marshal-validate.h
@@ -113,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-message-internal.h b/dbus/dbus-message-internal.h
index 2e995b47..032268f1 100644
--- a/dbus/dbus-message-internal.h
+++ b/dbus/dbus-message-internal.h
@@ -34,6 +34,9 @@ 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 *messgage,
+ const int **fds,
+ unsigned *n_fds);
void _dbus_message_lock (DBusMessage *message);
void _dbus_message_unlock (DBusMessage *message);
@@ -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 c1e368f7..1e8b107a 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,7 +111,7 @@ 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. */
long size_counter_delta; /**< Size we incremented the size counters by. */
@@ -111,6 +122,15 @@ 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 */
+#endif
};
dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
@@ -118,6 +138,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 46cbe4e3..1b139436 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;
@@ -939,6 +994,9 @@ _dbus_message_test (const char *test_data_dir)
unsigned char v_BYTE;
unsigned char v2_BYTE;
dbus_bool_t v_BOOLEAN;
+#ifdef HAVE_UNIX_FD_PASSING
+ int v_UNIX_FD;
+#endif
message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
"/org/freedesktop/TestPath",
@@ -1057,6 +1115,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,
@@ -1090,6 +1151,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;
@@ -1124,7 +1186,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));
@@ -1201,6 +1272,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 */
@@ -1217,7 +1302,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. */
@@ -1228,7 +1320,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);
@@ -1267,10 +1359,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();
/* Load all the sample messages from the message factory */
{
@@ -1304,9 +1397,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 edae4258..54bceb0d 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.
*
@@ -494,6 +522,33 @@ dbus_message_get_cached (void)
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)
@@ -528,6 +583,10 @@ dbus_message_cache_or_finalize (DBusMessage *message)
free_size_counter, message);
_dbus_list_clear (&message->size_counters);
+#ifdef HAVE_UNIX_FD_PASSING
+ close_unix_fds(message->unix_fds, &message->n_unix_fds);
+#endif
+
was_cached = FALSE;
_DBUS_LOCK (message_cache);
@@ -634,6 +693,12 @@ _dbus_message_iter_check (DBusMessageRealIter *iter)
* dbus_message_get_args() is the place to go for complete
* documentation.
*
+ * 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().
+ *
+ * @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 +738,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 +803,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*);
@@ -943,6 +1040,11 @@ dbus_message_finalize (DBusMessage *message)
_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 +1071,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;
@@ -981,6 +1088,10 @@ dbus_message_new_empty_header (void)
message->size_counter_delta = 0;
message->changed_stamp = 0;
+#ifdef HAVE_UNIX_FD_PASSING
+ message->n_unix_fds = 0;
+#endif
+
if (!from_cache)
_dbus_data_slot_list_init (&message->slot_list);
@@ -1308,8 +1419,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 +1460,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;
@@ -1555,8 +1693,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;
@@ -1973,8 +2112,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);
+ }
}
/**
@@ -2047,7 +2208,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);
@@ -2217,6 +2378,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.
@@ -2248,7 +2443,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;
@@ -2302,7 +2540,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);
@@ -3321,6 +3559,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);
@@ -3331,6 +3575,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;
}
@@ -3360,6 +3610,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);
@@ -3419,6 +3673,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.
@@ -3458,6 +3787,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;
@@ -3527,6 +3857,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))
@@ -3756,6 +4139,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-transport-protected.h b/dbus/dbus-transport-protected.h
index 4d56a72f..023549d0 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>
@@ -71,6 +73,10 @@ struct DBusTransportVTable
/**< Get socket file descriptor */
};
+/** How many unix file descriptors may be queued up before they are
+ handed off to messages */
+#define DBUS_MAX_QUEUED_FDS 1024
+
/**
* Object representing a transport such as a socket.
* A transport can shuttle messages from point A to point B,
diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c
index 6d7c89cd..c9d4d93c 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
@@ -551,6 +550,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 +590,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 && transport->can_pass_unix_fd)
{
+ /* 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));
+ }
}
}
@@ -700,6 +728,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
@@ -744,10 +775,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 (transport->can_pass_unix_fd)
+ {
+ 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);
@@ -1184,7 +1242,11 @@ _dbus_transport_new_for_socket (int fd,
&socket_vtable,
server_guid, address))
goto failed_4;
-
+
+#ifdef HAVE_UNIX_FD_PASSING
+ socket_transport->base.can_pass_unix_fd = _dbus_socket_can_pass_unix_fd(fd);
+#endif
+
socket_transport->fd = fd;
socket_transport->message_bytes_written = 0;
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index 35b7027d..97ee0e9b 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
@@ -180,7 +182,13 @@ _dbus_transport_init_base (DBusTransport *transport,
/* credentials read from socket if any */
transport->credentials = creds;
-
+
+#ifdef HAVE_UNIX_FD_PASSING
+ transport->can_pass_unix_fd = FALSE;
+ transport->unix_fds = NULL;
+ transport->n_unix_fds = 0;
+#endif
+
_dbus_counter_set_notify (transport->live_messages_size,
transport->max_live_messages_size,
live_messages_size_notify,
@@ -188,7 +196,7 @@ _dbus_transport_init_base (DBusTransport *transport,
if (transport->address)
_dbus_verbose ("Initialized transport on address %s\n", transport->address);
-
+
return TRUE;
}
@@ -803,6 +811,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.
*
diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h
index 691763ca..4554faf3 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,