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