1/* $NetBSD: passwd.c,v 1.3 2021/08/14 16:14:58 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-2021 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.3 2021/08/14 16:14:58 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 43const struct berval slap_EXOP_MODIFY_PASSWD = BER_BVC(LDAP_EXOP_MODIFY_PASSWD); 44 45static const char *defhash[] = { 46#ifdef LUTIL_SHA1_BYTES 47 "{SSHA}", 48#else 49 "{SMD5}", 50#endif 51 NULL 52}; 53 54int 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 261old_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 op->o_tag = LDAP_REQ_MODIFY; 300 op->o_callback = &cb; 301 op->orm_modlist = qpw->rs_mods; 302 op->orm_no_opattrs = 0; 303 304 cb.sc_private = qpw; /* let Modify know this was pwdMod, 305 * if it cares... */ 306 307 rs->sr_err = op->o_bd->be_modify( op, rs ); 308 309 /* be_modify() might have shuffled modifications */ 310 qpw->rs_mods = op->orm_modlist; 311 312 if ( rs->sr_err == LDAP_SUCCESS ) { 313 rs->sr_rspdata = rsp; 314 315 } else if ( rsp ) { 316 ber_bvfree( rsp ); 317 rsp = NULL; 318 } 319 op->o_tag = LDAP_REQ_EXTENDED; 320 for ( sc = &op->o_callback; *sc; sc = &(*sc)->sc_next ) { 321 if ( *sc == &cb ) { 322 *sc = cb.sc_next; 323 break; 324 } 325 } 326 } 327 328 rc = rs->sr_err; 329 op->oq_extended = qext; 330 331error_return:; 332 if ( qpw->rs_mods ) { 333 slap_mods_free( qpw->rs_mods, 1 ); 334 } 335 if ( freenewpw ) { 336 free( qpw->rs_new.bv_val ); 337 } 338 if ( !BER_BVISNULL( &dn ) ) { 339 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); 340 BER_BVZERO( &op->o_req_dn ); 341 } 342 if ( !BER_BVISNULL( &ndn ) ) { 343 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 344 BER_BVZERO( &op->o_req_ndn ); 345 } 346 347 return rc; 348} 349 350/* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will 351 * reject it in this condition, the caller must NUL-terminate it. 352 * FIXME: should dnNormalize still be complaining about that? 353 */ 354int slap_passwd_parse( struct berval *reqdata, 355 struct berval *id, 356 struct berval *oldpass, 357 struct berval *newpass, 358 const char **text ) 359{ 360 int rc = LDAP_SUCCESS; 361 ber_tag_t tag; 362 ber_len_t len = -1; 363 BerElementBuffer berbuf; 364 BerElement *ber = (BerElement *)&berbuf; 365 366 if( reqdata == NULL ) { 367 return LDAP_SUCCESS; 368 } 369 370 if( reqdata->bv_len == 0 ) { 371 *text = "empty request data field"; 372 return LDAP_PROTOCOL_ERROR; 373 } 374 375 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 376 ber_init2( ber, reqdata, 0 ); 377 378 tag = ber_skip_tag( ber, &len ); 379 380 if( tag != LBER_SEQUENCE ) { 381 Debug( LDAP_DEBUG_TRACE, 382 "slap_passwd_parse: decoding error\n" ); 383 rc = LDAP_PROTOCOL_ERROR; 384 goto done; 385 } 386 387 tag = ber_peek_tag( ber, &len ); 388 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { 389 if( id == NULL ) { 390 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n" ); 391 392 *text = "user must change own password"; 393 rc = LDAP_UNWILLING_TO_PERFORM; 394 goto done; 395 } 396 397 tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM ); 398 399 if( tag == LBER_ERROR ) { 400 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n" ); 401 402 goto decoding_error; 403 } 404 405 tag = ber_peek_tag( ber, &len ); 406 } 407 408 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) { 409 if( oldpass == NULL ) { 410 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n" ); 411 412 *text = "use bind to verify old password"; 413 rc = LDAP_UNWILLING_TO_PERFORM; 414 goto done; 415 } 416 417 tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM ); 418 419 if( tag == LBER_ERROR ) { 420 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n" ); 421 422 goto decoding_error; 423 } 424 425 if( oldpass->bv_len == 0 ) { 426 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n" ); 427 428 *text = "old password value is empty"; 429 rc = LDAP_UNWILLING_TO_PERFORM; 430 goto done; 431 } 432 433 tag = ber_peek_tag( ber, &len ); 434 } 435 436 if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) { 437 if( newpass == NULL ) { 438 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n" ); 439 440 *text = "user specified passwords disallowed"; 441 rc = LDAP_UNWILLING_TO_PERFORM; 442 goto done; 443 } 444 445 tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM ); 446 447 if( tag == LBER_ERROR ) { 448 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n" ); 449 450 goto decoding_error; 451 } 452 453 if( newpass->bv_len == 0 ) { 454 Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n" ); 455 456 *text = "new password value is empty"; 457 rc = LDAP_UNWILLING_TO_PERFORM; 458 goto done; 459 } 460 461 tag = ber_peek_tag( ber, &len ); 462 } 463 464 if( len != 0 ) { 465decoding_error: 466 Debug( LDAP_DEBUG_TRACE, 467 "slap_passwd_parse: decoding error, len=%ld\n", 468 (long) len ); 469 470 *text = "data decoding error"; 471 rc = LDAP_PROTOCOL_ERROR; 472 } 473 474done: 475 return rc; 476} 477 478struct berval * slap_passwd_return( 479 struct berval *cred ) 480{ 481 int rc; 482 struct berval *bv = NULL; 483 BerElementBuffer berbuf; 484 /* opaque structure, size unknown but smaller than berbuf */ 485 BerElement *ber = (BerElement *)&berbuf; 486 487 assert( cred != NULL ); 488 489 Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n", 490 (long) cred->bv_len ); 491 492 ber_init_w_nullc( ber, LBER_USE_DER ); 493 494 rc = ber_printf( ber, "{tON}", 495 LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred ); 496 497 if( rc >= 0 ) { 498 (void) ber_flatten( ber, &bv ); 499 } 500 501 ber_free_buf( ber ); 502 503 return bv; 504} 505 506/* 507 * if "e" is provided, access to each value of the password is checked first 508 */ 509int 510slap_passwd_check( 511 Operation *op, 512 Entry *e, 513 Attribute *a, 514 struct berval *cred, 515 const char **text ) 516{ 517 int result = 1; 518 struct berval *bv; 519 AccessControlState acl_state = ACL_STATE_INIT; 520 char credNul = cred->bv_val[cred->bv_len]; 521 522#ifdef SLAPD_SPASSWD 523 void *old_authctx = NULL; 524 525 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind, 526 op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL ); 527#endif 528 529 if ( credNul ) cred->bv_val[cred->bv_len] = 0; 530 531 for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) { 532 /* if e is provided, check access */ 533 if ( e && access_allowed( op, e, a->a_desc, bv, 534 ACL_AUTH, &acl_state ) == 0 ) 535 { 536 continue; 537 } 538 539 if ( !lutil_passwd( bv, cred, NULL, text ) ) { 540 result = 0; 541 break; 542 } 543 } 544 545 if ( credNul ) cred->bv_val[cred->bv_len] = credNul; 546 547#ifdef SLAPD_SPASSWD 548 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind, 549 old_authctx, 0, NULL, NULL ); 550#endif 551 552 return result; 553} 554 555void 556slap_passwd_generate( struct berval *pass ) 557{ 558 Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n" ); 559 BER_BVZERO( pass ); 560 561 /* 562 * generate passwords of only 8 characters as some getpass(3) 563 * implementations truncate at 8 characters. 564 */ 565 lutil_passwd_generate( pass, 8 ); 566} 567 568void 569slap_passwd_hash_type( 570 struct berval * cred, 571 struct berval * new, 572 char *hash, 573 const char **text ) 574{ 575 new->bv_len = 0; 576 new->bv_val = NULL; 577 578 assert( hash != NULL ); 579 580 lutil_passwd_hash( cred , hash, new, text ); 581} 582void 583slap_passwd_hash( 584 struct berval * cred, 585 struct berval * new, 586 const char **text ) 587{ 588 char *hash = NULL; 589 if ( default_passwd_hash ) { 590 hash = default_passwd_hash[0]; 591 } 592 if ( !hash ) { 593 hash = (char *)defhash[0]; 594 } 595 596 slap_passwd_hash_type( cred, new, hash, text ); 597} 598 599#ifdef SLAPD_CRYPT 600static ldap_pvt_thread_mutex_t passwd_mutex; 601static lutil_cryptfunc slapd_crypt; 602 603#ifdef HAVE_CRYPT_R 604static int slapd_crypt( const char *key, const char *salt, char **hash ) 605{ 606 char *cr; 607 int rc; 608 struct crypt_data data; 609 610 data.initialized = 0; 611 cr = crypt_r( key, salt, &data ); 612 if ( cr == NULL || cr[0] == '\0' ) { 613 /* salt must have been invalid */ 614 rc = LUTIL_PASSWD_ERR; 615 } else { 616 if ( hash ) { 617 *hash = ber_strdup( cr ); 618 rc = LUTIL_PASSWD_OK; 619 } else { 620 rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; 621 } 622 } 623 624 return rc; 625} 626#else 627static int slapd_crypt( const char *key, const char *salt, char **hash ) 628{ 629 char *cr; 630 int rc; 631 632 ldap_pvt_thread_mutex_lock( &passwd_mutex ); 633 634 cr = crypt( key, salt ); 635 if ( cr == NULL || cr[0] == '\0' ) { 636 /* salt must have been invalid */ 637 rc = LUTIL_PASSWD_ERR; 638 } else { 639 if ( hash ) { 640 *hash = ber_strdup( cr ); 641 rc = LUTIL_PASSWD_OK; 642 643 } else { 644 rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; 645 } 646 } 647 648 ldap_pvt_thread_mutex_unlock( &passwd_mutex ); 649 return rc; 650} 651#endif /* HAVE_CRYPT_R */ 652 653#endif /* SLAPD_CRYPT */ 654 655void slap_passwd_init() 656{ 657#ifdef SLAPD_CRYPT 658 ldap_pvt_thread_mutex_init( &passwd_mutex ); 659 lutil_cryptptr = slapd_crypt; 660#endif 661} 662 663