/* su.cpp UNIX-like Substitute User for Windows NT Usage: su [username [command-line]] where: username is the user to be impersonated. Default is Administrator. command-line is the command to be executed, with parameters. Default is CMD (Console) Authors: David Wihl (wihl@shore.net) Steffen Krause (skrause@informatik.hu-berlin.de) Revision History: 03-JUL-1995. Initial public release Design: Impersonating a user on Windows NT is a three step process: 1- Logon the user to create a Security identifier 2- Enabling access to the Windows Station so the newly logged on user can interact. This is necessary even if the Administrator is logging on. 3- Creating a process using the Security identifier Different privileges are required for steps (1) and (3). Logging on a user (LogonUser()) requires the SeTcbPrivilege. Creating a process as another user CreateProcessAsUser()) requires SeAssignPrimary and SeIncreaseQuota privileges. To grant these privileges, see the Installation Section. These two Security API calls were only stablized in NT 3.51, build 1057. SU will not work with earlier versions. In NT, there is no direct equivalent of UNIX's rwsr-xr-x file permission. Restrictions and Limitations: - Quotes (") in the command line are not passed correctly. - There is no logging of failed or successful usage. A future may incorporate writing to the Event Log. Installation: The easiest way to selectively grant the three privileges required to use this program is: 1- Start the User Manager (MUSRMGR) 2- Create a new group (e.g. "SU Users") 3- Add the three privileges to the group (via Policies\User Rights): "Act as part of the operating system" - SeTcbPrivilege "Increase quotas" - SeIncreaseQuota "Replace a process level token" - SeAssignPrimary NOTE: The three privileges will only be visible if you check "Show Advanced User Rights" in the dialog box. 4- Add the desired users to the new group (via User\Properties\Group) This program was compiled under Visual C++ 2.1. For more information about Porting from UNIX to NT check out the FAQ: http://www.shore.net/~wihl/unix2nt.html */ #include #include #include #define NAME_BUF_SZ 255 void ErrorHandler (LPSTR errmsg) { cerr << "Error: " << errmsg << ". Error Code: " << GetLastError() << endl; } BOOL SetUserObjectAllAccess(HANDLE hObject) { PSECURITY_DESCRIPTOR pSD; SECURITY_INFORMATION si; BOOL success; /* Initialize a security descriptor. */ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */ if (pSD == NULL) { ErrorHandler("Can't Allocate Local Memory"); return FALSE; } if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { /* defined in WINNT.H */ ErrorHandler("Can't Initialize Security Descriptor"); LocalFree ((HLOCAL) pSD); return FALSE; } /* Add a NULL disc. ACL to the security descriptor. */ if (!SetSecurityDescriptorDacl(pSD, TRUE, /* specifying a disc. ACL */ (PACL) NULL, FALSE)) /* not a default disc. ACL */ { ErrorHandler("Can't Set Security Descriptor DACL"); LocalFree ((HLOCAL) pSD); return FALSE; } /* Add the security descriptor to the file. */ si = DACL_SECURITY_INFORMATION; success = SetUserObjectSecurity(hObject, &si, pSD); LocalFree((HLOCAL) pSD); if ( ! success ) { ErrorHandler("Can't Set User Object Security"); return FALSE; } else { return TRUE; } } void main(int argc, char *argv[]) { CHAR pwstr[NAME_BUF_SZ], user[NAME_BUF_SZ], commandLine[NAME_BUF_SZ]; STARTUPINFO startUpInfo; PROCESS_INFORMATION procInfo; HDESK hDesktop; HWINSTA hWindowStation; HANDLE hRootsid, hConsole; DWORD err, oldConsoleMode, newConsoleMode; OSVERSIONINFO NTversion; // Make sure we are using the minimum OS version. NTversion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (! GetVersionEx(&NTversion) ) { ErrorHandler ("Unable to get OS version"); exit(1); } if (NTversion.dwPlatformId != VER_PLATFORM_WIN32_NT) { cerr << "su will run only on Windows NT." << endl; exit(1); } if (NTversion.dwBuildNumber < 1057) // Commercial 3.51 release { cerr << "su requires at minimum NT version 3.51 build 1057." << endl; exit(1); } #ifdef VERBOSE cout << "su: NT Version " << NTversion.dwMajorVersion << "." << NTversion.dwMinorVersion << ", build " << NTversion.dwBuildNumber << endl; #endif // Retrieve command line parameters switch (argc) { case 0: case 1: strcpy(user, "Administrator"); strcpy(commandLine, "cmd"); break; case 2: strcpy(user, *++argv); strcpy(commandLine, "cmd"); break; default: strcpy(user, *++argv); strcpy(commandLine, ""); while (*++argv) { strcat(commandLine, *argv); strcat(commandLine, " "); } break; } #ifdef VERBOSE cout << "argc: " << argc << endl << "user: (" << user << ")" << endl << "cmd : (" << commandLine << ")" << endl; #endif // Turn off console mode echo, since we don't want clear-screen passwords if ((hConsole = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { ErrorHandler ("Can't get handle of STDIN"); exit(1); } if (! GetConsoleMode(hConsole, &oldConsoleMode)) { ErrorHandler ("Can't get current Console Mode"); exit(1); } newConsoleMode = oldConsoleMode & ( ~ ENABLE_ECHO_INPUT ); if (! SetConsoleMode(hConsole, newConsoleMode)) { ErrorHandler ("Unable to turn off Echo"); exit(1); } // Ask for the password cout << "Enter password: "; cin.getline(pwstr,NAME_BUF_SZ); // When echo is off and user hits , CR-LF is not echoed, so do it for him cout << endl; if (! SetConsoleMode(hConsole, oldConsoleMode)) { ErrorHandler ("Unable to reset previous console mode"); exit(1); } CloseHandle (hConsole); // Logon the administrator if (!LogonUser (user,NULL,pwstr, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hRootsid) ) { err = GetLastError(); switch (err) { case ERROR_PRIVILEGE_NOT_HELD: cerr << "Error: you do not have the SeTcbPrivilege (act as part of OS) privilege." << endl; break; case ERROR_LOGON_FAILURE: cerr << "Wrong password." << endl; break; case ERROR_ACCESS_DENIED: ErrorHandler("Access is denied"); break; default: ErrorHandler("Unable to logon"); break; } exit(1); } // Retrieve the STARTUPINFO structure for the current process GetStartupInfo(&startUpInfo); // give the user access to the WindowStation and Desktop hWindowStation = GetProcessWindowStation(); if (!SetUserObjectAllAccess(hWindowStation)) { cerr << "Cannot set WindowStation security" ; CloseHandle (hRootsid); exit(1); } hDesktop = GetThreadDesktop(GetCurrentThreadId()); if (!SetUserObjectAllAccess(hDesktop)) { cerr << "Cannot set Desktop security"; CloseHandle (hRootsid); exit(1); } // Create the child process if ( ! CreateProcessAsUser(hRootsid, 0, commandLine, 0, 0, FALSE, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP , 0, 0, &startUpInfo, &procInfo) ) { err = GetLastError(); switch (err) { case ERROR_PRIVILEGE_NOT_HELD: cerr << "Error: you do not have the SeAssignPrimary or SeIncreaseQuota Privileges." << endl; break; default: ErrorHandler ("Can't create process"); break; } } // Wait for the child to complete WaitForSingleObject(procInfo.hProcess, INFINITE); CloseHandle(hWindowStation); CloseHandle(hDesktop); CloseHandle(hRootsid); exit(0); }