From 64b3062937b791faafddc02e9d38102a75b37a7a Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 16 Jun 2000 19:36:07 +0000 Subject: [PATCH] * cygwin.din: Define symbols for `cygwin_logon_user' and `cygwin_set_impersonation_token'. * dcrt0.cc (dll_crt0_1): Eliminate superfluous conditional statements. Add load statements for `ImpersonateLoggedOnUser', `LogonUserA' and `RevertToSelf'. * fork.cc (fork): Care for correct impersonation of parent and child process. * security.cc (cygwin_set_impersonation_token): New function. (cygwin_logon_user): Ditto. shared.h (class pinfo): New members `orig_uid', `orig_gid', `real_uid' nad `real_gid'. spawn.cc (spawn_guts): Care for impersonation when starting child process in a different user context. * syscalls.cc (setgid): Call `setegid' now. Set real_gid. (setuid): Call `seteuid' now. Set real_uid. (seteuid): Functionality moved from setuid to here. Care for correct impersonation. (setegid): Functionality moved from setgid to here. * uinfo.cc (uinfo_init): Initialization of additional pinfo members. (getuid): Return real uid. (getgid): Return real gid. (geteuid): Return effective uid. (getegid): Return effective gid. include/sys/cygwin.h: Add prototypes for `cygwin_logon_user' and `cygwin_set_impersonation_token'. include/cygwin/version.h: Bumb API minor version to 22. --- winsup/cygwin/ChangeLog | 31 ++++++ winsup/cygwin/cygwin.din | 2 + winsup/cygwin/dcrt0.cc | 8 +- winsup/cygwin/fork.cc | 34 +++++- winsup/cygwin/include/cygwin/version.h | 7 +- winsup/cygwin/include/sys/cygwin.h | 3 + winsup/cygwin/security.cc | 67 ++++++++++++ winsup/cygwin/shared.h | 9 ++ winsup/cygwin/spawn.cc | 11 ++ winsup/cygwin/syscalls.cc | 140 +++++++++++++++---------- winsup/cygwin/uinfo.cc | 13 ++- 11 files changed, 261 insertions(+), 64 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index a9e3980b0..3324b612a 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,34 @@ +Thu Jun 16 20:55:00 2000 Corinna Vinschen + + * cygwin.din: Define symbols for `cygwin_logon_user' and + `cygwin_set_impersonation_token'. + * dcrt0.cc (dll_crt0_1): Eliminate superfluous conditional + statements. + Add load statements for `ImpersonateLoggedOnUser', `LogonUserA' + and `RevertToSelf'. + * fork.cc (fork): Care for correct impersonation of parent + and child process. + * security.cc (cygwin_set_impersonation_token): New function. + (cygwin_logon_user): Ditto. + shared.h (class pinfo): New members `orig_uid', `orig_gid', + `real_uid' nad `real_gid'. + spawn.cc (spawn_guts): Care for impersonation when starting + child process in a different user context. + * syscalls.cc (setgid): Call `setegid' now. Set real_gid. + (setuid): Call `seteuid' now. Set real_uid. + (seteuid): Functionality moved from setuid to here. Care for + correct impersonation. + (setegid): Functionality moved from setgid to here. + * uinfo.cc (uinfo_init): Initialization of additional pinfo + members. + (getuid): Return real uid. + (getgid): Return real gid. + (geteuid): Return effective uid. + (getegid): Return effective gid. + include/sys/cygwin.h: Add prototypes for `cygwin_logon_user' and + `cygwin_set_impersonation_token'. + include/cygwin/version.h: Bumb API minor version to 22. + Thu Jun 15 18:12:36 2000 Christopher Faylor * path.cc (chdir): Don't set cache to offending chdir. Change comment diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din index a8c0006db..84f75192a 100644 --- a/winsup/cygwin/cygwin.din +++ b/winsup/cygwin/cygwin.din @@ -988,6 +988,8 @@ cygwin32_posix_to_win32_path_list = cygwin_posix_to_win32_path_list cygwin32_split_path = cygwin_split_path cygwin_winpid_to_pid cygwin32_winpid_to_pid = cygwin_winpid_to_pid +cygwin_logon_user +cygwin_set_impersonation_token realpath reent_data DATA getenv diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 4b87905b2..302207cec 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -763,10 +763,9 @@ dll_crt0_1 () /* Flush signals and ensure that signal thread is up and running. Can't do this for noncygwin case since the signal thread is blocked due to LoadLibrary serialization. */ - if (!dynamically_loaded) - sig_send (NULL, __SIGFLUSH); /* also initializes uid, gid */ + sig_send (NULL, __SIGFLUSH); /* also initializes uid, gid */ - if (user_data->main && !dynamically_loaded) + if (user_data->main) exit (user_data->main (argc, argv, *user_data->envptr)); } @@ -1157,10 +1156,12 @@ LoadDLLfunc (GetSidSubAuthority, 8, advapi32) LoadDLLfunc (GetSidSubAuthorityCount, 4, advapi32) LoadDLLfunc (GetTokenInformation, 20, advapi32) LoadDLLfunc (GetUserNameA, 8, advapi32) +LoadDLLfunc (ImpersonateLoggedOnUser, 4, advapi32) LoadDLLfunc (InitializeAcl, 12, advapi32) LoadDLLfunc (InitializeSecurityDescriptor, 8, advapi32) LoadDLLfunc (InitializeSid, 12, advapi32) LoadDLLfunc (IsValidSid, 4, advapi32) +LoadDLLfunc (LogonUserA, 24, advapi32) LoadDLLfunc (LookupAccountNameA, 28, advapi32) LoadDLLfunc (LookupAccountSidA, 28, advapi32) LoadDLLfunc (LookupPrivilegeValueA, 12, advapi32) @@ -1175,6 +1176,7 @@ LoadDLLfunc (RegQueryValueExA, 24, advapi32) LoadDLLfunc (RegSetValueExA, 24, advapi32) LoadDLLfunc (RegisterEventSourceA, 8, advapi32) LoadDLLfunc (ReportEventA, 36, advapi32) +LoadDLLfunc (RevertToSelf, 0, advapi32) LoadDLLfunc (SetKernelObjectSecurity, 12, advapi32) LoadDLLfunc (SetSecurityDescriptorDacl, 16, advapi32) LoadDLLfunc (SetSecurityDescriptorGroup, 12, advapi32) diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index baa5a9599..b0248026b 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -363,10 +363,16 @@ fork () goto cleanup; } + /* Remove impersonation */ + uid_t uid = geteuid(); + if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE) + seteuid (myself->orig_uid); + + char sa_buf[1024]; rc = CreateProcessA (myself->progname, /* image to run */ myself->progname, /* what we send in arg0 */ - &sec_none_nih, /* process security attrs */ - &sec_none_nih, /* thread security attrs */ + allow_ntsec ? sec_user (sa_buf) : &sec_none_nih, + allow_ntsec ? sec_user (sa_buf) : &sec_none_nih, TRUE, /* inherit handles from parent */ c_flags, NULL, /* environment filled in later */ @@ -384,9 +390,16 @@ fork () ForceCloseHandle(subproc_ready); ForceCloseHandle(forker_finished); subproc_ready = forker_finished = NULL; + /* Restore impersonation */ + if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE) + seteuid (uid); return -1; } + /* Restore impersonation */ + if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE) + seteuid (uid); + ProtectHandle (pi.hThread); /* Protect the handle but name it similarly to the way it will be called in subproc handling. */ @@ -410,6 +423,12 @@ fork () memcpy (child->sidbuf, myself->sidbuf, 40); memcpy (child->logsrv, myself->logsrv, 256); memcpy (child->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1); + child->token = myself->token; + child->impersonated = myself->impersonated; + child->orig_uid = myself->orig_uid; + child->orig_gid = myself->orig_gid; + child->real_uid = myself->real_uid; + child->real_gid = myself->real_gid; set_child_mmap_ptr (child); /* Wait for subproc to initialize itself. */ @@ -494,6 +513,17 @@ fork () debug_printf ("self %p, pid %d, ppid %d", myself, x, myself ? myself->ppid : -1); + /* Restore the inheritance state as in parent + Don't call setuid here! The flags are already set. */ + if (myself->impersonated) + { + debug_printf ("Impersonation of child, token: %d", myself->token); + if (myself->token == INVALID_HANDLE_VALUE) + RevertToSelf (); // probably not needed + else if (!ImpersonateLoggedOnUser (myself->token)) + system_printf ("Impersonate for forked child failed: %E"); + } + sync_with_parent ("after longjmp.", TRUE); ProtectHandle (hParent); diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index ffa17d080..2143bbba4 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -106,10 +106,15 @@ details. */ 19: Export fchown, lchown, lacl 20: regsub, inet_network 21: incompatible change to stdio cr/lf and buffering + 22: Export cygwin_logon_user, cygwin_set_impersonation_token. + geteuid, getegid return effective uid/gid. + getuid, getgid return real uid/gid. + seteuid, setegid set only effective uid/gid. + setuid, setgid set effective and real uid/gid. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 21 +#define CYGWIN_VERSION_API_MINOR 22 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index ff891cc10..6354f0f88 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -31,6 +31,9 @@ extern int cygwin_conv_to_full_posix_path (const char *, char *); extern int cygwin_posix_path_list_p (const char *); extern void cygwin_split_path (const char *, char *, char *); +extern HANDLE cygwin_logon_user (const struct passwd *, const char *); +extern void cygwin_set_impersonation_token (const HANDLE); + #ifdef _GNU_H_WINDOWS32_BASE /* included if is included */ extern int cygwin32_attach_handle_to_fd (char *, int, HANDLE, int, int); diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 8537a6db6..c468235fc 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -374,6 +374,73 @@ got_it: return TRUE; } +extern "C" +void +cygwin_set_impersonation_token (const HANDLE hToken) +{ + debug_printf ("set_impersonation_token (%d)", hToken); + if (myself->token != hToken) + { + if (myself->token != INVALID_HANDLE_VALUE) + CloseHandle (myself->token); + myself->token = hToken; + myself->impersonated = FALSE; + } +} + +extern "C" +HANDLE +cygwin_logon_user (const struct passwd *pw, const char *password) +{ + if (os_being_run != winNT) + { + set_errno (ENOSYS); + return INVALID_HANDLE_VALUE; + } + if (!pw) + { + set_errno (EINVAL); + return INVALID_HANDLE_VALUE; + } + + char *c, *nt_user, *nt_domain = NULL; + char usernamebuf[256]; + HANDLE hToken; + + strcpy (usernamebuf, pw->pw_name); + if (pw->pw_gecos) + { + if ((c = strstr (pw->pw_gecos, "U-")) != NULL && + (c == pw->pw_gecos || c[-1] == ',')) + { + usernamebuf[0] = '\0'; + strncat (usernamebuf, c + 2, 255); + if ((c = strchr (usernamebuf, ',')) != NULL) + *c = '\0'; + } + } + nt_user = usernamebuf; + if ((c = strchr (nt_user, '\\')) != NULL) + { + nt_domain = nt_user; + *c = '\0'; + nt_user = c + 1; + } + if (! LogonUserA (nt_user, nt_domain, (char *) password, + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, + &hToken) + || !SetHandleInformation (hToken, + HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + { + __seterrno (); + return INVALID_HANDLE_VALUE; + } + debug_printf ("%d = logon_user(%s,...)", hToken, pw->pw_name); + return hToken; +} + /* read_sd reads a security descriptor from a file. In case of error, -1 is returned and errno is set. If sd_buf is too small, 0 is returned and sd_size diff --git a/winsup/cygwin/shared.h b/winsup/cygwin/shared.h index 5c6eff5f1..1b594d5d7 100644 --- a/winsup/cygwin/shared.h +++ b/winsup/cygwin/shared.h @@ -93,6 +93,15 @@ class pinfo char logsrv[256]; /* Logon server, may be fully qualified DNS name */ char domain[MAX_COMPUTERNAME_LENGTH+1]; /* Logon domain of the user */ + /* token is needed if sexec should be called. It can be set by a call + to `set_impersonation_token()'. */ + HANDLE token; + BOOL impersonated; + uid_t orig_uid; /* Remains intact also after impersonation */ + uid_t orig_gid; /* Ditto */ + uid_t real_uid; /* Remains intact on seteuid, replaced by setuid */ + gid_t real_gid; /* Ditto */ + /* Non-zero if process was stopped by a signal. */ char stopsig; diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 1ff08e4bc..65e3bd665 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -503,6 +503,9 @@ skip_arg_parsing: /* Preallocated buffer for `sec_user' call */ char sa_buf[1024]; + if (!hToken && myself->token != INVALID_HANDLE_VALUE) + hToken = myself->token; + if (hToken) { /* allow the child to interact with our window station/desktop */ @@ -535,6 +538,11 @@ skip_arg_parsing: else system_printf ("GetTokenInformation: %E"); + /* Remove impersonation */ + uid_t uid = geteuid(); + if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE) + seteuid (myself->orig_uid); + rc = CreateProcessAsUser (hToken, real_path, /* image name - with full path */ one_line.buf, /* what was passed to exec */ @@ -550,6 +558,9 @@ skip_arg_parsing: 0, /* use current drive/directory */ &si, &pi); + /* Restore impersonation */ + if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE) + seteuid (uid); } else rc = CreateProcessA (real_path, /* image name - with full path */ diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index d2eeab0d4..6c40cc153 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1796,6 +1796,92 @@ mknod () extern "C" int setgid (gid_t gid) +{ + int ret = setegid (gid); + if (!ret) + myself->real_gid = myself->gid; + return ret; +} + +/* setuid: POSIX 4.2.2.1 */ +extern "C" +int +setuid (uid_t uid) +{ + int ret = seteuid (uid); + if (!ret) + myself->real_uid = myself->uid; + debug_printf ("real: %d, effective: %d", myself->real_uid, myself->uid); + return ret; +} + +extern char *internal_getlogin (struct pinfo *pi); + +/* seteuid: standards? */ +extern "C" +int +seteuid (uid_t uid) +{ + if (os_being_run == winNT) + { + if (uid != (uid_t) -1) + { + struct passwd *pw_new = getpwuid (uid); + if (!pw_new) + { + set_errno (EINVAL); + return -1; + } + + if (uid != myself->uid) + if (uid == myself->orig_uid) + { + debug_printf ("RevertToSelf() (uid == orig_uid, token=%d)", + myself->token); + RevertToSelf(); + if (myself->token != INVALID_HANDLE_VALUE) + myself->impersonated = FALSE; + } + else if (!myself->impersonated) + { + debug_printf ("Impersonate(uid == %d)", uid); + RevertToSelf(); + if (myself->token != INVALID_HANDLE_VALUE) + if (!ImpersonateLoggedOnUser (myself->token)) + system_printf ("Impersonate(%d) in set(e)uid failed: %E", + myself->token); + else + myself->impersonated = TRUE; + } + + struct pinfo pi; + pi.psid = (PSID) pi.sidbuf; + struct passwd *pw_cur = getpwnam (internal_getlogin (&pi)); + if (pw_cur != pw_new) + { + debug_printf ("Diffs!!! token: %d, cur: %d, new: %d, orig: %d", + myself->token, pw_cur->pw_uid, + pw_new->pw_uid, myself->orig_uid); + set_errno (EPERM); + return -1; + } + myself->uid = uid; + strcpy (myself->username, pi.username); + CopySid (40, myself->psid, pi.psid); + strcpy (myself->logsrv, pi.logsrv); + strcpy (myself->domain, pi.domain); + } + } + else + set_errno (ENOSYS); + debug_printf ("real: %d, effective: %d", myself->real_uid, myself->uid); + return 0; +} + +/* setegid: from System V. */ +extern "C" +int +setegid (gid_t gid) { if (os_being_run == winNT) { @@ -1814,60 +1900,6 @@ setgid (gid_t gid) return 0; } -extern char *internal_getlogin (struct pinfo *pi); - -/* setuid: POSIX 4.2.2.1 */ -extern "C" -int -setuid (uid_t uid) -{ - if (os_being_run == winNT) - { - if (uid != (uid_t) -1) - { - struct passwd *pw_new = getpwuid (uid); - if (!pw_new) - { - set_errno (EINVAL); - return -1; - } - - struct pinfo pi; - pi.psid = (PSID) pi.sidbuf; - struct passwd *pw_cur = getpwnam (internal_getlogin (&pi)); - if (pw_cur != pw_new) - { - set_errno (EPERM); - return -1; - } - myself->uid = uid; - strcpy (myself->username, pi.username); - CopySid (40, myself->psid, pi.psid); - strcpy (myself->logsrv, pi.logsrv); - strcpy (myself->domain, pi.domain); - } - } - else - set_errno (ENOSYS); - return 0; -} - -/* seteuid: standards? */ -extern "C" -int -seteuid (uid_t uid) -{ - return setuid (uid); -} - -/* setegid: from System V. */ -extern "C" -int -setegid (gid_t gid) -{ - return setgid (gid); -} - /* chroot: privileged Unix system call. */ extern "C" int diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 90bb72727..b50f155a2 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -126,6 +126,11 @@ uinfo_init () myself->uid = DEFAULT_UID; myself->gid = DEFAULT_GID; } + /* Set to non impersonated value. */ + myself->token = INVALID_HANDLE_VALUE; + myself->impersonated = TRUE; + myself->orig_uid = myself->real_uid = myself->uid; + myself->orig_gid = myself->real_gid = myself->gid; } extern "C" char * @@ -143,25 +148,25 @@ getlogin (void) extern "C" uid_t getuid (void) { - return myself->uid; + return myself->real_uid; } extern "C" gid_t getgid (void) { - return myself->gid; + return myself->real_gid; } extern "C" uid_t geteuid (void) { - return getuid (); + return myself->uid; } extern "C" gid_t getegid (void) { - return getgid (); + return myself->gid; } /* Not quite right - cuserid can change, getlogin can't */