sasl.c revision 1.1
1/* $OpenLDAP: pkg/ldap/servers/slapd/sasl.c,v 1.239.2.12 2008/02/12 00:54:34 quanah Exp $ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2008 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 16#include "portable.h" 17 18#include <stdio.h> 19#ifdef HAVE_LIMITS_H 20#include <limits.h> 21#endif 22 23#include <ac/stdlib.h> 24#include <ac/string.h> 25 26#include <lber.h> 27#include <ldap_log.h> 28 29#include "slap.h" 30 31#ifdef ENABLE_REWRITE 32#include <rewrite.h> 33#endif 34 35#ifdef HAVE_CYRUS_SASL 36# ifdef HAVE_SASL_SASL_H 37# include <sasl/sasl.h> 38# include <sasl/saslplug.h> 39# else 40# include <sasl.h> 41# include <saslplug.h> 42# endif 43 44# define SASL_CONST const 45 46#define SASL_VERSION_FULL ((SASL_VERSION_MAJOR << 16) |\ 47 (SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP) 48 49static sasl_security_properties_t sasl_secprops; 50#elif defined( SLAP_BUILTIN_SASL ) 51/* 52 * built-in SASL implementation 53 * only supports EXTERNAL 54 */ 55typedef struct sasl_ctx { 56 slap_ssf_t sc_external_ssf; 57 struct berval sc_external_id; 58} SASL_CTX; 59 60#endif 61 62#include <lutil.h> 63 64static struct berval ext_bv = BER_BVC( "EXTERNAL" ); 65 66#ifdef HAVE_CYRUS_SASL 67 68int 69slap_sasl_log( 70 void *context, 71 int priority, 72 const char *message) 73{ 74 Connection *conn = context; 75 int level; 76 const char * label; 77 78 if ( message == NULL ) { 79 return SASL_BADPARAM; 80 } 81 82 switch (priority) { 83 case SASL_LOG_NONE: 84 level = LDAP_DEBUG_NONE; 85 label = "None"; 86 break; 87 case SASL_LOG_ERR: 88 level = LDAP_DEBUG_ANY; 89 label = "Error"; 90 break; 91 case SASL_LOG_FAIL: 92 level = LDAP_DEBUG_ANY; 93 label = "Failure"; 94 break; 95 case SASL_LOG_WARN: 96 level = LDAP_DEBUG_TRACE; 97 label = "Warning"; 98 break; 99 case SASL_LOG_NOTE: 100 level = LDAP_DEBUG_TRACE; 101 label = "Notice"; 102 break; 103 case SASL_LOG_DEBUG: 104 level = LDAP_DEBUG_TRACE; 105 label = "Debug"; 106 break; 107 case SASL_LOG_TRACE: 108 level = LDAP_DEBUG_TRACE; 109 label = "Trace"; 110 break; 111 case SASL_LOG_PASS: 112 level = LDAP_DEBUG_TRACE; 113 label = "Password Trace"; 114 break; 115 default: 116 return SASL_BADPARAM; 117 } 118 119 Debug( level, "SASL [conn=%ld] %s: %s\n", 120 conn ? conn->c_connid: -1, 121 label, message ); 122 123 124 return SASL_OK; 125} 126 127static const char *slap_propnames[] = { 128 "*slapConn", "*slapAuthcDNlen", "*slapAuthcDN", 129 "*slapAuthzDNlen", "*slapAuthzDN", NULL }; 130 131static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL }; 132static struct berval generic_filterstr = BER_BVC("(objectclass=*)"); 133 134#define SLAP_SASL_PROP_CONN 0 135#define SLAP_SASL_PROP_AUTHCLEN 1 136#define SLAP_SASL_PROP_AUTHC 2 137#define SLAP_SASL_PROP_AUTHZLEN 3 138#define SLAP_SASL_PROP_AUTHZ 4 139#define SLAP_SASL_PROP_COUNT 5 /* Number of properties we used */ 140 141typedef struct lookup_info { 142 int flags; 143 const struct propval *list; 144 sasl_server_params_t *sparams; 145} lookup_info; 146 147static slap_response sasl_ap_lookup; 148 149static struct berval sc_cleartext = BER_BVC("{CLEARTEXT}"); 150 151static int 152sasl_ap_lookup( Operation *op, SlapReply *rs ) 153{ 154 BerVarray bv; 155 AttributeDescription *ad; 156 Attribute *a; 157 const char *text; 158 int rc, i; 159 lookup_info *sl = (lookup_info *)op->o_callback->sc_private; 160 161 if (rs->sr_type != REP_SEARCH) return 0; 162 163 for( i = 0; sl->list[i].name; i++ ) { 164 const char *name = sl->list[i].name; 165 166 if ( name[0] == '*' ) { 167 if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue; 168 /* Skip our private properties */ 169 if ( !strcmp( name, slap_propnames[0] )) { 170 i += SLAP_SASL_PROP_COUNT - 1; 171 continue; 172 } 173 name++; 174 } else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) ) 175 continue; 176 177 if ( sl->list[i].values ) { 178 if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue; 179 } 180 ad = NULL; 181 rc = slap_str2ad( name, &ad, &text ); 182 if ( rc != LDAP_SUCCESS ) { 183 Debug( LDAP_DEBUG_TRACE, 184 "slap_ap_lookup: str2ad(%s): %s\n", name, text, 0 ); 185 continue; 186 } 187 188 /* If it's the rootdn and a rootpw was present, we already set 189 * it so don't override it here. 190 */ 191 if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values && 192 be_isroot_dn( op->o_bd, &op->o_req_ndn )) 193 continue; 194 195 a = attr_find( rs->sr_entry->e_attrs, ad ); 196 if ( !a ) continue; 197 if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) { 198 continue; 199 } 200 if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) ) { 201 sl->sparams->utils->prop_erase( sl->sparams->propctx, 202 sl->list[i].name ); 203 } 204 for ( bv = a->a_vals; bv->bv_val; bv++ ) { 205 /* ITS#3846 don't give hashed passwords to SASL */ 206 if ( ad == slap_schema.si_ad_userPassword && 207 bv->bv_val[0] == '{' /*}*/ ) 208 { 209 if ( lutil_passwd_scheme( bv->bv_val ) ) { 210 /* If it's not a recognized scheme, just assume it's 211 * a cleartext password that happened to include brackets. 212 * 213 * If it's a recognized scheme, skip this value, unless the 214 * scheme is {CLEARTEXT}. In that case, skip over the 215 * scheme name and use the remainder. If there is nothing 216 * past the scheme name, skip this value. 217 */ 218#ifdef SLAPD_CLEARTEXT 219 if ( !strncasecmp( bv->bv_val, sc_cleartext.bv_val, 220 sc_cleartext.bv_len )) { 221 struct berval cbv; 222 cbv.bv_len = bv->bv_len - sc_cleartext.bv_len; 223 if ( cbv.bv_len > 0 ) { 224 cbv.bv_val = bv->bv_val + sc_cleartext.bv_len; 225 sl->sparams->utils->prop_set( sl->sparams->propctx, 226 sl->list[i].name, cbv.bv_val, cbv.bv_len ); 227 } 228 } 229#endif 230 continue; 231 } 232 } 233 sl->sparams->utils->prop_set( sl->sparams->propctx, 234 sl->list[i].name, bv->bv_val, bv->bv_len ); 235 } 236 } 237 return LDAP_SUCCESS; 238} 239 240static void 241slap_auxprop_lookup( 242 void *glob_context, 243 sasl_server_params_t *sparams, 244 unsigned flags, 245 const char *user, 246 unsigned ulen) 247{ 248 Operation op = {0}; 249 int i, doit = 0; 250 Connection *conn = NULL; 251 lookup_info sl; 252 253 sl.list = sparams->utils->prop_get( sparams->propctx ); 254 sl.sparams = sparams; 255 sl.flags = flags; 256 257 /* Find our DN and conn first */ 258 for( i = 0; sl.list[i].name; i++ ) { 259 if ( sl.list[i].name[0] == '*' ) { 260 if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) { 261 if ( sl.list[i].values && sl.list[i].values[0] ) 262 AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) ); 263 continue; 264 } 265 if ( flags & SASL_AUXPROP_AUTHZID ) { 266 if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZLEN] )) { 267 if ( sl.list[i].values && sl.list[i].values[0] ) 268 AC_MEMCPY( &op.o_req_ndn.bv_len, sl.list[i].values[0], 269 sizeof( op.o_req_ndn.bv_len ) ); 270 } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZ] )) { 271 if ( sl.list[i].values ) 272 op.o_req_ndn.bv_val = (char *)sl.list[i].values[0]; 273 break; 274 } 275 } 276 277 if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) { 278 if ( sl.list[i].values && sl.list[i].values[0] ) 279 AC_MEMCPY( &op.o_req_ndn.bv_len, sl.list[i].values[0], 280 sizeof( op.o_req_ndn.bv_len ) ); 281 } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) { 282 if ( sl.list[i].values ) { 283 op.o_req_ndn.bv_val = (char *)sl.list[i].values[0]; 284 if ( !(flags & SASL_AUXPROP_AUTHZID) ) 285 break; 286 } 287 } 288 } 289 } 290 291 /* Now see what else needs to be fetched */ 292 for( i = 0; sl.list[i].name; i++ ) { 293 const char *name = sl.list[i].name; 294 295 if ( name[0] == '*' ) { 296 if ( flags & SASL_AUXPROP_AUTHZID ) continue; 297 /* Skip our private properties */ 298 if ( !strcmp( name, slap_propnames[0] )) { 299 i += SLAP_SASL_PROP_COUNT - 1; 300 continue; 301 } 302 name++; 303 } else if ( !(flags & SASL_AUXPROP_AUTHZID ) ) 304 continue; 305 306 if ( sl.list[i].values ) { 307 if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue; 308 } 309 doit = 1; 310 break; 311 } 312 313 if (doit) { 314 slap_callback cb = { NULL, sasl_ap_lookup, NULL, NULL }; 315 316 cb.sc_private = &sl; 317 318 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 319 320 if ( op.o_bd ) { 321 /* For rootdn, see if we can use the rootpw */ 322 if ( be_isroot_dn( op.o_bd, &op.o_req_ndn ) && 323 !BER_BVISEMPTY( &op.o_bd->be_rootpw )) { 324 struct berval cbv = BER_BVNULL; 325 326 /* If there's a recognized scheme, see if it's CLEARTEXT */ 327 if ( lutil_passwd_scheme( op.o_bd->be_rootpw.bv_val )) { 328 if ( !strncasecmp( op.o_bd->be_rootpw.bv_val, 329 sc_cleartext.bv_val, sc_cleartext.bv_len )) { 330 331 /* If it's CLEARTEXT, skip past scheme spec */ 332 cbv.bv_len = op.o_bd->be_rootpw.bv_len - 333 sc_cleartext.bv_len; 334 if ( cbv.bv_len ) { 335 cbv.bv_val = op.o_bd->be_rootpw.bv_val + 336 sc_cleartext.bv_len; 337 } 338 } 339 /* No scheme, use the whole value */ 340 } else { 341 cbv = op.o_bd->be_rootpw; 342 } 343 if ( !BER_BVISEMPTY( &cbv )) { 344 for( i = 0; sl.list[i].name; i++ ) { 345 const char *name = sl.list[i].name; 346 347 if ( name[0] == '*' ) { 348 if ( flags & SASL_AUXPROP_AUTHZID ) continue; 349 name++; 350 } else if ( !(flags & SASL_AUXPROP_AUTHZID ) ) 351 continue; 352 353 if ( !strcasecmp(name,"userPassword") ) { 354 sl.sparams->utils->prop_set( sl.sparams->propctx, 355 sl.list[i].name, cbv.bv_val, cbv.bv_len ); 356 break; 357 } 358 } 359 } 360 } 361 362 if ( op.o_bd->be_search ) { 363 SlapReply rs = {REP_RESULT}; 364 op.o_hdr = conn->c_sasl_bindop->o_hdr; 365 op.o_tag = LDAP_REQ_SEARCH; 366 op.o_dn = conn->c_ndn; 367 op.o_ndn = conn->c_ndn; 368 op.o_callback = &cb; 369 slap_op_time( &op.o_time, &op.o_tincr ); 370 op.o_do_not_cache = 1; 371 op.o_is_auth_check = 1; 372 op.o_req_dn = op.o_req_ndn; 373 op.ors_scope = LDAP_SCOPE_BASE; 374 op.ors_deref = LDAP_DEREF_NEVER; 375 op.ors_tlimit = SLAP_NO_LIMIT; 376 op.ors_slimit = 1; 377 op.ors_filter = &generic_filter; 378 op.ors_filterstr = generic_filterstr; 379 /* FIXME: we want all attributes, right? */ 380 op.ors_attrs = NULL; 381 382 op.o_bd->be_search( &op, &rs ); 383 } 384 } 385 } 386} 387 388#if SASL_VERSION_FULL >= 0x020110 389static int 390slap_auxprop_store( 391 void *glob_context, 392 sasl_server_params_t *sparams, 393 struct propctx *prctx, 394 const char *user, 395 unsigned ulen) 396{ 397 Operation op = {0}; 398 Opheader oph; 399 SlapReply rs = {REP_RESULT}; 400 int rc, i, j; 401 Connection *conn = NULL; 402 const struct propval *pr; 403 Modifications *modlist = NULL, **modtail = &modlist, *mod; 404 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 405 char textbuf[SLAP_TEXT_BUFLEN]; 406 const char *text; 407 size_t textlen = sizeof(textbuf); 408 409 /* just checking if we are enabled */ 410 if (!prctx) return SASL_OK; 411 412 if (!sparams || !user) return SASL_BADPARAM; 413 414 pr = sparams->utils->prop_get( sparams->propctx ); 415 416 /* Find our DN and conn first */ 417 for( i = 0; pr[i].name; i++ ) { 418 if ( pr[i].name[0] == '*' ) { 419 if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) { 420 if ( pr[i].values && pr[i].values[0] ) 421 AC_MEMCPY( &conn, pr[i].values[0], sizeof( conn ) ); 422 continue; 423 } 424 if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) { 425 if ( pr[i].values && pr[i].values[0] ) 426 AC_MEMCPY( &op.o_req_ndn.bv_len, pr[i].values[0], 427 sizeof( op.o_req_ndn.bv_len ) ); 428 } else if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) { 429 if ( pr[i].values ) 430 op.o_req_ndn.bv_val = (char *)pr[i].values[0]; 431 } 432 } 433 } 434 if (!conn || !op.o_req_ndn.bv_val) return SASL_BADPARAM; 435 436 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 437 438 if ( !op.o_bd || !op.o_bd->be_modify ) return SASL_FAIL; 439 440 pr = sparams->utils->prop_get( prctx ); 441 if (!pr) return SASL_BADPARAM; 442 443 for (i=0; pr[i].name; i++); 444 if (!i) return SASL_BADPARAM; 445 446 for (i=0; pr[i].name; i++) { 447 mod = (Modifications *)ch_malloc( sizeof(Modifications) ); 448 mod->sml_op = LDAP_MOD_REPLACE; 449 mod->sml_flags = 0; 450 ber_str2bv( pr[i].name, 0, 0, &mod->sml_type ); 451 mod->sml_numvals = pr[i].nvalues; 452 mod->sml_values = (struct berval *)ch_malloc( (pr[i].nvalues + 1) * 453 sizeof(struct berval)); 454 for (j=0; j<pr[i].nvalues; j++) { 455 ber_str2bv( pr[i].values[j], 0, 1, &mod->sml_values[j]); 456 } 457 BER_BVZERO( &mod->sml_values[j] ); 458 mod->sml_nvalues = NULL; 459 mod->sml_desc = NULL; 460 *modtail = mod; 461 modtail = &mod->sml_next; 462 } 463 *modtail = NULL; 464 465 rc = slap_mods_check( &op, modlist, &text, textbuf, textlen, NULL ); 466 467 if ( rc == LDAP_SUCCESS ) { 468 rc = slap_mods_no_user_mod_check( &op, modlist, 469 &text, textbuf, textlen ); 470 471 if ( rc == LDAP_SUCCESS ) { 472 if ( conn->c_sasl_bindop ) { 473 op.o_hdr = conn->c_sasl_bindop->o_hdr; 474 } else { 475 op.o_hdr = &oph; 476 memset( &oph, 0, sizeof(oph) ); 477 operation_fake_init( conn, &op, ldap_pvt_thread_pool_context(), 0 ); 478 } 479 op.o_tag = LDAP_REQ_MODIFY; 480 op.o_ndn = op.o_req_ndn; 481 op.o_callback = &cb; 482 slap_op_time( &op.o_time, &op.o_tincr ); 483 op.o_do_not_cache = 1; 484 op.o_is_auth_check = 1; 485 op.o_req_dn = op.o_req_ndn; 486 op.orm_modlist = modlist; 487 488 rc = op.o_bd->be_modify( &op, &rs ); 489 } 490 } 491 slap_mods_free( modlist, 1 ); 492 return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK; 493} 494#endif /* SASL_VERSION_FULL >= 2.1.16 */ 495 496static sasl_auxprop_plug_t slap_auxprop_plugin = { 497 0, /* Features */ 498 0, /* spare */ 499 NULL, /* glob_context */ 500 NULL, /* auxprop_free */ 501 slap_auxprop_lookup, 502 "slapd", /* name */ 503#if SASL_VERSION_FULL >= 0x020110 504 slap_auxprop_store /* the declaration of this member changed 505 * in cyrus SASL from 2.1.15 to 2.1.16 */ 506#else 507 NULL 508#endif 509}; 510 511static int 512slap_auxprop_init( 513 const sasl_utils_t *utils, 514 int max_version, 515 int *out_version, 516 sasl_auxprop_plug_t **plug, 517 const char *plugname) 518{ 519 if ( !out_version || !plug ) return SASL_BADPARAM; 520 521 if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS; 522 523 *out_version = SASL_AUXPROP_PLUG_VERSION; 524 *plug = &slap_auxprop_plugin; 525 return SASL_OK; 526} 527 528/* Convert a SASL authcid or authzid into a DN. Store the DN in an 529 * auxiliary property, so that we can refer to it in sasl_authorize 530 * without interfering with anything else. Also, the SASL username 531 * buffer is constrained to 256 characters, and our DNs could be 532 * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192) 533 */ 534static int 535slap_sasl_canonicalize( 536 sasl_conn_t *sconn, 537 void *context, 538 const char *in, 539 unsigned inlen, 540 unsigned flags, 541 const char *user_realm, 542 char *out, 543 unsigned out_max, 544 unsigned *out_len) 545{ 546 Connection *conn = (Connection *)context; 547 struct propctx *props = sasl_auxprop_getctx( sconn ); 548 struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } }; 549 struct berval dn; 550 int rc, which; 551 const char *names[2]; 552 struct berval bvin; 553 554 *out_len = 0; 555 556 Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n", 557 conn ? conn->c_connid : -1, 558 (flags & SASL_CU_AUTHID) ? "authcid" : "authzid", 559 in ? in : "<empty>"); 560 561 /* If name is too big, just truncate. We don't care, we're 562 * using DNs, not the usernames. 563 */ 564 if ( inlen > out_max ) 565 inlen = out_max-1; 566 567 /* This is a Simple Bind using SPASSWD. That means the in-directory 568 * userPassword of the Binding user already points at SASL, so it 569 * cannot be used to actually satisfy a password comparison. Just 570 * ignore it, some other mech will process it. 571 */ 572 if ( !conn->c_sasl_bindop || 573 conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done; 574 575 /* See if we need to add request, can only do it once */ 576 prop_getnames( props, slap_propnames, auxvals ); 577 if ( !auxvals[0].name ) 578 prop_request( props, slap_propnames ); 579 580 if ( flags & SASL_CU_AUTHID ) 581 which = SLAP_SASL_PROP_AUTHCLEN; 582 else 583 which = SLAP_SASL_PROP_AUTHZLEN; 584 585 /* Need to store the Connection for auxprop_lookup */ 586 if ( !auxvals[SLAP_SASL_PROP_CONN].values ) { 587 names[0] = slap_propnames[SLAP_SASL_PROP_CONN]; 588 names[1] = NULL; 589 prop_set( props, names[0], (char *)&conn, sizeof( conn ) ); 590 } 591 592 /* Already been here? */ 593 if ( auxvals[which].values ) 594 goto done; 595 596 /* Normally we require an authzID to have a u: or dn: prefix. 597 * However, SASL frequently gives us an authzID that is just 598 * an exact copy of the authcID, without a prefix. We need to 599 * detect and allow this condition. If SASL calls canonicalize 600 * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer. 601 * But if it's broken into two calls, we need to remember the 602 * authcID so that we can compare the authzID later. We store 603 * the authcID temporarily in conn->c_sasl_dn. We necessarily 604 * finish Canonicalizing before Authorizing, so there is no 605 * conflict with slap_sasl_authorize's use of this temp var. 606 * 607 * The SASL EXTERNAL mech is backwards from all the other mechs, 608 * it does authzID before the authcID. If we see that authzID 609 * has already been done, don't do anything special with authcID. 610 */ 611 if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) { 612 conn->c_sasl_dn.bv_val = (char *) in; 613 conn->c_sasl_dn.bv_len = 0; 614 } else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) { 615 rc = strcmp( in, conn->c_sasl_dn.bv_val ); 616 conn->c_sasl_dn.bv_val = NULL; 617 /* They were equal, no work needed */ 618 if ( !rc ) goto done; 619 } 620 621 bvin.bv_val = (char *)in; 622 bvin.bv_len = inlen; 623 rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn, 624 (flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID ); 625 if ( rc != LDAP_SUCCESS ) { 626 sasl_seterror( sconn, 0, ldap_err2string( rc ) ); 627 return SASL_NOAUTHZ; 628 } 629 630 names[0] = slap_propnames[which]; 631 names[1] = NULL; 632 prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) ); 633 634 which++; 635 names[0] = slap_propnames[which]; 636 prop_set( props, names[0], dn.bv_val, dn.bv_len ); 637 638 Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n", 639 conn ? conn->c_connid : -1, names[0]+1, 640 dn.bv_val ? dn.bv_val : "<EMPTY>" ); 641 642 /* Not needed any more, SASL has copied it */ 643 if ( conn && conn->c_sasl_bindop ) 644 conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx ); 645 646done: 647 AC_MEMCPY( out, in, inlen ); 648 out[inlen] = '\0'; 649 650 *out_len = inlen; 651 652 return SASL_OK; 653} 654 655static int 656slap_sasl_authorize( 657 sasl_conn_t *sconn, 658 void *context, 659 char *requested_user, 660 unsigned rlen, 661 char *auth_identity, 662 unsigned alen, 663 const char *def_realm, 664 unsigned urlen, 665 struct propctx *props) 666{ 667 Connection *conn = (Connection *)context; 668 /* actually: 669 * (SLAP_SASL_PROP_COUNT - 1) because we skip "conn", 670 * + 1 for NULL termination? 671 */ 672 struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } }; 673 struct berval authcDN, authzDN = BER_BVNULL; 674 int rc; 675 676 /* Simple Binds don't support proxy authorization, ignore it */ 677 if ( !conn->c_sasl_bindop || 678 conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) return SASL_OK; 679 680 Debug( LDAP_DEBUG_ARGS, "SASL proxy authorize [conn=%ld]: " 681 "authcid=\"%s\" authzid=\"%s\"\n", 682 conn ? conn->c_connid : -1, auth_identity, requested_user ); 683 if ( conn->c_sasl_dn.bv_val ) { 684 BER_BVZERO( &conn->c_sasl_dn ); 685 } 686 687 /* Skip SLAP_SASL_PROP_CONN */ 688 prop_getnames( props, slap_propnames+1, auxvals ); 689 690 /* Should not happen */ 691 if ( !auxvals[0].values ) { 692 sasl_seterror( sconn, 0, "invalid authcid" ); 693 return SASL_NOAUTHZ; 694 } 695 696 AC_MEMCPY( &authcDN.bv_len, auxvals[0].values[0], sizeof(authcDN.bv_len) ); 697 authcDN.bv_val = auxvals[1].values ? (char *)auxvals[1].values[0] : NULL; 698 conn->c_sasl_dn = authcDN; 699 700 /* Nothing to do if no authzID was given */ 701 if ( !auxvals[2].name || !auxvals[2].values ) { 702 goto ok; 703 } 704 705 AC_MEMCPY( &authzDN.bv_len, auxvals[2].values[0], sizeof(authzDN.bv_len) ); 706 authzDN.bv_val = auxvals[3].values ? (char *)auxvals[3].values[0] : NULL; 707 708 rc = slap_sasl_authorized( conn->c_sasl_bindop, &authcDN, &authzDN ); 709 if ( rc != LDAP_SUCCESS ) { 710 Debug( LDAP_DEBUG_TRACE, "SASL Proxy Authorize [conn=%ld]: " 711 "proxy authorization disallowed (%d)\n", 712 (long) (conn ? conn->c_connid : -1), rc, 0 ); 713 714 sasl_seterror( sconn, 0, "not authorized" ); 715 return SASL_NOAUTHZ; 716 } 717 718 /* FIXME: we need yet another dup because slap_sasl_getdn() 719 * is using the bind operation slab */ 720 ber_dupbv( &conn->c_sasl_authz_dn, &authzDN ); 721 722ok: 723 if (conn->c_sasl_bindop) { 724 Statslog( LDAP_DEBUG_STATS, 725 "%s BIND authcid=\"%s\" authzid=\"%s\"\n", 726 conn->c_sasl_bindop->o_log_prefix, 727 auth_identity, requested_user, 0, 0 ); 728 } 729 730 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: " 731 " proxy authorization allowed authzDN=\"%s\"\n", 732 (long) (conn ? conn->c_connid : -1), 733 authzDN.bv_val ? authzDN.bv_val : "", 0 ); 734 return SASL_OK; 735} 736 737static int 738slap_sasl_err2ldap( int saslerr ) 739{ 740 int rc; 741 742 /* map SASL errors to LDAP resultCode returned by: 743 * sasl_server_new() 744 * SASL_OK, SASL_NOMEM 745 * sasl_server_step() 746 * SASL_OK, SASL_CONTINUE, SASL_TRANS, SASL_BADPARAM, SASL_BADPROT, 747 * ... 748 * sasl_server_start() 749 * + SASL_NOMECH 750 * sasl_setprop() 751 * SASL_OK, SASL_BADPARAM 752 */ 753 754 switch (saslerr) { 755 case SASL_OK: 756 rc = LDAP_SUCCESS; 757 break; 758 case SASL_CONTINUE: 759 rc = LDAP_SASL_BIND_IN_PROGRESS; 760 break; 761 case SASL_FAIL: 762 case SASL_NOMEM: 763 rc = LDAP_OTHER; 764 break; 765 case SASL_NOMECH: 766 rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; 767 break; 768 case SASL_BADAUTH: 769 case SASL_NOUSER: 770 case SASL_TRANS: 771 case SASL_EXPIRED: 772 rc = LDAP_INVALID_CREDENTIALS; 773 break; 774 case SASL_NOAUTHZ: 775 rc = LDAP_INSUFFICIENT_ACCESS; 776 break; 777 case SASL_TOOWEAK: 778 case SASL_ENCRYPT: 779 rc = LDAP_INAPPROPRIATE_AUTH; 780 break; 781 case SASL_UNAVAIL: 782 case SASL_TRYAGAIN: 783 rc = LDAP_UNAVAILABLE; 784 break; 785 case SASL_DISABLED: 786 rc = LDAP_UNWILLING_TO_PERFORM; 787 break; 788 default: 789 rc = LDAP_OTHER; 790 break; 791 } 792 793 return rc; 794} 795 796#ifdef SLAPD_SPASSWD 797 798static struct berval sasl_pwscheme = BER_BVC("{SASL}"); 799 800static int chk_sasl( 801 const struct berval *sc, 802 const struct berval * passwd, 803 const struct berval * cred, 804 const char **text ) 805{ 806 unsigned int i; 807 int rtn; 808 void *ctx, *sconn = NULL; 809 810 for( i=0; i<cred->bv_len; i++) { 811 if(cred->bv_val[i] == '\0') { 812 return LUTIL_PASSWD_ERR; /* NUL character in password */ 813 } 814 } 815 816 if( cred->bv_val[i] != '\0' ) { 817 return LUTIL_PASSWD_ERR; /* cred must behave like a string */ 818 } 819 820 for( i=0; i<passwd->bv_len; i++) { 821 if(passwd->bv_val[i] == '\0') { 822 return LUTIL_PASSWD_ERR; /* NUL character in password */ 823 } 824 } 825 826 if( passwd->bv_val[i] != '\0' ) { 827 return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ 828 } 829 830 rtn = LUTIL_PASSWD_ERR; 831 832 ctx = ldap_pvt_thread_pool_context(); 833 ldap_pvt_thread_pool_getkey( ctx, (void *)slap_sasl_bind, &sconn, NULL ); 834 835 if( sconn != NULL ) { 836 int sc; 837 sc = sasl_checkpass( sconn, 838 passwd->bv_val, passwd->bv_len, 839 cred->bv_val, cred->bv_len ); 840 rtn = ( sc != SASL_OK ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; 841 } 842 843 return rtn; 844} 845#endif /* SLAPD_SPASSWD */ 846 847#endif /* HAVE_CYRUS_SASL */ 848 849#ifdef ENABLE_REWRITE 850 851typedef struct slapd_map_data { 852 struct berval base; 853 struct berval filter; 854 AttributeName attrs[2]; 855 int scope; 856} slapd_map_data; 857 858static void * 859slapd_rw_config( const char *fname, int lineno, int argc, char **argv ) 860{ 861 slapd_map_data *ret = NULL; 862 LDAPURLDesc *lud = NULL; 863 char *uri; 864 AttributeDescription *ad = NULL; 865 int rc, flen = 0; 866 struct berval dn, ndn; 867 868 if ( argc != 1 ) { 869 Debug( LDAP_DEBUG_ANY, 870 "[%s:%d] slapd map needs URI\n", 871 fname, lineno, 0 ); 872 return NULL; 873 } 874 875 uri = argv[0]; 876 if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) { 877 uri += STRLENOF( "uri=" ); 878 } 879 880 if ( ldap_url_parse( uri, &lud ) != LDAP_URL_SUCCESS ) { 881 Debug( LDAP_DEBUG_ANY, 882 "[%s:%d] illegal URI '%s'\n", 883 fname, lineno, uri ); 884 return NULL; 885 } 886 887 if ( strcasecmp( lud->lud_scheme, "ldap" )) { 888 Debug( LDAP_DEBUG_ANY, 889 "[%s:%d] illegal URI scheme '%s'\n", 890 fname, lineno, lud->lud_scheme ); 891 goto done; 892 } 893 894 if (( lud->lud_host && lud->lud_host[0] ) || lud->lud_exts 895 || !lud->lud_dn ) { 896 Debug( LDAP_DEBUG_ANY, 897 "[%s:%d] illegal URI '%s'\n", 898 fname, lineno, uri ); 899 goto done; 900 } 901 902 if ( lud->lud_attrs ) { 903 if ( lud->lud_attrs[1] ) { 904 Debug( LDAP_DEBUG_ANY, 905 "[%s:%d] only one attribute allowed in URI\n", 906 fname, lineno, 0 ); 907 goto done; 908 } 909 if ( strcasecmp( lud->lud_attrs[0], "dn" ) && 910 strcasecmp( lud->lud_attrs[0], "entryDN" )) { 911 const char *text; 912 rc = slap_str2ad( lud->lud_attrs[0], &ad, &text ); 913 if ( rc ) 914 goto done; 915 } 916 } 917 ber_str2bv( lud->lud_dn, 0, 0, &dn ); 918 if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )) 919 goto done; 920 921 if ( lud->lud_filter ) { 922 flen = strlen( lud->lud_filter ) + 1; 923 } 924 ret = ch_malloc( sizeof( slapd_map_data ) + flen ); 925 ret->base = ndn; 926 if ( flen ) { 927 ret->filter.bv_val = (char *)(ret+1); 928 ret->filter.bv_len = flen - 1; 929 strcpy( ret->filter.bv_val, lud->lud_filter ); 930 } else { 931 BER_BVZERO( &ret->filter ); 932 } 933 ret->scope = lud->lud_scope; 934 if ( ad ) { 935 ret->attrs[0].an_name = ad->ad_cname; 936 } else { 937 BER_BVZERO( &ret->attrs[0].an_name ); 938 } 939 ret->attrs[0].an_desc = ad; 940 BER_BVZERO( &ret->attrs[1].an_name ); 941done: 942 ldap_free_urldesc( lud ); 943 return ret; 944} 945 946struct slapd_rw_info { 947 slapd_map_data *si_data; 948 struct berval si_val; 949}; 950 951static int 952slapd_rw_cb( Operation *op, SlapReply *rs ) 953{ 954 if ( rs->sr_type == REP_SEARCH ) { 955 struct slapd_rw_info *si = op->o_callback->sc_private; 956 957 if ( si->si_data->attrs[0].an_desc ) { 958 Attribute *a; 959 960 a = attr_find( rs->sr_entry->e_attrs, 961 si->si_data->attrs[0].an_desc ); 962 if ( a ) { 963 ber_dupbv( &si->si_val, a->a_vals ); 964 } 965 } else { 966 ber_dupbv( &si->si_val, &rs->sr_entry->e_name ); 967 } 968 } 969 return LDAP_SUCCESS; 970} 971 972static int 973slapd_rw_apply( void *private, const char *filter, struct berval *val ) 974{ 975 slapd_map_data *sl = private; 976 slap_callback cb = { NULL }; 977 Connection conn = {0}; 978 OperationBuffer opbuf; 979 Operation *op; 980 void *thrctx; 981 SlapReply rs = {REP_RESULT}; 982 struct slapd_rw_info si; 983 char *ptr; 984 int rc; 985 986 thrctx = ldap_pvt_thread_pool_context(); 987 connection_fake_init2( &conn, &opbuf, thrctx, 0 ); 988 op = &opbuf.ob_op; 989 990 op->o_tag = LDAP_REQ_SEARCH; 991 op->o_req_dn = op->o_req_ndn = sl->base; 992 op->o_bd = select_backend( &op->o_req_ndn, 1 ); 993 if ( !op->o_bd ) { 994 return REWRITE_ERR; 995 } 996 si.si_data = sl; 997 BER_BVZERO( &si.si_val ); 998 op->ors_scope = sl->scope; 999 op->ors_deref = LDAP_DEREF_NEVER; 1000 op->ors_slimit = 1; 1001 op->ors_tlimit = SLAP_NO_LIMIT; 1002 if ( sl->attrs[0].an_desc ) { 1003 op->ors_attrs = sl->attrs; 1004 } else { 1005 op->ors_attrs = slap_anlist_no_attrs; 1006 } 1007 if ( filter ) { 1008 rc = strlen( filter ); 1009 } else { 1010 rc = 0; 1011 } 1012 rc += sl->filter.bv_len; 1013 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( rc + 1, op->o_tmpmemctx ); 1014 if ( sl->filter.bv_len ) { 1015 ptr = lutil_strcopy( ptr, sl->filter.bv_val ); 1016 } else { 1017 *ptr = '\0'; 1018 } 1019 if ( filter ) { 1020 strcpy( ptr, filter ); 1021 } 1022 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val ); 1023 if ( !op->ors_filter ) { 1024 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 1025 return REWRITE_ERR; 1026 } 1027 1028 op->ors_attrsonly = 0; 1029 op->o_dn = op->o_bd->be_rootdn; 1030 op->o_ndn = op->o_bd->be_rootndn; 1031 op->o_do_not_cache = 1; 1032 1033 cb.sc_response = slapd_rw_cb; 1034 cb.sc_private = &si; 1035 op->o_callback = &cb; 1036 1037 rc = op->o_bd->be_search( op, &rs ); 1038 if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &si.si_val )) { 1039 *val = si.si_val; 1040 rc = REWRITE_SUCCESS; 1041 } else { 1042 if ( !BER_BVISNULL( &si.si_val )) { 1043 ch_free( si.si_val.bv_val ); 1044 } 1045 rc = REWRITE_ERR; 1046 } 1047 filter_free_x( op, op->ors_filter ); 1048 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 1049 return rc; 1050} 1051 1052static int 1053slapd_rw_destroy( void *private ) 1054{ 1055 slapd_map_data *md = private; 1056 1057 assert( private != NULL ); 1058 1059 ch_free( md->base.bv_val ); 1060 ch_free( md->filter.bv_val ); 1061 ch_free( md ); 1062 1063 return 0; 1064} 1065 1066static const rewrite_mapper slapd_mapper = { 1067 "slapd", 1068 slapd_rw_config, 1069 slapd_rw_apply, 1070 slapd_rw_destroy 1071}; 1072#endif 1073 1074int slap_sasl_init( void ) 1075{ 1076#ifdef HAVE_CYRUS_SASL 1077 int rc; 1078 static sasl_callback_t server_callbacks[] = { 1079 { SASL_CB_LOG, &slap_sasl_log, NULL }, 1080 { SASL_CB_LIST_END, NULL, NULL } 1081 }; 1082#endif 1083 1084#ifdef ENABLE_REWRITE 1085 rewrite_mapper_register( &slapd_mapper ); 1086#endif 1087 1088#ifdef HAVE_CYRUS_SASL 1089#ifdef HAVE_SASL_VERSION 1090 /* stringify the version number, sasl.h doesn't do it for us */ 1091#define VSTR0(maj, min, pat) #maj "." #min "." #pat 1092#define VSTR(maj, min, pat) VSTR0(maj, min, pat) 1093#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ 1094 SASL_VERSION_STEP) 1095 1096 sasl_version( NULL, &rc ); 1097 if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) || 1098 (rc & 0xffff) < SASL_VERSION_STEP) 1099 { 1100 char version[sizeof("xxx.xxx.xxxxx")]; 1101 sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff, 1102 rc & 0xffff ); 1103 Debug( LDAP_DEBUG_ANY, "slap_sasl_init: SASL library version mismatch:" 1104 " expected %s, got %s\n", 1105 SASL_VERSION_STRING, version, 0 ); 1106 return -1; 1107 } 1108#endif 1109 1110 sasl_set_mutex( 1111 ldap_pvt_sasl_mutex_new, 1112 ldap_pvt_sasl_mutex_lock, 1113 ldap_pvt_sasl_mutex_unlock, 1114 ldap_pvt_sasl_mutex_dispose ); 1115 1116 generic_filter.f_desc = slap_schema.si_ad_objectClass; 1117 1118 rc = sasl_auxprop_add_plugin( "slapd", slap_auxprop_init ); 1119 if( rc != SASL_OK ) { 1120 Debug( LDAP_DEBUG_ANY, "slap_sasl_init: auxprop add plugin failed\n", 1121 0, 0, 0 ); 1122 return -1; 1123 } 1124 1125 /* should provide callbacks for logging */ 1126 /* server name should be configurable */ 1127 rc = sasl_server_init( server_callbacks, "slapd" ); 1128 1129 if( rc != SASL_OK ) { 1130 Debug( LDAP_DEBUG_ANY, "slap_sasl_init: server init failed\n", 1131 0, 0, 0 ); 1132 1133 return -1; 1134 } 1135 1136#ifdef SLAPD_SPASSWD 1137 lutil_passwd_add( &sasl_pwscheme, chk_sasl, NULL ); 1138#endif 1139 1140 Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n", 1141 0, 0, 0 ); 1142 1143 /* default security properties */ 1144 memset( &sasl_secprops, '\0', sizeof(sasl_secprops) ); 1145 sasl_secprops.max_ssf = INT_MAX; 1146 sasl_secprops.maxbufsize = 65536; 1147 sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS; 1148#endif 1149 1150 return 0; 1151} 1152 1153int slap_sasl_destroy( void ) 1154{ 1155#ifdef HAVE_CYRUS_SASL 1156 sasl_done(); 1157#endif 1158 free( sasl_host ); 1159 sasl_host = NULL; 1160 1161 return 0; 1162} 1163 1164static char * 1165slap_sasl_peer2ipport( struct berval *peer ) 1166{ 1167 int isv6 = 0; 1168 char *ipport, *p, 1169 *addr = &peer->bv_val[ STRLENOF( "IP=" ) ]; 1170 ber_len_t plen = peer->bv_len - STRLENOF( "IP=" ); 1171 1172 /* IPv6? */ 1173 if ( addr[0] == '[' ) { 1174 isv6 = 1; 1175 plen--; 1176 } 1177 ipport = ch_strdup( &addr[isv6] ); 1178 1179 /* Convert IPv6/IPv4 addresses to address;port syntax. */ 1180 p = strrchr( ipport, ':' ); 1181 if ( p != NULL ) { 1182 *p = ';'; 1183 if ( isv6 ) { 1184 assert( p[-1] == ']' ); 1185 AC_MEMCPY( &p[-1], p, plen - ( p - ipport ) + 1 ); 1186 } 1187 1188 } else if ( isv6 ) { 1189 /* trim ']' */ 1190 plen--; 1191 assert( addr[plen] == ']' ); 1192 addr[plen] = '\0'; 1193 } 1194 1195 return ipport; 1196} 1197 1198int slap_sasl_open( Connection *conn, int reopen ) 1199{ 1200 int sc = LDAP_SUCCESS; 1201#ifdef HAVE_CYRUS_SASL 1202 int cb; 1203 1204 sasl_conn_t *ctx = NULL; 1205 sasl_callback_t *session_callbacks; 1206 char *ipremoteport = NULL, *iplocalport = NULL; 1207 1208 assert( conn->c_sasl_authctx == NULL ); 1209 1210 if ( !reopen ) { 1211 assert( conn->c_sasl_extra == NULL ); 1212 1213 session_callbacks = 1214 SLAP_CALLOC( 5, sizeof(sasl_callback_t)); 1215 if( session_callbacks == NULL ) { 1216 Debug( LDAP_DEBUG_ANY, 1217 "slap_sasl_open: SLAP_MALLOC failed", 0, 0, 0 ); 1218 return -1; 1219 } 1220 conn->c_sasl_extra = session_callbacks; 1221 1222 session_callbacks[cb=0].id = SASL_CB_LOG; 1223 session_callbacks[cb].proc = &slap_sasl_log; 1224 session_callbacks[cb++].context = conn; 1225 1226 session_callbacks[cb].id = SASL_CB_PROXY_POLICY; 1227 session_callbacks[cb].proc = &slap_sasl_authorize; 1228 session_callbacks[cb++].context = conn; 1229 1230 session_callbacks[cb].id = SASL_CB_CANON_USER; 1231 session_callbacks[cb].proc = &slap_sasl_canonicalize; 1232 session_callbacks[cb++].context = conn; 1233 1234 session_callbacks[cb].id = SASL_CB_LIST_END; 1235 session_callbacks[cb].proc = NULL; 1236 session_callbacks[cb++].context = NULL; 1237 } else { 1238 session_callbacks = conn->c_sasl_extra; 1239 } 1240 1241 conn->c_sasl_layers = 0; 1242 1243 /* create new SASL context */ 1244 if ( conn->c_sock_name.bv_len != 0 && 1245 strncmp( conn->c_sock_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 ) 1246 { 1247 iplocalport = slap_sasl_peer2ipport( &conn->c_sock_name ); 1248 } 1249 1250 if ( conn->c_peer_name.bv_len != 0 && 1251 strncmp( conn->c_peer_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 ) 1252 { 1253 ipremoteport = slap_sasl_peer2ipport( &conn->c_peer_name ); 1254 } 1255 1256 sc = sasl_server_new( "ldap", sasl_host, global_realm, 1257 iplocalport, ipremoteport, session_callbacks, SASL_SUCCESS_DATA, &ctx ); 1258 if ( iplocalport != NULL ) { 1259 ch_free( iplocalport ); 1260 } 1261 if ( ipremoteport != NULL ) { 1262 ch_free( ipremoteport ); 1263 } 1264 1265 if( sc != SASL_OK ) { 1266 Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n", 1267 sc, 0, 0 ); 1268 1269 return -1; 1270 } 1271 1272 conn->c_sasl_authctx = ctx; 1273 1274 if( sc == SASL_OK ) { 1275 sc = sasl_setprop( ctx, 1276 SASL_SEC_PROPS, &sasl_secprops ); 1277 1278 if( sc != SASL_OK ) { 1279 Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n", 1280 sc, 0, 0 ); 1281 1282 slap_sasl_close( conn ); 1283 return -1; 1284 } 1285 } 1286 1287 sc = slap_sasl_err2ldap( sc ); 1288 1289#elif defined(SLAP_BUILTIN_SASL) 1290 /* built-in SASL implementation */ 1291 SASL_CTX *ctx = (SASL_CTX *) SLAP_MALLOC(sizeof(SASL_CTX)); 1292 if( ctx == NULL ) return -1; 1293 1294 ctx->sc_external_ssf = 0; 1295 BER_BVZERO( &ctx->sc_external_id ); 1296 1297 conn->c_sasl_authctx = ctx; 1298#endif 1299 1300 return sc; 1301} 1302 1303int slap_sasl_external( 1304 Connection *conn, 1305 slap_ssf_t ssf, 1306 struct berval *auth_id ) 1307{ 1308#ifdef HAVE_CYRUS_SASL 1309 int sc; 1310 sasl_conn_t *ctx = conn->c_sasl_authctx; 1311 sasl_ssf_t sasl_ssf = ssf; 1312 1313 if ( ctx == NULL ) { 1314 return LDAP_UNAVAILABLE; 1315 } 1316 1317 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); 1318 1319 if ( sc != SASL_OK ) { 1320 return LDAP_OTHER; 1321 } 1322 1323 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, 1324 auth_id ? auth_id->bv_val : NULL ); 1325 1326 if ( sc != SASL_OK ) { 1327 return LDAP_OTHER; 1328 } 1329#elif defined(SLAP_BUILTIN_SASL) 1330 /* built-in SASL implementation */ 1331 SASL_CTX *ctx = conn->c_sasl_authctx; 1332 if ( ctx == NULL ) return LDAP_UNAVAILABLE; 1333 1334 ctx->sc_external_ssf = ssf; 1335 if( auth_id ) { 1336 ctx->sc_external_id = *auth_id; 1337 BER_BVZERO( auth_id ); 1338 } else { 1339 BER_BVZERO( &ctx->sc_external_id ); 1340 } 1341#endif 1342 1343 return LDAP_SUCCESS; 1344} 1345 1346int slap_sasl_reset( Connection *conn ) 1347{ 1348 return LDAP_SUCCESS; 1349} 1350 1351char ** slap_sasl_mechs( Connection *conn ) 1352{ 1353 char **mechs = NULL; 1354 1355#ifdef HAVE_CYRUS_SASL 1356 sasl_conn_t *ctx = conn->c_sasl_authctx; 1357 1358 if( ctx == NULL ) ctx = conn->c_sasl_sockctx; 1359 1360 if( ctx != NULL ) { 1361 int sc; 1362 SASL_CONST char *mechstr; 1363 1364 sc = sasl_listmech( ctx, 1365 NULL, NULL, ",", NULL, 1366 &mechstr, NULL, NULL ); 1367 1368 if( sc != SASL_OK ) { 1369 Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n", 1370 sc, 0, 0 ); 1371 1372 return NULL; 1373 } 1374 1375 mechs = ldap_str2charray( mechstr, "," ); 1376 } 1377#elif defined(SLAP_BUILTIN_SASL) 1378 /* builtin SASL implementation */ 1379 SASL_CTX *ctx = conn->c_sasl_authctx; 1380 if ( ctx != NULL && ctx->sc_external_id.bv_val ) { 1381 /* should check ssf */ 1382 mechs = ldap_str2charray( "EXTERNAL", "," ); 1383 } 1384#endif 1385 1386 return mechs; 1387} 1388 1389int slap_sasl_close( Connection *conn ) 1390{ 1391#ifdef HAVE_CYRUS_SASL 1392 sasl_conn_t *ctx = conn->c_sasl_authctx; 1393 1394 if( ctx != NULL ) { 1395 sasl_dispose( &ctx ); 1396 } 1397 if ( conn->c_sasl_sockctx && 1398 conn->c_sasl_authctx != conn->c_sasl_sockctx ) 1399 { 1400 ctx = conn->c_sasl_sockctx; 1401 sasl_dispose( &ctx ); 1402 } 1403 1404 conn->c_sasl_authctx = NULL; 1405 conn->c_sasl_sockctx = NULL; 1406 conn->c_sasl_done = 0; 1407 1408 free( conn->c_sasl_extra ); 1409 conn->c_sasl_extra = NULL; 1410 1411#elif defined(SLAP_BUILTIN_SASL) 1412 SASL_CTX *ctx = conn->c_sasl_authctx; 1413 if( ctx ) { 1414 if( ctx->sc_external_id.bv_val ) { 1415 free( ctx->sc_external_id.bv_val ); 1416 BER_BVZERO( &ctx->sc_external_id ); 1417 } 1418 free( ctx ); 1419 conn->c_sasl_authctx = NULL; 1420 } 1421#endif 1422 1423 return LDAP_SUCCESS; 1424} 1425 1426int slap_sasl_bind( Operation *op, SlapReply *rs ) 1427{ 1428#ifdef HAVE_CYRUS_SASL 1429 sasl_conn_t *ctx = op->o_conn->c_sasl_authctx; 1430 struct berval response; 1431 unsigned reslen = 0; 1432 int sc; 1433 1434 Debug(LDAP_DEBUG_ARGS, 1435 "==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n", 1436 op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "", 1437 op->o_conn->c_sasl_bind_in_progress ? "<continuing>" : 1438 op->o_conn->c_sasl_bind_mech.bv_val, 1439 op->orb_cred.bv_len ); 1440 1441 if( ctx == NULL ) { 1442 send_ldap_error( op, rs, LDAP_UNAVAILABLE, 1443 "SASL unavailable on this session" ); 1444 return rs->sr_err; 1445 } 1446 1447#define START( ctx, mech, cred, clen, resp, rlen, err ) \ 1448 sasl_server_start( ctx, mech, cred, clen, resp, rlen ) 1449#define STEP( ctx, cred, clen, resp, rlen, err ) \ 1450 sasl_server_step( ctx, cred, clen, resp, rlen ) 1451 1452 if ( !op->o_conn->c_sasl_bind_in_progress ) { 1453 /* If we already authenticated once, must use a new context */ 1454 if ( op->o_conn->c_sasl_done ) { 1455 sasl_ssf_t ssf = 0; 1456 const char *authid = NULL; 1457 sasl_getprop( ctx, SASL_SSF_EXTERNAL, (void *)&ssf ); 1458 sasl_getprop( ctx, SASL_AUTH_EXTERNAL, (void *)&authid ); 1459 if ( authid ) authid = ch_strdup( authid ); 1460 if ( ctx != op->o_conn->c_sasl_sockctx ) { 1461 sasl_dispose( &ctx ); 1462 } 1463 op->o_conn->c_sasl_authctx = NULL; 1464 1465 slap_sasl_open( op->o_conn, 1 ); 1466 ctx = op->o_conn->c_sasl_authctx; 1467 if ( authid ) { 1468 sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf ); 1469 sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); 1470 ch_free( (char *)authid ); 1471 } 1472 } 1473 sc = START( ctx, 1474 op->o_conn->c_sasl_bind_mech.bv_val, 1475 op->orb_cred.bv_val, op->orb_cred.bv_len, 1476 (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text ); 1477 1478 } else { 1479 sc = STEP( ctx, 1480 op->orb_cred.bv_val, op->orb_cred.bv_len, 1481 (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text ); 1482 } 1483 1484 response.bv_len = reslen; 1485 1486 if ( sc == SASL_OK ) { 1487 sasl_ssf_t *ssf = NULL; 1488 1489 ber_dupbv_x( &op->orb_edn, &op->o_conn->c_sasl_dn, op->o_tmpmemctx ); 1490 BER_BVZERO( &op->o_conn->c_sasl_dn ); 1491 op->o_conn->c_sasl_done = 1; 1492 1493 rs->sr_err = LDAP_SUCCESS; 1494 1495 (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf ); 1496 op->orb_ssf = ssf ? *ssf : 0; 1497 1498 ctx = NULL; 1499 if( op->orb_ssf ) { 1500 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 1501 op->o_conn->c_sasl_layers++; 1502 1503 /* If there's an old layer, set sockctx to NULL to 1504 * tell connection_read() to wait for us to finish. 1505 * Otherwise there is a race condition: we have to 1506 * send the Bind response using the old security 1507 * context and then remove it before reading any 1508 * new messages. 1509 */ 1510 if ( op->o_conn->c_sasl_sockctx ) { 1511 ctx = op->o_conn->c_sasl_sockctx; 1512 op->o_conn->c_sasl_sockctx = NULL; 1513 } else { 1514 op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx; 1515 } 1516 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 1517 } 1518 1519 /* Must send response using old security layer */ 1520 if (response.bv_len) rs->sr_sasldata = &response; 1521 send_ldap_sasl( op, rs ); 1522 1523 /* Now dispose of the old security layer. 1524 */ 1525 if ( ctx ) { 1526 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 1527 ldap_pvt_sasl_remove( op->o_conn->c_sb ); 1528 op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx; 1529 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 1530 sasl_dispose( &ctx ); 1531 } 1532 } else if ( sc == SASL_CONTINUE ) { 1533 rs->sr_err = LDAP_SASL_BIND_IN_PROGRESS, 1534 rs->sr_text = sasl_errdetail( ctx ); 1535 rs->sr_sasldata = &response; 1536 send_ldap_sasl( op, rs ); 1537 1538 } else { 1539 BER_BVZERO( &op->o_conn->c_sasl_dn ); 1540 rs->sr_text = sasl_errdetail( ctx ); 1541 rs->sr_err = slap_sasl_err2ldap( sc ), 1542 send_ldap_result( op, rs ); 1543 } 1544 1545 Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rs->sr_err, 0, 0); 1546 1547#elif defined(SLAP_BUILTIN_SASL) 1548 /* built-in SASL implementation */ 1549 SASL_CTX *ctx = op->o_conn->c_sasl_authctx; 1550 1551 if ( ctx == NULL ) { 1552 send_ldap_error( op, rs, LDAP_OTHER, 1553 "Internal SASL Error" ); 1554 1555 } else if ( bvmatch( &ext_bv, &op->o_conn->c_sasl_bind_mech ) ) { 1556 /* EXTERNAL */ 1557 1558 if( op->orb_cred.bv_len ) { 1559 rs->sr_text = "proxy authorization not support"; 1560 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 1561 send_ldap_result( op, rs ); 1562 1563 } else { 1564 op->orb_edn = ctx->sc_external_id; 1565 rs->sr_err = LDAP_SUCCESS; 1566 rs->sr_sasldata = NULL; 1567 send_ldap_sasl( op, rs ); 1568 } 1569 1570 } else { 1571 send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED, 1572 "requested SASL mechanism not supported" ); 1573 } 1574#else 1575 send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED, 1576 "SASL not supported" ); 1577#endif 1578 1579 return rs->sr_err; 1580} 1581 1582char* slap_sasl_secprops( const char *in ) 1583{ 1584#ifdef HAVE_CYRUS_SASL 1585 int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops ); 1586 1587 return rc == LDAP_SUCCESS ? NULL : "Invalid security properties"; 1588#else 1589 return "SASL not supported"; 1590#endif 1591} 1592 1593void slap_sasl_secprops_unparse( struct berval *bv ) 1594{ 1595#ifdef HAVE_CYRUS_SASL 1596 ldap_pvt_sasl_secprops_unparse( &sasl_secprops, bv ); 1597#endif 1598} 1599 1600#ifdef HAVE_CYRUS_SASL 1601int 1602slap_sasl_setpass( Operation *op, SlapReply *rs ) 1603{ 1604 struct berval id = BER_BVNULL; /* needs to come from connection */ 1605 struct berval new = BER_BVNULL; 1606 struct berval old = BER_BVNULL; 1607 1608 assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 ); 1609 1610 rs->sr_err = sasl_getprop( op->o_conn->c_sasl_authctx, SASL_USERNAME, 1611 (SASL_CONST void **)(char *)&id.bv_val ); 1612 1613 if( rs->sr_err != SASL_OK ) { 1614 rs->sr_text = "unable to retrieve SASL username"; 1615 rs->sr_err = LDAP_OTHER; 1616 goto done; 1617 } 1618 1619 Debug( LDAP_DEBUG_ARGS, "==> slap_sasl_setpass: \"%s\"\n", 1620 id.bv_val ? id.bv_val : "", 0, 0 ); 1621 1622 rs->sr_err = slap_passwd_parse( op->ore_reqdata, 1623 NULL, &old, &new, &rs->sr_text ); 1624 1625 if( rs->sr_err != LDAP_SUCCESS ) { 1626 goto done; 1627 } 1628 1629 if( new.bv_len == 0 ) { 1630 slap_passwd_generate(&new); 1631 1632 if( new.bv_len == 0 ) { 1633 rs->sr_text = "password generation failed."; 1634 rs->sr_err = LDAP_OTHER; 1635 goto done; 1636 } 1637 1638 rs->sr_rspdata = slap_passwd_return( &new ); 1639 } 1640 1641 rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx, id.bv_val, 1642 new.bv_val, new.bv_len, old.bv_val, old.bv_len, 0 ); 1643 if( rs->sr_err != SASL_OK ) { 1644 rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_authctx ); 1645 } 1646 switch(rs->sr_err) { 1647 case SASL_OK: 1648 rs->sr_err = LDAP_SUCCESS; 1649 break; 1650 1651 case SASL_NOCHANGE: 1652 case SASL_NOMECH: 1653 case SASL_DISABLED: 1654 case SASL_PWLOCK: 1655 case SASL_FAIL: 1656 case SASL_BADPARAM: 1657 default: 1658 rs->sr_err = LDAP_OTHER; 1659 } 1660 1661done: 1662 return rs->sr_err; 1663} 1664#endif /* HAVE_CYRUS_SASL */ 1665 1666/* Take any sort of identity string and return a DN with the "dn:" prefix. The 1667 * string returned in *dn is in its own allocated memory, and must be free'd 1668 * by the calling process. -Mark Adamson, Carnegie Mellon 1669 * 1670 * The "dn:" prefix is no longer used anywhere inside slapd. It is only used 1671 * on strings passed in directly from SASL. -Howard Chu, Symas Corp. 1672 */ 1673 1674#define SET_NONE 0 1675#define SET_DN 1 1676#define SET_U 2 1677 1678int slap_sasl_getdn( Connection *conn, Operation *op, struct berval *id, 1679 char *user_realm, struct berval *dn, int flags ) 1680{ 1681 int rc, is_dn = SET_NONE, do_norm = 1; 1682 struct berval dn2, *mech; 1683 1684 assert( conn != NULL ); 1685 assert( id != NULL ); 1686 1687 Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: conn %lu id=%s [len=%lu]\n", 1688 conn->c_connid, 1689 BER_BVISNULL( id ) ? "NULL" : ( BER_BVISEMPTY( id ) ? "<empty>" : id->bv_val ), 1690 BER_BVISNULL( id ) ? 0 : ( BER_BVISEMPTY( id ) ? 0 : 1691 (unsigned long) id->bv_len ) ); 1692 1693 if ( !op ) { 1694 op = conn->c_sasl_bindop; 1695 } 1696 assert( op != NULL ); 1697 1698 BER_BVZERO( dn ); 1699 1700 if ( !BER_BVISNULL( id ) ) { 1701 /* Blatantly anonymous ID */ 1702 static struct berval bv_anonymous = BER_BVC( "anonymous" ); 1703 1704 if ( ber_bvstrcasecmp( id, &bv_anonymous ) == 0 ) { 1705 return( LDAP_SUCCESS ); 1706 } 1707 1708 } else { 1709 /* FIXME: if empty, should we stop? */ 1710 BER_BVSTR( id, "" ); 1711 } 1712 1713 if ( !BER_BVISEMPTY( &conn->c_sasl_bind_mech ) ) { 1714 mech = &conn->c_sasl_bind_mech; 1715 } else { 1716 mech = &conn->c_authmech; 1717 } 1718 1719 /* An authcID needs to be converted to authzID form. Set the 1720 * values directly into *dn; they will be normalized later. (and 1721 * normalizing always makes a new copy.) An ID from a TLS certificate 1722 * is already normalized, so copy it and skip normalization. 1723 */ 1724 if( flags & SLAP_GETDN_AUTHCID ) { 1725 if( bvmatch( mech, &ext_bv )) { 1726 /* EXTERNAL DNs are already normalized */ 1727 assert( !BER_BVISNULL( id ) ); 1728 1729 do_norm = 0; 1730 is_dn = SET_DN; 1731 ber_dupbv_x( dn, id, op->o_tmpmemctx ); 1732 1733 } else { 1734 /* convert to u:<username> form */ 1735 is_dn = SET_U; 1736 *dn = *id; 1737 } 1738 } 1739 1740 if( is_dn == SET_NONE ) { 1741 if( !strncasecmp( id->bv_val, "u:", STRLENOF( "u:" ) ) ) { 1742 is_dn = SET_U; 1743 dn->bv_val = id->bv_val + STRLENOF( "u:" ); 1744 dn->bv_len = id->bv_len - STRLENOF( "u:" ); 1745 1746 } else if ( !strncasecmp( id->bv_val, "dn:", STRLENOF( "dn:" ) ) ) { 1747 is_dn = SET_DN; 1748 dn->bv_val = id->bv_val + STRLENOF( "dn:" ); 1749 dn->bv_len = id->bv_len - STRLENOF( "dn:" ); 1750 } 1751 } 1752 1753 /* No other possibilities from here */ 1754 if( is_dn == SET_NONE ) { 1755 BER_BVZERO( dn ); 1756 return( LDAP_INAPPROPRIATE_AUTH ); 1757 } 1758 1759 /* Username strings */ 1760 if( is_dn == SET_U ) { 1761 /* ITS#3419: values may need escape */ 1762 LDAPRDN DN[ 5 ]; 1763 LDAPAVA *RDNs[ 4 ][ 2 ]; 1764 LDAPAVA AVAs[ 4 ]; 1765 int irdn; 1766 1767 irdn = 0; 1768 DN[ irdn ] = RDNs[ irdn ]; 1769 RDNs[ irdn ][ 0 ] = &AVAs[ irdn ]; 1770 AVAs[ irdn ].la_attr = slap_schema.si_ad_uid->ad_cname; 1771 AVAs[ irdn ].la_value = *dn; 1772 AVAs[ irdn ].la_flags = LDAP_AVA_NULL; 1773 AVAs[ irdn ].la_private = NULL; 1774 RDNs[ irdn ][ 1 ] = NULL; 1775 1776 if ( user_realm && *user_realm ) { 1777 irdn++; 1778 DN[ irdn ] = RDNs[ irdn ]; 1779 RDNs[ irdn ][ 0 ] = &AVAs[ irdn ]; 1780 AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname; 1781 ber_str2bv( user_realm, 0, 0, &AVAs[ irdn ].la_value ); 1782 AVAs[ irdn ].la_flags = LDAP_AVA_NULL; 1783 AVAs[ irdn ].la_private = NULL; 1784 RDNs[ irdn ][ 1 ] = NULL; 1785 } 1786 1787 if ( !BER_BVISNULL( mech ) ) { 1788 irdn++; 1789 DN[ irdn ] = RDNs[ irdn ]; 1790 RDNs[ irdn ][ 0 ] = &AVAs[ irdn ]; 1791 AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname; 1792 AVAs[ irdn ].la_value = *mech; 1793 AVAs[ irdn ].la_flags = LDAP_AVA_NULL; 1794 AVAs[ irdn ].la_private = NULL; 1795 RDNs[ irdn ][ 1 ] = NULL; 1796 } 1797 1798 irdn++; 1799 DN[ irdn ] = RDNs[ irdn ]; 1800 RDNs[ irdn ][ 0 ] = &AVAs[ irdn ]; 1801 AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname; 1802 BER_BVSTR( &AVAs[ irdn ].la_value, "auth" ); 1803 AVAs[ irdn ].la_flags = LDAP_AVA_NULL; 1804 AVAs[ irdn ].la_private = NULL; 1805 RDNs[ irdn ][ 1 ] = NULL; 1806 1807 irdn++; 1808 DN[ irdn ] = NULL; 1809 1810 rc = ldap_dn2bv_x( DN, dn, LDAP_DN_FORMAT_LDAPV3, 1811 op->o_tmpmemctx ); 1812 if ( rc != LDAP_SUCCESS ) { 1813 BER_BVZERO( dn ); 1814 return rc; 1815 } 1816 1817 Debug( LDAP_DEBUG_TRACE, 1818 "slap_sasl_getdn: u:id converted to %s\n", 1819 dn->bv_val, 0, 0 ); 1820 1821 } else { 1822 1823 /* Dup the DN in any case, so we don't risk 1824 * leaks or dangling pointers later, 1825 * and the DN value is '\0' terminated */ 1826 ber_dupbv_x( &dn2, dn, op->o_tmpmemctx ); 1827 dn->bv_val = dn2.bv_val; 1828 } 1829 1830 /* All strings are in DN form now. Normalize if needed. */ 1831 if ( do_norm ) { 1832 rc = dnNormalize( 0, NULL, NULL, dn, &dn2, op->o_tmpmemctx ); 1833 1834 /* User DNs were constructed above and must be freed now */ 1835 slap_sl_free( dn->bv_val, op->o_tmpmemctx ); 1836 1837 if ( rc != LDAP_SUCCESS ) { 1838 BER_BVZERO( dn ); 1839 return rc; 1840 } 1841 *dn = dn2; 1842 } 1843 1844 /* Run thru regexp */ 1845 slap_sasl2dn( op, dn, &dn2, flags ); 1846 if( !BER_BVISNULL( &dn2 ) ) { 1847 slap_sl_free( dn->bv_val, op->o_tmpmemctx ); 1848 *dn = dn2; 1849 Debug( LDAP_DEBUG_TRACE, 1850 "slap_sasl_getdn: dn:id converted to %s\n", 1851 dn->bv_val, 0, 0 ); 1852 } 1853 1854 return( LDAP_SUCCESS ); 1855} 1856