Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: cyrus.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: cyrus.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 #include "ldap-int.h"
     24 
     25 #ifdef HAVE_CYRUS_SASL
     26 
     27 #include <stdio.h>
     28 
     29 #include <ac/socket.h>
     30 #include <ac/stdlib.h>
     31 #include <ac/string.h>
     32 #include <ac/time.h>
     33 #include <ac/errno.h>
     34 #include <ac/ctype.h>
     35 #include <ac/unistd.h>
     36 
     37 #ifdef HAVE_LIMITS_H
     38 #include <limits.h>
     39 #endif
     40 
     41 #ifndef INT_MAX
     42 #define	INT_MAX	2147483647	/* 32 bit signed max */
     43 #endif
     44 
     45 #if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
     46 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
     47 #endif
     48 
     49 #ifdef HAVE_SASL_SASL_H
     50 #include <sasl/sasl.h>
     51 #else
     52 #include <sasl.h>
     53 #endif
     54 
     55 #if SASL_VERSION_MAJOR >= 2
     56 #define SASL_CONST const
     57 #else
     58 #define SASL_CONST
     59 #endif
     60 
     61 /*
     62 * Various Cyrus SASL related stuff.
     63 */
     64 
     65 static const sasl_callback_t client_callbacks[] = {
     66 #ifdef SASL_CB_GETREALM
     67 	{ SASL_CB_GETREALM, NULL, NULL },
     68 #endif
     69 	{ SASL_CB_USER, NULL, NULL },
     70 	{ SASL_CB_AUTHNAME, NULL, NULL },
     71 	{ SASL_CB_PASS, NULL, NULL },
     72 	{ SASL_CB_ECHOPROMPT, NULL, NULL },
     73 	{ SASL_CB_NOECHOPROMPT, NULL, NULL },
     74 	{ SASL_CB_LIST_END, NULL, NULL }
     75 };
     76 
     77 /*
     78  * ldap_int_initialize is responsible for calling this only once.
     79  */
     80 int ldap_int_sasl_init( void )
     81 {
     82 #ifdef HAVE_SASL_VERSION
     83 	/* stringify the version number, sasl.h doesn't do it for us */
     84 #define VSTR0(maj, min, pat)	#maj "." #min "." #pat
     85 #define VSTR(maj, min, pat)	VSTR0(maj, min, pat)
     86 #define SASL_VERSION_STRING	VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
     87 				SASL_VERSION_STEP)
     88 	{ int rc;
     89 	sasl_version( NULL, &rc );
     90 	if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
     91 		(rc & 0xffff) < SASL_VERSION_STEP) {
     92 		char version[sizeof("xxx.xxx.xxxxx")];
     93 		sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
     94 			rc & 0xffff );
     95 
     96 		Debug1( LDAP_DEBUG_ANY,
     97 		"ldap_int_sasl_init: SASL library version mismatch:"
     98 		" expected " SASL_VERSION_STRING ","
     99 		" got %s\n", version );
    100 		return -1;
    101 	}
    102 	}
    103 #endif
    104 
    105 /* SASL 2 takes care of its own memory completely internally */
    106 #if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC)
    107 	sasl_set_alloc(
    108 		ber_memalloc,
    109 		ber_memcalloc,
    110 		ber_memrealloc,
    111 		ber_memfree );
    112 #endif /* CSRIMALLOC */
    113 
    114 #ifdef LDAP_R_COMPILE
    115 	sasl_set_mutex(
    116 		ldap_pvt_sasl_mutex_new,
    117 		ldap_pvt_sasl_mutex_lock,
    118 		ldap_pvt_sasl_mutex_unlock,
    119 		ldap_pvt_sasl_mutex_dispose );
    120 #endif
    121 
    122 	if ( sasl_client_init( NULL ) == SASL_OK ) {
    123 		return 0;
    124 	}
    125 
    126 #if SASL_VERSION_MAJOR < 2
    127 	/* A no-op to make sure we link with Cyrus 1.5 */
    128 	sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL );
    129 #endif
    130 	return -1;
    131 }
    132 
    133 static void
    134 sb_sasl_cyrus_init(
    135 	struct sb_sasl_generic_data *p,
    136 	ber_len_t *min_send,
    137 	ber_len_t *max_send,
    138 	ber_len_t *max_recv)
    139 {
    140 	sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
    141 	ber_len_t maxbuf;
    142 
    143 	sasl_getprop( sasl_context, SASL_MAXOUTBUF,
    144 		      (SASL_CONST void **)(char *) &maxbuf );
    145 
    146 	*min_send = SASL_MIN_BUFF_SIZE;
    147 	*max_send = maxbuf;
    148 	*max_recv = SASL_MAX_BUFF_SIZE;
    149 }
    150 
    151 static ber_int_t
    152 sb_sasl_cyrus_encode(
    153 	struct sb_sasl_generic_data *p,
    154 	unsigned char *buf,
    155 	ber_len_t len,
    156 	Sockbuf_Buf *dst)
    157 {
    158 	sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
    159 	ber_int_t ret;
    160 	unsigned tmpsize = dst->buf_size;
    161 
    162 	ret = sasl_encode( sasl_context, (char *)buf, len,
    163 			   (SASL_CONST char **)&dst->buf_base,
    164 			   &tmpsize );
    165 
    166 	dst->buf_size = tmpsize;
    167 	dst->buf_end = dst->buf_size;
    168 
    169 	if ( ret != SASL_OK ) {
    170 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    171 				"sb_sasl_cyrus_encode: failed to encode packet: %s\n",
    172 				sasl_errstring( ret, NULL, NULL ) );
    173 		return -1;
    174 	}
    175 
    176 	return 0;
    177 }
    178 
    179 static ber_int_t
    180 sb_sasl_cyrus_decode(
    181 	struct sb_sasl_generic_data *p,
    182 	const Sockbuf_Buf *src,
    183 	Sockbuf_Buf *dst)
    184 {
    185 	sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
    186 	ber_int_t ret;
    187 	unsigned tmpsize = dst->buf_size;
    188 
    189 	ret = sasl_decode( sasl_context,
    190 			   src->buf_base, src->buf_end,
    191 			   (SASL_CONST char **)&dst->buf_base,
    192 			   (unsigned *)&tmpsize );
    193 
    194 
    195 	dst->buf_size = tmpsize;
    196 	dst->buf_end = dst->buf_size;
    197 
    198 	if ( ret != SASL_OK ) {
    199 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    200 				"sb_sasl_cyrus_decode: failed to decode packet: %s\n",
    201 				sasl_errstring( ret, NULL, NULL ) );
    202 		return -1;
    203 	}
    204 
    205 	return 0;
    206 }
    207 
    208 static void
    209 sb_sasl_cyrus_reset_buf(
    210 	struct sb_sasl_generic_data *p,
    211 	Sockbuf_Buf *buf)
    212 {
    213 #if SASL_VERSION_MAJOR >= 2
    214 	ber_pvt_sb_buf_init( buf );
    215 #else
    216 	ber_pvt_sb_buf_destroy( buf );
    217 #endif
    218 }
    219 
    220 static void
    221 sb_sasl_cyrus_fini(
    222 	struct sb_sasl_generic_data *p)
    223 {
    224 #if SASL_VERSION_MAJOR >= 2
    225 	/*
    226 	 * SASLv2 encode/decode buffers are managed by
    227 	 * libsasl2. Ensure they are not freed by liblber.
    228 	 */
    229 	p->buf_in.buf_base = NULL;
    230 	p->buf_out.buf_base = NULL;
    231 #endif
    232 }
    233 
    234 static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = {
    235 	sb_sasl_cyrus_init,
    236 	sb_sasl_cyrus_encode,
    237 	sb_sasl_cyrus_decode,
    238 	sb_sasl_cyrus_reset_buf,
    239 	sb_sasl_cyrus_fini
    240  };
    241 
    242 int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
    243 {
    244 	struct sb_sasl_generic_install install_arg;
    245 
    246 	install_arg.ops		= &sb_sasl_cyrus_ops;
    247 	install_arg.ops_private = ctx_arg;
    248 
    249 	return ldap_pvt_sasl_generic_install( sb, &install_arg );
    250 }
    251 
    252 void ldap_pvt_sasl_remove( Sockbuf *sb )
    253 {
    254 	ldap_pvt_sasl_generic_remove( sb );
    255 }
    256 
    257 static int
    258 sasl_err2ldap( int saslerr )
    259 {
    260 	int rc;
    261 
    262 	/* map SASL errors to LDAP API errors returned by:
    263 	 *	sasl_client_new()
    264 	 *		SASL_OK, SASL_NOMECH, SASL_NOMEM
    265 	 *	sasl_client_start()
    266 	 *		SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT
    267 	 *	sasl_client_step()
    268 	 *		SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV
    269 	 */
    270 
    271 	switch (saslerr) {
    272 		case SASL_CONTINUE:
    273 			rc = LDAP_MORE_RESULTS_TO_RETURN;
    274 			break;
    275 		case SASL_INTERACT:
    276 			rc = LDAP_LOCAL_ERROR;
    277 			break;
    278 		case SASL_OK:
    279 			rc = LDAP_SUCCESS;
    280 			break;
    281 		case SASL_NOMEM:
    282 			rc = LDAP_NO_MEMORY;
    283 			break;
    284 		case SASL_NOMECH:
    285 			rc = LDAP_AUTH_UNKNOWN;
    286 			break;
    287 		case SASL_BADPROT:
    288 			rc = LDAP_DECODING_ERROR;
    289 			break;
    290 		case SASL_BADSERV:
    291 			rc = LDAP_AUTH_UNKNOWN;
    292 			break;
    293 
    294 		/* other codes */
    295 		case SASL_BADAUTH:
    296 			rc = LDAP_AUTH_UNKNOWN;
    297 			break;
    298 		case SASL_NOAUTHZ:
    299 			rc = LDAP_PARAM_ERROR;
    300 			break;
    301 		case SASL_FAIL:
    302 			rc = LDAP_LOCAL_ERROR;
    303 			break;
    304 		case SASL_TOOWEAK:
    305 		case SASL_ENCRYPT:
    306 			rc = LDAP_AUTH_UNKNOWN;
    307 			break;
    308 		default:
    309 			rc = LDAP_LOCAL_ERROR;
    310 			break;
    311 	}
    312 
    313 	assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
    314 	return rc;
    315 }
    316 
    317 int
    318 ldap_int_sasl_open(
    319 	LDAP *ld,
    320 	LDAPConn *lc,
    321 	const char * host )
    322 {
    323 	int rc;
    324 	sasl_conn_t *ctx;
    325 
    326 	assert( lc->lconn_sasl_authctx == NULL );
    327 
    328 	if ( host == NULL ) {
    329 		ld->ld_errno = LDAP_LOCAL_ERROR;
    330 		return ld->ld_errno;
    331 	}
    332 
    333 #if SASL_VERSION_MAJOR >= 2
    334 	rc = sasl_client_new( "ldap", host, NULL, NULL,
    335 		client_callbacks, 0, &ctx );
    336 #else
    337 	rc = sasl_client_new( "ldap", host, client_callbacks,
    338 		SASL_SECURITY_LAYER, &ctx );
    339 #endif
    340 
    341 	if ( rc != SASL_OK ) {
    342 		ld->ld_errno = sasl_err2ldap( rc );
    343 		return ld->ld_errno;
    344 	}
    345 
    346 	Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n",
    347 		host );
    348 
    349 	lc->lconn_sasl_authctx = ctx;
    350 
    351 	return LDAP_SUCCESS;
    352 }
    353 
    354 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
    355 {
    356 	sasl_conn_t *ctx = lc->lconn_sasl_authctx;
    357 
    358 	if( ctx != NULL ) {
    359 		sasl_dispose( &ctx );
    360 		if ( lc->lconn_sasl_sockctx &&
    361 			lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) {
    362 			ctx = lc->lconn_sasl_sockctx;
    363 			sasl_dispose( &ctx );
    364 		}
    365 		lc->lconn_sasl_sockctx = NULL;
    366 		lc->lconn_sasl_authctx = NULL;
    367 	}
    368 	if( lc->lconn_sasl_cbind ) {
    369 		ldap_memfree( lc->lconn_sasl_cbind );
    370 		lc->lconn_sasl_cbind = NULL;
    371 	}
    372 
    373 	return LDAP_SUCCESS;
    374 }
    375 
    376 int ldap_pvt_sasl_cbinding_parse( const char *arg )
    377 {
    378 	int i = -1;
    379 
    380 	if ( strcasecmp(arg, "none") == 0 )
    381 		i = LDAP_OPT_X_SASL_CBINDING_NONE;
    382 	else if ( strcasecmp(arg, "tls-unique") == 0 )
    383 		i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE;
    384 	else if ( strcasecmp(arg, "tls-endpoint") == 0 )
    385 		i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT;
    386 
    387 	return i;
    388 }
    389 
    390 void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server )
    391 {
    392 #if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS)
    393 	char unique_prefix[] = "tls-unique:";
    394 	char endpoint_prefix[] = "tls-server-end-point:";
    395 	char cbinding[ 64 ];
    396 	struct berval cbv = { 64, cbinding };
    397 	unsigned char *cb_data; /* used since cb->data is const* */
    398 	sasl_channel_binding_t *cb;
    399 	char *prefix;
    400 	int plen;
    401 
    402 	switch (type) {
    403 	case LDAP_OPT_X_SASL_CBINDING_NONE:
    404 		return NULL;
    405 	case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
    406 		if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server ))
    407 			return NULL;
    408 		prefix = unique_prefix;
    409 		plen = sizeof(unique_prefix) -1;
    410 		break;
    411 	case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
    412 		if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server ))
    413 			return NULL;
    414 		prefix = endpoint_prefix;
    415 		plen = sizeof(endpoint_prefix) -1;
    416 		break;
    417 	default:
    418 		return NULL;
    419 	}
    420 
    421 	cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len );
    422 	cb->len = plen + cbv.bv_len;
    423 	cb->data = cb_data = (unsigned char *)(cb+1);
    424 	memcpy( cb_data, prefix, plen );
    425 	memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len );
    426 	cb->name = "ldap";
    427 	cb->critical = 0;
    428 
    429 	return cb;
    430 #else
    431 	return NULL;
    432 #endif
    433 }
    434 
    435 int
    436 ldap_int_sasl_bind(
    437 	LDAP			*ld,
    438 	const char		*dn,
    439 	const char		*mechs,
    440 	LDAPControl		**sctrls,
    441 	LDAPControl		**cctrls,
    442 	unsigned		flags,
    443 	LDAP_SASL_INTERACT_PROC *interact,
    444 	void			*defaults,
    445 	LDAPMessage		*result,
    446 	const char		**rmech,
    447 	int				*msgid )
    448 {
    449 	const char		*mech;
    450 	sasl_ssf_t		*ssf;
    451 	sasl_conn_t		*ctx;
    452 	sasl_interact_t *prompts = NULL;
    453 	struct berval	ccred = BER_BVNULL;
    454 	int saslrc, rc;
    455 	unsigned credlen;
    456 #if !defined(_WIN32)
    457 	char my_hostname[HOST_NAME_MAX + 1];
    458 #endif
    459 	int free_saslhost = 0;
    460 
    461 	Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
    462 		mechs ? mechs : "<null>" );
    463 
    464 	/* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
    465 	if (ld->ld_version < LDAP_VERSION3) {
    466 		ld->ld_errno = LDAP_NOT_SUPPORTED;
    467 		return ld->ld_errno;
    468 	}
    469 
    470 	/* Starting a Bind */
    471 	if ( !result ) {
    472 		const char *pmech = NULL;
    473 		sasl_conn_t	*oldctx;
    474 		ber_socket_t		sd;
    475 		void	*ssl;
    476 
    477 		rc = 0;
    478 		LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    479 		ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
    480 
    481 		if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) {
    482 			/* not connected yet */
    483 
    484 			rc = ldap_open_defconn( ld );
    485 
    486 			if ( rc == 0 ) {
    487 				ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
    488 					LBER_SB_OPT_GET_FD, &sd );
    489 
    490 				if( sd == AC_SOCKET_INVALID ) {
    491 					ld->ld_errno = LDAP_LOCAL_ERROR;
    492 					rc = ld->ld_errno;
    493 				}
    494 			}
    495 		}
    496 		if ( rc == 0 && ld->ld_defconn &&
    497 			ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) {
    498 			rc = ldap_int_check_async_open( ld, sd );
    499 		}
    500 		LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    501 		if( rc != 0 ) return ld->ld_errno;
    502 
    503 		oldctx = ld->ld_defconn->lconn_sasl_authctx;
    504 
    505 		/* If we already have an authentication context, clear it out */
    506 		if( oldctx ) {
    507 			if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
    508 				sasl_dispose( &oldctx );
    509 			}
    510 			ld->ld_defconn->lconn_sasl_authctx = NULL;
    511 		}
    512 
    513 		{
    514 			char *saslhost;
    515 			int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
    516 				LDAP_BOOL_SASL_NOCANON );
    517 
    518 			/* If we don't need to canonicalize just use the host
    519 			 * from the LDAP URI.
    520 			 * Always use the result of gethostname() for LDAPI.
    521 			 * Skip for Windows which doesn't support LDAPI.
    522 			 */
    523 #if !defined(_WIN32)
    524 			if (ld->ld_defconn->lconn_server->lud_scheme != NULL &&
    525 			    strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) {
    526 				rc = gethostname(my_hostname, HOST_NAME_MAX + 1);
    527 				if (rc == 0) {
    528 					saslhost = my_hostname;
    529 				} else {
    530 					saslhost = "localhost";
    531 				}
    532 			} else
    533 #endif
    534 			if ( nocanon )
    535 				saslhost = ld->ld_defconn->lconn_server->lud_host;
    536 			else {
    537 				saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
    538 				"localhost" );
    539 				free_saslhost = 1;
    540 			}
    541 			rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
    542 			if ( free_saslhost )
    543 				LDAP_FREE( saslhost );
    544 		}
    545 
    546 		if ( rc != LDAP_SUCCESS ) return rc;
    547 
    548 		ctx = ld->ld_defconn->lconn_sasl_authctx;
    549 
    550 #ifdef HAVE_TLS
    551 		/* Check for TLS */
    552 		ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
    553 		if ( ssl ) {
    554 			struct berval authid = BER_BVNULL;
    555 			ber_len_t fac;
    556 
    557 			fac = ldap_pvt_tls_get_strength( ssl );
    558 			/* failure is OK, we just can't use SASL EXTERNAL */
    559 			(void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
    560 
    561 			(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
    562 			LDAP_FREE( authid.bv_val );
    563 #ifdef SASL_CHANNEL_BINDING	/* 2.1.25+ */
    564 			if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) {
    565 				void *cb;
    566 				cb = ldap_pvt_sasl_cbinding( ssl,
    567 							     ld->ld_options.ldo_sasl_cbinding,
    568 							     0 );
    569 				if ( cb != NULL ) {
    570 					sasl_setprop( ld->ld_defconn->lconn_sasl_authctx,
    571 						SASL_CHANNEL_BINDING, cb );
    572 					ld->ld_defconn->lconn_sasl_cbind = cb;
    573 				}
    574 			}
    575 #endif
    576 		}
    577 #endif
    578 
    579 #if !defined(_WIN32)
    580 		/* Check for local */
    581 		if ( ldap_pvt_url_scheme2proto(
    582 			ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
    583 		{
    584 			char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
    585 				"cn=peercred,cn=external,cn=auth")];
    586 			sprintf( authid, "gidNumber=%u+uidNumber=%u,"
    587 				"cn=peercred,cn=external,cn=auth",
    588 				getegid(), geteuid() );
    589 			(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
    590 				LDAP_PVT_SASL_LOCAL_SSF );
    591 		}
    592 #endif
    593 
    594 		/* (re)set security properties */
    595 		sasl_setprop( ctx, SASL_SEC_PROPS,
    596 			&ld->ld_options.ldo_sasl_secprops );
    597 
    598 		mech = NULL;
    599 
    600 		do {
    601 			saslrc = sasl_client_start( ctx,
    602 				mechs,
    603 #if SASL_VERSION_MAJOR < 2
    604 				NULL,
    605 #endif
    606 				&prompts,
    607 				(SASL_CONST char **)&ccred.bv_val,
    608 				&credlen,
    609 				&mech );
    610 
    611 			if( pmech == NULL && mech != NULL ) {
    612 				pmech = mech;
    613 				*rmech = mech;
    614 
    615 				if( flags != LDAP_SASL_QUIET ) {
    616 					fprintf(stderr,
    617 						"SASL/%s authentication started\n",
    618 						pmech );
    619 				}
    620 			}
    621 
    622 			if( saslrc == SASL_INTERACT ) {
    623 				int res;
    624 				if( !interact ) break;
    625 				res = (interact)( ld, flags, defaults, prompts );
    626 
    627 				if( res != LDAP_SUCCESS ) break;
    628 			}
    629 		} while ( saslrc == SASL_INTERACT );
    630 		rc = LDAP_SASL_BIND_IN_PROGRESS;
    631 
    632 	} else {
    633 		/* continuing an in-progress Bind */
    634 		struct berval *scred = NULL;
    635 
    636 		ctx = ld->ld_defconn->lconn_sasl_authctx;
    637 
    638 		rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
    639 		if ( rc != LDAP_SUCCESS ) {
    640 			if ( scred )
    641 				ber_bvfree( scred );
    642 			goto done;
    643 		}
    644 
    645 		rc = ldap_result2error( ld, result, 0 );
    646 		if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
    647 			if( scred ) {
    648 				/* and server provided us with data? */
    649 				Debug2( LDAP_DEBUG_TRACE,
    650 					"ldap_int_sasl_bind: rc=%d len=%ld\n",
    651 					rc, scred ? (long) scred->bv_len : -1L );
    652 				ber_bvfree( scred );
    653 				scred = NULL;
    654 			}
    655 			goto done;
    656 		}
    657 
    658 		mech = *rmech;
    659 		if ( rc == LDAP_SUCCESS && mech == NULL ) {
    660 			if ( scred )
    661 				ber_bvfree( scred );
    662 			goto success;
    663 		}
    664 
    665 		do {
    666 			if( ! scred ) {
    667 				/* no data! */
    668 				Debug0( LDAP_DEBUG_TRACE,
    669 					"ldap_int_sasl_bind: no data in step!\n" );
    670 			}
    671 
    672 			saslrc = sasl_client_step( ctx,
    673 				(scred == NULL) ? NULL : scred->bv_val,
    674 				(scred == NULL) ? 0 : scred->bv_len,
    675 				&prompts,
    676 				(SASL_CONST char **)&ccred.bv_val,
    677 				&credlen );
    678 
    679 			Debug1( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
    680 				saslrc );
    681 
    682 			if( saslrc == SASL_INTERACT ) {
    683 				int res;
    684 				if( !interact ) break;
    685 				res = (interact)( ld, flags, defaults, prompts );
    686 				if( res != LDAP_SUCCESS ) break;
    687 			}
    688 		} while ( saslrc == SASL_INTERACT );
    689 
    690 		ber_bvfree( scred );
    691 	}
    692 
    693 	if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
    694 		rc = ld->ld_errno = sasl_err2ldap( saslrc );
    695 #if SASL_VERSION_MAJOR >= 2
    696 		if ( ld->ld_error ) {
    697 			LDAP_FREE( ld->ld_error );
    698 		}
    699 		ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
    700 #endif
    701 		goto done;
    702 	}
    703 
    704 	if ( saslrc == SASL_OK )
    705 		*rmech = NULL;
    706 
    707 	ccred.bv_len = credlen;
    708 
    709 	if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
    710 		rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid );
    711 
    712 		if ( ccred.bv_val != NULL ) {
    713 #if SASL_VERSION_MAJOR < 2
    714 			LDAP_FREE( ccred.bv_val );
    715 #endif
    716 			ccred.bv_val = NULL;
    717 		}
    718 		if ( rc == LDAP_SUCCESS )
    719 			rc = LDAP_SASL_BIND_IN_PROGRESS;
    720 		goto done;
    721 	}
    722 
    723 success:
    724 	/* Conversation was completed successfully by now */
    725 	if( flags != LDAP_SASL_QUIET ) {
    726 		char *data;
    727 		saslrc = sasl_getprop( ctx, SASL_USERNAME,
    728 			(SASL_CONST void **)(char *) &data );
    729 		if( saslrc == SASL_OK && data && *data ) {
    730 			fprintf( stderr, "SASL username: %s\n", data );
    731 		}
    732 
    733 #if SASL_VERSION_MAJOR < 2
    734 		saslrc = sasl_getprop( ctx, SASL_REALM,
    735 			(SASL_CONST void **) &data );
    736 		if( saslrc == SASL_OK && data && *data ) {
    737 			fprintf( stderr, "SASL realm: %s\n", data );
    738 		}
    739 #endif
    740 	}
    741 
    742 	ssf = NULL;
    743 	saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
    744 	if( saslrc == SASL_OK ) {
    745 		if( flags != LDAP_SASL_QUIET ) {
    746 			fprintf( stderr, "SASL SSF: %lu\n",
    747 				(unsigned long) *ssf );
    748 		}
    749 
    750 		if( ssf && *ssf ) {
    751 			if ( ld->ld_defconn->lconn_sasl_sockctx ) {
    752 				sasl_conn_t	*oldctx = ld->ld_defconn->lconn_sasl_sockctx;
    753 				sasl_dispose( &oldctx );
    754 				ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
    755 			}
    756 			ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
    757 			ld->ld_defconn->lconn_sasl_sockctx = ctx;
    758 
    759 			if( flags != LDAP_SASL_QUIET ) {
    760 				fprintf( stderr, "SASL data security layer installed.\n" );
    761 			}
    762 		}
    763 	}
    764 	ld->ld_defconn->lconn_sasl_authctx = ctx;
    765 
    766 done:
    767 	return rc;
    768 }
    769 
    770 int
    771 ldap_int_sasl_external(
    772 	LDAP *ld,
    773 	LDAPConn *conn,
    774 	const char * authid,
    775 	ber_len_t ssf )
    776 {
    777 	int sc;
    778 	sasl_conn_t *ctx;
    779 #if SASL_VERSION_MAJOR < 2
    780 	sasl_external_properties_t extprops;
    781 #else
    782 	sasl_ssf_t sasl_ssf = ssf;
    783 #endif
    784 
    785 	ctx = conn->lconn_sasl_authctx;
    786 
    787 	if ( ctx == NULL ) {
    788 		return LDAP_LOCAL_ERROR;
    789 	}
    790 
    791 #if SASL_VERSION_MAJOR >= 2
    792 	sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
    793 	if ( sc == SASL_OK )
    794 		sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
    795 #else
    796 	memset( &extprops, '\0', sizeof(extprops) );
    797 	extprops.ssf = ssf;
    798 	extprops.auth_id = (char *) authid;
    799 
    800 	sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
    801 		(void *) &extprops );
    802 #endif
    803 
    804 	if ( sc != SASL_OK ) {
    805 		return LDAP_LOCAL_ERROR;
    806 	}
    807 
    808 	return LDAP_SUCCESS;
    809 }
    810 
    811 
    812 #define GOT_MINSSF	1
    813 #define	GOT_MAXSSF	2
    814 #define	GOT_MAXBUF	4
    815 
    816 static struct {
    817 	struct berval key;
    818 	int sflag;
    819 	int ival;
    820 	int idef;
    821 } sprops[] = {
    822 	{ BER_BVC("none"), 0, 0, 0 },
    823 	{ BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
    824 	{ BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
    825 	{ BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
    826 	{ BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
    827 	{ BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
    828 	{ BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
    829 	{ BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
    830 	{ BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
    831 	{ BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
    832 	{ BER_BVNULL, 0, 0, 0 }
    833 };
    834 
    835 void ldap_pvt_sasl_secprops_unparse(
    836 	sasl_security_properties_t *secprops,
    837 	struct berval *out )
    838 {
    839 	int i, l = 0;
    840 	int comma;
    841 	char *ptr;
    842 
    843 	if ( secprops == NULL || out == NULL ) {
    844 		return;
    845 	}
    846 
    847 	comma = 0;
    848 	for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
    849 		if ( sprops[i].ival ) {
    850 			int v = 0;
    851 
    852 			switch( sprops[i].ival ) {
    853 			case GOT_MINSSF: v = secprops->min_ssf; break;
    854 			case GOT_MAXSSF: v = secprops->max_ssf; break;
    855 			case GOT_MAXBUF: v = secprops->maxbufsize; break;
    856 			}
    857 			/* It is the default, ignore it */
    858 			if ( v == sprops[i].idef ) continue;
    859 
    860 			l += sprops[i].key.bv_len + 24;
    861 		} else if ( sprops[i].sflag ) {
    862 			if ( sprops[i].sflag & secprops->security_flags ) {
    863 				l += sprops[i].key.bv_len;
    864 			}
    865 		} else if ( secprops->security_flags == 0 ) {
    866 			l += sprops[i].key.bv_len;
    867 		}
    868 		if ( comma ) l++;
    869 		comma = 1;
    870 	}
    871 	l++;
    872 
    873 	out->bv_val = LDAP_MALLOC( l );
    874 	if ( out->bv_val == NULL ) {
    875 		out->bv_len = 0;
    876 		return;
    877 	}
    878 
    879 	ptr = out->bv_val;
    880 	comma = 0;
    881 	for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
    882 		if ( sprops[i].ival ) {
    883 			int v = 0;
    884 
    885 			switch( sprops[i].ival ) {
    886 			case GOT_MINSSF: v = secprops->min_ssf; break;
    887 			case GOT_MAXSSF: v = secprops->max_ssf; break;
    888 			case GOT_MAXBUF: v = secprops->maxbufsize; break;
    889 			}
    890 			/* It is the default, ignore it */
    891 			if ( v == sprops[i].idef ) continue;
    892 
    893 			if ( comma ) *ptr++ = ',';
    894 			ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
    895 			comma = 1;
    896 		} else if ( sprops[i].sflag ) {
    897 			if ( sprops[i].sflag & secprops->security_flags ) {
    898 				if ( comma ) *ptr++ = ',';
    899 				ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
    900 				comma = 1;
    901 			}
    902 		} else if ( secprops->security_flags == 0 ) {
    903 			if ( comma ) *ptr++ = ',';
    904 			ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
    905 			comma = 1;
    906 		}
    907 	}
    908 	out->bv_len = ptr - out->bv_val;
    909 }
    910 
    911 int ldap_pvt_sasl_secprops(
    912 	const char *in,
    913 	sasl_security_properties_t *secprops )
    914 {
    915 	unsigned i, j, l;
    916 	char **props;
    917 	unsigned sflags = 0;
    918 	int got_sflags = 0;
    919 	sasl_ssf_t max_ssf = 0;
    920 	int got_max_ssf = 0;
    921 	sasl_ssf_t min_ssf = 0;
    922 	int got_min_ssf = 0;
    923 	unsigned maxbufsize = 0;
    924 	int got_maxbufsize = 0;
    925 
    926 	if( secprops == NULL ) {
    927 		return LDAP_PARAM_ERROR;
    928 	}
    929 	props = ldap_str2charray( in, "," );
    930 	if( props == NULL ) {
    931 		return LDAP_PARAM_ERROR;
    932 	}
    933 
    934 	for( i=0; props[i]; i++ ) {
    935 		l = strlen( props[i] );
    936 		for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
    937 			if ( l < sprops[j].key.bv_len ) continue;
    938 			if ( strncasecmp( props[i], sprops[j].key.bv_val,
    939 				sprops[j].key.bv_len )) continue;
    940 			if ( sprops[j].ival ) {
    941 				unsigned v;
    942 				char *next = NULL;
    943 				if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
    944 					continue;
    945 				v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
    946 				if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
    947 				switch( sprops[j].ival ) {
    948 				case GOT_MINSSF:
    949 					min_ssf = v; got_min_ssf++; break;
    950 				case GOT_MAXSSF:
    951 					max_ssf = v; got_max_ssf++; break;
    952 				case GOT_MAXBUF:
    953 					maxbufsize = v; got_maxbufsize++; break;
    954 				}
    955 			} else {
    956 				if ( props[i][sprops[j].key.bv_len] ) continue;
    957 				if ( sprops[j].sflag )
    958 					sflags |= sprops[j].sflag;
    959 				else
    960 					sflags = 0;
    961 				got_sflags++;
    962 			}
    963 			break;
    964 		}
    965 		if ( BER_BVISNULL( &sprops[j].key )) {
    966 			ldap_charray_free( props );
    967 			return LDAP_NOT_SUPPORTED;
    968 		}
    969 	}
    970 
    971 	if(got_sflags) {
    972 		secprops->security_flags = sflags;
    973 	}
    974 	if(got_min_ssf) {
    975 		secprops->min_ssf = min_ssf;
    976 	}
    977 	if(got_max_ssf) {
    978 		secprops->max_ssf = max_ssf;
    979 	}
    980 	if(got_maxbufsize) {
    981 		secprops->maxbufsize = maxbufsize;
    982 	}
    983 
    984 	ldap_charray_free( props );
    985 	return LDAP_SUCCESS;
    986 }
    987 
    988 int
    989 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
    990 {
    991 	int rc, i;
    992 
    993 	switch( option ) {
    994 	case LDAP_OPT_X_SASL_SECPROPS:
    995 		rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
    996 		if( rc == LDAP_SUCCESS ) return 0;
    997 		break;
    998 	case LDAP_OPT_X_SASL_CBINDING:
    999 		i = ldap_pvt_sasl_cbinding_parse( arg );
   1000 		if ( i >= 0 ) {
   1001 			lo->ldo_sasl_cbinding = i;
   1002 			return 0;
   1003 		}
   1004 		break;
   1005 	}
   1006 
   1007 	return -1;
   1008 }
   1009 
   1010 int
   1011 ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
   1012 {
   1013 	if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
   1014 		*(char ***)arg = (char **)sasl_global_listmech();
   1015 		return 0;
   1016 	}
   1017 
   1018 	if ( ld == NULL )
   1019 		return -1;
   1020 
   1021 	switch ( option ) {
   1022 		case LDAP_OPT_X_SASL_MECH: {
   1023 			*(char **)arg = ld->ld_options.ldo_def_sasl_mech
   1024 				? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
   1025 		} break;
   1026 		case LDAP_OPT_X_SASL_REALM: {
   1027 			*(char **)arg = ld->ld_options.ldo_def_sasl_realm
   1028 				? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
   1029 		} break;
   1030 		case LDAP_OPT_X_SASL_AUTHCID: {
   1031 			*(char **)arg = ld->ld_options.ldo_def_sasl_authcid
   1032 				? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
   1033 		} break;
   1034 		case LDAP_OPT_X_SASL_AUTHZID: {
   1035 			*(char **)arg = ld->ld_options.ldo_def_sasl_authzid
   1036 				? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
   1037 		} break;
   1038 
   1039 		case LDAP_OPT_X_SASL_SSF: {
   1040 			int sc;
   1041 			sasl_ssf_t	*ssf;
   1042 			sasl_conn_t *ctx;
   1043 
   1044 			if( ld->ld_defconn == NULL ) {
   1045 				return -1;
   1046 			}
   1047 
   1048 			ctx = ld->ld_defconn->lconn_sasl_sockctx;
   1049 
   1050 			if ( ctx == NULL ) {
   1051 				return -1;
   1052 			}
   1053 
   1054 			sc = sasl_getprop( ctx, SASL_SSF,
   1055 				(SASL_CONST void **)(char *) &ssf );
   1056 
   1057 			if ( sc != SASL_OK ) {
   1058 				return -1;
   1059 			}
   1060 
   1061 			*(ber_len_t *)arg = *ssf;
   1062 		} break;
   1063 
   1064 		case LDAP_OPT_X_SASL_SSF_EXTERNAL:
   1065 			/* this option is write only */
   1066 			return -1;
   1067 
   1068 		case LDAP_OPT_X_SASL_SSF_MIN:
   1069 			*(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
   1070 			break;
   1071 		case LDAP_OPT_X_SASL_SSF_MAX:
   1072 			*(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
   1073 			break;
   1074 		case LDAP_OPT_X_SASL_MAXBUFSIZE:
   1075 			*(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
   1076 			break;
   1077 		case LDAP_OPT_X_SASL_NOCANON:
   1078 			*(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
   1079 			break;
   1080 
   1081 		case LDAP_OPT_X_SASL_USERNAME: {
   1082 			int sc;
   1083 			char *username;
   1084 			sasl_conn_t *ctx;
   1085 
   1086 			if( ld->ld_defconn == NULL ) {
   1087 				return -1;
   1088 			}
   1089 
   1090 			ctx = ld->ld_defconn->lconn_sasl_authctx;
   1091 
   1092 			if ( ctx == NULL ) {
   1093 				return -1;
   1094 			}
   1095 
   1096 			sc = sasl_getprop( ctx, SASL_USERNAME,
   1097 				(SASL_CONST void **)(char **) &username );
   1098 
   1099 			if ( sc != SASL_OK ) {
   1100 				return -1;
   1101 			}
   1102 
   1103 			*(char **)arg = username ? LDAP_STRDUP( username ) : NULL;
   1104 		} break;
   1105 
   1106 		case LDAP_OPT_X_SASL_SECPROPS:
   1107 			/* this option is write only */
   1108 			return -1;
   1109 
   1110 		case LDAP_OPT_X_SASL_CBINDING:
   1111 			*(int *)arg = ld->ld_options.ldo_sasl_cbinding;
   1112 			break;
   1113 
   1114 #ifdef SASL_GSS_CREDS
   1115 		case LDAP_OPT_X_SASL_GSS_CREDS: {
   1116 			sasl_conn_t *ctx;
   1117 			int sc;
   1118 
   1119 			if ( ld->ld_defconn == NULL )
   1120 				return -1;
   1121 
   1122 			ctx = ld->ld_defconn->lconn_sasl_authctx;
   1123 			if ( ctx == NULL )
   1124 				return -1;
   1125 
   1126 			sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg );
   1127 			if ( sc != SASL_OK )
   1128 				return -1;
   1129 			}
   1130 			break;
   1131 #endif
   1132 
   1133 		default:
   1134 			return -1;
   1135 	}
   1136 	return 0;
   1137 }
   1138 
   1139 int
   1140 ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
   1141 {
   1142 	if ( ld == NULL )
   1143 		return -1;
   1144 
   1145 	if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON )
   1146 		return -1;
   1147 
   1148 	switch ( option ) {
   1149 	case LDAP_OPT_X_SASL_SSF:
   1150 	case LDAP_OPT_X_SASL_USERNAME:
   1151 		/* This option is read-only */
   1152 		return -1;
   1153 
   1154 	case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
   1155 		int sc;
   1156 #if SASL_VERSION_MAJOR < 2
   1157 		sasl_external_properties_t extprops;
   1158 #else
   1159 		sasl_ssf_t sasl_ssf;
   1160 #endif
   1161 		sasl_conn_t *ctx;
   1162 
   1163 		if( ld->ld_defconn == NULL ) {
   1164 			return -1;
   1165 		}
   1166 
   1167 		ctx = ld->ld_defconn->lconn_sasl_authctx;
   1168 
   1169 		if ( ctx == NULL ) {
   1170 			return -1;
   1171 		}
   1172 
   1173 #if SASL_VERSION_MAJOR >= 2
   1174 		sasl_ssf = * (ber_len_t *)arg;
   1175 		sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
   1176 #else
   1177 		memset(&extprops, 0L, sizeof(extprops));
   1178 
   1179 		extprops.ssf = * (ber_len_t *) arg;
   1180 
   1181 		sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
   1182 			(void *) &extprops );
   1183 #endif
   1184 
   1185 		if ( sc != SASL_OK ) {
   1186 			return -1;
   1187 		}
   1188 		} break;
   1189 
   1190 	case LDAP_OPT_X_SASL_SSF_MIN:
   1191 		ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
   1192 		break;
   1193 	case LDAP_OPT_X_SASL_SSF_MAX:
   1194 		ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
   1195 		break;
   1196 	case LDAP_OPT_X_SASL_MAXBUFSIZE:
   1197 		ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
   1198 		break;
   1199 	case LDAP_OPT_X_SASL_NOCANON:
   1200 		if ( arg == LDAP_OPT_OFF ) {
   1201 			LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
   1202 		} else {
   1203 			LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
   1204 		}
   1205 		break;
   1206 
   1207 	case LDAP_OPT_X_SASL_SECPROPS: {
   1208 		int sc;
   1209 		sc = ldap_pvt_sasl_secprops( (char *) arg,
   1210 			&ld->ld_options.ldo_sasl_secprops );
   1211 
   1212 		return sc == LDAP_SUCCESS ? 0 : -1;
   1213 		}
   1214 
   1215 	case LDAP_OPT_X_SASL_CBINDING:
   1216 		if ( !arg ) return -1;
   1217 		switch( *(int *) arg ) {
   1218 		case LDAP_OPT_X_SASL_CBINDING_NONE:
   1219 		case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
   1220 		case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
   1221 			ld->ld_options.ldo_sasl_cbinding = *(int *) arg;
   1222 			return 0;
   1223 		}
   1224 		return -1;
   1225 
   1226 #ifdef SASL_GSS_CREDS
   1227 	case LDAP_OPT_X_SASL_GSS_CREDS: {
   1228 		sasl_conn_t *ctx;
   1229 		int sc;
   1230 
   1231 		if ( ld->ld_defconn == NULL )
   1232 			return -1;
   1233 
   1234 		ctx = ld->ld_defconn->lconn_sasl_authctx;
   1235 		if ( ctx == NULL )
   1236 			return -1;
   1237 
   1238 		sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg );
   1239 		if ( sc != SASL_OK )
   1240 			return -1;
   1241 		}
   1242 		break;
   1243 #endif
   1244 
   1245 	default:
   1246 		return -1;
   1247 	}
   1248 	return 0;
   1249 }
   1250 
   1251 #ifdef LDAP_R_COMPILE
   1252 #define LDAP_DEBUG_R_SASL
   1253 void *ldap_pvt_sasl_mutex_new(void)
   1254 {
   1255 	ldap_pvt_thread_mutex_t *mutex;
   1256 
   1257 	mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
   1258 		sizeof(ldap_pvt_thread_mutex_t) );
   1259 
   1260 	if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
   1261 		return mutex;
   1262 	}
   1263 	LDAP_FREE( mutex );
   1264 #ifndef LDAP_DEBUG_R_SASL
   1265 	assert( 0 );
   1266 #endif /* !LDAP_DEBUG_R_SASL */
   1267 	return NULL;
   1268 }
   1269 
   1270 int ldap_pvt_sasl_mutex_lock(void *mutex)
   1271 {
   1272 #ifdef LDAP_DEBUG_R_SASL
   1273 	if ( mutex == NULL ) {
   1274 		return SASL_OK;
   1275 	}
   1276 #else /* !LDAP_DEBUG_R_SASL */
   1277 	assert( mutex != NULL );
   1278 #endif /* !LDAP_DEBUG_R_SASL */
   1279 	return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
   1280 		? SASL_FAIL : SASL_OK;
   1281 }
   1282 
   1283 int ldap_pvt_sasl_mutex_unlock(void *mutex)
   1284 {
   1285 #ifdef LDAP_DEBUG_R_SASL
   1286 	if ( mutex == NULL ) {
   1287 		return SASL_OK;
   1288 	}
   1289 #else /* !LDAP_DEBUG_R_SASL */
   1290 	assert( mutex != NULL );
   1291 #endif /* !LDAP_DEBUG_R_SASL */
   1292 	return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
   1293 		? SASL_FAIL : SASL_OK;
   1294 }
   1295 
   1296 void ldap_pvt_sasl_mutex_dispose(void *mutex)
   1297 {
   1298 #ifdef LDAP_DEBUG_R_SASL
   1299 	if ( mutex == NULL ) {
   1300 		return;
   1301 	}
   1302 #else /* !LDAP_DEBUG_R_SASL */
   1303 	assert( mutex != NULL );
   1304 #endif /* !LDAP_DEBUG_R_SASL */
   1305 	(void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
   1306 	LDAP_FREE( mutex );
   1307 }
   1308 #endif
   1309 
   1310 #else
   1311 int ldap_int_sasl_init( void )
   1312 { return LDAP_SUCCESS; }
   1313 
   1314 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
   1315 { return LDAP_SUCCESS; }
   1316 
   1317 int
   1318 ldap_int_sasl_bind(
   1319 	LDAP			*ld,
   1320 	const char		*dn,
   1321 	const char		*mechs,
   1322 	LDAPControl		**sctrls,
   1323 	LDAPControl		**cctrls,
   1324 	unsigned		flags,
   1325 	LDAP_SASL_INTERACT_PROC *interact,
   1326 	void			*defaults,
   1327 	LDAPMessage		*result,
   1328 	const char		**rmech,
   1329 	int				*msgid )
   1330 { return LDAP_NOT_SUPPORTED; }
   1331 
   1332 int
   1333 ldap_int_sasl_external(
   1334 	LDAP *ld,
   1335 	LDAPConn *conn,
   1336 	const char * authid,
   1337 	ber_len_t ssf )
   1338 { return LDAP_SUCCESS; }
   1339 
   1340 #endif /* HAVE_CYRUS_SASL */
   1341