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/*
17 *	BindRequest ::= SEQUENCE {
18 *		version		INTEGER,
19 *		name		DistinguishedName,	 -- who
20 *		authentication	CHOICE {
21 *			simple		[0] OCTET STRING -- passwd
22 *			krbv42ldap	[1] OCTET STRING -- OBSOLETE
23 *			krbv42dsa	[2] OCTET STRING -- OBSOLETE
24 *			sasl		[3] SaslCredentials	-- LDAPv3
25 *		}
26 *	}
27 *
28 *	BindResponse ::= SEQUENCE {
29 *		COMPONENTS OF LDAPResult,
30 *		serverSaslCreds		OCTET STRING OPTIONAL -- LDAPv3
31 *	}
32 *
33 */
34
35#include "portable.h"
36
37#include <stdio.h>
38
39#include <ac/socket.h>
40#include <ac/stdlib.h>
41#include <ac/string.h>
42#include <ac/time.h>
43#include <ac/errno.h>
44
45#include "ldap-int.h"
46
47/*
48 * ldap_sasl_bind - bind to the ldap server (and X.500).
49 * The dn (usually NULL), mechanism, and credentials are provided.
50 * The message id of the request initiated is provided upon successful
51 * (LDAP_SUCCESS) return.
52 *
53 * Example:
54 *	ldap_sasl_bind( ld, NULL, "mechanism",
55 *		cred, NULL, NULL, &msgid )
56 */
57
58int
59ldap_sasl_bind(
60	LDAP			*ld,
61	LDAP_CONST char	*dn,
62	LDAP_CONST char	*mechanism,
63	struct berval	*cred,
64	LDAPControl		**sctrls,
65	LDAPControl		**cctrls,
66	int				*msgidp )
67{
68	BerElement	*ber;
69	int rc;
70	ber_int_t id;
71
72	Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
73
74	assert( ld != NULL );
75	assert( LDAP_VALID( ld ) );
76	assert( msgidp != NULL );
77
78	/* check client controls */
79	rc = ldap_int_client_controls( ld, cctrls );
80	if( rc != LDAP_SUCCESS ) return rc;
81
82	if( mechanism == LDAP_SASL_SIMPLE ) {
83		if( dn == NULL && cred != NULL && cred->bv_len ) {
84			/* use default binddn */
85			dn = ld->ld_defbinddn;
86		}
87
88	} else if( ld->ld_version < LDAP_VERSION3 ) {
89		ld->ld_errno = LDAP_NOT_SUPPORTED;
90		return ld->ld_errno;
91	}
92
93	if ( dn == NULL ) {
94		dn = "";
95	}
96
97	/* create a message to send */
98	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
99		ld->ld_errno = LDAP_NO_MEMORY;
100		return ld->ld_errno;
101	}
102
103	assert( LBER_VALID( ber ) );
104
105	LDAP_NEXT_MSGID( ld, id );
106	if( mechanism == LDAP_SASL_SIMPLE ) {
107		/* simple bind */
108		rc = ber_printf( ber, "{it{istON}" /*}*/,
109			id, LDAP_REQ_BIND,
110			ld->ld_version, dn, LDAP_AUTH_SIMPLE,
111			cred );
112
113	} else if ( cred == NULL || cred->bv_val == NULL ) {
114		/* SASL bind w/o credentials */
115		rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
116			id, LDAP_REQ_BIND,
117			ld->ld_version, dn, LDAP_AUTH_SASL,
118			mechanism );
119
120	} else {
121		/* SASL bind w/ credentials */
122		rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
123			id, LDAP_REQ_BIND,
124			ld->ld_version, dn, LDAP_AUTH_SASL,
125			mechanism, cred );
126	}
127
128	if( rc == -1 ) {
129		ld->ld_errno = LDAP_ENCODING_ERROR;
130		ber_free( ber, 1 );
131		return( -1 );
132	}
133
134	/* Put Server Controls */
135	if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
136		ber_free( ber, 1 );
137		return ld->ld_errno;
138	}
139
140	if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
141		ld->ld_errno = LDAP_ENCODING_ERROR;
142		ber_free( ber, 1 );
143		return ld->ld_errno;
144	}
145
146
147	/* send the message */
148	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id );
149
150	if(*msgidp < 0)
151		return ld->ld_errno;
152
153	return LDAP_SUCCESS;
154}
155
156
157int
158ldap_sasl_bind_s(
159	LDAP			*ld,
160	LDAP_CONST char	*dn,
161	LDAP_CONST char	*mechanism,
162	struct berval	*cred,
163	LDAPControl		**sctrls,
164	LDAPControl		**cctrls,
165	struct berval	**servercredp )
166{
167	int	rc, msgid;
168	LDAPMessage	*result;
169	struct berval	*scredp = NULL;
170
171	Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
172
173	/* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
174	if( servercredp != NULL ) {
175		if (ld->ld_version < LDAP_VERSION3) {
176			ld->ld_errno = LDAP_NOT_SUPPORTED;
177			return ld->ld_errno;
178		}
179		*servercredp = NULL;
180	}
181
182	rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
183
184	if ( rc != LDAP_SUCCESS ) {
185		return( rc );
186	}
187
188#ifdef LDAP_CONNECTIONLESS
189	if (LDAP_IS_UDP(ld)) {
190		return( rc );
191	}
192#endif
193
194	if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
195		return( ld->ld_errno );	/* ldap_result sets ld_errno */
196	}
197
198	/* parse the results */
199	scredp = NULL;
200	if( servercredp != NULL ) {
201		rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
202	}
203
204	if ( rc != LDAP_SUCCESS ) {
205		ldap_msgfree( result );
206		return( rc );
207	}
208
209	rc = ldap_result2error( ld, result, 1 );
210
211	if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
212		if( servercredp != NULL ) {
213			*servercredp = scredp;
214			scredp = NULL;
215		}
216	}
217
218	if ( scredp != NULL ) {
219		ber_bvfree(scredp);
220	}
221
222	return rc;
223}
224
225
226/*
227* Parse BindResponse:
228*
229*   BindResponse ::= [APPLICATION 1] SEQUENCE {
230*     COMPONENTS OF LDAPResult,
231*     serverSaslCreds  [7] OCTET STRING OPTIONAL }
232*
233*   LDAPResult ::= SEQUENCE {
234*     resultCode      ENUMERATED,
235*     matchedDN       LDAPDN,
236*     errorMessage    LDAPString,
237*     referral        [3] Referral OPTIONAL }
238*/
239
240int
241ldap_parse_sasl_bind_result(
242	LDAP			*ld,
243	LDAPMessage		*res,
244	struct berval	**servercredp,
245	int				freeit )
246{
247	ber_int_t errcode;
248	struct berval* scred;
249
250	ber_tag_t tag;
251	BerElement	*ber;
252
253	Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
254
255	assert( ld != NULL );
256	assert( LDAP_VALID( ld ) );
257	assert( res != NULL );
258
259	if( servercredp != NULL ) {
260		if( ld->ld_version < LDAP_VERSION2 ) {
261			return LDAP_NOT_SUPPORTED;
262		}
263		*servercredp = NULL;
264	}
265
266	if( res->lm_msgtype != LDAP_RES_BIND ) {
267		ld->ld_errno = LDAP_PARAM_ERROR;
268		return ld->ld_errno;
269	}
270
271	scred = NULL;
272
273	if ( ld->ld_error ) {
274		LDAP_FREE( ld->ld_error );
275		ld->ld_error = NULL;
276	}
277	if ( ld->ld_matched ) {
278		LDAP_FREE( ld->ld_matched );
279		ld->ld_matched = NULL;
280	}
281
282	/* parse results */
283
284	ber = ber_dup( res->lm_ber );
285
286	if( ber == NULL ) {
287		ld->ld_errno = LDAP_NO_MEMORY;
288		return ld->ld_errno;
289	}
290
291	if ( ld->ld_version < LDAP_VERSION2 ) {
292		tag = ber_scanf( ber, "{iA}",
293			&errcode, &ld->ld_error );
294
295		if( tag == LBER_ERROR ) {
296			ber_free( ber, 0 );
297			ld->ld_errno = LDAP_DECODING_ERROR;
298			return ld->ld_errno;
299		}
300
301	} else {
302		ber_len_t len;
303
304		tag = ber_scanf( ber, "{eAA" /*}*/,
305			&errcode, &ld->ld_matched, &ld->ld_error );
306
307		if( tag == LBER_ERROR ) {
308			ber_free( ber, 0 );
309			ld->ld_errno = LDAP_DECODING_ERROR;
310			return ld->ld_errno;
311		}
312
313		tag = ber_peek_tag(ber, &len);
314
315		if( tag == LDAP_TAG_REFERRAL ) {
316			/* skip 'em */
317			if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
318				ber_free( ber, 0 );
319				ld->ld_errno = LDAP_DECODING_ERROR;
320				return ld->ld_errno;
321			}
322
323			tag = ber_peek_tag(ber, &len);
324		}
325
326		if( tag == LDAP_TAG_SASL_RES_CREDS ) {
327			if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
328				ber_free( ber, 0 );
329				ld->ld_errno = LDAP_DECODING_ERROR;
330				return ld->ld_errno;
331			}
332		}
333	}
334
335	ber_free( ber, 0 );
336
337	if ( servercredp != NULL ) {
338		*servercredp = scred;
339
340	} else if ( scred != NULL ) {
341		ber_bvfree( scred );
342	}
343
344	ld->ld_errno = errcode;
345
346	if ( freeit ) {
347		ldap_msgfree( res );
348	}
349
350	return( LDAP_SUCCESS );
351}
352
353int
354ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
355{
356	/* we need to query the server for supported mechs anyway */
357	LDAPMessage *res, *e;
358	char *attrs[] = { "supportedSASLMechanisms", NULL };
359	char **values, *mechlist;
360	int rc;
361
362	Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
363
364	rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
365		NULL, attrs, 0, &res );
366
367	if ( rc != LDAP_SUCCESS ) {
368		return ld->ld_errno;
369	}
370
371	e = ldap_first_entry( ld, res );
372	if ( e == NULL ) {
373		ldap_msgfree( res );
374		if ( ld->ld_errno == LDAP_SUCCESS ) {
375			ld->ld_errno = LDAP_NO_SUCH_OBJECT;
376		}
377		return ld->ld_errno;
378	}
379
380	values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
381	if ( values == NULL ) {
382		ldap_msgfree( res );
383		ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
384		return ld->ld_errno;
385	}
386
387	mechlist = ldap_charray2str( values, " " );
388	if ( mechlist == NULL ) {
389		LDAP_VFREE( values );
390		ldap_msgfree( res );
391		ld->ld_errno = LDAP_NO_MEMORY;
392		return ld->ld_errno;
393	}
394
395	LDAP_VFREE( values );
396	ldap_msgfree( res );
397
398	*pmechlist = mechlist;
399
400	return LDAP_SUCCESS;
401}
402
403/*
404 * ldap_sasl_interactive_bind - interactive SASL authentication
405 *
406 * This routine uses interactive callbacks.
407 *
408 * LDAP_SUCCESS is returned upon success, the ldap error code
409 * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further
410 * calls are needed.
411 */
412int
413ldap_sasl_interactive_bind(
414	LDAP *ld,
415	LDAP_CONST char *dn, /* usually NULL */
416	LDAP_CONST char *mechs,
417	LDAPControl **serverControls,
418	LDAPControl **clientControls,
419	unsigned flags,
420	LDAP_SASL_INTERACT_PROC *interact,
421	void *defaults,
422	LDAPMessage *result,
423	const char **rmech,
424	int *msgid )
425{
426	char *smechs = NULL;
427	int rc;
428
429#if defined( HAVE_CYRUS_SASL )
430	LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex );
431#endif
432#ifdef LDAP_CONNECTIONLESS
433	if( LDAP_IS_UDP(ld) ) {
434		/* Just force it to simple bind, silly to make the user
435		 * ask all the time. No, we don't ever actually bind, but I'll
436		 * let the final bind handler take care of saving the cdn.
437		 */
438		rc = ldap_simple_bind( ld, dn, NULL );
439		rc = rc < 0 ? rc : 0;
440		goto done;
441	} else
442#endif
443
444	/* First time */
445	if ( !result ) {
446
447#ifdef HAVE_CYRUS_SASL
448	if( mechs == NULL || *mechs == '\0' ) {
449		mechs = ld->ld_options.ldo_def_sasl_mech;
450	}
451#endif
452
453	if( mechs == NULL || *mechs == '\0' ) {
454		/* FIXME: this needs to be asynchronous too;
455		 * perhaps NULL should be disallowed for async usage?
456		 */
457		rc = ldap_pvt_sasl_getmechs( ld, &smechs );
458		if( rc != LDAP_SUCCESS ) {
459			goto done;
460		}
461
462		Debug( LDAP_DEBUG_TRACE,
463			"ldap_sasl_interactive_bind: server supports: %s\n",
464			smechs, 0, 0 );
465
466		mechs = smechs;
467
468	} else {
469		Debug( LDAP_DEBUG_TRACE,
470			"ldap_sasl_interactive_bind: user selected: %s\n",
471			mechs, 0, 0 );
472        }
473	}
474	rc = ldap_int_sasl_bind( ld, dn, mechs,
475		serverControls, clientControls,
476		flags, interact, defaults, result, rmech, msgid );
477
478done:
479#if defined( HAVE_CYRUS_SASL )
480	LDAP_MUTEX_UNLOCK( &ldap_int_sasl_mutex );
481#endif
482	if ( smechs ) LDAP_FREE( smechs );
483
484	return rc;
485}
486
487/*
488 * ldap_sasl_interactive_bind_s - interactive SASL authentication
489 *
490 * This routine uses interactive callbacks.
491 *
492 * LDAP_SUCCESS is returned upon success, the ldap error code
493 * otherwise.
494 */
495int
496ldap_sasl_interactive_bind_s(
497	LDAP *ld,
498	LDAP_CONST char *dn, /* usually NULL */
499	LDAP_CONST char *mechs,
500	LDAPControl **serverControls,
501	LDAPControl **clientControls,
502	unsigned flags,
503	LDAP_SASL_INTERACT_PROC *interact,
504	void *defaults )
505{
506	const char *rmech = NULL;
507	LDAPMessage *result = NULL;
508	int rc, msgid;
509
510	do {
511		rc = ldap_sasl_interactive_bind( ld, dn, mechs,
512			serverControls, clientControls,
513			flags, interact, defaults, result, &rmech, &msgid );
514
515		ldap_msgfree( result );
516
517		if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
518			break;
519
520#ifdef LDAP_CONNECTIONLESS
521		if (LDAP_IS_UDP(ld)) {
522			break;
523		}
524#endif
525
526		if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
527			return( ld->ld_errno );	/* ldap_result sets ld_errno */
528		}
529	} while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
530
531	return rc;
532}
533
534#ifdef HAVE_CYRUS_SASL
535
536#ifdef HAVE_SASL_SASL_H
537#include <sasl/sasl.h>
538#else
539#include <sasl.h>
540#endif
541
542#endif /* HAVE_CYRUS_SASL */
543
544static int
545sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod );
546
547static int
548sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg )
549{
550	struct sb_sasl_generic_data	*p;
551	struct sb_sasl_generic_install	*i;
552
553	assert( sbiod != NULL );
554
555	i = (struct sb_sasl_generic_install *)arg;
556
557	p = LBER_MALLOC( sizeof( *p ) );
558	if ( p == NULL )
559		return -1;
560	p->ops = i->ops;
561	p->ops_private = i->ops_private;
562	p->sbiod = sbiod;
563	p->flags = 0;
564	ber_pvt_sb_buf_init( &p->sec_buf_in );
565	ber_pvt_sb_buf_init( &p->buf_in );
566	ber_pvt_sb_buf_init( &p->buf_out );
567
568	sbiod->sbiod_pvt = p;
569
570	p->ops->init( p, &p->min_send, &p->max_send, &p->max_recv );
571
572	if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, p->min_send ) < 0 ) {
573		sb_sasl_generic_remove( sbiod );
574		sock_errset(ENOMEM);
575		return -1;
576	}
577
578	return 0;
579}
580
581static int
582sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod )
583{
584	struct sb_sasl_generic_data	*p;
585
586	assert( sbiod != NULL );
587
588	p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
589
590	p->ops->fini(p);
591
592	ber_pvt_sb_buf_destroy( &p->sec_buf_in );
593	ber_pvt_sb_buf_destroy( &p->buf_in );
594	ber_pvt_sb_buf_destroy( &p->buf_out );
595	LBER_FREE( p );
596	sbiod->sbiod_pvt = NULL;
597	return 0;
598}
599
600static ber_len_t
601sb_sasl_generic_pkt_length(
602	struct sb_sasl_generic_data *p,
603	const unsigned char *buf,
604	int debuglevel )
605{
606	ber_len_t		size;
607
608	assert( buf != NULL );
609
610	size = buf[0] << 24
611		| buf[1] << 16
612		| buf[2] << 8
613		| buf[3];
614
615	if ( size > p->max_recv ) {
616		/* somebody is trying to mess me up. */
617		ber_log_printf( LDAP_DEBUG_ANY, debuglevel,
618			"sb_sasl_generic_pkt_length: "
619			"received illegal packet length of %lu bytes\n",
620			(unsigned long)size );
621		size = 16; /* this should lead to an error. */
622	}
623
624	return size + 4; /* include the size !!! */
625}
626
627/* Drop a processed packet from the input buffer */
628static void
629sb_sasl_generic_drop_packet (
630	struct sb_sasl_generic_data *p,
631	int debuglevel )
632{
633	ber_slen_t			len;
634
635	len = p->sec_buf_in.buf_ptr - p->sec_buf_in.buf_end;
636	if ( len > 0 )
637		AC_MEMCPY( p->sec_buf_in.buf_base, p->sec_buf_in.buf_base +
638			p->sec_buf_in.buf_end, len );
639
640	if ( len >= 4 ) {
641		p->sec_buf_in.buf_end = sb_sasl_generic_pkt_length(p,
642			(unsigned char *) p->sec_buf_in.buf_base, debuglevel);
643	}
644	else {
645		p->sec_buf_in.buf_end = 0;
646	}
647	p->sec_buf_in.buf_ptr = len;
648}
649
650static ber_slen_t
651sb_sasl_generic_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
652{
653	struct sb_sasl_generic_data	*p;
654	ber_slen_t			ret, bufptr;
655
656	assert( sbiod != NULL );
657	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
658
659	p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
660
661	/* Are there anything left in the buffer? */
662	ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len );
663	bufptr = ret;
664	len -= ret;
665
666	if ( len == 0 )
667		return bufptr;
668
669	p->ops->reset_buf( p, &p->buf_in );
670
671	/* Read the length of the packet */
672	while ( p->sec_buf_in.buf_ptr < 4 ) {
673		ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
674			p->sec_buf_in.buf_ptr,
675			4 - p->sec_buf_in.buf_ptr );
676#ifdef EINTR
677		if ( ( ret < 0 ) && ( errno == EINTR ) )
678			continue;
679#endif
680		if ( ret <= 0 )
681			return bufptr ? bufptr : ret;
682
683		p->sec_buf_in.buf_ptr += ret;
684	}
685
686	/* The new packet always starts at p->sec_buf_in.buf_base */
687	ret = sb_sasl_generic_pkt_length(p, (unsigned char *) p->sec_buf_in.buf_base,
688		sbiod->sbiod_sb->sb_debug );
689
690	/* Grow the packet buffer if neccessary */
691	if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) &&
692		ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 )
693	{
694		sock_errset(ENOMEM);
695		return -1;
696	}
697	p->sec_buf_in.buf_end = ret;
698
699	/* Did we read the whole encrypted packet? */
700	while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) {
701		/* No, we have got only a part of it */
702		ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr;
703
704		ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
705			p->sec_buf_in.buf_ptr, ret );
706#ifdef EINTR
707		if ( ( ret < 0 ) && ( errno == EINTR ) )
708			continue;
709#endif
710		if ( ret <= 0 )
711			return bufptr ? bufptr : ret;
712
713		p->sec_buf_in.buf_ptr += ret;
714   	}
715
716	/* Decode the packet */
717	ret = p->ops->decode( p, &p->sec_buf_in, &p->buf_in );
718
719	/* Drop the packet from the input buffer */
720	sb_sasl_generic_drop_packet( p, sbiod->sbiod_sb->sb_debug );
721
722	if ( ret != 0 ) {
723		ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
724			"sb_sasl_generic_read: failed to decode packet\n" );
725		sock_errset(EIO);
726		return -1;
727	}
728
729	bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len );
730
731	return bufptr;
732}
733
734static ber_slen_t
735sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
736{
737	struct sb_sasl_generic_data	*p;
738	int				ret;
739	ber_len_t			len2;
740
741	assert( sbiod != NULL );
742	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
743
744	p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
745
746	/* Is there anything left in the buffer? */
747	if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
748		ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
749		if ( ret < 0 ) return ret;
750
751		/* Still have something left?? */
752		if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
753			sock_errset(EAGAIN);
754			return -1;
755		}
756	}
757
758	len2 = p->max_send - 100;	/* For safety margin */
759	len2 = len > len2 ? len2 : len;
760
761	/* If we're just retrying a partial write, tell the
762	 * caller it's done. Let them call again if there's
763	 * still more left to write.
764	 */
765	if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) {
766		p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE;
767		return len2;
768	}
769
770	/* now encode the next packet. */
771	p->ops->reset_buf( p, &p->buf_out );
772
773	ret = p->ops->encode( p, buf, len2, &p->buf_out );
774
775	if ( ret != 0 ) {
776		ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
777			"sb_sasl_generic_write: failed to encode packet\n" );
778		sock_errset(EIO);
779		return -1;
780	}
781
782	ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
783
784	if ( ret < 0 ) {
785		/* error? */
786		int err = sock_errno();
787		/* caller can retry this */
788		if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR )
789			p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
790		return ret;
791	} else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
792		/* partial write? pretend nothing got written */
793		p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
794		sock_errset(EAGAIN);
795		len2 = -1;
796	}
797
798	/* return number of bytes encoded, not written, to ensure
799	 * no byte is encoded twice (even if only sent once).
800	 */
801	return len2;
802}
803
804static int
805sb_sasl_generic_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
806{
807	struct sb_sasl_generic_data	*p;
808
809	p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
810
811	if ( opt == LBER_SB_OPT_DATA_READY ) {
812		if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) return 1;
813	}
814
815	return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
816}
817
818Sockbuf_IO ldap_pvt_sockbuf_io_sasl_generic = {
819	sb_sasl_generic_setup,		/* sbi_setup */
820	sb_sasl_generic_remove,		/* sbi_remove */
821	sb_sasl_generic_ctrl,		/* sbi_ctrl */
822	sb_sasl_generic_read,		/* sbi_read */
823	sb_sasl_generic_write,		/* sbi_write */
824	NULL			/* sbi_close */
825};
826
827int ldap_pvt_sasl_generic_install(
828	Sockbuf *sb,
829	struct sb_sasl_generic_install *install_arg )
830{
831	Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_generic_install\n",
832		0, 0, 0 );
833
834	/* don't install the stuff unless security has been negotiated */
835
836	if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO,
837			&ldap_pvt_sockbuf_io_sasl_generic ) )
838	{
839#ifdef LDAP_DEBUG
840		ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
841			LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_generic_" );
842#endif
843		ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
844			LBER_SBIOD_LEVEL_APPLICATION, install_arg );
845	}
846
847	return LDAP_SUCCESS;
848}
849
850void ldap_pvt_sasl_generic_remove( Sockbuf *sb )
851{
852	ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
853		LBER_SBIOD_LEVEL_APPLICATION );
854#ifdef LDAP_DEBUG
855	ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
856		LBER_SBIOD_LEVEL_APPLICATION );
857#endif
858}
859