Matúš Honěk b325dd4
ITS#7595 Add Elliptic Curve support for OpenSSL
Matúš Honěk b325dd4
Matúš Honěk b325dd4
Cherry-picked upstream e631ce808ed56119e61321463d06db7999ba5a08
Matúš Honěk b325dd4
Author:    Howard Chu <hyc@openldap.org>
Matúš Honěk b325dd4
Date:      Sat Sep 7 09:47:19 2013 -0700
Matúš Honěk b325dd4
Matúš Honěk b325dd4
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
Matúš Honěk b325dd4
index 9c72e8296..2311c3096 100644
Matúš Honěk b325dd4
--- a/doc/man/man5/slapd-config.5
Matúš Honěk b325dd4
+++ b/doc/man/man5/slapd-config.5
Matúš Honěk b325dd4
@@ -922,6 +922,13 @@ are not used.
Matúš Honěk b325dd4
 When using Mozilla NSS these parameters are always generated randomly
Matúš Honěk b325dd4
 so this directive is ignored.
Matúš Honěk b325dd4
 .TP
Matúš Honěk b325dd4
+.B olcTLSECName: <name>
Matúš Honěk b325dd4
+Specify the name of a curve to use for Elliptic curve Diffie-Hellman
Matúš Honěk b325dd4
+ephemeral key exchange.  This is required to enable ECDHE algorithms in
Matúš Honěk b325dd4
+OpenSSL.  This option is not used with GnuTLS; the curves may be
Matúš Honěk b325dd4
+chosen in the GnuTLS ciphersuite specification. This option is also
Matúš Honěk b325dd4
+ignored for Mozilla NSS.
Matúš Honěk b325dd4
+.TP
Matúš Honěk b325dd4
 .B olcTLSProtocolMin: <major>[.<minor>]
Matúš Honěk b325dd4
 Specifies minimum SSL/TLS protocol version that will be negotiated.
Matúš Honěk b325dd4
 If the server doesn't support at least that version,
Matúš Honěk b325dd4
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
Matúš Honěk b325dd4
index f504adcf9..ef03e0ad8 100644
Matúš Honěk b325dd4
--- a/doc/man/man5/slapd.conf.5
Matúš Honěk b325dd4
+++ b/doc/man/man5/slapd.conf.5
Matúš Honěk b325dd4
@@ -1153,6 +1153,13 @@ are not used.
Matúš Honěk b325dd4
 When using Mozilla NSS these parameters are always generated randomly
Matúš Honěk b325dd4
 so this directive is ignored.
Matúš Honěk b325dd4
 .TP
Matúš Honěk b325dd4
+.B TLSECName <name>
Matúš Honěk b325dd4
+Specify the name of a curve to use for Elliptic curve Diffie-Hellman
Matúš Honěk b325dd4
+ephemeral key exchange.  This is required to enable ECDHE algorithms in
Matúš Honěk b325dd4
+OpenSSL.  This option is not used with GnuTLS; the curves may be
Matúš Honěk b325dd4
+chosen in the GnuTLS ciphersuite specification. This option is also
Matúš Honěk b325dd4
+ignored for Mozilla NSS.
Matúš Honěk b325dd4
+.TP
Matúš Honěk b325dd4
 .B TLSProtocolMin <major>[.<minor>]
Matúš Honěk b325dd4
 Specifies minimum SSL/TLS protocol version that will be negotiated.
Matúš Honěk b325dd4
 If the server doesn't support at least that version,
Matúš Honěk b325dd4
diff --git a/include/ldap.h b/include/ldap.h
Matúš Honěk b325dd4
index c245651c2..0964a193e 100644
Matúš Honěk b325dd4
--- a/include/ldap.h
Matúš Honěk b325dd4
+++ b/include/ldap.h
Matúš Honěk b325dd4
@@ -158,6 +158,7 @@ LDAP_BEGIN_DECL
Matúš Honěk b325dd4
 #define LDAP_OPT_X_TLS_NEWCTX		0x600f
Matúš Honěk b325dd4
 #define LDAP_OPT_X_TLS_CRLFILE		0x6010	/* GNUtls only */
Matúš Honěk b325dd4
 #define LDAP_OPT_X_TLS_PACKAGE		0x6011
Matúš Honěk b325dd4
+#define LDAP_OPT_X_TLS_ECNAME		0x6012
Matúš Honěk b325dd4
 
Matúš Honěk b325dd4
 #define LDAP_OPT_X_TLS_NEVER	0
Matúš Honěk b325dd4
 #define LDAP_OPT_X_TLS_HARD		1
Matúš Honěk b325dd4
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
Matúš Honěk b325dd4
index 66e04ae80..db7193f4f 100644
Matúš Honěk b325dd4
--- a/libraries/libldap/ldap-int.h
Matúš Honěk b325dd4
+++ b/libraries/libldap/ldap-int.h
Matúš Honěk b325dd4
@@ -165,6 +165,7 @@ struct ldaptls {
Matúš Honěk b325dd4
 	char		*lt_ciphersuite;
Matúš Honěk b325dd4
 	char		*lt_crlfile;
Matúš Honěk b325dd4
 	char		*lt_randfile;	/* OpenSSL only */
Matúš Honěk b325dd4
+	char		*lt_ecname;		/* OpenSSL only */
Matúš Honěk b325dd4
 	int		lt_protocol_min;
Matúš Honěk b325dd4
 };
Matúš Honěk b325dd4
 #endif
Matúš Honěk b325dd4
@@ -250,6 +251,7 @@ struct ldapoptions {
Matúš Honěk b325dd4
 #define ldo_tls_certfile	ldo_tls_info.lt_certfile
Matúš Honěk b325dd4
 #define ldo_tls_keyfile	ldo_tls_info.lt_keyfile
Matúš Honěk b325dd4
 #define ldo_tls_dhfile	ldo_tls_info.lt_dhfile
Matúš Honěk b325dd4
+#define ldo_tls_ecname	ldo_tls_info.lt_ecname
Matúš Honěk b325dd4
 #define ldo_tls_cacertfile	ldo_tls_info.lt_cacertfile
Matúš Honěk b325dd4
 #define ldo_tls_cacertdir	ldo_tls_info.lt_cacertdir
Matúš Honěk b325dd4
 #define ldo_tls_ciphersuite	ldo_tls_info.lt_ciphersuite
Matúš Honěk b325dd4
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
Matúš Honěk b325dd4
index d25c190ea..0451b01af 100644
Matúš Honěk b325dd4
--- a/libraries/libldap/tls2.c
Matúš Honěk b325dd4
+++ b/libraries/libldap/tls2.c
Matúš Honěk b325dd4
@@ -118,6 +118,10 @@ ldap_int_tls_destroy( struct ldapoptions *lo )
Matúš Honěk b325dd4
 		LDAP_FREE( lo->ldo_tls_dhfile );
Matúš Honěk b325dd4
 		lo->ldo_tls_dhfile = NULL;
Matúš Honěk b325dd4
 	}
Matúš Honěk b325dd4
+	if ( lo->ldo_tls_ecname ) {
Matúš Honěk b325dd4
+		LDAP_FREE( lo->ldo_tls_ecname );
Matúš Honěk b325dd4
+		lo->ldo_tls_ecname = NULL;
Matúš Honěk b325dd4
+	}
Matúš Honěk b325dd4
 	if ( lo->ldo_tls_cacertfile ) {
Matúš Honěk b325dd4
 		LDAP_FREE( lo->ldo_tls_cacertfile );
Matúš Honěk b325dd4
 		lo->ldo_tls_cacertfile = NULL;
Matúš Honěk b325dd4
@@ -232,6 +236,10 @@ ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
Matúš Honěk b325dd4
 		lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile );
Matúš Honěk b325dd4
 		__atoe( lts.lt_dhfile );
Matúš Honěk b325dd4
 	}
Matúš Honěk b325dd4
+	if ( lts.lt_ecname ) {
Matúš Honěk b325dd4
+		lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname );
Matúš Honěk b325dd4
+		__atoe( lts.lt_ecname );
Matúš Honěk b325dd4
+	}
Matúš Honěk b325dd4
 #endif
Matúš Honěk b325dd4
 	lo->ldo_tls_ctx = ti->ti_ctx_new( lo );
Matúš Honěk b325dd4
 	if ( lo->ldo_tls_ctx == NULL ) {
Matúš Honěk b325dd4
@@ -257,6 +265,7 @@ error_exit:
Matúš Honěk b325dd4
 	LDAP_FREE( lts.lt_crlfile );
Matúš Honěk b325dd4
 	LDAP_FREE( lts.lt_cacertdir );
Matúš Honěk b325dd4
 	LDAP_FREE( lts.lt_dhfile );
Matúš Honěk b325dd4
+	LDAP_FREE( lts.lt_ecname );
Matúš Honěk b325dd4
 #endif
Matúš Honěk b325dd4
 	return rc;
Matúš Honěk b325dd4
 }
Matúš Honěk b325dd4
@@ -646,6 +655,10 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
Matúš Honěk b325dd4
 		*(char **)arg = lo->ldo_tls_dhfile ?
Matúš Honěk b325dd4
 			LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL;
Matúš Honěk b325dd4
 		break;
Matúš Honěk b325dd4
+	case LDAP_OPT_X_TLS_ECNAME:
Matúš Honěk b325dd4
+		*(char **)arg = lo->ldo_tls_ecname ?
Matúš Honěk b325dd4
+			LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL;
Matúš Honěk b325dd4
+		break;
Matúš Honěk b325dd4
 	case LDAP_OPT_X_TLS_CRLFILE:	/* GnuTLS only */
Matúš Honěk b325dd4
 		*(char **)arg = lo->ldo_tls_crlfile ?
Matúš Honěk b325dd4
 			LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL;
Matúš Honěk b325dd4
@@ -765,6 +778,10 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
Matúš Honěk b325dd4
 		if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile );
Matúš Honěk b325dd4
 		lo->ldo_tls_dhfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
Matúš Honěk b325dd4
 		return 0;
Matúš Honěk b325dd4
+	case LDAP_OPT_X_TLS_ECNAME:
Matúš Honěk b325dd4
+		if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname );
Matúš Honěk b325dd4
+		lo->ldo_tls_ecname = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
Matúš Honěk b325dd4
+		return 0;
Matúš Honěk b325dd4
 	case LDAP_OPT_X_TLS_CRLFILE:	/* GnuTLS only */
Matúš Honěk b325dd4
 		if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile );
Matúš Honěk b325dd4
 		lo->ldo_tls_crlfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
Matúš Honěk b325dd4
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
Matúš Honěk b325dd4
index f24060b7e..1370923af 100644
Matúš Honěk b325dd4
--- a/libraries/libldap/tls_o.c
Matúš Honěk b325dd4
+++ b/libraries/libldap/tls_o.c
Matúš Honěk b325dd4
@@ -373,10 +373,9 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
Matúš Honěk b325dd4
 		return -1;
Matúš Honěk b325dd4
 	}
Matúš Honěk b325dd4
 
Matúš Honěk b325dd4
-	if ( lo->ldo_tls_dhfile ) {
Matúš Honěk b325dd4
-		DH *dh = NULL;
Matúš Honěk b325dd4
+	if ( is_server && lo->ldo_tls_dhfile ) {
Matúš Honěk b325dd4
+		DH *dh;
Matúš Honěk b325dd4
 		BIO *bio;
Matúš Honěk b325dd4
-		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
Matúš Honěk b325dd4
 
Matúš Honěk b325dd4
 		if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) {
Matúš Honěk b325dd4
 			Debug( LDAP_DEBUG_ANY,
Matúš Honěk b325dd4
@@ -395,7 +394,35 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
Matúš Honěk b325dd4
 		}
Matúš Honěk b325dd4
 		BIO_free( bio );
Matúš Honěk b325dd4
 		SSL_CTX_set_tmp_dh( ctx, dh );
Matúš Honěk b325dd4
+		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
Matúš Honěk b325dd4
+		DH_free( dh );
Matúš Honěk b325dd4
+	}
Matúš Honěk b325dd4
+
Matúš Honěk b325dd4
+#ifdef SSL_OP_SINGLE_ECDH_USE
Matúš Honěk b325dd4
+	if ( is_server && lo->ldo_tls_ecname ) {
Matúš Honěk b325dd4
+		EC_KEY *ecdh;
Matúš Honěk b325dd4
+
Matúš Honěk b325dd4
+		int nid = OBJ_sn2nid( lt->lt_ecname );
Matúš Honěk b325dd4
+		if ( nid == NID_undef ) {
Matúš Honěk b325dd4
+			Debug( LDAP_DEBUG_ANY,
Matúš Honěk b325dd4
+				"TLS: could not use EC name `%s'.\n",
Matúš Honěk b325dd4
+				lo->ldo_tls_ecname,0,0);
Matúš Honěk b325dd4
+			tlso_report_error();
Matúš Honěk b325dd4
+			return -1;
Matúš Honěk b325dd4
+		}
Matúš Honěk b325dd4
+		ecdh = EC_KEY_new_by_curve_name( nid );
Matúš Honěk b325dd4
+		if ( ecdh == NULL ) {
Matúš Honěk b325dd4
+			Debug( LDAP_DEBUG_ANY,
Matúš Honěk b325dd4
+				"TLS: could not generate key for EC name `%s'.\n",
Matúš Honěk b325dd4
+				lo->ldo_tls_ecname,0,0);
Matúš Honěk b325dd4
+			tlso_report_error();
Matúš Honěk b325dd4
+			return -1;
Matúš Honěk b325dd4
+		}
Matúš Honěk b325dd4
+		SSL_CTX_set_tmp_ecdh( ctx, ecdh );
Matúš Honěk b325dd4
+		SSL_CTX_set_options( ctx, SSL_OP_SINGLE_ECDH_USE );
Matúš Honěk b325dd4
+		EC_KEY_free( ecdh );
Matúš Honěk b325dd4
 	}
Matúš Honěk b325dd4
+#endif
Matúš Honěk b325dd4
 
Matúš Honěk b325dd4
 	if ( tlso_opt_trace ) {
Matúš Honěk b325dd4
 		SSL_CTX_set_info_callback( ctx, tlso_info_cb );
Matúš Honěk b325dd4
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
Matúš Honěk b325dd4
index 250f14100..8b1e4e582 100644
Matúš Honěk b325dd4
--- a/servers/slapd/bconfig.c
Matúš Honěk b325dd4
+++ b/servers/slapd/bconfig.c
Matúš Honěk b325dd4
@@ -194,6 +194,7 @@ enum {
Matúš Honěk b325dd4
 	CFG_ACL_ADD,
Matúš Honěk b325dd4
 	CFG_SYNC_SUBENTRY,
Matúš Honěk b325dd4
 	CFG_LTHREADS,
Matúš Honěk b325dd4
+	CFG_TLS_ECNAME,
Matúš Honěk b325dd4
 
Matúš Honěk b325dd4
 	CFG_LAST
Matúš Honěk b325dd4
 };
Matúš Honěk b325dd4
@@ -738,6 +739,14 @@ static ConfigTable config_back_cf_table[] = {
Matúš Honěk b325dd4
 #endif
Matúš Honěk b325dd4
 		"( OLcfgGlAt:77 NAME 'olcTLSDHParamFile' "
Matúš Honěk b325dd4
 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
Matúš Honěk b325dd4
+	{ "TLSECName", NULL, 2, 2, 0,
Matúš Honěk b325dd4
+#ifdef HAVE_TLS
Matúš Honěk b325dd4
+		CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC, &config_tls_option,
Matúš Honěk b325dd4
+#else
Matúš Honěk b325dd4
+		ARG_IGNORED, NULL,
Matúš Honěk b325dd4
+#endif
Matúš Honěk b325dd4
+		"( OLcfgGlAt:96 NAME 'olcTLSECName' "
Matúš Honěk b325dd4
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
Matúš Honěk b325dd4
 	{ "TLSProtocolMin",	NULL, 2, 2, 0,
Matúš Honěk b325dd4
 #ifdef HAVE_TLS
Matúš Honěk b325dd4
 		CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC, &config_tls_config,
Matúš Honěk b325dd4
@@ -819,7 +828,7 @@ static ConfigOCs cf_ocs[] = {
Matúš Honěk b325dd4
 		 "olcThreads $ olcTimeLimit $ olcTLSCACertificateFile $ "
Matúš Honěk b325dd4
 		 "olcTLSCACertificatePath $ olcTLSCertificateFile $ "
Matúš Honěk b325dd4
 		 "olcTLSCertificateKeyFile $ olcTLSCipherSuite $ olcTLSCRLCheck $ "
Matúš Honěk b325dd4
-		 "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ "
Matúš Honěk b325dd4
+		 "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ olcTLSECName $ "
Matúš Honěk b325dd4
 		 "olcTLSCRLFile $ olcTLSProtocolMin $ olcToolThreads $ olcWriteTimeout $ "
Matúš Honěk b325dd4
 		 "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ "
Matúš Honěk b325dd4
 		 "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global },
Matúš Honěk b325dd4
@@ -3824,6 +3833,7 @@ config_tls_option(ConfigArgs *c) {
Matúš Honěk b325dd4
 	case CFG_TLS_CA_PATH:	flag = LDAP_OPT_X_TLS_CACERTDIR;	break;
Matúš Honěk b325dd4
 	case CFG_TLS_CA_FILE:	flag = LDAP_OPT_X_TLS_CACERTFILE;	break;
Matúš Honěk b325dd4
 	case CFG_TLS_DH_FILE:	flag = LDAP_OPT_X_TLS_DHFILE;	break;
Matúš Honěk b325dd4
+	case CFG_TLS_ECNAME:	flag = LDAP_OPT_X_TLS_ECNAME;	break;
Matúš Honěk b325dd4
 #ifdef HAVE_GNUTLS
Matúš Honěk b325dd4
 	case CFG_TLS_CRL_FILE:	flag = LDAP_OPT_X_TLS_CRLFILE;	break;
Matúš Honěk b325dd4
 #endif