Blob Blame History Raw
ITS#7595 Add Elliptic Curve support for OpenSSL

Cherry-picked upstream e631ce808ed56119e61321463d06db7999ba5a08
Author:    Howard Chu <hyc@openldap.org>
Date:      Sat Sep 7 09:47:19 2013 -0700

diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
index 49a3959ae..9cd0a4dd1 100644
--- a/doc/man/man5/slapd-config.5
+++ b/doc/man/man5/slapd-config.5
@@ -918,6 +918,13 @@ from the default, otherwise no certificate exchanges or verification will
 be done. When using GnuTLS or Mozilla NSS these parameters are always generated randomly
 so this directive is ignored.
 .TP
+.B olcTLSECName: <name>
+Specify the name of a curve to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange.  This is required to enable ECDHE algorithms in
+OpenSSL.  This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification. This option is also
+ignored for Mozilla NSS.
+.TP
 .B olcTLSProtocolMin: <major>[.<minor>]
 Specifies minimum SSL/TLS protocol version that will be negotiated.
 If the server doesn't support at least that version,
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
index e2344547e..4eb238162 100644
--- a/doc/man/man5/slapd.conf.5
+++ b/doc/man/man5/slapd.conf.5
@@ -1149,6 +1149,13 @@ from the default, otherwise no certificate exchanges or verification will
 be done. When using GnuTLS these parameters are always generated randomly so
 this directive is ignored.  This directive is ignored when using Mozilla NSS.
 .TP
+.B TLSECName <name>
+Specify the name of a curve to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange.  This is required to enable ECDHE algorithms in
+OpenSSL.  This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification. This option is also
+ignored for Mozilla NSS.
+.TP
 .B TLSProtocolMin <major>[.<minor>]
 Specifies minimum SSL/TLS protocol version that will be negotiated.
 If the server doesn't support at least that version,
diff --git a/include/ldap.h b/include/ldap.h
index d4d10fa79..9922c9fa8 100644
--- a/include/ldap.h
+++ b/include/ldap.h
@@ -158,6 +158,7 @@ LDAP_BEGIN_DECL
 #define LDAP_OPT_X_TLS_NEWCTX		0x600f
 #define LDAP_OPT_X_TLS_CRLFILE		0x6010	/* GNUtls only */
 #define LDAP_OPT_X_TLS_PACKAGE		0x6011
+#define LDAP_OPT_X_TLS_ECNAME		0x6012
 #define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY  0x6050
 
 #define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_DISABLED	0
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
index 1a26b3cb0..5fff785d8 100644
--- a/libraries/libldap/ldap-int.h
+++ b/libraries/libldap/ldap-int.h
@@ -165,6 +165,7 @@ struct ldaptls {
 	char		*lt_ciphersuite;
 	char		*lt_crlfile;
 	char		*lt_randfile;	/* OpenSSL only */
+	char		*lt_ecname;		/* OpenSSL only */
 	int		lt_protocol_min;
 };
 #endif
@@ -250,6 +251,7 @@ struct ldapoptions {
 #define ldo_tls_certfile	ldo_tls_info.lt_certfile
 #define ldo_tls_keyfile	ldo_tls_info.lt_keyfile
 #define ldo_tls_dhfile	ldo_tls_info.lt_dhfile
+#define ldo_tls_ecname	ldo_tls_info.lt_ecname
 #define ldo_tls_cacertfile	ldo_tls_info.lt_cacertfile
 #define ldo_tls_cacertdir	ldo_tls_info.lt_cacertdir
 #define ldo_tls_ciphersuite	ldo_tls_info.lt_ciphersuite
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
index a616133da..f39546450 100644
--- a/libraries/libldap/tls2.c
+++ b/libraries/libldap/tls2.c
@@ -121,6 +121,10 @@ ldap_int_tls_destroy( struct ldapoptions *lo )
 		LDAP_FREE( lo->ldo_tls_dhfile );
 		lo->ldo_tls_dhfile = NULL;
 	}
+	if ( lo->ldo_tls_ecname ) {
+		LDAP_FREE( lo->ldo_tls_ecname );
+		lo->ldo_tls_ecname = NULL;
+	}
 	if ( lo->ldo_tls_cacertfile ) {
 		LDAP_FREE( lo->ldo_tls_cacertfile );
 		lo->ldo_tls_cacertfile = NULL;
@@ -257,6 +261,10 @@ ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
 		lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile );
 		__atoe( lts.lt_dhfile );
 	}
+	if ( lts.lt_ecname ) {
+		lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname );
+		__atoe( lts.lt_ecname );
+	}
 #endif
 	lo->ldo_tls_ctx = ti->ti_ctx_new( lo );
 	if ( lo->ldo_tls_ctx == NULL ) {
@@ -282,6 +290,7 @@ error_exit:
 	LDAP_FREE( lts.lt_crlfile );
 	LDAP_FREE( lts.lt_cacertdir );
 	LDAP_FREE( lts.lt_dhfile );
+	LDAP_FREE( lts.lt_ecname );
 #endif
 	return rc;
 }
@@ -674,6 +683,10 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
 		*(char **)arg = lo->ldo_tls_dhfile ?
 			LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL;
 		break;
+	case LDAP_OPT_X_TLS_ECNAME:
+		*(char **)arg = lo->ldo_tls_ecname ?
+			LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL;
+		break;
 	case LDAP_OPT_X_TLS_CRLFILE:	/* GnuTLS only */
 		*(char **)arg = lo->ldo_tls_crlfile ?
 			LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL;
@@ -796,6 +809,10 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
 		if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile );
 		lo->ldo_tls_dhfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
 		return 0;
+	case LDAP_OPT_X_TLS_ECNAME:
+		if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname );
+		lo->ldo_tls_ecname = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
+		return 0;
 	case LDAP_OPT_X_TLS_CRLFILE:	/* GnuTLS only */
 		if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile );
 		lo->ldo_tls_crlfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
index a2d9cd31f..1a81bc625 100644
--- a/libraries/libldap/tls_o.c
+++ b/libraries/libldap/tls_o.c
@@ -296,10 +296,9 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
 		return -1;
 	}
 
-	if ( lo->ldo_tls_dhfile ) {
-		DH *dh = NULL;
+	if ( is_server && lo->ldo_tls_dhfile ) {
+		DH *dh;
 		BIO *bio;
-		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
 
 		if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) {
 			Debug( LDAP_DEBUG_ANY,
@@ -318,7 +317,35 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
 		}
 		BIO_free( bio );
 		SSL_CTX_set_tmp_dh( ctx, dh );
+		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
+		DH_free( dh );
+	}
+
+#ifdef SSL_OP_SINGLE_ECDH_USE
+	if ( is_server && lo->ldo_tls_ecname ) {
+		EC_KEY *ecdh;
+
+		int nid = OBJ_sn2nid( lt->lt_ecname );
+		if ( nid == NID_undef ) {
+			Debug( LDAP_DEBUG_ANY,
+				"TLS: could not use EC name `%s'.\n",
+				lo->ldo_tls_ecname,0,0);
+			tlso_report_error();
+			return -1;
+		}
+		ecdh = EC_KEY_new_by_curve_name( nid );
+		if ( ecdh == NULL ) {
+			Debug( LDAP_DEBUG_ANY,
+				"TLS: could not generate key for EC name `%s'.\n",
+				lo->ldo_tls_ecname,0,0);
+			tlso_report_error();
+			return -1;
+		}
+		SSL_CTX_set_tmp_ecdh( ctx, ecdh );
+		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_ECDH_USE );
+		EC_KEY_free( ecdh );
 	}
+#endif
 
 	if ( tlso_opt_trace ) {
 		SSL_CTX_set_info_callback( ctx, tlso_info_cb );
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
index 8ade0c3f2..5a3c67a72 100644
--- a/servers/slapd/bconfig.c
+++ b/servers/slapd/bconfig.c
@@ -194,6 +194,7 @@ enum {
 	CFG_ACL_ADD,
 	CFG_SYNC_SUBENTRY,
 	CFG_LTHREADS,
+	CFG_TLS_ECNAME,
 
 	CFG_LAST
 };
@@ -738,6 +739,14 @@ static ConfigTable config_back_cf_table[] = {
 #endif
 		"( OLcfgGlAt:77 NAME 'olcTLSDHParamFile' "
 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "TLSECName", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+		CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+		ARG_IGNORED, NULL,
+#endif
+		"( OLcfgGlAt:96 NAME 'olcTLSECName' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
 	{ "TLSProtocolMin",	NULL, 2, 2, 0,
 #ifdef HAVE_TLS
 		CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC, &config_tls_config,
@@ -819,7 +828,7 @@ static ConfigOCs cf_ocs[] = {
 		 "olcThreads $ olcTimeLimit $ olcTLSCACertificateFile $ "
 		 "olcTLSCACertificatePath $ olcTLSCertificateFile $ "
 		 "olcTLSCertificateKeyFile $ olcTLSCipherSuite $ olcTLSCRLCheck $ "
-		 "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ "
+		 "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ olcTLSECName $ "
 		 "olcTLSCRLFile $ olcTLSProtocolMin $ olcToolThreads $ olcWriteTimeout $ "
 		 "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ "
 		 "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global },
@@ -3824,6 +3833,7 @@ config_tls_option(ConfigArgs *c) {
 	case CFG_TLS_CA_PATH:	flag = LDAP_OPT_X_TLS_CACERTDIR;	break;
 	case CFG_TLS_CA_FILE:	flag = LDAP_OPT_X_TLS_CACERTFILE;	break;
 	case CFG_TLS_DH_FILE:	flag = LDAP_OPT_X_TLS_DHFILE;	break;
+	case CFG_TLS_ECNAME:	flag = LDAP_OPT_X_TLS_ECNAME;	break;
 #ifdef HAVE_GNUTLS
 	case CFG_TLS_CRL_FILE:	flag = LDAP_OPT_X_TLS_CRLFILE;	break;
 #endif