2003-06-04 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Wed, 4 Jun 2003 05:20:20 +0000 (05:20 +0000)
committerHavoc Pennington <hp@redhat.com>
Wed, 4 Jun 2003 05:20:20 +0000 (05:20 +0000)
* 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.

12 files changed:
ChangeLog
bus/system.conf.in
configure.in
dbus/dbus-server-unix.c
dbus/dbus-server-unix.h
dbus/dbus-server.c
dbus/dbus-sysdeps.c
dbus/dbus-sysdeps.h
dbus/dbus-transport-unix.c
dbus/dbus-transport-unix.h
dbus/dbus-transport.c
doc/dbus-specification.sgml

index 9d92f6b..7874185 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2003-06-04  Havoc Pennington  <hp@pobox.com>
+
+       * 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  <hp@pobox.com>
 
        * tools/dbus-cleanup-sockets.c: add utility to clean up sockets 
index cab68d3..bd454ff 100644 (file)
   <!-- Only allow socket-credentials-based authentication -->
   <auth>EXTERNAL</auth>
 
-  <!-- Only listen on a local socket -->
-  <listen>unix:path=@DBUS_SYSTEM_SOCKET@</listen>
+  <!-- Only listen on a local socket. (abstract=/path/to/socket 
+       means use abstract namespace, don't really create filesystem 
+       file; only Linux supports this. Use path=/whatever on other 
+       systems.) -->
+  <listen>unix:@DBUS_PATH_OR_ABSTRACT@=@DBUS_SYSTEM_SOCKET@</listen>
 
   <policy context="default">
     <!-- Deny everything then punch holes -->
index a47d110..613bf2d 100644 (file)
@@ -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 <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+]],
+[[
+  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}
index fe4dbaa..036446a 100644 (file)
@@ -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)
index 1d038db..95f0b75 100644 (file)
@@ -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,
index e62c028..4007d7a 100644 (file)
@@ -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)
index ab79a72..35900d7 100644 (file)
@@ -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);
   
index 7a4696f..cfe0cd2 100644 (file)
@@ -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,
index 5df1c46..e5e446c 100644 (file)
@@ -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);
index ef7ac9b..8d3f5b1 100644 (file)
@@ -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,
index af1cb42..59ec6ea 100644 (file)
@@ -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)
        {
index d510ca6..8b3a1d3 100644 (file)
     </para>
     <para>
       [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.
     </para>
   </sect1>