tests: Fix t-gettime for a time_t of 64 and a long of 32 bit.
[gnupg.git] / common / homedir.c
index eccffec..6b40bb6 100644 (file)
@@ -25,7 +25,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #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;
 }
@@ -375,31 +398,242 @@ w32_commondir (void)
 
 
 /* Change the homedir.  Some care must be taken to set this early
- * enough becuase previous calls to gnupg_homedir may else return a
+ * enough because previous calls to gnupg_homedir may else return a
  * different string.  */
 void
 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)
+
+  char *name;
+
+  (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 directory name.
+ */
+const char *
+gnupg_socketdir (void)
+{
+  static char *name;
+
+  if (!name)
+    {
+      unsigned int dummy;
+      name = _gnupg_socketdir_internal (0, &dummy);
+    }
+
+  return name;
+}
+
+
 /* Return the name of the sysconfdir.  This is a static string.  This
    function is required because under Windows we can't simply compile
    it in.  */
@@ -578,60 +812,14 @@ gnupg_cachedir (void)
 }
 
 
-/* Return the system socket name used by DirMngr.  */
+/* Return the user socket name used by DirMngr.  */
 const char *
-dirmngr_sys_socket_name (void)
+dirmngr_socket_name (void)
 {
-#ifdef HAVE_W32_SYSTEM
   static char *name;
 
   if (!name)
-    {
-      char *p;
-# ifdef HAVE_W32CE_SYSTEM
-      const char *s1, *s2;
-
-      s1 = default_homedir ();
-# else
-      char s1buf[MAX_PATH];
-      const char *s1, *s2;
-
-      s1 = default_homedir ();
-      if (!w32_portable_app)
-        {
-          /* We need something akin CSIDL_COMMON_PROGRAMS, but local
-             (non-roaming).  This is because the file needs to be on
-             the local machine and makes only sense on that machine.
-             CSIDL_WINDOWS seems to be the only location which
-             guarantees that. */
-          if (w32_shgetfolderpath (NULL, CSIDL_WINDOWS, NULL, 0, s1buf) < 0)
-            strcpy (s1buf, "C:\\WINDOWS");
-          s1 = s1buf;
-        }
-# endif
-      s2 = DIRSEP_S DIRMNGR_SOCK_NAME;
-      name = xmalloc (strlen (s1) + strlen (s2) + 1);
-      strcpy (stpcpy (name, s1), s2);
-      for (p=name; *p; p++)
-        if (*p == '/')
-          *p = '\\';
-    }
-  return name;
-#else /*!HAVE_W32_SYSTEM*/
-  return GNUPG_LOCALSTATEDIR "/run/" PACKAGE_NAME "/"DIRMNGR_SOCK_NAME;
-#endif /*!HAVE_W32_SYSTEM*/
-}
-
-
-/* Return the user socket name used by DirMngr.  If a user specific
-   dirmngr installation is not supported, NULL is returned.  */
-const char *
-dirmngr_user_socket_name (void)
-{
-  static char *name;
-
-  if (!name)
-    name = make_absfilename (default_homedir (), DIRMNGR_SOCK_NAME, NULL);
+    name = make_filename (gnupg_socketdir (), DIRMNGR_SOCK_NAME, NULL);
   return name;
 }
 
@@ -696,15 +884,60 @@ get_default_pinentry_name (int reset)
 }
 
 
+/* If set, 'gnupg_module_name' returns modules from that build
+ * directory.  */
+static char *gnupg_build_directory;
+
+/* For sanity checks.  */
+static int gnupg_module_name_called;
+
+
+/* Set NEWDIR as the new build directory.  This will make
+ * 'gnupg_module_name' return modules from that build directory.  Must
+ * be called before any invocation of 'gnupg_module_name', and must
+ * not be called twice.  It can be used by test suites to make sure
+ * the components from the build directory are used instead of
+ * potentially outdated installed ones.  */
+void
+gnupg_set_builddir (const char *newdir)
+{
+  log_assert (! gnupg_module_name_called);
+  log_assert (! gnupg_build_directory);
+  gnupg_build_directory = xtrystrdup (newdir);
+}
+
+
+/* If no build directory has been configured, try to set it from the
+ * environment.  We only do this in development builds to avoid
+ * increasing the set of influential environment variables and hence
+ * the attack surface of production builds.  */
+static void
+gnupg_set_builddir_from_env (void)
+{
+#ifdef IS_DEVELOPMENT_VERSION
+  if (gnupg_build_directory)
+    return;
+
+  gnupg_build_directory = getenv ("GNUPG_BUILDDIR");
+#endif
+}
+
+
 /* Return the file name of a helper tool.  WHICH is one of the
    GNUPG_MODULE_NAME_foo constants.  */
 const char *
 gnupg_module_name (int which)
 {
-#define X(a,b) do {                                                     \
+  gnupg_set_builddir_from_env ();
+  gnupg_module_name_called = 1;
+
+#define X(a,b,c) do {                                                   \
     static char *name;                                                  \
     if (!name)                                                          \
-      name = xstrconcat (gnupg_ ## a (), DIRSEP_S b EXEEXT_S, NULL);    \
+      name = gnupg_build_directory                                      \
+        ? xstrconcat (gnupg_build_directory,                            \
+                      DIRSEP_S b DIRSEP_S c EXEEXT_S, NULL)             \
+        : xstrconcat (gnupg_ ## a (), DIRSEP_S c EXEEXT_S, NULL);       \
     return name;                                                        \
   } while (0)
 
@@ -714,7 +947,7 @@ gnupg_module_name (int which)
 #ifdef GNUPG_DEFAULT_AGENT
       return GNUPG_DEFAULT_AGENT;
 #else
-      X(bindir, "gpg-agent");
+      X(bindir, "agent", "gpg-agent");
 #endif
 
     case GNUPG_MODULE_NAME_PINENTRY:
@@ -728,48 +961,57 @@ gnupg_module_name (int which)
 #ifdef GNUPG_DEFAULT_SCDAEMON
       return GNUPG_DEFAULT_SCDAEMON;
 #else
-      X(libexecdir, "scdaemon");
+      X(libexecdir, "scd", "scdaemon");
 #endif
 
     case GNUPG_MODULE_NAME_DIRMNGR:
 #ifdef GNUPG_DEFAULT_DIRMNGR
       return GNUPG_DEFAULT_DIRMNGR;
 #else
-      X(bindir, DIRMNGR_NAME);
+      X(bindir, "dirmngr", DIRMNGR_NAME);
 #endif
 
     case GNUPG_MODULE_NAME_PROTECT_TOOL:
 #ifdef GNUPG_DEFAULT_PROTECT_TOOL
       return GNUPG_DEFAULT_PROTECT_TOOL;
 #else
-      X(libexecdir, "gpg-protect-tool");
+      X(libexecdir, "agent", "gpg-protect-tool");
 #endif
 
     case GNUPG_MODULE_NAME_DIRMNGR_LDAP:
 #ifdef GNUPG_DEFAULT_DIRMNGR_LDAP
       return GNUPG_DEFAULT_DIRMNGR_LDAP;
 #else
-      X(libexecdir, "dirmngr_ldap");
+      X(libexecdir, "dirmngr", "dirmngr_ldap");
 #endif
 
     case GNUPG_MODULE_NAME_CHECK_PATTERN:
-      X(libexecdir, "gpg-check-pattern");
+      X(libexecdir, "tools", "gpg-check-pattern");
 
     case GNUPG_MODULE_NAME_GPGSM:
-      X(bindir, "gpgsm");
+      X(bindir, "sm", "gpgsm");
 
     case GNUPG_MODULE_NAME_GPG:
 #if USE_GPG2_HACK
-      X(bindir, GPG_NAME "2");
-#else
-      X(bindir, GPG_NAME);
+      if (! gnupg_build_directory)
+        X(bindir, "g10", GPG_NAME "2");
+      else
+#endif
+        X(bindir, "g10", GPG_NAME);
+
+    case GNUPG_MODULE_NAME_GPGV:
+#if USE_GPG2_HACK
+      if (! gnupg_build_directory)
+        X(bindir, "g10", GPG_NAME "v2");
+      else
 #endif
+        X(bindir, "g10", GPG_NAME "v");
 
     case GNUPG_MODULE_NAME_CONNECT_AGENT:
-      X(bindir, "gpg-connect-agent");
+      X(bindir, "tools", "gpg-connect-agent");
 
     case GNUPG_MODULE_NAME_GPGCONF:
-      X(bindir, "gpgconf");
+      X(bindir, "tools", "gpgconf");
 
     default:
       BUG ();