/** * WinPR: Windows Portable Runtime * Process Thread Functions * * Copyright 2012 Marc-Andre Moreau * Copyright 2014 DI (FH) Martin Haimberger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../handle/nonehandle.h" #include /** * CreateProcessA * CreateProcessW * CreateProcessAsUserA * CreateProcessAsUserW * ExitProcess * GetCurrentProcess * GetCurrentProcessId * GetExitCodeProcess * GetProcessHandleCount * GetProcessId * GetProcessIdOfThread * GetProcessMitigationPolicy * GetProcessTimes * GetProcessVersion * OpenProcess * OpenProcessToken * ProcessIdToSessionId * SetProcessAffinityUpdateMode * SetProcessMitigationPolicy * SetProcessShutdownParameters * TerminateProcess */ #ifndef _WIN32 #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #include #endif /* __linux__ */ #include "thread.h" #include "../security/security.h" #ifndef NSIG #ifdef SIGMAX #define NSIG SIGMAX #else #define NSIG 64 #endif #endif /** * If the file name does not contain a directory path, the system searches for the executable file * in the following sequence: * * 1) The directory from which the application loaded. * 2) The current directory for the parent process. * 3) The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of * this directory. 4) The 16-bit Windows system directory. There is no function that obtains the * path of this directory, but it is searched. The name of this directory is System. 5) The Windows * directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The * directories that are listed in the PATH environment variable. Note that this function does not * search the per-application path specified by the App Paths registry key. To include this * per-application path in the search sequence, use the ShellExecute function. */ static char* FindApplicationPath(char* application) { LPCSTR pathName = "PATH"; char* path = NULL; char* save = NULL; DWORD nSize = 0; LPSTR lpSystemPath = NULL; char* filename = NULL; if (!application) return NULL; if (application[0] == '/') return _strdup(application); nSize = GetEnvironmentVariableA(pathName, NULL, 0); if (!nSize) return _strdup(application); lpSystemPath = (LPSTR)malloc(nSize); if (!lpSystemPath) return NULL; if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1) { free(lpSystemPath); return NULL; } save = NULL; path = strtok_s(lpSystemPath, ":", &save); while (path) { filename = GetCombinedPath(path, application); if (winpr_PathFileExists(filename)) { break; } free(filename); filename = NULL; path = strtok_s(NULL, ":", &save); } free(lpSystemPath); return filename; } static HANDLE CreateProcessHandle(pid_t pid); static BOOL ProcessHandleCloseHandle(HANDLE handle); static BOOL CreateProcessExA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { pid_t pid = 0; int numArgs = 0; LPSTR* pArgs = NULL; char** envp = NULL; char* filename = NULL; HANDLE thread = NULL; HANDLE process = NULL; WINPR_ACCESS_TOKEN* token = NULL; LPTCH lpszEnvironmentBlock = NULL; BOOL ret = FALSE; sigset_t oldSigMask; sigset_t newSigMask; BOOL restoreSigMask = FALSE; numArgs = 0; lpszEnvironmentBlock = NULL; /* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa */ if (lpCommandLine) pArgs = CommandLineToArgvA(lpCommandLine, &numArgs); else pArgs = CommandLineToArgvA(lpApplicationName, &numArgs); if (!pArgs) return FALSE; token = (WINPR_ACCESS_TOKEN*)hToken; if (lpEnvironment) { envp = EnvironmentBlockToEnvpA(lpEnvironment); } else { lpszEnvironmentBlock = GetEnvironmentStrings(); if (!lpszEnvironmentBlock) goto finish; envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock); } if (!envp) goto finish; filename = FindApplicationPath(pArgs[0]); if (NULL == filename) goto finish; /* block all signals so that the child can safely reset the caller's handlers */ sigfillset(&newSigMask); restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask); /* fork and exec */ pid = fork(); if (pid < 0) { /* fork failure */ goto finish; } if (pid == 0) { /* child process */ #ifndef __sun int maxfd = 0; #endif sigset_t set = { 0 }; struct sigaction act = { 0 }; /* set default signal handlers */ act.sa_handler = SIG_DFL; act.sa_flags = 0; sigemptyset(&act.sa_mask); for (int sig = 1; sig < NSIG; sig++) sigaction(sig, &act, NULL); /* unblock all signals */ sigfillset(&set); pthread_sigmask(SIG_UNBLOCK, &set, NULL); if (lpStartupInfo) { int handle_fd = 0; handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput); if (handle_fd != -1) dup2(handle_fd, STDOUT_FILENO); handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError); if (handle_fd != -1) dup2(handle_fd, STDERR_FILENO); handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput); if (handle_fd != -1) dup2(handle_fd, STDIN_FILENO); } #ifdef __sun closefrom(3); #else #ifdef F_MAXFD // on some BSD derivates maxfd = fcntl(0, F_MAXFD); #else maxfd = sysconf(_SC_OPEN_MAX); #endif for (int fd = 3; fd < maxfd; fd++) close(fd); #endif // __sun if (token) { if (token->GroupId) { int rc = setgid((gid_t)token->GroupId); if (rc < 0) { } else { initgroups(token->Username, (gid_t)token->GroupId); } } if (token->UserId) { int rc = setuid((uid_t)token->UserId); if (rc != 0) goto finish; } } /* TODO: add better cwd handling and error checking */ if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0) { int rc = chdir(lpCurrentDirectory); if (rc != 0) goto finish; } if (execve(filename, pArgs, envp) < 0) { /* execve failed - end the process */ _exit(1); } } else { /* parent process */ } process = CreateProcessHandle(pid); if (!process) { goto finish; } thread = CreateNoneHandle(); if (!thread) { ProcessHandleCloseHandle(process); goto finish; } lpProcessInformation->hProcess = process; lpProcessInformation->hThread = thread; lpProcessInformation->dwProcessId = (DWORD)pid; lpProcessInformation->dwThreadId = (DWORD)pid; ret = TRUE; finish: /* restore caller's original signal mask */ if (restoreSigMask) pthread_sigmask(SIG_SETMASK, &oldSigMask, NULL); free(filename); free(pArgs); if (lpszEnvironmentBlock) FreeEnvironmentStrings(lpszEnvironmentBlock); if (envp) { int i = 0; while (envp[i]) { free(envp[i]); i++; } free(envp); } return ret; } BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } BOOL CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return FALSE; } BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } BOOL CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return FALSE; } BOOL CreateProcessWithLogonA(LPCSTR lpUsername, LPCSTR lpDomain, LPCSTR lpPassword, DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return FALSE; } BOOL CreateProcessWithLogonW(LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return FALSE; } BOOL CreateProcessWithTokenA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, NULL, NULL, FALSE, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } BOOL CreateProcessWithTokenW(HANDLE hToken, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { return FALSE; } VOID ExitProcess(UINT uExitCode) { exit((int)uExitCode); } BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { WINPR_PROCESS* process = NULL; if (!hProcess) return FALSE; if (!lpExitCode) return FALSE; process = (WINPR_PROCESS*)hProcess; *lpExitCode = process->dwExitCode; return TRUE; } HANDLE _GetCurrentProcess(VOID) { return NULL; } DWORD GetCurrentProcessId(VOID) { return ((DWORD)getpid()); } BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode) { WINPR_PROCESS* process = NULL; process = (WINPR_PROCESS*)hProcess; if (!process || (process->pid <= 0)) return FALSE; if (kill(process->pid, SIGTERM)) return FALSE; return TRUE; } static BOOL ProcessHandleCloseHandle(HANDLE handle) { WINPR_PROCESS* process = (WINPR_PROCESS*)handle; WINPR_ASSERT(process); if (process->fd >= 0) { close(process->fd); process->fd = -1; } free(process); return TRUE; } static BOOL ProcessHandleIsHandle(HANDLE handle) { return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE); } static int ProcessGetFd(HANDLE handle) { WINPR_PROCESS* process = (WINPR_PROCESS*)handle; if (!ProcessHandleIsHandle(handle)) return -1; return process->fd; } static DWORD ProcessCleanupHandle(HANDLE handle) { WINPR_PROCESS* process = (WINPR_PROCESS*)handle; WINPR_ASSERT(process); if (process->fd > 0) { if (waitpid(process->pid, &process->status, WNOHANG) == process->pid) process->dwExitCode = (DWORD)process->status; } return WAIT_OBJECT_0; } static HANDLE_OPS ops = { ProcessHandleIsHandle, ProcessHandleCloseHandle, ProcessGetFd, ProcessCleanupHandle, /* CleanupHandle */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static int pidfd_open(pid_t pid) { #ifdef __linux__ #if !defined(__NR_pidfd_open) #define __NR_pidfd_open 434 #endif /* __NR_pidfd_open */ #ifndef PIDFD_NONBLOCK #define PIDFD_NONBLOCK O_NONBLOCK #endif /* PIDFD_NONBLOCK */ int fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK); if (fd < 0 && errno == EINVAL) { /* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it * non blocking afterward */ int flags = 0; fd = syscall(__NR_pidfd_open, pid, 0); if (fd < 0) return -1; flags = fcntl(fd, F_GETFL); if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { close(fd); fd = -1; } } return fd; #else return -1; #endif } HANDLE CreateProcessHandle(pid_t pid) { WINPR_PROCESS* process = NULL; process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS)); if (!process) return NULL; process->pid = pid; process->common.Type = HANDLE_TYPE_PROCESS; process->common.ops = &ops; process->fd = pidfd_open(pid); if (process->fd >= 0) process->common.Mode = WINPR_FD_READ; return (HANDLE)process; } #endif