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