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#include "portable.h" 17 18#include <stdio.h> 19 20#include <ac/socket.h> 21#include <ac/stdlib.h> 22#include <ac/string.h> 23#include <ac/time.h> 24#include <ac/errno.h> 25#include <ac/ctype.h> 26#include <ac/unistd.h> 27 28#ifdef HAVE_LIMITS_H 29#include <limits.h> 30#endif 31 32#include "ldap-int.h" 33 34#ifdef HAVE_CYRUS_SASL 35 36#ifdef HAVE_LIMITS_H 37#include <limits.h> 38#endif 39 40#ifndef INT_MAX 41#define INT_MAX 2147483647 /* 32 bit signed max */ 42#endif 43 44#ifdef LDAP_R_COMPILE 45ldap_pvt_thread_mutex_t ldap_int_sasl_mutex; 46#endif 47 48#ifdef HAVE_SASL_SASL_H 49#include <sasl/sasl.h> 50#else 51#include <sasl.h> 52#endif 53 54#if SASL_VERSION_MAJOR >= 2 55#define SASL_CONST const 56#else 57#define SASL_CONST 58#endif 59 60/* 61* Various Cyrus SASL related stuff. 62*/ 63 64static const sasl_callback_t client_callbacks[] = { 65#ifdef SASL_CB_GETREALM 66 { SASL_CB_GETREALM, NULL, NULL }, 67#endif 68 { SASL_CB_USER, NULL, NULL }, 69 { SASL_CB_AUTHNAME, NULL, NULL }, 70 { SASL_CB_PASS, NULL, NULL }, 71 { SASL_CB_ECHOPROMPT, NULL, NULL }, 72 { SASL_CB_NOECHOPROMPT, NULL, NULL }, 73 { SASL_CB_LIST_END, NULL, NULL } 74}; 75 76int ldap_int_sasl_init( void ) 77{ 78 /* XXX not threadsafe */ 79 static int sasl_initialized = 0; 80 81#ifdef HAVE_SASL_VERSION 82 /* stringify the version number, sasl.h doesn't do it for us */ 83#define VSTR0(maj, min, pat) #maj "." #min "." #pat 84#define VSTR(maj, min, pat) VSTR0(maj, min, pat) 85#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ 86 SASL_VERSION_STEP) 87 { int rc; 88 sasl_version( NULL, &rc ); 89 if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) || 90 (rc & 0xffff) < SASL_VERSION_STEP) { 91 char version[sizeof("xxx.xxx.xxxxx")]; 92 sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff, 93 rc & 0xffff ); 94 95 Debug( LDAP_DEBUG_ANY, 96 "ldap_int_sasl_init: SASL library version mismatch:" 97 " expected " SASL_VERSION_STRING "," 98 " got %s\n", version, 0, 0 ); 99 return -1; 100 } 101 } 102#endif 103 if ( sasl_initialized ) { 104 return 0; 105 } 106 107/* SASL 2 takes care of its own memory completely internally */ 108#if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC) 109 sasl_set_alloc( 110 ber_memalloc, 111 ber_memcalloc, 112 ber_memrealloc, 113 ber_memfree ); 114#endif /* CSRIMALLOC */ 115 116#ifdef LDAP_R_COMPILE 117 sasl_set_mutex( 118 ldap_pvt_sasl_mutex_new, 119 ldap_pvt_sasl_mutex_lock, 120 ldap_pvt_sasl_mutex_unlock, 121 ldap_pvt_sasl_mutex_dispose ); 122 123 ldap_pvt_thread_mutex_init( &ldap_int_sasl_mutex ); 124#endif 125 126 if ( sasl_client_init( NULL ) == SASL_OK ) { 127 sasl_initialized = 1; 128 return 0; 129 } 130 131#if SASL_VERSION_MAJOR < 2 132 /* A no-op to make sure we link with Cyrus 1.5 */ 133 sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL ); 134#endif 135 return -1; 136} 137 138static void 139sb_sasl_cyrus_init( 140 struct sb_sasl_generic_data *p, 141 ber_len_t *min_send, 142 ber_len_t *max_send, 143 ber_len_t *max_recv) 144{ 145 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; 146 ber_len_t maxbuf; 147 148 sasl_getprop( sasl_context, SASL_MAXOUTBUF, 149 (SASL_CONST void **)(char *) &maxbuf ); 150 151 *min_send = SASL_MIN_BUFF_SIZE; 152 *max_send = maxbuf; 153 *max_recv = SASL_MAX_BUFF_SIZE; 154} 155 156static ber_int_t 157sb_sasl_cyrus_encode( 158 struct sb_sasl_generic_data *p, 159 unsigned char *buf, 160 ber_len_t len, 161 Sockbuf_Buf *dst) 162{ 163 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; 164 ber_int_t ret; 165 unsigned tmpsize = dst->buf_size; 166 167 ret = sasl_encode( sasl_context, (char *)buf, len, 168 (SASL_CONST char **)&dst->buf_base, 169 &tmpsize ); 170 171 dst->buf_size = tmpsize; 172 dst->buf_end = dst->buf_size; 173 174 if ( ret != SASL_OK ) { 175 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, 176 "sb_sasl_cyrus_encode: failed to encode packet: %s\n", 177 sasl_errstring( ret, NULL, NULL ) ); 178 return -1; 179 } 180 181 return 0; 182} 183 184static ber_int_t 185sb_sasl_cyrus_decode( 186 struct sb_sasl_generic_data *p, 187 const Sockbuf_Buf *src, 188 Sockbuf_Buf *dst) 189{ 190 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; 191 ber_int_t ret; 192 unsigned tmpsize = dst->buf_size; 193 194 ret = sasl_decode( sasl_context, 195 src->buf_base, src->buf_end, 196 (SASL_CONST char **)&dst->buf_base, 197 (unsigned *)&tmpsize ); 198 199 200 dst->buf_size = tmpsize; 201 dst->buf_end = dst->buf_size; 202 203 if ( ret != SASL_OK ) { 204 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, 205 "sb_sasl_cyrus_decode: failed to decode packet: %s\n", 206 sasl_errstring( ret, NULL, NULL ) ); 207 return -1; 208 } 209 210 return 0; 211} 212 213static void 214sb_sasl_cyrus_reset_buf( 215 struct sb_sasl_generic_data *p, 216 Sockbuf_Buf *buf) 217{ 218#if SASL_VERSION_MAJOR >= 2 219 ber_pvt_sb_buf_init( buf ); 220#else 221 ber_pvt_sb_buf_destroy( buf ); 222#endif 223} 224 225static void 226sb_sasl_cyrus_fini( 227 struct sb_sasl_generic_data *p) 228{ 229#if SASL_VERSION_MAJOR >= 2 230 /* 231 * SASLv2 encode/decode buffers are managed by 232 * libsasl2. Ensure they are not freed by liblber. 233 */ 234 p->buf_in.buf_base = NULL; 235 p->buf_out.buf_base = NULL; 236#endif 237} 238 239static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = { 240 sb_sasl_cyrus_init, 241 sb_sasl_cyrus_encode, 242 sb_sasl_cyrus_decode, 243 sb_sasl_cyrus_reset_buf, 244 sb_sasl_cyrus_fini 245 }; 246 247int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg ) 248{ 249 struct sb_sasl_generic_install install_arg; 250 251 install_arg.ops = &sb_sasl_cyrus_ops; 252 install_arg.ops_private = ctx_arg; 253 254 return ldap_pvt_sasl_generic_install( sb, &install_arg ); 255} 256 257void ldap_pvt_sasl_remove( Sockbuf *sb ) 258{ 259 ldap_pvt_sasl_generic_remove( sb ); 260} 261 262static int 263sasl_err2ldap( int saslerr ) 264{ 265 int rc; 266 267 /* map SASL errors to LDAP API errors returned by: 268 * sasl_client_new() 269 * SASL_OK, SASL_NOMECH, SASL_NOMEM 270 * sasl_client_start() 271 * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT 272 * sasl_client_step() 273 * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV 274 */ 275 276 switch (saslerr) { 277 case SASL_CONTINUE: 278 rc = LDAP_MORE_RESULTS_TO_RETURN; 279 break; 280 case SASL_INTERACT: 281 rc = LDAP_LOCAL_ERROR; 282 break; 283 case SASL_OK: 284 rc = LDAP_SUCCESS; 285 break; 286 case SASL_NOMEM: 287 rc = LDAP_NO_MEMORY; 288 break; 289 case SASL_NOMECH: 290 rc = LDAP_AUTH_UNKNOWN; 291 break; 292 case SASL_BADPROT: 293 rc = LDAP_DECODING_ERROR; 294 break; 295 case SASL_BADSERV: 296 rc = LDAP_AUTH_UNKNOWN; 297 break; 298 299 /* other codes */ 300 case SASL_BADAUTH: 301 rc = LDAP_AUTH_UNKNOWN; 302 break; 303 case SASL_NOAUTHZ: 304 rc = LDAP_PARAM_ERROR; 305 break; 306 case SASL_FAIL: 307 rc = LDAP_LOCAL_ERROR; 308 break; 309 case SASL_TOOWEAK: 310 case SASL_ENCRYPT: 311 rc = LDAP_AUTH_UNKNOWN; 312 break; 313 default: 314 rc = LDAP_LOCAL_ERROR; 315 break; 316 } 317 318 assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) ); 319 return rc; 320} 321 322int 323ldap_int_sasl_open( 324 LDAP *ld, 325 LDAPConn *lc, 326 const char * host ) 327{ 328 int rc; 329 sasl_conn_t *ctx; 330 331 assert( lc->lconn_sasl_authctx == NULL ); 332 333 if ( host == NULL ) { 334 ld->ld_errno = LDAP_LOCAL_ERROR; 335 return ld->ld_errno; 336 } 337 338 if ( ldap_int_sasl_init() ) { 339 ld->ld_errno = LDAP_LOCAL_ERROR; 340 return ld->ld_errno; 341 } 342 343 /* use specified host */ 344 if ( ld->ld_options.ldo_sasl_fqdn != NULL) { 345 host = ld->ld_options.ldo_sasl_fqdn; 346 } 347 348#if SASL_VERSION_MAJOR >= 2 349 rc = sasl_client_new( "ldap", host, NULL, NULL, 350 client_callbacks, 0, &ctx ); 351#else 352 rc = sasl_client_new( "ldap", host, client_callbacks, 353 SASL_SECURITY_LAYER, &ctx ); 354#endif 355 356 if ( rc != SASL_OK ) { 357 ld->ld_errno = sasl_err2ldap( rc ); 358 return ld->ld_errno; 359 } 360 361 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n", 362 host, 0, 0 ); 363 364 lc->lconn_sasl_authctx = ctx; 365 366 return LDAP_SUCCESS; 367} 368 369int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) 370{ 371 sasl_conn_t *ctx = lc->lconn_sasl_authctx; 372 373 if( ctx != NULL ) { 374 sasl_dispose( &ctx ); 375 if ( lc->lconn_sasl_sockctx && 376 lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) { 377 ctx = lc->lconn_sasl_sockctx; 378 sasl_dispose( &ctx ); 379 } 380 lc->lconn_sasl_sockctx = NULL; 381 lc->lconn_sasl_authctx = NULL; 382 } 383 384 return LDAP_SUCCESS; 385} 386 387int 388ldap_int_sasl_bind( 389 LDAP *ld, 390 const char *dn, 391 const char *mechs, 392 LDAPControl **sctrls, 393 LDAPControl **cctrls, 394 unsigned flags, 395 LDAP_SASL_INTERACT_PROC *interact, 396 void *defaults, 397 LDAPMessage *result, 398 const char **rmech, 399 int *msgid ) 400{ 401 const char *mech; 402 sasl_ssf_t *ssf; 403 sasl_conn_t *ctx; 404 sasl_interact_t *prompts = NULL; 405 struct berval ccred = BER_BVNULL; 406 int saslrc, rc; 407 unsigned credlen; 408 409 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", 410 mechs ? mechs : "<null>", 0, 0 ); 411 412 /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ 413 if (ld->ld_version < LDAP_VERSION3) { 414 ld->ld_errno = LDAP_NOT_SUPPORTED; 415 return ld->ld_errno; 416 } 417 418 /* Starting a Bind */ 419 if ( !result ) { 420 const char *pmech = NULL; 421 sasl_conn_t *oldctx; 422 ber_socket_t sd; 423 void *ssl; 424 425 rc = 0; 426 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 427 ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); 428 429 if ( sd == AC_SOCKET_INVALID ) { 430 /* not connected yet */ 431 432 rc = ldap_open_defconn( ld ); 433 434 if ( rc == 0 ) { 435 ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, 436 LBER_SB_OPT_GET_FD, &sd ); 437 438 if( sd == AC_SOCKET_INVALID ) { 439 ld->ld_errno = LDAP_LOCAL_ERROR; 440 rc = ld->ld_errno; 441 } 442 } 443 } 444 if ( rc == 0 && ld->ld_defconn && 445 ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) { 446 rc = ldap_int_check_async_open( ld, sd ); 447 } 448 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 449 if( rc != 0 ) return ld->ld_errno; 450 451 oldctx = ld->ld_defconn->lconn_sasl_authctx; 452 453 /* If we already have an authentication context, clear it out */ 454 if( oldctx ) { 455 if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { 456 sasl_dispose( &oldctx ); 457 } 458 ld->ld_defconn->lconn_sasl_authctx = NULL; 459 } 460 461 { 462 char buffer[INET6_ADDRSTRLEN]; 463 char *saslhost; 464 int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, 465 LDAP_BOOL_SASL_NOCANON ); 466 467 /* If passed the local host, get the real host name from 468 * SCDynamicStore otherwise GSS will use 'localhost' in the 469 * principal name. 470 */ 471 if (strncmp(ld->ld_defconn->lconn_server->lud_host, "127.0.0.1", 9) == 0 || 472 strncmp(ld->ld_defconn->lconn_server->lud_host, "localhost", 9) == 0) { 473 saslhost = ldap_pvt_get_fqdn_from_sys_conf(); 474 if ( saslhost == NULL ) { 475 saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, 476 "localhost" ); 477 } 478 } else { 479 /* If we don't need to canonicalize just use the host 480 * from the LDAP URI. But if the URI contains an IP address, it 481 * must be resolved to prevent GSS from using the address in the 482 * principal name. 483 */ 484 if ( nocanon && 485 inet_pton(AF_INET, ld->ld_defconn->lconn_server->lud_host, buffer) != 1 && 486 inet_pton(AF_INET6, ld->ld_defconn->lconn_server->lud_host, buffer) != 1) { 487 saslhost = LDAP_STRDUP(ld->ld_defconn->lconn_server->lud_host); 488 } else { 489 saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, 490 "localhost" ); 491 } 492 } 493 rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); 494 LDAP_FREE( saslhost ); 495 } 496 497 if ( rc != LDAP_SUCCESS ) return rc; 498 499 ctx = ld->ld_defconn->lconn_sasl_authctx; 500 501#ifdef HAVE_TLS 502 /* Check for TLS */ 503 ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); 504 if ( ssl ) { 505 struct berval authid = BER_BVNULL; 506 ber_len_t fac; 507 508 fac = ldap_pvt_tls_get_strength( ssl ); 509 /* failure is OK, we just can't use SASL EXTERNAL */ 510 (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); 511 512 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); 513 LDAP_FREE( authid.bv_val ); 514 } 515#endif 516 517#if !defined(_WIN32) 518 /* Check for local */ 519 if ( ldap_pvt_url_scheme2proto( 520 ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) 521 { 522 char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," 523 "cn=peercred,cn=external,cn=auth")]; 524 sprintf( authid, "gidNumber=%u+uidNumber=%u," 525 "cn=peercred,cn=external,cn=auth", 526 getegid(), geteuid() ); 527 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, 528 LDAP_PVT_SASL_LOCAL_SSF ); 529 } 530#endif 531 532 /* (re)set security properties */ 533 sasl_setprop( ctx, SASL_SEC_PROPS, 534 &ld->ld_options.ldo_sasl_secprops ); 535 536 mech = NULL; 537 538 do { 539 saslrc = sasl_client_start( ctx, 540 mechs, 541#if SASL_VERSION_MAJOR < 2 542 NULL, 543#endif 544 &prompts, 545 (SASL_CONST char **)&ccred.bv_val, 546 &credlen, 547 &mech ); 548 549 if( pmech == NULL && mech != NULL ) { 550 pmech = mech; 551 *rmech = mech; 552 553 if( flags != LDAP_SASL_QUIET ) { 554 fprintf(stderr, 555 "SASL/%s authentication started\n", 556 pmech ); 557 } 558 } 559 560 if( saslrc == SASL_INTERACT ) { 561 int res; 562 if( !interact ) break; 563 res = (interact)( ld, flags, defaults, prompts ); 564 565 if( res != LDAP_SUCCESS ) break; 566 } 567 } while ( saslrc == SASL_INTERACT ); 568 rc = LDAP_SASL_BIND_IN_PROGRESS; 569 570 } else { 571 /* continuing an in-progress Bind */ 572 struct berval *scred = NULL; 573 574 ctx = ld->ld_defconn->lconn_sasl_authctx; 575 576 rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 ); 577 if ( rc != LDAP_SUCCESS ) 578 goto done; 579 580 rc = ldap_result2error( ld, result, 0 ); 581 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { 582 if( scred ) { 583 /* and server provided us with data? */ 584 Debug( LDAP_DEBUG_TRACE, 585 "ldap_int_sasl_bind: rc=%d len=%ld\n", 586 rc, scred ? (long) scred->bv_len : -1L, 0 ); 587 ber_bvfree( scred ); 588 scred = NULL; 589 } 590 goto done; 591 } 592 593 mech = *rmech; 594 if ( rc == LDAP_SUCCESS && mech == NULL ) { 595 if( scred ) { 596 ber_bvfree( scred ); 597 scred = NULL; 598 } 599 goto success; 600 } 601 602 do { 603 if( ! scred ) { 604 /* no data! */ 605 Debug( LDAP_DEBUG_TRACE, 606 "ldap_int_sasl_bind: no data in step!\n", 607 0, 0, 0 ); 608 } 609 610 saslrc = sasl_client_step( ctx, 611 (scred == NULL) ? NULL : scred->bv_val, 612 (scred == NULL) ? 0 : scred->bv_len, 613 &prompts, 614 (SASL_CONST char **)&ccred.bv_val, 615 &credlen ); 616 617 Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n", 618 saslrc, 0, 0 ); 619 620 if( saslrc == SASL_INTERACT ) { 621 int res; 622 if( !interact ) break; 623 res = (interact)( ld, flags, defaults, prompts ); 624 if( res != LDAP_SUCCESS ) break; 625 } 626 } while ( saslrc == SASL_INTERACT ); 627 628 ber_bvfree( scred ); 629 } 630 631 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { 632 rc = ld->ld_errno = sasl_err2ldap( saslrc ); 633#if SASL_VERSION_MAJOR >= 2 634 if ( ld->ld_error ) { 635 LDAP_FREE( ld->ld_error ); 636 } 637 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); 638#endif 639 goto done; 640 } 641 642 if ( saslrc == SASL_OK ) 643 *rmech = NULL; 644 645 ccred.bv_len = credlen; 646 647 if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) { 648 rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid ); 649 650 if ( ccred.bv_val != NULL ) { 651#if SASL_VERSION_MAJOR < 2 652 LDAP_FREE( ccred.bv_val ); 653#endif 654 ccred.bv_val = NULL; 655 } 656 if ( rc == LDAP_SUCCESS ) 657 rc = LDAP_SASL_BIND_IN_PROGRESS; 658 goto done; 659 } 660 661success: 662 /* Conversation was completed successfully by now */ 663 if( flags != LDAP_SASL_QUIET ) { 664 char *data; 665 saslrc = sasl_getprop( ctx, SASL_USERNAME, 666 (SASL_CONST void **)(char *) &data ); 667 if( saslrc == SASL_OK && data && *data ) { 668 fprintf( stderr, "SASL username: %s\n", data ); 669 } 670 671#if SASL_VERSION_MAJOR < 2 672 saslrc = sasl_getprop( ctx, SASL_REALM, 673 (SASL_CONST void **) &data ); 674 if( saslrc == SASL_OK && data && *data ) { 675 fprintf( stderr, "SASL realm: %s\n", data ); 676 } 677#endif 678 } 679 680 ssf = NULL; 681 saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); 682 if( saslrc == SASL_OK ) { 683 if( flags != LDAP_SASL_QUIET ) { 684 fprintf( stderr, "SASL SSF: %lu\n", 685 (unsigned long) *ssf ); 686 } 687 688 if( ssf && *ssf ) { 689 if ( ld->ld_defconn->lconn_sasl_sockctx ) { 690 sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx; 691 sasl_dispose( &oldctx ); 692 ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); 693 } 694 ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx ); 695 ld->ld_defconn->lconn_sasl_sockctx = ctx; 696 697 if( flags != LDAP_SASL_QUIET ) { 698 fprintf( stderr, "SASL data security layer installed.\n" ); 699 } 700 } 701 } 702 ld->ld_defconn->lconn_sasl_authctx = ctx; 703 704done: 705 return rc; 706} 707 708int 709ldap_int_sasl_external( 710 LDAP *ld, 711 LDAPConn *conn, 712 const char * authid, 713 ber_len_t ssf ) 714{ 715 int sc; 716 sasl_conn_t *ctx; 717#if SASL_VERSION_MAJOR < 2 718 sasl_external_properties_t extprops; 719#else 720 sasl_ssf_t sasl_ssf = ssf; 721#endif 722 723 ctx = conn->lconn_sasl_authctx; 724 725 if ( ctx == NULL ) { 726 return LDAP_LOCAL_ERROR; 727 } 728 729#if SASL_VERSION_MAJOR >= 2 730 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); 731 if ( sc == SASL_OK ) 732 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); 733#else 734 memset( &extprops, '\0', sizeof(extprops) ); 735 extprops.ssf = ssf; 736 extprops.auth_id = (char *) authid; 737 738 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, 739 (void *) &extprops ); 740#endif 741 742 if ( sc != SASL_OK ) { 743 return LDAP_LOCAL_ERROR; 744 } 745 746 return LDAP_SUCCESS; 747} 748 749 750#define GOT_MINSSF 1 751#define GOT_MAXSSF 2 752#define GOT_MAXBUF 4 753 754static struct { 755 struct berval key; 756 int sflag; 757 int ival; 758 int idef; 759} sprops[] = { 760 { BER_BVC("none"), 0, 0, 0 }, 761 { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 }, 762 { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 }, 763 { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 }, 764 { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 }, 765 { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 }, 766 { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 }, 767 { BER_BVC("minssf="), 0, GOT_MINSSF, 0 }, 768 { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX }, 769 { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 }, 770 { BER_BVNULL, 0, 0, 0 } 771}; 772 773void ldap_pvt_sasl_secprops_unparse( 774 sasl_security_properties_t *secprops, 775 struct berval *out ) 776{ 777 int i, l = 0; 778 int comma; 779 char *ptr; 780 781 if ( secprops == NULL || out == NULL ) { 782 return; 783 } 784 785 comma = 0; 786 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { 787 if ( sprops[i].ival ) { 788 int v = 0; 789 790 switch( sprops[i].ival ) { 791 case GOT_MINSSF: v = secprops->min_ssf; break; 792 case GOT_MAXSSF: v = secprops->max_ssf; break; 793 case GOT_MAXBUF: v = secprops->maxbufsize; break; 794 } 795 /* It is the default, ignore it */ 796 if ( v == sprops[i].idef ) continue; 797 798 l += sprops[i].key.bv_len + 24; 799 } else if ( sprops[i].sflag ) { 800 if ( sprops[i].sflag & secprops->security_flags ) { 801 l += sprops[i].key.bv_len; 802 } 803 } else if ( secprops->security_flags == 0 ) { 804 l += sprops[i].key.bv_len; 805 } 806 if ( comma ) l++; 807 comma = 1; 808 } 809 l++; 810 811 out->bv_val = LDAP_MALLOC( l ); 812 if ( out->bv_val == NULL ) { 813 out->bv_len = 0; 814 return; 815 } 816 817 ptr = out->bv_val; 818 comma = 0; 819 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { 820 if ( sprops[i].ival ) { 821 int v = 0; 822 823 switch( sprops[i].ival ) { 824 case GOT_MINSSF: v = secprops->min_ssf; break; 825 case GOT_MAXSSF: v = secprops->max_ssf; break; 826 case GOT_MAXBUF: v = secprops->maxbufsize; break; 827 } 828 /* It is the default, ignore it */ 829 if ( v == sprops[i].idef ) continue; 830 831 if ( comma ) *ptr++ = ','; 832 ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v ); 833 comma = 1; 834 } else if ( sprops[i].sflag ) { 835 if ( sprops[i].sflag & secprops->security_flags ) { 836 if ( comma ) *ptr++ = ','; 837 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); 838 comma = 1; 839 } 840 } else if ( secprops->security_flags == 0 ) { 841 if ( comma ) *ptr++ = ','; 842 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); 843 comma = 1; 844 } 845 } 846 out->bv_len = ptr - out->bv_val; 847} 848 849int ldap_pvt_sasl_secprops( 850 const char *in, 851 sasl_security_properties_t *secprops ) 852{ 853 unsigned i, j, l; 854 char **props; 855 unsigned sflags = 0; 856 int got_sflags = 0; 857 sasl_ssf_t max_ssf = 0; 858 int got_max_ssf = 0; 859 sasl_ssf_t min_ssf = 0; 860 int got_min_ssf = 0; 861 unsigned maxbufsize = 0; 862 int got_maxbufsize = 0; 863 864 if( secprops == NULL ) { 865 return LDAP_PARAM_ERROR; 866 } 867 props = ldap_str2charray( in, "," ); 868 if( props == NULL ) { 869 return LDAP_PARAM_ERROR; 870 } 871 872 for( i=0; props[i]; i++ ) { 873 l = strlen( props[i] ); 874 for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) { 875 if ( l < sprops[j].key.bv_len ) continue; 876 if ( strncasecmp( props[i], sprops[j].key.bv_val, 877 sprops[j].key.bv_len )) continue; 878 if ( sprops[j].ival ) { 879 unsigned v; 880 char *next = NULL; 881 if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] )) 882 continue; 883 v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 ); 884 if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue; 885 switch( sprops[j].ival ) { 886 case GOT_MINSSF: 887 min_ssf = v; got_min_ssf++; break; 888 case GOT_MAXSSF: 889 max_ssf = v; got_max_ssf++; break; 890 case GOT_MAXBUF: 891 maxbufsize = v; got_maxbufsize++; break; 892 } 893 } else { 894 if ( props[i][sprops[j].key.bv_len] ) continue; 895 if ( sprops[j].sflag ) 896 sflags |= sprops[j].sflag; 897 else 898 sflags = 0; 899 got_sflags++; 900 } 901 break; 902 } 903 if ( BER_BVISNULL( &sprops[j].key )) { 904 ldap_charray_free( props ); 905 return LDAP_NOT_SUPPORTED; 906 } 907 } 908 909 if(got_sflags) { 910 secprops->security_flags = sflags; 911 } 912 if(got_min_ssf) { 913 secprops->min_ssf = min_ssf; 914 } 915 if(got_max_ssf) { 916 secprops->max_ssf = max_ssf; 917 } 918 if(got_maxbufsize) { 919 secprops->maxbufsize = maxbufsize; 920 } 921 922 ldap_charray_free( props ); 923 return LDAP_SUCCESS; 924} 925 926int 927ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg ) 928{ 929 int rc; 930 931 switch( option ) { 932 case LDAP_OPT_X_SASL_SECPROPS: 933 rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops ); 934 if( rc == LDAP_SUCCESS ) return 0; 935 } 936 937 return -1; 938} 939 940int 941ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) 942{ 943 if ( option == LDAP_OPT_X_SASL_MECHLIST ) { 944 if ( ldap_int_sasl_init() ) 945 return -1; 946 *(char ***)arg = (char **)sasl_global_listmech(); 947 return 0; 948 } 949 950 if ( ld == NULL ) 951 return -1; 952 953 switch ( option ) { 954 case LDAP_OPT_X_SASL_MECH: { 955 *(char **)arg = ld->ld_options.ldo_def_sasl_mech 956 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL; 957 } break; 958 case LDAP_OPT_X_SASL_REALM: { 959 *(char **)arg = ld->ld_options.ldo_def_sasl_realm 960 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL; 961 } break; 962 case LDAP_OPT_X_SASL_AUTHCID: { 963 *(char **)arg = ld->ld_options.ldo_def_sasl_authcid 964 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL; 965 } break; 966 case LDAP_OPT_X_SASL_AUTHZID: { 967 *(char **)arg = ld->ld_options.ldo_def_sasl_authzid 968 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL; 969 } break; 970 971 case LDAP_OPT_X_SASL_SSF: { 972 int sc; 973 sasl_ssf_t *ssf; 974 sasl_conn_t *ctx; 975 976 if( ld->ld_defconn == NULL ) { 977 return -1; 978 } 979 980 ctx = ld->ld_defconn->lconn_sasl_sockctx; 981 982 if ( ctx == NULL ) { 983 return -1; 984 } 985 986 sc = sasl_getprop( ctx, SASL_SSF, 987 (SASL_CONST void **)(char *) &ssf ); 988 989 if ( sc != SASL_OK ) { 990 return -1; 991 } 992 993 *(ber_len_t *)arg = *ssf; 994 } break; 995 996 case LDAP_OPT_X_SASL_SSF_EXTERNAL: 997 /* this option is write only */ 998 return -1; 999 1000 case LDAP_OPT_X_SASL_SSF_MIN: 1001 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf; 1002 break; 1003 case LDAP_OPT_X_SASL_SSF_MAX: 1004 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf; 1005 break; 1006 case LDAP_OPT_X_SASL_MAXBUFSIZE: 1007 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize; 1008 break; 1009 case LDAP_OPT_X_SASL_NOCANON: 1010 *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 1011 break; 1012 1013 case LDAP_OPT_X_SASL_USERNAME: { 1014 int sc; 1015 char *username; 1016 sasl_conn_t *ctx; 1017 1018 if( ld->ld_defconn == NULL ) { 1019 return -1; 1020 } 1021 1022 ctx = ld->ld_defconn->lconn_sasl_authctx; 1023 1024 if ( ctx == NULL ) { 1025 return -1; 1026 } 1027 1028 sc = sasl_getprop( ctx, SASL_USERNAME, 1029 (SASL_CONST void **)(char **) &username ); 1030 1031 if ( sc != SASL_OK ) { 1032 return -1; 1033 } 1034 1035 *(char **)arg = username ? LDAP_STRDUP( username ) : NULL; 1036 } break; 1037 1038 case LDAP_OPT_X_SASL_SECPROPS: 1039 /* this option is write only */ 1040 return -1; 1041 1042 case LDAP_OPT_SASL_FQDN: { 1043 *(char **)arg = ld->ld_options.ldo_sasl_fqdn 1044 ? LDAP_STRDUP( ld->ld_options.ldo_sasl_fqdn ) : NULL; 1045 }break; 1046 1047#ifdef SASL_GSS_CREDS 1048 case LDAP_OPT_X_SASL_GSS_CREDS: { 1049 sasl_conn_t *ctx; 1050 int sc; 1051 1052 if ( ld->ld_defconn == NULL ) 1053 return -1; 1054 1055 ctx = ld->ld_defconn->lconn_sasl_authctx; 1056 if ( ctx == NULL ) 1057 return -1; 1058 1059 sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg ); 1060 if ( sc != SASL_OK ) 1061 return -1; 1062 } 1063 break; 1064#endif 1065 1066 default: 1067 return -1; 1068 } 1069 return 0; 1070} 1071 1072int 1073ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) 1074{ 1075 if ( ld == NULL ) 1076 return -1; 1077 1078 if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON ) 1079 return -1; 1080 1081 switch ( option ) { 1082 case LDAP_OPT_X_SASL_SSF: 1083 case LDAP_OPT_X_SASL_USERNAME: 1084 /* This option is read-only */ 1085 return -1; 1086 1087 case LDAP_OPT_X_SASL_SSF_EXTERNAL: { 1088 int sc; 1089#if SASL_VERSION_MAJOR < 2 1090 sasl_external_properties_t extprops; 1091#else 1092 sasl_ssf_t sasl_ssf; 1093#endif 1094 sasl_conn_t *ctx; 1095 1096 if( ld->ld_defconn == NULL ) { 1097 return -1; 1098 } 1099 1100 ctx = ld->ld_defconn->lconn_sasl_authctx; 1101 1102 if ( ctx == NULL ) { 1103 return -1; 1104 } 1105 1106#if SASL_VERSION_MAJOR >= 2 1107 sasl_ssf = * (ber_len_t *)arg; 1108 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf); 1109#else 1110 memset(&extprops, 0L, sizeof(extprops)); 1111 1112 extprops.ssf = * (ber_len_t *) arg; 1113 1114 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, 1115 (void *) &extprops ); 1116#endif 1117 1118 if ( sc != SASL_OK ) { 1119 return -1; 1120 } 1121 } break; 1122 1123 case LDAP_OPT_X_SASL_SSF_MIN: 1124 ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg; 1125 break; 1126 case LDAP_OPT_X_SASL_SSF_MAX: 1127 ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg; 1128 break; 1129 case LDAP_OPT_X_SASL_MAXBUFSIZE: 1130 ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg; 1131 break; 1132 case LDAP_OPT_X_SASL_NOCANON: 1133 if ( arg == LDAP_OPT_OFF ) { 1134 LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 1135 } else { 1136 LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 1137 } 1138 //Apple specific - no reverse lookup patch for sasl 1139 if ( ld->ld_options.ldo_sasl_secprops.property_names != NULL ) 1140 LDAP_FREE( ld->ld_options.ldo_sasl_secprops.property_names ); 1141 if ( ld->ld_options.ldo_sasl_secprops.property_values != NULL ) 1142 LDAP_FREE( ld->ld_options.ldo_sasl_secprops.property_values ); 1143 1144 /* basically the property value is either a pointer to something, or it is NULL if not set */ 1145 ld->ld_options.ldo_sasl_secprops.property_names = (const char **) LDAP_CALLOC( 2, sizeof(char *) ); 1146 ld->ld_options.ldo_sasl_secprops.property_names[0] = "KRB5-GSSAPI"; 1147 ld->ld_options.ldo_sasl_secprops.property_values = (const char **) LDAP_CALLOC( 2, sizeof(char *) ); 1148 ld->ld_options.ldo_sasl_secprops.property_values[0] = (arg == LDAP_OPT_OFF ? NULL : "1"); 1149 1150 break; 1151 1152 case LDAP_OPT_X_SASL_SECPROPS: { 1153 int sc; 1154 sc = ldap_pvt_sasl_secprops( (char *) arg, 1155 &ld->ld_options.ldo_sasl_secprops ); 1156 1157 return sc == LDAP_SUCCESS ? 0 : -1; 1158 } 1159 1160 case LDAP_OPT_SASL_FQDN: 1161 if (arg) 1162 ld->ld_options.ldo_sasl_fqdn = LDAP_STRDUP( (char *)arg) ; 1163 break; 1164#ifdef SASL_GSS_CREDS 1165 case LDAP_OPT_X_SASL_GSS_CREDS: { 1166 sasl_conn_t *ctx; 1167 int sc; 1168 1169 if ( ld->ld_defconn == NULL ) 1170 return -1; 1171 1172 ctx = ld->ld_defconn->lconn_sasl_authctx; 1173 if ( ctx == NULL ) 1174 return -1; 1175 1176 sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg ); 1177 if ( sc != SASL_OK ) 1178 return -1; 1179 } 1180 break; 1181#endif 1182 1183 default: 1184 return -1; 1185 } 1186 return 0; 1187} 1188 1189#ifdef LDAP_R_COMPILE 1190#define LDAP_DEBUG_R_SASL 1191void *ldap_pvt_sasl_mutex_new(void) 1192{ 1193 ldap_pvt_thread_mutex_t *mutex; 1194 1195 mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1, 1196 sizeof(ldap_pvt_thread_mutex_t) ); 1197 1198 if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) { 1199 return mutex; 1200 } 1201#ifndef LDAP_DEBUG_R_SASL 1202 assert( 0 ); 1203#endif /* !LDAP_DEBUG_R_SASL */ 1204 return NULL; 1205} 1206 1207int ldap_pvt_sasl_mutex_lock(void *mutex) 1208{ 1209#ifdef LDAP_DEBUG_R_SASL 1210 if ( mutex == NULL ) { 1211 return SASL_OK; 1212 } 1213#else /* !LDAP_DEBUG_R_SASL */ 1214 assert( mutex != NULL ); 1215#endif /* !LDAP_DEBUG_R_SASL */ 1216 return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex ) 1217 ? SASL_FAIL : SASL_OK; 1218} 1219 1220int ldap_pvt_sasl_mutex_unlock(void *mutex) 1221{ 1222#ifdef LDAP_DEBUG_R_SASL 1223 if ( mutex == NULL ) { 1224 return SASL_OK; 1225 } 1226#else /* !LDAP_DEBUG_R_SASL */ 1227 assert( mutex != NULL ); 1228#endif /* !LDAP_DEBUG_R_SASL */ 1229 return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex ) 1230 ? SASL_FAIL : SASL_OK; 1231} 1232 1233void ldap_pvt_sasl_mutex_dispose(void *mutex) 1234{ 1235#ifdef LDAP_DEBUG_R_SASL 1236 if ( mutex == NULL ) { 1237 return; 1238 } 1239#else /* !LDAP_DEBUG_R_SASL */ 1240 assert( mutex != NULL ); 1241#endif /* !LDAP_DEBUG_R_SASL */ 1242 (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex ); 1243 LDAP_FREE( mutex ); 1244} 1245#endif 1246 1247#else 1248int ldap_int_sasl_init( void ) 1249{ return LDAP_SUCCESS; } 1250 1251int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) 1252{ return LDAP_SUCCESS; } 1253 1254int 1255ldap_int_sasl_bind( 1256 LDAP *ld, 1257 const char *dn, 1258 const char *mechs, 1259 LDAPControl **sctrls, 1260 LDAPControl **cctrls, 1261 unsigned flags, 1262 LDAP_SASL_INTERACT_PROC *interact, 1263 void *defaults, 1264 LDAPMessage *result, 1265 const char **rmech, 1266 int *msgid ) 1267{ return LDAP_NOT_SUPPORTED; } 1268 1269int 1270ldap_int_sasl_external( 1271 LDAP *ld, 1272 LDAPConn *conn, 1273 const char * authid, 1274 ber_len_t ssf ) 1275{ return LDAP_SUCCESS; } 1276 1277#endif /* HAVE_CYRUS_SASL */ 1278