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