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