/*++ * NAME * tls_bio_ops 3 * SUMMARY * TLS network BIO management * SYNOPSIS * #include * #include * * int tls_bio_connect(fd, timeout, context) * int fd; * int timeout; * TLS_SESS_STATE *context; * * int tls_bio_accept(fd, timeout, context) * int fd; * int timeout; * TLS_SESS_STATE *context; * * int tls_bio_shutdown(fd, timeout, context) * int fd; * int timeout; * TLS_SESS_STATE *context; * * int tls_bio_read(fd, buf, len, timeout, context) * int fd; * void *buf; * int len; * int timeout; * TLS_SESS_STATE *context; * * int tls_bio_write(fd, buf, len, timeout, context) * int fd; * void *buf; * int len; * int timeout; * TLS_SESS_STATE *context; * DESCRIPTION * This layer synchronizes the TLS network buffers with the network * while performing TLS handshake or input/output operations. * * When the TLS layer is active, it converts plain-text * data from Postfix into encrypted network data and vice versa. * However, to handle network timeout conditions, Postfix * needs to maintain control over network input/output. This * rules out the usual approach of placing the TLS layer * between the application and the network socket. * * As shown below, Postfix reads/writes plain-text data from/to * the TLS layer. The TLS layer informs Postfix when it needs * to read/write encrypted data from/to the network; Postfix * then reads/writes encrypted data from/to the TLS layer and * takes care of the network socket I/O. * * The TLS layer to network interface is realized with a BIO pair: * * Postfix | TLS layer * | * smtp/smtpd | * /\ || | * || \/ | * vstream read/write <===> TLS read/write/etc * | /\ || * | || \/ * | BIO pair (internal_bio) * | BIO pair (network_bio) * | /\ || * | || \/ * socket read/write <===> BIO read/write * /\ || | * || \/ | * network | * * The Postfix VSTREAM read/write operations invoke the SSL * read/write operations to send and retrieve plain-text data. Inside * the TLS layer the data are converted to/from TLS protocol. * * Whenever an SSL operation reports success, or whenever it * indicates that network input/output needs to happen, Postfix * uses the BIO read/write routines to synchronize the * network_bio buffer with the network. Writing data to the * network has precedence over reading from the network. This * is necessary to avoid deadlock. * * The BIO pair buffer size is set to 8192 bytes. This is much * larger than the typical Path MTU, and avoids sending tiny TCP * segments. It is also larger than the default VSTREAM_BUFSIZE * (4096, see vstream.h), so that large write operations can * be handled within one request. The internal buffer in the * network/network_bio handling layer is set to the same * value, since this seems to be reasonable. The code is * however able to handle arbitrary values smaller or larger * than the buffer size in the BIO pair. * * tls_bio_connect() performs the SSL_connect() operation while * synchronizing the network_bio buffer with the network. * * tls_bio_accept() performs the SSL_accept() operation while * synchronizing the network_bio buffer with the network. * * tls_bio_shutdown() performs the SSL_shutdown() operation while * synchronizing the network_bio buffer with the network. * * tls_bio_read() performs the SSL_read() operation while * synchronizing the network_bio buffer with the network. * * tls_bio_write() performs the SSL_write() operation while * synchronizing the network_bio buffer with the network. * * Arguments: * .IP fd * Network socket. * .IP buf * Read/write buffer. * .IP len * Read/write request size. * .IP timeout * Read/write timeout. * .IP TLScontext * TLS session state. * DIAGNOSTICS * The result value is -1 in case of a network read/write * error, otherwise it is the result value of the TLS operation. * LICENSE * .ad * .fi * This software is free. You can do with it whatever you want. * The original author kindly requests that you acknowledge * the use of his software. * AUTHOR(S) * Originally written by: * Lutz Jaenicke * BTU Cottbus * Allgemeine Elektrotechnik * Universitaetsplatz 3-4 * D-03044 Cottbus, Germany * * Updated by: * Wietse Venema * IBM T.J. Watson Research * P.O. Box 704 * Yorktown Heights, NY 10598, USA * * Victor Duchovni * Morgan Stanley *--*/ /* System library. */ #include "StdAfx.h" #ifdef USE_TLS /* TLS library. */ #include "tls.h" #include "tls_private.h" /* Application-specific. */ #define NETLAYER_BUFFERSIZE 8192 /* network_biopair_interop - synchronize network with BIO pair */ static int network_biopair_interop(ACL_SOCKET fd, int timeout, BIO *network_bio) { const char *myname = "network_biopair_interop"; int want_write; int num_write; int write_pos; int from_bio; int want_read; int num_read; int to_bio; char buffer[NETLAYER_BUFFERSIZE]; /* * To avoid deadlock, write all pending data to the network before * attempting to read from the network. */ while ((want_write = (int) BIO_ctrl_pending(network_bio)) > 0) { if (want_write > (int) sizeof(buffer)) want_write = (int) sizeof(buffer); from_bio = BIO_read(network_bio, buffer, want_write); /* * Write the complete buffer contents to the network. */ for (write_pos = 0; write_pos < from_bio; /* see below */ ) { if (timeout > 0 && acl_write_wait(fd, timeout) < 0) return (-1); num_write = acl_socket_write(fd, buffer + write_pos, from_bio - write_pos, 0, 0, 0); if (num_write <= 0) { if ((num_write < 0) && (timeout > 0) && (errno == ACL_EAGAIN || errno == ACL_EINTR)) { acl_msg_warn("%s: write() returns EAGAIN on a writable file descriptor!", myname); acl_msg_warn("%s: pausing to avoid going into a tight select/write loop!", myname); sleep(1); } else { acl_msg_warn("%s: error writing %d bytes to the network: %s", myname, from_bio - write_pos, acl_last_serror()); return (-1); } } else { write_pos += num_write; } } } /* * Read data from the network into the BIO pair. */ while ((want_read = (int) BIO_ctrl_get_read_request(network_bio)) > 0) { if (want_read > (int) sizeof(buffer)) want_read = (int) sizeof(buffer); if (timeout > 0 && acl_read_wait(fd, timeout) < 0) return (-1); num_read = acl_socket_read(fd, buffer, want_read, 0, 0, 0); if (num_read == 0) /* FIX 200412 Cannot return a zero read count. */ return (-1); if (num_read < 0) { if ((num_read < 0) && (timeout > 0) && (errno == ACL_EAGAIN || errno == ACL_EINTR)) { acl_msg_warn("%s: read() returns EAGAIN on a readable file descriptor!", myname); acl_msg_warn("%s: pausing to avoid going into a tight select/write loop!", myname); sleep(1); } else { acl_msg_warn("%s: error reading %d bytes from the network: %s", myname, want_read, acl_last_serror()); return (-1); } } else { to_bio = BIO_write(network_bio, buffer, num_read); if (to_bio != num_read) acl_msg_panic("%s: BIO_write error: to_bio != num_read", myname); } } return (0); } /* tls_bio - perform SSL input/output operation with extreme prejudice */ int tls_bio(ACL_SOCKET fd, int timeout, TLS_SESS_STATE *TLScontext, int (*hsfunc) (SSL *), int (*rfunc) (SSL *, void *, int), int (*wfunc) (SSL *, const void *, int), void *buf, int num) { const char *myname = "tls_bio"; int status = 0; int err; int retval = 0; int biop_retval; int done; /* * If necessary, retry the SSL handshake or read/write operation after * handling any pending network I/O. */ for (done = 0; done == 0; /* void */ ) { if (hsfunc) { #if 1 status = hsfunc(TLScontext->con); #else status = SSL_do_handshake(TLScontext->con); #endif } else if (rfunc) status = rfunc(TLScontext->con, buf, num); else if (wfunc) status = wfunc(TLScontext->con, buf, num); else acl_msg_panic("%s: nothing to do here", myname); err = SSL_get_error(TLScontext->con, status); #if (OPENSSL_VERSION_NUMBER <= 0x0090581fL) /* * There is a bug up to and including OpenSSL-0.9.5a: if an error * occurs while checking the peers certificate due to some * certificate error (e.g. as happend with a RSA-padding error), the * error is put onto the error stack. If verification is not * enforced, this error should be ignored, but the error-queue is not * cleared, so we can find this error here. The bug has been fixed on * May 28, 2000. * * This bug so far has only manifested as 4800:error:0407006A:rsa * routines:RSA_padding_check_PKCS1_type_1:block type is not * 01:rsa_pk1.c:100: 4800:error:04067072:rsa * routines:RSA_EAY_PUBLIC_DECRYPT:padding check * failed:rsa_eay.c:396: 4800:error:0D079006:asn1 encoding * routines:ASN1_verify:bad get asn1 object call:a_verify.c:109: so * that we specifically test for this error. We print the errors to * the logfile and automatically clear the error queue. Then we retry * to get another error code. We cannot do better, since we can only * retrieve the last entry of the error-queue without actually * cleaning it on the way. * * This workaround is secure, as verify_result is set to "failed" * anyway. */ if (err == SSL_ERROR_SSL) { if (ERR_peek_error() == 0x0407006AL) { tls_print_errors(); acl_msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored"); err = SSL_get_error(TLScontext->con, status); } } #endif /* * Find out if we must retry the operation and/or if there is pending * network I/O. * * XXX If we're the first to invoke SSL_shutdown(), then the operation * isn't really complete when the call returns. We could hide that * anomaly here and repeat the call. */ switch (err) { case SSL_ERROR_NONE: /* success */ retval = status; done = 1; /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* flush/update buffers */ case SSL_ERROR_WANT_READ: biop_retval = network_biopair_interop(fd, timeout, TLScontext->network_bio); if (biop_retval < 0) return (-1); /* network read/write error */ break; /* * With tls_timed_read() and tls_timed_write() the caller is the * VSTREAM library module which is unaware of TLS, so we log the * TLS error stack here. In a better world, each VSTREAM I/O * object would provide an error reporting method in addition to * the timed_read and timed_write methods, so that we would not * need to have ad-hoc code like this. */ case SSL_ERROR_SSL: if (rfunc || wfunc) tls_print_errors(); /* FALLTHROUGH */ default: retval = status; done = 1; break; } } return (retval); } #endif