/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "istream-private.h"
#include "ostream-private.h"
#include "iostream-openssl.h"
{
char *new_str;
/* i_debug() may sometimes be overriden, making it write to this very
same SSL stream, in which case the provided str may be invalidated
before it is even used. Therefore, we duplicate it immediately. */
/* This error should normally be logged by lib-ssl-iostream's
caller. But if verbose=TRUE, log it here as well to make
sure that the error is always logged. */
}
}
{
if ((where & SSL_CB_ALERT) != 0) {
switch (ret & 0xff) {
case SSL_AD_CLOSE_NOTIFY:
i_debug("%sSSL alert: %s",
break;
default:
i_debug("%sSSL alert: where=0x%x, ret=%d: %s %s",
break;
}
} else if (ret == 0) {
i_debug("%sSSL failed: where=0x%x: %s",
} else {
i_debug("%sSSL: where=0x%x, ret=%d: %s",
}
}
static int
const char **error_r)
{
X509 *x;
int ret = 0;
return -1;
}
if (x != NULL) {
if (ERR_peek_error() != 0)
ret = 0;
X509_free(x);
}
if (ret == 0) {
return -1;
}
return 0;
}
static int
const struct ssl_iostream_cert *set,
const char **error_r)
{
int ret = 0;
return -1;
ret = -1;
}
return ret;
}
static int
{
certname[0] = '\0';
else
if (preverify_ok == 0) {
"Received invalid SSL certificate: %s: %s",
if (ssl_io->verbose_invalid_cert)
}
if (preverify_ok == 0) {
if (!ssl_io->allow_invalid_cert) {
return 0;
}
}
return 1;
}
static int
const struct ssl_iostream_settings *set,
const char **error_r)
{
int verify_flags;
"Can't set cipher list to '%s': %s",
return -1;
}
}
"Failed to set curve list to '%s'",
set->curve_list);
return -1;
}
}
#endif
if (set->prefer_server_ciphers)
#if defined(HAVE_SSL_CLEAR_OPTIONS)
#endif
long opts;
int min_protocol;
&min_protocol) < 0) {
"Unknown ssl_min_protocol setting '%s'",
set->min_protocol);
return -1;
}
#else
#endif
}
return -1;
}
return -1;
}
return -1;
}
return -1;
}
if (set->verify_remote_cert) {
else
}
"Invalid cert_username_field: %s",
return -1;
}
} else {
}
return 0;
}
static int
const struct ssl_iostream_settings *set,
struct ssl_iostream **iostream_r,
const char **error_r)
{
/* Don't allow an existing io_add_istream() to be use on the input.
It would seem to work, but it would also cause hangs. */
return -1;
}
/* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e).
Each of the BIOs have one "write buffer". BIO_write() copies data
to them, while BIO_read() reads from the other BIO's write buffer
into the given buffer. The bio_int is used by OpenSSL and bio_ext
is used by this library. */
return -1;
}
/* bio_int will be freed by SSL_free() */
#ifdef HAVE_SSL_GET_SERVERNAME
#endif
return -1;
}
*iostream_r = ssl_io;
return 0;
}
{
}
{
return;
}
{
/* if bidirectional shutdown fails we need to clear
the error queue */
}
/* close the plain i/o streams, because their fd may be closed soon,
but we may still keep this ssl-iostream referenced until later. */
}
{
int ret;
/* bytes contains how many SSL encrypted bytes we should be
sending out */
if (max_bytes == 0) {
/* wait until output buffer clears */
TRUE);
break;
}
}
/* BIO_read() is guaranteed to return all the bytes that
BIO_ctrl_pending() returned */
/* we limited number of read bytes to plain_output's
available size. this send() is guaranteed to either
fully succeed or completely fail due to some error. */
if (sent < 0) {
break;
}
bytes_sent = TRUE;
}
return bytes_sent;
}
static ssize_t
{
if (*size_r > 0)
return 0;
if (type == OPENSSL_IOSTREAM_SYNC_TYPE_CONTINUE_READ) {
/* only the first i_stream_read() call attempts to read more
input. the following reads will just process the buffered
data. */
return 0;
}
return -1;
return 0;
}
{
const unsigned char *data;
int ret;
/* bytes contains how many bytes we can write to bio_ext */
}
return FALSE;
}
if (size == 0) {
/* wait for more input */
break;
}
bytes_read = TRUE;
}
/* shouldn't happen */
i_error("SSL BIO buffer size too small");
i_strdup("SSL BIO buffer size too small");
return FALSE;
}
i_error("SSL: Too much data in buffered plain input buffer");
i_strdup("SSL: Too much data in buffered plain input buffer");
return FALSE;
}
if (bytes_read) {
if (ssl_io->ostream_flush_waiting_input) {
}
if (type != OPENSSL_IOSTREAM_SYNC_TYPE_FIRST_READ &&
}
return bytes_read;
}
{
bool ret;
return ret;
}
{
int ret;
if (!ssl_io->handshaked) {
return ret;
}
return 1;
}
{
if (ssl_io->plain_stream_errno != 0) {
} else {
}
}
const char *func_name)
{
int err;
switch (err) {
case SSL_ERROR_WANT_WRITE:
if (type != OPENSSL_IOSTREAM_SYNC_TYPE_WRITE)
i_panic("SSL ostream buffer size not unlimited");
return 0;
}
return -1;
}
return 1;
case SSL_ERROR_WANT_READ:
return -1;
}
case SSL_ERROR_SYSCALL:
/* eat up the error queue */
if (ERR_peek_error() != 0) {
} else if (ret != 0) {
} else {
/* EOF. */
errstr = "Disconnected";
break;
}
break;
case SSL_ERROR_ZERO_RETURN:
/* clean connection closing */
if (ssl_io->handshaked)
errstr = "SSL connection closed during handshake";
break;
}
return -1;
case SSL_ERROR_SSL:
break;
default:
break;
}
return -1;
}
static bool
const char *verify_name, const char **reason_r)
{
if (ssl_io->allow_invalid_cert)
return TRUE;
if (!ssl_iostream_has_valid_client_cert(ssl_io)) {
*reason_r = "Invalid certificate";
return FALSE;
}
}
{
int ret;
OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE, "SSL_connect()");
if (ret <= 0)
return ret;
}
} else {
OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE, "SSL_accept()");
if (ret <= 0)
return ret;
}
}
/* handshake finished */
}
"SSL certificate doesn't match expected host name %s: %s",
}
}
if (ssl_io->handshake_failed) {
return -1;
}
return 1;
}
static void
void *context)
{
}
static void
void *context)
{
}
static void
struct ssl_iostream_context *ctx)
{
}
}
const char *prefix)
{
}
{
return ssl_io->handshaked;
}
static bool
{
return ssl_io->handshake_failed;
}
static bool
{
}
static bool
{
}
static const char *
{
char *name;
int len;
return NULL;
if (len < 0)
name = "";
else {
name = "";
/* NUL characters in name. Someone's trying to fake
being another user? Don't allow it. */
name = "";
}
}
}
{
}
static const char *
{
#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
#else
return NULL;
#endif
}
static const char *
{
#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
#endif
const char *comp_str;
if (!ssl_io->handshaked)
return "";
#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
#else
comp_str = "";
#endif
return t_strdup_printf("%s with cipher %s (%d/%d bits)%s",
}
static const char *
{
return ssl_io->last_error;
}
static const char *
{
if (!ssl_io->handshaked)
return NULL;
return SSL_CIPHER_get_name(cipher);
}
static const char *
{
if (!ssl_io->handshaked)
return NULL;
#if defined(HAVE_SSL_CIPHER_get_kx_nid)
return OBJ_nid2sn(nid);
#else
char buf[128];
return "";
#endif
}
static const char *
{
if (!ssl_io->handshaked)
return NULL;
}
};
void ssl_iostream_openssl_init(void)
{
unsigned char buf;
i_fatal("OpenSSL RNG failed to initialize");
}
void ssl_iostream_openssl_deinit(void)
{
}