PostgreSQL Source Code  git master
be-secure-gssapi.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * be-secure-gssapi.c
4  * GSSAPI encryption support
5  *
6  * Portions Copyright (c) 2018-2020, 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 "libpq/pqformat.h"
22 #include "miscadmin.h"
23 #include "pgstat.h"
24 
25 
26 /*
27  * Handle the encryption/decryption of data using GSSAPI.
28  *
29  * In the encrypted data stream on the wire, we break up the data
30  * into packets where each packet starts with a uint32-size length
31  * word (in network byte order), then encrypted data of that length
32  * immediately following. Decryption yields the same data stream
33  * that would appear when not using encryption.
34  *
35  * Encrypted data typically ends up being larger than the same data
36  * unencrypted, so we use fixed-size buffers for handling the
37  * encryption/decryption which are larger than PQComm's buffer will
38  * typically be to minimize the times where we have to make multiple
39  * packets (and therefore multiple recv/send calls for a single
40  * read/write call to us).
41  *
42  * NOTE: The client and server have to agree on the max packet size,
43  * because we have to pass an entire packet to GSSAPI at a time and we
44  * don't want the other side to send arbitrarily huge packets as we
45  * would have to allocate memory for them to then pass them to GSSAPI.
46  *
47  * Therefore, these two #define's are effectively part of the protocol
48  * spec and can't ever be changed.
49  */
50 #define PQ_GSS_SEND_BUFFER_SIZE 16384
51 #define PQ_GSS_RECV_BUFFER_SIZE 16384
52 
53 /*
54  * Since we manage at most one GSS-encrypted connection per backend,
55  * we can just keep all this state in static variables. The char *
56  * variables point to buffers that are allocated once and re-used.
57  */
58 static char *PqGSSSendBuffer; /* Encrypted data waiting to be sent */
59 static int PqGSSSendLength; /* End of data available in PqGSSSendBuffer */
60 static int PqGSSSendNext; /* Next index to send a byte from
61  * PqGSSSendBuffer */
62 static int PqGSSSendConsumed; /* Number of *unencrypted* bytes consumed for
63  * current contents of PqGSSSendBuffer */
64 
65 static char *PqGSSRecvBuffer; /* Received, encrypted data */
66 static int PqGSSRecvLength; /* End of data available in PqGSSRecvBuffer */
67 
68 static char *PqGSSResultBuffer; /* Decryption of data in gss_RecvBuffer */
69 static int PqGSSResultLength; /* End of data available in PqGSSResultBuffer */
70 static int PqGSSResultNext; /* Next index to read a byte from
71  * PqGSSResultBuffer */
72 
73 static uint32 PqGSSMaxPktSize; /* Maximum size we can encrypt and fit the
74  * results into our output buffer */
75 
76 
77 /*
78  * Attempt to write len bytes of data from ptr to a GSSAPI-encrypted connection.
79  *
80  * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
81  * transport negotiation is complete).
82  *
83  * On success, returns the number of data bytes consumed (possibly less than
84  * len). On failure, returns -1 with errno set appropriately. (For fatal
85  * errors, we may just elog and exit, if errno wouldn't be sufficient to
86  * describe the error.) For retryable errors, caller should call again
87  * (passing the same data) once the socket is ready.
88  */
89 ssize_t
90 be_gssapi_write(Port *port, void *ptr, size_t len)
91 {
92  OM_uint32 major,
93  minor;
94  gss_buffer_desc input,
95  output;
96  size_t bytes_sent = 0;
97  size_t bytes_to_encrypt;
98  size_t bytes_encrypted;
99  gss_ctx_id_t gctx = port->gss->ctx;
100 
101  /*
102  * When we get a failure, we must not tell the caller we have successfully
103  * transmitted everything, else it won't retry. Hence a "success"
104  * (positive) return value must only count source bytes corresponding to
105  * fully-transmitted encrypted packets. The amount of source data
106  * corresponding to the current partly-transmitted packet is remembered in
107  * PqGSSSendConsumed. On a retry, the caller *must* be sending that data
108  * again, so if it offers a len less than that, something is wrong.
109  */
110  if (len < PqGSSSendConsumed)
111  elog(FATAL, "GSSAPI caller failed to retransmit all data needing to be retried");
112 
113  /* Discount whatever source data we already encrypted. */
114  bytes_to_encrypt = len - PqGSSSendConsumed;
115  bytes_encrypted = PqGSSSendConsumed;
116 
117  /*
118  * Loop through encrypting data and sending it out until it's all done or
119  * secure_raw_write() complains (which would likely mean that the socket
120  * is non-blocking and the requested send() would block, or there was some
121  * kind of actual error).
122  */
123  while (bytes_to_encrypt || PqGSSSendLength)
124  {
125  int conf_state = 0;
126  uint32 netlen;
127 
128  /*
129  * Check if we have data in the encrypted output buffer that needs to
130  * be sent (possibly left over from a previous call), and if so, try
131  * to send it. If we aren't able to, return that fact back up to the
132  * caller.
133  */
134  if (PqGSSSendLength)
135  {
136  ssize_t ret;
137  ssize_t amount = PqGSSSendLength - PqGSSSendNext;
138 
139  ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendNext, amount);
140  if (ret <= 0)
141  {
142  /*
143  * Report any previously-sent data; if there was none, reflect
144  * the secure_raw_write result up to our caller. When there
145  * was some, we're effectively assuming that any interesting
146  * failure condition will recur on the next try.
147  */
148  if (bytes_sent)
149  return bytes_sent;
150  return ret;
151  }
152 
153  /*
154  * Check if this was a partial write, and if so, move forward that
155  * far in our buffer and try again.
156  */
157  if (ret != amount)
158  {
159  PqGSSSendNext += ret;
160  continue;
161  }
162 
163  /* We've successfully sent whatever data was in that packet. */
164  bytes_sent += PqGSSSendConsumed;
165 
166  /* All encrypted data was sent, our buffer is empty now. */
167  PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
168  }
169 
170  /*
171  * Check if there are any bytes left to encrypt. If not, we're done.
172  */
173  if (!bytes_to_encrypt)
174  break;
175 
176  /*
177  * Check how much we are being asked to send, if it's too much, then
178  * we will have to loop and possibly be called multiple times to get
179  * through all the data.
180  */
181  if (bytes_to_encrypt > PqGSSMaxPktSize)
182  input.length = PqGSSMaxPktSize;
183  else
184  input.length = bytes_to_encrypt;
185 
186  input.value = (char *) ptr + bytes_encrypted;
187 
188  output.value = NULL;
189  output.length = 0;
190 
191  /* Create the next encrypted packet */
192  major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
193  &input, &conf_state, &output);
194  if (major != GSS_S_COMPLETE)
195  pg_GSS_error(FATAL, gettext_noop("GSSAPI wrap error"), major, minor);
196 
197  if (conf_state == 0)
198  ereport(FATAL,
199  (errmsg("outgoing GSSAPI message would not use confidentiality")));
200 
201  if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
202  ereport(FATAL,
203  (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
204  (size_t) output.length,
205  PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
206 
207  bytes_encrypted += input.length;
208  bytes_to_encrypt -= input.length;
209  PqGSSSendConsumed += input.length;
210 
211  /* 4 network-order bytes of length, then payload */
212  netlen = htonl(output.length);
213  memcpy(PqGSSSendBuffer + PqGSSSendLength, &netlen, sizeof(uint32));
214  PqGSSSendLength += sizeof(uint32);
215 
216  memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
217  PqGSSSendLength += output.length;
218 
219  /* Release buffer storage allocated by GSSAPI */
220  gss_release_buffer(&minor, &output);
221  }
222 
223  /* If we get here, our counters should all match up. */
224  Assert(bytes_sent == len);
225  Assert(bytes_sent == bytes_encrypted);
226 
227  return bytes_sent;
228 }
229 
230 /*
231  * Read up to len bytes of data into ptr from a GSSAPI-encrypted connection.
232  *
233  * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
234  * transport negotiation is complete).
235  *
236  * Returns the number of data bytes read, or on failure, returns -1
237  * with errno set appropriately. (For fatal errors, we may just elog and
238  * exit, if errno wouldn't be sufficient to describe the error.) For
239  * retryable errors, caller should call again once the socket is ready.
240  */
241 ssize_t
242 be_gssapi_read(Port *port, void *ptr, size_t len)
243 {
244  OM_uint32 major,
245  minor;
246  gss_buffer_desc input,
247  output;
248  ssize_t ret;
249  size_t bytes_returned = 0;
250  gss_ctx_id_t gctx = port->gss->ctx;
251 
252  /*
253  * The plan here is to read one incoming encrypted packet into
254  * PqGSSRecvBuffer, decrypt it into PqGSSResultBuffer, and then dole out
255  * data from there to the caller. When we exhaust the current input
256  * packet, read another.
257  */
258  while (bytes_returned < len)
259  {
260  int conf_state = 0;
261 
262  /* Check if we have data in our buffer that we can return immediately */
264  {
265  size_t bytes_in_buffer = PqGSSResultLength - PqGSSResultNext;
266  size_t bytes_to_copy = Min(bytes_in_buffer, len - bytes_returned);
267 
268  /*
269  * Copy the data from our result buffer into the caller's buffer,
270  * at the point where we last left off filling their buffer.
271  */
272  memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultNext, bytes_to_copy);
273  PqGSSResultNext += bytes_to_copy;
274  bytes_returned += bytes_to_copy;
275 
276  /*
277  * At this point, we've either filled the caller's buffer or
278  * emptied our result buffer. Either way, return to caller. In
279  * the second case, we could try to read another encrypted packet,
280  * but the odds are good that there isn't one available. (If this
281  * isn't true, we chose too small a max packet size.) In any
282  * case, there's no harm letting the caller process the data we've
283  * already returned.
284  */
285  break;
286  }
287 
288  /* Result buffer is empty, so reset buffer pointers */
290 
291  /*
292  * Because we chose above to return immediately as soon as we emit
293  * some data, bytes_returned must be zero at this point. Therefore
294  * the failure exits below can just return -1 without worrying about
295  * whether we already emitted some data.
296  */
297  Assert(bytes_returned == 0);
298 
299  /*
300  * At this point, our result buffer is empty with more bytes being
301  * requested to be read. We are now ready to load the next packet and
302  * decrypt it (entirely) into our result buffer.
303  */
304 
305  /* Collect the length if we haven't already */
306  if (PqGSSRecvLength < sizeof(uint32))
307  {
309  sizeof(uint32) - PqGSSRecvLength);
310 
311  /* If ret <= 0, secure_raw_read already set the correct errno */
312  if (ret <= 0)
313  return ret;
314 
315  PqGSSRecvLength += ret;
316 
317  /* If we still haven't got the length, return to the caller */
318  if (PqGSSRecvLength < sizeof(uint32))
319  {
320  errno = EWOULDBLOCK;
321  return -1;
322  }
323  }
324 
325  /* Decode the packet length and check for overlength packet */
326  input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
327 
328  if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
329  ereport(FATAL,
330  (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
331  (size_t) input.length,
332  PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
333 
334  /*
335  * Read as much of the packet as we are able to on this call into
336  * wherever we left off from the last time we were called.
337  */
338  ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength,
339  input.length - (PqGSSRecvLength - sizeof(uint32)));
340  /* If ret <= 0, secure_raw_read already set the correct errno */
341  if (ret <= 0)
342  return ret;
343 
344  PqGSSRecvLength += ret;
345 
346  /* If we don't yet have the whole packet, return to the caller */
347  if (PqGSSRecvLength - sizeof(uint32) < input.length)
348  {
349  errno = EWOULDBLOCK;
350  return -1;
351  }
352 
353  /*
354  * We now have the full packet and we can perform the decryption and
355  * refill our result buffer, then loop back up to pass data back to
356  * the caller.
357  */
358  output.value = NULL;
359  output.length = 0;
360  input.value = PqGSSRecvBuffer + sizeof(uint32);
361 
362  major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
363  if (major != GSS_S_COMPLETE)
364  pg_GSS_error(FATAL, gettext_noop("GSSAPI unwrap error"),
365  major, minor);
366 
367  if (conf_state == 0)
368  ereport(FATAL,
369  (errmsg("incoming GSSAPI message did not use confidentiality")));
370 
371  memcpy(PqGSSResultBuffer, output.value, output.length);
372  PqGSSResultLength = output.length;
373 
374  /* Our receive buffer is now empty, reset it */
375  PqGSSRecvLength = 0;
376 
377  /* Release buffer storage allocated by GSSAPI */
378  gss_release_buffer(&minor, &output);
379  }
380 
381  return bytes_returned;
382 }
383 
384 /*
385  * Read the specified number of bytes off the wire, waiting using
386  * WaitLatchOrSocket if we would block.
387  *
388  * Results are read into PqGSSRecvBuffer.
389  *
390  * Will always return either -1, to indicate a permanent error, or len.
391  */
392 static ssize_t
393 read_or_wait(Port *port, ssize_t len)
394 {
395  ssize_t ret;
396 
397  /*
398  * Keep going until we either read in everything we were asked to, or we
399  * error out.
400  */
401  while (PqGSSRecvLength < len)
402  {
404 
405  /*
406  * If we got back an error and it wasn't just
407  * EWOULDBLOCK/EAGAIN/EINTR, then give up.
408  */
409  if (ret < 0 &&
410  !(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
411  return -1;
412 
413  /*
414  * Ok, we got back either a positive value, zero, or a negative result
415  * indicating we should retry.
416  *
417  * If it was zero or negative, then we wait on the socket to be
418  * readable again.
419  */
420  if (ret <= 0)
421  {
424  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
425 
426  /*
427  * If we got back zero bytes, and then waited on the socket to be
428  * readable and got back zero bytes on a second read, then this is
429  * EOF and the client hung up on us.
430  *
431  * If we did get data here, then we can just fall through and
432  * handle it just as if we got data the first time.
433  *
434  * Otherwise loop back to the top and try again.
435  */
436  if (ret == 0)
437  {
439  if (ret == 0)
440  return -1;
441  }
442  if (ret < 0)
443  continue;
444  }
445 
446  PqGSSRecvLength += ret;
447  }
448 
449  return len;
450 }
451 
452 /*
453  * Start up a GSSAPI-encrypted connection. This performs GSSAPI
454  * authentication; after this function completes, it is safe to call
455  * be_gssapi_read and be_gssapi_write. Returns -1 and logs on failure;
456  * otherwise, returns 0 and marks the connection as ready for GSSAPI
457  * encryption.
458  *
459  * Note that unlike the be_gssapi_read/be_gssapi_write functions, this
460  * function WILL block on the socket to be ready for read/write (using
461  * WaitLatchOrSocket) as appropriate while establishing the GSSAPI
462  * session.
463  */
464 ssize_t
466 {
467  bool complete_next = false;
468  OM_uint32 major,
469  minor;
470 
471  /*
472  * Allocate buffers and initialize state variables. By malloc'ing the
473  * buffers at this point, we avoid wasting static data space in processes
474  * that will never use them, and we ensure that the buffers are
475  * sufficiently aligned for the length-word accesses that we do in some
476  * places in this file.
477  */
482  ereport(FATAL,
483  (errcode(ERRCODE_OUT_OF_MEMORY),
484  errmsg("out of memory")));
487 
488  /*
489  * Use the configured keytab, if there is one. Unfortunately, Heimdal
490  * doesn't support the cred store extensions, so use the env var.
491  */
492  if (pg_krb_server_keyfile != NULL && strlen(pg_krb_server_keyfile) > 0)
493  setenv("KRB5_KTNAME", pg_krb_server_keyfile, 1);
494 
495  while (true)
496  {
497  ssize_t ret;
498  gss_buffer_desc input,
499  output = GSS_C_EMPTY_BUFFER;
500 
501  /*
502  * The client always sends first, so try to go ahead and read the
503  * length and wait on the socket to be readable again if that fails.
504  */
505  ret = read_or_wait(port, sizeof(uint32));
506  if (ret < 0)
507  return ret;
508 
509  /*
510  * Get the length for this packet from the length header.
511  */
512  input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
513 
514  /* Done with the length, reset our buffer */
515  PqGSSRecvLength = 0;
516 
517  /*
518  * During initialization, packets are always fully consumed and
519  * shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
520  *
521  * Verify on our side that the client doesn't do something funny.
522  */
523  if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
524  ereport(FATAL,
525  (errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
526  (size_t) input.length,
528 
529  /*
530  * Get the rest of the packet so we can pass it to GSSAPI to accept
531  * the context.
532  */
533  ret = read_or_wait(port, input.length);
534  if (ret < 0)
535  return ret;
536 
537  input.value = PqGSSRecvBuffer;
538 
539  /* Process incoming data. (The client sends first.) */
540  major = gss_accept_sec_context(&minor, &port->gss->ctx,
541  GSS_C_NO_CREDENTIAL, &input,
542  GSS_C_NO_CHANNEL_BINDINGS,
543  &port->gss->name, NULL, &output, NULL,
544  NULL, NULL);
545  if (GSS_ERROR(major))
546  {
547  pg_GSS_error(ERROR, gettext_noop("could not accept GSSAPI security context"),
548  major, minor);
549  gss_release_buffer(&minor, &output);
550  return -1;
551  }
552  else if (!(major & GSS_S_CONTINUE_NEEDED))
553  {
554  /*
555  * rfc2744 technically permits context negotiation to be complete
556  * both with and without a packet to be sent.
557  */
558  complete_next = true;
559  }
560 
561  /* Done handling the incoming packet, reset our buffer */
562  PqGSSRecvLength = 0;
563 
564  /*
565  * Check if we have data to send and, if we do, make sure to send it
566  * all
567  */
568  if (output.length > 0)
569  {
570  uint32 netlen = htonl(output.length);
571 
572  if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
573  ereport(FATAL,
574  (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
575  (size_t) output.length,
576  PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
577 
578  memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
579  PqGSSSendLength += sizeof(uint32);
580 
581  memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
582  PqGSSSendLength += output.length;
583 
584  /* we don't bother with PqGSSSendConsumed here */
585 
587  {
590 
591  /*
592  * If we got back an error and it wasn't just
593  * EWOULDBLOCK/EAGAIN/EINTR, then give up.
594  */
595  if (ret < 0 &&
596  !(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
597  {
598  gss_release_buffer(&minor, &output);
599  return -1;
600  }
601 
602  /* Wait and retry if we couldn't write yet */
603  if (ret <= 0)
604  {
607  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
608  continue;
609  }
610 
611  PqGSSSendNext += ret;
612  }
613 
614  /* Done sending the packet, reset our buffer */
616 
617  gss_release_buffer(&minor, &output);
618  }
619 
620  /*
621  * If we got back that the connection is finished being set up, now
622  * that we've sent the last packet, exit our loop.
623  */
624  if (complete_next)
625  break;
626  }
627 
628  /*
629  * Determine the max packet size which will fit in our buffer, after
630  * accounting for the length. be_gssapi_write will need this.
631  */
632  major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
634  &PqGSSMaxPktSize);
635 
636  if (GSS_ERROR(major))
637  pg_GSS_error(FATAL, gettext_noop("GSSAPI size check error"),
638  major, minor);
639 
640  port->gss->enc = true;
641 
642  return 0;
643 }
644 
645 /*
646  * Return if GSSAPI authentication was used on this connection.
647  */
648 bool
650 {
651  if (!port || !port->gss)
652  return false;
653 
654  return port->gss->auth;
655 }
656 
657 /*
658  * Return if GSSAPI encryption is enabled and being used on this connection.
659  */
660 bool
662 {
663  if (!port || !port->gss)
664  return false;
665 
666  return port->gss->enc;
667 }
668 
669 /*
670  * Return the GSSAPI principal used for authentication on this connection.
671  */
672 const char *
674 {
675  if (!port || !port->gss->auth)
676  return NULL;
677 
678  return port->gss->princ;
679 }
static int PqGSSSendConsumed
#define WL_SOCKET_WRITEABLE
Definition: latch.h:126
const char * be_gssapi_get_princ(Port *port)
static int PqGSSResultNext
#define EAGAIN
Definition: win32_port.h:321
static void output(uint64 loop_count)
ssize_t secure_raw_write(Port *port, const void *ptr, size_t len)
Definition: be-secure.c:330
#define Min(x, y)
Definition: c.h:927
#define gettext_noop(x)
Definition: c.h:1166
int errcode(int sqlerrcode)
Definition: elog.c:610
Definition: libpq-be.h:120
static int PqGSSSendLength
#define WL_SOCKET_READABLE
Definition: latch.h:125
pgsocket sock
Definition: libpq-be.h:122
static int PqGSSRecvLength
#define malloc(a)
Definition: header.h:50
#define PQ_GSS_RECV_BUFFER_SIZE
#define ERROR
Definition: elog.h:43
static char * PqGSSResultBuffer
#define FATAL
Definition: elog.h:52
int WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock, long timeout, uint32 wait_event_info)
Definition: latch.c:390
unsigned int uint32
Definition: c.h:374
static int PqGSSSendNext
static uint32 PqGSSMaxPktSize
static int port
Definition: pg_regress.c:90
static int PqGSSResultLength
bool be_gssapi_get_enc(Port *port)
ssize_t be_gssapi_write(Port *port, void *ptr, size_t len)
static char * PqGSSRecvBuffer
static char * PqGSSSendBuffer
ssize_t secure_open_gssapi(Port *port)
#define ereport(elevel,...)
Definition: elog.h:144
ssize_t secure_raw_read(Port *port, void *ptr, size_t len)
Definition: be-secure.c:234
static ssize_t read_or_wait(Port *port, ssize_t len)
#define Assert(condition)
Definition: c.h:745
#define PQ_GSS_SEND_BUFFER_SIZE
char * pg_krb_server_keyfile
Definition: auth.c:169
void * gss
Definition: libpq-be.h:184
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define elog(elevel,...)
Definition: elog.h:214
struct Latch * MyLatch
Definition: globals.c:54
#define EWOULDBLOCK
Definition: win32_port.h:329
#define EINTR
Definition: win32_port.h:323
void pg_GSS_error(int severity, const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
bool be_gssapi_get_auth(Port *port)
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:129
ssize_t be_gssapi_read(Port *port, void *ptr, size_t len)