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