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