Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/thread/process.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Process Thread Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/config.h>
22
23
#include <winpr/handle.h>
24
#include "../handle/nonehandle.h"
25
26
#include <winpr/thread.h>
27
28
/**
29
 * CreateProcessA
30
 * CreateProcessW
31
 * CreateProcessAsUserA
32
 * CreateProcessAsUserW
33
 * ExitProcess
34
 * GetCurrentProcess
35
 * GetCurrentProcessId
36
 * GetExitCodeProcess
37
 * GetProcessHandleCount
38
 * GetProcessId
39
 * GetProcessIdOfThread
40
 * GetProcessMitigationPolicy
41
 * GetProcessTimes
42
 * GetProcessVersion
43
 * OpenProcess
44
 * OpenProcessToken
45
 * ProcessIdToSessionId
46
 * SetProcessAffinityUpdateMode
47
 * SetProcessMitigationPolicy
48
 * SetProcessShutdownParameters
49
 * TerminateProcess
50
 */
51
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
83
/**
84
 * If the file name does not contain a directory path, the system searches for the executable file
85
 * in the following sequence:
86
 *
87
 * 1) The directory from which the application loaded.
88
 * 2) The current directory for the parent process.
89
 * 3) The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of
90
 * this directory. 4) The 16-bit Windows system directory. There is no function that obtains the
91
 * path of this directory, but it is searched. The name of this directory is System. 5) The Windows
92
 * directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The
93
 * directories that are listed in the PATH environment variable. Note that this function does not
94
 * search the per-application path specified by the App Paths registry key. To include this
95
 * per-application path in the search sequence, use the ShellExecute function.
96
 */
97
98
static char* FindApplicationPath(char* application)
99
0
{
100
0
  LPCSTR pathName = "PATH";
101
0
  char* path = NULL;
102
0
  char* save = NULL;
103
0
  DWORD nSize = 0;
104
0
  LPSTR lpSystemPath = NULL;
105
0
  char* filename = NULL;
106
107
0
  if (!application)
108
0
    return NULL;
109
110
0
  if (application[0] == '/')
111
0
    return _strdup(application);
112
113
0
  nSize = GetEnvironmentVariableA(pathName, NULL, 0);
114
115
0
  if (!nSize)
116
0
    return _strdup(application);
117
118
0
  lpSystemPath = (LPSTR)malloc(nSize);
119
120
0
  if (!lpSystemPath)
121
0
    return NULL;
122
123
0
  if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1)
124
0
  {
125
0
    free(lpSystemPath);
126
0
    return NULL;
127
0
  }
128
129
0
  save = NULL;
130
0
  path = strtok_s(lpSystemPath, ":", &save);
131
132
0
  while (path)
133
0
  {
134
0
    filename = GetCombinedPath(path, application);
135
136
0
    if (winpr_PathFileExists(filename))
137
0
    {
138
0
      break;
139
0
    }
140
141
0
    free(filename);
142
0
    filename = NULL;
143
0
    path = strtok_s(NULL, ":", &save);
144
0
  }
145
146
0
  free(lpSystemPath);
147
0
  return filename;
148
0
}
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,
157
                              LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
158
                              LPPROCESS_INFORMATION lpProcessInformation)
159
0
{
160
0
  pid_t pid = 0;
161
0
  int numArgs = 0;
162
0
  LPSTR* pArgs = NULL;
163
0
  char** envp = NULL;
164
0
  char* filename = NULL;
165
0
  HANDLE thread = NULL;
166
0
  HANDLE process = NULL;
167
0
  WINPR_ACCESS_TOKEN* token = NULL;
168
0
  LPTCH lpszEnvironmentBlock = NULL;
169
0
  BOOL ret = FALSE;
170
0
  sigset_t oldSigMask;
171
0
  sigset_t newSigMask;
172
0
  BOOL restoreSigMask = FALSE;
173
0
  numArgs = 0;
174
0
  lpszEnvironmentBlock = NULL;
175
  /* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
176
   */
177
0
  if (lpCommandLine)
178
0
    pArgs = CommandLineToArgvA(lpCommandLine, &numArgs);
179
0
  else
180
0
    pArgs = CommandLineToArgvA(lpApplicationName, &numArgs);
181
182
0
  if (!pArgs)
183
0
    return FALSE;
184
185
0
  token = (WINPR_ACCESS_TOKEN*)hToken;
186
187
0
  if (lpEnvironment)
188
0
  {
189
0
    envp = EnvironmentBlockToEnvpA(lpEnvironment);
190
0
  }
191
0
  else
192
0
  {
193
0
    lpszEnvironmentBlock = GetEnvironmentStrings();
194
195
0
    if (!lpszEnvironmentBlock)
196
0
      goto finish;
197
198
0
    envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock);
199
0
  }
200
201
0
  if (!envp)
202
0
    goto finish;
203
204
0
  filename = FindApplicationPath(pArgs[0]);
205
206
0
  if (NULL == filename)
207
0
    goto finish;
208
209
  /* block all signals so that the child can safely reset the caller's handlers */
210
0
  sigfillset(&newSigMask);
211
0
  restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask);
212
  /* fork and exec */
213
0
  pid = fork();
214
215
0
  if (pid < 0)
216
0
  {
217
    /* fork failure */
218
0
    goto finish;
219
0
  }
220
221
0
  if (pid == 0)
222
0
  {
223
    /* child process */
224
0
#ifndef __sun
225
0
    int maxfd = 0;
226
0
#endif
227
0
    sigset_t set = { 0 };
228
0
    struct sigaction act = { 0 };
229
    /* set default signal handlers */
230
0
    act.sa_handler = SIG_DFL;
231
0
    act.sa_flags = 0;
232
0
    sigemptyset(&act.sa_mask);
233
234
0
    for (int sig = 1; sig < NSIG; sig++)
235
0
      sigaction(sig, &act, NULL);
236
237
    /* unblock all signals */
238
0
    sigfillset(&set);
239
0
    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
240
241
0
    if (lpStartupInfo)
242
0
    {
243
0
      int handle_fd = 0;
244
0
      handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput);
245
246
0
      if (handle_fd != -1)
247
0
        dup2(handle_fd, STDOUT_FILENO);
248
249
0
      handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError);
250
251
0
      if (handle_fd != -1)
252
0
        dup2(handle_fd, STDERR_FILENO);
253
254
0
      handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput);
255
256
0
      if (handle_fd != -1)
257
0
        dup2(handle_fd, STDIN_FILENO);
258
0
    }
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
0
    maxfd = sysconf(_SC_OPEN_MAX);
267
0
#endif
268
269
0
    for (int fd = 3; fd < maxfd; fd++)
270
0
      close(fd);
271
272
0
#endif // __sun
273
274
0
    if (token)
275
0
    {
276
0
      if (token->GroupId)
277
0
      {
278
0
        int rc = setgid((gid_t)token->GroupId);
279
280
0
        if (rc < 0)
281
0
        {
282
0
        }
283
0
        else
284
0
        {
285
0
          initgroups(token->Username, (gid_t)token->GroupId);
286
0
        }
287
0
      }
288
289
0
      if (token->UserId)
290
0
      {
291
0
        int rc = setuid((uid_t)token->UserId);
292
0
        if (rc != 0)
293
0
          goto finish;
294
0
      }
295
0
    }
296
297
    /* TODO: add better cwd handling and error checking */
298
0
    if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0)
299
0
    {
300
0
      int rc = chdir(lpCurrentDirectory);
301
0
      if (rc != 0)
302
0
        goto finish;
303
0
    }
304
305
0
    if (execve(filename, pArgs, envp) < 0)
306
0
    {
307
      /* execve failed - end the process */
308
0
      _exit(1);
309
0
    }
310
0
  }
311
0
  else
312
0
  {
313
    /* parent process */
314
0
  }
315
316
0
  process = CreateProcessHandle(pid);
317
318
0
  if (!process)
319
0
  {
320
0
    goto finish;
321
0
  }
322
323
0
  thread = CreateNoneHandle();
324
325
0
  if (!thread)
326
0
  {
327
0
    ProcessHandleCloseHandle(process);
328
0
    goto finish;
329
0
  }
330
331
0
  lpProcessInformation->hProcess = process;
332
0
  lpProcessInformation->hThread = thread;
333
0
  lpProcessInformation->dwProcessId = (DWORD)pid;
334
0
  lpProcessInformation->dwThreadId = (DWORD)pid;
335
0
  ret = TRUE;
336
0
finish:
337
338
  /* restore caller's original signal mask */
339
0
  if (restoreSigMask)
340
0
    pthread_sigmask(SIG_SETMASK, &oldSigMask, NULL);
341
342
0
  free(filename);
343
0
  free(pArgs);
344
345
0
  if (lpszEnvironmentBlock)
346
0
    FreeEnvironmentStrings(lpszEnvironmentBlock);
347
348
0
  if (envp)
349
0
  {
350
0
    int i = 0;
351
352
0
    while (envp[i])
353
0
    {
354
0
      free(envp[i]);
355
0
      i++;
356
0
    }
357
358
0
    free(envp);
359
0
  }
360
361
0
  return ret;
362
0
}
363
364
BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine,
365
                    LPSECURITY_ATTRIBUTES lpProcessAttributes,
366
                    LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
367
                    DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
368
                    LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
369
0
{
370
0
  return _CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
371
0
                           lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
372
0
                           lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
373
0
}
374
375
BOOL CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
376
                    LPSECURITY_ATTRIBUTES lpProcessAttributes,
377
                    LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
378
                    DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
379
                    LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
380
0
{
381
0
  return FALSE;
382
0
}
383
384
BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine,
385
                          LPSECURITY_ATTRIBUTES lpProcessAttributes,
386
                          LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
387
                          DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
388
                          LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
389
0
{
390
0
  return _CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
391
0
                           lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
392
0
                           lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
393
0
}
394
395
BOOL CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
396
                          LPSECURITY_ATTRIBUTES lpProcessAttributes,
397
                          LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
398
                          DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
399
                          LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
400
0
{
401
0
  return FALSE;
402
0
}
403
404
BOOL CreateProcessWithLogonA(LPCSTR lpUsername, LPCSTR lpDomain, LPCSTR lpPassword,
405
                             DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine,
406
                             DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
407
                             LPSTARTUPINFOA lpStartupInfo,
408
                             LPPROCESS_INFORMATION lpProcessInformation)
409
0
{
410
0
  return FALSE;
411
0
}
412
413
BOOL CreateProcessWithLogonW(LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword,
414
                             DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
415
                             DWORD dwCreationFlags, LPVOID lpEnvironment,
416
                             LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
417
                             LPPROCESS_INFORMATION lpProcessInformation)
418
0
{
419
0
  return FALSE;
420
0
}
421
422
BOOL CreateProcessWithTokenA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName,
423
                             LPSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment,
424
                             LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
425
                             LPPROCESS_INFORMATION lpProcessInformation)
426
0
{
427
0
  return _CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, NULL, NULL, FALSE,
428
0
                           dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo,
429
0
                           lpProcessInformation);
430
0
}
431
432
BOOL CreateProcessWithTokenW(HANDLE hToken, DWORD dwLogonFlags, LPCWSTR lpApplicationName,
433
                             LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment,
434
                             LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
435
                             LPPROCESS_INFORMATION lpProcessInformation)
436
0
{
437
0
  return FALSE;
438
0
}
439
440
VOID ExitProcess(UINT uExitCode)
441
0
{
442
0
  exit((int)uExitCode);
443
0
}
444
445
BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
446
0
{
447
0
  WINPR_PROCESS* process = NULL;
448
449
0
  if (!hProcess)
450
0
    return FALSE;
451
452
0
  if (!lpExitCode)
453
0
    return FALSE;
454
455
0
  process = (WINPR_PROCESS*)hProcess;
456
0
  *lpExitCode = process->dwExitCode;
457
0
  return TRUE;
458
0
}
459
460
HANDLE _GetCurrentProcess(VOID)
461
0
{
462
0
  return NULL;
463
0
}
464
465
DWORD GetCurrentProcessId(VOID)
466
6.91M
{
467
6.91M
  return ((DWORD)getpid());
468
6.91M
}
469
470
BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode)
471
0
{
472
0
  WINPR_PROCESS* process = NULL;
473
0
  process = (WINPR_PROCESS*)hProcess;
474
475
0
  if (!process || (process->pid <= 0))
476
0
    return FALSE;
477
478
0
  if (kill(process->pid, SIGTERM))
479
0
    return FALSE;
480
481
0
  return TRUE;
482
0
}
483
484
static BOOL ProcessHandleCloseHandle(HANDLE handle)
485
0
{
486
0
  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
487
0
  WINPR_ASSERT(process);
488
0
  if (process->fd >= 0)
489
0
  {
490
0
    close(process->fd);
491
0
    process->fd = -1;
492
0
  }
493
0
  free(process);
494
0
  return TRUE;
495
0
}
496
497
static BOOL ProcessHandleIsHandle(HANDLE handle)
498
0
{
499
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE);
500
0
}
501
502
static int ProcessGetFd(HANDLE handle)
503
0
{
504
0
  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
505
506
0
  if (!ProcessHandleIsHandle(handle))
507
0
    return -1;
508
509
0
  return process->fd;
510
0
}
511
512
static DWORD ProcessCleanupHandle(HANDLE handle)
513
0
{
514
0
  WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
515
516
0
  WINPR_ASSERT(process);
517
0
  if (process->fd > 0)
518
0
  {
519
0
    if (waitpid(process->pid, &process->status, WNOHANG) == process->pid)
520
0
      process->dwExitCode = (DWORD)process->status;
521
0
  }
522
0
  return WAIT_OBJECT_0;
523
0
}
524
525
static HANDLE_OPS ops = { ProcessHandleIsHandle,
526
                        ProcessHandleCloseHandle,
527
                        ProcessGetFd,
528
                        ProcessCleanupHandle, /* CleanupHandle */
529
                        NULL,
530
                        NULL,
531
                        NULL,
532
                        NULL,
533
                        NULL,
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
547
static int _pidfd_open(pid_t pid)
548
0
{
549
0
#ifdef __linux__
550
#if !defined(__NR_pidfd_open)
551
#define __NR_pidfd_open 434
552
#endif /* __NR_pidfd_open */
553
554
0
#ifndef PIDFD_NONBLOCK
555
0
#define PIDFD_NONBLOCK O_NONBLOCK
556
0
#endif /* PIDFD_NONBLOCK */
557
558
0
  int fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK);
559
0
  if (fd < 0 && errno == EINVAL)
560
0
  {
561
    /* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it
562
     * non blocking afterward */
563
0
    int flags = 0;
564
0
    fd = syscall(__NR_pidfd_open, pid, 0);
565
0
    if (fd < 0)
566
0
      return -1;
567
568
0
    flags = fcntl(fd, F_GETFL);
569
0
    if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
570
0
    {
571
0
      close(fd);
572
0
      fd = -1;
573
0
    }
574
0
  }
575
0
  return fd;
576
#else
577
  return -1;
578
#endif
579
0
}
580
581
HANDLE CreateProcessHandle(pid_t pid)
582
0
{
583
0
  WINPR_PROCESS* process = NULL;
584
0
  process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS));
585
586
0
  if (!process)
587
0
    return NULL;
588
589
0
  process->pid = pid;
590
0
  process->common.Type = HANDLE_TYPE_PROCESS;
591
0
  process->common.ops = &ops;
592
0
  process->fd = _pidfd_open(pid);
593
0
  if (process->fd >= 0)
594
0
    process->common.Mode = WINPR_FD_READ;
595
0
  return (HANDLE)process;
596
0
}
597
598
#endif