diff options
Diffstat (limited to 'src/backend/tcop/backend_startup.c')
-rw-r--r-- | src/backend/tcop/backend_startup.c | 98 |
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"), |