diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/system_views.sql | 3 | ||||
-rw-r--r-- | src/backend/replication/libpqwalreceiver/libpqwalreceiver.c | 52 | ||||
-rw-r--r-- | src/backend/replication/walreceiver.c | 85 |
3 files changed, 104 insertions, 36 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 272c02f32b7..f52de3ae182 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -681,7 +681,8 @@ CREATE VIEW pg_stat_wal_receiver AS s.last_msg_receipt_time, s.latest_end_lsn, s.latest_end_time, - s.slot_name + s.slot_name, + s.conn_info FROM pg_stat_get_wal_receiver() s WHERE s.pid IS NOT NULL; diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c index b61e39d7d8a..45dccb3e0de 100644 --- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c +++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c @@ -20,6 +20,7 @@ #include <sys/time.h> #include "libpq-fe.h" +#include "pqexpbuffer.h" #include "access/xlog.h" #include "miscadmin.h" #include "replication/walreceiver.h" @@ -47,6 +48,7 @@ static char *recvBuf = NULL; /* Prototypes for interface functions */ static void libpqrcv_connect(char *conninfo); +static char *libpqrcv_get_conninfo(void); static void libpqrcv_identify_system(TimeLineID *primary_tli); static void libpqrcv_readtimelinehistoryfile(TimeLineID tli, char **filename, char **content, int *len); static bool libpqrcv_startstreaming(TimeLineID tli, XLogRecPtr startpoint, @@ -74,6 +76,7 @@ _PG_init(void) walrcv_disconnect != NULL) elog(ERROR, "libpqwalreceiver already loaded"); walrcv_connect = libpqrcv_connect; + walrcv_get_conninfo = libpqrcv_get_conninfo; walrcv_identify_system = libpqrcv_identify_system; walrcv_readtimelinehistoryfile = libpqrcv_readtimelinehistoryfile; walrcv_startstreaming = libpqrcv_startstreaming; @@ -118,6 +121,55 @@ libpqrcv_connect(char *conninfo) } /* + * Return a user-displayable conninfo string. Any security-sensitive fields + * are obfuscated. + */ +static char * +libpqrcv_get_conninfo(void) +{ + PQconninfoOption *conn_opts; + PQconninfoOption *conn_opt; + PQExpBufferData buf; + char *retval; + + Assert(streamConn != NULL); + + initPQExpBuffer(&buf); + conn_opts = PQconninfo(streamConn); + + if (conn_opts == NULL) + ereport(ERROR, + (errmsg("could not parse connection string: %s", + _("out of memory")))); + + /* build a clean connection string from pieces */ + for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) + { + bool obfuscate; + + /* Skip debug and empty options */ + if (strchr(conn_opt->dispchar, 'D') || + conn_opt->val == NULL || + conn_opt->val[0] == '\0') + continue; + + /* Obfuscate security-sensitive options */ + obfuscate = strchr(conn_opt->dispchar, '*') != NULL; + + appendPQExpBuffer(&buf, "%s%s=%s", + buf.len == 0 ? "" : " ", + conn_opt->keyword, + obfuscate ? "********" : conn_opt->val); + } + + PQconninfoFree(conn_opts); + + retval = PQExpBufferDataBroken(buf) ? NULL : pstrdup(buf.data); + termPQExpBuffer(&buf); + return retval; +} + +/* * Check that primary's system identifier matches ours, and fetch the current * timeline ID of the primary. */ diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index ce311cb8972..d552f04901c 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -75,6 +75,7 @@ bool hot_standby_feedback; /* libpqreceiver hooks to these when loaded */ walrcv_connect_type walrcv_connect = NULL; +walrcv_get_conninfo_type walrcv_get_conninfo = NULL; walrcv_identify_system_type walrcv_identify_system = NULL; walrcv_startstreaming_type walrcv_startstreaming = NULL; walrcv_endstreaming_type walrcv_endstreaming = NULL; @@ -192,6 +193,7 @@ void WalReceiverMain(void) { char conninfo[MAXCONNINFO]; + char *tmp_conninfo; char slotname[NAMEDATALEN]; XLogRecPtr startpoint; TimeLineID startpointTLI; @@ -282,7 +284,9 @@ WalReceiverMain(void) /* Load the libpq-specific functions */ load_file("libpqwalreceiver", false); - if (walrcv_connect == NULL || walrcv_startstreaming == NULL || + if (walrcv_connect == NULL || + walrcv_get_conninfo == NULL || + walrcv_startstreaming == NULL || walrcv_endstreaming == NULL || walrcv_identify_system == NULL || walrcv_readtimelinehistoryfile == NULL || @@ -304,6 +308,21 @@ WalReceiverMain(void) walrcv_connect(conninfo); DisableWalRcvImmediateExit(); + /* + * Save user-visible connection string. This clobbers the original + * conninfo, for security. + */ + tmp_conninfo = walrcv_get_conninfo(); + SpinLockAcquire(&walrcv->mutex); + memset(walrcv->conninfo, 0, MAXCONNINFO); + if (tmp_conninfo) + { + strlcpy((char *) walrcv->conninfo, tmp_conninfo, MAXCONNINFO); + pfree(tmp_conninfo); + } + walrcv->ready_to_display = true; + SpinLockRelease(&walrcv->mutex); + first_stream = true; for (;;) { @@ -1308,10 +1327,9 @@ WalRcvGetStateString(WalRcvState state) Datum pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_WAL_RECEIVER_COLS 11 TupleDesc tupdesc; - Datum values[PG_STAT_GET_WAL_RECEIVER_COLS]; - bool nulls[PG_STAT_GET_WAL_RECEIVER_COLS]; + Datum *values; + bool *nulls; WalRcvData *walrcv = WalRcv; WalRcvState state; XLogRecPtr receive_start_lsn; @@ -1323,41 +1341,33 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) XLogRecPtr latest_end_lsn; TimestampTz latest_end_time; char *slotname; + char *conninfo; /* No WAL receiver, just return a tuple with NULL values */ if (walrcv->pid == 0) PG_RETURN_NULL(); - /* Initialise values and NULL flags arrays */ - MemSet(values, 0, sizeof(values)); - MemSet(nulls, 0, sizeof(nulls)); - - /* Initialise attributes information in the tuple descriptor */ - tupdesc = CreateTemplateTupleDesc(PG_STAT_GET_WAL_RECEIVER_COLS, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pid", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "status", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "receive_start_lsn", - LSNOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "receive_start_tli", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "received_lsn", - LSNOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 6, "received_tli", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 7, "last_msg_send_time", - TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 8, "last_msg_receipt_time", - TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 9, "latest_end_lsn", - LSNOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 10, "latest_end_time", - TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 11, "slot_name", - TEXTOID, -1, 0); - - BlessTupleDesc(tupdesc); + /* + * Users attempting to read this data mustn't be shown security sensitive + * data, so sleep until everything has been properly obfuscated. + */ +retry: + SpinLockAcquire(&walrcv->mutex); + if (!walrcv->ready_to_display) + { + SpinLockRelease(&walrcv->mutex); + CHECK_FOR_INTERRUPTS(); + pg_usleep(1000); + goto retry; + } + SpinLockRelease(&walrcv->mutex); + + /* determine result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + values = palloc0(sizeof(Datum) * tupdesc->natts); + nulls = palloc0(sizeof(bool) * tupdesc->natts); /* Take a lock to ensure value consistency */ SpinLockAcquire(&walrcv->mutex); @@ -1371,6 +1381,7 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) latest_end_lsn = walrcv->latestWalEnd; latest_end_time = walrcv->latestWalEndTime; slotname = pstrdup(walrcv->slotname); + conninfo = pstrdup(walrcv->conninfo); SpinLockRelease(&walrcv->mutex); /* Fetch values */ @@ -1382,7 +1393,7 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) * Only superusers can see details. Other users only get the pid value * to know whether it is a WAL receiver, but no details. */ - MemSet(&nulls[1], true, PG_STAT_GET_WAL_RECEIVER_COLS - 1); + MemSet(&nulls[1], true, sizeof(bool) * (tupdesc->natts - 1)); } else { @@ -1418,6 +1429,10 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) nulls[10] = true; else values[10] = CStringGetTextDatum(slotname); + if (*conninfo == '\0') + nulls[11] = true; + else + values[11] = CStringGetTextDatum(conninfo); } /* Returns the record as Datum */ |