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 65 static 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 */ 80 int 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 133 static void 134 sb_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 151 static ber_int_t 152 sb_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 179 static ber_int_t 180 sb_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 208 static void 209 sb_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 220 static void 221 sb_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 234 static 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 242 int 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 252 void ldap_pvt_sasl_remove( Sockbuf *sb ) 253 { 254 ldap_pvt_sasl_generic_remove( sb ); 255 } 256 257 static int 258 sasl_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 317 int 318 ldap_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 354 int 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 376 int 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 390 void *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 435 int 436 ldap_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 723 success: 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 766 done: 767 return rc; 768 } 769 770 int 771 ldap_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 816 static 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 835 void 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 911 int 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 988 int 989 ldap_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 1010 int 1011 ldap_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 1139 int 1140 ldap_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 1253 void *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 1270 int 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 1283 int 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 1296 void 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 1311 int ldap_int_sasl_init( void ) 1312 { return LDAP_SUCCESS; } 1313 1314 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) 1315 { return LDAP_SUCCESS; } 1316 1317 int 1318 ldap_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 1332 int 1333 ldap_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