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

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