FreeRDP
process.c
1 
21 #include <winpr/config.h>
22 
23 #include <winpr/handle.h>
24 #include "../handle/nonehandle.h"
25 
26 #include <winpr/thread.h>
27 
52 #ifndef _WIN32
53 
54 #include <winpr/assert.h>
55 #include <winpr/crt.h>
56 #include <winpr/path.h>
57 #include <winpr/environment.h>
58 
59 #include <grp.h>
60 
61 #include <signal.h>
62 #include <sys/types.h>
63 #include <sys/wait.h>
64 
65 #ifdef __linux__
66 #include <sys/syscall.h>
67 #include <fcntl.h>
68 #include <errno.h>
69 #endif /* __linux__ */
70 
71 #include "thread.h"
72 
73 #include "../security/security.h"
74 
75 #ifndef NSIG
76 #ifdef SIGMAX
77 #define NSIG SIGMAX
78 #else
79 #define NSIG 64
80 #endif
81 #endif
82 
98 static char* FindApplicationPath(char* application)
99 {
100  LPCSTR pathName = "PATH";
101  char* path = NULL;
102  char* save = NULL;
103  DWORD nSize = 0;
104  LPSTR lpSystemPath = NULL;
105  char* filename = NULL;
106 
107  if (!application)
108  return NULL;
109 
110  if (application[0] == '/')
111  return _strdup(application);
112 
113  nSize = GetEnvironmentVariableA(pathName, NULL, 0);
114 
115  if (!nSize)
116  return _strdup(application);
117 
118  lpSystemPath = (LPSTR)malloc(nSize);
119 
120  if (!lpSystemPath)
121  return NULL;
122 
123  if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1)
124  {
125  free(lpSystemPath);
126  return NULL;
127  }
128 
129  save = NULL;
130  path = strtok_s(lpSystemPath, ":", &save);
131 
132  while (path)
133  {
134  filename = GetCombinedPath(path, application);
135 
136  if (winpr_PathFileExists(filename))
137  {
138  break;
139  }
140 
141  free(filename);
142  filename = NULL;
143  path = strtok_s(NULL, ":", &save);
144  }
145 
146  free(lpSystemPath);
147  return filename;
148 }
149 
150 static HANDLE CreateProcessHandle(pid_t pid);
151 static BOOL ProcessHandleCloseHandle(HANDLE handle);
152 
153 static BOOL CreateProcessExA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName,
154  LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
155  LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
156  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
157  LPSTARTUPINFOA lpStartupInfo,
158  LPPROCESS_INFORMATION lpProcessInformation)
159 {
160  pid_t pid = 0;
161  int numArgs = 0;
162  LPSTR* pArgs = NULL;
163  char** envp = NULL;
164  char* filename = NULL;
165  HANDLE thread = NULL;
166  HANDLE process = NULL;
167  WINPR_ACCESS_TOKEN* token = NULL;
168  LPTCH lpszEnvironmentBlock = NULL;
169  BOOL ret = FALSE;
170  sigset_t oldSigMask;
171  sigset_t newSigMask;
172  BOOL restoreSigMask = FALSE;
173  numArgs = 0;
174  lpszEnvironmentBlock = NULL;
175  /* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
176  */
177  if (lpCommandLine)
178  pArgs = CommandLineToArgvA(lpCommandLine, &numArgs);
179  else
180  pArgs = CommandLineToArgvA(lpApplicationName, &numArgs);
181 
182  if (!pArgs)
183  return FALSE;
184 
185  token = (WINPR_ACCESS_TOKEN*)hToken;
186 
187  if (lpEnvironment)
188  {
189  envp = EnvironmentBlockToEnvpA(lpEnvironment);
190  }
191  else
192  {
193  lpszEnvironmentBlock = GetEnvironmentStrings();
194 
195  if (!lpszEnvironmentBlock)
196  goto finish;
197 
198  envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock);
199  }
200 
201  if (!envp)
202  goto finish;
203 
204  filename = FindApplicationPath(pArgs[0]);
205 
206  if (NULL == filename)
207  goto finish;
208 
209  /* block all signals so that the child can safely reset the caller's handlers */
210  sigfillset(&newSigMask);
211  restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask);
212  /* fork and exec */
213  pid = fork();
214 
215  if (pid < 0)
216  {
217  /* fork failure */
218  goto finish;
219  }
220 
221  if (pid == 0)
222  {
223  /* child process */
224 #ifndef __sun
225  int maxfd = 0;
226 #endif
227  sigset_t set = { 0 };
228  struct sigaction act = { 0 };
229  /* set default signal handlers */
230  act.sa_handler = SIG_DFL;
231  act.sa_flags = 0;
232  sigemptyset(&act.sa_mask);
233 
234  for (int sig = 1; sig < NSIG; sig++)
235  sigaction(sig, &act, NULL);
236 
237  /* unblock all signals */
238  sigfillset(&set);
239  pthread_sigmask(SIG_UNBLOCK, &set, NULL);
240 
241  if (lpStartupInfo)
242  {
243  int handle_fd = 0;
244  handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput);
245 
246  if (handle_fd != -1)
247  dup2(handle_fd, STDOUT_FILENO);
248 
249  handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError);
250 
251  if (handle_fd != -1)
252  dup2(handle_fd, STDERR_FILENO);
253 
254  handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput);
255 
256  if (handle_fd != -1)
257  dup2(handle_fd, STDIN_FILENO);
258  }
259 
260 #ifdef __sun
261  closefrom(3);
262 #else
263 #ifdef F_MAXFD // on some BSD derivates
264  maxfd = fcntl(0, F_MAXFD);
265 #else
266  {
267  const long rc = sysconf(_SC_OPEN_MAX);
268  if ((rc < INT32_MIN) || (rc > INT32_MAX))
269  goto finish;
270  maxfd = (int)rc;
271  }
272 #endif
273 
274  for (int fd = 3; fd < maxfd; fd++)
275  close(fd);
276 
277 #endif // __sun
278 
279  if (token)
280  {
281  if (token->GroupId)
282  {
283  int rc = setgid((gid_t)token->GroupId);
284 
285  if (rc < 0)
286  {
287  }
288  else
289  {
290  initgroups(token->Username, (gid_t)token->GroupId);
291  }
292  }
293 
294  if (token->UserId)
295  {
296  int rc = setuid((uid_t)token->UserId);
297  if (rc != 0)
298  goto finish;
299  }
300  }
301 
302  /* TODO: add better cwd handling and error checking */
303  if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0)
304  {
305  int rc = chdir(lpCurrentDirectory);
306  if (rc != 0)
307  goto finish;
308  }
309 
310  if (execve(filename, pArgs, envp) < 0)
311  {
312  /* execve failed - end the process */
313  _exit(1);
314  }
315  }
316  else
317  {
318  /* parent process */
319  }
320 
321  process = CreateProcessHandle(pid);
322 
323  if (!process)
324  {
325  goto finish;
326  }
327 
328  thread = CreateNoneHandle();
329 
330  if (!thread)
331  {
332  ProcessHandleCloseHandle(process);
333  goto finish;
334  }
335 
336  lpProcessInformation->hProcess = process;
337  lpProcessInformation->hThread = thread;
338  lpProcessInformation->dwProcessId = (DWORD)pid;
339  lpProcessInformation->dwThreadId = (DWORD)pid;
340  ret = TRUE;
341 finish:
342 
343  /* restore caller's original signal mask */
344  if (restoreSigMask)
345  pthread_sigmask(SIG_SETMASK, &oldSigMask, NULL);
346 
347  free(filename);
348  free(pArgs);
349 
350  if (lpszEnvironmentBlock)
351  FreeEnvironmentStrings(lpszEnvironmentBlock);
352 
353  if (envp)
354  {
355  int i = 0;
356 
357  while (envp[i])
358  {
359  free(envp[i]);
360  i++;
361  }
362 
363  free(envp);
364  }
365 
366  return ret;
367 }
368 
369 BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine,
370  LPSECURITY_ATTRIBUTES lpProcessAttributes,
371  LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
372  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
373  LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
374 {
375  return CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
376  lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
377  lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
378 }
379 
380 BOOL CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
381  LPSECURITY_ATTRIBUTES lpProcessAttributes,
382  LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
383  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
384  LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
385 {
386  return FALSE;
387 }
388 
389 BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine,
390  LPSECURITY_ATTRIBUTES lpProcessAttributes,
391  LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
392  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
393  LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
394 {
395  return CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
396  lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
397  lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
398 }
399 
400 BOOL CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
401  LPSECURITY_ATTRIBUTES lpProcessAttributes,
402  LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
403  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
404  LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
405 {
406  return FALSE;
407 }
408 
409 BOOL CreateProcessWithLogonA(LPCSTR lpUsername, LPCSTR lpDomain, LPCSTR lpPassword,
410  DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine,
411  DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
412  LPSTARTUPINFOA lpStartupInfo,
413  LPPROCESS_INFORMATION lpProcessInformation)
414 {
415  return FALSE;
416 }
417 
418 BOOL CreateProcessWithLogonW(LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword,
419  DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
420  DWORD dwCreationFlags, LPVOID lpEnvironment,
421  LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
422  LPPROCESS_INFORMATION lpProcessInformation)
423 {
424  return FALSE;
425 }
426 
427 BOOL CreateProcessWithTokenA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName,
428  LPSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment,
429  LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
430  LPPROCESS_INFORMATION lpProcessInformation)
431 {
432  return CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, NULL, NULL, FALSE,
433  dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo,
434  lpProcessInformation);
435 }
436 
437 BOOL CreateProcessWithTokenW(HANDLE hToken, DWORD dwLogonFlags, LPCWSTR lpApplicationName,
438  LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment,
439  LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
440  LPPROCESS_INFORMATION lpProcessInformation)
441 {
442  return FALSE;
443 }
444 
445 VOID ExitProcess(UINT uExitCode)
446 {
447  exit((int)uExitCode);
448 }
449 
450 BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
451 {
452  WINPR_PROCESS* process = NULL;
453 
454  if (!hProcess)
455  return FALSE;
456 
457  if (!lpExitCode)
458  return FALSE;
459 
460  process = (WINPR_PROCESS*)hProcess;
461  *lpExitCode = process->dwExitCode;
462  return TRUE;
463 }
464 
465 HANDLE _GetCurrentProcess(VOID)
466 {
467  return NULL;
468 }
469 
470 DWORD GetCurrentProcessId(VOID)
471 {
472  return ((DWORD)getpid());
473 }
474 
475 BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode)
476 {
477  WINPR_PROCESS* process = NULL;
478  process = (WINPR_PROCESS*)hProcess;
479 
480  if (!process || (process->pid <= 0))
481  return FALSE;
482 
483  if (kill(process->pid, SIGTERM))
484  return FALSE;
485 
486  return TRUE;
487 }
488 
489 static BOOL ProcessHandleCloseHandle(HANDLE handle)
490 {
491  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
492  WINPR_ASSERT(process);
493  if (process->fd >= 0)
494  {
495  close(process->fd);
496  process->fd = -1;
497  }
498  free(process);
499  return TRUE;
500 }
501 
502 static BOOL ProcessHandleIsHandle(HANDLE handle)
503 {
504  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE);
505 }
506 
507 static int ProcessGetFd(HANDLE handle)
508 {
509  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
510 
511  if (!ProcessHandleIsHandle(handle))
512  return -1;
513 
514  return process->fd;
515 }
516 
517 static DWORD ProcessCleanupHandle(HANDLE handle)
518 {
519  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
520 
521  WINPR_ASSERT(process);
522  if (process->fd > 0)
523  {
524  if (waitpid(process->pid, &process->status, WNOHANG) == process->pid)
525  process->dwExitCode = (DWORD)process->status;
526  }
527  return WAIT_OBJECT_0;
528 }
529 
530 static HANDLE_OPS ops = { ProcessHandleIsHandle,
531  ProcessHandleCloseHandle,
532  ProcessGetFd,
533  ProcessCleanupHandle, /* CleanupHandle */
534  NULL,
535  NULL,
536  NULL,
537  NULL,
538  NULL,
539  NULL,
540  NULL,
541  NULL,
542  NULL,
543  NULL,
544  NULL,
545  NULL,
546  NULL,
547  NULL,
548  NULL,
549  NULL,
550  NULL };
551 
552 static int pidfd_open(pid_t pid)
553 {
554 #ifdef __linux__
555 #if !defined(__NR_pidfd_open)
556 #define __NR_pidfd_open 434
557 #endif /* __NR_pidfd_open */
558 
559 #ifndef PIDFD_NONBLOCK
560 #define PIDFD_NONBLOCK O_NONBLOCK
561 #endif /* PIDFD_NONBLOCK */
562 
563  long fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK);
564  if (fd < 0 && errno == EINVAL)
565  {
566  /* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it
567  * non blocking afterward */
568  int flags = 0;
569  fd = syscall(__NR_pidfd_open, pid, 0);
570  if ((fd < 0) || (fd > INT32_MAX))
571  return -1;
572 
573  flags = fcntl((int)fd, F_GETFL);
574  if ((flags < 0) || fcntl((int)fd, F_SETFL, flags | O_NONBLOCK) < 0)
575  {
576  close((int)fd);
577  fd = -1;
578  }
579  }
580  if ((fd < 0) || (fd > INT32_MAX))
581  return -1;
582  return (int)fd;
583 #else
584  return -1;
585 #endif
586 }
587 
588 HANDLE CreateProcessHandle(pid_t pid)
589 {
590  WINPR_PROCESS* process = NULL;
591  process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS));
592 
593  if (!process)
594  return NULL;
595 
596  process->pid = pid;
597  process->common.Type = HANDLE_TYPE_PROCESS;
598  process->common.ops = &ops;
599  process->fd = pidfd_open(pid);
600  if (process->fd >= 0)
601  process->common.Mode = WINPR_FD_READ;
602  return (HANDLE)process;
603 }
604 
605 #endif