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