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