Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2012-02-02 17:58:20 +0400
committerCorinna Vinschen <corinna@vinschen.de>2012-02-02 17:58:20 +0400
commitb2ec6677eda5e44ee3d34df14a60dbee28a0e406 (patch)
tree4a08bc3cf36d0016402f911656c2602f7a54c823 /winsup/cygwin/fhandler_registry.cc
parent002a1b18e6857dbc4ef29c0d52ba9cc0106e8de1 (diff)
Fix memory and handle leaks due to fdopendir:
* dir.cc (closedir): Call global close instead of just releasing the fhandler. * fhandler_disk_file.cc (fhandler_disk_file::closedir): Don't close fhandler handle here, rely on global closedir doing the right thing. * fhandler_registry.cc (fhandler_registry::readdir): Also delete d_hash when closing registry key. (fhandler_registry::rewinddir): Ditto. Avoid infinite recursion in VirtualStore under UAC: * fhandler_registry.cc (VIRT_CLASSES_KEY_PREFIX): Define. (VIRT_CLASSES_KEY_SUFFIX): Ditto. (VIRT_CLASSES_KEY): Ditto. (VIRT_CLASSES_LINKTGT): Ditto. (fhandler_registry::exists): Return virt_symlink as file type if this is a Classes key under the VirtualStore. (fhandler_registry::fstat): Handle virt_symlink. (fhandler_registry::readdir): Return DT_LNK as d_type if this is a Classes key under the VirtualStore. (fhandler_registry::fill_filebuf): Handle Classes symlink. Handle user impersonation in /proc/registry access: * autoload.cc (RegOpenUserClassesRoot): Define. (RegOpenCurrentUser): Define. * fhandler_registry.cc (RegOpenUserClassesRoot): Declare function missing in w32api. (RegOpenCurrentUser): Ditto. (fetch_hkey): New function. (fhandler_registry::open): Call fetch_hkey to get root registry key. (open_key): Ditto.
Diffstat (limited to 'winsup/cygwin/fhandler_registry.cc')
-rw-r--r--winsup/cygwin/fhandler_registry.cc78
1 files changed, 72 insertions, 6 deletions
diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc
index f252a4435..e0e595f19 100644
--- a/winsup/cygwin/fhandler_registry.cc
+++ b/winsup/cygwin/fhandler_registry.cc
@@ -32,6 +32,20 @@ details. */
static const _off_t REG_ENUM_VALUES_MASK = 0x8000000;
static const _off_t REG_POSITION_MASK = 0xffff;
+/* These key paths are used below whenever we return key information.
+ The problem is UAC virtualization when running an admin account with
+ restricted rights. In that case the subkey "Classes" in the VirtualStore
+ points to the HKEY_CLASSES_ROOT key again. If "Classes" is handled as a
+ normal subdirectory, applications recursing throught the directory
+ hirarchy will invariably run into an infinite recursion. What we do here
+ is to handle the "Classes" subkey as a symlink to HKEY_CLASSES_ROOT. This
+ avoids the infinite recursion, unless the application blindly follows
+ symlinks pointing to directories, in which case it's their own fault. */
+#define VIRT_CLASSES_KEY_PREFIX "/VirtualStore/MACHINE/SOFTWARE"
+#define VIRT_CLASSES_KEY_SUFFIX "Classes"
+#define VIRT_CLASSES_KEY VIRT_CLASSES_KEY_PREFIX "/" VIRT_CLASSES_KEY_SUFFIX
+#define VIRT_CLASSES_LINKTGT "/proc/registry/HKEY_CLASSES_ROOT"
+
/* List of root keys in /proc/registry.
* Possibly we should filter out those not relevant to the flavour of Windows
* Cygwin is running on.
@@ -63,6 +77,33 @@ static const HKEY registry_keys[] =
static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY);
+extern "C" {
+ LONG WINAPI RegOpenUserClassesRoot (HANDLE, DWORD, REGSAM, PHKEY);
+ LONG WINAPI RegOpenCurrentUser (REGSAM, PHKEY);
+};
+
+/* Make sure to access the correct per-user HKCR and HKCU hives, even if
+ the current user is only impersonated in another user's session. */
+static HKEY
+fetch_hkey (int idx) /* idx *must* be valid */
+{
+ HKEY key;
+
+ if (registry_keys[idx] == HKEY_CLASSES_ROOT)
+ {
+ if (RegOpenUserClassesRoot (cygheap->user.issetuid ()
+ ? cygheap->user.imp_token () : hProcImpToken,
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
+ return key;
+ }
+ else if (registry_keys[idx] == HKEY_CURRENT_USER)
+ {
+ if (RegOpenCurrentUser (KEY_READ, &key) == ERROR_SUCCESS)
+ return key;
+ }
+ return registry_keys[idx];
+}
+
/* These get added to each subdirectory in /proc/registry.
* If we wanted to implement writing, we could maybe add a '.writable' entry or
* suchlike.
@@ -319,7 +360,14 @@ fhandler_registry::exists ()
if (!val_only)
hKey = open_key (path, KEY_READ, wow64, false);
if (hKey != (HKEY) INVALID_HANDLE_VALUE)
- file_type = virt_directory;
+ {
+ if (!strcasecmp (path + strlen (path)
+ - sizeof (VIRT_CLASSES_KEY) + 1,
+ VIRT_CLASSES_KEY))
+ file_type = virt_symlink;
+ else
+ file_type = virt_directory;
+ }
else
{
/* Key does not exist or open failed with EACCES,
@@ -429,6 +477,9 @@ fhandler_registry::fstat (struct __stat64 *buf)
case virt_none:
set_errno (ENOENT);
return -1;
+ case virt_symlink:
+ buf->st_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+ break;
case virt_directory:
buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
break;
@@ -550,7 +601,6 @@ fhandler_registry::readdir (DIR *dir, dirent *de)
{
DWORD buf_size = NAME_MAX + 1;
wchar_t buf[buf_size];
- HANDLE handle;
const char *path = dir->__d_dirname + proc_len + 1 + prefix_len;
LONG error;
int res = ENMFILE;
@@ -568,8 +618,7 @@ fhandler_registry::readdir (DIR *dir, dirent *de)
{
if (dir->__d_position != 0)
goto out;
- handle = open_key (path + 1, KEY_READ, wow64, false);
- dir->__handle = handle;
+ dir->__handle = open_key (path + 1, KEY_READ, wow64, false);
if (dir->__handle == INVALID_HANDLE_VALUE)
goto out;
dir->__d_internal = (unsigned) new __DIR_hash ();
@@ -615,6 +664,7 @@ retry:
}
if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA)
{
+ delete d_hash (dir);
RegCloseKey ((HKEY) dir->__handle);
dir->__handle = INVALID_HANDLE_VALUE;
if (error != ERROR_NO_MORE_ITEMS)
@@ -646,6 +696,11 @@ retry:
if (dir->__d_position & REG_ENUM_VALUES_MASK)
de->d_type = DT_REG;
+ else if (!strcasecmp (de->d_name, "Classes")
+ && !strcasecmp (path + strlen (path)
+ - sizeof (VIRT_CLASSES_KEY_PREFIX) + 1,
+ VIRT_CLASSES_KEY_PREFIX))
+ de->d_type = DT_LNK;
else
de->d_type = DT_DIR;
@@ -678,6 +733,7 @@ fhandler_registry::rewinddir (DIR * dir)
{
if (dir->__handle != INVALID_HANDLE_VALUE)
{
+ delete d_hash (dir);
RegCloseKey ((HKEY) dir->__handle);
dir->__handle = INVALID_HANDLE_VALUE;
}
@@ -766,7 +822,7 @@ fhandler_registry::open (int flags, mode_t mode)
}
else
{
- set_io_handle (registry_keys[i]);
+ set_io_handle (fetch_hkey (i));
/* Marking as nohandle allows to call dup on pseudo registry
handles. */
nohandle (true);
@@ -881,6 +937,16 @@ fhandler_registry::fill_filebuf ()
error = RegQueryValueExW (handle, value_name, NULL, &type, NULL, &size);
if (error != ERROR_SUCCESS)
{
+ if (error == ERROR_INVALID_HANDLE
+ && !strcasecmp (get_name () + strlen (get_name ())
+ - sizeof (VIRT_CLASSES_KEY) + 1,
+ VIRT_CLASSES_KEY))
+ {
+ filesize = sizeof (VIRT_CLASSES_LINKTGT);
+ filebuf = (char *) cmalloc_abort (HEAP_BUF, filesize);
+ strcpy (filebuf, VIRT_CLASSES_LINKTGT);
+ return true;
+ }
if (error != ERROR_FILE_NOT_FOUND)
{
seterrno_from_win_error (__FILE__, __LINE__, error);
@@ -1025,7 +1091,7 @@ open_key (const char *name, REGSAM access, DWORD wow64, bool isValue)
{
for (int i = 0; registry_listing[i]; i++)
if (strncasematch (anchor, registry_listing[i], name - anchor - 1))
- hKey = registry_keys[i];
+ hKey = fetch_hkey (i);
if (hKey == (HKEY) INVALID_HANDLE_VALUE)
return hKey;
hParentKey = hKey;