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