From 0802cbb59b21e06e16b4fd8596934c5565e7f659 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 11 Aug 2021 11:50:41 +0200 Subject: [PATCH] w32: Move socketdir to LCOAL_APPDATA * common/homedir.c (is_gnupg_default_homedir): Use standard_homedir instead of the constant which makes a difference on Windows. (_gnupg_socketdir_internal) [W32]: Move the directory to LOCAL_APPDATA. (gnupg_cachedir): Remove unsued function. * common/sysutils.c (gnupg_rmdir): New. * tools/gpgconf.c (main): s/rmdir/gnupg_rmdir/. -- That is actually a more correct directory than APPDATA. This fixes a problem with installations where the APPDATA is non a network drive and the resulting socket filename is truncated in our socket helper function (because we use sockaddr also for our local socket emulation on Windows). LOCAL_APPDATA is expected to be on the local box and thus in the majority of cases the resulting socket file name will be short enough. GnuPG-bug-id: 5537 Signed-off-by: Werner Koch --- common/homedir.c | 206 +++++++++++++++++++++++++++------------------- common/sysutils.c | 27 ++++++ common/sysutils.h | 1 + common/util.h | 1 - tools/gpgconf.c | 4 +- 5 files changed, 152 insertions(+), 87 deletions(-) diff --git a/common/homedir.c b/common/homedir.c index 191644893..7969a2c2e 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,6 +1,7 @@ /* homedir.c - Setup the home directory. * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. * Copyright (C) 2013, 2016 Werner Koch + * Copyright (C) 2021 g10 Code GmbH * * This file is part of GnuPG. * @@ -26,6 +27,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #include @@ -74,15 +76,15 @@ static byte non_default_homedir; #ifdef HAVE_W32_SYSTEM /* A flag used to indicate that a control file for gpgconf has been - detected. Under Windows the presence of this file indicates a - portable installations and triggers several changes: - - - The GNUGHOME directory is fixed relative to installation - directory. All other means to set the home directory are ignore. - - - All registry variables will be ignored. - - This flag is not used on Unix systems. + * detected. Under Windows the presence of this file indicates a + * portable installations and triggers several changes: + * + * - The GNUGHOME directory is fixed relative to installation + * directory. All other means to set the home directory are ignored. + * + * - All registry variables will be ignored. + * + * This flag is not used on Unix systems. */ static byte w32_portable_app; #endif /*HAVE_W32_SYSTEM*/ @@ -166,7 +168,7 @@ is_gnupg_default_homedir (const char *dir) { int result; char *a = make_absfilename (dir, NULL); - char *b = make_absfilename (GNUPG_DEFAULT_HOMEDIR, NULL); + char *b = make_absfilename (standard_homedir (), NULL); result = !compare_filenames (a, b); xfree (b); xfree (a); @@ -609,8 +611,117 @@ gnupg_daemon_rootdir (void) char * _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) { -#if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT) +#if defined(HAVE_W32_SYSTEM) + char *name; + (void)skip_checks; + + *r_info = 0; + + /* First make sure that non_default_homedir and w32_portable_app can + * be set. */ + gnupg_homedir (); + + if (w32_portable_app) + { + name = xstrconcat (w32_rootdir (), DIRSEP_S, "gnupg", NULL); + } + else + { + char *path; + + path = w32_shgetfolderpath (NULL, + CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0); + if (path) + { + name = xstrconcat (path, "\\gnupg", NULL); + xfree (path); + if (gnupg_access (name, F_OK)) + w32_try_mkdir (name); + } + else + { + name = xstrdup (gnupg_homedir ()); + } + } + + /* If a non default homedir is used, we check whether an + * corresponding sub directory below the socket dir is available + * and use that. We hash the non default homedir to keep the new + * subdir short enough. */ + if (non_default_homedir) + { + char sha1buf[20]; + struct stat sb; + char *suffix; + char *p; + + *r_info |= 32; /* Testing subdir. */ + + /* Canonicalize the name to avoid problems with mixed case + * names. Note that we use only 10 bytes of the hash because on + * Windows the account name is also part of the name. */ + suffix = ascii_strlwr (xstrdup (gnupg_homedir ())); + for (p=suffix; *p; p++) + if ( *p == '\\') + *p = '/'; + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, suffix, strlen (suffix)); + xfree (suffix); + suffix = zb32_encode (sha1buf, 8*10); + if (!suffix) + { + *r_info |= 1; /* Out of core etc. */ + goto leave_w32; + } + p = xstrconcat (name, "\\d.", suffix, NULL); + xfree (suffix); + xfree (name); + name = p; + + /* Stat that directory and check constraints. + * The command + * gpgconf --remove-socketdir + * can be used to remove that directory. */ + if (gnupg_stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else if (!skip_checks) + { + /* Try to create the directory and check again. */ + if (gnupg_mkdir (name, "-rwx")) + *r_info |= 16; /* mkdir failed. */ + else if (gnupg_stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else + *r_info |= 64; /* Subdir does not exist. */ + } + else + goto leave_w32; /* Success! */ + } + else + *r_info |= 64; /* Subdir does not exist. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave_w32; + } + } + } + + leave_w32: + /* If nothing works - fall back to the homedir. */ + if (!name) + { + *r_info |= 128; /* Fallback. */ + name = xstrdup (gnupg_homedir ()); + } + +#elif !defined(HAVE_STAT) char *name; (void)skip_checks; @@ -939,79 +1050,6 @@ gnupg_localedir (void) } -/* Return the name of the cache directory. The name is allocated in a - static area on the first use. Windows only: If the directory does - not exist it is created. */ -const char * -gnupg_cachedir (void) -{ -#ifdef HAVE_W32_SYSTEM - static const char *dir; - - if (!dir) - { - const char *rdir; - - rdir = w32_rootdir (); - if (w32_portable_app) - { - dir = xstrconcat (rdir, - DIRSEP_S, "var", - DIRSEP_S, "cache", - DIRSEP_S, "gnupg", NULL); - gpgrt_annotate_leaked_object (dir); - } - else - { - char *path; - const char *s1[] = { "GNU", "cache", "gnupg", NULL }; - int s1_len; - const char **comp; - - s1_len = 0; - for (comp = s1; *comp; comp++) - s1_len += 1 + strlen (*comp); - - path = w32_shgetfolderpath (NULL, - CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, - NULL, 0); - if (path) - { - char *tmp = xmalloc (strlen (path) + s1_len + 1); - char *p; - - p = stpcpy (tmp, path); - for (comp = s1; *comp; comp++) - { - p = stpcpy (p, "\\"); - p = stpcpy (p, *comp); - - if (gnupg_access (tmp, F_OK)) - w32_try_mkdir (tmp); - } - - dir = tmp; - xfree (path); - gpgrt_annotate_leaked_object (dir); - } - else - { - dir = "c:\\temp\\cache\\gnupg"; -#ifdef HAVE_W32CE_SYSTEM - dir += 2; - w32_try_mkdir ("\\temp\\cache"); - w32_try_mkdir ("\\temp\\cache\\gnupg"); -#endif - } - } - } - return dir; -#else /*!HAVE_W32_SYSTEM*/ - return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; -#endif /*!HAVE_W32_SYSTEM*/ -} - - /* Return the standard socket name used by gpg-agent. */ const char * gpg_agent_socket_name (void) diff --git a/common/sysutils.c b/common/sysutils.c index 8b2d70180..1342d1016 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -1004,6 +1004,33 @@ gnupg_chdir (const char *name) } +/* A wrapper around rmdir. NAME is expected to be utf8 encoded. */ +int +gnupg_rmdir (const char *name) +{ +#ifdef HAVE_W32_SYSTEM + int rc; + wchar_t *wfname; + + wfname = utf8_to_wchar (name); + if (!wfname) + rc = 0; + else + { + rc = RemoveDirectoryW (wfname); + if (!rc) + gnupg_w32_set_errno (-1); + xfree (wfname); + } + if (!rc) + return -1; + return 0; +#else + return rmdir (name); +#endif +} + + /* A wrapper around chmod which takes a string for the mode argument. This makes it easier to handle the mode argument which is not defined on all systems. The format of the modestring is the same diff --git a/common/sysutils.h b/common/sysutils.h index 9f2920bb3..7063da067 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -84,6 +84,7 @@ gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, int *block_signals); int gnupg_mkdir (const char *name, const char *modestr); int gnupg_chdir (const char *name); +int gnupg_rmdir (const char *name); int gnupg_chmod (const char *name, const char *modestr); char *gnupg_mkdtemp (char *template); int gnupg_setenv (const char *name, const char *value, int overwrite); diff --git a/common/util.h b/common/util.h index a2ffbb6b6..4e123776c 100644 --- a/common/util.h +++ b/common/util.h @@ -275,7 +275,6 @@ const char *gnupg_libexecdir (void); const char *gnupg_libdir (void); const char *gnupg_datadir (void); const char *gnupg_localedir (void); -const char *gnupg_cachedir (void); const char *gpg_agent_socket_name (void); const char *dirmngr_socket_name (void); const char *keyboxd_socket_name (void); diff --git a/tools/gpgconf.c b/tools/gpgconf.c index b4ffa1ab8..1a99c9844 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -925,7 +925,7 @@ main (int argc, char **argv) log_info ("ignoring request to remove non /run/user socket dir\n"); else if (opt.dry_run) ; - else if (rmdir (socketdir)) + else if (gnupg_rmdir (socketdir)) { /* If the director is not empty we first try to delete * socket files. */ @@ -952,7 +952,7 @@ main (int argc, char **argv) gnupg_remove (p); xfree (p); } - if (rmdir (socketdir)) + if (gnupg_rmdir (socketdir)) gc_error (1, 0, "error removing '%s': %s", socketdir, gpg_strerror (err)); }