aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop/backend_startup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop/backend_startup.c')
-rw-r--r--src/backend/tcop/backend_startup.c98
1 files changed, 89 insertions, 9 deletions
diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c
index 64df3ff32a2..b59df3f6603 100644
--- a/src/backend/tcop/backend_startup.c
+++ b/src/backend/tcop/backend_startup.c
@@ -41,6 +41,7 @@
bool Trace_connection_negotiation = false;
static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
+static int ProcessSSLStartup(Port *port);
static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
static void process_startup_packet_die(SIGNAL_ARGS);
@@ -251,11 +252,15 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
+ /* Handle direct SSL handshake */
+ status = ProcessSSLStartup(port);
+
/*
* Receive the startup packet (which might turn out to be a cancel request
* packet).
*/
- status = ProcessStartupPacket(port, false, false);
+ if (status == STATUS_OK)
+ status = ProcessStartupPacket(port, false, false);
/*
* If we're going to reject the connection due to database state, say so
@@ -348,6 +353,77 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
}
/*
+ * Check for a direct SSL connection.
+ *
+ * This happens before the startup packet so we are careful not to actually
+ * read any bytes from the stream if it's not a direct SSL connection.
+ */
+static int
+ProcessSSLStartup(Port *port)
+{
+ int firstbyte;
+
+ Assert(!port->ssl_in_use);
+
+ pq_startmsgread();
+ firstbyte = pq_peekbyte();
+ pq_endmsgread();
+ if (firstbyte == EOF)
+ {
+ /*
+ * Like in ProcessStartupPacket, if we get no data at all, don't
+ * clutter the log with a complaint.
+ */
+ return STATUS_ERROR;
+ }
+
+ if (firstbyte != 0x16)
+ {
+ /* Not an SSL handshake message */
+ return STATUS_OK;
+ }
+
+ /*
+ * First byte indicates standard SSL handshake message
+ *
+ * (It can't be a Postgres startup length because in network byte order
+ * that would be a startup packet hundreds of megabytes long)
+ */
+
+#ifdef USE_SSL
+ if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
+ {
+ /* SSL not supported */
+ goto reject;
+ }
+
+ if (secure_open_server(port) == -1)
+ {
+ /*
+ * we assume secure_open_server() sent an appropriate TLS alert
+ * already
+ */
+ goto reject;
+ }
+ Assert(port->ssl_in_use);
+
+ if (Trace_connection_negotiation)
+ ereport(LOG,
+ (errmsg("direct SSL connection accepted")));
+ return STATUS_OK;
+#else
+ /* SSL not supported by this build */
+ goto reject;
+#endif
+
+reject:
+ if (Trace_connection_negotiation)
+ ereport(LOG,
+ (errmsg("direct SSL connection rejected")));
+ return STATUS_ERROR;
+}
+
+/*
* Read a client's startup packet and do something according to it.
*
* Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and
@@ -468,8 +544,13 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
char SSLok;
#ifdef USE_SSL
- /* No SSL when disabled or on Unix sockets */
- if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
+
+ /*
+ * No SSL when disabled or on Unix sockets.
+ *
+ * Also no SSL negotiation if we already have a direct SSL connection
+ */
+ if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use)
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -487,11 +568,10 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
(errmsg("SSLRequest rejected")));
}
-retry1:
- if (send(port->sock, &SSLok, 1, 0) != 1)
+ while (secure_write(port, &SSLok, 1) != 1)
{
if (errno == EINTR)
- goto retry1; /* if interrupted, just retry */
+ continue; /* if interrupted, just retry */
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("failed to send SSL negotiation response: %m")));
@@ -509,7 +589,7 @@ retry1:
* encrypted and indeed may have been injected by a man-in-the-middle.
* We report this case to the client.
*/
- if (pq_buffer_has_data())
+ if (pq_buffer_remaining_data() > 0)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("received unencrypted data after SSL request"),
@@ -542,7 +622,7 @@ retry1:
(errmsg("GSSENCRequest rejected")));
}
- while (send(port->sock, &GSSok, 1, 0) != 1)
+ while (secure_write(port, &GSSok, 1) != 1)
{
if (errno == EINTR)
continue;
@@ -563,7 +643,7 @@ retry1:
* encrypted and indeed may have been injected by a man-in-the-middle.
* We report this case to the client.
*/
- if (pq_buffer_has_data())
+ if (pq_buffer_remaining_data() > 0)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("received unencrypted data after GSSAPI encryption request"),