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