From 70249d5687c6020064b70431e115fe1c0161cfa3 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 30 Jun 2003 13:07:36 +0000 Subject: [PATCH] * cygheap.h (enum impersonation): New enum. (cygheap_user::token): Delete. (cygheap_user::impersonated): Delete. (cygheap_user::external_token): New member. (cygheap_user::internal_token): New member. (cygheap_user::impersonation_state): New member. (cygheap_user::issetuid): Modify. (cygheap_user::token): New method. (cygheap_user::deimpersonate): New method. (cygheap_user::reimpersonate): New method. (cygheap_user::has_impersonation_tokens): New method. (cygheap_user::close_impersonation_tokens): New method. * dtable.cc (dtable::vfork_child_dup): Use new cygheap_user methods. * fhandler_socket.cc (fhandler_socket::dup): Ditto. * fork.cc (fork_child): Ditto. (fork_parent): Ditto. * grp.cc (internal_getgroups): Ditto. * security.cc (verify_token): Ditto. (check_file_access): Ditto. (cygwin_set_impersonation_token): Detect conflicts. Set user.external_token. * spawn.cc (spawn_guts): Use new cygheap_user methods. * syscalls.cc (seteuid32): Rearrange to use the two tokens in cygheap_user. (setegid32): Use new cygheap_user methods. * uinfo.cc: (internal_getlogin): Ditto. --- winsup/cygwin/ChangeLog | 29 +++++++ winsup/cygwin/cygheap.h | 48 +++++++++++- winsup/cygwin/dtable.cc | 6 +- winsup/cygwin/fhandler_socket.cc | 6 +- winsup/cygwin/fork.cc | 18 +---- winsup/cygwin/grp.cc | 6 +- winsup/cygwin/security.cc | 18 +++-- winsup/cygwin/spawn.cc | 9 +-- winsup/cygwin/syscalls.cc | 126 ++++++++++++++----------------- winsup/cygwin/uinfo.cc | 9 +-- 10 files changed, 160 insertions(+), 115 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index d41f7ab20..82840899f 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,32 @@ +2003-06-30 Pierre Humblet + + * cygheap.h (enum impersonation): New enum. + (cygheap_user::token): Delete. + (cygheap_user::impersonated): Delete. + (cygheap_user::external_token): New member. + (cygheap_user::internal_token): New member. + (cygheap_user::impersonation_state): New member. + (cygheap_user::issetuid): Modify. + (cygheap_user::token): New method. + (cygheap_user::deimpersonate): New method. + (cygheap_user::reimpersonate): New method. + (cygheap_user::has_impersonation_tokens): New method. + (cygheap_user::close_impersonation_tokens): New method. + * dtable.cc (dtable::vfork_child_dup): Use new cygheap_user methods. + * fhandler_socket.cc (fhandler_socket::dup): Ditto. + * fork.cc (fork_child): Ditto. + (fork_parent): Ditto. + * grp.cc (internal_getgroups): Ditto. + * security.cc (verify_token): Ditto. + (check_file_access): Ditto. + (cygwin_set_impersonation_token): Detect conflicts. Set + user.external_token. + * spawn.cc (spawn_guts): Use new cygheap_user methods. + * syscalls.cc (seteuid32): Rearrange to use the two tokens + in cygheap_user. + (setegid32): Use new cygheap_user methods. + * uinfo.cc: (internal_getlogin): Ditto. + 2003-06-25 Doru Carastan * Makefile.in: Use INSTALL_PROGRAM to install the cygwin DLL. diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index bfa7a9308..963d9c4db 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -92,7 +92,13 @@ enum homebodies CH_HOME }; -struct passwd; +enum impersonation +{ + IMP_BAD = -1, + IMP_NONE = 0, + IMP_EXTERNAL, + IMP_INTERNAL +}; class cygheap_user { @@ -117,8 +123,9 @@ public: /* token is needed if set(e)uid should be called. It can be set by a call to `set_impersonation_token()'. */ - HANDLE token; - BOOL impersonated; + HANDLE external_token; + HANDLE internal_token; + enum impersonation impersonation_state; /* CGF 2002-06-27. I removed the initializaton from this constructor since this class is always allocated statically. That means that everything @@ -165,7 +172,40 @@ public: const char *ontherange (homebodies what, struct passwd * = NULL); bool issetuid () const { - return impersonated && token != INVALID_HANDLE_VALUE; + return impersonation_state > IMP_NONE; + } + HANDLE token () + { + if (impersonation_state == IMP_EXTERNAL) + return external_token; + if (impersonation_state == IMP_INTERNAL) + return internal_token; + return INVALID_HANDLE_VALUE; + } + void deimpersonate () + { + if (impersonation_state > IMP_NONE) + RevertToSelf (); + } + void reimpersonate () + { + if (impersonation_state > IMP_NONE + && !ImpersonateLoggedOnUser (token ())) + system_printf ("ImpersonateLoggedOnUser: %E"); + } + bool has_impersonation_tokens () { return external_token || internal_token; } + void close_impersonation_tokens () + { + if (external_token) + { + CloseHandle (external_token); + external_token = 0; + } + if (internal_token) + { + CloseHandle (internal_token); + internal_token = 0; + } } const char *cygheap_user::test_uid (char *&, const char *, size_t) __attribute__ ((regparm (3))); diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 315505b14..973b3c429 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -634,8 +634,7 @@ dtable::vfork_child_dup () int res = 1; /* Remove impersonation */ - if (cygheap->user.issetuid ()) - RevertToSelf (); + cygheap->user.deimpersonate (); for (size_t i = 0; i < size; i++) if (not_open (i)) @@ -654,8 +653,7 @@ dtable::vfork_child_dup () out: /* Restore impersonation */ - if (cygheap->user.issetuid ()) - ImpersonateLoggedOnUser (cygheap->user.token); + cygheap->user.reimpersonate (); ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); return 1; diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 18cc018f3..4c66f9e21 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -330,12 +330,10 @@ fhandler_socket::dup (fhandler_base *child) If WSADuplicateSocket() still fails for some reason, we fall back to DuplicateHandle(). */ WSASetLastError (0); - if (cygheap->user.issetuid ()) - RevertToSelf (); + cygheap->user.deimpersonate (); fhs->set_io_handle (get_io_handle ()); fhs->fixup_before_fork_exec (GetCurrentProcessId ()); - if (cygheap->user.issetuid ()) - ImpersonateLoggedOnUser (cygheap->user.token); + cygheap->user.reimpersonate (); if (!WSAGetLastError ()) { fhs->fixup_after_fork (hMainProc); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 184e56672..efda7222b 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -236,14 +236,7 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) /* Restore the inheritance state as in parent Don't call setuid here! The flags are already set. */ - if (cygheap->user.impersonated) - { - debug_printf ("Impersonation of child, token: %d", cygheap->user.token); - if (cygheap->user.token == INVALID_HANDLE_VALUE) - RevertToSelf (); // probably not needed - else if (!ImpersonateLoggedOnUser (cygheap->user.token)) - system_printf ("Impersonate for forked child failed: %E"); - } + cygheap->user.reimpersonate (); sync_with_parent ("after longjmp.", TRUE); sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent, @@ -436,8 +429,7 @@ fork_parent (HANDLE& hParent, dll *&first_dll, si.cbReserved2 = sizeof (ch); /* Remove impersonation */ - if (cygheap->user.issetuid ()) - RevertToSelf (); + cygheap->user.deimpersonate (); ch.parent = hParent; #ifdef DEBUGGING @@ -485,8 +477,7 @@ fork_parent (HANDLE& hParent, dll *&first_dll, ForceCloseHandle (subproc_ready); ForceCloseHandle (forker_finished); /* Restore impersonation */ - if (cygheap->user.issetuid ()) - ImpersonateLoggedOnUser (cygheap->user.token); + cygheap->user.reimpersonate (); cygheap_setup_for_child_cleanup (newheap, &ch, 0); return -1; } @@ -513,8 +504,7 @@ fork_parent (HANDLE& hParent, dll *&first_dll, strcpy (forked->progname, myself->progname); /* Restore impersonation */ - if (cygheap->user.issetuid ()) - ImpersonateLoggedOnUser (cygheap->user.token); + cygheap->user.reimpersonate (); ProtectHandle (pi.hThread); /* Protect the handle but name it similarly to the way it will diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index 19b7fe90f..c83c1e55f 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -261,7 +261,7 @@ internal_getgroups (int gidsetsize, __gid32_t *grouplist, cygpsid * srchsid) { /* If impersonated, use impersonation token. */ if (cygheap->user.issetuid ()) - hToken = cygheap->user.token; + hToken = cygheap->user.token (); else if (!OpenProcessToken (hMainProc, TOKEN_QUERY, &hToken)) hToken = NULL; } @@ -295,7 +295,7 @@ internal_getgroups (int gidsetsize, __gid32_t *grouplist, cygpsid * srchsid) ++cnt; if (gidsetsize && cnt > gidsetsize) { - if (hToken != cygheap->user.token) + if (!cygheap->user.issetuid ()) CloseHandle (hToken); goto error; } @@ -305,7 +305,7 @@ internal_getgroups (int gidsetsize, __gid32_t *grouplist, cygpsid * srchsid) } else debug_printf ("%d = GetTokenInformation(NULL) %E", size); - if (hToken != cygheap->user.token) + if (!cygheap->user.issetuid ()) CloseHandle (hToken); return cnt; } diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 7bae8f4c7..5ccf37e3d 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -70,10 +70,16 @@ extern "C" void cygwin_set_impersonation_token (const HANDLE hToken) { debug_printf ("set_impersonation_token (%d)", hToken); - if (cygheap->user.token != hToken) + if (cygheap->user.impersonation_state == IMP_EXTERNAL + && cygheap->user.external_token != hToken) { - cygheap->user.token = hToken; - cygheap->user.impersonated = FALSE; + set_errno (EPERM); + return; + } + else + { + cygheap->user.external_token = hToken; + return; } } @@ -717,7 +723,7 @@ verify_token (HANDLE token, cygsid &usersid, user_groups &groups, BOOL *pintern) if (pintern) { TOKEN_SOURCE ts; - if (!GetTokenInformation (cygheap->user.token, TokenSource, + if (!GetTokenInformation (token, TokenSource, &ts, sizeof ts, &size)) debug_printf ("GetTokenInformation(): %E"); else @@ -1906,7 +1912,7 @@ check_file_access (const char *fn, int flags) goto done; if (cygheap->user.issetuid ()) - hToken = cygheap->user.token; + hToken = cygheap->user.token (); else if (!OpenProcessToken (hMainProc, TOKEN_DUPLICATE, &hToken)) { __seterrno (); @@ -1914,7 +1920,7 @@ check_file_access (const char *fn, int flags) } if (!(status = DuplicateToken (hToken, SecurityIdentification, &hIToken))) __seterrno (); - if (hToken != cygheap->user.token) + if (!cygheap->user.issetuid ()) CloseHandle (hToken); if (!status) goto done; diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index eeee13ccc..be469ef21 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -621,8 +621,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, cygbench ("spawn-guts"); cygheap->fdtab.set_file_pointers_for_exec (); - if (cygheap->user.issetuid ()) - RevertToSelf (); + cygheap->user.deimpersonate (); /* When ruid != euid we create the new process under the current original account and impersonate in child, this way maintaining the different effective vs. real ids. @@ -678,7 +677,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.token (), runpath, /* image name - with full path */ one_line.buf, /* what was passed to exec */ sec_attribs, /* process security attrs */ @@ -692,8 +691,8 @@ spawn_guts (const char * prog_arg, const char *const *argv, } /* Restore impersonation. In case of _P_OVERLAY this isn't allowed since it would overwrite child data. */ - if (mode != _P_OVERLAY && cygheap->user.issetuid ()) - ImpersonateLoggedOnUser (cygheap->user.token); + if (mode != _P_OVERLAY) + cygheap->user.reimpersonate (); MALLOC_CHECK; if (envblock) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index ad6a66fb9..8e545c4af 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -2057,66 +2057,52 @@ seteuid32 (__uid32_t uid) sigframe thisframe (mainthread); cygsid usersid; user_groups &groups = cygheap->user.groups; - HANDLE ptok, sav_token; - BOOL sav_impersonated, sav_token_is_internal_token; - BOOL process_ok, explicitly_created_token = FALSE; + HANDLE ptok, new_token = INVALID_HANDLE_VALUE; struct passwd * pw_new; PSID origpsid, psid2 = NO_SID; + enum impersonation new_state = IMP_BAD; + BOOL token_is_internal; pw_new = internal_getpwuid (uid); if (!wincap.has_security () && pw_new) - goto success; + goto success_9x; if (!usersid.getfrompw (pw_new)) { set_errno (EINVAL); return -1; } - /* Save current information */ - sav_token = cygheap->user.token; - sav_impersonated = cygheap->user.impersonated; RevertToSelf (); if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok)) { __seterrno (); - goto failed; - } - /* Verify if the process token is suitable. - Currently we do not try to differentiate between - internal tokens and others */ - process_ok = verify_token (ptok, usersid, groups); - debug_printf ("Process token %sverified", process_ok ? "" : "not "); - if (process_ok) - { - if (cygheap->user.issetuid ()) - cygheap->user.impersonated = FALSE; - else - { - CloseHandle (ptok); - goto success; /* No change */ - } + goto failed_ptok;; } - if (!process_ok && cygheap->user.token != INVALID_HANDLE_VALUE) + /* Verify if the process token is suitable. */ + if (verify_token (ptok, usersid, groups)) + new_state = IMP_NONE; + /* Verify if a current token is suitable */ + else if (cygheap->user.external_token + && verify_token (cygheap->user.external_token, usersid, groups)) { - /* Verify if the current tokem is suitable */ - BOOL token_ok = verify_token (cygheap->user.token, usersid, groups, - &sav_token_is_internal_token); - debug_printf ("Thread token %d %sverified", - cygheap->user.token, token_ok?"":"not "); - if (!token_ok) - cygheap->user.token = INVALID_HANDLE_VALUE; - else - { - /* Return if current token is valid */ - if (cygheap->user.impersonated) - { - CloseHandle (ptok); - if (!ImpersonateLoggedOnUser (cygheap->user.token)) - system_printf ("Impersonating in seteuid failed: %E"); - goto success; /* No change */ - } - } + new_token = cygheap->user.external_token; + new_state = IMP_EXTERNAL; + } + else if (cygheap->user.internal_token + && verify_token (cygheap->user.internal_token, usersid, groups, + &token_is_internal)) + { + new_token = cygheap->user.internal_token; + new_state = IMP_INTERNAL; + } + + debug_printf ("New token %d, state %d", new_token, new_state); + /* Return if current token is valid */ + if (cygheap->user.impersonation_state == new_state) + { + cygheap->user.reimpersonate (); + goto success; /* No change */ } /* Set process def dacl to allow access to impersonated token */ @@ -2132,72 +2118,72 @@ seteuid32 (__uid32_t uid) debug_printf ("SetTokenInformation" "(TokenDefaultDacl): %E"); } - CloseHandle (ptok); - if (!process_ok && cygheap->user.token == INVALID_HANDLE_VALUE) + if (new_state == IMP_BAD) { /* If no impersonation token is available, try to authenticate using NtCreateToken () or subauthentication. */ - cygheap->user.token = create_token (usersid, groups, pw_new); - if (cygheap->user.token != INVALID_HANDLE_VALUE) - explicitly_created_token = TRUE; - else + new_token = create_token (usersid, groups, pw_new); + if (new_token == INVALID_HANDLE_VALUE) { /* create_token failed. Try subauthentication. */ debug_printf ("create token failed, try subauthentication."); - cygheap->user.token = subauth (pw_new); - if (cygheap->user.token == INVALID_HANDLE_VALUE) + new_token = subauth (pw_new); + if (new_token == INVALID_HANDLE_VALUE) goto failed; } + new_state = IMP_INTERNAL; } /* If using the token, set info and impersonate */ - if (!process_ok) + if (new_state != IMP_NONE) { /* If the token was explicitly created, all information has already been set correctly. */ - if (!explicitly_created_token) + if (new_state == IMP_EXTERNAL) { /* Try setting owner to same value as user. */ - if (!SetTokenInformation (cygheap->user.token, TokenOwner, + if (!SetTokenInformation (new_token, TokenOwner, &usersid, sizeof usersid)) debug_printf ("SetTokenInformation(user.token, " "TokenOwner): %E"); /* Try setting primary group in token to current group */ - if (!SetTokenInformation (cygheap->user.token, + if (!SetTokenInformation (new_token, TokenPrimaryGroup, &groups.pgsid, sizeof (cygsid))) debug_printf ("SetTokenInformation(user.token, " "TokenPrimaryGroup): %E"); } - /* Now try to impersonate. */ - if (!ImpersonateLoggedOnUser (cygheap->user.token)) + /* Try to impersonate. */ + if (!ImpersonateLoggedOnUser (new_token)) { debug_printf ("ImpersonateLoggedOnUser %E"); __seterrno (); goto failed; } - cygheap->user.impersonated = TRUE; + /* Keep at most one internal token */ + if (new_state == IMP_INTERNAL) + { + if (cygheap->user.internal_token) + CloseHandle (cygheap->user.internal_token); + cygheap->user.internal_token = new_token; + } } - - /* If sav_token was internally created and is replaced, destroy it. */ - if (sav_token != INVALID_HANDLE_VALUE && - sav_token != cygheap->user.token && - sav_token_is_internal_token) - CloseHandle (sav_token); cygheap->user.set_sid (usersid); + success: + CloseHandle (ptok); + cygheap->user.impersonation_state = new_state; +success_9x: cygheap->user.set_name (pw_new->pw_name); myself->uid = uid; groups.ischanged = FALSE; return 0; failed: - cygheap->user.token = sav_token; - cygheap->user.impersonated = sav_impersonated; - if (cygheap->user.issetuid () - && !ImpersonateLoggedOnUser (cygheap->user.token)) - system_printf ("Impersonating in seteuid failed: %E"); + CloseHandle (ptok); +failed_ptok: + cygheap->user.reimpersonate (); return -1; } @@ -2278,7 +2264,7 @@ setegid32 (__gid32_t gid) /* If impersonated, update primary group and revert */ if (cygheap->user.issetuid ()) { - if (!SetTokenInformation (cygheap->user.token, + if (!SetTokenInformation (cygheap->user.token (), TokenPrimaryGroup, &gsid, sizeof gsid)) debug_printf ("SetTokenInformation(thread, " @@ -2296,7 +2282,7 @@ setegid32 (__gid32_t gid) CloseHandle (ptok); } if (cygheap->user.issetuid () - && !ImpersonateLoggedOnUser (cygheap->user.token)) + && !ImpersonateLoggedOnUser (cygheap->user.token ())) system_printf ("Impersonating in setegid failed: %E"); return 0; } diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 4e688a3fa..f63230d0a 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -102,7 +102,7 @@ internal_getlogin (cygheap_user &user) void uinfo_init () { - if (child_proc_info && cygheap->user.token == INVALID_HANDLE_VALUE) + if (child_proc_info && !cygheap->user.has_impersonation_tokens ()) return; if (!child_proc_info) @@ -114,17 +114,16 @@ uinfo_init () && cygheap->user.orig_gid == cygheap->user.real_gid && !cygheap->user.groups.issetgroups ()) { - if (!ImpersonateLoggedOnUser (cygheap->user.token)) - system_printf ("ImpersonateLoggedOnUser: %E"); + cygheap->user.reimpersonate (); return; } else - CloseHandle (cygheap->user.token); + cygheap->user.close_impersonation_tokens (); cygheap->user.orig_uid = cygheap->user.real_uid = myself->uid; cygheap->user.orig_gid = cygheap->user.real_gid = myself->gid; + cygheap->user.impersonation_state = IMP_NONE; cygheap->user.set_orig_sid (); /* Update the original sid */ - cygheap->user.token = INVALID_HANDLE_VALUE; /* No token present */ } extern "C" char *