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