]> granicus.if.org Git - postgresql/blob - src/common/exec.c
Add missing translate_columns array entry
[postgresql] / src / common / exec.c
1 /*-------------------------------------------------------------------------
2  *
3  * exec.c
4  *              Functions for finding and validating executable files
5  *
6  *
7  * Portions Copyright (c) 1996-2016, 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 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                 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         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         strlcpy(link_buf, fname, sizeof(link_buf));
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  */
509 int
510 pclose_check(FILE *stream)
511 {
512         int                     exitstatus;
513         char       *reason;
514
515         exitstatus = pclose(stream);
516
517         if (exitstatus == 0)
518                 return 0;                               /* all is well */
519
520         if (exitstatus == -1)
521         {
522                 /* pclose() itself failed, and hopefully set errno */
523                 log_error(_("pclose failed: %s"), strerror(errno));
524         }
525         else
526         {
527                 reason = wait_result_to_str(exitstatus);
528                 log_error("%s", reason);
529 #ifdef FRONTEND
530                 free(reason);
531 #else
532                 pfree(reason);
533 #endif
534         }
535         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 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
557         /* don't set LC_ALL in the backend */
558         if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
559         {
560                 setlocale(LC_ALL, "");
561
562                 /*
563                  * One could make a case for reproducing here PostmasterMain()'s test
564                  * for whether the process is multithreaded.  Unlike the postmaster,
565                  * no frontend program calls sigprocmask() or otherwise provides for
566                  * mutual exclusion between signal handlers.  While frontends using
567                  * fork(), if multithreaded, are formally exposed to undefined
568                  * behavior, we have not witnessed a concrete bug.  Therefore,
569                  * complaining about multithreading here may be mere pedantry.
570                  */
571         }
572
573         if (find_my_exec(argv0, my_exec_path) < 0)
574                 return;
575
576 #ifdef ENABLE_NLS
577         get_locale_path(my_exec_path, path);
578         bindtextdomain(app, path);
579         textdomain(app);
580
581         if (getenv("PGLOCALEDIR") == NULL)
582         {
583                 /* set for libpq to use */
584                 snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
585                 canonicalize_path(env_path + 12);
586                 putenv(strdup(env_path));
587         }
588 #endif
589
590         if (getenv("PGSYSCONFDIR") == NULL)
591         {
592                 get_etc_path(my_exec_path, path);
593
594                 /* set for libpq to use */
595                 snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
596                 canonicalize_path(env_path + 13);
597                 putenv(strdup(env_path));
598         }
599 }
600
601 #ifdef WIN32
602
603 /*
604  * AddUserToTokenDacl(HANDLE hToken)
605  *
606  * This function adds the current user account to the restricted
607  * token used when we create a restricted process.
608  *
609  * This is required because of some security changes in Windows
610  * that appeared in patches to XP/2K3 and in Vista/2008.
611  *
612  * On these machines, the Administrator account is not included in
613  * the default DACL - you just get Administrators + System. For
614  * regular users you get User + System. Because we strip Administrators
615  * when we create the restricted token, we are left with only System
616  * in the DACL which leads to access denied errors for later CreatePipe()
617  * and CreateProcess() calls when running as Administrator.
618  *
619  * This function fixes this problem by modifying the DACL of the
620  * token the process will use, and explicitly re-adding the current
621  * user account.  This is still secure because the Administrator account
622  * inherits its privileges from the Administrators group - it doesn't
623  * have any of its own.
624  */
625 BOOL
626 AddUserToTokenDacl(HANDLE hToken)
627 {
628         int                     i;
629         ACL_SIZE_INFORMATION asi;
630         ACCESS_ALLOWED_ACE *pace;
631         DWORD           dwNewAclSize;
632         DWORD           dwSize = 0;
633         DWORD           dwTokenInfoLength = 0;
634         PACL            pacl = NULL;
635         PTOKEN_USER pTokenUser = NULL;
636         TOKEN_DEFAULT_DACL tddNew;
637         TOKEN_DEFAULT_DACL *ptdd = NULL;
638         TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
639         BOOL            ret = FALSE;
640
641         /* Figure out the buffer size for the DACL info */
642         if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
643         {
644                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
645                 {
646                         ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
647                         if (ptdd == NULL)
648                         {
649                                 log_error("could not allocate %lu bytes of memory", dwSize);
650                                 goto cleanup;
651                         }
652
653                         if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
654                         {
655                                 log_error("could not get token information: error code %lu", GetLastError());
656                                 goto cleanup;
657                         }
658                 }
659                 else
660                 {
661                         log_error("could not get token information buffer size: error code %lu", GetLastError());
662                         goto cleanup;
663                 }
664         }
665
666         /* Get the ACL info */
667         if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
668                                                    (DWORD) sizeof(ACL_SIZE_INFORMATION),
669                                                    AclSizeInformation))
670         {
671                 log_error("could not get ACL information: error code %lu", GetLastError());
672                 goto cleanup;
673         }
674
675         /* Get the current user SID */
676         if (!GetTokenUser(hToken, &pTokenUser))
677                 goto cleanup;                   /* callee printed a message */
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: error code %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: error code %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: error code %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: error code %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: error code %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: error code %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: error code %lu", GetLastError());
792                 return FALSE;
793         }
794
795         /* Memory in *ppTokenUser is LocalFree():d by the caller */
796         return TRUE;
797 }
798
799 #endif