]> granicus.if.org Git - postgresql/blob - src/port/exec.c
Stamp copyrights for year 2011.
[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-2011, 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-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                 pclose(pgver);          /* no error checking */
362                 return NULL;
363         }
364
365         if (pclose_check(pgver))
366                 return NULL;
367
368         return line;
369 #else                                                   /* WIN32 */
370
371         SECURITY_ATTRIBUTES sattr;
372         HANDLE          childstdoutrd,
373                                 childstdoutwr,
374                                 childstdoutrddup;
375         PROCESS_INFORMATION pi;
376         STARTUPINFO si;
377         char       *retval = NULL;
378
379         sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
380         sattr.bInheritHandle = TRUE;
381         sattr.lpSecurityDescriptor = NULL;
382
383         if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
384                 return NULL;
385
386         if (!DuplicateHandle(GetCurrentProcess(),
387                                                  childstdoutrd,
388                                                  GetCurrentProcess(),
389                                                  &childstdoutrddup,
390                                                  0,
391                                                  FALSE,
392                                                  DUPLICATE_SAME_ACCESS))
393         {
394                 CloseHandle(childstdoutrd);
395                 CloseHandle(childstdoutwr);
396                 return NULL;
397         }
398
399         CloseHandle(childstdoutrd);
400
401         ZeroMemory(&pi, sizeof(pi));
402         ZeroMemory(&si, sizeof(si));
403         si.cb = sizeof(si);
404         si.dwFlags = STARTF_USESTDHANDLES;
405         si.hStdError = childstdoutwr;
406         si.hStdOutput = childstdoutwr;
407         si.hStdInput = INVALID_HANDLE_VALUE;
408
409         if (CreateProcess(NULL,
410                                           cmd,
411                                           NULL,
412                                           NULL,
413                                           TRUE,
414                                           0,
415                                           NULL,
416                                           NULL,
417                                           &si,
418                                           &pi))
419         {
420                 /* Successfully started the process */
421                 char       *lineptr;
422
423                 ZeroMemory(line, maxsize);
424
425                 /* Try to read at least one line from the pipe */
426                 /* This may require more than one wait/read attempt */
427                 for (lineptr = line; lineptr < line + maxsize - 1;)
428                 {
429                         DWORD           bytesread = 0;
430
431                         /* Let's see if we can read */
432                         if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
433                                 break;                  /* Timeout, but perhaps we got a line already */
434
435                         if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
436                                                   &bytesread, NULL))
437                                 break;                  /* Error, but perhaps we got a line already */
438
439                         lineptr += strlen(lineptr);
440
441                         if (!bytesread)
442                                 break;                  /* EOF */
443
444                         if (strchr(line, '\n'))
445                                 break;                  /* One or more lines read */
446                 }
447
448                 if (lineptr != line)
449                 {
450                         /* OK, we read some data */
451                         int                     len;
452
453                         /* If we got more than one line, cut off after the first \n */
454                         lineptr = strchr(line, '\n');
455                         if (lineptr)
456                                 *(lineptr + 1) = '\0';
457
458                         len = strlen(line);
459
460                         /*
461                          * If EOL is \r\n, convert to just \n. Because stdout is a
462                          * text-mode stream, the \n output by the child process is
463                          * received as \r\n, so we convert it to \n.  The server main.c
464                          * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
465                          * disabling \n to \r\n expansion for stdout.
466                          */
467                         if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
468                         {
469                                 line[len - 2] = '\n';
470                                 line[len - 1] = '\0';
471                                 len--;
472                         }
473
474                         /*
475                          * We emulate fgets() behaviour. So if there is no newline at the
476                          * end, we add one...
477                          */
478                         if (len == 0 || line[len - 1] != '\n')
479                                 strcat(line, "\n");
480
481                         retval = line;
482                 }
483
484                 CloseHandle(pi.hProcess);
485                 CloseHandle(pi.hThread);
486         }
487
488         CloseHandle(childstdoutwr);
489         CloseHandle(childstdoutrddup);
490
491         return retval;
492 #endif   /* WIN32 */
493 }
494
495
496 /*
497  * pclose() plus useful error reporting
498  * Is this necessary?  bjm 2004-05-11
499  * It is better here because pipe.c has win32 backend linkage.
500  */
501 int
502 pclose_check(FILE *stream)
503 {
504         int                     exitstatus;
505
506         exitstatus = pclose(stream);
507
508         if (exitstatus == 0)
509                 return 0;                               /* all is well */
510
511         if (exitstatus == -1)
512         {
513                 /* pclose() itself failed, and hopefully set errno */
514                 perror("pclose failed");
515         }
516         else if (WIFEXITED(exitstatus))
517                 log_error(_("child process exited with exit code %d"),
518                                   WEXITSTATUS(exitstatus));
519         else if (WIFSIGNALED(exitstatus))
520 #if defined(WIN32)
521                 log_error(_("child process was terminated by exception 0x%X"),
522                                   WTERMSIG(exitstatus));
523 #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
524         {
525                 char            str[256];
526
527                 snprintf(str, sizeof(str), "%d: %s", WTERMSIG(exitstatus),
528                                  WTERMSIG(exitstatus) < NSIG ?
529                                  sys_siglist[WTERMSIG(exitstatus)] : "(unknown)");
530                 log_error(_("child process was terminated by signal %s"), str);
531         }
532 #else
533                 log_error(_("child process was terminated by signal %d"),
534                                   WTERMSIG(exitstatus));
535 #endif
536         else
537                 log_error(_("child process exited with unrecognized status %d"),
538                                   exitstatus);
539
540         return -1;
541 }
542
543
544 /*
545  *      set_pglocale_pgservice
546  *
547  *      Set application-specific locale and service directory
548  *
549  *      This function takes the value of argv[0] rather than a full path.
550  *
551  * (You may be wondering why this is in exec.c.  It requires this module's
552  * services and doesn't introduce any new dependencies, so this seems as
553  * good as anyplace.)
554  */
555 void
556 set_pglocale_pgservice(const char *argv0, const char *app)
557 {
558         char            path[MAXPGPATH];
559         char            my_exec_path[MAXPGPATH];
560         char            env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
561                                                                                                                                  * PGLOCALEDIR */
562
563         /* don't set LC_ALL in the backend */
564         if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
565                 setlocale(LC_ALL, "");
566
567         if (find_my_exec(argv0, my_exec_path) < 0)
568                 return;
569
570 #ifdef ENABLE_NLS
571         get_locale_path(my_exec_path, path);
572         bindtextdomain(app, path);
573         textdomain(app);
574
575         if (getenv("PGLOCALEDIR") == NULL)
576         {
577                 /* set for libpq to use */
578                 snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
579                 canonicalize_path(env_path + 12);
580                 putenv(strdup(env_path));
581         }
582 #endif
583
584         if (getenv("PGSYSCONFDIR") == NULL)
585         {
586                 get_etc_path(my_exec_path, path);
587
588                 /* set for libpq to use */
589                 snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
590                 canonicalize_path(env_path + 13);
591                 putenv(strdup(env_path));
592         }
593 }
594
595 #ifdef WIN32
596
597 /*
598  * AddUserToTokenDacl(HANDLE hToken)
599  *
600  * This function adds the current user account to the restricted
601  * token used when we create a restricted process.
602  *
603  * This is required because of some security changes in Windows
604  * that appeared in patches to XP/2K3 and in Vista/2008.
605  *
606  * On these machines, the Administrator account is not included in
607  * the default DACL - you just get Administrators + System. For
608  * regular users you get User + System. Because we strip Administrators
609  * when we create the restricted token, we are left with only System
610  * in the DACL which leads to access denied errors for later CreatePipe()
611  * and CreateProcess() calls when running as Administrator.
612  *
613  * This function fixes this problem by modifying the DACL of the
614  * token the process will use, and explicitly re-adding the current
615  * user account.  This is still secure because the Administrator account
616  * inherits its privileges from the Administrators group - it doesn't
617  * have any of its own.
618  */
619 BOOL
620 AddUserToTokenDacl(HANDLE hToken)
621 {
622         int                     i;
623         ACL_SIZE_INFORMATION asi;
624         ACCESS_ALLOWED_ACE *pace;
625         DWORD           dwNewAclSize;
626         DWORD           dwSize = 0;
627         DWORD           dwTokenInfoLength = 0;
628         PACL            pacl = NULL;
629         PTOKEN_USER pTokenUser = NULL;
630         TOKEN_DEFAULT_DACL tddNew;
631         TOKEN_DEFAULT_DACL *ptdd = NULL;
632         TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
633         BOOL            ret = FALSE;
634
635         /* Figure out the buffer size for the DACL info */
636         if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
637         {
638                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
639                 {
640                         ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
641                         if (ptdd == NULL)
642                         {
643                                 log_error("could not allocate %lu bytes of memory", dwSize);
644                                 goto cleanup;
645                         }
646
647                         if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
648                         {
649                                 log_error("could not get token information: %lu", GetLastError());
650                                 goto cleanup;
651                         }
652                 }
653                 else
654                 {
655                         log_error("could not get token information buffer size: %lu", GetLastError());
656                         goto cleanup;
657                 }
658         }
659
660         /* Get the ACL info */
661         if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
662                                                    (DWORD) sizeof(ACL_SIZE_INFORMATION),
663                                                    AclSizeInformation))
664         {
665                 log_error("could not get ACL information: %lu", GetLastError());
666                 goto cleanup;
667         }
668
669         /*
670          * Get the user token for the current user, which provides us with the SID
671          * that is needed for creating the ACL.
672          */
673         if (!GetTokenUser(hToken, &pTokenUser))
674         {
675                 log_error("could not get user token: %lu", GetLastError());
676                 goto cleanup;
677         }
678
679         /* Figure out the size of the new ACL */
680         dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
681                 GetLengthSid(pTokenUser->User.Sid) -sizeof(DWORD);
682
683         /* Allocate the ACL buffer & initialize it */
684         pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
685         if (pacl == NULL)
686         {
687                 log_error("could not allocate %lu bytes of memory", dwNewAclSize);
688                 goto cleanup;
689         }
690
691         if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
692         {
693                 log_error("could not initialize ACL: %lu", GetLastError());
694                 goto cleanup;
695         }
696
697         /* Loop through the existing ACEs, and build the new ACL */
698         for (i = 0; i < (int) asi.AceCount; i++)
699         {
700                 if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
701                 {
702                         log_error("could not get ACE: %lu", GetLastError());
703                         goto cleanup;
704                 }
705
706                 if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
707                 {
708                         log_error("could not add ACE: %lu", GetLastError());
709                         goto cleanup;
710                 }
711         }
712
713         /* Add the new ACE for the current user */
714         if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
715         {
716                 log_error("could not add access allowed ACE: %lu", GetLastError());
717                 goto cleanup;
718         }
719
720         /* Set the new DACL in the token */
721         tddNew.DefaultDacl = pacl;
722
723         if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
724         {
725                 log_error("could not set token information: %lu", GetLastError());
726                 goto cleanup;
727         }
728
729         ret = TRUE;
730
731 cleanup:
732         if (pTokenUser)
733                 LocalFree((HLOCAL) pTokenUser);
734
735         if (pacl)
736                 LocalFree((HLOCAL) pacl);
737
738         if (ptdd)
739                 LocalFree((HLOCAL) ptdd);
740
741         return ret;
742 }
743
744 /*
745  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
746  *
747  * Get the users token information from a process token.
748  *
749  * The caller of this function is responsible for calling LocalFree() on the
750  * returned TOKEN_USER memory.
751  */
752 static BOOL
753 GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
754 {
755         DWORD           dwLength;
756
757         *ppTokenUser = NULL;
758
759         if (!GetTokenInformation(hToken,
760                                                          TokenUser,
761                                                          NULL,
762                                                          0,
763                                                          &dwLength))
764         {
765                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
766                 {
767                         *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
768
769                         if (*ppTokenUser == NULL)
770                         {
771                                 log_error("could not allocate %lu bytes of memory", dwLength);
772                                 return FALSE;
773                         }
774                 }
775                 else
776                 {
777                         log_error("could not get token information buffer size: %lu", GetLastError());
778                         return FALSE;
779                 }
780         }
781
782         if (!GetTokenInformation(hToken,
783                                                          TokenUser,
784                                                          *ppTokenUser,
785                                                          dwLength,
786                                                          &dwLength))
787         {
788                 LocalFree(*ppTokenUser);
789                 *ppTokenUser = NULL;
790
791                 log_error("could not get token information: %lu", GetLastError());
792                 return FALSE;
793         }
794
795         /* Memory in *ppTokenUser is LocalFree():d by the caller */
796         return TRUE;
797 }
798
799 #endif