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