.BR "char **" .
Its content needs to be freed by the caller using
.BR ldap_memfree (3).
+.B LDAP_OPT_X_SASL_CBINDING
+Sets/gets the channel-binding type to use in SASL,
+one of
+.BR LDAP_OPT_X_SASL_CBINDING_NONE
+(the default),
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE
+the "tls-unique" type from RCF 5929.
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
+the "tls-server-end-point" from RCF 5929, compatible with Windows.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.TP
.SH TCP OPTIONS
The TCP options are OpenLDAP specific.
Mainly intended for use with Linux, they may not be portable.
.TP
.B SASL_NOCANON <on/true/yes/off/false/no>
Do not perform reverse DNS lookups to canonicalize SASL host names. The default is off.
+.TP
+.B SASL_CBINDING <none/tls-unique/tls-endpoint>
+The channel-binding type to use, see also LDAP_OPT_X_SASL_CBINDING. The default is none.
.SH GSSAPI OPTIONS
If OpenLDAP is built with Generic Security Services Application Programming Interface support,
there are more options you can specify.
.B olcSaslRealm: <realm>
Specify SASL realm. Default is empty.
.TP
+.B olcSaslCbinding: none | tls-unique | tls-endpoint
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
+Default is none.
+.TP
.B olcSaslSecProps: <properties>
Used to specify Cyrus SASL security properties.
The
property specifies the maximum security layer receive buffer
size allowed. 0 disables security layers. The default is 65536.
.TP
+.B sasl\-cbinding none | tls-unique | tls-endpoint
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
+.TP
.B schemadn <dn>
Specify the distinguished name for the subschema subentry that
controls the entries on this server. The default is "cn=Subschema".
#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ((3 << 8) + 2)
#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ((3 << 8) + 3)
+#define LDAP_OPT_X_SASL_CBINDING_NONE 0
+#define LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE 1
+#define LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT 2
+
/* OpenLDAP SASL options */
#define LDAP_OPT_X_SASL_MECH 0x6100
#define LDAP_OPT_X_SASL_REALM 0x6101
#define LDAP_OPT_X_SASL_NOCANON 0x610b
#define LDAP_OPT_X_SASL_USERNAME 0x610c /* read-only */
#define LDAP_OPT_X_SASL_GSS_CREDS 0x610d
+#define LDAP_OPT_X_SASL_CBINDING 0x610e
/*
* OpenLDAP per connection tcp-keepalive settings
LDAP_F (int) ldap_pvt_sasl_mutex_lock LDAP_P((void *mutex));
LDAP_F (int) ldap_pvt_sasl_mutex_unlock LDAP_P((void *mutex));
LDAP_F (void) ldap_pvt_sasl_mutex_dispose LDAP_P((void *mutex));
+
+LDAP_F (int) ldap_pvt_sasl_cbinding_parse LDAP_P(( const char *arg ));
+LDAP_F (void *) ldap_pvt_sasl_cbinding LDAP_P(( void *ssl, int type,
+ int is_server ));
#endif /* HAVE_CYRUS_SASL */
struct sockbuf; /* avoid pulling in <lber.h> */
LDAPDN_rewrite_dummy *func, unsigned flags ));
LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx ));
LDAP_F (int) ldap_pvt_tls_get_unique LDAP_P(( void *ctx, struct berval *buf, int is_server ));
+LDAP_F (int) ldap_pvt_tls_get_endpoint LDAP_P(( void *ctx, struct berval *buf, int is_server ));
LDAP_F (const char *) ldap_pvt_tls_get_version LDAP_P(( void *ctx ));
LDAP_F (const char *) ldap_pvt_tls_get_cipher LDAP_P(( void *ctx ));
return LDAP_SUCCESS;
}
+int ldap_pvt_sasl_cbinding_parse( const char *arg )
+{
+ int i = -1;
+
+ if ( strcasecmp(arg, "none") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_NONE;
+ else if ( strcasecmp(arg, "tls-unique") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE;
+ else if ( strcasecmp(arg, "tls-endpoint") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT;
+
+ return i;
+}
+
+void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server )
+{
+#if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS)
+ char unique_prefix[] = "tls-unique:";
+ char endpoint_prefix[] = "tls-server-end-point:";
+ char cbinding[ 64 ];
+ struct berval cbv = { 64, cbinding };
+ void *cb_data; /* used since cb->data is const* */
+ sasl_channel_binding_t *cb;
+ char *prefix;
+ int plen;
+
+ switch (type) {
+ case LDAP_OPT_X_SASL_CBINDING_NONE:
+ return NULL;
+ case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
+ if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server ))
+ return NULL;
+ prefix = unique_prefix;
+ plen = sizeof(unique_prefix) -1;
+ break;
+ case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
+ if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server ))
+ return NULL;
+ prefix = endpoint_prefix;
+ plen = sizeof(endpoint_prefix) -1;
+ break;
+ default:
+ return NULL;
+ }
+
+ cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len );
+ cb->len = plen + cbv.bv_len;
+ cb->data = cb_data = cb+1;
+ memcpy( cb_data, prefix, plen );
+ memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len );
+ cb->name = "ldap";
+ cb->critical = 0;
+
+ return cb;
+#else
+ return NULL;
+#endif
+}
+
int
ldap_int_sasl_bind(
LDAP *ld,
(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
LDAP_FREE( authid.bv_val );
#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
- {
- char cbinding[64];
- struct berval cbv = { sizeof(cbinding), cbinding };
- if ( ld->ld_defconn->lconn_sasl_cbind == NULL &&
- ldap_pvt_tls_get_unique( ssl, &cbv, 0 )) {
- sasl_channel_binding_t *cb = ldap_memalloc( sizeof(*cb) +
- cbv.bv_len);
- void *cb_data; /* used since cb->data is const* */
- cb->name = "ldap";
- cb->critical = 0;
- cb->len = cbv.bv_len;
- cb->data = cb_data = cb+1;
- memcpy( cb_data, cbv.bv_val, cbv.bv_len );
+ if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) {
+ void *cb;
+ cb = ldap_pvt_sasl_cbinding( ssl,
+ ld->ld_options.ldo_sasl_cbinding,
+ 0 );
+ if ( cb != NULL ) {
sasl_setprop( ld->ld_defconn->lconn_sasl_authctx,
SASL_CHANNEL_BINDING, cb );
ld->ld_defconn->lconn_sasl_cbind = cb;
int
ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
{
- int rc;
+ int rc, i;
switch( option ) {
case LDAP_OPT_X_SASL_SECPROPS:
rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
if( rc == LDAP_SUCCESS ) return 0;
+ break;
+ case LDAP_OPT_X_SASL_CBINDING:
+ i = ldap_pvt_sasl_cbinding_parse( arg );
+ if ( i >= 0 ) {
+ lo->ldo_sasl_cbinding = i;
+ return 0;
+ }
+ break;
}
return -1;
/* this option is write only */
return -1;
+ case LDAP_OPT_X_SASL_CBINDING:
+ *(int *)arg = ld->ld_options.ldo_sasl_cbinding;
+ break;
+
#ifdef SASL_GSS_CREDS
case LDAP_OPT_X_SASL_GSS_CREDS: {
sasl_conn_t *ctx;
return sc == LDAP_SUCCESS ? 0 : -1;
}
+ case LDAP_OPT_X_SASL_CBINDING:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_SASL_CBINDING_NONE:
+ case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
+ case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
+ ld->ld_options.ldo_sasl_cbinding = *(int *) arg;
+ return 0;
+ }
+ return -1;
+
#ifdef SASL_GSS_CREDS
case LDAP_OPT_X_SASL_GSS_CREDS: {
sasl_conn_t *ctx;
offsetof(struct ldapoptions, ldo_def_sasl_authzid)},
{0, ATTR_SASL, "SASL_SECPROPS", NULL, LDAP_OPT_X_SASL_SECPROPS},
{0, ATTR_BOOL, "SASL_NOCANON", NULL, LDAP_BOOL_SASL_NOCANON},
+ {0, ATTR_SASL, "SASL_CBINDING", NULL, LDAP_OPT_X_SASL_CBINDING},
#endif
#ifdef HAVE_TLS
/* SASL Security Properties */
struct sasl_security_properties ldo_sasl_secprops;
+ int ldo_sasl_cbinding;
#define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0}
#else
#define LDAP_LDO_SASL_NULLARG
typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
typedef int (TI_session_strength)(tls_session *sess);
typedef int (TI_session_unique)(tls_session *sess, struct berval *buf, int is_server);
+typedef int (TI_session_endpoint)(tls_session *sess, struct berval *buf, int is_server);
typedef const char *(TI_session_name)(tls_session *s);
typedef int (TI_session_peercert)(tls_session *s, struct berval *der);
typedef int (TI_session_pinning)(LDAP *ld, tls_session *s, char *hashalg, struct berval *hash);
TI_session_chkhost *ti_session_chkhost;
TI_session_strength *ti_session_strength;
TI_session_unique *ti_session_unique;
+ TI_session_endpoint *ti_session_endpoint;
TI_session_name *ti_session_version;
TI_session_name *ti_session_cipher;
TI_session_peercert *ti_session_peercert;
return tls_imp->ti_session_unique( session, buf, is_server );
}
+int
+ldap_pvt_tls_get_endpoint( void *s, struct berval *buf, int is_server )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_endpoint( session, buf, is_server );
+}
+
const char *
ldap_pvt_tls_get_version( void *s )
{
return 0;
}
+static int
+tlsg_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ const gnutls_datum_t *cert_data;
+ gnutls_x509_crt_t server_cert;
+ gnutls_digest_algorithm_t md;
+ int sign_algo, md_len, rc;
+
+ if ( is_server )
+ cert_data = gnutls_certificate_get_ours( s->session );
+ else
+ cert_data = gnutls_certificate_get_peers( s->session, NULL );
+
+ if ( cert_data == NULL )
+ return 0;
+
+ rc = gnutls_x509_crt_init( &server_cert );
+ if ( rc != GNUTLS_E_SUCCESS )
+ return 0;
+
+ rc = gnutls_x509_crt_import( server_cert, cert_data, GNUTLS_X509_FMT_DER );
+ if ( rc != GNUTLS_E_SUCCESS ) {
+ gnutls_x509_crt_deinit( server_cert );
+ return 0;
+ }
+
+ sign_algo = gnutls_x509_crt_get_signature_algorithm( server_cert );
+ gnutls_x509_crt_deinit( server_cert );
+ if ( sign_algo <= GNUTLS_SIGN_UNKNOWN )
+ return 0;
+
+ md = gnutls_sign_get_hash_algorithm( sign_algo );
+ if ( md == GNUTLS_DIG_UNKNOWN )
+ return 0;
+
+ /* See RFC 5929 */
+ switch (md) {
+ case GNUTLS_DIG_NULL:
+ case GNUTLS_DIG_MD2:
+ case GNUTLS_DIG_MD5:
+ case GNUTLS_DIG_SHA1:
+ md = GNUTLS_DIG_SHA256;
+ }
+
+ md_len = gnutls_hash_get_len( md );
+ if ( md_len == 0 || md_len > buf->bv_len )
+ return 0;
+
+ rc = gnutls_hash_fast( md, cert_data->data, cert_data->size, buf->bv_val );
+ if ( rc != GNUTLS_E_SUCCESS )
+ return 0;
+
+ buf->bv_len = md_len;
+
+ return md_len;
+}
+
static const char *
tlsg_session_version( tls_session *sess )
{
tlsg_session_chkhost,
tlsg_session_strength,
tlsg_session_unique,
+ tlsg_session_endpoint,
tlsg_session_version,
tlsg_session_cipher,
tlsg_session_peercert,
return buf->bv_len;
}
+static int
+tlso_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
+{
+ tlso_session *s = (tlso_session *)sess;
+ const EVP_MD *md;
+ unsigned int md_len;
+ X509 *cert;
+
+ if ( buf->bv_len < EVP_MAX_MD_SIZE )
+ return 0;
+
+ if ( is_server )
+ cert = SSL_get_certificate( s );
+ else
+ cert = SSL_get_peer_certificate( s );
+
+ if ( cert == NULL )
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ md = EVP_get_digestbynid( X509_get_signature_nid( cert ));
+#else
+ md = EVP_get_digestbynid(OBJ_obj2nid( cert->sig_alg->algorithm ));
+#endif
+
+ /* See RFC 5929 */
+ if ( md == NULL ||
+ md == EVP_md_null() ||
+#ifndef OPENSSL_NO_MD2
+ md == EVP_md2() ||
+#endif
+ md == EVP_md4() ||
+ md == EVP_md5() ||
+ md == EVP_sha1() )
+ md = EVP_sha256();
+
+ if ( !X509_digest( cert, md, buf->bv_val, &md_len ))
+ return 0;
+
+ buf->bv_len = md_len;
+
+ return md_len;
+}
+
static const char *
tlso_session_version( tls_session *sess )
{
tlso_session_chkhost,
tlso_session_strength,
tlso_session_unique,
+ tlso_session_endpoint,
tlso_session_version,
tlso_session_cipher,
tlso_session_peercert,
"( OLcfgGlAt:92 NAME 'olcSaslAuxpropsDontUseCopyIgnore' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "sasl-cbinding", NULL, 2, 2, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_STRING, &sasl_cbinding,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:100 NAME 'olcSaslCBinding' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "sasl-host", "host", 2, 2, 0,
#ifdef HAVE_CYRUS_SASL
ARG_STRING|ARG_UNIQUE, &sasl_host,
"olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
"olcRootDSE $ "
"olcSaslAuxprops $ olcSaslAuxpropsDontUseCopy $ olcSaslAuxpropsDontUseCopyIgnore $ "
- "olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
+ "olcSaslCBinding $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
"olcSecurity $ olcServerID $ olcSizeLimit $ "
"olcSockbufMaxIncoming $ olcSockbufMaxIncomingAuth $ "
"olcTCPBuffer $ "
struct berval global_host_bv = BER_BVNULL;
char *global_realm = NULL;
char *sasl_host = NULL;
+char *sasl_cbinding = NULL;
char **default_passwd_hash = NULL;
struct berval default_search_base = BER_BVNULL;
struct berval default_search_nbase = BER_BVNULL;
msgbuf, ldap_pvt_tls_get_version( ssl ), ldap_pvt_tls_get_cipher( ssl ));
slap_sasl_external( c, c->c_tls_ssf, &authid );
if ( authid.bv_val ) free( authid.bv_val );
- {
- char cbinding[64];
- struct berval cbv = { sizeof(cbinding), cbinding };
- if ( ldap_pvt_tls_get_unique( ssl, &cbv, 1 ))
- slap_sasl_cbinding( c, &cbv );
- }
+
+ slap_sasl_cbinding( c, ssl );
+
} else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
LBER_SB_OPT_NEEDS_WRITE, NULL )) { /* need to retry */
slapd_set_write( s, 1 );
slap_ssf_t ssf, /* relative strength of external security */
struct berval *authid ); /* asserted authentication id */
-LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c,
- struct berval *cbv );
+LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c, void *ssl );
LDAP_SLAPD_F (int) slap_sasl_reset( Connection *c );
LDAP_SLAPD_F (int) slap_sasl_close( Connection *c );
LDAP_SLAPD_V (struct berval) global_host_bv;
LDAP_SLAPD_V (char *) global_realm;
LDAP_SLAPD_V (char *) sasl_host;
+LDAP_SLAPD_V (char *) sasl_cbinding;
LDAP_SLAPD_V (char *) slap_sasl_auxprops;
#ifdef SLAP_AUXPROP_DONTUSECOPY
LDAP_SLAPD_V (int) slap_dontUseCopy_ignore;
#endif
free( sasl_host );
sasl_host = NULL;
+ free( sasl_cbinding );
+ sasl_cbinding = NULL;
return 0;
}
return LDAP_SUCCESS;
}
-int slap_sasl_cbinding( Connection *conn, struct berval *cbv )
+int slap_sasl_cbinding( Connection *conn, void *ssl )
{
#ifdef SASL_CHANNEL_BINDING
- sasl_channel_binding_t *cb = ch_malloc( sizeof(*cb) + cbv->bv_len );;
- cb->name = "ldap";
- cb->critical = 0;
- cb->data = (char *)(cb+1);
- cb->len = cbv->bv_len;
- memcpy( (void *)cb->data, cbv->bv_val, cbv->bv_len );
- sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
- conn->c_sasl_cbind = cb;
+ void *cb;
+ int i;
+
+ if ( sasl_cbinding == NULL )
+ return LDAP_SUCCESS;
+
+ i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
+ if ( i < 0 )
+ return LDAP_SUCCESS;
+
+ cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
+ if ( cb != NULL ) {
+ sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
+ conn->c_sasl_cbind = cb;
+ }
#endif
return LDAP_SUCCESS;
}