Blame SOURCES/openldap-cbinding-ITS-9189_1-rework-sasl-cbinding-support.patch

d0db73
NOTE: The patch has been adjusted to match the base code before backporting.
d0db73
d0db73
From 3cd50fa8b32a21040a9892e2a8a7a9dfc7541ce6 Mon Sep 17 00:00:00 2001
d0db73
From: Isaac Boukris <iboukris@gmail.com>
d0db73
Date: Tue, 14 Apr 2020 16:10:48 +0300
d0db73
Subject: [PATCH] ITS#9189 rework sasl-cbinding support
d0db73
d0db73
Add LDAP_OPT_X_SASL_CBINDING option to define the binding type to use,
d0db73
defaults to "none".
d0db73
d0db73
Add "tls-endpoint" binding type implementing "tls-server-end-point" from
d0db73
RCF 5929, which is compatible with Windows.
d0db73
d0db73
Fix "tls-unique" to include the prefix in the bindings as per RFC 5056.
d0db73
---
d0db73
 doc/man/man3/ldap_get_option.3 |  16 ++++++
d0db73
 doc/man/man5/ldap.conf.5       |   3 +
d0db73
 doc/man/man5/slapd-config.5    |   4 ++
d0db73
 doc/man/man5/slapd.conf.5      |   3 +
d0db73
 include/ldap.h                 |   5 ++
d0db73
 include/ldap_pvt.h             |   5 ++
d0db73
 libraries/libldap/cyrus.c      | 101 +++++++++++++++++++++++++++++----
d0db73
 libraries/libldap/init.c       |   1 +
d0db73
 libraries/libldap/ldap-int.h   |   1 +
d0db73
 libraries/libldap/ldap-tls.h   |   2 +
d0db73
 libraries/libldap/tls2.c       |   7 +++
d0db73
 libraries/libldap/tls_g.c      |  59 +++++++++++++++++++
d0db73
 libraries/libldap/tls_o.c      |  45 +++++++++++++++
d0db73
 servers/slapd/bconfig.c        |  11 +++-
d0db73
 servers/slapd/config.c         |   1 +
d0db73
 servers/slapd/connection.c     |   9 +--
d0db73
 servers/slapd/proto-slap.h     |   4 +-
d0db73
 servers/slapd/sasl.c           |  27 ++++++---
d0db73
 18 files changed, 274 insertions(+), 30 deletions(-)
d0db73
d0db73
diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3
d0db73
index 7546875f5..e953900ce 100644
d0db73
--- a/doc/man/man3/ldap_get_option.3
d0db73
+++ b/doc/man/man3/ldap_get_option.3
d0db73
@@ -557,6 +557,22 @@ must be a
d0db73
 .BR "char **" .
d0db73
 Its content needs to be freed by the caller using
d0db73
 .BR ldap_memfree (3).
d0db73
+.B LDAP_OPT_X_SASL_CBINDING
d0db73
+Sets/gets the channel-binding type to use in SASL,
d0db73
+one of
d0db73
+.BR LDAP_OPT_X_SASL_CBINDING_NONE
d0db73
+(the default),
d0db73
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE
d0db73
+the "tls-unique" type from RCF 5929.
d0db73
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
d0db73
+the "tls-server-end-point" from RCF 5929, compatible with Windows.
d0db73
+.BR invalue
d0db73
+must be
d0db73
+.BR "const int *" ;
d0db73
+.BR outvalue
d0db73
+must be
d0db73
+.BR "int *" .
d0db73
+.TP
d0db73
 .SH TCP OPTIONS
d0db73
 The TCP options are OpenLDAP specific.
d0db73
 Mainly intended for use with Linux, they may not be portable.
d0db73
diff --git a/doc/man/man5/ldap.conf.5 b/doc/man/man5/ldap.conf.5
d0db73
index adf134899..29810fc9f 100644
d0db73
--- a/doc/man/man5/ldap.conf.5
d0db73
+++ b/doc/man/man5/ldap.conf.5
d0db73
@@ -286,6 +286,9 @@ size allowed.  0 disables security layers.  The default is 65536.
d0db73
 .TP
d0db73
 .B SASL_NOCANON <on/true/yes/off/false/no>
d0db73
 Do not perform reverse DNS lookups to canonicalize SASL host names. The default is off.
d0db73
+.TP
d0db73
+.B SASL_CBINDING <none/tls-unique/tls-endpoint>
d0db73
+The channel-binding type to use, see also LDAP_OPT_X_SASL_CBINDING. The default is none.
d0db73
 .SH GSSAPI OPTIONS
d0db73
 If OpenLDAP is built with Generic Security Services Application Programming Interface support,
d0db73
 there are more options you can specify.
d0db73
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
d0db73
index 0dddfdb6c..8c987d8c1 100644
d0db73
--- a/doc/man/man5/slapd-config.5
d0db73
+++ b/doc/man/man5/slapd-config.5
d0db73
@@ -699,6 +699,10 @@ Used to specify the fully qualified domain name used for SASL processing.
d0db73
 .B olcSaslRealm: <realm>
d0db73
 Specify SASL realm.  Default is empty.
d0db73
 .TP
d0db73
+.B olcSaslCbinding: none | tls-unique | tls-endpoint
d0db73
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
d0db73
+Default is none.
d0db73
+.TP
d0db73
 .B olcSaslSecProps: <properties>
d0db73
 Used to specify Cyrus SASL security properties.
d0db73
 The
d0db73
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
d0db73
index 0071072b1..203ab988e 100644
d0db73
--- a/doc/man/man5/slapd.conf.5
d0db73
+++ b/doc/man/man5/slapd.conf.5
d0db73
@@ -893,6 +893,9 @@ The
d0db73
 property specifies the maximum security layer receive buffer
d0db73
 size allowed.  0 disables security layers.  The default is 65536.
d0db73
 .TP
d0db73
+.B sasl\-cbinding none | tls-unique | tls-endpoint
d0db73
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
d0db73
+.TP
d0db73
 .B schemadn <dn>
d0db73
 Specify the distinguished name for the subschema subentry that
d0db73
 controls the entries on this server.  The default is "cn=Subschema".
d0db73
diff --git a/include/ldap.h b/include/ldap.h
d0db73
index 88bfcabf8..e8ac968a9 100644
d0db73
--- a/include/ldap.h
d0db73
+++ b/include/ldap.h
d0db73
@@ -180,6 +180,10 @@ LDAP_BEGIN_DECL
d0db73
 #define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1		((3 << 8) + 2)
d0db73
 #define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2		((3 << 8) + 3)
d0db73
 
d0db73
+#define LDAP_OPT_X_SASL_CBINDING_NONE		0
d0db73
+#define LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE	1
d0db73
+#define LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT	2
d0db73
+
d0db73
 /* OpenLDAP SASL options */
d0db73
 #define LDAP_OPT_X_SASL_MECH			0x6100
d0db73
 #define LDAP_OPT_X_SASL_REALM			0x6101
d0db73
@@ -195,6 +199,7 @@ LDAP_BEGIN_DECL
d0db73
 #define LDAP_OPT_X_SASL_NOCANON			0x610b
d0db73
 #define LDAP_OPT_X_SASL_USERNAME		0x610c /* read-only */
d0db73
 #define LDAP_OPT_X_SASL_GSS_CREDS		0x610d
d0db73
+#define LDAP_OPT_X_SASL_CBINDING		0x610e
d0db73
 
d0db73
 /* OpenLDAP GSSAPI options */
d0db73
 #define LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT      0x6200
d0db73
diff --git a/include/ldap_pvt.h b/include/ldap_pvt.h
d0db73
index c586a95b5..b71552ec5 100644
d0db73
--- a/include/ldap_pvt.h
d0db73
+++ b/include/ldap_pvt.h
d0db73
@@ -262,6 +262,10 @@ LDAP_F (void *) ldap_pvt_sasl_mutex_new LDAP_P((void));
d0db73
 LDAP_F (int) ldap_pvt_sasl_mutex_lock LDAP_P((void *mutex));
d0db73
 LDAP_F (int) ldap_pvt_sasl_mutex_unlock LDAP_P((void *mutex));
d0db73
 LDAP_F (void) ldap_pvt_sasl_mutex_dispose LDAP_P((void *mutex));
d0db73
+
d0db73
+LDAP_F (int) ldap_pvt_sasl_cbinding_parse LDAP_P(( const char *arg ));
d0db73
+LDAP_F (void *) ldap_pvt_sasl_cbinding LDAP_P(( void *ssl, int type,
d0db73
+					        int is_server ));
d0db73
 #endif /* HAVE_CYRUS_SASL */
d0db73
 
d0db73
 struct sockbuf; /* avoid pulling in <lber.h> */
d0db73
@@ -426,6 +430,7 @@ LDAP_F (int) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx, struct berval *dn,
d0db73
 	LDAPDN_rewrite_dummy *func, unsigned flags ));
d0db73
 LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx ));
d0db73
 LDAP_F (int) ldap_pvt_tls_get_unique LDAP_P(( void *ctx, struct berval *buf, int is_server ));
d0db73
+LDAP_F (int) ldap_pvt_tls_get_endpoint LDAP_P(( void *ctx, struct berval *buf, int is_server ));
d0db73
 
d0db73
 LDAP_END_DECL
d0db73
 
d0db73
diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c
d0db73
index 3171d56a3..081e3cea5 100644
d0db73
--- a/libraries/libldap/cyrus.c
d0db73
+++ b/libraries/libldap/cyrus.c
d0db73
@@ -368,6 +368,65 @@ int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
d0db73
 	return LDAP_SUCCESS;
d0db73
 }
d0db73
 
d0db73
+int ldap_pvt_sasl_cbinding_parse( const char *arg )
d0db73
+{
d0db73
+	int i = -1;
d0db73
+
d0db73
+	if ( strcasecmp(arg, "none") == 0 )
d0db73
+		i = LDAP_OPT_X_SASL_CBINDING_NONE;
d0db73
+	else if ( strcasecmp(arg, "tls-unique") == 0 )
d0db73
+		i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE;
d0db73
+	else if ( strcasecmp(arg, "tls-endpoint") == 0 )
d0db73
+		i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT;
d0db73
+
d0db73
+	return i;
d0db73
+}
d0db73
+
d0db73
+void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server )
d0db73
+{
d0db73
+#if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS)
d0db73
+	char unique_prefix[] = "tls-unique:";
d0db73
+	char endpoint_prefix[] = "tls-server-end-point:";
d0db73
+	char cbinding[ 64 ];
d0db73
+	struct berval cbv = { 64, cbinding };
d0db73
+	void *cb_data; /* used since cb->data is const* */
d0db73
+	sasl_channel_binding_t *cb;
d0db73
+	char *prefix;
d0db73
+	int plen;
d0db73
+
d0db73
+	switch (type) {
d0db73
+	case LDAP_OPT_X_SASL_CBINDING_NONE:
d0db73
+		return NULL;
d0db73
+	case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
d0db73
+		if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server ))
d0db73
+			return NULL;
d0db73
+		prefix = unique_prefix;
d0db73
+		plen = sizeof(unique_prefix) -1;
d0db73
+		break;
d0db73
+	case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
d0db73
+		if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server ))
d0db73
+			return NULL;
d0db73
+		prefix = endpoint_prefix;
d0db73
+		plen = sizeof(endpoint_prefix) -1;
d0db73
+		break;
d0db73
+	default:
d0db73
+		return NULL;
d0db73
+	}
d0db73
+
d0db73
+	cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len );
d0db73
+	cb->len = plen + cbv.bv_len;
d0db73
+	cb->data = cb_data = cb+1;
d0db73
+	memcpy( cb_data, prefix, plen );
d0db73
+	memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len );
d0db73
+	cb->name = "ldap";
d0db73
+	cb->critical = 0;
d0db73
+
d0db73
+	return cb;
d0db73
+#else
d0db73
+	return NULL;
d0db73
+#endif
d0db73
+}
d0db73
+
d0db73
 int
d0db73
 ldap_int_sasl_bind(
d0db73
 	LDAP			*ld,
d0db73
@@ -497,17 +556,12 @@ ldap_int_sasl_bind(
d0db73
 			(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
d0db73
 			LDAP_FREE( authid.bv_val );
d0db73
 #ifdef SASL_CHANNEL_BINDING	/* 2.1.25+ */
d0db73
-			{
d0db73
-				char cbinding[64];
d0db73
-				struct berval cbv = { sizeof(cbinding), cbinding };
d0db73
-				if ( ldap_pvt_tls_get_unique( ssl, &cbv, 0 )) {
d0db73
-					sasl_channel_binding_t *cb = ldap_memalloc( sizeof(*cb) +
d0db73
-						cbv.bv_len);
d0db73
-					cb->name = "ldap";
d0db73
-					cb->critical = 0;
d0db73
-					cb->data = (char *)(cb+1);
d0db73
-					cb->len = cbv.bv_len;
d0db73
-					memcpy( cb->data, cbv.bv_val, cbv.bv_len );
d0db73
+			if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) {
d0db73
+				void *cb;
d0db73
+				cb = ldap_pvt_sasl_cbinding( ssl,
d0db73
+							     ld->ld_options.ldo_sasl_cbinding,
d0db73
+							     0 );
d0db73
+				if ( cb != NULL ) {
d0db73
 					sasl_setprop( ld->ld_defconn->lconn_sasl_authctx,
d0db73
 						SASL_CHANNEL_BINDING, cb );
d0db73
 					ld->ld_defconn->lconn_sasl_cbind = cb;
d0db73
@@ -930,12 +984,20 @@ int ldap_pvt_sasl_secprops(
d0db73
 int
d0db73
 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
d0db73
 {
d0db73
-	int rc;
d0db73
+	int rc, i;
d0db73
 
d0db73
 	switch( option ) {
d0db73
 	case LDAP_OPT_X_SASL_SECPROPS:
d0db73
 		rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
d0db73
 		if( rc == LDAP_SUCCESS ) return 0;
d0db73
+		break;
d0db73
+	case LDAP_OPT_X_SASL_CBINDING:
d0db73
+		i = ldap_pvt_sasl_cbinding_parse( arg );
d0db73
+		if ( i >= 0 ) {
d0db73
+			lo->ldo_sasl_cbinding = i;
d0db73
+			return 0;
d0db73
+		}
d0db73
+		break;
d0db73
 	}
d0db73
 
d0db73
 	return -1;
d0db73
@@ -1041,6 +1103,10 @@ ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
d0db73
 			/* this option is write only */
d0db73
 			return -1;
d0db73
 
d0db73
+		case LDAP_OPT_X_SASL_CBINDING:
d0db73
+			*(int *)arg = ld->ld_options.ldo_sasl_cbinding;
d0db73
+			break;
d0db73
+
d0db73
 #ifdef SASL_GSS_CREDS
d0db73
 		case LDAP_OPT_X_SASL_GSS_CREDS: {
d0db73
 			sasl_conn_t *ctx;
d0db73
@@ -1142,6 +1208,17 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
d0db73
 		return sc == LDAP_SUCCESS ? 0 : -1;
d0db73
 		}
d0db73
 
d0db73
+	case LDAP_OPT_X_SASL_CBINDING:
d0db73
+		if ( !arg ) return -1;
d0db73
+		switch( *(int *) arg ) {
d0db73
+		case LDAP_OPT_X_SASL_CBINDING_NONE:
d0db73
+		case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
d0db73
+		case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
d0db73
+			ld->ld_options.ldo_sasl_cbinding = *(int *) arg;
d0db73
+			return 0;
d0db73
+		}
d0db73
+		return -1;
d0db73
+
d0db73
 #ifdef SASL_GSS_CREDS
d0db73
 	case LDAP_OPT_X_SASL_GSS_CREDS: {
d0db73
 		sasl_conn_t *ctx;
d0db73
diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
d0db73
index 746824fbd..0c4b6237e 100644
d0db73
--- a/libraries/libldap/init.c
d0db73
+++ b/libraries/libldap/init.c
d0db73
@@ -113,6 +113,7 @@ static const struct ol_attribute {
d0db73
 		offsetof(struct ldapoptions, ldo_def_sasl_authzid)},
d0db73
 	{0, ATTR_SASL,		"SASL_SECPROPS",	NULL,	LDAP_OPT_X_SASL_SECPROPS},
d0db73
 	{0, ATTR_BOOL,		"SASL_NOCANON",	NULL,	LDAP_BOOL_SASL_NOCANON},
d0db73
+	{0, ATTR_SASL,		"SASL_CBINDING",	NULL,	LDAP_OPT_X_SASL_CBINDING},
d0db73
 #endif
d0db73
 
d0db73
 #ifdef HAVE_GSSAPI
d0db73
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
d0db73
index 397894271..08d4b4a92 100644
d0db73
--- a/libraries/libldap/ldap-int.h
d0db73
+++ b/libraries/libldap/ldap-int.h
d0db73
@@ -276,6 +276,7 @@ struct ldapoptions {
d0db73
 
d0db73
 	/* SASL Security Properties */
d0db73
 	struct sasl_security_properties	ldo_sasl_secprops;
d0db73
+	int ldo_sasl_cbinding;
d0db73
 #define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0}
d0db73
 #else
d0db73
 #define LDAP_LDO_SASL_NULLARG
d0db73
diff --git a/libraries/libldap/ldap-tls.h b/libraries/libldap/ldap-tls.h
d0db73
index 103004fa7..77975bb6c 100644
d0db73
--- a/libraries/libldap/ldap-tls.h
d0db73
+++ b/libraries/libldap/ldap-tls.h
d0db73
@@ -42,6 +42,7 @@ typedef int (TI_session_dn)(tls_session *sess, struct berval *dn);
d0db73
 typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
d0db73
 typedef int (TI_session_strength)(tls_session *sess);
d0db73
 typedef int (TI_session_unique)(tls_session *sess, struct berval *buf, int is_server);
d0db73
+typedef int (TI_session_endpoint)(tls_session *sess, struct berval *buf, int is_server);
d0db73
 typedef int (TI_session_peercert)(tls_session *s, struct berval *der);
d0db73
 
d0db73
 typedef void (TI_thr_init)(void);
d0db73
@@ -67,6 +68,7 @@ typedef struct tls_impl {
d0db73
 	TI_session_chkhost *ti_session_chkhost;
d0db73
 	TI_session_strength *ti_session_strength;
d0db73
 	TI_session_unique *ti_session_unique;
d0db73
+	TI_session_endpoint *ti_session_endpoint;
d0db73
 	TI_session_peercert *ti_session_peercert;
d0db73
 
d0db73
 	Sockbuf_IO *ti_sbio;
d0db73
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
d0db73
index 8b1fee748..f74af7d1d 100644
d0db73
--- a/libraries/libldap/tls2.c
d0db73
+++ b/libraries/libldap/tls2.c
d0db73
@@ -1041,6 +1041,13 @@ ldap_pvt_tls_get_unique( void *s, struct berval *buf, int is_server )
d0db73
 	return tls_imp->ti_session_unique( session, buf, is_server );
d0db73
 }
d0db73
 
d0db73
+int
d0db73
+ldap_pvt_tls_get_endpoint( void *s, struct berval *buf, int is_server )
d0db73
+{
d0db73
+	tls_session *session = s;
d0db73
+	return tls_imp->ti_session_endpoint( session, buf, is_server );
d0db73
+}
d0db73
+
d0db73
 int
d0db73
 ldap_pvt_tls_get_peercert( void *s, struct berval *der )
d0db73
 {
d0db73
diff --git a/libraries/libldap/tls_g.c b/libraries/libldap/tls_g.c
d0db73
index 26d9f99ce..52dfcd3ab 100644
d0db73
--- a/libraries/libldap/tls_g.c
d0db73
+++ b/libraries/libldap/tls_g.c
d0db73
@@ -675,6 +675,64 @@ tlsg_session_unique( tls_session *sess, struct berval *buf, int is_server)
d0db73
 	return 0;
d0db73
 }
d0db73
 
d0db73
+static int
d0db73
+tlsg_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
d0db73
+{
d0db73
+	tlsg_session *s = (tlsg_session *)sess;
d0db73
+	const gnutls_datum_t *cert_data;
d0db73
+	gnutls_x509_crt_t server_cert;
d0db73
+	gnutls_digest_algorithm_t md;
d0db73
+	int sign_algo, md_len, rc;
d0db73
+
d0db73
+	if ( is_server )
d0db73
+		cert_data = gnutls_certificate_get_ours( s->session );
d0db73
+	else
d0db73
+		cert_data = gnutls_certificate_get_peers( s->session, NULL );
d0db73
+
d0db73
+	if ( cert_data == NULL )
d0db73
+		return 0;
d0db73
+
d0db73
+	rc = gnutls_x509_crt_init( &server_cert );
d0db73
+	if ( rc != GNUTLS_E_SUCCESS )
d0db73
+		return 0;
d0db73
+
d0db73
+	rc = gnutls_x509_crt_import( server_cert, cert_data, GNUTLS_X509_FMT_DER );
d0db73
+	if ( rc != GNUTLS_E_SUCCESS ) {
d0db73
+		gnutls_x509_crt_deinit( server_cert );
d0db73
+		return 0;
d0db73
+	}
d0db73
+
d0db73
+	sign_algo = gnutls_x509_crt_get_signature_algorithm( server_cert );
d0db73
+	gnutls_x509_crt_deinit( server_cert );
d0db73
+	if ( sign_algo <= GNUTLS_SIGN_UNKNOWN )
d0db73
+		return 0;
d0db73
+
d0db73
+	md = gnutls_sign_get_hash_algorithm( sign_algo );
d0db73
+	if ( md == GNUTLS_DIG_UNKNOWN )
d0db73
+		return 0;
d0db73
+
d0db73
+	/* See RFC 5929 */
d0db73
+	switch (md) {
d0db73
+	case GNUTLS_DIG_NULL:
d0db73
+	case GNUTLS_DIG_MD2:
d0db73
+	case GNUTLS_DIG_MD5:
d0db73
+	case GNUTLS_DIG_SHA1:
d0db73
+		md = GNUTLS_DIG_SHA256;
d0db73
+	}
d0db73
+
d0db73
+	md_len = gnutls_hash_get_len( md );
d0db73
+	if ( md_len == 0 || md_len > buf->bv_len )
d0db73
+		return 0;
d0db73
+
d0db73
+	rc = gnutls_hash_fast( md, cert_data->data, cert_data->size, buf->bv_val );
d0db73
+	if ( rc != GNUTLS_E_SUCCESS )
d0db73
+		return 0;
d0db73
+
d0db73
+	buf->bv_len = md_len;
d0db73
+
d0db73
+	return md_len;
d0db73
+}
d0db73
+
d0db73
 static int
d0db73
 tlsg_session_peercert( tls_session *sess, struct berval *der )
d0db73
 {
d0db73
@@ -950,6 +1008,7 @@ tls_impl ldap_int_tls_impl = {
d0db73
 	tlsg_session_chkhost,
d0db73
 	tlsg_session_strength,
d0db73
 	tlsg_session_unique,
d0db73
+	tlsg_session_endpoint,
d0db73
 	tlsg_session_peercert,
d0db73
 
d0db73
 	&tlsg_sbio,
d0db73
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
d0db73
index 157923289..8ede11572 100644
d0db73
--- a/libraries/libldap/tls_o.c
d0db73
+++ b/libraries/libldap/tls_o.c
d0db73
@@ -861,6 +861,50 @@ tlso_session_unique( tls_session *sess, struct berval *buf, int is_server)
d0db73
 	return buf->bv_len;
d0db73
 }
d0db73
 
d0db73
+static int
d0db73
+tlso_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
d0db73
+{
d0db73
+	tlso_session *s = (tlso_session *)sess;
d0db73
+	const EVP_MD *md;
d0db73
+	unsigned int md_len;
d0db73
+	X509 *cert;
d0db73
+
d0db73
+	if ( buf->bv_len < EVP_MAX_MD_SIZE )
d0db73
+		return 0;
d0db73
+
d0db73
+	if ( is_server )
d0db73
+		cert = SSL_get_certificate( s );
d0db73
+	else
d0db73
+		cert = SSL_get_peer_certificate( s );
d0db73
+
d0db73
+	if ( cert == NULL )
d0db73
+		return 0;
d0db73
+
d0db73
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
d0db73
+	md = EVP_get_digestbynid( X509_get_signature_nid( cert ));
d0db73
+#else
d0db73
+	md = EVP_get_digestbynid(OBJ_obj2nid( cert->sig_alg->algorithm ));
d0db73
+#endif
d0db73
+
d0db73
+	/* See RFC 5929 */
d0db73
+	if ( md == NULL ||
d0db73
+	     md == EVP_md_null() ||
d0db73
+#ifndef OPENSSL_NO_MD2
d0db73
+	     md == EVP_md2() ||
d0db73
+#endif
d0db73
+	     md == EVP_md4() ||
d0db73
+	     md == EVP_md5() ||
d0db73
+	     md == EVP_sha1() )
d0db73
+		md = EVP_sha256();
d0db73
+
d0db73
+	if ( !X509_digest( cert, md, buf->bv_val, &md_len ))
d0db73
+		return 0;
d0db73
+
d0db73
+	buf->bv_len = md_len;
d0db73
+
d0db73
+	return md_len;
d0db73
+}
d0db73
+
d0db73
 static int
d0db73
 tlso_session_peercert( tls_session *sess, struct berval *der )
d0db73
 {
d0db73
@@ -1394,6 +1438,7 @@ tls_impl ldap_int_tls_impl = {
d0db73
 	tlso_session_chkhost,
d0db73
 	tlso_session_strength,
d0db73
 	tlso_session_unique,
d0db73
+	tlso_session_endpoint,
d0db73
 	tlso_session_peercert,
d0db73
 
d0db73
 	&tlso_sbio,
d0db73
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
d0db73
index 3188ccfbe..8c4ccb860 100644
d0db73
--- a/servers/slapd/bconfig.c
d0db73
+++ b/servers/slapd/bconfig.c
d0db73
@@ -569,6 +569,15 @@ static ConfigTable config_back_cf_table[] = {
d0db73
 #endif
d0db73
 		"( OLcfgGlAt:89 NAME 'olcSaslAuxprops' "
d0db73
 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
d0db73
+	{ "sasl-cbinding", NULL, 2, 2, 0,
d0db73
+#ifdef HAVE_CYRUS_SASL
d0db73
+		ARG_STRING, &sasl_cbinding,
d0db73
+#else
d0db73
+		ARG_IGNORED, NULL,
d0db73
+#endif
d0db73
+		"( OLcfgGlAt:100 NAME 'olcSaslCBinding' "
d0db73
+			"EQUALITY caseIgnoreMatch "
d0db73
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
d0db73
 	{ "sasl-host", "host", 2, 2, 0,
d0db73
 #ifdef HAVE_CYRUS_SASL
d0db73
 		ARG_STRING|ARG_UNIQUE, &sasl_host,
d0db73
@@ -820,7 +829,7 @@ static ConfigOCs cf_ocs[] = {
d0db73
 		 "olcPluginLogFile $ olcReadOnly $ olcReferral $ "
d0db73
 		 "olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
d0db73
 		 "olcRootDSE $ "
d0db73
-		 "olcSaslAuxprops $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
d0db73
+		 "olcSaslAuxprops $ olcSaslCBinding $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
d0db73
 		 "olcSecurity $ olcServerID $ olcSizeLimit $ "
d0db73
 		 "olcSockbufMaxIncoming $ olcSockbufMaxIncomingAuth $ "
d0db73
 		 "olcTCPBuffer $ "
d0db73
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
d0db73
index 5108da696..77dd3c1ae 100644
d0db73
--- a/servers/slapd/config.c
d0db73
+++ b/servers/slapd/config.c
d0db73
@@ -73,6 +73,7 @@ char	*global_host = NULL;
d0db73
 struct berval global_host_bv = BER_BVNULL;
d0db73
 char	*global_realm = NULL;
d0db73
 char	*sasl_host = NULL;
d0db73
+char	*sasl_cbinding = NULL;
d0db73
 char		**default_passwd_hash = NULL;
d0db73
 struct berval default_search_base = BER_BVNULL;
d0db73
 struct berval default_search_nbase = BER_BVNULL;
d0db73
diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c
d0db73
index 0602fdceb..d074009e4 100644
d0db73
--- a/servers/slapd/connection.c
d0db73
+++ b/servers/slapd/connection.c
d0db73
@@ -1430,12 +1430,9 @@ connection_read( ber_socket_t s, conn_readinfo *cri )
d0db73
 			    c->c_connid, (int) s, c->c_tls_ssf, c->c_ssf, 0 );
d0db73
 			slap_sasl_external( c, c->c_tls_ssf, &authid );
d0db73
 			if ( authid.bv_val ) free( authid.bv_val );
d0db73
-			{
d0db73
-				char cbinding[64];
d0db73
-				struct berval cbv = { sizeof(cbinding), cbinding };
d0db73
-				if ( ldap_pvt_tls_get_unique( ssl, &cbv, 1 ))
d0db73
-					slap_sasl_cbinding( c, &cbv );
d0db73
-			}
d0db73
+
d0db73
+			slap_sasl_cbinding( c, ssl );
d0db73
+
d0db73
 		} else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
d0db73
 			LBER_SB_OPT_NEEDS_WRITE, NULL )) {	/* need to retry */
d0db73
 			slapd_set_write( s, 1 );
d0db73
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
d0db73
index de1cabf32..9b52760bd 100644
d0db73
--- a/servers/slapd/proto-slap.h
d0db73
+++ b/servers/slapd/proto-slap.h
d0db73
@@ -1657,8 +1657,7 @@ LDAP_SLAPD_F (int) slap_sasl_external( Connection *c,
d0db73
 	slap_ssf_t ssf,	/* relative strength of external security */
d0db73
 	struct berval *authid );	/* asserted authenication id */
d0db73
 
d0db73
-LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c,
d0db73
-	struct berval *cbv );
d0db73
+LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c, void *ssl );
d0db73
 
d0db73
 LDAP_SLAPD_F (int) slap_sasl_reset( Connection *c );
d0db73
 LDAP_SLAPD_F (int) slap_sasl_close( Connection *c );
d0db73
@@ -2039,6 +2038,7 @@ LDAP_SLAPD_V (char *)	global_host;
d0db73
 LDAP_SLAPD_V (struct berval)	global_host_bv;
d0db73
 LDAP_SLAPD_V (char *)	global_realm;
d0db73
 LDAP_SLAPD_V (char *)	sasl_host;
d0db73
+LDAP_SLAPD_V (char *)	sasl_cbinding;
d0db73
 LDAP_SLAPD_V (char *)	slap_sasl_auxprops;
d0db73
 LDAP_SLAPD_V (char **)	default_passwd_hash;
d0db73
 LDAP_SLAPD_V (int)		lber_debug;
d0db73
diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c
d0db73
index 258cd5407..c14e8a628 100644
d0db73
--- a/servers/slapd/sasl.c
d0db73
+++ b/servers/slapd/sasl.c
d0db73
@@ -1203,6 +1203,8 @@ int slap_sasl_destroy( void )
d0db73
 #endif
d0db73
 	free( sasl_host );
d0db73
 	sasl_host = NULL;
d0db73
+	free( sasl_cbinding );
d0db73
+	sasl_cbinding = NULL;
d0db73
 
d0db73
 	return 0;
d0db73
 }
d0db73
@@ -1389,17 +1391,24 @@ int slap_sasl_external(
d0db73
 	return LDAP_SUCCESS;
d0db73
 }
d0db73
 
d0db73
-int slap_sasl_cbinding( Connection *conn, struct berval *cbv )
d0db73
+int slap_sasl_cbinding( Connection *conn, void *ssl )
d0db73
 {
d0db73
 #ifdef SASL_CHANNEL_BINDING
d0db73
-	sasl_channel_binding_t *cb = ch_malloc( sizeof(*cb) + cbv->bv_len );;
d0db73
-	cb->name = "ldap";
d0db73
-	cb->critical = 0;
d0db73
-	cb->data = (char *)(cb+1);
d0db73
-	cb->len = cbv->bv_len;
d0db73
-	memcpy( cb->data, cbv->bv_val, cbv->bv_len );
d0db73
-	sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
d0db73
-	conn->c_sasl_cbind = cb;
d0db73
+	void *cb;
d0db73
+	int i;
d0db73
+
d0db73
+	if ( sasl_cbinding == NULL )
d0db73
+		return LDAP_SUCCESS;
d0db73
+
d0db73
+	i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
d0db73
+	if ( i < 0 )
d0db73
+		return LDAP_SUCCESS;
d0db73
+
d0db73
+	cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
d0db73
+	if ( cb != NULL ) {
d0db73
+		sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
d0db73
+		conn->c_sasl_cbind = cb;
d0db73
+	}
d0db73
 #endif
d0db73
 	return LDAP_SUCCESS;
d0db73
 }
d0db73
-- 
d0db73
2.29.2
d0db73