Content-Length: 784361 | pFad | http://github.com/postgres/postgres/commit/39b1d190714a11f98cb35916a7ceb1a998e24062

3C Allow larger packets during GSSAPI authentication exchange. · postgres/postgres@39b1d19 · GitHub
Skip to content

Commit 39b1d19

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 405cca9 commit 39b1d19

File tree

2 files changed

+96
-35
lines changed

2 files changed

+96
-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
}
@@ -510,10 +517,13 @@ secure_open_gssapi(Port *port)
510517
* that will never use them, and we ensure that the buffers are
511518
* sufficiently aligned for the length-word accesses that we do in some
512519
* places in this file.
520+
*
521+
* We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
522+
* negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
513523
*/
514-
PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
515-
PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
516-
PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
524+
PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
525+
PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
526+
PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
517527
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
518528
ereport(FATAL,
519529
(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -560,16 +570,16 @@ secure_open_gssapi(Port *port)
560570

561571
/*
562572
* During initialization, packets are always fully consumed and
563-
* shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
573+
* shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
564574
*
565575
* Verify on our side that the client doesn't do something funny.
566576
*/
567-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
577+
if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
568578
{
569579
ereport(COMMERROR,
570-
(errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
580+
(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
571581
(size_t) input.length,
572-
PQ_GSS_RECV_BUFFER_SIZE)));
582+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
573583
return -1;
574584
}
575585

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

619-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
629+
if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
620630
{
621631
ereport(COMMERROR,
622632
(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
623633
(size_t) output.length,
624-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
634+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
625635
gss_release_buffer(&minor, &output);
626636
return -1;
627637
}
@@ -676,12 +686,29 @@ secure_open_gssapi(Port *port)
676686
break;
677687
}
678688

689+
/*
690+
* Release the large authentication buffers and allocate the ones we want
691+
* for normal operation.
692+
*/
693+
free(PqGSSSendBuffer);
694+
free(PqGSSRecvBuffer);
695+
free(PqGSSResultBuffer);
696+
PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
697+
PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
698+
PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
699+
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
700+
ereport(FATAL,
701+
(errcode(ERRCODE_OUT_OF_MEMORY),
702+
errmsg("out of memory")));
703+
PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
704+
PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
705+
679706
/*
680707
* Determine the max packet size which will fit in our buffer, after
681708
* accounting for the length. be_gssapi_write will need this.
682709
*/
683710
major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
684-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
711+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
685712
&PqGSSMaxPktSize);
686713

687714
if (GSS_ERROR(major))

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

Lines changed: 52 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
@@ -204,12 +211,12 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
204211
goto cleanup;
205212
}
206213

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

347-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
354+
if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
348355
{
349356
appendPQExpBuffer(&conn->errorMessage,
350357
libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
351358
(size_t) input.length,
352-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
359+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
353360
errno = EIO; /* for lack of a better idea */
354361
return -1;
355362
}
@@ -488,12 +495,15 @@ pqsecure_open_gss(PGconn *conn)
488495
* initialize state variables. By malloc'ing the buffers separately, we
489496
* ensure that they are sufficiently aligned for the length-word accesses
490497
* that we do in some places in this file.
498+
*
499+
* We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
500+
* negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
491501
*/
492502
if (PqGSSSendBuffer == NULL)
493503
{
494-
PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
495-
PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
496-
PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
504+
PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
505+
PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
506+
PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
497507
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
498508
{
499509
appendPQExpBufferStr(&conn->errorMessage,
@@ -568,13 +578,13 @@ pqsecure_open_gss(PGconn *conn)
568578
* so leave a spot at the end for a NULL byte too) and report that
569579
* back to the caller.
570580
*/
571-
result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
581+
result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
572582
if (result != PGRES_POLLING_OK)
573583
return result;
574584

575585
PqGSSRecvLength += ret;
576586

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

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

589599
/* Get the length and check for over-length packet */
590600
input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
591-
if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
601+
if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
592602
{
593603
appendPQExpBuffer(&conn->errorMessage,
594604
libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
595605
(size_t) input.length,
596-
PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
606+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
597607
return PGRES_POLLING_FAILED;
598608
}
599609

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

668+
/*
669+
* Release the large authentication buffers and allocate the ones we
670+
* want for normal operation. (This maneuver is safe only because
671+
* pqDropConnection will drop the buffers; otherwise, during a
672+
* reconnection we'd be at risk of using undersized buffers during
673+
* negotiation.)
674+
*/
675+
free(PqGSSSendBuffer);
676+
free(PqGSSRecvBuffer);
677+
free(PqGSSResultBuffer);
678+
PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
679+
PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
680+
PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
681+
if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
682+
{
683+
appendPQExpBufferStr(&conn->errorMessage,
684+
libpq_gettext("out of memory\n"));
685+
return PGRES_POLLING_FAILED;
686+
}
687+
PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
688+
PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
689+
658690
/*
659691
* Determine the max packet size which will fit in our buffer, after
660692
* accounting for the length. pg_GSS_write will need this.
661693
*/
662694
major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
663-
PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
695+
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
664696
&PqGSSMaxPktSize);
665697

666698
if (GSS_ERROR(major))
@@ -674,10 +706,12 @@ pqsecure_open_gss(PGconn *conn)
674706
}
675707

676708
/* Must have output.length > 0 */
677-
if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
709+
if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
678710
{
679-
pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
680-
conn, major, minor);
711+
appendPQExpBuffer(&conn->errorMessage,
712+
libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
713+
(size_t) output.length,
714+
PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
681715
gss_release_buffer(&minor, &output);
682716
return PGRES_POLLING_FAILED;
683717
}

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgres/postgres/commit/39b1d190714a11f98cb35916a7ceb1a998e24062

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy