Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * fe-secure-gssapi.c
4 : : * The front-end (client) encryption support for GSSAPI
5 : : *
6 : : * Portions Copyright (c) 2016-2025, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/interfaces/libpq/fe-secure-gssapi.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres_fe.h"
15 : :
16 : : #include "fe-gssapi-common.h"
17 : : #include "libpq-fe.h"
18 : : #include "libpq-int.h"
19 : : #include "port/pg_bswap.h"
20 : :
21 : :
22 : : /*
23 : : * Require encryption support, as well as mutual authentication and
24 : : * tamperproofing measures.
25 : : */
26 : : #define GSS_REQUIRED_FLAGS GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \
27 : : GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG
28 : :
29 : : /*
30 : : * Handle the encryption/decryption of data using GSSAPI.
31 : : *
32 : : * In the encrypted data stream on the wire, we break up the data
33 : : * into packets where each packet starts with a uint32-size length
34 : : * word (in network byte order), then encrypted data of that length
35 : : * immediately following. Decryption yields the same data stream
36 : : * that would appear when not using encryption.
37 : : *
38 : : * Encrypted data typically ends up being larger than the same data
39 : : * unencrypted, so we use fixed-size buffers for handling the
40 : : * encryption/decryption which are larger than PQComm's buffer will
41 : : * typically be to minimize the times where we have to make multiple
42 : : * packets (and therefore multiple recv/send calls for a single
43 : : * read/write call to us).
44 : : *
45 : : * NOTE: The client and server have to agree on the max packet size,
46 : : * because we have to pass an entire packet to GSSAPI at a time and we
47 : : * don't want the other side to send arbitrarily huge packets as we
48 : : * would have to allocate memory for them to then pass them to GSSAPI.
49 : : *
50 : : * Therefore, this #define is effectively part of the protocol
51 : : * spec and can't ever be changed.
52 : : */
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 */
62 : :
63 : : /*
64 : : * We need these state variables per-connection. To allow the functions
65 : : * in this file to look mostly like those in be-secure-gssapi.c, set up
66 : : * these macros.
67 : : */
68 : : #define PqGSSSendBuffer (conn->gss_SendBuffer)
69 : : #define PqGSSSendLength (conn->gss_SendLength)
70 : : #define PqGSSSendNext (conn->gss_SendNext)
71 : : #define PqGSSSendConsumed (conn->gss_SendConsumed)
72 : : #define PqGSSRecvBuffer (conn->gss_RecvBuffer)
73 : : #define PqGSSRecvLength (conn->gss_RecvLength)
74 : : #define PqGSSResultBuffer (conn->gss_ResultBuffer)
75 : : #define PqGSSResultLength (conn->gss_ResultLength)
76 : : #define PqGSSResultNext (conn->gss_ResultNext)
77 : : #define PqGSSMaxPktSize (conn->gss_MaxPktSize)
78 : :
79 : :
80 : : /*
81 : : * Attempt to write len bytes of data from ptr to a GSSAPI-encrypted connection.
82 : : *
83 : : * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
84 : : * transport negotiation is complete).
85 : : *
86 : : * On success, returns the number of data bytes consumed (possibly less than
87 : : * len). On failure, returns -1 with errno set appropriately. If the errno
88 : : * indicates a non-retryable error, a message is added to conn->errorMessage.
89 : : * For retryable errors, caller should call again (passing the same or more
90 : : * data) once the socket is ready.
91 : : */
92 : : ssize_t
2399 sfrost@snowman.net 93 :CBC 367 : pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
94 : : {
95 : : OM_uint32 major,
96 : : minor;
97 : : gss_buffer_desc input,
2116 tgl@sss.pgh.pa.us 98 : 367 : output = GSS_C_EMPTY_BUFFER;
2399 sfrost@snowman.net 99 : 367 : ssize_t ret = -1;
100 : : size_t bytes_to_encrypt;
101 : : size_t bytes_encrypted;
2116 tgl@sss.pgh.pa.us 102 : 367 : gss_ctx_id_t gctx = conn->gctx;
103 : :
104 : : /*
105 : : * When we get a retryable failure, we must not tell the caller we have
106 : : * successfully transmitted everything, else it won't retry. For
107 : : * simplicity, we claim we haven't transmitted anything until we have
108 : : * successfully transmitted all "len" bytes. Between calls, the amount of
109 : : * the current input data that's already been encrypted and placed into
110 : : * PqGSSSendBuffer (and perhaps transmitted) is remembered in
111 : : * PqGSSSendConsumed. On a retry, the caller *must* be sending that data
112 : : * again, so if it offers a len less than that, something is wrong.
113 : : *
114 : : * Note: it may seem attractive to report partial write completion once
115 : : * we've successfully sent any encrypted packets. However, doing that
116 : : * expands the state space of this processing and has been responsible for
117 : : * bugs in the past (cf. commit d053a879b). We won't save much,
118 : : * typically, by letting callers discard data early, so don't risk it.
119 : : */
120 [ - + ]: 367 : if (len < PqGSSSendConsumed)
121 : : {
1750 tgl@sss.pgh.pa.us 122 :UBC 0 : appendPQExpBufferStr(&conn->errorMessage,
123 : : "GSSAPI caller failed to retransmit all data needing to be retried\n");
22 124 : 0 : SOCK_ERRNO_SET(EINVAL);
2116 125 : 0 : return -1;
126 : : }
127 : :
128 : : /* Discount whatever source data we already encrypted. */
2116 tgl@sss.pgh.pa.us 129 :CBC 367 : bytes_to_encrypt = len - PqGSSSendConsumed;
130 : 367 : bytes_encrypted = PqGSSSendConsumed;
131 : :
132 : : /*
133 : : * Loop through encrypting data and sending it out until it's all done or
134 : : * pqsecure_raw_write() complains (which would likely mean that the socket
135 : : * is non-blocking and the requested send() would block, or there was some
136 : : * kind of actual error).
137 : : */
138 [ + + + - ]: 734 : while (bytes_to_encrypt || PqGSSSendLength)
139 : : {
2230 peter@eisentraut.org 140 : 734 : int conf_state = 0;
141 : : uint32 netlen;
142 : :
143 : : /*
144 : : * Check if we have data in the encrypted output buffer that needs to
145 : : * be sent (possibly left over from a previous call), and if so, try
146 : : * to send it. If we aren't able to, return that fact back up to the
147 : : * caller.
148 : : */
2116 tgl@sss.pgh.pa.us 149 [ + + ]: 734 : if (PqGSSSendLength)
150 : : {
151 : : ssize_t retval;
152 : 367 : ssize_t amount = PqGSSSendLength - PqGSSSendNext;
153 : :
1116 drowley@postgresql.o 154 : 367 : retval = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
155 [ - + ]: 367 : if (retval <= 0)
1116 drowley@postgresql.o 156 :UBC 0 : return retval;
157 : :
158 : : /*
159 : : * Check if this was a partial write, and if so, move forward that
160 : : * far in our buffer and try again.
161 : : */
704 tgl@sss.pgh.pa.us 162 [ - + ]:CBC 367 : if (retval < amount)
163 : : {
1116 drowley@postgresql.o 164 :UBC 0 : PqGSSSendNext += retval;
2399 sfrost@snowman.net 165 : 0 : continue;
166 : : }
167 : :
168 : : /* We've successfully sent whatever data was in the buffer. */
704 tgl@sss.pgh.pa.us 169 :CBC 367 : PqGSSSendLength = PqGSSSendNext = 0;
170 : : }
171 : :
172 : : /*
173 : : * Check if there are any bytes left to encrypt. If not, we're done.
174 : : */
2399 sfrost@snowman.net 175 [ + + ]: 734 : if (!bytes_to_encrypt)
2116 tgl@sss.pgh.pa.us 176 : 367 : break;
177 : :
178 : : /*
179 : : * Check how much we are being asked to send, if it's too much, then
180 : : * we will have to loop and possibly be called multiple times to get
181 : : * through all the data.
182 : : */
183 [ - + ]: 367 : if (bytes_to_encrypt > PqGSSMaxPktSize)
2116 tgl@sss.pgh.pa.us 184 :UBC 0 : input.length = PqGSSMaxPktSize;
185 : : else
2399 sfrost@snowman.net 186 :CBC 367 : input.length = bytes_to_encrypt;
187 : :
188 : 367 : input.value = (char *) ptr + bytes_encrypted;
189 : :
190 : 367 : output.value = NULL;
191 : 367 : output.length = 0;
192 : :
193 : : /*
194 : : * Create the next encrypted packet. Any failure here is considered a
195 : : * hard failure, so we return -1 even if some data has been sent.
196 : : */
2116 tgl@sss.pgh.pa.us 197 : 367 : major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
198 : : &input, &conf_state, &output);
2399 sfrost@snowman.net 199 [ - + ]: 367 : if (major != GSS_S_COMPLETE)
200 : : {
2399 sfrost@snowman.net 201 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn, major, minor);
22 tgl@sss.pgh.pa.us 202 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2399 sfrost@snowman.net 203 : 0 : goto cleanup;
204 : : }
205 : :
2116 tgl@sss.pgh.pa.us 206 [ - + ]:CBC 367 : if (conf_state == 0)
207 : : {
1077 peter@eisentraut.org 208 :UBC 0 : libpq_append_conn_error(conn, "outgoing GSSAPI message would not use confidentiality");
22 tgl@sss.pgh.pa.us 209 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2399 sfrost@snowman.net 210 : 0 : goto cleanup;
211 : : }
212 : :
150 tgl@sss.pgh.pa.us 213 [ - + ]:CBC 367 : if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
214 : : {
1077 peter@eisentraut.org 215 :UBC 0 : libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
892 tgl@sss.pgh.pa.us 216 : 0 : (size_t) output.length,
217 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
22 218 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2399 sfrost@snowman.net 219 : 0 : goto cleanup;
220 : : }
221 : :
2399 sfrost@snowman.net 222 :CBC 367 : bytes_encrypted += input.length;
223 : 367 : bytes_to_encrypt -= input.length;
2116 tgl@sss.pgh.pa.us 224 : 367 : PqGSSSendConsumed += input.length;
225 : :
226 : : /* 4 network-order bytes of length, then payload */
1838 michael@paquier.xyz 227 : 367 : netlen = pg_hton32(output.length);
2116 tgl@sss.pgh.pa.us 228 : 367 : memcpy(PqGSSSendBuffer + PqGSSSendLength, &netlen, sizeof(uint32));
229 : 367 : PqGSSSendLength += sizeof(uint32);
230 : :
231 : 367 : memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
232 : 367 : PqGSSSendLength += output.length;
233 : :
234 : : /* Release buffer storage allocated by GSSAPI */
2001 235 : 367 : gss_release_buffer(&minor, &output);
236 : : }
237 : :
238 : : /* If we get here, our counters should all match up. */
704 239 [ - + ]: 367 : Assert(len == PqGSSSendConsumed);
240 [ - + ]: 367 : Assert(len == bytes_encrypted);
241 : :
242 : : /* We're reporting all the data as sent, so reset PqGSSSendConsumed. */
243 : 367 : PqGSSSendConsumed = 0;
244 : :
245 : 367 : ret = bytes_encrypted;
246 : :
2399 sfrost@snowman.net 247 : 367 : cleanup:
248 : : /* Release GSSAPI buffer storage, if we didn't already */
249 [ - + ]: 367 : if (output.value != NULL)
2399 sfrost@snowman.net 250 :UBC 0 : gss_release_buffer(&minor, &output);
2399 sfrost@snowman.net 251 :CBC 367 : return ret;
252 : : }
253 : :
254 : : /*
255 : : * Read up to len bytes of data into ptr from a GSSAPI-encrypted connection.
256 : : *
257 : : * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
258 : : * transport negotiation is complete).
259 : : *
260 : : * Returns the number of data bytes read, or on failure, returns -1
261 : : * with errno set appropriately. If the errno indicates a non-retryable
262 : : * error, a message is added to conn->errorMessage. For retryable errors,
263 : : * caller should call again once the socket is ready.
264 : : */
265 : : ssize_t
266 : 498 : pg_GSS_read(PGconn *conn, void *ptr, size_t len)
267 : : {
268 : : OM_uint32 major,
269 : : minor;
270 : 498 : gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
271 : 498 : output = GSS_C_EMPTY_BUFFER;
272 : : ssize_t ret;
273 : 498 : size_t bytes_returned = 0;
2116 tgl@sss.pgh.pa.us 274 : 498 : gss_ctx_id_t gctx = conn->gctx;
275 : :
276 : : /*
277 : : * The plan here is to read one incoming encrypted packet into
278 : : * PqGSSRecvBuffer, decrypt it into PqGSSResultBuffer, and then dole out
279 : : * data from there to the caller. When we exhaust the current input
280 : : * packet, read another.
281 : : */
282 [ + - ]: 912 : while (bytes_returned < len)
283 : : {
2230 peter@eisentraut.org 284 : 912 : int conf_state = 0;
285 : :
286 : : /* Check if we have data in our buffer that we can return immediately */
2116 tgl@sss.pgh.pa.us 287 [ + + ]: 912 : if (PqGSSResultNext < PqGSSResultLength)
288 : : {
289 : 414 : size_t bytes_in_buffer = PqGSSResultLength - PqGSSResultNext;
290 : 414 : size_t bytes_to_copy = Min(bytes_in_buffer, len - bytes_returned);
291 : :
292 : : /*
293 : : * Copy the data from our result buffer into the caller's buffer,
294 : : * at the point where we last left off filling their buffer.
295 : : */
296 : 414 : memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultNext, bytes_to_copy);
297 : 414 : PqGSSResultNext += bytes_to_copy;
2399 sfrost@snowman.net 298 : 414 : bytes_returned += bytes_to_copy;
299 : :
300 : : /*
301 : : * At this point, we've either filled the caller's buffer or
302 : : * emptied our result buffer. Either way, return to caller. In
303 : : * the second case, we could try to read another encrypted packet,
304 : : * but the odds are good that there isn't one available. (If this
305 : : * isn't true, we chose too small a max packet size.) In any
306 : : * case, there's no harm letting the caller process the data we've
307 : : * already returned.
308 : : */
2116 tgl@sss.pgh.pa.us 309 : 414 : break;
310 : : }
311 : :
312 : : /* Result buffer is empty, so reset buffer pointers */
313 : 498 : PqGSSResultLength = PqGSSResultNext = 0;
314 : :
315 : : /*
316 : : * Because we chose above to return immediately as soon as we emit
317 : : * some data, bytes_returned must be zero at this point. Therefore
318 : : * the failure exits below can just return -1 without worrying about
319 : : * whether we already emitted some data.
320 : : */
321 [ - + ]: 498 : Assert(bytes_returned == 0);
322 : :
323 : : /*
324 : : * At this point, our result buffer is empty with more bytes being
325 : : * requested to be read. We are now ready to load the next packet and
326 : : * decrypt it (entirely) into our result buffer.
327 : : */
328 : :
329 : : /* Collect the length if we haven't already */
2399 sfrost@snowman.net 330 [ + - ]: 498 : if (PqGSSRecvLength < sizeof(uint32))
331 : : {
332 : 498 : ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
333 : 498 : sizeof(uint32) - PqGSSRecvLength);
334 : :
335 : : /* If ret <= 0, pqsecure_raw_read already set the correct errno */
2116 tgl@sss.pgh.pa.us 336 [ + + ]: 498 : if (ret <= 0)
337 : 84 : return ret;
338 : :
2399 sfrost@snowman.net 339 : 414 : PqGSSRecvLength += ret;
340 : :
341 : : /* If we still haven't got the length, return to the caller */
342 [ - + ]: 414 : if (PqGSSRecvLength < sizeof(uint32))
343 : : {
22 tgl@sss.pgh.pa.us 344 :UBC 0 : SOCK_ERRNO_SET(EWOULDBLOCK);
2116 345 : 0 : return -1;
346 : : }
347 : : }
348 : :
349 : : /* Decode the packet length and check for overlength packet */
1838 michael@paquier.xyz 350 :CBC 414 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
351 : :
150 tgl@sss.pgh.pa.us 352 [ - + ]: 414 : if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
353 : : {
1077 peter@eisentraut.org 354 :UBC 0 : libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
892 tgl@sss.pgh.pa.us 355 : 0 : (size_t) input.length,
356 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
22 357 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2116 358 : 0 : return -1;
359 : : }
360 : :
361 : : /*
362 : : * Read as much of the packet as we are able to on this call into
363 : : * wherever we left off from the last time we were called.
364 : : */
2399 sfrost@snowman.net 365 :CBC 414 : ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
366 : 414 : input.length - (PqGSSRecvLength - sizeof(uint32)));
367 : : /* If ret <= 0, pqsecure_raw_read already set the correct errno */
2116 tgl@sss.pgh.pa.us 368 [ - + ]: 414 : if (ret <= 0)
2116 tgl@sss.pgh.pa.us 369 :UBC 0 : return ret;
370 : :
2399 sfrost@snowman.net 371 :CBC 414 : PqGSSRecvLength += ret;
372 : :
373 : : /* If we don't yet have the whole packet, return to the caller */
374 [ - + ]: 414 : if (PqGSSRecvLength - sizeof(uint32) < input.length)
375 : : {
22 tgl@sss.pgh.pa.us 376 :UBC 0 : SOCK_ERRNO_SET(EWOULDBLOCK);
2116 377 : 0 : return -1;
378 : : }
379 : :
380 : : /*
381 : : * We now have the full packet and we can perform the decryption and
382 : : * refill our result buffer, then loop back up to pass data back to
383 : : * the caller. Note that error exits below here must take care of
384 : : * releasing the gss output buffer.
385 : : */
2399 sfrost@snowman.net 386 :CBC 414 : output.value = NULL;
387 : 414 : output.length = 0;
388 : 414 : input.value = PqGSSRecvBuffer + sizeof(uint32);
389 : :
2116 tgl@sss.pgh.pa.us 390 : 414 : major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
2399 sfrost@snowman.net 391 [ - + ]: 414 : if (major != GSS_S_COMPLETE)
392 : : {
2399 sfrost@snowman.net 393 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
394 : : major, minor);
395 : 0 : ret = -1;
22 tgl@sss.pgh.pa.us 396 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2399 sfrost@snowman.net 397 : 0 : goto cleanup;
398 : : }
399 : :
2116 tgl@sss.pgh.pa.us 400 [ - + ]:CBC 414 : if (conf_state == 0)
401 : : {
1077 peter@eisentraut.org 402 :UBC 0 : libpq_append_conn_error(conn, "incoming GSSAPI message did not use confidentiality");
2399 sfrost@snowman.net 403 : 0 : ret = -1;
22 tgl@sss.pgh.pa.us 404 : 0 : SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
2399 sfrost@snowman.net 405 : 0 : goto cleanup;
406 : : }
407 : :
2399 sfrost@snowman.net 408 :CBC 414 : memcpy(PqGSSResultBuffer, output.value, output.length);
409 : 414 : PqGSSResultLength = output.length;
410 : :
411 : : /* Our receive buffer is now empty, reset it */
2116 tgl@sss.pgh.pa.us 412 : 414 : PqGSSRecvLength = 0;
413 : :
414 : : /* Release buffer storage allocated by GSSAPI */
2399 sfrost@snowman.net 415 : 414 : gss_release_buffer(&minor, &output);
416 : : }
417 : :
418 : 414 : ret = bytes_returned;
419 : :
420 : 414 : cleanup:
421 : : /* Release GSSAPI buffer storage, if we didn't already */
422 [ - + ]: 414 : if (output.value != NULL)
2399 sfrost@snowman.net 423 :UBC 0 : gss_release_buffer(&minor, &output);
2399 sfrost@snowman.net 424 :CBC 414 : return ret;
425 : : }
426 : :
427 : : /*
428 : : * Simple wrapper for reading from pqsecure_raw_read.
429 : : *
430 : : * This takes the same arguments as pqsecure_raw_read, plus an output parameter
431 : : * to return the number of bytes read. This handles if blocking would occur and
432 : : * if we detect EOF on the connection.
433 : : */
434 : : static PostgresPollingStatusType
435 : 355 : gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
436 : : {
437 : 355 : *ret = pqsecure_raw_read(conn, recv_buffer, length);
2116 tgl@sss.pgh.pa.us 438 [ + + ]: 355 : if (*ret < 0)
439 : : {
22 440 [ - + - - ]: 113 : if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
22 tgl@sss.pgh.pa.us 441 [ # # ]:UBC 0 : SOCK_ERRNO == EINTR)
2116 tgl@sss.pgh.pa.us 442 :CBC 113 : return PGRES_POLLING_READING;
443 : : else
2116 tgl@sss.pgh.pa.us 444 :UBC 0 : return PGRES_POLLING_FAILED;
445 : : }
446 : :
447 : : /* Check for EOF */
2399 sfrost@snowman.net 448 [ - + ]:CBC 242 : if (*ret == 0)
449 : : {
2399 sfrost@snowman.net 450 :UBC 0 : int result = pqReadReady(conn);
451 : :
452 [ # # ]: 0 : if (result < 0)
453 : 0 : return PGRES_POLLING_FAILED;
454 : :
455 [ # # ]: 0 : if (!result)
456 : 0 : return PGRES_POLLING_READING;
457 : :
458 : 0 : *ret = pqsecure_raw_read(conn, recv_buffer, length);
2116 tgl@sss.pgh.pa.us 459 [ # # ]: 0 : if (*ret < 0)
460 : : {
22 461 [ # # # # ]: 0 : if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
462 [ # # ]: 0 : SOCK_ERRNO == EINTR)
2116 463 : 0 : return PGRES_POLLING_READING;
464 : : else
465 : 0 : return PGRES_POLLING_FAILED;
466 : : }
2399 sfrost@snowman.net 467 [ # # ]: 0 : if (*ret == 0)
468 : 0 : return PGRES_POLLING_FAILED;
469 : : }
470 : :
2399 sfrost@snowman.net 471 :CBC 242 : return PGRES_POLLING_OK;
472 : : }
473 : :
474 : : /*
475 : : * Negotiate GSSAPI transport for a connection. When complete, returns
476 : : * PGRES_POLLING_OK. Will return PGRES_POLLING_READING or
477 : : * PGRES_POLLING_WRITING as appropriate whenever it would block, and
478 : : * PGRES_POLLING_FAILED if transport could not be negotiated.
479 : : */
480 : : PostgresPollingStatusType
481 : 355 : pqsecure_open_gss(PGconn *conn)
482 : : {
483 : : ssize_t ret;
484 : : OM_uint32 major,
485 : : minor,
928 486 : 355 : gss_flags = GSS_REQUIRED_FLAGS;
487 : : uint32 netlen;
488 : : PostgresPollingStatusType result;
2399 489 : 355 : gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
490 : 355 : output = GSS_C_EMPTY_BUFFER;
491 : :
492 : : /*
493 : : * If first time through for this connection, allocate buffers and
494 : : * initialize state variables. By malloc'ing the buffers separately, we
495 : : * ensure that they are sufficiently aligned for the length-word accesses
496 : : * that we do in some places in this file.
497 : : *
498 : : * We'll use PQ_GSS_AUTH_BUFFER_SIZE-sized buffers until transport
499 : : * negotiation is complete, then switch to PQ_GSS_MAX_PACKET_SIZE.
500 : : */
2116 tgl@sss.pgh.pa.us 501 [ + + ]: 355 : if (PqGSSSendBuffer == NULL)
502 : : {
150 503 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
504 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
505 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
2116 506 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
507 : : {
1077 peter@eisentraut.org 508 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
2116 tgl@sss.pgh.pa.us 509 : 0 : return PGRES_POLLING_FAILED;
510 : : }
2116 tgl@sss.pgh.pa.us 511 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
512 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
513 : : }
514 : :
515 : : /*
516 : : * Check if we have anything to send from a prior call and if so, send it.
517 : : */
518 [ + + ]: 355 : if (PqGSSSendLength)
519 : : {
520 : 121 : ssize_t amount = PqGSSSendLength - PqGSSSendNext;
521 : :
522 : 121 : ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
523 [ - + ]: 121 : if (ret < 0)
524 : : {
22 tgl@sss.pgh.pa.us 525 [ # # # # ]:UBC 0 : if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
526 [ # # ]: 0 : SOCK_ERRNO == EINTR)
2116 527 : 0 : return PGRES_POLLING_WRITING;
528 : : else
529 : 0 : return PGRES_POLLING_FAILED;
530 : : }
531 : :
2116 tgl@sss.pgh.pa.us 532 [ - + ]:CBC 121 : if (ret < amount)
533 : : {
2116 tgl@sss.pgh.pa.us 534 :UBC 0 : PqGSSSendNext += ret;
2399 sfrost@snowman.net 535 : 0 : return PGRES_POLLING_WRITING;
536 : : }
537 : :
2116 tgl@sss.pgh.pa.us 538 :CBC 121 : PqGSSSendLength = PqGSSSendNext = 0;
539 : : }
540 : :
541 : : /*
542 : : * Client sends first, and sending creates a context, therefore this will
543 : : * be false the first time through, and then when we get called again we
544 : : * will check for incoming data.
545 : : */
2399 sfrost@snowman.net 546 [ + + ]: 355 : if (conn->gctx)
547 : : {
548 : : /* Process any incoming data we might have */
549 : :
550 : : /* See if we are still trying to get the length */
551 [ + - ]: 234 : if (PqGSSRecvLength < sizeof(uint32))
552 : : {
553 : : /* Attempt to get the length first */
554 : 234 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, sizeof(uint32) - PqGSSRecvLength, &ret);
555 [ + + ]: 234 : if (result != PGRES_POLLING_OK)
556 : 113 : return result;
557 : :
558 : 121 : PqGSSRecvLength += ret;
559 : :
560 [ - + ]: 121 : if (PqGSSRecvLength < sizeof(uint32))
2399 sfrost@snowman.net 561 :UBC 0 : return PGRES_POLLING_READING;
562 : : }
563 : :
564 : : /*
565 : : * Check if we got an error packet
566 : : *
567 : : * This is safe to do because we shouldn't ever get a packet over 8192
568 : : * and therefore the actual length bytes, being that they are in
569 : : * network byte order, for any real packet will start with two zero
570 : : * bytes.
571 : : */
2399 sfrost@snowman.net 572 [ - + ]:CBC 121 : if (PqGSSRecvBuffer[0] == 'E')
573 : : {
574 : : /*
575 : : * For an error packet during startup, we don't get a length, so
576 : : * simply read as much as we can fit into our buffer (as a string,
577 : : * so leave a spot at the end for a NULL byte too) and report that
578 : : * back to the caller.
579 : : */
150 tgl@sss.pgh.pa.us 580 :UBC 0 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
2399 sfrost@snowman.net 581 [ # # ]: 0 : if (result != PGRES_POLLING_OK)
582 : 0 : return result;
583 : :
584 : 0 : PqGSSRecvLength += ret;
585 : :
150 tgl@sss.pgh.pa.us 586 [ # # ]: 0 : Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
994 michael@paquier.xyz 587 : 0 : PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
1750 tgl@sss.pgh.pa.us 588 : 0 : appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
589 : :
2399 sfrost@snowman.net 590 : 0 : return PGRES_POLLING_FAILED;
591 : : }
592 : :
593 : : /*
594 : : * We should have the whole length at this point, so pull it out and
595 : : * then read whatever we have left of the packet
596 : : */
597 : :
598 : : /* Get the length and check for over-length packet */
1838 michael@paquier.xyz 599 :CBC 121 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
150 tgl@sss.pgh.pa.us 600 [ - + ]: 121 : if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
601 : : {
1077 peter@eisentraut.org 602 :UBC 0 : libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
892 tgl@sss.pgh.pa.us 603 : 0 : (size_t) input.length,
604 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
2399 sfrost@snowman.net 605 : 0 : return PGRES_POLLING_FAILED;
606 : : }
607 : :
608 : : /*
609 : : * Read as much of the packet as we are able to on this call into
610 : : * wherever we left off from the last time we were called.
611 : : */
2399 sfrost@snowman.net 612 :CBC 121 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
613 : 121 : input.length - (PqGSSRecvLength - sizeof(uint32)), &ret);
614 [ - + ]: 121 : if (result != PGRES_POLLING_OK)
2399 sfrost@snowman.net 615 :UBC 0 : return result;
616 : :
2399 sfrost@snowman.net 617 :CBC 121 : PqGSSRecvLength += ret;
618 : :
619 : : /*
620 : : * If we got less than the rest of the packet then we need to return
621 : : * and be called again.
622 : : */
623 [ - + ]: 121 : if (PqGSSRecvLength - sizeof(uint32) < input.length)
2399 sfrost@snowman.net 624 :UBC 0 : return PGRES_POLLING_READING;
625 : :
2399 sfrost@snowman.net 626 :CBC 121 : input.value = PqGSSRecvBuffer + sizeof(uint32);
627 : : }
628 : :
629 : : /* Load the service name (no-op if already done */
630 : 242 : ret = pg_GSS_load_servicename(conn);
631 [ - + ]: 242 : if (ret != STATUS_OK)
2399 sfrost@snowman.net 632 :UBC 0 : return PGRES_POLLING_FAILED;
633 : :
889 tgl@sss.pgh.pa.us 634 [ + - + + ]:CBC 242 : if (conn->gssdelegation && conn->gssdelegation[0] == '1')
635 : : {
636 : : /* Acquire credentials if possible */
928 sfrost@snowman.net 637 [ - + ]: 32 : if (conn->gcred == GSS_C_NO_CREDENTIAL)
928 sfrost@snowman.net 638 :UBC 0 : (void) pg_GSS_have_cred_cache(&conn->gcred);
639 : :
640 : : /*
641 : : * We have credentials and gssdelegation is enabled, so request
642 : : * credential delegation. This may or may not actually result in
643 : : * credentials being delegated- it depends on if the forwardable flag
644 : : * has been set in the credential and if the server is configured to
645 : : * accept delegated credentials.
646 : : */
928 sfrost@snowman.net 647 [ + - ]:CBC 32 : if (conn->gcred != GSS_C_NO_CREDENTIAL)
648 : 32 : gss_flags |= GSS_C_DELEG_FLAG;
649 : : }
650 : :
651 : : /*
652 : : * Call GSS init context, either with an empty input, or with a complete
653 : : * packet from the server.
654 : : */
2399 655 : 242 : major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
656 : : conn->gtarg_nam, GSS_C_NO_OID,
657 : : gss_flags, 0, 0, &input, NULL,
658 : : &output, NULL, NULL);
659 : :
660 : : /* GSS Init Sec Context uses the whole packet, so clear it */
2116 tgl@sss.pgh.pa.us 661 : 242 : PqGSSRecvLength = 0;
662 : :
2399 sfrost@snowman.net 663 [ - + ]: 242 : if (GSS_ERROR(major))
664 : : {
2230 peter@eisentraut.org 665 :UBC 0 : pg_GSS_error(libpq_gettext("could not initiate GSSAPI security context"),
666 : : conn, major, minor);
2399 sfrost@snowman.net 667 : 0 : return PGRES_POLLING_FAILED;
668 : : }
669 : :
2116 tgl@sss.pgh.pa.us 670 [ + + ]:CBC 242 : if (output.length == 0)
671 : : {
672 : : /*
673 : : * We're done - hooray! Set flag to tell the low-level I/O routines
674 : : * to do GSS wrapping/unwrapping.
675 : : */
1764 676 : 121 : conn->gssenc = true;
928 sfrost@snowman.net 677 : 121 : conn->gssapi_used = true;
678 : :
679 : : /* Clean up */
2399 680 : 121 : gss_release_cred(&minor, &conn->gcred);
681 : 121 : conn->gcred = GSS_C_NO_CREDENTIAL;
2001 tgl@sss.pgh.pa.us 682 : 121 : gss_release_buffer(&minor, &output);
683 : :
684 : : /*
685 : : * Release the large authentication buffers and allocate the ones we
686 : : * want for normal operation. (This maneuver is safe only because
687 : : * pqDropConnection will drop the buffers; otherwise, during a
688 : : * reconnection we'd be at risk of using undersized buffers during
689 : : * negotiation.)
690 : : */
150 691 : 121 : free(PqGSSSendBuffer);
692 : 121 : free(PqGSSRecvBuffer);
693 : 121 : free(PqGSSResultBuffer);
694 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
695 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
696 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
697 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
698 : : {
150 tgl@sss.pgh.pa.us 699 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
700 : 0 : return PGRES_POLLING_FAILED;
701 : : }
150 tgl@sss.pgh.pa.us 702 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
703 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
704 : :
705 : : /*
706 : : * Determine the max packet size which will fit in our buffer, after
707 : : * accounting for the length. pg_GSS_write will need this.
708 : : */
2399 sfrost@snowman.net 709 : 121 : major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
710 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
2116 tgl@sss.pgh.pa.us 711 : 121 : &PqGSSMaxPktSize);
712 : :
2399 sfrost@snowman.net 713 [ - + ]: 121 : if (GSS_ERROR(major))
714 : : {
2399 sfrost@snowman.net 715 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI size check error"), conn,
716 : : major, minor);
2116 tgl@sss.pgh.pa.us 717 : 0 : return PGRES_POLLING_FAILED;
718 : : }
719 : :
2399 sfrost@snowman.net 720 :CBC 121 : return PGRES_POLLING_OK;
721 : : }
722 : :
723 : : /* Must have output.length > 0 */
150 tgl@sss.pgh.pa.us 724 [ - + ]: 121 : if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
725 : : {
150 tgl@sss.pgh.pa.us 726 :UBC 0 : libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
727 : 0 : (size_t) output.length,
728 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
2001 729 : 0 : gss_release_buffer(&minor, &output);
2399 sfrost@snowman.net 730 : 0 : return PGRES_POLLING_FAILED;
731 : : }
732 : :
733 : : /* Queue the token for writing */
1838 michael@paquier.xyz 734 :CBC 121 : netlen = pg_hton32(output.length);
735 : :
257 peter@eisentraut.org 736 : 121 : memcpy(PqGSSSendBuffer, &netlen, sizeof(uint32));
2116 tgl@sss.pgh.pa.us 737 : 121 : PqGSSSendLength += sizeof(uint32);
738 : :
739 : 121 : memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
740 : 121 : PqGSSSendLength += output.length;
741 : :
742 : : /* We don't bother with PqGSSSendConsumed here */
743 : :
744 : : /* Release buffer storage allocated by GSSAPI */
2399 sfrost@snowman.net 745 : 121 : gss_release_buffer(&minor, &output);
746 : :
747 : : /* Ask to be called again to write data */
748 : 121 : return PGRES_POLLING_WRITING;
749 : : }
750 : :
751 : : /*
752 : : * GSSAPI Information functions.
753 : : */
754 : :
755 : : /*
756 : : * Return the GSSAPI Context itself.
757 : : */
758 : : void *
2399 sfrost@snowman.net 759 :UBC 0 : PQgetgssctx(PGconn *conn)
760 : : {
761 [ # # ]: 0 : if (!conn)
762 : 0 : return NULL;
763 : :
764 : 0 : return conn->gctx;
765 : : }
766 : :
767 : : /*
768 : : * Return true if GSSAPI encryption is in use.
769 : : */
770 : : int
2399 sfrost@snowman.net 771 :CBC 3 : PQgssEncInUse(PGconn *conn)
772 : : {
773 [ + - + - ]: 3 : if (!conn || !conn->gctx)
774 : 3 : return 0;
775 : :
2399 sfrost@snowman.net 776 :UBC 0 : return conn->gssenc;
777 : : }
|