1/* passwd.c - password extended operation routines */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-2011 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 17#include "portable.h" 18 19#include <stdio.h> 20 21#include <ac/socket.h> 22#include <ac/string.h> 23#include <ac/unistd.h> 24 25#ifdef SLAPD_CRYPT 26#include <ac/crypt.h> 27#endif 28 29#include "slap.h" 30#ifdef __APPLE__ 31#include "applehelpers.h" 32#include "psauth.h" 33#endif 34 35#include <lber_pvt.h> 36#include <lutil.h> 37#include <lutil_sha1.h> 38 39const struct berval slap_EXOP_MODIFY_PASSWD = BER_BVC(LDAP_EXOP_MODIFY_PASSWD); 40 41static const char *defhash[] = { 42#ifdef LUTIL_SHA1_BYTES 43 "{SSHA}", 44#else 45 "{SMD5}", 46#endif 47 NULL 48}; 49 50int passwd_extop( 51 Operation *op, 52 SlapReply *rs ) 53{ 54 struct berval id = {0, NULL}, hash, *rsp = NULL; 55 req_pwdexop_s *qpw = &op->oq_pwdexop; 56 req_extended_s qext = op->oq_extended; 57 Modifications *ml; 58 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 59 int i, nhash; 60 char **hashes, idNul; 61 int rc; 62 BackendDB *op_be; 63 int freenewpw = 0; 64 struct berval dn = BER_BVNULL, ndn = BER_BVNULL; 65#ifdef __APPLE__ 66 CFDictionaryRef policy = NULL; 67 int isChangingOwnPassword = 0; 68 bool isldapi = false; 69 if((op->o_conn->c_listener->sl_url.bv_len == strlen("ldapi://%2Fvar%2Frun%2Fldapi")) && (strncmp(op->o_conn->c_listener->sl_url.bv_val, "ldapi://%2Fvar%2Frun%2Fldapi", op->o_conn->c_listener->sl_url.bv_len) == 0)) { 70 isldapi = true; 71 } 72#endif 73 74 assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 ); 75 76#ifndef __APPLE__ 77 if( op->o_dn.bv_len == 0 ) { 78 Statslog( LDAP_DEBUG_STATS, "%s PASSMOD\n", 79 op->o_log_prefix, 0, 0, 0, 0 ); 80 rs->sr_text = "only authenticated users may change passwords"; 81 return LDAP_STRONG_AUTH_REQUIRED; 82 } 83#endif 84 85 qpw->rs_old.bv_len = 0; 86 qpw->rs_old.bv_val = NULL; 87 qpw->rs_new.bv_len = 0; 88 qpw->rs_new.bv_val = NULL; 89 qpw->rs_mods = NULL; 90 qpw->rs_modtail = NULL; 91 92 rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id, 93 &qpw->rs_old, &qpw->rs_new, &rs->sr_text ); 94 95 if ( !BER_BVISNULL( &id )) { 96 idNul = id.bv_val[id.bv_len]; 97 id.bv_val[id.bv_len] = '\0'; 98 } 99 if ( rs->sr_err == LDAP_SUCCESS && !BER_BVISEMPTY( &id ) ) { 100 Statslog( LDAP_DEBUG_STATS, "%s PASSMOD id=\"%s\"%s%s\n", 101 op->o_log_prefix, id.bv_val, 102 qpw->rs_old.bv_val ? " old" : "", 103 qpw->rs_new.bv_val ? " new" : "", 0 ); 104 } else { 105 Statslog( LDAP_DEBUG_STATS, "%s PASSMOD%s%s\n", 106 op->o_log_prefix, 107 qpw->rs_old.bv_val ? " old" : "", 108 qpw->rs_new.bv_val ? " new" : "", 0, 0 ); 109 } 110 111 if ( rs->sr_err != LDAP_SUCCESS ) { 112 if ( !BER_BVISNULL( &id )) 113 id.bv_val[id.bv_len] = idNul; 114 return rs->sr_err; 115 } 116 117 if ( !BER_BVISEMPTY( &id ) ) { 118 rs->sr_err = dnPrettyNormal( NULL, &id, &dn, &ndn, op->o_tmpmemctx ); 119 id.bv_val[id.bv_len] = idNul; 120 if ( rs->sr_err != LDAP_SUCCESS ) { 121 rs->sr_text = "Invalid DN"; 122 rc = rs->sr_err; 123 goto error_return; 124 } 125 op->o_req_dn = dn; 126 op->o_req_ndn = ndn; 127 op->o_bd = select_backend( &op->o_req_ndn, 1 ); 128 129 } else { 130 ber_dupbv_x( &dn, &op->o_dn, op->o_tmpmemctx ); 131 ber_dupbv_x( &ndn, &op->o_ndn, op->o_tmpmemctx ); 132 op->o_req_dn = dn; 133 op->o_req_ndn = ndn; 134 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 135 op->o_bd = op->o_conn->c_authz_backend; 136 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 137 } 138 139 if( op->o_bd == NULL ) { 140 if ( qpw->rs_old.bv_val != NULL ) { 141 rs->sr_text = "unwilling to verify old password"; 142 rc = LDAP_UNWILLING_TO_PERFORM; 143 goto error_return; 144 } 145 146#ifdef HAVE_CYRUS_SASL 147 rc = slap_sasl_setpass( op, rs ); 148#else 149 rs->sr_text = "no authz backend"; 150 rc = LDAP_OTHER; 151#endif 152 goto error_return; 153 } 154 155 if ( op->o_req_ndn.bv_len == 0 ) { 156 rs->sr_text = "no password is associated with the Root DSE"; 157 rc = LDAP_UNWILLING_TO_PERFORM; 158 goto error_return; 159 } 160 161 /* If we've got a glued backend, check the real backend */ 162 op_be = op->o_bd; 163 if ( SLAP_GLUE_INSTANCE( op->o_bd )) { 164 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 165 } 166 167 if (backend_check_restrictions( op, rs, 168 (struct berval *)&slap_EXOP_MODIFY_PASSWD ) != LDAP_SUCCESS) { 169 rc = rs->sr_err; 170 goto error_return; 171 } 172 173 /* check for referrals */ 174 if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) { 175 rc = rs->sr_err; 176 goto error_return; 177 } 178 179 /* This does not apply to multi-master case */ 180 if(!( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ))) { 181 /* we SHOULD return a referral in this case */ 182 BerVarray defref = op->o_bd->be_update_refs 183 ? op->o_bd->be_update_refs : default_referral; 184 185 if( defref != NULL ) { 186 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs, 187 NULL, NULL, LDAP_SCOPE_DEFAULT ); 188 if(rs->sr_ref) { 189 rs->sr_flags |= REP_REF_MUSTBEFREED; 190 } else { 191 rs->sr_ref = defref; 192 } 193 rc = LDAP_REFERRAL; 194 goto error_return; 195 196 } 197 198 rs->sr_text = "shadow context; no update referral"; 199 rc = LDAP_UNWILLING_TO_PERFORM; 200 goto error_return; 201 } 202 203 /* generate a new password if none was provided */ 204 if ( qpw->rs_new.bv_len == 0 ) { 205 slap_passwd_generate( &qpw->rs_new ); 206 if ( qpw->rs_new.bv_len ) { 207 rsp = slap_passwd_return( &qpw->rs_new ); 208 freenewpw = 1; 209 } 210 } 211 if ( qpw->rs_new.bv_len == 0 ) { 212 rs->sr_text = "password generation failed"; 213 rc = LDAP_OTHER; 214 goto error_return; 215 } 216 217 op->o_bd = op_be; 218 219 /* Give the backend a chance to handle this itself */ 220 if ( op->o_bd->be_extended ) { 221 rs->sr_err = op->o_bd->be_extended( op, rs ); 222 if ( rs->sr_err != LDAP_UNWILLING_TO_PERFORM && 223 rs->sr_err != SLAP_CB_CONTINUE ) 224 { 225 rc = rs->sr_err; 226 if ( rsp ) { 227 rs->sr_rspdata = rsp; 228 rsp = NULL; 229 } 230 goto error_return; 231 } 232 } 233 234 /* The backend didn't handle it, so try it here */ 235 if( op->o_bd && !op->o_bd->be_modify ) { 236 rs->sr_text = "operation not supported for current user"; 237 rc = LDAP_UNWILLING_TO_PERFORM; 238 goto error_return; 239 } 240 241#ifndef __APPLE__ 242 if ( qpw->rs_old.bv_val != NULL ) { 243 Entry *e = NULL; 244 245 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, 246 slap_schema.si_ad_userPassword, 0, &e ); 247 if ( rc == LDAP_SUCCESS && e ) { 248 Attribute *a = attr_find( e->e_attrs, 249 slap_schema.si_ad_userPassword ); 250 if ( a ) 251 rc = slap_passwd_check( op, e, a, &qpw->rs_old, &rs->sr_text ); 252 else 253 rc = 1; 254 be_entry_release_r( op, e ); 255 if ( rc == LDAP_SUCCESS ) 256 goto old_good; 257 } 258 rs->sr_text = "unwilling to verify old password"; 259 rc = LDAP_UNWILLING_TO_PERFORM; 260 goto error_return; 261 } 262#else 263 if( !isldapi && op->o_dn.bv_len == 0 ) { 264 if ( qpw->rs_old.bv_val != NULL ) { 265 char *recname = odusers_copy_recname(op); 266 // nul terminated version of the old password 267 char *tmpoldpass = ch_calloc(qpw->rs_old.bv_len + 1, 1); 268 memcpy(tmpoldpass, qpw->rs_old.bv_val, qpw->rs_old.bv_len); 269 op->o_conn->c_sasl_bindop = op; 270 rc = (DoPSAuth(recname, tmpoldpass, NULL, op->o_conn, op->o_req_dn.bv_val) == kAuthNoError) ? LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS; 271 op->o_conn->c_sasl_bindop = NULL; 272 free(tmpoldpass); 273 free(recname); 274 if(rc == LDAP_SUCCESS) { 275 isChangingOwnPassword = 1; 276 goto old_good; 277 } 278 rs->sr_text = "unwilling to verify old password"; 279 rc = LDAP_UNWILLING_TO_PERFORM; 280 goto error_return; 281 } 282 283 rs->sr_text = "only authenticated users may change passwords"; 284 rc = LDAP_STRONG_AUTH_REQUIRED; 285 goto error_return; 286 } else { 287 if(ber_bvstrcasecmp(&op->o_req_dn, &op->o_dn) == 0) { 288 isChangingOwnPassword = 1; 289 } 290 } 291#endif 292 293old_good:; 294#ifndef __APPLE__ 295 ml = ch_malloc( sizeof(Modifications) ); 296 if ( !qpw->rs_modtail ) qpw->rs_modtail = &ml->sml_next; 297 298 if ( default_passwd_hash ) { 299 for ( nhash = 0; default_passwd_hash[nhash]; nhash++ ); 300 hashes = default_passwd_hash; 301 } else { 302 nhash = 1; 303 hashes = (char **)defhash; 304 } 305 ml->sml_numvals = nhash; 306 ml->sml_values = ch_malloc( (nhash+1)*sizeof(struct berval) ); 307 for ( i=0; hashes[i]; i++ ) { 308 slap_passwd_hash_type( &qpw->rs_new, &hash, hashes[i], &rs->sr_text ); 309 if ( hash.bv_len == 0 ) { 310 if ( !rs->sr_text ) { 311 rs->sr_text = "password hash failed"; 312 } 313 break; 314 } 315 ml->sml_values[i] = hash; 316 } 317 ml->sml_values[i].bv_val = NULL; 318 ml->sml_nvalues = NULL; 319 ml->sml_desc = slap_schema.si_ad_userPassword; 320 ml->sml_type = ml->sml_desc->ad_cname; 321 ml->sml_op = LDAP_MOD_REPLACE; 322 ml->sml_flags = 0; 323 ml->sml_next = qpw->rs_mods; 324 qpw->rs_mods = ml; 325 326 if ( hashes[i] ) { 327 rs->sr_err = LDAP_OTHER; 328 329 } else { 330 slap_callback *sc = op->o_callback; 331 332 op->o_tag = LDAP_REQ_MODIFY; 333 op->o_callback = &cb; 334 op->orm_modlist = qpw->rs_mods; 335 op->orm_no_opattrs = 0; 336 337 cb.sc_private = qpw; /* let Modify know this was pwdMod, 338 * if it cares... */ 339 340 rs->sr_err = op->o_bd->be_modify( op, rs ); 341 342 /* be_modify() might have shuffled modifications */ 343 qpw->rs_mods = op->orm_modlist; 344 345 if ( rs->sr_err == LDAP_SUCCESS ) { 346 rs->sr_rspdata = rsp; 347 348 } else if ( rsp ) { 349 ber_bvfree( rsp ); 350 rsp = NULL; 351 } 352 op->o_tag = LDAP_REQ_EXTENDED; 353 op->o_callback = sc; 354 } 355 rc = rs->sr_err; 356#else 357 bool isadmin = false; 358 bool isowner = false; 359 bool iscomputer = false; 360 char *tmppass = ch_calloc(qpw->rs_new.bv_len + 1, 1); 361 memcpy(tmppass, qpw->rs_new.bv_val, qpw->rs_new.bv_len); 362 363 if(strnstr(op->o_req_dn.bv_val, "cn=computer", op->o_req_dn.bv_len) != NULL) { 364 iscomputer = true; 365 } 366 367 // Always get the policy so odusers_store_history() can decide if it 368 // should store the password. But in general, policy is only checked 369 // for non-ldapi connections. 370 // 371 // If the connection isn't authenticated (they supplied the old password), 372 // then they are changing their own password. 373 if(op->o_dn.bv_len) { 374 policy = odusers_copy_effectiveuserpoldict(&op->o_conn->c_dn); 375 } else { 376 policy = odusers_copy_effectiveuserpoldict(&op->o_req_dn); 377 } 378 379 if(isldapi == false) { 380 if(!policy) { 381 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve policy for %s, failing password change for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val); 382 rs->sr_text = "unable to retrieve policy"; 383 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 384 free(tmppass); 385 goto error_return; 386 } 387 388 int disableReason = odusers_isdisabled(policy); 389 if(disableReason && (disableReason != kDisabledNewPasswordRequired)) { 390 Debug(LDAP_DEBUG_ANY, "%s: User %s is disabled, denying password change for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val); 391 rs->sr_text = "user is disabled"; 392 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 393 free(tmppass); 394 goto error_return; 395 } 396 397 if( op->o_dn.bv_len && (ber_bvcmp(&op->o_req_ndn, &op->o_conn->c_dn) != 0) ) { 398 if(!odusers_isadmin(policy)) { 399 char *ownername = odusers_copy_owner(&op->o_req_ndn); 400 if(ownername && strncmp(ownername, op->o_conn->c_dn.bv_val, op->o_conn->c_dn.bv_len) == 0) { 401 isowner = true; 402 } else { 403 Debug(LDAP_DEBUG_ANY, "%s: non-admin user %s denied attempt to change password for %s.\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val); 404 rs->sr_text = "permission denied"; 405 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 406 free(tmppass); 407 free(ownername); 408 goto error_return; 409 } 410 free(ownername); 411 } else { 412 isadmin = true; 413 } 414 } else { 415 CFNumberRef canchange = CFDictionaryGetValue(policy, CFSTR("canModifyPasswordforSelf")); 416 if(canchange) { 417 short tmpshort = 0; 418 CFNumberGetValue(canchange, kCFNumberShortType, &tmpshort); 419 if(!tmpshort) { 420 Debug(LDAP_DEBUG_ANY, "%s: canModifyPasswordforSelf on %s denied attempt to change password for %s.\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val); 421 rs->sr_text = "policy violation"; 422 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 423 free(tmppass); 424 goto error_return; 425 } 426 } else { 427 Debug(LDAP_DEBUG_ANY, "%s: No canModifyPasswordforSelf policy found in user %s dictionary", __func__, op->o_req_ndn.bv_val, 0); 428 } 429 } 430 } 431 432 /* ldapi and admins and computers skip policy checks */ 433 if(!isldapi && !isadmin && !isowner && !iscomputer) { 434 char *recname = odusers_copy_recname(op); 435 if(!recname) { 436 Debug(LDAP_DEBUG_ANY, "%s: Could not locate record name for %s\n", __func__, op->o_req_ndn.bv_val, 0); 437 rs->sr_text = "Couldn't locate recname"; 438 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 439 free(tmppass); 440 goto error_return; 441 } 442 443 if(odusers_verify_passwordquality(tmppass, recname, policy, rs) != 0) { 444 Debug(LDAP_DEBUG_ANY, "%s: password quality check failed for %s\n", __func__, op->o_req_ndn.bv_val, 0); 445 free(recname); 446 free(tmppass); 447 rc = rs->sr_err; 448 goto error_return; 449 } 450 free(recname); 451 452 if(odusers_check_history(&op->o_req_dn, tmppass, policy) != 0) { 453 Debug(LDAP_DEBUG_ANY, "%s: password history check failed for %s\n", __func__, op->o_req_dn.bv_val, 0); 454 free(tmppass); 455 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 456 goto error_return; 457 } 458 } 459 460 461 if(odusers_set_password(&op->o_req_dn, tmppass, isChangingOwnPassword) != 0) { 462 Debug(LDAP_DEBUG_ANY, "%s: set password for user %s failed\n", __func__, op->o_req_ndn.bv_val, 0); 463 rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM; 464 rs->sr_text = "Error setting password"; 465 free(tmppass); 466 goto error_return; 467 } 468 469 // Will only store if the policy has been set 470 odusers_store_history(&op->o_req_dn, tmppass, policy); 471 472 Debug(LDAP_DEBUG_ANY, "%s: %s changed password for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val); 473 free(tmppass); 474 rs->sr_err = rc = LDAP_SUCCESS; 475 rs->sr_text = NULL; 476 477#endif 478 479 op->oq_extended = qext; 480 481error_return:; 482 if(policy) CFRelease(policy); 483 if ( qpw->rs_mods ) { 484 slap_mods_free( qpw->rs_mods, 1 ); 485 } 486 if ( freenewpw ) { 487 free( qpw->rs_new.bv_val ); 488 } 489 if ( !BER_BVISNULL( &dn ) ) { 490 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); 491 BER_BVZERO( &op->o_req_dn ); 492 } 493 if ( !BER_BVISNULL( &ndn ) ) { 494 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 495 BER_BVZERO( &op->o_req_ndn ); 496 } 497 498 return rc; 499} 500 501/* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will 502 * reject it in this condition, the caller must NUL-terminate it. 503 * FIXME: should dnNormalize still be complaining about that? 504 */ 505int slap_passwd_parse( struct berval *reqdata, 506 struct berval *id, 507 struct berval *oldpass, 508 struct berval *newpass, 509 const char **text ) 510{ 511 int rc = LDAP_SUCCESS; 512 ber_tag_t tag; 513 ber_len_t len = -1; 514 BerElementBuffer berbuf; 515 BerElement *ber = (BerElement *)&berbuf; 516 517 if( reqdata == NULL ) { 518 return LDAP_SUCCESS; 519 } 520 521 if( reqdata->bv_len == 0 ) { 522 *text = "empty request data field"; 523 return LDAP_PROTOCOL_ERROR; 524 } 525 526 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 527 ber_init2( ber, reqdata, 0 ); 528 529 tag = ber_skip_tag( ber, &len ); 530 531 if( tag != LBER_SEQUENCE ) { 532 Debug( LDAP_DEBUG_TRACE, 533 "slap_passwd_parse: decoding error\n", 0, 0, 0 ); 534 rc = LDAP_PROTOCOL_ERROR; 535 goto done; 536 } 537 538 tag = ber_peek_tag( ber, &len ); 539 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { 540 if( id == NULL ) { 541 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n", 542 0, 0, 0 ); 543 544 *text = "user must change own password"; 545 rc = LDAP_UNWILLING_TO_PERFORM; 546 goto done; 547 } 548 549 tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM ); 550 551 if( tag == LBER_ERROR ) { 552 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n", 553 0, 0, 0 ); 554 555 goto decoding_error; 556 } 557 558 tag = ber_peek_tag( ber, &len ); 559 } 560 561 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) { 562 if( oldpass == NULL ) { 563 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n", 564 0, 0, 0 ); 565 566 *text = "use bind to verify old password"; 567 rc = LDAP_UNWILLING_TO_PERFORM; 568 goto done; 569 } 570 571 tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM ); 572 573 if( tag == LBER_ERROR ) { 574 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n", 575 0, 0, 0 ); 576 577 goto decoding_error; 578 } 579 580 if( oldpass->bv_len == 0 ) { 581 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n", 582 0, 0, 0 ); 583 584 *text = "old password value is empty"; 585 rc = LDAP_UNWILLING_TO_PERFORM; 586 goto done; 587 } 588 589 tag = ber_peek_tag( ber, &len ); 590 } 591 592 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) { 593 if( newpass == NULL ) { 594 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n", 595 0, 0, 0 ); 596 597 *text = "user specified passwords disallowed"; 598 rc = LDAP_UNWILLING_TO_PERFORM; 599 goto done; 600 } 601 602 tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM ); 603 604 if( tag == LBER_ERROR ) { 605 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n", 606 0, 0, 0 ); 607 608 goto decoding_error; 609 } 610 611 if( newpass->bv_len == 0 ) { 612 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n", 613 0, 0, 0 ); 614 615 *text = "new password value is empty"; 616 rc = LDAP_UNWILLING_TO_PERFORM; 617 goto done; 618 } 619 620 tag = ber_peek_tag( ber, &len ); 621 } 622 623 if( len != 0 ) { 624decoding_error: 625 Debug( LDAP_DEBUG_TRACE, 626 "slap_passwd_parse: decoding error, len=%ld\n", 627 (long) len, 0, 0 ); 628 629 *text = "data decoding error"; 630 rc = LDAP_PROTOCOL_ERROR; 631 } 632 633done: 634 return rc; 635} 636 637struct berval * slap_passwd_return( 638 struct berval *cred ) 639{ 640 int rc; 641 struct berval *bv = NULL; 642 BerElementBuffer berbuf; 643 /* opaque structure, size unknown but smaller than berbuf */ 644 BerElement *ber = (BerElement *)&berbuf; 645 646 assert( cred != NULL ); 647 648 Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n", 649 (long) cred->bv_len, 0, 0 ); 650 651 ber_init_w_nullc( ber, LBER_USE_DER ); 652 653 rc = ber_printf( ber, "{tON}", 654 LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred ); 655 656 if( rc >= 0 ) { 657 (void) ber_flatten( ber, &bv ); 658 } 659 660 ber_free_buf( ber ); 661 662 return bv; 663} 664 665/* 666 * if "e" is provided, access to each value of the password is checked first 667 */ 668int 669slap_passwd_check( 670 Operation *op, 671 Entry *e, 672 Attribute *a, 673 struct berval *cred, 674 const char **text ) 675{ 676 int result = 1; 677 struct berval *bv; 678 AccessControlState acl_state = ACL_STATE_INIT; 679 char credNul = cred->bv_val[cred->bv_len]; 680 681#ifdef SLAPD_SPASSWD 682 void *old_authctx = NULL; 683 684 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind, 685 op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL ); 686#endif 687 688 if ( credNul ) cred->bv_val[cred->bv_len] = 0; 689 690 for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) { 691 /* if e is provided, check access */ 692 if ( e && access_allowed( op, e, a->a_desc, bv, 693 ACL_AUTH, &acl_state ) == 0 ) 694 { 695 continue; 696 } 697 698 if ( !lutil_passwd( bv, cred, NULL, text ) ) { 699 result = 0; 700 break; 701 } 702 } 703 704 if ( credNul ) cred->bv_val[cred->bv_len] = credNul; 705 706#ifdef SLAPD_SPASSWD 707 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind, 708 old_authctx, 0, NULL, NULL ); 709#endif 710 711 return result; 712} 713 714void 715slap_passwd_generate( struct berval *pass ) 716{ 717 Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n", 0, 0, 0 ); 718 BER_BVZERO( pass ); 719 720 /* 721 * generate passwords of only 8 characters as some getpass(3) 722 * implementations truncate at 8 characters. 723 */ 724 lutil_passwd_generate( pass, 8 ); 725} 726 727void 728slap_passwd_hash_type( 729 struct berval * cred, 730 struct berval * new, 731 char *hash, 732 const char **text ) 733{ 734 new->bv_len = 0; 735 new->bv_val = NULL; 736 737 assert( hash != NULL ); 738 739 lutil_passwd_hash( cred , hash, new, text ); 740} 741void 742slap_passwd_hash( 743 struct berval * cred, 744 struct berval * new, 745 const char **text ) 746{ 747 char *hash = NULL; 748 if ( default_passwd_hash ) { 749 hash = default_passwd_hash[0]; 750 } 751 if ( !hash ) { 752 hash = (char *)defhash[0]; 753 } 754 755 slap_passwd_hash_type( cred, new, hash, text ); 756} 757 758#ifdef SLAPD_CRYPT 759static ldap_pvt_thread_mutex_t passwd_mutex; 760static lutil_cryptfunc slapd_crypt; 761 762static int slapd_crypt( const char *key, const char *salt, char **hash ) 763{ 764 char *cr; 765 int rc; 766 767 ldap_pvt_thread_mutex_lock( &passwd_mutex ); 768 769 cr = crypt( key, salt ); 770 if ( cr == NULL || cr[0] == '\0' ) { 771 /* salt must have been invalid */ 772 rc = LUTIL_PASSWD_ERR; 773 } else { 774 if ( hash ) { 775 *hash = ber_strdup( cr ); 776 rc = LUTIL_PASSWD_OK; 777 778 } else { 779 rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; 780 } 781 } 782 783 ldap_pvt_thread_mutex_unlock( &passwd_mutex ); 784 return rc; 785} 786#endif /* SLAPD_CRYPT */ 787 788void slap_passwd_init() 789{ 790#ifdef SLAPD_CRYPT 791 ldap_pvt_thread_mutex_init( &passwd_mutex ); 792 lutil_cryptptr = slapd_crypt; 793#endif 794} 795 796