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