iostream-openssl.c revision 18344a653fb063e599e24d1e9f7d5db4d8fd7b45
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2017 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
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainenstatic void
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainenopenssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen{
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. */
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen i_debug("%sSSL error: %s", ssl_io->log_prefix, str);
cb999a76f51081a4c5d7f7ac411de3fe6cff6c15Timo Sirainen }
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen i_free(ssl_io->last_error);
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen ssl_io->last_error = i_strdup(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);
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen if (set->protocols != NULL) {
0793a1dbdfc369b60ad8aa16f4ee58cc1c238f5cPaul Howarth#if defined(HAVE_SSL_CLEAR_OPTIONS)
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS);
0793a1dbdfc369b60ad8aa16f4ee58cc1c238f5cPaul Howarth#endif
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen SSL_set_options(ssl_io->ssl,
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen openssl_get_protocol_options(set->protocols));
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
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,
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen const char *verify_name)
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen{
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io))
18344a653fb063e599e24d1e9f7d5db4d8fd7b45Timo Sirainen return FALSE;
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen return openssl_cert_match_name(ssl_io->ssl, verify_name);
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen}
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen const char *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) {
18344a653fb063e599e24d1e9f7d5db4d8fd7b45Timo Sirainen if (!ssl_iostream_cert_match_name(ssl_io, ssl_io->connected_host)) {
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen openssl_iostream_set_error(ssl_io, t_strdup_printf(
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen "SSL certificate doesn't match expected host name %s",
4584a00276941db3f64c4db1a1bed91fe107af81Timo Sirainen ssl_io->connected_host));
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
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
fe4058e6f01bf0e104c44815b6df7cfefb80634cTimo Sirainenstatic const struct iostream_ssl_vfuncs ssl_vfuncs = {
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,
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,
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}