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