1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #ifdef LDAP_SASLIO_HOOKS 27 #include <assert.h> 28 #include "ldap-int.h" 29 #include "../ber/lber-int.h" 30 #include <sasl/sasl.h> 31 #include <thread.h> 32 #include <synch.h> 33 34 #define SEARCH_TIMEOUT_SECS 120 35 #define NSLDAPI_SM_BUF 128 36 37 extern void *sasl_create_context(void); 38 extern void sasl_free_context(void *ctx); 39 extern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks); 40 extern int _sasl_client_new(void *ctx, const char *service, 41 const char *serverFQDN, const char *iplocalport, 42 const char *ipremoteport, 43 const sasl_callback_t *prompt_supp, 44 unsigned flags, sasl_conn_t **pconn); 45 extern int _sasl_server_init(void *ctx, 46 const sasl_callback_t *callbacks, const char *appname); 47 extern int _sasl_server_new(void *ctx, const char *service, 48 const char *serverFQDN, const char *user_realm, 49 const char *iplocalport, const char *ipremoteport, 50 const sasl_callback_t *callbacks, 51 unsigned flags, sasl_conn_t **pconn); 52 53 static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb ); 54 55 /* 56 * SASL Dependent routines 57 * 58 * SASL security and integrity options are supported through the 59 * use of the extended I/O functionality. Because the extended 60 * I/O functions may already be in use prior to enabling encryption, 61 * when SASL encryption is enabled, these routine interpose themselves 62 * over the existng extended I/O routines and add an additional level 63 * of indirection. 64 * IE: Before SASL: client->libldap->lber->extio 65 * After SASL: client->libldap->lber->saslio->extio 66 * Any extio functions are still used for the raw i/O [IE prldap] 67 * but SASL will decrypt before passing to lber. 68 * SASL cannot decrypt a stream so full packets must be read 69 * before proceeding. 70 */ 71 72 static int nsldapi_sasl_fail() 73 { 74 return( SASL_FAIL ); 75 } 76 77 /* 78 * Global SASL Init data 79 */ 80 81 static sasl_callback_t client_callbacks[] = { 82 { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL }, 83 { SASL_CB_GETREALM, NULL, NULL }, 84 { SASL_CB_USER, NULL, NULL }, 85 { SASL_CB_AUTHNAME, NULL, NULL }, 86 { SASL_CB_PASS, NULL, NULL }, 87 { SASL_CB_ECHOPROMPT, NULL, NULL }, 88 { SASL_CB_NOECHOPROMPT, NULL, NULL }, 89 { SASL_CB_LIST_END, NULL, NULL } 90 }; 91 static mutex_t sasl_mutex = DEFAULTMUTEX; 92 static int nsldapi_sasl_inited = 0; 93 static void *gctx; /* intentially not freed - avoid libsasl re-inits */ 94 95 int nsldapi_sasl_init( void ) 96 { 97 int saslrc; 98 99 mutex_lock(&sasl_mutex); 100 if ( nsldapi_sasl_inited ) { 101 mutex_unlock(&sasl_mutex); 102 return( 0 ); 103 } 104 if ((gctx = (void *)sasl_create_context()) != NULL) { 105 saslrc = _sasl_client_init(gctx, client_callbacks); 106 if (saslrc == SASL_OK ) { 107 nsldapi_sasl_inited = 1; 108 mutex_unlock(&sasl_mutex); 109 return( 0 ); 110 } 111 } 112 mutex_unlock(&sasl_mutex); 113 return( -1 ); 114 } 115 116 /* 117 * SASL encryption routines 118 */ 119 120 /* 121 * Get the 4 octet header [size] for a sasl encrypted buffer. 122 * See RFC222 [section 3]. 123 */ 124 125 static int 126 nsldapi_sasl_pktlen( char *buf, int maxbufsize ) 127 { 128 int size; 129 130 #if defined( _WINDOWS ) || defined( _WIN32 ) 131 size = ntohl(*(long *)buf); 132 #else 133 size = ntohl(*(uint32_t *)buf); 134 #endif 135 136 if ( size < 0 || size > maxbufsize ) { 137 return (-1 ); 138 } 139 140 return( size + 4 ); /* include the first 4 bytes */ 141 } 142 143 static int 144 nsldapi_sasl_read( int s, void *buf, int len, 145 struct lextiof_socket_private *arg) 146 { 147 Sockbuf *sb = (Sockbuf *)arg; 148 LDAP *ld; 149 const char *dbuf; 150 char *cp; 151 int ret; 152 unsigned dlen, blen; 153 154 if (sb == NULL) { 155 return( -1 ); 156 } 157 158 ld = (LDAP *)sb->sb_sasl_prld; 159 if (ld == NULL) { 160 return( -1 ); 161 } 162 163 /* Is there anything left in the existing buffer? */ 164 if ((ret = sb->sb_sasl_ilen) > 0) { 165 ret = (ret > len ? len : ret); 166 SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret ); 167 if (ret == sb->sb_sasl_ilen) { 168 sb->sb_sasl_ilen = 0; 169 sb->sb_sasl_iptr = NULL; 170 } else { 171 sb->sb_sasl_ilen -= ret; 172 sb->sb_sasl_iptr += ret; 173 } 174 return( ret ); 175 } 176 177 /* buffer is empty - fill it */ 178 cp = sb->sb_sasl_ibuf; 179 dlen = 0; 180 181 /* Read the length of the packet */ 182 while ( dlen < 4 ) { 183 if (sb->sb_sasl_fns.lbextiofn_read != NULL) { 184 ret = sb->sb_sasl_fns.lbextiofn_read( 185 s, cp, 4 - dlen, 186 sb->sb_sasl_fns.lbextiofn_socket_arg); 187 } else { 188 ret = read( sb->sb_sd, cp, 4 - dlen ); 189 } 190 #ifdef EINTR 191 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) 192 continue; 193 #endif 194 if ( ret <= 0 ) 195 return( ret ); 196 197 cp += ret; 198 dlen += ret; 199 } 200 201 blen = 4; 202 203 ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz ); 204 if (ret < 0) { 205 LDAP_SET_ERRNO(ld, EIO); 206 return( -1 ); 207 } 208 dlen = ret - dlen; 209 210 /* read the rest of the encrypted packet */ 211 while ( dlen > 0 ) { 212 if (sb->sb_sasl_fns.lbextiofn_read != NULL) { 213 ret = sb->sb_sasl_fns.lbextiofn_read( 214 s, cp, dlen, 215 sb->sb_sasl_fns.lbextiofn_socket_arg); 216 } else { 217 ret = read( sb->sb_sd, cp, dlen ); 218 } 219 220 #ifdef EINTR 221 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) 222 continue; 223 #endif 224 if ( ret <= 0 ) 225 return( ret ); 226 227 cp += ret; 228 blen += ret; 229 dlen -= ret; 230 } 231 232 /* Decode the packet */ 233 ret = sasl_decode( sb->sb_sasl_ctx, 234 sb->sb_sasl_ibuf, blen, 235 &dbuf, &dlen); 236 if ( ret != SASL_OK ) { 237 /* sb_sasl_read: failed to decode packet, drop it, error */ 238 sb->sb_sasl_iptr = NULL; 239 sb->sb_sasl_ilen = 0; 240 LDAP_SET_ERRNO(ld, EIO); 241 return( -1 ); 242 } 243 244 /* copy decrypted packet to the input buffer */ 245 SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen ); 246 sb->sb_sasl_iptr = sb->sb_sasl_ibuf; 247 sb->sb_sasl_ilen = dlen; 248 249 ret = (dlen > (unsigned) len ? len : dlen); 250 SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret ); 251 if (ret == sb->sb_sasl_ilen) { 252 sb->sb_sasl_ilen = 0; 253 sb->sb_sasl_iptr = NULL; 254 } else { 255 sb->sb_sasl_ilen -= ret; 256 sb->sb_sasl_iptr += ret; 257 } 258 return( ret ); 259 } 260 261 static int 262 nsldapi_sasl_write( int s, const void *buf, int len, 263 struct lextiof_socket_private *arg) 264 { 265 Sockbuf *sb = (Sockbuf *)arg; 266 int ret; 267 const char *obuf, *optr; 268 unsigned olen; 269 270 if (sb == NULL) { 271 return( -1 ); 272 } 273 274 /* encode the next packet. */ 275 ret = sasl_encode( sb->sb_sasl_ctx, buf, (unsigned)len, &obuf, &olen); 276 if ( ret != SASL_OK ) { 277 /* XXX Log error? "sb_sasl_write: failed to encode packet..." */ 278 return( -1 ); 279 } 280 281 /* Write everything now, buffer is only good until next sasl_encode */ 282 optr = obuf; 283 while (olen > 0) { 284 if (sb->sb_sasl_fns.lbextiofn_write != NULL) { 285 ret = sb->sb_sasl_fns.lbextiofn_write( 286 s, optr, olen, 287 sb->sb_sasl_fns.lbextiofn_socket_arg); 288 } else { 289 ret = write( sb->sb_sd, optr, olen); 290 } 291 if ( ret < 0 ) 292 return( ret ); 293 optr += ret; 294 olen -= ret; 295 } 296 return( len ); 297 } 298 299 static int 300 nsldapi_sasl_poll( 301 LDAP_X_PollFD fds[], int nfds, int timeout, 302 struct lextiof_session_private *arg ) 303 { 304 Sockbuf *sb = (Sockbuf *)arg; 305 LDAP *ld; 306 int i; 307 308 if (sb == NULL) { 309 return( -1 ); 310 } 311 ld = (LDAP *)sb->sb_sasl_prld; 312 if (ld == NULL) { 313 return( -1 ); 314 } 315 316 if (fds && nfds > 0) { 317 for(i = 0; i < nfds; i++) { 318 if (fds[i].lpoll_socketarg == 319 (struct lextiof_socket_private *)sb) { 320 fds[i].lpoll_socketarg = 321 (struct lextiof_socket_private *) 322 sb->sb_sasl_fns.lbextiofn_socket_arg; 323 } 324 325 } 326 } 327 return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout, 328 (void *)ld->ld_sasl_io_fns.lextiof_session_arg) ); 329 } 330 331 /* no encryption indirect routines */ 332 333 static int 334 nsldapi_sasl_ne_read( int s, void *buf, int len, 335 struct lextiof_socket_private *arg) 336 { 337 Sockbuf *sb = (Sockbuf *)arg; 338 339 if (sb == NULL) { 340 return( -1 ); 341 } 342 343 return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len, 344 sb->sb_sasl_fns.lbextiofn_socket_arg) ); 345 } 346 347 static int 348 nsldapi_sasl_ne_write( int s, const void *buf, int len, 349 struct lextiof_socket_private *arg) 350 { 351 Sockbuf *sb = (Sockbuf *)arg; 352 353 if (sb == NULL) { 354 return( -1 ); 355 } 356 357 return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len, 358 sb->sb_sasl_fns.lbextiofn_socket_arg) ); 359 } 360 361 static int 362 nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg ) 363 { 364 Sockbuf *sb = (Sockbuf *)arg; 365 LDAP *ld; 366 367 if (sb == NULL) { 368 return( -1 ); 369 } 370 ld = (LDAP *)sb->sb_sasl_prld; 371 if (ld == NULL) { 372 return( -1 ); 373 } 374 /* undo function pointer interposing */ 375 ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns ); 376 ber_sockbuf_set_option( sb, 377 LBER_SOCKBUF_OPT_EXT_IO_FNS, 378 (void *)&sb->sb_sasl_fns); 379 380 /* undo SASL */ 381 nsldapi_sasl_close( ld, sb ); 382 383 return ( ld->ld_sasl_io_fns.lextiof_close( s, 384 (struct lextiof_socket_private *) 385 sb->sb_sasl_fns.lbextiofn_socket_arg ) ); 386 } 387 388 /* 389 * install encryption routines if security has been negotiated 390 */ 391 static int 392 nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf) 393 { 394 struct lber_x_ext_io_fns fns; 395 struct ldap_x_ext_io_fns iofns; 396 sasl_security_properties_t *secprops; 397 int rc, value; 398 int bufsiz; 399 int encrypt = 0; 400 401 if (ssf && *ssf) { 402 encrypt = 1; 403 } 404 rc = ber_sockbuf_get_option( sb, 405 LBER_SOCKBUF_OPT_TO_FILE_ONLY, 406 (void *) &value); 407 if (rc != 0 || value != 0) 408 return( LDAP_LOCAL_ERROR ); 409 410 if (encrypt) { 411 /* initialize input buffer - use MAX SIZE to avoid reallocs */ 412 sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg; 413 rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS, 414 (const void **)&secprops ); 415 if (rc != SASL_OK) 416 return( LDAP_LOCAL_ERROR ); 417 bufsiz = secprops->maxbufsize; 418 if (bufsiz <= 0) { 419 return( LDAP_LOCAL_ERROR ); 420 } 421 if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) { 422 return( LDAP_LOCAL_ERROR ); 423 } 424 sb->sb_sasl_iptr = NULL; 425 sb->sb_sasl_bfsz = bufsiz; 426 sb->sb_sasl_ilen = 0; 427 } 428 429 /* Reset Session then Socket Args */ 430 /* Get old values */ 431 (void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE); 432 sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; 433 rc = ber_sockbuf_get_option( sb, 434 LBER_SOCKBUF_OPT_EXT_IO_FNS, 435 (void *)&sb->sb_sasl_fns); 436 memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns)); 437 ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; 438 rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, 439 &ld->ld_sasl_io_fns ); 440 if (rc != 0 ) 441 return( LDAP_LOCAL_ERROR ); 442 443 /* Set new values */ 444 if ( ld->ld_sasl_io_fns.lextiof_read != NULL || 445 ld->ld_sasl_io_fns.lextiof_write != NULL || 446 ld->ld_sasl_io_fns.lextiof_poll != NULL || 447 ld->ld_sasl_io_fns.lextiof_connect != NULL || 448 ld->ld_sasl_io_fns.lextiof_close != NULL ) { 449 memset( &iofns, 0, sizeof(iofns)); 450 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; 451 if (encrypt) { 452 iofns.lextiof_read = nsldapi_sasl_read; 453 iofns.lextiof_write = nsldapi_sasl_write; 454 iofns.lextiof_poll = nsldapi_sasl_poll; 455 } else { 456 iofns.lextiof_read = nsldapi_sasl_ne_read; 457 iofns.lextiof_write = nsldapi_sasl_ne_write; 458 iofns.lextiof_poll = nsldapi_sasl_poll; 459 } 460 iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect; 461 iofns.lextiof_close = nsldapi_sasl_close_socket; 462 iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle; 463 iofns.lextiof_disposehandle = 464 ld->ld_sasl_io_fns.lextiof_disposehandle; 465 iofns.lextiof_session_arg = 466 (void *) sb; 467 /* ld->ld_sasl_io_fns.lextiof_session_arg; */ 468 rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, 469 &iofns ); 470 if (rc != 0 ) 471 return( LDAP_LOCAL_ERROR ); 472 sb->sb_sasl_prld = (void *)ld; 473 } 474 475 if (encrypt) { 476 (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE); 477 fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; 478 fns.lbextiofn_read = nsldapi_sasl_read; 479 fns.lbextiofn_write = nsldapi_sasl_write; 480 fns.lbextiofn_socket_arg = 481 (void *) sb; 482 /* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */ 483 rc = ber_sockbuf_set_option( sb, 484 LBER_SOCKBUF_OPT_EXT_IO_FNS, 485 (void *)&fns); 486 if (rc != 0) 487 return( LDAP_LOCAL_ERROR ); 488 } 489 490 return( LDAP_SUCCESS ); 491 } 492 493 static int 494 nsldapi_sasl_cvterrno( LDAP *ld, int err ) 495 { 496 int rc = LDAP_LOCAL_ERROR; 497 498 switch (err) { 499 case SASL_OK: 500 rc = LDAP_SUCCESS; 501 break; 502 case SASL_NOMECH: 503 rc = LDAP_AUTH_UNKNOWN; 504 break; 505 case SASL_BADSERV: 506 rc = LDAP_CONNECT_ERROR; 507 break; 508 case SASL_DISABLED: 509 case SASL_ENCRYPT: 510 case SASL_EXPIRED: 511 case SASL_NOUSERPASS: 512 case SASL_NOVERIFY: 513 case SASL_PWLOCK: 514 case SASL_TOOWEAK: 515 case SASL_UNAVAIL: 516 case SASL_WEAKPASS: 517 rc = LDAP_INAPPROPRIATE_AUTH; 518 break; 519 case SASL_BADAUTH: 520 case SASL_NOAUTHZ: 521 rc = LDAP_INVALID_CREDENTIALS; 522 break; 523 case SASL_NOMEM: 524 rc = LDAP_NO_MEMORY; 525 break; 526 case SASL_NOUSER: 527 rc = LDAP_NO_SUCH_OBJECT; 528 break; 529 case SASL_CONTINUE: 530 case SASL_FAIL: 531 case SASL_INTERACT: 532 default: 533 rc = LDAP_LOCAL_ERROR; 534 break; 535 } 536 537 LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); 538 return( rc ); 539 } 540 541 int 542 nsldapi_sasl_open(LDAP *ld) 543 { 544 Sockbuf *sb; 545 char * host; 546 int saslrc; 547 sasl_conn_t *ctx; 548 549 if (ld == NULL) { 550 return( LDAP_LOCAL_ERROR ); 551 } 552 553 if (ld->ld_defconn == NULL) { 554 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 555 return( LDAP_LOCAL_ERROR ); 556 } 557 sb = ld->ld_defconn->lconn_sb; 558 host = ld->ld_defhost; 559 560 if ( sb == NULL || host == NULL ) { 561 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 562 return( LDAP_LOCAL_ERROR ); 563 } 564 565 /* SASL is not properly initialized */ 566 mutex_lock(&sasl_mutex); 567 if (gctx == NULL) { 568 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 569 mutex_unlock(&sasl_mutex); 570 return( LDAP_LOCAL_ERROR ); 571 } 572 573 saslrc = _sasl_client_new(gctx, "ldap", host, 574 NULL, NULL, /* iplocal ipremote strings currently unused */ 575 NULL, 0, &ctx ); 576 577 if ( saslrc != SASL_OK ) { 578 mutex_unlock(&sasl_mutex); 579 return( nsldapi_sasl_cvterrno( ld, saslrc ) ); 580 } 581 582 sb->sb_sasl_ctx = (void *)ctx; 583 mutex_unlock(&sasl_mutex); 584 585 return( LDAP_SUCCESS ); 586 } 587 588 static void 589 destroy_sasliobuf(Sockbuf *sb) 590 { 591 if (sb != NULL && sb->sb_sasl_ibuf != NULL) { 592 NSLDAPI_FREE(sb->sb_sasl_ibuf); 593 sb->sb_sasl_ibuf = NULL; 594 } 595 } 596 597 static int 598 nsldapi_sasl_close( LDAP *ld, Sockbuf *sb ) 599 { 600 sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx; 601 602 destroy_sasliobuf(sb); 603 604 if( ctx != NULL ) { 605 sasl_dispose( &ctx ); 606 sb->sb_sasl_ctx = NULL; 607 } 608 return( LDAP_SUCCESS ); 609 } 610 611 static int 612 nsldapi_sasl_do_bind( LDAP *ld, const char *dn, 613 const char *mechs, unsigned flags, 614 LDAP_SASL_INTERACT_PROC *callback, void *defaults, 615 LDAPControl **sctrl, LDAPControl **cctrl ) 616 { 617 sasl_interact_t *prompts = NULL; 618 sasl_conn_t *ctx; 619 sasl_ssf_t *ssf = NULL; 620 const char *mech = NULL; 621 int saslrc, rc; 622 struct berval ccred; 623 unsigned credlen; 624 625 if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) { 626 LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); 627 return( LDAP_NOT_SUPPORTED ); 628 } 629 630 /* shouldn't happen */ 631 if (callback == NULL) { 632 return( LDAP_LOCAL_ERROR ); 633 } 634 635 if ( ld->ld_defconn == NULL || 636 ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) { 637 rc = nsldapi_open_ldap_defconn( ld ); 638 if( rc < 0 ) { 639 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 640 } 641 } 642 643 /* should have a valid ld connection - now create sasl connection */ 644 if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) { 645 LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); 646 return( rc ); 647 } 648 649 /* expect context to be initialized when connection is open */ 650 ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx; 651 652 if( ctx == NULL ) { 653 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 654 return( LDAP_LOCAL_ERROR ); 655 } 656 657 /* (re)set security properties */ 658 sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops ); 659 660 ccred.bv_val = NULL; 661 ccred.bv_len = 0; 662 663 do { 664 saslrc = sasl_client_start( ctx, 665 mechs, 666 &prompts, 667 (const char **)&ccred.bv_val, 668 &credlen, 669 &mech ); 670 671 LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n", 672 (mech ? mech : ""), 0, 0 ); 673 674 if( saslrc == SASL_INTERACT && 675 (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { 676 break; 677 } 678 } while ( saslrc == SASL_INTERACT ); 679 680 ccred.bv_len = credlen; 681 682 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { 683 return( nsldapi_sasl_cvterrno( ld, saslrc ) ); 684 } 685 686 do { 687 struct berval *scred; 688 689 scred = NULL; 690 691 /* notify server of a sasl bind step */ 692 rc = ldap_sasl_bind_s(ld, dn, mech, &ccred, 693 sctrl, cctrl, &scred); 694 695 if ( ccred.bv_val != NULL ) { 696 ccred.bv_val = NULL; 697 } 698 699 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { 700 if( scred && scred->bv_len ) { 701 /* and server provided us with data? */ 702 ber_bvfree( scred ); 703 } 704 return( rc ); 705 } 706 707 if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { 708 /* we're done, no need to step */ 709 if( scred && scred->bv_len ) { 710 /* but server provided us with data! */ 711 ber_bvfree( scred ); 712 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, 713 NULL, NULL ); 714 return( LDAP_LOCAL_ERROR ); 715 } 716 break; 717 } 718 719 /* perform the next step of the sasl bind */ 720 do { 721 saslrc = sasl_client_step( ctx, 722 (scred == NULL) ? NULL : scred->bv_val, 723 (scred == NULL) ? 0 : scred->bv_len, 724 &prompts, 725 (const char **)&ccred.bv_val, 726 &credlen ); 727 728 if( saslrc == SASL_INTERACT && 729 (callback)(ld, flags, defaults, prompts) 730 != LDAP_SUCCESS ) { 731 break; 732 } 733 } while ( saslrc == SASL_INTERACT ); 734 735 ccred.bv_len = credlen; 736 ber_bvfree( scred ); 737 738 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { 739 return( nsldapi_sasl_cvterrno( ld, saslrc ) ); 740 } 741 } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); 742 743 if ( rc != LDAP_SUCCESS ) { 744 return( rc ); 745 } 746 747 if ( saslrc != SASL_OK ) { 748 return( nsldapi_sasl_cvterrno( ld, saslrc ) ); 749 } 750 751 saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf ); 752 if( saslrc == SASL_OK ) { 753 rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf); 754 } 755 756 return( rc ); 757 } 758 759 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER 760 /* 761 * Get available SASL Mechanisms supported by the server 762 */ 763 764 static int 765 nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech ) 766 { 767 char *attr[] = { "supportedSASLMechanisms", NULL }; 768 char **values, **v, *mech, *m; 769 LDAPMessage *res, *e; 770 struct timeval timeout; 771 int slen, rc; 772 773 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 774 return( LDAP_PARAM_ERROR ); 775 } 776 777 timeout.tv_sec = SEARCH_TIMEOUT_SECS; 778 timeout.tv_usec = 0; 779 780 rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE, 781 "objectclass=*", attr, 0, &timeout, &res ); 782 783 if ( rc != LDAP_SUCCESS ) { 784 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 785 } 786 787 e = ldap_first_entry( ld, res ); 788 if ( e == NULL ) { 789 ldap_msgfree( res ); 790 if ( ld->ld_errno == LDAP_SUCCESS ) { 791 LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL ); 792 } 793 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 794 } 795 796 values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); 797 if ( values == NULL ) { 798 ldap_msgfree( res ); 799 LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL ); 800 return( LDAP_NO_SUCH_ATTRIBUTE ); 801 } 802 803 slen = 0; 804 for(v = values; *v != NULL; v++ ) { 805 slen += strlen(*v) + 1; 806 } 807 if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) { 808 ldap_value_free( values ); 809 ldap_msgfree( res ); 810 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); 811 return( LDAP_NO_MEMORY ); 812 } 813 m = mech; 814 for(v = values; *v; v++) { 815 if (v != values) { 816 *m++ = ' '; 817 } 818 slen = strlen(*v); 819 strncpy(m, *v, slen); 820 m += slen; 821 } 822 *m = '\0'; 823 824 ldap_value_free( values ); 825 ldap_msgfree( res ); 826 827 *pmech = mech; 828 829 return( LDAP_SUCCESS ); 830 } 831 #endif 832 833 int nsldapi_sasl_secprops( 834 const char *in, 835 sasl_security_properties_t *secprops ) 836 { 837 int i; 838 char **props = NULL; 839 char *inp; 840 unsigned sflags = 0; 841 sasl_ssf_t max_ssf = 0; 842 sasl_ssf_t min_ssf = 0; 843 unsigned maxbufsize = 0; 844 int got_sflags = 0; 845 int got_max_ssf = 0; 846 int got_min_ssf = 0; 847 int got_maxbufsize = 0; 848 849 if (in == NULL) { 850 return LDAP_PARAM_ERROR; 851 } 852 inp = nsldapi_strdup(in); 853 if (inp == NULL) { 854 return LDAP_PARAM_ERROR; 855 } 856 props = ldap_str2charray( inp, "," ); 857 NSLDAPI_FREE( inp ); 858 859 if( props == NULL || secprops == NULL ) { 860 return LDAP_PARAM_ERROR; 861 } 862 863 for( i=0; props[i]; i++ ) { 864 if( strcasecmp(props[i], "none") == 0 ) { 865 got_sflags++; 866 867 } else if( strcasecmp(props[i], "noactive") == 0 ) { 868 got_sflags++; 869 sflags |= SASL_SEC_NOACTIVE; 870 871 } else if( strcasecmp(props[i], "noanonymous") == 0 ) { 872 got_sflags++; 873 sflags |= SASL_SEC_NOANONYMOUS; 874 875 } else if( strcasecmp(props[i], "nodict") == 0 ) { 876 got_sflags++; 877 sflags |= SASL_SEC_NODICTIONARY; 878 879 } else if( strcasecmp(props[i], "noplain") == 0 ) { 880 got_sflags++; 881 sflags |= SASL_SEC_NOPLAINTEXT; 882 883 } else if( strcasecmp(props[i], "forwardsec") == 0 ) { 884 got_sflags++; 885 sflags |= SASL_SEC_FORWARD_SECRECY; 886 887 } else if( strcasecmp(props[i], "passcred") == 0 ) { 888 got_sflags++; 889 sflags |= SASL_SEC_PASS_CREDENTIALS; 890 891 } else if( strncasecmp(props[i], 892 "minssf=", sizeof("minssf")) == 0 ) { 893 if( isdigit( props[i][sizeof("minssf")] ) ) { 894 got_min_ssf++; 895 min_ssf = atoi( &props[i][sizeof("minssf")] ); 896 } else { 897 return LDAP_NOT_SUPPORTED; 898 } 899 900 } else if( strncasecmp(props[i], 901 "maxssf=", sizeof("maxssf")) == 0 ) { 902 if( isdigit( props[i][sizeof("maxssf")] ) ) { 903 got_max_ssf++; 904 max_ssf = atoi( &props[i][sizeof("maxssf")] ); 905 } else { 906 return LDAP_NOT_SUPPORTED; 907 } 908 909 } else if( strncasecmp(props[i], 910 "maxbufsize=", sizeof("maxbufsize")) == 0 ) { 911 if( isdigit( props[i][sizeof("maxbufsize")] ) ) { 912 got_maxbufsize++; 913 maxbufsize = atoi( &props[i][sizeof("maxbufsize")] ); 914 if( maxbufsize && 915 (( maxbufsize < SASL_MIN_BUFF_SIZE ) 916 || (maxbufsize > SASL_MAX_BUFF_SIZE ))) { 917 return( LDAP_PARAM_ERROR ); 918 } 919 } else { 920 return( LDAP_NOT_SUPPORTED ); 921 } 922 } else { 923 return( LDAP_NOT_SUPPORTED ); 924 } 925 } 926 927 if(got_sflags) { 928 secprops->security_flags = sflags; 929 } 930 if(got_min_ssf) { 931 secprops->min_ssf = min_ssf; 932 } 933 if(got_max_ssf) { 934 secprops->max_ssf = max_ssf; 935 } 936 if(got_maxbufsize) { 937 secprops->maxbufsize = maxbufsize; 938 } 939 940 ldap_charray_free( props ); 941 return( LDAP_SUCCESS ); 942 } 943 944 /* 945 * SASL Authentication Interface: ldap_sasl_interactive_bind_s 946 * 947 * This routine takes a DN, SASL mech list, and a SASL callback 948 * and performs the necessary sequencing to complete a SASL bind 949 * to the LDAP connection ld. The user provided callback can 950 * use an optionally provided set of default values to complete 951 * any necessary interactions. 952 * 953 * Currently inpose the following restrictions: 954 * A mech list must be provided, only LDAP_SASL_INTERACTIVE 955 * mode is supported 956 */ 957 int 958 LDAP_CALL 959 ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn, 960 const char *saslMechanism, 961 LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, 962 LDAP_SASL_INTERACT_PROC *callback, void *defaults ) 963 { 964 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER 965 char *smechs; 966 #endif 967 int rc; 968 969 LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0); 970 971 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 972 return( LDAP_PARAM_ERROR ); 973 } 974 975 if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) { 976 return( LDAP_PARAM_ERROR ); 977 } 978 979 LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK ); 980 981 if( saslMechanism == NULL || *saslMechanism == '\0' ) { 982 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER 983 rc = nsldapi_get_sasl_mechs( ld, &smechs ); 984 if( rc != LDAP_SUCCESS ) { 985 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); 986 return( rc ); 987 } 988 saslMechanism = smechs; 989 #else 990 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); 991 return( LDAP_PARAM_ERROR ); 992 #endif 993 } 994 995 /* initialize SASL library */ 996 if ( nsldapi_sasl_init() < 0 ) { 997 return( LDAP_PARAM_ERROR ); 998 } 999 1000 rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism, 1001 flags, callback, defaults, sctrl, cctrl); 1002 1003 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); 1004 return( rc ); 1005 } 1006 1007 #endif 1008