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