diff --git a/common/homedir.c b/common/homedir.c index 85e09c46a..636268f24 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,1195 +1,1250 @@ /* 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. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #include #include #include #include #include #ifdef HAVE_W32_SYSTEM #include /* Due to the stupid mingw64 requirement to include this header before windows.h which is often implicitly included. */ #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_COMMON_APPDATA #define CSIDL_COMMON_APPDATA 0x0023 #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_STAT #include /* for stat() */ #endif #include "util.h" #include "sysutils.h" #include "i18n.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 - 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*/ #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 byte w32_bin_is_bin; #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32_SYSTEM static const char *w32_rootdir (void); #endif #ifdef HAVE_W32_SYSTEM static void w32_try_mkdir (const char *dir) { #ifdef HAVE_W32CE_SYSTEM wchar_t *wdir = utf8_to_wchar (dir); if (wdir) { CreateDirectory (wdir, NULL); xfree (wdir); } #else CreateDirectory (dir, NULL); #endif } #endif /* This is a helper function to load and call a Windows function from * either of one DLLs. On success an UTF-8 file name is returned. * ERRNO is _not_ set on error. */ #ifdef HAVE_W32_SYSTEM static char * w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d) { static int initialized; static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPWSTR); wchar_t wfname[MAX_PATH]; if (!initialized) { static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; void *handle; int i; initialized = 1; for (i=0, handle = NULL; !handle && dllnames[i]; i++) { handle = dlopen (dllnames[i], RTLD_LAZY); if (handle) { func = dlsym (handle, "SHGetFolderPathW"); if (!func) { dlclose (handle); handle = NULL; } } } } if (func && func (a,b,c,d,wfname) >= 0) return wchar_to_utf8 (wfname); else return NULL; } #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); + char *b = make_absfilename (standard_homedir (), NULL); result = !compare_filenames (a, b); xfree (b); xfree (a); return result; } /* Helper to remove trailing slashes from NEWDIR. Return a new * allocated string if that has been done or NULL if there are no * slashes to remove. Also inserts a missing slash after a Windows * drive letter. */ static char * copy_dir_with_fixup (const char *newdir) { char *result = NULL; char *p; if (!*newdir) return NULL; #ifdef HAVE_W32_SYSTEM if (newdir[0] && newdir[1] == ':' && !(newdir[2] == '/' || newdir[2] == '\\')) { /* Drive letter with missing leading slash. */ p = result = xmalloc (strlen (newdir) + 1 + 1); *p++ = newdir[0]; *p++ = newdir[1]; *p++ = '\\'; strcpy (p, newdir+2); /* Remove trailing slashes. */ p = result + strlen (result) - 1; while (p > result+2 && (*p == '/' || *p == '\\')) *p-- = 0; } else if (newdir[strlen (newdir)-1] == '/' || newdir[strlen (newdir)-1] == '\\' ) { result = xstrdup (newdir); p = result + strlen (result) - 1; while (p > result && (*p == '/' || *p == '\\') && (p-1 > result && p[-1] != ':')) /* We keep "c:/". */ *p-- = 0; } #else /*!HAVE_W32_SYSTEM*/ if (newdir[strlen (newdir)-1] == '/') { result = xstrdup (newdir); p = result + strlen (result) - 1; while (p > result && *p == '/') *p-- = 0; } #endif /*!HAVE_W32_SYSTEM*/ 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 default_homedir(). */ const char * standard_homedir (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 "home", NULL); } else { char *path; path = w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0); if (path) { dir = xstrconcat (path, "\\gnupg", NULL); xfree (path); /* Try to create the directory if it does not yet exists. */ if (gnupg_access (dir, F_OK)) w32_try_mkdir (dir); } else dir = GNUPG_DEFAULT_HOMEDIR; } } return dir; #else/*!HAVE_W32_SYSTEM*/ return GNUPG_DEFAULT_HOMEDIR; #endif /*!HAVE_W32_SYSTEM*/ } /* Set up the default home directory. The usual --homedir option should be parsed later. */ const char * default_homedir (void) { const char *dir; #ifdef HAVE_W32_SYSTEM /* For a portable application we only use the standard homedir. */ w32_rootdir (); if (w32_portable_app) return standard_homedir (); #endif /*HAVE_W32_SYSTEM*/ dir = getenv ("GNUPGHOME"); #ifdef HAVE_W32_SYSTEM if (!dir || !*dir) { static const char *saved_dir; if (!saved_dir) { if (!dir || !*dir) { char *tmp, *p; /* This is deprecated; gpgconf --list-dirs prints a * warning if the homedir has been taken from the * registry. */ tmp = read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, "HomeDir"); if (tmp && !*tmp) { xfree (tmp); tmp = NULL; } if (tmp) { /* Strip trailing backslashes. */ p = tmp + strlen (tmp) - 1; while (p > tmp && *p == '\\') *p-- = 0; saved_dir = tmp; } } if (!saved_dir) saved_dir = standard_homedir (); } dir = saved_dir; } #endif /*HAVE_W32_SYSTEM*/ if (!dir || !*dir) dir = GNUPG_DEFAULT_HOMEDIR; else { char *p; p = copy_dir_with_fixup (dir); if (p) dir = p; if (!is_gnupg_default_homedir (dir)) non_default_homedir = 1; } return dir; } #ifdef HAVE_W32_SYSTEM /* Check whether gpgconf is installed and if so read the gpgconf.ctl file. */ static void check_portable_app (const char *dir) { char *fname; fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); if (!gnupg_access (fname, F_OK)) { strcpy (fname + strlen (fname) - 3, "ctl"); if (!gnupg_access (fname, F_OK)) { /* gpgconf.ctl file found. Record this fact. */ w32_portable_app = 1; { unsigned int flags; log_get_prefix (&flags); log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); } /* FIXME: We should read the file to detect special flags and print a warning if we don't understand them */ } } xfree (fname); } /* Determine the root directory of the gnupg installation on Windows. */ static const char * w32_rootdir (void) { static int got_dir; static char dir[MAX_PATH+5]; if (!got_dir) { char *p; int rc; wchar_t wdir [MAX_PATH+5]; rc = GetModuleFileNameW (NULL, wdir, MAX_PATH); if (rc && WideCharToMultiByte (CP_UTF8, 0, wdir, -1, dir, MAX_PATH-4, NULL, NULL) < 0) rc = 0; if (!rc) { log_debug ("GetModuleFileName failed: %s\n", w32_strerror (-1)); *dir = 0; } got_dir = 1; p = strrchr (dir, DIRSEP_C); if (p) { *p = 0; check_portable_app (dir); /* If we are installed below "bin" we strip that and use the top directory instead. */ p = strrchr (dir, DIRSEP_C); if (p && !strcmp (p+1, "bin")) { *p = 0; w32_bin_is_bin = 1; } } if (!p) { log_debug ("bad filename '%s' returned for this process\n", dir); *dir = 0; } } if (*dir) return dir; /* Fallback to the hardwired value. */ return GNUPG_LIBEXECDIR; } static const char * w32_commondir (void) { static char *dir; if (!dir) { const char *rdir; char *path; /* Make sure that w32_rootdir has been called so that we are able to check the portable application flag. The common dir is the identical to the rootdir. In that case there is also no need to strdup its value. */ rdir = w32_rootdir (); if (w32_portable_app) return rdir; path = w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, NULL, 0); if (path) { dir = xstrconcat (path, "\\GNU", NULL); /* No auto create of the directory. Either the installer or * the admin has to create these directories. */ } else { /* Folder not found or defined - probably an old Windows * version. Use the installation directory instead. */ dir = xstrdup (rdir); } } return dir; } #endif /*HAVE_W32_SYSTEM*/ /* Change the homedir. Some care must be taken to set this early * enough because previous calls to gnupg_homedir may else return a * different string. */ void gnupg_set_homedir (const char *newdir) { char *tmp = NULL; if (!newdir || !*newdir) newdir = default_homedir (); else { tmp = copy_dir_with_fixup (newdir); if (tmp) newdir = tmp; if (!is_gnupg_default_homedir (newdir)) non_default_homedir = 1; } xfree (the_gnupg_homedir); the_gnupg_homedir = make_absfilename (newdir, NULL);; xfree (tmp); } /* Create the homedir directory only if the supplied directory name is * the same as the default one. This way we avoid to create arbitrary * directories when a non-default home directory is used. To cope * with HOME, we do compare only the suffix if we see that the default * homedir does start with a tilde. If the mkdir fails the function * terminates the process. If QUIET is set not diagnostic is printed * on homedir creation. */ void gnupg_maybe_make_homedir (const char *fname, int quiet) { const char *defhome = standard_homedir (); if ( #ifdef HAVE_W32_SYSTEM ( !compare_filenames (fname, defhome) ) #else ( *defhome == '~' && (strlen(fname) >= strlen (defhome+1) && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) || (*defhome != '~' && !compare_filenames( fname, defhome ) ) #endif ) { if (gnupg_mkdir (fname, "-rwx")) log_fatal ( _("can't create directory '%s': %s\n"), fname, strerror(errno) ); else if (!quiet ) log_info ( _("directory '%s' created\n"), fname ); } } /* Return the homedir. The returned string is valid until another * 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 = 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; } /* Return the directory name used by daemons for their current working * directory. */ const char * gnupg_daemon_rootdir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) { char path[MAX_PATH]; size_t n; n = GetSystemDirectoryA (path, sizeof path); if (!n || n >= sizeof path) name = xstrdup ("/"); /* Error - use the curret top dir instead. */ else name = xstrdup (path); } return name; #else /*!HAVE_W32_SYSTEM*/ return "/"; #endif /*!HAVE_W32_SYSTEM*/ } /* 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) +#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; *r_info = 0; name = xstrdup (gnupg_homedir ()); #else /* Unix and stat(2) available. */ static const char * const bases[] = { #ifdef USE_RUN_GNUPG_USER_SOCKET "/run/gnupg", #endif "/run", #ifdef USE_RUN_GNUPG_USER_SOCKET "/var/run/gnupg", #endif "/var/run", NULL }; int i; struct stat sb; char prefix[19 + 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/[gnupg/]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 hash 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. * The command * gpgconf --remove-socketdir * can be used to remove that directory. */ if (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 (stat (prefix, &sb)) { if (errno != ENOENT) *r_info |= 1; /* stat failed. */ else *r_info |= 64; /* Subdir does not exist. */ } else goto leave; /* Success! */ } 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. */ const char * gnupg_sysconfdir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) { const char *s1, *s2; s1 = w32_commondir (); s2 = DIRSEP_S "etc" DIRSEP_S "gnupg"; name = xmalloc (strlen (s1) + strlen (s2) + 1); strcpy (stpcpy (name, s1), s2); } return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_SYSCONFDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_bindir (void) { #if defined (HAVE_W32CE_SYSTEM) static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "bin", NULL); return name; #elif defined(HAVE_W32_SYSTEM) const char *rdir; rdir = w32_rootdir (); if (w32_bin_is_bin) { static char *name; if (!name) name = xstrconcat (rdir, DIRSEP_S "bin", NULL); return name; } else return rdir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_BINDIR; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the name of the libexec directory. The name is allocated in a static area on the first use. This function won't fail. */ const char * gnupg_libexecdir (void) { #ifdef HAVE_W32_SYSTEM return gnupg_bindir (); #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LIBEXECDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_libdir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "lib" DIRSEP_S "gnupg", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LIBDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_datadir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "gnupg", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_DATADIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_localedir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "locale", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return LOCALEDIR; #endif /*!HAVE_W32_SYSTEM*/ } -/* 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. */ +/* Return the standard socket name used by gpg-agent. */ const char * -gnupg_cachedir (void) +gpg_agent_socket_name (void) { -#ifdef HAVE_W32_SYSTEM - static const char *dir; + static char *name; - if (!dir) + if (!name) { - const char *rdir; - - rdir = w32_rootdir (); - if (w32_portable_app) - { - dir = xstrconcat (rdir, - DIRSEP_S, "var", - DIRSEP_S, "cache", - DIRSEP_S, "gnupg", NULL); - } - 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); - } - else - { - dir = "c:\\temp\\cache\\gnupg"; -#ifdef HAVE_W32CE_SYSTEM - dir += 2; - w32_try_mkdir ("\\temp\\cache"); - w32_try_mkdir ("\\temp\\cache\\gnupg"); -#endif - } - } + name = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + gpgrt_annotate_leaked_object (name); } - return dir; -#else /*!HAVE_W32_SYSTEM*/ - return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; -#endif /*!HAVE_W32_SYSTEM*/ + return name; } /* Return the user socket name used by DirMngr. */ const char * dirmngr_socket_name (void) { static char *name; if (!name) name = make_filename (gnupg_socketdir (), DIRMNGR_SOCK_NAME, NULL); return name; } /* Return the default pinentry name. If RESET is true the internal cache is first flushed. */ static const char * get_default_pinentry_name (int reset) { static struct { const char *(*rfnc)(void); const char *name; } names[] = { /* The first entry is what we return in case we found no other pinentry. */ { gnupg_bindir, DIRSEP_S "pinentry" EXEEXT_S }, #ifdef HAVE_W32_SYSTEM /* Try Gpg4win directory (with bin and without.) */ { w32_rootdir, "\\..\\Gpg4win\\bin\\pinentry.exe" }, { w32_rootdir, "\\..\\Gpg4win\\pinentry.exe" }, /* Try a pinentry in a dir above us */ { w32_rootdir, "\\..\\bin\\pinentry.exe" }, /* Try old Gpgwin directory. */ { w32_rootdir, "\\..\\GNU\\GnuPG\\pinentry.exe" }, /* Try a Pinentry from the common GNU dir. */ { w32_rootdir, "\\..\\GNU\\bin\\pinentry.exe" }, #endif /* Last chance is a pinentry-basic (which comes with the GnuPG 2.1 Windows installer). */ { gnupg_bindir, DIRSEP_S "pinentry-basic" EXEEXT_S } }; static char *name; if (reset) { xfree (name); name = NULL; } if (!name) { int i; for (i=0; i < DIM(names); i++) { char *name2; name2 = xstrconcat (names[i].rfnc (), names[i].name, NULL); if (!gnupg_access (name2, F_OK)) { /* Use that pinentry. */ xfree (name); name = name2; break; } if (!i) /* Store the first as fallback return. */ name = name2; else xfree (name2); } } return name; } /* 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) { #if defined(IS_DEVELOPMENT_VERSION) || defined(ENABLE_GNUPG_BUILDDIR_ENVVAR) 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) { gnupg_set_builddir_from_env (); gnupg_module_name_called = 1; #define X(a,b,c) do { \ static char *name; \ if (!name) \ 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) switch (which) { case GNUPG_MODULE_NAME_AGENT: #ifdef GNUPG_DEFAULT_AGENT return GNUPG_DEFAULT_AGENT; #else X(bindir, "agent", "gpg-agent"); #endif case GNUPG_MODULE_NAME_PINENTRY: #ifdef GNUPG_DEFAULT_PINENTRY return GNUPG_DEFAULT_PINENTRY; /* (Set by a configure option) */ #else return get_default_pinentry_name (0); #endif case GNUPG_MODULE_NAME_SCDAEMON: #ifdef GNUPG_DEFAULT_SCDAEMON return GNUPG_DEFAULT_SCDAEMON; #else X(libexecdir, "scd", "scdaemon"); #endif case GNUPG_MODULE_NAME_DIRMNGR: #ifdef GNUPG_DEFAULT_DIRMNGR return GNUPG_DEFAULT_DIRMNGR; #else 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, "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", "dirmngr_ldap"); #endif case GNUPG_MODULE_NAME_CHECK_PATTERN: X(libexecdir, "tools", "gpg-check-pattern"); case GNUPG_MODULE_NAME_GPGSM: X(bindir, "sm", "gpgsm"); case GNUPG_MODULE_NAME_GPG: #if USE_GPG2_HACK 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, "tools", "gpg-connect-agent"); case GNUPG_MODULE_NAME_GPGCONF: X(bindir, "tools", "gpgconf"); default: BUG (); } #undef X } /* Flush some of the cached module names. This is for example used by gpg-agent to allow configuring a different pinentry. */ void gnupg_module_name_flush_some (void) { (void)get_default_pinentry_name (1); } diff --git a/common/sysutils.c b/common/sysutils.c index 1d73d2796..01f654250 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -1,1920 +1,1947 @@ /* sysutils.c - system helpers * Copyright (C) 1991-2001, 2003-2004, * 2006-2008 Free Software Foundation, Inc. * Copyright (C) 2013-2016 Werner Koch * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . */ #include #ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ # undef HAVE_NPTH # undef USE_NPTH #endif #include #include #include #include #ifdef HAVE_PWD_H # include #endif #include #include #ifdef HAVE_STAT # include #endif #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 # include # include #endif #include #ifdef HAVE_SETRLIMIT # include # include #endif #ifdef HAVE_W32_SYSTEM # if WINVER < 0x0500 # define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ # endif # ifdef HAVE_WINSOCK2_H # include # endif # include #else /*!HAVE_W32_SYSTEM*/ # include # include #endif #ifdef HAVE_INOTIFY_INIT # include #endif /*HAVE_INOTIFY_INIT*/ #ifdef HAVE_NPTH # include #endif #include #include #include #include "util.h" #include "i18n.h" #include "sysutils.h" #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) /* The object used with our opendir functions. We need to define our * own so that we can properly handle Unicode on Windows. */ struct gnupg_dir_s { #ifdef HAVE_W32_SYSTEM _WDIR *dir; /* The system's DIR pointer. */ #else DIR *dir; /* The system's DIR pointer. */ #endif struct gnupg_dirent_s dirent; /* The current dirent. */ size_t namesize; /* If not 0 the allocated size of dirent.d_name. */ char name[256]; /* Only used if NAMESIZE is 0. */ }; /* Flag to tell whether special file names are enabled. See gpg.c for * an explanation of these file names. */ static int allow_special_filenames; static GPGRT_INLINE gpg_error_t my_error_from_syserror (void) { return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); } static GPGRT_INLINE gpg_error_t my_error (int e) { return gpg_err_make (default_errsource, (e)); } #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #warning using trap_unaligned static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) { return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); } void trap_unaligned(void) { unsigned int buf[2]; buf[0] = SSIN_UACPROC; buf[1] = UAC_SIGBUS | UAC_NOPRINT; setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); } #else void trap_unaligned(void) { /* dummy */ } #endif int disable_core_dumps (void) { #ifdef HAVE_DOSISH_SYSTEM return 0; #else # ifdef HAVE_SETRLIMIT struct rlimit limit; /* We only set the current limit unless we were not able to retrieve the old value. */ if (getrlimit (RLIMIT_CORE, &limit)) limit.rlim_max = 0; limit.rlim_cur = 0; if( !setrlimit (RLIMIT_CORE, &limit) ) return 0; if( errno != EINVAL && errno != ENOSYS ) log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) ); #endif return 1; #endif } int enable_core_dumps (void) { #ifdef HAVE_DOSISH_SYSTEM return 0; #else # ifdef HAVE_SETRLIMIT struct rlimit limit; if (getrlimit (RLIMIT_CORE, &limit)) return 1; limit.rlim_cur = limit.rlim_max; setrlimit (RLIMIT_CORE, &limit); return 1; /* We always return true because this function is merely a debugging aid. */ # endif return 1; #endif } #ifdef HAVE_W32_SYSTEM static int any8bitchar (const char *string) { if (string) for ( ; *string; string++) if ((*string & 0x80)) return 1; return 0; } #endif /*HAVE_W32_SYSTEM*/ /* Helper for gnupg_w32_set_errno. */ #ifdef HAVE_W32_SYSTEM static int map_w32_to_errno (DWORD w32_err) { switch (w32_err) { case 0: return 0; case ERROR_FILE_NOT_FOUND: return ENOENT; case ERROR_PATH_NOT_FOUND: return ENOENT; case ERROR_ACCESS_DENIED: return EPERM; /* ReactOS uses EACCES ("Permission denied") and * is likely right because they used an * undocumented function to associate the error * codes. However we have always used EPERM * ("Operation not permitted", e.g. function is * required to be called by root) and we better * stick to that to avoid surprising bugs. */ case ERROR_INVALID_HANDLE: return EBADF; case ERROR_INVALID_BLOCK: return ENOMEM; case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; case ERROR_NO_DATA: return EPIPE; case ERROR_ALREADY_EXISTS: return EEXIST; /* This mapping has been taken from reactOS. */ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; case ERROR_ARENA_TRASHED: return ENOMEM; case ERROR_BAD_ENVIRONMENT: return E2BIG; case ERROR_BAD_FORMAT: return ENOEXEC; case ERROR_INVALID_DRIVE: return ENOENT; case ERROR_CURRENT_DIRECTORY: return EACCES; case ERROR_NOT_SAME_DEVICE: return EXDEV; case ERROR_NO_MORE_FILES: return ENOENT; case ERROR_WRITE_PROTECT: return EACCES; case ERROR_BAD_UNIT: return EACCES; case ERROR_NOT_READY: return EACCES; case ERROR_BAD_COMMAND: return EACCES; case ERROR_CRC: return EACCES; case ERROR_BAD_LENGTH: return EACCES; case ERROR_SEEK: return EACCES; case ERROR_NOT_DOS_DISK: return EACCES; case ERROR_SECTOR_NOT_FOUND: return EACCES; case ERROR_OUT_OF_PAPER: return EACCES; case ERROR_WRITE_FAULT: return EACCES; case ERROR_READ_FAULT: return EACCES; case ERROR_GEN_FAILURE: return EACCES; case ERROR_SHARING_VIOLATION: return EACCES; case ERROR_LOCK_VIOLATION: return EACCES; case ERROR_WRONG_DISK: return EACCES; case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES; case ERROR_BAD_NETPATH: return ENOENT; case ERROR_NETWORK_ACCESS_DENIED: return EACCES; case ERROR_BAD_NET_NAME: return ENOENT; case ERROR_FILE_EXISTS: return EEXIST; case ERROR_CANNOT_MAKE: return EACCES; case ERROR_FAIL_I24: return EACCES; case ERROR_NO_PROC_SLOTS: return EAGAIN; case ERROR_DRIVE_LOCKED: return EACCES; case ERROR_BROKEN_PIPE: return EPIPE; case ERROR_DISK_FULL: return ENOSPC; case ERROR_INVALID_TARGET_HANDLE: return EBADF; case ERROR_WAIT_NO_CHILDREN: return ECHILD; case ERROR_CHILD_NOT_COMPLETE: return ECHILD; case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; case ERROR_SEEK_ON_DEVICE: return EACCES; case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; case ERROR_NOT_LOCKED: return EACCES; case ERROR_BAD_PATHNAME: return ENOENT; case ERROR_MAX_THRDS_REACHED: return EAGAIN; case ERROR_LOCK_FAILED: return EACCES; case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC; case ERROR_INVALID_STACKSEG: return ENOEXEC; case ERROR_INVALID_MODULETYPE: return ENOEXEC; case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC; case ERROR_EXE_MARKED_INVALID: return ENOEXEC; case ERROR_BAD_EXE_FORMAT: return ENOEXEC; case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC; case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC; case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC; case ERROR_IOPL_NOT_ENABLED: return ENOEXEC; case ERROR_INVALID_SEGDPL: return ENOEXEC; case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC; case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC; case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC; case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC; case ERROR_FILENAME_EXCED_RANGE: return ENOENT; case ERROR_NESTING_NOT_ALLOWED: return EAGAIN; case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM; default: return EIO; } } #endif /*HAVE_W32_SYSTEM*/ /* Set ERRNO from the Windows error. EC may be -1 to use the last error. */ #ifdef HAVE_W32_SYSTEM void gnupg_w32_set_errno (int ec) { /* FIXME: Replace by gpgrt_w32_set_errno. */ if (ec == -1) ec = GetLastError (); _set_errno (map_w32_to_errno (ec)); } #endif /*HAVE_W32_SYSTEM*/ /* Allow the use of special "-&nnn" style file names. */ void enable_special_filenames (void) { allow_special_filenames = 1; } /* Return a string which is used as a kind of process ID. */ const byte * get_session_marker (size_t *rlen) { static byte marker[SIZEOF_UNSIGNED_LONG*2]; static int initialized; if (!initialized) { gcry_create_nonce (marker, sizeof marker); initialized = 1; } *rlen = sizeof (marker); return marker; } /* Return a random number in an unsigned int. */ unsigned int get_uint_nonce (void) { unsigned int value; gcry_create_nonce (&value, sizeof value); return value; } #if 0 /* not yet needed - Note that this will require inclusion of cmacros.am in Makefile.am */ int check_permissions(const char *path,int extension,int checkonly) { #if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) char *tmppath; struct stat statbuf; int ret=1; int isdir=0; if(opt.no_perm_warn) return 0; if(extension && path[0]!=DIRSEP_C) { if(strchr(path,DIRSEP_C)) tmppath=make_filename(path,NULL); else tmppath=make_filename(GNUPG_LIBDIR,path,NULL); } else tmppath=m_strdup(path); /* It's okay if the file doesn't exist */ if(stat(tmppath,&statbuf)!=0) { ret=0; goto end; } isdir=S_ISDIR(statbuf.st_mode); /* Per-user files must be owned by the user. Extensions must be owned by the user or root. */ if((!extension && statbuf.st_uid != getuid()) || (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) { if(!checkonly) log_info(_("Warning: unsafe ownership on %s \"%s\"\n"), isdir?"directory":extension?"extension":"file",path); goto end; } /* This works for both directories and files - basically, we don't care what the owner permissions are, so long as the group and other permissions are 0 for per-user files, and non-writable for extensions. */ if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) { char *dir; /* However, if the directory the directory/file is in is owned by the user and is 700, then this is not a problem. Theoretically, we could walk this test up to the root directory /, but for the sake of sanity, I'm stopping at one level down. */ dir= make_dirname (tmppath); if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() && S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) { xfree (dir); ret=0; goto end; } m_free(dir); if(!checkonly) log_info(_("Warning: unsafe permissions on %s \"%s\"\n"), isdir?"directory":extension?"extension":"file",path); goto end; } ret=0; end: m_free(tmppath); return ret; #endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ return 0; } #endif /* Wrapper around the usual sleep function. This one won't wake up before the sleep time has really elapsed. When build with Pth it merely calls pth_sleep and thus suspends only the current thread. */ void gnupg_sleep (unsigned int seconds) { #ifdef USE_NPTH npth_sleep (seconds); #else /* Fixme: make sure that a sleep won't wake up to early. */ # ifdef HAVE_W32_SYSTEM Sleep (seconds*1000); # else sleep (seconds); # endif #endif } /* Wrapper around the platforms usleep function. This one won't wake * up before the sleep time has really elapsed. When build with nPth * it merely calls npth_usleep and thus suspends only the current * thread. */ void gnupg_usleep (unsigned int usecs) { #if defined(USE_NPTH) npth_usleep (usecs); #elif defined(HAVE_W32_SYSTEM) Sleep ((usecs + 999) / 1000); #elif defined(HAVE_NANOSLEEP) if (usecs) { struct timespec req; struct timespec rem; req.tv_sec = usecs / 1000000; req.tv_nsec = (usecs % 1000000) * 1000; while (nanosleep (&req, &rem) < 0 && errno == EINTR) req = rem; } #else /*Standard Unix*/ if (usecs) { struct timeval tv; tv.tv_sec = usecs / 1000000; tv.tv_usec = usecs % 1000000; select (0, NULL, NULL, NULL, &tv); } #endif } /* This function is a NOP for POSIX systems but required under Windows as the file handles as returned by OS calls (like CreateFile) are different from the libc file descriptors (like open). This function translates system file handles to libc file handles. FOR_WRITE gives the direction of the handle. */ int translate_sys2libc_fd (gnupg_fd_t fd, int for_write) { #if defined(HAVE_W32CE_SYSTEM) (void)for_write; return (int) fd; #elif defined(HAVE_W32_SYSTEM) int x; if (fd == GNUPG_INVALID_FD) return -1; /* Note that _open_osfhandle is currently defined to take and return a long. */ x = _open_osfhandle ((long)fd, for_write ? 1 : 0); if (x == -1) log_error ("failed to translate osfhandle %p\n", (void *) fd); return x; #else /*!HAVE_W32_SYSTEM */ (void)for_write; return fd; #endif } /* This is the same as translate_sys2libc_fd but takes an integer which is assumed to be such an system handle. On WindowsCE the passed FD is a rendezvous ID and the function finishes the pipe creation. */ int translate_sys2libc_fd_int (int fd, int for_write) { #if HAVE_W32CE_SYSTEM fd = (int) _assuan_w32ce_finish_pipe (fd, for_write); return translate_sys2libc_fd ((void*)fd, for_write); #elif HAVE_W32_SYSTEM if (fd <= 2) return fd; /* Do not do this for error, stdin, stdout, stderr. */ return translate_sys2libc_fd ((void*)fd, for_write); #else (void)for_write; return fd; #endif } /* Check whether FNAME has the form "-&nnnn", where N is a non-zero * number. Returns this number or -1 if it is not the case. If the * caller wants to use the file descriptor for writing FOR_WRITE shall * be set to 1. If NOTRANSLATE is set the Windows specific mapping is * not done. */ int check_special_filename (const char *fname, int for_write, int notranslate) { if (allow_special_filenames && fname && *fname == '-' && fname[1] == '&') { int i; fname += 2; for (i=0; digitp (fname+i); i++ ) ; if (!fname[i]) return notranslate? atoi (fname) /**/ : translate_sys2libc_fd_int (atoi (fname), for_write); } return -1; } /* Replacement for tmpfile(). This is required because the tmpfile function of Windows' runtime library is broken, insecure, ignores TMPDIR and so on. In addition we create a file with an inheritable handle. */ FILE * gnupg_tmpfile (void) { #ifdef HAVE_W32_SYSTEM int attempts, n; #ifdef HAVE_W32CE_SYSTEM wchar_t buffer[MAX_PATH+7+12+1]; # define mystrlen(a) wcslen (a) wchar_t *name, *p; #else char buffer[MAX_PATH+7+12+1]; # define mystrlen(a) strlen (a) char *name, *p; #endif HANDLE file; int pid = GetCurrentProcessId (); unsigned int value; int i; SECURITY_ATTRIBUTES sec_attr; memset (&sec_attr, 0, sizeof sec_attr ); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = TRUE; n = GetTempPath (MAX_PATH+1, buffer); if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH) { gpg_err_set_errno (ENOENT); return NULL; } p = buffer + mystrlen (buffer); #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L"_gnupg"); p += 7; #else p = stpcpy (p, "_gnupg"); #endif /* We try to create the directory but don't care about an error as it may already exist and the CreateFile would throw an error anyway. */ CreateDirectory (buffer, NULL); *p++ = '\\'; name = p; for (attempts=0; attempts < 10; attempts++) { p = name; value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); for (i=0; i < 8; i++) { *p++ = tohex (((value >> 28) & 0x0f)); value <<= 4; } #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L".tmp"); #else strcpy (p, ".tmp"); #endif file = CreateFile (buffer, GENERIC_READ | GENERIC_WRITE, 0, &sec_attr, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (file != INVALID_HANDLE_VALUE) { FILE *fp; #ifdef HAVE_W32CE_SYSTEM int fd = (int)file; fp = _wfdopen (fd, L"w+b"); #else int fd = _open_osfhandle ((long)file, 0); if (fd == -1) { CloseHandle (file); return NULL; } fp = fdopen (fd, "w+b"); #endif if (!fp) { int save = errno; close (fd); gpg_err_set_errno (save); return NULL; } return fp; } Sleep (1); /* One ms as this is the granularity of GetTickCount. */ } gpg_err_set_errno (ENOENT); return NULL; #undef mystrlen #else /*!HAVE_W32_SYSTEM*/ return tmpfile (); #endif /*!HAVE_W32_SYSTEM*/ } /* Make sure that the standard file descriptors are opened. Obviously some folks close them before an exec and the next file we open will get one of them assigned and thus any output (i.e. diagnostics) end up in that file (e.g. the trustdb). Not actually a gpg problem as this will happen with almost all utilities when called in a wrong way. However we try to minimize the damage here and raise awareness of the problem. Must be called before we open any files! */ void gnupg_reopen_std (const char *pgmname) { #ifdef F_GETFD int did_stdin = 0; int did_stdout = 0; int did_stderr = 0; FILE *complain; if (fcntl (STDIN_FILENO, F_GETFD) == -1 && errno ==EBADF) { if (open ("/dev/null",O_RDONLY) == STDIN_FILENO) did_stdin = 1; else did_stdin = 2; } if (fcntl (STDOUT_FILENO, F_GETFD) == -1 && errno == EBADF) { if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO) did_stdout = 1; else did_stdout = 2; } if (fcntl (STDERR_FILENO, F_GETFD)==-1 && errno==EBADF) { if (open ("/dev/null", O_WRONLY) == STDERR_FILENO) did_stderr = 1; else did_stderr = 2; } /* It's hard to log this sort of thing since the filehandle we would complain to may be closed... */ if (!did_stderr) complain = stderr; else if (!did_stdout) complain = stdout; else complain = NULL; if (complain) { if (did_stdin == 1) fprintf (complain, "%s: WARNING: standard input reopened\n", pgmname); if (did_stdout == 1) fprintf (complain, "%s: WARNING: standard output reopened\n", pgmname); if (did_stderr == 1) fprintf (complain, "%s: WARNING: standard error reopened\n", pgmname); if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) fprintf(complain,"%s: fatal: unable to reopen standard input," " output, or error\n", pgmname); } if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) exit (3); #else /* !F_GETFD */ (void)pgmname; #endif } /* Hack required for Windows. */ void gnupg_allow_set_foregound_window (pid_t pid) { if (!pid) log_info ("%s called with invalid pid %lu\n", "gnupg_allow_set_foregound_window", (unsigned long)pid); #if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid)) log_info ("AllowSetForegroundWindow(%lu) failed: %s\n", (unsigned long)pid, w32_strerror (-1)); #endif } int gnupg_remove (const char *fname) { #ifdef HAVE_W32_SYSTEM int rc; wchar_t *wfname; wfname = utf8_to_wchar (fname); if (!wfname) rc = 0; else { rc = DeleteFileW (wfname); if (!rc) gnupg_w32_set_errno (-1); xfree (wfname); } if (!rc) return -1; return 0; #else return remove (fname); #endif } /* Helper for gnupg_rename_file. */ #ifdef HAVE_W32_SYSTEM static int w32_rename (const char *oldname, const char *newname) { if (any8bitchar (oldname) || any8bitchar (newname)) { wchar_t *woldname, *wnewname; int ret; woldname = utf8_to_wchar (oldname); if (!woldname) return -1; wnewname = utf8_to_wchar (newname); if (!wnewname) { xfree (wnewname); return -1; } ret = _wrename (woldname, wnewname); xfree (wnewname); xfree (woldname); return ret; } else return rename (oldname, newname); } #endif /*HAVE_W32_SYSTEM*/ /* Wrapper for rename(2) to handle Windows peculiarities. If * BLOCK_SIGNALS is not NULL and points to a variable set to true, all * signals will be blocked by calling gnupg_block_all_signals; the * caller needs to call gnupg_unblock_all_signals if that variable is * still set to true on return. */ gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, int *block_signals) { gpg_error_t err = 0; if (block_signals && *block_signals) gnupg_block_all_signals (); #ifdef HAVE_DOSISH_SYSTEM { int wtime = 0; gnupg_remove (newname); again: if (w32_rename (oldname, newname)) { if (GetLastError () == ERROR_SHARING_VIOLATION) { /* Another process has the file open. We do not use a * lock for read but instead we wait until the other * process has closed the file. This may take long but * that would also be the case with a dotlock approach for * read and write. Note that we don't need this on Unix * due to the inode concept. * * So let's wait until the rename has worked. The retry * intervals are 50, 100, 200, 400, 800, 50ms, ... */ if (!wtime || wtime >= 800) wtime = 50; else wtime *= 2; if (wtime >= 800) log_info (_("waiting for file '%s' to become accessible ...\n"), oldname); Sleep (wtime); goto again; } err = my_error_from_syserror (); } } #else /* Unix */ { #ifdef __riscos__ gnupg_remove (newname); #endif if (rename (oldname, newname) ) err = my_error_from_syserror (); } #endif /* Unix */ if (block_signals && *block_signals && err) { gnupg_unblock_all_signals (); *block_signals = 0; } if (err) log_error (_("renaming '%s' to '%s' failed: %s\n"), oldname, newname, gpg_strerror (err)); return err; } #ifndef HAVE_W32_SYSTEM static mode_t modestr_to_mode (const char *modestr, mode_t oldmode) { static struct { char letter; mode_t value; } table[] = { { '-', 0 }, { 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR }, { 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP }, { 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH } }; int idx; mode_t mode = 0; /* For now we only support a string as used by ls(1) and no octal * numbers. The first character must be a dash. */ for (idx=0; idx < 10 && *modestr; idx++, modestr++) { if (*modestr == table[idx].letter) mode |= table[idx].value; else if (*modestr == '.') { if (!idx) ; /* Skip the dummy. */ else if ((oldmode & table[idx].value)) mode |= (oldmode & table[idx].value); else mode &= ~(oldmode & table[idx].value); } else if (*modestr != '-') break; } return mode; } #endif /* A wrapper around mkdir 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 "-rwxrwxrwx" '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, write allowed, execution allowed with the first group for the user, the second for the group and the third for all others. If the string is shorter than above the missing mode characters are meant to be not set. */ int gnupg_mkdir (const char *name, const char *modestr) { #if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */ #ifdef HAVE_W32CE_SYSTEM wchar_t *wname; (void)modestr; wname = utf8_to_wchar (name); if (!wname) return -1; if (!CreateDirectoryW (wname, NULL)) { xfree (wname); return -1; /* ERRNO is automagically provided by gpg-error.h. */ } xfree (wname); return 0; #elif MKDIR_TAKES_ONE_ARG (void)modestr; /* Note: In the case of W32 we better use CreateDirectory and try to set appropriate permissions. However using mkdir is easier because this sets ERRNO. */ return mkdir (name); #else return mkdir (name, modestr_to_mode (modestr, 0)); #endif #else /* Note that gpgrt_mkdir also sets ERRNO in addition to returing an * gpg-error style error code. */ return gpgrt_mkdir (name, modestr); #endif } /* A simple wrapper around chdir. NAME is expected to be utf8 * encoded. */ int gnupg_chdir (const char *name) { #if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */ return chdir (name); #else /* Use the improved version from libgpg_error. */ /* Note that gpgrt_chdir also sets ERRNO in addition to returning a * gpg-error style error code. */ return gpgrt_chdir (name); #endif } +/* 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 as for gnupg_mkdir with extra feature that a '.' keeps the original mode bit. */ int gnupg_chmod (const char *name, const char *modestr) { #ifdef HAVE_W32_SYSTEM (void)name; (void)modestr; return 0; #else mode_t oldmode; if (strchr (modestr, '.')) { /* Get the old mode so that a '.' can copy that bit. */ struct stat st; if (stat (name, &st)) return -1; oldmode = st.st_mode; } else oldmode = 0; return chmod (name, modestr_to_mode (modestr, oldmode)); #endif } /* Our version of mkdtemp. The API is identical to POSIX.1-2008 version. We do not use a system provided mkdtemp because we have a good RNG instantly available and this way we don't have diverging versions. */ char * gnupg_mkdtemp (char *tmpl) { /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6 (5*36**3 for Windows). It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3 or 5*36**3) fail to give the system administrator the chance to remove the problems. */ #ifdef HAVE_W32_SYSTEM static const char letters[] = "abcdefghijklmnopqrstuvwxyz0123456789"; # define NUMBER_OF_LETTERS 36 # define ATTEMPTS_MIN (5 * 36 * 36 * 36) #else static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; # define NUMBER_OF_LETTERS 62 # define ATTEMPTS_MIN (62 * 62 * 62) #endif int len; char *XXXXXX; uint64_t value; unsigned int count; int save_errno = errno; /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { gpg_err_set_errno (EINVAL); return NULL; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get a random start value. */ gcry_create_nonce (&value, sizeof value); /* Loop until a directory was created. */ for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[1] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[2] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[3] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[4] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[5] = letters[v % NUMBER_OF_LETTERS]; if (!gnupg_mkdir (tmpl, "-rwx")) { gpg_err_set_errno (save_errno); return tmpl; } if (errno != EEXIST) return NULL; } /* We got out of the loop because we ran out of combinations to try. */ gpg_err_set_errno (EEXIST); return NULL; } int gnupg_setenv (const char *name, const char *value, int overwrite) { #ifdef HAVE_W32CE_SYSTEM (void)name; (void)value; (void)overwrite; return 0; #else /*!W32CE*/ # ifdef HAVE_W32_SYSTEM /* Windows maintains (at least) two sets of environment variables. One set can be accessed by GetEnvironmentVariable and SetEnvironmentVariable. This set is inherited by the children. The other set is maintained in the C runtime, and is accessed using getenv and putenv. We try to keep them in sync by modifying both sets. */ { int exists; char tmpbuf[10]; exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) { gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ return -1; } } # endif /*W32*/ # ifdef HAVE_SETENV return setenv (name, value, overwrite); # else /*!HAVE_SETENV*/ if (! getenv (name) || overwrite) { char *buf; (void)overwrite; if (!name || !value) { gpg_err_set_errno (EINVAL); return -1; } buf = strconcat (name, "=", value, NULL); if (!buf) return -1; # if __GNUC__ # warning no setenv - using putenv but leaking memory. # endif return putenv (buf); } return 0; # endif /*!HAVE_SETENV*/ #endif /*!W32CE*/ } int gnupg_unsetenv (const char *name) { #ifdef HAVE_W32CE_SYSTEM (void)name; return 0; #else /*!W32CE*/ # ifdef HAVE_W32_SYSTEM /* Windows maintains (at least) two sets of environment variables. One set can be accessed by GetEnvironmentVariable and SetEnvironmentVariable. This set is inherited by the children. The other set is maintained in the C runtime, and is accessed using getenv and putenv. We try to keep them in sync by modifying both sets. */ if (!SetEnvironmentVariable (name, NULL)) { gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ return -1; } # endif /*W32*/ # ifdef HAVE_UNSETENV return unsetenv (name); # else /*!HAVE_UNSETENV*/ { char *buf; if (!name) { gpg_err_set_errno (EINVAL); return -1; } buf = xtrystrdup (name); if (!buf) return -1; # if __GNUC__ # warning no unsetenv - trying putenv but leaking memory. # endif return putenv (buf); } # endif /*!HAVE_UNSETENV*/ #endif /*!W32CE*/ } /* Return the current working directory as a malloced string. Return NULL and sets ERRNO on error. */ char * gnupg_getcwd (void) { #if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */ # ifdef HAVE_W32_SYSTEM wchar_t wbuffer[MAX_PATH + sizeof(wchar_t)]; DWORD wlen; char *buf, *p; wlen = GetCurrentDirectoryW (MAX_PATH, wbuffer); if (!wlen) { gpg_err_set_errno (EINVAL); return NULL; } else if (wlen > MAX_PATH) { gpg_err_set_errno (ENAMETOOLONG); return NULL; } buf = wchar_to_utf8 (wbuffer); if (buf) { for (p=buf; *p; p++) if (*p == '\\') *p = '/'; } return buf; # else /*Unix*/ char *buffer; size_t size = 100; for (;;) { buffer = xtrymalloc (size+1); if (!buffer) return NULL; if (getcwd (buffer, size) == buffer) return buffer; xfree (buffer); if (errno != ERANGE) return NULL; size *= 2; } # endif /*Unix*/ #else return gpgrt_getcwd (); #endif } /* A simple wrapper around access. NAME is expected to be utf8 * encoded. This function returns an error code and sets ERRNO. */ gpg_err_code_t gnupg_access (const char *name, int mode) { #if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */ # ifdef HAVE_W32_SYSTEM wchar_t *wfname; gpg_err_code_t ec; wfname = utf8_to_wchar (name); if (!wfname) ec = gpg_err_code_from_syserror (); else { ec = _waccess (wfname, mode)? gpg_err_code_from_syserror () : 0; xfree (wfname); } return ec; # else return access (name, mode)? gpg_err_code_from_syserror () : 0; # endif #else /* gpgrt 1.40 or newer. */ return gpgrt_access (name, mode); #endif } /* A wrapper around stat to handle Unicode file names under Windows. */ #ifdef HAVE_STAT int gnupg_stat (const char *name, struct stat *statbuf) { # ifdef HAVE_W32_SYSTEM if (any8bitchar (name)) { wchar_t *wname; struct _stat32 st32; int ret; wname = utf8_to_wchar (name); if (!wname) return -1; ret = _wstat (wname, &st32); xfree (wname); if (!ret) { statbuf->st_dev = st32.st_dev; statbuf->st_ino = st32.st_ino; statbuf->st_mode = st32.st_mode; statbuf->st_nlink = st32.st_nlink; statbuf->st_uid = st32.st_uid; statbuf->st_gid = st32.st_gid; statbuf->st_rdev = st32.st_rdev; statbuf->st_size = st32.st_size; statbuf->st_atime = st32.st_atime; statbuf->st_mtime = st32.st_mtime; statbuf->st_ctime = st32.st_ctime; } return ret; } else return stat (name, statbuf); # else return stat (name, statbuf); # endif } #endif /*HAVE_STAT*/ /* Wrapper around fopen for the cases where we have not yet switched * to es_fopen. Note that for convenience the prototype is in util.h */ FILE * gnupg_fopen (const char *fname, const char *mode) { #ifdef HAVE_W32_SYSTEM if (any8bitchar (fname)) { wchar_t *wfname; const wchar_t *wmode; wchar_t *wmodebuf = NULL; FILE *ret; wfname = utf8_to_wchar (fname); if (!wfname) return NULL; if (!strcmp (mode, "r")) wmode = L"r"; else if (!strcmp (mode, "rb")) wmode = L"rb"; else if (!strcmp (mode, "w")) wmode = L"w"; else if (!strcmp (mode, "wb")) wmode = L"wb"; else { wmodebuf = utf8_to_wchar (mode); if (!wmodebuf) { xfree (wfname); return NULL; } wmode = wmodebuf; } ret = _wfopen (wfname, wmode); xfree (wfname); xfree (wmodebuf); return ret; } else return fopen (fname, mode); #else /*Unix*/ return fopen (fname, mode); #endif /*Unix*/ } /* A wrapper around open to handle Unicode file names under Windows. */ int gnupg_open (const char *name, int flags, unsigned int mode) { #ifdef HAVE_W32_SYSTEM if (any8bitchar (name)) { wchar_t *wname; int ret; wname = utf8_to_wchar (name); if (!wname) return -1; ret = _wopen (wname, flags, mode); xfree (wname); return ret; } else return open (name, flags, mode); #else return open (name, flags, mode); #endif } /* A wrapper around opendir to handle Unicode file names under * Windows. This assumes the mingw toolchain. */ gnupg_dir_t gnupg_opendir (const char *name) { #ifdef HAVE_W32_SYSTEM _WDIR *dir; wchar_t *wname; #else DIR *dir; #endif gnupg_dir_t gdir; #ifdef HAVE_W32_SYSTEM /* Note: See gpgtar-create for an alternative implementation which * could be used here to avoid a mingw dependency. */ wname = utf8_to_wchar (name); if (!wname) return NULL; dir = _wopendir (wname); xfree (wname); #else dir = opendir (name); #endif if (!dir) return NULL; gdir = xtrymalloc (sizeof *gdir); if (!gdir) { int save_errno = errno; #ifdef HAVE_W32_SYSTEM _wclosedir (dir); #else closedir (dir); #endif gpg_err_set_errno (save_errno); return NULL; } gdir->dir = dir; gdir->namesize = 0; gdir->dirent.d_name = gdir->name; return gdir; } gnupg_dirent_t gnupg_readdir (gnupg_dir_t gdir) { #ifdef HAVE_W32_SYSTEM char *namebuffer = NULL; struct _wdirent *de; #else struct dirent *de; #endif size_t n; gnupg_dirent_t gde; const char *name; if (!gdir) { gpg_err_set_errno (EINVAL); return 0; } #ifdef HAVE_W32_SYSTEM de = _wreaddir (gdir->dir); if (!de) return NULL; namebuffer = wchar_to_utf8 (de->d_name); if (!namebuffer) return NULL; name = namebuffer; #else de = readdir (gdir->dir); if (!de) return NULL; name = de->d_name; #endif gde = &gdir->dirent; n = strlen (name); if (gdir->namesize) { /* Use allocated buffer. */ if (n+1 >= gdir->namesize || !gde->d_name) { gdir->namesize = n + 256; xfree (gde->d_name); gde->d_name = xtrymalloc (gdir->namesize); if (!gde->d_name) return NULL; /* ERRNO is already set. */ } strcpy (gde->d_name, name); } else if (n+1 >= sizeof (gdir->name)) { /* Switch to allocated buffer. */ gdir->namesize = n + 256; gde->d_name = xtrymalloc (gdir->namesize); if (!gde->d_name) return NULL; /* ERRNO is already set. */ strcpy (gde->d_name, name); } else { /* Use static buffer. */ gde->d_name = gdir->name; strcpy (gde->d_name, name); } #ifdef HAVE_W32_SYSTEM xfree (namebuffer); #endif return gde; } int gnupg_closedir (gnupg_dir_t gdir) { #ifdef HAVE_W32_SYSTEM _WDIR *dir; #else DIR *dir; #endif if (!gdir) return 0; dir = gdir->dir; if (gdir->namesize) xfree (gdir->dirent.d_name); xfree (gdir); #ifdef HAVE_W32_SYSTEM return _wclosedir (dir); #else return closedir (dir); #endif } #ifdef HAVE_W32CE_SYSTEM /* There is a isatty function declaration in cegcc but it does not make sense, thus we redefine it. */ int _gnupg_isatty (int fd) { (void)fd; return 0; } #endif #ifdef HAVE_W32CE_SYSTEM /* Replacement for getenv which takes care of the our use of getenv. The code is not thread safe but we expect it to work in all cases because it is called for the first time early enough. */ char * _gnupg_getenv (const char *name) { static int initialized; static char *assuan_debug; if (!initialized) { assuan_debug = read_w32_registry_string (NULL, "\\Software\\GNU\\libassuan", "debug"); initialized = 1; } if (!strcmp (name, "ASSUAN_DEBUG")) return assuan_debug; else return NULL; } #endif /*HAVE_W32CE_SYSTEM*/ #ifdef HAVE_W32_SYSTEM /* Return the user's security identifier from the current process. */ PSID w32_get_user_sid (void) { int okay = 0; HANDLE proc = NULL; HANDLE token = NULL; TOKEN_USER *user = NULL; PSID sid = NULL; DWORD tokenlen, sidlen; proc = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); if (!proc) goto leave; if (!OpenProcessToken (proc, TOKEN_QUERY, &token)) goto leave; if (!GetTokenInformation (token, TokenUser, NULL, 0, &tokenlen) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto leave; user = xtrymalloc (tokenlen); if (!user) goto leave; if (!GetTokenInformation (token, TokenUser, user, tokenlen, &tokenlen)) goto leave; if (!IsValidSid (user->User.Sid)) goto leave; sidlen = GetLengthSid (user->User.Sid); sid = xtrymalloc (sidlen); if (!sid) goto leave; if (!CopySid (sidlen, sid, user->User.Sid)) goto leave; okay = 1; leave: xfree (user); if (token) CloseHandle (token); if (proc) CloseHandle (proc); if (!okay) { xfree (sid); sid = NULL; } return sid; } #endif /*HAVE_W32_SYSTEM*/ /* Support for inotify under Linux. */ /* Store a new inotify file handle for FNAME at R_FD or return an * error code. This file descriptor watch the removal of FNAME. */ gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname) { #if HAVE_INOTIFY_INIT gpg_error_t err; int fd; *r_fd = -1; if (!fname) return my_error (GPG_ERR_INV_VALUE); fd = inotify_init (); if (fd == -1) return my_error_from_syserror (); if (inotify_add_watch (fd, fname, IN_DELETE_SELF) == -1) { err = my_error_from_syserror (); close (fd); return err; } *r_fd = fd; return 0; #else /*!HAVE_INOTIFY_INIT*/ (void)fname; *r_fd = -1; return my_error (GPG_ERR_NOT_SUPPORTED); #endif /*!HAVE_INOTIFY_INIT*/ } /* Store a new inotify file handle for SOCKET_NAME at R_FD or return * an error code. */ gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name) { #if HAVE_INOTIFY_INIT gpg_error_t err; char *fname; int fd; char *p; *r_fd = -1; if (!socket_name) return my_error (GPG_ERR_INV_VALUE); fname = xtrystrdup (socket_name); if (!fname) return my_error_from_syserror (); fd = inotify_init (); if (fd == -1) { err = my_error_from_syserror (); xfree (fname); return err; } /* We need to watch the directory for the file because there won't * be an IN_DELETE_SELF for a socket file. To handle a removal of * the directory we also watch the directory itself. */ p = strrchr (fname, '/'); if (p) *p = 0; if (inotify_add_watch (fd, fname, (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1) { err = my_error_from_syserror (); close (fd); xfree (fname); return err; } xfree (fname); *r_fd = fd; return 0; #else /*!HAVE_INOTIFY_INIT*/ (void)socket_name; *r_fd = -1; return my_error (GPG_ERR_NOT_SUPPORTED); #endif /*!HAVE_INOTIFY_INIT*/ } /* Read an inotify event and return true if it matches NAME or if it * sees an IN_DELETE_SELF event for the directory of NAME. */ int gnupg_inotify_has_name (int fd, const char *name) { #if USE_NPTH && HAVE_INOTIFY_INIT #define BUFSIZE_FOR_INOTIFY (sizeof (struct inotify_event) + 255 + 1) union { struct inotify_event ev; char _buf[sizeof (struct inotify_event) + 255 + 1]; } buf; struct inotify_event *evp; int n; n = npth_read (fd, &buf, sizeof buf); /* log_debug ("notify read: n=%d\n", n); */ evp = &buf.ev; while (n >= sizeof (struct inotify_event)) { /* log_debug (" mask=%x len=%u name=(%s)\n", */ /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */ if ((evp->mask & IN_UNMOUNT)) { /* log_debug (" found (dir unmounted)\n"); */ return 3; /* Directory was unmounted. */ } if ((evp->mask & IN_DELETE_SELF)) { /* log_debug (" found (dir removed)\n"); */ return 2; /* Directory was removed. */ } if ((evp->mask & IN_DELETE)) { if (evp->len >= strlen (name) && !strcmp (evp->name, name)) { /* log_debug (" found (file removed)\n"); */ return 1; /* File was removed. */ } } n -= sizeof (*evp) + evp->len; evp = (struct inotify_event *)(void *) ((char *)evp + sizeof (*evp) + evp->len); } #else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ (void)fd; (void)name; #endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ return 0; /* Not found. */ } /* Return a malloc'ed string that is the path to the passed * unix-domain socket (or return NULL if this is not a valid * unix-domain socket). We use a plain int here because it is only * used on Linux. * * FIXME: This function needs to be moved to libassuan. */ #ifndef HAVE_W32_SYSTEM char * gnupg_get_socket_name (int fd) { struct sockaddr_un un; socklen_t len = sizeof(un); char *name = NULL; if (getsockname (fd, (struct sockaddr*)&un, &len) != 0) log_error ("could not getsockname(%d): %s\n", fd, gpg_strerror (my_error_from_syserror ())); else if (un.sun_family != AF_UNIX) log_error ("file descriptor %d is not a unix-domain socket\n", fd); else if (len <= offsetof (struct sockaddr_un, sun_path)) log_error ("socket name not present for file descriptor %d\n", fd); else if (len > sizeof(un)) log_error ("socket name for file descriptor %d was truncated " "(passed %zu bytes, wanted %u)\n", fd, sizeof(un), len); else { size_t namelen = len - offsetof (struct sockaddr_un, sun_path); /* log_debug ("file descriptor %d has path %s (%zu octets)\n", fd, */ /* un.sun_path, namelen); */ name = xtrymalloc (namelen + 1); if (!name) log_error ("failed to allocate memory for name of fd %d: %s\n", fd, gpg_strerror (my_error_from_syserror ())); else { memcpy (name, un.sun_path, namelen); name[namelen] = 0; } } return name; } #endif /*!HAVE_W32_SYSTEM*/ /* Check whether FD is valid. */ int gnupg_fd_valid (int fd) { int d = dup (fd); if (d < 0) return 0; close (d); return 1; } /* Return a malloced copy of the current user's account name; this may * return NULL on memory failure. Note that this should eventually be * replaced by a gpgrt function. */ char * gnupg_getusername (void) { char *result = NULL; #ifdef HAVE_W32_SYSTEM wchar_t wtmp[1]; wchar_t *wbuf; DWORD wsize = 1; GetUserNameW (wtmp, &wsize); wbuf = xtrymalloc (wsize * sizeof *wbuf); if (!wbuf) { gpg_err_set_errno (ENOMEM); return NULL; } if (!GetUserNameW (wbuf, &wsize)) { gpg_err_set_errno (EINVAL); xfree (wbuf); return NULL; } result= wchar_to_utf8 (wbuf); xfree (wbuf); #else /* !HAVE_W32_SYSTEM */ # if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) struct passwd *pwd; pwd = getpwuid (getuid()); if (pwd) result = xtrystrdup (pwd->pw_name); # endif /*HAVE_PWD_H*/ #endif /* !HAVE_W32_SYSTEM */ return result; } diff --git a/common/sysutils.h b/common/sysutils.h index b374415f3..e22156b0a 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -1,116 +1,117 @@ /* sysutils.h - System utility functions for Gnupg * Copyright (C) 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . */ #ifndef GNUPG_COMMON_SYSUTILS_H #define GNUPG_COMMON_SYSUTILS_H /* Because we use system handles and not libc low level file descriptors on W32, we need to declare them as HANDLE (which actually is a plain pointer). This is required to eventually support 64 bits Windows systems. */ #ifdef HAVE_W32_SYSTEM typedef void *gnupg_fd_t; #define GNUPG_INVALID_FD ((void*)(-1)) #define INT2FD(s) ((void *)(s)) #define FD2INT(h) ((unsigned int)(h)) #else typedef int gnupg_fd_t; #define GNUPG_INVALID_FD (-1) #define INT2FD(s) (s) #define FD2INT(h) (h) #endif #ifdef HAVE_STAT # include #endif struct gnupg_dir_s; typedef struct gnupg_dir_s *gnupg_dir_t; struct gnupg_dirent_s { /* We don't have a d_ino because that can't be used on Windows * anyway. D_NAME is a pointer into the gnupg_dir_s which has a * static buffer or allocates sufficient space as needed. This is * only valid after gnupg_readdir. */ char *d_name; }; typedef struct gnupg_dirent_s *gnupg_dirent_t; void trap_unaligned (void); int disable_core_dumps (void); int enable_core_dumps (void); void enable_special_filenames (void); const unsigned char *get_session_marker (size_t *rlen); unsigned int get_uint_nonce (void); /*int check_permissions (const char *path,int extension,int checkonly);*/ void gnupg_sleep (unsigned int seconds); void gnupg_usleep (unsigned int usecs); int translate_sys2libc_fd (gnupg_fd_t fd, int for_write); int translate_sys2libc_fd_int (int fd, int for_write); int check_special_filename (const char *fname, int for_write, int notranslate); FILE *gnupg_tmpfile (void); void gnupg_reopen_std (const char *pgmname); void gnupg_allow_set_foregound_window (pid_t pid); int gnupg_remove (const char *fname); 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); int gnupg_unsetenv (const char *name); char *gnupg_getcwd (void); gpg_err_code_t gnupg_access (const char *name, int mode); #ifdef HAVE_STAT int gnupg_stat (const char *name, struct stat *statbuf); #endif /*HAVE_STAT*/ int gnupg_open (const char *name, int flags, unsigned int mode); gnupg_dir_t gnupg_opendir (const char *name); gnupg_dirent_t gnupg_readdir (gnupg_dir_t gdir); int gnupg_closedir (gnupg_dir_t gdir); char *gnupg_get_socket_name (int fd); int gnupg_fd_valid (int fd); char *gnupg_getusername (void); gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname); gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); int gnupg_inotify_has_name (int fd, const char *name); #ifdef HAVE_W32_SYSTEM void gnupg_w32_set_errno (int ec); void *w32_get_user_sid (void); #include "../common/w32help.h" #endif /*HAVE_W32_SYSTEM*/ #endif /*GNUPG_COMMON_SYSUTILS_H*/ diff --git a/common/util.h b/common/util.h index ed0355fce..8b5166e4c 100644 --- a/common/util.h +++ b/common/util.h @@ -1,410 +1,410 @@ /* util.h - Utility functions for GnuPG * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute and/or modify this * part of GnuPG under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * GnuPG is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef GNUPG_COMMON_UTIL_H #define GNUPG_COMMON_UTIL_H #include /* We need this for the memory function protos. */ #include /* We need errno. */ #include /* We need gpg_error_t and estream. */ /* These error codes might be used but not defined in the required * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ #if GPG_ERROR_VERSION_NUMBER < 0x012400 /* 1.36 */ # define GPG_ERR_NO_AUTH 314 # define GPG_ERR_BAD_AUTH 315 #endif #ifndef EXTERN_UNLESS_MAIN_MODULE # if !defined (INCLUDED_BY_MAIN_MODULE) # define EXTERN_UNLESS_MAIN_MODULE extern # else # define EXTERN_UNLESS_MAIN_MODULE # endif #endif /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) /* The length of the keygrip. This is a SHA-1 hash of the key * parameters as generated by gcry_pk_get_keygrip. */ #define KEYGRIP_LEN 20 /* Get all the stuff from jnlib. */ #include "../common/logging.h" #include "../common/argparse.h" #include "../common/stringhelp.h" #include "../common/mischelp.h" #include "../common/strlist.h" #include "../common/dotlock.h" #include "../common/utf8conv.h" #include "../common/dynload.h" #include "../common/fwddecl.h" #include "../common/utilproto.h" #include "gettime.h" /* Redefine asprintf by our estream version which uses our own memory allocator.. */ #define asprintf gpgrt_asprintf #define vasprintf gpgrt_vasprintf /* Due to a bug in mingw32's snprintf related to the 'l' modifier and for increased portability we use our snprintf on all systems. */ #undef snprintf #define snprintf gpgrt_snprintf /* Replacements for macros not available with libgpg-error < 1.20. */ /* We need this type even if we are not using libreadline and or we did not include libreadline in the current file. */ #ifndef GNUPG_LIBREADLINE_H_INCLUDED typedef char **rl_completion_func_t (const char *, int, int); #endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ /* Handy malloc macros - please use only them. */ #define xtrymalloc(a) gcry_malloc ((a)) #define xtrymalloc_secure(a) gcry_malloc_secure ((a)) #define xtrycalloc(a,b) gcry_calloc ((a),(b)) #define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) #define xtryrealloc(a,b) gcry_realloc ((a),(b)) #define xtrystrdup(a) gcry_strdup ((a)) #define xfree(a) gcry_free ((a)) #define xfree_fnc gcry_free #define xmalloc(a) gcry_xmalloc ((a)) #define xmalloc_secure(a) gcry_xmalloc_secure ((a)) #define xcalloc(a,b) gcry_xcalloc ((a),(b)) #define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) #define xrealloc(a,b) gcry_xrealloc ((a),(b)) #define xstrdup(a) gcry_xstrdup ((a)) /* For compatibility with gpg 1.4 we also define these: */ #define xmalloc_clear(a) gcry_xcalloc (1, (a)) #define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) /* The default error source of the application. This is different from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the source file and thus is usable in code shared by applications. Defined by init.c. */ extern gpg_err_source_t default_errsource; /* Convenience function to return a gpg-error code for memory allocation failures. This function makes sure that an error will be returned even if accidentally ERRNO is not set. */ static inline gpg_error_t out_of_core (void) { return gpg_error_from_syserror (); } /*-- yesno.c --*/ int answer_is_yes (const char *s); int answer_is_yes_no_default (const char *s, int def_answer); int answer_is_yes_no_quit (const char *s); int answer_is_okay_cancel (const char *s, int def_answer); /*-- xreadline.c --*/ ssize_t read_line (FILE *fp, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length); /*-- b64enc.c and b64dec.c --*/ struct b64state { unsigned int flags; int idx; int quad_count; FILE *fp; estream_t stream; char *title; unsigned char radbuf[4]; u32 crc; int stop_seen:1; int invalid_encoding:1; gpg_error_t lasterr; }; gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, const char *title); gpg_error_t b64enc_write (struct b64state *state, const void *buffer, size_t nbytes); gpg_error_t b64enc_finish (struct b64state *state); gpg_error_t b64dec_start (struct b64state *state, const char *title); gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, size_t *r_nbytes); gpg_error_t b64dec_finish (struct b64state *state); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); void log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen); void log_printsexp (const char *text, gcry_sexp_t sexp); gpg_error_t make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); int cmp_canon_sexp (const unsigned char *a, size_t alen, const unsigned char *b, size_t blen, int (*tcmp)(void *ctx, int depth, const unsigned char *aval, size_t avallen, const unsigned char *bval, size_t bvallen), void *tcmpctx); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); int hash_algo_from_sigval (const unsigned char *sigval); unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, const void *e, size_t elen, size_t *r_len); gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_n, size_t *r_nlen, unsigned char const **r_e, size_t *r_elen); gpg_error_t get_ecc_q_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_q, size_t *r_qlen); gpg_error_t uncompress_ecc_q_in_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char **r_newkeydata, size_t *r_newkeydatalen); int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid); const char *pubkey_algo_to_string (int algo); const char *hash_algo_to_string (int algo); const char *cipher_mode_to_string (int mode); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); char *percent_data_escape (int plus_escape, const char *prefix, const void *data, size_t datalen); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); size_t percent_plus_unescape_inplace (char *string, int nulrepl); size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); char *openpgp_oidbuf_to_str (const unsigned char *buf, size_t len); char *openpgp_oid_to_str (gcry_mpi_t a); int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len); int openpgp_oid_is_ed25519 (gcry_mpi_t a); int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); int openpgp_oid_is_cv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo); const char *openpgp_oid_to_curve (const char *oid, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits); /*-- homedir.c --*/ const char *standard_homedir (void); const char *default_homedir (void); void gnupg_set_homedir (const char *newdir); void gnupg_maybe_make_homedir (const char *fname, int quiet); const char *gnupg_homedir (void); int gnupg_default_homedir_p (void); const char *gnupg_daemon_rootdir (void); const char *gnupg_socketdir (void); const char *gnupg_sysconfdir (void); const char *gnupg_bindir (void); 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); 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 #define GNUPG_MODULE_NAME_PINENTRY 2 #define GNUPG_MODULE_NAME_SCDAEMON 3 #define GNUPG_MODULE_NAME_DIRMNGR 4 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5 #define GNUPG_MODULE_NAME_CHECK_PATTERN 6 #define GNUPG_MODULE_NAME_GPGSM 7 #define GNUPG_MODULE_NAME_GPG 8 #define GNUPG_MODULE_NAME_CONNECT_AGENT 9 #define GNUPG_MODULE_NAME_GPGCONF 10 #define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 #define GNUPG_MODULE_NAME_GPGV 12 const char *gnupg_module_name (int which); void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); /*-- gpgrlhelp.c --*/ void gnupg_rl_initialize (void); /*-- helpfile.c --*/ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); /*-- sysutils.c --*/ FILE *gnupg_fopen (const char *fname, const char *mode); /*-- miscellaneous.c --*/ /* This function is called at startup to tell libgcrypt to use our own logging subsystem. */ void setup_libgcrypt_logging (void); /* Print an out of core message and die. */ void xoutofcore (void); /* Same as estream_asprintf but die on memory failure. */ char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* This is now an alias to estream_asprintf. */ char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); void *xtryreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); /* Replacement for gcry_cipher_algo_name. */ const char *gnupg_cipher_algo_name (int algo); void obsolete_option (const char *configname, unsigned int configlineno, const char *name); const char *print_fname_stdout (const char *s); const char *print_fname_stdin (const char *s); void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, const char *delim); void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); void print_utf8_buffer (estream_t fp, const void *p, size_t n); void print_utf8_string (estream_t stream, const char *p); void print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved); char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); int is_file_compressed (const char *s, int *ret_rc); int match_multistr (const char *multistr,const char *match); int gnupg_compare_version (const char *a, const char *b); struct debug_flags_s { unsigned int flag; const char *name; }; int parse_debug_flag (const char *string, unsigned int *debugvar, const struct debug_flags_s *flags); /*-- Simple replacement functions. */ /* We use the gnupg_ttyname macro to be safe not to run into conflicts which an extisting but broken ttyname. */ #if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) # define gnupg_ttyname(n) _gnupg_ttyname ((n)) /* Systems without ttyname (W32) will merely return NULL. */ static inline char * _gnupg_ttyname (int fd) { (void)fd; return NULL; } #else /*HAVE_TTYNAME*/ # define gnupg_ttyname(n) ttyname ((n)) #endif /*HAVE_TTYNAME */ #ifdef HAVE_W32CE_SYSTEM #define getpid() GetCurrentProcessId () char *_gnupg_getenv (const char *name); /* See sysutils.c */ #define getenv(a) _gnupg_getenv ((a)) char *_gnupg_setenv (const char *name); /* See sysutils.c */ #define setenv(a,b,c) _gnupg_setenv ((a),(b),(c)) int _gnupg_isatty (int fd); #define gnupg_isatty(a) _gnupg_isatty ((a)) #else #define gnupg_isatty(a) isatty ((a)) #endif /*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ || (*(p) >= 'a' && *(p) <= 'z')) #define alnump(p) (alphap (p) || digitp (p)) #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) /* Note this isn't identical to a C locale isspace() without \f and \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') /* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) #endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/tools/gpgconf.c b/tools/gpgconf.c index f1779923a..d991c857c 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -1,1132 +1,1132 @@ /* gpgconf.c - Configuration utility for GnuPG * Copyright (C) 2003, 2007, 2009, 2011 Free Software Foundation, Inc. * Copyright (C) 2016, 2020 g10 Code GmbH. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #define INCLUDED_BY_MAIN_MODULE 1 #include "gpgconf.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../common/init.h" #include "../common/status.h" #include "../common/exechelp.h" /* Constants to identify the commands and options. */ enum cmd_and_opt_values { aNull = 0, oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oVerbose = 'v', oRuntime = 'r', oComponent = 'c', oNull = '0', oNoVerbose = 500, oHomedir, oBuilddir, oStatusFD, oShowSocket, aListComponents, aCheckPrograms, aListOptions, aChangeOptions, aCheckOptions, aApplyDefaults, aListConfig, aCheckConfig, aQuerySWDB, aListDirs, aLaunch, aKill, aCreateSocketDir, aRemoveSocketDir, aApplyProfile, aReload, aShowVersions }; /* The list of commands and options. */ static ARGPARSE_OPTS opts[] = { { 300, NULL, 0, N_("@Commands:\n ") }, { aListComponents, "list-components", 256, N_("list all components") }, { aCheckPrograms, "check-programs", 256, N_("check all programs") }, { aListOptions, "list-options", 256, N_("|COMPONENT|list options") }, { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") }, { aCheckOptions, "check-options", 256, N_("|COMPONENT|check options") }, { aApplyDefaults, "apply-defaults", 256, N_("apply global default values") }, { aApplyProfile, "apply-profile", 256, N_("|FILE|update configuration files using FILE") }, { aListDirs, "list-dirs", 256, N_("get the configuration directories for @GPGCONF@") }, { aListConfig, "list-config", 256, N_("list global configuration file") }, { aCheckConfig, "check-config", 256, N_("check global configuration file") }, { aQuerySWDB, "query-swdb", 256, N_("query the software version database") }, { aReload, "reload", 256, N_("reload all or a given component")}, { aLaunch, "launch", 256, N_("launch a given component")}, { aKill, "kill", 256, N_("kill a given component")}, { aCreateSocketDir, "create-socketdir", 256, "@"}, { aRemoveSocketDir, "remove-socketdir", 256, "@"}, ARGPARSE_c (aShowVersions, "show-versions", "@"), { 301, NULL, 0, N_("@\nOptions:\n ") }, { oOutput, "output", 2, N_("use as output file") }, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("quiet") }, { oDryRun, "dry-run", 0, N_("do not make any changes") }, { oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") }, ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), /* hidden options */ { oHomedir, "homedir", 2, "@" }, { oBuilddir, "build-prefix", 2, "@" }, { oNull, "null", 0, "@" }, { oNoVerbose, "no-verbose", 0, "@"}, ARGPARSE_s_n (oShowSocket, "show-socket", "@"), ARGPARSE_end(), }; /* The stream to output the status information. Status Output is disabled if * this is NULL. */ static estream_t statusfp; static void show_versions (estream_t fp); /* Print usage information and provide strings for help. */ static const char * my_strusage( int level ) { const char *p; switch (level) { case 9: p = "GPL-3.0-or-later"; break; case 11: p = "@GPGCONF@ (@GNUPG@)"; break; case 13: p = VERSION; break; case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: @GPGCONF@ [options] (-h for help)"); break; case 41: p = _("Syntax: @GPGCONF@ [options]\n" "Manage configuration options for tools of the @GNUPG@ system\n"); break; default: p = NULL; break; } return p; } /* Return the fp for the output. This is usually stdout unless --output has been used. In the latter case this function opens that file. */ static estream_t get_outfp (estream_t *fp) { if (!*fp) { if (opt.outfile) { *fp = es_fopen (opt.outfile, "w"); if (!*fp) gc_error (1, errno, "can not open '%s'", opt.outfile); } else *fp = es_stdout; } return *fp; } /* Set the status FD. */ static void set_status_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; if (statusfp && statusfp != es_stdout && statusfp != es_stderr) es_fclose (statusfp); statusfp = NULL; if (fd == -1) return; if (fd == 1) statusfp = es_stdout; else if (fd == 2) statusfp = es_stderr; else statusfp = es_fdopen (fd, "w"); if (!statusfp) { log_fatal ("can't open fd %d for status output: %s\n", fd, gpg_strerror (gpg_error_from_syserror ())); } last_fd = fd; } /* Write a status line with code NO followed by the output of the * printf style FORMAT. The caller needs to make sure that LFs and * CRs are not printed. */ void gpgconf_write_status (int no, const char *format, ...) { va_list arg_ptr; if (!statusfp) return; /* Not enabled. */ es_fputs ("[GNUPG:] ", statusfp); es_fputs (get_status_string (no), statusfp); if (format) { es_putc (' ', statusfp); va_start (arg_ptr, format); es_vfprintf (statusfp, format, arg_ptr); va_end (arg_ptr); } es_putc ('\n', statusfp); } static void list_dirs (estream_t fp, char **names) { static struct { const char *name; const char *(*fnc)(void); const char *extra; } list[] = { { "sysconfdir", gnupg_sysconfdir, NULL }, { "bindir", gnupg_bindir, NULL }, { "libexecdir", gnupg_libexecdir, NULL }, { "libdir", gnupg_libdir, NULL }, { "datadir", gnupg_datadir, NULL }, { "localedir", gnupg_localedir, NULL }, { "socketdir", gnupg_socketdir, NULL }, { "dirmngr-socket", dirmngr_socket_name, NULL,}, { "agent-ssh-socket", gnupg_socketdir, GPG_AGENT_SSH_SOCK_NAME }, { "agent-extra-socket", gnupg_socketdir, GPG_AGENT_EXTRA_SOCK_NAME }, { "agent-browser-socket",gnupg_socketdir, GPG_AGENT_BROWSER_SOCK_NAME }, { "agent-socket", gnupg_socketdir, GPG_AGENT_SOCK_NAME }, { "homedir", gnupg_homedir, NULL } }; int idx, j; char *tmp; const char *s; for (idx = 0; idx < DIM (list); idx++) { s = list[idx].fnc (); if (list[idx].extra) { tmp = make_filename (s, list[idx].extra, NULL); s = tmp; } else tmp = NULL; if (!names) es_fprintf (fp, "%s:%s\n", list[idx].name, gc_percent_escape (s)); else { for (j=0; names[j]; j++) if (!strcmp (names[j], list[idx].name)) { es_fputs (s, fp); es_putc (opt.null? '\0':'\n', fp); } } xfree (tmp); } #ifdef HAVE_W32_SYSTEM tmp = read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, "HomeDir"); if (tmp) { int hkcu = 0; int hklm = 0; xfree (tmp); if ((tmp = read_w32_registry_string ("HKEY_CURRENT_USER", GNUPG_REGISTRY_DIR, "HomeDir"))) { xfree (tmp); hkcu = 1; } if ((tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGISTRY_DIR, "HomeDir"))) { xfree (tmp); hklm = 1; } es_fflush (fp); log_info ("Warning: homedir taken from registry key (%s:%s) in%s%s\n", GNUPG_REGISTRY_DIR, "HomeDir", hkcu?" HKCU":"", hklm?" HKLM":""); } else if ((tmp = read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, NULL))) { xfree (tmp); es_fflush (fp); log_info ("Warning: registry key (%s) without value in HKCU or HKLM\n", GNUPG_REGISTRY_DIR); } #endif /*HAVE_W32_SYSTEM*/ } /* Check whether NAME is valid argument for query_swdb(). Valid names * start with a letter and contain only alphanumeric characters or an * underscore. */ static int valid_swdb_name_p (const char *name) { if (!name || !*name || !alphap (name)) return 0; for (name++; *name; name++) if (!alnump (name) && *name != '_') return 0; return 1; } /* Query the SWDB file. If necessary and possible this functions asks * the dirmngr to load an updated version of that file. The caller * needs to provide the NAME to query (e.g. "gnupg", "libgcrypt") and * optional the currently installed version in CURRENT_VERSION. The * output written to OUT is a colon delimited line with these fields: * * name :: The name of the package * curvers:: The installed version if given. * status :: This value tells the status of the software package * '-' :: No information available * (error or CURRENT_VERSION not given) * '?' :: Unknown NAME * 'u' :: Update available * 'c' :: The version is Current * 'n' :: The current version is already Newer than the * available one. * urgency :: If the value is greater than zero an urgent update is required. * error :: 0 on success or an gpg_err_code_t * Common codes seen: * GPG_ERR_TOO_OLD :: The SWDB file is to old to be used. * GPG_ERR_ENOENT :: The SWDB file is not available. * GPG_ERR_BAD_SIGNATURE :: Currupted SWDB file. * filedate:: Date of the swdb file (yyyymmddThhmmss) * verified:: Date we checked the validity of the file (yyyyymmddThhmmss) * version :: The version string from the swdb. * reldate :: Release date of that version (yyyymmddThhmmss) * size :: Size of the package in bytes. * hash :: SHA-2 hash of the package. * */ static void query_swdb (estream_t out, const char *name, const char *current_version) { gpg_error_t err; const char *search_name; char *fname = NULL; estream_t fp = NULL; char *line = NULL; char *self_version = NULL; size_t length_of_line = 0; size_t maxlen; ssize_t len; char *fields[2]; char *p; gnupg_isotime_t filedate = {0}; gnupg_isotime_t verified = {0}; char *value_ver = NULL; gnupg_isotime_t value_date = {0}; char *value_size = NULL; char *value_sha2 = NULL; unsigned long value_size_ul = 0; int status, i; if (!valid_swdb_name_p (name)) { log_error ("error in package name '%s': %s\n", name, gpg_strerror (GPG_ERR_INV_NAME)); goto leave; } if (!strcmp (name, "gnupg")) search_name = GNUPG_SWDB_TAG; else if (!strcmp (name, "gnupg1")) search_name = "gnupg1"; else search_name = name; if (!current_version && !strcmp (name, "gnupg")) { /* Use our own version but string a possible beta string. */ self_version = xstrdup (PACKAGE_VERSION); p = strchr (self_version, '-'); if (p) *p = 0; current_version = self_version; } if (current_version && (strchr (current_version, ':') || compare_version_strings (current_version, NULL))) { log_error ("error in version string '%s': %s\n", current_version, gpg_strerror (GPG_ERR_INV_ARG)); goto leave; } fname = make_filename (gnupg_homedir (), "swdb.lst", NULL); fp = es_fopen (fname, "r"); if (!fp) { err = gpg_error_from_syserror (); es_fprintf (out, "%s:%s:-::%u:::::::\n", name, current_version? current_version : "", gpg_err_code (err)); if (gpg_err_code (err) != GPG_ERR_ENOENT) log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } /* Note that the parser uses the first occurrence of a matching * values and ignores possible duplicated values. */ maxlen = 2048; /* Set limit. */ while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0) { if (!maxlen) { err = gpg_error (GPG_ERR_LINE_TOO_LONG); log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } /* Strip newline and carriage return, if present. */ while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) line[--len] = '\0'; if (split_fields (line, fields, DIM (fields)) < DIM(fields)) continue; /* Skip empty lines and names w/o a value. */ if (*fields[0] == '#') continue; /* Skip comments. */ /* Record the meta data. */ if (!*filedate && !strcmp (fields[0], ".filedate")) { string2isotime (filedate, fields[1]); continue; } if (!*verified && !strcmp (fields[0], ".verified")) { string2isotime (verified, fields[1]); continue; } /* Tokenize the name. */ p = strrchr (fields[0], '_'); if (!p) continue; /* Name w/o an underscore. */ *p++ = 0; /* Wait for the requested name. */ if (!strcmp (fields[0], search_name)) { if (!strcmp (p, "ver") && !value_ver) value_ver = xstrdup (fields[1]); else if (!strcmp (p, "date") && !*value_date) string2isotime (value_date, fields[1]); else if (!strcmp (p, "size") && !value_size) value_size = xstrdup (fields[1]); else if (!strcmp (p, "sha2") && !value_sha2) value_sha2 = xstrdup (fields[1]); } } if (len < 0 || es_ferror (fp)) { err = gpg_error_from_syserror (); log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } if (!*filedate || !*verified) { err = gpg_error (GPG_ERR_INV_TIME); es_fprintf (out, "%s:%s:-::%u:::::::\n", name, current_version? current_version : "", gpg_err_code (err)); goto leave; } if (!value_ver) { es_fprintf (out, "%s:%s:?:::::::::\n", name, current_version? current_version : ""); goto leave; } if (value_size) { gpg_err_set_errno (0); value_size_ul = strtoul (value_size, &p, 10); if (errno) value_size_ul = 0; else if (*p == 'k') value_size_ul *= 1024; } err = 0; status = '-'; if (compare_version_strings (value_ver, NULL)) err = gpg_error (GPG_ERR_INV_VALUE); else if (!current_version) ; else if (!(i = compare_version_strings (value_ver, current_version))) status = 'c'; else if (i > 0) status = 'u'; else status = 'n'; es_fprintf (out, "%s:%s:%c::%d:%s:%s:%s:%s:%lu:%s:\n", name, current_version? current_version : "", status, err, filedate, verified, value_ver, value_date, value_size_ul, value_sha2? value_sha2 : ""); leave: xfree (value_ver); xfree (value_size); xfree (value_sha2); xfree (line); es_fclose (fp); xfree (fname); xfree (self_version); } /* gpgconf main. */ int main (int argc, char **argv) { gpg_error_t err; ARGPARSE_ARGS pargs; const char *fname; int no_more_options = 0; enum cmd_and_opt_values cmd = 0; estream_t outfp = NULL; int show_socket = 0; early_system_init (); gnupg_reopen_std (GPGCONF_NAME); set_strusage (my_strusage); log_set_prefix (GPGCONF_NAME, GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init(); init_common_subsystems (&argc, &argv); gc_components_init (); /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; pargs.flags = ARGPARSE_FLAG_KEEP; while (!no_more_options && gnupg_argparse (NULL, &pargs, opts)) { switch (pargs.r_opt) { case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; case oDryRun: opt.dry_run = 1; break; case oRuntime: opt.runtime = 1; break; case oVerbose: opt.verbose++; break; case oNoVerbose: opt.verbose = 0; break; case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; case oBuilddir: gnupg_set_builddir (pargs.r.ret_str); break; case oNull: opt.null = 1; break; case oStatusFD: set_status_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oShowSocket: show_socket = 1; break; case aListDirs: case aListComponents: case aCheckPrograms: case aListOptions: case aChangeOptions: case aCheckOptions: case aApplyDefaults: case aApplyProfile: case aListConfig: case aCheckConfig: case aQuerySWDB: case aReload: case aLaunch: case aKill: case aCreateSocketDir: case aRemoveSocketDir: case aShowVersions: cmd = pargs.r_opt; break; default: pargs.err = ARGPARSE_PRINT_ERROR; break; } } gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */ if (log_get_errorcount (0)) gpgconf_failure (GPG_ERR_USER_2); /* Print a warning if an argument looks like an option. */ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) { int i; for (i=0; i < argc; i++) if (argv[i][0] == '-' && argv[i][1] == '-') log_info (_("Note: '%s' is not considered an option\n"), argv[i]); } fname = argc ? *argv : NULL; switch (cmd) { case aListComponents: default: /* List all components. */ gc_component_list_components (get_outfp (&outfp)); break; case aCheckPrograms: /* Check all programs. */ gc_check_programs (get_outfp (&outfp)); break; case aListOptions: case aChangeOptions: case aCheckOptions: if (!fname) { es_fprintf (es_stderr, _("usage: %s [options] "), GPGCONF_NAME); es_putc ('\n', es_stderr); es_fputs (_("Need one component argument"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (GPG_ERR_USER_2); } else { int idx = gc_component_find (fname); if (idx < 0) { es_fputs (_("Component not found"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (0); } if (cmd == aCheckOptions) gc_component_check_options (idx, get_outfp (&outfp), NULL); else { gc_component_retrieve_options (idx); if (gc_process_gpgconf_conf (NULL, 1, 0, NULL)) gpgconf_failure (0); if (cmd == aListOptions) gc_component_list_options (idx, get_outfp (&outfp)); else if (cmd == aChangeOptions) gc_component_change_options (idx, es_stdin, get_outfp (&outfp), 0); } } break; case aLaunch: case aKill: if (!fname) { es_fprintf (es_stderr, _("usage: %s [options] "), GPGCONF_NAME); es_putc ('\n', es_stderr); es_fputs (_("Need one component argument"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (GPG_ERR_USER_2); } else if (!strcmp (fname, "all")) { if (cmd == aLaunch) { if (gc_component_launch (-1)) gpgconf_failure (0); } else { gc_component_kill (-1); } } else { /* Launch/Kill a given component. */ int idx; idx = gc_component_find (fname); if (idx < 0) { es_fputs (_("Component not found"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (0); } else if (cmd == aLaunch) { err = gc_component_launch (idx); if (show_socket) { char *names[2]; if (idx == GC_COMPONENT_GPG_AGENT) names[0] = "agent-socket"; else if (idx == GC_COMPONENT_DIRMNGR) names[0] = "dirmngr-socket"; else names[0] = NULL; names[1] = NULL; get_outfp (&outfp); list_dirs (outfp, names); } if (err) gpgconf_failure (0); } else { /* We don't error out if the kill failed because this command should do nothing if the component is not running. */ gc_component_kill (idx); } } break; case aReload: if (!fname || !strcmp (fname, "all")) { /* Reload all. */ gc_component_reload (-1); } else { /* Reload given component. */ int idx; idx = gc_component_find (fname); if (idx < 0) { es_fputs (_("Component not found"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (0); } else { gc_component_reload (idx); } } break; case aListConfig: if (gc_process_gpgconf_conf (fname, 0, 0, get_outfp (&outfp))) gpgconf_failure (0); break; case aCheckConfig: if (gc_process_gpgconf_conf (fname, 0, 0, NULL)) gpgconf_failure (0); break; case aApplyDefaults: if (fname) { es_fprintf (es_stderr, _("usage: %s [options] "), GPGCONF_NAME); es_putc ('\n', es_stderr); es_fputs (_("No argument allowed"), es_stderr); es_putc ('\n', es_stderr); gpgconf_failure (GPG_ERR_USER_2); } if (!opt.dry_run && gnupg_access (gnupg_homedir (), F_OK)) gnupg_maybe_make_homedir (gnupg_homedir (), opt.quiet); gc_component_retrieve_options (-1); if (gc_process_gpgconf_conf (NULL, 1, 1, NULL)) gpgconf_failure (0); break; case aApplyProfile: if (!opt.dry_run && gnupg_access (gnupg_homedir (), F_OK)) gnupg_maybe_make_homedir (gnupg_homedir (), opt.quiet); gc_component_retrieve_options (-1); if (gc_apply_profile (fname)) gpgconf_failure (0); break; case aListDirs: /* Show the system configuration directories for gpgconf. */ get_outfp (&outfp); list_dirs (outfp, argc? argv : NULL); break; case aQuerySWDB: /* Query the software version database. */ if (!fname || argc > 2) { es_fprintf (es_stderr, "usage: %s --query-swdb NAME [VERSION]\n", GPGCONF_NAME); gpgconf_failure (GPG_ERR_USER_2); } get_outfp (&outfp); query_swdb (outfp, fname, argc > 1? argv[1] : NULL); break; case aCreateSocketDir: { char *socketdir; unsigned int flags; /* Make sure that the top /run/user/UID/gnupg dir has been * created. */ gnupg_socketdir (); /* Check the /var/run dir. */ socketdir = _gnupg_socketdir_internal (1, &flags); if ((flags & 64) && !opt.dry_run) { /* No sub dir - create it. */ if (gnupg_mkdir (socketdir, "-rwx")) gc_error (1, errno, "error creating '%s'", socketdir); /* Try again. */ xfree (socketdir); socketdir = _gnupg_socketdir_internal (1, &flags); } /* Give some info. */ if ( (flags & ~32) || opt.verbose || opt.dry_run) { log_info ("socketdir is '%s'\n", socketdir); if ((flags & 1)) log_info ("\tgeneral error\n"); if ((flags & 2)) log_info ("\tno /run/user dir\n"); if ((flags & 4)) log_info ("\tbad permissions\n"); if ((flags & 8)) log_info ("\tbad permissions (subdir)\n"); if ((flags & 16)) log_info ("\tmkdir failed\n"); if ((flags & 32)) log_info ("\tnon-default homedir\n"); if ((flags & 64)) log_info ("\tno such subdir\n"); if ((flags & 128)) log_info ("\tusing homedir as fallback\n"); } if ((flags & ~32) && !opt.dry_run) gc_error (1, 0, "error creating socket directory"); xfree (socketdir); } break; case aRemoveSocketDir: { char *socketdir; unsigned int flags; /* Check the /var/run dir. */ socketdir = _gnupg_socketdir_internal (1, &flags); if ((flags & 128)) 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 delet * socket files. */ err = gpg_error_from_syserror (); if (gpg_err_code (err) == GPG_ERR_ENOTEMPTY || gpg_err_code (err) == GPG_ERR_EEXIST) { static const char * const names[] = { GPG_AGENT_SOCK_NAME, GPG_AGENT_EXTRA_SOCK_NAME, GPG_AGENT_BROWSER_SOCK_NAME, GPG_AGENT_SSH_SOCK_NAME, SCDAEMON_SOCK_NAME, DIRMNGR_SOCK_NAME }; int i; char *p; for (i=0; i < DIM(names); i++) { p = strconcat (socketdir , "/", names[i], NULL); if (p) gnupg_remove (p); xfree (p); } - if (rmdir (socketdir)) + if (gnupg_rmdir (socketdir)) gc_error (1, 0, "error removing '%s': %s", socketdir, gpg_strerror (err)); } else if (gpg_err_code (err) == GPG_ERR_ENOENT) gc_error (0, 0, "warning: removing '%s' failed: %s", socketdir, gpg_strerror (err)); else gc_error (1, 0, "error removing '%s': %s", socketdir, gpg_strerror (err)); } xfree (socketdir); } break; case aShowVersions: { get_outfp (&outfp); show_versions (outfp); } break; } if (outfp != es_stdout) if (es_fclose (outfp)) gc_error (1, errno, "error closing '%s'", opt.outfile); if (log_get_errorcount (0)) gpgconf_failure (0); else gpgconf_write_status (STATUS_SUCCESS, NULL); return 0; } void gpgconf_failure (gpg_error_t err) { log_flush (); if (!err) err = gpg_error (GPG_ERR_GENERAL); gpgconf_write_status (STATUS_FAILURE, "- %u", gpg_err_code (err) == GPG_ERR_USER_2? GPG_ERR_EINVAL : err); exit (gpg_err_code (err) == GPG_ERR_USER_2? 2 : 1); } /* Parse the revision part from the extended version blurb. */ static const char * get_revision_from_blurb (const char *blurb, int *r_len) { const char *s = blurb? blurb : ""; int n; for (; *s; s++) if (*s == '\n' && s[1] == '(') break; if (s) { s += 2; for (n=0; s[n] && s[n] != ' '; n++) ; } else { s = "?"; n = 1; } *r_len = n; return s; } static void show_version_gnupg (estream_t fp) { es_fprintf (fp, "* GnuPG %s (%s)\n%s\n", strusage (13), BUILD_REVISION, strusage (17)); #ifdef HAVE_W32_SYSTEM { OSVERSIONINFO osvi = { sizeof (osvi) }; GetVersionEx (&osvi); es_fprintf (fp, "Windows %lu.%lu build %lu%s%s%s\n", (unsigned long)osvi.dwMajorVersion, (unsigned long)osvi.dwMinorVersion, (unsigned long)osvi.dwBuildNumber, *osvi.szCSDVersion? " (":"", osvi.szCSDVersion, *osvi.szCSDVersion? ")":"" ); } #endif /*HAVE_W32_SYSTEM*/ } static void show_version_libgcrypt (estream_t fp) { const char *s; int n; s = get_revision_from_blurb (gcry_check_version ("\x01\x01"), &n); es_fprintf (fp, "* Libgcrypt %s (%.*s)\n", gcry_check_version (NULL), n, s); s = gcry_get_config (0, NULL); if (s) es_fputs (s, fp); } static void show_version_gpgrt (estream_t fp) { const char *s; int n; s = get_revision_from_blurb (gpg_error_check_version ("\x01\x01"), &n); es_fprintf (fp, "* GpgRT %s (%.*s)\n", gpg_error_check_version (NULL), n, s); } /* Printing version information for other libraries is problematic * because we don't want to link gpgconf to all these libraries. The * best solution is delegating this to dirmngr which uses libassuan, * libksba, libnpth and ntbtls anyway. */ static void show_versions_via_dirmngr (estream_t fp) { gpg_error_t err; const char *pgmname; const char *argv[2]; estream_t outfp; pid_t pid; char *line = NULL; size_t line_len = 0; ssize_t length; int exitcode; pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); argv[0] = "--gpgconf-versions"; argv[1] = NULL; err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0, NULL, &outfp, NULL, &pid); if (err) { log_error ("error spawning %s: %s", pgmname, gpg_strerror (err)); es_fprintf (fp, "[error: can't get further info]\n"); return; } while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { /* Strip newline and carriage return, if present. */ while (length > 0 && (line[length - 1] == '\n' || line[length - 1] == '\r')) line[--length] = '\0'; es_fprintf (fp, "%s\n", line); } if (length < 0 || es_ferror (outfp)) { err = gpg_error_from_syserror (); log_error ("error reading from %s: %s\n", pgmname, gpg_strerror (err)); } if (es_fclose (outfp)) { err = gpg_error_from_syserror (); log_error ("error closing output stream of %s: %s\n", pgmname, gpg_strerror (err)); } err = gnupg_wait_process (pgmname, pid, 1, &exitcode); if (err) { log_error ("running %s failed (exitcode=%d): %s\n", pgmname, exitcode, gpg_strerror (err)); es_fprintf (fp, "[error: can't get further info]\n"); } gnupg_release_process (pid); xfree (line); } /* Show all kind of version information. */ static void show_versions (estream_t fp) { show_version_gnupg (fp); es_fputc ('\n', fp); show_version_libgcrypt (fp); es_fputc ('\n', fp); show_version_gpgrt (fp); es_fputc ('\n', fp); show_versions_via_dirmngr (fp); }