LCOV - differential code coverage report
Current view: top level - src/common - exec.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 64.9 % 111 72 9 6 18 6 5 40 16 11 17 36 11 19
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 6 2 5 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a