diff --git a/winsup/cygwin/release/2.6.0 b/winsup/cygwin/release/2.6.0 index eb39f8fec..ffa6c17d1 100644 --- a/winsup/cygwin/release/2.6.0 +++ b/winsup/cygwin/release/2.6.0 @@ -84,3 +84,6 @@ Bug Fixes - Fix off_t typedef on 64-bit. Addresses: https://sourceware.org/ml/newlib/2016/msg01028.html + +- Fix weird problem running passwd on newer Windows versions. + Addresses: https://cygwin.com/ml/cygwin/2016-08/msg00608.html diff --git a/winsup/utils/passwd.c b/winsup/utils/passwd.c index 9510be514..70778b88d 100644 --- a/winsup/utils/passwd.c +++ b/winsup/utils/passwd.c @@ -113,57 +113,57 @@ EvalRet (int ret, const char *user) } PUSER_INFO_3 -GetPW (char *user, int print_win_name, LPWSTR *server) +GetPW (char *user, int print_win_name, LPWSTR *server, LPWSTR domain) { char usr_buf[UNLEN + 1]; WCHAR name[UNLEN + 1]; DWORD ret; PUSER_INFO_3 ui; struct passwd *pw; - char *domain = (char *) alloca (INTERNET_MAX_HOST_NAME_LENGTH + 1); + char dom[INTERNET_MAX_HOST_NAME_LENGTH + 1]; /* Get the Win32 username and a suitable server. */ - if ((pw = getpwnam (user))) + pw = getpwnam (user); + if (!pw) { - cygwin_internal (CW_EXTRACT_DOMAIN_AND_USER, pw, domain, usr_buf); - if (strcasecmp (pw->pw_name, usr_buf)) - { - /* Hack to avoid problem with LookupAccountSid after impersonation */ - if (strcasecmp (usr_buf, "SYSTEM")) - { - user = usr_buf; - if (print_win_name) - printf ("Windows username : %s\n", user); - } - } - if (!*server) - { - PDOMAIN_CONTROLLER_INFOW dci; - char machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - DWORD mlen = sizeof machine; - WCHAR wdom[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + EvalRet (NERR_UserNotFound, user); + return NULL; + } - /* If we can't fetch the local machine name, or if the machine name - is not the same as the user's domain name, try to fetch the DC via - DsGetDcName. Otherwise, just stick to a NULL servername, since - that's the same as using the local machine. */ - if (!GetComputerNameExA (ComputerNameNetBIOS, machine, &mlen) - || strcasecmp (domain, machine) != 0) - { - mbstowcs (wdom, domain, INTERNET_MAX_HOST_NAME_LENGTH + 1); - if (!DsGetDcNameW (NULL, wdom, NULL, NULL, DS_IS_FLAT_NAME, &dci)) - *server = dci->DomainControllerName; - } - } + cygwin_internal (CW_EXTRACT_DOMAIN_AND_USER, pw, dom, usr_buf); + /* Hack to avoid problem with LookupAccountSid after impersonation + using the simple NtCreateToken method. */ + if (strcasecmp (pw->pw_name, usr_buf) && strcasecmp (usr_buf, "SYSTEM")) + { + user = usr_buf; + if (print_win_name) + printf ("Windows username : %s\n", user); } mbstowcs (name, user, UNLEN + 1); + mbstowcs (domain, dom, INTERNET_MAX_HOST_NAME_LENGTH + 1); + if (!*server) + { + PDOMAIN_CONTROLLER_INFOW dci; + WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + DWORD mlen = INTERNET_MAX_HOST_NAME_LENGTH + 1; + + /* If the machine name is not the same as the user's domain name we're + in a domain. Fetch the DC via DsGetDcName. Otherwise, just stick + to a NULL servername, since that's the same as using the local + machine. */ + if ((!GetComputerNameExW (ComputerNameNetBIOS, machine, &mlen) + || wcscasecmp (domain, machine) != 0) + && !DsGetDcNameW (NULL, domain, NULL, NULL, DS_IS_FLAT_NAME, &dci)) + *server = dci->DomainControllerName; + } + ret = NetUserGetInfo (*server, name, 3, (void *) &ui); return EvalRet (ret, user) ? NULL : ui; } int -ChangePW (const char *user, PCWSTR name, const char *oldpwd, const char *pwd, - int justcheck, LPCWSTR server) +ChangePW (const char *user, PCWSTR domain, PCWSTR name, const char *oldpwd, + const char *pwd, int justcheck, PCWSTR server) { WCHAR oldpass[512], pass[512]; DWORD ret; @@ -179,7 +179,11 @@ ChangePW (const char *user, PCWSTR name, const char *oldpwd, const char *pwd, else { mbstowcs (oldpass, oldpwd, 512); - ret = NetUserChangePassword (server, name, oldpass, pass); + /* NetUserChangePassword has changed between W7 and W8.1. For some + reason it doesn't accept the usual "\\server" servername anymore, + rather you have to use the domain name as server parameter, otherwise + you suffer an error 1265, ERROR_DOWNGRADE_DETECTED. */ + ret = NetUserChangePassword (domain, name, oldpass, pass); } if (justcheck && ret != ERROR_INVALID_PASSWORD) return 0; @@ -189,7 +193,7 @@ ChangePW (const char *user, PCWSTR name, const char *oldpwd, const char *pwd, } void -PrintPW (PUSER_INFO_3 ui, LPCWSTR server) +PrintPW (PUSER_INFO_3 ui, PCWSTR server) { time_t t = time (NULL) - ui->usri3_password_age; int ret; @@ -230,7 +234,7 @@ PrintPW (PUSER_INFO_3 ui, LPCWSTR server) } int -SetModals (int xarg, int narg, int iarg, int Larg, LPCWSTR server) +SetModals (int xarg, int narg, int iarg, int Larg, PCWSTR server) { int ret; PUSER_MODALS_INFO_0 mi; @@ -379,7 +383,9 @@ int main (int argc, char **argv) { char *logonserver; - char user[UNLEN + 1], oldpwd[_PASSWORD_LEN + 1], newpwd[_PASSWORD_LEN + 1]; + char user[UNLEN + 1]; + WCHAR domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + char oldpwd[_PASSWORD_LEN + 1], newpwd[_PASSWORD_LEN + 1]; int ret = 0; int cnt = 0; int opt; @@ -604,7 +610,7 @@ main (int argc, char **argv) } } - ui = GetPW (user, 1, &server); + ui = GetPW (user, 1, &server, domain); if (!ui) return 1; @@ -652,7 +658,7 @@ main (int argc, char **argv) if (!caller_is_admin ()) { strcpy (oldpwd, getpass ("Old password: ")); - if (ChangePW (user, ui->usri3_name, oldpwd, oldpwd, 1, server)) + if (ChangePW (user, domain, ui->usri3_name, oldpwd, oldpwd, 1, server)) return 1; } @@ -661,8 +667,8 @@ main (int argc, char **argv) strcpy (newpwd, getpass ("New password: ")); if (strcmp (newpwd, getpass ("Re-enter new password: "))) eprint (0, "Password is not identical."); - else if (!ChangePW (user, ui->usri3_name, *oldpwd ? oldpwd : NULL, - newpwd, 0, server)) + else if (!ChangePW (user, domain, ui->usri3_name, + *oldpwd ? oldpwd : NULL, newpwd, 0, server)) ret = 1; if (!ret && cnt < 2) eprint (0, "Try again.");