Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups - Mailing list pgsql-bugs

From Tom Lane
Subject Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups
Date
Msg-id 3125964.1748115528@sss.pgh.pa.us
Whole thread Raw
In response to Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups
List pgsql-bugs
I wrote:
> OK, here's a set of draft patches for that.

Sigh, it'd help if I actually attached the patches ...

            regards, tom lane

diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 717ba9824f9..910c7f22711 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -46,11 +46,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * Since we manage at most one GSS-encrypted connection per backend,
@@ -210,12 +218,12 @@ be_gssapi_write(Port *port, const void *ptr, size_t len)
             errno = ECONNRESET;
             return -1;
         }
-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                             (size_t) output.length,
-                            PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -346,12 +354,12 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -517,10 +525,13 @@ secure_open_gssapi(Port *port)
      * that will never use them, and we ensure that the buffers are
      * sufficiently aligned for the length-word accesses that we do in some
      * places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
-    PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-    PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-    PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+    PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
     if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         ereport(FATAL,
                 (errcode(ERRCODE_OUT_OF_MEMORY),
@@ -568,16 +579,16 @@ secure_open_gssapi(Port *port)

         /*
          * During initialization, packets are always fully consumed and
-         * shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
+         * shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
          *
          * Verify on our side that the client doesn't do something funny.
          */
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
-                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
+                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE)));
+                            PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
             return -1;
         }

@@ -631,12 +642,12 @@ secure_open_gssapi(Port *port)
         {
             uint32        netlen = pg_hton32(output.length);

-            if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+            if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
             {
                 ereport(COMMERROR,
                         (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                                 (size_t) output.length,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                                PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
                 gss_release_buffer(&minor, &output);
                 return -1;
             }
@@ -691,12 +702,29 @@ secure_open_gssapi(Port *port)
             break;
     }

+    /*
+     * Release the large authentication buffers and allocate the ones we want
+     * for normal operation.
+     */
+    free(PqGSSSendBuffer);
+    free(PqGSSRecvBuffer);
+    free(PqGSSResultBuffer);
+    PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        ereport(FATAL,
+                (errcode(ERRCODE_OUT_OF_MEMORY),
+                 errmsg("out of memory")));
+    PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+    PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
     /*
      * Determine the max packet size which will fit in our buffer, after
      * accounting for the length.  be_gssapi_write will need this.
      */
     major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                 &PqGSSMaxPktSize);

     if (GSS_ERROR(major))
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index ce183bc04b4..9bdf0192f1e 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -47,11 +47,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * We need these state variables per-connection.  To allow the functions
@@ -203,11 +211,11 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
             goto cleanup;
         }

-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
                                     (size_t) output.length,
-                                    PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
+                                    PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             goto cleanup;
         }
@@ -342,11 +350,11 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
                                     (size_t) input.length,
-                                    PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                                    PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             return -1;
         }
@@ -485,12 +493,15 @@ pqsecure_open_gss(PGconn *conn)
      * initialize state variables.  By malloc'ing the buffers separately, we
      * ensure that they are sufficiently aligned for the length-word accesses
      * that we do in some places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
     if (PqGSSSendBuffer == NULL)
     {
-        PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-        PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-        PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+        PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
         if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         {
             libpq_append_conn_error(conn, "out of memory");
@@ -564,13 +575,13 @@ pqsecure_open_gss(PGconn *conn)
              * so leave a spot at the end for a NULL byte too) and report that
              * back to the caller.
              */
-            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
+            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
             if (result != PGRES_POLLING_OK)
                 return result;

             PqGSSRecvLength += ret;

-            Assert(PqGSSRecvLength < PQ_GSS_RECV_BUFFER_SIZE);
+            Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
             PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
             appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);

@@ -584,11 +595,11 @@ pqsecure_open_gss(PGconn *conn)

         /* Get the length and check for over-length packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
                                     (size_t) input.length,
-                                    PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                                    PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
             return PGRES_POLLING_FAILED;
         }

@@ -668,12 +679,33 @@ pqsecure_open_gss(PGconn *conn)
         conn->gcred = GSS_C_NO_CREDENTIAL;
         gss_release_buffer(&minor, &output);

+        /*
+         * Release the large authentication buffers and allocate the ones we
+         * want for normal operation.  (This maneuver is safe only because
+         * pqDropConnection will drop the buffers; otherwise, during a
+         * reconnection we'd be at risk of using undersized buffers during
+         * negotiation.)
+         */
+        free(PqGSSSendBuffer);
+        free(PqGSSRecvBuffer);
+        free(PqGSSResultBuffer);
+        PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        {
+            libpq_append_conn_error(conn, "out of memory");
+            return PGRES_POLLING_FAILED;
+        }
+        PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+        PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
         /*
          * Determine the max packet size which will fit in our buffer, after
          * accounting for the length.  pg_GSS_write will need this.
          */
         major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
-                                    PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                    PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                     &PqGSSMaxPktSize);

         if (GSS_ERROR(major))
@@ -687,10 +719,11 @@ pqsecure_open_gss(PGconn *conn)
     }

     /* Must have output.length > 0 */
-    if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+    if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
     {
-        pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
-                     conn, major, minor);
+        libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
+                                (size_t) output.length,
+                                PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
         gss_release_buffer(&minor, &output);
         return PGRES_POLLING_FAILED;
     }
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index d3337a3d1c4..4aa828bef08 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -45,11 +45,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * Since we manage at most one GSS-encrypted connection per backend,
@@ -209,12 +217,12 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
             errno = ECONNRESET;
             return -1;
         }
-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                             (size_t) output.length,
-                            PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -345,12 +353,12 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -510,10 +518,13 @@ secure_open_gssapi(Port *port)
      * that will never use them, and we ensure that the buffers are
      * sufficiently aligned for the length-word accesses that we do in some
      * places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
-    PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-    PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-    PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+    PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
     if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         ereport(FATAL,
                 (errcode(ERRCODE_OUT_OF_MEMORY),
@@ -560,16 +571,16 @@ secure_open_gssapi(Port *port)

         /*
          * During initialization, packets are always fully consumed and
-         * shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
+         * shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
          *
          * Verify on our side that the client doesn't do something funny.
          */
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
-                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
+                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE)));
+                            PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
             return -1;
         }

@@ -616,12 +627,12 @@ secure_open_gssapi(Port *port)
         {
             uint32        netlen = pg_hton32(output.length);

-            if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+            if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
             {
                 ereport(COMMERROR,
                         (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                                 (size_t) output.length,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                                PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
                 gss_release_buffer(&minor, &output);
                 return -1;
             }
@@ -676,12 +687,29 @@ secure_open_gssapi(Port *port)
             break;
     }

+    /*
+     * Release the large authentication buffers and allocate the ones we want
+     * for normal operation.
+     */
+    free(PqGSSSendBuffer);
+    free(PqGSSRecvBuffer);
+    free(PqGSSResultBuffer);
+    PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        ereport(FATAL,
+                (errcode(ERRCODE_OUT_OF_MEMORY),
+                 errmsg("out of memory")));
+    PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+    PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
     /*
      * Determine the max packet size which will fit in our buffer, after
      * accounting for the length.  be_gssapi_write will need this.
      */
     major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                 &PqGSSMaxPktSize);

     if (GSS_ERROR(major))
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index a3768cd70a3..eb23731645f 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -47,11 +47,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * We need these state variables per-connection.  To allow the functions
@@ -204,12 +212,12 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
             goto cleanup;
         }

-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
                               (size_t) output.length,
-                              PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             goto cleanup;
         }
@@ -344,12 +352,12 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
-                              PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             return -1;
         }
@@ -488,12 +496,15 @@ pqsecure_open_gss(PGconn *conn)
      * initialize state variables.  By malloc'ing the buffers separately, we
      * ensure that they are sufficiently aligned for the length-word accesses
      * that we do in some places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
     if (PqGSSSendBuffer == NULL)
     {
-        PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-        PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-        PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+        PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
         if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         {
             appendPQExpBufferStr(&conn->errorMessage,
@@ -568,13 +579,13 @@ pqsecure_open_gss(PGconn *conn)
              * so leave a spot at the end for a NULL byte too) and report that
              * back to the caller.
              */
-            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
+            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
             if (result != PGRES_POLLING_OK)
                 return result;

             PqGSSRecvLength += ret;

-            Assert(PqGSSRecvLength < PQ_GSS_RECV_BUFFER_SIZE);
+            Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
             PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
             appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);

@@ -588,12 +599,12 @@ pqsecure_open_gss(PGconn *conn)

         /* Get the length and check for over-length packet */
         input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             appendPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
-                              PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
             return PGRES_POLLING_FAILED;
         }

@@ -655,12 +666,34 @@ pqsecure_open_gss(PGconn *conn)
         conn->gcred = GSS_C_NO_CREDENTIAL;
         gss_release_buffer(&minor, &output);

+        /*
+         * Release the large authentication buffers and allocate the ones we
+         * want for normal operation.  (This maneuver is safe only because
+         * pqDropConnection will drop the buffers; otherwise, during a
+         * reconnection we'd be at risk of using undersized buffers during
+         * negotiation.)
+         */
+        free(PqGSSSendBuffer);
+        free(PqGSSRecvBuffer);
+        free(PqGSSResultBuffer);
+        PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        {
+            appendPQExpBufferStr(&conn->errorMessage,
+                                 libpq_gettext("out of memory\n"));
+            return PGRES_POLLING_FAILED;
+        }
+        PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+        PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
         /*
          * Determine the max packet size which will fit in our buffer, after
          * accounting for the length.  pg_GSS_write will need this.
          */
         major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
-                                    PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                    PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                     &PqGSSMaxPktSize);

         if (GSS_ERROR(major))
@@ -674,10 +707,12 @@ pqsecure_open_gss(PGconn *conn)
     }

     /* Must have output.length > 0 */
-    if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+    if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
     {
-        pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
-                     conn, major, minor);
+        appendPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
+                          (size_t) output.length,
+                          PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
         gss_release_buffer(&minor, &output);
         return PGRES_POLLING_FAILED;
     }
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 0fc7d565e2a..4992ff5bd20 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -45,11 +45,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * Since we manage at most one GSS-encrypted connection per backend,
@@ -209,12 +217,12 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
             errno = ECONNRESET;
             return -1;
         }
-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                             (size_t) output.length,
-                            PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -345,12 +353,12 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
                     (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
+                            PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
             errno = ECONNRESET;
             return -1;
         }
@@ -510,10 +518,13 @@ secure_open_gssapi(Port *port)
      * that will never use them, and we ensure that the buffers are
      * sufficiently aligned for the length-word accesses that we do in some
      * places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
-    PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-    PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-    PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+    PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
     if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         ereport(FATAL,
                 (errcode(ERRCODE_OUT_OF_MEMORY),
@@ -560,16 +571,16 @@ secure_open_gssapi(Port *port)

         /*
          * During initialization, packets are always fully consumed and
-         * shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
+         * shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
          *
          * Verify on our side that the client doesn't do something funny.
          */
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             ereport(COMMERROR,
-                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
+                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                             (size_t) input.length,
-                            PQ_GSS_RECV_BUFFER_SIZE)));
+                            PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
             return -1;
         }

@@ -616,12 +627,12 @@ secure_open_gssapi(Port *port)
         {
             uint32        netlen = htonl(output.length);

-            if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+            if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
             {
                 ereport(COMMERROR,
                         (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                                 (size_t) output.length,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+                                PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
                 gss_release_buffer(&minor, &output);
                 return -1;
             }
@@ -676,12 +687,29 @@ secure_open_gssapi(Port *port)
             break;
     }

+    /*
+     * Release the large authentication buffers and allocate the ones we want
+     * for normal operation.
+     */
+    free(PqGSSSendBuffer);
+    free(PqGSSRecvBuffer);
+    free(PqGSSResultBuffer);
+    PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+    if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        ereport(FATAL,
+                (errcode(ERRCODE_OUT_OF_MEMORY),
+                 errmsg("out of memory")));
+    PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+    PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
     /*
      * Determine the max packet size which will fit in our buffer, after
      * accounting for the length.  be_gssapi_write will need this.
      */
     major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
-                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                 &PqGSSMaxPktSize);

     if (GSS_ERROR(major))
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index ac2a8c8563f..abc07fe5f04 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -47,11 +47,19 @@
  * don't want the other side to send arbitrarily huge packets as we
  * would have to allocate memory for them to then pass them to GSSAPI.
  *
- * Therefore, these two #define's are effectively part of the protocol
+ * Therefore, this #define is effectively part of the protocol
  * spec and can't ever be changed.
  */
-#define PQ_GSS_SEND_BUFFER_SIZE 16384
-#define PQ_GSS_RECV_BUFFER_SIZE 16384
+#define PQ_GSS_MAX_PACKET_SIZE 16384
+
+/*
+ * However, during the authentication exchange we must cope with whatever
+ * message size the GSSAPI library wants to send (because our protocol
+ * doesn't support splitting those messages).  Depending on configuration
+ * those messages might be as much as 64kB.  To provide some safety margin
+ * we use 128kB buffers during transport negotiation.
+ */
+#define PQ_GSS_AUTH_BUFFER_SIZE 131072

 /*
  * We need these state variables per-connection.  To allow the functions
@@ -204,12 +212,12 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
             goto cleanup;
         }

-        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+        if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             printfPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
                               (size_t) output.length,
-                              PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             goto cleanup;
         }
@@ -344,12 +352,12 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
         /* Decode the packet length and check for overlength packet */
         input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);

-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
         {
             printfPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
-                              PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
             errno = EIO;        /* for lack of a better idea */
             return -1;
         }
@@ -488,12 +496,15 @@ pqsecure_open_gss(PGconn *conn)
      * initialize state variables.  By malloc'ing the buffers separately, we
      * ensure that they are sufficiently aligned for the length-word accesses
      * that we do in some places in this file.
+     *
+     * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
+     * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
      */
     if (PqGSSSendBuffer == NULL)
     {
-        PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
-        PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
-        PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+        PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
         if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
         {
             printfPQExpBuffer(&conn->errorMessage,
@@ -568,13 +579,13 @@ pqsecure_open_gss(PGconn *conn)
              * so leave a spot at the end for a NULL byte too) and report that
              * back to the caller.
              */
-            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
+            result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1,
&ret);
             if (result != PGRES_POLLING_OK)
                 return result;

             PqGSSRecvLength += ret;

-            Assert(PqGSSRecvLength < PQ_GSS_RECV_BUFFER_SIZE);
+            Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
             PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
             printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);

@@ -588,12 +599,12 @@ pqsecure_open_gss(PGconn *conn)

         /* Get the length and check for over-length packet */
         input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
-        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+        if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
         {
             printfPQExpBuffer(&conn->errorMessage,
                               libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
                               (size_t) input.length,
-                              PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+                              PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
             return PGRES_POLLING_FAILED;
         }

@@ -655,12 +666,34 @@ pqsecure_open_gss(PGconn *conn)
         conn->gcred = GSS_C_NO_CREDENTIAL;
         gss_release_buffer(&minor, &output);

+        /*
+         * Release the large authentication buffers and allocate the ones we
+         * want for normal operation.  (This maneuver is safe only because
+         * pqDropConnection will drop the buffers; otherwise, during a
+         * reconnection we'd be at risk of using undersized buffers during
+         * negotiation.)
+         */
+        free(PqGSSSendBuffer);
+        free(PqGSSRecvBuffer);
+        free(PqGSSResultBuffer);
+        PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
+        if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+        {
+            printfPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("out of memory\n"));
+            return PGRES_POLLING_FAILED;
+        }
+        PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+        PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+
         /*
          * Determine the max packet size which will fit in our buffer, after
          * accounting for the length.  pg_GSS_write will need this.
          */
         major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
-                                    PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+                                    PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
                                     &PqGSSMaxPktSize);

         if (GSS_ERROR(major))
@@ -674,10 +707,12 @@ pqsecure_open_gss(PGconn *conn)
     }

     /* Must have output.length > 0 */
-    if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+    if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
     {
-        pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
-                     conn, major, minor);
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
+                          (size_t) output.length,
+                          PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
         gss_release_buffer(&minor, &output);
         return PGRES_POLLING_FAILED;
     }

pgsql-bugs by date:

Previous
From: Tom Lane
Date:
Subject: Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups
Next
From: Nico Williams
Date:
Subject: Re: [EXT] Re: GSS Auth issue when user member of lots of AD groups