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