Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * exec.c
4 : * Functions for finding and validating executable files
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2017, 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 : #ifndef FRONTEND
18 : #include "postgres.h"
19 : #else
20 : #include "postgres_fe.h"
21 : #endif
22 :
23 : #include <signal.h>
24 : #include <sys/stat.h>
25 : #include <sys/wait.h>
26 : #include <unistd.h>
27 :
28 : #ifndef FRONTEND
29 : /* We use only 3- and 4-parameter elog calls in this file, for simplicity */
30 : /* NOTE: caller must provide gettext call around str! */
31 : #define log_error(str, param) elog(LOG, str, param)
32 : #define log_error4(str, param, arg1) elog(LOG, str, param, arg1)
33 : #else
34 : #define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr))
35 : #define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
36 : #endif
37 :
38 : #ifdef _MSC_VER
39 : #define getcwd(cwd,len) GetCurrentDirectory(len, cwd)
40 : #endif
41 :
42 : static int validate_exec(const char *path);
43 : static int resolve_symlinks(char *path);
44 : static char *pipe_read_line(char *cmd, char *line, int maxsize);
45 :
46 : #ifdef WIN32
47 : static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
48 : #endif
49 :
50 : /*
51 : * validate_exec -- validate "path" as an executable file
52 : *
53 : * returns 0 if the file is found and no error is encountered.
54 : * -1 if the regular file "path" does not exist or cannot be executed.
55 : * -2 if the file is otherwise valid but cannot be read.
56 : */
57 : static int
58 201 : validate_exec(const char *path)
59 : {
60 : struct stat buf;
61 : int is_r;
62 : int is_x;
63 :
64 : #ifdef WIN32
65 : char path_exe[MAXPGPATH + sizeof(".exe") - 1];
66 :
67 : /* Win32 requires a .exe suffix for stat() */
68 : if (strlen(path) >= strlen(".exe") &&
69 : pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
70 : {
71 : strlcpy(path_exe, path, sizeof(path_exe) - 4);
72 : strcat(path_exe, ".exe");
73 : path = path_exe;
74 : }
75 : #endif
76 :
77 : /*
78 : * Ensure that the file exists and is a regular file.
79 : *
80 : * XXX if you have a broken system where stat() looks at the symlink
81 : * instead of the underlying file, you lose.
82 : */
83 201 : if (stat(path, &buf) < 0)
84 0 : return -1;
85 :
86 201 : if (!S_ISREG(buf.st_mode))
87 0 : return -1;
88 :
89 : /*
90 : * Ensure that the file is both executable and readable (required for
91 : * dynamic loading).
92 : */
93 : #ifndef WIN32
94 201 : is_r = (access(path, R_OK) == 0);
95 201 : is_x = (access(path, X_OK) == 0);
96 : #else
97 : is_r = buf.st_mode & S_IRUSR;
98 : is_x = buf.st_mode & S_IXUSR;
99 : #endif
100 201 : return is_x ? (is_r ? 0 : -2) : -1;
101 : }
102 :
103 :
104 : /*
105 : * find_my_exec -- find an absolute path to a valid executable
106 : *
107 : * argv0 is the name passed on the command line
108 : * retpath is the output area (must be of size MAXPGPATH)
109 : * Returns 0 if OK, -1 if error.
110 : *
111 : * The reason we have to work so hard to find an absolute path is that
112 : * on some platforms we can't do dynamic loading unless we know the
113 : * executable's location. Also, we need a full path not a relative
114 : * path because we will later change working directory. Finally, we want
115 : * a true path not a symlink location, so that we can locate other files
116 : * that are part of our installation relative to the executable.
117 : */
118 : int
119 200 : find_my_exec(const char *argv0, char *retpath)
120 : {
121 : char cwd[MAXPGPATH],
122 : test_path[MAXPGPATH];
123 : char *path;
124 :
125 200 : if (!getcwd(cwd, MAXPGPATH))
126 : {
127 0 : log_error(_("could not identify current directory: %s"),
128 : strerror(errno));
129 0 : return -1;
130 : }
131 :
132 : /*
133 : * If argv0 contains a separator, then PATH wasn't used.
134 : */
135 200 : if (first_dir_separator(argv0) != NULL)
136 : {
137 10 : if (is_absolute_path(argv0))
138 9 : StrNCpy(retpath, argv0, MAXPGPATH);
139 : else
140 1 : join_path_components(retpath, cwd, argv0);
141 10 : canonicalize_path(retpath);
142 :
143 10 : if (validate_exec(retpath) == 0)
144 10 : return resolve_symlinks(retpath);
145 :
146 0 : log_error(_("invalid binary \"%s\""), retpath);
147 0 : return -1;
148 : }
149 :
150 : #ifdef WIN32
151 : /* Win32 checks the current directory first for names without slashes */
152 : join_path_components(retpath, cwd, argv0);
153 : if (validate_exec(retpath) == 0)
154 : return resolve_symlinks(retpath);
155 : #endif
156 :
157 : /*
158 : * Since no explicit path was supplied, the user must have been relying on
159 : * PATH. We'll search the same PATH.
160 : */
161 190 : if ((path = getenv("PATH")) && *path)
162 : {
163 190 : char *startp = NULL,
164 190 : *endp = NULL;
165 :
166 : do
167 : {
168 190 : if (!startp)
169 190 : startp = path;
170 : else
171 0 : startp = endp + 1;
172 :
173 190 : endp = first_path_var_separator(startp);
174 190 : if (!endp)
175 0 : endp = startp + strlen(startp); /* point to end */
176 :
177 190 : StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
178 :
179 190 : if (is_absolute_path(test_path))
180 190 : join_path_components(retpath, test_path, argv0);
181 : else
182 : {
183 0 : join_path_components(retpath, cwd, test_path);
184 0 : join_path_components(retpath, retpath, argv0);
185 : }
186 190 : canonicalize_path(retpath);
187 :
188 190 : switch (validate_exec(retpath))
189 : {
190 : case 0: /* found ok */
191 190 : return resolve_symlinks(retpath);
192 : case -1: /* wasn't even a candidate, keep looking */
193 0 : break;
194 : case -2: /* found but disqualified */
195 0 : log_error(_("could not read binary \"%s\""),
196 : retpath);
197 0 : break;
198 : }
199 0 : } while (*endp);
200 : }
201 :
202 0 : log_error(_("could not find a \"%s\" to execute"), argv0);
203 0 : return -1;
204 : }
205 :
206 :
207 : /*
208 : * resolve_symlinks - resolve symlinks to the underlying file
209 : *
210 : * Replace "path" by the absolute path to the referenced file.
211 : *
212 : * Returns 0 if OK, -1 if error.
213 : *
214 : * Note: we are not particularly tense about producing nice error messages
215 : * because we are not really expecting error here; we just determined that
216 : * the symlink does point to a valid executable.
217 : */
218 : static int
219 200 : resolve_symlinks(char *path)
220 : {
221 : #ifdef HAVE_READLINK
222 : struct stat buf;
223 : char orig_wd[MAXPGPATH],
224 : link_buf[MAXPGPATH];
225 : char *fname;
226 :
227 : /*
228 : * To resolve a symlink properly, we have to chdir into its directory and
229 : * then chdir to where the symlink points; otherwise we may fail to
230 : * resolve relative links correctly (consider cases involving mount
231 : * points, for example). After following the final symlink, we use
232 : * getcwd() to figure out where the heck we're at.
233 : *
234 : * One might think we could skip all this if path doesn't point to a
235 : * symlink to start with, but that's wrong. We also want to get rid of
236 : * any directory symlinks that are present in the given path. We expect
237 : * getcwd() to give us an accurate, symlink-free path.
238 : */
239 200 : if (!getcwd(orig_wd, MAXPGPATH))
240 : {
241 0 : log_error(_("could not identify current directory: %s"),
242 : strerror(errno));
243 0 : return -1;
244 : }
245 :
246 : for (;;)
247 : {
248 : char *lsep;
249 : int rllen;
250 :
251 200 : lsep = last_dir_separator(path);
252 200 : if (lsep)
253 : {
254 200 : *lsep = '\0';
255 200 : if (chdir(path) == -1)
256 : {
257 0 : log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
258 0 : return -1;
259 : }
260 200 : fname = lsep + 1;
261 : }
262 : else
263 0 : fname = path;
264 :
265 400 : if (lstat(fname, &buf) < 0 ||
266 200 : !S_ISLNK(buf.st_mode))
267 : break;
268 :
269 0 : rllen = readlink(fname, link_buf, sizeof(link_buf));
270 0 : if (rllen < 0 || rllen >= sizeof(link_buf))
271 : {
272 0 : log_error(_("could not read symbolic link \"%s\""), fname);
273 0 : return -1;
274 : }
275 0 : link_buf[rllen] = '\0';
276 0 : strcpy(path, link_buf);
277 0 : }
278 :
279 : /* must copy final component out of 'path' temporarily */
280 200 : strlcpy(link_buf, fname, sizeof(link_buf));
281 :
282 200 : if (!getcwd(path, MAXPGPATH))
283 : {
284 0 : log_error(_("could not identify current directory: %s"),
285 : strerror(errno));
286 0 : return -1;
287 : }
288 200 : join_path_components(path, path, link_buf);
289 200 : canonicalize_path(path);
290 :
291 200 : if (chdir(orig_wd) == -1)
292 : {
293 0 : log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
294 0 : return -1;
295 : }
296 : #endif /* HAVE_READLINK */
297 :
298 200 : return 0;
299 : }
300 :
301 :
302 : /*
303 : * Find another program in our binary's directory,
304 : * then make sure it is the proper version.
305 : */
306 : int
307 1 : find_other_exec(const char *argv0, const char *target,
308 : const char *versionstr, char *retpath)
309 : {
310 : char cmd[MAXPGPATH];
311 : char line[100];
312 :
313 1 : if (find_my_exec(argv0, retpath) < 0)
314 0 : return -1;
315 :
316 : /* Trim off program name and keep just directory */
317 1 : *last_dir_separator(retpath) = '\0';
318 1 : canonicalize_path(retpath);
319 :
320 : /* Now append the other program's name */
321 1 : snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
322 : "/%s%s", target, EXE);
323 :
324 1 : if (validate_exec(retpath) != 0)
325 0 : return -1;
326 :
327 1 : snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
328 :
329 1 : if (!pipe_read_line(cmd, line, sizeof(line)))
330 0 : return -1;
331 :
332 1 : if (strcmp(line, versionstr) != 0)
333 0 : return -2;
334 :
335 1 : return 0;
336 : }
337 :
338 :
339 : /*
340 : * The runtime library's popen() on win32 does not work when being
341 : * called from a service when running on windows <= 2000, because
342 : * there is no stdin/stdout/stderr.
343 : *
344 : * Executing a command in a pipe and reading the first line from it
345 : * is all we need.
346 : */
347 : static char *
348 1 : pipe_read_line(char *cmd, char *line, int maxsize)
349 : {
350 : #ifndef WIN32
351 : FILE *pgver;
352 :
353 : /* flush output buffers in case popen does not... */
354 1 : fflush(stdout);
355 1 : fflush(stderr);
356 :
357 1 : errno = 0;
358 1 : if ((pgver = popen(cmd, "r")) == NULL)
359 : {
360 0 : perror("popen failure");
361 0 : return NULL;
362 : }
363 :
364 1 : errno = 0;
365 1 : if (fgets(line, maxsize, pgver) == NULL)
366 : {
367 0 : if (feof(pgver))
368 0 : fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
369 : else
370 0 : perror("fgets failure");
371 0 : pclose(pgver); /* no error checking */
372 0 : return NULL;
373 : }
374 :
375 1 : if (pclose_check(pgver))
376 0 : return NULL;
377 :
378 1 : return line;
379 : #else /* WIN32 */
380 :
381 : SECURITY_ATTRIBUTES sattr;
382 : HANDLE childstdoutrd,
383 : childstdoutwr,
384 : childstdoutrddup;
385 : PROCESS_INFORMATION pi;
386 : STARTUPINFO si;
387 : char *retval = NULL;
388 :
389 : sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
390 : sattr.bInheritHandle = TRUE;
391 : sattr.lpSecurityDescriptor = NULL;
392 :
393 : if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
394 : return NULL;
395 :
396 : if (!DuplicateHandle(GetCurrentProcess(),
397 : childstdoutrd,
398 : GetCurrentProcess(),
399 : &childstdoutrddup,
400 : 0,
401 : FALSE,
402 : DUPLICATE_SAME_ACCESS))
403 : {
404 : CloseHandle(childstdoutrd);
405 : CloseHandle(childstdoutwr);
406 : return NULL;
407 : }
408 :
409 : CloseHandle(childstdoutrd);
410 :
411 : ZeroMemory(&pi, sizeof(pi));
412 : ZeroMemory(&si, sizeof(si));
413 : si.cb = sizeof(si);
414 : si.dwFlags = STARTF_USESTDHANDLES;
415 : si.hStdError = childstdoutwr;
416 : si.hStdOutput = childstdoutwr;
417 : si.hStdInput = INVALID_HANDLE_VALUE;
418 :
419 : if (CreateProcess(NULL,
420 : cmd,
421 : NULL,
422 : NULL,
423 : TRUE,
424 : 0,
425 : NULL,
426 : NULL,
427 : &si,
428 : &pi))
429 : {
430 : /* Successfully started the process */
431 : char *lineptr;
432 :
433 : ZeroMemory(line, maxsize);
434 :
435 : /* Try to read at least one line from the pipe */
436 : /* This may require more than one wait/read attempt */
437 : for (lineptr = line; lineptr < line + maxsize - 1;)
438 : {
439 : DWORD bytesread = 0;
440 :
441 : /* Let's see if we can read */
442 : if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
443 : break; /* Timeout, but perhaps we got a line already */
444 :
445 : if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
446 : &bytesread, NULL))
447 : break; /* Error, but perhaps we got a line already */
448 :
449 : lineptr += strlen(lineptr);
450 :
451 : if (!bytesread)
452 : break; /* EOF */
453 :
454 : if (strchr(line, '\n'))
455 : break; /* One or more lines read */
456 : }
457 :
458 : if (lineptr != line)
459 : {
460 : /* OK, we read some data */
461 : int len;
462 :
463 : /* If we got more than one line, cut off after the first \n */
464 : lineptr = strchr(line, '\n');
465 : if (lineptr)
466 : *(lineptr + 1) = '\0';
467 :
468 : len = strlen(line);
469 :
470 : /*
471 : * If EOL is \r\n, convert to just \n. Because stdout is a
472 : * text-mode stream, the \n output by the child process is
473 : * received as \r\n, so we convert it to \n. The server main.c
474 : * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
475 : * disabling \n to \r\n expansion for stdout.
476 : */
477 : if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
478 : {
479 : line[len - 2] = '\n';
480 : line[len - 1] = '\0';
481 : len--;
482 : }
483 :
484 : /*
485 : * We emulate fgets() behaviour. So if there is no newline at the
486 : * end, we add one...
487 : */
488 : if (len == 0 || line[len - 1] != '\n')
489 : strcat(line, "\n");
490 :
491 : retval = line;
492 : }
493 :
494 : CloseHandle(pi.hProcess);
495 : CloseHandle(pi.hThread);
496 : }
497 :
498 : CloseHandle(childstdoutwr);
499 : CloseHandle(childstdoutrddup);
500 :
501 : return retval;
502 : #endif /* WIN32 */
503 : }
504 :
505 :
506 : /*
507 : * pclose() plus useful error reporting
508 : */
509 : int
510 3 : pclose_check(FILE *stream)
511 : {
512 : int exitstatus;
513 : char *reason;
514 :
515 3 : exitstatus = pclose(stream);
516 :
517 3 : if (exitstatus == 0)
518 3 : return 0; /* all is well */
519 :
520 0 : if (exitstatus == -1)
521 : {
522 : /* pclose() itself failed, and hopefully set errno */
523 0 : log_error(_("pclose failed: %s"), strerror(errno));
524 : }
525 : else
526 : {
527 0 : reason = wait_result_to_str(exitstatus);
528 0 : log_error("%s", reason);
529 : #ifdef FRONTEND
530 0 : free(reason);
531 : #else
532 0 : pfree(reason);
533 : #endif
534 : }
535 0 : return exitstatus;
536 : }
537 :
538 : /*
539 : * set_pglocale_pgservice
540 : *
541 : * Set application-specific locale and service directory
542 : *
543 : * This function takes the value of argv[0] rather than a full path.
544 : *
545 : * (You may be wondering why this is in exec.c. It requires this module's
546 : * services and doesn't introduce any new dependencies, so this seems as
547 : * good as anyplace.)
548 : */
549 : void
550 194 : set_pglocale_pgservice(const char *argv0, const char *app)
551 : {
552 : char path[MAXPGPATH];
553 : char my_exec_path[MAXPGPATH];
554 : char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than
555 : * PGLOCALEDIR */
556 : char *dup_path;
557 :
558 : /* don't set LC_ALL in the backend */
559 194 : if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
560 : {
561 188 : setlocale(LC_ALL, "");
562 :
563 : /*
564 : * One could make a case for reproducing here PostmasterMain()'s test
565 : * for whether the process is multithreaded. Unlike the postmaster,
566 : * no frontend program calls sigprocmask() or otherwise provides for
567 : * mutual exclusion between signal handlers. While frontends using
568 : * fork(), if multithreaded, are formally exposed to undefined
569 : * behavior, we have not witnessed a concrete bug. Therefore,
570 : * complaining about multithreading here may be mere pedantry.
571 : */
572 : }
573 :
574 194 : if (find_my_exec(argv0, my_exec_path) < 0)
575 194 : return;
576 :
577 : #ifdef ENABLE_NLS
578 : get_locale_path(my_exec_path, path);
579 : bindtextdomain(app, path);
580 : textdomain(app);
581 :
582 : if (getenv("PGLOCALEDIR") == NULL)
583 : {
584 : /* set for libpq to use */
585 : snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
586 : canonicalize_path(env_path + 12);
587 : dup_path = strdup(env_path);
588 : if (dup_path)
589 : putenv(dup_path);
590 : }
591 : #endif
592 :
593 194 : if (getenv("PGSYSCONFDIR") == NULL)
594 : {
595 1 : get_etc_path(my_exec_path, path);
596 :
597 : /* set for libpq to use */
598 1 : snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
599 1 : canonicalize_path(env_path + 13);
600 1 : dup_path = strdup(env_path);
601 1 : if (dup_path)
602 1 : putenv(dup_path);
603 : }
604 : }
605 :
606 : #ifdef WIN32
607 :
608 : /*
609 : * AddUserToTokenDacl(HANDLE hToken)
610 : *
611 : * This function adds the current user account to the restricted
612 : * token used when we create a restricted process.
613 : *
614 : * This is required because of some security changes in Windows
615 : * that appeared in patches to XP/2K3 and in Vista/2008.
616 : *
617 : * On these machines, the Administrator account is not included in
618 : * the default DACL - you just get Administrators + System. For
619 : * regular users you get User + System. Because we strip Administrators
620 : * when we create the restricted token, we are left with only System
621 : * in the DACL which leads to access denied errors for later CreatePipe()
622 : * and CreateProcess() calls when running as Administrator.
623 : *
624 : * This function fixes this problem by modifying the DACL of the
625 : * token the process will use, and explicitly re-adding the current
626 : * user account. This is still secure because the Administrator account
627 : * inherits its privileges from the Administrators group - it doesn't
628 : * have any of its own.
629 : */
630 : BOOL
631 : AddUserToTokenDacl(HANDLE hToken)
632 : {
633 : int i;
634 : ACL_SIZE_INFORMATION asi;
635 : ACCESS_ALLOWED_ACE *pace;
636 : DWORD dwNewAclSize;
637 : DWORD dwSize = 0;
638 : DWORD dwTokenInfoLength = 0;
639 : PACL pacl = NULL;
640 : PTOKEN_USER pTokenUser = NULL;
641 : TOKEN_DEFAULT_DACL tddNew;
642 : TOKEN_DEFAULT_DACL *ptdd = NULL;
643 : TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
644 : BOOL ret = FALSE;
645 :
646 : /* Figure out the buffer size for the DACL info */
647 : if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
648 : {
649 : if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
650 : {
651 : ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
652 : if (ptdd == NULL)
653 : {
654 : log_error("could not allocate %lu bytes of memory", dwSize);
655 : goto cleanup;
656 : }
657 :
658 : if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
659 : {
660 : log_error("could not get token information: error code %lu", GetLastError());
661 : goto cleanup;
662 : }
663 : }
664 : else
665 : {
666 : log_error("could not get token information buffer size: error code %lu", GetLastError());
667 : goto cleanup;
668 : }
669 : }
670 :
671 : /* Get the ACL info */
672 : if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
673 : (DWORD) sizeof(ACL_SIZE_INFORMATION),
674 : AclSizeInformation))
675 : {
676 : log_error("could not get ACL information: error code %lu", GetLastError());
677 : goto cleanup;
678 : }
679 :
680 : /* Get the current user SID */
681 : if (!GetTokenUser(hToken, &pTokenUser))
682 : goto cleanup; /* callee printed a message */
683 :
684 : /* Figure out the size of the new ACL */
685 : dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
686 : GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
687 :
688 : /* Allocate the ACL buffer & initialize it */
689 : pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
690 : if (pacl == NULL)
691 : {
692 : log_error("could not allocate %lu bytes of memory", dwNewAclSize);
693 : goto cleanup;
694 : }
695 :
696 : if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
697 : {
698 : log_error("could not initialize ACL: error code %lu", GetLastError());
699 : goto cleanup;
700 : }
701 :
702 : /* Loop through the existing ACEs, and build the new ACL */
703 : for (i = 0; i < (int) asi.AceCount; i++)
704 : {
705 : if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
706 : {
707 : log_error("could not get ACE: error code %lu", GetLastError());
708 : goto cleanup;
709 : }
710 :
711 : if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
712 : {
713 : log_error("could not add ACE: error code %lu", GetLastError());
714 : goto cleanup;
715 : }
716 : }
717 :
718 : /* Add the new ACE for the current user */
719 : if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
720 : {
721 : log_error("could not add access allowed ACE: error code %lu", GetLastError());
722 : goto cleanup;
723 : }
724 :
725 : /* Set the new DACL in the token */
726 : tddNew.DefaultDacl = pacl;
727 :
728 : if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
729 : {
730 : log_error("could not set token information: error code %lu", GetLastError());
731 : goto cleanup;
732 : }
733 :
734 : ret = TRUE;
735 :
736 : cleanup:
737 : if (pTokenUser)
738 : LocalFree((HLOCAL) pTokenUser);
739 :
740 : if (pacl)
741 : LocalFree((HLOCAL) pacl);
742 :
743 : if (ptdd)
744 : LocalFree((HLOCAL) ptdd);
745 :
746 : return ret;
747 : }
748 :
749 : /*
750 : * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
751 : *
752 : * Get the users token information from a process token.
753 : *
754 : * The caller of this function is responsible for calling LocalFree() on the
755 : * returned TOKEN_USER memory.
756 : */
757 : static BOOL
758 : GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
759 : {
760 : DWORD dwLength;
761 :
762 : *ppTokenUser = NULL;
763 :
764 : if (!GetTokenInformation(hToken,
765 : TokenUser,
766 : NULL,
767 : 0,
768 : &dwLength))
769 : {
770 : if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
771 : {
772 : *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
773 :
774 : if (*ppTokenUser == NULL)
775 : {
776 : log_error("could not allocate %lu bytes of memory", dwLength);
777 : return FALSE;
778 : }
779 : }
780 : else
781 : {
782 : log_error("could not get token information buffer size: error code %lu", GetLastError());
783 : return FALSE;
784 : }
785 : }
786 :
787 : if (!GetTokenInformation(hToken,
788 : TokenUser,
789 : *ppTokenUser,
790 : dwLength,
791 : &dwLength))
792 : {
793 : LocalFree(*ppTokenUser);
794 : *ppTokenUser = NULL;
795 :
796 : log_error("could not get token information: error code %lu", GetLastError());
797 : return FALSE;
798 : }
799 :
800 : /* Memory in *ppTokenUser is LocalFree():d by the caller */
801 : return TRUE;
802 : }
803 :
804 : #endif
|