]> granicus.if.org Git - postgresql/blob - src/port/exec.c
Update copyrights for 2013
[postgresql] / src / port / exec.c
1 /*-------------------------------------------------------------------------
2  *
3  * exec.c
4  *              Functions for finding and validating executable files
5  *
6  *
7  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        src/port/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 WIN32_ONLY_COMPILER
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 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                 strcpy(path_exe, path);
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         if (stat(path, &buf) < 0)
84                 return -1;
85
86         if (!S_ISREG(buf.st_mode))
87                 return -1;
88
89         /*
90          * Ensure that the file is both executable and readable (required for
91          * dynamic loading).
92          */
93 #ifndef WIN32
94         is_r = (access(path, R_OK) == 0);
95         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         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 find_my_exec(const char *argv0, char *retpath)
120 {
121         char            cwd[MAXPGPATH],
122                                 test_path[MAXPGPATH];
123         char       *path;
124
125         if (!getcwd(cwd, MAXPGPATH))
126         {
127                 log_error(_("could not identify current directory: %s"),
128                                   strerror(errno));
129                 return -1;
130         }
131
132         /*
133          * If argv0 contains a separator, then PATH wasn't used.
134          */
135         if (first_dir_separator(argv0) != NULL)
136         {
137                 if (is_absolute_path(argv0))
138                         StrNCpy(retpath, argv0, MAXPGPATH);
139                 else
140                         join_path_components(retpath, cwd, argv0);
141                 canonicalize_path(retpath);
142
143                 if (validate_exec(retpath) == 0)
144                         return resolve_symlinks(retpath);
145
146                 log_error(_("invalid binary \"%s\""), retpath);
147                 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         if ((path = getenv("PATH")) && *path)
162         {
163                 char       *startp = NULL,
164                                    *endp = NULL;
165
166                 do
167                 {
168                         if (!startp)
169                                 startp = path;
170                         else
171                                 startp = endp + 1;
172
173                         endp = first_path_var_separator(startp);
174                         if (!endp)
175                                 endp = startp + strlen(startp); /* point to end */
176
177                         StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
178
179                         if (is_absolute_path(test_path))
180                                 join_path_components(retpath, test_path, argv0);
181                         else
182                         {
183                                 join_path_components(retpath, cwd, test_path);
184                                 join_path_components(retpath, retpath, argv0);
185                         }
186                         canonicalize_path(retpath);
187
188                         switch (validate_exec(retpath))
189                         {
190                                 case 0: /* found ok */
191                                         return resolve_symlinks(retpath);
192                                 case -1:                /* wasn't even a candidate, keep looking */
193                                         break;
194                                 case -2:                /* found but disqualified */
195                                         log_error(_("could not read binary \"%s\""),
196                                                           retpath);
197                                         break;
198                         }
199                 } while (*endp);
200         }
201
202         log_error(_("could not find a \"%s\" to execute"), argv0);
203         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 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         if (!getcwd(orig_wd, MAXPGPATH))
240         {
241                 log_error(_("could not identify current directory: %s"),
242                                   strerror(errno));
243                 return -1;
244         }
245
246         for (;;)
247         {
248                 char       *lsep;
249                 int                     rllen;
250
251                 lsep = last_dir_separator(path);
252                 if (lsep)
253                 {
254                         *lsep = '\0';
255                         if (chdir(path) == -1)
256                         {
257                                 log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
258                                 return -1;
259                         }
260                         fname = lsep + 1;
261                 }
262                 else
263                         fname = path;
264
265                 if (lstat(fname, &buf) < 0 ||
266                         !S_ISLNK(buf.st_mode))
267                         break;
268
269                 rllen = readlink(fname, link_buf, sizeof(link_buf));
270                 if (rllen < 0 || rllen >= sizeof(link_buf))
271                 {
272                         log_error(_("could not read symbolic link \"%s\""), fname);
273                         return -1;
274                 }
275                 link_buf[rllen] = '\0';
276                 strcpy(path, link_buf);
277         }
278
279         /* must copy final component out of 'path' temporarily */
280         strcpy(link_buf, fname);
281
282         if (!getcwd(path, MAXPGPATH))
283         {
284                 log_error(_("could not identify current directory: %s"),
285                                   strerror(errno));
286                 return -1;
287         }
288         join_path_components(path, path, link_buf);
289         canonicalize_path(path);
290
291         if (chdir(orig_wd) == -1)
292         {
293                 log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
294                 return -1;
295         }
296 #endif   /* HAVE_READLINK */
297
298         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 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         if (find_my_exec(argv0, retpath) < 0)
314                 return -1;
315
316         /* Trim off program name and keep just directory */
317         *last_dir_separator(retpath) = '\0';
318         canonicalize_path(retpath);
319
320         /* Now append the other program's name */
321         snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
322                          "/%s%s", target, EXE);
323
324         if (validate_exec(retpath) != 0)
325                 return -1;
326
327         snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
328
329         if (!pipe_read_line(cmd, line, sizeof(line)))
330                 return -1;
331
332         if (strcmp(line, versionstr) != 0)
333                 return -2;
334
335         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 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         fflush(stdout);
355         fflush(stderr);
356
357         errno = 0;
358         if ((pgver = popen(cmd, "r")) == NULL)
359         {
360                 perror("popen failure");
361                 return NULL;
362         }
363
364         errno = 0;
365         if (fgets(line, maxsize, pgver) == NULL)
366         {
367                 if (feof(pgver))
368                         fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
369                 else
370                         perror("fgets failure");
371                 pclose(pgver);                  /* no error checking */
372                 return NULL;
373         }
374
375         if (pclose_check(pgver))
376                 return NULL;
377
378         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  * Is this necessary?  bjm 2004-05-11
509  * Originally this was stated to be here because pipe.c had backend linkage.
510  * Perhaps that's no longer so now we have got rid of pipe.c amd 2012-03-28
511  */
512 int
513 pclose_check(FILE *stream)
514 {
515         int                     exitstatus;
516
517         exitstatus = pclose(stream);
518
519         if (exitstatus == 0)
520                 return 0;                               /* all is well */
521
522         if (exitstatus == -1)
523         {
524                 /* pclose() itself failed, and hopefully set errno */
525                 perror("pclose failed");
526         }
527         else if (WIFEXITED(exitstatus))
528                 log_error(_("child process exited with exit code %d"),
529                                   WEXITSTATUS(exitstatus));
530         else if (WIFSIGNALED(exitstatus))
531 #if defined(WIN32)
532                 log_error(_("child process was terminated by exception 0x%X"),
533                                   WTERMSIG(exitstatus));
534 #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
535         {
536                 char            str[256];
537
538                 snprintf(str, sizeof(str), "%d: %s", WTERMSIG(exitstatus),
539                                  WTERMSIG(exitstatus) < NSIG ?
540                                  sys_siglist[WTERMSIG(exitstatus)] : "(unknown)");
541                 log_error(_("child process was terminated by signal %s"), str);
542         }
543 #else
544                 log_error(_("child process was terminated by signal %d"),
545                                   WTERMSIG(exitstatus));
546 #endif
547         else
548                 log_error(_("child process exited with unrecognized status %d"),
549                                   exitstatus);
550
551         return -1;
552 }
553
554
555 /*
556  *      set_pglocale_pgservice
557  *
558  *      Set application-specific locale and service directory
559  *
560  *      This function takes the value of argv[0] rather than a full path.
561  *
562  * (You may be wondering why this is in exec.c.  It requires this module's
563  * services and doesn't introduce any new dependencies, so this seems as
564  * good as anyplace.)
565  */
566 void
567 set_pglocale_pgservice(const char *argv0, const char *app)
568 {
569         char            path[MAXPGPATH];
570         char            my_exec_path[MAXPGPATH];
571         char            env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
572                                                                                                                                  * PGLOCALEDIR */
573
574         /* don't set LC_ALL in the backend */
575         if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
576                 setlocale(LC_ALL, "");
577
578         if (find_my_exec(argv0, my_exec_path) < 0)
579                 return;
580
581 #ifdef ENABLE_NLS
582         get_locale_path(my_exec_path, path);
583         bindtextdomain(app, path);
584         textdomain(app);
585
586         if (getenv("PGLOCALEDIR") == NULL)
587         {
588                 /* set for libpq to use */
589                 snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
590                 canonicalize_path(env_path + 12);
591                 putenv(strdup(env_path));
592         }
593 #endif
594
595         if (getenv("PGSYSCONFDIR") == NULL)
596         {
597                 get_etc_path(my_exec_path, path);
598
599                 /* set for libpq to use */
600                 snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
601                 canonicalize_path(env_path + 13);
602                 putenv(strdup(env_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         /*
681          * Get the user token for the current user, which provides us with the SID
682          * that is needed for creating the ACL.
683          */
684         if (!GetTokenUser(hToken, &pTokenUser))
685         {
686                 log_error("could not get user token: error code %lu", GetLastError());
687                 goto cleanup;
688         }
689
690         /* Figure out the size of the new ACL */
691         dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
692                 GetLengthSid(pTokenUser->User.Sid) -sizeof(DWORD);
693
694         /* Allocate the ACL buffer & initialize it */
695         pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
696         if (pacl == NULL)
697         {
698                 log_error("could not allocate %lu bytes of memory", dwNewAclSize);
699                 goto cleanup;
700         }
701
702         if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
703         {
704                 log_error("could not initialize ACL: error code %lu", GetLastError());
705                 goto cleanup;
706         }
707
708         /* Loop through the existing ACEs, and build the new ACL */
709         for (i = 0; i < (int) asi.AceCount; i++)
710         {
711                 if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
712                 {
713                         log_error("could not get ACE: error code %lu", GetLastError());
714                         goto cleanup;
715                 }
716
717                 if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
718                 {
719                         log_error("could not add ACE: error code %lu", GetLastError());
720                         goto cleanup;
721                 }
722         }
723
724         /* Add the new ACE for the current user */
725         if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
726         {
727                 log_error("could not add access allowed ACE: error code %lu", GetLastError());
728                 goto cleanup;
729         }
730
731         /* Set the new DACL in the token */
732         tddNew.DefaultDacl = pacl;
733
734         if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
735         {
736                 log_error("could not set token information: error code %lu", GetLastError());
737                 goto cleanup;
738         }
739
740         ret = TRUE;
741
742 cleanup:
743         if (pTokenUser)
744                 LocalFree((HLOCAL) pTokenUser);
745
746         if (pacl)
747                 LocalFree((HLOCAL) pacl);
748
749         if (ptdd)
750                 LocalFree((HLOCAL) ptdd);
751
752         return ret;
753 }
754
755 /*
756  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
757  *
758  * Get the users token information from a process token.
759  *
760  * The caller of this function is responsible for calling LocalFree() on the
761  * returned TOKEN_USER memory.
762  */
763 static BOOL
764 GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
765 {
766         DWORD           dwLength;
767
768         *ppTokenUser = NULL;
769
770         if (!GetTokenInformation(hToken,
771                                                          TokenUser,
772                                                          NULL,
773                                                          0,
774                                                          &dwLength))
775         {
776                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
777                 {
778                         *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
779
780                         if (*ppTokenUser == NULL)
781                         {
782                                 log_error("could not allocate %lu bytes of memory", dwLength);
783                                 return FALSE;
784                         }
785                 }
786                 else
787                 {
788                         log_error("could not get token information buffer size: error code %lu", GetLastError());
789                         return FALSE;
790                 }
791         }
792
793         if (!GetTokenInformation(hToken,
794                                                          TokenUser,
795                                                          *ppTokenUser,
796                                                          dwLength,
797                                                          &dwLength))
798         {
799                 LocalFree(*ppTokenUser);
800                 *ppTokenUser = NULL;
801
802                 log_error("could not get token information: error code %lu", GetLastError());
803                 return FALSE;
804         }
805
806         /* Memory in *ppTokenUser is LocalFree():d by the caller */
807         return TRUE;
808 }
809
810 #endif