Mercurial > dovecot > original-hg > dovecot-1.2
diff src/login-common/ssl-proxy-openssl.c @ 8985:f43bebab3dac HEAD
imap/pop3 proxy: Support SSL/TLS connections to remote servers.
passdb can return ssl=yes, ssl=any-cert and starttls options.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 28 Apr 2009 22:31:40 -0400 |
parents | 1c6361f7111d |
children | d475e17d01a3 |
line wrap: on
line diff
--- a/src/login-common/ssl-proxy-openssl.c Tue Apr 28 19:57:10 2009 -0400 +++ b/src/login-common/ssl-proxy-openssl.c Tue Apr 28 22:31:40 2009 -0400 @@ -49,11 +49,15 @@ unsigned char sslout_buf[1024]; unsigned int sslout_size; + ssl_handshake_callback_t *handshake_callback; + void *handshake_callback_context; + char *last_error; unsigned int handshaked:1; unsigned int destroyed:1; unsigned int cert_received:1; unsigned int cert_broken:1; + unsigned int client:1; }; struct ssl_parameters { @@ -65,7 +69,8 @@ }; static int extdata_index; -static SSL_CTX *ssl_ctx; +static SSL_CTX *ssl_server_ctx; +static SSL_CTX *ssl_client_ctx; static unsigned int ssl_proxy_count; static struct ssl_proxy *ssl_proxies; static struct ssl_parameters ssl_params; @@ -396,16 +401,27 @@ { int ret; - ret = SSL_accept(proxy->ssl); - if (ret != 1) - ssl_handle_error(proxy, ret, "SSL_accept()"); - else { - i_free_and_null(proxy->last_error); - proxy->handshaked = TRUE; + if (proxy->client) { + ret = SSL_connect(proxy->ssl); + if (ret != 1) { + ssl_handle_error(proxy, ret, "SSL_connect()"); + return; + } + } else { + ret = SSL_accept(proxy->ssl); + if (ret != 1) { + ssl_handle_error(proxy, ret, "SSL_accept()"); + return; + } + } + i_free_and_null(proxy->last_error); + proxy->handshaked = TRUE; - ssl_set_io(proxy, SSL_ADD_INPUT); - plain_block_input(proxy, FALSE); - } + ssl_set_io(proxy, SSL_ADD_INPUT); + plain_block_input(proxy, FALSE); + + if (proxy->handshake_callback(proxy->handshake_callback_context) < 0) + ssl_proxy_destroy(proxy); } static void ssl_read(struct ssl_proxy *proxy) @@ -473,7 +489,8 @@ ssl_proxy_unref(proxy); } -int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r) +static int ssl_proxy_new_common(SSL_CTX *ssl_ctx, int fd, struct ip_addr *ip, + struct ssl_proxy **proxy_r) { struct ssl_proxy *proxy; SSL *ssl; @@ -523,13 +540,39 @@ ssl_proxy_count++; DLLIST_PREPEND(&ssl_proxies, proxy); - ssl_step(proxy); main_ref(); *proxy_r = proxy; return sfd[1]; } +int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r) +{ + int ret; + + if ((ret = ssl_proxy_new_common(ssl_server_ctx, fd, ip, proxy_r)) < 0) + return -1; + + ssl_step(*proxy_r); + return ret; +} + +int ssl_proxy_client_new(int fd, struct ip_addr *ip, + ssl_handshake_callback_t *callback, void *context, + struct ssl_proxy **proxy_r) +{ + int ret; + + if ((ret = ssl_proxy_new_common(ssl_client_ctx, fd, ip, proxy_r)) < 0) + return -1; + + (*proxy_r)->handshake_callback = callback; + (*proxy_r)->handshake_callback_context = context; + (*proxy_r)->client = TRUE; + ssl_step(*proxy_r); + return ret; +} + bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) { return proxy->cert_received && !proxy->cert_broken; @@ -737,53 +780,63 @@ return strstr(buf, "PRIVATE KEY---") != NULL; } -void ssl_proxy_init(void) +static void ssl_proxy_ctx_init(SSL_CTX *ssl_ctx) { - static char dovecot[] = "dovecot"; - const char *cafile, *certfile, *keyfile, *cipher_list, *username_field; - char *password; - unsigned char buf; - unsigned long err; - - memset(&ssl_params, 0, sizeof(ssl_params)); - - cafile = getenv("SSL_CA_FILE"); - certfile = getenv("SSL_CERT_FILE"); - keyfile = getenv("SSL_KEY_FILE"); - ssl_params.fname = getenv("SSL_PARAM_FILE"); - password = getenv("SSL_KEY_PASSWORD"); - - if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) { - /* SSL support is disabled */ - return; - } - - SSL_library_init(); - SSL_load_error_strings(); - - extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL); - - if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - i_fatal("SSL_CTX_new() failed"); + const char *cafile; SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); - cipher_list = getenv("SSL_CIPHER_LIST"); - if (cipher_list == NULL) - cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST; - if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list) != 1) { - i_fatal("Can't set cipher list to '%s': %s", - cipher_list, ssl_last_error()); - } - + cafile = getenv("SSL_CA_FILE"); if (cafile != NULL) { if (SSL_CTX_load_verify_locations(ssl_ctx, cafile, NULL) != 1) { i_fatal("Can't load CA file %s: %s", cafile, ssl_last_error()); } } + if (verbose_ssl) + SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback); + if (SSL_CTX_need_tmp_RSA(ssl_ctx)) + SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); + SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); +} - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) { +static void ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx) +{ + const char *cafile; +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + X509_STORE *store; + + store = SSL_CTX_get_cert_store(ssl_ctx); + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); +#endif + cafile = getenv("SSL_CA_FILE"); + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, + ssl_verify_client_cert); + SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(cafile)); +} + +static void ssl_proxy_init_server(const char *certfile, const char *keyfile) +{ + const char *cipher_list, *username_field; + char *password; + unsigned long err; + + password = getenv("SSL_KEY_PASSWORD"); + + if ((ssl_server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + i_fatal("SSL_CTX_new() failed"); + ssl_proxy_ctx_init(ssl_server_ctx); + + cipher_list = getenv("SSL_CIPHER_LIST"); + if (cipher_list == NULL) + cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST; + if (SSL_CTX_set_cipher_list(ssl_server_ctx, cipher_list) != 1) { + i_fatal("Can't set cipher list to '%s': %s", + cipher_list, ssl_last_error()); + } + + if (SSL_CTX_use_certificate_chain_file(ssl_server_ctx, certfile) != 1) { err = ERR_peek_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM || ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { @@ -801,35 +854,16 @@ } } - SSL_CTX_set_default_passwd_cb(ssl_ctx, pem_password_callback); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, password); - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, + SSL_CTX_set_default_passwd_cb(ssl_server_ctx, pem_password_callback); + SSL_CTX_set_default_passwd_cb_userdata(ssl_server_ctx, password); + if (SSL_CTX_use_PrivateKey_file(ssl_server_ctx, keyfile, SSL_FILETYPE_PEM) != 1) { i_fatal("Can't load private key file %s: %s", keyfile, ssl_last_error()); } - if (SSL_CTX_need_tmp_RSA(ssl_ctx)) - SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); - SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); - - if (verbose_ssl) - SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback); - - if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) { -#if OPENSSL_VERSION_NUMBER >= 0x00907000L - X509_STORE *store; - - store = SSL_CTX_get_cert_store(ssl_ctx); - X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | - X509_V_FLAG_CRL_CHECK_ALL); -#endif - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE, - ssl_verify_client_cert); - SSL_CTX_set_client_CA_list(ssl_ctx, - SSL_load_client_CA_file(cafile)); - } + if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) + ssl_proxy_ctx_verify_client(ssl_server_ctx); username_field = getenv("SSL_CERT_USERNAME_FIELD"); if (username_field == NULL) @@ -841,6 +875,39 @@ username_field); } } +} + +static void ssl_proxy_init_client(void) +{ + if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) + i_fatal("SSL_CTX_new() failed"); + ssl_proxy_ctx_init(ssl_client_ctx); + ssl_proxy_ctx_verify_client(ssl_client_ctx); +} + +void ssl_proxy_init(void) +{ + static char dovecot[] = "dovecot"; + const char *certfile, *keyfile; + unsigned char buf; + + memset(&ssl_params, 0, sizeof(ssl_params)); + + certfile = getenv("SSL_CERT_FILE"); + keyfile = getenv("SSL_KEY_FILE"); + ssl_params.fname = getenv("SSL_PARAM_FILE"); + + if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) { + /* SSL support is disabled */ + return; + } + + SSL_library_init(); + SSL_load_error_strings(); + + extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL); + ssl_proxy_init_server(certfile, keyfile); + ssl_proxy_init_client(); /* PRNG initialization might want to use /dev/urandom, make sure it does it before chrooting. We might not have enough entropy at @@ -862,7 +929,8 @@ ssl_proxy_destroy(ssl_proxies); ssl_free_parameters(&ssl_params); - SSL_CTX_free(ssl_ctx); + SSL_CTX_free(ssl_server_ctx); + SSL_CTX_free(ssl_client_ctx); EVP_cleanup(); ERR_free_strings(); }