cyrus.c revision 1.1.1.7
1/* $NetBSD: cyrus.c,v 1.1.1.7 2019/08/08 13:31:15 christos Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2019 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.1.1.7 2019/08/08 13:31:15 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 Debug( LDAP_DEBUG_ANY, 97 "ldap_int_sasl_init: SASL library version mismatch:" 98 " expected " SASL_VERSION_STRING "," 99 " got %s\n", version, 0, 0 ); 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 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n", 347 host, 0, 0 ); 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 369 return LDAP_SUCCESS; 370} 371 372int 373ldap_int_sasl_bind( 374 LDAP *ld, 375 const char *dn, 376 const char *mechs, 377 LDAPControl **sctrls, 378 LDAPControl **cctrls, 379 unsigned flags, 380 LDAP_SASL_INTERACT_PROC *interact, 381 void *defaults, 382 LDAPMessage *result, 383 const char **rmech, 384 int *msgid ) 385{ 386 const char *mech; 387 sasl_ssf_t *ssf; 388 sasl_conn_t *ctx; 389 sasl_interact_t *prompts = NULL; 390 struct berval ccred = BER_BVNULL; 391 int saslrc, rc; 392 unsigned credlen; 393#if !defined(_WIN32) 394 char my_hostname[HOST_NAME_MAX + 1]; 395#endif 396 int free_saslhost = 0; 397 398 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", 399 mechs ? mechs : "<null>", 0, 0 ); 400 401 /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ 402 if (ld->ld_version < LDAP_VERSION3) { 403 ld->ld_errno = LDAP_NOT_SUPPORTED; 404 return ld->ld_errno; 405 } 406 407 /* Starting a Bind */ 408 if ( !result ) { 409 const char *pmech = NULL; 410 sasl_conn_t *oldctx; 411 ber_socket_t sd; 412 void *ssl; 413 414 rc = 0; 415 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 416 ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); 417 418 if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) { 419 /* not connected yet */ 420 421 rc = ldap_open_defconn( ld ); 422 423 if ( rc == 0 ) { 424 ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, 425 LBER_SB_OPT_GET_FD, &sd ); 426 427 if( sd == AC_SOCKET_INVALID ) { 428 ld->ld_errno = LDAP_LOCAL_ERROR; 429 rc = ld->ld_errno; 430 } 431 } 432 } 433 if ( rc == 0 && ld->ld_defconn && 434 ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) { 435 rc = ldap_int_check_async_open( ld, sd ); 436 } 437 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 438 if( rc != 0 ) return ld->ld_errno; 439 440 oldctx = ld->ld_defconn->lconn_sasl_authctx; 441 442 /* If we already have an authentication context, clear it out */ 443 if( oldctx ) { 444 if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { 445 sasl_dispose( &oldctx ); 446 } 447 ld->ld_defconn->lconn_sasl_authctx = NULL; 448 } 449 450 { 451 char *saslhost; 452 int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, 453 LDAP_BOOL_SASL_NOCANON ); 454 455 /* If we don't need to canonicalize just use the host 456 * from the LDAP URI. 457 * Always use the result of gethostname() for LDAPI. 458 * Skip for Windows which doesn't support LDAPI. 459 */ 460#if !defined(_WIN32) 461 if (ld->ld_defconn->lconn_server->lud_scheme != NULL && 462 strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) { 463 rc = gethostname(my_hostname, HOST_NAME_MAX + 1); 464 if (rc == 0) { 465 saslhost = my_hostname; 466 } else { 467 saslhost = "localhost"; 468 } 469 } else 470#endif 471 if ( nocanon ) 472 saslhost = ld->ld_defconn->lconn_server->lud_host; 473 else { 474 saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, 475 "localhost" ); 476 free_saslhost = 1; 477 } 478 rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); 479 if ( free_saslhost ) 480 LDAP_FREE( saslhost ); 481 } 482 483 if ( rc != LDAP_SUCCESS ) return rc; 484 485 ctx = ld->ld_defconn->lconn_sasl_authctx; 486 487#ifdef HAVE_TLS 488 /* Check for TLS */ 489 ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); 490 if ( ssl ) { 491 struct berval authid = BER_BVNULL; 492 ber_len_t fac; 493 494 fac = ldap_pvt_tls_get_strength( ssl ); 495 /* failure is OK, we just can't use SASL EXTERNAL */ 496 (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); 497 498 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); 499 LDAP_FREE( authid.bv_val ); 500 } 501#endif 502 503#if !defined(_WIN32) 504 /* Check for local */ 505 if ( ldap_pvt_url_scheme2proto( 506 ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) 507 { 508 char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," 509 "cn=peercred,cn=external,cn=auth")]; 510 sprintf( authid, "gidNumber=%u+uidNumber=%u," 511 "cn=peercred,cn=external,cn=auth", 512 getegid(), geteuid() ); 513 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, 514 LDAP_PVT_SASL_LOCAL_SSF ); 515 } 516#endif 517 518 /* (re)set security properties */ 519 sasl_setprop( ctx, SASL_SEC_PROPS, 520 &ld->ld_options.ldo_sasl_secprops ); 521 522 mech = NULL; 523 524 do { 525 saslrc = sasl_client_start( ctx, 526 mechs, 527#if SASL_VERSION_MAJOR < 2 528 NULL, 529#endif 530 &prompts, 531 (SASL_CONST char **)&ccred.bv_val, 532 &credlen, 533 &mech ); 534 535 if( pmech == NULL && mech != NULL ) { 536 pmech = mech; 537 *rmech = mech; 538 539 if( flags != LDAP_SASL_QUIET ) { 540 fprintf(stderr, 541 "SASL/%s authentication started\n", 542 pmech ); 543 } 544 } 545 546 if( saslrc == SASL_INTERACT ) { 547 int res; 548 if( !interact ) break; 549 res = (interact)( ld, flags, defaults, prompts ); 550 551 if( res != LDAP_SUCCESS ) break; 552 } 553 } while ( saslrc == SASL_INTERACT ); 554 rc = LDAP_SASL_BIND_IN_PROGRESS; 555 556 } else { 557 /* continuing an in-progress Bind */ 558 struct berval *scred = NULL; 559 560 ctx = ld->ld_defconn->lconn_sasl_authctx; 561 562 rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 ); 563 if ( rc != LDAP_SUCCESS ) { 564 if ( scred ) 565 ber_bvfree( scred ); 566 goto done; 567 } 568 569 rc = ldap_result2error( ld, result, 0 ); 570 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { 571 if( scred ) { 572 /* and server provided us with data? */ 573 Debug( LDAP_DEBUG_TRACE, 574 "ldap_int_sasl_bind: rc=%d len=%ld\n", 575 rc, scred ? (long) scred->bv_len : -1L, 0 ); 576 ber_bvfree( scred ); 577 scred = NULL; 578 } 579 goto done; 580 } 581 582 mech = *rmech; 583 if ( rc == LDAP_SUCCESS && mech == NULL ) { 584 if ( scred ) 585 ber_bvfree( scred ); 586 goto success; 587 } 588 589 do { 590 if( ! scred ) { 591 /* no data! */ 592 Debug( LDAP_DEBUG_TRACE, 593 "ldap_int_sasl_bind: no data in step!\n", 594 0, 0, 0 ); 595 } 596 597 saslrc = sasl_client_step( ctx, 598 (scred == NULL) ? NULL : scred->bv_val, 599 (scred == NULL) ? 0 : scred->bv_len, 600 &prompts, 601 (SASL_CONST char **)&ccred.bv_val, 602 &credlen ); 603 604 Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n", 605 saslrc, 0, 0 ); 606 607 if( saslrc == SASL_INTERACT ) { 608 int res; 609 if( !interact ) break; 610 res = (interact)( ld, flags, defaults, prompts ); 611 if( res != LDAP_SUCCESS ) break; 612 } 613 } while ( saslrc == SASL_INTERACT ); 614 615 ber_bvfree( scred ); 616 } 617 618 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { 619 rc = ld->ld_errno = sasl_err2ldap( saslrc ); 620#if SASL_VERSION_MAJOR >= 2 621 if ( ld->ld_error ) { 622 LDAP_FREE( ld->ld_error ); 623 } 624 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); 625#endif 626 goto done; 627 } 628 629 if ( saslrc == SASL_OK ) 630 *rmech = NULL; 631 632 ccred.bv_len = credlen; 633 634 if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) { 635 rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid ); 636 637 if ( ccred.bv_val != NULL ) { 638#if SASL_VERSION_MAJOR < 2 639 LDAP_FREE( ccred.bv_val ); 640#endif 641 ccred.bv_val = NULL; 642 } 643 if ( rc == LDAP_SUCCESS ) 644 rc = LDAP_SASL_BIND_IN_PROGRESS; 645 goto done; 646 } 647 648success: 649 /* Conversation was completed successfully by now */ 650 if( flags != LDAP_SASL_QUIET ) { 651 char *data; 652 saslrc = sasl_getprop( ctx, SASL_USERNAME, 653 (SASL_CONST void **)(char *) &data ); 654 if( saslrc == SASL_OK && data && *data ) { 655 fprintf( stderr, "SASL username: %s\n", data ); 656 } 657 658#if SASL_VERSION_MAJOR < 2 659 saslrc = sasl_getprop( ctx, SASL_REALM, 660 (SASL_CONST void **) &data ); 661 if( saslrc == SASL_OK && data && *data ) { 662 fprintf( stderr, "SASL realm: %s\n", data ); 663 } 664#endif 665 } 666 667 ssf = NULL; 668 saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); 669 if( saslrc == SASL_OK ) { 670 if( flags != LDAP_SASL_QUIET ) { 671 fprintf( stderr, "SASL SSF: %lu\n", 672 (unsigned long) *ssf ); 673 } 674 675 if( ssf && *ssf ) { 676 if ( ld->ld_defconn->lconn_sasl_sockctx ) { 677 sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx; 678 sasl_dispose( &oldctx ); 679 ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); 680 } 681 ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx ); 682 ld->ld_defconn->lconn_sasl_sockctx = ctx; 683 684 if( flags != LDAP_SASL_QUIET ) { 685 fprintf( stderr, "SASL data security layer installed.\n" ); 686 } 687 } 688 } 689 ld->ld_defconn->lconn_sasl_authctx = ctx; 690 691done: 692 return rc; 693} 694 695int 696ldap_int_sasl_external( 697 LDAP *ld, 698 LDAPConn *conn, 699 const char * authid, 700 ber_len_t ssf ) 701{ 702 int sc; 703 sasl_conn_t *ctx; 704#if SASL_VERSION_MAJOR < 2 705 sasl_external_properties_t extprops; 706#else 707 sasl_ssf_t sasl_ssf = ssf; 708#endif 709 710 ctx = conn->lconn_sasl_authctx; 711 712 if ( ctx == NULL ) { 713 return LDAP_LOCAL_ERROR; 714 } 715 716#if SASL_VERSION_MAJOR >= 2 717 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); 718 if ( sc == SASL_OK ) 719 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); 720#else 721 memset( &extprops, '\0', sizeof(extprops) ); 722 extprops.ssf = ssf; 723 extprops.auth_id = (char *) authid; 724 725 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, 726 (void *) &extprops ); 727#endif 728 729 if ( sc != SASL_OK ) { 730 return LDAP_LOCAL_ERROR; 731 } 732 733 return LDAP_SUCCESS; 734} 735 736 737#define GOT_MINSSF 1 738#define GOT_MAXSSF 2 739#define GOT_MAXBUF 4 740 741static struct { 742 struct berval key; 743 int sflag; 744 int ival; 745 int idef; 746} sprops[] = { 747 { BER_BVC("none"), 0, 0, 0 }, 748 { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 }, 749 { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 }, 750 { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 }, 751 { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 }, 752 { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 }, 753 { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 }, 754 { BER_BVC("minssf="), 0, GOT_MINSSF, 0 }, 755 { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX }, 756 { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 }, 757 { BER_BVNULL, 0, 0, 0 } 758}; 759 760void ldap_pvt_sasl_secprops_unparse( 761 sasl_security_properties_t *secprops, 762 struct berval *out ) 763{ 764 int i, l = 0; 765 int comma; 766 char *ptr; 767 768 if ( secprops == NULL || out == NULL ) { 769 return; 770 } 771 772 comma = 0; 773 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { 774 if ( sprops[i].ival ) { 775 int v = 0; 776 777 switch( sprops[i].ival ) { 778 case GOT_MINSSF: v = secprops->min_ssf; break; 779 case GOT_MAXSSF: v = secprops->max_ssf; break; 780 case GOT_MAXBUF: v = secprops->maxbufsize; break; 781 } 782 /* It is the default, ignore it */ 783 if ( v == sprops[i].idef ) continue; 784 785 l += sprops[i].key.bv_len + 24; 786 } else if ( sprops[i].sflag ) { 787 if ( sprops[i].sflag & secprops->security_flags ) { 788 l += sprops[i].key.bv_len; 789 } 790 } else if ( secprops->security_flags == 0 ) { 791 l += sprops[i].key.bv_len; 792 } 793 if ( comma ) l++; 794 comma = 1; 795 } 796 l++; 797 798 out->bv_val = LDAP_MALLOC( l ); 799 if ( out->bv_val == NULL ) { 800 out->bv_len = 0; 801 return; 802 } 803 804 ptr = out->bv_val; 805 comma = 0; 806 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { 807 if ( sprops[i].ival ) { 808 int v = 0; 809 810 switch( sprops[i].ival ) { 811 case GOT_MINSSF: v = secprops->min_ssf; break; 812 case GOT_MAXSSF: v = secprops->max_ssf; break; 813 case GOT_MAXBUF: v = secprops->maxbufsize; break; 814 } 815 /* It is the default, ignore it */ 816 if ( v == sprops[i].idef ) continue; 817 818 if ( comma ) *ptr++ = ','; 819 ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v ); 820 comma = 1; 821 } else if ( sprops[i].sflag ) { 822 if ( sprops[i].sflag & secprops->security_flags ) { 823 if ( comma ) *ptr++ = ','; 824 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); 825 comma = 1; 826 } 827 } else if ( secprops->security_flags == 0 ) { 828 if ( comma ) *ptr++ = ','; 829 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); 830 comma = 1; 831 } 832 } 833 out->bv_len = ptr - out->bv_val; 834} 835 836int ldap_pvt_sasl_secprops( 837 const char *in, 838 sasl_security_properties_t *secprops ) 839{ 840 unsigned i, j, l; 841 char **props; 842 unsigned sflags = 0; 843 int got_sflags = 0; 844 sasl_ssf_t max_ssf = 0; 845 int got_max_ssf = 0; 846 sasl_ssf_t min_ssf = 0; 847 int got_min_ssf = 0; 848 unsigned maxbufsize = 0; 849 int got_maxbufsize = 0; 850 851 if( secprops == NULL ) { 852 return LDAP_PARAM_ERROR; 853 } 854 props = ldap_str2charray( in, "," ); 855 if( props == NULL ) { 856 return LDAP_PARAM_ERROR; 857 } 858 859 for( i=0; props[i]; i++ ) { 860 l = strlen( props[i] ); 861 for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) { 862 if ( l < sprops[j].key.bv_len ) continue; 863 if ( strncasecmp( props[i], sprops[j].key.bv_val, 864 sprops[j].key.bv_len )) continue; 865 if ( sprops[j].ival ) { 866 unsigned v; 867 char *next = NULL; 868 if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] )) 869 continue; 870 v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 ); 871 if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue; 872 switch( sprops[j].ival ) { 873 case GOT_MINSSF: 874 min_ssf = v; got_min_ssf++; break; 875 case GOT_MAXSSF: 876 max_ssf = v; got_max_ssf++; break; 877 case GOT_MAXBUF: 878 maxbufsize = v; got_maxbufsize++; break; 879 } 880 } else { 881 if ( props[i][sprops[j].key.bv_len] ) continue; 882 if ( sprops[j].sflag ) 883 sflags |= sprops[j].sflag; 884 else 885 sflags = 0; 886 got_sflags++; 887 } 888 break; 889 } 890 if ( BER_BVISNULL( &sprops[j].key )) { 891 ldap_charray_free( props ); 892 return LDAP_NOT_SUPPORTED; 893 } 894 } 895 896 if(got_sflags) { 897 secprops->security_flags = sflags; 898 } 899 if(got_min_ssf) { 900 secprops->min_ssf = min_ssf; 901 } 902 if(got_max_ssf) { 903 secprops->max_ssf = max_ssf; 904 } 905 if(got_maxbufsize) { 906 secprops->maxbufsize = maxbufsize; 907 } 908 909 ldap_charray_free( props ); 910 return LDAP_SUCCESS; 911} 912 913int 914ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg ) 915{ 916 int rc; 917 918 switch( option ) { 919 case LDAP_OPT_X_SASL_SECPROPS: 920 rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops ); 921 if( rc == LDAP_SUCCESS ) return 0; 922 } 923 924 return -1; 925} 926 927int 928ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) 929{ 930 if ( option == LDAP_OPT_X_SASL_MECHLIST ) { 931 *(char ***)arg = (char **)sasl_global_listmech(); 932 return 0; 933 } 934 935 if ( ld == NULL ) 936 return -1; 937 938 switch ( option ) { 939 case LDAP_OPT_X_SASL_MECH: { 940 *(char **)arg = ld->ld_options.ldo_def_sasl_mech 941 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL; 942 } break; 943 case LDAP_OPT_X_SASL_REALM: { 944 *(char **)arg = ld->ld_options.ldo_def_sasl_realm 945 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL; 946 } break; 947 case LDAP_OPT_X_SASL_AUTHCID: { 948 *(char **)arg = ld->ld_options.ldo_def_sasl_authcid 949 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL; 950 } break; 951 case LDAP_OPT_X_SASL_AUTHZID: { 952 *(char **)arg = ld->ld_options.ldo_def_sasl_authzid 953 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL; 954 } break; 955 956 case LDAP_OPT_X_SASL_SSF: { 957 int sc; 958 sasl_ssf_t *ssf; 959 sasl_conn_t *ctx; 960 961 if( ld->ld_defconn == NULL ) { 962 return -1; 963 } 964 965 ctx = ld->ld_defconn->lconn_sasl_sockctx; 966 967 if ( ctx == NULL ) { 968 return -1; 969 } 970 971 sc = sasl_getprop( ctx, SASL_SSF, 972 (SASL_CONST void **)(char *) &ssf ); 973 974 if ( sc != SASL_OK ) { 975 return -1; 976 } 977 978 *(ber_len_t *)arg = *ssf; 979 } break; 980 981 case LDAP_OPT_X_SASL_SSF_EXTERNAL: 982 /* this option is write only */ 983 return -1; 984 985 case LDAP_OPT_X_SASL_SSF_MIN: 986 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf; 987 break; 988 case LDAP_OPT_X_SASL_SSF_MAX: 989 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf; 990 break; 991 case LDAP_OPT_X_SASL_MAXBUFSIZE: 992 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize; 993 break; 994 case LDAP_OPT_X_SASL_NOCANON: 995 *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 996 break; 997 998 case LDAP_OPT_X_SASL_USERNAME: { 999 int sc; 1000 char *username; 1001 sasl_conn_t *ctx; 1002 1003 if( ld->ld_defconn == NULL ) { 1004 return -1; 1005 } 1006 1007 ctx = ld->ld_defconn->lconn_sasl_authctx; 1008 1009 if ( ctx == NULL ) { 1010 return -1; 1011 } 1012 1013 sc = sasl_getprop( ctx, SASL_USERNAME, 1014 (SASL_CONST void **)(char **) &username ); 1015 1016 if ( sc != SASL_OK ) { 1017 return -1; 1018 } 1019 1020 *(char **)arg = username ? LDAP_STRDUP( username ) : NULL; 1021 } break; 1022 1023 case LDAP_OPT_X_SASL_SECPROPS: 1024 /* this option is write only */ 1025 return -1; 1026 1027#ifdef SASL_GSS_CREDS 1028 case LDAP_OPT_X_SASL_GSS_CREDS: { 1029 sasl_conn_t *ctx; 1030 int sc; 1031 1032 if ( ld->ld_defconn == NULL ) 1033 return -1; 1034 1035 ctx = ld->ld_defconn->lconn_sasl_authctx; 1036 if ( ctx == NULL ) 1037 return -1; 1038 1039 sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg ); 1040 if ( sc != SASL_OK ) 1041 return -1; 1042 } 1043 break; 1044#endif 1045 1046 default: 1047 return -1; 1048 } 1049 return 0; 1050} 1051 1052int 1053ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) 1054{ 1055 if ( ld == NULL ) 1056 return -1; 1057 1058 if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON ) 1059 return -1; 1060 1061 switch ( option ) { 1062 case LDAP_OPT_X_SASL_SSF: 1063 case LDAP_OPT_X_SASL_USERNAME: 1064 /* This option is read-only */ 1065 return -1; 1066 1067 case LDAP_OPT_X_SASL_SSF_EXTERNAL: { 1068 int sc; 1069#if SASL_VERSION_MAJOR < 2 1070 sasl_external_properties_t extprops; 1071#else 1072 sasl_ssf_t sasl_ssf; 1073#endif 1074 sasl_conn_t *ctx; 1075 1076 if( ld->ld_defconn == NULL ) { 1077 return -1; 1078 } 1079 1080 ctx = ld->ld_defconn->lconn_sasl_authctx; 1081 1082 if ( ctx == NULL ) { 1083 return -1; 1084 } 1085 1086#if SASL_VERSION_MAJOR >= 2 1087 sasl_ssf = * (ber_len_t *)arg; 1088 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf); 1089#else 1090 memset(&extprops, 0L, sizeof(extprops)); 1091 1092 extprops.ssf = * (ber_len_t *) arg; 1093 1094 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, 1095 (void *) &extprops ); 1096#endif 1097 1098 if ( sc != SASL_OK ) { 1099 return -1; 1100 } 1101 } break; 1102 1103 case LDAP_OPT_X_SASL_SSF_MIN: 1104 ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg; 1105 break; 1106 case LDAP_OPT_X_SASL_SSF_MAX: 1107 ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg; 1108 break; 1109 case LDAP_OPT_X_SASL_MAXBUFSIZE: 1110 ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg; 1111 break; 1112 case LDAP_OPT_X_SASL_NOCANON: 1113 if ( arg == LDAP_OPT_OFF ) { 1114 LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 1115 } else { 1116 LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); 1117 } 1118 break; 1119 1120 case LDAP_OPT_X_SASL_SECPROPS: { 1121 int sc; 1122 sc = ldap_pvt_sasl_secprops( (char *) arg, 1123 &ld->ld_options.ldo_sasl_secprops ); 1124 1125 return sc == LDAP_SUCCESS ? 0 : -1; 1126 } 1127 1128#ifdef SASL_GSS_CREDS 1129 case LDAP_OPT_X_SASL_GSS_CREDS: { 1130 sasl_conn_t *ctx; 1131 int sc; 1132 1133 if ( ld->ld_defconn == NULL ) 1134 return -1; 1135 1136 ctx = ld->ld_defconn->lconn_sasl_authctx; 1137 if ( ctx == NULL ) 1138 return -1; 1139 1140 sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg ); 1141 if ( sc != SASL_OK ) 1142 return -1; 1143 } 1144 break; 1145#endif 1146 1147 default: 1148 return -1; 1149 } 1150 return 0; 1151} 1152 1153#ifdef LDAP_R_COMPILE 1154#define LDAP_DEBUG_R_SASL 1155void *ldap_pvt_sasl_mutex_new(void) 1156{ 1157 ldap_pvt_thread_mutex_t *mutex; 1158 1159 mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1, 1160 sizeof(ldap_pvt_thread_mutex_t) ); 1161 1162 if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) { 1163 return mutex; 1164 } 1165 LDAP_FREE( mutex ); 1166#ifndef LDAP_DEBUG_R_SASL 1167 assert( 0 ); 1168#endif /* !LDAP_DEBUG_R_SASL */ 1169 return NULL; 1170} 1171 1172int ldap_pvt_sasl_mutex_lock(void *mutex) 1173{ 1174#ifdef LDAP_DEBUG_R_SASL 1175 if ( mutex == NULL ) { 1176 return SASL_OK; 1177 } 1178#else /* !LDAP_DEBUG_R_SASL */ 1179 assert( mutex != NULL ); 1180#endif /* !LDAP_DEBUG_R_SASL */ 1181 return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex ) 1182 ? SASL_FAIL : SASL_OK; 1183} 1184 1185int ldap_pvt_sasl_mutex_unlock(void *mutex) 1186{ 1187#ifdef LDAP_DEBUG_R_SASL 1188 if ( mutex == NULL ) { 1189 return SASL_OK; 1190 } 1191#else /* !LDAP_DEBUG_R_SASL */ 1192 assert( mutex != NULL ); 1193#endif /* !LDAP_DEBUG_R_SASL */ 1194 return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex ) 1195 ? SASL_FAIL : SASL_OK; 1196} 1197 1198void ldap_pvt_sasl_mutex_dispose(void *mutex) 1199{ 1200#ifdef LDAP_DEBUG_R_SASL 1201 if ( mutex == NULL ) { 1202 return; 1203 } 1204#else /* !LDAP_DEBUG_R_SASL */ 1205 assert( mutex != NULL ); 1206#endif /* !LDAP_DEBUG_R_SASL */ 1207 (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex ); 1208 LDAP_FREE( mutex ); 1209} 1210#endif 1211 1212#else 1213int ldap_int_sasl_init( void ) 1214{ return LDAP_SUCCESS; } 1215 1216int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) 1217{ return LDAP_SUCCESS; } 1218 1219int 1220ldap_int_sasl_bind( 1221 LDAP *ld, 1222 const char *dn, 1223 const char *mechs, 1224 LDAPControl **sctrls, 1225 LDAPControl **cctrls, 1226 unsigned flags, 1227 LDAP_SASL_INTERACT_PROC *interact, 1228 void *defaults, 1229 LDAPMessage *result, 1230 const char **rmech, 1231 int *msgid ) 1232{ return LDAP_NOT_SUPPORTED; } 1233 1234int 1235ldap_int_sasl_external( 1236 LDAP *ld, 1237 LDAPConn *conn, 1238 const char * authid, 1239 ber_len_t ssf ) 1240{ return LDAP_SUCCESS; } 1241 1242#endif /* HAVE_CYRUS_SASL */ 1243