bool ValidateUser(char *pszAccount, char *pszPassword, bool bUseCache, int *pError) { HANDLE hUser; char account[100], domain[100] = "", *pszDomain; ParseAccountDomain(pszAccount, account, domain); if (strlen(domain) < 1) pszDomain = NULL; else pszDomain = domain; if (bUseCache) hUser = GetUserHandle(account, pszDomain, pszPassword, pError); else hUser = GetUserHandleNoCache(account, pszDomain, pszPassword, pError); // does hUser need to be closed? return (hUser != INVALID_HANDLE_VALUE); }
HANDLE LaunchProcessLogon(char *domainaccount, char *password, char *cmd, char *env, char *map, char *dir, int priorityClass, int priority, HANDLE *hIn, HANDLE *hOut, HANDLE *hErr, int *pdwPid, int *nError, char *pszError, bool bDebug) { HANDLE hStdin, hStdout, hStderr; HANDLE hPipeStdinR=NULL, hPipeStdinW=NULL; HANDLE hPipeStdoutR=NULL, hPipeStdoutW=NULL; HANDLE hPipeStderrR=NULL, hPipeStderrW=NULL; STARTUPINFO saInfo; PROCESS_INFORMATION psInfo; void *pEnv=NULL; char tSavedPath[MAX_PATH] = "."; HANDLE hRetVal = INVALID_HANDLE_VALUE; char account[100], domain[100] = "", *pszDomain = NULL; HANDLE hUser; int num_tries; int error; DWORD launch_flag; // Launching of the client processes must be synchronized because // stdin,out,err are redirected for the entire process, not just this thread. WaitForSingleObject(g_hLaunchMutex, INFINITE); // Don't handle errors, just let the process die. // In the future this will be configurable to allow various debugging options. #ifdef USE_SET_ERROR_MODE DWORD dwOriginalErrorMode; dwOriginalErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); #endif // Save stdin, stdout, and stderr hStdin = GetStdHandle(STD_INPUT_HANDLE); hStdout = GetStdHandle(STD_OUTPUT_HANDLE); hStderr = GetStdHandle(STD_ERROR_HANDLE); if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE || hStderr == INVALID_HANDLE_VALUE) { *nError = GetLastError(); strcpy(pszError, "GetStdHandle failed, "); ReleaseMutex(g_hLaunchMutex); #ifdef USE_SET_ERROR_MODE SetErrorMode(dwOriginalErrorMode); #endif return INVALID_HANDLE_VALUE; } // Set the security attributes to allow handles to be inherited SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.lpSecurityDescriptor = NULL; saAttr.bInheritHandle = TRUE; // Create pipes for stdin, stdout, and stderr if (!CreatePipe(&hPipeStdinR, &hPipeStdinW, &saAttr, 0)) { *nError = GetLastError(); strcpy(pszError, "CreatePipe failed, "); goto CLEANUP; } if (!CreatePipe(&hPipeStdoutR, &hPipeStdoutW, &saAttr, 0)) { *nError = GetLastError(); strcpy(pszError, "CreatePipe failed, "); goto CLEANUP; } if (!CreatePipe(&hPipeStderrR, &hPipeStderrW, &saAttr, 0)) { *nError = GetLastError(); strcpy(pszError, "CreatePipe failed, "); goto CLEANUP; } // Make the ends of the pipes that this process will use not inheritable if (!DuplicateHandle(GetCurrentProcess(), hPipeStdinW, GetCurrentProcess(), hIn, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { *nError = GetLastError(); strcpy(pszError, "DuplicateHandle failed, "); goto CLEANUP; } if (!DuplicateHandle(GetCurrentProcess(), hPipeStdoutR, GetCurrentProcess(), hOut, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { *nError = GetLastError(); strcpy(pszError, "DuplicateHandle failed, "); CloseHandle(*hIn); goto CLEANUP; } if (!DuplicateHandle(GetCurrentProcess(), hPipeStderrR, GetCurrentProcess(), hErr, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { *nError = GetLastError(); strcpy(pszError, "DuplicateHandle failed, "); CloseHandle(*hIn); CloseHandle(*hOut); goto CLEANUP; } // Set stdin, stdout, and stderr to the ends of the pipe the created process will use if (!SetStdHandle(STD_INPUT_HANDLE, hPipeStdinR)) { *nError = GetLastError(); strcpy(pszError, "SetStdHandle failed, "); CloseHandle(*hIn); CloseHandle(*hOut); CloseHandle(*hErr); goto CLEANUP; } if (!SetStdHandle(STD_OUTPUT_HANDLE, hPipeStdoutW)) { *nError = GetLastError(); strcpy(pszError, "SetStdHandle failed, "); CloseHandle(*hIn); CloseHandle(*hOut); CloseHandle(*hErr); goto RESTORE_CLEANUP; } if (!SetStdHandle(STD_ERROR_HANDLE, hPipeStderrW)) { *nError = GetLastError(); strcpy(pszError, "SetStdHandle failed, "); CloseHandle(*hIn); CloseHandle(*hOut); CloseHandle(*hErr); goto RESTORE_CLEANUP; } // Create the process memset(&saInfo, 0, sizeof(STARTUPINFO)); saInfo.cb = sizeof(STARTUPINFO); saInfo.hStdInput = hPipeStdinR; saInfo.hStdOutput = hPipeStdoutW; saInfo.hStdError = hPipeStderrW; saInfo.dwFlags = STARTF_USESTDHANDLES; //saInfo.lpDesktop = "WinSta0\\Default"; //saInfo.wShowWindow = SW_SHOW; SetEnvironmentVariables(env); pEnv = GetEnvironmentStrings(); ParseAccountDomain(domainaccount, account, domain); if (strlen(domain) < 1) pszDomain = NULL; else pszDomain = domain; hUser = GetUserHandle(account, pszDomain, password, nError); if (hUser == INVALID_HANDLE_VALUE) { strcpy(pszError, "LogonUser failed, "); FreeEnvironmentStrings((TCHAR*)pEnv); SetCurrentDirectory(tSavedPath); RemoveEnvironmentVariables(env); CloseHandle(*hIn); CloseHandle(*hOut); CloseHandle(*hErr); goto RESTORE_CLEANUP; } if (ImpersonateLoggedOnUser(hUser)) { if (!MapUserDrives(map, domainaccount, password, pszError)) { err_printf("LaunchProcessLogon:MapUserDrives(%s, %s) failed, %s", map, domainaccount, pszError); } GetCurrentDirectory(MAX_PATH, tSavedPath); SetCurrentDirectory(dir); launch_flag = //DETACHED_PROCESS | IDLE_PRIORITY_CLASS; //CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS; //CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS; //CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS | CREATE_NEW_PROCESS_GROUP; //DETACHED_PROCESS | IDLE_PRIORITY_CLASS | CREATE_NEW_PROCESS_GROUP; //CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS | CREATE_SUSPENDED; CREATE_SUSPENDED | CREATE_NO_WINDOW | priorityClass; if (bDebug) launch_flag = launch_flag | DEBUG_PROCESS; #ifdef USE_WINDOW_STATIONS AttachToWorkstation(); #endif /*PrintPriorities(priorityClass, priority);*/ num_tries = 4; do { if (CreateProcessAsUser( hUser, NULL, cmd, NULL, NULL, TRUE, launch_flag, pEnv, NULL, &saInfo, &psInfo)) { SetThreadPriority(psInfo.hThread, priority); DWORD dwClass, dwPriority; dwClass = GetPriorityClass(psInfo.hProcess); dwPriority = GetThreadPriority(psInfo.hThread); PrintPriorities(dwClass, dwPriority); ResumeThread(psInfo.hThread); hRetVal = psInfo.hProcess; *pdwPid = psInfo.dwProcessId; CloseHandle(psInfo.hThread); num_tries = 0; } else { error = GetLastError(); if (error == ERROR_REQ_NOT_ACCEP) { Sleep(1000); num_tries--; if (num_tries == 0) { *nError = error; strcpy(pszError, "CreateProcessAsUser failed, "); } } else { *nError = error; strcpy(pszError, "CreateProcessAsUser failed, "); num_tries = 0; } } } while (num_tries); //RevertToSelf(); // If you call RevertToSelf, the network mapping goes away. #ifdef USE_WINDOW_STATIONS DetachFromWorkstation(); #endif } else { *nError = GetLastError(); strcpy(pszError, "ImpersonateLoggedOnUser failed, "); } FreeEnvironmentStrings((TCHAR*)pEnv); SetCurrentDirectory(tSavedPath); RemoveEnvironmentVariables(env); RESTORE_CLEANUP: // Restore stdin, stdout, stderr SetStdHandle(STD_INPUT_HANDLE, hStdin); SetStdHandle(STD_OUTPUT_HANDLE, hStdout); SetStdHandle(STD_ERROR_HANDLE, hStderr); CLEANUP: ReleaseMutex(g_hLaunchMutex); CloseHandle(hPipeStdinR); CloseHandle(hPipeStdoutW); CloseHandle(hPipeStderrW); #ifdef USE_SET_ERROR_MODE SetErrorMode(dwOriginalErrorMode); #endif return hRetVal; }
bool StartProcess(Settings& settings, HANDLE hCmdPipe) { //Launching as one of: //1. System Account //2. Specified account (or limited account) //3. As current process DWORD gle = 0; BOOL bLoadedProfile = FALSE; PROFILEINFO profile = {0}; profile.dwSize = sizeof(profile); profile.lpUserName = (LPWSTR)(LPCWSTR)settings.user; profile.dwFlags = PI_NOUI; if(false == GetUserHandle(settings, bLoadedProfile, profile, hCmdPipe)) return false; PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; if(!BAD_HANDLE(settings.hStdErr)) { si.hStdError = settings.hStdErr; si.hStdInput = settings.hStdIn; si.hStdOutput = settings.hStdOut; si.dwFlags |= STARTF_USESTDHANDLES; #ifdef _DEBUG Log(L"DEBUG: Using redirected handles", false); #endif } #ifdef _DEBUG else Log(L"DEBUG: Not using redirected IO", false); #endif CString path = StrFormat(L"\"%s\"", settings.app); if(FALSE == settings.appArgs.IsEmpty()) { path += L" "; path += settings.appArgs; } LPCWSTR startingDir = NULL; if(FALSE == settings.workingDir.IsEmpty()) startingDir = settings.workingDir; DWORD launchGLE = 0; CleanupInteractive ci = {0}; if(settings.bInteractive || settings.bShowUIOnWinLogon) { BOOL b = PrepForInteractiveProcess(settings, &ci, settings.sessionToInteractWith); if(FALSE == b) Log(L"Failed to PrepForInteractiveProcess", true); if(NULL == si.lpDesktop) si.lpDesktop = L"WinSta0\\Default"; if(settings.bShowUIOnWinLogon) si.lpDesktop = L"winsta0\\Winlogon"; //Log(StrFormat(L"Using desktop: %s", si.lpDesktop), false); //http://blogs.msdn.com/b/winsdk/archive/2009/07/14/launching-an-interactive-process-from-windows-service-in-windows-vista-and-later.aspx //indicates desktop names are case sensitive } #ifdef _DEBUG Log(StrFormat(L"DEBUG: PAExec using desktop %s", si.lpDesktop == NULL ? L"{default}" : si.lpDesktop), false); #endif DWORD dwFlags = CREATE_SUSPENDED | CREATE_NEW_CONSOLE; LPVOID pEnvironment = NULL; VERIFY(CreateEnvironmentBlock(&pEnvironment, settings.hUser, TRUE)); if(NULL != pEnvironment) dwFlags |= CREATE_UNICODE_ENVIRONMENT; #ifdef _DEBUG gle = GetLastError(); Log(L"DEBUG: CreateEnvironmentBlock", gle); #endif if(settings.bDisableFileRedirection) DisableFileRedirection(); if(settings.bRunLimited) if(false == LimitRights(settings.hUser)) return false; if(settings.bRunElevated) if(false == ElevateUserToken(settings.hUser)) return false; CString user, domain; GetUserDomain(settings.user, user, domain); #ifdef _DEBUG Log(StrFormat(L"DEBUG: U:%s D:%s P:%s bP:%d Env:%s WD:%s", user, domain, settings.password, settings.bDontLoadProfile, pEnvironment ? L"true" : L"null", startingDir ? startingDir : L"null"), false); #endif BOOL bLaunched = FALSE; if(settings.bUseSystemAccount) { Log(StrFormat(L"PAExec starting process [%s] as Local System", path), false); if(BAD_HANDLE(settings.hUser)) Log(L"Have bad user handle", true); EnablePrivilege(SE_IMPERSONATE_NAME); BOOL bImpersonated = ImpersonateLoggedOnUser(settings.hUser); if(FALSE == bImpersonated) { Log(L"Failed to impersonate", GetLastError()); _ASSERT(bImpersonated); } EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME); EnablePrivilege(SE_INCREASE_QUOTA_NAME); bLaunched = CreateProcessAsUser(settings.hUser, NULL, path.LockBuffer(), NULL, NULL, TRUE, dwFlags, pEnvironment, startingDir, &si, &pi); launchGLE = GetLastError(); path.UnlockBuffer(); #ifdef _DEBUG if(0 != launchGLE) Log(StrFormat(L"Launch (launchGLE=%u) params: user=[x%X] path=[%s] flags=[x%X], pEnv=[%s], dir=[%s], stdin=[x%X], stdout=[x%X], stderr=[x%X]", launchGLE, (DWORD)settings.hUser, path, dwFlags, pEnvironment ? L"{env}" : L"{null}", startingDir ? startingDir : L"{null}", (DWORD)si.hStdInput, (DWORD)si.hStdOutput, (DWORD)si.hStdError), false); #endif RevertToSelf(); } else { if(FALSE == settings.user.IsEmpty()) //launching as a specific user { Log(StrFormat(L"PAExec starting process [%s] as %s", path, settings.user), false); if(false == settings.bRunLimited) { bLaunched = CreateProcessWithLogonW(user, domain.IsEmpty() ? NULL : domain, settings.password, settings.bDontLoadProfile ? 0 : LOGON_WITH_PROFILE, NULL, path.LockBuffer(), dwFlags, pEnvironment, startingDir, &si, &pi); launchGLE = GetLastError(); path.UnlockBuffer(); #ifdef _DEBUG if(0 != launchGLE) Log(StrFormat(L"Launch (launchGLE=%u) params: user=[%s] domain=[%s] prof=[x%X] path=[%s] flags=[x%X], pEnv=[%s], dir=[%s], stdin=[x%X], stdout=[x%X], stderr=[x%X]", launchGLE, user, domain, settings.bDontLoadProfile ? 0 : LOGON_WITH_PROFILE, path, dwFlags, pEnvironment ? L"{env}" : L"{null}", startingDir ? startingDir : L"{null}", (DWORD)si.hStdInput, (DWORD)si.hStdOutput, (DWORD)si.hStdError), false); #endif } else bLaunched = FALSE; //force to run with CreateProcessAsUser so rights can be limited //CreateProcessWithLogonW can't be called from LocalSystem on Win2003 and earlier, so LogonUser/CreateProcessAsUser must be used. Might as well try for everyone if((FALSE == bLaunched) && !BAD_HANDLE(settings.hUser)) { #ifdef _DEBUG Log(L"DEBUG: Failed CreateProcessWithLogonW - trying CreateProcessAsUser", GetLastError()); #endif EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME); EnablePrivilege(SE_INCREASE_QUOTA_NAME); EnablePrivilege(SE_IMPERSONATE_NAME); BOOL bImpersonated = ImpersonateLoggedOnUser(settings.hUser); if(FALSE == bImpersonated) { Log(L"Failed to impersonate", GetLastError()); _ASSERT(bImpersonated); } bLaunched = CreateProcessAsUser(settings.hUser, NULL, path.LockBuffer(), NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, pEnvironment, startingDir, &si, &pi); if(0 == GetLastError()) launchGLE = 0; //mark as successful, otherwise return our original error path.UnlockBuffer(); #ifdef _DEBUG if(0 != launchGLE) Log(StrFormat(L"Launch (launchGLE=%u) params: user=[x%X] path=[%s] pEnv=[%s], dir=[%s], stdin=[x%X], stdout=[x%X], stderr=[x%X]", launchGLE, (DWORD)settings.hUser, path, pEnvironment ? L"{env}" : L"{null}", startingDir ? startingDir : L"{null}", (DWORD)si.hStdInput, (DWORD)si.hStdOutput, (DWORD)si.hStdError), false); #endif RevertToSelf(); } } else { Log(StrFormat(L"PAExec starting process [%s] as current user", path), false); EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME); EnablePrivilege(SE_INCREASE_QUOTA_NAME); EnablePrivilege(SE_IMPERSONATE_NAME); if(NULL != settings.hUser) bLaunched = CreateProcessAsUser(settings.hUser, NULL, path.LockBuffer(), NULL, NULL, TRUE, dwFlags, pEnvironment, startingDir, &si, &pi); if(FALSE == bLaunched) bLaunched = CreateProcess(NULL, path.LockBuffer(), NULL, NULL, TRUE, dwFlags, pEnvironment, startingDir, &si, &pi); launchGLE = GetLastError(); //#ifdef _DEBUG if(0 != launchGLE) Log(StrFormat(L"Launch (launchGLE=%u) params: path=[%s] user=[%s], pEnv=[%s], dir=[%s], stdin=[x%X], stdout=[x%X], stderr=[x%X]", launchGLE, path, settings.hUser ? L"{non-null}" : L"{null}", pEnvironment ? L"{env}" : L"{null}", startingDir ? startingDir : L"{null}", (DWORD)si.hStdInput, (DWORD)si.hStdOutput, (DWORD)si.hStdError), false); //#endif path.UnlockBuffer(); } } if(bLaunched) { if(gbInService) Log(L"Successfully launched", false); settings.hProcess = pi.hProcess; settings.processID = pi.dwProcessId; if(false == settings.allowedProcessors.empty()) { DWORD sysMask = 0, procMask = 0; VERIFY(GetProcessAffinityMask(pi.hProcess, &procMask, &sysMask)); procMask = 0; for(std::vector<WORD>::iterator itr = settings.allowedProcessors.begin(); settings.allowedProcessors.end() != itr; itr++) { DWORD bit = 1; bit = bit << (*itr - 1); procMask |= bit & sysMask; } VERIFY(SetProcessAffinityMask(pi.hProcess, procMask)); } VERIFY(SetPriorityClass(pi.hProcess, settings.priority)); ResumeThread(pi.hThread); VERIFY(CloseHandle(pi.hThread)); } else { Log(StrFormat(L"Failed to start %s.", path), launchGLE); if((ERROR_ELEVATION_REQUIRED == launchGLE) && (false == gbInService)) Log(L"HINT: PAExec probably needs to be \"Run As Administrator\"", false); } if(ci.bPreped) CleanUpInteractiveProcess(&ci); if(settings.bDisableFileRedirection) RevertFileRedirection(); if(NULL != pEnvironment) DestroyEnvironmentBlock(pEnvironment); pEnvironment = NULL; if(bLoadedProfile) UnloadUserProfile(settings.hUser, profile.hProfile); if(!BAD_HANDLE(settings.hUser)) { CloseHandle(settings.hUser); settings.hUser = NULL; } return bLaunched ? true : false; }