Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gconf/ChangeLog,v
retrieving revision 1.489.2.31
diff -u -r1.489.2.31 ChangeLog
--- ChangeLog	9 Jul 2004 05:52:17 -0000	1.489.2.31
+++ ChangeLog	5 Aug 2004 15:24:36 -0000
@@ -1,3 +1,17 @@
+2004-08-03  Federico Mena Quintero  <federico@ximian.com>
+
+	Fix #141138:
+
+	* gconf/gconf-internals.c (test_safe_lock_dir): New function,
+	stolen from ORBit.  Tests the permissions and owner of our lock
+	directory.
+	(scan_lock_dir): New function, stolen from ORBit.  Scans a
+	directory for a valid lock directory.
+	(tmpdir_init): New function, stolen from ORBit.  Creates the lock
+	directory in /tmp.  Modified to use g_random_int() rather than
+	ORBit's UUID generator.
+	(gconf_get_daemon_dir): Use tmpdir_init().
+
 2004-07-09  Mark McLoughlin  <mark@skynet.ie>
 
 	Patch from Kai Willadsen <kaiw@itee.uq.edu.au> in
Index: gconf/gconf-internals.c
===================================================================
RCS file: /cvs/gnome/gconf/gconf/gconf-internals.c,v
retrieving revision 1.121.4.2
diff -u -r1.121.4.2 gconf-internals.c
--- gconf/gconf-internals.c	9 Jun 2004 07:13:11 -0000	1.121.4.2
+++ gconf/gconf-internals.c	5 Aug 2004 15:24:36 -0000
@@ -26,7 +26,9 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <dirent.h>
 #include <unistd.h>
+#include <utime.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -2778,22 +2780,161 @@
   return ret;
 }
 
-char*
-gconf_get_daemon_dir (void)
-{  
-  if (gconf_use_local_locks ())
+static gboolean
+test_safe_lock_dir (const char *dirname)
+{
+  struct stat statbuf;
+
+  if (stat (dirname, &statbuf) != 0)
     {
-      char *s;
-      char *subdir;
+      g_warning ("Cannot stat %s\n", dirname);
+      return FALSE;
+    }
 
-      subdir = g_strconcat ("gconfd-", g_get_user_name (), NULL);
-      
-      s = g_build_filename (g_get_tmp_dir (), subdir, NULL);
+  if (statbuf.st_uid != getuid ())
+    {
+      g_warning ("Owner of %s is not the current user\n", dirname);
+      return FALSE;
+    }
+
+  if ((statbuf.st_mode & (S_IRWXG|S_IRWXO)) || !S_ISDIR (statbuf.st_mode))
+    {
+      g_warning ("Wrong permissions for %s, or it is not a directory\n", dirname);
+      return FALSE;
+    }
+
+  return TRUE;
+}
 
-      g_free (subdir);
+static char *
+scan_lock_dir (const char *dir, const char *prefix)
+{
+  int prefix_len;
+  char *cur_dir = NULL;
+  DIR   *dirh;
+  struct dirent *dent;
+
+  g_assert (dir != NULL);
+  g_assert (prefix != NULL);
+
+  dirh = opendir (dir);
+  if (!dirh)
+    return NULL;
+  prefix_len = strlen (prefix);
 
-      return s;
+  while ((dent = readdir (dirh)) != NULL)
+    {
+      char *name;
+
+      if (strncmp (dent->d_name, prefix, prefix_len) != 0)
+	continue;
+
+      name = g_strconcat (dir, "/", dent->d_name, NULL);
+
+      /* Check its credentials */
+      if (!test_safe_lock_dir (name))
+	{
+	  g_free (name);
+	  continue;
+	}
+
+      /* Sort into some repeatable order */
+      if (!cur_dir || strcmp (cur_dir, name) > 0)
+	{
+	  g_free (cur_dir);
+	  cur_dir = name;
+	}
+      else
+	g_free (name);
     }
+  closedir (dirh);
+
+  return cur_dir;
+}
+
+static const char *
+tmpdir_init (void)
+{
+  const char *tmp_root;
+  char *dirname;
+  char *safe_dir = NULL;
+  long iteration = 0;
+  static char *tmp_dir = NULL;
+
+  if (tmp_dir)
+    return tmp_dir;
+
+  tmp_root = g_get_tmp_dir ();
+  dirname = g_strdup_printf ("gconfd-%s", g_get_user_name ());
+  while (!safe_dir)
+    {
+      char *newname;
+
+      safe_dir = scan_lock_dir (tmp_root, dirname);
+      if (safe_dir)
+	{
+	  g_free (dirname);
+	  tmp_dir = safe_dir;
+	  return tmp_dir;
+	}
+
+      if (iteration == 0)
+	newname = g_strconcat (tmp_root, "/", dirname, NULL);
+      else
+	newname = g_strdup_printf ("%s/%s-%4x", tmp_root, dirname, g_random_int ());
+
+      if (mkdir (newname, 0700) < 0)
+	{
+	  switch (errno)
+	    {
+	    case EACCES:
+	      g_error ("I can't write to '%s', GConf init failed",
+		       newname);
+	      break;
+
+	    case ENAMETOOLONG:
+	      g_error ("Name '%s' too long your unix is broken",
+		       newname);
+	      break;
+
+	    case ENOMEM:
+	    case ELOOP:
+	    case ENOSPC:
+	    case ENOTDIR:
+	    case ENOENT:
+	      g_error ("Resource problem creating '%s'", newname);
+	      break;
+
+	    default: /* carry on going */
+	      break;
+	    }
+	}
+
+      {
+	/* Hide some information ( apparently ) */
+	struct utimbuf utb;
+	memset (&utb, 0, sizeof (utb));
+	utime (newname, &utb);
+      }
+
+      /* Possible race - so we re-scan. */
+
+      iteration++;
+      g_free (newname);
+
+      if (iteration == 1000)
+	g_error ("Cannot find a safe lock path in '%s'", tmp_root);
+    }
+
+  g_assert_not_reached ();
+  return NULL;
+}
+
+char*
+gconf_get_daemon_dir (void)
+{
+  if (gconf_use_local_locks ())
+    return g_strdup (tmpdir_init ());
   else
     return g_strconcat (g_get_home_dir (), "/.gconfd", NULL);
 }
