OSDN Git Service

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