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