static int o_stream_ssl_flush_buffer(struct ssl_ostream *sstream) { size_t pos = 0; int ret = 1; while (pos < sstream->buffer->used) { /* we're writing plaintext data to OpenSSL, which it encrypts and writes to bio_int's buffer. ssl_iostream_bio_sync() reads it from there and adds to plain_output stream. */ ret = SSL_write(sstream->ssl_io->ssl, CONST_PTR_OFFSET(sstream->buffer->data, pos), sstream->buffer->used - pos); if (ret <= 0) { ret = openssl_iostream_handle_write_error(sstream->ssl_io, ret, "SSL_write"); if (ret < 0) { io_stream_set_error(&sstream->ostream.iostream, "%s", sstream->ssl_io->last_error); sstream->ostream.ostream.stream_errno = errno; break; } if (ret == 0) break; } else { pos += ret; (void)openssl_iostream_bio_sync(sstream->ssl_io); } } buffer_delete(sstream->buffer, 0, pos); return ret <= 0 ? ret : 1; }
int openssl_iostream_more(struct ssl_iostream *ssl_io) { int ret; if (!ssl_io->handshaked) { if ((ret = ssl_iostream_handshake(ssl_io)) <= 0) return ret; } (void)openssl_iostream_bio_sync(ssl_io); return 1; }
static int openssl_iostream_handshake(struct ssl_iostream *ssl_io) { const char *error = NULL; int ret; i_assert(!ssl_io->handshaked); if (ssl_io->ctx->client_ctx) { while ((ret = SSL_connect(ssl_io->ssl)) <= 0) { ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_connect()"); if (ret <= 0) return ret; } } else { while ((ret = SSL_accept(ssl_io->ssl)) <= 0) { ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_accept()"); if (ret <= 0) return ret; } } /* handshake finished */ (void)openssl_iostream_bio_sync(ssl_io); if (ssl_io->handshake_callback != NULL) { if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) < 0) { i_assert(error != NULL); i_stream_close(ssl_io->plain_input); o_stream_close(ssl_io->plain_output); openssl_iostream_set_error(ssl_io, error); ssl_io->handshake_failed = TRUE; errno = EINVAL; return -1; } } i_free_and_null(ssl_io->last_error); ssl_io->handshaked = TRUE; if (ssl_io->ssl_output != NULL) (void)o_stream_flush(ssl_io->ssl_output); return 1; }
static int openssl_iostream_handle_error_full(struct ssl_iostream *ssl_io, int ret, const char *func_name, bool write_error) { const char *errstr = NULL; int err; err = SSL_get_error(ssl_io->ssl, ret); switch (err) { case SSL_ERROR_WANT_WRITE: if (!openssl_iostream_bio_sync(ssl_io)) { if (!write_error) i_panic("SSL ostream buffer size not unlimited"); return 0; } if (ssl_io->closed) { errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return 1; case SSL_ERROR_WANT_READ: ssl_io->want_read = TRUE; (void)openssl_iostream_bio_sync(ssl_io); if (ssl_io->closed) { errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return ssl_io->want_read ? 0 : 1; case SSL_ERROR_SYSCALL: /* eat up the error queue */ if (ERR_peek_error() != 0) { errstr = openssl_iostream_error(); errno = EINVAL; } else if (ret != 0) { i_assert(errno != 0); errstr = strerror(errno); } else { /* EOF. */ errno = ECONNRESET; errstr = "Disconnected"; break; } errstr = t_strdup_printf("%s syscall failed: %s", func_name, errstr); break; case SSL_ERROR_ZERO_RETURN: /* clean connection closing */ errno = ECONNRESET; break; case SSL_ERROR_SSL: errstr = t_strdup_printf("%s failed: %s", func_name, openssl_iostream_error()); errno = EINVAL; break; default: errstr = t_strdup_printf("%s failed: unknown failure %d (%s)", func_name, err, openssl_iostream_error()); errno = EINVAL; break; } if (errstr != NULL) openssl_iostream_set_error(ssl_io, errstr); return -1; }