diff options
Diffstat (limited to '')
-rw-r--r-- | winpr/libwinpr/thread/process.c | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/winpr/libwinpr/thread/process.c b/winpr/libwinpr/thread/process.c new file mode 100644 index 0000000..0dbd940 --- /dev/null +++ b/winpr/libwinpr/thread/process.c @@ -0,0 +1,598 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * + * 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 <winpr/config.h> + +#include <winpr/handle.h> +#include "../handle/nonehandle.h" + +#include <winpr/thread.h> + +/** + * CreateProcessA + * CreateProcessW + * CreateProcessAsUserA + * CreateProcessAsUserW + * ExitProcess + * GetCurrentProcess + * GetCurrentProcessId + * GetExitCodeProcess + * GetProcessHandleCount + * GetProcessId + * GetProcessIdOfThread + * GetProcessMitigationPolicy + * GetProcessTimes + * GetProcessVersion + * OpenProcess + * OpenProcessToken + * ProcessIdToSessionId + * SetProcessAffinityUpdateMode + * SetProcessMitigationPolicy + * SetProcessShutdownParameters + * TerminateProcess + */ + +#ifndef _WIN32 + +#include <winpr/assert.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/environment.h> + +#include <grp.h> + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +#ifdef __linux__ +#include <sys/syscall.h> +#include <fcntl.h> +#include <errno.h> +#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 |