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