* configure.in: Add test/name-test/Makefile to the generated
[platform/upstream/dbus.git] / dbus / dbus-sysdeps.c
index 83c1ce4..030d080 100644 (file)
@@ -57,7 +57,9 @@
 #ifdef HAVE_BACKTRACE
 #include <execinfo.h>
 #endif
-
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -396,6 +398,7 @@ _dbus_connect_unix_socket (const char     *path,
                            DBusError      *error)
 {
   int fd;
+  size_t path_len;
   struct sockaddr_un addr;  
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -417,15 +420,23 @@ _dbus_connect_unix_socket (const char     *path,
 
   _DBUS_ZERO (addr);
   addr.sun_family = AF_UNIX;
+  path_len = strlen (path);
 
   if (abstract)
     {
 #ifdef HAVE_ABSTRACT_SOCKETS
-      /* remember that abstract names aren't nul-terminated so we rely
-       * on sun_path being filled in with zeroes above.
-       */
       addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
-      strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2);
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (fd);
+          return -1;
+       }
+       
+      strncpy (&addr.sun_path[1], path, path_len);
       /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
 #else /* HAVE_ABSTRACT_SOCKETS */
       dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
@@ -436,10 +447,18 @@ _dbus_connect_unix_socket (const char     *path,
     }
   else
     {
-      strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Socket name too long\n");
+          close (fd);
+          return -1;
+       }
+
+      strncpy (addr.sun_path, path, path_len);
     }
   
-  if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
+  if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
     {      
       dbus_set_error (error,
                       _dbus_error_from_errno (errno),
@@ -487,6 +506,7 @@ _dbus_listen_unix_socket (const char     *path,
 {
   int listen_fd;
   struct sockaddr_un addr;
+  size_t path_len;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
 
@@ -505,6 +525,7 @@ _dbus_listen_unix_socket (const char     *path,
 
   _DBUS_ZERO (addr);
   addr.sun_family = AF_UNIX;
+  path_len = strlen (path);
   
   if (abstract)
     {
@@ -513,7 +534,17 @@ _dbus_listen_unix_socket (const char     *path,
        * on sun_path being filled in with zeroes above.
        */
       addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
-      strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2);
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (listen_fd);
+          return -1;
+       }
+      
+      strncpy (&addr.sun_path[1], path, path_len);
       /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
 #else /* HAVE_ABSTRACT_SOCKETS */
       dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
@@ -542,10 +573,18 @@ _dbus_listen_unix_socket (const char     *path,
           unlink (path);
       }
 
-      strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (listen_fd);
+          return -1;
+       }
+       
+      strncpy (addr.sun_path, path, path_len);
     }
   
-  if (bind (listen_fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
+  if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
     {
       dbus_set_error (error, _dbus_error_from_errno (errno),
                       "Failed to bind socket \"%s\": %s",
@@ -742,12 +781,40 @@ write_credentials_byte (int             server_fd,
 {
   int bytes_written;
   char buf[1] = { '\0' };
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  struct {
+         struct cmsghdr hdr;
+         struct cmsgcred cred;
+  } cmsg;
+  struct iovec iov;
+  struct msghdr msg;
+#endif
+
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  iov.iov_base = buf;
+  iov.iov_len = 1;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
+  memset (&cmsg, 0, sizeof (cmsg));
+  cmsg.hdr.cmsg_len = sizeof (cmsg);
+  cmsg.hdr.cmsg_level = SOL_SOCKET;
+  cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   
  again:
 
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  bytes_written = sendmsg (server_fd, &msg, 0);
+#else
   bytes_written = write (server_fd, buf, 1);
+#endif
 
   if (bytes_written < 0 && errno == EINTR)
     goto again;
@@ -801,8 +868,10 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   char buf;
 
 #ifdef HAVE_CMSGCRED 
-  char cmsgmem[CMSG_SPACE (sizeof (struct cmsgcred))];
-  struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
+  struct {
+         struct cmsghdr hdr;
+         struct cmsgcred cred;
+  } cmsg;
 #endif
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -837,9 +906,9 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   msg.msg_iovlen = 1;
 
 #ifdef HAVE_CMSGCRED
-  memset (cmsgmem, 0, sizeof (cmsgmem));
-  msg.msg_control = cmsgmem;
-  msg.msg_controllen = sizeof (cmsgmem);
+  memset (&cmsg, 0, sizeof (cmsg));
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
 #endif
 
  again:
@@ -862,10 +931,10 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
     }
 
 #ifdef HAVE_CMSGCRED
-  if (cmsg->cmsg_len < sizeof (cmsgmem) || cmsg->cmsg_type != SCM_CREDS)
+  if (cmsg.hdr.cmsg_len < sizeof (cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS)
     {
-      dbus_set_error (error, DBUS_ERROR_FAILED);
-      _dbus_verbose ("Message from recvmsg() was not SCM_CREDS\n");
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Message from recvmsg() was not SCM_CREDS");
       return FALSE;
     }
 #endif
@@ -890,14 +959,36 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
                       cr_len, (int) sizeof (cr), _dbus_strerror (errno));
       }
 #elif defined(HAVE_CMSGCRED)
-    struct cmsgcred *cred;
-
-    cred = (struct cmsgcred *) CMSG_DATA (cmsg);
-
-    credentials->pid = cred->cmcred_pid;
-    credentials->uid = cred->cmcred_euid;
-    credentials->gid = cred->cmcred_groups[0];
-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
+    credentials->pid = cmsg.cred.cmcred_pid;
+    credentials->uid = cmsg.cred.cmcred_euid;
+    credentials->gid = cmsg.cred.cmcred_groups[0];
+#elif defined(HAVE_GETPEEREID)
+    uid_t euid;
+    gid_t egid;
+    if (getpeereid (client_fd, &euid, &egid) == 0)
+      {
+        credentials->uid = euid;
+        credentials->gid = egid;
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
+      }
+#elif defined(HAVE_GETPEERUCRED)
+    ucred_t * ucred = NULL;
+    if (getpeerucred (client_fd, &ucred) == 0)
+      {
+        credentials->pid = ucred_getpid (ucred);
+        credentials->uid = ucred_geteuid (ucred);
+        credentials->gid = ucred_getegid (ucred);
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
+      }
+    if (ucred != NULL)
+      ucred_free (ucred);
+#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEEREID && !HAVE_GETPEERUCRED */
     _dbus_verbose ("Socket credentials not supported on this OS\n");
 #endif
   }
@@ -1131,8 +1222,43 @@ _dbus_string_parse_int (const DBusString *str,
   return TRUE;
 }
 
-#ifdef DBUS_BUILD_TESTS
-/* Not currently used, so only built when tests are enabled */
+/**
+* Checks to make sure the given directory is 
+* private to the user 
+*
+* @param dir the name of the directory
+* @param error error return
+* @returns #FALSE on failure
+**/
+dbus_bool_t
+_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error)
+{
+  const char *directory;
+  struct stat sb;
+       
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+    
+  directory = _dbus_string_get_const_data (dir);
+       
+  if (stat (directory, &sb) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "%s", _dbus_strerror (errno));
+   
+      return FALSE;
+    }
+    
+  if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) ||
+      (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                     "%s directory is not private to the user", directory);
+      return FALSE;
+    }
+    
+  return TRUE;
+}
+
 /**
  * Parses an unsigned integer contained in a DBusString. Either return
  * parameter may be #NULL if you aren't interested in it. The integer
@@ -1171,7 +1297,6 @@ _dbus_string_parse_uint (const DBusString *str,
 
   return TRUE;
 }
-#endif /* DBUS_BUILD_TESTS */
 
 #ifdef DBUS_BUILD_TESTS
 static dbus_bool_t
@@ -1434,7 +1559,7 @@ fill_user_info (DBusUserInfo       *info,
    * checks
    */
   
-#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
+#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
   {
     struct passwd *p;
     int result;
@@ -1442,8 +1567,8 @@ fill_user_info (DBusUserInfo       *info,
     struct passwd p_str;
 
     p = NULL;
-#ifdef HAVE_POSIX_GETPWNAME_R
-    if (uid >= 0)
+#ifdef HAVE_POSIX_GETPWNAM_R
+    if (uid != DBUS_UID_UNSET)
       result = getpwuid_r (uid, &p_str, buf, sizeof (buf),
                            &p);
     else
@@ -1455,7 +1580,7 @@ fill_user_info (DBusUserInfo       *info,
     else
       p = getpwnam_r (username_c, &p_str, buf, sizeof (buf));
     result = 0;
-#endif /* !HAVE_POSIX_GETPWNAME_R */
+#endif /* !HAVE_POSIX_GETPWNAM_R */
     if (result == 0 && p == &p_str)
       {
         if (!fill_user_info_from_passwd (p, info, error))
@@ -2327,16 +2452,13 @@ _dbus_concat_dir_and_file (DBusString       *dir,
                             _dbus_string_get_length (dir));
 }
 
-static dbus_bool_t
-pseudorandom_generate_random_bytes (DBusString *str,
-                                    int         n_bytes)
+static void
+pseudorandom_generate_random_bytes_buffer (char *buffer,
+                                           int   n_bytes)
 {
-  int old_len;
   unsigned long tv_usec;
   int i;
   
-  old_len = _dbus_string_get_length (str);
-
   /* fall back to pseudorandom */
   _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
                  n_bytes);
@@ -2352,18 +2474,60 @@ pseudorandom_generate_random_bytes (DBusString *str,
           
       r = rand ();
       b = (r / (double) RAND_MAX) * 255.0;
-          
-      if (!_dbus_string_append_byte (str, b))
-        goto failed;
-          
+
+      buffer[i] = b;
+
       ++i;
     }
+}
+
+static dbus_bool_t
+pseudorandom_generate_random_bytes (DBusString *str,
+                                    int         n_bytes)
+{
+  int old_len;
+  char *p;
+  
+  old_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, n_bytes))
+    return FALSE;
+
+  p = _dbus_string_get_data_len (str, old_len, n_bytes);
+
+  pseudorandom_generate_random_bytes_buffer (p, n_bytes);
 
   return TRUE;
+}
 
- failed:
-  _dbus_string_set_length (str, old_len);
-  return FALSE;
+/**
+ * Fills n_bytes of the given buffer with random bytes.
+ *
+ * @param buffer an allocated buffer
+ * @param n_bytes the number of bytes in buffer to write to
+ */
+void
+_dbus_generate_random_bytes_buffer (char *buffer,
+                                    int   n_bytes)
+{
+  DBusString str;
+
+  if (!_dbus_string_init (&str))
+    {
+      pseudorandom_generate_random_bytes_buffer (buffer, n_bytes);
+      return;
+    }
+
+  if (!_dbus_generate_random_bytes (&str, n_bytes))
+    {
+      _dbus_string_free (&str);
+      pseudorandom_generate_random_bytes_buffer (buffer, n_bytes);
+      return;
+    }
+
+  _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
+
+  _dbus_string_free (&str);
 }
 
 /**