]> granicus.if.org Git - postgresql/commitdiff
Prevent a double free by not reentering be_tls_close().
authorNoah Misch <noah@leadboat.com>
Mon, 18 May 2015 14:02:31 +0000 (10:02 -0400)
committerNoah Misch <noah@leadboat.com>
Mon, 18 May 2015 14:02:38 +0000 (10:02 -0400)
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

src/backend/libpq/be-secure.c
src/backend/libpq/pqcomm.c
src/backend/postmaster/postmaster.c

index 6e094965057a551367548634916034172b0ec997..6ddcfd8476f8222b2fe8525482f900f4a2f03f21 100644 (file)
@@ -906,7 +906,6 @@ open_server_SSL(Port *port)
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                 errmsg("could not initialize SSL connection: %s",
                                                SSLerrmessage())));
-               close_SSL(port);
                return -1;
        }
        if (!my_SSL_set_fd(port->ssl, port->sock))
@@ -915,7 +914,6 @@ open_server_SSL(Port *port)
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                 errmsg("could not set SSL socket: %s",
                                                SSLerrmessage())));
-               close_SSL(port);
                return -1;
        }
 
@@ -963,7 +961,6 @@ aloop:
                                                                err)));
                                break;
                }
-               close_SSL(port);
                return -1;
        }
 
@@ -992,7 +989,6 @@ aloop:
                        {
                                /* shouldn't happen */
                                pfree(peer_cn);
-                               close_SSL(port);
                                return -1;
                        }
 
@@ -1006,7 +1002,6 @@ aloop:
                                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                                 errmsg("SSL certificate's common name contains embedded null")));
                                pfree(peer_cn);
-                               close_SSL(port);
                                return -1;
                        }
 
index 47b752aa154c47d5e5b96e99826972568b8a347e..9f35536a6eb55c7835e8c1295d4c54c07f2957d0 100644 (file)
@@ -182,32 +182,45 @@ pq_comm_reset(void)
 /* --------------------------------
  *             pq_close - shutdown libpq at backend exit
  *
- * Note: in a standalone backend MyProcPort will be null,
- * don't crash during exit...
+ * This is the one pg_on_exit_callback in place during BackendInitialize().
+ * That function's unusual signal handling constrains that this callback be
+ * safe to run at any instant.
  * --------------------------------
  */
 static void
 pq_close(int code, Datum arg)
 {
+       /* Nothing to do in a standalone backend, where MyProcPort is NULL. */
        if (MyProcPort != NULL)
        {
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 #ifdef ENABLE_GSS
                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)
                        gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
 
                if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
                        gss_release_cred(&min_s, &MyProcPort->gss->cred);
 #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);
 #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);
 
                /*
index 775a7e3ad57c08be894078aa3f5b895ae1d9de6e..0199ab8add6cd3488c7c63d2f6527828a56d062c 100644 (file)
@@ -3462,7 +3462,16 @@ BackendInitialize(Port *port)
         * We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT or
         * timeout while trying to collect the startup packet.  Otherwise the
         * 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(SIGQUIT, startup_die);