Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * be-secure-gssapi.c
4 : : * GSSAPI encryption support
5 : : *
6 : : * Portions Copyright (c) 2018-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/backend/libpq/be-secure-gssapi.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres.h"
15 : :
16 : : #include <unistd.h>
17 : :
18 : : #include "libpq/auth.h"
19 : : #include "libpq/be-gssapi-common.h"
20 : : #include "libpq/libpq.h"
21 : : #include "miscadmin.h"
22 : : #include "pgstat.h"
23 : : #include "port/pg_bswap.h"
24 : : #include "storage/latch.h"
25 : : #include "utils/injection_point.h"
26 : : #include "utils/memutils.h"
27 : : #include "utils/wait_event.h"
28 : :
29 : :
30 : : /*
31 : : * Handle the encryption/decryption of data using GSSAPI.
32 : : *
33 : : * In the encrypted data stream on the wire, we break up the data
34 : : * into packets where each packet starts with a uint32-size length
35 : : * word (in network byte order), then encrypted data of that length
36 : : * immediately following. Decryption yields the same data stream
37 : : * that would appear when not using encryption.
38 : : *
39 : : * Encrypted data typically ends up being larger than the same data
40 : : * unencrypted, so we use fixed-size buffers for handling the
41 : : * encryption/decryption which are larger than PQComm's buffer will
42 : : * typically be to minimize the times where we have to make multiple
43 : : * packets (and therefore multiple recv/send calls for a single
44 : : * read/write call to us).
45 : : *
46 : : * NOTE: The client and server have to agree on the max packet size,
47 : : * because we have to pass an entire packet to GSSAPI at a time and we
48 : : * don't want the other side to send arbitrarily huge packets as we
49 : : * would have to allocate memory for them to then pass them to GSSAPI.
50 : : *
51 : : * Therefore, this #define is effectively part of the protocol
52 : : * spec and can't ever be changed.
53 : : */
54 : : #define PQ_GSS_MAX_PACKET_SIZE 16384 /* includes uint32 header word */
55 : :
56 : : /*
57 : : * However, during the authentication exchange we must cope with whatever
58 : : * message size the GSSAPI library wants to send (because our protocol
59 : : * doesn't support splitting those messages). Depending on configuration
60 : : * those messages might be as much as 64kB.
61 : : */
62 : : #define PQ_GSS_AUTH_BUFFER_SIZE 65536 /* includes uint32 header word */
63 : :
64 : : /*
65 : : * Since we manage at most one GSS-encrypted connection per backend,
66 : : * we can just keep all this state in static variables. The char *
67 : : * variables point to buffers that are allocated once and re-used.
68 : : */
69 : : static char *PqGSSSendBuffer; /* Encrypted data waiting to be sent */
70 : : static int PqGSSSendLength; /* End of data available in PqGSSSendBuffer */
71 : : static int PqGSSSendNext; /* Next index to send a byte from
72 : : * PqGSSSendBuffer */
73 : : static int PqGSSSendConsumed; /* Number of source bytes encrypted but not
74 : : * yet reported as sent */
75 : :
76 : : static char *PqGSSRecvBuffer; /* Received, encrypted data */
77 : : static int PqGSSRecvLength; /* End of data available in PqGSSRecvBuffer */
78 : :
79 : : static char *PqGSSResultBuffer; /* Decryption of data in gss_RecvBuffer */
80 : : static int PqGSSResultLength; /* End of data available in PqGSSResultBuffer */
81 : : static int PqGSSResultNext; /* Next index to read a byte from
82 : : * PqGSSResultBuffer */
83 : :
84 : : static uint32 PqGSSMaxPktSize; /* Maximum size we can encrypt and fit the
85 : : * results into our output buffer */
86 : :
87 : :
88 : : /*
89 : : * Attempt to write len bytes of data from ptr to a GSSAPI-encrypted connection.
90 : : *
91 : : * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
92 : : * transport negotiation is complete).
93 : : *
94 : : * On success, returns the number of data bytes consumed (possibly less than
95 : : * len). On failure, returns -1 with errno set appropriately. For retryable
96 : : * errors, caller should call again (passing the same or more data) once the
97 : : * socket is ready.
98 : : *
99 : : * Dealing with fatal errors here is a bit tricky: we can't invoke elog(FATAL)
100 : : * since it would try to write to the client, probably resulting in infinite
101 : : * recursion. Instead, use elog(COMMERROR) to log extra info about the
102 : : * failure if necessary, and then return an errno indicating connection loss.
103 : : */
104 : : ssize_t
399 peter@eisentraut.org 105 :CBC 415 : be_gssapi_write(Port *port, const void *ptr, size_t len)
106 : : {
107 : : OM_uint32 major,
108 : : minor;
109 : : gss_buffer_desc input,
110 : : output;
111 : : size_t bytes_to_encrypt;
112 : : size_t bytes_encrypted;
2255 tgl@sss.pgh.pa.us 113 : 415 : gss_ctx_id_t gctx = port->gss->ctx;
114 : :
115 : : /*
116 : : * When we get a retryable failure, we must not tell the caller we have
117 : : * successfully transmitted everything, else it won't retry. For
118 : : * simplicity, we claim we haven't transmitted anything until we have
119 : : * successfully transmitted all "len" bytes. Between calls, the amount of
120 : : * the current input data that's already been encrypted and placed into
121 : : * PqGSSSendBuffer (and perhaps transmitted) is remembered in
122 : : * PqGSSSendConsumed. On a retry, the caller *must* be sending that data
123 : : * again, so if it offers a len less than that, something is wrong.
124 : : *
125 : : * Note: it may seem attractive to report partial write completion once
126 : : * we've successfully sent any encrypted packets. However, doing that
127 : : * expands the state space of this processing and has been responsible for
128 : : * bugs in the past (cf. commit d053a879b). We won't save much,
129 : : * typically, by letting callers discard data early, so don't risk it.
130 : : */
131 [ - + ]: 415 : if (len < PqGSSSendConsumed)
132 : : {
1903 tgl@sss.pgh.pa.us 133 [ # # ]:UBC 0 : elog(COMMERROR, "GSSAPI caller failed to retransmit all data needing to be retried");
134 : 0 : errno = ECONNRESET;
135 : 0 : return -1;
136 : : }
137 : :
138 : : /* Discount whatever source data we already encrypted. */
2255 tgl@sss.pgh.pa.us 139 :CBC 415 : bytes_to_encrypt = len - PqGSSSendConsumed;
140 : 415 : bytes_encrypted = PqGSSSendConsumed;
141 : :
142 : : /*
143 : : * Loop through encrypting data and sending it out until it's all done or
144 : : * secure_raw_write() complains (which would likely mean that the socket
145 : : * is non-blocking and the requested send() would block, or there was some
146 : : * kind of actual error).
147 : : */
148 [ + + + - ]: 830 : while (bytes_to_encrypt || PqGSSSendLength)
149 : : {
2369 peter@eisentraut.org 150 : 830 : int conf_state = 0;
151 : : uint32 netlen;
152 : :
153 : : /*
154 : : * Check if we have data in the encrypted output buffer that needs to
155 : : * be sent (possibly left over from a previous call), and if so, try
156 : : * to send it. If we aren't able to, return that fact back up to the
157 : : * caller.
158 : : */
2255 tgl@sss.pgh.pa.us 159 [ + + ]: 830 : if (PqGSSSendLength)
160 : : {
161 : : ssize_t ret;
162 : 415 : ssize_t amount = PqGSSSendLength - PqGSSSendNext;
163 : :
164 : 415 : ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendNext, amount);
2538 sfrost@snowman.net 165 [ - + ]: 415 : if (ret <= 0)
2255 tgl@sss.pgh.pa.us 166 :UBC 0 : return ret;
167 : :
168 : : /*
169 : : * Check if this was a partial write, and if so, move forward that
170 : : * far in our buffer and try again.
171 : : */
843 tgl@sss.pgh.pa.us 172 [ - + ]:CBC 415 : if (ret < amount)
173 : : {
2255 tgl@sss.pgh.pa.us 174 :UBC 0 : PqGSSSendNext += ret;
2538 sfrost@snowman.net 175 : 0 : continue;
176 : : }
177 : :
178 : : /* We've successfully sent whatever data was in the buffer. */
843 tgl@sss.pgh.pa.us 179 :CBC 415 : PqGSSSendLength = PqGSSSendNext = 0;
180 : : }
181 : :
182 : : /*
183 : : * Check if there are any bytes left to encrypt. If not, we're done.
184 : : */
2538 sfrost@snowman.net 185 [ + + ]: 830 : if (!bytes_to_encrypt)
2255 tgl@sss.pgh.pa.us 186 : 415 : break;
187 : :
188 : : /*
189 : : * Check how much we are being asked to send, if it's too much, then
190 : : * we will have to loop and possibly be called multiple times to get
191 : : * through all the data.
192 : : */
193 [ - + ]: 415 : if (bytes_to_encrypt > PqGSSMaxPktSize)
2255 tgl@sss.pgh.pa.us 194 :UBC 0 : input.length = PqGSSMaxPktSize;
195 : : else
2538 sfrost@snowman.net 196 :CBC 415 : input.length = bytes_to_encrypt;
197 : :
198 : 415 : input.value = (char *) ptr + bytes_encrypted;
199 : :
200 : 415 : output.value = NULL;
201 : 415 : output.length = 0;
202 : :
203 : : /*
204 : : * Create the next encrypted packet. Any failure here is considered a
205 : : * hard failure, so we return -1 even if some data has been sent.
206 : : */
2255 tgl@sss.pgh.pa.us 207 : 415 : major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
208 : : &input, &conf_state, &output);
2538 sfrost@snowman.net 209 [ - + ]: 415 : if (major != GSS_S_COMPLETE)
210 : : {
1903 tgl@sss.pgh.pa.us 211 :UBC 0 : pg_GSS_error(_("GSSAPI wrap error"), major, minor);
212 : 0 : errno = ECONNRESET;
213 : 0 : return -1;
214 : : }
2369 peter@eisentraut.org 215 [ - + ]:CBC 415 : if (conf_state == 0)
216 : : {
1903 tgl@sss.pgh.pa.us 217 [ # # ]:UBC 0 : ereport(COMMERROR,
218 : : (errmsg("outgoing GSSAPI message would not use confidentiality")));
219 : 0 : errno = ECONNRESET;
220 : 0 : return -1;
221 : : }
289 tgl@sss.pgh.pa.us 222 [ - + ]:CBC 415 : if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
223 : : {
1903 tgl@sss.pgh.pa.us 224 [ # # ]:UBC 0 : ereport(COMMERROR,
225 : : (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
226 : : (size_t) output.length,
227 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
228 : 0 : errno = ECONNRESET;
229 : 0 : return -1;
230 : : }
231 : :
2538 sfrost@snowman.net 232 :CBC 415 : bytes_encrypted += input.length;
233 : 415 : bytes_to_encrypt -= input.length;
2255 tgl@sss.pgh.pa.us 234 : 415 : PqGSSSendConsumed += input.length;
235 : :
236 : : /* 4 network-order bytes of length, then payload */
1977 michael@paquier.xyz 237 : 415 : netlen = pg_hton32(output.length);
2255 tgl@sss.pgh.pa.us 238 : 415 : memcpy(PqGSSSendBuffer + PqGSSSendLength, &netlen, sizeof(uint32));
239 : 415 : PqGSSSendLength += sizeof(uint32);
240 : :
241 : 415 : memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
242 : 415 : PqGSSSendLength += output.length;
243 : :
244 : : /* Release buffer storage allocated by GSSAPI */
2140 245 : 415 : gss_release_buffer(&minor, &output);
246 : : }
247 : :
248 : : /* If we get here, our counters should all match up. */
843 249 [ - + ]: 415 : Assert(len == PqGSSSendConsumed);
250 [ - + ]: 415 : Assert(len == bytes_encrypted);
251 : :
252 : : /* We're reporting all the data as sent, so reset PqGSSSendConsumed. */
253 : 415 : PqGSSSendConsumed = 0;
254 : :
255 : 415 : return bytes_encrypted;
256 : : }
257 : :
258 : : /*
259 : : * Read up to len bytes of data into ptr from a GSSAPI-encrypted connection.
260 : : *
261 : : * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
262 : : * transport negotiation is complete).
263 : : *
264 : : * Returns the number of data bytes read, or on failure, returns -1
265 : : * with errno set appropriately. For retryable errors, caller should call
266 : : * again once the socket is ready.
267 : : *
268 : : * We treat fatal errors the same as in be_gssapi_write(), even though the
269 : : * argument about infinite recursion doesn't apply here.
270 : : */
271 : : ssize_t
2538 sfrost@snowman.net 272 : 750 : be_gssapi_read(Port *port, void *ptr, size_t len)
273 : : {
274 : : OM_uint32 major,
275 : : minor;
276 : : gss_buffer_desc input,
277 : : output;
278 : : ssize_t ret;
279 : 750 : size_t bytes_returned = 0;
2255 tgl@sss.pgh.pa.us 280 : 750 : gss_ctx_id_t gctx = port->gss->ctx;
281 : :
282 : : /*
283 : : * The plan here is to read one incoming encrypted packet into
284 : : * PqGSSRecvBuffer, decrypt it into PqGSSResultBuffer, and then dole out
285 : : * data from there to the caller. When we exhaust the current input
286 : : * packet, read another.
287 : : */
288 [ + - ]: 1117 : while (bytes_returned < len)
289 : : {
290 : 1117 : int conf_state = 0;
291 : :
292 : : /* Check if we have data in our buffer that we can return immediately */
293 [ + + ]: 1117 : if (PqGSSResultNext < PqGSSResultLength)
294 : : {
295 : 438 : size_t bytes_in_buffer = PqGSSResultLength - PqGSSResultNext;
296 : 438 : size_t bytes_to_copy = Min(bytes_in_buffer, len - bytes_returned);
297 : :
298 : : /*
299 : : * Copy the data from our result buffer into the caller's buffer,
300 : : * at the point where we last left off filling their buffer.
301 : : */
302 : 438 : memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultNext, bytes_to_copy);
303 : 438 : PqGSSResultNext += bytes_to_copy;
2538 sfrost@snowman.net 304 : 438 : bytes_returned += bytes_to_copy;
305 : :
306 : : /*
307 : : * At this point, we've either filled the caller's buffer or
308 : : * emptied our result buffer. Either way, return to caller. In
309 : : * the second case, we could try to read another encrypted packet,
310 : : * but the odds are good that there isn't one available. (If this
311 : : * isn't true, we chose too small a max packet size.) In any
312 : : * case, there's no harm letting the caller process the data we've
313 : : * already returned.
314 : : */
2255 tgl@sss.pgh.pa.us 315 : 438 : break;
316 : : }
317 : :
318 : : /* Result buffer is empty, so reset buffer pointers */
319 : 679 : PqGSSResultLength = PqGSSResultNext = 0;
320 : :
321 : : /*
322 : : * Because we chose above to return immediately as soon as we emit
323 : : * some data, bytes_returned must be zero at this point. Therefore
324 : : * the failure exits below can just return -1 without worrying about
325 : : * whether we already emitted some data.
326 : : */
327 [ - + ]: 679 : Assert(bytes_returned == 0);
328 : :
329 : : /*
330 : : * At this point, our result buffer is empty with more bytes being
331 : : * requested to be read. We are now ready to load the next packet and
332 : : * decrypt it (entirely) into our result buffer.
333 : : */
334 : :
335 : : /* Collect the length if we haven't already */
2538 sfrost@snowman.net 336 [ + + ]: 679 : if (PqGSSRecvLength < sizeof(uint32))
337 : : {
338 : 675 : ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength,
339 : : sizeof(uint32) - PqGSSRecvLength);
340 : :
341 : : /* If ret <= 0, secure_raw_read already set the correct errno */
2255 tgl@sss.pgh.pa.us 342 [ + + ]: 675 : if (ret <= 0)
343 : 312 : return ret;
344 : :
2538 sfrost@snowman.net 345 : 367 : PqGSSRecvLength += ret;
346 : :
347 : : /* If we still haven't got the length, return to the caller */
348 [ - + ]: 367 : if (PqGSSRecvLength < sizeof(uint32))
349 : : {
2255 tgl@sss.pgh.pa.us 350 :UBC 0 : errno = EWOULDBLOCK;
351 : 0 : return -1;
352 : : }
353 : : }
354 : :
355 : : /* Decode the packet length and check for overlength packet */
1977 michael@paquier.xyz 356 :CBC 371 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
357 : :
289 tgl@sss.pgh.pa.us 358 [ - + ]: 371 : if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
359 : : {
1903 tgl@sss.pgh.pa.us 360 [ # # ]:UBC 0 : ereport(COMMERROR,
361 : : (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
362 : : (size_t) input.length,
363 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))));
364 : 0 : errno = ECONNRESET;
365 : 0 : return -1;
366 : : }
367 : :
368 : : /*
369 : : * Read as much of the packet as we are able to on this call into
370 : : * wherever we left off from the last time we were called.
371 : : */
2538 sfrost@snowman.net 372 :CBC 371 : ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength,
373 : 371 : input.length - (PqGSSRecvLength - sizeof(uint32)));
374 : : /* If ret <= 0, secure_raw_read already set the correct errno */
2255 tgl@sss.pgh.pa.us 375 [ - + ]: 371 : if (ret <= 0)
2255 tgl@sss.pgh.pa.us 376 :UBC 0 : return ret;
377 : :
2538 sfrost@snowman.net 378 :CBC 371 : PqGSSRecvLength += ret;
379 : :
380 : : /* If we don't yet have the whole packet, return to the caller */
381 [ + + ]: 371 : if (PqGSSRecvLength - sizeof(uint32) < input.length)
382 : : {
2255 tgl@sss.pgh.pa.us 383 : 4 : errno = EWOULDBLOCK;
384 : 4 : return -1;
385 : : }
386 : :
387 : : /*
388 : : * We now have the full packet and we can perform the decryption and
389 : : * refill our result buffer, then loop back up to pass data back to
390 : : * the caller.
391 : : */
2538 sfrost@snowman.net 392 : 367 : output.value = NULL;
393 : 367 : output.length = 0;
394 : 367 : input.value = PqGSSRecvBuffer + sizeof(uint32);
395 : :
2255 tgl@sss.pgh.pa.us 396 : 367 : major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
2538 sfrost@snowman.net 397 [ - + ]: 367 : if (major != GSS_S_COMPLETE)
398 : : {
1903 tgl@sss.pgh.pa.us 399 :UBC 0 : pg_GSS_error(_("GSSAPI unwrap error"), major, minor);
400 : 0 : errno = ECONNRESET;
401 : 0 : return -1;
402 : : }
2369 peter@eisentraut.org 403 [ - + ]:CBC 367 : if (conf_state == 0)
404 : : {
1903 tgl@sss.pgh.pa.us 405 [ # # ]:UBC 0 : ereport(COMMERROR,
406 : : (errmsg("incoming GSSAPI message did not use confidentiality")));
407 : 0 : errno = ECONNRESET;
408 : 0 : return -1;
409 : : }
410 : :
2538 sfrost@snowman.net 411 :CBC 367 : memcpy(PqGSSResultBuffer, output.value, output.length);
412 : 367 : PqGSSResultLength = output.length;
413 : :
414 : : /* Our receive buffer is now empty, reset it */
415 : 367 : PqGSSRecvLength = 0;
416 : :
417 : : /* Release buffer storage allocated by GSSAPI */
418 : 367 : gss_release_buffer(&minor, &output);
419 : : }
420 : :
421 : 438 : return bytes_returned;
422 : : }
423 : :
424 : : /*
425 : : * Read the specified number of bytes off the wire, waiting using
426 : : * WaitLatchOrSocket if we would block.
427 : : *
428 : : * Results are read into PqGSSRecvBuffer.
429 : : *
430 : : * Will always return either -1, to indicate a permanent error, or len.
431 : : */
432 : : static ssize_t
433 : 242 : read_or_wait(Port *port, ssize_t len)
434 : : {
435 : : ssize_t ret;
436 : :
437 : : /*
438 : : * Keep going until we either read in everything we were asked to, or we
439 : : * error out.
440 : : */
2255 tgl@sss.pgh.pa.us 441 [ + + ]: 605 : while (PqGSSRecvLength < len)
442 : : {
2538 sfrost@snowman.net 443 : 363 : ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength, len - PqGSSRecvLength);
444 : :
445 : : /*
446 : : * If we got back an error and it wasn't just
447 : : * EWOULDBLOCK/EAGAIN/EINTR, then give up.
448 : : */
2255 tgl@sss.pgh.pa.us 449 [ + + ]: 363 : if (ret < 0 &&
450 [ - + - - : 121 : !(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
- - ]
2538 sfrost@snowman.net 451 :UBC 0 : return -1;
452 : :
453 : : /*
454 : : * Ok, we got back either a positive value, zero, or a negative result
455 : : * indicating we should retry.
456 : : *
457 : : * If it was zero or negative, then we wait on the socket to be
458 : : * readable again.
459 : : */
2538 sfrost@snowman.net 460 [ + + ]:CBC 363 : if (ret <= 0)
461 : : {
526 heikki.linnakangas@i 462 : 121 : WaitLatchOrSocket(NULL,
463 : : WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
464 : : port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
465 : :
466 : : /*
467 : : * If we got back zero bytes, and then waited on the socket to be
468 : : * readable and got back zero bytes on a second read, then this is
469 : : * EOF and the client hung up on us.
470 : : *
471 : : * If we did get data here, then we can just fall through and
472 : : * handle it just as if we got data the first time.
473 : : *
474 : : * Otherwise loop back to the top and try again.
475 : : */
2538 sfrost@snowman.net 476 [ - + ]: 121 : if (ret == 0)
477 : : {
2538 sfrost@snowman.net 478 :UBC 0 : ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength, len - PqGSSRecvLength);
479 [ # # ]: 0 : if (ret == 0)
480 : 0 : return -1;
481 : : }
2255 tgl@sss.pgh.pa.us 482 [ + - ]:CBC 121 : if (ret < 0)
2538 sfrost@snowman.net 483 : 121 : continue;
484 : : }
485 : :
486 : 242 : PqGSSRecvLength += ret;
487 : : }
488 : :
489 : 242 : return len;
490 : : }
491 : :
492 : : /*
493 : : * Start up a GSSAPI-encrypted connection. This performs GSSAPI
494 : : * authentication; after this function completes, it is safe to call
495 : : * be_gssapi_read and be_gssapi_write. Returns -1 and logs on failure;
496 : : * otherwise, returns 0 and marks the connection as ready for GSSAPI
497 : : * encryption.
498 : : *
499 : : * Note that unlike the be_gssapi_read/be_gssapi_write functions, this
500 : : * function WILL block on the socket to be ready for read/write (using
501 : : * WaitLatchOrSocket) as appropriate while establishing the GSSAPI
502 : : * session.
503 : : */
504 : : ssize_t
505 : 122 : secure_open_gssapi(Port *port)
506 : : {
507 : 122 : bool complete_next = false;
508 : : OM_uint32 major,
509 : : minor;
510 : : gss_cred_id_t delegated_creds;
511 : :
309 michael@paquier.xyz 512 : 122 : INJECTION_POINT("backend-gssapi-startup", NULL);
513 : :
514 : : /*
515 : : * Allocate subsidiary Port data for GSSAPI operations.
516 : : */
1903 tgl@sss.pgh.pa.us 517 : 121 : port->gss = (pg_gssinfo *)
518 : 121 : MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
519 : :
1067 sfrost@snowman.net 520 : 121 : delegated_creds = GSS_C_NO_CREDENTIAL;
521 : 121 : port->gss->delegated_creds = false;
522 : :
523 : : /*
524 : : * Allocate buffers and initialize state variables. By malloc'ing the
525 : : * buffers at this point, we avoid wasting static data space in processes
526 : : * that will never use them, and we ensure that the buffers are
527 : : * sufficiently aligned for the length-word accesses that we do in some
528 : : * places in this file.
529 : : *
530 : : * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
531 : : * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
532 : : */
289 tgl@sss.pgh.pa.us 533 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
534 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
535 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
2255 536 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
2255 tgl@sss.pgh.pa.us 537 [ # # ]:UBC 0 : ereport(FATAL,
538 : : (errcode(ERRCODE_OUT_OF_MEMORY),
539 : : errmsg("out of memory")));
2255 tgl@sss.pgh.pa.us 540 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
541 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
542 : :
543 : : /*
544 : : * Use the configured keytab, if there is one. As we now require MIT
545 : : * Kerberos, we might consider using the credential store extensions in
546 : : * the future instead of the environment variable.
547 : : */
1901 548 [ - + - + ]: 121 : if (pg_krb_server_keyfile != NULL && pg_krb_server_keyfile[0] != '\0')
549 : : {
550 [ + - ]: 121 : if (setenv("KRB5_KTNAME", pg_krb_server_keyfile, 1) != 0)
551 : : {
552 : : /* The only likely failure cause is OOM, so use that errcode */
1901 tgl@sss.pgh.pa.us 553 [ # # ]:UBC 0 : ereport(FATAL,
554 : : (errcode(ERRCODE_OUT_OF_MEMORY),
555 : : errmsg("could not set environment: %m")));
556 : : }
557 : : }
558 : :
559 : : while (true)
2538 sfrost@snowman.net 560 : 0 : {
561 : : ssize_t ret;
562 : : gss_buffer_desc input,
2538 sfrost@snowman.net 563 :CBC 121 : output = GSS_C_EMPTY_BUFFER;
564 : :
565 : : /*
566 : : * The client always sends first, so try to go ahead and read the
567 : : * length and wait on the socket to be readable again if that fails.
568 : : */
569 : 121 : ret = read_or_wait(port, sizeof(uint32));
570 [ - + ]: 121 : if (ret < 0)
2538 sfrost@snowman.net 571 :UBC 0 : return ret;
572 : :
573 : : /*
574 : : * Get the length for this packet from the length header.
575 : : */
1977 michael@paquier.xyz 576 :CBC 121 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
577 : :
578 : : /* Done with the length, reset our buffer */
2538 sfrost@snowman.net 579 : 121 : PqGSSRecvLength = 0;
580 : :
581 : : /*
582 : : * During initialization, packets are always fully consumed and
583 : : * shouldn't ever be over PQ_GSS_AUTH_BUFFER_SIZE in total length.
584 : : *
585 : : * Verify on our side that the client doesn't do something funny.
586 : : */
289 tgl@sss.pgh.pa.us 587 [ - + ]: 121 : if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
588 : : {
1903 tgl@sss.pgh.pa.us 589 [ # # ]:UBC 0 : ereport(COMMERROR,
590 : : (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
591 : : (size_t) input.length,
592 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
593 : 0 : return -1;
594 : : }
595 : :
596 : : /*
597 : : * Get the rest of the packet so we can pass it to GSSAPI to accept
598 : : * the context.
599 : : */
2538 sfrost@snowman.net 600 :CBC 121 : ret = read_or_wait(port, input.length);
601 [ - + ]: 121 : if (ret < 0)
2538 sfrost@snowman.net 602 :UBC 0 : return ret;
603 : :
2538 sfrost@snowman.net 604 :CBC 121 : input.value = PqGSSRecvBuffer;
605 : :
606 : : /* Process incoming data. (The client sends first.) */
607 : 121 : major = gss_accept_sec_context(&minor, &port->gss->ctx,
608 : : GSS_C_NO_CREDENTIAL, &input,
609 : : GSS_C_NO_CHANNEL_BINDINGS,
610 : 121 : &port->gss->name, NULL, &output, NULL,
1030 bruce@momjian.us 611 [ + + ]: 121 : NULL, pg_gss_accept_delegation ? &delegated_creds : NULL);
612 : :
2538 sfrost@snowman.net 613 [ - + ]: 121 : if (GSS_ERROR(major))
614 : : {
1903 tgl@sss.pgh.pa.us 615 :UBC 0 : pg_GSS_error(_("could not accept GSSAPI security context"),
616 : : major, minor);
2538 sfrost@snowman.net 617 : 0 : gss_release_buffer(&minor, &output);
618 : 0 : return -1;
619 : : }
2538 sfrost@snowman.net 620 [ + - ]:CBC 121 : else if (!(major & GSS_S_CONTINUE_NEEDED))
621 : : {
622 : : /*
623 : : * rfc2744 technically permits context negotiation to be complete
624 : : * both with and without a packet to be sent.
625 : : */
626 : 121 : complete_next = true;
627 : : }
628 : :
1067 629 [ + + ]: 121 : if (delegated_creds != GSS_C_NO_CREDENTIAL)
630 : : {
631 : 10 : pg_store_delegated_credential(delegated_creds);
632 : 10 : port->gss->delegated_creds = true;
633 : : }
634 : :
635 : : /* Done handling the incoming packet, reset our buffer */
2538 636 : 121 : PqGSSRecvLength = 0;
637 : :
638 : : /*
639 : : * Check if we have data to send and, if we do, make sure to send it
640 : : * all
641 : : */
2255 tgl@sss.pgh.pa.us 642 [ + - ]: 121 : if (output.length > 0)
643 : : {
1977 michael@paquier.xyz 644 : 121 : uint32 netlen = pg_hton32(output.length);
645 : :
289 tgl@sss.pgh.pa.us 646 [ - + ]: 121 : if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
647 : : {
1903 tgl@sss.pgh.pa.us 648 [ # # ]:UBC 0 : ereport(COMMERROR,
649 : : (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
650 : : (size_t) output.length,
651 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))));
652 : 0 : gss_release_buffer(&minor, &output);
653 : 0 : return -1;
654 : : }
655 : :
396 peter@eisentraut.org 656 :CBC 121 : memcpy(PqGSSSendBuffer, &netlen, sizeof(uint32));
2255 tgl@sss.pgh.pa.us 657 : 121 : PqGSSSendLength += sizeof(uint32);
658 : :
659 : 121 : memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
660 : 121 : PqGSSSendLength += output.length;
661 : :
662 : : /* we don't bother with PqGSSSendConsumed here */
663 : :
664 [ + + ]: 242 : while (PqGSSSendNext < PqGSSSendLength)
665 : : {
666 : 121 : ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendNext,
667 : 121 : PqGSSSendLength - PqGSSSendNext);
668 : :
669 : : /*
670 : : * If we got back an error and it wasn't just
671 : : * EWOULDBLOCK/EAGAIN/EINTR, then give up.
672 : : */
673 [ - + ]: 121 : if (ret < 0 &&
2255 tgl@sss.pgh.pa.us 674 [ # # # # :UBC 0 : !(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
# # ]
675 : : {
2140 676 : 0 : gss_release_buffer(&minor, &output);
2255 677 : 0 : return -1;
678 : : }
679 : :
680 : : /* Wait and retry if we couldn't write yet */
2538 sfrost@snowman.net 681 [ - + ]:CBC 121 : if (ret <= 0)
682 : : {
526 heikki.linnakangas@i 683 :UBC 0 : WaitLatchOrSocket(NULL,
684 : : WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
685 : : port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
2538 sfrost@snowman.net 686 : 0 : continue;
687 : : }
688 : :
2255 tgl@sss.pgh.pa.us 689 :CBC 121 : PqGSSSendNext += ret;
690 : : }
691 : :
692 : : /* Done sending the packet, reset our buffer */
693 : 121 : PqGSSSendLength = PqGSSSendNext = 0;
694 : :
2538 sfrost@snowman.net 695 : 121 : gss_release_buffer(&minor, &output);
696 : : }
697 : :
698 : : /*
699 : : * If we got back that the connection is finished being set up, now
700 : : * that we've sent the last packet, exit our loop.
701 : : */
702 [ + - ]: 121 : if (complete_next)
703 : 121 : break;
704 : : }
705 : :
706 : : /*
707 : : * Release the large authentication buffers and allocate the ones we want
708 : : * for normal operation.
709 : : */
289 tgl@sss.pgh.pa.us 710 : 121 : free(PqGSSSendBuffer);
711 : 121 : free(PqGSSRecvBuffer);
712 : 121 : free(PqGSSResultBuffer);
713 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
714 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
715 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
716 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
289 tgl@sss.pgh.pa.us 717 [ # # ]:UBC 0 : ereport(FATAL,
718 : : (errcode(ERRCODE_OUT_OF_MEMORY),
719 : : errmsg("out of memory")));
289 tgl@sss.pgh.pa.us 720 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
721 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
722 : :
723 : : /*
724 : : * Determine the max packet size which will fit in our buffer, after
725 : : * accounting for the length. be_gssapi_write will need this.
726 : : */
2538 sfrost@snowman.net 727 : 121 : major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
728 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
729 : : &PqGSSMaxPktSize);
730 : :
731 [ - + ]: 121 : if (GSS_ERROR(major))
732 : : {
1903 tgl@sss.pgh.pa.us 733 :UBC 0 : pg_GSS_error(_("GSSAPI size check error"), major, minor);
734 : 0 : return -1;
735 : : }
736 : :
2538 sfrost@snowman.net 737 :CBC 121 : port->gss->enc = true;
738 : :
739 : 121 : return 0;
740 : : }
741 : :
742 : : /*
743 : : * Return if GSSAPI authentication was used on this connection.
744 : : */
745 : : bool
746 : 180 : be_gssapi_get_auth(Port *port)
747 : : {
748 [ + - - + ]: 180 : if (!port || !port->gss)
2538 sfrost@snowman.net 749 :UBC 0 : return false;
750 : :
2538 sfrost@snowman.net 751 :CBC 180 : return port->gss->auth;
752 : : }
753 : :
754 : : /*
755 : : * Return if GSSAPI encryption is enabled and being used on this connection.
756 : : */
757 : : bool
758 : 180 : be_gssapi_get_enc(Port *port)
759 : : {
760 [ + - - + ]: 180 : if (!port || !port->gss)
2538 sfrost@snowman.net 761 :UBC 0 : return false;
762 : :
2538 sfrost@snowman.net 763 :CBC 180 : return port->gss->enc;
764 : : }
765 : :
766 : : /*
767 : : * Return the GSSAPI principal used for authentication on this connection
768 : : * (NULL if we did not perform GSSAPI authentication).
769 : : */
770 : : const char *
771 : 180 : be_gssapi_get_princ(Port *port)
772 : : {
1903 tgl@sss.pgh.pa.us 773 [ + - - + ]: 180 : if (!port || !port->gss)
2538 sfrost@snowman.net 774 :UBC 0 : return NULL;
775 : :
2538 sfrost@snowman.net 776 :CBC 180 : return port->gss->princ;
777 : : }
778 : :
779 : : /*
780 : : * Return if GSSAPI delegated credentials were included on this
781 : : * connection.
782 : : */
783 : : bool
1030 bruce@momjian.us 784 : 206 : be_gssapi_get_delegation(Port *port)
785 : : {
1067 sfrost@snowman.net 786 [ + - + + ]: 206 : if (!port || !port->gss)
1066 tgl@sss.pgh.pa.us 787 : 13 : return false;
788 : :
1067 sfrost@snowman.net 789 : 193 : return port->gss->delegated_creds;
790 : : }
|