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