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