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

Generated by: LCOV version 1.13