Commit b0ce3850 authored by Noah Misch's avatar Noah Misch

Prevent a double free by not reentering be_tls_close().

Reentering this function with the right timing caused a double free,
typically crashing the backend.  By synchronizing a disconnection with
the authentication timeout, an unauthenticated attacker could achieve
this somewhat consistently.  Call be_tls_close() solely from within
proc_exit_prepare().  Back-patch to 9.0 (all supported versions).

Benkocs Norbert Attila

Security: CVE-2015-3165
parent 8cc7a4c5
...@@ -353,7 +353,6 @@ be_tls_open_server(Port *port) ...@@ -353,7 +353,6 @@ be_tls_open_server(Port *port)
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not initialize SSL connection: %s", errmsg("could not initialize SSL connection: %s",
SSLerrmessage()))); SSLerrmessage())));
be_tls_close(port);
return -1; return -1;
} }
if (!my_SSL_set_fd(port, port->sock)) if (!my_SSL_set_fd(port, port->sock))
...@@ -362,7 +361,6 @@ be_tls_open_server(Port *port) ...@@ -362,7 +361,6 @@ be_tls_open_server(Port *port)
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not set SSL socket: %s", errmsg("could not set SSL socket: %s",
SSLerrmessage()))); SSLerrmessage())));
be_tls_close(port);
return -1; return -1;
} }
port->ssl_in_use = true; port->ssl_in_use = true;
...@@ -419,7 +417,6 @@ aloop: ...@@ -419,7 +417,6 @@ aloop:
err))); err)));
break; break;
} }
be_tls_close(port);
return -1; return -1;
} }
...@@ -449,7 +446,6 @@ aloop: ...@@ -449,7 +446,6 @@ aloop:
{ {
/* shouldn't happen */ /* shouldn't happen */
pfree(peer_cn); pfree(peer_cn);
be_tls_close(port);
return -1; return -1;
} }
...@@ -463,7 +459,6 @@ aloop: ...@@ -463,7 +459,6 @@ aloop:
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("SSL certificate's common name contains embedded null"))); errmsg("SSL certificate's common name contains embedded null")));
pfree(peer_cn); pfree(peer_cn);
be_tls_close(port);
return -1; return -1;
} }
......
...@@ -220,32 +220,45 @@ socket_comm_reset(void) ...@@ -220,32 +220,45 @@ socket_comm_reset(void)
/* -------------------------------- /* --------------------------------
* socket_close - shutdown libpq at backend exit * socket_close - shutdown libpq at backend exit
* *
* Note: in a standalone backend MyProcPort will be null, * This is the one pg_on_exit_callback in place during BackendInitialize().
* don't crash during exit... * That function's unusual signal handling constrains that this callback be
* safe to run at any instant.
* -------------------------------- * --------------------------------
*/ */
static void static void
socket_close(int code, Datum arg) socket_close(int code, Datum arg)
{ {
/* Nothing to do in a standalone backend, where MyProcPort is NULL. */
if (MyProcPort != NULL) if (MyProcPort != NULL)
{ {
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
#ifdef ENABLE_GSS #ifdef ENABLE_GSS
OM_uint32 min_s; OM_uint32 min_s;
/* Shutdown GSSAPI layer */ /*
* Shutdown GSSAPI layer. This section does nothing when interrupting
* BackendInitialize(), because pg_GSS_recvauth() makes first use of
* "ctx" and "cred".
*/
if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT) if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL); gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL) if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
gss_release_cred(&min_s, &MyProcPort->gss->cred); gss_release_cred(&min_s, &MyProcPort->gss->cred);
#endif /* ENABLE_GSS */ #endif /* ENABLE_GSS */
/* GSS and SSPI share the port->gss struct */
/*
* GSS and SSPI share the port->gss struct. Since nowhere else does a
* postmaster child free this, doing so is safe when interrupting
* BackendInitialize().
*/
free(MyProcPort->gss); free(MyProcPort->gss);
#endif /* ENABLE_GSS || ENABLE_SSPI */ #endif /* ENABLE_GSS || ENABLE_SSPI */
/* Cleanly shut down SSL layer */ /*
* Cleanly shut down SSL layer. Nowhere else does a postmaster child
* call this, so this is safe when interrupting BackendInitialize().
*/
secure_close(MyProcPort); secure_close(MyProcPort);
/* /*
......
...@@ -3960,7 +3960,16 @@ BackendInitialize(Port *port) ...@@ -3960,7 +3960,16 @@ BackendInitialize(Port *port)
* We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT or * We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT or
* timeout while trying to collect the startup packet. Otherwise the * timeout while trying to collect the startup packet. Otherwise the
* postmaster cannot shutdown the database FAST or IMMED cleanly if a * postmaster cannot shutdown the database FAST or IMMED cleanly if a
* buggy client fails to send the packet promptly. * buggy client fails to send the packet promptly. XXX it follows that
* the remainder of this function must tolerate losing control at any
* instant. Likewise, any pg_on_exit_callback registered before or during
* this function must be prepared to execute at any instant between here
* and the end of this function. Furthermore, affected callbacks execute
* partially or not at all when a second exit-inducing signal arrives
* after proc_exit_prepare() decrements on_proc_exit_index. (Thanks to
* that mechanic, callbacks need not anticipate more than one call.) This
* is fragile; it ought to instead follow the norm of handling interrupts
* at selected, safe opportunities.
*/ */
pqsignal(SIGTERM, startup_die); pqsignal(SIGTERM, startup_die);
pqsignal(SIGQUIT, startup_die); pqsignal(SIGQUIT, startup_die);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment