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