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