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

ef2d9b
NOTE: The patch has been adjusted to match the base code before backporting.
ef2d9b
ef2d9b
From 3cd50fa8b32a21040a9892e2a8a7a9dfc7541ce6 Mon Sep 17 00:00:00 2001
ef2d9b
From: Isaac Boukris <iboukris@gmail.com>
ef2d9b
Date: Tue, 14 Apr 2020 16:10:48 +0300
ef2d9b
Subject: [PATCH] ITS#9189 rework sasl-cbinding support
ef2d9b
ef2d9b
Add LDAP_OPT_X_SASL_CBINDING option to define the binding type to use,
ef2d9b
defaults to "none".
ef2d9b
ef2d9b
Add "tls-endpoint" binding type implementing "tls-server-end-point" from
ef2d9b
RCF 5929, which is compatible with Windows.
ef2d9b
ef2d9b
Fix "tls-unique" to include the prefix in the bindings as per RFC 5056.
ef2d9b
---
ef2d9b
 doc/man/man3/ldap_get_option.3 |  16 +++++
ef2d9b
 doc/man/man5/ldap.conf.5       |   3 +
ef2d9b
 doc/man/man5/slapd-config.5    |   4 ++
ef2d9b
 doc/man/man5/slapd.conf.5      |   3 +
ef2d9b
 include/ldap.h                 |   5 ++
ef2d9b
 include/ldap_pvt.h             |   5 ++
ef2d9b
 libraries/libldap/cyrus.c      | 103 ++++++++++++++++++++++++++++-----
ef2d9b
 libraries/libldap/init.c       |   1 +
ef2d9b
 libraries/libldap/ldap-int.h   |   1 +
ef2d9b
 libraries/libldap/ldap-tls.h   |   2 +
ef2d9b
 libraries/libldap/tls2.c       |   7 +++
ef2d9b
 libraries/libldap/tls_g.c      |  59 +++++++++++++++++++
ef2d9b
 libraries/libldap/tls_o.c      |  45 ++++++++++++++
ef2d9b
 servers/slapd/bconfig.c        |  11 +++-
ef2d9b
 servers/slapd/config.c         |   1 +
ef2d9b
 servers/slapd/connection.c     |   9 +--
ef2d9b
 servers/slapd/proto-slap.h     |   4 +-
ef2d9b
 servers/slapd/sasl.c           |  27 ++++++---
ef2d9b
 18 files changed, 274 insertions(+), 32 deletions(-)
ef2d9b
ef2d9b
diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3
ef2d9b
index 4f03a01a3..fd1b3c91c 100644
ef2d9b
--- a/doc/man/man3/ldap_get_option.3
ef2d9b
+++ b/doc/man/man3/ldap_get_option.3
ef2d9b
@@ -563,6 +563,22 @@ must be a
ef2d9b
 .BR "char **" .
ef2d9b
 Its content needs to be freed by the caller using
ef2d9b
 .BR ldap_memfree (3).
ef2d9b
+.B LDAP_OPT_X_SASL_CBINDING
ef2d9b
+Sets/gets the channel-binding type to use in SASL,
ef2d9b
+one of
ef2d9b
+.BR LDAP_OPT_X_SASL_CBINDING_NONE
ef2d9b
+(the default),
ef2d9b
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE
ef2d9b
+the "tls-unique" type from RCF 5929.
ef2d9b
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
ef2d9b
+the "tls-server-end-point" from RCF 5929, compatible with Windows.
ef2d9b
+.BR invalue
ef2d9b
+must be
ef2d9b
+.BR "const int *" ;
ef2d9b
+.BR outvalue
ef2d9b
+must be
ef2d9b
+.BR "int *" .
ef2d9b
+.TP
ef2d9b
 .SH TCP OPTIONS
ef2d9b
 The TCP options are OpenLDAP specific.
ef2d9b
 Mainly intended for use with Linux, they may not be portable.
ef2d9b
diff --git a/doc/man/man5/ldap.conf.5 b/doc/man/man5/ldap.conf.5
ef2d9b
index 65ad40c1b..4974f8340 100644
ef2d9b
--- a/doc/man/man5/ldap.conf.5
ef2d9b
+++ b/doc/man/man5/ldap.conf.5
ef2d9b
@@ -286,6 +286,9 @@ size allowed.  0 disables security layers.  The default is 65536.
ef2d9b
 .TP
ef2d9b
 .B SASL_NOCANON <on/true/yes/off/false/no>
ef2d9b
 Do not perform reverse DNS lookups to canonicalize SASL host names. The default is off.
ef2d9b
+.TP
ef2d9b
+.B SASL_CBINDING <none/tls-unique/tls-endpoint>
ef2d9b
+The channel-binding type to use, see also LDAP_OPT_X_SASL_CBINDING. The default is none.
ef2d9b
 .SH GSSAPI OPTIONS
ef2d9b
 If OpenLDAP is built with Generic Security Services Application Programming Interface support,
ef2d9b
 there are more options you can specify.
ef2d9b
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
ef2d9b
index 18518a186..dc0ab769f 100644
ef2d9b
--- a/doc/man/man5/slapd-config.5
ef2d9b
+++ b/doc/man/man5/slapd-config.5
ef2d9b
@@ -720,6 +720,10 @@ Used to specify the fully qualified domain name used for SASL processing.
ef2d9b
 .B olcSaslRealm: <realm>
ef2d9b
 Specify SASL realm.  Default is empty.
ef2d9b
 .TP
ef2d9b
+.B olcSaslCbinding: none | tls-unique | tls-endpoint
ef2d9b
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
ef2d9b
+Default is none.
ef2d9b
+.TP
ef2d9b
 .B olcSaslSecProps: <properties>
ef2d9b
 Used to specify Cyrus SASL security properties.
ef2d9b
 The
ef2d9b
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
ef2d9b
index f2094b7fd..73a151a70 100644
ef2d9b
--- a/doc/man/man5/slapd.conf.5
ef2d9b
+++ b/doc/man/man5/slapd.conf.5
ef2d9b
@@ -914,6 +914,9 @@ The
ef2d9b
 property specifies the maximum security layer receive buffer
ef2d9b
 size allowed.  0 disables security layers.  The default is 65536.
ef2d9b
 .TP
ef2d9b
+.B sasl\-cbinding none | tls-unique | tls-endpoint
ef2d9b
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
ef2d9b
+.TP
ef2d9b
 .B schemadn <dn>
ef2d9b
 Specify the distinguished name for the subschema subentry that
ef2d9b
 controls the entries on this server.  The default is "cn=Subschema".
ef2d9b
diff --git a/include/ldap.h b/include/ldap.h
ef2d9b
index 7b4fc9d64..9d5679ae8 100644
ef2d9b
--- a/include/ldap.h
ef2d9b
+++ b/include/ldap.h
ef2d9b
@@ -186,6 +186,10 @@ LDAP_BEGIN_DECL
ef2d9b
 #define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1		((3 << 8) + 2)
ef2d9b
 #define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2		((3 << 8) + 3)
ef2d9b
 
ef2d9b
+#define LDAP_OPT_X_SASL_CBINDING_NONE		0
ef2d9b
+#define LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE	1
ef2d9b
+#define LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT	2
ef2d9b
+
ef2d9b
 /* OpenLDAP SASL options */
ef2d9b
 #define LDAP_OPT_X_SASL_MECH			0x6100
ef2d9b
 #define LDAP_OPT_X_SASL_REALM			0x6101
ef2d9b
@@ -201,6 +205,7 @@ LDAP_BEGIN_DECL
ef2d9b
 #define LDAP_OPT_X_SASL_NOCANON			0x610b
ef2d9b
 #define LDAP_OPT_X_SASL_USERNAME		0x610c /* read-only */
ef2d9b
 #define LDAP_OPT_X_SASL_GSS_CREDS		0x610d
ef2d9b
+#define LDAP_OPT_X_SASL_CBINDING		0x610e
ef2d9b
 
ef2d9b
 /* OpenLDAP GSSAPI options */
ef2d9b
 #define LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT      0x6200
ef2d9b
diff --git a/include/ldap_pvt.h b/include/ldap_pvt.h
ef2d9b
index 783d280a5..01220d00a 100644
ef2d9b
--- a/include/ldap_pvt.h
ef2d9b
+++ b/include/ldap_pvt.h
ef2d9b
@@ -262,6 +262,10 @@ LDAP_F (void *) ldap_pvt_sasl_mutex_new LDAP_P((void));
ef2d9b
 LDAP_F (int) ldap_pvt_sasl_mutex_lock LDAP_P((void *mutex));
ef2d9b
 LDAP_F (int) ldap_pvt_sasl_mutex_unlock LDAP_P((void *mutex));
ef2d9b
 LDAP_F (void) ldap_pvt_sasl_mutex_dispose LDAP_P((void *mutex));
ef2d9b
+
ef2d9b
+LDAP_F (int) ldap_pvt_sasl_cbinding_parse LDAP_P(( const char *arg ));
ef2d9b
+LDAP_F (void *) ldap_pvt_sasl_cbinding LDAP_P(( void *ssl, int type,
ef2d9b
+					        int is_server ));
ef2d9b
 #endif /* HAVE_CYRUS_SASL */
ef2d9b
 
ef2d9b
 struct sockbuf; /* avoid pulling in <lber.h> */
ef2d9b
@@ -438,6 +442,7 @@ LDAP_F (int) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx, struct berval *dn,
ef2d9b
 	LDAPDN_rewrite_dummy *func, unsigned flags ));
ef2d9b
 LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx ));
ef2d9b
 LDAP_F (int) ldap_pvt_tls_get_unique LDAP_P(( void *ctx, struct berval *buf, int is_server ));
ef2d9b
+LDAP_F (int) ldap_pvt_tls_get_endpoint LDAP_P(( void *ctx, struct berval *buf, int is_server ));
ef2d9b
 
ef2d9b
 LDAP_END_DECL
ef2d9b
 
ef2d9b
diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c
ef2d9b
index beb1cf4a0..4d4d5b3e3 100644
ef2d9b
--- a/libraries/libldap/cyrus.c
ef2d9b
+++ b/libraries/libldap/cyrus.c
ef2d9b
@@ -372,6 +372,65 @@ int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
ef2d9b
 	return LDAP_SUCCESS;
ef2d9b
 }
ef2d9b
 
ef2d9b
+int ldap_pvt_sasl_cbinding_parse( const char *arg )
ef2d9b
+{
ef2d9b
+	int i = -1;
ef2d9b
+
ef2d9b
+	if ( strcasecmp(arg, "none") == 0 )
ef2d9b
+		i = LDAP_OPT_X_SASL_CBINDING_NONE;
ef2d9b
+	else if ( strcasecmp(arg, "tls-unique") == 0 )
ef2d9b
+		i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE;
ef2d9b
+	else if ( strcasecmp(arg, "tls-endpoint") == 0 )
ef2d9b
+		i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT;
ef2d9b
+
ef2d9b
+	return i;
ef2d9b
+}
ef2d9b
+
ef2d9b
+void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server )
ef2d9b
+{
ef2d9b
+#if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS)
ef2d9b
+	char unique_prefix[] = "tls-unique:";
ef2d9b
+	char endpoint_prefix[] = "tls-server-end-point:";
ef2d9b
+	char cbinding[ 64 ];
ef2d9b
+	struct berval cbv = { 64, cbinding };
ef2d9b
+	void *cb_data; /* used since cb->data is const* */
ef2d9b
+	sasl_channel_binding_t *cb;
ef2d9b
+	char *prefix;
ef2d9b
+	int plen;
ef2d9b
+
ef2d9b
+	switch (type) {
ef2d9b
+	case LDAP_OPT_X_SASL_CBINDING_NONE:
ef2d9b
+		return NULL;
ef2d9b
+	case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
ef2d9b
+		if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server ))
ef2d9b
+			return NULL;
ef2d9b
+		prefix = unique_prefix;
ef2d9b
+		plen = sizeof(unique_prefix) -1;
ef2d9b
+		break;
ef2d9b
+	case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
ef2d9b
+		if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server ))
ef2d9b
+			return NULL;
ef2d9b
+		prefix = endpoint_prefix;
ef2d9b
+		plen = sizeof(endpoint_prefix) -1;
ef2d9b
+		break;
ef2d9b
+	default:
ef2d9b
+		return NULL;
ef2d9b
+	}
ef2d9b
+
ef2d9b
+	cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len );
ef2d9b
+	cb->len = plen + cbv.bv_len;
ef2d9b
+	cb->data = cb_data = cb+1;
ef2d9b
+	memcpy( cb_data, prefix, plen );
ef2d9b
+	memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len );
ef2d9b
+	cb->name = "ldap";
ef2d9b
+	cb->critical = 0;
ef2d9b
+
ef2d9b
+	return cb;
ef2d9b
+#else
ef2d9b
+	return NULL;
ef2d9b
+#endif
ef2d9b
+}
ef2d9b
+
ef2d9b
 int
ef2d9b
 ldap_int_sasl_bind(
ef2d9b
 	LDAP			*ld,
ef2d9b
@@ -497,17 +556,12 @@ ldap_int_sasl_bind(
ef2d9b
 			(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
ef2d9b
 			LDAP_FREE( authid.bv_val );
ef2d9b
 #ifdef SASL_CHANNEL_BINDING	/* 2.1.25+ */
ef2d9b
-			{
ef2d9b
-				char cbinding[64];
ef2d9b
-				struct berval cbv = { sizeof(cbinding), cbinding };
ef2d9b
-				if ( ldap_pvt_tls_get_unique( ssl, &cbv, 0 )) {
ef2d9b
-					sasl_channel_binding_t *cb = ldap_memalloc( sizeof(*cb) +
ef2d9b
-						cbv.bv_len);
ef2d9b
-					cb->name = "ldap";
ef2d9b
-					cb->critical = 0;
ef2d9b
-					cb->data = (char *)(cb+1);
ef2d9b
-					cb->len = cbv.bv_len;
ef2d9b
-					memcpy( cb->data, cbv.bv_val, cbv.bv_len );
ef2d9b
+			if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) {
ef2d9b
+				void *cb;
ef2d9b
+				cb = ldap_pvt_sasl_cbinding( ssl,
ef2d9b
+							     ld->ld_options.ldo_sasl_cbinding,
ef2d9b
+							     0 );
ef2d9b
+				if ( cb != NULL ) {
ef2d9b
 					sasl_setprop( ld->ld_defconn->lconn_sasl_authctx,
ef2d9b
 						SASL_CHANNEL_BINDING, cb );
ef2d9b
 					ld->ld_defconn->lconn_sasl_cbind = cb;
ef2d9b
@@ -931,12 +983,20 @@ int ldap_pvt_sasl_secprops(
ef2d9b
 int
ef2d9b
 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
ef2d9b
 {
ef2d9b
-	int rc;
ef2d9b
+	int rc, i;
ef2d9b
 
ef2d9b
 	switch( option ) {
ef2d9b
 	case LDAP_OPT_X_SASL_SECPROPS:
ef2d9b
 		rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
ef2d9b
 		if( rc == LDAP_SUCCESS ) return 0;
ef2d9b
+		break;
ef2d9b
+	case LDAP_OPT_X_SASL_CBINDING:
ef2d9b
+		i = ldap_pvt_sasl_cbinding_parse( arg );
ef2d9b
+		if ( i >= 0 ) {
ef2d9b
+			lo->ldo_sasl_cbinding = i;
ef2d9b
+			return 0;
ef2d9b
+		}
ef2d9b
+		break;
ef2d9b
 	}
ef2d9b
 
ef2d9b
 	return -1;
ef2d9b
@@ -1042,6 +1102,10 @@ ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
ef2d9b
 			/* this option is write only */
ef2d9b
 			return -1;
ef2d9b
 
ef2d9b
+		case LDAP_OPT_X_SASL_CBINDING:
ef2d9b
+			*(int *)arg = ld->ld_options.ldo_sasl_cbinding;
ef2d9b
+			break;
ef2d9b
+
ef2d9b
 #ifdef SASL_GSS_CREDS
ef2d9b
 		case LDAP_OPT_X_SASL_GSS_CREDS: {
ef2d9b
 			sasl_conn_t *ctx;
ef2d9b
@@ -1143,6 +1207,17 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
ef2d9b
 		return sc == LDAP_SUCCESS ? 0 : -1;
ef2d9b
 		}
ef2d9b
 
ef2d9b
+	case LDAP_OPT_X_SASL_CBINDING:
ef2d9b
+		if ( !arg ) return -1;
ef2d9b
+		switch( *(int *) arg ) {
ef2d9b
+		case LDAP_OPT_X_SASL_CBINDING_NONE:
ef2d9b
+		case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
ef2d9b
+		case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
ef2d9b
+			ld->ld_options.ldo_sasl_cbinding = *(int *) arg;
ef2d9b
+			return 0;
ef2d9b
+		}
ef2d9b
+		return -1;
ef2d9b
+
ef2d9b
 #ifdef SASL_GSS_CREDS
ef2d9b
 	case LDAP_OPT_X_SASL_GSS_CREDS: {
ef2d9b
 		sasl_conn_t *ctx;
ef2d9b
diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
ef2d9b
index 3468ee249..dfe1ea9da 100644
ef2d9b
--- a/libraries/libldap/init.c
ef2d9b
+++ b/libraries/libldap/init.c
ef2d9b
@@ -110,6 +110,7 @@ static const struct ol_attribute {
ef2d9b
 		offsetof(struct ldapoptions, ldo_def_sasl_authzid)},
ef2d9b
 	{0, ATTR_SASL,		"SASL_SECPROPS",	NULL,	LDAP_OPT_X_SASL_SECPROPS},
ef2d9b
 	{0, ATTR_BOOL,		"SASL_NOCANON",	NULL,	LDAP_BOOL_SASL_NOCANON},
ef2d9b
+	{0, ATTR_SASL,		"SASL_CBINDING",	NULL,	LDAP_OPT_X_SASL_CBINDING},
ef2d9b
 #endif
ef2d9b
 
ef2d9b
 #ifdef HAVE_GSSAPI
ef2d9b
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
ef2d9b
index 67e8bd6da..c6c6891a9 100644
ef2d9b
--- a/libraries/libldap/ldap-int.h
ef2d9b
+++ b/libraries/libldap/ldap-int.h
ef2d9b
@@ -300,6 +300,7 @@ struct ldapoptions {
ef2d9b
 
ef2d9b
 	/* SASL Security Properties */
ef2d9b
 	struct sasl_security_properties	ldo_sasl_secprops;
ef2d9b
+	int ldo_sasl_cbinding;
ef2d9b
 #define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0}
ef2d9b
 #else
ef2d9b
 #define LDAP_LDO_SASL_NULLARG
ef2d9b
diff --git a/libraries/libldap/ldap-tls.h b/libraries/libldap/ldap-tls.h
ef2d9b
index efd51aaa2..9f01ddda1 100644
ef2d9b
--- a/libraries/libldap/ldap-tls.h
ef2d9b
+++ b/libraries/libldap/ldap-tls.h
ef2d9b
@@ -42,6 +42,7 @@ typedef int (TI_session_dn)(tls_session *sess, struct berval *dn);
ef2d9b
 typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
ef2d9b
 typedef int (TI_session_strength)(tls_session *sess);
ef2d9b
 typedef int (TI_session_unique)(tls_session *sess, struct berval *buf, int is_server);
ef2d9b
+typedef int (TI_session_endpoint)(tls_session *sess, struct berval *buf, int is_server);
ef2d9b
 typedef int (TI_session_peercert)(tls_session *s, struct berval *der);
ef2d9b
ef2d9b
 typedef void (TI_thr_init)(void);
ef2d9b
@@ -69,6 +70,7 @@ typedef struct tls_impl {
ef2d9b
 	TI_session_chkhost *ti_session_chkhost;
ef2d9b
 	TI_session_strength *ti_session_strength;
ef2d9b
 	TI_session_unique *ti_session_unique;
ef2d9b
+	TI_session_endpoint *ti_session_endpoint;
ef2d9b
 	TI_session_peercert *ti_session_peercert;
ef2d9b
 
ef2d9b
 	Sockbuf_IO *ti_sbio;
ef2d9b
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
ef2d9b
index 79a651a38..72827a1a3 100644
ef2d9b
--- a/libraries/libldap/tls2.c
ef2d9b
+++ b/libraries/libldap/tls2.c
ef2d9b
@@ -1200,6 +1200,13 @@ ldap_pvt_tls_get_unique( void *s, struct berval *buf, int is_server )
ef2d9b
 	return tls_imp->ti_session_unique( session, buf, is_server );
ef2d9b
 }
ef2d9b
 
ef2d9b
+int
ef2d9b
+ldap_pvt_tls_get_endpoint( void *s, struct berval *buf, int is_server )
ef2d9b
+{
ef2d9b
+	tls_session *session = s;
ef2d9b
+	return tls_imp->ti_session_endpoint( session, buf, is_server );
ef2d9b
+}
ef2d9b
+
ef2d9b
 int
ef2d9b
 ldap_pvt_tls_get_peercert( void *s, struct berval *der )
ef2d9b
 {
ef2d9b
diff --git a/libraries/libldap/tls_g.c b/libraries/libldap/tls_g.c
ef2d9b
index 956a9ec90..ef0f44e20 100644
ef2d9b
--- a/libraries/libldap/tls_g.c
ef2d9b
+++ b/libraries/libldap/tls_g.c
ef2d9b
@@ -729,6 +729,64 @@ tlsg_session_unique( tls_session *sess, struct berval *buf, int is_server)
ef2d9b
 	return 0;
ef2d9b
 }
ef2d9b
 
ef2d9b
+static int
ef2d9b
+tlsg_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
ef2d9b
+{
ef2d9b
+	tlsg_session *s = (tlsg_session *)sess;
ef2d9b
+	const gnutls_datum_t *cert_data;
ef2d9b
+	gnutls_x509_crt_t server_cert;
ef2d9b
+	gnutls_digest_algorithm_t md;
ef2d9b
+	int sign_algo, md_len, rc;
ef2d9b
+
ef2d9b
+	if ( is_server )
ef2d9b
+		cert_data = gnutls_certificate_get_ours( s->session );
ef2d9b
+	else
ef2d9b
+		cert_data = gnutls_certificate_get_peers( s->session, NULL );
ef2d9b
+
ef2d9b
+	if ( cert_data == NULL )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	rc = gnutls_x509_crt_init( &server_cert );
ef2d9b
+	if ( rc != GNUTLS_E_SUCCESS )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	rc = gnutls_x509_crt_import( server_cert, cert_data, GNUTLS_X509_FMT_DER );
ef2d9b
+	if ( rc != GNUTLS_E_SUCCESS ) {
ef2d9b
+		gnutls_x509_crt_deinit( server_cert );
ef2d9b
+		return 0;
ef2d9b
+	}
ef2d9b
+
ef2d9b
+	sign_algo = gnutls_x509_crt_get_signature_algorithm( server_cert );
ef2d9b
+	gnutls_x509_crt_deinit( server_cert );
ef2d9b
+	if ( sign_algo <= GNUTLS_SIGN_UNKNOWN )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	md = gnutls_sign_get_hash_algorithm( sign_algo );
ef2d9b
+	if ( md == GNUTLS_DIG_UNKNOWN )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	/* See RFC 5929 */
ef2d9b
+	switch (md) {
ef2d9b
+	case GNUTLS_DIG_NULL:
ef2d9b
+	case GNUTLS_DIG_MD2:
ef2d9b
+	case GNUTLS_DIG_MD5:
ef2d9b
+	case GNUTLS_DIG_SHA1:
ef2d9b
+		md = GNUTLS_DIG_SHA256;
ef2d9b
+	}
ef2d9b
+
ef2d9b
+	md_len = gnutls_hash_get_len( md );
ef2d9b
+	if ( md_len == 0 || md_len > buf->bv_len )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	rc = gnutls_hash_fast( md, cert_data->data, cert_data->size, buf->bv_val );
ef2d9b
+	if ( rc != GNUTLS_E_SUCCESS )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	buf->bv_len = md_len;
ef2d9b
+
ef2d9b
+	return md_len;
ef2d9b
+}
ef2d9b
+
ef2d9b
 static int
ef2d9b
 tlsg_session_peercert( tls_session *sess, struct berval *der )
ef2d9b
 {
ef2d9b
@@ -1117,6 +1175,7 @@ tls_impl ldap_int_tls_impl = {
ef2d9b
 	tlsg_session_chkhost,
ef2d9b
 	tlsg_session_strength,
ef2d9b
 	tlsg_session_unique,
ef2d9b
+	tlsg_session_endpoint,
ef2d9b
 	tlsg_session_peercert,
ef2d9b
 
ef2d9b
 	&tlsg_sbio,
ef2d9b
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
ef2d9b
index cf97d7632..aa855d77a 100644
ef2d9b
--- a/libraries/libldap/tls_o.c
ef2d9b
+++ b/libraries/libldap/tls_o.c
ef2d9b
@@ -858,6 +858,50 @@ tlso_session_unique( tls_session *sess, struct berval *buf, int is_server)
ef2d9b
 	return buf->bv_len;
ef2d9b
 }
ef2d9b
 
ef2d9b
+static int
ef2d9b
+tlso_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
ef2d9b
+{
ef2d9b
+	tlso_session *s = (tlso_session *)sess;
ef2d9b
+	const EVP_MD *md;
ef2d9b
+	unsigned int md_len;
ef2d9b
+	X509 *cert;
ef2d9b
+
ef2d9b
+	if ( buf->bv_len < EVP_MAX_MD_SIZE )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	if ( is_server )
ef2d9b
+		cert = SSL_get_certificate( s );
ef2d9b
+	else
ef2d9b
+		cert = SSL_get_peer_certificate( s );
ef2d9b
+
ef2d9b
+	if ( cert == NULL )
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
ef2d9b
+	md = EVP_get_digestbynid( X509_get_signature_nid( cert ));
ef2d9b
+#else
ef2d9b
+	md = EVP_get_digestbynid(OBJ_obj2nid( cert->sig_alg->algorithm ));
ef2d9b
+#endif
ef2d9b
+
ef2d9b
+	/* See RFC 5929 */
ef2d9b
+	if ( md == NULL ||
ef2d9b
+	     md == EVP_md_null() ||
ef2d9b
+#ifndef OPENSSL_NO_MD2
ef2d9b
+	     md == EVP_md2() ||
ef2d9b
+#endif
ef2d9b
+	     md == EVP_md4() ||
ef2d9b
+	     md == EVP_md5() ||
ef2d9b
+	     md == EVP_sha1() )
ef2d9b
+		md = EVP_sha256();
ef2d9b
+
ef2d9b
+	if ( !X509_digest( cert, md, buf->bv_val, &md_len ))
ef2d9b
+		return 0;
ef2d9b
+
ef2d9b
+	buf->bv_len = md_len;
ef2d9b
+
ef2d9b
+	return md_len;
ef2d9b
+}
ef2d9b
+
ef2d9b
 static int
ef2d9b
 tlso_session_peercert( tls_session *sess, struct berval *der )
ef2d9b
 {
ef2d9b
@@ -1474,6 +1518,7 @@ tls_impl ldap_int_tls_impl = {
ef2d9b
 	tlso_session_chkhost,
ef2d9b
 	tlso_session_strength,
ef2d9b
 	tlso_session_unique,
ef2d9b
+	tlso_session_endpoint,
ef2d9b
 	tlso_session_peercert,
ef2d9b
 
ef2d9b
 	&tlso_sbio,
ef2d9b
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
ef2d9b
index 6069ee203..4c90715be 100644
ef2d9b
--- a/servers/slapd/bconfig.c
ef2d9b
+++ b/servers/slapd/bconfig.c
ef2d9b
@@ -630,6 +630,15 @@ static ConfigTable config_back_cf_table[] = {
ef2d9b
 #endif
ef2d9b
 		"( OLcfgGlAt:89 NAME 'olcSaslAuxprops' "
ef2d9b
 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
ef2d9b
+	{ "sasl-cbinding", NULL, 2, 2, 0,
ef2d9b
+#ifdef HAVE_CYRUS_SASL
ef2d9b
+		ARG_STRING, &sasl_cbinding,
ef2d9b
+#else
ef2d9b
+		ARG_IGNORED, NULL,
ef2d9b
+#endif
ef2d9b
+		"( OLcfgGlAt:100 NAME 'olcSaslCBinding' "
ef2d9b
+			"EQUALITY caseIgnoreMatch "
ef2d9b
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
ef2d9b
 	{ "sasl-host", "host", 2, 2, 0,
ef2d9b
 #ifdef HAVE_CYRUS_SASL
ef2d9b
 		ARG_STRING|ARG_UNIQUE, &sasl_host,
ef2d9b
@@ -948,7 +957,7 @@ static ConfigOCs cf_ocs[] = {
ef2d9b
 		 "olcPluginLogFile $ olcReadOnly $ olcReferral $ "
ef2d9b
 		 "olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
ef2d9b
 		 "olcRootDSE $ "
ef2d9b
-		 "olcSaslAuxprops $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
ef2d9b
+		 "olcSaslAuxprops $ olcSaslCBinding $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
ef2d9b
 		 "olcSecurity $ olcServerID $ olcSizeLimit $ "
ef2d9b
 		 "olcSockbufMaxIncoming $ olcSockbufMaxIncomingAuth $ "
ef2d9b
 		 "olcTCPBuffer $ "
ef2d9b
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
ef2d9b
index 060d3410f..3d713d4fb 100644
ef2d9b
--- a/servers/slapd/config.c
ef2d9b
+++ b/servers/slapd/config.c
ef2d9b
@@ -73,6 +73,7 @@ char	*global_host = NULL;
ef2d9b
 struct berval global_host_bv = BER_BVNULL;
ef2d9b
 char	*global_realm = NULL;
ef2d9b
 char	*sasl_host = NULL;
ef2d9b
+char	*sasl_cbinding = NULL;
ef2d9b
 char		**default_passwd_hash = NULL;
ef2d9b
 struct berval default_search_base = BER_BVNULL;
ef2d9b
 struct berval default_search_nbase = BER_BVNULL;
ef2d9b
diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c
ef2d9b
index 5f11a0cf1..6d9bb8e85 100644
ef2d9b
--- a/servers/slapd/connection.c
ef2d9b
+++ b/servers/slapd/connection.c
ef2d9b
@@ -1440,12 +1440,9 @@ connection_read( ber_socket_t s, conn_readinfo *cri )
ef2d9b
 			    c->c_connid, (int) s, c->c_tls_ssf, c->c_ssf, 0 );
ef2d9b
 			slap_sasl_external( c, c->c_tls_ssf, &authid );
ef2d9b
 			if ( authid.bv_val ) free( authid.bv_val );
ef2d9b
-			{
ef2d9b
-				char cbinding[64];
ef2d9b
-				struct berval cbv = { sizeof(cbinding), cbinding };
ef2d9b
-				if ( ldap_pvt_tls_get_unique( ssl, &cbv, 1 ))
ef2d9b
-					slap_sasl_cbinding( c, &cbv );
ef2d9b
-			}
ef2d9b
+
ef2d9b
+			slap_sasl_cbinding( c, ssl );
ef2d9b
+
ef2d9b
 		} else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
ef2d9b
 			LBER_SB_OPT_NEEDS_WRITE, NULL )) {	/* need to retry */
ef2d9b
 			slapd_set_write( s, 1 );
ef2d9b
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
ef2d9b
index b89fa836a..0790a8004 100644
ef2d9b
--- a/servers/slapd/proto-slap.h
ef2d9b
+++ b/servers/slapd/proto-slap.h
ef2d9b
@@ -1681,8 +1681,7 @@ LDAP_SLAPD_F (int) slap_sasl_external( Connection *c,
ef2d9b
 	slap_ssf_t ssf,	/* relative strength of external security */
ef2d9b
 	struct berval *authid );	/* asserted authenication id */
ef2d9b
 
ef2d9b
-LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c,
ef2d9b
-	struct berval *cbv );
ef2d9b
+LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c, void *ssl );
ef2d9b
 
ef2d9b
 LDAP_SLAPD_F (int) slap_sasl_reset( Connection *c );
ef2d9b
 LDAP_SLAPD_F (int) slap_sasl_close( Connection *c );
ef2d9b
@@ -2072,6 +2071,7 @@ LDAP_SLAPD_V (char *)	global_host;
ef2d9b
 LDAP_SLAPD_V (struct berval)	global_host_bv;
ef2d9b
 LDAP_SLAPD_V (char *)	global_realm;
ef2d9b
 LDAP_SLAPD_V (char *)	sasl_host;
ef2d9b
+LDAP_SLAPD_V (char *)	sasl_cbinding;
ef2d9b
 LDAP_SLAPD_V (char *)	slap_sasl_auxprops;
ef2d9b
 LDAP_SLAPD_V (char **)	default_passwd_hash;
ef2d9b
 LDAP_SLAPD_V (int)		lber_debug;
ef2d9b
diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c
ef2d9b
index fc023904a..5cced358c 100644
ef2d9b
--- a/servers/slapd/sasl.c
ef2d9b
+++ b/servers/slapd/sasl.c
ef2d9b
@@ -1320,6 +1320,8 @@ int slap_sasl_destroy( void )
ef2d9b
 #endif
ef2d9b
 	free( sasl_host );
ef2d9b
 	sasl_host = NULL;
ef2d9b
+	free( sasl_cbinding );
ef2d9b
+	sasl_cbinding = NULL;
ef2d9b
 
ef2d9b
 	return 0;
ef2d9b
 }
ef2d9b
@@ -1506,17 +1508,24 @@ int slap_sasl_external(
ef2d9b
 	return LDAP_SUCCESS;
ef2d9b
 }
ef2d9b
 
ef2d9b
-int slap_sasl_cbinding( Connection *conn, struct berval *cbv )
ef2d9b
+int slap_sasl_cbinding( Connection *conn, void *ssl )
ef2d9b
 {
ef2d9b
 #ifdef SASL_CHANNEL_BINDING
ef2d9b
-	sasl_channel_binding_t *cb = ch_malloc( sizeof(*cb) + cbv->bv_len );;
ef2d9b
-	cb->name = "ldap";
ef2d9b
-	cb->critical = 0;
ef2d9b
-	cb->data = (char *)(cb+1);
ef2d9b
-	cb->len = cbv->bv_len;
ef2d9b
-	memcpy( cb->data, cbv->bv_val, cbv->bv_len );
ef2d9b
-	sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
ef2d9b
-	conn->c_sasl_cbind = cb;
ef2d9b
+	void *cb;
ef2d9b
+	int i;
ef2d9b
+
ef2d9b
+	if ( sasl_cbinding == NULL )
ef2d9b
+		return LDAP_SUCCESS;
ef2d9b
+
ef2d9b
+	i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
ef2d9b
+	if ( i < 0 )
ef2d9b
+		return LDAP_SUCCESS;
ef2d9b
+
ef2d9b
+	cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
ef2d9b
+	if ( cb != NULL ) {
ef2d9b
+		sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
ef2d9b
+		conn->c_sasl_cbind = cb;
ef2d9b
+	}
ef2d9b
 #endif
ef2d9b
 	return LDAP_SUCCESS;
ef2d9b
 }
ef2d9b
-- 
ef2d9b
2.26.2
ef2d9b