LCOV - code coverage report
Current view: top level - src/common - exec.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 71.6 % 102 73
Test Date: 2026-03-04 04:14:49 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * exec.c
       4              :  *      Functions for finding and validating executable files
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, 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              : /*
      18              :  * On macOS, "man realpath" avers:
      19              :  *    Defining _DARWIN_C_SOURCE or _DARWIN_BETTER_REALPATH before including
      20              :  *    stdlib.h will cause the provided implementation of realpath() to use
      21              :  *    F_GETPATH from fcntl(2) to discover the path.
      22              :  * This should be harmless everywhere else.
      23              :  */
      24              : #define _DARWIN_BETTER_REALPATH
      25              : 
      26              : #ifndef FRONTEND
      27              : #include "postgres.h"
      28              : #else
      29              : #include "postgres_fe.h"
      30              : #endif
      31              : 
      32              : #include <signal.h>
      33              : #include <sys/stat.h>
      34              : #include <sys/wait.h>
      35              : #include <unistd.h>
      36              : 
      37              : #ifdef EXEC_BACKEND
      38              : #if defined(HAVE_SYS_PERSONALITY_H)
      39              : #include <sys/personality.h>
      40              : #elif defined(HAVE_SYS_PROCCTL_H)
      41              : #include <sys/procctl.h>
      42              : #endif
      43              : #endif
      44              : 
      45              : #include "common/string.h"
      46              : 
      47              : /* Inhibit mingw CRT's auto-globbing of command line arguments */
      48              : #if defined(WIN32) && !defined(_MSC_VER)
      49              : extern int  _CRT_glob;
      50              : int         _CRT_glob = 0;      /* 0 turns off globbing; 1 turns it on */
      51              : #endif
      52              : 
      53              : /*
      54              :  * Hacky solution to allow expressing both frontend and backend error reports
      55              :  * in one macro call.  First argument of log_error is an errcode() call of
      56              :  * some sort (ignored if FRONTEND); the rest are errmsg_internal() arguments,
      57              :  * i.e. message string and any parameters for it.
      58              :  *
      59              :  * Caller must provide the gettext wrapper around the message string, if
      60              :  * appropriate, so that it gets translated in the FRONTEND case; this
      61              :  * motivates using errmsg_internal() not errmsg().  We handle appending a
      62              :  * newline, if needed, inside the macro, so that there's only one translatable
      63              :  * string per call not two.
      64              :  */
      65              : #ifndef FRONTEND
      66              : #define log_error(errcodefn, ...) \
      67              :     ereport(LOG, (errcodefn, errmsg_internal(__VA_ARGS__)))
      68              : #else
      69              : #define log_error(errcodefn, ...) \
      70              :     (fprintf(stderr, __VA_ARGS__), fputc('\n', stderr))
      71              : #endif
      72              : 
      73              : static int  normalize_exec_path(char *path);
      74              : static char *pg_realpath(const char *fname);
      75              : 
      76              : #ifdef WIN32
      77              : static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
      78              : #endif
      79              : 
      80              : /*
      81              :  * validate_exec -- validate "path" as an executable file
      82              :  *
      83              :  * returns 0 if the file is found and no error is encountered.
      84              :  *        -1 if the regular file "path" does not exist or cannot be executed.
      85              :  *        -2 if the file is otherwise valid but cannot be read.
      86              :  * in the failure cases, errno is set appropriately
      87              :  */
      88              : int
      89        20633 : validate_exec(const char *path)
      90              : {
      91              :     struct stat buf;
      92              :     int         is_r;
      93              :     int         is_x;
      94              : 
      95              : #ifdef WIN32
      96              :     char        path_exe[MAXPGPATH + sizeof(".exe") - 1];
      97              : 
      98              :     /* Win32 requires a .exe suffix for stat() */
      99              :     if (strlen(path) < strlen(".exe") ||
     100              :         pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
     101              :     {
     102              :         strlcpy(path_exe, path, sizeof(path_exe) - 4);
     103              :         strcat(path_exe, ".exe");
     104              :         path = path_exe;
     105              :     }
     106              : #endif
     107              : 
     108              :     /*
     109              :      * Ensure that the file exists and is a regular file.
     110              :      *
     111              :      * XXX if you have a broken system where stat() looks at the symlink
     112              :      * instead of the underlying file, you lose.
     113              :      */
     114        20633 :     if (stat(path, &buf) < 0)
     115            0 :         return -1;
     116              : 
     117        20633 :     if (!S_ISREG(buf.st_mode))
     118              :     {
     119              :         /*
     120              :          * POSIX offers no errno code that's simply "not a regular file".  If
     121              :          * it's a directory we can use EISDIR.  Otherwise, it's most likely a
     122              :          * device special file, and EPERM (Operation not permitted) isn't too
     123              :          * horribly off base.
     124              :          */
     125            0 :         errno = S_ISDIR(buf.st_mode) ? EISDIR : EPERM;
     126            0 :         return -1;
     127              :     }
     128              : 
     129              :     /*
     130              :      * Ensure that the file is both executable and readable (required for
     131              :      * dynamic loading).
     132              :      */
     133              : #ifndef WIN32
     134        20633 :     is_r = (access(path, R_OK) == 0);
     135        20633 :     is_x = (access(path, X_OK) == 0);
     136              :     /* access() will set errno if it returns -1 */
     137              : #else
     138              :     is_r = buf.st_mode & S_IRUSR;
     139              :     is_x = buf.st_mode & S_IXUSR;
     140              :     errno = EACCES;             /* appropriate thing if we return nonzero */
     141              : #endif
     142        20633 :     return is_x ? (is_r ? 0 : -2) : -1;
     143              : }
     144              : 
     145              : 
     146              : /*
     147              :  * find_my_exec -- find an absolute path to this program's executable
     148              :  *
     149              :  *  argv0 is the name passed on the command line
     150              :  *  retpath is the output area (must be of size MAXPGPATH)
     151              :  *  Returns 0 if OK, -1 if error.
     152              :  *
     153              :  * The reason we have to work so hard to find an absolute path is that
     154              :  * on some platforms we can't do dynamic loading unless we know the
     155              :  * executable's location.  Also, we need an absolute path not a relative
     156              :  * path because we may later change working directory.  Finally, we want
     157              :  * a true path not a symlink location, so that we can locate other files
     158              :  * that are part of our installation relative to the executable.
     159              :  */
     160              : int
     161        19423 : find_my_exec(const char *argv0, char *retpath)
     162              : {
     163              :     char       *path;
     164              : 
     165              :     /*
     166              :      * If argv0 contains a separator, then PATH wasn't used.
     167              :      */
     168        19423 :     strlcpy(retpath, argv0, MAXPGPATH);
     169        19423 :     if (first_dir_separator(retpath) != NULL)
     170              :     {
     171        14594 :         if (validate_exec(retpath) == 0)
     172        14594 :             return normalize_exec_path(retpath);
     173              : 
     174            0 :         log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
     175              :                   _("invalid binary \"%s\": %m"), retpath);
     176            0 :         return -1;
     177              :     }
     178              : 
     179              : #ifdef WIN32
     180              :     /* Win32 checks the current directory first for names without slashes */
     181              :     if (validate_exec(retpath) == 0)
     182              :         return normalize_exec_path(retpath);
     183              : #endif
     184              : 
     185              :     /*
     186              :      * Since no explicit path was supplied, the user must have been relying on
     187              :      * PATH.  We'll search the same PATH.
     188              :      */
     189         4829 :     if ((path = getenv("PATH")) && *path)
     190              :     {
     191         4829 :         char       *startp = NULL,
     192         4829 :                    *endp = NULL;
     193              : 
     194              :         do
     195              :         {
     196         4829 :             if (!startp)
     197         4829 :                 startp = path;
     198              :             else
     199            0 :                 startp = endp + 1;
     200              : 
     201         4829 :             endp = first_path_var_separator(startp);
     202         4829 :             if (!endp)
     203            0 :                 endp = startp + strlen(startp); /* point to end */
     204              : 
     205         4829 :             strlcpy(retpath, startp, Min(endp - startp + 1, MAXPGPATH));
     206              : 
     207         4829 :             join_path_components(retpath, retpath, argv0);
     208         4829 :             canonicalize_path(retpath);
     209              : 
     210         4829 :             switch (validate_exec(retpath))
     211              :             {
     212         4829 :                 case 0:         /* found ok */
     213         4829 :                     return normalize_exec_path(retpath);
     214            0 :                 case -1:        /* wasn't even a candidate, keep looking */
     215            0 :                     break;
     216            0 :                 case -2:        /* found but disqualified */
     217            0 :                     log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
     218              :                               _("could not read binary \"%s\": %m"),
     219              :                               retpath);
     220            0 :                     break;
     221              :             }
     222            0 :         } while (*endp);
     223              :     }
     224              : 
     225            0 :     log_error(errcode(ERRCODE_UNDEFINED_FILE),
     226              :               _("could not find a \"%s\" to execute"), argv0);
     227            0 :     return -1;
     228              : }
     229              : 
     230              : 
     231              : /*
     232              :  * normalize_exec_path - resolve symlinks and convert to absolute path
     233              :  *
     234              :  * Given a path that refers to an executable, chase through any symlinks
     235              :  * to find the real file location; then convert that to an absolute path.
     236              :  *
     237              :  * On success, replaces the contents of "path" with the absolute path.
     238              :  * ("path" is assumed to be of size MAXPGPATH.)
     239              :  * Returns 0 if OK, -1 if error.
     240              :  */
     241              : static int
     242        19423 : normalize_exec_path(char *path)
     243              : {
     244              :     /*
     245              :      * We used to do a lot of work ourselves here, but now we just let
     246              :      * realpath(3) do all the heavy lifting.
     247              :      */
     248        19423 :     char       *abspath = pg_realpath(path);
     249              : 
     250        19423 :     if (abspath == NULL)
     251              :     {
     252            0 :         log_error(errcode_for_file_access(),
     253              :                   _("could not resolve path \"%s\" to absolute form: %m"),
     254              :                   path);
     255            0 :         return -1;
     256              :     }
     257        19423 :     strlcpy(path, abspath, MAXPGPATH);
     258        19423 :     free(abspath);
     259              : 
     260              : #ifdef WIN32
     261              :     /* On Windows, be sure to convert '\' to '/' */
     262              :     canonicalize_path(path);
     263              : #endif
     264              : 
     265        19423 :     return 0;
     266              : }
     267              : 
     268              : 
     269              : /*
     270              :  * pg_realpath() - realpath(3) with POSIX.1-2008 semantics
     271              :  *
     272              :  * This is equivalent to realpath(fname, NULL), in that it returns a
     273              :  * malloc'd buffer containing the absolute path equivalent to fname.
     274              :  * On error, returns NULL with errno set.
     275              :  *
     276              :  * On Windows, what you get is spelled per platform conventions,
     277              :  * so you probably want to apply canonicalize_path() to the result.
     278              :  *
     279              :  * For now, this is needed only here so mark it static.  If you choose to
     280              :  * move it into its own file, move the _DARWIN_BETTER_REALPATH #define too!
     281              :  */
     282              : static char *
     283        19423 : pg_realpath(const char *fname)
     284              : {
     285              :     char       *path;
     286              : 
     287              : #ifndef WIN32
     288        19423 :     path = realpath(fname, NULL);
     289              : #else                           /* WIN32 */
     290              : 
     291              :     /*
     292              :      * Microsoft is resolutely non-POSIX, but _fullpath() does the same thing.
     293              :      * The documentation claims it reports errors by setting errno, which is a
     294              :      * bit surprising for Microsoft, but we'll believe that until it's proven
     295              :      * wrong.  Clear errno first, though, so we can at least tell if a failure
     296              :      * occurs and doesn't set it.
     297              :      */
     298              :     errno = 0;
     299              :     path = _fullpath(NULL, fname, 0);
     300              : #endif
     301              : 
     302        19423 :     return path;
     303              : }
     304              : 
     305              : 
     306              : /*
     307              :  * Find another program in our binary's directory,
     308              :  * then make sure it is the proper version.
     309              :  */
     310              : int
     311          958 : find_other_exec(const char *argv0, const char *target,
     312              :                 const char *versionstr, char *retpath)
     313              : {
     314              :     char        cmd[MAXPGPATH];
     315              :     char       *line;
     316              : 
     317          958 :     if (find_my_exec(argv0, retpath) < 0)
     318            0 :         return -1;
     319              : 
     320              :     /* Trim off program name and keep just directory */
     321          958 :     *last_dir_separator(retpath) = '\0';
     322          958 :     canonicalize_path(retpath);
     323              : 
     324              :     /* Now append the other program's name */
     325          958 :     snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
     326              :              "/%s%s", target, EXE);
     327              : 
     328          958 :     if (validate_exec(retpath) != 0)
     329            0 :         return -1;
     330              : 
     331          958 :     snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
     332              : 
     333          958 :     if ((line = pipe_read_line(cmd)) == NULL)
     334            0 :         return -1;
     335              : 
     336          958 :     if (strcmp(line, versionstr) != 0)
     337              :     {
     338            0 :         pfree(line);
     339            0 :         return -2;
     340              :     }
     341              : 
     342          958 :     pfree(line);
     343          958 :     return 0;
     344              : }
     345              : 
     346              : 
     347              : /*
     348              :  * Execute a command in a pipe and read the first line from it. The returned
     349              :  * string is palloc'd (malloc'd in frontend code), the caller is responsible
     350              :  * for freeing.
     351              :  */
     352              : char *
     353         1211 : pipe_read_line(char *cmd)
     354              : {
     355              :     FILE       *pipe_cmd;
     356              :     char       *line;
     357              : 
     358         1211 :     fflush(NULL);
     359              : 
     360         1211 :     errno = 0;
     361         1211 :     if ((pipe_cmd = popen(cmd, "r")) == NULL)
     362              :     {
     363            0 :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     364              :                   _("could not execute command \"%s\": %m"), cmd);
     365            0 :         return NULL;
     366              :     }
     367              : 
     368              :     /* Make sure popen() didn't change errno */
     369         1211 :     errno = 0;
     370         1211 :     line = pg_get_line(pipe_cmd, NULL);
     371              : 
     372         1211 :     if (line == NULL)
     373              :     {
     374            0 :         if (ferror(pipe_cmd))
     375            0 :             log_error(errcode_for_file_access(),
     376              :                       _("could not read from command \"%s\": %m"), cmd);
     377              :         else
     378            0 :             log_error(errcode(ERRCODE_NO_DATA),
     379              :                       _("no data was returned by command \"%s\""), cmd);
     380              :     }
     381              : 
     382         1211 :     (void) pclose_check(pipe_cmd);
     383              : 
     384         1211 :     return line;
     385              : }
     386              : 
     387              : 
     388              : /*
     389              :  * pclose() plus useful error reporting
     390              :  */
     391              : int
     392         1314 : pclose_check(FILE *stream)
     393              : {
     394              :     int         exitstatus;
     395              :     char       *reason;
     396              : 
     397         1314 :     exitstatus = pclose(stream);
     398              : 
     399         1314 :     if (exitstatus == 0)
     400         1311 :         return 0;               /* all is well */
     401              : 
     402            3 :     if (exitstatus == -1)
     403              :     {
     404              :         /* pclose() itself failed, and hopefully set errno */
     405            0 :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     406              :                   _("%s() failed: %m"), "pclose");
     407              :     }
     408              :     else
     409              :     {
     410            3 :         reason = wait_result_to_str(exitstatus);
     411            3 :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     412              :                   "%s", reason);
     413            3 :         pfree(reason);
     414              :     }
     415            3 :     return exitstatus;
     416              : }
     417              : 
     418              : /*
     419              :  *  set_pglocale_pgservice
     420              :  *
     421              :  *  Set application-specific locale and service directory
     422              :  *
     423              :  *  This function takes the value of argv[0] rather than a full path.
     424              :  *
     425              :  * (You may be wondering why this is in exec.c.  It requires this module's
     426              :  * services and doesn't introduce any new dependencies, so this seems as
     427              :  * good as anyplace.)
     428              :  */
     429              : void
     430        16695 : set_pglocale_pgservice(const char *argv0, const char *app)
     431              : {
     432              :     char        path[MAXPGPATH];
     433              :     char        my_exec_path[MAXPGPATH];
     434              : 
     435              :     /* don't set LC_ALL in the backend */
     436        16695 :     if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
     437              :     {
     438        14685 :         setlocale(LC_ALL, "");
     439              : 
     440              :         /*
     441              :          * One could make a case for reproducing here PostmasterMain()'s test
     442              :          * for whether the process is multithreaded.  Unlike the postmaster,
     443              :          * no frontend program calls sigprocmask() or otherwise provides for
     444              :          * mutual exclusion between signal handlers.  While frontends using
     445              :          * fork(), if multithreaded, are formally exposed to undefined
     446              :          * behavior, we have not witnessed a concrete bug.  Therefore,
     447              :          * complaining about multithreading here may be mere pedantry.
     448              :          */
     449              :     }
     450              : 
     451        16695 :     if (find_my_exec(argv0, my_exec_path) < 0)
     452            0 :         return;
     453              : 
     454              : #ifdef ENABLE_NLS
     455        16695 :     get_locale_path(my_exec_path, path);
     456        16695 :     bindtextdomain(app, path);
     457        16695 :     textdomain(app);
     458              :     /* set for libpq to use, but don't override existing setting */
     459        16695 :     setenv("PGLOCALEDIR", path, 0);
     460              : #endif
     461              : 
     462        16695 :     if (getenv("PGSYSCONFDIR") == NULL)
     463              :     {
     464        12472 :         get_etc_path(my_exec_path, path);
     465              :         /* set for libpq to use */
     466        12472 :         setenv("PGSYSCONFDIR", path, 0);
     467              :     }
     468              : }
     469              : 
     470              : #ifdef EXEC_BACKEND
     471              : /*
     472              :  * For the benefit of PostgreSQL developers testing EXEC_BACKEND on Unix
     473              :  * systems (code paths normally exercised only on Windows), provide a way to
     474              :  * disable address space layout randomization, if we know how on this platform.
     475              :  * Otherwise, backends may fail to attach to shared memory at the fixed address
     476              :  * chosen by the postmaster.  (See also the macOS-specific hack in
     477              :  * sysv_shmem.c.)
     478              :  */
     479              : int
     480              : pg_disable_aslr(void)
     481              : {
     482              : #if defined(HAVE_SYS_PERSONALITY_H)
     483              :     return personality(ADDR_NO_RANDOMIZE);
     484              : #elif defined(HAVE_SYS_PROCCTL_H) && defined(PROC_ASLR_FORCE_DISABLE)
     485              :     int         data = PROC_ASLR_FORCE_DISABLE;
     486              : 
     487              :     return procctl(P_PID, 0, PROC_ASLR_CTL, &data);
     488              : #else
     489              :     errno = ENOSYS;
     490              :     return -1;
     491              : #endif
     492              : }
     493              : #endif
     494              : 
     495              : #ifdef WIN32
     496              : 
     497              : /*
     498              :  * AddUserToTokenDacl(HANDLE hToken)
     499              :  *
     500              :  * This function adds the current user account to the restricted
     501              :  * token used when we create a restricted process.
     502              :  *
     503              :  * This is required because of some security changes in Windows
     504              :  * that appeared in patches to XP/2K3 and in Vista/2008.
     505              :  *
     506              :  * On these machines, the Administrator account is not included in
     507              :  * the default DACL - you just get Administrators + System. For
     508              :  * regular users you get User + System. Because we strip Administrators
     509              :  * when we create the restricted token, we are left with only System
     510              :  * in the DACL which leads to access denied errors for later CreatePipe()
     511              :  * and CreateProcess() calls when running as Administrator.
     512              :  *
     513              :  * This function fixes this problem by modifying the DACL of the
     514              :  * token the process will use, and explicitly re-adding the current
     515              :  * user account.  This is still secure because the Administrator account
     516              :  * inherits its privileges from the Administrators group - it doesn't
     517              :  * have any of its own.
     518              :  */
     519              : BOOL
     520              : AddUserToTokenDacl(HANDLE hToken)
     521              : {
     522              :     int         i;
     523              :     ACL_SIZE_INFORMATION asi;
     524              :     ACCESS_ALLOWED_ACE *pace;
     525              :     DWORD       dwNewAclSize;
     526              :     DWORD       dwSize = 0;
     527              :     DWORD       dwTokenInfoLength = 0;
     528              :     PACL        pacl = NULL;
     529              :     PTOKEN_USER pTokenUser = NULL;
     530              :     TOKEN_DEFAULT_DACL tddNew;
     531              :     TOKEN_DEFAULT_DACL *ptdd = NULL;
     532              :     TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
     533              :     BOOL        ret = FALSE;
     534              : 
     535              :     /* Figure out the buffer size for the DACL info */
     536              :     if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
     537              :     {
     538              :         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     539              :         {
     540              :             ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
     541              :             if (ptdd == NULL)
     542              :             {
     543              :                 log_error(errcode(ERRCODE_OUT_OF_MEMORY),
     544              :                           _("out of memory"));
     545              :                 goto cleanup;
     546              :             }
     547              : 
     548              :             if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
     549              :             {
     550              :                 log_error(errcode(ERRCODE_SYSTEM_ERROR),
     551              :                           "could not get token information: error code %lu",
     552              :                           GetLastError());
     553              :                 goto cleanup;
     554              :             }
     555              :         }
     556              :         else
     557              :         {
     558              :             log_error(errcode(ERRCODE_SYSTEM_ERROR),
     559              :                       "could not get token information buffer size: error code %lu",
     560              :                       GetLastError());
     561              :             goto cleanup;
     562              :         }
     563              :     }
     564              : 
     565              :     /* Get the ACL info */
     566              :     if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
     567              :                            (DWORD) sizeof(ACL_SIZE_INFORMATION),
     568              :                            AclSizeInformation))
     569              :     {
     570              :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     571              :                   "could not get ACL information: error code %lu",
     572              :                   GetLastError());
     573              :         goto cleanup;
     574              :     }
     575              : 
     576              :     /* Get the current user SID */
     577              :     if (!GetTokenUser(hToken, &pTokenUser))
     578              :         goto cleanup;           /* callee printed a message */
     579              : 
     580              :     /* Figure out the size of the new ACL */
     581              :     dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
     582              :         GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
     583              : 
     584              :     /* Allocate the ACL buffer & initialize it */
     585              :     pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
     586              :     if (pacl == NULL)
     587              :     {
     588              :         log_error(errcode(ERRCODE_OUT_OF_MEMORY),
     589              :                   _("out of memory"));
     590              :         goto cleanup;
     591              :     }
     592              : 
     593              :     if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
     594              :     {
     595              :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     596              :                   "could not initialize ACL: error code %lu", GetLastError());
     597              :         goto cleanup;
     598              :     }
     599              : 
     600              :     /* Loop through the existing ACEs, and build the new ACL */
     601              :     for (i = 0; i < (int) asi.AceCount; i++)
     602              :     {
     603              :         if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
     604              :         {
     605              :             log_error(errcode(ERRCODE_SYSTEM_ERROR),
     606              :                       "could not get ACE: error code %lu", GetLastError());
     607              :             goto cleanup;
     608              :         }
     609              : 
     610              :         if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
     611              :         {
     612              :             log_error(errcode(ERRCODE_SYSTEM_ERROR),
     613              :                       "could not add ACE: error code %lu", GetLastError());
     614              :             goto cleanup;
     615              :         }
     616              :     }
     617              : 
     618              :     /* Add the new ACE for the current user */
     619              :     if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
     620              :     {
     621              :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     622              :                   "could not add access allowed ACE: error code %lu",
     623              :                   GetLastError());
     624              :         goto cleanup;
     625              :     }
     626              : 
     627              :     /* Set the new DACL in the token */
     628              :     tddNew.DefaultDacl = pacl;
     629              : 
     630              :     if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
     631              :     {
     632              :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     633              :                   "could not set token information: error code %lu",
     634              :                   GetLastError());
     635              :         goto cleanup;
     636              :     }
     637              : 
     638              :     ret = TRUE;
     639              : 
     640              : cleanup:
     641              :     if (pTokenUser)
     642              :         LocalFree((HLOCAL) pTokenUser);
     643              : 
     644              :     if (pacl)
     645              :         LocalFree((HLOCAL) pacl);
     646              : 
     647              :     if (ptdd)
     648              :         LocalFree((HLOCAL) ptdd);
     649              : 
     650              :     return ret;
     651              : }
     652              : 
     653              : /*
     654              :  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
     655              :  *
     656              :  * Get the users token information from a process token.
     657              :  *
     658              :  * The caller of this function is responsible for calling LocalFree() on the
     659              :  * returned TOKEN_USER memory.
     660              :  */
     661              : static BOOL
     662              : GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
     663              : {
     664              :     DWORD       dwLength;
     665              : 
     666              :     *ppTokenUser = NULL;
     667              : 
     668              :     if (!GetTokenInformation(hToken,
     669              :                              TokenUser,
     670              :                              NULL,
     671              :                              0,
     672              :                              &dwLength))
     673              :     {
     674              :         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     675              :         {
     676              :             *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
     677              : 
     678              :             if (*ppTokenUser == NULL)
     679              :             {
     680              :                 log_error(errcode(ERRCODE_OUT_OF_MEMORY),
     681              :                           _("out of memory"));
     682              :                 return FALSE;
     683              :             }
     684              :         }
     685              :         else
     686              :         {
     687              :             log_error(errcode(ERRCODE_SYSTEM_ERROR),
     688              :                       "could not get token information buffer size: error code %lu",
     689              :                       GetLastError());
     690              :             return FALSE;
     691              :         }
     692              :     }
     693              : 
     694              :     if (!GetTokenInformation(hToken,
     695              :                              TokenUser,
     696              :                              *ppTokenUser,
     697              :                              dwLength,
     698              :                              &dwLength))
     699              :     {
     700              :         LocalFree(*ppTokenUser);
     701              :         *ppTokenUser = NULL;
     702              : 
     703              :         log_error(errcode(ERRCODE_SYSTEM_ERROR),
     704              :                   "could not get token information: error code %lu",
     705              :                   GetLastError());
     706              :         return FALSE;
     707              :     }
     708              : 
     709              :     /* Memory in *ppTokenUser is LocalFree():d by the caller */
     710              :     return TRUE;
     711              : }
     712              : 
     713              : #endif
        

Generated by: LCOV version 2.0-1