dovecot-2.0: login: Support per-connection ssl_cert/ssl_key.

dovecot at dovecot.org dovecot at dovecot.org
Fri May 15 02:44:33 EEST 2009


details:   http://hg.dovecot.org/dovecot-2.0/rev/a5973f031e7a
changeset: 9285:a5973f031e7a
user:      Timo Sirainen <tss at iki.fi>
date:      Thu May 14 19:44:27 2009 -0400
description:
login: Support per-connection ssl_cert/ssl_key.

diffstat:

1 file changed, 104 insertions(+), 28 deletions(-)
src/login-common/ssl-proxy-openssl.c |  132 ++++++++++++++++++++++++++--------

diffs (188 lines):

diff -r 90312c7416c3 -r a5973f031e7a src/login-common/ssl-proxy-openssl.c
--- a/src/login-common/ssl-proxy-openssl.c	Thu May 14 19:43:50 2009 -0400
+++ b/src/login-common/ssl-proxy-openssl.c	Thu May 14 19:44:27 2009 -0400
@@ -86,6 +86,9 @@ static void ssl_step(struct ssl_proxy *p
 static void ssl_step(struct ssl_proxy *proxy);
 static void ssl_proxy_destroy(struct ssl_proxy *proxy);
 static void ssl_proxy_unref(struct ssl_proxy *proxy);
+
+static int ssl_proxy_use_certificate(SSL *ssl, const char *cert);
+static int ssl_proxy_use_key(SSL *ssl, const struct login_settings *set);
 
 static void ssl_params_corrupted(const char *path)
 {
@@ -521,6 +524,15 @@ ssl_proxy_new_common(SSL_CTX *ssl_ctx, i
 		return -1;
 	}
 
+	if (strcmp(global_login_settings->ssl_cert, set->ssl_cert) != 0 ||
+	    strcmp(global_login_settings->ssl_key, set->ssl_key) != 0) {
+		if (ssl_proxy_use_certificate(ssl, set->ssl_cert) < 0 ||
+		    ssl_proxy_use_key(ssl, set) < 0) {
+			SSL_free(ssl);
+			return -1;
+		}
+	}
+
 	if (SSL_set_fd(ssl, fd) != 1) {
 		i_error("SSL_set_fd() failed: %s", ssl_last_error());
 		SSL_free(ssl);
@@ -812,13 +824,100 @@ ssl_proxy_ctx_verify_client(SSL_CTX *ssl
 				   SSL_load_client_CA_file(set->ssl_ca_file));
 }
 
+static const char *ssl_proxy_get_use_certificate_error(const char *cert)
+{
+	unsigned long err;
+
+	err = ERR_peek_error();
+	if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+	    ERR_GET_REASON(err) != PEM_R_NO_START_LINE)
+		return ssl_last_error();
+	else if (is_pem_key(cert)) {
+		return "The file contains a private key "
+			"(you've mixed ssl_cert and ssl_key settings)";
+	} else {
+		return "There is no certificate.";
+	}
+}
+
+static int ssl_proxy_use_certificate(SSL *ssl, const char *cert)
+{
+	BIO *in;
+	X509 *x;
+	int ret = 0;
+
+	in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
+	if (in == NULL)
+		i_fatal("BIO_new_mem_buf() failed");
+
+	x = PEM_read_bio_X509(in, NULL, NULL, NULL);
+	if (x != NULL) {
+		ret = SSL_use_certificate(ssl, x);
+		if (ERR_peek_error() != 0)
+			ret = 0;
+	}
+
+	if (x != NULL) X509_free(x);
+	BIO_free(in);
+
+	if (ret == 0) {
+		i_error("Can't load ssl_cert: %s",
+			ssl_proxy_get_use_certificate_error(cert));
+		return -1;
+	}
+	return 0;
+}
+
+static EVP_PKEY *ssl_proxy_load_key(const struct login_settings *set)
+{
+	EVP_PKEY *pkey;
+	BIO *bio;
+	char *password;
+
+	bio = BIO_new_mem_buf(t_strdup_noconst(set->ssl_key),
+			      strlen(set->ssl_key));
+	if (bio == NULL)
+		i_fatal("BIO_new_mem_buf() failed");
+
+	password = t_strdup_noconst(set->ssl_key_password);
+	pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback,
+				       password);
+	if (pkey == NULL)
+		i_fatal("Couldn't parse private ssl_key");
+	BIO_free(bio);
+	return pkey;
+}
+
+static void ssl_proxy_ctx_use_key(SSL_CTX *ctx, const struct login_settings *set)
+{
+	EVP_PKEY *pkey;
+
+	pkey = ssl_proxy_load_key(set);
+	if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1)
+		i_fatal("Can't load private ssl_key: %s", ssl_last_error());
+	EVP_PKEY_free(pkey);
+}
+
+static int ssl_proxy_use_key(SSL *ssl, const struct login_settings *set)
+{
+	EVP_PKEY *pkey;
+
+	pkey = ssl_proxy_load_key(set);
+	if (SSL_use_PrivateKey(ssl, pkey) != 1) {
+		i_error("Can't load private ssl_key: %s", ssl_last_error());
+		return -1;
+	}
+	EVP_PKEY_free(pkey);
+	return 0;
+}
+
 static int
 ssl_proxy_ctx_use_certificate_chain(SSL_CTX *ctx, const char *cert)
 {
 	/* mostly just copy&pasted from SSL_CTX_use_certificate_chain_file() */
 	BIO *in;
+	X509 *x;
 	int ret = 0;
-	X509 *x;
 
 	in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
 	if (in == NULL)
@@ -858,16 +957,13 @@ ssl_proxy_ctx_use_certificate_chain(SSL_
 
 end:
 	if (x != NULL) X509_free(x);
-	if (in != NULL) BIO_free(in);
+	BIO_free(in);
 	return ret;
 }
 
 static void ssl_proxy_init_server(const struct login_settings *set)
 {
-	BIO *bio;
-	EVP_PKEY *pkey;
 	char *password;
-	unsigned long err;
 
 	if ((ssl_server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
 		i_fatal("SSL_CTX_new() failed");
@@ -880,35 +976,15 @@ static void ssl_proxy_init_server(const 
 
 	if (ssl_proxy_ctx_use_certificate_chain(ssl_server_ctx,
 						set->ssl_cert) != 1) {
-		err = ERR_peek_error();
-		if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
-		    ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
-			i_fatal("Can't load ssl_cert: %s", ssl_last_error());
-		} else if (is_pem_key(set->ssl_cert)) {
-			i_fatal("Can't load ssl_cert: "
-				"The file contains a private key "
-				"(you've mixed ssl_cert and ssl_key settings)");
-		} else {
-			i_fatal("Can't load ssl_cert: There is no certificate.");
-		}
+		i_fatal("Can't load ssl_cert: %s",
+			ssl_proxy_get_use_certificate_error(set->ssl_cert));
 	}
 
 	password = t_strdup_noconst(set->ssl_key_password);
         SSL_CTX_set_default_passwd_cb(ssl_server_ctx, pem_password_callback);
         SSL_CTX_set_default_passwd_cb_userdata(ssl_server_ctx, password);
 
-	bio = BIO_new_mem_buf(t_strdup_noconst(set->ssl_key),
-			      strlen(set->ssl_key));
-	if (bio == NULL)
-		i_fatal("BIO_new_mem_buf() failed");
-	pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback,
-				       password);
-	if (pkey == NULL)
-		i_fatal("Couldn't parse private ssl_key");
-	if (SSL_CTX_use_PrivateKey(ssl_server_ctx, pkey) != 1)
-		i_fatal("Can't load private ssl_key: %s", ssl_last_error());
-	EVP_PKEY_free(pkey);
-
+	ssl_proxy_ctx_use_key(ssl_server_ctx, set);
 	safe_memset(password, 0, strlen(password));
 
 	if (set->verbose_ssl)


More information about the dovecot-cvs mailing list