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
|
|
|
*
|
2023-01-02 21:00:37 +01:00
|
|
|
* Portions Copyright (c) 1996-2023, 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
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
/*
|
|
|
|
* On macOS, "man realpath" avers:
|
|
|
|
* Defining _DARWIN_C_SOURCE or _DARWIN_BETTER_REALPATH before including
|
|
|
|
* stdlib.h will cause the provided implementation of realpath() to use
|
|
|
|
* F_GETPATH from fcntl(2) to discover the path.
|
|
|
|
* This should be harmless everywhere else.
|
|
|
|
*/
|
|
|
|
#define _DARWIN_BETTER_REALPATH
|
|
|
|
|
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
|
|
|
|
2022-01-10 11:54:11 +01:00
|
|
|
#ifdef EXEC_BACKEND
|
|
|
|
#if defined(HAVE_SYS_PERSONALITY_H)
|
|
|
|
#include <sys/personality.h>
|
|
|
|
#elif defined(HAVE_SYS_PROCCTL_H)
|
|
|
|
#include <sys/procctl.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2022-04-25 21:02:13 +02:00
|
|
|
/* Inhibit mingw CRT's auto-globbing of command line arguments */
|
|
|
|
#if defined(WIN32) && !defined(_MSC_VER)
|
|
|
|
extern int _CRT_glob = 0; /* 0 turns off globbing; 1 turns it on */
|
|
|
|
#endif
|
|
|
|
|
2018-10-09 19:36:16 +02:00
|
|
|
/*
|
|
|
|
* Hacky solution to allow expressing both frontend and backend error reports
|
|
|
|
* in one macro call. First argument of log_error is an errcode() call of
|
|
|
|
* some sort (ignored if FRONTEND); the rest are errmsg_internal() arguments,
|
|
|
|
* i.e. message string and any parameters for it.
|
|
|
|
*
|
|
|
|
* Caller must provide the gettext wrapper around the message string, if
|
|
|
|
* appropriate, so that it gets translated in the FRONTEND case; this
|
|
|
|
* motivates using errmsg_internal() not errmsg(). We handle appending a
|
|
|
|
* newline, if needed, inside the macro, so that there's only one translatable
|
|
|
|
* string per call not two.
|
|
|
|
*/
|
2004-05-19 19:15:21 +02:00
|
|
|
#ifndef FRONTEND
|
2018-10-09 19:36:16 +02:00
|
|
|
#define log_error(errcodefn, ...) \
|
|
|
|
ereport(LOG, (errcodefn, errmsg_internal(__VA_ARGS__)))
|
2004-05-19 19:15:21 +02:00
|
|
|
#else
|
2018-10-09 19:36:16 +02:00
|
|
|
#define log_error(errcodefn, ...) \
|
|
|
|
(fprintf(stderr, __VA_ARGS__), fputc('\n', stderr))
|
2004-05-19 19:15:21 +02:00
|
|
|
#endif
|
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
static int normalize_exec_path(char *path);
|
|
|
|
static char *pg_realpath(const char *fname);
|
2004-11-07 00:06:29 +01:00
|
|
|
|
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.
|
|
|
|
* -1 if the regular file "path" does not exist or cannot be executed.
|
|
|
|
* -2 if the file is otherwise valid but cannot be read.
|
2022-07-12 21:37:39 +02:00
|
|
|
* in the failure cases, errno is set appropriately
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2021-03-03 09:44:46 +01:00
|
|
|
int
|
2004-05-24 22:23:50 +02:00
|
|
|
validate_exec(const char *path)
|
1996-07-09 08:22:35 +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() */
|
2023-03-23 23:17:49 +01:00
|
|
|
if (strlen(path) < strlen(".exe") ||
|
2004-05-11 23:57:15 +02:00
|
|
|
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
|
|
|
/*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Ensure that the file exists and is a regular file.
|
|
|
|
*
|
|
|
|
* XXX if you have a broken system where stat() looks at the symlink
|
|
|
|
* instead of the underlying file, you lose.
|
|
|
|
*/
|
|
|
|
if (stat(path, &buf) < 0)
|
|
|
|
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))
|
2022-07-12 21:37:39 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* POSIX offers no errno code that's simply "not a regular file". If
|
|
|
|
* it's a directory we can use EISDIR. Otherwise, it's most likely a
|
|
|
|
* device special file, and EPERM (Operation not permitted) isn't too
|
|
|
|
* horribly off base.
|
|
|
|
*/
|
|
|
|
errno = S_ISDIR(buf.st_mode) ? EISDIR : EPERM;
|
1996-07-09 08:22:35 +02:00
|
|
|
return -1;
|
2022-07-12 21:37:39 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Ensure that the file is both executable and readable (required for
|
|
|
|
* dynamic loading).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2010-01-14 01:14:06 +01:00
|
|
|
#ifndef WIN32
|
|
|
|
is_r = (access(path, R_OK) == 0);
|
|
|
|
is_x = (access(path, X_OK) == 0);
|
2022-07-12 21:37:39 +02:00
|
|
|
/* access() will set errno if it returns -1 */
|
2010-01-14 01:14:06 +01:00
|
|
|
#else
|
2003-05-15 18:35:30 +02:00
|
|
|
is_r = buf.st_mode & S_IRUSR;
|
|
|
|
is_x = buf.st_mode & S_IXUSR;
|
2022-07-12 21:37:39 +02:00
|
|
|
errno = EACCES; /* appropriate thing if we return nonzero */
|
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
|
|
|
/*
|
2023-03-23 23:17:49 +01:00
|
|
|
* find_my_exec -- find an absolute path to this program's 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
|
2023-03-23 23:17:49 +01:00
|
|
|
* executable's location. Also, we need an absolute path not a relative
|
|
|
|
* path because we may later change working directory. Finally, we want
|
2004-11-07 00:06:29 +01:00
|
|
|
* 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-05-25 00:35:37 +02:00
|
|
|
char *path;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2004-11-06 02:16:22 +01:00
|
|
|
* If argv0 contains a separator, then PATH wasn't used.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2023-03-23 23:17:49 +01:00
|
|
|
strlcpy(retpath, argv0, MAXPGPATH);
|
|
|
|
if (first_dir_separator(retpath) != NULL)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-05-20 17:38:11 +02:00
|
|
|
if (validate_exec(retpath) == 0)
|
2023-03-23 23:17:49 +01:00
|
|
|
return normalize_exec_path(retpath);
|
2004-11-06 02:16:22 +01:00
|
|
|
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
2022-07-12 21:37:39 +02:00
|
|
|
_("invalid binary \"%s\": %m"), 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
|
|
|
if (validate_exec(retpath) == 0)
|
2023-03-23 23:17:49 +01:00
|
|
|
return normalize_exec_path(retpath);
|
2004-05-20 17:35:41 +02:00
|
|
|
#endif
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2004-11-06 02:16:22 +01:00
|
|
|
* Since no explicit path was supplied, the user must have been relying on
|
|
|
|
* PATH. We'll search the same PATH.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2004-05-25 00:35:37 +02:00
|
|
|
if ((path = getenv("PATH")) && *path)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-05-25 00:35:37 +02:00
|
|
|
char *startp = NULL,
|
|
|
|
*endp = NULL;
|
|
|
|
|
|
|
|
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)
|
|
|
|
endp = startp + strlen(startp); /* point to end */
|
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
strlcpy(retpath, startp, Min(endp - startp + 1, MAXPGPATH));
|
2004-05-25 00:35:37 +02:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
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
|
|
|
{
|
2004-11-06 02:16:22 +01:00
|
|
|
case 0: /* found ok */
|
2023-03-23 23:17:49 +01:00
|
|
|
return normalize_exec_path(retpath);
|
1996-07-09 08:22:35 +02:00
|
|
|
case -1: /* wasn't even a candidate, keep looking */
|
2004-11-06 02:16:22 +01:00
|
|
|
break;
|
1996-07-09 08:22:35 +02:00
|
|
|
case -2: /* found but disqualified */
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
2022-07-12 21:37:39 +02:00
|
|
|
_("could not read binary \"%s\": %m"),
|
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
|
|
|
}
|
|
|
|
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_UNDEFINED_FILE),
|
|
|
|
_("could not find a \"%s\" to execute"), argv0);
|
1996-07-09 08:22:35 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-05-11 23:57:15 +02:00
|
|
|
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
/*
|
2023-03-23 23:17:49 +01:00
|
|
|
* normalize_exec_path - resolve symlinks and convert to absolute path
|
2004-11-07 00:06:29 +01:00
|
|
|
*
|
2023-03-23 23:17:49 +01:00
|
|
|
* Given a path that refers to an executable, chase through any symlinks
|
|
|
|
* to find the real file location; then convert that to an absolute path.
|
2004-11-07 00:06:29 +01:00
|
|
|
*
|
2023-03-23 23:17:49 +01:00
|
|
|
* On success, replaces the contents of "path" with the absolute path.
|
|
|
|
* ("path" is assumed to be of size MAXPGPATH.)
|
2004-11-07 00:06:29 +01:00
|
|
|
* Returns 0 if OK, -1 if error.
|
|
|
|
*/
|
|
|
|
static int
|
2023-03-23 23:17:49 +01:00
|
|
|
normalize_exec_path(char *path)
|
2004-11-07 00:06:29 +01:00
|
|
|
{
|
|
|
|
/*
|
2023-03-23 23:17:49 +01:00
|
|
|
* We used to do a lot of work ourselves here, but now we just let
|
|
|
|
* realpath(3) do all the heavy lifting.
|
2004-11-07 00:06:29 +01:00
|
|
|
*/
|
2023-03-23 23:17:49 +01:00
|
|
|
char *abspath = pg_realpath(path);
|
|
|
|
|
|
|
|
if (abspath == NULL)
|
2004-11-07 00:06:29 +01:00
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode_for_file_access(),
|
2023-03-23 23:17:49 +01:00
|
|
|
_("could not resolve path \"%s\" to absolute form: %m"),
|
|
|
|
path);
|
2004-11-07 00:06:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2023-03-23 23:17:49 +01:00
|
|
|
strlcpy(path, abspath, MAXPGPATH);
|
|
|
|
free(abspath);
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
/* On Windows, be sure to convert '\' to '/' */
|
|
|
|
canonicalize_path(path);
|
|
|
|
#endif
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2004-11-07 00:06:29 +01:00
|
|
|
|
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
/*
|
|
|
|
* pg_realpath() - realpath(3) with POSIX.1-2008 semantics
|
|
|
|
*
|
|
|
|
* This is equivalent to realpath(fname, NULL), in that it returns a
|
|
|
|
* malloc'd buffer containing the absolute path equivalent to fname.
|
|
|
|
* On error, returns NULL with errno set.
|
|
|
|
*
|
|
|
|
* On Windows, what you get is spelled per platform conventions,
|
|
|
|
* so you probably want to apply canonicalize_path() to the result.
|
|
|
|
*
|
|
|
|
* For now, this is needed only here so mark it static. If you choose to
|
|
|
|
* move it into its own file, move the _DARWIN_BETTER_REALPATH #define too!
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
pg_realpath(const char *fname)
|
|
|
|
{
|
|
|
|
char *path;
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
#ifndef WIN32
|
|
|
|
path = realpath(fname, NULL);
|
|
|
|
if (path == NULL && errno == EINVAL)
|
2004-11-07 00:06:29 +01:00
|
|
|
{
|
2023-03-23 23:17:49 +01:00
|
|
|
/*
|
|
|
|
* Cope with old-POSIX systems that require a user-provided buffer.
|
|
|
|
* Assume MAXPGPATH is enough room on all such systems.
|
|
|
|
*/
|
|
|
|
char *buf = malloc(MAXPGPATH);
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
if (buf == NULL)
|
|
|
|
return NULL; /* assume errno is set */
|
|
|
|
path = realpath(fname, buf);
|
|
|
|
if (path == NULL) /* don't leak memory */
|
|
|
|
{
|
|
|
|
int save_errno = errno;
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
errno = save_errno;
|
|
|
|
}
|
2004-11-07 00:06:29 +01:00
|
|
|
}
|
2023-03-23 23:17:49 +01:00
|
|
|
#else /* WIN32 */
|
2004-11-07 00:06:29 +01:00
|
|
|
|
2023-03-23 23:17:49 +01:00
|
|
|
/*
|
|
|
|
* Microsoft is resolutely non-POSIX, but _fullpath() does the same thing.
|
|
|
|
* The documentation claims it reports errors by setting errno, which is a
|
|
|
|
* bit surprising for Microsoft, but we'll believe that until it's proven
|
|
|
|
* wrong. Clear errno first, though, so we can at least tell if a failure
|
|
|
|
* occurs and doesn't set it.
|
|
|
|
*/
|
|
|
|
errno = 0;
|
|
|
|
path = _fullpath(NULL, fname, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return path;
|
2004-11-07 00:06:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
/*
|
2020-02-19 05:20:33 +01:00
|
|
|
* Execute a command in a pipe and read the first line from it.
|
2004-07-26 03:48:00 +02:00
|
|
|
*/
|
2020-04-01 03:57:03 +02:00
|
|
|
char *
|
2004-07-26 03:48:00 +02:00
|
|
|
pipe_read_line(char *cmd, char *line, int maxsize)
|
|
|
|
{
|
|
|
|
FILE *pgver;
|
|
|
|
|
2022-08-29 19:55:38 +02:00
|
|
|
fflush(NULL);
|
2004-07-26 03:48:00 +02:00
|
|
|
|
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");
|
2010-12-16 21:15:37 +01: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;
|
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)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
return 0; /* all is well */
|
|
|
|
|
|
|
|
if (exitstatus == -1)
|
|
|
|
{
|
|
|
|
/* pclose() itself failed, and hopefully set errno */
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_ERROR),
|
2021-04-23 14:18:11 +02:00
|
|
|
_("%s() failed: %m"), "pclose");
|
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);
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_ERROR),
|
|
|
|
"%s", reason);
|
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);
|
|
|
|
}
|
|
|
|
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];
|
|
|
|
|
|
|
|
/* 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);
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
/* set for libpq to use, but don't override existing setting */
|
|
|
|
setenv("PGLOCALEDIR", path, 0);
|
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 */
|
Use setenv() in preference to putenv().
Since at least 2001 we've used putenv() and avoided setenv(), on the
grounds that the latter was unportable and not in POSIX. However,
POSIX added it that same year, and by now the situation has reversed:
setenv() is probably more portable than putenv(), since POSIX now
treats the latter as not being a core function. And setenv() has
cleaner semantics too. So, let's reverse that old policy.
This commit adds a simple src/port/ implementation of setenv() for
any stragglers (we have one in the buildfarm, but I'd not be surprised
if that code is never used in the field). More importantly, extend
win32env.c to also support setenv(). Then, replace usages of putenv()
with setenv(), and get rid of some ad-hoc implementations of setenv()
wannabees.
Also, adjust our src/port/ implementation of unsetenv() to follow the
POSIX spec that it returns an error indicator, rather than returning
void as per the ancient BSD convention. I don't feel a need to make
all the call sites check for errors, but the portability stub ought
to match real-world practice.
Discussion: https://postgr.es/m/2065122.1609212051@sss.pgh.pa.us
2020-12-30 18:55:59 +01:00
|
|
|
setenv("PGSYSCONFDIR", path, 0);
|
2006-09-11 22:10:30 +02:00
|
|
|
}
|
|
|
|
}
|
2008-02-29 16:31:33 +01:00
|
|
|
|
2022-01-10 11:54:11 +01:00
|
|
|
#ifdef EXEC_BACKEND
|
|
|
|
/*
|
|
|
|
* For the benefit of PostgreSQL developers testing EXEC_BACKEND on Unix
|
|
|
|
* systems (code paths normally exercised only on Windows), provide a way to
|
|
|
|
* disable address space layout randomization, if we know how on this platform.
|
|
|
|
* Otherwise, backends may fail to attach to shared memory at the fixed address
|
|
|
|
* chosen by the postmaster. (See also the macOS-specific hack in
|
|
|
|
* sysv_shmem.c.)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pg_disable_aslr(void)
|
|
|
|
{
|
|
|
|
#if defined(HAVE_SYS_PERSONALITY_H)
|
|
|
|
return personality(ADDR_NO_RANDOMIZE);
|
|
|
|
#elif defined(HAVE_SYS_PROCCTL_H) && defined(PROC_ASLR_FORCE_DISABLE)
|
|
|
|
int data = PROC_ASLR_FORCE_DISABLE;
|
|
|
|
|
|
|
|
return procctl(P_PID, 0, PROC_ASLR_CTL, &data);
|
|
|
|
#else
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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;
|
2009-07-27 10:46:10 +02: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)
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_OUT_OF_MEMORY),
|
|
|
|
_("out of memory"));
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_ERROR),
|
|
|
|
"could not get token information: error code %lu",
|
|
|
|
GetLastError());
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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 */
|
|
|
|
if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
|
|
|
|
(DWORD) sizeof(ACL_SIZE_INFORMATION),
|
|
|
|
AclSizeInformation))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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) +
|
|
|
|
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)
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_OUT_OF_MEMORY),
|
|
|
|
_("out of memory"));
|
2008-02-29 16:31:33 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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++)
|
|
|
|
{
|
|
|
|
if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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;
|
|
|
|
|
|
|
|
if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_OUT_OF_MEMORY),
|
|
|
|
_("out of memory"));
|
2008-02-29 16:31:33 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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
|
|
|
|
2018-10-09 19:36:16 +02:00
|
|
|
log_error(errcode(ERRCODE_SYSTEM_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
|