bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_free(struct ssl_iostream *ssl_io);
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenvoid openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
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. */
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);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_info_callback(const SSL *ssl, int where, int ret)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen i_debug("%sSSL alert: where=0x%x, ret=%d: %s %s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ret == 0) {
20905b89a05e27f0d1c6606a6b49b33dc23a1323Timo Sirainen ssl_io->log_prefix, where, SSL_state_string_long(ssl));
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainenopenssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("BIO_new_mem_buf() failed: %s",
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("Can't load ssl_cert: %s",
8b5d186ec2f8b56ded72a7f45a70b7542caad9d0Timo Sirainen openssl_iostream_use_certificate_error(cert, NULL));
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_use_key(struct ssl_iostream *ssl_io,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_load_key(set, &pkey, error_r) < 0)
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",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
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);
c281d6630970d51a0e017366be9d86a061303d4bAki Tuomi subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx));
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen X509_NAME_oneline(subject, certname, sizeof(certname)) == NULL)
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen certname[sizeof(certname)-1] = '\0'; /* just in case.. */
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 i_info("Received valid SSL certificate: %s", certname);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set(struct ssl_iostream *ssl_io,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
319bc5ff46e9c941efb573b1e00f85fdeb08942dTimo Sirainen const struct ssl_iostream_settings *ctx_set = &ssl_io->ctx->set;
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen SSL_set_info_callback(ssl_io->ssl, openssl_info_callback);
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 "Can't set cipher list to '%s': %s",
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 "Failed to set curve list to '%s'",
f974134f495e47ba7173f5b0f75fbd5cbacf1fe2Timo Sirainen SSL_set_options(ssl_io->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS);
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi if (openssl_min_protocol_to_options(set->min_protocol, &opts,
976dee5384c4827dc648c9bc53825390521c388eMartti Rannanjärvi "Unknown ssl_min_protocol setting '%s'",
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
804bca734d3bdf930f1678ee24885965a6aa756cMartti Rannanjärvi SSL_set_min_proto_version(ssl_io->ssl, min_protocol);
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)
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)
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 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)
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid = OBJ_txt2nid(set->cert_username_field);
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen "Invalid cert_username_field: %s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->username_nid = ssl_io->ctx->username_nid;
b4d850a0ffd519c1c745557568daf7d48e18c820Timo Sirainen ssl_io->verbose_invalid_cert = set->verbose_invalid_cert || set->verbose;
095481fee84040436ce2dccca472c9bb1df4d5bbTimo Sirainen ssl_io->allow_invalid_cert = set->allow_invalid_cert;
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenopenssl_iostream_create(struct ssl_iostream_context *ctx, const char *host,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen struct istream **input, struct ostream **output,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
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);
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen *error_r = t_strdup_printf("SSL_new() failed: %s",
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",
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainen ssl_io->log_prefix = host == NULL ? i_strdup("") :
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* bio_int will be freed by SSL_free() */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io);
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_set(ssl_io, set, error_r) < 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen *output = openssl_o_stream_create_ssl(ssl_io);
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));
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen if (ssl_io->plain_output->real_stream->error_handling_disabled)
130fadd21e01ea380170b3bda859f6a27ff8e1a8Timo Sirainen o_stream_set_no_error_handling(*output, TRUE);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_free(struct ssl_iostream *ssl_io)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_unref(struct ssl_iostream *ssl_io)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_destroy(struct ssl_iostream *ssl_io)
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel /* if bidirectional shutdown fails we need to clear
e823661ef75e798ed18dc2bf0a46ed66ffc27c24manuel the error queue */
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_more(ssl_io, OPENSSL_IOSTREAM_SYNC_TYPE_WRITE);
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. */
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_bio_output(struct ssl_iostream *ssl_io)
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 /* wait until output buffer clears */
e21a2c0821c559080550ead6a901f613e59af10eTimo Sirainen o_stream_set_flush_pending(ssl_io->plain_output,
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);
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);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen i_strdup(o_stream_get_error(ssl_io->plain_output));
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_read_more(struct ssl_iostream *ssl_io,
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen *data_r = i_stream_get_data(ssl_io->plain_input, size_r);
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
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody if (i_stream_read_more(ssl_io->plain_input, data_r, size_r) < 0)
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomistatic bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io,
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;
eefcf71c762ef4614f5bb423dd3bd8e5c44981c5Timo Sirainen i_strdup(i_stream_get_error(ssl_io->plain_input));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* wait for more input */
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen if (bytes == 0 && !bytes_read && ssl_io->want_read) {
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen /* shouldn't happen */
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_strdup("SSL: Too much data in buffered plain input buffer");
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen o_stream_set_flush_pending(ssl_io->plain_output, TRUE);
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi if (type != OPENSSL_IOSTREAM_SYNC_TYPE_FIRST_READ &&
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi i_stream_set_input_pending(ssl_io->ssl_input, TRUE);
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomibool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io,
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomiint openssl_iostream_more(struct ssl_iostream *ssl_io,
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if ((ret = ssl_iostream_handshake(ssl_io)) <= 0)
497cdac280b75dd6a94d9642991e50ac73640c95Timo Sirainenstatic void openssl_iostream_closed(struct ssl_iostream *ssl_io)
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 openssl_iostream_set_error(ssl_io, "Connection closed");
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomiint openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
ed41ec8aa0efaa50954fd16cb44c86c8350dadccTimo Sirainen i_panic("SSL ostream buffer size not unlimited");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* eat up the error queue */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ret != 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s syscall failed: %s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* clean connection closing */
b4c64e78f02025103718091bea1898c5eb7e4fafTimo Sirainen errstr = "SSL connection closed during handshake";
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen const char *verify_name, const char **reason_r)
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io)) {
6315f87da1b28578d2deb4d51aa624dc178efb0aTimo Sirainen return openssl_cert_match_name(ssl_io->ssl, verify_name, reason_r);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
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 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()");
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* handshake finished */
8bcf6fd065a71ae0ca6dc76989250e819d08d7f6Aki Tuomi (void)openssl_iostream_bio_sync(ssl_io, OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE);
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) < 0) {
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",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenopenssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
14a07d2bb34f1d52fce3e3218799f271f118d501Timo Sirainenopenssl_iostream_change_context(struct ssl_iostream *ssl_io,
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenstatic void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
71b60849a773dd68bdc015cb6a8ea1664d16b359Timo Sirainenopenssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->cert_received && !ssl_io->cert_broken;
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return ssl_io->cert_received && ssl_io->cert_broken;
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_peer_name(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io))
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* NUL characters in name. Someone's trying to fake
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen being another user? Don't allow it. */
3b4bd183cc469f70eb91d82a7f01f60ffc24ca5bTimo Sirainenstatic const char *openssl_iostream_get_server_name(struct ssl_iostream *ssl_io)
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomistatic const char *
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomiopenssl_iostream_get_compression(struct ssl_iostream *ssl_io)
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
48e243933060ae3e77abbdc9c0fd0bc2143be26eAki Tuomi return comp == NULL ? NULL : SSL_COMP_get_name(comp);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
531b963539cd4c68bdb7cd6d087cd4a06e1983adTimo Sirainen#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
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 t_strconcat(" ", SSL_COMP_get_name(comp), NULL);
8f2444f788368b08edb4ac037d5f7e5919cdee0aTimo Sirainen return t_strdup_printf("%s with cipher %s (%d/%d bits)%s",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_last_error(struct ssl_iostream *ssl_io)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_cipher(struct ssl_iostream *ssl_io, unsigned int *bits_r)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl_io->ssl);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_pfs(struct ssl_iostream *ssl_io)
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl_io->ssl);
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomi if ((desc = SSL_CIPHER_description(cipher, buf, sizeof(buf)))==NULL ||
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomistatic const char *
33631b9b711b95ee47bd4ddbdb419f46a12cebe4Aki Tuomiopenssl_iostream_get_protocol_name(struct ssl_iostream *ssl_io)
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_unref = openssl_iostream_context_unref,
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 .set_log_prefix = openssl_iostream_set_log_prefix,
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_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,