Header And Logo

PostgreSQL
| The world's most advanced open source database.

exec.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * exec.c
00004  *      Functions for finding and validating executable files
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/port/exec.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 
00017 #ifndef FRONTEND
00018 #include "postgres.h"
00019 #else
00020 #include "postgres_fe.h"
00021 #endif
00022 
00023 #include <signal.h>
00024 #include <sys/stat.h>
00025 #include <sys/wait.h>
00026 #include <unistd.h>
00027 
00028 #ifndef FRONTEND
00029 /* We use only 3- and 4-parameter elog calls in this file, for simplicity */
00030 /* NOTE: caller must provide gettext call around str! */
00031 #define log_error(str, param)   elog(LOG, str, param)
00032 #define log_error4(str, param, arg1)    elog(LOG, str, param, arg1)
00033 #else
00034 #define log_error(str, param)   (fprintf(stderr, str, param), fputc('\n', stderr))
00035 #define log_error4(str, param, arg1)    (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
00036 #endif
00037 
00038 #ifdef WIN32_ONLY_COMPILER
00039 #define getcwd(cwd,len)  GetCurrentDirectory(len, cwd)
00040 #endif
00041 
00042 static int  validate_exec(const char *path);
00043 static int  resolve_symlinks(char *path);
00044 static char *pipe_read_line(char *cmd, char *line, int maxsize);
00045 
00046 #ifdef WIN32
00047 static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
00048 #endif
00049 
00050 /*
00051  * validate_exec -- validate "path" as an executable file
00052  *
00053  * returns 0 if the file is found and no error is encountered.
00054  *        -1 if the regular file "path" does not exist or cannot be executed.
00055  *        -2 if the file is otherwise valid but cannot be read.
00056  */
00057 static int
00058 validate_exec(const char *path)
00059 {
00060     struct stat buf;
00061     int         is_r;
00062     int         is_x;
00063 
00064 #ifdef WIN32
00065     char        path_exe[MAXPGPATH + sizeof(".exe") - 1];
00066 
00067     /* Win32 requires a .exe suffix for stat() */
00068     if (strlen(path) >= strlen(".exe") &&
00069         pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
00070     {
00071         strcpy(path_exe, path);
00072         strcat(path_exe, ".exe");
00073         path = path_exe;
00074     }
00075 #endif
00076 
00077     /*
00078      * Ensure that the file exists and is a regular file.
00079      *
00080      * XXX if you have a broken system where stat() looks at the symlink
00081      * instead of the underlying file, you lose.
00082      */
00083     if (stat(path, &buf) < 0)
00084         return -1;
00085 
00086     if (!S_ISREG(buf.st_mode))
00087         return -1;
00088 
00089     /*
00090      * Ensure that the file is both executable and readable (required for
00091      * dynamic loading).
00092      */
00093 #ifndef WIN32
00094     is_r = (access(path, R_OK) == 0);
00095     is_x = (access(path, X_OK) == 0);
00096 #else
00097     is_r = buf.st_mode & S_IRUSR;
00098     is_x = buf.st_mode & S_IXUSR;
00099 #endif
00100     return is_x ? (is_r ? 0 : -2) : -1;
00101 }
00102 
00103 
00104 /*
00105  * find_my_exec -- find an absolute path to a valid executable
00106  *
00107  *  argv0 is the name passed on the command line
00108  *  retpath is the output area (must be of size MAXPGPATH)
00109  *  Returns 0 if OK, -1 if error.
00110  *
00111  * The reason we have to work so hard to find an absolute path is that
00112  * on some platforms we can't do dynamic loading unless we know the
00113  * executable's location.  Also, we need a full path not a relative
00114  * path because we will later change working directory.  Finally, we want
00115  * a true path not a symlink location, so that we can locate other files
00116  * that are part of our installation relative to the executable.
00117  */
00118 int
00119 find_my_exec(const char *argv0, char *retpath)
00120 {
00121     char        cwd[MAXPGPATH],
00122                 test_path[MAXPGPATH];
00123     char       *path;
00124 
00125     if (!getcwd(cwd, MAXPGPATH))
00126     {
00127         log_error(_("could not identify current directory: %s"),
00128                   strerror(errno));
00129         return -1;
00130     }
00131 
00132     /*
00133      * If argv0 contains a separator, then PATH wasn't used.
00134      */
00135     if (first_dir_separator(argv0) != NULL)
00136     {
00137         if (is_absolute_path(argv0))
00138             StrNCpy(retpath, argv0, MAXPGPATH);
00139         else
00140             join_path_components(retpath, cwd, argv0);
00141         canonicalize_path(retpath);
00142 
00143         if (validate_exec(retpath) == 0)
00144             return resolve_symlinks(retpath);
00145 
00146         log_error(_("invalid binary \"%s\""), retpath);
00147         return -1;
00148     }
00149 
00150 #ifdef WIN32
00151     /* Win32 checks the current directory first for names without slashes */
00152     join_path_components(retpath, cwd, argv0);
00153     if (validate_exec(retpath) == 0)
00154         return resolve_symlinks(retpath);
00155 #endif
00156 
00157     /*
00158      * Since no explicit path was supplied, the user must have been relying on
00159      * PATH.  We'll search the same PATH.
00160      */
00161     if ((path = getenv("PATH")) && *path)
00162     {
00163         char       *startp = NULL,
00164                    *endp = NULL;
00165 
00166         do
00167         {
00168             if (!startp)
00169                 startp = path;
00170             else
00171                 startp = endp + 1;
00172 
00173             endp = first_path_var_separator(startp);
00174             if (!endp)
00175                 endp = startp + strlen(startp); /* point to end */
00176 
00177             StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
00178 
00179             if (is_absolute_path(test_path))
00180                 join_path_components(retpath, test_path, argv0);
00181             else
00182             {
00183                 join_path_components(retpath, cwd, test_path);
00184                 join_path_components(retpath, retpath, argv0);
00185             }
00186             canonicalize_path(retpath);
00187 
00188             switch (validate_exec(retpath))
00189             {
00190                 case 0: /* found ok */
00191                     return resolve_symlinks(retpath);
00192                 case -1:        /* wasn't even a candidate, keep looking */
00193                     break;
00194                 case -2:        /* found but disqualified */
00195                     log_error(_("could not read binary \"%s\""),
00196                               retpath);
00197                     break;
00198             }
00199         } while (*endp);
00200     }
00201 
00202     log_error(_("could not find a \"%s\" to execute"), argv0);
00203     return -1;
00204 }
00205 
00206 
00207 /*
00208  * resolve_symlinks - resolve symlinks to the underlying file
00209  *
00210  * Replace "path" by the absolute path to the referenced file.
00211  *
00212  * Returns 0 if OK, -1 if error.
00213  *
00214  * Note: we are not particularly tense about producing nice error messages
00215  * because we are not really expecting error here; we just determined that
00216  * the symlink does point to a valid executable.
00217  */
00218 static int
00219 resolve_symlinks(char *path)
00220 {
00221 #ifdef HAVE_READLINK
00222     struct stat buf;
00223     char        orig_wd[MAXPGPATH],
00224                 link_buf[MAXPGPATH];
00225     char       *fname;
00226 
00227     /*
00228      * To resolve a symlink properly, we have to chdir into its directory and
00229      * then chdir to where the symlink points; otherwise we may fail to
00230      * resolve relative links correctly (consider cases involving mount
00231      * points, for example).  After following the final symlink, we use
00232      * getcwd() to figure out where the heck we're at.
00233      *
00234      * One might think we could skip all this if path doesn't point to a
00235      * symlink to start with, but that's wrong.  We also want to get rid of
00236      * any directory symlinks that are present in the given path. We expect
00237      * getcwd() to give us an accurate, symlink-free path.
00238      */
00239     if (!getcwd(orig_wd, MAXPGPATH))
00240     {
00241         log_error(_("could not identify current directory: %s"),
00242                   strerror(errno));
00243         return -1;
00244     }
00245 
00246     for (;;)
00247     {
00248         char       *lsep;
00249         int         rllen;
00250 
00251         lsep = last_dir_separator(path);
00252         if (lsep)
00253         {
00254             *lsep = '\0';
00255             if (chdir(path) == -1)
00256             {
00257                 log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
00258                 return -1;
00259             }
00260             fname = lsep + 1;
00261         }
00262         else
00263             fname = path;
00264 
00265         if (lstat(fname, &buf) < 0 ||
00266             !S_ISLNK(buf.st_mode))
00267             break;
00268 
00269         rllen = readlink(fname, link_buf, sizeof(link_buf));
00270         if (rllen < 0 || rllen >= sizeof(link_buf))
00271         {
00272             log_error(_("could not read symbolic link \"%s\""), fname);
00273             return -1;
00274         }
00275         link_buf[rllen] = '\0';
00276         strcpy(path, link_buf);
00277     }
00278 
00279     /* must copy final component out of 'path' temporarily */
00280     strcpy(link_buf, fname);
00281 
00282     if (!getcwd(path, MAXPGPATH))
00283     {
00284         log_error(_("could not identify current directory: %s"),
00285                   strerror(errno));
00286         return -1;
00287     }
00288     join_path_components(path, path, link_buf);
00289     canonicalize_path(path);
00290 
00291     if (chdir(orig_wd) == -1)
00292     {
00293         log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
00294         return -1;
00295     }
00296 #endif   /* HAVE_READLINK */
00297 
00298     return 0;
00299 }
00300 
00301 
00302 /*
00303  * Find another program in our binary's directory,
00304  * then make sure it is the proper version.
00305  */
00306 int
00307 find_other_exec(const char *argv0, const char *target,
00308                 const char *versionstr, char *retpath)
00309 {
00310     char        cmd[MAXPGPATH];
00311     char        line[100];
00312 
00313     if (find_my_exec(argv0, retpath) < 0)
00314         return -1;
00315 
00316     /* Trim off program name and keep just directory */
00317     *last_dir_separator(retpath) = '\0';
00318     canonicalize_path(retpath);
00319 
00320     /* Now append the other program's name */
00321     snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
00322              "/%s%s", target, EXE);
00323 
00324     if (validate_exec(retpath) != 0)
00325         return -1;
00326 
00327     snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
00328 
00329     if (!pipe_read_line(cmd, line, sizeof(line)))
00330         return -1;
00331 
00332     if (strcmp(line, versionstr) != 0)
00333         return -2;
00334 
00335     return 0;
00336 }
00337 
00338 
00339 /*
00340  * The runtime library's popen() on win32 does not work when being
00341  * called from a service when running on windows <= 2000, because
00342  * there is no stdin/stdout/stderr.
00343  *
00344  * Executing a command in a pipe and reading the first line from it
00345  * is all we need.
00346  */
00347 static char *
00348 pipe_read_line(char *cmd, char *line, int maxsize)
00349 {
00350 #ifndef WIN32
00351     FILE       *pgver;
00352 
00353     /* flush output buffers in case popen does not... */
00354     fflush(stdout);
00355     fflush(stderr);
00356 
00357     errno = 0;
00358     if ((pgver = popen(cmd, "r")) == NULL)
00359     {
00360         perror("popen failure");
00361         return NULL;
00362     }
00363 
00364     errno = 0;
00365     if (fgets(line, maxsize, pgver) == NULL)
00366     {
00367         if (feof(pgver))
00368             fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
00369         else
00370             perror("fgets failure");
00371         pclose(pgver);          /* no error checking */
00372         return NULL;
00373     }
00374 
00375     if (pclose_check(pgver))
00376         return NULL;
00377 
00378     return line;
00379 #else                           /* WIN32 */
00380 
00381     SECURITY_ATTRIBUTES sattr;
00382     HANDLE      childstdoutrd,
00383                 childstdoutwr,
00384                 childstdoutrddup;
00385     PROCESS_INFORMATION pi;
00386     STARTUPINFO si;
00387     char       *retval = NULL;
00388 
00389     sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
00390     sattr.bInheritHandle = TRUE;
00391     sattr.lpSecurityDescriptor = NULL;
00392 
00393     if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
00394         return NULL;
00395 
00396     if (!DuplicateHandle(GetCurrentProcess(),
00397                          childstdoutrd,
00398                          GetCurrentProcess(),
00399                          &childstdoutrddup,
00400                          0,
00401                          FALSE,
00402                          DUPLICATE_SAME_ACCESS))
00403     {
00404         CloseHandle(childstdoutrd);
00405         CloseHandle(childstdoutwr);
00406         return NULL;
00407     }
00408 
00409     CloseHandle(childstdoutrd);
00410 
00411     ZeroMemory(&pi, sizeof(pi));
00412     ZeroMemory(&si, sizeof(si));
00413     si.cb = sizeof(si);
00414     si.dwFlags = STARTF_USESTDHANDLES;
00415     si.hStdError = childstdoutwr;
00416     si.hStdOutput = childstdoutwr;
00417     si.hStdInput = INVALID_HANDLE_VALUE;
00418 
00419     if (CreateProcess(NULL,
00420                       cmd,
00421                       NULL,
00422                       NULL,
00423                       TRUE,
00424                       0,
00425                       NULL,
00426                       NULL,
00427                       &si,
00428                       &pi))
00429     {
00430         /* Successfully started the process */
00431         char       *lineptr;
00432 
00433         ZeroMemory(line, maxsize);
00434 
00435         /* Try to read at least one line from the pipe */
00436         /* This may require more than one wait/read attempt */
00437         for (lineptr = line; lineptr < line + maxsize - 1;)
00438         {
00439             DWORD       bytesread = 0;
00440 
00441             /* Let's see if we can read */
00442             if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
00443                 break;          /* Timeout, but perhaps we got a line already */
00444 
00445             if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
00446                           &bytesread, NULL))
00447                 break;          /* Error, but perhaps we got a line already */
00448 
00449             lineptr += strlen(lineptr);
00450 
00451             if (!bytesread)
00452                 break;          /* EOF */
00453 
00454             if (strchr(line, '\n'))
00455                 break;          /* One or more lines read */
00456         }
00457 
00458         if (lineptr != line)
00459         {
00460             /* OK, we read some data */
00461             int         len;
00462 
00463             /* If we got more than one line, cut off after the first \n */
00464             lineptr = strchr(line, '\n');
00465             if (lineptr)
00466                 *(lineptr + 1) = '\0';
00467 
00468             len = strlen(line);
00469 
00470             /*
00471              * If EOL is \r\n, convert to just \n. Because stdout is a
00472              * text-mode stream, the \n output by the child process is
00473              * received as \r\n, so we convert it to \n.  The server main.c
00474              * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
00475              * disabling \n to \r\n expansion for stdout.
00476              */
00477             if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
00478             {
00479                 line[len - 2] = '\n';
00480                 line[len - 1] = '\0';
00481                 len--;
00482             }
00483 
00484             /*
00485              * We emulate fgets() behaviour. So if there is no newline at the
00486              * end, we add one...
00487              */
00488             if (len == 0 || line[len - 1] != '\n')
00489                 strcat(line, "\n");
00490 
00491             retval = line;
00492         }
00493 
00494         CloseHandle(pi.hProcess);
00495         CloseHandle(pi.hThread);
00496     }
00497 
00498     CloseHandle(childstdoutwr);
00499     CloseHandle(childstdoutrddup);
00500 
00501     return retval;
00502 #endif   /* WIN32 */
00503 }
00504 
00505 
00506 /*
00507  * pclose() plus useful error reporting
00508  */
00509 int
00510 pclose_check(FILE *stream)
00511 {
00512     int         exitstatus;
00513     char       *reason;
00514 
00515     exitstatus = pclose(stream);
00516 
00517     if (exitstatus == 0)
00518         return 0;               /* all is well */
00519 
00520     if (exitstatus == -1)
00521     {
00522         /* pclose() itself failed, and hopefully set errno */
00523         log_error(_("pclose failed: %s"), strerror(errno));
00524     }
00525     else
00526     {
00527         reason = wait_result_to_str(exitstatus);
00528         log_error("%s", reason);
00529 #ifdef FRONTEND
00530         free(reason);
00531 #else
00532         pfree(reason);
00533 #endif
00534     }
00535     return exitstatus;
00536 }
00537 
00538 /*
00539  *  set_pglocale_pgservice
00540  *
00541  *  Set application-specific locale and service directory
00542  *
00543  *  This function takes the value of argv[0] rather than a full path.
00544  *
00545  * (You may be wondering why this is in exec.c.  It requires this module's
00546  * services and doesn't introduce any new dependencies, so this seems as
00547  * good as anyplace.)
00548  */
00549 void
00550 set_pglocale_pgservice(const char *argv0, const char *app)
00551 {
00552     char        path[MAXPGPATH];
00553     char        my_exec_path[MAXPGPATH];
00554     char        env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
00555                                                                  * PGLOCALEDIR */
00556 
00557     /* don't set LC_ALL in the backend */
00558     if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
00559         setlocale(LC_ALL, "");
00560 
00561     if (find_my_exec(argv0, my_exec_path) < 0)
00562         return;
00563 
00564 #ifdef ENABLE_NLS
00565     get_locale_path(my_exec_path, path);
00566     bindtextdomain(app, path);
00567     textdomain(app);
00568 
00569     if (getenv("PGLOCALEDIR") == NULL)
00570     {
00571         /* set for libpq to use */
00572         snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
00573         canonicalize_path(env_path + 12);
00574         putenv(strdup(env_path));
00575     }
00576 #endif
00577 
00578     if (getenv("PGSYSCONFDIR") == NULL)
00579     {
00580         get_etc_path(my_exec_path, path);
00581 
00582         /* set for libpq to use */
00583         snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
00584         canonicalize_path(env_path + 13);
00585         putenv(strdup(env_path));
00586     }
00587 }
00588 
00589 #ifdef WIN32
00590 
00591 /*
00592  * AddUserToTokenDacl(HANDLE hToken)
00593  *
00594  * This function adds the current user account to the restricted
00595  * token used when we create a restricted process.
00596  *
00597  * This is required because of some security changes in Windows
00598  * that appeared in patches to XP/2K3 and in Vista/2008.
00599  *
00600  * On these machines, the Administrator account is not included in
00601  * the default DACL - you just get Administrators + System. For
00602  * regular users you get User + System. Because we strip Administrators
00603  * when we create the restricted token, we are left with only System
00604  * in the DACL which leads to access denied errors for later CreatePipe()
00605  * and CreateProcess() calls when running as Administrator.
00606  *
00607  * This function fixes this problem by modifying the DACL of the
00608  * token the process will use, and explicitly re-adding the current
00609  * user account.  This is still secure because the Administrator account
00610  * inherits its privileges from the Administrators group - it doesn't
00611  * have any of its own.
00612  */
00613 BOOL
00614 AddUserToTokenDacl(HANDLE hToken)
00615 {
00616     int         i;
00617     ACL_SIZE_INFORMATION asi;
00618     ACCESS_ALLOWED_ACE *pace;
00619     DWORD       dwNewAclSize;
00620     DWORD       dwSize = 0;
00621     DWORD       dwTokenInfoLength = 0;
00622     PACL        pacl = NULL;
00623     PTOKEN_USER pTokenUser = NULL;
00624     TOKEN_DEFAULT_DACL tddNew;
00625     TOKEN_DEFAULT_DACL *ptdd = NULL;
00626     TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
00627     BOOL        ret = FALSE;
00628 
00629     /* Figure out the buffer size for the DACL info */
00630     if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
00631     {
00632         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
00633         {
00634             ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
00635             if (ptdd == NULL)
00636             {
00637                 log_error("could not allocate %lu bytes of memory", dwSize);
00638                 goto cleanup;
00639             }
00640 
00641             if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
00642             {
00643                 log_error("could not get token information: error code %lu", GetLastError());
00644                 goto cleanup;
00645             }
00646         }
00647         else
00648         {
00649             log_error("could not get token information buffer size: error code %lu", GetLastError());
00650             goto cleanup;
00651         }
00652     }
00653 
00654     /* Get the ACL info */
00655     if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
00656                            (DWORD) sizeof(ACL_SIZE_INFORMATION),
00657                            AclSizeInformation))
00658     {
00659         log_error("could not get ACL information: error code %lu", GetLastError());
00660         goto cleanup;
00661     }
00662 
00663     /*
00664      * Get the user token for the current user, which provides us with the SID
00665      * that is needed for creating the ACL.
00666      */
00667     if (!GetTokenUser(hToken, &pTokenUser))
00668     {
00669         log_error("could not get user token: error code %lu", GetLastError());
00670         goto cleanup;
00671     }
00672 
00673     /* Figure out the size of the new ACL */
00674     dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
00675         GetLengthSid(pTokenUser->User.Sid) -sizeof(DWORD);
00676 
00677     /* Allocate the ACL buffer & initialize it */
00678     pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
00679     if (pacl == NULL)
00680     {
00681         log_error("could not allocate %lu bytes of memory", dwNewAclSize);
00682         goto cleanup;
00683     }
00684 
00685     if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
00686     {
00687         log_error("could not initialize ACL: error code %lu", GetLastError());
00688         goto cleanup;
00689     }
00690 
00691     /* Loop through the existing ACEs, and build the new ACL */
00692     for (i = 0; i < (int) asi.AceCount; i++)
00693     {
00694         if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
00695         {
00696             log_error("could not get ACE: error code %lu", GetLastError());
00697             goto cleanup;
00698         }
00699 
00700         if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
00701         {
00702             log_error("could not add ACE: error code %lu", GetLastError());
00703             goto cleanup;
00704         }
00705     }
00706 
00707     /* Add the new ACE for the current user */
00708     if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
00709     {
00710         log_error("could not add access allowed ACE: error code %lu", GetLastError());
00711         goto cleanup;
00712     }
00713 
00714     /* Set the new DACL in the token */
00715     tddNew.DefaultDacl = pacl;
00716 
00717     if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
00718     {
00719         log_error("could not set token information: error code %lu", GetLastError());
00720         goto cleanup;
00721     }
00722 
00723     ret = TRUE;
00724 
00725 cleanup:
00726     if (pTokenUser)
00727         LocalFree((HLOCAL) pTokenUser);
00728 
00729     if (pacl)
00730         LocalFree((HLOCAL) pacl);
00731 
00732     if (ptdd)
00733         LocalFree((HLOCAL) ptdd);
00734 
00735     return ret;
00736 }
00737 
00738 /*
00739  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
00740  *
00741  * Get the users token information from a process token.
00742  *
00743  * The caller of this function is responsible for calling LocalFree() on the
00744  * returned TOKEN_USER memory.
00745  */
00746 static BOOL
00747 GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
00748 {
00749     DWORD       dwLength;
00750 
00751     *ppTokenUser = NULL;
00752 
00753     if (!GetTokenInformation(hToken,
00754                              TokenUser,
00755                              NULL,
00756                              0,
00757                              &dwLength))
00758     {
00759         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
00760         {
00761             *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
00762 
00763             if (*ppTokenUser == NULL)
00764             {
00765                 log_error("could not allocate %lu bytes of memory", dwLength);
00766                 return FALSE;
00767             }
00768         }
00769         else
00770         {
00771             log_error("could not get token information buffer size: error code %lu", GetLastError());
00772             return FALSE;
00773         }
00774     }
00775 
00776     if (!GetTokenInformation(hToken,
00777                              TokenUser,
00778                              *ppTokenUser,
00779                              dwLength,
00780                              &dwLength))
00781     {
00782         LocalFree(*ppTokenUser);
00783         *ppTokenUser = NULL;
00784 
00785         log_error("could not get token information: error code %lu", GetLastError());
00786         return FALSE;
00787     }
00788 
00789     /* Memory in *ppTokenUser is LocalFree():d by the caller */
00790     return TRUE;
00791 }
00792 
00793 #endif