Skip to content

Commit 5f09c58

Browse files
committed
Secure Unix-domain sockets of "make check" temporary clusters.
Any OS user able to access the socket can connect as the bootstrap superuser and proceed to execute arbitrary code as the OS user running the test. Protect against that by placing the socket in a temporary, mode-0700 subdirectory of /tmp. The pg_regress-based test suites and the pg_upgrade test suite were vulnerable; the $(prove_check)-based test suites were already secure. Back-patch to 8.4 (all supported versions). The hazard remains wherever the temporary cluster accepts TCP connections, notably on Windows. As a convenient side effect, this lets testing proceed smoothly in builds that override DEFAULT_PGSOCKET_DIR. Popular non-default values like /var/run/postgresql are often unwritable to the build user. Security: CVE-2014-0067
1 parent 52c3734 commit 5f09c58

File tree

4 files changed

+120
-25
lines changed

4 files changed

+120
-25
lines changed

doc/src/sgml/regress.sgml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,14 @@ gmake check
5757

5858
<warning>
5959
<para>
60-
This test method starts a temporary server, which is configured to accept
61-
any connection originating on the local machine. Any local user can gain
62-
database superuser privileges when connecting to this server, and could
63-
in principle exploit all privileges of the operating-system user running
64-
the tests. Therefore, it is not recommended that you use <literal>gmake
65-
check</> on machines shared with untrusted users. Instead, run the tests
66-
after completing the installation, as described in the next section.
67-
</para>
68-
69-
<para>
70-
On Unix-like machines, this danger can be avoided if the temporary
71-
server's socket file is made inaccessible to other users, for example
72-
by running the tests in a protected chroot. On Windows, the temporary
73-
server opens a locally-accessible TCP socket, so filesystem protections
74-
cannot help.
60+
On systems lacking Unix-domain sockets, notably Windows, this test method
61+
starts a temporary server configured to accept any connection originating
62+
on the local machine. Any local user can gain database superuser
63+
privileges when connecting to this server, and could in principle exploit
64+
all privileges of the operating-system user running the tests. Therefore,
65+
it is not recommended that you use <literal>gmake check</> on an affected
66+
system shared with untrusted users. Instead, run the tests after
67+
completing the installation, as described in the next section.
7568
</para>
7669
</warning>
7770

src/test/regress/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/pqsignal.c
2+
13
# Local binaries
24
/pg_regress
35

src/test/regress/GNUmakefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,18 @@ EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \
5252

5353
all: submake-libpgport pg_regress$(X)
5454

55-
pg_regress$(X): pg_regress.o pg_regress_main.o
55+
pg_regress$(X): pg_regress.o pg_regress_main.o pqsignal.o
5656
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@
5757

5858
# dependencies ensure that path changes propagate
5959
pg_regress.o: pg_regress.c $(top_builddir)/src/port/pg_config_paths.h
6060
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(top_builddir)/src/port $(EXTRADEFS) -c -o $@ $<
6161

62+
# Pull in pqsignal like initdb does.
63+
pqsignal.c: % : $(top_srcdir)/src/interfaces/libpq/%
64+
rm -f $@ && $(LN_S) $< .
65+
pqsignal.o: override CPPFLAGS += -I$(libpq_srcdir)
66+
6267
$(top_builddir)/src/port/pg_config_paths.h: $(top_builddir)/src/Makefile.global
6368
$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
6469

@@ -171,7 +176,8 @@ bigcheck: all
171176

172177
clean distclean maintainer-clean: clean-lib
173178
# things built by `all' target
174-
rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) pg_regress_main.o pg_regress.o pg_regress$(X)
179+
rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX)
180+
rm -f pqsignal.c pg_regress_main.o pg_regress.o pqsignal.o pg_regress$(X)
175181
# things created by various check targets
176182
rm -f $(output_files) $(input_files)
177183
rm -rf testtablespace

src/test/regress/pg_regress.c

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#endif
3131

3232
#include "getopt_long.h"
33+
#include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
34+
#include "libpq/pqsignal.h"
3335
#include "pg_config_paths.h"
3436

3537
/* for resultmap we need a list of pairs of strings */
@@ -106,6 +108,12 @@ static const char *progname;
106108
static char *logfilename;
107109
static FILE *logfile;
108110
static char *difffilename;
111+
static const char *sockdir;
112+
#ifdef HAVE_UNIX_SOCKETS
113+
static const char *temp_sockdir;
114+
static char sockself[MAXPGPATH];
115+
static char socklock[MAXPGPATH];
116+
#endif
109117

110118
static _resultmap *resultmap = NULL;
111119

@@ -302,6 +310,81 @@ stop_postmaster(void)
302310
}
303311
}
304312

313+
#ifdef HAVE_UNIX_SOCKETS
314+
/*
315+
* Remove the socket temporary directory. pg_regress never waits for a
316+
* postmaster exit, so it is indeterminate whether the postmaster has yet to
317+
* unlink the socket and lock file. Unlink them here so we can proceed to
318+
* remove the directory. Ignore errors; leaking a temporary directory is
319+
* unimportant. This can run from a signal handler. The code is not
320+
* acceptable in a Windows signal handler (see initdb.c:trapsig()), but
321+
* Windows is not a HAVE_UNIX_SOCKETS platform.
322+
*/
323+
static void
324+
remove_temp(void)
325+
{
326+
unlink(sockself);
327+
unlink(socklock);
328+
rmdir(temp_sockdir);
329+
}
330+
331+
/*
332+
* Signal handler that calls remove_temp() and reraises the signal.
333+
*/
334+
static void
335+
signal_remove_temp(int signum)
336+
{
337+
remove_temp();
338+
339+
pqsignal(signum, SIG_DFL);
340+
raise(signum);
341+
}
342+
343+
/*
344+
* Create a temporary directory suitable for the server's Unix-domain socket.
345+
* The directory will have mode 0700 or stricter, so no other OS user can open
346+
* our socket to exploit our use of trust authentication. Most systems
347+
* constrain the length of socket paths well below _POSIX_PATH_MAX, so we
348+
* place the directory under /tmp rather than relative to the possibly-deep
349+
* current working directory.
350+
*
351+
* Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
352+
* testing to work in builds that relocate it to a directory not writable to
353+
* the build/test user.
354+
*/
355+
static const char *
356+
make_temp_sockdir(void)
357+
{
358+
char *template = strdup("/tmp/pg_regress-XXXXXX");
359+
360+
temp_sockdir = mkdtemp(template);
361+
if (temp_sockdir == NULL)
362+
{
363+
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
364+
progname, template, strerror(errno));
365+
exit(2);
366+
}
367+
368+
/* Stage file names for remove_temp(). Unsafe in a signal handler. */
369+
UNIXSOCK_PATH(sockself, port, temp_sockdir);
370+
snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
371+
372+
/* Remove the directory during clean exit. */
373+
atexit(remove_temp);
374+
375+
/*
376+
* Remove the directory before dying to the usual signals. Omit SIGQUIT,
377+
* preserving it as a quick, untidy exit.
378+
*/
379+
pqsignal(SIGHUP, signal_remove_temp);
380+
pqsignal(SIGINT, signal_remove_temp);
381+
pqsignal(SIGPIPE, signal_remove_temp);
382+
pqsignal(SIGTERM, signal_remove_temp);
383+
384+
return temp_sockdir;
385+
}
386+
#endif /* HAVE_UNIX_SOCKETS */
387+
305388
/*
306389
* Always exit through here, not through plain exit(), to ensure we make
307390
* an effort to shut down a temp postmaster
@@ -763,8 +846,7 @@ initialize_environment(void)
763846
* the wrong postmaster, or otherwise behave in nondefault ways. (Note
764847
* we also use psql's -X switch consistently, so that ~/.psqlrc files
765848
* won't mess things up.) Also, set PGPORT to the temp port, and set
766-
* or unset PGHOST depending on whether we are using TCP or Unix
767-
* sockets.
849+
* PGHOST depending on whether we are using TCP or Unix sockets.
768850
*/
769851
unsetenv("PGDATABASE");
770852
unsetenv("PGUSER");
@@ -773,10 +855,19 @@ initialize_environment(void)
773855
unsetenv("PGREQUIRESSL");
774856
unsetenv("PGCONNECT_TIMEOUT");
775857
unsetenv("PGDATA");
858+
#ifdef HAVE_UNIX_SOCKETS
776859
if (hostname != NULL)
777860
doputenv("PGHOST", hostname);
778861
else
779-
unsetenv("PGHOST");
862+
{
863+
sockdir = getenv("PG_REGRESS_SOCK_DIR");
864+
if (!sockdir)
865+
sockdir = make_temp_sockdir();
866+
doputenv("PGHOST", sockdir);
867+
}
868+
#else
869+
doputenv("PGHOST", hostname);
870+
#endif
780871
unsetenv("PGHOSTADDR");
781872
if (port != -1)
782873
{
@@ -2053,7 +2144,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
20532144
/*
20542145
* To reduce chances of interference with parallel installations, use
20552146
* a port number starting in the private range (49152-65535)
2056-
* calculated from the version number.
2147+
* calculated from the version number. This aids !HAVE_UNIX_SOCKETS
2148+
* systems; elsewhere, the use of a private socket directory already
2149+
* prevents interference.
20572150
*/
20582151
port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
20592152

@@ -2203,10 +2296,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
22032296
*/
22042297
header(_("starting postmaster"));
22052298
snprintf(buf, sizeof(buf),
2206-
SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
2207-
bindir, temp_install,
2208-
debug ? " -d 5" : "",
2209-
hostname ? hostname : "",
2299+
SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s "
2300+
"-c \"listen_addresses=%s\" -k \"%s\" "
2301+
"> \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
2302+
bindir, temp_install, debug ? " -d 5" : "",
2303+
hostname ? hostname : "", sockdir ? sockdir : "",
22102304
outputdir);
22112305
postmaster_pid = spawn_process(buf);
22122306
if (postmaster_pid == INVALID_PID)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy