From a70b042f0dcebb10689975c65a2c45b52b7a4437 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 4 Jun 2003 05:20:20 +0000 Subject: 2003-06-04 Havoc Pennington * dbus/dbus-server.c (dbus_server_listen): allow abstract sockets using unix:abstract=/foo, and when listening in a tmpdir i.e. unix:tmpdir=/tmp, always use abstract sockets if we can. * dbus/dbus-transport.c (_dbus_transport_open): support unix:abstract=/foo * dbus/dbus-server-unix.c (_dbus_server_new_for_domain_socket): support abstract sockets * dbus/dbus-transport-unix.c (_dbus_transport_new_for_domain_socket): support abstract sockets * dbus/dbus-sysdeps.c (_dbus_connect_unix_socket): add "abstract" toggle as an argument, implement abstract namespace support (_dbus_listen_unix_socket): ditto * configure.in: add --enable-abstract-sockets and implement a configure check for autodetection of the right value. --- ChangeLog | 22 +++++++++++ bus/system.conf.in | 7 +++- configure.in | 90 +++++++++++++++++++++++++++++++++++++++----- dbus/dbus-server-unix.c | 9 ++++- dbus/dbus-server-unix.h | 1 + dbus/dbus-server.c | 32 +++++++++++----- dbus/dbus-sysdeps.c | 91 ++++++++++++++++++++++++++++++++------------- dbus/dbus-sysdeps.h | 2 + dbus/dbus-transport-unix.c | 14 +++++-- dbus/dbus-transport-unix.h | 1 + dbus/dbus-transport.c | 20 ++++++++-- doc/dbus-specification.sgml | 9 ++--- 12 files changed, 238 insertions(+), 60 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9d92f6bf..7874185a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003-06-04 Havoc Pennington + + * dbus/dbus-server.c (dbus_server_listen): allow abstract sockets + using unix:abstract=/foo, and when listening in a tmpdir + i.e. unix:tmpdir=/tmp, always use abstract sockets if we can. + + * dbus/dbus-transport.c (_dbus_transport_open): support + unix:abstract=/foo + + * dbus/dbus-server-unix.c (_dbus_server_new_for_domain_socket): + support abstract sockets + + * dbus/dbus-transport-unix.c + (_dbus_transport_new_for_domain_socket): support abstract sockets + + * dbus/dbus-sysdeps.c (_dbus_connect_unix_socket): add "abstract" + toggle as an argument, implement abstract namespace support + (_dbus_listen_unix_socket): ditto + + * configure.in: add --enable-abstract-sockets and implement + a configure check for autodetection of the right value. + 2003-06-01 Havoc Pennington * tools/dbus-cleanup-sockets.c: add utility to clean up sockets diff --git a/bus/system.conf.in b/bus/system.conf.in index cab68d3a..bd454ff3 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -26,8 +26,11 @@ EXTERNAL - - unix:path=@DBUS_SYSTEM_SOCKET@ + + unix:@DBUS_PATH_OR_ABSTRACT@=@DBUS_SYSTEM_SOCKET@ diff --git a/configure.in b/configure.in index a47d1109..613bf2dc 100644 --- a/configure.in +++ b/configure.in @@ -23,15 +23,17 @@ AC_ISC_POSIX AC_HEADER_STDC AM_PROG_LIBTOOL -AC_ARG_ENABLE(qt, [ --enable-qt enable Qt-friendly client library],enable_qt=$enableval,enable_qt=auto) -AC_ARG_ENABLE(glib, [ --enable-glib enable GLib-friendly client library],enable_glib=$enableval,enable_glib=auto) -AC_ARG_ENABLE(tests, [ --enable-tests enable unit test code],enable_tests=$enableval,enable_tests=$USE_MAINTAINER_MODE) -AC_ARG_ENABLE(ansi, [ --enable-ansi enable -ansi -pedantic gcc flags],enable_ansi=$enableval,enable_ansi=no) -AC_ARG_ENABLE(verbose-mode, [ --enable-verbose-mode support verbose debug mode],enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE) -AC_ARG_ENABLE(asserts, [ --enable-asserts include assertion checks],enable_asserts=$enableval,enable_asserts=$USE_MAINTAINER_MODE) -AC_ARG_ENABLE(checks, [ --enable-checks include sanity checks on public API],enable_checks=$enableval,enable_checks=yes) -AC_ARG_ENABLE(docs, [ --enable-docs build documentation (requires Doxygen and jade)],enable_docs=$enableval,enable_docs=auto) -AC_ARG_ENABLE(gcov, [ --enable-gcov compile with coverage profiling instrumentation (gcc only)],enable_gcov=$enableval,enable_gcov=no) +AC_ARG_ENABLE(qt, [ --enable-qt enable Qt-friendly client library],enable_qt=$enableval,enable_qt=auto) +AC_ARG_ENABLE(glib, [ --enable-glib enable GLib-friendly client library],enable_glib=$enableval,enable_glib=auto) +AC_ARG_ENABLE(tests, [ --enable-tests enable unit test code],enable_tests=$enableval,enable_tests=$USE_MAINTAINER_MODE) +AC_ARG_ENABLE(ansi, [ --enable-ansi enable -ansi -pedantic gcc flags],enable_ansi=$enableval,enable_ansi=no) +AC_ARG_ENABLE(verbose-mode, [ --enable-verbose-mode support verbose debug mode],enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE) +AC_ARG_ENABLE(asserts, [ --enable-asserts include assertion checks],enable_asserts=$enableval,enable_asserts=$USE_MAINTAINER_MODE) +AC_ARG_ENABLE(checks, [ --enable-checks include sanity checks on public API],enable_checks=$enableval,enable_checks=yes) +AC_ARG_ENABLE(docs, [ --enable-docs build documentation (requires Doxygen and jade)],enable_docs=$enableval,enable_docs=auto) +AC_ARG_ENABLE(gcov, [ --enable-gcov compile with coverage profiling instrumentation (gcc only)],enable_gcov=$enableval,enable_gcov=no) +AC_ARG_ENABLE(abstract-sockets, [ --enable-abstract-sockets use abstract socket namespace (linux only)],enable_abstract_sockets=$enableval,enable_abstract_sockets=auto) + AC_ARG_WITH(xml, [ --with-xml=[libxml/expat] XML library to use]) AC_ARG_WITH(init-scripts, [ --with-init-scripts=[redhat] Style of init scripts to install]) @@ -317,6 +319,75 @@ if test x$dbus_have_struct_cmsgcred = xyes; then fi +#### Abstract sockets + +AC_MSG_CHECKING(abstract socket namespace) +AC_LANG_PUSH(C) +AC_RUN_IFELSE([AC_LANG_PROGRAM( +[[ +#include +#include +#include +#include +#include +#include +#include +]], +[[ + int listen_fd; + struct sockaddr_un addr; + + listen_fd = socket (PF_UNIX, SOCK_STREAM, 0); + + if (listen_fd < 0) + { + fprintf (stderr, "socket() failed: %s\n", strerror (errno)); + exit (1); + } + + memset (&addr, '\0', sizeof (addr)); + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, "X/tmp/dbus-fake-socket-path-used-in-configure-test"); + addr.sun_path[0] = '\0'; /* this is what makes it abstract */ + + if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0) + { + fprintf (stderr, "Abstract socket namespace bind() failed: %s\n", + strerror (errno)); + exit (1); + } + else + exit (0); +]])], + [have_abstract_sockets=yes], + [have_abstract_sockets=no]) +AC_LANG_POP(C) +AC_MSG_RESULT($have_abstract_sockets) + +if test x$enable_abstract_sockets = xyes; then + if test x$have_abstract_sockets = xno; then + AC_MSG_ERROR([Abstract sockets explicitly required, and support not detected.]) + fi +fi + +if test x$enable_abstract_sockets = xno; then + have_abstract_sockets=no; +fi + +if test x$have_abstract_sockets = xyes ; then + abstract_sockets=1 + DBUS_PATH_OR_ABSTRACT=abstract +else + abstract_sockets=0 + DBUS_PATH_OR_ABSTRACT=path +fi + +AC_DEFINE_UNQUOTED(HAVE_ABSTRACT_SOCKETS, $abstract_sockets, [Have abstract socket namespace]) + +# this is used in addresses to prefer abstract, e.g. +# unix:path=/foo or unix:abstract=/foo +AC_SUBST(DBUS_PATH_OR_ABSTRACT) + #### Sort out XML library # see what we have @@ -692,6 +763,7 @@ echo " Building documentation: ${enable_docs} Using XML parser: ${with_xml} Init scripts style: ${with_init_scripts} + Abstract socket names: ${have_abstract_sockets} System bus socket: ${DBUS_SYSTEM_SOCKET} System bus PID file: ${DBUS_SYSTEM_PID_FILE} Session bus socket dir: ${DBUS_SESSION_SOCKET_DIR} diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c index fe4dbaa4..036446af 100644 --- a/dbus/dbus-server-unix.c +++ b/dbus/dbus-server-unix.c @@ -268,11 +268,13 @@ _dbus_server_new_for_fd (int fd, * Creates a new server listening on the given Unix domain socket. * * @param path the path for the domain socket. + * @param abstract #TRUE to use abstract socket namespace * @param error location to store reason for failure. * @returns the new server, or #NULL on failure. */ DBusServer* _dbus_server_new_for_domain_socket (const char *path, + dbus_bool_t abstract, DBusError *error) { DBusServer *server; @@ -289,7 +291,10 @@ _dbus_server_new_for_domain_socket (const char *path, return NULL; } - if (!_dbus_string_append (&address, "unix:path=") || + if ((abstract && + !_dbus_string_append (&address, "unix:abstract=")) || + (!abstract && + !_dbus_string_append (&address, "unix:path=")) || !_dbus_string_append (&address, path)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); @@ -303,7 +308,7 @@ _dbus_server_new_for_domain_socket (const char *path, goto failed_0; } - listen_fd = _dbus_listen_unix_socket (path, error); + listen_fd = _dbus_listen_unix_socket (path, abstract, error); _dbus_fd_set_close_on_exec (listen_fd); if (listen_fd < 0) diff --git a/dbus/dbus-server-unix.h b/dbus/dbus-server-unix.h index 1d038db0..95f0b756 100644 --- a/dbus/dbus-server-unix.h +++ b/dbus/dbus-server-unix.h @@ -31,6 +31,7 @@ DBUS_BEGIN_DECLS; DBusServer* _dbus_server_new_for_fd (int fd, const DBusString *address); DBusServer* _dbus_server_new_for_domain_socket (const char *path, + dbus_bool_t abstract, DBusError *error); DBusServer* _dbus_server_new_for_tcp_socket (const char *host, dbus_uint32_t port, diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c index e62c0284..4007d7a5 100644 --- a/dbus/dbus-server.c +++ b/dbus/dbus-server.c @@ -295,17 +295,20 @@ dbus_server_listen (const char *address, { const char *path = dbus_address_entry_get_value (entries[i], "path"); const char *tmpdir = dbus_address_entry_get_value (entries[i], "tmpdir"); + const char *abstract = dbus_address_entry_get_value (entries[i], "abstract"); - if (path == NULL && tmpdir == NULL) + if (path == NULL && tmpdir == NULL && abstract == NULL) { address_problem_type = "unix"; - address_problem_field = "path or tmpdir"; + address_problem_field = "path or tmpdir or abstract"; goto bad_address; } - if (path && tmpdir) + if ((path && tmpdir) || + (path && abstract) || + (tmpdir && abstract)) { - address_problem_other = "cannot specify both \"path\" and \"tmpdir\" at the same time"; + address_problem_other = "cannot specify two of \"path\" and \"tmpdir\" and \"abstract\" at the same time"; goto bad_address; } @@ -339,14 +342,22 @@ dbus_server_listen (const char *address, goto out; } - /* FIXME - we will unconditionally unlink() the path. - * unlink() does not follow symlinks, but would like - * independent confirmation this is safe enough. See - * also _dbus_listen_unix_socket() and comments therein. + /* FIXME - we will unconditionally unlink() the path if + * we don't support abstract namespace. unlink() does + * not follow symlinks, but would like independent + * confirmation this is safe enough. See also + * _dbus_listen_unix_socket() and comments therein. */ + + /* Always use abstract namespace if possible with tmpdir */ server = _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path), +#ifdef HAVE_ABSTRACT_SOCKETS + TRUE, +#else + FALSE, +#endif error); _dbus_string_free (&full_path); @@ -354,7 +365,10 @@ dbus_server_listen (const char *address, } else { - server = _dbus_server_new_for_domain_socket (path, error); + if (path) + server = _dbus_server_new_for_domain_socket (path, FALSE, error); + else + server = _dbus_server_new_for_domain_socket (abstract, TRUE, error); } } else if (strcmp (method, "tcp") == 0) diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index ab79a722..35900d70 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -373,13 +373,19 @@ _dbus_write_two (int fd, * Creates a socket and connects it to the UNIX domain socket at the * given path. The connection fd is returned, and is set up as * nonblocking. + * + * Uses abstract sockets instead of filesystem-linked sockets if + * requested (it's possible only on Linux; see "man 7 unix" on Linux). + * On non-Linux abstract socket usage always fails. * * @param path the path to UNIX domain socket + * @param abstract #TRUE to use abstract namespace * @param error return location for error code * @returns connection file descriptor or -1 on error */ int _dbus_connect_unix_socket (const char *path, + dbus_bool_t abstract, DBusError *error) { int fd; @@ -401,7 +407,23 @@ _dbus_connect_unix_socket (const char *path, _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; - strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); + + if (abstract) + { +#ifdef HAVE_ABSTRACT_SOCKETS + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2); +#else /* HAVE_ABSTRACT_SOCKETS */ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Operating system does not support abstract socket namespace\n"); + close (fd); + return -1; +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + } + else + { + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); + } if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) { @@ -434,18 +456,19 @@ _dbus_connect_unix_socket (const char *path, * then listens on the socket. The socket is * set to be nonblocking. * - * @todo we'd like to be able to use the abstract namespace on linux - * (see "man 7 unix"). The question is whether to silently move all - * paths into that namespace if we can (I think that's best) or to - * require it to be specified explicitly in the dbus address. Also, - * need to sort out how to check for abstract namespace support. + * Uses abstract sockets instead of filesystem-linked + * sockets if requested (it's possible only on Linux; + * see "man 7 unix" on Linux). + * On non-Linux abstract socket usage always fails. * * @param path the socket name + * @param abstract #TRUE to use abstract namespace * @param error return location for errors * @returns the listening file descriptor or -1 on error */ int _dbus_listen_unix_socket (const char *path, + dbus_bool_t abstract, DBusError *error) { int listen_fd; @@ -463,27 +486,43 @@ _dbus_listen_unix_socket (const char *path, return -1; } - /* FIXME discussed security implications of this with Nalin, - * and we couldn't think of where it would kick our ass, but - * it still seems a bit sucky. It also has non-security suckage; - * really we'd prefer to exit if the socket is already in use. - * But there doesn't seem to be a good way to do this. - * - * Just to be extra careful, I threw in the stat() - clearly - * the stat() can't *fix* any security issue, but it at least - * avoids inadvertent/accidental data loss. - */ - { - struct stat sb; - - if (stat (path, &sb) == 0 && - S_ISSOCK (sb.st_mode)) - unlink (path); - } - _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; - strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); + + if (abstract) + { +#ifdef HAVE_ABSTRACT_SOCKETS + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2); +#else /* HAVE_ABSTRACT_SOCKETS */ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Operating system does not support abstract socket namespace\n"); + close (listen_fd); + return -1; +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + } + else + { + /* FIXME discussed security implications of this with Nalin, + * and we couldn't think of where it would kick our ass, but + * it still seems a bit sucky. It also has non-security suckage; + * really we'd prefer to exit if the socket is already in use. + * But there doesn't seem to be a good way to do this. + * + * Just to be extra careful, I threw in the stat() - clearly + * the stat() can't *fix* any security issue, but it at least + * avoids inadvertent/accidental data loss. + */ + { + struct stat sb; + + if (stat (path, &sb) == 0 && + S_ISSOCK (sb.st_mode)) + unlink (path); + } + + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); + } if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0) { @@ -513,7 +552,7 @@ _dbus_listen_unix_socket (const char *path, /* Try opening up the permissions, but if we can't, just go ahead * and continue, maybe it will be good enough. */ - if (chmod (path, 0777) < 0) + if (!abstract && chmod (path, 0777) < 0) _dbus_warn ("Could not set mode 0777 on socket %s\n", path); diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 7a4696f3..cfe0cd25 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -105,8 +105,10 @@ typedef struct } DBusCredentials; int _dbus_connect_unix_socket (const char *path, + dbus_bool_t abstract, DBusError *error); int _dbus_listen_unix_socket (const char *path, + dbus_bool_t abstract, DBusError *error); int _dbus_connect_tcp_socket (const char *host, dbus_uint32_t port, diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index 5df1c461..e5e446c2 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -1080,12 +1080,17 @@ _dbus_transport_new_for_fd (int fd, * Creates a new transport for the given Unix domain socket * path. This creates a client-side of a transport. * + * @todo once we add a way to escape paths in a dbus + * address, this function needs to do escaping. + * * @param path the path to the domain socket. + * @param abstract #TRUE to use abstract socket namespace * @param error address where an error can be returned. * @returns a new transport, or #NULL on failure. */ DBusTransport* _dbus_transport_new_for_domain_socket (const char *path, + dbus_bool_t abstract, DBusError *error) { int fd; @@ -1101,15 +1106,18 @@ _dbus_transport_new_for_domain_socket (const char *path, } fd = -1; - - if (!_dbus_string_append (&address, "unix:path=") || + + if ((abstract && + !_dbus_string_append (&address, "unix:abstract=")) || + (!abstract && + !_dbus_string_append (&address, "unix:path=")) || !_dbus_string_append (&address, path)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto failed_0; } - fd = _dbus_connect_unix_socket (path, error); + fd = _dbus_connect_unix_socket (path, abstract, error); if (fd < 0) { _DBUS_ASSERT_ERROR_IS_SET (error); diff --git a/dbus/dbus-transport-unix.h b/dbus/dbus-transport-unix.h index ef7ac9b1..8d3f5b10 100644 --- a/dbus/dbus-transport-unix.h +++ b/dbus/dbus-transport-unix.h @@ -31,6 +31,7 @@ DBusTransport* _dbus_transport_new_for_fd (int fd, dbus_bool_t server, const DBusString *address); DBusTransport* _dbus_transport_new_for_domain_socket (const char *path, + dbus_bool_t abstract, DBusError *error); DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host, dbus_int32_t port, diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index af1cb421..59ec6ea1 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -243,21 +243,33 @@ _dbus_transport_open (const char *address, { const char *path = dbus_address_entry_get_value (entries[i], "path"); const char *tmpdir = dbus_address_entry_get_value (entries[i], "tmpdir"); - + const char *abstract = dbus_address_entry_get_value (entries[i], "abstract"); + if (tmpdir != NULL) { address_problem_other = "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on"; goto bad_address; } - if (path == NULL) + if (path == NULL && abstract == NULL) { address_problem_type = "unix"; - address_problem_field = "path"; + address_problem_field = "path or abstract"; goto bad_address; } - transport = _dbus_transport_new_for_domain_socket (path, &tmp_error); + if (path != NULL && abstract != NULL) + { + address_problem_other = "can't specify both \"path\" and \"abstract\" options in an address"; + goto bad_address; + } + + if (path) + transport = _dbus_transport_new_for_domain_socket (path, FALSE, + &tmp_error); + else + transport = _dbus_transport_new_for_domain_socket (abstract, TRUE, + &tmp_error); } else if (strcmp (method, "tcp") == 0) { diff --git a/doc/dbus-specification.sgml b/doc/dbus-specification.sgml index d510ca6a..8b3a1d36 100644 --- a/doc/dbus-specification.sgml +++ b/doc/dbus-specification.sgml @@ -898,11 +898,10 @@ [FIXME we need to specify in detail each transport and its possible arguments] - Currently, a transport over local UNIX sockets exists, a debug - transport that only works in-process and therefore can be used - for for unit testing also exists. It is possible that other - transports are added, such as a TCP/IP transport, and a - transport that works over X11. + Current transports include: unix domain sockets (including + abstract namespace on linux), TCP/IP, and a debug/testing transport using + in-process pipes. Future possible transports include one that + tunnels over X11 protocol. -- cgit