1 /* netsec.c -- Network security routines for handling protocols that
2 * require SASL and/or TLS.
4 * This code is Copyright (c) 2016, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
14 #include <sys/select.h>
18 #include <sasl/sasl.h>
19 #include <sasl/saslutil.h>
20 # if SASL_VERSION_FULL < 0x020125
21 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
22 which has an explicit void parameter list, according to best
23 practice. So we need to cast to avoid compile warnings.
24 Provide this prototype for earlier versions. */
25 typedef int (*sasl_callback_ft
)();
26 # endif /* SASL_VERSION_FULL < 0x020125 */
28 static int netsec_get_user(void *context
, int id
, const char **result
,
30 static int netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
31 sasl_secret_t
**psecret
);
33 static int sasl_initialized
= 0;
35 #define SASL_MAXRECVBUF 65536
36 #endif /* CYRUS_SASL */
39 #include <openssl/ssl.h>
40 #include <openssl/err.h>
42 static int tls_initialized
= 0;
43 static SSL_CTX
*sslctx
= NULL
; /* SSL Context */
45 #endif /* TLS_SUPPORT */
47 /* I'm going to hardcode this for now; maybe make it adjustable later? */
48 #define NETSEC_BUFSIZE 65536
51 * Our context structure, which holds all of the relevant information
55 struct _netsec_context
{
56 int ns_readfd
; /* Read descriptor for network connection */
57 int ns_writefd
; /* Write descriptor for network connection */
58 int ns_noclose
; /* Do not close file descriptors if set */
59 int ns_snoop
; /* If true, display network data */
60 netsec_snoop_callback
*ns_snoop_cb
; /* Snoop output callback */
61 void *ns_snoop_context
; /* Context data for snoop function */
62 char *ns_snoop_savebuf
; /* Save buffer for snoop data */
63 int ns_timeout
; /* Network read timeout, in seconds */
64 char *ns_userid
; /* Userid for authentication */
65 char *ns_hostname
; /* Hostname we've connected to */
66 unsigned char *ns_inbuffer
; /* Our read input buffer */
67 unsigned char *ns_inptr
; /* Our read buffer input pointer */
68 unsigned int ns_inbuflen
; /* Length of data in input buffer */
69 unsigned int ns_inbufsize
; /* Size of input buffer */
70 unsigned char *ns_outbuffer
;/* Output buffer */
71 unsigned char *ns_outptr
; /* Output buffer pointer */
72 unsigned int ns_outbuflen
; /* Output buffer data length */
73 unsigned int ns_outbufsize
; /* Output buffer size */
74 char *sasl_mech
; /* User-requested mechanism */
75 char *sasl_chosen_mech
; /* Mechanism chosen by SASL */
76 netsec_sasl_callback sasl_proto_cb
; /* SASL callback we use */
77 void *sasl_proto_context
; /* Context to be used by SASL callback */
79 char *oauth_service
; /* OAuth2 service name */
80 #endif /* OAUTH_SUPPORT */
82 sasl_conn_t
*sasl_conn
; /* SASL connection context */
83 sasl_ssf_t sasl_ssf
; /* SASL Security Strength Factor */
84 sasl_callback_t
*sasl_cbs
; /* Callbacks used by SASL */
85 nmh_creds_t sasl_creds
; /* Credentials (username/password) */
86 sasl_secret_t
*sasl_secret
; /* SASL password structure */
87 int sasl_seclayer
; /* If true, SASL security layer is enabled */
88 char *sasl_tmpbuf
; /* Temporary read buffer for decodes */
89 size_t sasl_maxbufsize
; /* Maximum negotiated SASL buffer size */
90 #endif /* CYRUS_SASL */
92 BIO
*ssl_io
; /* BIO used for connection I/O */
93 int tls_active
; /* If true, TLS is running */
94 #endif /* TLS_SUPPORT */
98 * Function to read data from the actual network socket
101 static int netsec_fillread(netsec_context
*ns_context
, char **errstr
);
104 * Code to check the ASCII content of a byte array.
107 static int checkascii(const unsigned char *byte
, size_t len
);
110 * How this code works, in general.
112 * _If_ we are using no encryption then we buffer the network data
113 * through ns_inbuffer and ns_outbuffer. That should be relatively
116 * If we use encryption, then ns_inbuffer and ns_outbuffer contain the
117 * cleartext data. When it comes time to send the encrypted data on the
118 * (either from a flush or the buffer is full) we either use BIO_write()
119 * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For
120 * reads we either use BIO_read() (TLS) or do a network read into a
121 * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if
122 * negotiate TLS then we disable SASL encryption.
124 * We used to use a buffering BIO for the reads/writes for TLS, but it
125 * ended up being complicated to special-case the buffering for everything
126 * except TLS, so the buffering is now unified, no matter which encryption
127 * method is being used (even none).
129 * For SASL authentication, we make use of (for now) the Cyrus-SASL
130 * library. For some mechanisms, we implement those mechanisms directly
131 * since the Cyrus SASL library doesn't support them (like OAuth).
135 * Allocate and initialize our security context
145 nsc
->ns_writefd
= -1;
148 nsc
->ns_snoop_cb
= NULL
;
149 nsc
->ns_snoop_context
= NULL
;
150 nsc
->ns_snoop_savebuf
= NULL
;
151 nsc
->ns_userid
= NULL
;
152 nsc
->ns_hostname
= NULL
;
153 nsc
->ns_timeout
= 60; /* Our default */
154 nsc
->ns_inbufsize
= NETSEC_BUFSIZE
;
155 nsc
->ns_inbuffer
= mh_xmalloc(nsc
->ns_inbufsize
);
156 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
157 nsc
->ns_inbuflen
= 0;
158 nsc
->ns_outbufsize
= NETSEC_BUFSIZE
;
159 nsc
->ns_outbuffer
= mh_xmalloc(nsc
->ns_outbufsize
);
160 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
161 nsc
->ns_outbuflen
= 0;
162 nsc
->sasl_mech
= NULL
;
163 nsc
->sasl_chosen_mech
= NULL
;
164 nsc
->sasl_proto_cb
= NULL
;
165 nsc
->sasl_proto_context
= NULL
;
167 nsc
->oauth_service
= NULL
;
168 #endif /* OAUTH_SUPPORT */
170 nsc
->sasl_conn
= NULL
;
171 nsc
->sasl_cbs
= NULL
;
172 nsc
->sasl_creds
= NULL
;
173 nsc
->sasl_secret
= NULL
;
175 nsc
->sasl_seclayer
= 0;
176 nsc
->sasl_tmpbuf
= NULL
;
177 nsc
->sasl_maxbufsize
= 0;
178 #endif /* CYRUS_SASL */
182 #endif /* TLS_SUPPORT */
187 * Shutdown the connection completely and free all resources.
191 netsec_shutdown(netsec_context
*nsc
)
193 free(nsc
->ns_userid
);
194 free(nsc
->ns_hostname
);
195 free(nsc
->ns_inbuffer
);
196 free(nsc
->ns_outbuffer
);
197 free(nsc
->sasl_mech
);
198 free(nsc
->sasl_chosen_mech
);
199 free(nsc
->ns_snoop_savebuf
);
201 free(nsc
->oauth_service
);
202 #endif /* OAUTH_SERVICE */
205 sasl_dispose(&nsc
->sasl_conn
);
208 nmh_credentials_free(nsc
->sasl_creds
);
209 if (nsc
->sasl_secret
) {
210 if (nsc
->sasl_secret
->len
> 0) {
211 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
213 free(nsc
->sasl_secret
);
215 free(nsc
->sasl_tmpbuf
);
216 #endif /* CYRUS_SASL */
220 * I checked; BIO_free_all() will cause SSL_shutdown to be called
221 * on the SSL object in the chain.
223 BIO_free_all(nsc
->ssl_io
);
224 #endif /* TLS_SUPPORT */
226 if (! nsc
->ns_noclose
) {
227 if (nsc
->ns_readfd
!= -1)
228 close(nsc
->ns_readfd
);
229 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
230 close(nsc
->ns_writefd
);
237 * Set the file descriptor for our context
241 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
243 nsc
->ns_readfd
= readfd
;
244 nsc
->ns_writefd
= writefd
;
248 * Set the userid used for authentication for this context
252 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
254 nsc
->ns_userid
= getcpy(userid
);
258 * Set the hostname of the remote host we're connecting to.
262 netsec_set_hostname(netsec_context
*nsc
, const char *hostname
)
264 nsc
->ns_hostname
= mh_xstrdup(hostname
);
268 * Get the snoop flag for this connection
272 netsec_get_snoop(netsec_context
*nsc
)
274 return nsc
->ns_snoop
;
278 * Set the snoop flag for this connection
282 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
284 nsc
->ns_snoop
= snoop
;
288 * Set the snoop callback for this connection.
291 void netsec_set_snoop_callback(netsec_context
*nsc
,
292 netsec_snoop_callback callback
, void *context
)
294 nsc
->ns_snoop_cb
= callback
;
295 nsc
->ns_snoop_context
= context
;
299 * A base64-decoding snoop callback
303 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
306 unsigned char *decoded
;
311 offset
= context
? *((int *) context
) : 0;
315 * Output non-base64 data first.
317 fprintf(stderr
, "%.*s", offset
, string
);
322 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
324 * Some mechanisms produce large binary tokens, which aren't really
325 * readable. So let's do a simple heuristic. If the token is greater
326 * than 100 characters _and_ the first 100 bytes are more than 50%
327 * non-ASCII, then don't print the decoded buffer, just the
330 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
331 fprintf(stderr
, "%.*s\n", (int) len
, string
);
334 hexify(decoded
, decodedlen
, &hexified
);
335 fprintf(stderr
, "b64<%s>\n", hexified
);
340 fprintf(stderr
, "%.*s\n", (int) len
, string
);
345 * If the ASCII content is > 50%, return 1
349 checkascii(const unsigned char *bytes
, size_t len
)
351 size_t count
= 0, half
= len
/ 2;
354 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
357 /* No chance by this point */
358 if (count
+ len
< half
)
366 * Set the read timeout for this connection
370 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
372 nsc
->ns_timeout
= timeout
;
376 * Read data from the network. Basically, return anything in our buffer,
377 * otherwise fill from the network.
381 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
386 * If our buffer is empty, then we should fill it now
389 if (nsc
->ns_inbuflen
== 0) {
390 if (netsec_fillread(nsc
, errstr
) != OK
)
395 * netsec_fillread only returns if the buffer is full, so we can
396 * assume here that this has something in it.
399 retlen
= min(size
, nsc
->ns_inbuflen
);
401 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
403 if (retlen
== (int) nsc
->ns_inbuflen
) {
405 * We've emptied our buffer, so reset everything.
407 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
408 nsc
->ns_inbuflen
= 0;
410 nsc
->ns_inptr
+= size
;
411 nsc
->ns_inbuflen
-= size
;
418 * Get a "line" (CR/LF) terminated from the network.
420 * Okay, we play some games here, so pay attention:
422 * - Unlike every other function, we return a pointer to the
423 * existing buffer. This pointer is valid until you call another
424 * read function again.
425 * - We NUL-terminate the buffer right at the end, before the CR-LF terminator.
426 * - Technically we look for a LF; if we find a CR right before it, then
428 * - If your data may contain embedded NULs, this won't work. You should
429 * be using netsec_read() in that case.
433 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
435 unsigned char *ptr
= nsc
->ns_inptr
;
436 size_t count
= 0, offset
;
440 * Search through our existing buffer for a LF
443 while (count
< nsc
->ns_inbuflen
) {
445 if (*ptr
++ == '\n') {
446 char *sptr
= (char *) nsc
->ns_inptr
;
447 if (count
> 1 && *(ptr
- 2) == '\r')
451 *len
= ptr
- nsc
->ns_inptr
;
452 nsc
->ns_inptr
+= count
;
453 nsc
->ns_inbuflen
-= count
;
456 if (nsc
->sasl_seclayer
)
457 fprintf(stderr
, "(sasl-decrypted) ");
458 #endif /* CYRUS_SASL */
461 fprintf(stderr
, "(tls-decrypted) ");
462 #endif /* TLS_SUPPORT */
463 fprintf(stderr
, "<= ");
464 if (nsc
->ns_snoop_cb
)
465 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
466 nsc
->ns_snoop_context
);
477 * Hm, we didn't find a \n. If we've already searched half of the input
478 * buffer, return an error.
481 if (count
>= nsc
->ns_inbufsize
/ 2) {
482 netsec_err(errstr
, "Unable to find a line terminator after %zu bytes",
488 * Okay, get some more network data. This may move inptr, so regenerate
492 offset
= ptr
- nsc
->ns_inptr
;
494 if (netsec_fillread(nsc
, errstr
) != OK
)
497 ptr
= nsc
->ns_inptr
+ offset
;
501 return NULL
; /* Should never reach this */
505 * Fill our read buffer with some data from the network.
509 netsec_fillread(netsec_context
*nsc
, char **errstr
)
513 size_t readbufsize
, remaining
, startoffset
;
517 * If inbuflen is zero, that means the buffer has been emptied
518 * completely. In that case move inptr back to the start.
521 if (nsc
->ns_inbuflen
== 0) {
522 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
525 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
527 #endif /* CYRUS_SASL || TLS_SUPPORT */
529 * If we are using TLS and there's anything pending, then skip the
533 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
534 #endif /* TLS_SUPPORT */
540 FD_SET(nsc
->ns_readfd
, &rfds
);
542 tv
.tv_sec
= nsc
->ns_timeout
;
545 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
548 netsec_err(errstr
, "select() while reading failed: %s",
554 netsec_err(errstr
, "read() timed out after %d seconds",
560 * At this point, we know that rc is 1, so there's not even any
561 * point to check to see if our descriptor is set in rfds.
568 * startoffset is the offset from the beginning of the input
569 * buffer to data that is in our input buffer, but has not yet
570 * been consumed. This can be non-zero if functions like
571 * netsec_readline() leave leftover data.
573 * remaining is the remaining amount of unconsumed data in the input
576 * end is a pointer to the end of the valid data + 1; it's where
577 * the next read should go.
580 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
581 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
582 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
585 * If we're past the halfway point in our read buffers, shuffle everything
586 * back to the beginning.
589 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
590 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
591 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
593 remaining
= nsc
->ns_inbufsize
- nsc
->ns_inbuflen
;
594 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
598 * If we are using TLS, then just read via the BIO. But we still
599 * use our local buffer.
602 if (nsc
->tls_active
) {
603 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
609 * Check to see if we're supposed to retry; if so,
610 * then go back and read again.
613 if (BIO_should_retry(nsc
->ssl_io
))
617 * Okay, fine. Get the real error out of the SSL context.
620 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
621 netsec_err(errstr
, "SSL_read() returned 0, but cannot "
622 "retrieve SSL context");
626 errcode
= SSL_get_error(ssl
, rc
);
627 if (errcode
== SSL_ERROR_ZERO_RETURN
) {
628 netsec_err(errstr
, "TLS peer closed remote connection");
630 netsec_err(errstr
, "TLS network read failed: %s",
631 ERR_error_string(ERR_peek_last_error(), NULL
));
634 ERR_print_errors_fp(stderr
);
638 /* Definitely an error */
639 netsec_err(errstr
, "Read on TLS connection failed: %s",
640 ERR_error_string(ERR_get_error(), NULL
));
644 nsc
->ns_inbuflen
+= rc
;
648 #endif /* TLS_SUPPORT */
651 * Okay, time to read some data. Either we're just doing it straight
652 * or we're passing it through sasl_decode() first.
656 if (nsc
->sasl_seclayer
) {
657 readbuf
= nsc
->sasl_tmpbuf
;
658 readbufsize
= nsc
->sasl_maxbufsize
;
660 #endif /* CYRUS_SASL */
662 readbuf
= (char *) end
;
663 readbufsize
= remaining
;
667 * At this point, we should have active data on the connection (see
668 * select() above) so this read SHOULDN'T block. Hopefully.
671 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
674 netsec_err(errstr
, "Received EOF on network read");
679 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
684 * Okay, so we've had a successful read. If we are doing SASL security
685 * layers, pass this through sasl_decode(). sasl_decode() can return
686 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
687 * we can just update our length pointer.
691 if (nsc
->sasl_seclayer
) {
693 unsigned int tmpoutlen
;
695 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
696 &tmpout
, &tmpoutlen
);
699 netsec_err(errstr
, "Unable to decode SASL network data: %s",
700 sasl_errdetail(nsc
->sasl_conn
));
711 if (tmpoutlen
> remaining
) {
712 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
716 memcpy(end
, tmpout
, tmpoutlen
);
718 nsc
->ns_inbuflen
+= tmpoutlen
;
720 #endif /* CYRUS_SASL */
721 nsc
->ns_inbuflen
+= rc
;
727 * Write data to our network connection. Really, fill up the buffer as
728 * much as we can, and flush it out if necessary. netsec_flush() does
733 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
736 const unsigned char *bufptr
= buffer
;
745 * Run a loop copying in data to our local buffer; when we're done with
746 * any buffer overflows then just copy any remaining data in.
749 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
750 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
753 * In theory I should increment outptr, but netsec_flush just resets
756 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
758 rc
= netsec_flush(nsc
, errstr
);
768 * Copy any leftover data into the buffer.
772 memcpy(nsc
->ns_outptr
, bufptr
, size
);
773 nsc
->ns_outptr
+= size
;
774 nsc
->ns_outbuflen
+= size
;
781 * Our network printf() routine, which really just calls netsec_vprintf().
785 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
790 va_start(ap
, format
);
791 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
798 * Write bytes to the network using printf()-style formatting.
800 * Again, for the most part copy stuff into our buffer to be flushed
805 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
811 * Cheat a little. If we can fit the data into our outgoing buffer,
812 * great! If not, generate a flush and retry once.
816 rc
= vsnprintf((char *) nsc
->ns_outptr
,
817 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
819 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
821 * This means we have an overflow. Note that we don't actually
822 * make use of the terminating NUL, but according to the spec
823 * vsnprintf() won't write to the last byte in the string; that's
824 * why we have to use >= in the comparison above.
826 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
828 * Whoops, if the buffer pointer was the same as the start of the
829 * buffer, that means we overflowed the internal buffer.
830 * At that point, just give up.
832 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
833 "%d bytes, but our buffer size was only %d bytes",
834 rc
, nsc
->ns_outbufsize
);
838 * Generate a flush (which may be inefficient, but hopefully
839 * it isn't) and then try again.
841 if (netsec_flush(nsc
, errstr
) != OK
)
844 * After this, outbuffer should == outptr, so we shouldn't
845 * hit this next time around.
850 nsc
->ns_outptr
+= rc
;
851 nsc
->ns_outbuflen
+= rc
;
857 * Flush out any buffered data in our output buffers. This routine is
858 * actually where the real network writes take place.
862 netsec_flush(netsec_context
*nsc
, char **errstr
)
864 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
865 unsigned int netoutlen
= nsc
->ns_outbuflen
;
876 * If we have snoop turned on, output the data.
878 * Note here; if we don't have a CR or LF at the end, save the data
879 * in ns_snoop_savebuf for later and print it next time.
883 unsigned int snoopoutlen
= netoutlen
;
884 const char *snoopoutbuf
= (const char *) nsc
->ns_outbuffer
;
886 while (snoopoutlen
> 0) {
887 const char *end
= strpbrk(snoopoutbuf
, "\r\n");
891 if (nsc
->ns_snoop_savebuf
) {
892 nsc
->ns_snoop_savebuf
= mh_xrealloc(nsc
->ns_snoop_savebuf
,
893 strlen(nsc
->ns_snoop_savebuf
) +
895 strncat(nsc
->ns_snoop_savebuf
, snoopoutbuf
, snoopoutlen
);
897 nsc
->ns_snoop_savebuf
= mh_xmalloc(snoopoutlen
+ 1);
898 strncpy(nsc
->ns_snoop_savebuf
, snoopoutbuf
, snoopoutlen
);
899 nsc
->ns_snoop_savebuf
[snoopoutlen
] = '\0';
904 outlen
= end
- snoopoutbuf
;
907 if (nsc
->sasl_seclayer
)
908 fprintf(stderr
, "(sasl-encrypted) ");
909 #endif /* CYRUS_SASL */
912 fprintf(stderr
, "(tls-encrypted) ");
913 #endif /* TLS_SUPPORT */
914 fprintf(stderr
, "=> ");
915 if (nsc
->ns_snoop_cb
) {
917 unsigned int cb_len
= outlen
;
919 if (nsc
->ns_snoop_savebuf
) {
920 cb_len
+= strlen(nsc
->ns_snoop_savebuf
);
921 nsc
->ns_snoop_savebuf
= mh_xrealloc(nsc
->ns_snoop_savebuf
,
923 ptr
= nsc
->ns_snoop_savebuf
;
928 nsc
->ns_snoop_cb(nsc
, ptr
, cb_len
, nsc
->ns_snoop_context
);
930 if (nsc
->ns_snoop_savebuf
) {
931 free(nsc
->ns_snoop_savebuf
);
932 nsc
->ns_snoop_savebuf
= NULL
;
935 if (nsc
->ns_snoop_savebuf
) {
936 fprintf(stderr
, "%s", nsc
->ns_snoop_savebuf
);
937 free(nsc
->ns_snoop_savebuf
);
938 nsc
->ns_snoop_savebuf
= NULL
;
940 fprintf(stderr
, "%.*s\n", outlen
, snoopoutbuf
);
944 * Alright, hopefully any previous leftover data is done,
945 * and we have the current line output. Move things past the
949 snoopoutlen
-= outlen
;
950 snoopoutbuf
+= outlen
;
952 if (snoopoutlen
> 0 && *snoopoutbuf
== '\r') {
957 if (snoopoutlen
> 0 && *snoopoutbuf
== '\n') {
965 * If SASL security layers are in effect, run the data through
966 * sasl_encode() first.
969 if (nsc
->sasl_seclayer
) {
970 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
971 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
974 netsec_err(errstr
, "SASL data encoding failed: %s",
975 sasl_errdetail(nsc
->sasl_conn
));
980 #endif /* CYRUS_SASL */
983 * If TLS is active, then use those functions to write out the
987 if (nsc
->tls_active
) {
988 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
989 netsec_err(errstr
, "Error writing to TLS connection: %s",
990 ERR_error_string(ERR_get_error(), NULL
));
994 #endif /* TLS_SUPPORT */
996 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
999 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
1004 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1005 nsc
->ns_outbuflen
= 0;
1011 * Set various SASL protocol parameters
1015 netsec_set_sasl_params(netsec_context
*nsc
, const char *service
,
1016 const char *mechanism
, netsec_sasl_callback callback
,
1017 void *context
, char **errstr
)
1020 sasl_callback_t
*sasl_cbs
;
1023 if (!nsc
->ns_hostname
) {
1024 netsec_err(errstr
, "Internal error: ns_hostname is NULL");
1028 if (! sasl_initialized
) {
1029 retval
= sasl_client_init(NULL
);
1030 if (retval
!= SASL_OK
) {
1031 netsec_err(errstr
, "SASL client initialization failed: %s",
1032 sasl_errstring(retval
, NULL
, NULL
));
1039 * Allocate an array of SASL callbacks for this connection.
1040 * Right now we just allocate an array of four callbacks.
1043 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
1045 sasl_cbs
[0].id
= SASL_CB_USER
;
1046 sasl_cbs
[0].proc
= (sasl_callback_ft
) netsec_get_user
;
1047 sasl_cbs
[0].context
= nsc
;
1049 sasl_cbs
[1].id
= SASL_CB_AUTHNAME
;
1050 sasl_cbs
[1].proc
= (sasl_callback_ft
) netsec_get_user
;
1051 sasl_cbs
[1].context
= nsc
;
1053 sasl_cbs
[2].id
= SASL_CB_PASS
;
1054 sasl_cbs
[2].proc
= (sasl_callback_ft
) netsec_get_password
;
1055 sasl_cbs
[2].context
= nsc
;
1057 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
1058 sasl_cbs
[3].proc
= NULL
;
1059 sasl_cbs
[3].context
= NULL
;
1061 nsc
->sasl_cbs
= sasl_cbs
;
1063 retval
= sasl_client_new(service
, nsc
->ns_hostname
, NULL
, NULL
,
1064 nsc
->sasl_cbs
, 0, &nsc
->sasl_conn
);
1067 netsec_err(errstr
, "SASL new client allocation failed: %s",
1068 sasl_errstring(retval
, NULL
, NULL
));
1073 * Set up our credentials
1076 nsc
->sasl_creds
= nmh_get_credentials(nsc
->ns_hostname
, nsc
->ns_userid
);
1078 #else /* CYRUS_SASL */
1079 NMH_UNUSED(service
);
1081 #endif /* CYRUS_SASL */
1084 * According to the RFC, mechanisms can only be uppercase letter, numbers,
1085 * and a hyphen or underscore. So make sure we uppercase any letters
1086 * in case the user passed in lowercase.
1090 nsc
->sasl_mech
= mh_xstrdup(mechanism
);
1091 to_upper(nsc
->sasl_mech
);
1094 nsc
->sasl_proto_cb
= callback
;
1095 nsc
->sasl_proto_context
= context
;
1102 * Our userid callback; return the specified username to the SASL
1103 * library when asked.
1106 int netsec_get_user(void *context
, int id
, const char **result
,
1109 netsec_context
*nsc
= (netsec_context
*) context
;
1111 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
1112 return SASL_BADPARAM
;
1114 *result
= nmh_cred_get_user(nsc
->sasl_creds
);
1117 *len
= strlen(*result
);
1123 * Retrieve a password and return it to SASL
1127 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1128 sasl_secret_t
**psecret
)
1130 netsec_context
*nsc
= (netsec_context
*) context
;
1131 const char *password
;
1136 if (! psecret
|| id
!= SASL_CB_PASS
)
1137 return SASL_BADPARAM
;
1139 password
= nmh_cred_get_password(nsc
->sasl_creds
);
1141 len
= strlen(password
);
1144 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1145 * us room for a terminating NUL
1148 *psecret
= malloc(sizeof(sasl_secret_t
) + len
);
1153 (*psecret
)->len
= len
;
1154 strcpy((char *) (*psecret
)->data
, password
);
1156 nsc
->sasl_secret
= *psecret
;
1160 #endif /* CYRUS_SASL */
1163 * Negotiate SASL on this connection
1167 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1170 sasl_security_properties_t secprops
;
1171 const char *chosen_mech
;
1172 const unsigned char *saslbuf
;
1173 unsigned char *outbuf
;
1174 unsigned int saslbuflen
, outbuflen
;
1178 #ifdef OAUTH_SUPPORT
1179 unsigned char *xoauth_client_res
;
1180 size_t xoauth_client_res_len
;
1181 #endif /* OAUTH_SUPPORT */
1182 #if defined CYRUS_SASL || defined OAUTH_SUPPORT
1184 #endif /* CYRUS_SASL || OAUTH_SUPPORT */
1187 * If we've been passed a requested mechanism, check our mechanism
1188 * list from the protocol. If it's not supported, return an error.
1191 if (nsc
->sasl_mech
) {
1192 char **str
, *mlist
= getcpy(mechlist
);
1195 str
= brkstring(mlist
, " ", NULL
);
1197 for (i
= 0; str
[i
] != NULL
; i
++) {
1198 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1203 i
= (str
[i
] == NULL
);
1208 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1214 #ifdef OAUTH_SUPPORT
1215 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1217 * This should be relatively straightforward, but requires some
1218 * help from the plugin. Basically, if XOAUTH2 is a success,
1219 * the callback has to return success, but no output data. If
1220 * there is output data, it will be assumed that it is the JSON
1224 if (! nsc
->oauth_service
) {
1225 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1229 nsc
->sasl_chosen_mech
= mh_xstrdup(nsc
->sasl_mech
);
1231 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1232 &xoauth_client_res
, &xoauth_client_res_len
,
1233 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1234 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1239 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1240 xoauth_client_res_len
, NULL
, 0,
1241 nsc
->sasl_proto_context
, errstr
);
1242 free(xoauth_client_res
);
1248 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1249 * success, we indicate that with no output data. But if we
1250 * fail, then send a blank message and get the resulting
1254 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1255 nsc
->sasl_proto_context
, errstr
);
1259 * We're going to assume the error here is a JSON response;
1260 * we ignore it and send a blank message in response. We should
1261 * then get a failure messages with a useful error. We should
1262 * NOT get a success message at this point.
1265 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0,
1266 nsc
->sasl_proto_context
, NULL
);
1267 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1268 nsc
->sasl_proto_context
, errstr
);
1270 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1276 #endif /* OAUTH_SUPPORT */
1280 * In netsec_set_sasl_params, we've already done all of our setup with
1281 * sasl_client_init() and sasl_client_new(). So time to set security
1282 * properties, call sasl_client_start(), and generate the protocol
1287 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1290 * If we're using TLS, do not negotiate a security layer
1295 nsc
->tls_active
? 0 :
1296 #endif /* TLS_SUPPORT */
1299 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1301 if (rc
!= SASL_OK
) {
1302 netsec_err(errstr
, "SASL security property initialization failed: %s",
1303 sasl_errstring(rc
, NULL
, NULL
));
1308 * Start the actual protocol negotiation, and go through the
1309 * sasl_client_step() loop (after sasl_client_start, of course).
1312 rc
= sasl_client_start(nsc
->sasl_conn
,
1313 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1314 (const char **) &saslbuf
, &saslbuflen
,
1317 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1318 netsec_err(errstr
, "SASL client start failed: %s",
1319 sasl_errdetail(nsc
->sasl_conn
));
1323 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1325 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1326 nsc
->sasl_proto_context
, errstr
) != OK
)
1330 * We've written out our first message; enter in the step loop
1333 while (rc
== SASL_CONTINUE
) {
1335 * Call our SASL callback, which will handle the details of
1336 * reading data from the network.
1339 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1340 nsc
->sasl_proto_context
, errstr
) != OK
) {
1341 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1342 nsc
->sasl_proto_context
, NULL
);
1346 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1347 (const char **) &saslbuf
, &saslbuflen
);
1351 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1352 netsec_err(errstr
, "SASL client negotiation failed: %s",
1353 sasl_errdetail(nsc
->sasl_conn
));
1354 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1355 nsc
->sasl_proto_context
, NULL
);
1359 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1360 NULL
, 0, nsc
->sasl_proto_context
,
1362 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1363 nsc
->sasl_proto_context
, NULL
);
1369 * SASL exchanges should be complete, process the final response message
1373 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1374 nsc
->sasl_proto_context
, errstr
) != OK
) {
1376 * At this point we can't really send an abort since the SASL dialog
1377 * has completed, so just bubble back up the error message.
1384 * At this point, SASL should be complete. Get a few properties
1385 * from the authentication exchange.
1388 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1390 if (rc
!= SASL_OK
) {
1391 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1392 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1396 nsc
->sasl_ssf
= *ssf
;
1398 if (nsc
->sasl_ssf
> 0) {
1399 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1400 (const void **) &outbufmax
);
1402 if (rc
!= SASL_OK
) {
1403 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1404 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1409 * If our output buffer isn't the same size as the input buffer,
1410 * reallocate it and set the new size (since we won't encode any
1411 * data larger than that).
1414 nsc
->sasl_maxbufsize
= *outbufmax
;
1416 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1417 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1418 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1419 nsc
->ns_outbufsize
);
1421 * There shouldn't be any data in the buffer, but for
1422 * consistency's sake discard it.
1424 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1425 nsc
->ns_outbuflen
= 0;
1429 * Allocate a buffer to do temporary reads into, before we
1430 * call sasl_decode()
1433 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1436 * Okay, this is a bit weird. Make sure that the input buffer
1437 * is at least TWICE the size of the max buffer size. That's
1438 * because if we're consuming data but want to extend the current
1439 * buffer, we want to be sure there's room for another full buffer's
1443 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1444 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1445 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1446 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1447 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1450 nsc
->sasl_seclayer
= 1;
1456 * If we're at this point, then either we have NEITHER OAuth2 or
1457 * Cyrus-SASL compiled in, or have OAuth2 but didn't give the XOAUTH2
1458 * mechanism on the command line.
1461 if (! nsc
->sasl_mech
)
1462 netsec_err(errstr
, "SASL library support not available; please "
1463 "specify a SASL mechanism to use");
1465 netsec_err(errstr
, "No support for the %s SASL mechanism",
1469 #endif /* CYRUS_SASL */
1473 * Retrieve our chosen SASL mechanism
1477 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1479 return nsc
->sasl_chosen_mech
;
1483 * Return the negotiated SASL strength security factor (SSF)
1487 netsec_get_sasl_ssf(netsec_context
*nsc
)
1490 return nsc
->sasl_ssf
;
1491 #else /* CYRUS_SASL */
1493 #endif /* CYRUS_SASL */
1497 * Set an OAuth2 service name, if we support it.
1501 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1503 #ifdef OAUTH_SUPPORT
1504 nsc
->oauth_service
= getcpy(service
);
1506 #else /* OAUTH_SUPPORT */
1508 NMH_UNUSED(service
);
1510 #endif /* OAUTH_SUPPORT */
1514 * Initialize (and enable) TLS for this connection
1518 netsec_set_tls(netsec_context
*nsc
, int tls
, int noverify
, char **errstr
)
1523 BIO
*rbio
, *wbio
, *ssl_bio
;
1525 if (! tls_initialized
) {
1527 SSL_load_error_strings();
1530 * Create the SSL context; this has the properties for all
1531 * SSL connections (we are only doing one), though. Make sure
1532 * we only support secure (known as of now) TLS protocols.
1535 sslctx
= SSL_CTX_new(SSLv23_client_method());
1538 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1539 ERR_error_string(ERR_get_error(), NULL
));
1543 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1546 if (!SSL_CTX_set_default_verify_paths(sslctx
)) {
1547 netsec_err(errstr
, "Unable to set default certificate "
1548 "verification paths: %s",
1549 ERR_error_string(ERR_get_error(), NULL
));
1556 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1557 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1562 * Create the SSL structure which holds the data for a single
1566 ssl
= SSL_new(sslctx
);
1569 netsec_err(errstr
, "Unable to create SSL connection: %s",
1570 ERR_error_string(ERR_get_error(), NULL
));
1575 * Never bother us, since we are using blocking sockets.
1578 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1581 * This is a bit weird, so pay attention.
1583 * We create a socket BIO, and bind it to our SSL connection.
1584 * That means reads and writes to the SSL connection will use our
1587 * Then we create an SSL BIO, and assign our current SSL connection
1588 * to it. This is done so our code stays simple if we want to use
1589 * any buffering BIOs (right now we do our own buffering).
1590 * So the chain looks like:
1592 * SSL BIO -> socket BIO.
1595 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_CLOSE
);
1598 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1599 ERR_error_string(ERR_get_error(), NULL
));
1604 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_CLOSE
);
1607 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1608 ERR_error_string(ERR_get_error(), NULL
));
1614 SSL_set_bio(ssl
, rbio
, wbio
);
1615 SSL_set_connect_state(ssl
);
1618 * If noverify is NOT set, then do certificate validation.
1619 * Turning on SSL_VERIFY_PEER will verify the certificate chain
1620 * against locally stored root certificates (the locations are
1621 * set using SSL_CTX_set_default_verify_paths()), and we put
1622 * the hostname in the X509 verification parameters so the OpenSSL
1623 * code will verify that the hostname appears in the server
1628 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1629 X509_VERIFY_PARAM
*param
;
1630 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1632 SSL_set_verify(ssl
, SSL_VERIFY_PEER
, NULL
);
1633 if (! nsc
->ns_hostname
) {
1634 netsec_err(errstr
, "Internal error: hostname not set and "
1635 "certification verification enabled");
1640 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1641 param
= SSL_get0_param(ssl
);
1643 if (! X509_VERIFY_PARAM_set1_host(param
, nsc
->ns_hostname
, 0)) {
1644 netsec_err(errstr
, "Unable to add hostname %s to cert "
1645 "verification parameters: %s", nsc
->ns_hostname
,
1646 ERR_error_string(ERR_get_error(), NULL
));
1650 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1653 ssl_bio
= BIO_new(BIO_f_ssl());
1656 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1657 ERR_error_string(ERR_get_error(), NULL
));
1662 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1663 nsc
->ssl_io
= ssl_bio
;
1666 * Since SSL now owns these file descriptors, have it handle the
1667 * closing of them instead of netsec_shutdown().
1670 nsc
->ns_noclose
= 1;
1674 BIO_free_all(nsc
->ssl_io
);
1677 #else /* TLS_SUPPORT */
1679 NMH_UNUSED(noverify
);
1682 netsec_err(errstr
, "TLS is not supported");
1685 #endif /* TLS_SUPPORT */
1691 * Start TLS negotiation on this connection
1695 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1698 if (! nsc
->ssl_io
) {
1699 netsec_err(errstr
, "TLS has not been configured for this connection");
1703 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1704 unsigned long errcode
= ERR_get_error();
1707 * Print a more detailed message if it was certificate verification
1711 if (ERR_GET_LIB(errcode
) == ERR_LIB_SSL
&&
1712 ERR_GET_REASON(errcode
) == SSL_R_CERTIFICATE_VERIFY_FAILED
) {
1715 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1716 netsec_err(errstr
, "Certificate verification failed, but "
1717 "cannot retrieve SSL handle: %s",
1718 ERR_error_string(ERR_get_error(), NULL
));
1720 netsec_err(errstr
, "Server certificate verification failed: %s",
1721 X509_verify_cert_error_string(
1722 SSL_get_verify_result(ssl
)));
1725 netsec_err(errstr
, "TLS negotiation failed: %s",
1726 ERR_error_string(errcode
, NULL
));
1730 * Because negotiation failed, shut down TLS so we don't get any
1731 * garbage on the connection. Because of weirdness with SSL_shutdown,
1732 * we end up calling it twice: once explicitly, once as part of
1736 BIO_ssl_shutdown(nsc
->ssl_io
);
1737 BIO_free_all(nsc
->ssl_io
);
1743 if (nsc
->ns_snoop
) {
1746 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1747 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1749 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1750 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1751 SSL_CIPHER_get_name(cipher
),
1752 SSL_CIPHER_get_bits(cipher
, NULL
),
1753 SSL_CIPHER_get_version(cipher
));
1754 SSL_SESSION_print_fp(stderr
, SSL_get_session(ssl
));
1758 nsc
->tls_active
= 1;
1761 #else /* TLS_SUPPORT */
1763 netsec_err(errstr
, "TLS not supported");
1766 #endif /* TLS_SUPPORT */
1770 * Generate an (allocated) error string
1774 netsec_err(char **errstr
, const char *fmt
, ...)
1778 char *errbuf
= NULL
;
1785 errbufsize
= rc
+ 1;
1786 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1788 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1790 } while (rc
>= (int) errbufsize
);