Allow installation with a gpgconf.ctl changed homedir.

* common/homedir.c (gpgconf_ctl): Add field "gnupg".
(parse_gpgconf_ctl): Support keyword "gnupg".
(my_gnupg_dirname): New.
(my_fixed_default_homedir): New.
(gnupg_registry_dir): New.
(standard_homedir): Use my_gnupg_dirname and my_fixed_default_homedir.
(default_homedir): Use gnupg_registry_dir and
my_fixed_default_homedir.
(_gnupg_socketdir_internal): Use my_gnupg_dirname.  Increase size of
prefixbuffer.
(gnupg_sysconfdir): Use my_gnupg_dirname.
* tools/gpgconf.c (list_dirs): Use gnupg_registry_dir.
(show_other_registry_entries): Ditto.
--

This will be useful to install versions of GnuPG VS-Desktop and GnuPG
Desktop in addition to a standard GnuPG version.  Only basic tests on
Unix done; Windows testing is still outstanding.

GnuPG-bug-id: 7040
This commit is contained in:
Werner Koch 2024-03-21 17:41:10 +01:00
parent fb3fe38d28
commit a0bfbdaaa2
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 145 additions and 26 deletions

View File

@ -1,7 +1,7 @@
/* homedir.c - Setup the home directory. /* homedir.c - Setup the home directory.
* Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2013, 2016 Werner Koch * Copyright (C) 2013, 2016 Werner Koch
* Copyright (C) 2021 g10 Code GmbH * Copyright (C) 2021, 2024 g10 Code GmbH
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -102,6 +102,7 @@ static struct
unsigned int empty:1; /* The file is empty except for comments. */ unsigned int empty:1; /* The file is empty except for comments. */
unsigned int valid:1; /* The entries in gpgconf.ctl are valid. */ unsigned int valid:1; /* The entries in gpgconf.ctl are valid. */
unsigned int portable:1;/* Windows portable installation. */ unsigned int portable:1;/* Windows portable installation. */
char *gnupg; /* The "gnupg" directory part. */
char *rootdir; /* rootdir or NULL */ char *rootdir; /* rootdir or NULL */
char *sysconfdir; /* sysconfdir or NULL */ char *sysconfdir; /* sysconfdir or NULL */
char *socketdir; /* socketdir or NULL */ char *socketdir; /* socketdir or NULL */
@ -134,6 +135,87 @@ static byte w32_bin_is_bin;
static const char *w32_rootdir (void); static const char *w32_rootdir (void);
#endif #endif
/* Return the name of the gnupg dir. This is usually "gnupg". */
static const char *
my_gnupg_dirname (void)
{
if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
return gpgconf_ctl.gnupg;
return "gnupg";
}
/* Return the hardwired home directory which is not anymore so
* hardwired because it may now be modified using the gpgconf.ctl
* "gnupg" keyword. */
static const char *
my_fixed_default_homedir (void)
{
if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
{
static char *name;
char *p;
if (!name)
{
name = xmalloc (strlen (GNUPG_DEFAULT_HOMEDIR)
+ strlen (gpgconf_ctl.gnupg) + 1);
strcpy (name, GNUPG_DEFAULT_HOMEDIR);
p = strrchr (name, '/');
if (p)
p++;
else
p = name;
if (*p == '.')
p++; /* Keep a leading dot. */
strcpy (p, gpgconf_ctl.gnupg);
gpgrt_annotate_leaked_object (name);
}
return name;
}
return GNUPG_DEFAULT_HOMEDIR;
}
/* Under Windows we need to modify the standard registry key with the
* "gnupg" keyword from a gpgconf.ctl. */
#ifdef HAVE_W32_SYSTEM
const char *
gnupg_registry_dir (void)
{
if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
{
static char *name;
char *p;
if (!name)
{
name = xmalloc (strlen (GNUPG_REGISTRY_DIR)
+ strlen (gpgconf_ctl.gnupg) + 1);
strcpy (name, GNUPG_REGISTRY_DIR);
p = strrchr (name, '\\');
if (p)
p++;
else
p = name;
strcpy (p, gpgconf_ctl.gnupg);
if (!strncmp (p, "gnupg", 5))
{
/* Registry keys are case-insensitive and we use a
* capitalized version of gnupg by default. So, if the
* new value starts with "gnupg" we apply the usual
* capitalization for this first part. */
p[0] = 'G';
p[3] = 'P';
p[4] = 'G';
}
gpgrt_annotate_leaked_object (name);
}
return name;
}
return GNUPG_REGISTRY_DIR;
}
#endif /*HAVE_W32_SYSTEM*/
/* This is a helper function to load and call a Windows function from /* This is a helper function to load and call a Windows function from
@ -339,7 +421,7 @@ standard_homedir (void)
NULL, 0); NULL, 0);
if (path) if (path)
{ {
dir = xstrconcat (path, "\\gnupg", NULL); dir = xstrconcat (path, "\\", my_gnupg_dirname (), NULL);
xfree (path); xfree (path);
gpgrt_annotate_leaked_object (dir); gpgrt_annotate_leaked_object (dir);
@ -350,12 +432,12 @@ standard_homedir (void)
} }
else else
dir = GNUPG_DEFAULT_HOMEDIR; dir = my_fixed_default_homedir ();
} }
} }
return dir; return dir;
#else/*!HAVE_W32_SYSTEM*/ #else/*!HAVE_W32_SYSTEM*/
return GNUPG_DEFAULT_HOMEDIR; return my_fixed_default_homedir ();
#endif /*!HAVE_W32_SYSTEM*/ #endif /*!HAVE_W32_SYSTEM*/
} }
@ -389,7 +471,7 @@ default_homedir (void)
* warning if the homedir has been taken from the * warning if the homedir has been taken from the
* registry. */ * registry. */
tmp = read_w32_registry_string (NULL, tmp = read_w32_registry_string (NULL,
GNUPG_REGISTRY_DIR, gnupg_registry_dir (),
"HomeDir"); "HomeDir");
if (tmp && !*tmp) if (tmp && !*tmp)
{ {
@ -414,7 +496,7 @@ default_homedir (void)
#endif /*HAVE_W32_SYSTEM*/ #endif /*HAVE_W32_SYSTEM*/
if (!dir || !*dir) if (!dir || !*dir)
dir = GNUPG_DEFAULT_HOMEDIR; dir = my_fixed_default_homedir ();
else else
{ {
char *p; char *p;
@ -470,6 +552,7 @@ parse_gpgconf_ctl (const char *fname)
const char *name; const char *name;
int anyitem = 0; int anyitem = 0;
int ignoreall = 0; int ignoreall = 0;
char *gnupgval = NULL;
char *rootdir = NULL; char *rootdir = NULL;
char *sysconfdir = NULL; char *sysconfdir = NULL;
char *socketdir = NULL; char *socketdir = NULL;
@ -500,6 +583,7 @@ parse_gpgconf_ctl (const char *fname)
{ {
static const char *names[] = static const char *names[] =
{ {
"gnupg",
"rootdir", "rootdir",
"sysconfdir", "sysconfdir",
"socketdir", "socketdir",
@ -555,6 +639,11 @@ parse_gpgconf_ctl (const char *fname)
ignoreall = 1; /* No, this file shall be ignored. */ ignoreall = 1; /* No, this file shall be ignored. */
xfree (p); xfree (p);
} }
else if (!strcmp (name, "gnupg"))
{
xfree (gnupgval);
gnupgval = p;
}
else if (!strcmp (name, "sysconfdir")) else if (!strcmp (name, "sysconfdir"))
{ {
xfree (sysconfdir); xfree (sysconfdir);
@ -589,6 +678,13 @@ parse_gpgconf_ctl (const char *fname)
if (ignoreall) if (ignoreall)
; /* Forced error. Note that .found is still set. */ ; /* Forced error. Note that .found is still set. */
else if (gnupgval && (!*gnupgval || strpbrk (gnupgval, "/\\")))
{
/* We don't allow a slash or backslash in the value because our
* code assumes this is a single directory name. */
log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
"gnupg", gnupgval);
}
else if (rootdir && (!*rootdir || *rootdir != '/')) else if (rootdir && (!*rootdir || *rootdir != '/'))
{ {
log_info ("invalid %s '%s' specified in gpgconf.ctl\n", log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
@ -606,6 +702,12 @@ parse_gpgconf_ctl (const char *fname)
} }
else else
{ {
if (gnupgval)
{
gpgconf_ctl.gnupg = gnupgval;
gpgrt_annotate_leaked_object (gpgconf_ctl.gnupg);
/* log_info ("want gnupg '%s'\n", dir); */
}
if (rootdir) if (rootdir)
{ {
while (*rootdir && rootdir[strlen (rootdir)-1] == '/') while (*rootdir && rootdir[strlen (rootdir)-1] == '/')
@ -637,9 +739,11 @@ parse_gpgconf_ctl (const char *fname)
if (!gpgconf_ctl.valid) if (!gpgconf_ctl.valid)
{ {
/* Error reading some entries - clear them all. */ /* Error reading some entries - clear them all. */
xfree (gnupgval);
xfree (rootdir); xfree (rootdir);
xfree (sysconfdir); xfree (sysconfdir);
xfree (socketdir); xfree (socketdir);
gpgconf_ctl.gnupg = NULL;
gpgconf_ctl.rootdir = NULL; gpgconf_ctl.rootdir = NULL;
gpgconf_ctl.sysconfdir = NULL; gpgconf_ctl.sysconfdir = NULL;
gpgconf_ctl.socketdir = NULL; gpgconf_ctl.socketdir = NULL;
@ -1031,7 +1135,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
if (w32_portable_app) if (w32_portable_app)
{ {
name = xstrconcat (w32_rootdir (), DIRSEP_S, "gnupg", NULL); name = xstrconcat (w32_rootdir (), DIRSEP_S, my_gnupg_dirname (), NULL);
} }
else else
{ {
@ -1042,7 +1146,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
NULL, 0); NULL, 0);
if (path) if (path)
{ {
name = xstrconcat (path, "\\gnupg", NULL); name = xstrconcat (path, "\\", my_gnupg_dirname (), NULL);
xfree (path); xfree (path);
if (gnupg_access (name, F_OK)) if (gnupg_access (name, F_OK))
gnupg_mkdir (name, "-rwx"); gnupg_mkdir (name, "-rwx");
@ -1150,10 +1254,11 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
}; };
int i; int i;
struct stat sb; struct stat sb;
char prefixbuffer[19 + 1 + 20 + 6 + 1]; char prefixbuffer[256];
const char *prefix; const char *prefix;
const char *s; const char *s;
char *name = NULL; char *name = NULL;
const char *gnupgname = my_gnupg_dirname ();
*r_info = 0; *r_info = 0;
@ -1192,12 +1297,13 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
goto leave; goto leave;
} }
if (strlen (prefix) + 7 >= sizeof prefixbuffer) if (strlen (prefix) + strlen (gnupgname) + 2 >= sizeof prefixbuffer)
{ {
*r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */
goto leave; goto leave;
} }
strcat (prefixbuffer, "/gnupg"); strcat (prefixbuffer, "/");
strcat (prefixbuffer, gnupgname);
} }
/* Check whether the gnupg sub directory (or the specified diretory) /* Check whether the gnupg sub directory (or the specified diretory)
@ -1352,11 +1458,8 @@ gnupg_sysconfdir (void)
if (!name) if (!name)
{ {
const char *s1, *s2; name = xstrconcat (w32_commondir (), DIRSEP_S, "etc", DIRSEP_S,
s1 = w32_commondir (); my_gnupg_dirname (), NULL);
s2 = DIRSEP_S "etc" DIRSEP_S "gnupg";
name = xmalloc (strlen (s1) + strlen (s2) + 1);
strcpy (stpcpy (name, s1), s2);
gpgrt_annotate_leaked_object (name); gpgrt_annotate_leaked_object (name);
} }
return name; return name;

View File

@ -244,6 +244,7 @@ void gnupg_set_homedir (const char *newdir);
void gnupg_maybe_make_homedir (const char *fname, int quiet); void gnupg_maybe_make_homedir (const char *fname, int quiet);
const char *gnupg_homedir (void); const char *gnupg_homedir (void);
int gnupg_default_homedir_p (void); int gnupg_default_homedir_p (void);
const char *gnupg_registry_dir (void);
const char *gnupg_daemon_rootdir (void); const char *gnupg_daemon_rootdir (void);
const char *gnupg_socketdir (void); const char *gnupg_socketdir (void);
const char *gnupg_sysconfdir (void); const char *gnupg_sysconfdir (void);

View File

@ -1159,6 +1159,13 @@ More fields may be added in future to the output.
exist and are writable: @file{ROOT/home} for the GnuPG home and exist and are writable: @file{ROOT/home} for the GnuPG home and
@file{ROOT@value{LOCALCACHEDIR}} for internal cache files. @file{ROOT@value{LOCALCACHEDIR}} for internal cache files.
On both platforms the keyword @code{gnupg} can be used to change the
standard home directory. For example a value of "gnupg-vsd" will
change the default home directory on unix from @file{~/.gnupg} to
@file{~/.gnupg-vsd}. The socket directory is changed accordingly
unless the @code{socketdir} keyword has been used. On Windows the
Registry keys are modified as well.
@item /etc/gnupg/gpgconf.conf @item /etc/gnupg/gpgconf.conf
@cindex gpgconf.conf @cindex gpgconf.conf

View File

@ -315,7 +315,7 @@ list_dirs (estream_t fp, char **names, int special)
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
tmp = read_w32_registry_string (NULL, tmp = read_w32_registry_string (NULL,
GNUPG_REGISTRY_DIR, gnupg_registry_dir (),
"HomeDir"); "HomeDir");
if (tmp) if (tmp)
{ {
@ -324,14 +324,14 @@ list_dirs (estream_t fp, char **names, int special)
xfree (tmp); xfree (tmp);
if ((tmp = read_w32_registry_string ("HKEY_CURRENT_USER", if ((tmp = read_w32_registry_string ("HKEY_CURRENT_USER",
GNUPG_REGISTRY_DIR, gnupg_registry_dir (),
"HomeDir"))) "HomeDir")))
{ {
xfree (tmp); xfree (tmp);
hkcu = 1; hkcu = 1;
} }
if ((tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE", if ((tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
GNUPG_REGISTRY_DIR, gnupg_registry_dir (),
"HomeDir"))) "HomeDir")))
{ {
xfree (tmp); xfree (tmp);
@ -344,15 +344,15 @@ list_dirs (estream_t fp, char **names, int special)
"### Note: homedir taken from registry key %s%s\\%s:%s\n" "### Note: homedir taken from registry key %s%s\\%s:%s\n"
"\n", "\n",
hkcu?"HKCU":"", hklm?"HKLM":"", hkcu?"HKCU":"", hklm?"HKLM":"",
GNUPG_REGISTRY_DIR, "HomeDir"); gnupg_registry_dir (), "HomeDir");
else else
log_info ("Warning: homedir taken from registry key (%s:%s) in%s%s\n", log_info ("Warning: homedir taken from registry key (%s:%s) in%s%s\n",
GNUPG_REGISTRY_DIR, "HomeDir", gnupg_registry_dir (), "HomeDir",
hkcu?" HKCU":"", hkcu?" HKCU":"",
hklm?" HKLM":""); hklm?" HKLM":"");
} }
else if ((tmp = read_w32_registry_string (NULL, else if ((tmp = read_w32_registry_string (NULL,
GNUPG_REGISTRY_DIR, gnupg_registry_dir (),
NULL))) NULL)))
{ {
xfree (tmp); xfree (tmp);
@ -360,10 +360,10 @@ list_dirs (estream_t fp, char **names, int special)
if (special) if (special)
es_fprintf (fp, "\n" es_fprintf (fp, "\n"
"### Note: registry %s without value in HKCU or HKLM\n" "### Note: registry %s without value in HKCU or HKLM\n"
"\n", GNUPG_REGISTRY_DIR); "\n", gnupg_registry_dir ());
else else
log_info ("Warning: registry key (%s) without value in HKCU or HKLM\n", log_info ("Warning: registry key (%s) without value in HKCU or HKLM\n",
GNUPG_REGISTRY_DIR); gnupg_registry_dir ());
} }
#else /*!HAVE_W32_SYSTEM*/ #else /*!HAVE_W32_SYSTEM*/
@ -1456,13 +1456,14 @@ show_other_registry_entries (estream_t outfp)
static struct { static struct {
int group; int group;
const char *name; const char *name;
unsigned int prependregkey:1;
} names[] = } names[] =
{ {
{ 1, "HKLM\\Software\\Gpg4win:Install Directory" }, { 1, "HKLM\\Software\\Gpg4win:Install Directory" },
{ 1, "HKLM\\Software\\Gpg4win:Desktop-Version" }, { 1, "HKLM\\Software\\Gpg4win:Desktop-Version" },
{ 1, "HKLM\\Software\\Gpg4win:VS-Desktop-Version" }, { 1, "HKLM\\Software\\Gpg4win:VS-Desktop-Version" },
{ 1, "\\" GNUPG_REGISTRY_DIR ":HomeDir" }, { 1, ":HomeDir", 1 },
{ 1, "\\" GNUPG_REGISTRY_DIR ":DefaultLogFile" }, { 1, ":DefaultLogFile", 1 },
{ 2, "\\Software\\Microsoft\\Office\\Outlook\\Addins\\GNU.GpgOL" { 2, "\\Software\\Microsoft\\Office\\Outlook\\Addins\\GNU.GpgOL"
":LoadBehavior" }, ":LoadBehavior" },
{ 2, "HKCU\\Software\\Microsoft\\Office\\16.0\\Outlook\\Options\\Mail:" { 2, "HKCU\\Software\\Microsoft\\Office\\16.0\\Outlook\\Options\\Mail:"
@ -1508,6 +1509,13 @@ show_other_registry_entries (estream_t outfp)
names[idx].name, NULL); names[idx].name, NULL);
name = namebuf; name = namebuf;
} }
else if (names[idx].prependregkey)
{
xfree (namebuf);
namebuf = xstrconcat ("\\", gnupg_registry_dir (),
names[idx].name, NULL);
name = namebuf;
}
value = read_w32_reg_string (name, &from_hklm); value = read_w32_reg_string (name, &from_hklm);
if (!value) if (!value)