bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "lib.h"
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen#include "istream-private.h"
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen#include "ostream-private.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "iostream-openssl.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
98c2cac72e2032f223050a4edd229993c1d5c1f0Aki Tuomi#include <openssl/rand.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include <openssl/err.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_free(struct ssl_iostream *ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenvoid openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen{
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch char *new_str;
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch /* i_debug() may sometimes be overriden, making it write to this very
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch same SSL stream, in which case the provided str may be invalidated
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch before it is even used. Therefore, we duplicate it immediately. */
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch new_str = i_strdup(str);
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen if (ssl_io->verbose) {
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen /* This error should normally be logged by lib-ssl-iostream's
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen caller. But if verbose=TRUE, log it here as well to make
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen sure that the error is always logged. */
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch i_debug("%sSSL error: %s", ssl_io->log_prefix, new_str);
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen }
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen i_free(ssl_io->last_error);
df12456ea9a15e9bebd692207b551800521310c5Stephan Bosch ssl_io->last_error = new_str;
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen}
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_info_callback(const SSL *ssl, int where, int ret)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_iostream *ssl_io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((where & SSL_CB_ALERT) != 0) {
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen switch (ret & 0xff) {
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen case SSL_AD_CLOSE_NOTIFY:
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen i_debug("%sSSL alert: %s",
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen ssl_io->log_prefix,
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen SSL_alert_desc_string_long(ret));
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen break;
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen default:
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen i_debug("%sSSL alert: where=0x%x, ret=%d: %s %s",
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen ssl_io->log_prefix, where, ret,
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen SSL_alert_type_string_long(ret),
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen SSL_alert_desc_string_long(ret));
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen break;
af4cf954021adac7e021262bf6ae3cc3cec3ba54Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ret == 0) {
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen i_debug("%sSSL failed: where=0x%x: %s",
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen ssl_io->log_prefix, where, SSL_state_string_long(ssl));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen i_debug("%sSSL: where=0x%x, ret=%d: %s",
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen ssl_io->log_prefix, where, ret,
c09d07e85ae22aa620945648a4430ca2a5a676f4Timo Sirainen SSL_state_string_long(ssl));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainenopenssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen BIO *in;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen X509 *x;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (in == NULL) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("BIO_new_mem_buf() failed: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen openssl_iostream_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen x = PEM_read_bio_X509(in, NULL, NULL, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (x != NULL) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = SSL_use_certificate(ssl_io->ssl, x);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ERR_peek_error() != 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen X509_free(x);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen BIO_free(in);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret == 0) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("Can't load ssl_cert: %s",
8b5d186ec2f8b56ded72a7f45a70b7542caad9d0Timo Sirainen openssl_iostream_use_certificate_error(cert, NULL));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_use_key(struct ssl_iostream *ssl_io,
9f7ba3807f77209a65e0faa56cac8545b06cd116Aki Tuomi const struct ssl_iostream_cert *set,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen EVP_PKEY *pkey;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_load_key(set, &pkey, error_r) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (SSL_use_PrivateKey(ssl_io->ssl, pkey) != 1) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("Can't load SSL private key: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen openssl_iostream_key_load_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen EVP_PKEY_free(pkey);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL *ssl;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_iostream *ssl_io;
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen char certname[1024];
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen X509_NAME *subject;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_extidx);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->cert_received = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
c281d6630970d51a0e017366be9d86a061303d4bAki Tuomi subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx));
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen if (subject == NULL ||
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen X509_NAME_oneline(subject, certname, sizeof(certname)) == NULL)
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen certname[0] = '\0';
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen else
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen certname[sizeof(certname)-1] = '\0'; /* just in case.. */
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (preverify_ok == 0) {
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen openssl_iostream_set_error(ssl_io, t_strdup_printf(
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen "Received invalid SSL certificate: %s: %s",
c281d6630970d51a0e017366be9d86a061303d4bAki Tuomi X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), certname));
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen if (ssl_io->verbose_invalid_cert)
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen i_info("%s", ssl_io->last_error);
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen } else if (ssl_io->verbose) {
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen i_info("Received valid SSL certificate: %s", certname);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (preverify_ok == 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->cert_broken = TRUE;
095481fee84040436ce2dccca472c9bb1df4d5bbTimo Sirainen if (!ssl_io->allow_invalid_cert) {
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen ssl_io->handshake_failed = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 0;
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set(struct ssl_iostream *ssl_io,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const struct ssl_iostream_settings *set,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
319bc5ff46e9c941efb573b1e00f85fdeb08942dTimo Sirainen const struct ssl_iostream_settings *ctx_set = &ssl_io->ctx->set;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen int verify_flags;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (set->verbose)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen SSL_set_info_callback(ssl_io->ssl, openssl_info_callback);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen if (set->cipher_list != NULL &&
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen strcmp(ctx_set->cipher_list, set->cipher_list) != 0) {
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (SSL_set_cipher_list(ssl_io->ssl, set->cipher_list) == 0) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf(
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen "Can't set cipher list to '%s': %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen set->cipher_list, openssl_iostream_error());
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho#ifdef HAVE_SSL_CTX_SET1_CURVES_LIST
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho if (set->curve_list != NULL && strlen(set->curve_list) > 0 &&
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho (ctx_set->curve_list == NULL || strcmp(ctx_set->curve_list, set->curve_list) != 0)) {
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho if (SSL_set1_curves_list(ssl_io->ssl, set->curve_list) == 0) {
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho *error_r = t_strdup_printf(
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho "Failed to set curve list to '%s'",
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho set->curve_list);
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho return -1;
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho }
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho }
c4d66e8ccbb8440622f1a70791ed2a8f99659af1Juha Koho#endif
f974134f495e47ba7173f5b0f75fbd5cbacf1fe2Timo Sirainen if (set->prefer_server_ciphers)
f974134f495e47ba7173f5b0f75fbd5cbacf1fe2Timo Sirainen SSL_set_options(ssl_io->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi if (set->min_protocol != NULL) {
0793a1dbdfc369b60ad8aa16f4ee58cc1c238f5cPaul Howarth#if defined(HAVE_SSL_CLEAR_OPTIONS)
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS);
0793a1dbdfc369b60ad8aa16f4ee58cc1c238f5cPaul Howarth#endif
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi long opts;
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi int min_protocol;
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi if (openssl_min_protocol_to_options(set->min_protocol, &opts,
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi &min_protocol) < 0) {
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi *error_r = t_strdup_printf(
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi "Unknown ssl_min_protocol setting '%s'",
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi set->min_protocol);
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi return -1;
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi }
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi SSL_set_min_proto_version(ssl_io->ssl, min_protocol);
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi#else
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi SSL_set_options(ssl_io->ssl, opts);
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi#endif
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen }
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen
9f7ba3807f77209a65e0faa56cac8545b06cd116Aki Tuomi if (set->cert.cert != NULL && strcmp(ctx_set->cert.cert, set->cert.cert) != 0) {
9f7ba3807f77209a65e0faa56cac8545b06cd116Aki Tuomi if (openssl_iostream_use_certificate(ssl_io, set->cert.cert, error_r) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
9f7ba3807f77209a65e0faa56cac8545b06cd116Aki Tuomi if (set->cert.key != NULL && strcmp(ctx_set->cert.key, set->cert.key) != 0) {
9f7ba3807f77209a65e0faa56cac8545b06cd116Aki Tuomi if (openssl_iostream_use_key(ssl_io, &set->cert, error_r) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi if (set->alt_cert.cert != NULL && strcmp(ctx_set->alt_cert.cert, set->alt_cert.cert) != 0) {
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi if (openssl_iostream_use_certificate(ssl_io, set->alt_cert.cert, error_r) < 0)
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi return -1;
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi }
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi if (set->alt_cert.key != NULL && strcmp(ctx_set->alt_cert.key, set->alt_cert.key) != 0) {
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi if (openssl_iostream_use_key(ssl_io, &set->alt_cert, error_r) < 0)
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi return -1;
0577701d04beea222fc49a7318851ddcea3b99d3Aki Tuomi }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (set->verify_remote_cert) {
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen if (ssl_io->ctx->client_ctx)
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen verify_flags = SSL_VERIFY_NONE;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen else
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen SSL_set_verify(ssl_io->ssl, verify_flags,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen openssl_iostream_verify_client_cert);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (set->cert_username_field != NULL) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid = OBJ_txt2nid(set->cert_username_field);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ssl_io->username_nid == NID_undef) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf(
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen "Invalid cert_username_field: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen set->cert_username_field);
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid = ssl_io->ctx->username_nid;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->verbose = set->verbose;
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen ssl_io->verbose_invalid_cert = set->verbose_invalid_cert || set->verbose;
095481fee84040436ce2dccca472c9bb1df4d5bbTimo Sirainen ssl_io->allow_invalid_cert = set->allow_invalid_cert;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic int
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenopenssl_iostream_create(struct ssl_iostream_context *ctx, const char *host,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen const struct ssl_iostream_settings *set,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen struct istream **input, struct ostream **output,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen struct ssl_iostream **iostream_r,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_iostream *ssl_io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL *ssl;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen BIO *bio_int, *bio_ext;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
d3ce8bae67c6a73c4d068fbb3d59a5a990c9acd9Timo Sirainen /* Don't allow an existing io_add_istream() to be use on the input.
d3ce8bae67c6a73c4d068fbb3d59a5a990c9acd9Timo Sirainen It would seem to work, but it would also cause hangs. */
d3ce8bae67c6a73c4d068fbb3d59a5a990c9acd9Timo Sirainen i_assert(i_stream_get_root_io(*input)->real_stream->io == NULL);
d3ce8bae67c6a73c4d068fbb3d59a5a990c9acd9Timo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl = SSL_new(ctx->ssl_ctx);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ssl == NULL) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("SSL_new() failed: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen openssl_iostream_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e).
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen Each of the BIOs have one "write buffer". BIO_write() copies data
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen to them, while BIO_read() reads from the other BIO's write buffer
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen into the given buffer. The bio_int is used by OpenSSL and bio_ext
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen is used by this library. */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("BIO_new_bio_pair() failed: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen openssl_iostream_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_free(ssl);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io = i_new(struct ssl_iostream, 1);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->refcount = 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->ctx = ctx;
3c376d8f5e41c5decd6379a1068a95c3f0738770Timo Sirainen ssl_iostream_context_ref(ssl_io->ctx);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->ssl = ssl;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->bio_ext = bio_ext;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->plain_input = *input;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->plain_output = *output;
d185226aa3dc88a9ee9f16b4c8b2e38000ac8b96Timo Sirainen ssl_io->connected_host = i_strdup(host);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen ssl_io->log_prefix = host == NULL ? i_strdup("") :
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen i_strdup_printf("%s: ", host);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* bio_int will be freed by SSL_free() */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_set_bio(ssl_io->ssl, bio_int, bio_int);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen#ifdef HAVE_SSL_GET_SERVERNAME
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen SSL_set_tlsext_host_name(ssl_io->ssl, host);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen#endif
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_set(ssl_io, set, error_r) < 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen openssl_iostream_free(ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
9ab0786966c0afa8fa09a2faff7c067bc388e694Timo Sirainen o_stream_uncork(ssl_io->plain_output);
9ab0786966c0afa8fa09a2faff7c067bc388e694Timo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen *input = openssl_i_stream_create_ssl(ssl_io);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen *output = openssl_o_stream_create_ssl(ssl_io);
2244ec1fda50f777b0b5c2c780f058eb31268a28Timo Sirainen i_stream_set_name(*input, t_strconcat("SSL ",
2244ec1fda50f777b0b5c2c780f058eb31268a28Timo Sirainen i_stream_get_name(ssl_io->plain_input), NULL));
2244ec1fda50f777b0b5c2c780f058eb31268a28Timo Sirainen o_stream_set_name(*output, t_strconcat("SSL ",
2244ec1fda50f777b0b5c2c780f058eb31268a28Timo Sirainen o_stream_get_name(ssl_io->plain_output), NULL));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen if (ssl_io->plain_output->real_stream->error_handling_disabled)
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen o_stream_set_no_error_handling(*output, TRUE);
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen
87da941c0b0a0671997f592a52ee2c0b35d0e41eTimo Sirainen ssl_io->ssl_input = *input;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->ssl_output = *output;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen *iostream_r = ssl_io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_free(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
3c376d8f5e41c5decd6379a1068a95c3f0738770Timo Sirainen ssl_iostream_context_unref(&ssl_io->ctx);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen o_stream_unref(&ssl_io->plain_output);
1fea3af0bb1cb4daab44ae4e1a06ce2e42bbf714Aki Tuomi i_stream_unref(&ssl_io->plain_input);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen BIO_free(ssl_io->bio_ext);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_free(ssl_io->ssl);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_free(ssl_io->plain_stream_errstr);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_free(ssl_io->last_error);
d185226aa3dc88a9ee9f16b4c8b2e38000ac8b96Timo Sirainen i_free(ssl_io->connected_host);
d185226aa3dc88a9ee9f16b4c8b2e38000ac8b96Timo Sirainen i_free(ssl_io->sni_host);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen i_free(ssl_io->log_prefix);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_free(ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_unref(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(ssl_io->refcount > 0);
4b794181a01c04d1dd33c9a8339ddbc826106408Timo Sirainen if (--ssl_io->refcount > 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen openssl_iostream_free(ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_destroy(struct ssl_iostream *ssl_io)
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen{
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel if (SSL_shutdown(ssl_io->ssl) != 1) {
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel /* if bidirectional shutdown fails we need to clear
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel the error queue */
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel openssl_iostream_clear_errors();
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel }
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_more(ssl_io, OPENSSL_IOSTREAM_SYNC_TYPE_WRITE);
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen (void)o_stream_flush(ssl_io->plain_output);
40ba79bfba7c2331fb8e1a2a694da748a6ebaec0Timo Sirainen /* close the plain i/o streams, because their fd may be closed soon,
40ba79bfba7c2331fb8e1a2a694da748a6ebaec0Timo Sirainen but we may still keep this ssl-iostream referenced until later. */
40ba79bfba7c2331fb8e1a2a694da748a6ebaec0Timo Sirainen i_stream_close(ssl_io->plain_input);
40ba79bfba7c2331fb8e1a2a694da748a6ebaec0Timo Sirainen o_stream_close(ssl_io->plain_output);
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen ssl_iostream_unref(&ssl_io);
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen}
38f1423a23f6c9a37c01152595ce3ca8a0a65121Timo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_bio_output(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t bytes, max_bytes;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssize_t sent;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen unsigned char buffer[IO_BLOCK_SIZE];
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bool bytes_sent = FALSE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen o_stream_cork(ssl_io->plain_output);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen while ((bytes = BIO_ctrl_pending(ssl_io->bio_ext)) > 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* bytes contains how many SSL encrypted bytes we should be
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen sending out */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen max_bytes = o_stream_get_buffer_avail_size(ssl_io->plain_output);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (bytes > max_bytes) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (max_bytes == 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* wait until output buffer clears */
e21a2c0821c559080550ead6a901f613e59af10eTimo Sirainen o_stream_set_flush_pending(ssl_io->plain_output,
e21a2c0821c559080550ead6a901f613e59af10eTimo Sirainen TRUE);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes = max_bytes;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (bytes > sizeof(buffer))
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes = sizeof(buffer);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* BIO_read() is guaranteed to return all the bytes that
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen BIO_ctrl_pending() returned */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = BIO_read(ssl_io->bio_ext, buffer, bytes);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(ret == (int)bytes);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* we limited number of read bytes to plain_output's
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen available size. this send() is guaranteed to either
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen fully succeed or completely fail due to some error. */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sent = o_stream_send(ssl_io->plain_output, buffer, bytes);
148a8396be2c1cf7d2aaa55566f7f7dea05388ddTimo Sirainen if (sent < 0) {
689d163c6bc35383f6142c439065575f0bfc6e10Timo Sirainen i_assert(ssl_io->plain_output->closed ||
689d163c6bc35383f6142c439065575f0bfc6e10Timo Sirainen ssl_io->plain_output->stream_errno != 0);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_free(ssl_io->plain_stream_errstr);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen ssl_io->plain_stream_errstr =
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_strdup(o_stream_get_error(ssl_io->plain_output));
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen ssl_io->plain_stream_errno =
148a8396be2c1cf7d2aaa55566f7f7dea05388ddTimo Sirainen ssl_io->plain_output->stream_errno;
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen ssl_io->closed = TRUE;
148a8396be2c1cf7d2aaa55566f7f7dea05388ddTimo Sirainen break;
148a8396be2c1cf7d2aaa55566f7f7dea05388ddTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(sent == (ssize_t)bytes);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes_sent = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen o_stream_uncork(ssl_io->plain_output);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return bytes_sent;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
739125f23e3312045e620014812fe2249a309cc4Timo Sirainenstatic ssize_t
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_read_more(struct ssl_iostream *ssl_io,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi enum openssl_iostream_sync_type type,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen const unsigned char **data_r, size_t *size_r)
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen{
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen *data_r = i_stream_get_data(ssl_io->plain_input, size_r);
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen if (*size_r > 0)
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen return 0;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (type == OPENSSL_IOSTREAM_SYNC_TYPE_CONTINUE_READ) {
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi /* only the first i_stream_read() call attempts to read more
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi input. the following reads will just process the buffered
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi data. */
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi return 0;
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi }
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody if (i_stream_read_more(ssl_io->plain_input, data_r, size_r) < 0)
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen return -1;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen return 0;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen}
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomistatic bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi enum openssl_iostream_sync_type type)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const unsigned char *data;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen size_t bytes, size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen bool bytes_read = FALSE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen while ((bytes = BIO_ctrl_get_write_guarantee(ssl_io->bio_ext)) > 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* bytes contains how many bytes we can write to bio_ext */
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->plain_input->real_stream->try_alloc_limit = bytes;
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi ret = openssl_iostream_read_more(ssl_io, type, &data, &size);
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->plain_input->real_stream->try_alloc_limit = 0;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen if (ret == -1 && size == 0 && !bytes_read) {
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen if (ssl_io->plain_input->stream_errno != 0) {
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen i_free(ssl_io->plain_stream_errstr);
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen ssl_io->plain_stream_errstr =
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen i_strdup(i_stream_get_error(ssl_io->plain_input));
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen ssl_io->plain_stream_errno =
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen ssl_io->plain_input->stream_errno;
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen }
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen ssl_io->closed = TRUE;
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen return FALSE;
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (size == 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* wait for more input */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (size > bytes)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen size = bytes;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = BIO_write(ssl_io->bio_ext, data, size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(ret == (ssize_t)size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_stream_skip(ssl_io->plain_input, size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes_read = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen if (bytes == 0 && !bytes_read && ssl_io->want_read) {
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen /* shouldn't happen */
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen i_error("SSL BIO buffer size too small");
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_free(ssl_io->plain_stream_errstr);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen ssl_io->plain_stream_errstr =
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_strdup("SSL BIO buffer size too small");
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->plain_stream_errno = EINVAL;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->closed = TRUE;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen return FALSE;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen }
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen if (i_stream_get_data_size(ssl_io->plain_input) > 0) {
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen i_error("SSL: Too much data in buffered plain input buffer");
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_free(ssl_io->plain_stream_errstr);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen ssl_io->plain_stream_errstr =
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_strdup("SSL: Too much data in buffered plain input buffer");
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->plain_stream_errno = EINVAL;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->closed = TRUE;
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen return FALSE;
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen }
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen if (bytes_read) {
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen if (ssl_io->ostream_flush_waiting_input) {
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen ssl_io->ostream_flush_waiting_input = FALSE;
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen o_stream_set_flush_pending(ssl_io->plain_output, TRUE);
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen }
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (type != OPENSSL_IOSTREAM_SYNC_TYPE_FIRST_READ &&
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi type != OPENSSL_IOSTREAM_SYNC_TYPE_CONTINUE_READ)
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi i_stream_set_input_pending(ssl_io->ssl_input, TRUE);
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen ssl_io->want_read = FALSE;
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return bytes_read;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomibool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi enum openssl_iostream_sync_type type)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bool ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen ret = openssl_iostream_bio_output(ssl_io);
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (openssl_iostream_bio_input(ssl_io, type))
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomiint openssl_iostream_more(struct ssl_iostream *ssl_io,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi enum openssl_iostream_sync_type type)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen{
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen int ret;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (!ssl_io->handshaked) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if ((ret = ssl_iostream_handshake(ssl_io)) <= 0)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return ret;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_bio_sync(ssl_io, type);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return 1;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen}
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainenstatic void openssl_iostream_closed(struct ssl_iostream *ssl_io)
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen{
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen if (ssl_io->plain_stream_errno != 0) {
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen i_assert(ssl_io->plain_stream_errstr != NULL);
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen openssl_iostream_set_error(ssl_io, ssl_io->plain_stream_errstr);
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen errno = ssl_io->plain_stream_errno;
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen } else {
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen openssl_iostream_set_error(ssl_io, "Connection closed");
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen errno = EPIPE;
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen }
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen}
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomiint openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi enum openssl_iostream_sync_type type,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi const char *func_name)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const char *errstr = NULL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int err;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen err = SSL_get_error(ssl_io->ssl, ret);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen switch (err) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen case SSL_ERROR_WANT_WRITE:
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (!openssl_iostream_bio_sync(ssl_io, type)) {
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (type != OPENSSL_IOSTREAM_SYNC_TYPE_WRITE)
ed41ec8aa0efaa50954fd16cb44c86c8350dadccTimo Sirainen i_panic("SSL ostream buffer size not unlimited");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 0;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen if (ssl_io->closed) {
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen openssl_iostream_closed(ssl_io);
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen return -1;
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen case SSL_ERROR_WANT_READ:
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen ssl_io->want_read = TRUE;
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_bio_sync(ssl_io, type);
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen if (ssl_io->closed) {
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainen openssl_iostream_closed(ssl_io);
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen return -1;
c2c0c1e5d2e97ae114ad83d8cb486b0aab23ac38Timo Sirainen }
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen return ssl_io->want_read ? 0 : 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen case SSL_ERROR_SYSCALL:
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* eat up the error queue */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ERR_peek_error() != 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen errstr = openssl_iostream_error();
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errno = EINVAL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ret != 0) {
6e9eb28a08f219a64e1e8719941b4c4034ef17d8Timo Sirainen i_assert(errno != 0);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = strerror(errno);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* EOF. */
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen errno = EPIPE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = "Disconnected";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s syscall failed: %s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen func_name, errstr);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen case SSL_ERROR_ZERO_RETURN:
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* clean connection closing */
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen errno = EPIPE;
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen if (ssl_io->handshaked)
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen i_free_and_null(ssl_io->last_error);
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen else if (ssl_io->last_error == NULL) {
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen errstr = "SSL connection closed during handshake";
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen break;
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen }
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen case SSL_ERROR_SSL:
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s failed: %s",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen func_name, openssl_iostream_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errno = EINVAL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen default:
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen func_name, err,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen openssl_iostream_error());
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errno = EINVAL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen openssl_iostream_set_error(ssl_io, errstr);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
18344a653fb063e599e24d1e9f7d5db4d8fd7b45Timo Sirainenstatic bool
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen const char *verify_name, const char **reason_r)
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen{
bcb65da2bec56ee1baadc9f0233f12dce5d06dddTimo Sirainen if (ssl_io->allow_invalid_cert)
bcb65da2bec56ee1baadc9f0233f12dce5d06dddTimo Sirainen return TRUE;
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io)) {
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen *reason_r = "Invalid certificate";
18344a653fb063e599e24d1e9f7d5db4d8fd7b45Timo Sirainen return FALSE;
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen }
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen return openssl_cert_match_name(ssl_io->ssl, verify_name, reason_r);
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen}
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen const char *reason, *error = NULL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(!ssl_io->handshaked);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ssl_io->ctx->client_ctx) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen while ((ret = SSL_connect(ssl_io->ssl)) <= 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen ret = openssl_iostream_handle_error(ssl_io, ret,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE, "SSL_connect()");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret <= 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen while ((ret = SSL_accept(ssl_io->ssl)) <= 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen ret = openssl_iostream_handle_error(ssl_io, ret,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE, "SSL_accept()");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret <= 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* handshake finished */
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_bio_sync(ssl_io, OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ssl_io->handshake_callback != NULL) {
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) < 0) {
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen i_assert(error != NULL);
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen openssl_iostream_set_error(ssl_io, error);
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen ssl_io->handshake_failed = TRUE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen } else if (ssl_io->connected_host != NULL && !ssl_io->handshake_failed) {
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen if (!ssl_iostream_cert_match_name(ssl_io, ssl_io->connected_host, &reason)) {
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen openssl_iostream_set_error(ssl_io, t_strdup_printf(
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen "SSL certificate doesn't match expected host name %s: %s",
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen ssl_io->connected_host, reason));
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen ssl_io->handshake_failed = TRUE;
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen if (ssl_io->handshake_failed) {
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen i_stream_close(ssl_io->plain_input);
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen o_stream_close(ssl_io->plain_output);
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen errno = EINVAL;
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen return -1;
4cc68747aa932fb9a98f1504b9d874f5cfbf0decTimo Sirainen }
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen i_free_and_null(ssl_io->last_error);
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen ssl_io->handshaked = TRUE;
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ssl_io->ssl_output != NULL)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (void)o_stream_flush(ssl_io->ssl_output);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen ssl_iostream_handshake_callback_t *callback,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen void *context)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->handshake_callback = callback;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->handshake_context = context;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenstatic void
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenopenssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_iostream_sni_callback_t *callback,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen void *context)
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen{
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_io->sni_callback = callback;
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_io->sni_context = context;
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen}
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenstatic void
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenopenssl_iostream_change_context(struct ssl_iostream *ssl_io,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen struct ssl_iostream_context *ctx)
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen{
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen if (ctx != ssl_io->ctx) {
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen SSL_set_SSL_CTX(ssl_io->ssl, ctx->ssl_ctx);
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_iostream_context_ref(ctx);
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_iostream_context_unref(&ssl_io->ctx);
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen ssl_io->ctx = ctx;
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen }
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen}
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenstatic void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io,
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen const char *prefix)
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen{
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen i_free(ssl_io->log_prefix);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen ssl_io->log_prefix = i_strdup(prefix);
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen}
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->handshaked;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainenstatic bool
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainenopenssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io)
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen{
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen return ssl_io->handshake_failed;
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen}
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->cert_received && !ssl_io->cert_broken;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->cert_received && ssl_io->cert_broken;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_peer_name(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen X509 *x509;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen char *name;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int len;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io))
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return NULL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen x509 = SSL_get_peer_certificate(ssl_io->ssl);
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen i_assert(x509 != NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid, NULL, 0);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (len < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen name = "";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen else {
11f02cda5a67ba09167ef001e5600833c5803a7bMartti Rannanjärvi name = t_malloc0(len + 1);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen name, len + 1) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen name = "";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen else if (strlen(name) != (size_t)len) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* NUL characters in name. Someone's trying to fake
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen being another user? Don't allow it. */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen name = "";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen X509_free(x509);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return *name == '\0' ? NULL : name;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenstatic const char *openssl_iostream_get_server_name(struct ssl_iostream *ssl_io)
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen{
d185226aa3dc88a9ee9f16b4c8b2e38000ac8b96Timo Sirainen return ssl_io->sni_host;
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen}
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomistatic const char *
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomiopenssl_iostream_get_compression(struct ssl_iostream *ssl_io)
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi{
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi const COMP_METHOD *comp;
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi comp = SSL_get_current_compression(ssl_io->ssl);
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi return comp == NULL ? NULL : SSL_COMP_get_name(comp);
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi#else
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi return NULL;
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi#endif
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi}
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
68a8f0794a1c2c2267ae4015ef3ccc00604e9175Timo Sirainen const SSL_CIPHER *cipher;
531b963539cd4c68bdb7cd6d087cd4a06e1983adTimo Sirainen#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const COMP_METHOD *comp;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#endif
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const char *comp_str;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int bits, alg_bits;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (!ssl_io->handshaked)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return "";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen cipher = SSL_get_current_cipher(ssl_io->ssl);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
531b963539cd4c68bdb7cd6d087cd4a06e1983adTimo Sirainen#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen comp = SSL_get_current_compression(ssl_io->ssl);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen comp_str = comp == NULL ? "" :
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen t_strconcat(" ", SSL_COMP_get_name(comp), NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#else
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen comp_str = "";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#endif
8f2444f788368b08edb4ac037d5f7e5919cdee0aTimo Sirainen return t_strdup_printf("%s with cipher %s (%d/%d bits)%s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_get_version(ssl_io->ssl),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_CIPHER_get_name(cipher),
8f2444f788368b08edb4ac037d5f7e5919cdee0aTimo Sirainen bits, alg_bits, comp_str);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_last_error(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->last_error;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_cipher(struct ssl_iostream *ssl_io, unsigned int *bits_r)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi{
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi if (!ssl_io->handshaked)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return NULL;
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl_io->ssl);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi *bits_r = SSL_CIPHER_get_bits(cipher, NULL);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return SSL_CIPHER_get_name(cipher);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi}
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_pfs(struct ssl_iostream *ssl_io)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi{
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi if (!ssl_io->handshaked)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return NULL;
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl_io->ssl);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi#if defined(HAVE_SSL_CIPHER_get_kx_nid)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi int nid = SSL_CIPHER_get_kx_nid(cipher);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return OBJ_nid2sn(nid);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi#else
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi char buf[128];
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi const char *desc, *ptr;
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi if ((desc = SSL_CIPHER_description(cipher, buf, sizeof(buf)))==NULL ||
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi (ptr = strstr(desc, "Kx=")) == NULL)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return "";
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return t_strcut(ptr+3, ' ');
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi#endif
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi}
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_protocol_name(struct ssl_iostream *ssl_io)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi{
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi if (!ssl_io->handshaked)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return NULL;
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi return SSL_get_version(ssl_io->ssl);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi}
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainenstatic const struct iostream_ssl_vfuncs ssl_vfuncs = {
15aa67e8a9dd7fc631d58ce13c54fe004bb4d0c1Timo Sirainen .global_init = openssl_iostream_global_init,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .context_init_client = openssl_iostream_context_init_client,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .context_init_server = openssl_iostream_context_init_server,
96359599bbd4a2d704c3f343ff4c2fcd03f0dd02Timo Sirainen .context_ref = openssl_iostream_context_ref,
96359599bbd4a2d704c3f343ff4c2fcd03f0dd02Timo Sirainen .context_unref = openssl_iostream_context_unref,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .create = openssl_iostream_create,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .unref = openssl_iostream_unref,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .destroy = openssl_iostream_destroy,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .handshake = openssl_iostream_handshake,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .set_handshake_callback = openssl_iostream_set_handshake_callback,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen .set_sni_callback = openssl_iostream_set_sni_callback,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainen .change_context = openssl_iostream_change_context,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .set_log_prefix = openssl_iostream_set_log_prefix,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .is_handshaked = openssl_iostream_is_handshaked,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .has_handshake_failed = openssl_iostream_has_handshake_failed,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .has_valid_client_cert = openssl_iostream_has_valid_client_cert,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .has_broken_client_cert = openssl_iostream_has_broken_client_cert,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .cert_match_name = openssl_iostream_cert_match_name,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .get_peer_name = openssl_iostream_get_peer_name,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .get_server_name = openssl_iostream_get_server_name,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .get_compression = openssl_iostream_get_compression,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .get_security_string = openssl_iostream_get_security_string,
c141f3682c0ec1d171f89a7a0ff95152c31ea450Aki Tuomi .get_last_error = openssl_iostream_get_last_error,
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi .get_cipher = openssl_iostream_get_cipher,
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi .get_pfs = openssl_iostream_get_pfs,
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi .get_protocol_name = openssl_iostream_get_protocol_name,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen};
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen
ecc2fb34641f1bd39e10c774192ca18527ecb953Timo Sirainenvoid ssl_iostream_openssl_init(void)
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen{
98c2cac72e2032f223050a4edd229993c1d5c1f0Aki Tuomi unsigned char buf;
98c2cac72e2032f223050a4edd229993c1d5c1f0Aki Tuomi if (RAND_bytes(&buf, 1) < 1)
98c2cac72e2032f223050a4edd229993c1d5c1f0Aki Tuomi i_fatal("OpenSSL RNG failed to initialize");
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen iostream_ssl_module_init(&ssl_vfuncs);
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen}
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen
ecc2fb34641f1bd39e10c774192ca18527ecb953Timo Sirainenvoid ssl_iostream_openssl_deinit(void)
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen{
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen openssl_iostream_global_deinit();
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainen}