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
2348 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,
2065 tgl@sss.pgh.pa.us 98 : 367 : output = GSS_C_EMPTY_BUFFER;
2348 sfrost@snowman.net 99 : 367 : ssize_t ret = -1;
100 : : size_t bytes_to_encrypt;
101 : : size_t bytes_encrypted;
2065 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 : : {
1699 tgl@sss.pgh.pa.us 122 :UBC 0 : appendPQExpBufferStr(&conn->errorMessage,
123 : : "GSSAPI caller failed to retransmit all data needing to be retried\n");
2065 124 : 0 : errno = EINVAL;
125 : 0 : return -1;
126 : : }
127 : :
128 : : /* Discount whatever source data we already encrypted. */
2065 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 : : {
2179 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 : : */
2065 tgl@sss.pgh.pa.us 149 [ + + ]: 734 : if (PqGSSSendLength)
150 : : {
151 : : ssize_t retval;
152 : 367 : ssize_t amount = PqGSSSendLength - PqGSSSendNext;
153 : :
1065 drowley@postgresql.o 154 : 367 : retval = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
155 [ - + ]: 367 : if (retval <= 0)
1065 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 : : */
653 tgl@sss.pgh.pa.us 162 [ - + ]:CBC 367 : if (retval < amount)
163 : : {
1065 drowley@postgresql.o 164 :UBC 0 : PqGSSSendNext += retval;
2348 sfrost@snowman.net 165 : 0 : continue;
166 : : }
167 : :
168 : : /* We've successfully sent whatever data was in the buffer. */
653 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 : : */
2348 sfrost@snowman.net 175 [ + + ]: 734 : if (!bytes_to_encrypt)
2065 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)
2065 tgl@sss.pgh.pa.us 184 :UBC 0 : input.length = PqGSSMaxPktSize;
185 : : else
2348 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 : : */
2065 tgl@sss.pgh.pa.us 197 : 367 : major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
198 : : &input, &conf_state, &output);
2348 sfrost@snowman.net 199 [ - + ]: 367 : if (major != GSS_S_COMPLETE)
200 : : {
2348 sfrost@snowman.net 201 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn, major, minor);
2065 tgl@sss.pgh.pa.us 202 : 0 : errno = EIO; /* for lack of a better idea */
2348 sfrost@snowman.net 203 : 0 : goto cleanup;
204 : : }
205 : :
2065 tgl@sss.pgh.pa.us 206 [ - + ]:CBC 367 : if (conf_state == 0)
207 : : {
1026 peter@eisentraut.org 208 :UBC 0 : libpq_append_conn_error(conn, "outgoing GSSAPI message would not use confidentiality");
2065 tgl@sss.pgh.pa.us 209 : 0 : errno = EIO; /* for lack of a better idea */
2348 sfrost@snowman.net 210 : 0 : goto cleanup;
211 : : }
212 : :
99 tgl@sss.pgh.pa.us 213 [ - + ]:CBC 367 : if (output.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
214 : : {
1026 peter@eisentraut.org 215 :UBC 0 : libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
841 tgl@sss.pgh.pa.us 216 : 0 : (size_t) output.length,
217 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
2065 218 : 0 : errno = EIO; /* for lack of a better idea */
2348 sfrost@snowman.net 219 : 0 : goto cleanup;
220 : : }
221 : :
2348 sfrost@snowman.net 222 :CBC 367 : bytes_encrypted += input.length;
223 : 367 : bytes_to_encrypt -= input.length;
2065 tgl@sss.pgh.pa.us 224 : 367 : PqGSSSendConsumed += input.length;
225 : :
226 : : /* 4 network-order bytes of length, then payload */
1787 michael@paquier.xyz 227 : 367 : netlen = pg_hton32(output.length);
2065 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 */
1950 235 : 367 : gss_release_buffer(&minor, &output);
236 : : }
237 : :
238 : : /* If we get here, our counters should all match up. */
653 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 : :
2348 sfrost@snowman.net 247 : 367 : cleanup:
248 : : /* Release GSSAPI buffer storage, if we didn't already */
249 [ - + ]: 367 : if (output.value != NULL)
2348 sfrost@snowman.net 250 :UBC 0 : gss_release_buffer(&minor, &output);
2348 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;
2065 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 : : {
2179 peter@eisentraut.org 284 : 912 : int conf_state = 0;
285 : :
286 : : /* Check if we have data in our buffer that we can return immediately */
2065 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;
2348 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 : : */
2065 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 */
2348 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 */
2065 tgl@sss.pgh.pa.us 336 [ + + ]: 498 : if (ret <= 0)
337 : 84 : return ret;
338 : :
2348 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 : : {
2065 tgl@sss.pgh.pa.us 344 :UBC 0 : errno = EWOULDBLOCK;
345 : 0 : return -1;
346 : : }
347 : : }
348 : :
349 : : /* Decode the packet length and check for overlength packet */
1787 michael@paquier.xyz 350 :CBC 414 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
351 : :
99 tgl@sss.pgh.pa.us 352 [ - + ]: 414 : if (input.length > PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32))
353 : : {
1026 peter@eisentraut.org 354 :UBC 0 : libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
841 tgl@sss.pgh.pa.us 355 : 0 : (size_t) input.length,
356 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
2065 357 : 0 : errno = EIO; /* for lack of a better idea */
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 : : */
2348 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 */
2065 tgl@sss.pgh.pa.us 368 [ - + ]: 414 : if (ret <= 0)
2065 tgl@sss.pgh.pa.us 369 :UBC 0 : return ret;
370 : :
2348 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 : : {
2065 tgl@sss.pgh.pa.us 376 :UBC 0 : errno = EWOULDBLOCK;
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 : : */
2348 sfrost@snowman.net 386 :CBC 414 : output.value = NULL;
387 : 414 : output.length = 0;
388 : 414 : input.value = PqGSSRecvBuffer + sizeof(uint32);
389 : :
2065 tgl@sss.pgh.pa.us 390 : 414 : major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
2348 sfrost@snowman.net 391 [ - + ]: 414 : if (major != GSS_S_COMPLETE)
392 : : {
2348 sfrost@snowman.net 393 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
394 : : major, minor);
395 : 0 : ret = -1;
2065 tgl@sss.pgh.pa.us 396 : 0 : errno = EIO; /* for lack of a better idea */
2348 sfrost@snowman.net 397 : 0 : goto cleanup;
398 : : }
399 : :
2065 tgl@sss.pgh.pa.us 400 [ - + ]:CBC 414 : if (conf_state == 0)
401 : : {
1026 peter@eisentraut.org 402 :UBC 0 : libpq_append_conn_error(conn, "incoming GSSAPI message did not use confidentiality");
2348 sfrost@snowman.net 403 : 0 : ret = -1;
2065 tgl@sss.pgh.pa.us 404 : 0 : errno = EIO; /* for lack of a better idea */
2348 sfrost@snowman.net 405 : 0 : goto cleanup;
406 : : }
407 : :
2348 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 */
2065 tgl@sss.pgh.pa.us 412 : 414 : PqGSSRecvLength = 0;
413 : :
414 : : /* Release buffer storage allocated by GSSAPI */
2348 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)
2348 sfrost@snowman.net 423 :UBC 0 : gss_release_buffer(&minor, &output);
2348 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 : 354 : gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
436 : : {
437 : 354 : *ret = pqsecure_raw_read(conn, recv_buffer, length);
2065 tgl@sss.pgh.pa.us 438 [ + + ]: 354 : if (*ret < 0)
439 : : {
440 [ - + - - : 112 : if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- - ]
441 : 112 : return PGRES_POLLING_READING;
442 : : else
2065 tgl@sss.pgh.pa.us 443 :UBC 0 : return PGRES_POLLING_FAILED;
444 : : }
445 : :
446 : : /* Check for EOF */
2348 sfrost@snowman.net 447 [ - + ]:CBC 242 : if (*ret == 0)
448 : : {
2348 sfrost@snowman.net 449 :UBC 0 : int result = pqReadReady(conn);
450 : :
451 [ # # ]: 0 : if (result < 0)
452 : 0 : return PGRES_POLLING_FAILED;
453 : :
454 [ # # ]: 0 : if (!result)
455 : 0 : return PGRES_POLLING_READING;
456 : :
457 : 0 : *ret = pqsecure_raw_read(conn, recv_buffer, length);
2065 tgl@sss.pgh.pa.us 458 [ # # ]: 0 : if (*ret < 0)
459 : : {
460 [ # # # # : 0 : if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
# # ]
461 : 0 : return PGRES_POLLING_READING;
462 : : else
463 : 0 : return PGRES_POLLING_FAILED;
464 : : }
2348 sfrost@snowman.net 465 [ # # ]: 0 : if (*ret == 0)
466 : 0 : return PGRES_POLLING_FAILED;
467 : : }
468 : :
2348 sfrost@snowman.net 469 :CBC 242 : return PGRES_POLLING_OK;
470 : : }
471 : :
472 : : /*
473 : : * Negotiate GSSAPI transport for a connection. When complete, returns
474 : : * PGRES_POLLING_OK. Will return PGRES_POLLING_READING or
475 : : * PGRES_POLLING_WRITING as appropriate whenever it would block, and
476 : : * PGRES_POLLING_FAILED if transport could not be negotiated.
477 : : */
478 : : PostgresPollingStatusType
479 : 354 : pqsecure_open_gss(PGconn *conn)
480 : : {
481 : : ssize_t ret;
482 : : OM_uint32 major,
483 : : minor,
877 484 : 354 : gss_flags = GSS_REQUIRED_FLAGS;
485 : : uint32 netlen;
486 : : PostgresPollingStatusType result;
2348 487 : 354 : gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
488 : 354 : output = GSS_C_EMPTY_BUFFER;
489 : :
490 : : /*
491 : : * If first time through for this connection, allocate buffers and
492 : : * initialize state variables. By malloc'ing the buffers separately, we
493 : : * ensure that they are sufficiently aligned for the length-word accesses
494 : : * 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.
498 : : */
2065 tgl@sss.pgh.pa.us 499 [ + + ]: 354 : if (PqGSSSendBuffer == NULL)
500 : : {
99 501 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
502 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
503 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_AUTH_BUFFER_SIZE);
2065 504 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
505 : : {
1026 peter@eisentraut.org 506 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
2065 tgl@sss.pgh.pa.us 507 : 0 : return PGRES_POLLING_FAILED;
508 : : }
2065 tgl@sss.pgh.pa.us 509 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
510 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
511 : : }
512 : :
513 : : /*
514 : : * Check if we have anything to send from a prior call and if so, send it.
515 : : */
516 [ + + ]: 354 : if (PqGSSSendLength)
517 : : {
518 : 121 : ssize_t amount = PqGSSSendLength - PqGSSSendNext;
519 : :
520 : 121 : ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
521 [ - + ]: 121 : if (ret < 0)
522 : : {
2065 tgl@sss.pgh.pa.us 523 [ # # # # :UBC 0 : if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
# # ]
524 : 0 : return PGRES_POLLING_WRITING;
525 : : else
526 : 0 : return PGRES_POLLING_FAILED;
527 : : }
528 : :
2065 tgl@sss.pgh.pa.us 529 [ - + ]:CBC 121 : if (ret < amount)
530 : : {
2065 tgl@sss.pgh.pa.us 531 :UBC 0 : PqGSSSendNext += ret;
2348 sfrost@snowman.net 532 : 0 : return PGRES_POLLING_WRITING;
533 : : }
534 : :
2065 tgl@sss.pgh.pa.us 535 :CBC 121 : PqGSSSendLength = PqGSSSendNext = 0;
536 : : }
537 : :
538 : : /*
539 : : * Client sends first, and sending creates a context, therefore this will
540 : : * be false the first time through, and then when we get called again we
541 : : * will check for incoming data.
542 : : */
2348 sfrost@snowman.net 543 [ + + ]: 354 : if (conn->gctx)
544 : : {
545 : : /* Process any incoming data we might have */
546 : :
547 : : /* See if we are still trying to get the length */
548 [ + - ]: 233 : if (PqGSSRecvLength < sizeof(uint32))
549 : : {
550 : : /* Attempt to get the length first */
551 : 233 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, sizeof(uint32) - PqGSSRecvLength, &ret);
552 [ + + ]: 233 : if (result != PGRES_POLLING_OK)
553 : 112 : return result;
554 : :
555 : 121 : PqGSSRecvLength += ret;
556 : :
557 [ - + ]: 121 : if (PqGSSRecvLength < sizeof(uint32))
2348 sfrost@snowman.net 558 :UBC 0 : return PGRES_POLLING_READING;
559 : : }
560 : :
561 : : /*
562 : : * Check if we got an error packet
563 : : *
564 : : * This is safe to do because we shouldn't ever get a packet over 8192
565 : : * and therefore the actual length bytes, being that they are in
566 : : * network byte order, for any real packet will start with two zero
567 : : * bytes.
568 : : */
2348 sfrost@snowman.net 569 [ - + ]:CBC 121 : if (PqGSSRecvBuffer[0] == 'E')
570 : : {
571 : : /*
572 : : * For an error packet during startup, we don't get a length, so
573 : : * simply read as much as we can fit into our buffer (as a string,
574 : : * so leave a spot at the end for a NULL byte too) and report that
575 : : * back to the caller.
576 : : */
99 tgl@sss.pgh.pa.us 577 :UBC 0 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_AUTH_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
2348 sfrost@snowman.net 578 [ # # ]: 0 : if (result != PGRES_POLLING_OK)
579 : 0 : return result;
580 : :
581 : 0 : PqGSSRecvLength += ret;
582 : :
99 tgl@sss.pgh.pa.us 583 [ # # ]: 0 : Assert(PqGSSRecvLength < PQ_GSS_AUTH_BUFFER_SIZE);
943 michael@paquier.xyz 584 : 0 : PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
1699 tgl@sss.pgh.pa.us 585 : 0 : appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
586 : :
2348 sfrost@snowman.net 587 : 0 : return PGRES_POLLING_FAILED;
588 : : }
589 : :
590 : : /*
591 : : * We should have the whole length at this point, so pull it out and
592 : : * then read whatever we have left of the packet
593 : : */
594 : :
595 : : /* Get the length and check for over-length packet */
1787 michael@paquier.xyz 596 :CBC 121 : input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
99 tgl@sss.pgh.pa.us 597 [ - + ]: 121 : if (input.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
598 : : {
1026 peter@eisentraut.org 599 :UBC 0 : libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
841 tgl@sss.pgh.pa.us 600 : 0 : (size_t) input.length,
601 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
2348 sfrost@snowman.net 602 : 0 : return PGRES_POLLING_FAILED;
603 : : }
604 : :
605 : : /*
606 : : * Read as much of the packet as we are able to on this call into
607 : : * wherever we left off from the last time we were called.
608 : : */
2348 sfrost@snowman.net 609 :CBC 121 : result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
610 : 121 : input.length - (PqGSSRecvLength - sizeof(uint32)), &ret);
611 [ - + ]: 121 : if (result != PGRES_POLLING_OK)
2348 sfrost@snowman.net 612 :UBC 0 : return result;
613 : :
2348 sfrost@snowman.net 614 :CBC 121 : PqGSSRecvLength += ret;
615 : :
616 : : /*
617 : : * If we got less than the rest of the packet then we need to return
618 : : * and be called again.
619 : : */
620 [ - + ]: 121 : if (PqGSSRecvLength - sizeof(uint32) < input.length)
2348 sfrost@snowman.net 621 :UBC 0 : return PGRES_POLLING_READING;
622 : :
2348 sfrost@snowman.net 623 :CBC 121 : input.value = PqGSSRecvBuffer + sizeof(uint32);
624 : : }
625 : :
626 : : /* Load the service name (no-op if already done */
627 : 242 : ret = pg_GSS_load_servicename(conn);
628 [ - + ]: 242 : if (ret != STATUS_OK)
2348 sfrost@snowman.net 629 :UBC 0 : return PGRES_POLLING_FAILED;
630 : :
838 tgl@sss.pgh.pa.us 631 [ + - + + ]:CBC 242 : if (conn->gssdelegation && conn->gssdelegation[0] == '1')
632 : : {
633 : : /* Acquire credentials if possible */
877 sfrost@snowman.net 634 [ - + ]: 32 : if (conn->gcred == GSS_C_NO_CREDENTIAL)
877 sfrost@snowman.net 635 :UBC 0 : (void) pg_GSS_have_cred_cache(&conn->gcred);
636 : :
637 : : /*
638 : : * We have credentials and gssdelegation is enabled, so request
639 : : * credential delegation. This may or may not actually result in
640 : : * credentials being delegated- it depends on if the forwardable flag
641 : : * has been set in the credential and if the server is configured to
642 : : * accept delegated credentials.
643 : : */
877 sfrost@snowman.net 644 [ + - ]:CBC 32 : if (conn->gcred != GSS_C_NO_CREDENTIAL)
645 : 32 : gss_flags |= GSS_C_DELEG_FLAG;
646 : : }
647 : :
648 : : /*
649 : : * Call GSS init context, either with an empty input, or with a complete
650 : : * packet from the server.
651 : : */
2348 652 : 242 : major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
653 : : conn->gtarg_nam, GSS_C_NO_OID,
654 : : gss_flags, 0, 0, &input, NULL,
655 : : &output, NULL, NULL);
656 : :
657 : : /* GSS Init Sec Context uses the whole packet, so clear it */
2065 tgl@sss.pgh.pa.us 658 : 242 : PqGSSRecvLength = 0;
659 : :
2348 sfrost@snowman.net 660 [ - + ]: 242 : if (GSS_ERROR(major))
661 : : {
2179 peter@eisentraut.org 662 :UBC 0 : pg_GSS_error(libpq_gettext("could not initiate GSSAPI security context"),
663 : : conn, major, minor);
2348 sfrost@snowman.net 664 : 0 : return PGRES_POLLING_FAILED;
665 : : }
666 : :
2065 tgl@sss.pgh.pa.us 667 [ + + ]:CBC 242 : if (output.length == 0)
668 : : {
669 : : /*
670 : : * We're done - hooray! Set flag to tell the low-level I/O routines
671 : : * to do GSS wrapping/unwrapping.
672 : : */
1713 673 : 121 : conn->gssenc = true;
877 sfrost@snowman.net 674 : 121 : conn->gssapi_used = true;
675 : :
676 : : /* Clean up */
2348 677 : 121 : gss_release_cred(&minor, &conn->gcred);
678 : 121 : conn->gcred = GSS_C_NO_CREDENTIAL;
1950 tgl@sss.pgh.pa.us 679 : 121 : gss_release_buffer(&minor, &output);
680 : :
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 : : */
99 688 : 121 : free(PqGSSSendBuffer);
689 : 121 : free(PqGSSRecvBuffer);
690 : 121 : free(PqGSSResultBuffer);
691 : 121 : PqGSSSendBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
692 : 121 : PqGSSRecvBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
693 : 121 : PqGSSResultBuffer = malloc(PQ_GSS_MAX_PACKET_SIZE);
694 [ + - + - : 121 : if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
- + ]
695 : : {
99 tgl@sss.pgh.pa.us 696 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
697 : 0 : return PGRES_POLLING_FAILED;
698 : : }
99 tgl@sss.pgh.pa.us 699 :CBC 121 : PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
700 : 121 : PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
701 : :
702 : : /*
703 : : * Determine the max packet size which will fit in our buffer, after
704 : : * accounting for the length. pg_GSS_write will need this.
705 : : */
2348 sfrost@snowman.net 706 : 121 : major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
707 : : PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32),
2065 tgl@sss.pgh.pa.us 708 : 121 : &PqGSSMaxPktSize);
709 : :
2348 sfrost@snowman.net 710 [ - + ]: 121 : if (GSS_ERROR(major))
711 : : {
2348 sfrost@snowman.net 712 :UBC 0 : pg_GSS_error(libpq_gettext("GSSAPI size check error"), conn,
713 : : major, minor);
2065 tgl@sss.pgh.pa.us 714 : 0 : return PGRES_POLLING_FAILED;
715 : : }
716 : :
2348 sfrost@snowman.net 717 :CBC 121 : return PGRES_POLLING_OK;
718 : : }
719 : :
720 : : /* Must have output.length > 0 */
99 tgl@sss.pgh.pa.us 721 [ - + ]: 121 : if (output.length > PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32))
722 : : {
99 tgl@sss.pgh.pa.us 723 :UBC 0 : libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
724 : 0 : (size_t) output.length,
725 : : PQ_GSS_AUTH_BUFFER_SIZE - sizeof(uint32));
1950 726 : 0 : gss_release_buffer(&minor, &output);
2348 sfrost@snowman.net 727 : 0 : return PGRES_POLLING_FAILED;
728 : : }
729 : :
730 : : /* Queue the token for writing */
1787 michael@paquier.xyz 731 :CBC 121 : netlen = pg_hton32(output.length);
732 : :
206 peter@eisentraut.org 733 : 121 : memcpy(PqGSSSendBuffer, &netlen, sizeof(uint32));
2065 tgl@sss.pgh.pa.us 734 : 121 : PqGSSSendLength += sizeof(uint32);
735 : :
736 : 121 : memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
737 : 121 : PqGSSSendLength += output.length;
738 : :
739 : : /* We don't bother with PqGSSSendConsumed here */
740 : :
741 : : /* Release buffer storage allocated by GSSAPI */
2348 sfrost@snowman.net 742 : 121 : gss_release_buffer(&minor, &output);
743 : :
744 : : /* Ask to be called again to write data */
745 : 121 : return PGRES_POLLING_WRITING;
746 : : }
747 : :
748 : : /*
749 : : * GSSAPI Information functions.
750 : : */
751 : :
752 : : /*
753 : : * Return the GSSAPI Context itself.
754 : : */
755 : : void *
2348 sfrost@snowman.net 756 :UBC 0 : PQgetgssctx(PGconn *conn)
757 : : {
758 [ # # ]: 0 : if (!conn)
759 : 0 : return NULL;
760 : :
761 : 0 : return conn->gctx;
762 : : }
763 : :
764 : : /*
765 : : * Return true if GSSAPI encryption is in use.
766 : : */
767 : : int
2348 sfrost@snowman.net 768 :CBC 2 : PQgssEncInUse(PGconn *conn)
769 : : {
770 [ + - + - ]: 2 : if (!conn || !conn->gctx)
771 : 2 : return 0;
772 : :
2348 sfrost@snowman.net 773 :UBC 0 : return conn->gssenc;
774 : : }
|