* 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:
Corinna Vinschen 2009-10-13 10:23:31 +00:00
parent 6c41e710c9
commit 0191627a26
9 changed files with 122 additions and 16 deletions

View File

@ -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> 2009-10-13 Eric Blake <ebb9@byu.net>
* hires.h (hires_ms): Change initime_us to initime_ns, with 10x * hires.h (hires_ms): Change initime_us to initime_ns, with 10x

View File

@ -108,6 +108,9 @@ public:
HANDLE internal_token; HANDLE internal_token;
HANDLE curr_primary_token; HANDLE curr_primary_token;
HANDLE curr_imp_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 /* CGF 2002-06-27. I removed the initializaton from this constructor
since this class is always allocated statically. That means that everything since this class is always allocated statically. That means that everything

View File

@ -415,6 +415,13 @@ cygwin_internal (cygwin_getinfo_types t, ...)
int useTerminateProcess = va_arg (arg, int); int useTerminateProcess = va_arg (arg, int);
exit_process (status, !!useTerminateProcess); /* no return */ 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: default:
break; break;

View File

@ -143,9 +143,17 @@ typedef enum
CW_SET_DOS_FILE_WARNING, CW_SET_DOS_FILE_WARNING,
CW_SET_PRIV_KEY, CW_SET_PRIV_KEY,
CW_SETERRNO, CW_SETERRNO,
CW_EXIT_PROCESS CW_EXIT_PROCESS,
CW_SET_EXTERNAL_TOKEN
} cygwin_getinfo_types; } 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 */ #define CW_NEXTPID 0x80000000 /* or with pid to get next one */
unsigned long cygwin_internal (cygwin_getinfo_types, ...); unsigned long cygwin_internal (cygwin_getinfo_types, ...);

View File

@ -30,11 +30,19 @@ details. */
#include "cygserver_setpwd.h" #include "cygserver_setpwd.h"
#include <cygwin/version.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 extern "C" void
cygwin_set_impersonation_token (const HANDLE hToken) cygwin_set_impersonation_token (const HANDLE hToken)
{ {
debug_printf ("set_impersonation_token (%d)", hToken); set_imp_token (hToken, CW_TOKEN_IMPERSONATION);
cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken;
} }
void void

View File

@ -366,6 +366,8 @@ extern "C" int acl32 (const char *, int, int, __acl32 *);
int getacl (HANDLE, path_conv &, int, __acl32 *); int getacl (HANDLE, path_conv &, int, __acl32 *);
int setacl (HANDLE, path_conv &, int, __acl32 *, bool &); 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. */ /* Function creating a token by calling NtCreateToken. */
HANDLE create_token (cygsid &usersid, user_groups &groups, struct passwd * pw); HANDLE create_token (cygsid &usersid, user_groups &groups, struct passwd * pw);
/* LSA authentication function. */ /* LSA authentication function. */

View File

@ -537,7 +537,8 @@ loop:
if (!cygheap->user.issetuid () if (!cygheap->user.issetuid ()
|| (cygheap->user.saved_uid == cygheap->user.real_uid || (cygheap->user.saved_uid == cygheap->user.real_uid
&& cygheap->user.saved_gid == cygheap->user.real_gid && 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 */ rc = CreateProcessW (runpath, /* image name - with full path */
wone_line, /* what was passed to exec */ wone_line, /* what was passed to exec */
@ -571,7 +572,8 @@ loop:
risk, but we don't want to disable this behaviour for older risk, but we don't want to disable this behaviour for older
OSes because it's still heavily used by some users. They have OSes because it's still heavily used by some users. They have
been warned. */ been warned. */
if (wcscasecmp (wstname, L"WinSta0") != 0) if (!cygheap->user.setuid_to_restricted
&& wcscasecmp (wstname, L"WinSta0") != 0)
{ {
WCHAR sid[128]; WCHAR sid[128];

View File

@ -2664,7 +2664,28 @@ seteuid32 (__uid32_t uid)
debug_printf ("uid: %u myself->uid: %u myself->gid: %u", debug_printf ("uid: %u myself->uid: %u myself->gid: %u",
uid, myself->uid, myself->gid); 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"); debug_printf ("Nothing happens");
return 0; return 0;
@ -2686,6 +2707,22 @@ seteuid32 (__uid32_t uid)
cygheap->user.deimpersonate (); cygheap->user.deimpersonate ();
/* Verify if the process token is suitable. */ /* 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 /* 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. 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 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 Therefore we try this shortcut now. When switching back to the
privileged user, we probably always want a correct (aka original) privileged user, we probably always want a correct (aka original)
user token for this privileged user, not only in sshd. */ 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
|| verify_token (hProcToken, usersid, groups)) && usersid == cygheap->user.saved_sid ())
|| verify_token (hProcToken, usersid, groups))
new_token = hProcToken; new_token = hProcToken;
/* Verify if the external token is suitable */ /* Verify if the external token is suitable */
else if (cygheap->user.external_token != NO_IMPERSONATION else if (cygheap->user.external_token != NO_IMPERSONATION
@ -2763,9 +2801,12 @@ seteuid32 (__uid32_t uid)
if (new_token != hProcToken) if (new_token != hProcToken)
{ {
/* Avoid having HKCU use default user */ if (!request_restricted_uid_switch)
WCHAR name[128]; {
load_registry_hive (usersid.string (name)); /* Avoid having HKCU use default user */
WCHAR name[128];
load_registry_hive (usersid.string (name));
}
/* Try setting owner to same value as user. */ /* Try setting owner to same value as user. */
if (!SetTokenInformation (new_token, TokenOwner, if (!SetTokenInformation (new_token, TokenOwner,
@ -2790,6 +2831,8 @@ seteuid32 (__uid32_t uid)
cygheap->user.set_sid (usersid); cygheap->user.set_sid (usersid);
cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION cygheap->user.curr_primary_token = new_token == hProcToken ? NO_IMPERSONATION
: new_token; : new_token;
cygheap->user.curr_token_is_restricted = false;
cygheap->user.setuid_to_restricted = false;
if (cygheap->user.curr_imp_token != NO_IMPERSONATION) if (cygheap->user.curr_imp_token != NO_IMPERSONATION)
{ {
CloseHandle (cygheap->user.curr_imp_token); CloseHandle (cygheap->user.curr_imp_token);
@ -2797,14 +2840,19 @@ seteuid32 (__uid32_t uid)
} }
if (cygheap->user.curr_primary_token != NO_IMPERSONATION) if (cygheap->user.curr_primary_token != NO_IMPERSONATION)
{ {
if (!DuplicateTokenEx (cygheap->user.curr_primary_token, MAXIMUM_ALLOWED, /* HANDLE_FLAG_INHERIT may be missing in external token. */
&sec_none, SecurityImpersonation, if (!SetHandleInformation (cygheap->user.curr_primary_token,
TokenImpersonation, &cygheap->user.curr_imp_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 (); __seterrno ();
cygheap->user.curr_primary_token = NO_IMPERSONATION; cygheap->user.curr_primary_token = NO_IMPERSONATION;
return -1; 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_primary_token);
set_cygwin_privileges (cygheap->user.curr_imp_token); set_cygwin_privileges (cygheap->user.curr_imp_token);
} }
@ -2835,7 +2883,11 @@ setuid32 (__uid32_t uid)
{ {
int ret = seteuid32 (uid); int ret = seteuid32 (uid);
if (!ret) 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); debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
return ret; return ret;
} }

View File

@ -136,7 +136,8 @@ uinfo_init ()
else if (cygheap->user.issetuid () else if (cygheap->user.issetuid ()
&& cygheap->user.saved_uid == cygheap->user.real_uid && cygheap->user.saved_uid == cygheap->user.real_uid
&& cygheap->user.saved_gid == cygheap->user.real_gid && cygheap->user.saved_gid == cygheap->user.real_gid
&& !cygheap->user.groups.issetgroups ()) && !cygheap->user.groups.issetgroups ()
&& !cygheap->user.setuid_to_restricted)
{ {
cygheap->user.reimpersonate (); cygheap->user.reimpersonate ();
return; return;
@ -150,6 +151,9 @@ uinfo_init ()
cygheap->user.internal_token = NO_IMPERSONATION; cygheap->user.internal_token = NO_IMPERSONATION;
cygheap->user.curr_primary_token = NO_IMPERSONATION; cygheap->user.curr_primary_token = NO_IMPERSONATION;
cygheap->user.curr_imp_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.set_saved_sid (); /* Update the original sid */
cygheap->user.reimpersonate (); cygheap->user.reimpersonate ();
} }