Implement /run/user/UID/gnupg based sockets.
authorWerner Koch <wk@gnupg.org>
Wed, 8 Jun 2016 08:12:32 +0000 (10:12 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 8 Jun 2016 11:59:28 +0000 (13:59 +0200)
* common/homedir.c: Include sys/stat.h and zb32.h.
(w32_portable_app, w32_bin_is_bin): Change type from int to byte.
(non_default_homedir): New.
(is_gnupg_default_homedir): New.
(default_homedir): Set non_default_homedir.
(gnupg_set_homedir): Set non_default_homedir and make
the_gnupg_homedir and absolute directory name.
(gnupg_homedir): Return an absolute directory name.
(_gnupg_socketdir_internal): New.
(gnupg_socketdir): Implement /run/user/ based sockets.
* tools/gpg-connect-agent.c (get_var_ext): Replace now obsolete
make_filename by xstrdup.
* tools/gpgconf.c (main): Sue gnupg_homedir for the "homedir:" output.
--

If a [/var]/run/user/$(id -u)/ directory exists, a gnupg subdir is
created as needed and the permissions of the directories are checked.
If that all matches that directory name is returned instead of the
homedir.

To cope with non standard homedirs (via GNUPGHOME or --homedir) the
SHA-1 hash of the homedir is computed, left truncated to 120 bits,
zBase-32 encoded, prefixed with "d.", and appended to
"[/var]/run/user/$(id -u)/gnupg/".  If that directory exists and has
proper permissions it is returned as socket dir - if not the homedir
is used.  Due to cleanup issues, this directory will not be
auto-created but needs to be created by the user in advance.

The required permissions are: directory owned by the user, group and
others bits not set.

Signed-off-by: Werner Koch <wk@gnupg.org>
agent/gpg-agent.c
common/homedir.c
common/util.h
tools/gpg-connect-agent.c
tools/gpgconf.c

index 3e23a19..d140ba5 100644 (file)
@@ -1020,9 +1020,6 @@ main (int argc, char **argv )
 
   finalize_rereadable_options ();
 
-  /* Turn the homedir into an absolute one. */
-  gnupg_set_homedir (make_absfilename (gnupg_homedir (), NULL));
-
   /* Print a warning if an argument looks like an option.  */
   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
     {
index 8992bc6..58f100f 100644 (file)
 #endif
 #endif /*HAVE_W32_SYSTEM*/
 
+#ifdef HAVE_STAT
+#include <sys/stat.h> /* for stat() */
+#endif
+
 
 
 #include "util.h"
 #include "sysutils.h"
-
+#include "zb32.h"
 
 /* The GnuPG homedir.  This is only accessed by the functions
  * gnupg_homedir and gnupg_set_homedir.  Malloced.  */
 static char *the_gnupg_homedir;
 
+/* Flag indicating that home directory is not the default one.  */
+static byte non_default_homedir;
+
 
 #ifdef HAVE_W32_SYSTEM
 /* A flag used to indicate that a control file for gpgconf has been
@@ -76,13 +83,13 @@ static char *the_gnupg_homedir;
 
    This flag is not used on Unix systems.
  */
-static int w32_portable_app;
+static byte w32_portable_app;
 #endif /*HAVE_W32_SYSTEM*/
 
 #ifdef HAVE_W32_SYSTEM
 /* This flag is true if this process' binary has been installed under
    bin and not in the root directory as often used before GnuPG 2.1. */
-static int w32_bin_is_bin;
+static byte w32_bin_is_bin;
 #endif /*HAVE_W32_SYSTEM*/
 
 
@@ -150,6 +157,20 @@ w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
 #endif /*HAVE_W32_SYSTEM*/
 
 
+/* Check whether DIR is the default homedir.  */
+static int
+is_gnupg_default_homedir (const char *dir)
+{
+  int result;
+  char *a = make_absfilename (dir, NULL);
+  char *b = make_absfilename (GNUPG_DEFAULT_HOMEDIR, NULL);
+  result = !compare_filenames (a, b);
+  xfree (b);
+  xfree (a);
+  return result;
+}
+
+
 /* Get the standard home directory.  In general this function should
    not be used as it does not consider a registry value (under W32) or
    the GNUPGHOME environment variable.  It is better to use
@@ -248,6 +269,8 @@ default_homedir (void)
 #endif /*HAVE_W32_SYSTEM*/
   if (!dir || !*dir)
     dir = GNUPG_DEFAULT_HOMEDIR;
+  else if (!is_gnupg_default_homedir (dir))
+    non_default_homedir = 1;
 
   return dir;
 }
@@ -382,27 +405,217 @@ gnupg_set_homedir (const char *newdir)
 {
   if (!newdir || !*newdir)
     newdir = default_homedir ();
+  else if (!is_gnupg_default_homedir (newdir))
+    non_default_homedir = 1;
   xfree (the_gnupg_homedir);
-  the_gnupg_homedir = xstrdup (newdir);
+  the_gnupg_homedir = make_absfilename (newdir, NULL);;
 }
 
 
 /* Return the homedir.  The returned string is valid until another
- * gnupg-set-homedir call.  Note that this may be a relative string.
- * This function replaced the former global opt.homedir.  */
+ * gnupg-set-homedir call.  This is always an absolute directory name.
+ * The function replaces the former global var opt.homedir.  */
 const char *
 gnupg_homedir (void)
 {
   /* If a homedir has not been set, set it to the default.  */
   if (!the_gnupg_homedir)
-    the_gnupg_homedir = xstrdup (default_homedir ());
+    the_gnupg_homedir = make_absfilename (default_homedir (), NULL);
   return the_gnupg_homedir;
 }
 
 
+/* Return whether the home dir is the default one.  */
+int
+gnupg_default_homedir_p (void)
+{
+  return !non_default_homedir;
+}
+
+
+/* Helper for gnupg-socketdir.  This is a global function, so that
+ * gpgconf can use it for its --create-socketdir command.  If
+ * SKIP_CHECKS is set permission checks etc. are not done.  The
+ * function always returns a malloced directory name and stores these
+ * bit flags at R_INFO:
+ *
+ *   1 := Internal error, stat failed, out of core, etc.
+ *   2 := No /run/user directory.
+ *   4 := Directory not owned by the user, not a directory
+ *        or wrong permissions.
+ *   8 := Same as 4 but for the subdir.
+ *  16 := mkdir failed
+ *  32 := Non default homedir; checking subdir.
+ *  64 := Subdir does not exist.
+ * 128 := Using homedir as fallback.
+ */
+char *
+_gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
+{
+#if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT)
+
+  (void)skip_checks;
+  *r_info = 0;
+  name = xstrdup (gnupg_homedir ());
+
+#else /* Unix and stat(2) available. */
+
+  static const char * const bases[] = { "/run", "/var/run", NULL};
+  int i;
+  struct stat sb;
+  char prefix[13 + 1 + 20 + 6 + 1];
+  const char *s;
+  char *name = NULL;
+
+  *r_info = 0;
+
+  /* First make sure that non_default_homedir can be set.  */
+  gnupg_homedir ();
+
+  /* It has been suggested to first check XDG_RUNTIME_DIR envvar.
+   * However, the specs state that the lifetime of the directory MUST
+   * be bound to the user being logged in.  Now GnuPG may also be run
+   * as a background process with no (desktop) user logged in.  Thus
+   * we better don't do that.  */
+
+  /* Check whether we have a /run/user dir.  */
+  for (i=0; bases[i]; i++)
+    {
+      snprintf (prefix, sizeof prefix, "%s/user/%u",
+                bases[i], (unsigned int)getuid ());
+      if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode))
+        break;
+    }
+  if (!bases[i])
+    {
+      *r_info |= 2; /* No /run/user directory.  */
+      goto leave;
+    }
+
+  if (sb.st_uid != getuid ())
+    {
+      *r_info |= 4; /* Not owned by the user.  */
+      if (!skip_checks)
+        goto leave;
+    }
+
+  if (strlen (prefix) + 7 >= sizeof prefix)
+    {
+      *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg".  */
+      goto leave;
+    }
+  strcat (prefix, "/gnupg");
+
+  /* Check whether the gnupg sub directory has proper permissions.  */
+  if (stat (prefix, &sb))
+    {
+      if (errno != ENOENT)
+        {
+          *r_info |= 1; /* stat failed.  */
+          goto leave;
+        }
+
+      /* Try to create the directory and check again.  */
+      if (gnupg_mkdir (prefix, "-rwx"))
+        {
+          *r_info |= 16; /* mkdir failed.  */
+          goto leave;
+        }
+      if (stat (prefix, &sb))
+        {
+          *r_info |= 1; /* stat failed.  */
+          goto leave;
+        }
+    }
+  /* Check that it is a directory, owned by the user, and only the
+   * user has permissions to use it.  */
+  if (!S_ISDIR(sb.st_mode)
+      || sb.st_uid != getuid ()
+      || (sb.st_mode & (S_IRWXG|S_IRWXO)))
+    {
+      *r_info |= 4; /* Bad permissions or not a directory. */
+      if (!skip_checks)
+        goto leave;
+    }
+
+  /* If a non default homedir is used, we check whether an
+   * corresponding sub directory below the socket dir is available
+   * and use that.  We has the non default homedir to keep the new
+   * subdir short enough.  */
+  if (non_default_homedir)
+    {
+      char sha1buf[20];
+      char *suffix;
+
+      *r_info |= 32; /* Testing subdir.  */
+      s = gnupg_homedir ();
+      gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, s, strlen (s));
+      suffix = zb32_encode (sha1buf, 8*15);
+      if (!suffix)
+        {
+          *r_info |= 1; /* Out of core etc. */
+          goto leave;
+        }
+      name = strconcat (prefix, "/d.", suffix, NULL);
+      xfree (suffix);
+      if (!name)
+        {
+          *r_info |= 1; /* Out of core etc. */
+          goto leave;
+        }
+
+      /* Stat that directory and check constraints.  Note that we
+       * do not auto create such a directory because we would not
+       * have a way to remove it.  Thus the directory needs to be
+       * pre-created.  The command
+       *    gpgconf --create-socketdir
+       * can be used tocreate that directory.  */
+      if (stat (name, &sb))
+        {
+          if (errno != ENOENT)
+            *r_info |= 1; /* stat failed. */
+          else
+            *r_info |= 64; /* Subdir does not exist.  */
+          if (!skip_checks)
+            {
+              xfree (name);
+              name = NULL;
+              goto leave;
+            }
+        }
+      else if (!S_ISDIR(sb.st_mode)
+               || sb.st_uid != getuid ()
+               || (sb.st_mode & (S_IRWXG|S_IRWXO)))
+        {
+          *r_info |= 8; /* Bad permissions or subdir is not a directory.  */
+          if (!skip_checks)
+            {
+              xfree (name);
+              name = NULL;
+              goto leave;
+            }
+        }
+    }
+  else
+    name = xstrdup (prefix);
+
+ leave:
+  /* If nothing works fall back to the homedir.  */
+  if (!name)
+    {
+      *r_info |= 128; /* Fallback.  */
+      name = xstrdup (gnupg_homedir ());
+    }
+
+#endif /* Unix */
+
+  return name;
+}
+
+
 /*
  * Return the name of the socket dir.  That is the directory used for
- * the IPC local sockets.  This is an absolute filename.
+ * the IPC local sockets.  This is an absolute directory name.
  */
 const char *
 gnupg_socketdir (void)
@@ -411,18 +624,8 @@ gnupg_socketdir (void)
 
   if (!name)
     {
-      /* Check XDG variable.  */
-
-      /* XDG is not set: Check whether we have a /run directory.  */
-
-      /* If there is no run directpry we assume a /var/run directory.  */
-
-      /* Check that the user directory exists or create it if
-       * required,  */
-
-      /* If nothing works fall back to the homedir.  */
-      if (!name)
-        name = make_absfilename (gnupg_homedir (), NULL);
+      unsigned int dummy;
+      name = _gnupg_socketdir_internal (0, &dummy);
     }
 
   return name;
index 0621047..c84847a 100644 (file)
@@ -222,6 +222,7 @@ const char *standard_homedir (void);
 const char *default_homedir (void);
 void gnupg_set_homedir (const char *newdir);
 const char *gnupg_homedir (void);
+int gnupg_default_homedir_p (void);
 const char *gnupg_socketdir (void);
 const char *gnupg_sysconfdir (void);
 const char *gnupg_bindir (void);
@@ -233,6 +234,8 @@ const char *gnupg_cachedir (void);
 const char *dirmngr_sys_socket_name (void);
 const char *dirmngr_user_socket_name (void);
 
+char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info);
+
 /* All module names.  We also include gpg and gpgsm for the sake for
    gpgconf. */
 #define GNUPG_MODULE_NAME_AGENT        1
index 0eb43fb..1cd554f 100644 (file)
@@ -555,7 +555,7 @@ get_var_ext (const char *name)
             log_error ("getcwd failed: %s\n", strerror (errno));
         }
       else if (!strcmp (s, "homedir"))
-        result = make_filename (gnupg_homedir (), NULL);
+        result = xstrdup (gnupg_homedir ());
       else if (!strcmp (s, "sysconfdir"))
         result = xstrdup (gnupg_sysconfdir ());
       else if (!strcmp (s, "bindir"))
index 63cc654..fb1032b 100644 (file)
@@ -383,8 +383,7 @@ main (int argc, char **argv)
         xfree (tmp);
       }
       {
-        /* We need to use make_filename to expand a possible "~/".  */
-        char *tmp = make_filename (default_homedir (), NULL);
+        char *tmp = xstrdup (gnupg_homedir ());
         es_fprintf (outfp, "homedir:%s\n", gc_percent_escape (tmp));
         xfree (tmp);
       }