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