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