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