From 0191627a260aee41f464d3e0a2c2d4d067cad5ad Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 13 Oct 2009 10:23:31 +0000 Subject: [PATCH] * include/sys/cygwin.h: Add new cygwin_getinfo_type CW_SET_EXTERNAL_TOKEN. Add new enum CW_TOKEN_IMPERSONATION, CW_TOKEN_RESTRICTED. * cygheap.h (cyguser): New flags ext_token_is_restricted, curr_token_is_restricted and setuid_to_restricted. * external.cc (cygwin_internal): Add CW_SET_EXTERNAL_TOKEN. * sec_auth.cc (set_imp_token): New function. (cygwin_set_impersonation_token): Call set_imp_token (). * security.h (set_imp_token): New prototype. * spawn.cc (spawn_guts): Use CreateProcessAsUserW if restricted token was enabled by setuid(). Do not create new window station in this case. * syscalls.cc (seteuid32): Add handling of restricted external tokens. Set HANDLE_FLAG_INHERIT for primary token. (setuid32): Set setuid_to_restricted flag. * uinfo.cc (uinfo_init): Do not reimpersonate if restricted token was enabled by setuid (). Initialize user.*_restricted flags. --- winsup/cygwin/ChangeLog | 20 +++++++++ winsup/cygwin/cygheap.h | 3 ++ winsup/cygwin/external.cc | 7 +++ winsup/cygwin/include/sys/cygwin.h | 10 ++++- winsup/cygwin/sec_auth.cc | 12 ++++- winsup/cygwin/security.h | 2 + winsup/cygwin/spawn.cc | 6 ++- winsup/cygwin/syscalls.cc | 72 +++++++++++++++++++++++++----- winsup/cygwin/uinfo.cc | 6 ++- 9 files changed, 122 insertions(+), 16 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index c4128e916..8e0226bf6 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,23 @@ +2009-10-13 Christian Franke + Corinna Vinschen + + * include/sys/cygwin.h: Add new cygwin_getinfo_type + CW_SET_EXTERNAL_TOKEN. + Add new enum CW_TOKEN_IMPERSONATION, CW_TOKEN_RESTRICTED. + * cygheap.h (cyguser): New flags ext_token_is_restricted, + curr_token_is_restricted and setuid_to_restricted. + * external.cc (cygwin_internal): Add CW_SET_EXTERNAL_TOKEN. + * sec_auth.cc (set_imp_token): New function. + (cygwin_set_impersonation_token): Call set_imp_token (). + * security.h (set_imp_token): New prototype. + * spawn.cc (spawn_guts): Use CreateProcessAsUserW if restricted token + was enabled by setuid(). Do not create new window station in this case. + * syscalls.cc (seteuid32): Add handling of restricted external tokens. + Set HANDLE_FLAG_INHERIT for primary token. + (setuid32): Set setuid_to_restricted flag. + * uinfo.cc (uinfo_init): Do not reimpersonate if restricted token was + enabled by setuid (). Initialize user.*_restricted flags. + 2009-10-13 Eric Blake * hires.h (hires_ms): Change initime_us to initime_ns, with 10x diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 7a39de627..b8ffd3fbe 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -108,6 +108,9 @@ public: HANDLE internal_token; HANDLE curr_primary_token; HANDLE curr_imp_token; + bool ext_token_is_restricted; /* external_token is restricted token */ + bool curr_token_is_restricted; /* curr_primary_token is restricted token */ + bool setuid_to_restricted; /* switch to restricted token by setuid () */ /* CGF 2002-06-27. I removed the initializaton from this constructor since this class is always allocated statically. That means that everything diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index 23e108804..d42971d2b 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -415,6 +415,13 @@ cygwin_internal (cygwin_getinfo_types t, ...) int useTerminateProcess = va_arg (arg, int); exit_process (status, !!useTerminateProcess); /* no return */ } + case CW_SET_EXTERNAL_TOKEN: + { + HANDLE token = va_arg (arg, HANDLE); + int type = va_arg (arg, int); + set_imp_token (token, type); + return 0; + } default: break; diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index 5f38278d7..ce9bebfdd 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -143,9 +143,17 @@ typedef enum CW_SET_DOS_FILE_WARNING, CW_SET_PRIV_KEY, CW_SETERRNO, - CW_EXIT_PROCESS + CW_EXIT_PROCESS, + CW_SET_EXTERNAL_TOKEN } cygwin_getinfo_types; +/* Token type for CW_SET_EXTERNAL_TOKEN */ +enum +{ + CW_TOKEN_IMPERSONATION = 0, + CW_TOKEN_RESTRICTED = 1 +}; + #define CW_NEXTPID 0x80000000 /* or with pid to get next one */ unsigned long cygwin_internal (cygwin_getinfo_types, ...); diff --git a/winsup/cygwin/sec_auth.cc b/winsup/cygwin/sec_auth.cc index 028b5a8c7..34e571f29 100644 --- a/winsup/cygwin/sec_auth.cc +++ b/winsup/cygwin/sec_auth.cc @@ -30,11 +30,19 @@ details. */ #include "cygserver_setpwd.h" #include +void +set_imp_token (HANDLE token, int type) +{ + debug_printf ("set_imp_token (%d, %d)", token, type); + cygheap->user.external_token = (token == INVALID_HANDLE_VALUE + ? NO_IMPERSONATION : token); + cygheap->user.ext_token_is_restricted = (type == CW_TOKEN_RESTRICTED); +} + extern "C" void cygwin_set_impersonation_token (const HANDLE hToken) { - debug_printf ("set_impersonation_token (%d)", hToken); - cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken; + set_imp_token (hToken, CW_TOKEN_IMPERSONATION); } void diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index 781def9d5..d0cb226a9 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -366,6 +366,8 @@ extern "C" int acl32 (const char *, int, int, __acl32 *); int getacl (HANDLE, path_conv &, int, __acl32 *); int setacl (HANDLE, path_conv &, int, __acl32 *, bool &); +/* Set impersonation or restricted token. */ +void set_imp_token (HANDLE token, int type); /* Function creating a token by calling NtCreateToken. */ HANDLE create_token (cygsid &usersid, user_groups &groups, struct passwd * pw); /* LSA authentication function. */ diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 5c86c4b5c..a6ac9f0e7 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -537,7 +537,8 @@ loop: if (!cygheap->user.issetuid () || (cygheap->user.saved_uid == cygheap->user.real_uid && cygheap->user.saved_gid == cygheap->user.real_gid - && !cygheap->user.groups.issetgroups ())) + && !cygheap->user.groups.issetgroups () + && !cygheap->user.setuid_to_restricted)) { rc = CreateProcessW (runpath, /* image name - with full path */ wone_line, /* what was passed to exec */ @@ -571,7 +572,8 @@ loop: risk, but we don't want to disable this behaviour for older OSes because it's still heavily used by some users. They have been warned. */ - if (wcscasecmp (wstname, L"WinSta0") != 0) + if (!cygheap->user.setuid_to_restricted + && wcscasecmp (wstname, L"WinSta0") != 0) { WCHAR sid[128]; diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 1529cb66b..aa112c493 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -2664,7 +2664,28 @@ seteuid32 (__uid32_t uid) debug_printf ("uid: %u myself->uid: %u myself->gid: %u", uid, myself->uid, myself->gid); - if (uid == myself->uid && !cygheap->user.groups.ischanged) + /* Same uid as we're just running under is usually a no-op. + + Except we have an external token which is a restricted token. Or, + the external token is NULL, but the current impersonation token is + a restricted token. This allows to restrict user rights temporarily + like this: + + cygwin_internal(CW_SET_EXTERNAL_TOKEN, restricted_token, + CW_TOKEN_RESTRICTED); + setuid (getuid ()); + [...do stuff with restricted rights...] + cygwin_internal(CW_SET_EXTERNAL_TOKEN, INVALID_HANDLE_VALUE, + CW_TOKEN_RESTRICTED); + setuid (getuid ()); + + Note that using the current uid is a requirement! Starting with Windows + Vista, we have restricted tokens galore (UAC), so this is really just + a special case to restict your own processes to lesser rights. */ + bool request_restricted_uid_switch = (uid == myself->uid + && cygheap->user.ext_token_is_restricted); + if (uid == myself->uid && !cygheap->user.groups.ischanged + && !request_restricted_uid_switch) { debug_printf ("Nothing happens"); return 0; @@ -2686,6 +2707,22 @@ seteuid32 (__uid32_t uid) cygheap->user.deimpersonate (); /* Verify if the process token is suitable. */ + /* First of all, skip all checks if a switch to a restricted token has been + requested, or if trying to switch back from it. */ + if (request_restricted_uid_switch) + { + if (cygheap->user.external_token != NO_IMPERSONATION) + { + debug_printf ("Switch to restricted token"); + new_token = cygheap->user.external_token; + } + else + { + debug_printf ("Switch back from restricted token"); + new_token = hProcToken; + cygheap->user.ext_token_is_restricted = false; + } + } /* TODO, CV 2008-11-25: The check against saved_sid is a kludge and a shortcut. We must check if it's really feasible in the long run. The reason to add this shortcut is this: sshd switches back to the @@ -2701,8 +2738,9 @@ seteuid32 (__uid32_t uid) Therefore we try this shortcut now. When switching back to the privileged user, we probably always want a correct (aka original) user token for this privileged user, not only in sshd. */ - if ((uid == cygheap->user.saved_uid && usersid == cygheap->user.saved_sid ()) - || verify_token (hProcToken, usersid, groups)) + else if ((uid == cygheap->user.saved_uid + && usersid == cygheap->user.saved_sid ()) + || verify_token (hProcToken, usersid, groups)) new_token = hProcToken; /* Verify if the external token is suitable */ else if (cygheap->user.external_token != NO_IMPERSONATION @@ -2763,9 +2801,12 @@ seteuid32 (__uid32_t uid) if (new_token != hProcToken) { - /* Avoid having HKCU use default user */ - WCHAR name[128]; - load_registry_hive (usersid.string (name)); + if (!request_restricted_uid_switch) + { + /* Avoid having HKCU use default user */ + WCHAR name[128]; + load_registry_hive (usersid.string (name)); + } /* Try setting owner to same value as user. */ if (!SetTokenInformation (new_token, TokenOwner, @@ -2790,6 +2831,8 @@ seteuid32 (__uid32_t uid) cygheap->user.set_sid (usersid); cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION : new_token; + cygheap->user.curr_token_is_restricted = false; + cygheap->user.setuid_to_restricted = false; if (cygheap->user.curr_imp_token != NO_IMPERSONATION) { CloseHandle (cygheap->user.curr_imp_token); @@ -2797,14 +2840,19 @@ seteuid32 (__uid32_t uid) } if (cygheap->user.curr_primary_token != NO_IMPERSONATION) { - if (!DuplicateTokenEx (cygheap->user.curr_primary_token, MAXIMUM_ALLOWED, - &sec_none, SecurityImpersonation, - TokenImpersonation, &cygheap->user.curr_imp_token)) + /* HANDLE_FLAG_INHERIT may be missing in external token. */ + if (!SetHandleInformation (cygheap->user.curr_primary_token, + HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) + || !DuplicateTokenEx (cygheap->user.curr_primary_token, + MAXIMUM_ALLOWED, &sec_none, + SecurityImpersonation, TokenImpersonation, + &cygheap->user.curr_imp_token)) { __seterrno (); cygheap->user.curr_primary_token = NO_IMPERSONATION; return -1; } + cygheap->user.curr_token_is_restricted = request_restricted_uid_switch; set_cygwin_privileges (cygheap->user.curr_primary_token); set_cygwin_privileges (cygheap->user.curr_imp_token); } @@ -2835,7 +2883,11 @@ setuid32 (__uid32_t uid) { int ret = seteuid32 (uid); if (!ret) - cygheap->user.real_uid = myself->uid; + { + cygheap->user.real_uid = myself->uid; + /* If restricted token, forget original privileges on exec (). */ + cygheap->user.setuid_to_restricted = cygheap->user.curr_token_is_restricted; + } debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid); return ret; } diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 8adfd37b8..a480f9979 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -136,7 +136,8 @@ uinfo_init () else if (cygheap->user.issetuid () && cygheap->user.saved_uid == cygheap->user.real_uid && cygheap->user.saved_gid == cygheap->user.real_gid - && !cygheap->user.groups.issetgroups ()) + && !cygheap->user.groups.issetgroups () + && !cygheap->user.setuid_to_restricted) { cygheap->user.reimpersonate (); return; @@ -150,6 +151,9 @@ uinfo_init () cygheap->user.internal_token = NO_IMPERSONATION; cygheap->user.curr_primary_token = NO_IMPERSONATION; cygheap->user.curr_imp_token = NO_IMPERSONATION; + cygheap->user.ext_token_is_restricted = false; + cygheap->user.curr_token_is_restricted = false; + cygheap->user.setuid_to_restricted = false; cygheap->user.set_saved_sid (); /* Update the original sid */ cygheap->user.reimpersonate (); }