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