Cygwin uses a "delete queue" in a shared file mapping to hold the names of files that could not be deleted on unlink, usually because they were still opened. The queue is scanned by all processes so that the files eventually get deleted after they are closed.
Because Everyone has write access to the file mapping, any user can add names to the delete queue, and thus any user can trick other processes into deleting any and all files on a PC where a cygwin daemon is running as SYSTEM. The solution is simple: create per user delete queues. They are placed in the same mapping as the mount table. So the change is extremely straightforward. The length of the change log comes from renaming many variable to have names reflect functions. There will be a follow up patch with the following cleanup: remove now unneeded fields from the mount_info and shared_info and run the "magic" on the new/modified structures. Pierre 2003-09-15 Pierre Humblet <[EMAIL PROTECTED]> * shared_info.h (class user_info): New. (cygwin_user_h): New. (user_shared): New. (enum shared_locations): Replace SH_MOUNT_TABLE by SH_USER_SHARED; (mount_table): Change from variable to macro. * shared.cc: Use sizeof(user_info) in "offsets". (user_shared_initialize): Add "reinit" argument to indicate need to reinitialize the mapping. Replace "mount_table" by "user_shared" throughout. Call user_shared->mountinfo.init and user_shared->delqueue.init. (shared_info::initialize): Do not call delqueue.init. (memory_init): Add argument to user_shared_initialize. * child_info.h (child_info::mount_h): Delete. (child_info::user_h): New. * sigpproc.cc (init_child_info): Use user_h instead of mount_h. * dcrt0.cc (_dll_crt0): Ditto. * fhandler_disk_file.cc (fhandler_disk_file::close): Use user_shared->delqueue instead of cygwin_shared->delqueue. * fhandler_virtual.cc (fhandler_virtual::close): Ditto. * syscalls.cc (close_all_files): Ditto. (unlink): Ditto. (seteuid32): Add argument to user_shared_initialize.
Index: shared_info.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/shared_info.h,v retrieving revision 1.32 diff -u -p -r1.32 shared_info.h --- shared_info.h 10 Sep 2003 21:01:40 -0000 1.32 +++ shared_info.h 16 Sep 2003 01:05:06 -0000 @@ -42,10 +42,9 @@ class mount_item scheme should be satisfactory for a long while yet. */ #define MAX_MOUNTS 30 -#define MOUNT_VERSION 27 // increment when mount table changes and -#define MOUNT_VERSION_MAGIC CYGWIN_VERSION_MAGIC (MOUNT_MAGIC, MOUNT_VERSION) -#define CURR_MOUNT_MAGIC 0x4fe431cdU -#define MOUNT_INFO_CB 16488 +#define USER_VERSION 1 // increment when mount table changes and +#define USER_VERSION_MAGIC CYGWIN_VERSION_MAGIC (MOUNT_MAGIC, USER_VERSION) +#define CURR_MOUNT_MAGIC 0x4fe431cdU /* FIXME */ class reg_key; @@ -132,6 +131,14 @@ public: void process_queue (); }; +class user_info +{ +public: + DWORD version; + DWORD cb; + delqueue_list delqueue; + mount_info mountinfo; +}; /******** Shared Info ********/ /* Data accessible to all tasks */ @@ -161,13 +168,14 @@ class shared_info }; extern shared_info *cygwin_shared; -extern mount_info *mount_table; -extern HANDLE cygwin_mount_h; +extern user_info *user_shared; +#define mount_table (&(user_shared->mountinfo)) +extern HANDLE cygwin_user_h; enum shared_locations { SH_CYGWIN_SHARED, - SH_MOUNT_TABLE, + SH_USER_SHARED, SH_SHARED_CONSOLE, SH_MYSELF, SH_TOTAL_SIZE @@ -192,5 +200,5 @@ struct console_state char *__stdcall shared_name (char *, const char *, int); void *__stdcall open_shared (const char *name, int n, HANDLE &shared_h, DWORD size, shared_locations, PSECURITY_ATTRIBUTES psa = &sec_all); -extern void user_shared_initialize (); +extern void user_shared_initialize (bool reinit); Index: shared.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/shared.cc,v retrieving revision 1.73 diff -u -p -r1.73 shared.cc --- shared.cc 11 Sep 2003 07:57:39 -0000 1.73 +++ shared.cc 16 Sep 2003 01:05:06 -0000 @@ -28,8 +28,8 @@ details. */ #include "child_info.h" shared_info NO_COPY *cygwin_shared; -mount_info NO_COPY *mount_table; -HANDLE NO_COPY cygwin_mount_h; +user_info NO_COPY *user_shared; +HANDLE NO_COPY cygwin_user_h; char * __stdcall shared_name (char *ret_buf, const char *str, int num) @@ -54,14 +54,14 @@ static char *offsets[] = + pround (sizeof (shared_info)), (char *) cygwin_shared_address + pround (sizeof (shared_info)) - + pround (sizeof (mount_info)), + + pround (sizeof (user_info)), (char *) cygwin_shared_address + pround (sizeof (shared_info)) - + pround (sizeof (mount_info)) + + pround (sizeof (user_info)) + pround (sizeof (console_state)), (char *) cygwin_shared_address + pround (sizeof (shared_info)) - + pround (sizeof (mount_info)) + + pround (sizeof (user_info)) + pround (sizeof (console_state)) + pround (sizeof (_pinfo)) }; @@ -146,50 +146,54 @@ open_shared (const char *name, int n, HA } void -user_shared_initialize () +user_shared_initialize (bool reinit) { - char name[UNLEN + 1] = ""; + char name[UNLEN + 1] = ""; /* Large enough for SID */ - if (wincap.has_security ()) + if (reinit) { - cygsid tu (cygheap->user.sid ()); - tu.string (name); + if (!UnmapViewOfFile (user_shared)) + debug_printf("UnmapViewOfFile %E"); + if (!ForceCloseHandle (cygwin_user_h)) + debug_printf("CloseHandle %E"); + cygwin_user_h = NULL; } - else - strcpy (name, cygheap->user.name ()); - if (cygwin_mount_h) /* Reinit */ + if (!cygwin_user_h) { - if (!UnmapViewOfFile (mount_table)) - debug_printf("UnmapViewOfFile %E"); - if (!ForceCloseHandle (cygwin_mount_h)) - debug_printf("CloseHandle %E"); - cygwin_mount_h = NULL; + if (wincap.has_security ()) + { + cygsid tu (cygheap->user.sid ()); + tu.string (name); + } + else + strcpy (name, cygheap->user.name ()); } - mount_table = (mount_info *) open_shared (name, MOUNT_VERSION, - cygwin_mount_h, sizeof (mount_info), - SH_MOUNT_TABLE, &sec_none); - debug_printf ("opening mount table for '%s' at %p", name, - mount_table); - ProtectHandleINH (cygwin_mount_h); - debug_printf ("mount table version %x at %p", mount_table->version, mount_table); - - /* Initialize the Cygwin per-user mount table, if necessary */ - if (!mount_table->version) - { - mount_table->version = MOUNT_VERSION_MAGIC; - debug_printf ("initializing mount table"); - mount_table->cb = sizeof (*mount_table); - if (mount_table->cb != MOUNT_INFO_CB) - system_printf ("size of mount table region changed from %u to %u", - MOUNT_INFO_CB, mount_table->cb); - mount_table->init (); /* Initialize the mount table. */ - } - else if (mount_table->version != MOUNT_VERSION_MAGIC) - multiple_cygwin_problem ("mount", mount_table->version, MOUNT_VERSION); - else if (mount_table->cb != MOUNT_INFO_CB) - multiple_cygwin_problem ("mount table size", mount_table->cb, MOUNT_INFO_CB); + user_shared = (user_info *) open_shared (name, USER_VERSION, + cygwin_user_h, sizeof (user_info), + SH_USER_SHARED, &sec_none); + debug_printf ("opening user shared for '%s' at %p", name, user_shared); + ProtectHandleINH (cygwin_user_h); + debug_printf ("user shared version %x", user_shared->version); + + /* Initialize the Cygwin per-user shared, if necessary */ + if (!user_shared->version) + { + user_shared->version = USER_VERSION_MAGIC; + debug_printf ("initializing user shared"); + user_shared->cb = sizeof (*user_shared); + if (user_shared->cb != sizeof (*user_shared)) + system_printf ("size of user shared region changed from %u to %u", + sizeof (*user_shared), user_shared->cb); + user_shared->mountinfo.init (); /* Initialize the mount table. */ + /* Initialize the queue of deleted files. */ + user_shared->delqueue.init (); + } + else if (user_shared->version != USER_VERSION_MAGIC) + multiple_cygwin_problem ("user", user_shared->version, USER_VERSION_MAGIC); + else if (user_shared->cb != sizeof (*user_shared)) + multiple_cygwin_problem ("user shared size", user_shared->cb, sizeof (*user_shared)); } void @@ -198,9 +202,6 @@ shared_info::initialize () DWORD sversion = (DWORD) InterlockedExchange ((LONG *) &version, SHARED_VERSION_MAGIC); if (!sversion) { - /* Initialize the queue of deleted files. */ - delqueue.init (); - /* Initialize tty table. */ tty.init (); } @@ -249,7 +250,7 @@ memory_init () cygheap->shared_h = shared_h; ProtectHandleINH (cygheap->shared_h); - user_shared_initialize (); + user_shared_initialize (false); } unsigned Index: child_info.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/child_info.h,v retrieving revision 1.37 diff -u -p -r1.37 child_info.h --- child_info.h 25 Jan 2003 10:36:46 -0000 1.37 +++ child_info.h 16 Sep 2003 01:05:06 -0000 @@ -44,7 +44,7 @@ public: unsigned short type; // type of record, exec, spawn, fork int cygpid; // cygwin pid of child process HANDLE subproc_ready; // used for synchronization with parent - HANDLE mount_h; + HANDLE user_h; HANDLE parent; HANDLE pppid_handle; init_cygheap *cygheap; Index: dcrt0.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/dcrt0.cc,v retrieving revision 1.182 diff -u -p -r1.182 dcrt0.cc --- dcrt0.cc 16 Sep 2003 00:45:50 -0000 1.182 +++ dcrt0.cc 16 Sep 2003 01:05:08 -0000 @@ -900,7 +900,7 @@ _dll_crt0 () multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union)); else { - cygwin_mount_h = child_proc_info->mount_h; + cygwin_user_h = child_proc_info->user_h; mypid = child_proc_info->cygpid; break; } Index: sigproc.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/sigproc.cc,v retrieving revision 1.154 diff -u -p -r1.154 sigproc.cc --- sigproc.cc 9 Sep 2003 03:11:31 -0000 1.154 +++ sigproc.cc 16 Sep 2003 01:05:11 -0000 @@ -844,7 +844,7 @@ init_child_info (DWORD chtype, child_inf ch->subproc_ready = subproc_ready; ch->pppid_handle = myself->ppid_handle; ch->fhandler_union_cb = sizeof (fhandler_union); - ch->mount_h = cygwin_mount_h; + ch->user_h = cygwin_user_h; } /* Check the state of all of our children to see if any are stopped or Index: fhandler_disk_file.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/fhandler_disk_file.cc,v retrieving revision 1.64 diff -u -p -r1.64 fhandler_disk_file.cc --- fhandler_disk_file.cc 14 Sep 2003 00:07:50 -0000 1.64 +++ fhandler_disk_file.cc 16 Sep 2003 01:05:12 -0000 @@ -427,7 +427,7 @@ fhandler_disk_file::close () { int res = fhandler_base::close (); if (!res) - cygwin_shared->delqueue.process_queue (); + user_shared->delqueue.process_queue (); return res; } Index: fhandler_virtual.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/fhandler_virtual.cc,v retrieving revision 1.19 diff -u -p -r1.19 fhandler_virtual.cc --- fhandler_virtual.cc 8 Sep 2003 04:04:18 -0000 1.19 +++ fhandler_virtual.cc 16 Sep 2003 01:05:12 -0000 @@ -167,7 +167,7 @@ fhandler_virtual::close () free (filebuf); filebuf = NULL; bufalloc = (size_t) -1; - cygwin_shared->delqueue.process_queue (); + user_shared->delqueue.process_queue (); return 0; } Index: syscalls.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/syscalls.cc,v retrieving revision 1.289 diff -u -p -r1.289 syscalls.cc --- syscalls.cc 16 Sep 2003 00:45:50 -0000 1.289 +++ syscalls.cc 16 Sep 2003 01:05:16 -0000 @@ -92,7 +92,7 @@ close_all_files (void) } ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files"); - cygwin_shared->delqueue.process_queue (); + user_shared->delqueue.process_queue (); } int @@ -216,7 +216,7 @@ unlink (const char *ourname) syscall_printf ("couldn't delete file, err %d", lasterr); /* Add file to the "to be deleted" queue. */ - cygwin_shared->delqueue.queue_file (win32_name); + user_shared->delqueue.queue_file (win32_name); /* Success condition. */ ok: @@ -2176,7 +2176,7 @@ seteuid32 (__uid32_t uid) RegCloseKey(HKEY_CURRENT_USER); cygheap->user.reimpersonate (); if (!issamesid) - user_shared_initialize (); + user_shared_initialize (true); success_9x: cygheap->user.set_name (pw_new->pw_name);