Beispiel #1
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;
}
Beispiel #2
0
static int
openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
{
	int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
	SSL *ssl;
	struct ssl_iostream *ssl_io;
	char certname[1024];
	X509_NAME *subject;

	ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_extidx);
	ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
	ssl_io->cert_received = TRUE;

	subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx));
	if (subject == NULL ||
	    X509_NAME_oneline(subject, certname, sizeof(certname)) == NULL)
		certname[0] = '\0';
	else
		certname[sizeof(certname)-1] = '\0'; /* just in case.. */
	if (preverify_ok == 0) {
		openssl_iostream_set_error(ssl_io, t_strdup_printf(
			"Received invalid SSL certificate: %s: %s",
			X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), certname));
		if (ssl_io->verbose_invalid_cert)
			i_info("%s", ssl_io->last_error);
	} else if (ssl_io->verbose) {
		i_info("Received valid SSL certificate: %s", certname);
	}
	if (preverify_ok == 0) {
		ssl_io->cert_broken = TRUE;
		if (!ssl_io->allow_invalid_cert) {
			ssl_io->handshake_failed = TRUE;
			return 0;
		}
	}
	return 1;
}
Beispiel #3
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;
}