Skip to content

Commit ca70ee6

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 63fa7ca commit ca70ee6

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
@@ -45,11 +45,18 @@
4545
* don't want the other side to send arbitrarily huge packets as we
4646
* would have to allocate memory for them to then pass them to GSSAPI.
4747
*
48-
* Therefore, these two #define's are effectively part of the protocol
48+
* Therefore, this #define is effectively part of the protocol
4949
* spec and can't ever be changed.
5050
*/
51-
#define PQ_GSS_SEND_BUFFER_SIZE 16384
52-
#define PQ_GSS_RECV_BUFFER_SIZE 16384
51+
#define PQ_GSS_MAX_PACKET_SIZE 16384 /* includes uint32 header word */
52+
53+
/*
54+
* However, during the authentication exchange we must cope with whatever
55+
* message size the GSSAPI library wants to send (because our protocol
56+
* doesn't support splitting those messages). Depending on configuration
57+
* those messages might be as much as 64kB.
58+
*/
59+
#define PQ_GSS_AUTH_BUFFER_SIZE 65536 /* includes uint32 header word */
5360

5461
/*
5562
* Since we manage at most one GSS-encrypted connection per backend,
@@ -209,12 +216,12 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
209216
errno = ECONNRESET;
210217
return -1;
211218
}
212-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
219+
if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
213220
{
214221
ereport(COMMERROR,
215222
(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
216223
(size_t) output.length,
217-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
224+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
218225
errno = ECONNRESET;
219226
return -1;
220227
}
@@ -345,12 +352,12 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
345352
/* Decode the packet length and check for overlength packet */
346353
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
347354

348-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
355+
if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
349356
{
350357
ereport(COMMERROR,
351358
(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
352359
(size_t) input.length,
353-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
360+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
354361
errno = ECONNRESET;
355362
return -1;
356363
}
@@ -514,10 +521,13 @@ secure_open_gssapi(Port *port)
514521
* that will never use them, and we ensure that the buffers are
515522
* sufficiently aligned for the length-word accesses that we do in some
516523
* places in this file.
524+
*
525+
* We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
526+
* negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
517527
*/
518-
PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
519-
PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
520-
PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
528+
PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
529+
PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
530+
PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
521531
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
522532
ereport(FATAL,
523533
(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -565,16 +575,16 @@ secure_open_gssapi(Port *port)
565575

566576
/*
567577
* During initialization, packets are always fully consumed and
568-
* shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
578+
* shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
569579
*
570580
* Verify on our side that the client doesn't do something funny.
571581
*/
572-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
582+
if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
573583
{
574584
ereport(COMMERROR,
575-
(errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
585+
(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
576586
(size_t) input.length,
577-
PQ_GSS_RECV_BUFFER_SIZE)));
587+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
578588
return -1;
579589
}
580590

@@ -628,12 +638,12 @@ secure_open_gssapi(Port *port)
628638
{
629639
uint32 netlen = pg_hton32(output.length);
630640

631-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
641+
if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
632642
{
633643
ereport(COMMERROR,
634644
(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
635645
(size_t) output.length,
636-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
646+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
637647
gss_release_buffer(&minor, &output);
638648
return -1;
639649
}
@@ -688,12 +698,29 @@ secure_open_gssapi(Port *port)
688698
break;
689699
}
690700

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

699726
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy