1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
2004-05-11 23:57:15 +02:00
|
|
|
* exec.c
|
2004-11-07 00:06:29 +01:00
|
|
|
* Functions for finding and validating executable files
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2018-01-03 05:30:12 +01:00
|
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2013-10-18 03:52:54 +02:00
|
|
|
* src/common/exec.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2004-05-11 23:57:15 +02:00
|
|
|
|
|
|
|
#ifndef FRONTEND
|
2001-10-21 05:43:54 +02:00
|
|
|
#include "postgres.h"
|
2004-05-11 23:57:15 +02:00
|
|
|
#else
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
#endif
|
2001-10-21 05:43:54 +02:00
|
|
|
|
2007-01-28 03:33:09 +01:00
|
|
|
#include <signal.h>
|
1997-08-27 05:48:50 +02:00
|
|
|
#include <sys/stat.h>
|
2004-05-21 18:06:23 +02:00
|
|
|
#include <sys/wait.h>
|
1996-11-06 11:32:10 +01:00
|
|
|
#include <unistd.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2004-05-19 19:15:21 +02:00
|
|
|
#ifndef FRONTEND
|
2012-08-29 05:01:23 +02:00
|
|
|
/* We use only 3- and 4-parameter elog calls in this file, for simplicity */
|
2005-01-14 18:47:49 +01:00
|
|
|
/* NOTE: caller must provide gettext call around str! */
|
2004-11-06 02:16:22 +01:00
|
|
|
#define log_error(str, param) elog(LOG, str, param)
|
2012-08-29 05:01:23 +02:00
|
|
|
#define log_error4(str, param, arg1) elog(LOG, str, param, arg1)
|
2004-05-19 19:15:21 +02:00
|
|
|
#else
|
2004-11-06 02:16:22 +01:00
|
|
|
#define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr))
|
2012-08-29 05:01:23 +02:00
|
|
|
#define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
|
2004-05-19 19:15:21 +02:00
|
|
|
#endif
|
|
|
|
|
2017-04-11 15:21:25 +02:00
|
|
|
#ifdef _MSC_VER
|
2004-11-07 00:06:29 +01:00
|
|
|
#define getcwd(cwd,len) GetCurrentDirectory(len, cwd)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int validate_exec(const char *path);
|
|
|
|
static int resolve_symlinks(char *path);
|
|
|
|
static char *pipe_read_line(char *cmd, char *line, int maxsize);
|
|
|
|
|
2008-02-29 16:31:33 +01:00
|
|
|
#ifdef WIN32
|
2009-07-27 10:46:10 +02:00
|
|
|
static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
|
2008-02-29 16:31:33 +01:00
|
|
|
#endif
|
2004-05-21 18:06:23 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2004-05-11 23:57:15 +02:00
|
|
|
* validate_exec -- validate "path" as an executable file
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* returns 0 if the file is found and no error is encountered.
|
1997-09-07 07:04:48 +02:00
|
|
|
* -1 if the regular file "path" does not exist or cannot be executed.
|
|
|
|
* -2 if the file is otherwise valid but cannot be read.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-06-09 00:38:00 +02:00
|
|
|
static int
|
2004-05-24 22:23:50 +02:00
|
|
|
validate_exec(const char *path)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
struct stat buf;
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
int is_r;
|
|
|
|
int is_x;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-11-11 04:53:33 +01:00
|
|
|
#ifdef WIN32
|
2010-01-14 01:14:06 +01:00
|
|
|
char path_exe[MAXPGPATH + sizeof(".exe") - 1];
|
|
|
|
|
2003-11-11 04:53:33 +01:00
|
|
|
/* Win32 requires a .exe suffix for stat() */
|
2004-05-11 23:57:15 +02:00
|
|
|
if (strlen(path) >= strlen(".exe") &&
|
|
|
|
pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
|
2003-11-11 04:53:33 +01:00
|
|
|
{
|
2014-02-17 17:20:21 +01:00
|
|
|
strlcpy(path_exe, path, sizeof(path_exe) - 4);
|
2003-11-11 04:53:33 +01:00
|
|
|
strcat(path_exe, ".exe");
|
|
|
|
path = path_exe;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* Ensure that the file exists and is a regular file.
|
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* XXX if you have a broken system where stat() looks at the symlink
|
|
|
|
* instead of the underlying file, you lose.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (stat(path, &buf) < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
2001-05-09 21:28:31 +02:00
|
|
|
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
if (!S_ISREG(buf.st_mode))
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that the file is both executable and readable (required for
|
|
|
|
* dynamic loading).
|
|
|
|
*/
|
2010-01-14 01:14:06 +01:00
|
|
|
#ifndef WIN32
|
|
|
|
is_r = (access(path, R_OK) == 0);
|
|
|
|
is_x = (access(path, X_OK) == 0);
|
|
|
|
#else
|
2003-08-04 02:43:34 +02:00
|
|
|
is_r = buf.st_mode & S_IRUSR;
|
|
|
|
is_x = buf.st_mode & S_IXUSR;
|
2003-05-15 18:35:30 +02:00
|
|
|
#endif
|
2010-01-14 01:14:06 +01:00
|
|
|
return is_x ? (is_r ? 0 : -2) : -1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2004-11-07 00:06:29 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2004-05-12 15:38:49 +02:00
|
|
|
* find_my_exec -- find an absolute path to a valid executable
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-11-07 00:06:29 +01:00
|
|
|
* argv0 is the name passed on the command line
|
|
|
|
* retpath is the output area (must be of size MAXPGPATH)
|
|
|
|
* Returns 0 if OK, -1 if error.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* The reason we have to work so hard to find an absolute path is that
|
2001-10-21 05:43:54 +02:00
|
|
|
* on some platforms we can't do dynamic loading unless we know the
|
|
|
|
* executable's location. Also, we need a full path not a relative
|
2004-11-07 00:06:29 +01:00
|
|
|
* path because we will later change working directory. Finally, we want
|
|
|
|
* a true path not a symlink location, so that we can locate other files
|
|
|
|
* that are part of our installation relative to the executable.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
2004-05-20 17:38:11 +02:00
|
|
|
find_my_exec(const char *argv0, char *retpath)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
char cwd[MAXPGPATH],
|
|
|
|
test_path[MAXPGPATH];
|
|
|
|
char *path;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-20 17:35:41 +02:00
|
|
|
if (!getcwd(cwd, MAXPGPATH))
|
2004-11-07 00:06:29 +01:00
|
|
|
{
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not identify current directory: %s"),
|
2004-11-07 00:06:29 +01:00
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
2004-05-20 17:35:41 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2004-11-06 02:16:22 +01:00
|
|
|
* If argv0 contains a separator, then PATH wasn't used.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2004-11-06 02:16:22 +01:00
|
|
|
if (first_dir_separator(argv0) != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-20 17:35:41 +02:00
|
|
|
if (is_absolute_path(argv0))
|
2004-05-20 17:38:11 +02:00
|
|
|
StrNCpy(retpath, argv0, MAXPGPATH);
|
2004-05-20 17:35:41 +02:00
|
|
|
else
|
2004-11-06 02:16:22 +01:00
|
|
|
join_path_components(retpath, cwd, argv0);
|
2004-05-20 17:38:11 +02:00
|
|
|
canonicalize_path(retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
|
2004-05-20 17:38:11 +02:00
|
|
|
if (validate_exec(retpath) == 0)
|
2004-11-07 00:06:29 +01:00
|
|
|
return resolve_symlinks(retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("invalid binary \"%s\""), retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
return -1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-20 17:35:41 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
/* Win32 checks the current directory first for names without slashes */
|
2004-11-06 02:16:22 +01:00
|
|
|
join_path_components(retpath, cwd, argv0);
|
|
|
|
if (validate_exec(retpath) == 0)
|
2004-11-07 00:06:29 +01:00
|
|
|
return resolve_symlinks(retpath);
|
2004-05-20 17:35:41 +02:00
|
|
|
#endif
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Since no explicit path was supplied, the user must have been relying on
|
|
|
|
* PATH. We'll search the same PATH.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2004-05-25 00:35:37 +02:00
|
|
|
if ((path = getenv("PATH")) && *path)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
char *startp = NULL,
|
|
|
|
*endp = NULL;
|
2004-05-25 00:35:37 +02:00
|
|
|
|
|
|
|
do
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-25 00:35:37 +02:00
|
|
|
if (!startp)
|
|
|
|
startp = path;
|
|
|
|
else
|
|
|
|
startp = endp + 1;
|
2004-05-20 17:35:41 +02:00
|
|
|
|
2011-02-03 04:49:54 +01:00
|
|
|
endp = first_path_var_separator(startp);
|
2004-05-25 00:35:37 +02:00
|
|
|
if (!endp)
|
2004-08-29 07:07:03 +02:00
|
|
|
endp = startp + strlen(startp); /* point to end */
|
2004-05-25 00:35:37 +02:00
|
|
|
|
|
|
|
StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
|
|
|
|
|
|
|
|
if (is_absolute_path(test_path))
|
2004-11-06 02:16:22 +01:00
|
|
|
join_path_components(retpath, test_path, argv0);
|
2004-05-20 17:35:41 +02:00
|
|
|
else
|
2004-11-06 02:16:22 +01:00
|
|
|
{
|
|
|
|
join_path_components(retpath, cwd, test_path);
|
|
|
|
join_path_components(retpath, retpath, argv0);
|
|
|
|
}
|
2004-05-20 17:38:11 +02:00
|
|
|
canonicalize_path(retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
|
2004-05-20 17:38:11 +02:00
|
|
|
switch (validate_exec(retpath))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2017-06-21 20:39:04 +02:00
|
|
|
case 0: /* found ok */
|
2004-11-07 00:06:29 +01:00
|
|
|
return resolve_symlinks(retpath);
|
1997-09-08 04:41:22 +02:00
|
|
|
case -1: /* wasn't even a candidate, keep looking */
|
2004-11-06 02:16:22 +01:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
case -2: /* found but disqualified */
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not read binary \"%s\""),
|
2005-01-14 18:47:49 +01:00
|
|
|
retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2004-05-25 00:35:37 +02:00
|
|
|
} while (*endp);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not find a \"%s\" to execute"), argv0);
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2004-05-11 23:57:15 +02:00
|
|
|
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* resolve_symlinks - resolve symlinks to the underlying file
|
|
|
|
*
|
2004-12-24 17:55:43 +01:00
|
|
|
* Replace "path" by the absolute path to the referenced file.
|
2004-11-07 00:06:29 +01:00
|
|
|
*
|
|
|
|
* Returns 0 if OK, -1 if error.
|
|
|
|
*
|
|
|
|
* Note: we are not particularly tense about producing nice error messages
|
|
|
|
* because we are not really expecting error here; we just determined that
|
|
|
|
* the symlink does point to a valid executable.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
resolve_symlinks(char *path)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_READLINK
|
|
|
|
struct stat buf;
|
|
|
|
char orig_wd[MAXPGPATH],
|
|
|
|
link_buf[MAXPGPATH];
|
|
|
|
char *fname;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* To resolve a symlink properly, we have to chdir into its directory and
|
|
|
|
* then chdir to where the symlink points; otherwise we may fail to
|
2004-11-07 00:06:29 +01:00
|
|
|
* resolve relative links correctly (consider cases involving mount
|
|
|
|
* points, for example). After following the final symlink, we use
|
|
|
|
* getcwd() to figure out where the heck we're at.
|
2004-12-24 17:55:43 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* One might think we could skip all this if path doesn't point to a
|
|
|
|
* symlink to start with, but that's wrong. We also want to get rid of
|
|
|
|
* any directory symlinks that are present in the given path. We expect
|
2005-10-15 04:49:52 +02:00
|
|
|
* getcwd() to give us an accurate, symlink-free path.
|
2004-11-07 00:06:29 +01:00
|
|
|
*/
|
|
|
|
if (!getcwd(orig_wd, MAXPGPATH))
|
|
|
|
{
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not identify current directory: %s"),
|
2004-11-07 00:06:29 +01:00
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
char *lsep;
|
|
|
|
int rllen;
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
lsep = last_dir_separator(path);
|
|
|
|
if (lsep)
|
|
|
|
{
|
|
|
|
*lsep = '\0';
|
|
|
|
if (chdir(path) == -1)
|
|
|
|
{
|
2012-08-29 05:01:23 +02:00
|
|
|
log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
|
2004-11-07 00:06:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fname = lsep + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fname = path;
|
|
|
|
|
|
|
|
if (lstat(fname, &buf) < 0 ||
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
!S_ISLNK(buf.st_mode))
|
2004-11-07 00:06:29 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
rllen = readlink(fname, link_buf, sizeof(link_buf));
|
|
|
|
if (rllen < 0 || rllen >= sizeof(link_buf))
|
|
|
|
{
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not read symbolic link \"%s\""), fname);
|
2004-11-07 00:06:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
link_buf[rllen] = '\0';
|
|
|
|
strcpy(path, link_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* must copy final component out of 'path' temporarily */
|
2014-02-17 17:20:21 +01:00
|
|
|
strlcpy(link_buf, fname, sizeof(link_buf));
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
if (!getcwd(path, MAXPGPATH))
|
|
|
|
{
|
2005-02-22 05:43:23 +01:00
|
|
|
log_error(_("could not identify current directory: %s"),
|
2004-11-07 00:06:29 +01:00
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
join_path_components(path, path, link_buf);
|
|
|
|
canonicalize_path(path);
|
|
|
|
|
|
|
|
if (chdir(orig_wd) == -1)
|
|
|
|
{
|
2012-08-29 05:01:23 +02:00
|
|
|
log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
|
2004-11-07 00:06:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
#endif /* HAVE_READLINK */
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find another program in our binary's directory,
|
|
|
|
* then make sure it is the proper version.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
find_other_exec(const char *argv0, const char *target,
|
|
|
|
const char *versionstr, char *retpath)
|
|
|
|
{
|
|
|
|
char cmd[MAXPGPATH];
|
2018-04-19 15:45:15 +02:00
|
|
|
char line[MAXPGPATH];
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
if (find_my_exec(argv0, retpath) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Trim off program name and keep just directory */
|
|
|
|
*last_dir_separator(retpath) = '\0';
|
|
|
|
canonicalize_path(retpath);
|
|
|
|
|
|
|
|
/* Now append the other program's name */
|
|
|
|
snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
|
|
|
|
"/%s%s", target, EXE);
|
|
|
|
|
|
|
|
if (validate_exec(retpath) != 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-07-28 01:31:13 +02:00
|
|
|
snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
if (!pipe_read_line(cmd, line, sizeof(line)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (strcmp(line, versionstr) != 0)
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
/*
|
2004-11-06 02:16:22 +01:00
|
|
|
* The runtime library's popen() on win32 does not work when being
|
2004-07-26 03:48:00 +02:00
|
|
|
* called from a service when running on windows <= 2000, because
|
|
|
|
* there is no stdin/stdout/stderr.
|
|
|
|
*
|
|
|
|
* Executing a command in a pipe and reading the first line from it
|
|
|
|
* is all we need.
|
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
static char *
|
|
|
|
pipe_read_line(char *cmd, char *line, int maxsize)
|
2004-07-26 03:48:00 +02:00
|
|
|
{
|
|
|
|
#ifndef WIN32
|
2004-08-29 07:07:03 +02:00
|
|
|
FILE *pgver;
|
2004-07-26 03:48:00 +02:00
|
|
|
|
|
|
|
/* flush output buffers in case popen does not... */
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
|
2012-07-28 01:31:13 +02:00
|
|
|
errno = 0;
|
2004-07-26 03:48:00 +02:00
|
|
|
if ((pgver = popen(cmd, "r")) == NULL)
|
2012-07-28 01:31:13 +02:00
|
|
|
{
|
|
|
|
perror("popen failure");
|
2004-07-26 03:48:00 +02:00
|
|
|
return NULL;
|
2012-07-28 01:31:13 +02:00
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2012-07-28 01:31:13 +02:00
|
|
|
errno = 0;
|
2004-07-26 03:48:00 +02:00
|
|
|
if (fgets(line, maxsize, pgver) == NULL)
|
|
|
|
{
|
2012-07-28 01:31:13 +02:00
|
|
|
if (feof(pgver))
|
|
|
|
fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
|
|
|
|
else
|
|
|
|
perror("fgets failure");
|
2011-04-10 17:42:00 +02:00
|
|
|
pclose(pgver); /* no error checking */
|
2004-07-26 03:48:00 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pclose_check(pgver))
|
|
|
|
return NULL;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
return line;
|
2005-10-15 04:49:52 +02:00
|
|
|
#else /* WIN32 */
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
SECURITY_ATTRIBUTES sattr;
|
2004-08-29 07:07:03 +02:00
|
|
|
HANDLE childstdoutrd,
|
|
|
|
childstdoutwr,
|
|
|
|
childstdoutrddup;
|
2004-07-26 03:48:00 +02:00
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
STARTUPINFO si;
|
2004-08-29 07:07:03 +02:00
|
|
|
char *retval = NULL;
|
2004-07-26 03:48:00 +02:00
|
|
|
|
|
|
|
sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
sattr.bInheritHandle = TRUE;
|
|
|
|
sattr.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
|
|
|
|
return NULL;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),
|
|
|
|
childstdoutrd,
|
|
|
|
GetCurrentProcess(),
|
|
|
|
&childstdoutrddup,
|
|
|
|
0,
|
|
|
|
FALSE,
|
|
|
|
DUPLICATE_SAME_ACCESS))
|
|
|
|
{
|
|
|
|
CloseHandle(childstdoutrd);
|
|
|
|
CloseHandle(childstdoutwr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle(childstdoutrd);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
|
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
2004-07-26 03:48:00 +02:00
|
|
|
si.cb = sizeof(si);
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
si.hStdError = childstdoutwr;
|
|
|
|
si.hStdOutput = childstdoutwr;
|
|
|
|
si.hStdInput = INVALID_HANDLE_VALUE;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
if (CreateProcess(NULL,
|
|
|
|
cmd,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
TRUE,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
&si,
|
|
|
|
&pi))
|
|
|
|
{
|
|
|
|
/* Successfully started the process */
|
2005-10-15 04:49:52 +02:00
|
|
|
char *lineptr;
|
2004-07-26 03:48:00 +02:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
ZeroMemory(line, maxsize);
|
|
|
|
|
2004-12-20 18:40:59 +01:00
|
|
|
/* Try to read at least one line from the pipe */
|
|
|
|
/* This may require more than one wait/read attempt */
|
2005-10-15 04:49:52 +02:00
|
|
|
for (lineptr = line; lineptr < line + maxsize - 1;)
|
2004-07-26 03:48:00 +02:00
|
|
|
{
|
2004-12-20 18:40:59 +01:00
|
|
|
DWORD bytesread = 0;
|
|
|
|
|
|
|
|
/* Let's see if we can read */
|
|
|
|
if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
|
2005-10-15 04:49:52 +02:00
|
|
|
break; /* Timeout, but perhaps we got a line already */
|
2004-12-20 18:40:59 +01:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
|
2004-12-20 18:40:59 +01:00
|
|
|
&bytesread, NULL))
|
2005-10-15 04:49:52 +02:00
|
|
|
break; /* Error, but perhaps we got a line already */
|
2004-12-20 18:40:59 +01:00
|
|
|
|
|
|
|
lineptr += strlen(lineptr);
|
|
|
|
|
|
|
|
if (!bytesread)
|
2005-10-15 04:49:52 +02:00
|
|
|
break; /* EOF */
|
2004-12-20 18:40:59 +01:00
|
|
|
|
|
|
|
if (strchr(line, '\n'))
|
2005-10-15 04:49:52 +02:00
|
|
|
break; /* One or more lines read */
|
2004-07-26 03:48:00 +02:00
|
|
|
}
|
2004-08-08 05:21:39 +02:00
|
|
|
|
2004-12-20 18:40:59 +01:00
|
|
|
if (lineptr != line)
|
2004-07-26 03:48:00 +02:00
|
|
|
{
|
2004-12-20 18:40:59 +01:00
|
|
|
/* OK, we read some data */
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* If we got more than one line, cut off after the first \n */
|
2005-10-15 04:49:52 +02:00
|
|
|
lineptr = strchr(line, '\n');
|
2004-12-20 18:40:59 +01:00
|
|
|
if (lineptr)
|
2005-10-15 04:49:52 +02:00
|
|
|
*(lineptr + 1) = '\0';
|
2004-12-20 18:40:59 +01:00
|
|
|
|
|
|
|
len = strlen(line);
|
2004-07-26 03:48:00 +02:00
|
|
|
|
2004-08-08 05:21:39 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* If EOL is \r\n, convert to just \n. Because stdout is a
|
|
|
|
* text-mode stream, the \n output by the child process is
|
2005-10-15 04:49:52 +02:00
|
|
|
* received as \r\n, so we convert it to \n. The server main.c
|
|
|
|
* sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
|
|
|
|
* disabling \n to \r\n expansion for stdout.
|
2004-08-08 05:21:39 +02:00
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
|
2004-08-08 05:21:39 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
line[len - 2] = '\n';
|
|
|
|
line[len - 1] = '\0';
|
2004-08-16 03:26:31 +02:00
|
|
|
len--;
|
2004-08-08 05:21:39 +02:00
|
|
|
}
|
|
|
|
|
2004-08-08 04:22:55 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We emulate fgets() behaviour. So if there is no newline at the
|
|
|
|
* end, we add one...
|
2004-08-08 04:22:55 +02:00
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
if (len == 0 || line[len - 1] != '\n')
|
|
|
|
strcat(line, "\n");
|
2004-12-20 18:40:59 +01:00
|
|
|
|
|
|
|
retval = line;
|
2004-07-26 03:48:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-07-26 03:48:00 +02:00
|
|
|
CloseHandle(childstdoutwr);
|
|
|
|
CloseHandle(childstdoutrddup);
|
|
|
|
|
|
|
|
return retval;
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
#endif /* WIN32 */
|
2004-05-11 23:57:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-18 22:18:59 +02:00
|
|
|
/*
|
|
|
|
* pclose() plus useful error reporting
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pclose_check(FILE *stream)
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
int exitstatus;
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
char *reason;
|
2004-05-18 22:18:59 +02:00
|
|
|
|
|
|
|
exitstatus = pclose(stream);
|
|
|
|
|
|
|
|
if (exitstatus == 0)
|
2004-08-29 07:07:03 +02:00
|
|
|
return 0; /* all is well */
|
2004-05-18 22:18:59 +02:00
|
|
|
|
|
|
|
if (exitstatus == -1)
|
|
|
|
{
|
|
|
|
/* pclose() itself failed, and hopefully set errno */
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
log_error(_("pclose failed: %s"), strerror(errno));
|
2004-05-18 22:18:59 +02:00
|
|
|
}
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
else
|
2007-01-29 21:17:40 +01:00
|
|
|
{
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
reason = wait_result_to_str(exitstatus);
|
|
|
|
log_error("%s", reason);
|
|
|
|
#ifdef FRONTEND
|
|
|
|
free(reason);
|
2007-01-22 19:31:51 +01:00
|
|
|
#else
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
pfree(reason);
|
2007-01-22 19:31:51 +01:00
|
|
|
#endif
|
Add support for piping COPY to/from an external program.
This includes backend "COPY TO/FROM PROGRAM '...'" syntax, and corresponding
psql \copy syntax. Like with reading/writing files, the backend version is
superuser-only, and in the psql version, the program is run in the client.
In the passing, the psql \copy STDIN/STDOUT syntax is subtly changed: if you
the stdin/stdout is quoted, it's now interpreted as a filename. For example,
"\copy foo from 'stdin'" now reads from a file called 'stdin', not from
standard input. Before this, there was no way to specify a filename called
stdin, stdout, pstdin or pstdout.
This creates a new function in pgport, wait_result_to_str(), which can
be used to convert the exit status of a process, as returned by wait(3),
to a human-readable string.
Etsuro Fujita, reviewed by Amit Kapila.
2013-02-27 17:17:21 +01:00
|
|
|
}
|
|
|
|
return exitstatus;
|
2004-05-18 22:18:59 +02:00
|
|
|
}
|
2006-09-11 22:10:30 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* set_pglocale_pgservice
|
|
|
|
*
|
|
|
|
* Set application-specific locale and service directory
|
|
|
|
*
|
|
|
|
* This function takes the value of argv[0] rather than a full path.
|
|
|
|
*
|
|
|
|
* (You may be wondering why this is in exec.c. It requires this module's
|
|
|
|
* services and doesn't introduce any new dependencies, so this seems as
|
|
|
|
* good as anyplace.)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_pglocale_pgservice(const char *argv0, const char *app)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
char my_exec_path[MAXPGPATH];
|
|
|
|
char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than
|
|
|
|
* PGLOCALEDIR */
|
2016-08-31 00:22:43 +02:00
|
|
|
char *dup_path;
|
2006-09-11 22:10:30 +02:00
|
|
|
|
|
|
|
/* don't set LC_ALL in the backend */
|
2008-12-11 08:34:09 +01:00
|
|
|
if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
|
2015-01-08 04:35:44 +01:00
|
|
|
{
|
2006-09-11 22:10:30 +02:00
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
2015-01-08 04:35:44 +01:00
|
|
|
/*
|
|
|
|
* One could make a case for reproducing here PostmasterMain()'s test
|
|
|
|
* for whether the process is multithreaded. Unlike the postmaster,
|
|
|
|
* no frontend program calls sigprocmask() or otherwise provides for
|
|
|
|
* mutual exclusion between signal handlers. While frontends using
|
|
|
|
* fork(), if multithreaded, are formally exposed to undefined
|
|
|
|
* behavior, we have not witnessed a concrete bug. Therefore,
|
|
|
|
* complaining about multithreading here may be mere pedantry.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2006-09-11 22:10:30 +02:00
|
|
|
if (find_my_exec(argv0, my_exec_path) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef ENABLE_NLS
|
|
|
|
get_locale_path(my_exec_path, path);
|
|
|
|
bindtextdomain(app, path);
|
|
|
|
textdomain(app);
|
|
|
|
|
|
|
|
if (getenv("PGLOCALEDIR") == NULL)
|
|
|
|
{
|
|
|
|
/* set for libpq to use */
|
|
|
|
snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
|
|
|
|
canonicalize_path(env_path + 12);
|
2016-08-31 00:22:43 +02:00
|
|
|
dup_path = strdup(env_path);
|
|
|
|
if (dup_path)
|
|
|
|
putenv(dup_path);
|
2006-09-11 22:10:30 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (getenv("PGSYSCONFDIR") == NULL)
|
|
|
|
{
|
|
|
|
get_etc_path(my_exec_path, path);
|
|
|
|
|
|
|
|
/* set for libpq to use */
|
|
|
|
snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
|
|
|
|
canonicalize_path(env_path + 13);
|
2016-08-31 00:22:43 +02:00
|
|
|
dup_path = strdup(env_path);
|
|
|
|
if (dup_path)
|
|
|
|
putenv(dup_path);
|
2006-09-11 22:10:30 +02:00
|
|
|
}
|
|
|
|
}
|
2008-02-29 16:31:33 +01:00
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
/*
|
2009-11-14 16:39:36 +01:00
|
|
|
* AddUserToTokenDacl(HANDLE hToken)
|
2008-02-29 16:31:33 +01:00
|
|
|
*
|
2009-11-14 16:39:36 +01:00
|
|
|
* This function adds the current user account to the restricted
|
|
|
|
* token used when we create a restricted process.
|
2008-02-29 16:31:33 +01:00
|
|
|
*
|
|
|
|
* This is required because of some security changes in Windows
|
|
|
|
* that appeared in patches to XP/2K3 and in Vista/2008.
|
|
|
|
*
|
|
|
|
* On these machines, the Administrator account is not included in
|
|
|
|
* the default DACL - you just get Administrators + System. For
|
|
|
|
* regular users you get User + System. Because we strip Administrators
|
|
|
|
* when we create the restricted token, we are left with only System
|
|
|
|
* in the DACL which leads to access denied errors for later CreatePipe()
|
|
|
|
* and CreateProcess() calls when running as Administrator.
|
|
|
|
*
|
|
|
|
* This function fixes this problem by modifying the DACL of the
|
2009-11-14 16:39:36 +01:00
|
|
|
* token the process will use, and explicitly re-adding the current
|
|
|
|
* user account. This is still secure because the Administrator account
|
|
|
|
* inherits its privileges from the Administrators group - it doesn't
|
|
|
|
* have any of its own.
|
2008-02-29 16:31:33 +01:00
|
|
|
*/
|
|
|
|
BOOL
|
2009-11-14 16:39:36 +01:00
|
|
|
AddUserToTokenDacl(HANDLE hToken)
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
ACL_SIZE_INFORMATION asi;
|
|
|
|
ACCESS_ALLOWED_ACE *pace;
|
|
|
|
DWORD dwNewAclSize;
|
|
|
|
DWORD dwSize = 0;
|
|
|
|
DWORD dwTokenInfoLength = 0;
|
|
|
|
PACL pacl = NULL;
|
2010-02-26 03:01:40 +01:00
|
|
|
PTOKEN_USER pTokenUser = NULL;
|
2008-02-29 16:31:33 +01:00
|
|
|
TOKEN_DEFAULT_DACL tddNew;
|
|
|
|
TOKEN_DEFAULT_DACL *ptdd = NULL;
|
|
|
|
TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
|
|
|
|
/* Figure out the buffer size for the DACL info */
|
|
|
|
if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
|
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
|
|
{
|
|
|
|
ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
|
|
|
|
if (ptdd == NULL)
|
|
|
|
{
|
2008-04-17 00:16:00 +02:00
|
|
|
log_error("could not allocate %lu bytes of memory", dwSize);
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get token information: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get token information buffer size: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the ACL info */
|
2009-06-11 16:49:15 +02:00
|
|
|
if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
|
2008-02-29 16:31:33 +01:00
|
|
|
(DWORD) sizeof(ACL_SIZE_INFORMATION),
|
|
|
|
AclSizeInformation))
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get ACL information: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-04-02 03:53:18 +02:00
|
|
|
/* Get the current user SID */
|
2009-07-27 10:46:10 +02:00
|
|
|
if (!GetTokenUser(hToken, &pTokenUser))
|
2016-04-07 05:40:51 +02:00
|
|
|
goto cleanup; /* callee printed a message */
|
2008-02-29 16:31:33 +01:00
|
|
|
|
|
|
|
/* Figure out the size of the new ACL */
|
2009-07-27 10:46:10 +02:00
|
|
|
dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
|
2017-06-21 20:39:04 +02:00
|
|
|
GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
|
2008-02-29 16:31:33 +01:00
|
|
|
|
|
|
|
/* Allocate the ACL buffer & initialize it */
|
|
|
|
pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
|
|
|
|
if (pacl == NULL)
|
|
|
|
{
|
2008-04-17 00:16:00 +02:00
|
|
|
log_error("could not allocate %lu bytes of memory", dwNewAclSize);
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not initialize ACL: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop through the existing ACEs, and build the new ACL */
|
|
|
|
for (i = 0; i < (int) asi.AceCount; i++)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get ACE: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not add ACE: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the new ACE for the current user */
|
2009-11-14 16:39:36 +01:00
|
|
|
if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not add access allowed ACE: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the new DACL in the token */
|
|
|
|
tddNew.DefaultDacl = pacl;
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not set token information: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
cleanup:
|
2009-07-27 10:46:10 +02:00
|
|
|
if (pTokenUser)
|
|
|
|
LocalFree((HLOCAL) pTokenUser);
|
2008-02-29 16:31:33 +01:00
|
|
|
|
|
|
|
if (pacl)
|
|
|
|
LocalFree((HLOCAL) pacl);
|
|
|
|
|
|
|
|
if (ptdd)
|
|
|
|
LocalFree((HLOCAL) ptdd);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-07-27 10:46:10 +02:00
|
|
|
* GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
|
|
|
|
*
|
|
|
|
* Get the users token information from a process token.
|
2008-02-29 16:31:33 +01:00
|
|
|
*
|
2009-07-27 10:46:10 +02:00
|
|
|
* The caller of this function is responsible for calling LocalFree() on the
|
|
|
|
* returned TOKEN_USER memory.
|
2008-02-29 16:31:33 +01:00
|
|
|
*/
|
|
|
|
static BOOL
|
2009-07-27 10:46:10 +02:00
|
|
|
GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
|
|
|
DWORD dwLength;
|
|
|
|
|
2009-07-27 10:46:10 +02:00
|
|
|
*ppTokenUser = NULL;
|
2008-02-29 16:31:33 +01:00
|
|
|
|
|
|
|
if (!GetTokenInformation(hToken,
|
|
|
|
TokenUser,
|
2009-07-27 10:46:10 +02:00
|
|
|
NULL,
|
2008-02-29 16:31:33 +01:00
|
|
|
0,
|
|
|
|
&dwLength))
|
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
|
|
{
|
2009-07-27 10:46:10 +02:00
|
|
|
*ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
|
2008-02-29 16:31:33 +01:00
|
|
|
|
2009-07-27 10:46:10 +02:00
|
|
|
if (*ppTokenUser == NULL)
|
2008-02-29 16:31:33 +01:00
|
|
|
{
|
2008-04-17 00:16:00 +02:00
|
|
|
log_error("could not allocate %lu bytes of memory", dwLength);
|
2008-02-29 16:31:33 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get token information buffer size: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetTokenInformation(hToken,
|
|
|
|
TokenUser,
|
2009-07-27 10:46:10 +02:00
|
|
|
*ppTokenUser,
|
2008-02-29 16:31:33 +01:00
|
|
|
dwLength,
|
|
|
|
&dwLength))
|
|
|
|
{
|
2009-07-27 10:46:10 +02:00
|
|
|
LocalFree(*ppTokenUser);
|
|
|
|
*ppTokenUser = NULL;
|
2008-02-29 16:31:33 +01:00
|
|
|
|
2011-08-23 21:00:52 +02:00
|
|
|
log_error("could not get token information: error code %lu", GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-07-27 10:46:10 +02:00
|
|
|
/* Memory in *ppTokenUser is LocalFree():d by the caller */
|
2008-02-29 16:31:33 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|