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. # wrapper process.
libexec_PROGRAMS = gpgme-w32spawn libexec_PROGRAMS = gpgme-w32spawn
gpgme_w32spawn_CFLAGS = -municode
RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)

View File

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