iostream-openssl.c revision ac645fe16c0619771c0a961db91df16485513c52
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2009-2013 Dovecot authors, see the included COPYING file */
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic void openssl_iostream_free(struct ssl_iostream *ssl_io);
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);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_warning("%s: SSL alert: where=0x%x, ret=%d: %s %s",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ret == 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->source, 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",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_iostream_get_use_certificate_error(cert));
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);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (ssl_io->verbose_invalid_cert && !preverify_ok)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen subject = X509_get_subject_name(ctx->current_cert);
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen X509_NAME_oneline(subject, buf, sizeof(buf)) == NULL)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen buf[sizeof(buf)-1] = '\0'; /* just in case.. */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen X509_verify_cert_error_string(ctx->error), buf);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set(struct ssl_iostream *ssl_io,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo 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) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (!SSL_set_cipher_list(ssl_io->ssl, set->cipher_list)) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen "Can't set cipher list to '%s': %s",
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS);
6a4212e6d7c41de83bcac63edec3118e6a7a0f68Timo Sirainen openssl_get_protocol_options(set->protocols));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (set->cert != NULL && strcmp(ctx_set->cert, set->cert) != 0) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_use_certificate(ssl_io, set->cert, error_r) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (set->key != NULL && strcmp(ctx_set->key, set->key) != 0) {
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen if (openssl_iostream_use_key(ssl_io, set, 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;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->verbose_invalid_cert = set->verbose_invalid_cert;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->require_valid_cert = set->require_valid_cert;
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_create(struct ssl_iostream_context *ctx, const char *source,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen struct istream **input, struct ostream **output,
ba1c847d0af4afe4787ed470d0c818e948e184e2Timo Sirainen const char **error_r)
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",
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)
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);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_read_more(struct ssl_iostream *ssl_io,
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen *data_r = i_stream_get_data(ssl_io->plain_input, size_r);
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen /* read plain_input only when we came here from input handler.
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen this makes sure that we don't get stuck with some input
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen unexpectedly buffered. */
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen if (i_stream_read_data(ssl_io->plain_input, data_r, size_r, 0) < 0)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const unsigned char *data;
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;
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen ret = openssl_iostream_read_more(ssl_io, &data, &size);
739125f23e3312045e620014812fe2249a309cc4Timo Sirainen ssl_io->plain_input->real_stream->try_alloc_limit = 0;
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");
6c6b1e9fd9ab19249e73f5269931d01d831e4346Timo Sirainen o_stream_set_flush_pending(ssl_io->plain_output, TRUE);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenbool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenint openssl_iostream_more(struct ssl_iostream *ssl_io)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if ((ret = ssl_iostream_handshake(ssl_io)) <= 0)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_handle_error_full(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 */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenint openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen return openssl_iostream_handle_error_full(ssl_io, ret, func_name, FALSE);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenint openssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen return openssl_iostream_handle_error_full(ssl_io, ret, func_name, TRUE);
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen if (!ssl_iostream_has_valid_client_cert(ssl_io))
831f3bcdde51fa388462eda1daa555e90651ca2eTimo Sirainen return openssl_cert_match_name(ssl_io->ssl, verify_name);
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,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen "SSL_connect()");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen while ((ret = SSL_accept(ssl_io->ssl)) <= 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen ret = openssl_iostream_handle_error(ssl_io, ret,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainen "SSL_accept()");
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* handshake finished */
ac645fe16c0619771c0a961db91df16485513c52Timo Sirainen if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) < 0) {
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic bool openssl_iostream_is_handshaked(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. */
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenstatic const char *
3faa1040e5a3f9f35ffad29110216094ab2f5880Timo Sirainenopenssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
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)