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