LCOV - code coverage report
Current view: top level - src/common - exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 78 129 60.5 %
Date: 2017-09-29 13:40:31 Functions: 7 7 100.0 %
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-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/common/exec.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : 
      17             : #ifndef FRONTEND
      18             : #include "postgres.h"
      19             : #else
      20             : #include "postgres_fe.h"
      21             : #endif
      22             : 
      23             : #include <signal.h>
      24             : #include <sys/stat.h>
      25             : #include <sys/wait.h>
      26             : #include <unistd.h>
      27             : 
      28             : #ifndef FRONTEND
      29             : /* We use only 3- and 4-parameter elog calls in this file, for simplicity */
      30             : /* NOTE: caller must provide gettext call around str! */
      31             : #define log_error(str, param)   elog(LOG, str, param)
      32             : #define log_error4(str, param, arg1)    elog(LOG, str, param, arg1)
      33             : #else
      34             : #define log_error(str, param)   (fprintf(stderr, str, param), fputc('\n', stderr))
      35             : #define log_error4(str, param, arg1)    (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
      36             : #endif
      37             : 
      38             : #ifdef _MSC_VER
      39             : #define getcwd(cwd,len)  GetCurrentDirectory(len, cwd)
      40             : #endif
      41             : 
      42             : static int  validate_exec(const char *path);
      43             : static int  resolve_symlinks(char *path);
      44             : static char *pipe_read_line(char *cmd, char *line, int maxsize);
      45             : 
      46             : #ifdef WIN32
      47             : static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
      48             : #endif
      49             : 
      50             : /*
      51             :  * validate_exec -- validate "path" as an executable file
      52             :  *
      53             :  * returns 0 if the file is found and no error is encountered.
      54             :  *        -1 if the regular file "path" does not exist or cannot be executed.
      55             :  *        -2 if the file is otherwise valid but cannot be read.
      56             :  */
      57             : static int
      58         200 : validate_exec(const char *path)
      59             : {
      60             :     struct stat buf;
      61             :     int         is_r;
      62             :     int         is_x;
      63             : 
      64             : #ifdef WIN32
      65             :     char        path_exe[MAXPGPATH + sizeof(".exe") - 1];
      66             : 
      67             :     /* Win32 requires a .exe suffix for stat() */
      68             :     if (strlen(path) >= strlen(".exe") &&
      69             :         pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
      70             :     {
      71             :         strlcpy(path_exe, path, sizeof(path_exe) - 4);
      72             :         strcat(path_exe, ".exe");
      73             :         path = path_exe;
      74             :     }
      75             : #endif
      76             : 
      77             :     /*
      78             :      * Ensure that the file exists and is a regular file.
      79             :      *
      80             :      * XXX if you have a broken system where stat() looks at the symlink
      81             :      * instead of the underlying file, you lose.
      82             :      */
      83         200 :     if (stat(path, &buf) < 0)
      84           0 :         return -1;
      85             : 
      86         200 :     if (!S_ISREG(buf.st_mode))
      87           0 :         return -1;
      88             : 
      89             :     /*
      90             :      * Ensure that the file is both executable and readable (required for
      91             :      * dynamic loading).
      92             :      */
      93             : #ifndef WIN32
      94         200 :     is_r = (access(path, R_OK) == 0);
      95         200 :     is_x = (access(path, X_OK) == 0);
      96             : #else
      97             :     is_r = buf.st_mode & S_IRUSR;
      98             :     is_x = buf.st_mode & S_IXUSR;
      99             : #endif
     100         200 :     return is_x ? (is_r ? 0 : -2) : -1;
     101             : }
     102             : 
     103             : 
     104             : /*
     105             :  * find_my_exec -- find an absolute path to a valid executable
     106             :  *
     107             :  *  argv0 is the name passed on the command line
     108             :  *  retpath is the output area (must be of size MAXPGPATH)
     109             :  *  Returns 0 if OK, -1 if error.
     110             :  *
     111             :  * The reason we have to work so hard to find an absolute path is that
     112             :  * on some platforms we can't do dynamic loading unless we know the
     113             :  * executable's location.  Also, we need a full path not a relative
     114             :  * path because we will later change working directory.  Finally, we want
     115             :  * a true path not a symlink location, so that we can locate other files
     116             :  * that are part of our installation relative to the executable.
     117             :  */
     118             : int
     119         199 : find_my_exec(const char *argv0, char *retpath)
     120             : {
     121             :     char        cwd[MAXPGPATH],
     122             :                 test_path[MAXPGPATH];
     123             :     char       *path;
     124             : 
     125         199 :     if (!getcwd(cwd, MAXPGPATH))
     126             :     {
     127           0 :         log_error(_("could not identify current directory: %s"),
     128             :                   strerror(errno));
     129           0 :         return -1;
     130             :     }
     131             : 
     132             :     /*
     133             :      * If argv0 contains a separator, then PATH wasn't used.
     134             :      */
     135         199 :     if (first_dir_separator(argv0) != NULL)
     136             :     {
     137          10 :         if (is_absolute_path(argv0))
     138           9 :             StrNCpy(retpath, argv0, MAXPGPATH);
     139             :         else
     140           1 :             join_path_components(retpath, cwd, argv0);
     141          10 :         canonicalize_path(retpath);
     142             : 
     143          10 :         if (validate_exec(retpath) == 0)
     144          10 :             return resolve_symlinks(retpath);
     145             : 
     146           0 :         log_error(_("invalid binary \"%s\""), retpath);
     147           0 :         return -1;
     148             :     }
     149             : 
     150             : #ifdef WIN32
     151             :     /* Win32 checks the current directory first for names without slashes */
     152             :     join_path_components(retpath, cwd, argv0);
     153             :     if (validate_exec(retpath) == 0)
     154             :         return resolve_symlinks(retpath);
     155             : #endif
     156             : 
     157             :     /*
     158             :      * Since no explicit path was supplied, the user must have been relying on
     159             :      * PATH.  We'll search the same PATH.
     160             :      */
     161         189 :     if ((path = getenv("PATH")) && *path)
     162             :     {
     163         189 :         char       *startp = NULL,
     164         189 :                    *endp = NULL;
     165             : 
     166             :         do
     167             :         {
     168         189 :             if (!startp)
     169         189 :                 startp = path;
     170             :             else
     171           0 :                 startp = endp + 1;
     172             : 
     173         189 :             endp = first_path_var_separator(startp);
     174         189 :             if (!endp)
     175           0 :                 endp = startp + strlen(startp); /* point to end */
     176             : 
     177         189 :             StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
     178             : 
     179         189 :             if (is_absolute_path(test_path))
     180         189 :                 join_path_components(retpath, test_path, argv0);
     181             :             else
     182             :             {
     183           0 :                 join_path_components(retpath, cwd, test_path);
     184           0 :                 join_path_components(retpath, retpath, argv0);
     185             :             }
     186         189 :             canonicalize_path(retpath);
     187             : 
     188         189 :             switch (validate_exec(retpath))
     189             :             {
     190             :                 case 0:         /* found ok */
     191         189 :                     return resolve_symlinks(retpath);
     192             :                 case -1:        /* wasn't even a candidate, keep looking */
     193           0 :                     break;
     194             :                 case -2:        /* found but disqualified */
     195           0 :                     log_error(_("could not read binary \"%s\""),
     196             :                               retpath);
     197           0 :                     break;
     198             :             }
     199           0 :         } while (*endp);
     200             :     }
     201             : 
     202           0 :     log_error(_("could not find a \"%s\" to execute"), argv0);
     203           0 :     return -1;
     204             : }
     205             : 
     206             : 
     207             : /*
     208             :  * resolve_symlinks - resolve symlinks to the underlying file
     209             :  *
     210             :  * Replace "path" by the absolute path to the referenced file.
     211             :  *
     212             :  * Returns 0 if OK, -1 if error.
     213             :  *
     214             :  * Note: we are not particularly tense about producing nice error messages
     215             :  * because we are not really expecting error here; we just determined that
     216             :  * the symlink does point to a valid executable.
     217             :  */
     218             : static int
     219         199 : resolve_symlinks(char *path)
     220             : {
     221             : #ifdef HAVE_READLINK
     222             :     struct stat buf;
     223             :     char        orig_wd[MAXPGPATH],
     224             :                 link_buf[MAXPGPATH];
     225             :     char       *fname;
     226             : 
     227             :     /*
     228             :      * To resolve a symlink properly, we have to chdir into its directory and
     229             :      * then chdir to where the symlink points; otherwise we may fail to
     230             :      * resolve relative links correctly (consider cases involving mount
     231             :      * points, for example).  After following the final symlink, we use
     232             :      * getcwd() to figure out where the heck we're at.
     233             :      *
     234             :      * One might think we could skip all this if path doesn't point to a
     235             :      * symlink to start with, but that's wrong.  We also want to get rid of
     236             :      * any directory symlinks that are present in the given path. We expect
     237             :      * getcwd() to give us an accurate, symlink-free path.
     238             :      */
     239         199 :     if (!getcwd(orig_wd, MAXPGPATH))
     240             :     {
     241           0 :         log_error(_("could not identify current directory: %s"),
     242             :                   strerror(errno));
     243           0 :         return -1;
     244             :     }
     245             : 
     246             :     for (;;)
     247             :     {
     248             :         char       *lsep;
     249             :         int         rllen;
     250             : 
     251         199 :         lsep = last_dir_separator(path);
     252         199 :         if (lsep)
     253             :         {
     254         199 :             *lsep = '\0';
     255         199 :             if (chdir(path) == -1)
     256             :             {
     257           0 :                 log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
     258           0 :                 return -1;
     259             :             }
     260         199 :             fname = lsep + 1;
     261             :         }
     262             :         else
     263           0 :             fname = path;
     264             : 
     265         398 :         if (lstat(fname, &buf) < 0 ||
     266         199 :             !S_ISLNK(buf.st_mode))
     267             :             break;
     268             : 
     269           0 :         rllen = readlink(fname, link_buf, sizeof(link_buf));
     270           0 :         if (rllen < 0 || rllen >= sizeof(link_buf))
     271             :         {
     272           0 :             log_error(_("could not read symbolic link \"%s\""), fname);
     273           0 :             return -1;
     274             :         }
     275           0 :         link_buf[rllen] = '\0';
     276           0 :         strcpy(path, link_buf);
     277           0 :     }
     278             : 
     279             :     /* must copy final component out of 'path' temporarily */
     280         199 :     strlcpy(link_buf, fname, sizeof(link_buf));
     281             : 
     282         199 :     if (!getcwd(path, MAXPGPATH))
     283             :     {
     284           0 :         log_error(_("could not identify current directory: %s"),
     285             :                   strerror(errno));
     286           0 :         return -1;
     287             :     }
     288         199 :     join_path_components(path, path, link_buf);
     289         199 :     canonicalize_path(path);
     290             : 
     291         199 :     if (chdir(orig_wd) == -1)
     292             :     {
     293           0 :         log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
     294           0 :         return -1;
     295             :     }
     296             : #endif                          /* HAVE_READLINK */
     297             : 
     298         199 :     return 0;
     299             : }
     300             : 
     301             : 
     302             : /*
     303             :  * Find another program in our binary's directory,
     304             :  * then make sure it is the proper version.
     305             :  */
     306             : int
     307           1 : find_other_exec(const char *argv0, const char *target,
     308             :                 const char *versionstr, char *retpath)
     309             : {
     310             :     char        cmd[MAXPGPATH];
     311             :     char        line[100];
     312             : 
     313           1 :     if (find_my_exec(argv0, retpath) < 0)
     314           0 :         return -1;
     315             : 
     316             :     /* Trim off program name and keep just directory */
     317           1 :     *last_dir_separator(retpath) = '\0';
     318           1 :     canonicalize_path(retpath);
     319             : 
     320             :     /* Now append the other program's name */
     321           1 :     snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
     322             :              "/%s%s", target, EXE);
     323             : 
     324           1 :     if (validate_exec(retpath) != 0)
     325           0 :         return -1;
     326             : 
     327           1 :     snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
     328             : 
     329           1 :     if (!pipe_read_line(cmd, line, sizeof(line)))
     330           0 :         return -1;
     331             : 
     332           1 :     if (strcmp(line, versionstr) != 0)
     333           0 :         return -2;
     334             : 
     335           1 :     return 0;
     336             : }
     337             : 
     338             : 
     339             : /*
     340             :  * The runtime library's popen() on win32 does not work when being
     341             :  * called from a service when running on windows <= 2000, because
     342             :  * there is no stdin/stdout/stderr.
     343             :  *
     344             :  * Executing a command in a pipe and reading the first line from it
     345             :  * is all we need.
     346             :  */
     347             : static char *
     348           1 : pipe_read_line(char *cmd, char *line, int maxsize)
     349             : {
     350             : #ifndef WIN32
     351             :     FILE       *pgver;
     352             : 
     353             :     /* flush output buffers in case popen does not... */
     354           1 :     fflush(stdout);
     355           1 :     fflush(stderr);
     356             : 
     357           1 :     errno = 0;
     358           1 :     if ((pgver = popen(cmd, "r")) == NULL)
     359             :     {
     360           0 :         perror("popen failure");
     361           0 :         return NULL;
     362             :     }
     363             : 
     364           1 :     errno = 0;
     365           1 :     if (fgets(line, maxsize, pgver) == NULL)
     366             :     {
     367           0 :         if (feof(pgver))
     368           0 :             fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
     369             :         else
     370           0 :             perror("fgets failure");
     371           0 :         pclose(pgver);          /* no error checking */
     372           0 :         return NULL;
     373             :     }
     374             : 
     375           1 :     if (pclose_check(pgver))
     376           0 :         return NULL;
     377             : 
     378           1 :     return line;
     379             : #else                           /* WIN32 */
     380             : 
     381             :     SECURITY_ATTRIBUTES sattr;
     382             :     HANDLE      childstdoutrd,
     383             :                 childstdoutwr,
     384             :                 childstdoutrddup;
     385             :     PROCESS_INFORMATION pi;
     386             :     STARTUPINFO si;
     387             :     char       *retval = NULL;
     388             : 
     389             :     sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
     390             :     sattr.bInheritHandle = TRUE;
     391             :     sattr.lpSecurityDescriptor = NULL;
     392             : 
     393             :     if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
     394             :         return NULL;
     395             : 
     396             :     if (!DuplicateHandle(GetCurrentProcess(),
     397             :                          childstdoutrd,
     398             :                          GetCurrentProcess(),
     399             :                          &childstdoutrddup,
     400             :                          0,
     401             :                          FALSE,
     402             :                          DUPLICATE_SAME_ACCESS))
     403             :     {
     404             :         CloseHandle(childstdoutrd);
     405             :         CloseHandle(childstdoutwr);
     406             :         return NULL;
     407             :     }
     408             : 
     409             :     CloseHandle(childstdoutrd);
     410             : 
     411             :     ZeroMemory(&pi, sizeof(pi));
     412             :     ZeroMemory(&si, sizeof(si));
     413             :     si.cb = sizeof(si);
     414             :     si.dwFlags = STARTF_USESTDHANDLES;
     415             :     si.hStdError = childstdoutwr;
     416             :     si.hStdOutput = childstdoutwr;
     417             :     si.hStdInput = INVALID_HANDLE_VALUE;
     418             : 
     419             :     if (CreateProcess(NULL,
     420             :                       cmd,
     421             :                       NULL,
     422             :                       NULL,
     423             :                       TRUE,
     424             :                       0,
     425             :                       NULL,
     426             :                       NULL,
     427             :                       &si,
     428             :                       &pi))
     429             :     {
     430             :         /* Successfully started the process */
     431             :         char       *lineptr;
     432             : 
     433             :         ZeroMemory(line, maxsize);
     434             : 
     435             :         /* Try to read at least one line from the pipe */
     436             :         /* This may require more than one wait/read attempt */
     437             :         for (lineptr = line; lineptr < line + maxsize - 1;)
     438             :         {
     439             :             DWORD       bytesread = 0;
     440             : 
     441             :             /* Let's see if we can read */
     442             :             if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
     443             :                 break;          /* Timeout, but perhaps we got a line already */
     444             : 
     445             :             if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
     446             :                           &bytesread, NULL))
     447             :                 break;          /* Error, but perhaps we got a line already */
     448             : 
     449             :             lineptr += strlen(lineptr);
     450             : 
     451             :             if (!bytesread)
     452             :                 break;          /* EOF */
     453             : 
     454             :             if (strchr(line, '\n'))
     455             :                 break;          /* One or more lines read */
     456             :         }
     457             : 
     458             :         if (lineptr != line)
     459             :         {
     460             :             /* OK, we read some data */
     461             :             int         len;
     462             : 
     463             :             /* If we got more than one line, cut off after the first \n */
     464             :             lineptr = strchr(line, '\n');
     465             :             if (lineptr)
     466             :                 *(lineptr + 1) = '\0';
     467             : 
     468             :             len = strlen(line);
     469             : 
     470             :             /*
     471             :              * If EOL is \r\n, convert to just \n. Because stdout is a
     472             :              * text-mode stream, the \n output by the child process is
     473             :              * received as \r\n, so we convert it to \n.  The server main.c
     474             :              * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
     475             :              * disabling \n to \r\n expansion for stdout.
     476             :              */
     477             :             if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
     478             :             {
     479             :                 line[len - 2] = '\n';
     480             :                 line[len - 1] = '\0';
     481             :                 len--;
     482             :             }
     483             : 
     484             :             /*
     485             :              * We emulate fgets() behaviour. So if there is no newline at the
     486             :              * end, we add one...
     487             :              */
     488             :             if (len == 0 || line[len - 1] != '\n')
     489             :                 strcat(line, "\n");
     490             : 
     491             :             retval = line;
     492             :         }
     493             : 
     494             :         CloseHandle(pi.hProcess);
     495             :         CloseHandle(pi.hThread);
     496             :     }
     497             : 
     498             :     CloseHandle(childstdoutwr);
     499             :     CloseHandle(childstdoutrddup);
     500             : 
     501             :     return retval;
     502             : #endif                          /* WIN32 */
     503             : }
     504             : 
     505             : 
     506             : /*
     507             :  * pclose() plus useful error reporting
     508             :  */
     509             : int
     510           3 : pclose_check(FILE *stream)
     511             : {
     512             :     int         exitstatus;
     513             :     char       *reason;
     514             : 
     515           3 :     exitstatus = pclose(stream);
     516             : 
     517           3 :     if (exitstatus == 0)
     518           3 :         return 0;               /* all is well */
     519             : 
     520           0 :     if (exitstatus == -1)
     521             :     {
     522             :         /* pclose() itself failed, and hopefully set errno */
     523           0 :         log_error(_("pclose failed: %s"), strerror(errno));
     524             :     }
     525             :     else
     526             :     {
     527           0 :         reason = wait_result_to_str(exitstatus);
     528           0 :         log_error("%s", reason);
     529             : #ifdef FRONTEND
     530           0 :         free(reason);
     531             : #else
     532           0 :         pfree(reason);
     533             : #endif
     534             :     }
     535           0 :     return exitstatus;
     536             : }
     537             : 
     538             : /*
     539             :  *  set_pglocale_pgservice
     540             :  *
     541             :  *  Set application-specific locale and service directory
     542             :  *
     543             :  *  This function takes the value of argv[0] rather than a full path.
     544             :  *
     545             :  * (You may be wondering why this is in exec.c.  It requires this module's
     546             :  * services and doesn't introduce any new dependencies, so this seems as
     547             :  * good as anyplace.)
     548             :  */
     549             : void
     550         193 : set_pglocale_pgservice(const char *argv0, const char *app)
     551             : {
     552             :     char        path[MAXPGPATH];
     553             :     char        my_exec_path[MAXPGPATH];
     554             :     char        env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];    /* longer than
     555             :                                                                  * PGLOCALEDIR */
     556             :     char       *dup_path;
     557             : 
     558             :     /* don't set LC_ALL in the backend */
     559         193 :     if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
     560             :     {
     561         187 :         setlocale(LC_ALL, "");
     562             : 
     563             :         /*
     564             :          * One could make a case for reproducing here PostmasterMain()'s test
     565             :          * for whether the process is multithreaded.  Unlike the postmaster,
     566             :          * no frontend program calls sigprocmask() or otherwise provides for
     567             :          * mutual exclusion between signal handlers.  While frontends using
     568             :          * fork(), if multithreaded, are formally exposed to undefined
     569             :          * behavior, we have not witnessed a concrete bug.  Therefore,
     570             :          * complaining about multithreading here may be mere pedantry.
     571             :          */
     572             :     }
     573             : 
     574         193 :     if (find_my_exec(argv0, my_exec_path) < 0)
     575         193 :         return;
     576             : 
     577             : #ifdef ENABLE_NLS
     578             :     get_locale_path(my_exec_path, path);
     579             :     bindtextdomain(app, path);
     580             :     textdomain(app);
     581             : 
     582             :     if (getenv("PGLOCALEDIR") == NULL)
     583             :     {
     584             :         /* set for libpq to use */
     585             :         snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
     586             :         canonicalize_path(env_path + 12);
     587             :         dup_path = strdup(env_path);
     588             :         if (dup_path)
     589             :             putenv(dup_path);
     590             :     }
     591             : #endif
     592             : 
     593         193 :     if (getenv("PGSYSCONFDIR") == NULL)
     594             :     {
     595           1 :         get_etc_path(my_exec_path, path);
     596             : 
     597             :         /* set for libpq to use */
     598           1 :         snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
     599           1 :         canonicalize_path(env_path + 13);
     600           1 :         dup_path = strdup(env_path);
     601           1 :         if (dup_path)
     602           1 :             putenv(dup_path);
     603             :     }
     604             : }
     605             : 
     606             : #ifdef WIN32
     607             : 
     608             : /*
     609             :  * AddUserToTokenDacl(HANDLE hToken)
     610             :  *
     611             :  * This function adds the current user account to the restricted
     612             :  * token used when we create a restricted process.
     613             :  *
     614             :  * This is required because of some security changes in Windows
     615             :  * that appeared in patches to XP/2K3 and in Vista/2008.
     616             :  *
     617             :  * On these machines, the Administrator account is not included in
     618             :  * the default DACL - you just get Administrators + System. For
     619             :  * regular users you get User + System. Because we strip Administrators
     620             :  * when we create the restricted token, we are left with only System
     621             :  * in the DACL which leads to access denied errors for later CreatePipe()
     622             :  * and CreateProcess() calls when running as Administrator.
     623             :  *
     624             :  * This function fixes this problem by modifying the DACL of the
     625             :  * token the process will use, and explicitly re-adding the current
     626             :  * user account.  This is still secure because the Administrator account
     627             :  * inherits its privileges from the Administrators group - it doesn't
     628             :  * have any of its own.
     629             :  */
     630             : BOOL
     631             : AddUserToTokenDacl(HANDLE hToken)
     632             : {
     633             :     int         i;
     634             :     ACL_SIZE_INFORMATION asi;
     635             :     ACCESS_ALLOWED_ACE *pace;
     636             :     DWORD       dwNewAclSize;
     637             :     DWORD       dwSize = 0;
     638             :     DWORD       dwTokenInfoLength = 0;
     639             :     PACL        pacl = NULL;
     640             :     PTOKEN_USER pTokenUser = NULL;
     641             :     TOKEN_DEFAULT_DACL tddNew;
     642             :     TOKEN_DEFAULT_DACL *ptdd = NULL;
     643             :     TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
     644             :     BOOL        ret = FALSE;
     645             : 
     646             :     /* Figure out the buffer size for the DACL info */
     647             :     if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
     648             :     {
     649             :         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     650             :         {
     651             :             ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
     652             :             if (ptdd == NULL)
     653             :             {
     654             :                 log_error("could not allocate %lu bytes of memory", dwSize);
     655             :                 goto cleanup;
     656             :             }
     657             : 
     658             :             if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
     659             :             {
     660             :                 log_error("could not get token information: error code %lu", GetLastError());
     661             :                 goto cleanup;
     662             :             }
     663             :         }
     664             :         else
     665             :         {
     666             :             log_error("could not get token information buffer size: error code %lu", GetLastError());
     667             :             goto cleanup;
     668             :         }
     669             :     }
     670             : 
     671             :     /* Get the ACL info */
     672             :     if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
     673             :                            (DWORD) sizeof(ACL_SIZE_INFORMATION),
     674             :                            AclSizeInformation))
     675             :     {
     676             :         log_error("could not get ACL information: error code %lu", GetLastError());
     677             :         goto cleanup;
     678             :     }
     679             : 
     680             :     /* Get the current user SID */
     681             :     if (!GetTokenUser(hToken, &pTokenUser))
     682             :         goto cleanup;           /* callee printed a message */
     683             : 
     684             :     /* Figure out the size of the new ACL */
     685             :     dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
     686             :         GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
     687             : 
     688             :     /* Allocate the ACL buffer & initialize it */
     689             :     pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
     690             :     if (pacl == NULL)
     691             :     {
     692             :         log_error("could not allocate %lu bytes of memory", dwNewAclSize);
     693             :         goto cleanup;
     694             :     }
     695             : 
     696             :     if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
     697             :     {
     698             :         log_error("could not initialize ACL: error code %lu", GetLastError());
     699             :         goto cleanup;
     700             :     }
     701             : 
     702             :     /* Loop through the existing ACEs, and build the new ACL */
     703             :     for (i = 0; i < (int) asi.AceCount; i++)
     704             :     {
     705             :         if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
     706             :         {
     707             :             log_error("could not get ACE: error code %lu", GetLastError());
     708             :             goto cleanup;
     709             :         }
     710             : 
     711             :         if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
     712             :         {
     713             :             log_error("could not add ACE: error code %lu", GetLastError());
     714             :             goto cleanup;
     715             :         }
     716             :     }
     717             : 
     718             :     /* Add the new ACE for the current user */
     719             :     if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
     720             :     {
     721             :         log_error("could not add access allowed ACE: error code %lu", GetLastError());
     722             :         goto cleanup;
     723             :     }
     724             : 
     725             :     /* Set the new DACL in the token */
     726             :     tddNew.DefaultDacl = pacl;
     727             : 
     728             :     if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
     729             :     {
     730             :         log_error("could not set token information: error code %lu", GetLastError());
     731             :         goto cleanup;
     732             :     }
     733             : 
     734             :     ret = TRUE;
     735             : 
     736             : cleanup:
     737             :     if (pTokenUser)
     738             :         LocalFree((HLOCAL) pTokenUser);
     739             : 
     740             :     if (pacl)
     741             :         LocalFree((HLOCAL) pacl);
     742             : 
     743             :     if (ptdd)
     744             :         LocalFree((HLOCAL) ptdd);
     745             : 
     746             :     return ret;
     747             : }
     748             : 
     749             : /*
     750             :  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
     751             :  *
     752             :  * Get the users token information from a process token.
     753             :  *
     754             :  * The caller of this function is responsible for calling LocalFree() on the
     755             :  * returned TOKEN_USER memory.
     756             :  */
     757             : static BOOL
     758             : GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
     759             : {
     760             :     DWORD       dwLength;
     761             : 
     762             :     *ppTokenUser = NULL;
     763             : 
     764             :     if (!GetTokenInformation(hToken,
     765             :                              TokenUser,
     766             :                              NULL,
     767             :                              0,
     768             :                              &dwLength))
     769             :     {
     770             :         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     771             :         {
     772             :             *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
     773             : 
     774             :             if (*ppTokenUser == NULL)
     775             :             {
     776             :                 log_error("could not allocate %lu bytes of memory", dwLength);
     777             :                 return FALSE;
     778             :             }
     779             :         }
     780             :         else
     781             :         {
     782             :             log_error("could not get token information buffer size: error code %lu", GetLastError());
     783             :             return FALSE;
     784             :         }
     785             :     }
     786             : 
     787             :     if (!GetTokenInformation(hToken,
     788             :                              TokenUser,
     789             :                              *ppTokenUser,
     790             :                              dwLength,
     791             :                              &dwLength))
     792             :     {
     793             :         LocalFree(*ppTokenUser);
     794             :         *ppTokenUser = NULL;
     795             : 
     796             :         log_error("could not get token information: error code %lu", GetLastError());
     797             :         return FALSE;
     798             :     }
     799             : 
     800             :     /* Memory in *ppTokenUser is LocalFree():d by the caller */
     801             :     return TRUE;
     802             : }
     803             : 
     804             : #endif

Generated by: LCOV version 1.11