diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index bfee67960..c0a87d546 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,13 @@ +Sat Jun 9 23:20:00 2001 Corinna Vinschen + + * syscalls.cc (seteuid): Set environment variables USERNAME and + USERDOMAIN before impersonation to workaround a LookupAccountSid() + misbehaviour. + * uinfo.cc (internal_getlogin): Revert most of the previous change. + Don't set environment variables USERNAME and USERDOMAIN. That's + the job of seteuid() now. Try to get logon server from Lsa + only if logon server isn't already known. + Thu Jun 7 15:54:32 2001 Robert Collins * thread.cc (pthread_cond::Broadcast): Don't print error messages on diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 788adc1e8..e6e48f3fc 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -22,6 +22,7 @@ details. */ #include #include #include +#include #include /* for UNLEN */ #include #include @@ -1971,6 +1972,14 @@ seteuid (uid_t uid) sigframe thisframe (mainthread); if (os_being_run == winNT) { + char orig_username[UNLEN + 1]; + char orig_domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + char username[UNLEN + 1]; + DWORD ulen = UNLEN + 1; + char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + DWORD dlen = INTERNET_MAX_HOST_NAME_LENGTH + 1; + SID_NAME_USE use; + if (uid == (uid_t) -1 || uid == myself->uid) { debug_printf ("new euid == current euid, nothing happens"); @@ -1983,18 +1992,44 @@ seteuid (uid_t uid) return -1; } + cygsid tok_usersid; + DWORD siz; + + char *env; + orig_username[0] = orig_domain[0] = '\0'; + if ((env = getenv ("USERNAME"))) + strncat (orig_username, env, UNLEN + 1); + if ((env = getenv ("USERDOMAIN"))) + strncat (orig_domain, env, INTERNET_MAX_HOST_NAME_LENGTH + 1); if (uid == cygheap->user.orig_uid) { + debug_printf ("RevertToSelf () (uid == orig_uid, token=%d)", cygheap->user.token); RevertToSelf (); if (cygheap->user.token != INVALID_HANDLE_VALUE) cygheap->user.impersonated = FALSE; + + HANDLE ptok = INVALID_HANDLE_VALUE; + if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok)) + debug_printf ("OpenProcessToken(): %E\n"); + else if (!GetTokenInformation (ptok, TokenUser, &tok_usersid, + sizeof tok_usersid, &siz)) + debug_printf ("GetTokenInformation(): %E"); + else if (!LookupAccountSid (NULL, tok_usersid, username, &ulen, + domain, &dlen, &use)) + debug_printf ("LookupAccountSid(): %E"); + else + { + setenv ("USERNAME", username, 1); + setenv ("USERDOMAIN", domain, 1); + } + if (ptok != INVALID_HANDLE_VALUE) + CloseHandle (ptok); } else { - cygsid usersid, pgrpsid, tok_usersid, tok_pgrpsid; - DWORD siz; + cygsid usersid, pgrpsid, tok_pgrpsid; HANDLE sav_token = INVALID_HANDLE_VALUE; BOOL sav_impersonation; BOOL current_token_is_internal_token = FALSE; @@ -2104,11 +2139,18 @@ seteuid (uid_t uid) } /* Now try to impersonate. */ - if (!ImpersonateLoggedOnUser (cygheap->user.token)) + if (!LookupAccountSid (NULL, usersid, username, &ulen, + domain, &dlen, &use)) + debug_printf ("LookupAccountSid (): %E"); + else if (!ImpersonateLoggedOnUser (cygheap->user.token)) system_printf ("Impersonating (%d) in set(e)uid failed: %E", cygheap->user.token); else - cygheap->user.impersonated = TRUE; + { + cygheap->user.impersonated = TRUE; + setenv ("USERNAME", username, 1); + setenv ("USERDOMAIN", domain, 1); + } } } @@ -2124,6 +2166,8 @@ seteuid (uid_t uid) debug_printf ("Diffs!!! token: %d, cur: %d, new: %d, orig: %d", cygheap->user.token, pw_cur->pw_uid, pw_new->pw_uid, cygheap->user.orig_uid); + setenv ("USERNAME", orig_username, 1); + setenv ("USERDOMAIN", orig_domain, 1); set_errno (EPERM); return -1; } diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index a3b734ebf..867f3cee1 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -31,61 +31,61 @@ struct passwd * internal_getlogin (cygheap_user &user) { char username[UNLEN + 1]; - DWORD ulen = UNLEN + 1; + DWORD username_len = UNLEN + 1; struct passwd *pw = NULL; - if (!GetUserName (username, &ulen)) + if (!GetUserName (username, &username_len)) user.set_name ("unknown"); else user.set_name (username); + debug_printf ("GetUserName() = %s", user.name ()); if (os_being_run == winNT) { - HANDLE ptok = user.token; /* Which is INVALID_HANDLE_VALUE if no - impersonation took place. */ - DWORD siz; - cygsid tu; - NET_API_STATUS ret = 0; - char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - DWORD dlen = INTERNET_MAX_HOST_NAME_LENGTH + 1; - SID_NAME_USE use; - char buf[MAX_PATH]; + LPWKSTA_USER_INFO_1 wui; + NET_API_STATUS ret; + char buf[512]; + char *env; - /* Try to get the SID either from already impersonated token - or from current process first. To differ that two cases is - important, because you can't rely on the user information - in a process token of a currently impersonated process. */ - user.set_sid (NO_SID); - if (ptok == INVALID_HANDLE_VALUE - && !OpenProcessToken (GetCurrentProcess (), - TOKEN_ADJUST_DEFAULT | TOKEN_QUERY, - &ptok)) - debug_printf ("OpenProcessToken(): %E"); - else if (!GetTokenInformation (ptok, TokenUser, &tu, sizeof tu, &siz)) - debug_printf ("GetTokenInformation(): %E"); - else if (!(ret = user.set_sid (tu))) - debug_printf ("Couldn't retrieve SID from access token!"); - else if (!LookupAccountSid (NULL, user.sid (), username, &ulen, - domain, &dlen, &use)) - debug_printf ("LookupAccountSid (): %E"); - else + user.set_logsrv (NULL); + /* First trying to get logon info from environment */ + if ((env = getenv ("USERNAME")) != NULL) + user.set_name (env); + if ((env = getenv ("USERDOMAIN")) != NULL) + user.set_domain (env); + if ((env = getenv ("LOGONSERVER")) != NULL) + user.set_logsrv (env + 2); /* filter leading double backslashes */ + if (user.name () && user.domain ()) + debug_printf ("User: %s, Domain: %s, Logon Server: %s", + user.name (), user.domain (), user.logsrv ()); + else if (!(ret = NetWkstaUserGetInfo (NULL, 1, (LPBYTE *)&wui))) { - user.set_name (username); - user.set_domain (domain); + sys_wcstombs (buf, wui->wkui1_username, UNLEN + 1); + user.set_name (buf); + sys_wcstombs (buf, wui->wkui1_logon_server, + INTERNET_MAX_HOST_NAME_LENGTH + 1); + user.set_logsrv (buf); + sys_wcstombs (buf, wui->wkui1_logon_domain, + INTERNET_MAX_HOST_NAME_LENGTH + 1); + user.set_domain (buf); + NetApiBufferFree (wui); + } + if (!user.logsrv () && get_logon_server_and_user_domain (buf, NULL)) + { + user.set_logsrv (buf + 2); + setenv ("LOGONSERVER", buf, 1); } - if (get_logon_server_and_user_domain (domain, NULL)) - user.set_logsrv (domain + 2); - setenv ("USERNAME", user.name (), 1); - setenv ("LOGONSERVER", user.logsrv (), 1); - setenv ("USERDOMAIN", user.domain (), 1); - - LPUSER_INFO_3 ui; - WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + LPUSER_INFO_3 ui = NULL; WCHAR wuser[UNLEN + 1]; - sys_mbstowcs (wlogsrv, user.logsrv (), INTERNET_MAX_HOST_NAME_LENGTH + 1); + WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3]; + + /* HOMEDRIVE and HOMEPATH are wrong most of the time, too, + after changing user context! */ sys_mbstowcs (wuser, user.name (), UNLEN + 1); - if (!NetUserGetInfo (wlogsrv, wuser, 3, (LPBYTE *)&ui) || - !NetUserGetInfo (NULL, wuser, 3, (LPBYTE *)&ui)) + strcat (strcpy (buf, "\\\\"), user.logsrv ()); + sys_mbstowcs (wlogsrv, buf, INTERNET_MAX_HOST_NAME_LENGTH + 3); + if (!NetUserGetInfo (NULL, wuser, 3, (LPBYTE *)&ui) + || !NetUserGetInfo (wlogsrv, wuser, 3,(LPBYTE *)&ui)) { sys_wcstombs (buf, ui->usri3_home_dir, MAX_PATH); if (!buf[0]) @@ -95,7 +95,7 @@ internal_getlogin (cygheap_user &user) strcat (buf, "\\"); else { - char *env = getenv ("SYSTEMDRIVE"); + env = getenv ("SYSTEMDRIVE"); if (env && *env) strcat (strcpy (buf, env), "\\"); else @@ -107,46 +107,93 @@ internal_getlogin (cygheap_user &user) setenv ("HOMEDRIVE", buf, 1); NetApiBufferFree (ui); } + debug_printf ("Domain: %s, Logon Server: %s, Windows Username: %s", + user.domain (), user.logsrv (), user.name ()); - /* If we have a SID, try to get the corresponding Cygwin user name - which can be different from the Windows user name. */ - cygsid gsid (NO_SID); - if (user.sid ()) + if (allow_ntsec) { - cygsid psid; + HANDLE ptok = user.token; /* Which is INVALID_HANDLE_VALUE if no + impersonation took place. */ + DWORD siz; + cygsid tu; + int ret = 0; - for (int pidx = 0; (pw = internal_getpwent (pidx)); ++pidx) - if (psid.getfrompw (pw) && EqualSid (user.sid (), psid)) - { - user.set_name (pw->pw_name); - struct group *gr = getgrgid (pw->pw_gid); - if (gr && !gsid.getfromgr (gr)) - gsid = NO_SID; - } - if (!strcasematch (user.name (), "SYSTEM")) - if (get_registry_hive_path (user.sid (), buf)) - setenv ("USERPROFILE", buf, 1); - else - unsetenv ("USERPROFILE"); + /* Try to get the SID either from already impersonated token + or from current process first. To differ that two cases is + important, because you can't rely on the user information + in a process token of a currently impersonated process. */ + if (ptok == INVALID_HANDLE_VALUE + && !OpenProcessToken (GetCurrentProcess (), + TOKEN_ADJUST_DEFAULT | TOKEN_QUERY, + &ptok)) + debug_printf ("OpenProcessToken(): %E\n"); + else if (!GetTokenInformation (ptok, TokenUser, &tu, sizeof tu, &siz)) + debug_printf ("GetTokenInformation(): %E"); + else if (!(ret = user.set_sid (tu))) + debug_printf ("Couldn't retrieve SID from access token!"); + /* If that failes, try to get the SID from localhost. This can only + be done if a domain is given because there's a chance that a local + and a domain user may have the same name. */ + if (!ret && user.domain ()) + { + /* Concat DOMAIN\USERNAME for the next lookup */ + strcat (strcat (strcpy (buf, user.domain ()), "\\"), user.name ()); + if (!(ret = lookup_name (buf, NULL, user.sid ()))) + debug_printf ("Couldn't retrieve SID locally!"); + } + + /* If that fails, too, as a last resort try to get the SID from + the logon server. */ + if (!ret && !(ret = lookup_name (user.name (), user.logsrv (), + user.sid ()))) + debug_printf ("Couldn't retrieve SID from '%s'!", user.logsrv ()); + + /* If we have a SID, try to get the corresponding Cygwin user name + which can be different from the Windows user name. */ + cygsid gsid (NO_SID); + if (ret) + { + char dom[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + cygsid psid; + + for (int pidx = 0; (pw = internal_getpwent (pidx)); ++pidx) + if (psid.getfrompw (pw) && EqualSid (user.sid (), psid)) + { + user.set_name (pw->pw_name); + struct group *gr = getgrgid (pw->pw_gid); + if (gr) + if (!gsid.getfromgr (gr)) + gsid = NO_SID; + break; + } + if (!strcasematch (user.name (), "SYSTEM") + && user.domain () && user.logsrv ()) + { + if (get_registry_hive_path (user.sid (), buf)) + setenv ("USERPROFILE", buf, 1); + else + unsetenv ("USERPROFILE"); + } + } + + /* If this process is started from a non Cygwin process, + set token owner to the same value as token user and + primary group to the group which is set as primary group + in /etc/passwd. */ + if (ptok != INVALID_HANDLE_VALUE && myself->ppid == 1) + { + if (!SetTokenInformation (ptok, TokenOwner, &tu, sizeof tu)) + debug_printf ("SetTokenInformation(TokenOwner): %E"); + if (gsid && !SetTokenInformation (ptok, TokenPrimaryGroup, + &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(TokenPrimaryGroup): %E"); + } + + /* Close token only if it's a result from OpenProcessToken(). */ + if (ptok != INVALID_HANDLE_VALUE + && user.token == INVALID_HANDLE_VALUE) + CloseHandle (ptok); } - - /* If this process is started from a non Cygwin process, - set token owner to the same value as token user and - primary group to the group which is set as primary group - in /etc/passwd. */ - if (ptok != INVALID_HANDLE_VALUE && myself->ppid == 1) - { - if (!SetTokenInformation (ptok, TokenOwner, &tu, sizeof tu)) - debug_printf ("SetTokenInformation(TokenOwner): %E"); - if (gsid && !SetTokenInformation (ptok, TokenPrimaryGroup, - &gsid, sizeof gsid)) - debug_printf ("SetTokenInformation(TokenPrimaryGroup): %E"); - } - - /* Close token only if it's a result from OpenProcessToken(). */ - if (ptok != INVALID_HANDLE_VALUE - && user.token == INVALID_HANDLE_VALUE) - CloseHandle (ptok); } debug_printf ("Cygwins Username: %s", user.name ()); return pw ?: getpwnam(user.name ());