1 /*	$NetBSD: sasl.c,v 1.3 2021/08/14 16:14:56 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 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.3 2021/08/14 16:14:56 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 *
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)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
ldap_sasl_bind(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * mechanism,struct berval * cred,LDAPControl ** sctrls,LDAPControl ** cctrls,int * msgidp)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
ldap_sasl_bind_s(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * mechanism,struct berval * cred,LDAPControl ** sctrls,LDAPControl ** cctrls,struct berval ** servercredp)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
ldap_parse_sasl_bind_result(LDAP * ld,LDAPMessage * res,struct berval ** servercredp,int freeit)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
ldap_pvt_sasl_getmechs(LDAP * ld,char ** pmechlist)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
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)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
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)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
sb_sasl_generic_setup(Sockbuf_IO_Desc * sbiod,void * arg)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
sb_sasl_generic_remove(Sockbuf_IO_Desc * sbiod)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
sb_sasl_generic_pkt_length(struct sb_sasl_generic_data * p,const unsigned char * buf,int debuglevel)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
sb_sasl_generic_drop_packet(struct sb_sasl_generic_data * p,int debuglevel)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
sb_sasl_generic_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)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
sb_sasl_generic_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)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
sb_sasl_generic_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)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 
ldap_pvt_sasl_generic_install(Sockbuf * sb,struct sb_sasl_generic_install * install_arg)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 
ldap_pvt_sasl_generic_remove(Sockbuf * sb)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