Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * exec.c
4 : * Functions for finding and validating executable files
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : *
11 : * IDENTIFICATION
12 : * src/common/exec.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 :
17 : /*
18 : * On macOS, "man realpath" avers:
19 : * Defining _DARWIN_C_SOURCE or _DARWIN_BETTER_REALPATH before including
20 : * stdlib.h will cause the provided implementation of realpath() to use
21 : * F_GETPATH from fcntl(2) to discover the path.
22 : * This should be harmless everywhere else.
23 : */
24 : #define _DARWIN_BETTER_REALPATH
25 :
26 : #ifndef FRONTEND
27 : #include "postgres.h"
28 : #else
29 : #include "postgres_fe.h"
30 : #endif
31 :
32 : #include <signal.h>
33 : #include <sys/stat.h>
34 : #include <sys/wait.h>
35 : #include <unistd.h>
36 :
37 : #ifdef EXEC_BACKEND
38 : #if defined(HAVE_SYS_PERSONALITY_H)
39 : #include <sys/personality.h>
40 : #elif defined(HAVE_SYS_PROCCTL_H)
41 : #include <sys/procctl.h>
42 : #endif
43 : #endif
44 :
45 : /* Inhibit mingw CRT's auto-globbing of command line arguments */
46 : #if defined(WIN32) && !defined(_MSC_VER)
47 : extern int _CRT_glob = 0; /* 0 turns off globbing; 1 turns it on */
48 : #endif
49 :
50 : /*
51 : * Hacky solution to allow expressing both frontend and backend error reports
52 : * in one macro call. First argument of log_error is an errcode() call of
53 : * some sort (ignored if FRONTEND); the rest are errmsg_internal() arguments,
54 : * i.e. message string and any parameters for it.
55 : *
56 : * Caller must provide the gettext wrapper around the message string, if
57 : * appropriate, so that it gets translated in the FRONTEND case; this
58 : * motivates using errmsg_internal() not errmsg(). We handle appending a
59 : * newline, if needed, inside the macro, so that there's only one translatable
60 : * string per call not two.
61 : */
62 : #ifndef FRONTEND
63 : #define log_error(errcodefn, ...) \
64 : ereport(LOG, (errcodefn, errmsg_internal(__VA_ARGS__)))
65 : #else
66 : #define log_error(errcodefn, ...) \
67 : (fprintf(stderr, __VA_ARGS__), fputc('\n', stderr))
68 : #endif
69 :
70 : static int normalize_exec_path(char *path);
71 : static char *pg_realpath(const char *fname);
72 :
73 : #ifdef WIN32
74 : static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
75 : #endif
76 :
77 : /*
78 : * validate_exec -- validate "path" as an executable file
79 : *
80 : * returns 0 if the file is found and no error is encountered.
81 : * -1 if the regular file "path" does not exist or cannot be executed.
82 : * -2 if the file is otherwise valid but cannot be read.
83 : * in the failure cases, errno is set appropriately
84 : */
85 : int
6894 bruce 86 GIC 16399 : validate_exec(const char *path)
87 : {
88 : struct stat buf;
89 : int is_r;
90 : int is_x;
91 :
92 : #ifdef WIN32
4833 tgl 93 ECB : char path_exe[MAXPGPATH + sizeof(".exe") - 1];
94 :
95 : /* Win32 requires a .exe suffix for stat() */
96 : if (strlen(path) < strlen(".exe") ||
97 : pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
98 : {
99 : strlcpy(path_exe, path, sizeof(path_exe) - 4);
100 : strcat(path_exe, ".exe");
101 : path = path_exe;
102 : }
103 : #endif
104 :
105 : /*
106 : * Ensure that the file exists and is a regular file.
107 : *
108 : * XXX if you have a broken system where stat() looks at the symlink
109 : * instead of the underlying file, you lose.
110 : */
9345 bruce 111 GIC 16399 : if (stat(path, &buf) < 0)
8986 bruce 112 UIC 0 : return -1;
113 :
5487 tgl 114 GIC 16399 : if (!S_ISREG(buf.st_mode))
115 : {
116 : /*
117 : * POSIX offers no errno code that's simply "not a regular file". If
118 : * it's a directory we can use EISDIR. Otherwise, it's most likely a
119 : * device special file, and EPERM (Operation not permitted) isn't too
120 : * horribly off base.
121 : */
271 tgl 122 UNC 0 : errno = S_ISDIR(buf.st_mode) ? EISDIR : EPERM;
8986 bruce 123 UIC 0 : return -1;
124 : }
125 :
126 : /*
9345 bruce 127 ECB : * Ensure that the file is both executable and readable (required for
9345 bruce 128 EUB : * dynamic loading).
129 : */
4833 tgl 130 ECB : #ifndef WIN32
4833 tgl 131 GIC 16399 : is_r = (access(path, R_OK) == 0);
132 16399 : is_x = (access(path, X_OK) == 0);
133 : /* access() will set errno if it returns -1 */
134 : #else
135 : is_r = buf.st_mode & S_IRUSR;
136 : is_x = buf.st_mode & S_IXUSR;
137 : errno = EACCES; /* appropriate thing if we return nonzero */
138 : #endif
139 16399 : return is_x ? (is_r ? 0 : -2) : -1;
9770 scrappy 140 EUB : }
141 :
142 :
143 : /*
144 : * find_my_exec -- find an absolute path to this program's executable
145 : *
146 : * argv0 is the name passed on the command line
147 : * retpath is the output area (must be of size MAXPGPATH)
148 : * Returns 0 if OK, -1 if error.
6728 tgl 149 ECB : *
9770 scrappy 150 : * The reason we have to work so hard to find an absolute path is that
151 : * on some platforms we can't do dynamic loading unless we know the
152 : * executable's location. Also, we need an absolute path not a relative
153 : * path because we may later change working directory. Finally, we want
154 : * a true path not a symlink location, so that we can locate other files
155 : * that are part of our installation relative to the executable.
156 : */
157 : int
6898 bruce 158 GIC 15500 : find_my_exec(const char *argv0, char *retpath)
159 : {
160 : char *path;
161 :
162 : /*
163 : * If argv0 contains a separator, then PATH wasn't used.
164 : */
17 tgl 165 GNC 15500 : strlcpy(retpath, argv0, MAXPGPATH);
166 15500 : if (first_dir_separator(retpath) != NULL)
167 : {
6898 bruce 168 GIC 11199 : if (validate_exec(retpath) == 0)
17 tgl 169 GNC 11199 : return normalize_exec_path(retpath);
6728 tgl 170 ECB :
1643 tgl 171 UIC 0 : log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
172 : _("invalid binary \"%s\": %m"), retpath);
6728 tgl 173 LBC 0 : return -1;
174 : }
9345 bruce 175 EUB :
176 : #ifdef WIN32
6898 177 : /* Win32 checks the current directory first for names without slashes */
178 : if (validate_exec(retpath) == 0)
179 : return normalize_exec_path(retpath);
180 : #endif
181 :
182 : /*
183 : * Since no explicit path was supplied, the user must have been relying on
184 : * PATH. We'll search the same PATH.
185 : */
6894 bruce 186 GIC 4301 : if ((path = getenv("PATH")) && *path)
187 : {
6797 188 4301 : char *startp = NULL,
6797 bruce 189 CBC 4301 : *endp = NULL;
190 :
6894 bruce 191 ECB : do
9345 192 : {
6894 bruce 193 GIC 4301 : if (!startp)
194 4301 : startp = path;
195 : else
6894 bruce 196 LBC 0 : startp = endp + 1;
6898 bruce 197 ECB :
4449 bruce 198 GIC 4301 : endp = first_path_var_separator(startp);
6894 bruce 199 GBC 4301 : if (!endp)
6797 bruce 200 UIC 0 : endp = startp + strlen(startp); /* point to end */
6894 bruce 201 ECB :
17 tgl 202 GNC 4301 : strlcpy(retpath, startp, Min(endp - startp + 1, MAXPGPATH));
6894 bruce 203 EUB :
17 tgl 204 GNC 4301 : join_path_components(retpath, retpath, argv0);
6898 bruce 205 GIC 4301 : canonicalize_path(retpath);
6728 tgl 206 ECB :
6898 bruce 207 CBC 4301 : switch (validate_exec(retpath))
9345 bruce 208 EUB : {
2118 tgl 209 GBC 4301 : case 0: /* found ok */
17 tgl 210 GNC 4301 : return normalize_exec_path(retpath);
9344 bruce 211 UBC 0 : case -1: /* wasn't even a candidate, keep looking */
6728 tgl 212 UIC 0 : break;
9344 bruce 213 0 : case -2: /* found but disqualified */
1643 tgl 214 UBC 0 : log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
215 : _("could not read binary \"%s\": %m"),
6659 tgl 216 EUB : retpath);
6728 tgl 217 UIC 0 : break;
218 : }
6894 bruce 219 UBC 0 : } while (*endp);
220 : }
9770 scrappy 221 EUB :
1643 tgl 222 UIC 0 : log_error(errcode(ERRCODE_UNDEFINED_FILE),
223 : _("could not find a \"%s\" to execute"), argv0);
8986 bruce 224 0 : return -1;
225 : }
226 :
227 :
228 : /*
229 : * normalize_exec_path - resolve symlinks and convert to absolute path
230 : *
231 : * Given a path that refers to an executable, chase through any symlinks
232 : * to find the real file location; then convert that to an absolute path.
233 : *
234 : * On success, replaces the contents of "path" with the absolute path.
235 : * ("path" is assumed to be of size MAXPGPATH.)
236 : * Returns 0 if OK, -1 if error.
237 : */
238 : static int
17 tgl 239 GNC 15500 : normalize_exec_path(char *path)
240 : {
241 : /*
242 : * We used to do a lot of work ourselves here, but now we just let
243 : * realpath(3) do all the heavy lifting.
6728 tgl 244 ECB : */
17 tgl 245 GNC 15500 : char *abspath = pg_realpath(path);
246 :
247 15500 : if (abspath == NULL)
248 : {
1643 tgl 249 UIC 0 : log_error(errcode_for_file_access(),
250 : _("could not resolve path \"%s\" to absolute form: %m"),
251 : path);
6728 252 0 : return -1;
253 : }
17 tgl 254 GNC 15500 : strlcpy(path, abspath, MAXPGPATH);
255 15500 : free(abspath);
256 :
257 : #ifdef WIN32
258 : /* On Windows, be sure to convert '\' to '/' */
259 : canonicalize_path(path);
260 : #endif
261 :
17 tgl 262 GIC 15500 : return 0;
17 tgl 263 ECB : }
264 :
265 :
266 : /*
267 : * pg_realpath() - realpath(3) with POSIX.1-2008 semantics
268 : *
269 : * This is equivalent to realpath(fname, NULL), in that it returns a
270 : * malloc'd buffer containing the absolute path equivalent to fname.
271 : * On error, returns NULL with errno set.
272 : *
273 : * On Windows, what you get is spelled per platform conventions,
274 : * so you probably want to apply canonicalize_path() to the result.
275 : *
276 : * For now, this is needed only here so mark it static. If you choose to
277 : * move it into its own file, move the _DARWIN_BETTER_REALPATH #define too!
278 : */
279 : static char *
17 tgl 280 GNC 15500 : pg_realpath(const char *fname)
281 : {
282 : char *path;
283 :
284 : #ifndef WIN32
285 15500 : path = realpath(fname, NULL);
286 15500 : if (path == NULL && errno == EINVAL)
287 : {
288 : /*
289 : * Cope with old-POSIX systems that require a user-provided buffer.
290 : * Assume MAXPGPATH is enough room on all such systems.
291 : */
17 tgl 292 UNC 0 : char *buf = malloc(MAXPGPATH);
293 :
294 0 : if (buf == NULL)
295 0 : return NULL; /* assume errno is set */
296 0 : path = realpath(fname, buf);
297 0 : if (path == NULL) /* don't leak memory */
298 : {
299 0 : int save_errno = errno;
300 :
301 0 : free(buf);
302 0 : errno = save_errno;
303 : }
304 : }
305 : #else /* WIN32 */
306 :
307 : /*
308 : * Microsoft is resolutely non-POSIX, but _fullpath() does the same thing.
309 : * The documentation claims it reports errors by setting errno, which is a
310 : * bit surprising for Microsoft, but we'll believe that until it's proven
311 : * wrong. Clear errno first, though, so we can at least tell if a failure
312 : * occurs and doesn't set it.
313 : */
314 : errno = 0;
315 : path = _fullpath(NULL, fname, 0);
316 : #endif
317 :
17 tgl 318 GNC 15500 : return path;
319 : }
320 :
321 :
322 : /*
323 : * Find another program in our binary's directory,
324 : * then make sure it is the proper version.
6728 tgl 325 ECB : */
6728 tgl 326 EUB : int
6728 tgl 327 GIC 871 : find_other_exec(const char *argv0, const char *target,
328 : const char *versionstr, char *retpath)
6728 tgl 329 ECB : {
330 : char cmd[MAXPGPATH];
331 : char line[MAXPGPATH];
332 :
6728 tgl 333 CBC 871 : if (find_my_exec(argv0, retpath) < 0)
6728 tgl 334 UIC 0 : return -1;
335 :
6728 tgl 336 ECB : /* Trim off program name and keep just directory */
6728 tgl 337 GBC 871 : *last_dir_separator(retpath) = '\0';
6728 tgl 338 GIC 871 : canonicalize_path(retpath);
6728 tgl 339 ECB :
340 : /* Now append the other program's name */
6728 tgl 341 CBC 871 : snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
6728 tgl 342 EUB : "/%s%s", target, EXE);
343 :
6728 tgl 344 CBC 871 : if (validate_exec(retpath) != 0)
6728 tgl 345 UBC 0 : return -1;
346 :
3908 tgl 347 CBC 871 : snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
348 :
6728 tgl 349 GIC 871 : if (!pipe_read_line(cmd, line, sizeof(line)))
6728 tgl 350 UIC 0 : return -1;
351 :
6728 tgl 352 GIC 871 : if (strcmp(line, versionstr) != 0)
6728 tgl 353 UIC 0 : return -2;
354 :
6728 tgl 355 CBC 871 : return 0;
356 : }
357 :
358 :
6831 bruce 359 ECB : /*
360 : * Execute a command in a pipe and read the first line from it.
361 : */
1103 michael 362 : char *
6797 bruce 363 GIC 900 : pipe_read_line(char *cmd, char *line, int maxsize)
6831 bruce 364 EUB : {
6797 365 : FILE *pgver;
366 :
223 tgl 367 GNC 900 : fflush(NULL);
368 :
3908 tgl 369 GBC 900 : errno = 0;
6831 bruce 370 900 : if ((pgver = popen(cmd, "r")) == NULL)
371 : {
3908 tgl 372 UBC 0 : perror("popen failure");
6831 bruce 373 0 : return NULL;
3908 tgl 374 EUB : }
375 :
3908 tgl 376 GIC 900 : errno = 0;
6831 bruce 377 CBC 900 : if (fgets(line, maxsize, pgver) == NULL)
6831 bruce 378 EUB : {
3908 tgl 379 UIC 0 : if (feof(pgver))
3908 tgl 380 LBC 0 : fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
381 : else
3908 tgl 382 UIC 0 : perror("fgets failure");
4382 bruce 383 0 : pclose(pgver); /* no error checking */
6831 384 0 : return NULL;
385 : }
386 :
6831 bruce 387 GIC 900 : if (pclose_check(pgver))
6831 bruce 388 LBC 0 : return NULL;
389 :
6831 bruce 390 GIC 900 : return line;
391 : }
392 :
6907 bruce 393 ECB :
394 : /*
6900 395 : * pclose() plus useful error reporting
396 : */
397 : int
6900 bruce 398 CBC 1511 : pclose_check(FILE *stream)
399 : {
400 : int exitstatus;
3693 heikki.linnakangas 401 EUB : char *reason;
402 :
6900 bruce 403 GIC 1511 : exitstatus = pclose(stream);
404 :
405 1511 : if (exitstatus == 0)
6797 bruce 406 CBC 1508 : return 0; /* all is well */
6900 bruce 407 ECB :
6900 bruce 408 GIC 3 : if (exitstatus == -1)
6900 bruce 409 ECB : {
410 : /* pclose() itself failed, and hopefully set errno */
1643 tgl 411 LBC 0 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
412 : _("%s() failed: %m"), "pclose");
413 : }
414 : else
415 : {
3693 heikki.linnakangas 416 GIC 3 : reason = wait_result_to_str(exitstatus);
1643 tgl 417 3 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
418 : "%s", reason);
3693 heikki.linnakangas 419 3 : pfree(reason);
420 : }
421 3 : return exitstatus;
422 : }
423 :
424 : /*
425 : * set_pglocale_pgservice
6054 tgl 426 ECB : *
427 : * Set application-specific locale and service directory
428 : *
429 : * This function takes the value of argv[0] rather than a full path.
430 : *
431 : * (You may be wondering why this is in exec.c. It requires this module's
432 : * services and doesn't introduce any new dependencies, so this seems as
433 : * good as anyplace.)
434 : */
435 : void
6054 tgl 436 GIC 12379 : set_pglocale_pgservice(const char *argv0, const char *app)
437 : {
438 : char path[MAXPGPATH];
439 : char my_exec_path[MAXPGPATH];
440 :
441 : /* don't set LC_ALL in the backend */
5232 peter_e 442 12379 : if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
443 : {
6054 tgl 444 9760 : setlocale(LC_ALL, "");
445 :
446 : /*
3014 noah 447 ECB : * One could make a case for reproducing here PostmasterMain()'s test
3014 noah 448 EUB : * for whether the process is multithreaded. Unlike the postmaster,
449 : * no frontend program calls sigprocmask() or otherwise provides for
450 : * mutual exclusion between signal handlers. While frontends using
3014 noah 451 ECB : * fork(), if multithreaded, are formally exposed to undefined
452 : * behavior, we have not witnessed a concrete bug. Therefore,
453 : * complaining about multithreading here may be mere pedantry.
454 : */
455 : }
456 :
6054 tgl 457 GIC 12379 : if (find_my_exec(argv0, my_exec_path) < 0)
6054 tgl 458 LBC 0 : return;
459 :
6054 tgl 460 ECB : #ifdef ENABLE_NLS
6054 tgl 461 GIC 12379 : get_locale_path(my_exec_path, path);
6054 tgl 462 CBC 12379 : bindtextdomain(app, path);
6054 tgl 463 GIC 12379 : textdomain(app);
464 : /* set for libpq to use, but don't override existing setting */
830 465 12379 : setenv("PGLOCALEDIR", path, 0);
466 : #endif
467 :
6054 468 12379 : if (getenv("PGSYSCONFDIR") == NULL)
469 : {
470 8079 : get_etc_path(my_exec_path, path);
471 : /* set for libpq to use */
830 472 8079 : setenv("PGSYSCONFDIR", path, 0);
473 : }
474 : }
475 :
476 : #ifdef EXEC_BACKEND
477 : /*
478 : * For the benefit of PostgreSQL developers testing EXEC_BACKEND on Unix
479 : * systems (code paths normally exercised only on Windows), provide a way to
480 : * disable address space layout randomization, if we know how on this platform.
481 : * Otherwise, backends may fail to attach to shared memory at the fixed address
482 : * chosen by the postmaster. (See also the macOS-specific hack in
483 : * sysv_shmem.c.)
484 : */
485 : int
486 : pg_disable_aslr(void)
487 : {
488 : #if defined(HAVE_SYS_PERSONALITY_H)
489 : return personality(ADDR_NO_RANDOMIZE);
490 : #elif defined(HAVE_SYS_PROCCTL_H) && defined(PROC_ASLR_FORCE_DISABLE)
491 : int data = PROC_ASLR_FORCE_DISABLE;
492 :
493 : return procctl(P_PID, 0, PROC_ASLR_CTL, &data);
494 : #else
495 : errno = ENOSYS;
496 : return -1;
497 : #endif
498 : }
499 : #endif
500 :
501 : #ifdef WIN32
502 :
503 : /*
504 : * AddUserToTokenDacl(HANDLE hToken)
505 : *
506 : * This function adds the current user account to the restricted
507 : * token used when we create a restricted process.
508 : *
509 : * This is required because of some security changes in Windows
510 : * that appeared in patches to XP/2K3 and in Vista/2008.
511 : *
512 : * On these machines, the Administrator account is not included in
513 : * the default DACL - you just get Administrators + System. For
514 : * regular users you get User + System. Because we strip Administrators
515 : * when we create the restricted token, we are left with only System
516 : * in the DACL which leads to access denied errors for later CreatePipe()
517 : * and CreateProcess() calls when running as Administrator.
518 : *
519 : * This function fixes this problem by modifying the DACL of the
520 : * token the process will use, and explicitly re-adding the current
521 : * user account. This is still secure because the Administrator account
522 : * inherits its privileges from the Administrators group - it doesn't
523 : * have any of its own.
524 : */
525 : BOOL
526 : AddUserToTokenDacl(HANDLE hToken)
527 : {
528 : int i;
529 : ACL_SIZE_INFORMATION asi;
530 : ACCESS_ALLOWED_ACE *pace;
531 : DWORD dwNewAclSize;
532 : DWORD dwSize = 0;
533 : DWORD dwTokenInfoLength = 0;
534 : PACL pacl = NULL;
535 : PTOKEN_USER pTokenUser = NULL;
536 : TOKEN_DEFAULT_DACL tddNew;
537 : TOKEN_DEFAULT_DACL *ptdd = NULL;
538 : TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
539 : BOOL ret = FALSE;
540 :
541 : /* Figure out the buffer size for the DACL info */
542 : if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
543 : {
544 : if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
545 : {
546 : ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
547 : if (ptdd == NULL)
548 : {
549 : log_error(errcode(ERRCODE_OUT_OF_MEMORY),
550 : _("out of memory"));
551 : goto cleanup;
552 : }
553 :
554 : if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
555 : {
556 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
557 : "could not get token information: error code %lu",
558 : GetLastError());
559 : goto cleanup;
560 : }
561 : }
562 : else
563 : {
564 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
565 : "could not get token information buffer size: error code %lu",
566 : GetLastError());
567 : goto cleanup;
568 : }
569 : }
570 :
571 : /* Get the ACL info */
572 : if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
573 : (DWORD) sizeof(ACL_SIZE_INFORMATION),
574 : AclSizeInformation))
575 : {
576 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
577 : "could not get ACL information: error code %lu",
578 : GetLastError());
579 : goto cleanup;
580 : }
581 :
582 : /* Get the current user SID */
583 : if (!GetTokenUser(hToken, &pTokenUser))
584 : goto cleanup; /* callee printed a message */
585 :
586 : /* Figure out the size of the new ACL */
587 : dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
588 : GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
589 :
590 : /* Allocate the ACL buffer & initialize it */
591 : pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
592 : if (pacl == NULL)
593 : {
594 : log_error(errcode(ERRCODE_OUT_OF_MEMORY),
595 : _("out of memory"));
596 : goto cleanup;
597 : }
598 :
599 : if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
600 : {
601 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
602 : "could not initialize ACL: error code %lu", GetLastError());
603 : goto cleanup;
604 : }
605 :
606 : /* Loop through the existing ACEs, and build the new ACL */
607 : for (i = 0; i < (int) asi.AceCount; i++)
608 : {
609 : if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
610 : {
611 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
612 : "could not get ACE: error code %lu", GetLastError());
613 : goto cleanup;
614 : }
615 :
616 : if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
617 : {
618 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
619 : "could not add ACE: error code %lu", GetLastError());
620 : goto cleanup;
621 : }
622 : }
623 :
624 : /* Add the new ACE for the current user */
625 : if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
626 : {
627 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
628 : "could not add access allowed ACE: error code %lu",
629 : GetLastError());
630 : goto cleanup;
631 : }
632 :
633 : /* Set the new DACL in the token */
634 : tddNew.DefaultDacl = pacl;
635 :
636 : if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
637 : {
638 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
639 : "could not set token information: error code %lu",
640 : GetLastError());
641 : goto cleanup;
642 : }
643 :
644 : ret = TRUE;
645 :
646 : cleanup:
647 : if (pTokenUser)
648 : LocalFree((HLOCAL) pTokenUser);
649 :
650 : if (pacl)
651 : LocalFree((HLOCAL) pacl);
652 :
653 : if (ptdd)
654 : LocalFree((HLOCAL) ptdd);
655 :
656 : return ret;
657 : }
658 :
659 : /*
660 : * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
661 : *
662 : * Get the users token information from a process token.
663 : *
664 : * The caller of this function is responsible for calling LocalFree() on the
665 : * returned TOKEN_USER memory.
666 : */
667 : static BOOL
668 : GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
669 : {
670 : DWORD dwLength;
671 :
672 : *ppTokenUser = NULL;
673 :
674 : if (!GetTokenInformation(hToken,
675 : TokenUser,
676 : NULL,
677 : 0,
678 : &dwLength))
679 : {
680 : if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
681 : {
682 : *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
683 :
684 : if (*ppTokenUser == NULL)
685 : {
686 : log_error(errcode(ERRCODE_OUT_OF_MEMORY),
687 : _("out of memory"));
688 : return FALSE;
689 : }
690 : }
691 : else
692 : {
693 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
694 : "could not get token information buffer size: error code %lu",
695 : GetLastError());
696 : return FALSE;
697 : }
698 : }
699 :
700 : if (!GetTokenInformation(hToken,
701 : TokenUser,
702 : *ppTokenUser,
703 : dwLength,
704 : &dwLength))
705 : {
706 : LocalFree(*ppTokenUser);
707 : *ppTokenUser = NULL;
708 :
709 : log_error(errcode(ERRCODE_SYSTEM_ERROR),
710 : "could not get token information: error code %lu",
711 : GetLastError());
712 : return FALSE;
713 : }
714 :
715 : /* Memory in *ppTokenUser is LocalFree():d by the caller */
716 : return TRUE;
717 : }
718 :
719 : #endif
|