From f4a1f8a1dbc28b1f0330ebb19ed3eef2ea0618b2 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sun, 3 Apr 2005 13:06:43 +0000 Subject: [PATCH] * cygheap.cc (cygheap_init): Accomodate set_process_privilege change. * cygheap.h (cygheap_user::curr_primary_token): New member. (cygheap_user::primary_token): New method. (cygheap_user::deimpersonate): Always revert to processes' impersonation token. (cygheap_user::reimpersonate): Set processes' or setuid token as necessary. (cygheap_user::has_impersonation_tokens): Look for curr_primary_token value. (cygheap_user::close_impersonation_tokens): Close curr_primary_token here if necessary. Don't reset token values to NO_IMPERSONATION since that's done in uinfo_init anyway. (init_cygheap::luid): New LUID array keeping privilege LUIDs. * cygtls.cc (_cygtls::init_thread): Call cygheap->user.reimpersonate. * dcrt0.cc (hProcToken): New global variable to keep process token. (hProcImpToken): Ditto for process impersonation token. (dll_crt0_0): Open process token here once. Duplicate to create hProcImpToken. (dll_crt0_1): Call set_cygwin_privileges. * environ.cc (allow_ntea): Drop duplicate declaration. (allow_smbntsec): Ditto. (set_traverse): Only set allow_traverse here. (environ_init): Ditto. * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Drop call to enable_restore_privilege. (fhandler_disk_file::fchown): Ditto. (fhandler_disk_file::facl): Ditto. * fork.cc (fork_child): Move call to cygheap->user.reimpersonate after syn with parent. Call set_cygwin_privileges. * grp.cc (internal_getgroups): Use hProcImpToken instead of opening process token. * path.cc (fs_info::update): Bypass traverse checking when retrieving volume information using push/pop_thread_privileges. * registry.cc (load_registry_hive): Drop setting restore privilege since it's already set if available. * sec_helper.cc: Include cygtls.h. (cygpriv): Privilege string array. (privilege_luid): New function, evaluate LUID from cygpriv_idx. (privilege_luid_by_name): New function, evaluate LUID from privilege string. (privilege_name): New function, evaluate privilege string from cygpriv_idx. (set_privilege): New static function called by set_process_privilege and set_thread_privilege. Call privilege_luid to get privilege LUID. Fix bug in return value evaluation. Improve debug output. (set_cygwin_privileges): New function. (set_process_privilege): Remove. (enable_restore_privilege): Remove. * security.cc (allow_traverse): New global variable. (sys_privs): Change type to cygpriv_idx and store privilege indices instead of strings. (SYSTEM_PRIVILEGES_COUNT): Renamed from SYSTEM_PERMISSION_COUNT. (get_system_priv_list): Don't use numerical constant in malloc call. Use privilege_luid to get privilege LUIDs. (get_priv_list): Call privilege_luid_by_name to get LUIDs. Improve inner privilege LUID comparison loop. (create_token): Enable create token privilege using push/pop_self_privileges. Use hProcToken instead of opening process token. Use default DACL when duplicating token. (subauth): Enable tcb privilege using push/pop_self_privileges. Use sec_none instead of homw made security attributes when duplicating token. (check_file_access): Don't duplicate access token, use active impersonation token as is. * security.h (enum cygpriv_idx): New enumeration type enumerating possible privileges. (privilege_luid): Declare new function. (privilege_luid_by_name): Ditto. (privilege_name): Ditto. (allow_traverse): Declare. (set_privilege): Declare function. (set_process_privilege): Define as macro. (enable_restore_privilege): Remove declaration. (_push_thread_privilege): Define macro. (push_thread_privilege): Ditto. (pop_thread_privilege): Ditto. (pop_self_privilege): Ditto. * spawn.cc (spawn_guts): Use cygheap->user.primary_token instead of cygheap->user.token. * syscalls.cc (statvfs): Bypass traverse checking when retrieving volume information using push/pop_thread_privileges. Rearrange code to simplify push/pop bracketing. (seteuid32): Use hProcToken instead of opening process token. Call cygheap->user.deimpersonate instead of RevertToSelf. Create impersonation token from primary internal or external token. Set cygheap->user.curr_primary_token and cygheap->user.current_token privileges once here. Drop "failed" and "failed_ptok" labels. Drop setting DefaultDacl of process token. (setegid32): Use hProcToken and hProcImpToken instead of opening process token. Always reimpersonate afterwards. * uinfo.cc (cygheap_user::init): Use hProcToken instead of opening process token. (internal_getlogin): Ditto. Set hProcImpToken, too. (uinfo_init): Initialize cygheap->user.curr_primary_token. * winsup.h (hProcToken): Declare. (hProcImpToken): Declare. --- winsup/cygwin/ChangeLog | 99 ++++++++++++++ winsup/cygwin/cygheap.cc | 3 +- winsup/cygwin/cygheap.h | 36 +++-- winsup/cygwin/cygtls.cc | 3 + winsup/cygwin/dcrt0.cc | 13 ++ winsup/cygwin/environ.cc | 9 +- winsup/cygwin/fhandler_disk_file.cc | 4 - winsup/cygwin/fork.cc | 10 +- winsup/cygwin/grp.cc | 12 +- winsup/cygwin/path.cc | 15 ++- winsup/cygwin/registry.cc | 2 - winsup/cygwin/sec_helper.cc | 118 ++++++++++++----- winsup/cygwin/security.cc | 147 ++++++++++----------- winsup/cygwin/security.h | 74 ++++++++++- winsup/cygwin/spawn.cc | 2 +- winsup/cygwin/syscalls.cc | 197 ++++++++++++++-------------- winsup/cygwin/uinfo.cc | 39 +++--- winsup/cygwin/winsup.h | 2 + 18 files changed, 498 insertions(+), 287 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index b46ff2561..d3605571c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,102 @@ +2005-04-03 Corinna Vinschen + + * cygheap.cc (cygheap_init): Accomodate set_process_privilege change. + * cygheap.h (cygheap_user::curr_primary_token): New member. + (cygheap_user::primary_token): New method. + (cygheap_user::deimpersonate): Always revert to processes' + impersonation token. + (cygheap_user::reimpersonate): Set processes' or setuid token as + necessary. + (cygheap_user::has_impersonation_tokens): Look for curr_primary_token + value. + (cygheap_user::close_impersonation_tokens): Close curr_primary_token + here if necessary. Don't reset token values to NO_IMPERSONATION since + that's done in uinfo_init anyway. + (init_cygheap::luid): New LUID array keeping privilege LUIDs. + * cygtls.cc (_cygtls::init_thread): Call cygheap->user.reimpersonate. + * dcrt0.cc (hProcToken): New global variable to keep process token. + (hProcImpToken): Ditto for process impersonation token. + (dll_crt0_0): Open process token here once. Duplicate to create + hProcImpToken. + (dll_crt0_1): Call set_cygwin_privileges. + * environ.cc (allow_ntea): Drop duplicate declaration. + (allow_smbntsec): Ditto. + (set_traverse): Only set allow_traverse here. + (environ_init): Ditto. + * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Drop call to + enable_restore_privilege. + (fhandler_disk_file::fchown): Ditto. + (fhandler_disk_file::facl): Ditto. + * fork.cc (fork_child): Move call to cygheap->user.reimpersonate after + syn with parent. Call set_cygwin_privileges. + * grp.cc (internal_getgroups): Use hProcImpToken instead of opening + process token. + * path.cc (fs_info::update): Bypass traverse checking when retrieving + volume information using push/pop_thread_privileges. + * registry.cc (load_registry_hive): Drop setting restore privilege + since it's already set if available. + * sec_helper.cc: Include cygtls.h. + (cygpriv): Privilege string array. + (privilege_luid): New function, evaluate LUID from cygpriv_idx. + (privilege_luid_by_name): New function, evaluate LUID from privilege + string. + (privilege_name): New function, evaluate privilege string from + cygpriv_idx. + (set_privilege): New static function called by set_process_privilege + and set_thread_privilege. Call privilege_luid to get privilege LUID. + Fix bug in return value evaluation. Improve debug output. + (set_cygwin_privileges): New function. + (set_process_privilege): Remove. + (enable_restore_privilege): Remove. + * security.cc (allow_traverse): New global variable. + (sys_privs): Change type to cygpriv_idx and store privilege indices + instead of strings. + (SYSTEM_PRIVILEGES_COUNT): Renamed from SYSTEM_PERMISSION_COUNT. + (get_system_priv_list): Don't use numerical constant in malloc call. + Use privilege_luid to get privilege LUIDs. + (get_priv_list): Call privilege_luid_by_name to get LUIDs. Improve + inner privilege LUID comparison loop. + (create_token): Enable create token privilege using + push/pop_self_privileges. Use hProcToken instead of opening process + token. Use default DACL when duplicating token. + (subauth): Enable tcb privilege using push/pop_self_privileges. + Use sec_none instead of homw made security attributes when duplicating + token. + (check_file_access): Don't duplicate access token, use active + impersonation token as is. + * security.h (enum cygpriv_idx): New enumeration type enumerating + possible privileges. + (privilege_luid): Declare new function. + (privilege_luid_by_name): Ditto. + (privilege_name): Ditto. + (allow_traverse): Declare. + (set_privilege): Declare function. + (set_process_privilege): Define as macro. + (enable_restore_privilege): Remove declaration. + (_push_thread_privilege): Define macro. + (push_thread_privilege): Ditto. + (pop_thread_privilege): Ditto. + (pop_self_privilege): Ditto. + * spawn.cc (spawn_guts): Use cygheap->user.primary_token instead of + cygheap->user.token. + * syscalls.cc (statvfs): Bypass traverse checking when retrieving + volume information using push/pop_thread_privileges. Rearrange code + to simplify push/pop bracketing. + (seteuid32): Use hProcToken instead of opening process token. Call + cygheap->user.deimpersonate instead of RevertToSelf. Create + impersonation token from primary internal or external token. Set + cygheap->user.curr_primary_token and cygheap->user.current_token + privileges once here. Drop "failed" and "failed_ptok" labels. + Drop setting DefaultDacl of process token. + (setegid32): Use hProcToken and hProcImpToken instead of opening + process token. Always reimpersonate afterwards. + * uinfo.cc (cygheap_user::init): Use hProcToken instead of opening + process token. + (internal_getlogin): Ditto. Set hProcImpToken, too. + (uinfo_init): Initialize cygheap->user.curr_primary_token. + * winsup.h (hProcToken): Declare. + (hProcImpToken): Declare. + 2005-04-03 Corinna Vinschen Unify usage of CYG_MAX_PATH throughout. Change buffers from diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index ef5804d6d..a44901886 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -268,7 +268,7 @@ cygheap_init () if (!cygheap->shared_prefix) cygheap->shared_prefix = cstrdup ( wincap.has_terminal_services () - && (set_process_privilege (SE_CREATE_GLOBAL_NAME, true) >= 0 + && (set_process_privilege (SE_CREATE_GLOBAL_PRIV, true) >= 0 || GetLastError () == ERROR_NO_SUCH_PRIVILEGE) ? "Global\\" : ""); } @@ -511,4 +511,3 @@ cygheap_user::set_name (const char *new_name) cfree_and_set (pdomain); cfree_and_set (pwinname); } - diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index a338c6ed7..3679ddb56 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -123,6 +123,7 @@ public: to `set_impersonation_token()'. */ HANDLE external_token; HANDLE internal_token; + HANDLE curr_primary_token; HANDLE current_token; /* CGF 2002-06-27. I removed the initializaton from this constructor @@ -172,40 +173,36 @@ public: const char *ontherange (homebodies what, struct passwd * = NULL); #define NO_IMPERSONATION NULL bool issetuid () const { return current_token != NO_IMPERSONATION; } + HANDLE primary_token () { return curr_primary_token; } HANDLE token () { return current_token; } void deimpersonate () { if (issetuid ()) - RevertToSelf (); + { + RevertToSelf (); + ImpersonateLoggedOnUser (hProcImpToken); + } } - void reimpersonate () + bool reimpersonate () { - if (issetuid () - && !ImpersonateLoggedOnUser (token ())) - system_printf ("ImpersonateLoggedOnUser: %E"); + return ImpersonateLoggedOnUser (issetuid () ? token () : hProcImpToken); } bool has_impersonation_tokens () { return external_token != NO_IMPERSONATION || internal_token != NO_IMPERSONATION - || current_token != NO_IMPERSONATION; } + || curr_primary_token != NO_IMPERSONATION; } void close_impersonation_tokens () { if (current_token != NO_IMPERSONATION) - { - if( current_token != external_token && current_token != internal_token) - CloseHandle (current_token); - current_token = NO_IMPERSONATION; - } + CloseHandle (current_token); + if (curr_primary_token != NO_IMPERSONATION + && curr_primary_token != external_token + && curr_primary_token != internal_token) + CloseHandle (curr_primary_token); if (external_token != NO_IMPERSONATION) - { - CloseHandle (external_token); - external_token = NO_IMPERSONATION; - } + CloseHandle (external_token); if (internal_token != NO_IMPERSONATION) - { - CloseHandle (internal_token); - internal_token = NO_IMPERSONATION; - } + CloseHandle (internal_token); } char * get_windows_id (char * buf) { @@ -284,6 +281,7 @@ struct init_cygheap char *cygwin_regname; cwdstuff cwd; dtable fdtab; + LUID luid[SE_NUM_PRIVS]; const char *shared_prefix; #ifdef DEBUGGING cygheap_debug debug; diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc index e14f248cf..4055f0228 100644 --- a/winsup/cygwin/cygtls.cc +++ b/winsup/cygwin/cygtls.cc @@ -124,6 +124,9 @@ _cygtls::init_thread (void *x, DWORD (*func) (void *, void *)) || (void *) func == (void *) cygthread::simplestub) return; + if (wincap.has_security ()) + cygheap->user.reimpersonate (); + sentry here (INFINITE); if (nthreads >= cygheap->sthreads) { diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 13f7c347f..e7478fde6 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -45,6 +45,8 @@ void ld_preload (); HANDLE NO_COPY hMainProc = (HANDLE) -1; HANDLE NO_COPY hMainThread; +HANDLE NO_COPY hProcToken; +HANDLE NO_COPY hProcImpToken; bool display_title; bool strip_title_path; @@ -589,6 +591,8 @@ dll_crt0_0 () DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc, &hMainThread, 0, false, DUPLICATE_SAME_ACCESS); + if (wincap.has_security ()) + OpenProcessToken (hMainProc, MAXIMUM_ALLOWED, &hProcToken); (void) SetErrorMode (SEM_FAILCRITICALERRORS); @@ -688,6 +692,12 @@ dll_crt0_0 () events_init (); cygheap->cwd.init (); + + /* Late duplicate simplifies tweaking the process token in uinfo.cc. */ + if (wincap.has_security ()) + DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL, + SecurityImpersonation, TokenImpersonation, + &hProcImpToken); } /* Take over from libc's crt0.o and start the application. Note the @@ -759,6 +769,9 @@ dll_crt0_1 (char *) /* Initialize our process table entry. */ pinfo_init (envp, envc); + /* Can be set only after environment has been initialized. */ + set_cygwin_privileges (hProcImpToken); + if (!old_title && GetConsoleTitle (title_buf, TITLESIZE)) old_title = title_buf; diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index e7b17c31a..714bf3c3f 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -22,14 +22,13 @@ details. */ #include "fhandler.h" #include "dtable.h" #include "cygheap.h" +#include "cygtls.h" #include "registry.h" #include "environ.h" #include "child_info.h" extern bool allow_glob; extern bool ignore_case_with_glob; -extern bool allow_ntea; -extern bool allow_smbntsec; extern bool allow_winsymlinks; extern bool strip_title_path; extern int pcheck_case; @@ -533,9 +532,7 @@ set_ntsec (const char *buf) static void set_traverse (const char *buf) { - if (wincap.has_security ()) - set_process_privilege (SE_CHANGE_NOTIFY_NAME, - !buf || !strcasematch (buf, "yes")); + allow_traverse = (buf && strcasematch (buf, "yes") && wincap.has_security ()); } static void @@ -738,7 +735,7 @@ environ_init (char **envp, int envc) if (wincap.has_security ()) { allow_ntsec = true; - set_process_privilege (SE_CHANGE_NOTIFY_NAME, false); + allow_traverse = true; } if (!envp) diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index dba3a1be3..8f52425fc 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -409,7 +409,6 @@ fhandler_disk_file::fchmod (mode_t mode) if (wincap.has_security ()) { - enable_restore_privilege (); if (!get_io_handle () && pc.has_acls ()) { query_open (query_write_control); @@ -461,7 +460,6 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid) return 0; } - enable_restore_privilege (); if (!get_io_handle ()) { query_open (query_write_control); @@ -548,8 +546,6 @@ fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp) } else { - if (cmd == SETACL) - enable_restore_privilege (); if (!get_io_handle ()) { query_open (cmd == SETACL ? query_write_control : query_read_control); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 3fb90724c..41b9064e7 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -160,10 +160,6 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) debug_printf ("child is running. pid %d, ppid %d, stack here %p", myself->pid, myself->ppid, __builtin_frame_address (0)); - /* Restore the inheritance state as in parent - Don't call setuid here! The flags are already set. */ - cygheap->user.reimpersonate (); - sync_with_parent ("after longjmp", true); sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent, first_dll, load_dlls); @@ -179,6 +175,12 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) _impure_ptr = &_main_tls->local_clib; } + if (wincap.has_security ()) + { + set_cygwin_privileges (hProcImpToken); + cygheap->user.reimpersonate (); + } + #ifdef DEBUGGING char c; if (GetEnvironmentVariable ("FORKDEBUG", &c, 1)) diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index 4a475c801..df5a14ee9 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -347,8 +347,8 @@ internal_getgroups (int gidsetsize, __gid32_t *grouplist, cygpsid * srchsid) /* If impersonated, use impersonation token. */ if (cygheap->user.issetuid ()) hToken = cygheap->user.token (); - else if (!OpenProcessToken (hMainProc, TOKEN_QUERY, &hToken)) - hToken = NULL; + else + hToken = hProcImpToken; } if (hToken) { @@ -379,19 +379,13 @@ internal_getgroups (int gidsetsize, __gid32_t *grouplist, cygpsid * srchsid) grouplist[cnt] = gr->gr_gid; ++cnt; if (gidsetsize && cnt > gidsetsize) - { - if (!cygheap->user.issetuid ()) - CloseHandle (hToken); - goto error; - } + goto error; break; } } } else debug_printf ("%d = GetTokenInformation(NULL) %E", size); - if (!cygheap->user.issetuid ()) - CloseHandle (hToken); return cnt; } diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index b9d737d08..688804a07 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -348,6 +348,7 @@ fs_info::update (const char *win32_path) { char fsname [CYG_MAX_PATH]; char root_dir [CYG_MAX_PATH]; + bool ret; if (!rootdir (win32_path, root_dir)) { @@ -372,6 +373,10 @@ fs_info::update (const char *win32_path) } name_hash = tmp_name_hash; + /* I have no idea why, but some machines require SeChangeNotifyPrivilege + to access volume information. */ + push_thread_privilege (SE_CHANGE_NOTIFY_PRIV, true); + drive_type (GetDriveType (root_dir)); if (drive_type () == DRIVE_REMOTE || (drive_type () == DRIVE_UNKNOWN @@ -380,9 +385,12 @@ fs_info::update (const char *win32_path) else is_remote_drive (false); - if (!GetVolumeInformation (root_dir, NULL, 0, &status.serial, NULL, - &status.flags, fsname, sizeof (fsname)) - && !is_remote_drive ()) + ret = GetVolumeInformation (root_dir, NULL, 0, &status.serial, NULL, + &status.flags, fsname, sizeof (fsname)); + + pop_thread_privilege (); + + if (!ret && !is_remote_drive ()) { debug_printf ("Cannot get volume information (%s), %E", root_dir); has_buggy_open (false); @@ -390,6 +398,7 @@ fs_info::update (const char *win32_path) flags () = serial () = 0; return false; } + /* FIXME: Samba by default returns "NTFS" in file system name, but * doesn't support Extended Attributes. If there's some fast way to * distinguish between samba and real ntfs, it should be implemented diff --git a/winsup/cygwin/registry.cc b/winsup/cygwin/registry.cc index 7e99fb84d..b443b92f4 100644 --- a/winsup/cygwin/registry.cc +++ b/winsup/cygwin/registry.cc @@ -251,8 +251,6 @@ load_registry_hive (const char * name) RegCloseKey (hkey); return; } - /* This is only called while deimpersonated */ - set_process_privilege (SE_RESTORE_NAME); if (get_registry_hive_path (name, path)) { if (wincap.is_winnt ()) diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc index 852ef5b52..3aa3b9d1d 100644 --- a/winsup/cygwin/sec_helper.cc +++ b/winsup/cygwin/sec_helper.cc @@ -30,6 +30,7 @@ details. */ #include "dtable.h" #include "pinfo.h" #include "cygheap.h" +#include "cygtls.h" #include "pwdgrp.h" /* General purpose security attribute objects for global use. */ @@ -321,37 +322,95 @@ got_it: #undef DOMLEN #endif //unused -int -set_process_privilege (const char *privilege, bool enable, bool use_thread) +/* Order must be same as cygperm_idx in winsup.h. */ +static const char *cygpriv[] = { - HANDLE hToken = NULL; - LUID priv_luid; - TOKEN_PRIVILEGES new_priv, orig_priv; - int ret = -1; - DWORD size; + SE_CREATE_TOKEN_NAME, + SE_ASSIGNPRIMARYTOKEN_NAME, + SE_LOCK_MEMORY_NAME, + SE_INCREASE_QUOTA_NAME, + SE_UNSOLICITED_INPUT_NAME, + SE_MACHINE_ACCOUNT_NAME, + SE_TCB_NAME, + SE_SECURITY_NAME, + SE_TAKE_OWNERSHIP_NAME, + SE_LOAD_DRIVER_NAME, + SE_SYSTEM_PROFILE_NAME, + SE_SYSTEMTIME_NAME, + SE_PROF_SINGLE_PROCESS_NAME, + SE_INC_BASE_PRIORITY_NAME, + SE_CREATE_PAGEFILE_NAME, + SE_CREATE_PERMANENT_NAME, + SE_BACKUP_NAME, + SE_RESTORE_NAME, + SE_SHUTDOWN_NAME, + SE_DEBUG_NAME, + SE_AUDIT_NAME, + SE_SYSTEM_ENVIRONMENT_NAME, + SE_CHANGE_NOTIFY_NAME, + SE_REMOTE_SHUTDOWN_NAME, + SE_CREATE_GLOBAL_NAME, + SE_UNDOCK_NAME, + SE_MANAGE_VOLUME_NAME, + SE_IMPERSONATE_NAME, + SE_ENABLE_DELEGATION_NAME, + SE_SYNC_AGENT_NAME +}; - if (!LookupPrivilegeValue (NULL, privilege, &priv_luid)) +const LUID * +privilege_luid (cygpriv_idx idx) +{ + if (idx < 0 || idx >= SE_NUM_PRIVS) + return NULL; + if (!cygheap->luid[idx].LowPart && !cygheap->luid[idx].HighPart + && !LookupPrivilegeValue (NULL, cygpriv[idx], &cygheap->luid[idx])) { __seterrno (); - goto out; + return NULL; } + return &cygheap->luid[idx]; +} - if ((use_thread - && !OpenThreadToken (GetCurrentThread (), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, - 0, &hToken)) - ||(!use_thread - && !OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, - &hToken))) +const LUID * +privilege_luid_by_name (const char *pname) +{ + int idx; + + if (!pname) + return NULL; + for (idx = 0; idx < SE_NUM_PRIVS; ++idx) + if (!strcmp (pname, cygpriv[idx])) + return privilege_luid ((cygpriv_idx) idx); + return NULL; +} + +const char * +privilege_name (cygpriv_idx idx) +{ + if (idx < 0 || idx >= SE_NUM_PRIVS) + return ""; + return cygpriv[idx]; +} + +int +set_privilege (HANDLE token, cygpriv_idx privilege, bool enable) +{ + int ret = -1; + const LUID *priv_luid; + TOKEN_PRIVILEGES new_priv, orig_priv; + DWORD size; + + if (!(priv_luid = privilege_luid (privilege))) { __seterrno (); goto out; } new_priv.PrivilegeCount = 1; - new_priv.Privileges[0].Luid = priv_luid; + new_priv.Privileges[0].Luid = *priv_luid; new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; - if (!AdjustTokenPrivileges (hToken, FALSE, &new_priv, + if (!AdjustTokenPrivileges (token, FALSE, &new_priv, sizeof orig_priv, &orig_priv, &size)) { __seterrno (); @@ -365,28 +424,23 @@ set_process_privilege (const char *privilege, bool enable, bool use_thread) goto out; } - ret = orig_priv.Privileges[0].Attributes == SE_PRIVILEGE_ENABLED ? 1 : 0; + /* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */ + if (!orig_priv.PrivilegeCount) + ret = enable ? 1 : 0; + else + ret = (orig_priv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? 1 : 0; out: - if (hToken) - CloseHandle (hToken); - - syscall_printf ("%d = set_process_privilege (%s, %d)", ret, privilege, enable); + syscall_printf ("%d = set_privilege ((token %x) %s, %d)", + ret, token, privilege_name (privilege), enable); return ret; } -/* Helper function to set the SE_RESTORE_NAME privilege once. */ void -enable_restore_privilege () +set_cygwin_privileges (HANDLE token) { - static int NO_COPY saved_res; - bool issetuid = cygheap->user.issetuid (); - if (!saved_res || issetuid) - { - int res = 2 + set_process_privilege (SE_RESTORE_NAME, true, issetuid); - if (!issetuid) - saved_res = res; - } + set_privilege (token, SE_RESTORE_PRIV, true); + set_privilege (token, SE_CHANGE_NOTIFY_PRIV, !allow_traverse); } /* diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 5fd401908..44ae8b477 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -45,6 +45,7 @@ bool allow_ntsec; It's defined here because of it's strong relationship to allow_ntsec. The default is TRUE to reflect the old behaviour. */ bool allow_smbntsec; +bool allow_traverse; cygsid * cygsidlist::alloc_sids (int n) @@ -593,36 +594,44 @@ get_setgroups_sidlist (cygsidlist &tmp_list, PTOKEN_GROUPS my_grps, tmp_list += pgpsid; } -static const char *sys_privs[] = { - SE_TCB_NAME, - SE_ASSIGNPRIMARYTOKEN_NAME, - SE_CREATE_TOKEN_NAME, - SE_CHANGE_NOTIFY_NAME, - SE_SECURITY_NAME, - SE_BACKUP_NAME, - SE_RESTORE_NAME, - SE_SYSTEMTIME_NAME, - SE_SHUTDOWN_NAME, - SE_REMOTE_SHUTDOWN_NAME, - SE_TAKE_OWNERSHIP_NAME, - SE_DEBUG_NAME, - SE_SYSTEM_ENVIRONMENT_NAME, - SE_SYSTEM_PROFILE_NAME, - SE_PROF_SINGLE_PROCESS_NAME, - SE_INC_BASE_PRIORITY_NAME, - SE_LOAD_DRIVER_NAME, - SE_CREATE_PAGEFILE_NAME, - SE_INCREASE_QUOTA_NAME +static const cygpriv_idx sys_privs[] = { + SE_TCB_PRIV, + SE_ASSIGNPRIMARYTOKEN_PRIV, + SE_CREATE_TOKEN_PRIV, + SE_CHANGE_NOTIFY_PRIV, + SE_SECURITY_PRIV, + SE_BACKUP_PRIV, + SE_RESTORE_PRIV, + SE_SYSTEMTIME_PRIV, + SE_SHUTDOWN_PRIV, + SE_REMOTE_SHUTDOWN_PRIV, + SE_TAKE_OWNERSHIP_PRIV, + SE_DEBUG_PRIV, + SE_SYSTEM_ENVIRONMENT_PRIV, + SE_SYSTEM_PROFILE_PRIV, + SE_PROF_SINGLE_PROCESS_PRIV, + SE_INC_BASE_PRIORITY_PRIV, + SE_LOAD_DRIVER_PRIV, + SE_CREATE_PAGEFILE_PRIV, + SE_INCREASE_QUOTA_PRIV, + SE_LOCK_MEMORY_PRIV, + SE_CREATE_PERMANENT_PRIV, + SE_AUDIT_PRIV, + SE_UNDOCK_PRIV, + SE_MANAGE_VOLUME_PRIV, + SE_IMPERSONATE_PRIV, + SE_CREATE_GLOBAL_PRIV }; -#define SYSTEM_PERMISSION_COUNT (sizeof sys_privs / sizeof (const char *)) +#define SYSTEM_PRIVILEGES_COUNT (sizeof sys_privs / sizeof *sys_privs) PTOKEN_PRIVILEGES get_system_priv_list (cygsidlist &grp_list) { - LUID priv; + const LUID *priv; PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES) - malloc (sizeof (ULONG) + 20 * sizeof (LUID_AND_ATTRIBUTES)); + malloc (sizeof (ULONG) + + SYSTEM_PRIVILEGES_COUNT * sizeof (LUID_AND_ATTRIBUTES)); if (!privs) { debug_printf ("malloc (system_privs) failed."); @@ -630,10 +639,10 @@ get_system_priv_list (cygsidlist &grp_list) } privs->PrivilegeCount = 0; - for (DWORD i = 0; i < SYSTEM_PERMISSION_COUNT; ++i) - if (LookupPrivilegeValue (NULL, sys_privs[i], &priv)) + for (DWORD i = 0; i < SYSTEM_PRIVILEGES_COUNT; ++i) + if ((priv = privilege_luid (sys_privs[i]))) { - privs->Privileges[privs->PrivilegeCount].Luid = priv; + privs->Privileges[privs->PrivilegeCount].Luid = *priv; privs->Privileges[privs->PrivilegeCount].Attributes = SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; ++privs->PrivilegeCount; @@ -667,17 +676,23 @@ get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list) continue; for (ULONG i = 0; i < cnt; ++i) { - LUID priv; + const LUID *priv; PTOKEN_PRIVILEGES tmp; DWORD tmp_count; lsa2str (buf, privstrs[i], sizeof (buf) - 1); - if (!LookupPrivilegeValue (NULL, buf, &priv)) + if (!(priv = privilege_luid_by_name (buf))) continue; - for (DWORD p = 0; privs && p < privs->PrivilegeCount; ++p) - if (!memcmp (&priv, &privs->Privileges[p].Luid, sizeof (LUID))) - goto next_account_right; + if (privs) + { + DWORD pcnt = privs->PrivilegeCount; + LUID_AND_ATTRIBUTES *p = privs->Privileges; + for (; pcnt > 0; --pcnt, ++p) + if (priv->HighPart == p->Luid.HighPart + && priv->LowPart == p->Luid.LowPart) + goto next_account_right; + } tmp_count = privs ? privs->PrivilegeCount : 0; tmp = (PTOKEN_PRIVILEGES) @@ -693,7 +708,7 @@ get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list) } tmp->PrivilegeCount = tmp_count; privs = tmp; - privs->Privileges[privs->PrivilegeCount].Luid = priv; + privs->Privileges[privs->PrivilegeCount].Luid = *priv; privs->Privileges[privs->PrivilegeCount].Attributes = SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; ++privs->PrivilegeCount; @@ -812,16 +827,13 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) { NTSTATUS ret; LSA_HANDLE lsa = INVALID_HANDLE_VALUE; - int old_priv_state; cygsidlist tmp_gsids (cygsidlist_auto, 12); SECURITY_QUALITY_OF_SERVICE sqos = { sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE }; OBJECT_ATTRIBUTES oa = { sizeof oa, 0, 0, 0, 0, &sqos }; - PSECURITY_ATTRIBUTES psa; bool special_pgrp = false; - char sa_buf[1024]; LUID auth_luid = SYSTEM_LUID; LARGE_INTEGER exp = { QuadPart:INT64_MAX }; @@ -840,13 +852,11 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) HANDLE token = INVALID_HANDLE_VALUE; HANDLE primary_token = INVALID_HANDLE_VALUE; - HANDLE my_token = INVALID_HANDLE_VALUE; PTOKEN_GROUPS my_tok_gsids = NULL; DWORD size; /* SE_CREATE_TOKEN_NAME privilege needed to call NtCreateToken. */ - if ((old_priv_state = set_process_privilege (SE_CREATE_TOKEN_NAME)) < 0) - goto out; + push_self_privilege (SE_CREATE_TOKEN_PRIV, true); /* Open policy object. */ if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE) @@ -858,35 +868,32 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) owner.Owner = usersid; /* Retrieve authentication id and group list from own process. */ - if (!OpenProcessToken (hMainProc, TOKEN_QUERY, &my_token)) - debug_printf ("OpenProcessToken(my_token), %E"); - else + if (hProcToken) { /* Switching user context to SYSTEM doesn't inherit the authentication id of the user account running current process. */ if (usersid != well_known_system_sid) - if (!GetTokenInformation (my_token, TokenStatistics, + if (!GetTokenInformation (hProcToken, TokenStatistics, &stats, sizeof stats, &size)) debug_printf - ("GetTokenInformation(my_token, TokenStatistics), %E"); + ("GetTokenInformation(hProcToken, TokenStatistics), %E"); else auth_luid = stats.AuthenticationId; /* Retrieving current processes group list to be able to inherit some important well known group sids. */ - if (!GetTokenInformation (my_token, TokenGroups, NULL, 0, &size) && + if (!GetTokenInformation (hProcToken, TokenGroups, NULL, 0, &size) && GetLastError () != ERROR_INSUFFICIENT_BUFFER) - debug_printf ("GetTokenInformation(my_token, TokenGroups), %E"); + debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); else if (!(my_tok_gsids = (PTOKEN_GROUPS) malloc (size))) debug_printf ("malloc (my_tok_gsids) failed."); - else if (!GetTokenInformation (my_token, TokenGroups, my_tok_gsids, + else if (!GetTokenInformation (hProcToken, TokenGroups, my_tok_gsids, size, &size)) { - debug_printf ("GetTokenInformation(my_token, TokenGroups), %E"); + debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); free (my_tok_gsids); my_tok_gsids = NULL; } - CloseHandle (my_token); } /* Create list of groups, the user is member in. */ @@ -932,18 +939,10 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) } else { - /* Set security descriptor and primary group */ - psa = sec_user (sa_buf, usersid); - if (psa->lpSecurityDescriptor && - !SetSecurityDescriptorGroup ((PSECURITY_DESCRIPTOR) - psa->lpSecurityDescriptor, - special_pgrp ? new_groups.pgsid - : well_known_null_sid, - FALSE)) - debug_printf ("SetSecurityDescriptorGroup %E"); /* Convert to primary token. */ - if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, psa, SecurityImpersonation, - TokenPrimary, &primary_token)) + if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, &sec_none, + SecurityImpersonation, TokenPrimary, + &primary_token)) { __seterrno (); debug_printf ("DuplicateTokenEx %E"); @@ -951,8 +950,7 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) } out: - if (old_priv_state >= 0) - set_process_privilege (SE_CREATE_TOKEN_NAME, old_priv_state); + pop_self_privilege (); if (token != INVALID_HANDLE_VALUE) CloseHandle (token); if (privs) @@ -993,13 +991,10 @@ subauth (struct passwd *pw) QUOTA_LIMITS quota; char nt_domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; char nt_user[UNLEN + 1]; - SECURITY_ATTRIBUTES sa = { sizeof sa, NULL, TRUE }; HANDLE user_token = INVALID_HANDLE_VALUE; HANDLE primary_token = INVALID_HANDLE_VALUE; - int old_tcb_state; - if ((old_tcb_state = set_process_privilege (SE_TCB_NAME)) < 0) - return INVALID_HANDLE_VALUE; + push_self_privilege (SE_TCB_PRIV, true); /* Register as logon process. */ str2lsa (name, "Cygwin"); @@ -1057,12 +1052,12 @@ subauth (struct passwd *pw) } LsaFreeReturnBuffer (profile); /* Convert to primary token. */ - if (!DuplicateTokenEx (user_token, TOKEN_ALL_ACCESS, &sa, + if (!DuplicateTokenEx (user_token, TOKEN_ALL_ACCESS, &sec_none, SecurityImpersonation, TokenPrimary, &primary_token)) __seterrno (); out: - set_process_privilege (SE_TCB_NAME, old_tcb_state); + pop_self_privilege (); if (user_token != INVALID_HANDLE_VALUE) CloseHandle (user_token); return primary_token; @@ -1832,7 +1827,7 @@ check_file_access (const char *fn, int flags) security_descriptor sd; - HANDLE hToken, hIToken; + HANDLE hToken; BOOL status; char pbuf[sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES)]; DWORD desired = 0, granted, plength = sizeof pbuf; @@ -1845,17 +1840,8 @@ check_file_access (const char *fn, int flags) if (cygheap->user.issetuid ()) hToken = cygheap->user.token (); - else if (!OpenProcessToken (hMainProc, TOKEN_DUPLICATE, &hToken)) - { - __seterrno (); - goto done; - } - if (!(status = DuplicateToken (hToken, SecurityIdentification, &hIToken))) - __seterrno (); - if (!cygheap->user.issetuid ()) - CloseHandle (hToken); - if (!status) - goto done; + else + hToken = hProcImpToken; if (flags & R_OK) desired |= FILE_READ_DATA; @@ -1863,14 +1849,13 @@ check_file_access (const char *fn, int flags) desired |= FILE_WRITE_DATA; if (flags & X_OK) desired |= FILE_EXECUTE; - if (!AccessCheck (sd, hIToken, desired, &mapping, + if (!AccessCheck (sd, hToken, desired, &mapping, (PPRIVILEGE_SET) pbuf, &plength, &granted, &status)) __seterrno (); else if (!status) set_errno (EACCES); else ret = 0; - CloseHandle (hIToken); done: debug_printf ("flags %x, ret %d", flags, ret); return ret; diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index a0035a966..0ee8e7134 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -236,6 +236,46 @@ extern cygpsid well_known_authenticated_users_sid; extern cygpsid well_known_system_sid; extern cygpsid well_known_admins_sid; +/* Order must be same as cygpriv in sec_helper.cc. */ +enum cygpriv_idx { + SE_CREATE_TOKEN_PRIV = 0, + SE_ASSIGNPRIMARYTOKEN_PRIV, + SE_LOCK_MEMORY_PRIV, + SE_INCREASE_QUOTA_PRIV, + SE_UNSOLICITED_INPUT_PRIV, + SE_MACHINE_ACCOUNT_PRIV, + SE_TCB_PRIV, + SE_SECURITY_PRIV, + SE_TAKE_OWNERSHIP_PRIV, + SE_LOAD_DRIVER_PRIV, + SE_SYSTEM_PROFILE_PRIV, + SE_SYSTEMTIME_PRIV, + SE_PROF_SINGLE_PROCESS_PRIV, + SE_INC_BASE_PRIORITY_PRIV, + SE_CREATE_PAGEFILE_PRIV, + SE_CREATE_PERMANENT_PRIV, + SE_BACKUP_PRIV, + SE_RESTORE_PRIV, + SE_SHUTDOWN_PRIV, + SE_DEBUG_PRIV, + SE_AUDIT_PRIV, + SE_SYSTEM_ENVIRONMENT_PRIV, + SE_CHANGE_NOTIFY_PRIV, + SE_REMOTE_SHUTDOWN_PRIV, + SE_CREATE_GLOBAL_PRIV, + SE_UNDOCK_PRIV, + SE_MANAGE_VOLUME_PRIV, + SE_IMPERSONATE_PRIV, + SE_ENABLE_DELEGATION_PRIV, + SE_SYNC_AGENT_PRIV, + + SE_NUM_PRIVS +}; + +const LUID *privilege_luid (enum cygpriv_idx idx); +const LUID *privilege_luid_by_name (const char *pname); +const char *privilege_name (enum cygpriv_idx idx); + inline BOOL legal_sid_type (SID_NAME_USE type) { @@ -246,6 +286,7 @@ legal_sid_type (SID_NAME_USE type) extern bool allow_ntea; extern bool allow_ntsec; extern bool allow_smbntsec; +extern bool allow_traverse; /* File manipulation */ int __stdcall get_file_attribute (int, HANDLE, const char *, mode_t *, @@ -291,8 +332,37 @@ void extract_nt_dom_user (const struct passwd *pw, char *domain, char *user); bool get_logon_server (const char * domain, char * server, WCHAR *wserver = NULL); /* sec_helper.cc: Security helper functions. */ -int set_process_privilege (const char *privilege, bool enable = true, bool use_thread = false); -void enable_restore_privilege (void); +int set_privilege (HANDLE token, enum cygpriv_idx privilege, bool enable); +void set_cygwin_privileges (HANDLE token); + +#define set_process_privilege(p,v) set_privilege (hProcImpToken, (p), (v)) + +#define _push_thread_privilege(_priv, _val, _check) { \ + HANDLE _token = NULL, _dup_token = NULL; \ + if (wincap.has_security ()) \ + { \ + _token = (cygheap->user.issetuid () && (_check)) \ + ? cygheap->user.token () : hProcImpToken; \ + if (!DuplicateTokenEx (_token, MAXIMUM_ALLOWED, NULL, \ + SecurityImpersonation, TokenImpersonation, \ + &_dup_token)) \ + debug_printf ("DuplicateTokenEx: %E"); \ + else if (!ImpersonateLoggedOnUser (_dup_token)) \ + debug_printf ("ImpersonateLoggedOnUser: %E"); \ + else \ + set_privilege (_dup_token, (_priv), (_val)); \ + } +#define push_thread_privilege(_priv, _val) _push_thread_privilege(_priv,_val,1) +#define push_self_privilege(_priv, _val) _push_thread_privilege(_priv,_val,0) + +#define pop_thread_privilege() \ + if (_dup_token) \ + { \ + ImpersonateLoggedOnUser (_token); \ + CloseHandle (_dup_token); \ + } \ + } +#define pop_self_privilege() pop_thread_privilege() /* shared.cc: */ /* Retrieve a security descriptor that allows all access */ diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 6aa596edd..8cb8b7dc1 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -711,7 +711,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, real_path.iscygexec ()); newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ()); - rc = CreateProcessAsUser (cygheap->user.token (), + rc = CreateProcessAsUser (cygheap->user.primary_token (), runpath, /* image name - with full path */ one_line.buf, /* what was passed to exec */ &sec_none_nih, /* process security attrs */ diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 7cca8b610..25c4a002a 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1698,6 +1698,7 @@ get_osfhandle (int fd) extern "C" int statvfs (const char *fname, struct statvfs *sfs) { + int ret = -1; char root[CYG_MAX_PATH]; if (check_null_empty_str_errno (fname) @@ -1716,47 +1717,47 @@ statvfs (const char *fname, struct statvfs *sfs) if (!rootdir (full_path, root)) return -1; + ULARGE_INTEGER availb, freeb, totalb; + DWORD spc, bps, availc, freec, totalc, vsn, maxlen, flags; + BOOL status; + + push_thread_privilege (SE_CHANGE_NOTIFY_PRIV, true); + /* GetDiskFreeSpaceEx must be called before GetDiskFreeSpace on WinME, to avoid the MS KB 314417 bug */ - ULARGE_INTEGER availb, freeb, totalb; - BOOL status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb); - - DWORD spc, bps, availc, freec, totalc; - - if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc)) + status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb); + if (GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc)) { - __seterrno (); - return -1; + if (status) + { + availc = availb.QuadPart / (spc*bps); + totalc = totalb.QuadPart / (spc*bps); + freec = freeb.QuadPart / (spc*bps); + } + else + availc = freec; + if (GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0)) + { + sfs->f_bsize = spc*bps; + sfs->f_frsize = spc*bps; + sfs->f_blocks = totalc; + sfs->f_bfree = freec; + sfs->f_bavail = availc; + sfs->f_files = ULONG_MAX; + sfs->f_ffree = ULONG_MAX; + sfs->f_favail = ULONG_MAX; + sfs->f_fsid = vsn; + sfs->f_flag = flags; + sfs->f_namemax = maxlen; + ret = 0; + } } + if (ret) + __seterrno (); - if (status) - { - availc = availb.QuadPart / (spc*bps); - totalc = totalb.QuadPart / (spc*bps); - freec = freeb.QuadPart / (spc*bps); - } - else - availc = freec; + pop_thread_privilege (); - DWORD vsn, maxlen, flags; - - if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0)) - { - __seterrno (); - return -1; - } - sfs->f_bsize = spc*bps; - sfs->f_frsize = spc*bps; - sfs->f_blocks = totalc; - sfs->f_bfree = freec; - sfs->f_bavail = availc; - sfs->f_files = ULONG_MAX; - sfs->f_ffree = ULONG_MAX; - sfs->f_favail = ULONG_MAX; - sfs->f_fsid = vsn; - sfs->f_flag = flags; - sfs->f_namemax = maxlen; - return 0; + return ret; } extern "C" int @@ -1968,11 +1969,9 @@ seteuid32 (__uid32_t uid) cygsid usersid; user_groups &groups = cygheap->user.groups; - HANDLE ptok, new_token = INVALID_HANDLE_VALUE; + HANDLE new_token = INVALID_HANDLE_VALUE; struct passwd * pw_new; bool token_is_internal, issamesid = false; - char dacl_buf[MAX_DACL_LEN (5)]; - TOKEN_DEFAULT_DACL tdacl = {}; pw_new = internal_getpwuid (uid); if (!wincap.has_security () && pw_new) @@ -1986,29 +1985,24 @@ seteuid32 (__uid32_t uid) return -1; } - RevertToSelf (); - if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok)) - { - __seterrno (); - goto failed_ptok;; - } + cygheap->user.deimpersonate (); /* Verify if the process token is suitable. */ - if (verify_token (ptok, usersid, groups)) - new_token = ptok; + if (verify_token (hProcToken, usersid, groups)) + new_token = hProcToken; /* Verify if the external token is suitable */ else if (cygheap->user.external_token != NO_IMPERSONATION && verify_token (cygheap->user.external_token, usersid, groups)) new_token = cygheap->user.external_token; /* Verify if the current token (internal or former external) is suitable */ - else if (cygheap->user.current_token != NO_IMPERSONATION - && cygheap->user.current_token != cygheap->user.external_token - && verify_token (cygheap->user.current_token, usersid, groups, + else if (cygheap->user.curr_primary_token != NO_IMPERSONATION + && cygheap->user.curr_primary_token != cygheap->user.external_token + && verify_token (cygheap->user.curr_primary_token, usersid, groups, &token_is_internal)) - new_token = cygheap->user.current_token; + new_token = cygheap->user.curr_primary_token; /* Verify if the internal token is suitable */ else if (cygheap->user.internal_token != NO_IMPERSONATION - && cygheap->user.internal_token != cygheap->user.current_token + && cygheap->user.internal_token != cygheap->user.curr_primary_token && verify_token (cygheap->user.internal_token, usersid, groups, &token_is_internal)) new_token = cygheap->user.internal_token; @@ -2026,7 +2020,10 @@ seteuid32 (__uid32_t uid) debug_printf ("create token failed, try subauthentication."); new_token = subauth (pw_new); if (new_token == INVALID_HANDLE_VALUE) - goto failed; + { + cygheap->user.reimpersonate (); + return -1; + } } /* Keep at most one internal token */ if (cygheap->user.internal_token != NO_IMPERSONATION) @@ -2034,17 +2031,7 @@ seteuid32 (__uid32_t uid) cygheap->user.internal_token = new_token; } - /* Set process def dacl to allow access to impersonated token */ - if (sec_acl ((PACL) dacl_buf, true, true, usersid)) - { - tdacl.DefaultDacl = (PACL) dacl_buf; - if (!SetTokenInformation (ptok, TokenDefaultDacl, - &tdacl, sizeof dacl_buf)) - debug_printf ("SetTokenInformation" - "(TokenDefaultDacl), %E"); - } - - if (new_token != ptok) + if (new_token != hProcToken) { /* Avoid having HKCU use default user */ char name[128]; @@ -2053,26 +2040,48 @@ seteuid32 (__uid32_t uid) /* Try setting owner to same value as user. */ if (!SetTokenInformation (new_token, TokenOwner, &usersid, sizeof usersid)) - debug_printf ("SetTokenInformation(user.token, " - "TokenOwner), %E"); + debug_printf ("SetTokenInformation(user.token, TokenOwner), %E"); /* Try setting primary group in token to current group */ if (!SetTokenInformation (new_token, TokenPrimaryGroup, &groups.pgsid, sizeof (cygsid))) - debug_printf ("SetTokenInformation(user.token, " - "TokenPrimaryGroup), %E"); + debug_printf ("SetTokenInformation(user.token, TokenPrimaryGroup), %E"); /* Try setting default DACL */ - if (tdacl.DefaultDacl - && !SetTokenInformation (new_token, TokenDefaultDacl, - &tdacl, sizeof (tdacl))) - debug_printf ("SetTokenInformation (TokenDefaultDacl), %E"); + char dacl_buf[MAX_DACL_LEN (5)]; + if (sec_acl ((PACL) dacl_buf, true, true, usersid)) + { + TOKEN_DEFAULT_DACL tdacl = { (PACL) dacl_buf }; + if (!SetTokenInformation (new_token, TokenDefaultDacl, + &tdacl, sizeof (tdacl))) + debug_printf ("SetTokenInformation (TokenDefaultDacl), %E"); + } } - CloseHandle (ptok); issamesid = (usersid == cygheap->user.sid ()); cygheap->user.set_sid (usersid); - cygheap->user.current_token = new_token == ptok ? NO_IMPERSONATION - : new_token; - cygheap->user.reimpersonate (); + cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION + : new_token; + if (cygheap->user.current_token != NO_IMPERSONATION) + { + CloseHandle (cygheap->user.current_token); + cygheap->user.current_token = NO_IMPERSONATION; + } + if (cygheap->user.curr_primary_token != NO_IMPERSONATION) + { + if (!DuplicateTokenEx (cygheap->user.curr_primary_token, MAXIMUM_ALLOWED, + &sec_none, SecurityImpersonation, + TokenImpersonation, &cygheap->user.current_token)) + { + __seterrno (); + cygheap->user.curr_primary_token = NO_IMPERSONATION; + return -1; + } + set_cygwin_privileges (cygheap->user.current_token); + } + if (!cygheap->user.reimpersonate ()) + { + __seterrno (); + return -1; + } success_9x: cygheap->user.set_name (pw_new->pw_name); @@ -2081,12 +2090,6 @@ success_9x: if (!issamesid) user_shared_initialize (true); return 0; - -failed: - CloseHandle (ptok); -failed_ptok: - cygheap->user.reimpersonate (); - return -1; } extern "C" int @@ -2151,7 +2154,6 @@ setegid32 (__gid32_t gid) user_groups * groups = &cygheap->user.groups; cygsid gsid; - HANDLE ptok; struct __group32 * gr = internal_getgrgid (gid); if (!gsid.getfromgr (gr)) @@ -2162,29 +2164,24 @@ setegid32 (__gid32_t gid) myself->gid = gid; groups->update_pgrp (gsid); - /* If impersonated, update primary group and revert */ if (cygheap->user.issetuid ()) { - if (!SetTokenInformation (cygheap->user.token (), - TokenPrimaryGroup, - &gsid, sizeof gsid)) - debug_printf ("SetTokenInformation(thread, " + /* If impersonated, update impersonation token... */ + if (!SetTokenInformation (cygheap->user.primary_token (), + TokenPrimaryGroup, &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(primary_token, " "TokenPrimaryGroup), %E"); - RevertToSelf (); - } - if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok)) - debug_printf ("OpenProcessToken(), %E"); - else - { - if (!SetTokenInformation (ptok, TokenPrimaryGroup, + if (!SetTokenInformation (cygheap->user.token (), TokenPrimaryGroup, &gsid, sizeof gsid)) - debug_printf ("SetTokenInformation(process, " - "TokenPrimaryGroup), %E"); - CloseHandle (ptok); + debug_printf ("SetTokenInformation(token, TokenPrimaryGroup), %E"); } - if (cygheap->user.issetuid () - && !ImpersonateLoggedOnUser (cygheap->user.token ())) - system_printf ("Impersonating in setegid failed, %E"); + cygheap->user.deimpersonate (); + if (!SetTokenInformation (hProcToken, TokenPrimaryGroup, &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(hProcToken, TokenPrimaryGroup), %E"); + if (!SetTokenInformation (hProcImpToken, TokenPrimaryGroup, &gsid, + sizeof gsid)) + debug_printf ("SetTokenInformation(hProcImpToken, TokenPrimaryGroup), %E"); + cygheap->user.reimpersonate (); return 0; } diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index b023dcaff..0ee38f791 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -44,51 +44,45 @@ cygheap_user::init () if (!wincap.has_security ()) return; - HANDLE ptok; DWORD siz; PSECURITY_DESCRIPTOR psd; - if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT | TOKEN_QUERY, - &ptok)) - { - system_printf ("OpenProcessToken(), %E"); - return; - } - if (!GetTokenInformation (ptok, TokenPrimaryGroup, + if (!GetTokenInformation (hProcToken, TokenPrimaryGroup, &groups.pgsid, sizeof (cygsid), &siz)) system_printf ("GetTokenInformation (TokenPrimaryGroup), %E"); /* Get the SID from current process and store it in effec_cygsid */ - if (!GetTokenInformation (ptok, TokenUser, &effec_cygsid, sizeof (cygsid), &siz)) + if (!GetTokenInformation (hProcToken, TokenUser, &effec_cygsid, + sizeof (cygsid), &siz)) { system_printf ("GetTokenInformation (TokenUser), %E"); - goto out; + return; } /* Set token owner to the same value as token user */ - if (!SetTokenInformation (ptok, TokenOwner, &effec_cygsid, sizeof (cygsid))) + if (!SetTokenInformation (hProcToken, TokenOwner, &effec_cygsid, + sizeof (cygsid))) debug_printf ("SetTokenInformation(TokenOwner), %E"); /* Standard way to build a security descriptor with the usual DACL */ char sa_buf[1024]; - psd = (PSECURITY_DESCRIPTOR) (sec_user_nih (sa_buf, sid()))->lpSecurityDescriptor; + psd = (PSECURITY_DESCRIPTOR) + (sec_user_nih (sa_buf, sid()))->lpSecurityDescriptor; BOOL acl_exists, dummy; TOKEN_DEFAULT_DACL dacl; - if (GetSecurityDescriptorDacl (psd, &acl_exists, - &dacl.DefaultDacl, &dummy) + if (GetSecurityDescriptorDacl (psd, &acl_exists, &dacl.DefaultDacl, &dummy) && acl_exists && dacl.DefaultDacl) { /* Set the default DACL and the process DACL */ - if (!SetTokenInformation (ptok, TokenDefaultDacl, &dacl, sizeof (dacl))) + if (!SetTokenInformation (hProcToken, TokenDefaultDacl, &dacl, + sizeof (dacl))) system_printf ("SetTokenInformation (TokenDefaultDacl), %E"); if (!SetKernelObjectSecurity (hMainProc, DACL_SECURITY_INFORMATION, psd)) system_printf ("SetKernelObjectSecurity, %E"); } else system_printf("Cannot get dacl, %E"); - out: - CloseHandle (ptok); } void @@ -115,17 +109,17 @@ internal_getlogin (cygheap_user &user) cygsid gsid; if (gsid.getfromgr (internal_getgrgid (pw->pw_gid))) { - HANDLE ptok; - if (gsid != user.groups.pgsid - && OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok)) + if (gsid != user.groups.pgsid) { /* Set primary group to the group in /etc/passwd. */ - if (!SetTokenInformation (ptok, TokenPrimaryGroup, + if (!SetTokenInformation (hProcToken, TokenPrimaryGroup, + &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(TokenPrimaryGroup), %E"); + if (!SetTokenInformation (hProcImpToken, TokenPrimaryGroup, &gsid, sizeof gsid)) debug_printf ("SetTokenInformation(TokenPrimaryGroup), %E"); else user.groups.pgsid = gsid; - CloseHandle (ptok); } } else @@ -162,6 +156,7 @@ uinfo_init () cygheap->user.saved_gid = cygheap->user.real_gid = myself->gid; cygheap->user.external_token = NO_IMPERSONATION; cygheap->user.internal_token = NO_IMPERSONATION; + cygheap->user.curr_primary_token = NO_IMPERSONATION; cygheap->user.current_token = NO_IMPERSONATION; cygheap->user.set_saved_sid (); /* Update the original sid */ } diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index 515298ba2..068ffdf78 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -343,6 +343,8 @@ extern bool display_title; extern HANDLE hMainThread; extern HANDLE hMainProc; +extern HANDLE hProcToken; +extern HANDLE hProcImpToken; extern bool cygwin_testing;