]> 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:37 +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 bccea5470080a8792c4df27dd31c21eebea82ab0..f433a827bb6aef20dc76afeb1bdebef653051ebf 100644 (file)
@@ -887,7 +887,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))
@@ -896,7 +895,6 @@ open_server_SSL(Port *port)
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                 errmsg("could not set SSL socket: %s",
                                                SSLerrmessage())));
-               close_SSL(port);
                return -1;
        }
 
@@ -944,7 +942,6 @@ aloop:
                                                                err)));
                                break;
                }
-               close_SSL(port);
                return -1;
        }
 
@@ -973,7 +970,6 @@ aloop:
                        {
                                /* shouldn't happen */
                                pfree(peer_cn);
-                               close_SSL(port);
                                return -1;
                        }
 
@@ -987,7 +983,6 @@ aloop:
                                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                                 errmsg("SSL certificate's common name contains embedded null")));
                                pfree(peer_cn);
-                               close_SSL(port);
                                return -1;
                        }
 
index aeeffd72609d80b64d36ad78b990eb9f63ec3011..3296b756bb70d2401cfb399ddc07a21af19fea4c 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 07aca31a93c205ba7ab0f36c0217f8132dd3881a..c6cca64051a50353c75e4f86603cde7153e3736e 100644 (file)
@@ -3464,7 +3464,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);