diff --git a/common/homedir.c b/common/homedir.c index 15475c256..830b1e2ce 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,1192 +1,1225 @@ /* homedir.c - Setup the home directory. * Copyright (C) 2004, 2006, 2007, 2010 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 #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. */ static byte w32_portable_app; #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32_SYSTEM /* This flag is true if this process's 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); 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) 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. */ const char * gnupg_cachedir (void) { #ifdef HAVE_W32_SYSTEM static const char *dir; if (!dir) { const char *rdir; rdir = w32_rootdir (); if (w32_portable_app) { dir = xstrconcat (rdir, DIRSEP_S, "var", DIRSEP_S, "cache", DIRSEP_S, "gnupg", NULL); } 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 } } } return dir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the standard socket name used by gpg-agent. */ const char * gpg_agent_socket_name (void) { static char *name; if (!name) name = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); 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 user socket name used by Keyboxd. */ const char * keyboxd_socket_name (void) { static char *name; if (!name) name = make_filename (gnupg_socketdir (), KEYBOXD_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_KEYBOXD: #ifdef GNUPG_DEFAULT_KEYBOXD return GNUPG_DEFAULT_KEYBOXD; #else X(libexecdir, "kbx", KEYBOXD_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/util.h b/common/util.h index 7217a5208..18217af7b 100644 --- a/common/util.h +++ b/common/util.h @@ -1,424 +1,425 @@ /* 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 are used but not defined in the required * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ #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 /* The length of the unique blob identifier as used by the keyboxd. * This is the possible truncated fingerprint of the primary key. */ #define UBID_LEN 20 /* Get all the stuff from jnlib. */ #include "../common/logging.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 xtryreallocarray(a,b,c,d) gpgrt_reallocarray ((a),(b),(c),(d)) #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)) /* See also the xreallocarray prototype below. */ /* 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); 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); unsigned int hex2fixedbuf (const char *hexstr, void *buffer, size_t bufsize); /*-- percent.c --*/ char *percent_plus_escape (const char *string); char *percent_data_escape (int plus, 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); int openpgp_oid_is_cv448 (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_oid_or_name_to_curve (const char *oidname, 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); const char *get_keyalgo_string (enum gcry_pk_algos algo, unsigned int nbits, const char *curve); /*-- 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); const char *keyboxd_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 #define GNUPG_MODULE_NAME_KEYBOXD 13 const char *gnupg_module_name (int which); void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); /* A list of constants to identify protocols. This is used by tools * which need to distinguish between the different protocols * implemented by GnuPG. May be used as bit flags. */ #define GNUPG_PROTOCOL_OPENPGP 1 /* The one and only (gpg). */ #define GNUPG_PROTOCOL_CMS 2 /* The core of S/MIME (gpgsm) */ #define GNUPG_PROTOCOL_SSH_AGENT 4 /* Out ssh-agent implementation */ /*-- 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); /*-- 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); /* Wrapper aroung gpgrt_reallocarray. Uses the gpgrt alloc function * which are redirect to the Libgcrypt versions via * init_common_subsystems. Thus they can be used interchangeable with * the other alloc functions. */ void *xreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); /* 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); /* 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); char *decode_c_string (const char *src); 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/g10/openfile.c b/g10/openfile.c index be186bd81..12a232a19 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,423 +1,405 @@ /* openfile.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009, * 2010 Free Software Foundation, Inc. * * 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 . */ #include #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "../common/ttyio.h" #include "options.h" #include "main.h" #include "../common/status.h" #include "../common/i18n.h" #ifdef HAVE_W32_SYSTEM #define NAME_OF_DEV_NULL "nul" #else #define NAME_OF_DEV_NULL "/dev/null" #endif #if defined (HAVE_DRIVE_LETTERS) || defined (__riscos__) #define CMP_FILENAME(a,b) ascii_strcasecmp( (a), (b) ) #else #define CMP_FILENAME(a,b) strcmp( (a), (b) ) #endif /* FIXME: Implement opt.interactive. */ /* * Check whether FNAME exists and ask if it's okay to overwrite an * existing one. * Returns: True: it's okay to overwrite or the file does not exist * False: Do not overwrite */ int overwrite_filep( const char *fname ) { if ( iobuf_is_pipe_filename (fname) ) return 1; /* Writing to stdout is always okay. */ if ( access( fname, F_OK ) ) return 1; /* Does not exist. */ if ( !compare_filenames (fname, NAME_OF_DEV_NULL) ) return 1; /* Does not do any harm. */ if (opt.answer_yes) return 1; if (opt.answer_no || opt.batch) return 0; /* Do not overwrite. */ tty_printf (_("File '%s' exists. "), fname); if (cpr_enabled ()) tty_printf ("\n"); if (cpr_get_answer_is_yes ("openfile.overwrite.okay", _("Overwrite? (y/N) ")) ) return 1; return 0; } /* * Strip known extensions from iname and return a newly allocated * filename. Return NULL if we can't do that. */ char * make_outfile_name (const char *iname) { size_t n; if (iobuf_is_pipe_filename (iname)) return xstrdup ("-"); n = strlen (iname); if (n > 4 && (!CMP_FILENAME(iname+n-4, EXTSEP_S GPGEXT_GPG) || !CMP_FILENAME(iname+n-4, EXTSEP_S "pgp") || !CMP_FILENAME(iname+n-4, EXTSEP_S "sig") || !CMP_FILENAME(iname+n-4, EXTSEP_S "asc"))) { char *buf = xstrdup (iname); buf[n-4] = 0; return buf; } else if (n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign")) { char *buf = xstrdup (iname); buf[n-5] = 0; return buf; } log_info (_("%s: unknown suffix\n"), iname); return NULL; } /* Ask for an output filename; use the given one as default. Return NULL if no file has been given or if it is not possible to ask the user. NAME is the template len which might contain enbedded Nuls. NAMELEN is its actual length. */ char * ask_outfile_name( const char *name, size_t namelen ) { size_t n; const char *s; char *prompt; char *fname; char *defname; if ( opt.batch ) return NULL; defname = name && namelen? make_printable_string (name, namelen, 0) : NULL; s = _("Enter new filename"); n = strlen(s) + (defname?strlen (defname):0) + 10; prompt = xmalloc (n); if (defname) snprintf (prompt, n, "%s [%s]: ", s, defname ); else snprintf (prompt, n, "%s: ", s ); tty_enable_completion(NULL); fname = cpr_get ("openfile.askoutname", prompt ); cpr_kill_prompt (); tty_disable_completion (); xfree (prompt); if ( !*fname ) { xfree (fname); fname = defname; defname = NULL; } xfree (defname); if (fname) trim_spaces (fname); return fname; } /* * Make an output filename for the inputfile INAME. * Returns an IOBUF and an errorcode * Mode 0 = use ".gpg" * 1 = use ".asc" * 2 = use ".sig" * 3 = use ".rev" * * With RESTRICTEDPERM a file will be created with mode 700 if * possible. * * If OUT_FD is not -1 the function simply creates an IOBUF for that * file descriptor and ignores INAME and MODE. Note that OUT_FD won't * be closed if the returned IOBUF is closed. This is used for gpg's * --server mode. */ int open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a) { int rc = 0; *a = NULL; if (out_fd != -1) { char xname[64]; *a = iobuf_fdopen_nc (out_fd, "wb"); if (!*a) { rc = gpg_error_from_syserror (); snprintf (xname, sizeof xname, "[fd %d]", out_fd); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { snprintf (xname, sizeof xname, "[fd %d]", out_fd); log_info (_("writing to '%s'\n"), xname); } } else if (iobuf_is_pipe_filename (iname) && !opt.outfile) { *a = iobuf_create (NULL, 0); if ( !*a ) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), "[stdout]", strerror(errno) ); } else if ( opt.verbose ) log_info (_("writing to stdout\n")); } else { char *buf = NULL; const char *name; if (opt.dry_run) name = NAME_OF_DEV_NULL; else if (opt.outfile) name = opt.outfile; else { #ifdef USE_ONLY_8DOT3 if (opt.mangle_dos_filenames) { /* It is quite common for DOS systems to have only one dot in a filename. If we have something like this, we simple replace the suffix except in cases where the suffix is larger than 3 characters and not the same as the new one. We don't map the filenames to 8.3 because this is a duty of the file system. */ char *dot; const char *newsfx; newsfx = (mode==1 ? ".asc" : mode==2 ? ".sig" : mode==3 ? ".rev" : ".gpg"); buf = xmalloc (strlen(iname)+4+1); strcpy (buf, iname); dot = strchr (buf, '.' ); if ( dot && dot > buf && dot[1] && strlen(dot) <= 4 && CMP_FILENAME (newsfx, dot) ) strcpy (dot, newsfx); else if (dot && !dot[1]) /* Do not duplicate a dot. */ strcpy (dot, newsfx+1); else strcat (buf, newsfx); } if (!buf) #endif /* USE_ONLY_8DOT3 */ { buf = xstrconcat (iname, (mode==1 ? EXTSEP_S "asc" : mode==2 ? EXTSEP_S "sig" : mode==3 ? EXTSEP_S "rev" : /* */ EXTSEP_S GPGEXT_GPG), NULL); } name = buf; } rc = 0; while ( !overwrite_filep (name) ) { char *tmp = ask_outfile_name (NULL, 0); if ( !tmp || !*tmp ) { xfree (tmp); rc = gpg_error (GPG_ERR_EEXIST); break; } xfree (buf); name = buf = tmp; } if ( !rc ) { if (is_secured_filename (name) ) { *a = NULL; gpg_err_set_errno (EPERM); } else *a = iobuf_create (name, restrictedperm); if (!*a) { rc = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), name, strerror(errno) ); } else if( opt.verbose ) log_info (_("writing to '%s'\n"), name ); } xfree(buf); } if (*a) iobuf_ioctl (*a, IOBUF_IOCTL_NO_CACHE, 1, NULL); return rc; } /* Find a matching data file for the signature file SIGFILENAME and return it as a malloced string. If no matching data file is found, return NULL. */ char * get_matching_datafile (const char *sigfilename) { char *fname = NULL; size_t len; if (iobuf_is_pipe_filename (sigfilename)) return NULL; len = strlen (sigfilename); if (len > 4 && (!strcmp (sigfilename + len - 4, EXTSEP_S "sig") || (len > 5 && !strcmp(sigfilename + len - 5, EXTSEP_S "sign")) || !strcmp(sigfilename + len - 4, EXTSEP_S "asc"))) { fname = xstrdup (sigfilename); fname[len-(fname[len-1]=='n'?5:4)] = 0 ; if (gnupg_access (fname, R_OK )) { /* Not found or other error. */ xfree (fname); fname = NULL; } } return fname; } /* * Try to open a file without the extension ".sig" or ".asc" * Return NULL if such a file is not available. */ iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx) { iobuf_t a = NULL; char *buf; buf = get_matching_datafile (sigfilename); if (buf) { a = iobuf_open (buf); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if (a) log_info (_("assuming signed data in '%s'\n"), buf); if (a && pfx) handle_progress (pfx, a, buf); xfree (buf); } return a; } +/* Create the 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. */ void try_make_homedir (const char *fname) { - const char *defhome = standard_homedir (); - - /* Create the 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 ( opt.dry_run || opt.no_homedir_creation ) return; - 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 (!opt.quiet ) - log_info ( _("directory '%s' created\n"), fname ); - } + gnupg_maybe_make_homedir (fname, opt.quiet); } /* Get and if needed create a string with the directory used to store openpgp revocations. */ char * get_openpgp_revocdir (const char *home) { char *fname; struct stat statbuf; fname = make_filename (home, GNUPG_OPENPGP_REVOC_DIR, NULL); if (gnupg_stat (fname, &statbuf) && errno == ENOENT) { if (gnupg_mkdir (fname, "-rwx")) log_error (_("can't create directory '%s': %s\n"), fname, strerror (errno) ); else if (!opt.quiet) log_info (_("directory '%s' created\n"), fname); } return fname; } diff --git a/po/POTFILES.in b/po/POTFILES.in index 28d0f3602..a5a23398e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,134 +1,135 @@ # List of files with translatable strings agent/call-pinentry.c agent/command-ssh.c agent/divert-scd.c agent/genkey.c agent/gpg-agent.c agent/preset-passphrase.c agent/protect-tool.c agent/trustlist.c agent/findkey.c agent/pksign.c agent/cvt-openpgp.c common/exechelp-posix.c common/exechelp-w32.c common/exechelp-w32ce.c common/simple-pwquery.c common/sysutils.c common/yesno.c common/miscellaneous.c common/asshelp.c common/audit.c common/helpfile.c common/gettime.c common/ksba-io-support.c common/utf8conv.c common/dotlock.c common/init.c +common/homedir.c g10/armor.c g10/build-packet.c g10/call-agent.c g10/card-util.c g10/dearmor.c g10/decrypt.c g10/delkey.c g10/encrypt.c g10/decrypt-data.c g10/exec.c g10/export.c g10/getkey.c g10/gpg.c g10/gpgv.c g10/helptext.c g10/import.c g10/keydb.c g10/keyedit.c g10/keygen.c g10/keyid.c g10/keylist.c g10/keyring.c g10/keyserver.c g10/mainproc.c g10/misc.c g10/openfile.c g10/parse-packet.c g10/passphrase.c g10/photoid.c g10/pkclist.c g10/plaintext.c g10/pubkey-enc.c g10/revoke.c g10/seskey.c g10/sig-check.c g10/sign.c g10/skclist.c g10/tdbdump.c g10/tdbio.c g10/textfilter.c g10/tofu.c g10/trustdb.c g10/trust.c g10/verify.c g10/cipher-cfb.c g10/cipher-aead.c kbx/kbxutil.c scd/app-piv.c scd/app-p15.c scd/app-nks.c scd/app-openpgp.c scd/app-dinsig.c scd/scdaemon.c sm/call-agent.c sm/call-dirmngr.c sm/certchain.c sm/certcheck.c sm/certdump.c sm/certlist.c sm/certreqgen.c sm/certreqgen-ui.c sm/decrypt.c sm/delete.c sm/encrypt.c sm/export.c sm/gpgsm.c sm/import.c sm/keydb.c sm/keylist.c sm/misc.c sm/qualified.c sm/sign.c sm/verify.c dirmngr/certcache.c dirmngr/crlcache.c dirmngr/crlfetch.c dirmngr/dirmngr-client.c dirmngr/dirmngr.c dirmngr/dirmngr_ldap.c dirmngr/http.c dirmngr/ldap-wrapper.c dirmngr/ldap.c dirmngr/ldapserver.c dirmngr/misc.c dirmngr/ocsp.c dirmngr/server.c dirmngr/validate.c tools/gpg-connect-agent.c tools/gpgconf-comp.c tools/gpgconf.c tools/no-libgcrypt.c tools/symcryptrun.c tools/gpg-check-pattern.c tools/gpg-card.c tools/card-misc.c tools/card-keys.c tools/card-yubikey.c tools/card-call-scd.c diff --git a/sm/keydb.c b/sm/keydb.c index 71b98d764..e092e4a01 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1,2156 +1,2133 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2014, 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 . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include "../kbx/keybox.h" #include "keydb.h" #include "../common/i18n.h" #include "../common/asshelp.h" #include "../kbx/kbx-client-util.h" typedef enum { KEYDB_RESOURCE_TYPE_NONE = 0, KEYDB_RESOURCE_TYPE_KEYBOX } KeydbResourceType; #define MAX_KEYDB_RESOURCES 20 struct resource_item { KeydbResourceType type; union { KEYBOX_HANDLE kr; } u; void *token; }; /* Data used to keep track of keybox daemon sessions. This allows us * to use several sessions with the keyboxd and also to re-use already * established sessions. Note that gpgdm.h defines the type * keydb_local_t for this structure. */ struct keydb_local_s { /* Link to other keyboxd contexts which are used simultaneously. */ struct keydb_local_s *next; /* The active Assuan context. */ assuan_context_t ctx; /* The client data helper context. */ kbx_client_data_t kcd; /* I/O buffer with the last search result or NULL. Used if * D-lines are used to convey the keyblocks. */ struct { char *buf; size_t len; } search_result; /* This flag set while an operation is running on this context. */ unsigned int is_active : 1; /* Flag indicating that a search reset is required. */ unsigned int need_search_reset : 1; }; static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; static int used_resources; /* Whether we have successfully registered any resource. */ static int any_registered; /* Number of active handles. */ static int active_handles; struct keydb_handle { /* CTRL object passed to keydb_new. */ ctrl_t ctrl; /* If set the keyboxdd is used instead of the local files. */ int use_keyboxd; /* BEGIN USE_KEYBOXD */ /* (These fields are only valid if USE_KEYBOXD is set.) */ /* Connection info which also keeps the local state. (This points * into the CTRL->keybox_local list.) */ keydb_local_t kbl; /* Various flags. */ unsigned int last_ubid_valid:1; unsigned int last_is_ephemeral; /* Last found key is ephemeral. */ /* The UBID of the last returned keyblock. */ unsigned char last_ubid[UBID_LEN]; /* END USE_KEYBOXD */ /* BEGIN !USE_KEYBOXD */ /* (The remaining fields are only valid if USE_KEYBOXD is cleared.) */ /* If this flag is set the resources is locked. */ int locked; /* If this flag is set a lock will only be released by * keydb_release. */ int keep_lock; int found; int saved_found; int current; int is_ephemeral; int used; /* items in active */ struct resource_item active[MAX_KEYDB_RESOURCES]; /* END !USE_KEYBOXD */ }; static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); /* Deinitialize all session resources pertaining to the keyboxd. */ void gpgsm_keydb_deinit_session_data (ctrl_t ctrl) { keydb_local_t kbl; while ((kbl = ctrl->keydb_local)) { ctrl->keydb_local = kbl->next; if (kbl->is_active) log_error ("oops: trying to cleanup an active keydb context\n"); else { kbx_client_data_release (kbl->kcd); kbl->kcd = NULL; assuan_release (kbl->ctx); kbl->ctx = NULL; } xfree (kbl); } } static void try_make_homedir (const char *fname) { - const char *defhome = standard_homedir (); - - /* Create the 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 ( opt.dry_run || opt.no_homedir_creation ) return; - 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_info (_("can't create directory '%s': %s\n"), - fname, strerror(errno) ); - else if (!opt.quiet ) - log_info (_("directory '%s' created\n"), fname); - } + gnupg_maybe_make_homedir (fname, opt.quiet); } /* Handle the creation of a keybox if it does not yet exist. Take into account that other processes might have the keybox already locked. This lock check does not work if the directory itself is not yet available. If R_CREATED is not NULL it will be set to true if the function created a new keybox. */ static gpg_error_t maybe_create_keybox (char *filename, int force, int *r_created) { gpg_err_code_t ec; dotlock_t lockhd = NULL; estream_t fp; int rc; mode_t oldmask; char *last_slash_in_filename; int save_slash; if (r_created) *r_created = 0; /* A quick test whether the filename already exists. */ if (!gnupg_access (filename, F_OK)) return !gnupg_access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES); /* If we don't want to create a new file at all, there is no need to go any further - bail out right here. */ if (!force) return gpg_error (GPG_ERR_ENOENT); /* First of all we try to create the home directory. Note, that we don't do any locking here because any sane application of gpg would create the home directory by itself and not rely on gpg's tricky auto-creation which is anyway only done for some home directory name patterns. */ last_slash_in_filename = strrchr (filename, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *p = strrchr (filename, '/'); if (!last_slash_in_filename || p > last_slash_in_filename) last_slash_in_filename = p; } #endif /*HAVE_W32_SYSTEM*/ if (!last_slash_in_filename) return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should not happen though. */ save_slash = *last_slash_in_filename; *last_slash_in_filename = 0; if (gnupg_access(filename, F_OK)) { static int tried; if (!tried) { tried = 1; try_make_homedir (filename); } if ((ec = gnupg_access (filename, F_OK))) { rc = gpg_error (ec); *last_slash_in_filename = save_slash; goto leave; } } *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keybox (it is removed during an update for a short time), we do the next stuff in a locked state. */ lockhd = dotlock_create (filename, 0); if (!lockhd) { /* A reason for this to fail is that the directory is not writable. However, this whole locking stuff does not make sense if this is the case. An empty non-writable directory with no keyring is not really useful at all. */ if (opt.verbose) log_info ("can't allocate lock for '%s'\n", filename ); if (!force) return gpg_error (GPG_ERR_ENOENT); else return gpg_error (GPG_ERR_GENERAL); } if ( dotlock_take (lockhd, -1) ) { /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock '%s'\n", filename); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Now the real test while we are locked. */ if (!access(filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; } /* The file does not yet exist, create it now. */ oldmask = umask (077); fp = es_fopen (filename, "wb"); if (!fp) { rc = gpg_error_from_syserror (); umask (oldmask); log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } umask (oldmask); /* Make sure that at least one record is in a new keybox file, so that the detection magic for OpenPGP keyboxes works the next time it is used. */ rc = _keybox_write_header_blob (fp, 0); if (rc) { es_fclose (fp); log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } if (!opt.quiet) log_info (_("keybox '%s' created\n"), filename); if (r_created) *r_created = 1; es_fclose (fp); rc = 0; leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } return rc; } /* * Register a resource (which currently may only be a keybox file). * The first keybox which is added by this function is created if it * does not exist. If AUTO_CREATED is not NULL it will be set to true * if the function has created a new keybox. */ gpg_error_t keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created) { const char *resname = url; char *filename = NULL; gpg_error_t err = 0; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; if (auto_created) *auto_created = 0; /* Do we have an URL? gnupg-kbx:filename := this is a plain keybox filename := See what it is, but create as plain keybox. */ if (strlen (resname) > 10) { if (!strncmp (resname, "gnupg-kbx:", 10) ) { rt = KEYDB_RESOURCE_TYPE_KEYBOX; resname += 10; } #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ } if (*resname != DIRSEP_C ) { /* do tilde expansion etc */ if (strchr(resname, DIRSEP_C) ) filename = make_filename (resname, NULL); else filename = make_filename (gnupg_homedir (), resname, NULL); } else filename = xstrdup (resname); if (!force) force = !any_registered; /* see whether we can determine the filetype */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { estream_t fp; fp = es_fopen( filename, "rb" ); if (fp) { u32 magic; /* FIXME: check for the keybox magic */ if (es_fread (&magic, 4, 1, fp) == 1 ) { if (magic == 0x13579ace || magic == 0xce9a5713) ; /* GDBM magic - no more support */ else rt = KEYDB_RESOURCE_TYPE_KEYBOX; } else /* maybe empty: assume keybox */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; es_fclose (fp); } else /* no file yet: create keybox */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; } switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; case KEYDB_RESOURCE_TYPE_KEYBOX: err = maybe_create_keybox (filename, force, auto_created); if (err) goto leave; /* Now register the file */ { void *token; err = keybox_register_file (filename, 0, &token); if (gpg_err_code (err) == GPG_ERR_EEXIST) ; /* Already registered - ignore. */ else if (err) ; /* Other error. */ else if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { KEYBOX_HANDLE kbxhd; all_resources[used_resources].type = rt; all_resources[used_resources].u.kr = NULL; /* Not used here */ all_resources[used_resources].token = token; /* Do a compress run if needed and the keybox is not locked. */ kbxhd = keybox_new_x509 (token, 0); if (kbxhd) { if (!keybox_lock (kbxhd, 1, 0)) { keybox_compress (kbxhd); keybox_lock (kbxhd, 0, 0); } keybox_release (kbxhd); } used_resources++; } } break; default: log_error ("resource type of '%s' not supported\n", url); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } /* fixme: check directory permissions and print a warning */ leave: if (err) { log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror (err)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "add_keyblock_resource", err); } else any_registered = 1; xfree (filename); return err; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername) { return warn_server_version_mismatch (ctx, servername, 0, gpgsm_status2, ctrl, !opt.quiet); } /* Connect to the keybox daemon and launch it if necessary. Handle * the server's initial greeting and set global options. Returns a * new assuan context or an error. */ static gpg_error_t create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) { gpg_error_t err; assuan_context_t ctx; *r_ctx = NULL; err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, opt.autostart, opt.verbose, DBG_IPC, NULL, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) { static int shown; if (!shown) { shown = 1; log_info (_("no keyboxd running in this session\n")); } } else if (!err && !(err = warn_version_mismatch (ctrl, ctx, KEYBOXD_NAME))) { /* Place to emit global options. */ } if (err) assuan_release (ctx); else *r_ctx = ctx; return err; } /* Get a context for accessing keyboxd. If no context is available a * new one is created and if necessary keyboxd is started. R_KBL * receives a pointer to the local context object. */ static gpg_error_t open_context (ctrl_t ctrl, keydb_local_t *r_kbl) { gpg_error_t err; keydb_local_t kbl; *r_kbl = NULL; for (;;) { for (kbl = ctrl->keydb_local; kbl && kbl->is_active; kbl = kbl->next) ; if (kbl) { /* Found an inactive keyboxd session - return that. */ log_assert (!kbl->is_active); kbl->is_active = 1; kbl->need_search_reset = 1; *r_kbl = kbl; return 0; } /* None found. Create a new session and retry. */ kbl = xtrycalloc (1, sizeof *kbl); if (!kbl) return gpg_error_from_syserror (); err = create_new_context (ctrl, &kbl->ctx); if (err) { xfree (kbl); return err; } err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 1); if (err) { assuan_release (kbl->ctx); xfree (kbl); return err; } /* For thread-saftey we add it to the list and retry; this is * easier than to employ a lock. */ kbl->next = ctrl->keydb_local; ctrl->keydb_local = kbl; } /*NOTREACHED*/ } KEYDB_HANDLE keydb_new (ctrl_t ctrl) { gpg_error_t err; KEYDB_HANDLE hd; int rc, i, j; if (DBG_CLOCK) log_clock ("%s: enter\n", __func__); hd = xcalloc (1, sizeof *hd); hd->found = -1; hd->saved_found = -1; hd->use_keyboxd = opt.use_keyboxd; hd->ctrl = ctrl; if (hd->use_keyboxd) { err = open_context (ctrl, &hd->kbl); if (err) { log_error (_("error opening key DB: %s\n"), gpg_strerror (err)); xfree (hd); hd = NULL; if (!(rc = gpg_err_code_to_errno (err))) rc = gpg_err_code_to_errno (GPG_ERR_EIO); gpg_err_set_errno (rc); goto leave; } } else /* Use the local keybox. */ { log_assert (used_resources <= MAX_KEYDB_RESOURCES); for (i=j=0; i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, 0); if (!hd->active[j].u.kr) { xfree (hd); return NULL; /* fixme: free all previously allocated handles*/ } j++; break; } } hd->used = j; } active_handles++; leave: if (DBG_CLOCK) log_clock ("%s: leave (hd=%p)\n", __func__, hd); return hd; } void keydb_release (KEYDB_HANDLE hd) { keydb_local_t kbl; int i; if (!hd) return; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); log_assert (active_handles > 0); active_handles--; if (hd->use_keyboxd) { kbl = hd->kbl; if (DBG_CLOCK) log_clock ("close_context (found)"); if (!kbl->is_active) log_fatal ("closing inactive keyboxd context %p\n", kbl); kbl->is_active = 0; hd->kbl = NULL; } else { hd->keep_lock = 0; unlock_all (hd); for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_release (hd->active[i].u.kr); break; } } } xfree (hd); if (DBG_CLOCK) log_clock ("%s: leave\n", __func__); } /* Return the name of the current resource. This is function first looks for the last found found, then for the current search position, and last returns the first available resource. The returned string is only valid as long as the handle exists. This function does only return NULL if no handle is specified, in all other error cases an empty string is returned. */ const char * keydb_get_resource_name (KEYDB_HANDLE hd) { int idx; const char *s = NULL; if (!hd) return NULL; if (hd->use_keyboxd) return "[keyboxd]"; if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else idx = 0; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: s = NULL; break; case KEYDB_RESOURCE_TYPE_KEYBOX: s = keybox_get_resource_name (hd->active[idx].u.kr); break; } return s? s: ""; } /* Switch the handle into ephemeral mode and return the original value. */ int keydb_set_ephemeral (KEYDB_HANDLE hd, int yes) { int i; if (!hd) return 0; if (hd->use_keyboxd) return 0; /* FIXME: No support yet. */ yes = !!yes; if (hd->is_ephemeral != yes) { for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_set_ephemeral (hd->active[i].u.kr, yes); break; } } } i = hd->is_ephemeral; hd->is_ephemeral = yes; return i; } /* If the keyring has not yet been locked, lock it now. This * operation is required before any update operation; it is optional * for an insert operation. The lock is kept until a keydb_release so * that internal unlock_all calls have no effect. */ gpg_error_t keydb_lock (KEYDB_HANDLE hd) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (hd->use_keyboxd) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); err = lock_all (hd); if (!err) hd->keep_lock = 1; if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } static int lock_all (KEYDB_HANDLE hd) { int i, rc = 0; if (hd->use_keyboxd) return 0; /* Fixme: This locking scheme may lead to deadlock if the resources are not added in the same order by all processes. We are currently only allowing one resource so it is not a problem. */ for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_lock (hd->active[i].u.kr, 1, -1); break; } if (rc) break; } if (rc) { /* Revert the already set locks. */ for (i--; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kr, 0, 0); break; } } } else hd->locked = 1; return rc; } static void unlock_all (KEYDB_HANDLE hd) { int i; if (hd->use_keyboxd) return; if (!hd->locked || hd->keep_lock) return; for (i=hd->used-1; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kr, 0, 0); break; } } hd->locked = 0; } /* Push the last found state if any. */ void keydb_push_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->use_keyboxd) return; /* FIXME: Do we need this? */ if (hd->found < 0 || hd->found >= hd->used) { hd->saved_found = -1; return; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_push_found_state (hd->active[hd->found].u.kr); break; } hd->saved_found = hd->found; hd->found = -1; if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } /* Pop the last found state. */ void keydb_pop_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->use_keyboxd) return; /* FIXME: Do we need this? */ hd->found = hd->saved_found; hd->saved_found = -1; if (hd->found < 0 || hd->found >= hd->used) return; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_pop_found_state (hd->active[hd->found].u.kr); break; } if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } /* Return the last found certificate. Caller must free it. */ int keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) { int err = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { ksba_cert_t cert; /* Fixme: We should clear that also in non-keyboxd mode but we * did not in the past and thus all code should be checked * whether this is okay. If we run into error in keyboxd mode, * this is a not as severe because keyboxd is currently * experimental. */ *r_cert = NULL; if (!hd->kbl->search_result.buf || !hd->kbl->search_result.len) { err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); goto leave; } err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, hd->kbl->search_result.buf, hd->kbl->search_result.len); if (err) { ksba_cert_release (cert); goto leave; } xfree (hd->kbl->search_result.buf); hd->kbl->search_result.buf = NULL; hd->kbl->search_result.len = 0; *r_cert = cert; goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { /* Fixme: It would be better to use GPG_ERR_VALUE_NOT_FOUND here * but for now we use NOT_FOUND because that is our standard * replacement for the formerly used (-1). */ err = gpg_error (GPG_ERR_NOT_FOUND); /* nothing found */ goto leave; } err = GPG_ERR_BUG; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_get_cert (hd->active[hd->found].u.kr, r_cert); break; } leave: if (DBG_CLOCK) log_clock ("%s: leave (rc=%d)\n", __func__, err); return err; } /* Return a flag of the last found object. WHICH is the flag requested; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored at the address given by VALUE. Return 0 on success or an error code. */ gpg_error_t keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ *value = 0; err = 0; goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { err = gpg_error (GPG_ERR_NOTHING_FOUND); goto leave; } err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value); break; } leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* Set a flag of the last found object. WHICH is the flag to be set; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored in the keybox. Note, that some flag values can't be updated and thus may return an error, some other flag values may be masked out before an update. Returns 0 on success or an error code. */ gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value) { gpg_error_t err = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { err = gpg_error (GPG_ERR_NOTHING_FOUND); goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value); break; } leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* Communication object for Keyboxd STORE commands. */ struct store_parm_s { assuan_context_t ctx; const void *data; /* The certificate in X.509 binary format. */ size_t datalen; /* The length of DATA. */ }; /* Handle the inquiries from the STORE command. */ static gpg_error_t store_inq_cb (void *opaque, const char *line) { struct store_parm_s *parm = opaque; gpg_error_t err = 0; if (has_leading_keyword (line, "BLOB")) { if (parm->data) err = assuan_send_data (parm->ctx, parm->data, parm->datalen); } else return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); return err; } /* * Insert a new Certificate into one of the resources. */ gpg_error_t keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { gpg_error_t err; int idx; unsigned char digest[20]; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { struct store_parm_s parm; parm.ctx = hd->kbl->ctx; parm.data = ksba_cert_get_image (cert, &parm.datalen); if (!parm.data) { log_debug ("broken ksba cert object\n"); err = gpg_error (GPG_ERR_GENERAL); goto leave; } err = assuan_transact (hd->kbl->ctx, "STORE --insert", NULL, NULL, store_inq_cb, &parm, NULL, NULL); goto leave; } if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else { err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ err = gpg_error (GPG_ERR_BUG); switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* Update the current keyblock with KB. */ /* Note: This function is currently not called. */ gpg_error_t keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { (void)hd; (void)cert; return GPG_ERR_BUG; #if 0 gpg_error_t err; unsigned char digest[20]; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if ( hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ goto leave; } err = lock_all (hd); if (err) goto leave; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; #endif /*0*/ } /* * The current keyblock or cert will be deleted. */ gpg_error_t keydb_delete (KEYDB_HANDLE hd) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->use_keyboxd && (hd->found < 0 || hd->found >= hd->used)) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { unsigned char hexubid[UBID_LEN * 2 + 1]; char line[ASSUAN_LINELENGTH]; if (!hd->last_ubid_valid) { err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); goto leave; } bin2hex (hd->last_ubid, UBID_LEN, hexubid); snprintf (line, sizeof line, "DELETE %s", hexubid); err = assuan_transact (hd->kbl->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_delete (hd->active[hd->found].u.kr); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* * Locate the default writable key resource, so that the next * operation (which is only relevant for inserts) will be done on this * resource. */ static gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) { int rc; (void)reserved; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (hd->use_keyboxd) return 0; /* Not required. */ rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); break; case KEYDB_RESOURCE_TYPE_KEYBOX: if (keybox_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; } } return gpg_error (GPG_ERR_NOT_FOUND); } /* * Rebuild the caches of all key resources. */ void keydb_rebuild_caches (void) { int i; /* This function does nothing and thus we don't need to handle keyboxd in a * special way. */ for (i=0; i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: /* rc = keybox_rebuild_cache (all_resources[i].token); */ /* if (rc) */ /* log_error (_("failed to rebuild keybox cache: %s\n"), */ /* g10_errstr (rc)); */ break; } } } /* * Start the next search on this handle right at the beginning */ gpg_error_t keydb_search_reset (KEYDB_HANDLE hd) { gpg_error_t err = 0; int i; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); hd->current = 0; hd->found = -1; if (hd->use_keyboxd) { /* All we need is to tell search that a reset is pending. Note that * keydb_new sets this flag as well. To comply with the * specification of keydb_delete_keyblock we also need to clear the * ubid flag so that after a reset a delete can't be performed. */ hd->kbl->need_search_reset = 1; hd->last_ubid_valid = 0; } else { /* Reset all resources */ for (i=0; !err && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_search_reset (hd->active[i].u.kr); break; } } } if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } char * keydb_search_desc_dump (struct keydb_search_desc *desc) { char *fpr; char *result; switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: return xasprintf ("EXACT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SUBSTR: return xasprintf ("SUBSTR: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAIL: return xasprintf ("MAIL: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILSUB: return xasprintf ("MAILSUB: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILEND: return xasprintf ("MAILEND: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_WORDS: return xasprintf ("WORDS: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SHORT_KID: return xasprintf ("SHORT_KID: '%08lX'", (ulong)desc->u.kid[1]); case KEYDB_SEARCH_MODE_LONG_KID: return xasprintf ("LONG_KID: '%08lX%08lX'", (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); case KEYDB_SEARCH_MODE_FPR: fpr = bin2hexcolon (desc->u.fpr, desc->fprlen, NULL); result = xasprintf ("FPR%02d: '%s'", desc->fprlen, fpr); xfree (fpr); return result; case KEYDB_SEARCH_MODE_ISSUER: return xasprintf ("ISSUER: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_ISSUER_SN: return xasprintf ("ISSUER_SN: '#%.*s/%s'", (int)desc->snlen,desc->sn, desc->u.name); case KEYDB_SEARCH_MODE_SN: return xasprintf ("SN: '%.*s'", (int)desc->snlen, desc->sn); case KEYDB_SEARCH_MODE_SUBJECT: return xasprintf ("SUBJECT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_KEYGRIP: return xasprintf ("KEYGRIP: %s", desc->u.grip); case KEYDB_SEARCH_MODE_FIRST: return xasprintf ("FIRST"); case KEYDB_SEARCH_MODE_NEXT: return xasprintf ("NEXT"); default: return xasprintf ("Bad search mode (%d)", desc->mode); } } /* Status callback for SEARCH and NEXT operaions. */ static gpg_error_t search_status_cb (void *opaque, const char *line) { KEYDB_HANDLE hd = opaque; gpg_error_t err = 0; const char *s; unsigned int n; if ((s = has_leading_keyword (line, "PUBKEY_INFO"))) { if (atoi (s) != PUBKEY_TYPE_X509) err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE); else { hd->last_ubid_valid = 0; while (*s && !spacep (s)) s++; if (!(n=hex2fixedbuf (s, hd->last_ubid, sizeof hd->last_ubid))) err = gpg_error (GPG_ERR_INV_VALUE); else { hd->last_ubid_valid = 1; s += n; hd->last_is_ephemeral = (*s == 'e'); } } } return err; } /* Search through all keydb resources, starting at the current * position, for a keyblock which contains one of the keys described * in the DESC array. In keyboxd mode the search is instead delegated * to the keyboxd. * * DESC is an array of search terms with NDESC entries. The search * terms are or'd together. That is, the next entry in the DB that * matches any of the descriptions will be returned. * * Note: this function resumes searching where the last search left * off (i.e., at the current file position). If you want to search * from the start of the database, then you need to first call * keydb_search_reset(). * * If no key matches the search description, the error code * GPG_ERR_NOT_FOUND is retruned. If there was a match, 0 is * returned. If an error occurred, that error code is returned. * * The returned key is considered to be selected and the certificate * can be detched via keydb_get_cert. */ gpg_error_t keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) { gpg_error_t err = gpg_error (GPG_ERR_EOF); unsigned long skipped; int i; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!any_registered && !hd->use_keyboxd) { gpgsm_status_with_error (ctrl, STATUS_ERROR, "keydb_search", gpg_error (GPG_ERR_KEYRING_OPEN)); return gpg_error (GPG_ERR_NOT_FOUND); } if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (DBG_LOOKUP) { log_debug ("%s: %zd search description(s):\n", __func__, ndesc); for (i = 0; i < ndesc; i ++) { char *t = keydb_search_desc_dump (&desc[i]); log_debug ("%s: %d: %s\n", __func__, i, t); xfree (t); } } if (hd->use_keyboxd) { char line[ASSUAN_LINELENGTH]; /* Clear the result objects. */ if (hd->kbl->search_result.buf) { xfree (hd->kbl->search_result.buf); hd->kbl->search_result.buf = NULL; hd->kbl->search_result.len = 0; } /* Check whether this is a NEXT search. */ if (!hd->kbl->need_search_reset) { /* A reset was not requested thus continue the search. The * keyboxd keeps the context of the search and thus the NEXT * operates on the last search pattern. This is the way how * we always used the keydb functions. In theory we were * able to modify the search pattern between searches but * that is not anymore supported by keyboxd and a cursory * check does not show that we actually made use of that * misfeature. */ snprintf (line, sizeof line, "NEXT --x509"); goto do_search; } hd->kbl->need_search_reset = 0; if (!ndesc) { err = gpg_error (GPG_ERR_INV_ARG); goto leave; } /* FIXME: Implement --multi */ switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: snprintf (line, sizeof line, "SEARCH --x509 =%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_SUBSTR: snprintf (line, sizeof line, "SEARCH --x509 *%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_MAIL: snprintf (line, sizeof line, "SEARCH --x509 <%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_MAILSUB: snprintf (line, sizeof line, "SEARCH --x509 @%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_MAILEND: snprintf (line, sizeof line, "SEARCH --x509 .%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_WORDS: snprintf (line, sizeof line, "SEARCH --x509 +%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_SHORT_KID: snprintf (line, sizeof line, "SEARCH --x509 0x%08lX", (ulong)desc->u.kid[1]); break; case KEYDB_SEARCH_MODE_LONG_KID: snprintf (line, sizeof line, "SEARCH --x509 0x%08lX%08lX", (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); break; case KEYDB_SEARCH_MODE_FPR: { unsigned char hexfpr[MAX_FINGERPRINT_LEN * 2 + 1]; log_assert (desc[0].fprlen <= MAX_FINGERPRINT_LEN); bin2hex (desc[0].u.fpr, desc[0].fprlen, hexfpr); snprintf (line, sizeof line, "SEARCH --x509 0x%s", hexfpr); } break; case KEYDB_SEARCH_MODE_ISSUER: snprintf (line, sizeof line, "SEARCH --x509 #/%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_ISSUER_SN: if (desc[0].snhex) snprintf (line, sizeof line, "SEARCH --x509 #%.*s/%s", (int)desc[0].snlen, desc[0].sn, desc[0].u.name); else { char *hexsn = bin2hex (desc[0].sn, desc[0].snlen, NULL); if (!hexsn) { err = gpg_error_from_syserror (); goto leave; } snprintf (line, sizeof line, "SEARCH --x509 #%s/%s", hexsn, desc[0].u.name); xfree (hexsn); } break; case KEYDB_SEARCH_MODE_SN: snprintf (line, sizeof line, "SEARCH --x509 #%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_SUBJECT: snprintf (line, sizeof line, "SEARCH --x509 /%s", desc[0].u.name); break; case KEYDB_SEARCH_MODE_KEYGRIP: { unsigned char hexgrip[KEYGRIP_LEN * 2 + 1]; bin2hex (desc[0].u.grip, KEYGRIP_LEN, hexgrip); snprintf (line, sizeof line, "SEARCH --x509 &%s", hexgrip); } break; case KEYDB_SEARCH_MODE_UBID: { unsigned char hexubid[UBID_LEN * 2 + 1]; bin2hex (desc[0].u.ubid, UBID_LEN, hexubid); snprintf (line, sizeof line, "SEARCH --x509 ^%s", hexubid); } break; case KEYDB_SEARCH_MODE_FIRST: snprintf (line, sizeof line, "SEARCH --x509"); break; case KEYDB_SEARCH_MODE_NEXT: log_debug ("%s: mode next - we should not get to here!\n", __func__); snprintf (line, sizeof line, "NEXT --x509"); break; default: err = gpg_error (GPG_ERR_INV_ARG); goto leave; } do_search: hd->last_ubid_valid = 0; /* To avoid silent truncation we error out on a too long line. */ if (strlen (line) + 5 >= sizeof line) err = gpg_error (GPG_ERR_ASS_LINE_TOO_LONG); else err = kbx_client_data_cmd (hd->kbl->kcd, line, search_status_cb, hd); if (!err && !(err = kbx_client_data_wait (hd->kbl->kcd, &hd->kbl->search_result.buf, &hd->kbl->search_result.len))) { /* if (hd->last_ubid_valid) */ /* log_printhex (hd->last_ubid, 20, "found UBID%s:", */ /* hd->last_is_ephemeral? "(ephemeral)":""); */ } } else /* Local keyring search. */ { while (gpg_err_code (err) == GPG_ERR_EOF && hd->current >= 0 && hd->current < hd->used) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); /* we should never see it here */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_search (hd->active[hd->current].u.kr, desc, ndesc, KEYBOX_BLOBTYPE_X509, NULL, &skipped); if (err == -1) /* Map legacy code. */ err = gpg_error (GPG_ERR_EOF); break; } if (DBG_LOOKUP) log_debug ("%s: searched %s (resource %d of %d) => %s\n", __func__, hd->active[hd->current].type==KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type", hd->current, hd->used, gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; } else if (!err) hd->found = hd->current; } } leave: /* The NOTHING_FOUND error is triggered by a NEXT command. */ if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NOTHING_FOUND) err = gpg_error (GPG_ERR_NOT_FOUND); if (DBG_CLOCK) log_clock ("%s: leave (%s)\n", __func__, gpg_strerror (err)); return err; } int keydb_search_first (ctrl_t ctrl, KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_next (ctrl_t ctrl, KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_NEXT; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_kid (ctrl_t ctrl, KEYDB_HANDLE hd, u32 *kid) { KEYDB_SEARCH_DESC desc; (void)kid; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = kid[0]; desc.u.kid[1] = kid[1]; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_fpr (ctrl_t ctrl, KEYDB_HANDLE hd, const byte *fpr) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FPR; memcpy (desc.u.fpr, fpr, 20); desc.fprlen = 20; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_issuer (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer) { KEYDB_SEARCH_DESC desc; int rc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_ISSUER; desc.u.name = issuer; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } int keydb_search_issuer_sn (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer, ksba_const_sexp_t serial) { KEYDB_SEARCH_DESC desc; int rc; const unsigned char *s; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN; s = serial; if (*s !='(') return gpg_error (GPG_ERR_INV_VALUE); s++; for (desc.snlen = 0; digitp (s); s++) desc.snlen = 10*desc.snlen + atoi_1 (s); if (*s !=':') return gpg_error (GPG_ERR_INV_VALUE); desc.sn = s+1; desc.u.name = issuer; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } int keydb_search_subject (ctrl_t ctrl, KEYDB_HANDLE hd, const char *name) { KEYDB_SEARCH_DESC desc; int rc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_SUBJECT; desc.u.name = name; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } /* Store the certificate in the key DB but make sure that it does not already exists. We do this simply by comparing the fingerprint. If EXISTED is not NULL it will be set to true if the certificate was already in the DB. */ int keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed) { KEYDB_HANDLE kh; int rc; unsigned char fpr[20]; if (existed) *existed = 0; if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) { log_error (_("failed to get the fingerprint\n")); return gpg_error (GPG_ERR_GENERAL); } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); return gpg_error (GPG_ERR_ENOMEM);; } /* Set the ephemeral flag so that the search looks at all records. */ keydb_set_ephemeral (kh, 1); if (!kh->use_keyboxd) { rc = lock_all (kh); if (rc) return rc; } rc = keydb_search_fpr (ctrl, kh, fpr); if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) { keydb_release (kh); if (!rc) { if (existed) *existed = 1; if (!ephemeral) { /* Remove ephemeral flags from existing certificate to "store" it permanently. */ rc = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (rc) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (rc)); return rc; } } return 0; /* okay */ } log_error (_("problem looking for existing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Reset the ephemeral flag if not requested. */ if (!ephemeral) keydb_set_ephemeral (kh, 0); rc = keydb_locate_writable (kh, 0); if (rc) { log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc)); keydb_release (kh); return rc; } rc = keydb_insert_cert (kh, cert); if (rc) { log_error (_("error storing certificate: %s\n"), gpg_strerror (rc)); keydb_release (kh); return rc; } keydb_release (kh); return 0; } /* This is basically keydb_set_flags but it implements a complete transaction by locating the certificate in the DB and updating the flags. */ gpg_error_t keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int which, int idx, unsigned int mask, unsigned int value) { KEYDB_HANDLE kh; gpg_error_t err; unsigned char fpr[20]; unsigned int old_value; if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) { log_error (_("failed to get the fingerprint\n")); return gpg_error (GPG_ERR_GENERAL); } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); return gpg_error (GPG_ERR_ENOMEM);; } if (ephemeral) keydb_set_ephemeral (kh, 1); if (!kh->use_keyboxd) { err = keydb_lock (kh); if (err) { log_error (_("error locking keybox: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } } err = keydb_search_fpr (ctrl, kh, fpr); if (err) { if (gpg_err_code (err) != gpg_error (GPG_ERR_NOT_FOUND)) log_error (_("problem re-searching certificate: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } err = keydb_get_flags (kh, which, idx, &old_value); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } value = ((old_value & ~mask) | (value & mask)); if (value != old_value) { err = keydb_set_flags (kh, which, idx, value); if (err) { log_error (_("error storing flags: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } } keydb_release (kh); return 0; } /* Reset all the certificate flags we have stored with the certificates for performance reasons. */ void keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names) { gpg_error_t err; KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; strlist_t sl; int rc=0; unsigned int old_value, value; (void)ctrl; hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); else ndesc++; } } if (!hd->use_keyboxd) { err = keydb_lock (hd); if (err) { log_error (_("error locking keybox: %s\n"), gpg_strerror (err)); goto leave; } } while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); goto leave; } value = (old_value & ~VALIDITY_REVOKED); if (value != old_value) { err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value); if (err) { log_error (_("error storing flags: %s\n"), gpg_strerror (err)); goto leave; } } } if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); leave: xfree (desc); keydb_release (hd); }