From 9119d13db870473f40d7678a980009e12a0d2379 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 2 Dec 2014 10:16:03 +0000 Subject: [PATCH] * autoload.cc (CreateEnvironmentBlock): Import. (DestroyEnvironmentBlock): Import. * environ.cc (env_compare): New static bsearch comparison function. (build_env): Add parameter taking a user token. If token is non-NULL, fetch user's default Windows environment and merge it into the resulting environment. Explain what we do in preceeding comment. * environ,h (build_env): Align prototype to above change. * external.cc (create_winenv): Call build_env with NULL token. * spawn.cc (child_info_spawn::worker): When spawning new process under another user account, call build_env with new token to allow merging user's default Windows environment. * winlean.h (_USERENV_): Define to override dllimport. --- winsup/cygwin/ChangeLog | 15 +++++ winsup/cygwin/autoload.cc | 3 + winsup/cygwin/environ.cc | 113 ++++++++++++++++++++++++++++++++++++-- winsup/cygwin/environ.h | 4 +- winsup/cygwin/external.cc | 3 +- winsup/cygwin/spawn.cc | 8 ++- winsup/cygwin/winlean.h | 1 + 7 files changed, 137 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 7251509cd..8e7bebad4 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,18 @@ +2014-12-02 Corinna Vinschen + + * autoload.cc (CreateEnvironmentBlock): Import. + (DestroyEnvironmentBlock): Import. + * environ.cc (env_compare): New static bsearch comparison function. + (build_env): Add parameter taking a user token. If token is non-NULL, + fetch user's default Windows environment and merge it into the resulting + environment. Explain what we do in preceeding comment. + * environ,h (build_env): Align prototype to above change. + * external.cc (create_winenv): Call build_env with NULL token. + * spawn.cc (child_info_spawn::worker): When spawning new process under + another user account, call build_env with new token to allow merging + user's default Windows environment. + * winlean.h (_USERENV_): Define to override dllimport. + 2014-12-02 Corinna Vinschen * spawn.cc (child_info_spawn::worker): Fix formatting. diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 6340e8080..7859725ff 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -675,6 +675,9 @@ LoadDLLfunc (SetParent, 8, user32) LoadDLLfunc (SetProcessWindowStation, 4, user32) LoadDLLfunc (SetThreadDesktop, 4, user32) +LoadDLLfunc (CreateEnvironmentBlock, 12, userenv) +LoadDLLfunc (DestroyEnvironmentBlock, 4, userenv) + LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 1, 0, 1) LoadDLLfuncEx3 (waveInClose, 4, winmm, 1, 0, 1) LoadDLLfuncEx3 (waveInGetNumDevs, 0, winmm, 1, 0, 1) diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 9b9be4a00..e33256fe3 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -9,12 +9,14 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" +#include #include #include #include #include #include #include +#include #include #include "pinfo.h" #include "perprocess.h" @@ -1047,14 +1049,39 @@ raise_envblock (int new_tl, PWCHAR &envblock, PWCHAR &s) #define SPENVS_SIZE (sizeof (spenvs) / sizeof (spenvs[0])) +int +env_compare (const void *key, const void *memb) +{ + const char *k = *(const char **) key; + const char *m = *(const char **) memb; + + char *ke = strchr (k, '='); + char *me = strchr (m, '='); + if (ke == NULL || me == NULL) + return strcasecmp (k, m); + int ret = strncasecmp (k, m, MIN (ke - k, me - m)); + if (!ret) + ret = (ke - k) - (me - m); + return ret; +} + /* Create a Windows-style environment block, i.e. a typical character buffer filled with null terminated strings, terminated by double null characters. Converts environment variables noted in conv_envvars into win32 form - prior to placing them in the string. */ + prior to placing them in the string. + + If new_token is set, we're going to switch the user account in + child_info_spawn::worker. If so, we're also fetching the Windows default + environment for the new user, and merge it into the environment we propage + to the child. */ char ** __reg3 build_env (const char * const *envp, PWCHAR &envblock, int &envc, - bool no_envblock) + bool no_envblock, HANDLE new_token) { + PWCHAR cwinenv = NULL; + size_t winnum = 0; + char **winenv = NULL; + int len, n; const char * const *srcp; char **dstp; @@ -1066,13 +1093,57 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, for (n = 0; envp[n]; n++) continue; + /* Fetch windows env and convert to POSIX-style env. */ + if (new_token + && CreateEnvironmentBlock ((LPVOID *) &cwinenv, new_token, FALSE)) + { + PWCHAR var = cwinenv; + while (*var) + { + ++winnum; + var = wcschr (var, L'\0') + 1; + } + winenv = (char **) calloc (winnum + 1, sizeof (char *)); + if (winenv) + { + for (winnum = 0, var = cwinenv; + *var; + ++winnum, var = wcschr (var, L'\0') + 1) + sys_wcstombs_alloc (&winenv[winnum], HEAP_NOTHEAP, var); + } + DestroyEnvironmentBlock (cwinenv); + /* Eliminate variables which are already available in envp. The windows + env is sorted, so we can use bsearch. We're doing this first step, + so the following code doesn't allocate too much memory. */ + if (winenv) + { + for (srcp = envp; *srcp; srcp++) + { + char **elem = (char **) bsearch (srcp, winenv, winnum, + sizeof *winenv, env_compare); + if (elem) + { + system_printf ("remove: %s", *elem); + free (*elem); + /* Use memmove to keep array sorted. + winnum - (elem - winenv) copies all elements following + elem, including the trailing NULL pointer. */ + memmove (elem, elem + 1, + (winnum - (elem - winenv)) * sizeof *elem); + --winnum; + } + } + } + } + /* Allocate a new "argv-style" environ list with room for extra stuff. */ char **newenv = (char **) cmalloc_abort (HEAP_1_ARGV, sizeof (char *) * - (n + SPENVS_SIZE + 1)); + (n + winnum + SPENVS_SIZE + 1)); int tl = 0; char **pass_dstp; - char **pass_env = (char **) alloca (sizeof (char *) * (n + SPENVS_SIZE + 1)); + char **pass_env = (char **) alloca (sizeof (char *) + * (n + winnum + SPENVS_SIZE + 1)); /* Iterate over input list, generating a new environment list and refreshing "special" entries, if necessary. */ for (srcp = envp, dstp = newenv, pass_dstp = pass_env; *srcp; srcp++) @@ -1107,19 +1178,49 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, assert ((srcp - envp) == n); /* Fill in any required-but-missing environment variables. */ for (unsigned i = 0; i < SPENVS_SIZE; i++) - if (!saw_spenv[i] && (spenvs[i].force_into_environment || cygheap->user.issetuid ())) + if (!saw_spenv[i] && (spenvs[i].force_into_environment + || cygheap->user.issetuid ())) { *dstp = spenvs[i].retrieve (false); if (*dstp && *dstp != env_dontadd) { *pass_dstp++ = *dstp; tl += strlen (*dstp) + 1; + /* Eliminate from winenv. */ + if (winenv) + { + char **elem = (char **) bsearch (dstp, winenv, winnum, + sizeof *winenv, env_compare); + if (elem) + { + system_printf ("remove: %s", *elem); + free (*elem); + memmove (elem, elem + 1, + (winnum - (elem - winenv)) * sizeof *elem); + --winnum; + } + } dstp++; } } + /* Fill in any Windows environment vars still missing. */ + if (winenv) + { + char **elem; + for (elem = winenv; *elem; ++elem) + { + *dstp = cstrdup1 (*elem); + free (*elem); + *pass_dstp++ = *dstp; + tl += strlen (*dstp) + 1; + ++dstp; + } + free (winenv); + } + envc = dstp - newenv; /* Number of entries in newenv */ - assert ((size_t) envc <= (n + SPENVS_SIZE)); + assert ((size_t) envc <= (n + winnum + SPENVS_SIZE)); *dstp = NULL; /* Terminate */ size_t pass_envc = pass_dstp - pass_env; diff --git a/winsup/cygwin/environ.h b/winsup/cygwin/environ.h index b2d65c8bd..5048fcd2f 100644 --- a/winsup/cygwin/environ.h +++ b/winsup/cygwin/environ.h @@ -1,6 +1,6 @@ /* environ.h: Declarations for environ manipulation - Copyright 2000, 2001, 2002, 2003, 2005, 2006, 2008, 2013 Red Hat, Inc. + Copyright 2000, 2001, 2002, 2003, 2005, 2006, 2008, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -45,6 +45,6 @@ extern "C" char **__cygwin_environ, ***main_environ; extern "C" char __stdcall **cur_environ (); #endif char ** __reg3 build_env (const char * const *envp, PWCHAR &envblock, - int &envc, bool need_envblock); + int &envc, bool need_envblock, HANDLE new_token); #define ENV_CVT -1 diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index b42c284e1..5fac4bb66 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -138,7 +138,8 @@ create_winenv (const char * const *env) { int unused_envc; PWCHAR envblock = NULL; - char **envp = build_env (env ?: cur_environ (), envblock, unused_envc, false); + char **envp = build_env (env ?: cur_environ (), envblock, unused_envc, false, + NULL); PWCHAR p = envblock; if (envp) diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index e29ba6a8c..a0686c5d8 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -531,8 +531,14 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (!real_path.iscygexec()) ::cygheap->fdtab.set_file_pointers_for_exec (); + /* If we switch the user, merge the user's Windows environment. */ + bool switch_user = ::cygheap->user.issetuid () + && (::cygheap->user.saved_uid + != ::cygheap->user.real_uid); moreinfo->envp = build_env (envp, envblock, moreinfo->envc, - real_path.iscygexec ()); + real_path.iscygexec (), + switch_user ? ::cygheap->user.primary_token () + : NULL); if (!moreinfo->envp || !envblock) { set_errno (E2BIG); diff --git a/winsup/cygwin/winlean.h b/winsup/cygwin/winlean.h index 63cebdb96..329199a28 100644 --- a/winsup/cygwin/winlean.h +++ b/winsup/cygwin/winlean.h @@ -28,6 +28,7 @@ details. */ #define _SHELL32_ #define _SPOOL32_ #define _USER32_ +#define _USERENV_ #define _WINMM_ #define WINIMPM #define WINSOCK_API_LINKAGE