Exemple #1
0
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;
}
Exemple #2
0
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;
}
Exemple #3
0
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;
}
Exemple #4
0
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;
}