From 41f77e25f11d2b152d2f4da12d1e6378c64fc78e Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 2 Dec 2014 10:49:47 +0000 Subject: [PATCH] * autoload.cc (CreateProfile): Import. (LoadUserProfileW): Import. * registry.cc (get_registry_hive_path): Move to sec_auth.cc. (load_registry_hive): Remove. * registry.h (get_registry_hive_path): Drop declaration. (load_registry_hive): Ditto. * sec_auth.cc (get_user_profile_directory): Moved from registry.cc and renamed. Take third parameter with buffer length. (load_user_profile): New function taking over for load_registry_hive. Use official functions to load profile. If profile is missing, create it on Vista and later. * security.h (get_user_profile_directory): Declare. (load_user_profile): Declare. * syscalls.cc (seteuid32): Replace call to load_registry_hive with call to load_user_profile. * uinfo.cc (cygheap_user::env_userprofile): Replace call to get_registry_hive_path with call to get_user_profile_directory. --- winsup/cygwin/ChangeLog | 20 +++++++ winsup/cygwin/autoload.cc | 2 + winsup/cygwin/registry.cc | 96 +-------------------------------- winsup/cygwin/registry.h | 6 +-- winsup/cygwin/sec_auth.cc | 110 ++++++++++++++++++++++++++++++++++++++ winsup/cygwin/security.h | 6 +++ winsup/cygwin/syscalls.cc | 6 +-- winsup/cygwin/uinfo.cc | 8 +-- 8 files changed, 145 insertions(+), 109 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 960d1ce03..7d4e03e2a 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,23 @@ +2014-12-02 Corinna Vinschen + + * autoload.cc (CreateProfile): Import. + (LoadUserProfileW): Import. + * registry.cc (get_registry_hive_path): Move to sec_auth.cc. + (load_registry_hive): Remove. + * registry.h (get_registry_hive_path): Drop declaration. + (load_registry_hive): Ditto. + * sec_auth.cc (get_user_profile_directory): Moved from registry.cc and + renamed. Take third parameter with buffer length. + (load_user_profile): New function taking over for load_registry_hive. + Use official functions to load profile. If profile is missing, create + it on Vista and later. + * security.h (get_user_profile_directory): Declare. + (load_user_profile): Declare. + * syscalls.cc (seteuid32): Replace call to load_registry_hive with call + to load_user_profile. + * uinfo.cc (cygheap_user::env_userprofile): Replace call to + get_registry_hive_path with call to get_user_profile_directory. + 2014-12-02 Corinna Vinschen * uinfo.cc (fetch_from_description): Make static. diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 7859725ff..ce5d32834 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -676,7 +676,9 @@ LoadDLLfunc (SetProcessWindowStation, 4, user32) LoadDLLfunc (SetThreadDesktop, 4, user32) LoadDLLfunc (CreateEnvironmentBlock, 12, userenv) +LoadDLLfuncEx2 (CreateProfile, 16, userenv, 1, 1) LoadDLLfunc (DestroyEnvironmentBlock, 4, userenv) +LoadDLLfunc (LoadUserProfileW, 8, userenv) LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 1, 0, 1) LoadDLLfuncEx3 (waveInClose, 4, winmm, 1, 0, 1) diff --git a/winsup/cygwin/registry.cc b/winsup/cygwin/registry.cc index 632260aae..99b1d9b2d 100644 --- a/winsup/cygwin/registry.cc +++ b/winsup/cygwin/registry.cc @@ -1,7 +1,7 @@ /* registry.cc: registry interface Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -19,7 +19,6 @@ details. */ #include "tls_pbuf.h" #include "ntdll.h" #include -#include /* Opens a key under the appropriate Cygwin key. Do not use HKCU per MS KB 199190 */ @@ -213,96 +212,3 @@ reg_key::~reg_key () NtClose (key); key_is_invalid = 1; } - -/* The buffer path points to should be at least MAX_PATH bytes. */ -PWCHAR -get_registry_hive_path (PCWSTR name, PWCHAR path) -{ - if (!name || !path) - return NULL; - - WCHAR key[256]; - UNICODE_STRING buf; - tmp_pathbuf tp; - tp.u_get (&buf); - NTSTATUS status; - - RTL_QUERY_REGISTRY_TABLE tab[2] = { - { NULL, RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_DIRECT - | RTL_QUERY_REGISTRY_REQUIRED, - L"ProfileImagePath", &buf, REG_NONE, NULL, 0 }, - { NULL, 0, NULL, NULL, 0, NULL, 0 } - }; - wcpcpy (wcpcpy (key, L"ProfileList\\"), name); - status = RtlQueryRegistryValues (RTL_REGISTRY_WINDOWS_NT, key, tab, - NULL, NULL); - if (!NT_SUCCESS (status) || buf.Length == 0) - { - debug_printf ("ProfileImagePath for %W not found, status %y", name, - status); - return NULL; - } - ExpandEnvironmentStringsW (buf.Buffer, path, MAX_PATH); - debug_printf ("ProfileImagePath for %W: %W", name, path); - return path; -} - -void -load_registry_hive (PCWSTR name) -{ - if (!name) - return; - - /* Fetch the path. Prepend native NT path prefix. */ - tmp_pathbuf tp; - PWCHAR path = tp.w_get (); - if (!get_registry_hive_path (name, wcpcpy (path, L"\\??\\"))) - return; - - WCHAR key[256]; - PWCHAR path_comp; - UNICODE_STRING ukey, upath; - OBJECT_ATTRIBUTES key_attr, path_attr; - NTSTATUS status; - - /* Create keyname and path strings and object attributes. */ - wcpcpy (wcpcpy (key, L"\\Registry\\User\\"), name); - RtlInitUnicodeString (&ukey, key); - InitializeObjectAttributes (&key_attr, &ukey, OBJ_CASE_INSENSITIVE, - NULL, NULL); - /* First try to load the "normal" registry hive, which is what the user - is supposed to see under HKEY_CURRENT_USER. */ - path_comp = wcschr (path, L'\0'); - wcpcpy (path_comp, L"\\ntuser.dat"); - RtlInitUnicodeString (&upath, path); - InitializeObjectAttributes (&path_attr, &upath, OBJ_CASE_INSENSITIVE, - NULL, NULL); - status = NtLoadKey (&key_attr, &path_attr); - if (!NT_SUCCESS (status)) - { - debug_printf ("Loading user registry hive %S into %S failed: %y", - &upath, &ukey, status); - return; - } - debug_printf ("Loading user registry hive %S into %S SUCCEEDED: %y", - &upath, &ukey, status); - /* If loading the normal hive worked, try to load the classes hive into - the sibling *_Classes subkey, which is what the user is supposed to - see under HKEY_CLASSES_ROOT, merged with the machine-wide classes. */ - wcscat (key, L"_Classes"); - RtlInitUnicodeString (&ukey, key); - /* Path to UsrClass.dat changed in Vista to - \\AppData\\Local\\Microsoft\\Windows\\UsrClass.dat - but old path is still available via symlinks. */ - wcpcpy (path_comp, L"\\Local Settings\\Application Data\\Microsoft\\" - "Windows\\UsrClass.dat"); - RtlInitUnicodeString (&upath, path); - /* Load UsrClass.dat file into key. */ - status = NtLoadKey (&key_attr, &path_attr); - if (!NT_SUCCESS (status)) - debug_printf ("Loading user classes hive %S into %S failed: %y", - &upath, &ukey, status); - else - debug_printf ("Loading user classes hive %S into %S SUCCEEDED: %y", - &upath, &ukey, status); -} diff --git a/winsup/cygwin/registry.h b/winsup/cygwin/registry.h index 50d6345ab..6a885db4d 100644 --- a/winsup/cygwin/registry.h +++ b/winsup/cygwin/registry.h @@ -1,7 +1,7 @@ /* registry.h: shared info for cygwin Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011 Red Hat, Inc. + 2011, 2012, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -37,7 +37,3 @@ public: ~reg_key (); }; - -/* Evaluates path to the directory of the local user registry hive */ -PWCHAR __stdcall get_registry_hive_path (PCWSTR name, PWCHAR path); -void __stdcall load_registry_hive (PCWSTR name); diff --git a/winsup/cygwin/sec_auth.cc b/winsup/cygwin/sec_auth.cc index b616c45e2..2906b715d 100644 --- a/winsup/cygwin/sec_auth.cc +++ b/winsup/cygwin/sec_auth.cc @@ -20,10 +20,13 @@ details. */ #include "fhandler.h" #include "dtable.h" #include "cygheap.h" +#include "registry.h" #include "ntdll.h" #include "tls_pbuf.h" #include #include +#include +#include #include "cyglsa.h" #include "cygserver_setpwd.h" #include @@ -172,6 +175,113 @@ cygwin_logon_user (const struct passwd *pw, const char *password) return hToken; } +/* The buffer path points to should be at least MAX_PATH bytes. */ +PWCHAR +get_user_profile_directory (PCWSTR sidstr, PWCHAR path, SIZE_T path_len) +{ + if (!sidstr || !path) + return NULL; + + UNICODE_STRING buf; + tmp_pathbuf tp; + tp.u_get (&buf); + NTSTATUS status; + + RTL_QUERY_REGISTRY_TABLE tab[2] = { + { NULL, RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_DIRECT + | RTL_QUERY_REGISTRY_REQUIRED, + L"ProfileImagePath", &buf, REG_NONE, NULL, 0 }, + { NULL, 0, NULL, NULL, 0, NULL, 0 } + }; + + WCHAR key[wcslen (sidstr) + 16]; + wcpcpy (wcpcpy (key, L"ProfileList\\"), sidstr); + status = RtlQueryRegistryValues (RTL_REGISTRY_WINDOWS_NT, key, tab, + NULL, NULL); + if (!NT_SUCCESS (status) || buf.Length == 0) + { + debug_printf ("ProfileImagePath for %W not found, status %y", sidstr, + status); + return NULL; + } + ExpandEnvironmentStringsW (buf.Buffer, path, path_len); + debug_printf ("ProfileImagePath for %W: %W", sidstr, path); + return path; +} + +/* The CreateProfile prototype is for some reason missing in our w32api headers, + even though it's defined upstream since Dec-2013. */ +extern "C" { + HRESULT WINAPI CreateProfile (LPCWSTR pszUserSid, LPCWSTR pszUserName, + LPWSTR pszProfilePath, DWORD cchProfilePath); +} + +/* Load user profile if it's not already loaded. If the user profile doesn't + exist on the machine, and if we're running Vista or later, try to create it. + + Return a handle to the loaded user registry hive only if it got actually + loaded here, not if it already existed. There's no reliable way to know + when to unload the hive yet, so we're leaking this registry handle for now. + TODO: Try to find a way to reliably unload the user profile again. */ +HANDLE +load_user_profile (HANDLE token, struct passwd *pw, cygpsid &usersid) +{ + WCHAR domain[DNLEN + 1]; + WCHAR username[UNLEN + 1]; + WCHAR sid[128]; + HKEY hkey; + WCHAR userpath[MAX_PATH]; + PROFILEINFOW pi; + WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 3]; + NET_API_STATUS nas = NERR_UserNotFound; + PUSER_INFO_3 ui; + + extract_nt_dom_user (pw, domain, username); + usersid.string (sid); + debug_printf ("user: <%W> <%W>", username, sid); + /* Check if user hive is already loaded. */ + if (!RegOpenKeyExW (HKEY_USERS, sid, 0, KEY_READ, &hkey)) + { + debug_printf ("User registry hive for %W already exists", username); + RegCloseKey (hkey); + return NULL; + } + /* Check if the local profile dir has already been created. */ + if (!get_user_profile_directory (sid, userpath, MAX_PATH)) + { + /* No, try to create it. This function exists only on Vista and later. */ + HRESULT res = CreateProfile (sid, username, userpath, MAX_PATH); + if (res != S_OK) + { + /* If res is 1 (S_FALSE), autoloading failed (XP or 2K3). */ + if (res != S_FALSE) + debug_printf ("CreateProfile, HRESULT %x", res); + return NULL; + } + } + /* Fill PROFILEINFO */ + memset (&pi, 0, sizeof pi); + pi.dwSize = sizeof pi; + pi.dwFlags = PI_NOUI; + pi.lpUserName = username; + /* Check if user has a roaming profile and fill in lpProfilePath, if so. */ + if (get_logon_server (domain, server, DS_IS_FLAT_NAME)) + { + nas = NetUserGetInfo (server, username, 3, (PBYTE *) &ui); + if (NetUserGetInfo (server, username, 3, (PBYTE *) &ui) != NERR_Success) + debug_printf ("NetUserGetInfo, %u", nas); + else if (ui->usri3_profile && *ui->usri3_profile) + pi.lpProfilePath = ui->usri3_profile; + } + + if (!LoadUserProfileW (token, &pi)) + debug_printf ("LoadUserProfileW, %E"); + /* Free buffer created by NetUserGetInfo */ + if (nas == NERR_Success) + NetApiBufferFree (ui); + return pi.hProfile; +} + HANDLE lsa_open_policy (PWCHAR server, ACCESS_MASK access) { diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index eb3e0f1e5..50b5b3cc0 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -472,6 +472,12 @@ void extract_nt_dom_user (const struct passwd *pw, PWCHAR domain, PWCHAR user); /* Get default logonserver for a domain. */ bool get_logon_server (PWCHAR domain, PWCHAR wserver, ULONG flags); +/* Fetch user profile path from registry, if it already exists. */ +PWCHAR get_user_profile_directory (PCWSTR sidstr, PWCHAR path, SIZE_T path_len); + +/* Load user profile if it's not already loaded. */ +HANDLE load_user_profile (HANDLE token, struct passwd *pw, cygpsid &sid); + HANDLE lsa_open_policy (PWCHAR server, ACCESS_MASK access); void lsa_close_policy (HANDLE lsa); diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 933bfe464..9890db4f2 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -3387,11 +3387,7 @@ seteuid32 (uid_t uid) NTSTATUS status; if (!request_restricted_uid_switch) - { - /* Avoid having HKCU use default user */ - WCHAR name[128]; - load_registry_hive (usersid.string (name)); - } + load_user_profile (new_token, pw_new, usersid); /* Try setting owner to same value as user. */ status = NtSetInformationToken (new_token, TokenOwner, diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index e0243d4ac..7c161622d 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -482,13 +482,13 @@ cygheap_user::env_userprofile (const char *name, size_t namelen) if (test_uid (puserprof, name, namelen)) return puserprof; - /* User hive path is never longer than MAX_PATH. */ - WCHAR userprofile_env_buf[MAX_PATH]; + /* User profile path is never longer than MAX_PATH. */ + WCHAR profile[MAX_PATH]; WCHAR win_id[UNLEN + 1]; /* Large enough for SID */ cfree_and_set (puserprof, almost_null); - if (get_registry_hive_path (get_windows_id (win_id), userprofile_env_buf)) - sys_wcstombs_alloc (&puserprof, HEAP_STR, userprofile_env_buf); + if (get_user_profile_directory (get_windows_id (win_id), profile, MAX_PATH)) + sys_wcstombs_alloc (&puserprof, HEAP_STR, profile); return puserprof; }