Change gpgme-w32-spawn to unicode

* src/Makefile.am (gpgme_w32spawn_CFLAGS): Add -municode.
* src/gpgme-w32-spawn.c (build_commandline, my_spawn)
(translate_handles): Convert to wchar_t API.
(main): Use wmain instead.

--
Some time ago we introduced an inconsistency that w32-util called
gpgme-w32-spawn through CreateProcessW but since gpgme-w32-spawn
internally worked with 8 bit the chars were mangled and the
arguments not passed correctly through the CreateProcessA of the
child process. Since the GnuPG processes use GetCommandLineW
this is the proper way to pass on Unicode command line arguments.

Please note that we did not pass UTF-8 before this patch but
rather some broken native encoding where Windows replaces
unicode characters with question marks etc.

GnuPG-Bug-Id: T6728
This commit is contained in:
Andre Heinecke 2023-10-25 17:20:56 +02:00
parent 8faaf7b72b
commit a0a4cd411c
No known key found for this signature in database
GPG Key ID: 2978E9D40CBABA5C
2 changed files with 50 additions and 42 deletions

View File

@ -120,6 +120,8 @@ if HAVE_W32_SYSTEM
# wrapper process.
libexec_PROGRAMS = gpgme-w32spawn
gpgme_w32spawn_CFLAGS = -municode
RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)

View File

@ -54,13 +54,13 @@ static FILE *mystderr;
static char *
build_commandline (char **argv)
static wchar_t *
build_commandline (wchar_t **argv)
{
int i;
int n = 0;
char *buf;
char *p;
wchar_t *buf;
wchar_t *p;
/* We have to quote some things because under Windows the program
parses the commandline and does some unquoting. We enclose the
@ -75,7 +75,7 @@ build_commandline (char **argv)
while (*p)
{
/* An extra one for each literal that must be escaped. */
if (*p == '\\' || *p == '"')
if (*p == L'\\' || *p == L'"')
n++;
n++;
p++;
@ -86,22 +86,22 @@ build_commandline (char **argv)
/* And a trailing zero. */
n++;
buf = p = malloc (n);
buf = p = malloc (n * sizeof (wchar_t));
if (!buf)
return NULL;
for (i = 0; argv[i]; i++)
{
char *argvp = argv[i];
wchar_t *argvp = argv[i];
*(p++) = '"';
*(p++) = L'"';
while (*argvp)
{
if (*argvp == '\\' || *argvp == '"')
*(p++) = '\\';
if (*argvp == L'\\' || *argvp == L'"')
*(p++) = L'\\';
*(p++) = *(argvp++);
}
*(p++) = '"';
*(p++) = ' ';
*(p++) = L'"';
*(p++) = L' ';
}
*(p++) = 0;
@ -110,7 +110,7 @@ build_commandline (char **argv)
int
my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
my_spawn (wchar_t **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
@ -120,12 +120,11 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
0, /* returns pid */
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
STARTUPINFOW si;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char *arg_string;
wchar_t *arg_string;
int duped_stdin = 0;
int duped_stdout = 0;
int duped_stderr = 0;
@ -134,7 +133,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
i = 0;
while (argv[i])
{
fprintf (mystderr, PGM": argv[%2i] = %s\n", i, argv[i]);
fprintf (mystderr, PGM": argv[%2i] = %S\n", i, argv[i]);
i++;
}
@ -184,7 +183,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
memset (&sa, 0, sizeof sa);
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ("nul",
hnul = CreateFileW (L"nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
@ -210,13 +209,13 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
}
cr_flags |= CREATE_SUSPENDED;
if (!CreateProcessA (argv[0],
if (!CreateProcessW (argv[0],
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi)) /* returns process information */
@ -247,7 +246,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
{
/* Available since W2000; thus we dynload it. */
initialized = 1;
handle = LoadLibrary ("user32.dll");
handle = LoadLibraryA ("user32.dll");
if (handle)
{
func = GetProcAddress (handle, "AllowSetForegroundWindow");
@ -275,7 +274,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
#define MAX_TRANS 10
int
translate_get_from_file (const char *trans_file,
translate_get_from_file (const wchar_t *trans_file,
struct spawn_fd_item_s *fd_list,
unsigned int *r_flags)
{
@ -292,7 +291,7 @@ translate_get_from_file (const char *trans_file,
*r_flags = 0;
fd = open (trans_file, O_RDONLY);
fd = _wopen (trans_file, O_RDONLY);
if (fd < 0)
return -1;
@ -389,14 +388,14 @@ translate_get_from_file (const char *trans_file,
/* Read the translated handles from TRANS_FILE and do a substitution
in ARGV. Returns the new argv and the list of substitutions in
FD_LIST (which must be MAX_TRANS+1 large). */
char **
translate_handles (const char *trans_file, const char * const *argv,
wchar_t **
translate_handles (const wchar_t *trans_file, const wchar_t * const *argv,
struct spawn_fd_item_s *fd_list, unsigned int *r_flags)
{
int res;
int idx;
int n_args;
char **args;
wchar_t **args;
res = translate_get_from_file (trans_file, fd_list, r_flags);
if (res < 0)
@ -407,7 +406,7 @@ translate_handles (const char *trans_file, const char * const *argv,
args = malloc (sizeof (*args) * (idx + 1));
for (idx = 0; argv[idx]; idx++)
{
args[idx] = strdup (argv[idx]);
args[idx] = wcsdup (argv[idx]);
if (!args[idx])
return NULL;
}
@ -416,7 +415,7 @@ translate_handles (const char *trans_file, const char * const *argv,
for (idx = 0; fd_list[idx].fd != -1; idx++)
{
char buf[25];
wchar_t buf[25];
int aidx;
aidx = fd_list[idx].arg_loc;
@ -439,20 +438,27 @@ translate_handles (const char *trans_file, const char * const *argv,
/* NOTE: Here is the part where application specific knowledge
comes in. GPGME/GnuPG uses two forms of descriptor
specification, a plain number and a "-&" form. */
if (argv[aidx][0] == '-' && argv[aidx][1] == '&')
snprintf (args[aidx], sizeof (buf), "-&%d", fd_list[idx].peer_name);
if (argv[aidx][0] == L'-' && argv[aidx][1] == L'&')
snwprintf (args[aidx], sizeof (buf), L"-&%d", fd_list[idx].peer_name);
else
snprintf (args[aidx], sizeof (buf), "%d", fd_list[idx].peer_name);
snwprintf (args[aidx], sizeof (buf), L"%d", fd_list[idx].peer_name);
}
return args;
}
/* Since GPGME might be installed in a unicode directory it
must be callable with CreateProcessW which provides the
arguments in Unicode form.
So GPGME converts from its internal UTF-8 representation
to wchar, spawns gpgme-w32-spawn with CreateProcessW and then
we also forward this as wchar. */
int
main (int argc, const char * const *argv)
wmain (int argc, const wchar_t * const *argv)
{
int rc = 0;
char **argv_spawn;
wchar_t **argv_spawn;
struct spawn_fd_item_s fd_list[MAX_TRANS + 1];
unsigned int flags;
@ -479,7 +485,7 @@ main (int argc, const char * const *argv)
rc = my_spawn (argv_spawn, fd_list, flags);
if (rc < 0)
{
fprintf (mystderr, PGM": executing `%s' failed: %s\n",
fprintf (mystderr, PGM": executing `%S' failed: %s\n",
argv[0], strerror (errno));
rc = 2;
goto leave;
@ -491,8 +497,8 @@ main (int argc, const char * const *argv)
/* Always try to delete the temporary file. */
if (argc >= 2)
{
if (DeleteFile (argv[1]) == 0)
fprintf (mystderr, PGM": failed to delete %s: ec=%ld\n",
if (DeleteFileW (argv[1]) == 0)
fprintf (mystderr, PGM": failed to delete %S: ec=%ld\n",
argv[1], GetLastError ());
}
return rc;