* 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.
This commit is contained in:
		| @@ -1,3 +1,23 @@ | ||||
| 2009-10-13  Christian Franke  <franke@computer.org> | ||||
| 	    Corinna Vinschen  <corinna@vinschen.de> | ||||
|  | ||||
| 	* 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  <ebb9@byu.net> | ||||
|  | ||||
| 	* hires.h (hires_ms): Change initime_us to initime_ns, with 10x | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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, ...); | ||||
|  | ||||
|   | ||||
| @@ -30,11 +30,19 @@ details. */ | ||||
| #include "cygserver_setpwd.h" | ||||
| #include <cygwin/version.h> | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -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. */ | ||||
|   | ||||
| @@ -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]; | ||||
|  | ||||
|   | ||||
| @@ -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,7 +2738,8 @@ 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 ()) | ||||
|   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 */ | ||||
| @@ -2762,10 +2800,13 @@ seteuid32 (__uid32_t uid) | ||||
|     } | ||||
|  | ||||
|   if (new_token != hProcToken) | ||||
|     { | ||||
|       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; | ||||
|       /* 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; | ||||
| } | ||||
|   | ||||
| @@ -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 (); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user