1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #ifdef LDAP_SASLIO_HOOKS
27 #include <assert.h>
28 #include "ldap-int.h"
29 #include "../ber/lber-int.h"
30 #include <sasl/sasl.h>
31 #include <thread.h>
32 #include <synch.h>
33 
34 #define SEARCH_TIMEOUT_SECS	120
35 #define NSLDAPI_SM_BUF	128
36 
37 extern void *sasl_create_context(void);
38 extern void sasl_free_context(void *ctx);
39 extern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks);
40 extern int _sasl_client_new(void *ctx, const char *service,
41 	const char *serverFQDN, const char *iplocalport,
42 	const char *ipremoteport,
43 	const sasl_callback_t *prompt_supp,
44 	unsigned flags, sasl_conn_t **pconn);
45 extern int _sasl_server_init(void *ctx,
46 	const sasl_callback_t *callbacks, const char *appname);
47 extern int _sasl_server_new(void *ctx, const char *service,
48 	const char *serverFQDN, const char *user_realm,
49 	const char *iplocalport, const char *ipremoteport,
50 	const sasl_callback_t *callbacks,
51 	unsigned flags, sasl_conn_t **pconn);
52 
53 static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb );
54 static void destroy_sasliobuf(Sockbuf *sb);
55 
56 /*
57  * SASL Dependent routines
58  *
59  * SASL security and integrity options are supported through the
60  * use of the extended I/O functionality.  Because the extended
61  * I/O functions may already be in use prior to enabling encryption,
62  * when SASL encryption is enabled, these routine interpose themselves
63  * over the existng extended I/O routines and add an additional level
64  * of indirection.
65  *  IE: Before SASL:  client->libldap->lber->extio
66  *      After  SASL:  client->libldap->lber->saslio->extio
67  * Any extio functions are still used for the raw i/O [IE prldap]
68  * but SASL will decrypt before passing to lber.
69  * SASL cannot decrypt a stream so full packets must be read
70  * before proceeding.
71  */
72 
73 static int nsldapi_sasl_fail()
74 {
75 	return( SASL_FAIL );
76 }
77 
78 /*
79  * Global SASL Init data
80  */
81 
82 static sasl_callback_t client_callbacks[] = {
83 	{ SASL_CB_GETOPT, nsldapi_sasl_fail, NULL },
84 	{ SASL_CB_GETREALM, NULL, NULL },
85 	{ SASL_CB_USER, NULL, NULL },
86 	{ SASL_CB_AUTHNAME, NULL, NULL },
87 	{ SASL_CB_PASS, NULL, NULL },
88 	{ SASL_CB_ECHOPROMPT, NULL, NULL },
89 	{ SASL_CB_NOECHOPROMPT, NULL, NULL },
90 	{ SASL_CB_LIST_END, NULL, NULL }
91 };
92 static mutex_t sasl_mutex = DEFAULTMUTEX;
93 static int nsldapi_sasl_inited = 0;
94 static void *gctx;  /* intentially not freed - avoid libsasl re-inits */
95 
96 int nsldapi_sasl_init( void )
97 {
98 	int	saslrc;
99 
100 	mutex_lock(&sasl_mutex);
101 	if ( nsldapi_sasl_inited ) {
102 		mutex_unlock(&sasl_mutex);
103 		return( 0 );
104 	}
105 	if ((gctx = (void *)sasl_create_context()) != NULL) {
106 		saslrc = _sasl_client_init(gctx, client_callbacks);
107 		if (saslrc == SASL_OK ) {
108 			nsldapi_sasl_inited = 1;
109 			mutex_unlock(&sasl_mutex);
110 			return( 0 );
111 		}
112 	}
113 	mutex_unlock(&sasl_mutex);
114 	return( -1 );
115 }
116 
117 /*
118  * SASL encryption routines
119  */
120 
121 /*
122  * Get the 4 octet header [size] for a sasl encrypted buffer.
123  * See RFC222 [section 3].
124  */
125 
126 static int
127 nsldapi_sasl_pktlen( char *buf, int maxbufsize )
128 {
129 	int	size;
130 
131 #if defined( _WINDOWS ) || defined( _WIN32 )
132 	size = ntohl(*(long *)buf);
133 #else
134 	size = ntohl(*(uint32_t *)buf);
135 #endif
136 
137 	if ( size < 0 || size > maxbufsize ) {
138 		return (-1 );
139 	}
140 
141 	return( size + 4 ); /* include the first 4 bytes */
142 }
143 
144 static int
145 nsldapi_sasl_read( int s, void *buf, int  len,
146 	struct lextiof_socket_private *arg)
147 {
148 	Sockbuf		*sb = (Sockbuf *)arg;
149 	LDAP		*ld;
150 	const char	*dbuf;
151 	char		*cp;
152 	int		ret;
153 	unsigned	dlen, blen;
154 
155 	if (sb == NULL) {
156 		return( -1 );
157 	}
158 
159 	ld = (LDAP *)sb->sb_sasl_prld;
160 	if (ld == NULL) {
161 		return( -1 );
162 	}
163 
164 	/* Is there anything left in the existing buffer? */
165 	if ((ret = sb->sb_sasl_ilen) > 0) {
166 		ret = (ret > len ? len : ret);
167 		SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
168 		if (ret == sb->sb_sasl_ilen) {
169 			sb->sb_sasl_ilen = 0;
170 			sb->sb_sasl_iptr = NULL;
171 		} else {
172 			sb->sb_sasl_ilen -= ret;
173 			sb->sb_sasl_iptr += ret;
174 		}
175 		return( ret );
176 	}
177 
178 	/* buffer is empty - fill it */
179 	cp = sb->sb_sasl_ibuf;
180 	dlen = 0;
181 
182 	/* Read the length of the packet */
183 	while ( dlen < 4 ) {
184 		if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
185 			ret = sb->sb_sasl_fns.lbextiofn_read(
186 				s, cp, 4 - dlen,
187 				sb->sb_sasl_fns.lbextiofn_socket_arg);
188 		} else {
189 			ret = read( sb->sb_sd, cp, 4 - dlen );
190 		}
191 #ifdef EINTR
192 		if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
193 			continue;
194 #endif
195 		if ( ret <= 0 )
196 			return( ret );
197 
198 		cp += ret;
199 		dlen += ret;
200 	}
201 
202 	blen = 4;
203 
204 	ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz );
205 	if (ret < 0) {
206 		LDAP_SET_ERRNO(ld, EIO);
207 		return( -1 );
208 	}
209 	dlen = ret - dlen;
210 
211 	/* read the rest of the encrypted packet */
212 	while ( dlen > 0 ) {
213 		if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
214 			ret = sb->sb_sasl_fns.lbextiofn_read(
215 				s, cp, dlen,
216 				sb->sb_sasl_fns.lbextiofn_socket_arg);
217 		} else {
218 			ret = read( sb->sb_sd, cp, dlen );
219 		}
220 
221 #ifdef EINTR
222 		if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
223 			continue;
224 #endif
225 		if ( ret <= 0 )
226 			return( ret );
227 
228 		cp += ret;
229 		blen += ret;
230 		dlen -= ret;
231    	}
232 
233 	/* Decode the packet */
234 	ret = sasl_decode( sb->sb_sasl_ctx,
235 			   sb->sb_sasl_ibuf, blen,
236 			   &dbuf, &dlen);
237 	if ( ret != SASL_OK ) {
238 		/* sb_sasl_read: failed to decode packet, drop it, error */
239 		sb->sb_sasl_iptr = NULL;
240 		sb->sb_sasl_ilen = 0;
241 		LDAP_SET_ERRNO(ld, EIO);
242 		return( -1 );
243 	}
244 
245 	/* copy decrypted packet to the input buffer */
246 	SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen );
247 	sb->sb_sasl_iptr = sb->sb_sasl_ibuf;
248 	sb->sb_sasl_ilen = dlen;
249 
250 	ret = (dlen > (unsigned) len ? len : dlen);
251 	SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
252 	if (ret == sb->sb_sasl_ilen) {
253 		sb->sb_sasl_ilen = 0;
254 		sb->sb_sasl_iptr = NULL;
255 	} else {
256 		sb->sb_sasl_ilen -= ret;
257 		sb->sb_sasl_iptr += ret;
258 	}
259 	return( ret );
260 }
261 
262 static int
263 nsldapi_sasl_write( int s, const void *buf, int  len,
264 	struct lextiof_socket_private *arg)
265 {
266 	Sockbuf		*sb = (Sockbuf *)arg;
267 	int		ret = 0;
268 	const char	*obuf, *optr, *cbuf = (const char *)buf;
269 	unsigned	olen, clen, tlen = 0;
270 	unsigned	*maxbuf;
271 
272 	if (sb == NULL) {
273 		return( -1 );
274 	}
275 
276 	ret = sasl_getprop(sb->sb_sasl_ctx, SASL_MAXOUTBUF,
277 					     (const void **)&maxbuf);
278 	if ( ret != SASL_OK ) {
279 		/* just a sanity check, should never happen */
280 		return( -1 );
281 	}
282 
283 	while (len > 0) {
284 		clen = (len > *maxbuf) ? *maxbuf : len;
285 		/* encode the next packet. */
286 		ret = sasl_encode( sb->sb_sasl_ctx, cbuf, clen, &obuf, &olen);
287 		if ( ret != SASL_OK ) {
288 			/* XXX Log error? "sb_sasl_write: failed to encode packet..." */
289 			return( -1 );
290 		}
291 		/* Write everything now, buffer is only good until next sasl_encode */
292 		optr = obuf;
293 		while (olen > 0) {
294 			if (sb->sb_sasl_fns.lbextiofn_write != NULL) {
295 				ret = sb->sb_sasl_fns.lbextiofn_write(
296 					s, optr, olen,
297 					sb->sb_sasl_fns.lbextiofn_socket_arg);
298 			} else {
299 				ret = write( sb->sb_sd, optr, olen);
300 			}
301 			if ( ret < 0 )
302 				return( ret );
303 			optr += ret;
304 			olen -= ret;
305 		}
306 		len -= clen;
307 		cbuf += clen;
308 		tlen += clen;
309 	}
310 	return( tlen );
311 }
312 
313 static int
314 nsldapi_sasl_poll(
315 	LDAP_X_PollFD fds[], int nfds, int timeout,
316 	struct lextiof_session_private *arg )
317 {
318 	Sockbuf		*sb = (Sockbuf *)arg;
319 	LDAP		*ld;
320 	int		i;
321 
322 	if (sb == NULL) {
323 		return( -1 );
324 	}
325 	ld = (LDAP *)sb->sb_sasl_prld;
326 	if (ld == NULL) {
327 		return( -1 );
328 	}
329 
330 	if (fds && nfds > 0) {
331 		for(i = 0; i < nfds; i++) {
332 			if (fds[i].lpoll_socketarg ==
333 			     (struct lextiof_socket_private *)sb) {
334 				fds[i].lpoll_socketarg =
335 					(struct lextiof_socket_private *)
336 					sb->sb_sasl_fns.lbextiofn_socket_arg;
337 			}
338 
339 		}
340 	}
341 	return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout,
342 		(void *)ld->ld_sasl_io_fns.lextiof_session_arg) );
343 }
344 
345 /* no encryption indirect routines */
346 
347 static int
348 nsldapi_sasl_ne_read( int s, void *buf, int  len,
349 	struct lextiof_socket_private *arg)
350 {
351 	Sockbuf		*sb = (Sockbuf *)arg;
352 
353 	if (sb == NULL) {
354 		return( -1 );
355 	}
356 
357 	return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len,
358 			sb->sb_sasl_fns.lbextiofn_socket_arg) );
359 }
360 
361 static int
362 nsldapi_sasl_ne_write( int s, const void *buf, int  len,
363 	struct lextiof_socket_private *arg)
364 {
365 	Sockbuf		*sb = (Sockbuf *)arg;
366 
367 	if (sb == NULL) {
368 		return( -1 );
369 	}
370 
371 	return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len,
372 			sb->sb_sasl_fns.lbextiofn_socket_arg) );
373 }
374 
375 static int
376 nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg )
377 {
378 	Sockbuf		*sb = (Sockbuf *)arg;
379 	LDAP		*ld;
380 
381 	if (sb == NULL) {
382 		return( -1 );
383 	}
384 	ld = (LDAP *)sb->sb_sasl_prld;
385 	if (ld == NULL) {
386 		return( -1 );
387 	}
388 	/* undo function pointer interposing */
389 	ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns );
390 	ber_sockbuf_set_option( sb,
391 			LBER_SOCKBUF_OPT_EXT_IO_FNS,
392 			(void *)&sb->sb_sasl_fns);
393 
394 	/* undo SASL */
395 	nsldapi_sasl_close( ld, sb );
396 
397 	return ( ld->ld_sasl_io_fns.lextiof_close( s,
398 		(struct lextiof_socket_private *)
399 		sb->sb_sasl_fns.lbextiofn_socket_arg ) );
400 }
401 
402 /*
403  * install encryption routines if security has been negotiated
404  */
405 static int
406 nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf)
407 {
408 	struct lber_x_ext_io_fns	fns;
409 	struct ldap_x_ext_io_fns	iofns;
410 	sasl_security_properties_t 	*secprops;
411 	int	rc, value;
412 	int	bufsiz;
413 	int	encrypt = 0;
414 
415 	if (ssf && *ssf) {
416 		encrypt = 1;
417 	}
418 	if ( sb == NULL ) {
419 		return( LDAP_LOCAL_ERROR );
420 	}
421 	rc = ber_sockbuf_get_option( sb,
422 			LBER_SOCKBUF_OPT_TO_FILE_ONLY,
423 			(void *) &value);
424 	if (rc != 0 || value != 0)
425 		return( LDAP_LOCAL_ERROR );
426 
427 	if (encrypt) {
428 		/* initialize input buffer - use MAX SIZE to avoid reallocs */
429 		sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg;
430 		rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS,
431 				   (const void **)&secprops );
432 		if (rc != SASL_OK)
433 			return( LDAP_LOCAL_ERROR );
434 		bufsiz = secprops->maxbufsize;
435 		if (bufsiz <= 0) {
436 			return( LDAP_LOCAL_ERROR );
437 		}
438 		if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) {
439 			return( LDAP_LOCAL_ERROR );
440 		}
441 		sb->sb_sasl_iptr = NULL;
442 		sb->sb_sasl_bfsz = bufsiz;
443 		sb->sb_sasl_ilen = 0;
444 	}
445 
446 	/* Reset Session then Socket Args */
447 	/* Get old values */
448 	(void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE);
449 	sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
450 	rc = ber_sockbuf_get_option( sb,
451 			LBER_SOCKBUF_OPT_EXT_IO_FNS,
452 			(void *)&sb->sb_sasl_fns);
453 	if (rc != 0) {
454 		destroy_sasliobuf(sb);
455 		return( LDAP_LOCAL_ERROR );
456 	}
457 	memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns));
458 	ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
459 	rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
460 			      &ld->ld_sasl_io_fns );
461 	if (rc != 0 ) {
462 		destroy_sasliobuf(sb);
463 		return( LDAP_LOCAL_ERROR );
464 	}
465 
466 	/* Set new values */
467 	if (  ld->ld_sasl_io_fns.lextiof_read != NULL ||
468 	      ld->ld_sasl_io_fns.lextiof_write != NULL ||
469 	      ld->ld_sasl_io_fns.lextiof_poll != NULL ||
470 	      ld->ld_sasl_io_fns.lextiof_connect != NULL ||
471 	      ld->ld_sasl_io_fns.lextiof_close != NULL ) {
472 		memset( &iofns, 0, sizeof(iofns));
473 		iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
474 		if (encrypt) {
475 			iofns.lextiof_read = nsldapi_sasl_read;
476 			iofns.lextiof_write = nsldapi_sasl_write;
477 			iofns.lextiof_poll = nsldapi_sasl_poll;
478 		} else {
479 			iofns.lextiof_read = nsldapi_sasl_ne_read;
480 			iofns.lextiof_write = nsldapi_sasl_ne_write;
481 			iofns.lextiof_poll = nsldapi_sasl_poll;
482 		}
483 		iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect;
484 		iofns.lextiof_close = nsldapi_sasl_close_socket;
485 		iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle;
486 		iofns.lextiof_disposehandle =
487 				ld->ld_sasl_io_fns.lextiof_disposehandle;
488 		iofns.lextiof_session_arg =
489 				(void *) sb;
490 				/* ld->ld_sasl_io_fns.lextiof_session_arg; */
491 		rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
492 			      &iofns );
493 		if (rc != 0 ) {
494 			destroy_sasliobuf(sb);
495 			return( LDAP_LOCAL_ERROR );
496 		}
497 		sb->sb_sasl_prld = (void *)ld;
498 	}
499 
500 	if (encrypt) {
501 		(void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE);
502 		fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
503 		fns.lbextiofn_read = nsldapi_sasl_read;
504 		fns.lbextiofn_write = nsldapi_sasl_write;
505 		fns.lbextiofn_socket_arg =
506 			(void *) sb;
507 			/* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */
508 		rc = ber_sockbuf_set_option( sb,
509 				LBER_SOCKBUF_OPT_EXT_IO_FNS,
510 				(void *)&fns);
511 		if (rc != 0) {
512 			destroy_sasliobuf(sb);
513 			return( LDAP_LOCAL_ERROR );
514 		}
515 	}
516 
517 	return( LDAP_SUCCESS );
518 }
519 
520 static int
521 nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg )
522 {
523 	int rc = LDAP_LOCAL_ERROR;
524 
525 	switch (err) {
526 	case SASL_OK:
527 		rc = LDAP_SUCCESS;
528 		break;
529 	case SASL_NOMECH:
530 		rc = LDAP_AUTH_UNKNOWN;
531 		break;
532 	case SASL_BADSERV:
533 		rc = LDAP_CONNECT_ERROR;
534 		break;
535 	case SASL_DISABLED:
536 	case SASL_ENCRYPT:
537 	case SASL_EXPIRED:
538 	case SASL_NOUSERPASS:
539 	case SASL_NOVERIFY:
540 	case SASL_PWLOCK:
541 	case SASL_TOOWEAK:
542 	case SASL_UNAVAIL:
543 	case SASL_WEAKPASS:
544 		rc = LDAP_INAPPROPRIATE_AUTH;
545 		break;
546 	case SASL_BADAUTH:
547 	case SASL_NOAUTHZ:
548 		rc = LDAP_INVALID_CREDENTIALS;
549 		break;
550 	case SASL_NOMEM:
551 		rc = LDAP_NO_MEMORY;
552 		break;
553 	case SASL_NOUSER:
554 		rc = LDAP_NO_SUCH_OBJECT;
555 		break;
556 	case SASL_CONTINUE:
557 	case SASL_FAIL:
558 	case SASL_INTERACT:
559 	default:
560 		rc = LDAP_LOCAL_ERROR;
561 		break;
562 	}
563 
564 	LDAP_SET_LDERRNO( ld, rc, NULL, msg );
565 	return( rc );
566 }
567 
568 int
569 nsldapi_sasl_open(LDAP *ld)
570 {
571 	Sockbuf *sb;
572 	char * host;
573 	int saslrc;
574 	sasl_conn_t *ctx = NULL;
575 
576 	if (ld == NULL) {
577 		return( LDAP_LOCAL_ERROR );
578 	}
579 
580 	if (ld->ld_defconn == NULL) {
581 		LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
582 		return( LDAP_LOCAL_ERROR );
583 	}
584 	sb = ld->ld_defconn->lconn_sb;
585 	host = ld->ld_defhost;
586 
587 	if ( sb == NULL || host == NULL ) {
588 		LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
589 		return( LDAP_LOCAL_ERROR );
590 	}
591 
592 	if (sb->sb_sasl_ctx) {
593 	    sasl_dispose(&sb->sb_sasl_ctx);
594 	    sb->sb_sasl_ctx = NULL;
595 	}
596 
597 	/* SASL is not properly initialized */
598 	mutex_lock(&sasl_mutex);
599 	if (gctx == NULL) {
600 		LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
601 		mutex_unlock(&sasl_mutex);
602 		return( LDAP_LOCAL_ERROR );
603 	}
604 
605 	saslrc = _sasl_client_new(gctx, "ldap", host,
606 		NULL, NULL, /* iplocal ipremote strings currently unused */
607 		NULL, 0, &ctx );
608 
609 	if ( saslrc != SASL_OK ) {
610 		mutex_unlock(&sasl_mutex);
611 		sasl_dispose(&ctx);
612 		return( nsldapi_sasl_cvterrno( ld, saslrc, NULL ) );
613 	}
614 
615 	sb->sb_sasl_ctx = (void *)ctx;
616 	mutex_unlock(&sasl_mutex);
617 
618 	return( LDAP_SUCCESS );
619 }
620 
621 static void
622 destroy_sasliobuf(Sockbuf *sb)
623 {
624 	if (sb != NULL && sb->sb_sasl_ibuf != NULL) {
625 		NSLDAPI_FREE(sb->sb_sasl_ibuf);
626 		sb->sb_sasl_ibuf = NULL;
627 		sb->sb_sasl_iptr = NULL;
628 		sb->sb_sasl_bfsz = 0;
629 		sb->sb_sasl_ilen = 0;
630 	}
631 }
632 
633 static int
634 nsldapi_sasl_close( LDAP *ld, Sockbuf *sb )
635 {
636 	sasl_conn_t	*ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
637 
638 	destroy_sasliobuf(sb);
639 
640 	if( ctx != NULL ) {
641 		sasl_dispose( &ctx );
642 		sb->sb_sasl_ctx = NULL;
643 	}
644 	return( LDAP_SUCCESS );
645 }
646 
647 static int
648 nsldapi_sasl_do_bind( LDAP *ld, const char *dn,
649 	const char *mechs, unsigned flags,
650 	LDAP_SASL_INTERACT_PROC *callback, void *defaults,
651 	LDAPControl **sctrl, LDAPControl **cctrl )
652 {
653 	sasl_interact_t *prompts = NULL;
654 	sasl_conn_t	*ctx;
655 	sasl_ssf_t	*ssf = NULL;
656 	const char	*mech = NULL;
657 	int		saslrc, rc;
658 	struct berval	ccred;
659 	unsigned	credlen;
660 	int		stepnum = 1;
661 	char *sasl_username = NULL;
662 
663 	if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
664 		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
665 		return( LDAP_NOT_SUPPORTED );
666 	}
667 
668 	/* shouldn't happen */
669 	if (callback == NULL) {
670 		return( LDAP_LOCAL_ERROR );
671 	}
672 
673 	if ( ld->ld_defconn == NULL ||
674 	     ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) {
675 		rc = nsldapi_open_ldap_defconn( ld );
676 		if( rc < 0 )  {
677 			return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
678 		}
679 	}
680 
681 	/* should have a valid ld connection - now create sasl connection */
682 	if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) {
683 		LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
684 		return( rc );
685 	}
686 
687 	/* expect context to be initialized when connection is open */
688 	ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx;
689 
690 	if( ctx == NULL ) {
691 		LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
692 		return( LDAP_LOCAL_ERROR );
693 	}
694 
695 	/* (re)set security properties */
696 	sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops );
697 
698 	ccred.bv_val = NULL;
699 	ccred.bv_len = 0;
700 
701 	LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
702 		  (mech ? mech : ""), 0, 0 );
703 
704 	do {
705 		saslrc = sasl_client_start( ctx,
706 			mechs,
707 			&prompts,
708 			(const char **)&ccred.bv_val,
709 			&credlen,
710 			&mech );
711 
712 		LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
713 			  stepnum, (mech ? mech : ""), 0 );
714 		stepnum++;
715 
716 		if( saslrc == SASL_INTERACT &&
717 		    (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
718 			break;
719 		}
720 	} while ( saslrc == SASL_INTERACT );
721 
722 	ccred.bv_len = credlen;
723 
724 	if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
725 		return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
726 	}
727 
728 	stepnum = 1;
729 
730 	do {
731 		struct berval *scred;
732 		int clientstepnum = 1;
733 
734 		scred = NULL;
735 
736 		LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
737                           stepnum, (mech ? mech : ""), 0 );
738                 stepnum++;
739 
740 		/* notify server of a sasl bind step */
741 		rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
742 				      sctrl, cctrl, &scred);
743 
744 		if ( ccred.bv_val != NULL ) {
745 			ccred.bv_val = NULL;
746 		}
747 
748 		if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
749 			ber_bvfree( scred );
750 			return( rc );
751 		}
752 
753 		if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
754 			/* we're done, no need to step */
755 			if( scred ) {
756 			    if (scred->bv_len  == 0 ) { /* MS AD sends back empty screds */
757 				LDAPDebug(LDAP_DEBUG_ANY,
758 					  "SASL BIND complete - ignoring empty credential response\n",
759 					  0, 0, 0);
760 				ber_bvfree( scred );
761 			    } else {
762 				/* but server provided us with data! */
763 				LDAPDebug(LDAP_DEBUG_TRACE,
764 					  "SASL BIND complete but invalid because server responded with credentials - length [%u]\n",
765 					  scred->bv_len, 0, 0);
766 				ber_bvfree( scred );
767 				LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
768 				    NULL, nsldapi_strdup( dgettext(TEXT_DOMAIN,
769 				    "Error during SASL handshake - "
770 				    "invalid server credential response") ));
771 				return( LDAP_LOCAL_ERROR );
772 			    }
773 			}
774 			break;
775 		}
776 
777 		/* perform the next step of the sasl bind */
778 		do {
779 			LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n",
780 				  clientstepnum, stepnum, (mech ? mech : "") );
781 			clientstepnum++;
782 			saslrc = sasl_client_step( ctx,
783 				(scred == NULL) ? NULL : scred->bv_val,
784 				(scred == NULL) ? 0 : scred->bv_len,
785 				&prompts,
786 				(const char **)&ccred.bv_val,
787 				&credlen );
788 
789 			if( saslrc == SASL_INTERACT &&
790 			    (callback)(ld, flags, defaults, prompts)
791 							!= LDAP_SUCCESS ) {
792 				break;
793 			}
794 		} while ( saslrc == SASL_INTERACT );
795 
796 		ccred.bv_len = credlen;
797 		ber_bvfree( scred );
798 
799 		if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
800 			return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
801 		}
802 	} while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
803 
804 	if ( rc != LDAP_SUCCESS ) {
805 		return( rc );
806 	}
807 
808 	if ( saslrc != SASL_OK ) {
809 		return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
810 	}
811 
812 	saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username );
813 	if ( (saslrc == SASL_OK) && sasl_username ) {
814 		LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0);
815 	}
816 
817 	saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
818 	if( saslrc == SASL_OK ) {
819 		if( ssf && *ssf ) {
820 			LDAPDebug(LDAP_DEBUG_TRACE,
821 				"SASL install encryption, for SSF: %lu\n",
822 				(unsigned long) *ssf, 0, 0 );
823 		}
824 		rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf);
825 	}
826 
827 	return( rc );
828 }
829 
830 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
831 /*
832  * Get available SASL Mechanisms supported by the server
833  */
834 
835 static int
836 nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
837 {
838 	char *attr[] = { "supportedSASLMechanisms", NULL };
839 	char **values, **v, *mech, *m;
840 	LDAPMessage *res, *e;
841 	struct timeval	timeout;
842 	int slen, rc;
843 
844 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
845 		return( LDAP_PARAM_ERROR );
846 	}
847 
848 	timeout.tv_sec = SEARCH_TIMEOUT_SECS;
849 	timeout.tv_usec = 0;
850 
851 	rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
852 		"objectclass=*", attr, 0, &timeout, &res );
853 
854 	if ( rc != LDAP_SUCCESS ) {
855 		return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
856 	}
857 
858 	e = ldap_first_entry( ld, res );
859 	if ( e == NULL ) {
860 		ldap_msgfree( res );
861 		if ( ld->ld_errno == LDAP_SUCCESS ) {
862 			LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL );
863 		}
864 		return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
865 	}
866 
867 	values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
868 	if ( values == NULL ) {
869 		ldap_msgfree( res );
870 		LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL );
871 		return( LDAP_NO_SUCH_ATTRIBUTE );
872 	}
873 
874 	slen = 0;
875 	for(v = values; *v != NULL; v++ ) {
876 		slen += strlen(*v) + 1;
877 	}
878 	if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) {
879 		ldap_value_free( values );
880 		ldap_msgfree( res );
881 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
882 		return( LDAP_NO_MEMORY );
883 	}
884 	m = mech;
885 	for(v = values; *v; v++) {
886 		if (v != values) {
887 			*m++ = ' ';
888 		}
889 		slen = strlen(*v);
890 		strncpy(m, *v, slen);
891 		m += slen;
892 	}
893 	*m = '\0';
894 
895 	ldap_value_free( values );
896 	ldap_msgfree( res );
897 
898 	*pmech = mech;
899 
900 	return( LDAP_SUCCESS );
901 }
902 #endif
903 
904 int nsldapi_sasl_secprops(
905 	const char *in,
906 	sasl_security_properties_t *secprops )
907 {
908 	int i;
909 	char **props = NULL;
910 	char *inp;
911 	unsigned sflags = 0;
912 	sasl_ssf_t max_ssf = 0;
913 	sasl_ssf_t min_ssf = 0;
914 	unsigned maxbufsize = 0;
915 	int got_sflags = 0;
916 	int got_max_ssf = 0;
917 	int got_min_ssf = 0;
918 	int got_maxbufsize = 0;
919 
920 	if (in == NULL) {
921 		return LDAP_PARAM_ERROR;
922 	}
923 	inp = nsldapi_strdup(in);
924 	if (inp == NULL) {
925 		return LDAP_PARAM_ERROR;
926 	}
927 	props = ldap_str2charray( inp, "," );
928 	NSLDAPI_FREE( inp );
929 
930 	if( props == NULL || secprops == NULL ) {
931 		return LDAP_PARAM_ERROR;
932 	}
933 
934 	for( i=0; props[i]; i++ ) {
935 		if( strcasecmp(props[i], "none") == 0 ) {
936 			got_sflags++;
937 
938 		} else if( strcasecmp(props[i], "noactive") == 0 ) {
939 			got_sflags++;
940 			sflags |= SASL_SEC_NOACTIVE;
941 
942 		} else if( strcasecmp(props[i], "noanonymous") == 0 ) {
943 			got_sflags++;
944 			sflags |= SASL_SEC_NOANONYMOUS;
945 
946 		} else if( strcasecmp(props[i], "nodict") == 0 ) {
947 			got_sflags++;
948 			sflags |= SASL_SEC_NODICTIONARY;
949 
950 		} else if( strcasecmp(props[i], "noplain") == 0 ) {
951 			got_sflags++;
952 			sflags |= SASL_SEC_NOPLAINTEXT;
953 
954 		} else if( strcasecmp(props[i], "forwardsec") == 0 ) {
955 			got_sflags++;
956 			sflags |= SASL_SEC_FORWARD_SECRECY;
957 
958 		} else if( strcasecmp(props[i], "passcred") == 0 ) {
959 			got_sflags++;
960 			sflags |= SASL_SEC_PASS_CREDENTIALS;
961 
962 		} else if( strncasecmp(props[i],
963 			"minssf=", sizeof("minssf")) == 0 ) {
964 			if( isdigit( props[i][sizeof("minssf")] ) ) {
965 				got_min_ssf++;
966 				min_ssf = atoi( &props[i][sizeof("minssf")] );
967 			} else {
968 				return LDAP_NOT_SUPPORTED;
969 			}
970 
971 		} else if( strncasecmp(props[i],
972 			"maxssf=", sizeof("maxssf")) == 0 ) {
973 			if( isdigit( props[i][sizeof("maxssf")] ) ) {
974 				got_max_ssf++;
975 				max_ssf = atoi( &props[i][sizeof("maxssf")] );
976 			} else {
977 				return LDAP_NOT_SUPPORTED;
978 			}
979 
980 		} else if( strncasecmp(props[i],
981 			"maxbufsize=", sizeof("maxbufsize")) == 0 ) {
982 			if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
983 				got_maxbufsize++;
984 				maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
985 				if( maxbufsize &&
986 				    (( maxbufsize < SASL_MIN_BUFF_SIZE )
987 				    || (maxbufsize > SASL_MAX_BUFF_SIZE ))) {
988 					return( LDAP_PARAM_ERROR );
989 				}
990 			} else {
991 				return( LDAP_NOT_SUPPORTED );
992 			}
993 		} else {
994 			return( LDAP_NOT_SUPPORTED );
995 		}
996 	}
997 
998 	if(got_sflags) {
999 		secprops->security_flags = sflags;
1000 	}
1001 	if(got_min_ssf) {
1002 		secprops->min_ssf = min_ssf;
1003 	}
1004 	if(got_max_ssf) {
1005 		secprops->max_ssf = max_ssf;
1006 	}
1007 	if(got_maxbufsize) {
1008 		secprops->maxbufsize = maxbufsize;
1009 	}
1010 
1011 	ldap_charray_free( props );
1012 	return( LDAP_SUCCESS );
1013 }
1014 
1015 /*
1016  * SASL Authentication Interface: ldap_sasl_interactive_bind_s
1017  *
1018  * This routine takes a DN, SASL mech list, and a SASL callback
1019  * and performs the necessary sequencing to complete a SASL bind
1020  * to the LDAP connection ld.  The user provided callback can
1021  * use an optionally provided set of default values to complete
1022  * any necessary interactions.
1023  *
1024  * Currently inpose the following restrictions:
1025  *   A mech list must be provided, only LDAP_SASL_INTERACTIVE
1026  *   mode is supported
1027  */
1028 int
1029 LDAP_CALL
1030 ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn,
1031 	const char *saslMechanism,
1032 	LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
1033 	LDAP_SASL_INTERACT_PROC *callback, void *defaults )
1034 {
1035 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1036 	char *smechs;
1037 #endif
1038 	int rc;
1039 
1040 	LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0);
1041 
1042 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1043 		return( LDAP_PARAM_ERROR );
1044 	}
1045 
1046 	if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) {
1047 		return( LDAP_PARAM_ERROR );
1048 	}
1049 
1050 	LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
1051 
1052 	if( saslMechanism == NULL || *saslMechanism == '\0' ) {
1053 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1054 		rc = nsldapi_get_sasl_mechs( ld, &smechs );
1055 		if( rc != LDAP_SUCCESS ) {
1056 			LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1057 			return( rc );
1058 		}
1059 		saslMechanism = smechs;
1060 #else
1061 		LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1062 		return( LDAP_PARAM_ERROR );
1063 #endif
1064 	}
1065 
1066 	/* initialize SASL library */
1067 	if ( nsldapi_sasl_init() < 0 ) {
1068 	    return( LDAP_PARAM_ERROR );
1069 	}
1070 
1071 	rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
1072 			flags, callback, defaults, sctrl, cctrl);
1073 
1074 	LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1075 	return( rc );
1076 }
1077 
1078 #endif
1079