Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: sasl.c,v 1.4 2025/09/05 21:16:25 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: sasl.c,v 1.4 2025/09/05 21:16:25 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 #include <stdio.h>
     24 #ifdef HAVE_LIMITS_H
     25 #include <limits.h>
     26 #endif
     27 
     28 #include <ac/stdlib.h>
     29 #include <ac/string.h>
     30 
     31 #include <lber.h>
     32 #include <ldap_log.h>
     33 
     34 #include "slap.h"
     35 
     36 #include <rewrite.h>
     37 
     38 #ifdef HAVE_CYRUS_SASL
     39 # ifdef HAVE_SASL_SASL_H
     40 #  include <sasl/sasl.h>
     41 #  include <sasl/saslplug.h>
     42 # else
     43 #  include <sasl.h>
     44 #  include <saslplug.h>
     45 # endif
     46 
     47 # define	SASL_CONST const
     48 
     49 #define SASL_VERSION_FULL	((SASL_VERSION_MAJOR << 16) |\
     50 	(SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP)
     51 
     52 #if SASL_VERSION_FULL >= 0x020119 /* 2.1.25 */
     53 typedef sasl_callback_ft slap_sasl_cb_ft;
     54 #else
     55 typedef int (*slap_sasl_cb_ft)();
     56 #endif
     57 
     58 static sasl_security_properties_t sasl_secprops;
     59 #elif defined( SLAP_BUILTIN_SASL )
     60 /*
     61  * built-in SASL implementation
     62  *	only supports EXTERNAL
     63  */
     64 typedef struct sasl_ctx {
     65 	slap_ssf_t sc_external_ssf;
     66 	struct berval sc_external_id;
     67 } SASL_CTX;
     68 
     69 #endif
     70 
     71 #include <lutil.h>
     72 
     73 static struct berval ext_bv = BER_BVC( "EXTERNAL" );
     74 
     75 char *slap_sasl_auxprops;
     76 
     77 #ifdef HAVE_CYRUS_SASL
     78 
     79 /* Just use our internal auxprop by default */
     80 static int
     81 slap_sasl_getopt(
     82 	void *context,
     83 	const char *plugin_name,
     84 	const char *option,
     85 	const char **result,
     86 	unsigned *len)
     87 {
     88 	if ( strcmp( option, "auxprop_plugin" )) {
     89 		return SASL_FAIL;
     90 	}
     91 	if ( slap_sasl_auxprops )
     92 		*result = slap_sasl_auxprops;
     93 	else
     94 		*result = "slapd";
     95 	return SASL_OK;
     96 }
     97 
     98 int
     99 slap_sasl_log(
    100 	void *context,
    101 	int priority,
    102 	const char *message)
    103 {
    104 	Connection *conn = context;
    105 	int level;
    106 	const char * label;
    107 
    108 	if ( message == NULL ) {
    109 		return SASL_BADPARAM;
    110 	}
    111 
    112 	switch (priority) {
    113 	case SASL_LOG_NONE:
    114 		level = LDAP_DEBUG_NONE;
    115 		label = "None";
    116 		break;
    117 	case SASL_LOG_ERR:
    118 		level = LDAP_DEBUG_ANY;
    119 		label = "Error";
    120 		break;
    121 	case SASL_LOG_FAIL:
    122 		level = LDAP_DEBUG_ANY;
    123 		label = "Failure";
    124 		break;
    125 	case SASL_LOG_WARN:
    126 		level = LDAP_DEBUG_TRACE;
    127 		label = "Warning";
    128 		break;
    129 	case SASL_LOG_NOTE:
    130 		level = LDAP_DEBUG_TRACE;
    131 		label = "Notice";
    132 		break;
    133 	case SASL_LOG_DEBUG:
    134 		level = LDAP_DEBUG_TRACE;
    135 		label = "Debug";
    136 		break;
    137 	case SASL_LOG_TRACE:
    138 		level = LDAP_DEBUG_TRACE;
    139 		label = "Trace";
    140 		break;
    141 	case SASL_LOG_PASS:
    142 		level = LDAP_DEBUG_TRACE;
    143 		label = "Password Trace";
    144 		break;
    145 	default:
    146 		return SASL_BADPARAM;
    147 	}
    148 
    149 	Debug( level, "SASL [conn=%ld] %s: %s\n",
    150 		conn ? (long) conn->c_connid: -1L,
    151 		label, message );
    152 
    153 
    154 	return SASL_OK;
    155 }
    156 
    157 static const char *slap_propnames[] = {
    158 	"*slapConn", "*slapAuthcDNlen", "*slapAuthcDN",
    159 	"*slapAuthzDNlen", "*slapAuthzDN", NULL };
    160 
    161 #ifdef SLAP_AUXPROP_DONTUSECOPY
    162 int slap_dontUseCopy_ignore;
    163 BerVarray slap_dontUseCopy_propnames;
    164 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    165 
    166 static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
    167 static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
    168 
    169 #define	SLAP_SASL_PROP_CONN	0
    170 #define	SLAP_SASL_PROP_AUTHCLEN	1
    171 #define	SLAP_SASL_PROP_AUTHC	2
    172 #define	SLAP_SASL_PROP_AUTHZLEN	3
    173 #define	SLAP_SASL_PROP_AUTHZ	4
    174 #define	SLAP_SASL_PROP_COUNT	5	/* Number of properties we used */
    175 
    176 typedef struct lookup_info {
    177 	int flags;
    178 	const struct propval *list;
    179 	sasl_server_params_t *sparams;
    180 } lookup_info;
    181 
    182 static slap_response sasl_ap_lookup;
    183 
    184 static struct berval sc_cleartext = BER_BVC("{CLEARTEXT}");
    185 
    186 static int
    187 sasl_ap_lookup( Operation *op, SlapReply *rs )
    188 {
    189 	BerVarray bv;
    190 	AttributeDescription *ad;
    191 	Attribute *a;
    192 	const char *text;
    193 	int rc, i;
    194 	lookup_info *sl = (lookup_info *)op->o_callback->sc_private;
    195 
    196 	/* return the actual error code,
    197 	 * to allow caller to handle specific errors
    198 	 */
    199 	if (rs->sr_type != REP_SEARCH) return rs->sr_err;
    200 
    201 	for( i = 0; sl->list[i].name; i++ ) {
    202 		const char *name = sl->list[i].name;
    203 
    204 		if ( name[0] == '*' ) {
    205 			if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue;
    206 			/* Skip our private properties */
    207 			if ( !strcmp( name, slap_propnames[0] )) {
    208 				i += SLAP_SASL_PROP_COUNT - 1;
    209 				continue;
    210 			}
    211 			name++;
    212 		} else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) )
    213 			continue;
    214 
    215 		if ( sl->list[i].values ) {
    216 			if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue;
    217 		}
    218 		ad = NULL;
    219 		rc = slap_str2ad( name, &ad, &text );
    220 		if ( rc != LDAP_SUCCESS ) {
    221 			Debug( LDAP_DEBUG_TRACE,
    222 				"slap_ap_lookup: str2ad(%s): %s\n", name, text );
    223 			continue;
    224 		}
    225 
    226 		/* If it's the rootdn and a rootpw was present, we already set
    227 		 * it so don't override it here.
    228 		 */
    229 		if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values &&
    230 			be_isroot_dn( op->o_bd, &op->o_req_ndn ))
    231 			continue;
    232 
    233 		a = attr_find( rs->sr_entry->e_attrs, ad );
    234 		if ( !a ) continue;
    235 		if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) {
    236 			continue;
    237 		}
    238 		if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) ) {
    239 			sl->sparams->utils->prop_erase( sl->sparams->propctx,
    240 			sl->list[i].name );
    241 		}
    242 		for ( bv = a->a_vals; bv->bv_val; bv++ ) {
    243 			/* ITS#3846 don't give hashed passwords to SASL */
    244 			if ( ad == slap_schema.si_ad_userPassword &&
    245 				bv->bv_val[0] == '{' /*}*/ )
    246 			{
    247 				if ( lutil_passwd_scheme( bv->bv_val ) ) {
    248 					/* If it's not a recognized scheme, just assume it's
    249 					 * a cleartext password that happened to include brackets.
    250 					 *
    251 					 * If it's a recognized scheme, skip this value, unless the
    252 					 * scheme is {CLEARTEXT}. In that case, skip over the
    253 					 * scheme name and use the remainder. If there is nothing
    254 					 * past the scheme name, skip this value.
    255 					 */
    256 #ifdef SLAPD_CLEARTEXT
    257 					if ( !strncasecmp( bv->bv_val, sc_cleartext.bv_val,
    258 						sc_cleartext.bv_len )) {
    259 						struct berval cbv;
    260 						cbv.bv_len = bv->bv_len - sc_cleartext.bv_len;
    261 						if ( cbv.bv_len > 0 ) {
    262 							cbv.bv_val = bv->bv_val + sc_cleartext.bv_len;
    263 							sl->sparams->utils->prop_set( sl->sparams->propctx,
    264 								sl->list[i].name, cbv.bv_val, cbv.bv_len );
    265 						}
    266 					}
    267 #endif
    268 					continue;
    269 				}
    270 			}
    271 			sl->sparams->utils->prop_set( sl->sparams->propctx,
    272 				sl->list[i].name, bv->bv_val, bv->bv_len );
    273 		}
    274 	}
    275 	return LDAP_SUCCESS;
    276 }
    277 
    278 #if SASL_VERSION_FULL >= 0x020118
    279 static int
    280 #else
    281 static void
    282 #endif
    283 slap_auxprop_lookup(
    284 	void *glob_context,
    285 	sasl_server_params_t *sparams,
    286 	unsigned flags,
    287 	const char *user,
    288 	unsigned ulen)
    289 {
    290 	OperationBuffer opbuf = {{ NULL }};
    291 	Operation *op = (Operation *)&opbuf;
    292 	int i, doit = 0;
    293 	Connection *conn = NULL;
    294 	lookup_info sl;
    295 	int rc = LDAP_SUCCESS;
    296 #ifdef SLAP_AUXPROP_DONTUSECOPY
    297 	int dontUseCopy = 0;
    298 	BackendDB *dontUseCopy_bd = NULL;
    299 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    300 
    301 	sl.list = sparams->utils->prop_get( sparams->propctx );
    302 	sl.sparams = sparams;
    303 	sl.flags = flags;
    304 
    305 	/* Find our DN and conn first */
    306 	for( i = 0; sl.list[i].name; i++ ) {
    307 		if ( sl.list[i].name[0] == '*' ) {
    308 			if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
    309 				if ( sl.list[i].values && sl.list[i].values[0] )
    310 					AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) );
    311 				continue;
    312 			}
    313 			if ( flags & SASL_AUXPROP_AUTHZID ) {
    314 				if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZLEN] )) {
    315 					if ( sl.list[i].values && sl.list[i].values[0] )
    316 						AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
    317 							sizeof( op->o_req_ndn.bv_len ) );
    318 				} else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZ] )) {
    319 					if ( sl.list[i].values )
    320 						op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
    321 					break;
    322 				}
    323 			}
    324 
    325 			if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
    326 				if ( sl.list[i].values && sl.list[i].values[0] )
    327 					AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
    328 						sizeof( op->o_req_ndn.bv_len ) );
    329 			} else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
    330 				if ( sl.list[i].values ) {
    331 					op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
    332 					if ( !(flags & SASL_AUXPROP_AUTHZID) )
    333 						break;
    334 				}
    335 			}
    336 #ifdef SLAP_AUXPROP_DONTUSECOPY
    337 			if ( slap_dontUseCopy_propnames != NULL ) {
    338 				int j;
    339 				struct berval bv;
    340 				ber_str2bv( &sl.list[i].name[1], 0, 1, &bv );
    341 				for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ]); j++ ) {
    342 					if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
    343 						dontUseCopy = 1;
    344 						break;
    345 					}
    346 				}
    347 			}
    348 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    349 		}
    350 	}
    351 
    352 	/* we don't know anything about this, ignore it */
    353 	if ( !conn ) {
    354 		rc = LDAP_SUCCESS;
    355 		goto done;
    356 	}
    357 
    358 	/* Now see what else needs to be fetched */
    359 	for( i = 0; sl.list[i].name; i++ ) {
    360 		const char *name = sl.list[i].name;
    361 
    362 		if ( name[0] == '*' ) {
    363 			if ( flags & SASL_AUXPROP_AUTHZID ) continue;
    364 			/* Skip our private properties */
    365 			if ( !strcmp( name, slap_propnames[0] )) {
    366 				i += SLAP_SASL_PROP_COUNT - 1;
    367 				continue;
    368 			}
    369 			name++;
    370 		} else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
    371 			continue;
    372 
    373 		if ( sl.list[i].values ) {
    374 			if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
    375 		}
    376 		doit = 1;
    377 		break;
    378 	}
    379 
    380 	if (doit) {
    381 		slap_callback cb = { NULL, sasl_ap_lookup, NULL, NULL };
    382 
    383 		cb.sc_private = &sl;
    384 
    385 		op->o_bd = select_backend( &op->o_req_ndn, 1 );
    386 
    387 		if ( op->o_bd ) {
    388 			/* For rootdn, see if we can use the rootpw */
    389 			if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) &&
    390 				!BER_BVISEMPTY( &op->o_bd->be_rootpw )) {
    391 				struct berval cbv = BER_BVNULL;
    392 
    393 				/* If there's a recognized scheme, see if it's CLEARTEXT */
    394 				if ( lutil_passwd_scheme( op->o_bd->be_rootpw.bv_val )) {
    395 					if ( !strncasecmp( op->o_bd->be_rootpw.bv_val,
    396 						sc_cleartext.bv_val, sc_cleartext.bv_len )) {
    397 
    398 						/* If it's CLEARTEXT, skip past scheme spec */
    399 						cbv.bv_len = op->o_bd->be_rootpw.bv_len -
    400 							sc_cleartext.bv_len;
    401 						if ( cbv.bv_len ) {
    402 							cbv.bv_val = op->o_bd->be_rootpw.bv_val +
    403 								sc_cleartext.bv_len;
    404 						}
    405 					}
    406 				/* No scheme, use the whole value */
    407 				} else {
    408 					cbv = op->o_bd->be_rootpw;
    409 				}
    410 				if ( !BER_BVISEMPTY( &cbv )) {
    411 					for( i = 0; sl.list[i].name; i++ ) {
    412 						const char *name = sl.list[i].name;
    413 
    414 						if ( name[0] == '*' ) {
    415 							if ( flags & SASL_AUXPROP_AUTHZID ) continue;
    416 								name++;
    417 						} else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
    418 							continue;
    419 
    420 						if ( !strcasecmp(name,"userPassword") ) {
    421 							sl.sparams->utils->prop_set( sl.sparams->propctx,
    422 								sl.list[i].name, cbv.bv_val, cbv.bv_len );
    423 							break;
    424 						}
    425 					}
    426 				}
    427 			}
    428 
    429 #ifdef SLAP_AUXPROP_DONTUSECOPY
    430 			if ( SLAP_SHADOW( op->o_bd ) && dontUseCopy ) {
    431 				dontUseCopy_bd = op->o_bd;
    432 				op->o_bd = frontendDB;
    433 			}
    434 
    435 retry_dontUseCopy:;
    436 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    437 
    438 			if ( op->o_bd->be_search ) {
    439 				SlapReply rs = {REP_RESULT};
    440 #ifdef SLAP_AUXPROP_DONTUSECOPY
    441 				LDAPControl **save_ctrls = NULL, c;
    442 				int save_dontUseCopy;
    443 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    444 
    445 				op->o_hdr = conn->c_sasl_bindop->o_hdr;
    446 				op->o_controls = opbuf.ob_controls;
    447 				op->o_tag = LDAP_REQ_SEARCH;
    448 				op->o_dn = conn->c_ndn;
    449 				op->o_ndn = conn->c_ndn;
    450 				op->o_callback = &cb;
    451 				slap_op_time( &op->o_time, &op->o_tincr );
    452 				op->o_do_not_cache = 1;
    453 				op->o_is_auth_check = 1;
    454 				op->o_req_dn = op->o_req_ndn;
    455 				op->ors_scope = LDAP_SCOPE_BASE;
    456 				op->ors_deref = LDAP_DEREF_NEVER;
    457 				op->ors_tlimit = SLAP_NO_LIMIT;
    458 				op->ors_slimit = 1;
    459 				op->ors_filter = &generic_filter;
    460 				op->ors_filterstr = generic_filterstr;
    461 				op->o_authz = conn->c_authz;
    462 				/* FIXME: we want all attributes, right? */
    463 				op->ors_attrs = NULL;
    464 
    465 #ifdef SLAP_AUXPROP_DONTUSECOPY
    466 				if ( dontUseCopy ) {
    467 					save_dontUseCopy = op->o_dontUseCopy;
    468 					if ( !op->o_dontUseCopy ) {
    469 						int cnt = 0;
    470 						save_ctrls = op->o_ctrls;
    471 						if ( op->o_ctrls ) {
    472 							for ( ; op->o_ctrls[ cnt ]; cnt++ )
    473 								;
    474 						}
    475 						op->o_ctrls = op->o_tmpcalloc( sizeof(LDAPControl *), cnt + 2, op->o_tmpmemctx );
    476 						if ( cnt ) {
    477 							for ( cnt = 0; save_ctrls[ cnt ]; cnt++ ) {
    478 								op->o_ctrls[ cnt ] = save_ctrls[ cnt ];
    479 							}
    480 						}
    481 						c.ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
    482 						c.ldctl_iscritical = 1;
    483 						BER_BVZERO( &c.ldctl_value );
    484 						op->o_ctrls[ cnt ] = &c;
    485 					}
    486 					op->o_dontUseCopy = SLAP_CONTROL_CRITICAL;
    487 				}
    488 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    489 
    490 				rc = op->o_bd->be_search( op, &rs );
    491 
    492 #ifdef SLAP_AUXPROP_DONTUSECOPY
    493 				if ( dontUseCopy ) {
    494 					if ( save_ctrls != op->o_ctrls ) {
    495 						op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
    496 						op->o_ctrls = save_ctrls;
    497 						op->o_dontUseCopy = save_dontUseCopy;
    498 					}
    499 
    500 					if ( rs.sr_err == LDAP_UNAVAILABLE && slap_dontUseCopy_ignore )
    501 					{
    502 						op->o_bd = dontUseCopy_bd;
    503 						dontUseCopy = 0;
    504 						goto retry_dontUseCopy;
    505 					}
    506 				}
    507 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    508 			}
    509 		}
    510 	}
    511 done:;
    512 #if SASL_VERSION_FULL >= 0x020118
    513 	return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
    514 #endif
    515 }
    516 
    517 #if SASL_VERSION_FULL >= 0x020110
    518 static int
    519 slap_auxprop_store(
    520 	void *glob_context,
    521 	sasl_server_params_t *sparams,
    522 	struct propctx *prctx,
    523 	const char *user,
    524 	unsigned ulen)
    525 {
    526 	Operation op = {0};
    527 	Opheader oph;
    528 	int rc, i;
    529 	unsigned j;
    530 	Connection *conn = NULL;
    531 	const struct propval *pr;
    532 	Modifications *modlist = NULL, **modtail = &modlist, *mod;
    533 	slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
    534 	char textbuf[SLAP_TEXT_BUFLEN];
    535 	const char *text;
    536 	size_t textlen = sizeof(textbuf);
    537 #ifdef SLAP_AUXPROP_DONTUSECOPY
    538 	int dontUseCopy = 0;
    539 	BackendDB *dontUseCopy_bd = NULL;
    540 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    541 
    542 	/* just checking if we are enabled */
    543 	if (!prctx) return SASL_OK;
    544 
    545 	if (!sparams || !user) return SASL_BADPARAM;
    546 
    547 	pr = sparams->utils->prop_get( sparams->propctx );
    548 
    549 	/* Find our DN and conn first */
    550 	for( i = 0; pr[i].name; i++ ) {
    551 		if ( pr[i].name[0] == '*' ) {
    552 			if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
    553 				if ( pr[i].values && pr[i].values[0] )
    554 					AC_MEMCPY( &conn, pr[i].values[0], sizeof( conn ) );
    555 				continue;
    556 			}
    557 			if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
    558 				if ( pr[i].values && pr[i].values[0] )
    559 					AC_MEMCPY( &op.o_req_ndn.bv_len, pr[i].values[0],
    560 						sizeof( op.o_req_ndn.bv_len ) );
    561 			} else if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
    562 				if ( pr[i].values )
    563 					op.o_req_ndn.bv_val = (char *)pr[i].values[0];
    564 			}
    565 #ifdef SLAP_AUXPROP_DONTUSECOPY
    566 			if ( slap_dontUseCopy_propnames != NULL ) {
    567 				struct berval bv;
    568 				ber_str2bv( &pr[i].name[1], 0, 1, &bv );
    569 				for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ] ); j++ ) {
    570 					if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
    571 						dontUseCopy = 1;
    572 						break;
    573 					}
    574 				}
    575 			}
    576 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    577 		}
    578 	}
    579 	if (!conn || !op.o_req_ndn.bv_val) return SASL_BADPARAM;
    580 
    581 	op.o_bd = select_backend( &op.o_req_ndn, 1 );
    582 
    583 	if ( !op.o_bd || !op.o_bd->be_modify ) return SASL_FAIL;
    584 
    585 #ifdef SLAP_AUXPROP_DONTUSECOPY
    586 	if ( SLAP_SHADOW( op.o_bd ) && dontUseCopy ) {
    587 		dontUseCopy_bd = op.o_bd;
    588 		op.o_bd = frontendDB;
    589 		op.o_dontUseCopy = SLAP_CONTROL_CRITICAL;
    590 	}
    591 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    592 
    593 	pr = sparams->utils->prop_get( prctx );
    594 	if (!pr) return SASL_BADPARAM;
    595 
    596 	for (i=0; pr[i].name; i++);
    597 	if (!i) return SASL_BADPARAM;
    598 
    599 	for (i=0; pr[i].name; i++) {
    600 		mod = (Modifications *)ch_malloc( sizeof(Modifications) );
    601 		mod->sml_op = LDAP_MOD_REPLACE;
    602 		mod->sml_flags = 0;
    603 		ber_str2bv( pr[i].name, 0, 0, &mod->sml_type );
    604 		mod->sml_numvals = pr[i].nvalues;
    605 		mod->sml_values = (struct berval *)ch_malloc( (pr[i].nvalues + 1) *
    606 			sizeof(struct berval));
    607 		for (j=0; j<pr[i].nvalues; j++) {
    608 			ber_str2bv( pr[i].values[j], 0, 1, &mod->sml_values[j]);
    609 		}
    610 		BER_BVZERO( &mod->sml_values[j] );
    611 		mod->sml_nvalues = NULL;
    612 		mod->sml_desc = NULL;
    613 		*modtail = mod;
    614 		modtail = &mod->sml_next;
    615 	}
    616 	*modtail = NULL;
    617 
    618 	rc = slap_mods_check( &op, modlist, &text, textbuf, textlen, NULL );
    619 
    620 	if ( rc == LDAP_SUCCESS ) {
    621 		rc = slap_mods_no_user_mod_check( &op, modlist,
    622 			&text, textbuf, textlen );
    623 
    624 		if ( rc == LDAP_SUCCESS ) {
    625 			if ( conn->c_sasl_bindop ) {
    626 				op.o_hdr = conn->c_sasl_bindop->o_hdr;
    627 			} else {
    628 				op.o_hdr = &oph;
    629 				memset( &oph, 0, sizeof(oph) );
    630 				operation_fake_init( conn, &op, ldap_pvt_thread_pool_context(), 0 );
    631 			}
    632 			op.o_tag = LDAP_REQ_MODIFY;
    633 			op.o_ndn = op.o_req_ndn;
    634 			op.o_callback = &cb;
    635 			slap_op_time( &op.o_time, &op.o_tincr );
    636 			op.o_do_not_cache = 1;
    637 			op.o_is_auth_check = 1;
    638 			op.o_req_dn = op.o_req_ndn;
    639 			op.orm_modlist = modlist;
    640 
    641 			for (;;) {
    642 				SlapReply rs = {REP_RESULT};
    643 				rc = op.o_bd->be_modify( &op, &rs );
    644 
    645 #ifdef SLAP_AUXPROP_DONTUSECOPY
    646 				if ( dontUseCopy &&
    647 					rs.sr_err == LDAP_UNAVAILABLE &&
    648 					slap_dontUseCopy_ignore )
    649 				{
    650 					op.o_bd = dontUseCopy_bd;
    651 					op.o_dontUseCopy = SLAP_CONTROL_NONE;
    652 					dontUseCopy = 0;
    653 					continue;
    654 				}
    655 #endif /* SLAP_AUXPROP_DONTUSECOPY */
    656 				break;
    657 			}
    658 		}
    659 	}
    660 	slap_mods_free( modlist, 1 );
    661 	return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
    662 }
    663 #endif /* SASL_VERSION_FULL >= 2.1.16 */
    664 
    665 static sasl_auxprop_plug_t slap_auxprop_plugin = {
    666 	0,	/* Features */
    667 	0,	/* spare */
    668 	NULL,	/* glob_context */
    669 	NULL,	/* auxprop_free */
    670 	slap_auxprop_lookup,
    671 	"slapd",	/* name */
    672 #if SASL_VERSION_FULL >= 0x020110
    673 	slap_auxprop_store	/* the declaration of this member changed
    674 				 * in cyrus SASL from 2.1.15 to 2.1.16 */
    675 #else
    676 	NULL
    677 #endif
    678 };
    679 
    680 static int
    681 slap_auxprop_init(
    682 	const sasl_utils_t *utils,
    683 	int max_version,
    684 	int *out_version,
    685 	sasl_auxprop_plug_t **plug,
    686 	const char *plugname)
    687 {
    688 	if ( !out_version || !plug ) return SASL_BADPARAM;
    689 
    690 	if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS;
    691 
    692 	*out_version = SASL_AUXPROP_PLUG_VERSION;
    693 	*plug = &slap_auxprop_plugin;
    694 	return SASL_OK;
    695 }
    696 
    697 /* Convert a SASL authcid or authzid into a DN. Store the DN in an
    698  * auxiliary property, so that we can refer to it in sasl_authorize
    699  * without interfering with anything else. Also, the SASL username
    700  * buffer is constrained to 256 characters, and our DNs could be
    701  * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192)
    702  */
    703 static int
    704 slap_sasl_canonicalize(
    705 	sasl_conn_t *sconn,
    706 	void *context,
    707 	const char *in,
    708 	unsigned inlen,
    709 	unsigned flags,
    710 	const char *user_realm,
    711 	char *out,
    712 	unsigned out_max,
    713 	unsigned *out_len)
    714 {
    715 	Connection *conn = (Connection *)context;
    716 	struct propctx *props = sasl_auxprop_getctx( sconn );
    717 	struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
    718 	struct berval dn;
    719 	int rc, which;
    720 	const char *names[2];
    721 	struct berval	bvin;
    722 
    723 	*out_len = 0;
    724 
    725 	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
    726 		conn ? (long) conn->c_connid : -1L,
    727 		(flags & SASL_CU_AUTHID) ? "authcid" : "authzid",
    728 		in ? in : "<empty>");
    729 
    730 	/* If name is too big, just truncate. We don't care, we're
    731 	 * using DNs, not the usernames.
    732 	 */
    733 	if ( inlen > out_max )
    734 		inlen = out_max-1;
    735 
    736 	/* This is a Simple Bind using SPASSWD. That means the in-directory
    737 	 * userPassword of the Binding user already points at SASL, so it
    738 	 * cannot be used to actually satisfy a password comparison. Just
    739 	 * ignore it, some other mech will process it.
    740 	 */
    741 	if ( !conn->c_sasl_bindop ||
    742 		conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done;
    743 
    744 	/* See if we need to add request, can only do it once */
    745 	prop_getnames( props, slap_propnames, auxvals );
    746 	if ( !auxvals[0].name )
    747 		prop_request( props, slap_propnames );
    748 
    749 	if ( flags & SASL_CU_AUTHID )
    750 		which = SLAP_SASL_PROP_AUTHCLEN;
    751 	else
    752 		which = SLAP_SASL_PROP_AUTHZLEN;
    753 
    754 	/* Need to store the Connection for auxprop_lookup */
    755 	if ( !auxvals[SLAP_SASL_PROP_CONN].values ) {
    756 		names[0] = slap_propnames[SLAP_SASL_PROP_CONN];
    757 		names[1] = NULL;
    758 		prop_set( props, names[0], (char *)&conn, sizeof( conn ) );
    759 	}
    760 
    761 	/* Already been here? */
    762 	if ( auxvals[which].values )
    763 		goto done;
    764 
    765 	/* Normally we require an authzID to have a u: or dn: prefix.
    766 	 * However, SASL frequently gives us an authzID that is just
    767 	 * an exact copy of the authcID, without a prefix. We need to
    768 	 * detect and allow this condition. If SASL calls canonicalize
    769 	 * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer.
    770 	 * But if it's broken into two calls, we need to remember the
    771 	 * authcID so that we can compare the authzID later. We store
    772 	 * the authcID temporarily in conn->c_sasl_dn. We necessarily
    773 	 * finish Canonicalizing before Authorizing, so there is no
    774 	 * conflict with slap_sasl_authorize's use of this temp var.
    775 	 *
    776 	 * The SASL EXTERNAL mech is backwards from all the other mechs,
    777 	 * it does authzID before the authcID. If we see that authzID
    778 	 * has already been done, don't do anything special with authcID.
    779 	 */
    780 	if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) {
    781 		conn->c_sasl_dn.bv_val = (char *) in;
    782 		conn->c_sasl_dn.bv_len = 0;
    783 	} else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) {
    784 		rc = strcmp( in, conn->c_sasl_dn.bv_val );
    785 		conn->c_sasl_dn.bv_val = NULL;
    786 		/* They were equal, no work needed */
    787 		if ( !rc ) goto done;
    788 	}
    789 
    790 	bvin.bv_val = (char *)in;
    791 	bvin.bv_len = inlen;
    792 	rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn,
    793 		(flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID );
    794 	if ( rc != LDAP_SUCCESS ) {
    795 		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
    796 		return SASL_NOAUTHZ;
    797 	}
    798 
    799 	names[0] = slap_propnames[which];
    800 	names[1] = NULL;
    801 	prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) );
    802 
    803 	which++;
    804 	names[0] = slap_propnames[which];
    805 	prop_set( props, names[0], dn.bv_val, dn.bv_len );
    806 
    807 	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
    808 		conn ? (long) conn->c_connid : -1L, names[0]+1,
    809 		dn.bv_val ? dn.bv_val : "<EMPTY>" );
    810 
    811 	/* Not needed any more, SASL has copied it */
    812 	if ( conn && conn->c_sasl_bindop )
    813 		conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx );
    814 
    815 done:
    816 	AC_MEMCPY( out, in, inlen );
    817 	out[inlen] = '\0';
    818 
    819 	*out_len = inlen;
    820 
    821 	return SASL_OK;
    822 }
    823 
    824 static int
    825 slap_sasl_authorize(
    826 	sasl_conn_t *sconn,
    827 	void *context,
    828 	char *requested_user,
    829 	unsigned rlen,
    830 	char *auth_identity,
    831 	unsigned alen,
    832 	const char *def_realm,
    833 	unsigned urlen,
    834 	struct propctx *props)
    835 {
    836 	Connection *conn = (Connection *)context;
    837 	/* actually:
    838 	 *	(SLAP_SASL_PROP_COUNT - 1)	because we skip "conn",
    839 	 *	+ 1				for NULL termination?
    840 	 */
    841 	struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
    842 	struct berval authcDN, authzDN = BER_BVNULL;
    843 	int rc;
    844 
    845 	/* Simple Binds don't support proxy authorization, ignore it */
    846 	if ( !conn->c_sasl_bindop ||
    847 		conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) return SASL_OK;
    848 
    849 	Debug( LDAP_DEBUG_ARGS, "SASL proxy authorize [conn=%ld]: "
    850 		"authcid=\"%s\" authzid=\"%s\"\n",
    851 		conn ? (long) conn->c_connid : -1L, auth_identity, requested_user );
    852 	if ( conn->c_sasl_dn.bv_val ) {
    853 		BER_BVZERO( &conn->c_sasl_dn );
    854 	}
    855 
    856 	/* Skip SLAP_SASL_PROP_CONN */
    857 	prop_getnames( props, slap_propnames+1, auxvals );
    858 
    859 	/* Should not happen */
    860 	if ( !auxvals[0].values ) {
    861 		sasl_seterror( sconn, 0, "invalid authcid" );
    862 		return SASL_NOAUTHZ;
    863 	}
    864 
    865 	AC_MEMCPY( &authcDN.bv_len, auxvals[0].values[0], sizeof(authcDN.bv_len) );
    866 	authcDN.bv_val = auxvals[1].values ? (char *)auxvals[1].values[0] : NULL;
    867 	conn->c_sasl_dn = authcDN;
    868 
    869 	/* Nothing to do if no authzID was given */
    870 	if ( !auxvals[2].name || !auxvals[2].values ) {
    871 		goto ok;
    872 	}
    873 
    874 	AC_MEMCPY( &authzDN.bv_len, auxvals[2].values[0], sizeof(authzDN.bv_len) );
    875 	authzDN.bv_val = auxvals[3].values ? (char *)auxvals[3].values[0] : NULL;
    876 
    877 	rc = slap_sasl_authorized( conn->c_sasl_bindop, &authcDN, &authzDN );
    878 	if ( rc != LDAP_SUCCESS ) {
    879 		Debug( LDAP_DEBUG_TRACE, "SASL Proxy Authorize [conn=%ld]: "
    880 			"proxy authorization disallowed (%d)\n",
    881 			conn ? (long) conn->c_connid : -1L, rc );
    882 
    883 		sasl_seterror( sconn, 0, "not authorized" );
    884 		return SASL_NOAUTHZ;
    885 	}
    886 
    887 	/* FIXME: we need yet another dup because slap_sasl_getdn()
    888 	 * is using the bind operation slab */
    889 	ber_dupbv( &conn->c_sasl_authz_dn, &authzDN );
    890 
    891 ok:
    892 	if (conn->c_sasl_bindop) {
    893 		Debug( LDAP_DEBUG_STATS,
    894 			"%s BIND authcid=\"%s\" authzid=\"%s\"\n",
    895 			conn->c_sasl_bindop->o_log_prefix,
    896 			auth_identity, requested_user );
    897 	}
    898 
    899 	Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
    900 		" proxy authorization allowed authzDN=\"%s\"\n",
    901 		conn ? (long) conn->c_connid : -1L,
    902 		authzDN.bv_val ? authzDN.bv_val : "" );
    903 	return SASL_OK;
    904 }
    905 
    906 static int
    907 slap_sasl_err2ldap( int saslerr )
    908 {
    909 	int rc;
    910 
    911 	/* map SASL errors to LDAP resultCode returned by:
    912 	 *	sasl_server_new()
    913 	 *		SASL_OK, SASL_NOMEM
    914 	 *	sasl_server_step()
    915 	 *		SASL_OK, SASL_CONTINUE, SASL_TRANS, SASL_BADPARAM, SASL_BADPROT,
    916 	 *      ...
    917 	 *	sasl_server_start()
    918 	 *      + SASL_NOMECH
    919 	 *	sasl_setprop()
    920 	 *		SASL_OK, SASL_BADPARAM
    921 	 */
    922 
    923 	switch (saslerr) {
    924 		case SASL_OK:
    925 			rc = LDAP_SUCCESS;
    926 			break;
    927 		case SASL_CONTINUE:
    928 			rc = LDAP_SASL_BIND_IN_PROGRESS;
    929 			break;
    930 		case SASL_FAIL:
    931 		case SASL_NOMEM:
    932 			rc = LDAP_OTHER;
    933 			break;
    934 		case SASL_NOMECH:
    935 			rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
    936 			break;
    937 		case SASL_BADAUTH:
    938 		case SASL_NOUSER:
    939 		case SASL_TRANS:
    940 		case SASL_EXPIRED:
    941 			rc = LDAP_INVALID_CREDENTIALS;
    942 			break;
    943 		case SASL_NOAUTHZ:
    944 			rc = LDAP_INSUFFICIENT_ACCESS;
    945 			break;
    946 		case SASL_TOOWEAK:
    947 		case SASL_ENCRYPT:
    948 			rc = LDAP_INAPPROPRIATE_AUTH;
    949 			break;
    950 		case SASL_UNAVAIL:
    951 		case SASL_TRYAGAIN:
    952 			rc = LDAP_UNAVAILABLE;
    953 			break;
    954 		case SASL_DISABLED:
    955 			rc = LDAP_UNWILLING_TO_PERFORM;
    956 			break;
    957 		default:
    958 			rc = LDAP_OTHER;
    959 			break;
    960 	}
    961 
    962 	return rc;
    963 }
    964 
    965 #ifdef SLAPD_SPASSWD
    966 
    967 static struct berval sasl_pwscheme = BER_BVC("{SASL}");
    968 
    969 static int chk_sasl(
    970 	const struct berval *sc,
    971 	const struct berval * passwd,
    972 	const struct berval * cred,
    973 	const char **text )
    974 {
    975 	unsigned int i;
    976 	int rtn;
    977 	void *ctx, *sconn = NULL;
    978 
    979 	for( i=0; i<cred->bv_len; i++) {
    980 		if(cred->bv_val[i] == '\0') {
    981 			return LUTIL_PASSWD_ERR;	/* NUL character in password */
    982 		}
    983 	}
    984 
    985 	if( cred->bv_val[i] != '\0' ) {
    986 		return LUTIL_PASSWD_ERR;	/* cred must behave like a string */
    987 	}
    988 
    989 	for( i=0; i<passwd->bv_len; i++) {
    990 		if(passwd->bv_val[i] == '\0') {
    991 			return LUTIL_PASSWD_ERR;	/* NUL character in password */
    992 		}
    993 	}
    994 
    995 	if( passwd->bv_val[i] != '\0' ) {
    996 		return LUTIL_PASSWD_ERR;	/* passwd must behave like a string */
    997 	}
    998 
    999 	rtn = LUTIL_PASSWD_ERR;
   1000 
   1001 	ctx = ldap_pvt_thread_pool_context();
   1002 	ldap_pvt_thread_pool_getkey( ctx, (void *)slap_sasl_bind, &sconn, NULL );
   1003 
   1004 	if( sconn != NULL ) {
   1005 		int sc;
   1006 		sc = sasl_checkpass( sconn,
   1007 			passwd->bv_val, passwd->bv_len,
   1008 			cred->bv_val, cred->bv_len );
   1009 		rtn = ( sc != SASL_OK ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
   1010 	}
   1011 
   1012 	return rtn;
   1013 }
   1014 #endif /* SLAPD_SPASSWD */
   1015 
   1016 #endif /* HAVE_CYRUS_SASL */
   1017 
   1018 typedef struct slapd_map_data {
   1019 	struct berval base;
   1020 	struct berval filter;
   1021 	AttributeName attrs[2];
   1022 	int scope;
   1023 } slapd_map_data;
   1024 
   1025 static void *
   1026 slapd_rw_config( const char *fname, int lineno, int argc, char **argv )
   1027 {
   1028 	slapd_map_data *ret = NULL;
   1029 	LDAPURLDesc *lud = NULL;
   1030 	char *uri;
   1031 	AttributeDescription *ad = NULL;
   1032 	int rc, flen = 0;
   1033 	struct berval dn, ndn;
   1034 
   1035 	if ( argc != 1 ) {
   1036 		Debug( LDAP_DEBUG_ANY,
   1037 			"[%s:%d] slapd map needs URI\n",
   1038 			fname, lineno );
   1039         return NULL;
   1040 	}
   1041 
   1042 	uri = argv[0];
   1043 	if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
   1044 		uri += STRLENOF( "uri=" );
   1045 	}
   1046 
   1047 	if ( ldap_url_parse( uri, &lud ) != LDAP_URL_SUCCESS ) {
   1048 		Debug( LDAP_DEBUG_ANY,
   1049 			"[%s:%d] illegal URI '%s'\n",
   1050 			fname, lineno, uri );
   1051         return NULL;
   1052 	}
   1053 
   1054 	if ( strcasecmp( lud->lud_scheme, "ldap" )) {
   1055 		Debug( LDAP_DEBUG_ANY,
   1056 			"[%s:%d] illegal URI scheme '%s'\n",
   1057 			fname, lineno, lud->lud_scheme );
   1058 		goto done;
   1059 	}
   1060 
   1061 	if (( lud->lud_host && lud->lud_host[0] ) || lud->lud_exts
   1062 		|| !lud->lud_dn ) {
   1063 		Debug( LDAP_DEBUG_ANY,
   1064 			"[%s:%d] illegal URI '%s'\n",
   1065 			fname, lineno, uri );
   1066 		goto done;
   1067 	}
   1068 
   1069 	if ( lud->lud_attrs ) {
   1070 		if ( lud->lud_attrs[1] ) {
   1071 			Debug( LDAP_DEBUG_ANY,
   1072 				"[%s:%d] only one attribute allowed in URI\n",
   1073 				fname, lineno );
   1074 			goto done;
   1075 		}
   1076 		if ( strcasecmp( lud->lud_attrs[0], "dn" ) &&
   1077 			strcasecmp( lud->lud_attrs[0], "entryDN" )) {
   1078 			const char *text;
   1079 			rc = slap_str2ad( lud->lud_attrs[0], &ad, &text );
   1080 			if ( rc )
   1081 				goto done;
   1082 		}
   1083 	}
   1084 	ber_str2bv( lud->lud_dn, 0, 0, &dn );
   1085 	if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ))
   1086 		goto done;
   1087 
   1088 	if ( lud->lud_filter ) {
   1089 		flen = strlen( lud->lud_filter ) + 1;
   1090 	}
   1091 	ret = ch_malloc( sizeof( slapd_map_data ) + flen );
   1092 	ret->base = ndn;
   1093 	if ( flen ) {
   1094 		ret->filter.bv_val = (char *)(ret+1);
   1095 		ret->filter.bv_len = flen - 1;
   1096 		strcpy( ret->filter.bv_val, lud->lud_filter );
   1097 	} else {
   1098 		BER_BVZERO( &ret->filter );
   1099 	}
   1100 	ret->scope = lud->lud_scope;
   1101 	if ( ad ) {
   1102 		ret->attrs[0].an_name = ad->ad_cname;
   1103 	} else {
   1104 		BER_BVZERO( &ret->attrs[0].an_name );
   1105 	}
   1106 	ret->attrs[0].an_desc = ad;
   1107 	BER_BVZERO( &ret->attrs[1].an_name );
   1108 done:
   1109 	ldap_free_urldesc( lud );
   1110 	return ret;
   1111 }
   1112 
   1113 struct slapd_rw_info {
   1114 	slapd_map_data *si_data;
   1115 	struct berval si_val;
   1116 };
   1117 
   1118 static int
   1119 slapd_rw_cb( Operation *op, SlapReply *rs )
   1120 {
   1121 	if ( rs->sr_type == REP_SEARCH ) {
   1122 		struct slapd_rw_info *si = op->o_callback->sc_private;
   1123 
   1124 		if ( si->si_data->attrs[0].an_desc ) {
   1125 			Attribute *a;
   1126 
   1127 			a = attr_find( rs->sr_entry->e_attrs,
   1128 				si->si_data->attrs[0].an_desc );
   1129 			if ( a ) {
   1130 				ber_dupbv( &si->si_val, a->a_vals );
   1131 			}
   1132 		} else {
   1133 			ber_dupbv( &si->si_val, &rs->sr_entry->e_name );
   1134 		}
   1135 	}
   1136 	return LDAP_SUCCESS;
   1137 }
   1138 
   1139 static int
   1140 slapd_rw_apply( void *private, const char *filter, struct berval *val )
   1141 {
   1142 	slapd_map_data *sl = private;
   1143 	slap_callback cb = { NULL };
   1144 	Connection conn = {0};
   1145 	OperationBuffer opbuf;
   1146 	Operation *op;
   1147 	void *thrctx;
   1148 	SlapReply rs = {REP_RESULT};
   1149 	struct slapd_rw_info si;
   1150 	char *ptr;
   1151 	int rc;
   1152 
   1153 	thrctx = ldap_pvt_thread_pool_context();
   1154 	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
   1155 	op = &opbuf.ob_op;
   1156 
   1157 	op->o_tag = LDAP_REQ_SEARCH;
   1158 	op->o_req_dn = op->o_req_ndn = sl->base;
   1159 	op->o_bd = select_backend( &op->o_req_ndn, 1 );
   1160 	if ( !op->o_bd ) {
   1161 		return REWRITE_ERR;
   1162 	}
   1163 	si.si_data = sl;
   1164 	BER_BVZERO( &si.si_val );
   1165 	op->ors_scope = sl->scope;
   1166 	op->ors_deref = LDAP_DEREF_NEVER;
   1167 	op->ors_slimit = 1;
   1168 	op->ors_tlimit = SLAP_NO_LIMIT;
   1169 	if ( sl->attrs[0].an_desc ) {
   1170 		op->ors_attrs = sl->attrs;
   1171 	} else {
   1172 		op->ors_attrs = slap_anlist_no_attrs;
   1173 	}
   1174 	if ( filter ) {
   1175 		rc = strlen( filter );
   1176 	} else {
   1177 		rc = 0;
   1178 	}
   1179 	rc += sl->filter.bv_len;
   1180 	ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( rc + 1, op->o_tmpmemctx );
   1181 	if ( sl->filter.bv_len ) {
   1182 		ptr = lutil_strcopy( ptr, sl->filter.bv_val );
   1183 	} else {
   1184 		*ptr = '\0';
   1185 	}
   1186 	if ( filter ) {
   1187 		strcpy( ptr, filter );
   1188 	}
   1189 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
   1190 	if ( !op->ors_filter ) {
   1191 		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
   1192 		return REWRITE_ERR;
   1193 	}
   1194 
   1195 	op->ors_attrsonly = 0;
   1196 	op->o_dn = op->o_bd->be_rootdn;
   1197 	op->o_ndn = op->o_bd->be_rootndn;
   1198 	op->o_do_not_cache = 1;
   1199 
   1200 	cb.sc_response = slapd_rw_cb;
   1201 	cb.sc_private = &si;
   1202 	op->o_callback = &cb;
   1203 
   1204 	rc = op->o_bd->be_search( op, &rs );
   1205 	if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &si.si_val )) {
   1206 		*val = si.si_val;
   1207 		rc = REWRITE_SUCCESS;
   1208 	} else {
   1209 		if ( !BER_BVISNULL( &si.si_val )) {
   1210 			ch_free( si.si_val.bv_val );
   1211 		}
   1212 		rc = REWRITE_ERR;
   1213 	}
   1214 	filter_free_x( op, op->ors_filter, 1 );
   1215 	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
   1216 	return rc;
   1217 }
   1218 
   1219 static int
   1220 slapd_rw_destroy( void *private )
   1221 {
   1222 	slapd_map_data *md = private;
   1223 
   1224 	assert( private != NULL );
   1225 
   1226 	ch_free( md->base.bv_val );
   1227 	ch_free( md );
   1228 
   1229 	return 0;
   1230 }
   1231 
   1232 static const rewrite_mapper slapd_mapper = {
   1233 	"slapd",
   1234 	slapd_rw_config,
   1235 	slapd_rw_apply,
   1236 	slapd_rw_destroy
   1237 };
   1238 
   1239 int slap_sasl_init( void )
   1240 {
   1241 #ifdef HAVE_CYRUS_SASL
   1242 	int rc;
   1243 	static sasl_callback_t server_callbacks[] = {
   1244 		{ SASL_CB_LOG, (slap_sasl_cb_ft)&slap_sasl_log, NULL },
   1245 		{ SASL_CB_GETOPT, (slap_sasl_cb_ft)&slap_sasl_getopt, NULL },
   1246 		{ SASL_CB_LIST_END, NULL, NULL }
   1247 	};
   1248 #endif
   1249 
   1250 	rewrite_mapper_register( &slapd_mapper );
   1251 
   1252 #ifdef HAVE_CYRUS_SASL
   1253 #ifdef HAVE_SASL_VERSION
   1254 	/* stringify the version number, sasl.h doesn't do it for us */
   1255 #define	VSTR0(maj, min, pat)	#maj "." #min "." #pat
   1256 #define	VSTR(maj, min, pat)	VSTR0(maj, min, pat)
   1257 #define	SASL_VERSION_STRING	VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
   1258 				SASL_VERSION_STEP)
   1259 
   1260 	sasl_version( NULL, &rc );
   1261 	if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
   1262 		(rc & 0xffff) < SASL_VERSION_STEP)
   1263 	{
   1264 		char version[sizeof("xxx.xxx.xxxxx")];
   1265 		sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
   1266 			rc & 0xffff );
   1267 		Debug( LDAP_DEBUG_ANY, "slap_sasl_init: SASL library version mismatch:"
   1268 			" expected %s, got %s\n",
   1269 			SASL_VERSION_STRING, version );
   1270 		return -1;
   1271 	}
   1272 #endif
   1273 
   1274 	sasl_set_mutex(
   1275 		ldap_pvt_sasl_mutex_new,
   1276 		ldap_pvt_sasl_mutex_lock,
   1277 		ldap_pvt_sasl_mutex_unlock,
   1278 		ldap_pvt_sasl_mutex_dispose );
   1279 
   1280 	generic_filter.f_desc = slap_schema.si_ad_objectClass;
   1281 
   1282 	rc = sasl_auxprop_add_plugin( "slapd", slap_auxprop_init );
   1283 	if( rc != SASL_OK ) {
   1284 		Debug( LDAP_DEBUG_ANY, "slap_sasl_init: auxprop add plugin failed\n" );
   1285 		return -1;
   1286 	}
   1287 
   1288 	/* should provide callbacks for logging */
   1289 	/* server name should be configurable */
   1290 	rc = sasl_server_init( server_callbacks, "slapd" );
   1291 
   1292 	if( rc != SASL_OK ) {
   1293 		Debug( LDAP_DEBUG_ANY, "slap_sasl_init: server init failed\n" );
   1294 
   1295 		return -1;
   1296 	}
   1297 
   1298 #ifdef SLAPD_SPASSWD
   1299 	lutil_passwd_add( &sasl_pwscheme, chk_sasl, NULL );
   1300 #endif
   1301 
   1302 	Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n" );
   1303 
   1304 	/* default security properties */
   1305 	memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
   1306 	sasl_secprops.max_ssf = INT_MAX;
   1307 	sasl_secprops.maxbufsize = 65536;
   1308 	sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
   1309 #endif
   1310 
   1311 	return 0;
   1312 }
   1313 
   1314 int slap_sasl_destroy( void )
   1315 {
   1316 #ifdef HAVE_CYRUS_SASL
   1317 	sasl_done();
   1318 
   1319 #ifdef SLAP_AUXPROP_DONTUSECOPY
   1320 	if ( slap_dontUseCopy_propnames ) {
   1321 		ber_bvarray_free( slap_dontUseCopy_propnames );
   1322 		slap_dontUseCopy_propnames = NULL;
   1323 	}
   1324 #endif /* SLAP_AUXPROP_DONTUSECOPY */
   1325 #endif
   1326 	free( sasl_host );
   1327 	sasl_host = NULL;
   1328 	free( sasl_cbinding );
   1329 	sasl_cbinding = NULL;
   1330 
   1331 	return 0;
   1332 }
   1333 
   1334 static char *
   1335 slap_sasl_peer2ipport( struct berval *peer )
   1336 {
   1337 	int		isv6 = 0;
   1338 	char		*ipport, *p,
   1339 			*addr = &peer->bv_val[ STRLENOF( "IP=" ) ];
   1340 	ber_len_t	plen = peer->bv_len - STRLENOF( "IP=" );
   1341 
   1342 	/* IPv6? */
   1343 	if ( addr[0] == '[' ) {
   1344 		isv6 = 1;
   1345 		plen--;
   1346 	}
   1347 	ipport = ch_strdup( &addr[isv6] );
   1348 
   1349 	/* Convert IPv6/IPv4 addresses to address;port syntax. */
   1350 	p = strrchr( ipport, ':' );
   1351 	if ( p != NULL ) {
   1352 		*p = ';';
   1353 		if ( isv6 ) {
   1354 			assert( p[-1] == ']' );
   1355 			AC_MEMCPY( &p[-1], p, plen - ( p - ipport ) + 1 );
   1356 		}
   1357 
   1358 	} else if ( isv6 ) {
   1359 		/* trim ']' */
   1360 		plen--;
   1361 		assert( addr[plen] == ']' );
   1362 		addr[plen] = '\0';
   1363 	}
   1364 
   1365 	return ipport;
   1366 }
   1367 
   1368 int slap_sasl_open( Connection *conn, int reopen )
   1369 {
   1370 	int sc = LDAP_SUCCESS;
   1371 #ifdef HAVE_CYRUS_SASL
   1372 	int cb;
   1373 
   1374 	sasl_conn_t *ctx = NULL;
   1375 	sasl_callback_t *session_callbacks;
   1376 	char *ipremoteport = NULL, *iplocalport = NULL;
   1377 
   1378 	assert( conn->c_sasl_authctx == NULL );
   1379 
   1380 	if ( !reopen ) {
   1381 		assert( conn->c_sasl_extra == NULL );
   1382 
   1383 		session_callbacks =
   1384 			SLAP_CALLOC( 5, sizeof(sasl_callback_t));
   1385 		if( session_callbacks == NULL ) {
   1386 			Debug( LDAP_DEBUG_ANY,
   1387 				"slap_sasl_open: SLAP_MALLOC failed" );
   1388 			return -1;
   1389 		}
   1390 		conn->c_sasl_extra = session_callbacks;
   1391 
   1392 		session_callbacks[cb=0].id = SASL_CB_LOG;
   1393 		session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_log;
   1394 		session_callbacks[cb++].context = conn;
   1395 
   1396 		session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
   1397 		session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_authorize;
   1398 		session_callbacks[cb++].context = conn;
   1399 
   1400 		session_callbacks[cb].id = SASL_CB_CANON_USER;
   1401 		session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_canonicalize;
   1402 		session_callbacks[cb++].context = conn;
   1403 
   1404 		session_callbacks[cb].id = SASL_CB_LIST_END;
   1405 		session_callbacks[cb].proc = NULL;
   1406 		session_callbacks[cb++].context = NULL;
   1407 	} else {
   1408 		session_callbacks = conn->c_sasl_extra;
   1409 	}
   1410 
   1411 	conn->c_sasl_layers = 0;
   1412 
   1413 	/* create new SASL context */
   1414 	if ( conn->c_sock_name.bv_len != 0 &&
   1415 		strncmp( conn->c_sock_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
   1416 	{
   1417 		iplocalport = slap_sasl_peer2ipport( &conn->c_sock_name );
   1418 	}
   1419 
   1420 	if ( conn->c_peer_name.bv_len != 0 &&
   1421 		strncmp( conn->c_peer_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
   1422 	{
   1423 		ipremoteport = slap_sasl_peer2ipport( &conn->c_peer_name );
   1424 	}
   1425 
   1426 	sc = sasl_server_new( "ldap", sasl_host, global_realm,
   1427 		iplocalport, ipremoteport, session_callbacks, SASL_SUCCESS_DATA, &ctx );
   1428 	if ( iplocalport != NULL ) {
   1429 		ch_free( iplocalport );
   1430 	}
   1431 	if ( ipremoteport != NULL ) {
   1432 		ch_free( ipremoteport );
   1433 	}
   1434 
   1435 	if( sc != SASL_OK ) {
   1436 		Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
   1437 			sc );
   1438 
   1439 		return -1;
   1440 	}
   1441 
   1442 	conn->c_sasl_authctx = ctx;
   1443 
   1444 	if( sc == SASL_OK ) {
   1445 		sc = sasl_setprop( ctx,
   1446 			SASL_SEC_PROPS, &sasl_secprops );
   1447 
   1448 		if( sc != SASL_OK ) {
   1449 			Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
   1450 				sc );
   1451 
   1452 			slap_sasl_close( conn );
   1453 			return -1;
   1454 		}
   1455 	}
   1456 
   1457 	sc = slap_sasl_err2ldap( sc );
   1458 
   1459 #elif defined(SLAP_BUILTIN_SASL)
   1460 	/* built-in SASL implementation */
   1461 	SASL_CTX *ctx = (SASL_CTX *) SLAP_MALLOC(sizeof(SASL_CTX));
   1462 	if( ctx == NULL ) return -1;
   1463 
   1464 	ctx->sc_external_ssf = 0;
   1465 	BER_BVZERO( &ctx->sc_external_id );
   1466 
   1467 	conn->c_sasl_authctx = ctx;
   1468 #endif
   1469 
   1470 	return sc;
   1471 }
   1472 
   1473 int slap_sasl_external(
   1474 	Connection *conn,
   1475 	slap_ssf_t ssf,
   1476 	struct berval *auth_id )
   1477 {
   1478 #ifdef HAVE_CYRUS_SASL
   1479 	int sc;
   1480 	sasl_conn_t *ctx = conn->c_sasl_authctx;
   1481 	sasl_ssf_t sasl_ssf = ssf;
   1482 
   1483 	if ( ctx == NULL ) {
   1484 		return LDAP_UNAVAILABLE;
   1485 	}
   1486 
   1487 	sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
   1488 
   1489 	if ( sc != SASL_OK ) {
   1490 		return LDAP_OTHER;
   1491 	}
   1492 
   1493 	sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL,
   1494 		auth_id ? auth_id->bv_val : NULL );
   1495 
   1496 	if ( sc != SASL_OK ) {
   1497 		return LDAP_OTHER;
   1498 	}
   1499 #elif defined(SLAP_BUILTIN_SASL)
   1500 	/* built-in SASL implementation */
   1501 	SASL_CTX *ctx = conn->c_sasl_authctx;
   1502 	if ( ctx == NULL ) return LDAP_UNAVAILABLE;
   1503 
   1504 	ctx->sc_external_ssf = ssf;
   1505 	if( auth_id ) {
   1506 		ctx->sc_external_id = *auth_id;
   1507 		BER_BVZERO( auth_id );
   1508 	} else {
   1509 		BER_BVZERO( &ctx->sc_external_id );
   1510 	}
   1511 #endif
   1512 
   1513 	return LDAP_SUCCESS;
   1514 }
   1515 
   1516 int slap_sasl_cbinding( Connection *conn, void *ssl )
   1517 {
   1518 #ifdef SASL_CHANNEL_BINDING
   1519 	void *cb;
   1520 	int i;
   1521 
   1522 	if ( sasl_cbinding == NULL )
   1523 		return LDAP_SUCCESS;
   1524 
   1525 	i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
   1526 	if ( i < 0 )
   1527 		return LDAP_SUCCESS;
   1528 
   1529 	cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
   1530 	if ( cb != NULL ) {
   1531 		sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
   1532 		conn->c_sasl_cbind = cb;
   1533 	}
   1534 #endif
   1535 	return LDAP_SUCCESS;
   1536 }
   1537 
   1538 int slap_sasl_reset( Connection *conn )
   1539 {
   1540 	return LDAP_SUCCESS;
   1541 }
   1542 
   1543 char ** slap_sasl_mechs( Connection *conn )
   1544 {
   1545 	char **mechs = NULL;
   1546 
   1547 #ifdef HAVE_CYRUS_SASL
   1548 	sasl_conn_t *ctx = conn->c_sasl_authctx;
   1549 
   1550 	if( ctx == NULL ) ctx = conn->c_sasl_sockctx;
   1551 
   1552 	if( ctx != NULL ) {
   1553 		int sc;
   1554 		SASL_CONST char *mechstr;
   1555 
   1556 		sc = sasl_listmech( ctx,
   1557 			NULL, NULL, ",", NULL,
   1558 			&mechstr, NULL, NULL );
   1559 
   1560 		if( sc != SASL_OK ) {
   1561 			Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
   1562 				sc );
   1563 
   1564 			return NULL;
   1565 		}
   1566 
   1567 		mechs = ldap_str2charray( mechstr, "," );
   1568 	}
   1569 #elif defined(SLAP_BUILTIN_SASL)
   1570 	/* builtin SASL implementation */
   1571 	SASL_CTX *ctx = conn->c_sasl_authctx;
   1572 	if ( ctx != NULL && ctx->sc_external_id.bv_val ) {
   1573 		/* should check ssf */
   1574 		mechs = ldap_str2charray( "EXTERNAL", "," );
   1575 	}
   1576 #endif
   1577 
   1578 	return mechs;
   1579 }
   1580 
   1581 int slap_sasl_close( Connection *conn )
   1582 {
   1583 #ifdef HAVE_CYRUS_SASL
   1584 	sasl_conn_t *ctx = conn->c_sasl_authctx;
   1585 
   1586 	if( ctx != NULL ) {
   1587 		sasl_dispose( &ctx );
   1588 	}
   1589 	if ( conn->c_sasl_sockctx &&
   1590 		conn->c_sasl_authctx != conn->c_sasl_sockctx )
   1591 	{
   1592 		ctx = conn->c_sasl_sockctx;
   1593 		sasl_dispose( &ctx );
   1594 	}
   1595 
   1596 	conn->c_sasl_authctx = NULL;
   1597 	conn->c_sasl_sockctx = NULL;
   1598 	conn->c_sasl_done = 0;
   1599 
   1600 	free( conn->c_sasl_extra );
   1601 	conn->c_sasl_extra = NULL;
   1602 
   1603 	free( conn->c_sasl_cbind );
   1604 	conn->c_sasl_cbind = NULL;
   1605 
   1606 #elif defined(SLAP_BUILTIN_SASL)
   1607 	SASL_CTX *ctx = conn->c_sasl_authctx;
   1608 	if( ctx ) {
   1609 		if( ctx->sc_external_id.bv_val ) {
   1610 			free( ctx->sc_external_id.bv_val );
   1611 			BER_BVZERO( &ctx->sc_external_id );
   1612 		}
   1613 		free( ctx );
   1614 		conn->c_sasl_authctx = NULL;
   1615 	}
   1616 #endif
   1617 
   1618 	return LDAP_SUCCESS;
   1619 }
   1620 
   1621 int slap_sasl_bind( Operation *op, SlapReply *rs )
   1622 {
   1623 #ifdef HAVE_CYRUS_SASL
   1624 	sasl_conn_t *ctx = op->o_conn->c_sasl_authctx;
   1625 	struct berval response;
   1626 	unsigned reslen = 0;
   1627 	int sc;
   1628 
   1629 	Debug(LDAP_DEBUG_ARGS,
   1630 		"==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n",
   1631 		op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "",
   1632 		op->o_conn->c_sasl_bind_in_progress ? "<continuing>" :
   1633 		op->o_conn->c_sasl_bind_mech.bv_val,
   1634 		op->orb_cred.bv_len );
   1635 
   1636 	if( ctx == NULL ) {
   1637 		send_ldap_error( op, rs, LDAP_UNAVAILABLE,
   1638 			"SASL unavailable on this session" );
   1639 		return rs->sr_err;
   1640 	}
   1641 
   1642 #define	START( ctx, mech, cred, clen, resp, rlen, err ) \
   1643 	sasl_server_start( ctx, mech, cred, clen, resp, rlen )
   1644 #define	STEP( ctx, cred, clen, resp, rlen, err ) \
   1645 	sasl_server_step( ctx, cred, clen, resp, rlen )
   1646 
   1647 	if ( !op->o_conn->c_sasl_bind_in_progress ) {
   1648 		/* If we already authenticated once, must use a new context */
   1649 		if ( op->o_conn->c_sasl_done ) {
   1650 			sasl_ssf_t ssf = 0;
   1651 			sasl_ssf_t *ssfp = NULL;
   1652 			const char *authid = NULL;
   1653 
   1654 			sasl_getprop( ctx, SASL_SSF_EXTERNAL, (void *)&ssfp );
   1655 			if ( ssfp ) ssf = *ssfp;
   1656 
   1657 			sasl_getprop( ctx, SASL_AUTH_EXTERNAL, (void *)&authid );
   1658 			if ( authid ) authid = ch_strdup( authid );
   1659 
   1660 			if ( ctx != op->o_conn->c_sasl_sockctx ) {
   1661 				sasl_dispose( &ctx );
   1662 			}
   1663 			op->o_conn->c_sasl_authctx = NULL;
   1664 
   1665 			slap_sasl_open( op->o_conn, 1 );
   1666 			ctx = op->o_conn->c_sasl_authctx;
   1667 			sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
   1668 			if ( authid ) {
   1669 				sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
   1670 				ch_free( (char *)authid );
   1671 			}
   1672 		}
   1673 		sc = START( ctx,
   1674 			op->o_conn->c_sasl_bind_mech.bv_val,
   1675 			op->orb_cred.bv_val, op->orb_cred.bv_len,
   1676 			(SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
   1677 
   1678 	} else {
   1679 		sc = STEP( ctx,
   1680 			op->orb_cred.bv_val, op->orb_cred.bv_len,
   1681 			(SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
   1682 	}
   1683 
   1684 	response.bv_len = reslen;
   1685 
   1686 	if ( sc == SASL_OK ) {
   1687 		sasl_ssf_t *ssf = NULL;
   1688 
   1689 		ber_dupbv_x( &op->orb_edn, &op->o_conn->c_sasl_dn, op->o_tmpmemctx );
   1690 		BER_BVZERO( &op->o_conn->c_sasl_dn );
   1691 		op->o_conn->c_sasl_done = 1;
   1692 
   1693 		rs->sr_err = LDAP_SUCCESS;
   1694 
   1695 		(void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
   1696 		op->orb_ssf = ssf ? *ssf : 0;
   1697 
   1698 		ctx = NULL;
   1699 		if( op->orb_ssf ) {
   1700 			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
   1701 			op->o_conn->c_sasl_layers++;
   1702 
   1703 			/* If there's an old layer, set sockctx to NULL to
   1704 			 * tell connection_read() to wait for us to finish.
   1705 			 * Otherwise there is a race condition: we have to
   1706 			 * send the Bind response using the old security
   1707 			 * context and then remove it before reading any
   1708 			 * new messages.
   1709 			 */
   1710 			if ( op->o_conn->c_sasl_sockctx ) {
   1711 				ctx = op->o_conn->c_sasl_sockctx;
   1712 				op->o_conn->c_sasl_sockctx = NULL;
   1713 			} else {
   1714 				op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
   1715 			}
   1716 			ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
   1717 		}
   1718 
   1719 		/* Must send response using old security layer */
   1720 		rs->sr_sasldata = (response.bv_len ? &response : NULL);
   1721 		send_ldap_sasl( op, rs );
   1722 
   1723 		/* Now dispose of the old security layer.
   1724 		 */
   1725 		if ( ctx ) {
   1726 			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
   1727 			ldap_pvt_sasl_remove( op->o_conn->c_sb );
   1728 			op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
   1729 			ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
   1730 			sasl_dispose( &ctx );
   1731 		}
   1732 	} else if ( sc == SASL_CONTINUE ) {
   1733 		rs->sr_err = LDAP_SASL_BIND_IN_PROGRESS,
   1734 		rs->sr_text = sasl_errdetail( ctx );
   1735 		rs->sr_sasldata = &response;
   1736 		send_ldap_sasl( op, rs );
   1737 
   1738 	} else {
   1739 		BER_BVZERO( &op->o_conn->c_sasl_dn );
   1740 		rs->sr_text = sasl_errdetail( ctx );
   1741 		rs->sr_err = slap_sasl_err2ldap( sc ),
   1742 		send_ldap_result( op, rs );
   1743 	}
   1744 
   1745 	Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rs->sr_err );
   1746 
   1747 #elif defined(SLAP_BUILTIN_SASL)
   1748 	/* built-in SASL implementation */
   1749 	SASL_CTX *ctx = op->o_conn->c_sasl_authctx;
   1750 
   1751 	if ( ctx == NULL ) {
   1752 		send_ldap_error( op, rs, LDAP_OTHER,
   1753 			"Internal SASL Error" );
   1754 
   1755 	} else if ( bvmatch( &ext_bv, &op->o_conn->c_sasl_bind_mech ) ) {
   1756 		/* EXTERNAL */
   1757 
   1758 		if( op->orb_cred.bv_len ) {
   1759 			rs->sr_text = "proxy authorization not supported";
   1760 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
   1761 			send_ldap_result( op, rs );
   1762 
   1763 		} else {
   1764 			op->orb_edn = ctx->sc_external_id;
   1765 			rs->sr_err = LDAP_SUCCESS;
   1766 			rs->sr_sasldata = NULL;
   1767 			send_ldap_sasl( op, rs );
   1768 		}
   1769 
   1770 	} else {
   1771 		send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
   1772 			"requested SASL mechanism not supported" );
   1773 	}
   1774 #else
   1775 	send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
   1776 		"SASL not supported" );
   1777 #endif
   1778 
   1779 	return rs->sr_err;
   1780 }
   1781 
   1782 char* slap_sasl_secprops( const char *in )
   1783 {
   1784 #ifdef HAVE_CYRUS_SASL
   1785 	int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
   1786 
   1787 	return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
   1788 #else
   1789 	return "SASL not supported";
   1790 #endif
   1791 }
   1792 
   1793 void slap_sasl_secprops_unparse( struct berval *bv )
   1794 {
   1795 #ifdef HAVE_CYRUS_SASL
   1796 	ldap_pvt_sasl_secprops_unparse( &sasl_secprops, bv );
   1797 #endif
   1798 }
   1799 
   1800 #ifdef HAVE_CYRUS_SASL
   1801 int
   1802 slap_sasl_setpass( Operation *op, SlapReply *rs )
   1803 {
   1804 	struct berval id = BER_BVNULL;	/* needs to come from connection */
   1805 	struct berval new = BER_BVNULL;
   1806 	struct berval old = BER_BVNULL;
   1807 
   1808 	assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
   1809 
   1810 	rs->sr_err = sasl_getprop( op->o_conn->c_sasl_authctx, SASL_USERNAME,
   1811 		(SASL_CONST void **)(char *)&id.bv_val );
   1812 
   1813 	if( rs->sr_err != SASL_OK ) {
   1814 		rs->sr_text = "unable to retrieve SASL username";
   1815 		rs->sr_err = LDAP_OTHER;
   1816 		goto done;
   1817 	}
   1818 
   1819 	Debug( LDAP_DEBUG_ARGS, "==> slap_sasl_setpass: \"%s\"\n",
   1820 		id.bv_val ? id.bv_val : "" );
   1821 
   1822 	rs->sr_err = slap_passwd_parse( op->ore_reqdata,
   1823 		NULL, &old, &new, &rs->sr_text );
   1824 
   1825 	if( rs->sr_err != LDAP_SUCCESS ) {
   1826 		goto done;
   1827 	}
   1828 
   1829 	if( new.bv_len == 0 ) {
   1830 		slap_passwd_generate(&new);
   1831 
   1832 		if( new.bv_len == 0 ) {
   1833 			rs->sr_text = "password generation failed.";
   1834 			rs->sr_err = LDAP_OTHER;
   1835 			goto done;
   1836 		}
   1837 
   1838 		rs->sr_rspdata = slap_passwd_return( &new );
   1839 	}
   1840 
   1841 	rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx, id.bv_val,
   1842 		new.bv_val, new.bv_len, old.bv_val, old.bv_len, 0 );
   1843 	if( rs->sr_err != SASL_OK ) {
   1844 		rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_authctx );
   1845 	}
   1846 	switch(rs->sr_err) {
   1847 		case SASL_OK:
   1848 			rs->sr_err = LDAP_SUCCESS;
   1849 			break;
   1850 
   1851 		case SASL_NOCHANGE:
   1852 		case SASL_NOMECH:
   1853 		case SASL_DISABLED:
   1854 		case SASL_PWLOCK:
   1855 		case SASL_FAIL:
   1856 		case SASL_BADPARAM:
   1857 		default:
   1858 			rs->sr_err = LDAP_OTHER;
   1859 	}
   1860 
   1861 done:
   1862 	return rs->sr_err;
   1863 }
   1864 #endif /* HAVE_CYRUS_SASL */
   1865 
   1866 /* Take any sort of identity string and return a DN with the "dn:" prefix. The
   1867  * string returned in *dn is in its own allocated memory, and must be free'd
   1868  * by the calling process.  -Mark Adamson, Carnegie Mellon
   1869  *
   1870  * The "dn:" prefix is no longer used anywhere inside slapd. It is only used
   1871  * on strings passed in directly from SASL.  -Howard Chu, Symas Corp.
   1872  */
   1873 
   1874 #define SET_NONE	0
   1875 #define	SET_DN		1
   1876 #define	SET_U		2
   1877 
   1878 int slap_sasl_getdn( Connection *conn, Operation *op, struct berval *id,
   1879 	char *user_realm, struct berval *dn, int flags )
   1880 {
   1881 	int rc, is_dn = SET_NONE, do_norm = 1;
   1882 	struct berval dn2, *mech;
   1883 
   1884 	assert( conn != NULL );
   1885 	assert( id != NULL );
   1886 
   1887 	Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: conn %lu id=%s [len=%lu]\n",
   1888 		conn->c_connid,
   1889 		BER_BVISNULL( id ) ? "NULL" : ( BER_BVISEMPTY( id ) ? "<empty>" : id->bv_val ),
   1890 		BER_BVISNULL( id ) ? 0 : ( BER_BVISEMPTY( id ) ? 0 :
   1891 		                           (unsigned long) id->bv_len ) );
   1892 
   1893 	if ( !op ) {
   1894 		op = conn->c_sasl_bindop;
   1895 	}
   1896 	assert( op != NULL );
   1897 
   1898 	BER_BVZERO( dn );
   1899 
   1900 	if ( !BER_BVISNULL( id ) ) {
   1901 		/* Blatantly anonymous ID */
   1902 		static struct berval bv_anonymous = BER_BVC( "anonymous" );
   1903 
   1904 		if ( ber_bvstrcasecmp( id, &bv_anonymous ) == 0 ) {
   1905 			return( LDAP_SUCCESS );
   1906 		}
   1907 
   1908 	} else {
   1909 		/* FIXME: if empty, should we stop? */
   1910 		BER_BVSTR( id, "" );
   1911 	}
   1912 
   1913 	if ( !BER_BVISEMPTY( &conn->c_sasl_bind_mech ) ) {
   1914 		mech = &conn->c_sasl_bind_mech;
   1915 	} else {
   1916 		mech = &conn->c_authmech;
   1917 	}
   1918 
   1919 	/* An authcID needs to be converted to authzID form. Set the
   1920 	 * values directly into *dn; they will be normalized later. (and
   1921 	 * normalizing always makes a new copy.) An ID from a TLS certificate
   1922 	 * is already normalized, so copy it and skip normalization.
   1923 	 */
   1924 	if( flags & SLAP_GETDN_AUTHCID ) {
   1925 		if( bvmatch( mech, &ext_bv )) {
   1926 			/* EXTERNAL DNs are already normalized */
   1927 			assert( !BER_BVISNULL( id ) );
   1928 
   1929 			do_norm = 0;
   1930 			is_dn = SET_DN;
   1931 			ber_dupbv_x( dn, id, op->o_tmpmemctx );
   1932 
   1933 		} else {
   1934 			/* convert to u:<username> form */
   1935 			is_dn = SET_U;
   1936 			*dn = *id;
   1937 		}
   1938 	}
   1939 
   1940 	if( is_dn == SET_NONE ) {
   1941 		if( !strncasecmp( id->bv_val, "u:", STRLENOF( "u:" ) ) ) {
   1942 			is_dn = SET_U;
   1943 			dn->bv_val = id->bv_val + STRLENOF( "u:" );
   1944 			dn->bv_len = id->bv_len - STRLENOF( "u:" );
   1945 
   1946 		} else if ( !strncasecmp( id->bv_val, "dn:", STRLENOF( "dn:" ) ) ) {
   1947 			is_dn = SET_DN;
   1948 			dn->bv_val = id->bv_val + STRLENOF( "dn:" );
   1949 			dn->bv_len = id->bv_len - STRLENOF( "dn:" );
   1950 		}
   1951 	}
   1952 
   1953 	/* No other possibilities from here */
   1954 	if( is_dn == SET_NONE ) {
   1955 		BER_BVZERO( dn );
   1956 		return( LDAP_INAPPROPRIATE_AUTH );
   1957 	}
   1958 
   1959 	/* Username strings */
   1960 	if( is_dn == SET_U ) {
   1961 		/* ITS#3419: values may need escape */
   1962 		LDAPRDN		DN[ 5 ];
   1963 		LDAPAVA 	*RDNs[ 4 ][ 2 ];
   1964 		LDAPAVA 	AVAs[ 4 ];
   1965 		int		irdn;
   1966 
   1967 		irdn = 0;
   1968 		DN[ irdn ] = RDNs[ irdn ];
   1969 		RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
   1970 		AVAs[ irdn ].la_attr = slap_schema.si_ad_uid->ad_cname;
   1971 		AVAs[ irdn ].la_value = *dn;
   1972 		AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
   1973 		AVAs[ irdn ].la_private = NULL;
   1974 		RDNs[ irdn ][ 1 ] = NULL;
   1975 
   1976 		if ( user_realm && *user_realm ) {
   1977 			irdn++;
   1978 			DN[ irdn ] = RDNs[ irdn ];
   1979 			RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
   1980 			AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
   1981 			ber_str2bv( user_realm, 0, 0, &AVAs[ irdn ].la_value );
   1982 			AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
   1983 			AVAs[ irdn ].la_private = NULL;
   1984 			RDNs[ irdn ][ 1 ] = NULL;
   1985 		}
   1986 
   1987 		if ( !BER_BVISNULL( mech ) ) {
   1988 			irdn++;
   1989 			DN[ irdn ] = RDNs[ irdn ];
   1990 			RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
   1991 			AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
   1992 			AVAs[ irdn ].la_value = *mech;
   1993 			AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
   1994 			AVAs[ irdn ].la_private = NULL;
   1995 			RDNs[ irdn ][ 1 ] = NULL;
   1996 		}
   1997 
   1998 		irdn++;
   1999 		DN[ irdn ] = RDNs[ irdn ];
   2000 		RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
   2001 		AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
   2002 		BER_BVSTR( &AVAs[ irdn ].la_value, "auth" );
   2003 		AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
   2004 		AVAs[ irdn ].la_private = NULL;
   2005 		RDNs[ irdn ][ 1 ] = NULL;
   2006 
   2007 		irdn++;
   2008 		DN[ irdn ] = NULL;
   2009 
   2010 		rc = ldap_dn2bv_x( DN, dn, LDAP_DN_FORMAT_LDAPV3,
   2011 				op->o_tmpmemctx );
   2012 		if ( rc != LDAP_SUCCESS ) {
   2013 			BER_BVZERO( dn );
   2014 			return rc;
   2015 		}
   2016 
   2017 		Debug( LDAP_DEBUG_TRACE,
   2018 			"slap_sasl_getdn: u:id converted to %s\n",
   2019 			dn->bv_val );
   2020 
   2021 	} else {
   2022 
   2023 		/* Dup the DN in any case, so we don't risk
   2024 		 * leaks or dangling pointers later,
   2025 		 * and the DN value is '\0' terminated */
   2026 		ber_dupbv_x( &dn2, dn, op->o_tmpmemctx );
   2027 		dn->bv_val = dn2.bv_val;
   2028 	}
   2029 
   2030 	/* All strings are in DN form now. Normalize if needed. */
   2031 	if ( do_norm ) {
   2032 		rc = dnNormalize( 0, NULL, NULL, dn, &dn2, op->o_tmpmemctx );
   2033 
   2034 		/* User DNs were constructed above and must be freed now */
   2035 		slap_sl_free( dn->bv_val, op->o_tmpmemctx );
   2036 
   2037 		if ( rc != LDAP_SUCCESS ) {
   2038 			BER_BVZERO( dn );
   2039 			return rc;
   2040 		}
   2041 		*dn = dn2;
   2042 	}
   2043 
   2044 	/* Run thru regexp */
   2045 	slap_sasl2dn( op, dn, &dn2, flags );
   2046 	if( !BER_BVISNULL( &dn2 ) ) {
   2047 		slap_sl_free( dn->bv_val, op->o_tmpmemctx );
   2048 		*dn = dn2;
   2049 		Debug( LDAP_DEBUG_TRACE,
   2050 			"slap_sasl_getdn: dn:id converted to %s\n",
   2051 			dn->bv_val );
   2052 	}
   2053 
   2054 	return( LDAP_SUCCESS );
   2055 }
   2056