mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
f618731f7e
* common/init.c (_init_common_subsystems): Test and set the DEP Policy. -- Note that this change will now definitely require Windows XP SP3.
402 lines
11 KiB
C
402 lines
11 KiB
C
/* init.c - Various initializations
|
|
* Copyright (C) 2007 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify
|
|
* it under the terms of either
|
|
*
|
|
* - the GNU Lesser General Public License as published by the Free
|
|
* Software Foundation; either version 3 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* or
|
|
*
|
|
* - the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* or both in parallel, as here.
|
|
*
|
|
* This file is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
# if _WIN32_WINNT < 0x0600
|
|
# define _WIN32_WINNT 0x0600 /* Required for SetProcessDEPPolicy. */
|
|
# endif
|
|
# ifdef HAVE_WINSOCK2_H
|
|
# include <winsock2.h>
|
|
# endif
|
|
# include <windows.h>
|
|
#endif
|
|
#ifdef HAVE_W32CE_SYSTEM
|
|
# include <assuan.h> /* For _assuan_w32ce_finish_pipe. */
|
|
#endif
|
|
|
|
#include <gcrypt.h>
|
|
#include "util.h"
|
|
#include "i18n.h"
|
|
#include "w32help.h"
|
|
|
|
/* This object is used to register memory cleanup functions.
|
|
Technically they are not needed but they can avoid frequent
|
|
questions about un-released memory. Note that we use the system
|
|
malloc and not any wrappers. */
|
|
struct mem_cleanup_item_s;
|
|
typedef struct mem_cleanup_item_s *mem_cleanup_item_t;
|
|
|
|
struct mem_cleanup_item_s
|
|
{
|
|
mem_cleanup_item_t next;
|
|
void (*func) (void);
|
|
};
|
|
|
|
static mem_cleanup_item_t mem_cleanup_list;
|
|
|
|
|
|
/* 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.
|
|
Note that we need to initialize it because otherwise some linkers
|
|
(OS X at least) won't find the symbol when linking the t-*.c
|
|
files. */
|
|
gpg_err_source_t default_errsource = 0;
|
|
|
|
|
|
#ifdef HAVE_W32CE_SYSTEM
|
|
static void parse_std_file_handles (int *argcp, char ***argvp);
|
|
static void
|
|
sleep_on_exit (void)
|
|
{
|
|
/* The sshd on CE swallows some of the command output. Sleeping a
|
|
while usually helps. */
|
|
Sleep (400);
|
|
}
|
|
#endif /*HAVE_W32CE_SYSTEM*/
|
|
|
|
#if HAVE_W32_SYSTEM
|
|
static void prepare_w32_commandline (int *argcp, char ***argvp);
|
|
#endif /*HAVE_W32_SYSTEM*/
|
|
|
|
|
|
|
|
static void
|
|
run_mem_cleanup (void)
|
|
{
|
|
mem_cleanup_item_t next;
|
|
|
|
while (mem_cleanup_list)
|
|
{
|
|
next = mem_cleanup_list->next;
|
|
mem_cleanup_list->func ();
|
|
free (mem_cleanup_list);
|
|
mem_cleanup_list = next;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
register_mem_cleanup_func (void (*func)(void))
|
|
{
|
|
mem_cleanup_item_t item;
|
|
|
|
for (item = mem_cleanup_list; item; item = item->next)
|
|
if (item->func == func)
|
|
return; /* Function has already been registered. */
|
|
|
|
item = malloc (sizeof *item);
|
|
if (item)
|
|
{
|
|
item->func = func;
|
|
item->next = mem_cleanup_list;
|
|
mem_cleanup_list = item;
|
|
}
|
|
}
|
|
|
|
|
|
/* If STRING is not NULL write string to es_stdout or es_stderr. MODE
|
|
must be 1 or 2. If STRING is NULL flush the respective stream. */
|
|
static int
|
|
writestring_via_estream (int mode, const char *string)
|
|
{
|
|
if (mode == 1 || mode == 2)
|
|
{
|
|
if (string)
|
|
return es_fputs (string, mode == 1? es_stdout : es_stderr);
|
|
else
|
|
return es_fflush (mode == 1? es_stdout : es_stderr);
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* This function should be the first called after main. */
|
|
void
|
|
early_system_init (void)
|
|
{
|
|
}
|
|
|
|
|
|
/* This function is to be used early at program startup to make sure
|
|
that some subsystems are initialized. This is in particular
|
|
important for W32 to initialize the sockets so that our socket
|
|
emulation code used directly as well as in libassuan may be used.
|
|
It should best be called before any I/O is done so that setup
|
|
required for logging is ready. ARGCP and ARGVP are the addresses
|
|
of the parameters given to main. This function may modify them.
|
|
|
|
This function should be called only via the macro
|
|
init_common_subsystems.
|
|
|
|
CAUTION: This might be called while running suid(root). */
|
|
void
|
|
_init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp)
|
|
{
|
|
/* Store the error source in a global variable. */
|
|
default_errsource = errsource;
|
|
|
|
atexit (run_mem_cleanup);
|
|
|
|
/* Try to auto set the character set. */
|
|
set_native_charset (NULL);
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
/* For W32 we need to initialize the socket layer. This is because
|
|
we use recv and send in libassuan as well as at some other
|
|
places. */
|
|
{
|
|
WSADATA wsadat;
|
|
|
|
WSAStartup (0x202, &wsadat);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_W32CE_SYSTEM
|
|
/* Register the sleep exit function before the estream init so that
|
|
the sleep will be called after the estream registered atexit
|
|
function which flushes the left open estream streams and in
|
|
particular es_stdout. */
|
|
atexit (sleep_on_exit);
|
|
#endif
|
|
|
|
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
|
|
{
|
|
log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
|
|
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL));
|
|
}
|
|
|
|
/* Initialize the Estream library. */
|
|
gpgrt_init ();
|
|
gpgrt_set_alloc_func (gcry_realloc);
|
|
|
|
#ifdef HAVE_W32CE_SYSTEM
|
|
/* Special hack for Windows CE: We extract some options from arg
|
|
to setup the standard handles. */
|
|
parse_std_file_handles (argcp, argvp);
|
|
#endif
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
/* We want gettext to always output UTF-8 and we put the console in
|
|
* utf-8 mode. */
|
|
gettext_use_utf8 (1);
|
|
if (!SetConsoleCP (CP_UTF8) || !SetConsoleOutputCP (CP_UTF8))
|
|
{
|
|
/* Don't show the error if the program does not have a console.
|
|
* This is for example the case for daemons. */
|
|
int rc = GetLastError ();
|
|
if (rc != ERROR_INVALID_HANDLE)
|
|
{
|
|
log_info ("SetConsoleCP failed: %s\n", w32_strerror (rc));
|
|
log_info ("Warning: Garbled console data possible\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Access the standard estreams as early as possible. If we don't
|
|
do this the original stdio streams may have been closed when
|
|
_es_get_std_stream is first use and in turn it would connect to
|
|
the bit bucket. */
|
|
{
|
|
int i;
|
|
for (i=0; i < 3; i++)
|
|
(void)_gpgrt_get_std_stream (i);
|
|
}
|
|
|
|
/* --version et al shall use estream as well. */
|
|
gnupg_set_usage_outfnc (writestring_via_estream);
|
|
|
|
/* Register our string mapper with gpgrt. */
|
|
gnupg_set_fixed_string_mapper (map_static_macro_string);
|
|
|
|
/* Logging shall use the standard socket directory as fallback. */
|
|
log_set_socket_dir_cb (gnupg_socketdir);
|
|
|
|
#if HAVE_W32_SYSTEM
|
|
/* Make sure that Data Execution Prevention is enabled. */
|
|
if (GetSystemDEPPolicy () >= 2)
|
|
{
|
|
DWORD flags;
|
|
BOOL perm;
|
|
|
|
if (!GetProcessDEPPolicy (GetCurrentProcess (), &flags, &perm))
|
|
log_info ("error getting DEP policy: %s\n",
|
|
w32_strerror (GetLastError()));
|
|
else if (!(flags & PROCESS_DEP_ENABLE)
|
|
&& !SetProcessDEPPolicy (PROCESS_DEP_ENABLE))
|
|
log_info ("Warning: Enabling DEP failed: %s (%d,%d)\n",
|
|
w32_strerror (GetLastError ()), (int)flags, (int)perm);
|
|
}
|
|
/* On Windows we use our own parser for the command line
|
|
* so that we can return an array of utf-8 encoded strings. */
|
|
prepare_w32_commandline (argcp, argvp);
|
|
#else
|
|
(void)argcp;
|
|
(void)argvp;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* WindowsCE uses a very strange way of handling the standard streams.
|
|
There is a function SetStdioPath to associate a standard stream
|
|
with a file or a device but what we really want is to use pipes as
|
|
standard streams. Despite that we implement pipes using a device,
|
|
we would have some limitations on the number of open pipes due to
|
|
the 3 character limit of device file name. Thus we don't take this
|
|
path. Another option would be to install a file system driver with
|
|
support for pipes; this would allow us to get rid of the device
|
|
name length limitation. However, with GnuPG we can get away be
|
|
redefining the standard streams and passing the handles to be used
|
|
on the command line. This has also the advantage that it makes
|
|
creating a process much easier and does not require the
|
|
SetStdioPath set and restore game. The caller needs to pass the
|
|
rendezvous ids using up to three options:
|
|
|
|
-&S0=<rvid> -&S1=<rvid> -&S2=<rvid>
|
|
|
|
They are all optional but they must be the first arguments on the
|
|
command line. Parsing stops as soon as an invalid option is found.
|
|
These rendezvous ids are then used to finish the pipe creation.*/
|
|
#ifdef HAVE_W32CE_SYSTEM
|
|
static void
|
|
parse_std_file_handles (int *argcp, char ***argvp)
|
|
{
|
|
int argc = *argcp;
|
|
char **argv = *argvp;
|
|
const char *s;
|
|
assuan_fd_t fd;
|
|
int i;
|
|
int fixup = 0;
|
|
|
|
if (!argc)
|
|
return;
|
|
|
|
for (argc--, argv++; argc; argc--, argv++)
|
|
{
|
|
s = *argv;
|
|
if (*s == '-' && s[1] == '&' && s[2] == 'S'
|
|
&& (s[3] == '0' || s[3] == '1' || s[3] == '2')
|
|
&& s[4] == '='
|
|
&& (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null")))
|
|
{
|
|
if (s[5] == 'n')
|
|
fd = ASSUAN_INVALID_FD;
|
|
else
|
|
fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0');
|
|
_es_set_std_fd (s[3] - '0', (int)fd);
|
|
fixup++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (fixup)
|
|
{
|
|
argc = *argcp;
|
|
argc -= fixup;
|
|
*argcp = argc;
|
|
|
|
argv = *argvp;
|
|
for (i=1; i < argc; i++)
|
|
argv[i] = argv[i + fixup];
|
|
for (; i < argc + fixup; i++)
|
|
argv[i] = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
#endif /*HAVE_W32CE_SYSTEM*/
|
|
|
|
|
|
/* For Windows we need to parse the command line so that we can
|
|
* provide an UTF-8 encoded argv. If there is any Unicode character
|
|
* we return a new array but if there is no Unicode character we do
|
|
* nothing. */
|
|
#ifdef HAVE_W32_SYSTEM
|
|
static void
|
|
prepare_w32_commandline (int *r_argc, char ***r_argv)
|
|
{
|
|
const wchar_t *wcmdline, *ws;
|
|
char *cmdline;
|
|
int argc;
|
|
char **argv;
|
|
const char *s;
|
|
int i, globing, itemsalloced;
|
|
|
|
s = strusage (95);
|
|
globing = (s && *s == '1');
|
|
|
|
wcmdline = GetCommandLineW ();
|
|
if (!wcmdline)
|
|
{
|
|
log_error ("GetCommandLineW failed\n");
|
|
return; /* Ooops. */
|
|
}
|
|
|
|
if (!globing)
|
|
{
|
|
/* If globbing is not enabled we use our own parser only if
|
|
* there are any non-ASCII characters. */
|
|
for (ws=wcmdline; *ws; ws++)
|
|
if (!iswascii (*ws))
|
|
break;
|
|
if (!*ws)
|
|
return; /* No Unicode - return directly. */
|
|
}
|
|
|
|
cmdline = wchar_to_utf8 (wcmdline);
|
|
if (!cmdline)
|
|
{
|
|
log_error ("parsing command line failed: %s\n", strerror (errno));
|
|
return; /* Ooops. */
|
|
}
|
|
gpgrt_annotate_leaked_object (cmdline);
|
|
|
|
argv = w32_parse_commandline (cmdline, globing, &argc, &itemsalloced);
|
|
if (!argv)
|
|
{
|
|
log_error ("parsing command line failed: %s\n", "internal error");
|
|
return; /* Ooops. */
|
|
}
|
|
gpgrt_annotate_leaked_object (argv);
|
|
if (itemsalloced)
|
|
{
|
|
for (i=0; i < argc; i++)
|
|
gpgrt_annotate_leaked_object (argv[i]);
|
|
}
|
|
*r_argv = argv;
|
|
*r_argc = argc;
|
|
}
|
|
#endif /*HAVE_W32_SYSTEM*/
|