Skip to content

Commit d98cefe

Browse files
committed
Allow larger packets during GSSAPI authentication exchange.
Our GSSAPI code only allows packet sizes up to 16kB. However it emerges that during authentication, larger packets might be needed; various authorities suggest 48kB or 64kB as the maximum packet size. This limitation caused login failure for AD users who belong to many AD groups. To add insult to injury, we gave an unintelligible error message, typically "GSSAPI context establishment error: The routine must be called again to complete its function: Unknown error". As noted in code comments, the 16kB packet limit is effectively a protocol constant once we are doing normal data transmission: the GSSAPI code splits the data stream at those points, and if we change the limit then we will have cross-version compatibility problems due to the receiver's buffer being too small in some combinations. However, during the authentication exchange the packet sizes are not determined by us, but by the underlying GSSAPI library. So we might as well just try to send what the library tells us to. An unpatched recipient will fail on a packet larger than 16kB, but that's not worse than the sender failing without even trying. So this doesn't introduce any meaningful compatibility problem. We still need a buffer size limit, but we can easily make it be 64kB rather than 16kB until transport negotiation is complete. (Larger values were discussed, but don't seem likely to add anything.) Reported-by: Chris Gooch <cgooch@bamfunds.com> Fix-suggested-by: Jacob Champion <jacob.champion@enterprisedb.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com> Discussion: https://postgr.es/m/DS0PR22MB5971A9C8A3F44BCC6293C4DABE99A@DS0PR22MB5971.namprd22.prod.outlook.com Backpatch-through: 13
1 parent 961553d commit d98cefe

File tree

2 files changed

+94
-35
lines changed

2 files changed

+94
-35
lines changed

src/backend/libpq/be-secure-gssapi.c

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,18 @@
4646
* don't want the other side to send arbitrarily huge packets as we
4747
* would have to allocate memory for them to then pass them to GSSAPI.
4848
*
49-
* Therefore, these two #define's are effectively part of the protocol
49+
* Therefore, this #define is effectively part of the protocol
5050
* spec and can't ever be changed.
5151
*/
52-
#define PQ_GSS_SEND_BUFFER_SIZE 16384
53-
#define PQ_GSS_RECV_BUFFER_SIZE 16384
52+
#define PQ_GSS_MAX_PACKET_SIZE 16384 /* includes uint32 header word */
53+
54+
/*
55+
* However, during the authentication exchange we must cope with whatever
56+
* message size the GSSAPI library wants to send (because our protocol
57+
* doesn't support splitting those messages). Depending on configuration
58+
* those messages might be as much as 64kB.
59+
*/
60+
#define PQ_GSS_AUTH_BUFFER_SIZE 65536 /* includes uint32 header word */
5461

5562
/*
5663
* Since we manage at most one GSS-encrypted connection per backend,
@@ -210,12 +217,12 @@ be_gssapi_write(Port *port, const void *ptr, size_t len)
210217
errno = ECONNRESET;
211218
return -1;
212219
}
213-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
220+
if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
214221
{
215222
ereport(COMMERROR,
216223
(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
217224
(size_t) output.length,
218-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
225+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
219226
errno = ECONNRESET;
220227
return -1;
221228
}
@@ -346,12 +353,12 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
346353
/* Decode the packet length and check for overlength packet */
347354
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
348355

349-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
356+
if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
350357
{
351358
ereport(COMMERROR,
352359
(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
353360
(size_t) input.length,
354-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
361+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
355362
errno = ECONNRESET;
356363
return -1;
357364
}
@@ -517,10 +524,13 @@ secure_open_gssapi(Port *port)
517524
* that will never use them, and we ensure that the buffers are
518525
* sufficiently aligned for the length-word accesses that we do in some
519526
* places in this file.
527+
*
528+
* We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
529+
* negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
520530
*/
521-
PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
522-
PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
523-
PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
531+
PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
532+
PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
533+
PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
524534
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
525535
ereport(FATAL,
526536
(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -568,16 +578,16 @@ secure_open_gssapi(Port *port)
568578

569579
/*
570580
* During initialization, packets are always fully consumed and
571-
* shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
581+
* shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
572582
*
573583
* Verify on our side that the client doesn't do something funny.
574584
*/
575-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
585+
if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
576586
{
577587
ereport(COMMERROR,
578-
(errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
588+
(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
579589
(size_t) input.length,
580-
PQ_GSS_RECV_BUFFER_SIZE)));
590+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
581591
return -1;
582592
}
583593

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

634-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
644+
if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
635645
{
636646
ereport(COMMERROR,
637647
(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
638648
(size_t) output.length,
639-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
649+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
640650
gss_release_buffer(&minor, &output);
641651
return -1;
642652
}
@@ -691,12 +701,29 @@ secure_open_gssapi(Port *port)
691701
break;
692702
}
693703

704+
/*
705+
* Release the large authentication buffers and allocate the ones we want
706+
* for normal operation.
707+
*/
708+
free(PqGSSSendBuffer);
709+
free(PqGSSRecvBuffer);
710+
free(PqGSSResultBuffer);
711+
PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
712+
PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
713+
PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
714+
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
715+
ereport(FATAL,
716+
(errcode(ERRCODE_OUT_OF_MEMORY),
717+
errmsg("out of memory")));
718+
PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
719+
PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
720+
694721
/*
695722
* Determine the max packet size which will fit in our buffer, after
696723
* accounting for the length. be_gssapi_write will need this.
697724
*/
698725
major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
699-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
726+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
700727
&PqGSSMaxPktSize);
701728

702729
if (GSS_ERROR(major))

src/interfaces/libpq/fe-secure-gssapi.c

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,18 @@
4747
* don't want the other side to send arbitrarily huge packets as we
4848
* would have to allocate memory for them to then pass them to GSSAPI.
4949
*
50-
* Therefore, these two #define's are effectively part of the protocol
50+
* Therefore, this #define is effectively part of the protocol
5151
* spec and can't ever be changed.
5252
*/
53-
#define PQ_GSS_SEND_BUFFER_SIZE 16384
54-
#define PQ_GSS_RECV_BUFFER_SIZE 16384
53+
#define PQ_GSS_MAX_PACKET_SIZE 16384 /* includes uint32 header word */
54+
55+
/*
56+
* However, during the authentication exchange we must cope with whatever
57+
* message size the GSSAPI library wants to send (because our protocol
58+
* doesn't support splitting those messages). Depending on configuration
59+
* those messages might be as much as 64kB.
60+
*/
61+
#define PQ_GSS_AUTH_BUFFER_SIZE 65536 /* includes uint32 header word */
5562

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

206-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
213+
if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
207214
{
208215
libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
209216
(size_t) output.length,
210-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
217+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
211218
errno = EIO; /* for lack of a better idea */
212219
goto cleanup;
213220
}
@@ -342,11 +349,11 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
342349
/* Decode the packet length and check for overlength packet */
343350
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
344351

345-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
352+
if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
346353
{
347354
libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
348355
(size_t) input.length,
349-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
356+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
350357
errno = EIO; /* for lack of a better idea */
351358
return -1;
352359
}
@@ -485,12 +492,15 @@ pqsecure_open_gss(PGconn *conn)
485492
* initialize state variables. By malloc'ing the buffers separately, we
486493
* ensure that they are sufficiently aligned for the length-word accesses
487494
* that we do in some places in this file.
495+
*
496+
* We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
497+
* negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
488498
*/
489499
if (PqGSSSendBuffer == NULL)
490500
{
491-
PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
492-
PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
493-
PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
501+
PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
502+
PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
503+
PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
494504
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
495505
{
496506
libpq_append_conn_error(conn, "out of memory");
@@ -564,13 +574,13 @@ pqsecure_open_gss(PGconn *conn)
564574
* so leave a spot at the end for a NULL byte too) and report that
565575
* back to the caller.
566576
*/
567-
result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
577+
result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
568578
if (result != PGRES_POLLING_OK)
569579
return result;
570580

571581
PqGSSRecvLength += ret;
572582

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

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

585595
/* Get the length and check for over-length packet */
586596
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
587-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
597+
if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
588598
{
589599
libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
590600
(size_t) input.length,
591-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
601+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
592602
return PGRES_POLLING_FAILED;
593603
}
594604

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

681+
/*
682+
* Release the large authentication buffers and allocate the ones we
683+
* want for normal operation. (This maneuver is safe only because
684+
* pqDropConnection will drop the buffers; otherwise, during a
685+
* reconnection we'd be at risk of using undersized buffers during
686+
* negotiation.)
687+
*/
688+
free(PqGSSSendBuffer);
689+
free(PqGSSRecvBuffer);
690+
free(PqGSSResultBuffer);
691+
PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
692+
PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
693+
PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
694+
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
695+
{
696+
libpq_append_conn_error(conn, "out of memory");
697+
return PGRES_POLLING_FAILED;
698+
}
699+
PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
700+
PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
701+
671702
/*
672703
* Determine the max packet size which will fit in our buffer, after
673704
* accounting for the length. pg_GSS_write will need this.
674705
*/
675706
major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
676-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
707+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
677708
&PqGSSMaxPktSize);
678709

679710
if (GSS_ERROR(major))
@@ -687,10 +718,11 @@ pqsecure_open_gss(PGconn *conn)
687718
}
688719

689720
/* Must have output.length > 0 */
690-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
721+
if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
691722
{
692-
pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
693-
conn, major, minor);
723+
libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
724+
(size_t) output.length,
725+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
694726
gss_release_buffer(&minor, &output);
695727
return PGRES_POLLING_FAILED;
696728
}

0 commit comments

Comments
 (0)