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.
10 #include "credentials.h"
12 #include "brkstring.h"
17 #include <sys/select.h>
21 #include <sasl/sasl.h>
22 #include <sasl/saslutil.h>
23 # if SASL_VERSION_FULL < 0x020125
24 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
25 which has an explicit void parameter list, according to best
26 practice. So we need to cast to avoid compile warnings.
27 Provide this prototype for earlier versions. */
28 typedef int (*sasl_callback_ft
)();
29 # endif /* SASL_VERSION_FULL < 0x020125 */
31 static int netsec_get_user(void *context
, int id
, const char **result
,
33 static int netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
34 sasl_secret_t
**psecret
);
36 static bool sasl_initialized
;
38 #define SASL_MAXRECVBUF 65536
39 #endif /* CYRUS_SASL */
42 #include <openssl/ssl.h>
43 #include <openssl/err.h>
45 static bool tls_initialized
;
46 static SSL_CTX
*sslctx
= NULL
; /* SSL Context */
48 #endif /* TLS_SUPPORT */
50 /* I'm going to hardcode this for now; maybe make it adjustable later? */
51 #define NETSEC_BUFSIZE 65536
54 * Our context structure, which holds all of the relevant information
58 struct _netsec_context
{
59 int ns_readfd
; /* Read descriptor for network connection */
60 int ns_writefd
; /* Write descriptor for network connection */
61 int ns_noclose
; /* Do not close file descriptors if set */
62 int ns_snoop
; /* If true, display network data */
63 netsec_snoop_callback
*ns_snoop_cb
; /* Snoop output callback */
64 void *ns_snoop_context
; /* Context data for snoop function */
65 char *ns_snoop_savebuf
; /* Save buffer for snoop data */
66 int ns_timeout
; /* Network read timeout, in seconds */
67 char *ns_userid
; /* Userid for authentication */
68 char *ns_hostname
; /* Hostname we've connected to */
69 unsigned char *ns_inbuffer
; /* Our read input buffer */
70 unsigned char *ns_inptr
; /* Our read buffer input pointer */
71 unsigned int ns_inbuflen
; /* Length of data in input buffer */
72 unsigned int ns_inbufsize
; /* Size of input buffer */
73 unsigned char *ns_outbuffer
;/* Output buffer */
74 unsigned char *ns_outptr
; /* Output buffer pointer */
75 unsigned int ns_outbuflen
; /* Output buffer data length */
76 unsigned int ns_outbufsize
; /* Output buffer size */
77 char *sasl_mech
; /* User-requested mechanism */
78 char *sasl_chosen_mech
; /* Mechanism chosen by SASL */
79 netsec_sasl_callback sasl_proto_cb
; /* SASL callback we use */
80 void *sasl_proto_context
; /* Context to be used by SASL callback */
82 char *oauth_service
; /* OAuth2 service name */
83 #endif /* OAUTH_SUPPORT */
85 sasl_conn_t
*sasl_conn
; /* SASL connection context */
86 sasl_ssf_t sasl_ssf
; /* SASL Security Strength Factor */
87 sasl_callback_t
*sasl_cbs
; /* Callbacks used by SASL */
88 nmh_creds_t sasl_creds
; /* Credentials (username/password) */
89 sasl_secret_t
*sasl_secret
; /* SASL password structure */
90 int sasl_seclayer
; /* If true, SASL security layer is enabled */
91 char *sasl_tmpbuf
; /* Temporary read buffer for decodes */
92 size_t sasl_maxbufsize
; /* Maximum negotiated SASL buffer size */
93 #endif /* CYRUS_SASL */
95 BIO
*ssl_io
; /* BIO used for connection I/O */
96 int tls_active
; /* If true, TLS is running */
97 #endif /* TLS_SUPPORT */
101 * Function to read data from the actual network socket
104 static int netsec_fillread(netsec_context
*ns_context
, char **errstr
);
107 * Code to check the ASCII content of a byte array.
110 static int checkascii(const unsigned char *byte
, size_t len
);
113 * How this code works, in general.
115 * _If_ we are using no encryption then we buffer the network data
116 * through ns_inbuffer and ns_outbuffer. That should be relatively
119 * If we use encryption, then ns_inbuffer and ns_outbuffer contain the
120 * cleartext data. When it comes time to send the encrypted data on the
121 * (either from a flush or the buffer is full) we either use BIO_write()
122 * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For
123 * reads we either use BIO_read() (TLS) or do a network read into a
124 * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if
125 * negotiate TLS then we disable SASL encryption.
127 * We used to use a buffering BIO for the reads/writes for TLS, but it
128 * ended up being complicated to special-case the buffering for everything
129 * except TLS, so the buffering is now unified, no matter which encryption
130 * method is being used (even none).
132 * For SASL authentication, we make use of (for now) the Cyrus-SASL
133 * library. For some mechanisms, we implement those mechanisms directly
134 * since the Cyrus SASL library doesn't support them (like OAuth).
138 * Allocate and initialize our security context
148 nsc
->ns_writefd
= -1;
151 nsc
->ns_snoop_cb
= NULL
;
152 nsc
->ns_snoop_context
= NULL
;
153 nsc
->ns_snoop_savebuf
= NULL
;
154 nsc
->ns_userid
= NULL
;
155 nsc
->ns_hostname
= NULL
;
156 nsc
->ns_timeout
= 60; /* Our default */
157 nsc
->ns_inbufsize
= NETSEC_BUFSIZE
;
158 nsc
->ns_inbuffer
= mh_xmalloc(nsc
->ns_inbufsize
);
159 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
160 nsc
->ns_inbuflen
= 0;
161 nsc
->ns_outbufsize
= NETSEC_BUFSIZE
;
162 nsc
->ns_outbuffer
= mh_xmalloc(nsc
->ns_outbufsize
);
163 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
164 nsc
->ns_outbuflen
= 0;
165 nsc
->sasl_mech
= NULL
;
166 nsc
->sasl_chosen_mech
= NULL
;
167 nsc
->sasl_proto_cb
= NULL
;
168 nsc
->sasl_proto_context
= NULL
;
170 nsc
->oauth_service
= NULL
;
171 #endif /* OAUTH_SUPPORT */
173 nsc
->sasl_conn
= NULL
;
174 nsc
->sasl_cbs
= NULL
;
175 nsc
->sasl_creds
= NULL
;
176 nsc
->sasl_secret
= NULL
;
178 nsc
->sasl_seclayer
= 0;
179 nsc
->sasl_tmpbuf
= NULL
;
180 nsc
->sasl_maxbufsize
= 0;
181 #endif /* CYRUS_SASL */
185 #endif /* TLS_SUPPORT */
190 * Shutdown the connection completely and free all resources.
194 netsec_shutdown(netsec_context
*nsc
)
196 free(nsc
->ns_userid
);
197 free(nsc
->ns_hostname
);
198 free(nsc
->ns_inbuffer
);
199 free(nsc
->ns_outbuffer
);
200 free(nsc
->sasl_mech
);
201 free(nsc
->sasl_chosen_mech
);
202 free(nsc
->ns_snoop_savebuf
);
204 free(nsc
->oauth_service
);
205 #endif /* OAUTH_SERVICE */
208 sasl_dispose(&nsc
->sasl_conn
);
211 nmh_credentials_free(nsc
->sasl_creds
);
212 if (nsc
->sasl_secret
) {
213 if (nsc
->sasl_secret
->len
> 0) {
214 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
216 free(nsc
->sasl_secret
);
218 free(nsc
->sasl_tmpbuf
);
219 #endif /* CYRUS_SASL */
223 * I checked; BIO_free_all() will cause SSL_shutdown to be called
224 * on the SSL object in the chain.
226 BIO_free_all(nsc
->ssl_io
);
227 #endif /* TLS_SUPPORT */
229 if (! nsc
->ns_noclose
) {
230 if (nsc
->ns_readfd
!= -1)
231 close(nsc
->ns_readfd
);
232 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
233 close(nsc
->ns_writefd
);
240 * Set the file descriptor for our context
244 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
246 nsc
->ns_readfd
= readfd
;
247 nsc
->ns_writefd
= writefd
;
251 * Set the userid used for authentication for this context
255 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
257 nsc
->ns_userid
= getcpy(userid
);
261 * Set the hostname of the remote host we're connecting to.
265 netsec_set_hostname(netsec_context
*nsc
, const char *hostname
)
267 nsc
->ns_hostname
= mh_xstrdup(hostname
);
271 * Get the snoop flag for this connection
275 netsec_get_snoop(netsec_context
*nsc
)
277 return nsc
->ns_snoop
;
281 * Set the snoop flag for this connection
285 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
287 nsc
->ns_snoop
= snoop
;
291 * Set the snoop callback for this connection.
295 netsec_set_snoop_callback(netsec_context
*nsc
,
296 netsec_snoop_callback callback
, void *context
)
298 nsc
->ns_snoop_cb
= callback
;
299 nsc
->ns_snoop_context
= context
;
303 * A base64-decoding snoop callback
307 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
310 unsigned char *decoded
;
315 offset
= context
? *((int *) context
) : 0;
319 * Output non-base64 data first.
321 fprintf(stderr
, "%.*s", offset
, string
);
326 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
328 * Some mechanisms produce large binary tokens, which aren't really
329 * readable. So let's do a simple heuristic. If the token is greater
330 * than 100 characters _and_ the first 100 bytes are more than 50%
331 * non-ASCII, then don't print the decoded buffer, just the
334 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
335 fprintf(stderr
, "%.*s\n", (int) len
, string
);
338 hexify(decoded
, decodedlen
, &hexified
);
339 fprintf(stderr
, "b64<%s>\n", hexified
);
344 fprintf(stderr
, "%.*s\n", (int) len
, string
);
349 * If the ASCII content is > 50%, return 1
353 checkascii(const unsigned char *bytes
, size_t len
)
355 size_t count
= 0, half
= len
/ 2;
358 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
361 /* No chance by this point */
362 if (count
+ len
< half
)
370 * Set the read timeout for this connection
374 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
376 nsc
->ns_timeout
= timeout
;
380 * Read data from the network. Basically, return anything in our buffer,
381 * otherwise fill from the network.
385 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
390 * If our buffer is empty, then we should fill it now
393 if (nsc
->ns_inbuflen
== 0) {
394 if (netsec_fillread(nsc
, errstr
) != OK
)
399 * netsec_fillread only returns if the buffer is full, so we can
400 * assume here that this has something in it.
403 retlen
= min(size
, nsc
->ns_inbuflen
);
405 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
407 if (retlen
== (int) nsc
->ns_inbuflen
) {
409 * We've emptied our buffer, so reset everything.
411 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
412 nsc
->ns_inbuflen
= 0;
414 nsc
->ns_inptr
+= size
;
415 nsc
->ns_inbuflen
-= size
;
422 * Get a "line" (CR/LF) terminated from the network.
424 * Okay, we play some games here, so pay attention:
426 * - Unlike every other function, we return a pointer to the
427 * existing buffer. This pointer is valid until you call another
428 * read function again.
429 * - We NUL-terminate the buffer right at the end, before the CR-LF terminator.
430 * - Technically we look for a LF; if we find a CR right before it, then
432 * - If your data may contain embedded NULs, this won't work. You should
433 * be using netsec_read() in that case.
437 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
439 unsigned char *ptr
= nsc
->ns_inptr
;
440 size_t count
= 0, offset
;
444 * Search through our existing buffer for a LF
447 while (count
< nsc
->ns_inbuflen
) {
449 if (*ptr
++ == '\n') {
450 char *sptr
= (char *) nsc
->ns_inptr
;
451 if (count
> 1 && *(ptr
- 2) == '\r')
455 *len
= ptr
- nsc
->ns_inptr
;
456 nsc
->ns_inptr
+= count
;
457 nsc
->ns_inbuflen
-= count
;
460 if (nsc
->sasl_seclayer
)
461 fprintf(stderr
, "(sasl-decrypted) ");
462 #endif /* CYRUS_SASL */
465 fprintf(stderr
, "(tls-decrypted) ");
466 #endif /* TLS_SUPPORT */
467 fprintf(stderr
, "<= ");
468 if (nsc
->ns_snoop_cb
)
469 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
470 nsc
->ns_snoop_context
);
481 * Hm, we didn't find a \n. If we've already searched half of the input
482 * buffer, return an error.
485 if (count
>= nsc
->ns_inbufsize
/ 2) {
486 netsec_err(errstr
, "Unable to find a line terminator after %zu bytes",
492 * Okay, get some more network data. This may move inptr, so regenerate
496 offset
= ptr
- nsc
->ns_inptr
;
498 if (netsec_fillread(nsc
, errstr
) != OK
)
501 ptr
= nsc
->ns_inptr
+ offset
;
505 return NULL
; /* Should never reach this */
509 * Fill our read buffer with some data from the network.
513 netsec_fillread(netsec_context
*nsc
, char **errstr
)
517 size_t readbufsize
, remaining
, startoffset
;
521 * If inbuflen is zero, that means the buffer has been emptied
522 * completely. In that case move inptr back to the start.
525 if (nsc
->ns_inbuflen
== 0) {
526 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
529 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
531 #endif /* CYRUS_SASL || TLS_SUPPORT */
533 * If we are using TLS and there's anything pending, then skip the
537 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
538 #endif /* TLS_SUPPORT */
544 FD_SET(nsc
->ns_readfd
, &rfds
);
546 tv
.tv_sec
= nsc
->ns_timeout
;
549 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
552 netsec_err(errstr
, "select() while reading failed: %s",
558 netsec_err(errstr
, "read() timed out after %d seconds",
564 * At this point, we know that rc is 1, so there's not even any
565 * point to check to see if our descriptor is set in rfds.
572 * startoffset is the offset from the beginning of the input
573 * buffer to data that is in our input buffer, but has not yet
574 * been consumed. This can be non-zero if functions like
575 * netsec_readline() leave leftover data.
577 * remaining is the remaining amount of unconsumed data in the input
580 * end is a pointer to the end of the valid data + 1; it's where
581 * the next read should go.
584 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
585 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
586 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
589 * If we're past the halfway point in our read buffers, shuffle everything
590 * back to the beginning.
593 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
594 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
595 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
597 remaining
= nsc
->ns_inbufsize
- nsc
->ns_inbuflen
;
598 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
602 * If we are using TLS, then just read via the BIO. But we still
603 * use our local buffer.
606 if (nsc
->tls_active
) {
607 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
613 * Check to see if we're supposed to retry; if so,
614 * then go back and read again.
617 if (BIO_should_retry(nsc
->ssl_io
))
621 * Okay, fine. Get the real error out of the SSL context.
624 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
625 netsec_err(errstr
, "SSL_read() returned 0, but cannot "
626 "retrieve SSL context");
630 errcode
= SSL_get_error(ssl
, rc
);
631 if (errcode
== SSL_ERROR_ZERO_RETURN
) {
632 netsec_err(errstr
, "TLS peer closed remote connection");
634 netsec_err(errstr
, "TLS network read failed: %s",
635 ERR_error_string(ERR_peek_last_error(), NULL
));
638 ERR_print_errors_fp(stderr
);
642 /* Definitely an error */
643 netsec_err(errstr
, "Read on TLS connection failed: %s",
644 ERR_error_string(ERR_get_error(), NULL
));
648 nsc
->ns_inbuflen
+= rc
;
652 #endif /* TLS_SUPPORT */
655 * Okay, time to read some data. Either we're just doing it straight
656 * or we're passing it through sasl_decode() first.
660 if (nsc
->sasl_seclayer
) {
661 readbuf
= nsc
->sasl_tmpbuf
;
662 readbufsize
= nsc
->sasl_maxbufsize
;
664 #endif /* CYRUS_SASL */
666 readbuf
= (char *) end
;
667 readbufsize
= remaining
;
671 * At this point, we should have active data on the connection (see
672 * select() above) so this read SHOULDN'T block. Hopefully.
675 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
678 netsec_err(errstr
, "Received EOF on network read");
683 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
688 * Okay, so we've had a successful read. If we are doing SASL security
689 * layers, pass this through sasl_decode(). sasl_decode() can return
690 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
691 * we can just update our length pointer.
695 if (nsc
->sasl_seclayer
) {
697 unsigned int tmpoutlen
;
699 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
700 &tmpout
, &tmpoutlen
);
703 netsec_err(errstr
, "Unable to decode SASL network data: %s",
704 sasl_errdetail(nsc
->sasl_conn
));
715 if (tmpoutlen
> remaining
) {
716 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
720 memcpy(end
, tmpout
, tmpoutlen
);
722 nsc
->ns_inbuflen
+= tmpoutlen
;
724 #endif /* CYRUS_SASL */
725 nsc
->ns_inbuflen
+= rc
;
731 * Write data to our network connection. Really, fill up the buffer as
732 * much as we can, and flush it out if necessary. netsec_flush() does
737 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
740 const unsigned char *bufptr
= buffer
;
749 * Run a loop copying in data to our local buffer; when we're done with
750 * any buffer overflows then just copy any remaining data in.
753 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
754 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
757 * In theory I should increment outptr, but netsec_flush just resets
760 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
762 rc
= netsec_flush(nsc
, errstr
);
772 * Copy any leftover data into the buffer.
776 memcpy(nsc
->ns_outptr
, bufptr
, size
);
777 nsc
->ns_outptr
+= size
;
778 nsc
->ns_outbuflen
+= size
;
785 * Our network printf() routine, which really just calls netsec_vprintf().
789 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
794 va_start(ap
, format
);
795 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
802 * Write bytes to the network using printf()-style formatting.
804 * Again, for the most part copy stuff into our buffer to be flushed
809 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
816 * Cheat a little. If we can fit the data into our outgoing buffer,
817 * great! If not, generate a flush and retry once.
822 rc
= vsnprintf((char *) nsc
->ns_outptr
,
823 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, apcopy
);
826 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
828 * This means we have an overflow. Note that we don't actually
829 * make use of the terminating NUL, but according to the spec
830 * vsnprintf() won't write to the last byte in the string; that's
831 * why we have to use >= in the comparison above.
833 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
835 * Whoops, if the buffer pointer was the same as the start of the
836 * buffer, that means we overflowed the internal buffer.
837 * At that point, just give up.
839 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
840 "%d bytes, but our buffer size was only %d bytes",
841 rc
, nsc
->ns_outbufsize
);
845 * Generate a flush (which may be inefficient, but hopefully
846 * it isn't) and then try again.
848 if (netsec_flush(nsc
, errstr
) != OK
)
851 * After this, outbuffer should == outptr, so we shouldn't
852 * hit this next time around.
857 nsc
->ns_outptr
+= rc
;
858 nsc
->ns_outbuflen
+= rc
;
864 * Flush out any buffered data in our output buffers. This routine is
865 * actually where the real network writes take place.
869 netsec_flush(netsec_context
*nsc
, char **errstr
)
871 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
872 unsigned int netoutlen
= nsc
->ns_outbuflen
;
883 * If we have snoop turned on, output the data.
885 * Note here; if we don't have a CR or LF at the end, save the data
886 * in ns_snoop_savebuf for later and print it next time.
890 unsigned int snoopoutlen
= netoutlen
;
891 const char *snoopoutbuf
= (const char *) nsc
->ns_outbuffer
;
893 while (snoopoutlen
> 0) {
894 const char *end
= strpbrk(snoopoutbuf
, "\r\n");
898 if (nsc
->ns_snoop_savebuf
) {
899 nsc
->ns_snoop_savebuf
= mh_xrealloc(nsc
->ns_snoop_savebuf
,
900 strlen(nsc
->ns_snoop_savebuf
) +
902 strncat(nsc
->ns_snoop_savebuf
, snoopoutbuf
, snoopoutlen
);
904 nsc
->ns_snoop_savebuf
= mh_xmalloc(snoopoutlen
+ 1);
905 strncpy(nsc
->ns_snoop_savebuf
, snoopoutbuf
, snoopoutlen
);
906 nsc
->ns_snoop_savebuf
[snoopoutlen
] = '\0';
911 outlen
= end
- snoopoutbuf
;
914 if (nsc
->sasl_seclayer
)
915 fprintf(stderr
, "(sasl-encrypted) ");
916 #endif /* CYRUS_SASL */
919 fprintf(stderr
, "(tls-encrypted) ");
920 #endif /* TLS_SUPPORT */
921 fprintf(stderr
, "=> ");
922 if (nsc
->ns_snoop_cb
) {
924 unsigned int cb_len
= outlen
;
926 if (nsc
->ns_snoop_savebuf
) {
927 cb_len
+= strlen(nsc
->ns_snoop_savebuf
);
928 nsc
->ns_snoop_savebuf
= mh_xrealloc(nsc
->ns_snoop_savebuf
,
930 ptr
= nsc
->ns_snoop_savebuf
;
935 nsc
->ns_snoop_cb(nsc
, ptr
, cb_len
, nsc
->ns_snoop_context
);
937 if (nsc
->ns_snoop_savebuf
) {
938 free(nsc
->ns_snoop_savebuf
);
939 nsc
->ns_snoop_savebuf
= NULL
;
942 if (nsc
->ns_snoop_savebuf
) {
943 fprintf(stderr
, "%s", nsc
->ns_snoop_savebuf
);
944 free(nsc
->ns_snoop_savebuf
);
945 nsc
->ns_snoop_savebuf
= NULL
;
947 fprintf(stderr
, "%.*s\n", outlen
, snoopoutbuf
);
951 * Alright, hopefully any previous leftover data is done,
952 * and we have the current line output. Move things past the
956 snoopoutlen
-= outlen
;
957 snoopoutbuf
+= outlen
;
959 if (snoopoutlen
> 0 && *snoopoutbuf
== '\r') {
964 if (snoopoutlen
> 0 && *snoopoutbuf
== '\n') {
972 * If SASL security layers are in effect, run the data through
973 * sasl_encode() first.
976 if (nsc
->sasl_seclayer
) {
977 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
978 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
981 netsec_err(errstr
, "SASL data encoding failed: %s",
982 sasl_errdetail(nsc
->sasl_conn
));
987 #endif /* CYRUS_SASL */
990 * If TLS is active, then use those functions to write out the
994 if (nsc
->tls_active
) {
995 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
996 netsec_err(errstr
, "Error writing to TLS connection: %s",
997 ERR_error_string(ERR_get_error(), NULL
));
1001 #endif /* TLS_SUPPORT */
1003 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
1006 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
1011 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1012 nsc
->ns_outbuflen
= 0;
1018 * Set various SASL protocol parameters
1022 netsec_set_sasl_params(netsec_context
*nsc
, const char *service
,
1023 const char *mechanism
, netsec_sasl_callback callback
,
1024 void *context
, char **errstr
)
1027 sasl_callback_t
*sasl_cbs
;
1030 if (!nsc
->ns_hostname
) {
1031 netsec_err(errstr
, "Internal error: ns_hostname is NULL");
1035 if (! sasl_initialized
) {
1036 retval
= sasl_client_init(NULL
);
1037 if (retval
!= SASL_OK
) {
1038 netsec_err(errstr
, "SASL client initialization failed: %s",
1039 sasl_errstring(retval
, NULL
, NULL
));
1042 sasl_initialized
= true;
1046 * Allocate an array of SASL callbacks for this connection.
1047 * Right now we just allocate an array of four callbacks.
1050 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
1052 sasl_cbs
[0].id
= SASL_CB_USER
;
1053 sasl_cbs
[0].proc
= (sasl_callback_ft
) netsec_get_user
;
1054 sasl_cbs
[0].context
= nsc
;
1056 sasl_cbs
[1].id
= SASL_CB_AUTHNAME
;
1057 sasl_cbs
[1].proc
= (sasl_callback_ft
) netsec_get_user
;
1058 sasl_cbs
[1].context
= nsc
;
1060 sasl_cbs
[2].id
= SASL_CB_PASS
;
1061 sasl_cbs
[2].proc
= (sasl_callback_ft
) netsec_get_password
;
1062 sasl_cbs
[2].context
= nsc
;
1064 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
1065 sasl_cbs
[3].proc
= NULL
;
1066 sasl_cbs
[3].context
= NULL
;
1068 nsc
->sasl_cbs
= sasl_cbs
;
1070 retval
= sasl_client_new(service
, nsc
->ns_hostname
, NULL
, NULL
,
1071 nsc
->sasl_cbs
, 0, &nsc
->sasl_conn
);
1074 netsec_err(errstr
, "SASL new client allocation failed: %s",
1075 sasl_errstring(retval
, NULL
, NULL
));
1080 * Set up our credentials
1083 nsc
->sasl_creds
= nmh_get_credentials(nsc
->ns_hostname
, nsc
->ns_userid
);
1085 #else /* CYRUS_SASL */
1086 NMH_UNUSED(service
);
1088 #endif /* CYRUS_SASL */
1091 * According to the RFC, mechanisms can only be uppercase letter, numbers,
1092 * and a hyphen or underscore. So make sure we uppercase any letters
1093 * in case the user passed in lowercase.
1097 nsc
->sasl_mech
= mh_xstrdup(mechanism
);
1098 to_upper(nsc
->sasl_mech
);
1101 nsc
->sasl_proto_cb
= callback
;
1102 nsc
->sasl_proto_context
= context
;
1109 * Our userid callback; return the specified username to the SASL
1110 * library when asked.
1114 netsec_get_user(void *context
, int id
, const char **result
,
1117 netsec_context
*nsc
= (netsec_context
*) context
;
1119 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
1120 return SASL_BADPARAM
;
1122 *result
= nmh_cred_get_user(nsc
->sasl_creds
);
1125 *len
= strlen(*result
);
1131 * Retrieve a password and return it to SASL
1135 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1136 sasl_secret_t
**psecret
)
1138 netsec_context
*nsc
= (netsec_context
*) context
;
1139 const char *password
;
1144 if (! psecret
|| id
!= SASL_CB_PASS
)
1145 return SASL_BADPARAM
;
1147 password
= nmh_cred_get_password(nsc
->sasl_creds
);
1149 len
= strlen(password
);
1152 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1153 * us room for a terminating NUL
1156 *psecret
= malloc(sizeof(sasl_secret_t
) + len
);
1161 (*psecret
)->len
= len
;
1162 strcpy((char *) (*psecret
)->data
, password
);
1164 nsc
->sasl_secret
= *psecret
;
1168 #endif /* CYRUS_SASL */
1171 * Negotiate SASL on this connection
1175 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1178 sasl_security_properties_t secprops
;
1179 const char *chosen_mech
;
1180 const unsigned char *saslbuf
;
1181 unsigned char *outbuf
;
1182 unsigned int saslbuflen
, outbuflen
;
1186 #ifdef OAUTH_SUPPORT
1187 unsigned char *xoauth_client_res
;
1188 size_t xoauth_client_res_len
;
1189 #endif /* OAUTH_SUPPORT */
1190 #if defined CYRUS_SASL || defined OAUTH_SUPPORT
1192 #endif /* CYRUS_SASL || OAUTH_SUPPORT */
1195 * If we've been passed a requested mechanism, check our mechanism
1196 * list from the protocol. If it's not supported, return an error.
1199 if (nsc
->sasl_mech
) {
1200 char **str
, *mlist
= getcpy(mechlist
);
1203 str
= brkstring(mlist
, " ", NULL
);
1205 for (i
= 0; str
[i
] != NULL
; i
++) {
1206 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1211 i
= (str
[i
] == NULL
);
1216 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1222 #ifdef OAUTH_SUPPORT
1223 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1225 * This should be relatively straightforward, but requires some
1226 * help from the plugin. Basically, if XOAUTH2 is a success,
1227 * the callback has to return success, but no output data. If
1228 * there is output data, it will be assumed that it is the JSON
1232 if (! nsc
->oauth_service
) {
1233 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1237 nsc
->sasl_chosen_mech
= mh_xstrdup(nsc
->sasl_mech
);
1239 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1240 &xoauth_client_res
, &xoauth_client_res_len
,
1241 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1242 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1247 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1248 xoauth_client_res_len
, NULL
, 0,
1249 nsc
->sasl_proto_context
, errstr
);
1250 free(xoauth_client_res
);
1256 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1257 * success, we indicate that with no output data. But if we
1258 * fail, then send a blank message and get the resulting
1262 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1263 nsc
->sasl_proto_context
, errstr
);
1267 * We're going to assume the error here is a JSON response;
1268 * we ignore it and send a blank message in response. We should
1269 * then get a failure messages with a useful error. We should
1270 * NOT get a success message at this point.
1273 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0,
1274 nsc
->sasl_proto_context
, NULL
);
1275 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1276 nsc
->sasl_proto_context
, errstr
);
1278 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1284 #endif /* OAUTH_SUPPORT */
1288 * In netsec_set_sasl_params, we've already done all of our setup with
1289 * sasl_client_init() and sasl_client_new(). So time to set security
1290 * properties, call sasl_client_start(), and generate the protocol
1295 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1298 * If we're using TLS, do not negotiate a security layer
1303 nsc
->tls_active
? 0 :
1304 #endif /* TLS_SUPPORT */
1307 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1309 if (rc
!= SASL_OK
) {
1310 netsec_err(errstr
, "SASL security property initialization failed: %s",
1311 sasl_errstring(rc
, NULL
, NULL
));
1316 * Start the actual protocol negotiation, and go through the
1317 * sasl_client_step() loop (after sasl_client_start, of course).
1320 rc
= sasl_client_start(nsc
->sasl_conn
,
1321 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1322 (const char **) &saslbuf
, &saslbuflen
,
1325 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1326 netsec_err(errstr
, "SASL client start failed: %s",
1327 sasl_errdetail(nsc
->sasl_conn
));
1331 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1333 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1334 nsc
->sasl_proto_context
, errstr
) != OK
)
1338 * We've written out our first message; enter in the step loop
1341 while (rc
== SASL_CONTINUE
) {
1343 * Call our SASL callback, which will handle the details of
1344 * reading data from the network.
1347 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1348 nsc
->sasl_proto_context
, errstr
) != OK
) {
1349 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1350 nsc
->sasl_proto_context
, NULL
);
1354 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1355 (const char **) &saslbuf
, &saslbuflen
);
1359 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1360 netsec_err(errstr
, "SASL client negotiation failed: %s",
1361 sasl_errdetail(nsc
->sasl_conn
));
1362 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1363 nsc
->sasl_proto_context
, NULL
);
1367 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1368 NULL
, 0, nsc
->sasl_proto_context
,
1370 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0,
1371 nsc
->sasl_proto_context
, NULL
);
1377 * SASL exchanges should be complete, process the final response message
1381 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1382 nsc
->sasl_proto_context
, errstr
) != OK
) {
1384 * At this point we can't really send an abort since the SASL dialog
1385 * has completed, so just bubble back up the error message.
1392 * At this point, SASL should be complete. Get a few properties
1393 * from the authentication exchange.
1396 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1398 if (rc
!= SASL_OK
) {
1399 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1400 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1404 nsc
->sasl_ssf
= *ssf
;
1406 if (nsc
->sasl_ssf
> 0) {
1407 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1408 (const void **) &outbufmax
);
1410 if (rc
!= SASL_OK
) {
1411 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1412 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1417 * If our output buffer isn't the same size as the input buffer,
1418 * reallocate it and set the new size (since we won't encode any
1419 * data larger than that).
1422 nsc
->sasl_maxbufsize
= *outbufmax
;
1424 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1425 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1426 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1427 nsc
->ns_outbufsize
);
1429 * There shouldn't be any data in the buffer, but for
1430 * consistency's sake discard it.
1432 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1433 nsc
->ns_outbuflen
= 0;
1437 * Allocate a buffer to do temporary reads into, before we
1438 * call sasl_decode()
1441 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1444 * Okay, this is a bit weird. Make sure that the input buffer
1445 * is at least TWICE the size of the max buffer size. That's
1446 * because if we're consuming data but want to extend the current
1447 * buffer, we want to be sure there's room for another full buffer's
1451 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1452 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1453 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1454 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1455 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1458 nsc
->sasl_seclayer
= 1;
1464 * If we're at this point, then either we have NEITHER OAuth2 or
1465 * Cyrus-SASL compiled in, or have OAuth2 but didn't give the XOAUTH2
1466 * mechanism on the command line.
1469 if (! nsc
->sasl_mech
)
1470 netsec_err(errstr
, "SASL library support not available; please "
1471 "specify a SASL mechanism to use");
1473 netsec_err(errstr
, "No support for the %s SASL mechanism",
1477 #endif /* CYRUS_SASL */
1481 * Retrieve our chosen SASL mechanism
1485 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1487 return nsc
->sasl_chosen_mech
;
1491 * Return the negotiated SASL strength security factor (SSF)
1495 netsec_get_sasl_ssf(netsec_context
*nsc
)
1498 return nsc
->sasl_ssf
;
1499 #else /* CYRUS_SASL */
1501 #endif /* CYRUS_SASL */
1505 * Set an OAuth2 service name, if we support it.
1509 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1511 #ifdef OAUTH_SUPPORT
1512 nsc
->oauth_service
= getcpy(service
);
1514 #else /* OAUTH_SUPPORT */
1516 NMH_UNUSED(service
);
1518 #endif /* OAUTH_SUPPORT */
1522 * Initialize (and enable) TLS for this connection
1526 netsec_set_tls(netsec_context
*nsc
, int tls
, int noverify
, char **errstr
)
1531 BIO
*rbio
, *wbio
, *ssl_bio
;
1533 if (! tls_initialized
) {
1535 SSL_load_error_strings();
1538 * Create the SSL context; this has the properties for all
1539 * SSL connections (we are only doing one), though. Make sure
1540 * we only support secure (known as of now) TLS protocols.
1543 sslctx
= SSL_CTX_new(SSLv23_client_method());
1546 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1547 ERR_error_string(ERR_get_error(), NULL
));
1551 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1554 if (!SSL_CTX_set_default_verify_paths(sslctx
)) {
1555 netsec_err(errstr
, "Unable to set default certificate "
1556 "verification paths: %s",
1557 ERR_error_string(ERR_get_error(), NULL
));
1561 tls_initialized
= true;
1564 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1565 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1570 * Create the SSL structure which holds the data for a single
1574 ssl
= SSL_new(sslctx
);
1577 netsec_err(errstr
, "Unable to create SSL connection: %s",
1578 ERR_error_string(ERR_get_error(), NULL
));
1583 * Never bother us, since we are using blocking sockets.
1586 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1589 * This is a bit weird, so pay attention.
1591 * We create a socket BIO, and bind it to our SSL connection.
1592 * That means reads and writes to the SSL connection will use our
1595 * Then we create an SSL BIO, and assign our current SSL connection
1596 * to it. This is done so our code stays simple if we want to use
1597 * any buffering BIOs (right now we do our own buffering).
1598 * So the chain looks like:
1600 * SSL BIO -> socket BIO.
1603 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_CLOSE
);
1606 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1607 ERR_error_string(ERR_get_error(), NULL
));
1612 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_CLOSE
);
1615 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1616 ERR_error_string(ERR_get_error(), NULL
));
1622 SSL_set_bio(ssl
, rbio
, wbio
);
1623 SSL_set_connect_state(ssl
);
1626 * If noverify is NOT set, then do certificate validation.
1627 * Turning on SSL_VERIFY_PEER will verify the certificate chain
1628 * against locally stored root certificates (the locations are
1629 * set using SSL_CTX_set_default_verify_paths()), and we put
1630 * the hostname in the X509 verification parameters so the OpenSSL
1631 * code will verify that the hostname appears in the server
1636 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1637 X509_VERIFY_PARAM
*param
;
1638 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1640 SSL_set_verify(ssl
, SSL_VERIFY_PEER
, NULL
);
1641 if (! nsc
->ns_hostname
) {
1642 netsec_err(errstr
, "Internal error: hostname not set and "
1643 "certification verification enabled");
1648 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1649 param
= SSL_get0_param(ssl
);
1651 if (! X509_VERIFY_PARAM_set1_host(param
, nsc
->ns_hostname
, 0)) {
1652 netsec_err(errstr
, "Unable to add hostname %s to cert "
1653 "verification parameters: %s", nsc
->ns_hostname
,
1654 ERR_error_string(ERR_get_error(), NULL
));
1658 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1661 ssl_bio
= BIO_new(BIO_f_ssl());
1664 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1665 ERR_error_string(ERR_get_error(), NULL
));
1670 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1671 nsc
->ssl_io
= ssl_bio
;
1674 * Since SSL now owns these file descriptors, have it handle the
1675 * closing of them instead of netsec_shutdown().
1678 nsc
->ns_noclose
= 1;
1682 BIO_free_all(nsc
->ssl_io
);
1685 #else /* TLS_SUPPORT */
1687 NMH_UNUSED(noverify
);
1690 netsec_err(errstr
, "TLS is not supported");
1693 #endif /* TLS_SUPPORT */
1699 * Start TLS negotiation on this connection
1703 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1706 if (! nsc
->ssl_io
) {
1707 netsec_err(errstr
, "TLS has not been configured for this connection");
1711 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1712 unsigned long errcode
= ERR_get_error();
1715 * Print a more detailed message if it was certificate verification
1719 if (ERR_GET_LIB(errcode
) == ERR_LIB_SSL
&&
1720 ERR_GET_REASON(errcode
) == SSL_R_CERTIFICATE_VERIFY_FAILED
) {
1723 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1724 netsec_err(errstr
, "Certificate verification failed, but "
1725 "cannot retrieve SSL handle: %s",
1726 ERR_error_string(ERR_get_error(), NULL
));
1728 netsec_err(errstr
, "Server certificate verification failed: %s",
1729 X509_verify_cert_error_string(
1730 SSL_get_verify_result(ssl
)));
1733 netsec_err(errstr
, "TLS negotiation failed: %s",
1734 ERR_error_string(errcode
, NULL
));
1738 * Because negotiation failed, shut down TLS so we don't get any
1739 * garbage on the connection. Because of weirdness with SSL_shutdown,
1740 * we end up calling it twice: once explicitly, once as part of
1744 BIO_ssl_shutdown(nsc
->ssl_io
);
1745 BIO_free_all(nsc
->ssl_io
);
1751 if (nsc
->ns_snoop
) {
1754 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1755 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1757 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1758 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1759 SSL_CIPHER_get_name(cipher
),
1760 SSL_CIPHER_get_bits(cipher
, NULL
),
1761 SSL_CIPHER_get_version(cipher
));
1762 SSL_SESSION_print_fp(stderr
, SSL_get_session(ssl
));
1766 nsc
->tls_active
= 1;
1769 #else /* TLS_SUPPORT */
1771 netsec_err(errstr
, "TLS not supported");
1774 #endif /* TLS_SUPPORT */
1778 * Generate an (allocated) error string
1782 netsec_err(char **errstr
, const char *fmt
, ...)
1786 char *errbuf
= NULL
;
1793 errbufsize
= rc
+ 1;
1794 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1796 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1798 } while (rc
>= (int) errbufsize
);