Back to index

openldap  2.4.31
passwd.c
Go to the documentation of this file.
00001 /* passwd.c - password extended operation routines */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 
00021 #include <ac/socket.h>
00022 #include <ac/string.h>
00023 #include <ac/unistd.h>
00024 
00025 #ifdef SLAPD_CRYPT
00026 #include <ac/crypt.h>
00027 #endif
00028 
00029 #include "slap.h"
00030 
00031 #include <lber_pvt.h>
00032 #include <lutil.h>
00033 #include <lutil_sha1.h>
00034 
00035 const struct berval slap_EXOP_MODIFY_PASSWD = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
00036 
00037 static const char *defhash[] = {
00038 #ifdef LUTIL_SHA1_BYTES
00039        "{SSHA}",
00040 #else
00041        "{SMD5}",
00042 #endif
00043        NULL
00044 };
00045 
00046 int passwd_extop(
00047        Operation *op,
00048        SlapReply *rs )
00049 {
00050        struct berval id = {0, NULL}, hash, *rsp = NULL;
00051        req_pwdexop_s *qpw = &op->oq_pwdexop;
00052        req_extended_s qext = op->oq_extended;
00053        Modifications *ml;
00054        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
00055        int i, nhash;
00056        char **hashes, idNul;
00057        int rc;
00058        BackendDB *op_be;
00059        int freenewpw = 0;
00060        struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
00061 
00062        assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
00063 
00064        if( op->o_dn.bv_len == 0 ) {
00065               Statslog( LDAP_DEBUG_STATS, "%s PASSMOD\n",
00066                      op->o_log_prefix, 0, 0, 0, 0 );
00067               rs->sr_text = "only authenticated users may change passwords";
00068               return LDAP_STRONG_AUTH_REQUIRED;
00069        }
00070 
00071        qpw->rs_old.bv_len = 0;
00072        qpw->rs_old.bv_val = NULL;
00073        qpw->rs_new.bv_len = 0;
00074        qpw->rs_new.bv_val = NULL;
00075        qpw->rs_mods = NULL;
00076        qpw->rs_modtail = NULL;
00077 
00078        rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
00079               &qpw->rs_old, &qpw->rs_new, &rs->sr_text );
00080 
00081        if ( !BER_BVISNULL( &id )) {
00082               idNul = id.bv_val[id.bv_len];
00083               id.bv_val[id.bv_len] = '\0';
00084        }
00085        if ( rs->sr_err == LDAP_SUCCESS && !BER_BVISEMPTY( &id ) ) {
00086               Statslog( LDAP_DEBUG_STATS, "%s PASSMOD id=\"%s\"%s%s\n",
00087                      op->o_log_prefix, id.bv_val,
00088                      qpw->rs_old.bv_val ? " old" : "",
00089                      qpw->rs_new.bv_val ? " new" : "", 0 );
00090        } else {
00091               Statslog( LDAP_DEBUG_STATS, "%s PASSMOD%s%s\n",
00092                      op->o_log_prefix,
00093                      qpw->rs_old.bv_val ? " old" : "",
00094                      qpw->rs_new.bv_val ? " new" : "", 0, 0 );
00095        }
00096 
00097        if ( rs->sr_err != LDAP_SUCCESS ) {
00098               if ( !BER_BVISNULL( &id ))
00099                      id.bv_val[id.bv_len] = idNul;
00100               return rs->sr_err;
00101        }
00102 
00103        if ( !BER_BVISEMPTY( &id ) ) {
00104               rs->sr_err = dnPrettyNormal( NULL, &id, &dn, &ndn, op->o_tmpmemctx );
00105               id.bv_val[id.bv_len] = idNul;
00106               if ( rs->sr_err != LDAP_SUCCESS ) {
00107                      rs->sr_text = "Invalid DN";
00108                      rc = rs->sr_err;
00109                      goto error_return;
00110               }
00111               op->o_req_dn = dn;
00112               op->o_req_ndn = ndn;
00113               op->o_bd = select_backend( &op->o_req_ndn, 1 );
00114 
00115        } else {
00116               ber_dupbv_x( &dn, &op->o_dn, op->o_tmpmemctx );
00117               ber_dupbv_x( &ndn, &op->o_ndn, op->o_tmpmemctx );
00118               op->o_req_dn = dn;
00119               op->o_req_ndn = ndn;
00120               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
00121               op->o_bd = op->o_conn->c_authz_backend;
00122               ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
00123        }
00124 
00125        if( op->o_bd == NULL ) {
00126               if ( qpw->rs_old.bv_val != NULL ) {
00127                      rs->sr_text = "unwilling to verify old password";
00128                      rc = LDAP_UNWILLING_TO_PERFORM;
00129                      goto error_return;
00130               }
00131 
00132 #ifdef HAVE_CYRUS_SASL
00133               rc = slap_sasl_setpass( op, rs );
00134 #else
00135               rs->sr_text = "no authz backend";
00136               rc = LDAP_OTHER;
00137 #endif
00138               goto error_return;
00139        }
00140 
00141        if ( op->o_req_ndn.bv_len == 0 ) {
00142               rs->sr_text = "no password is associated with the Root DSE";
00143               rc = LDAP_UNWILLING_TO_PERFORM;
00144               goto error_return;
00145        }
00146 
00147        /* If we've got a glued backend, check the real backend */
00148        op_be = op->o_bd;
00149        if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
00150               op->o_bd = select_backend( &op->o_req_ndn, 0 );
00151        }
00152 
00153        if (backend_check_restrictions( op, rs,
00154                      (struct berval *)&slap_EXOP_MODIFY_PASSWD ) != LDAP_SUCCESS) {
00155               rc = rs->sr_err;
00156               goto error_return;
00157        }
00158 
00159        /* check for referrals */
00160        if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
00161               rc = rs->sr_err;
00162               goto error_return;
00163        }
00164 
00165        /* This does not apply to multi-master case */
00166        if(!( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ))) {
00167               /* we SHOULD return a referral in this case */
00168               BerVarray defref = op->o_bd->be_update_refs
00169                      ? op->o_bd->be_update_refs : default_referral; 
00170 
00171               if( defref != NULL ) {
00172                      rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
00173                             NULL, NULL, LDAP_SCOPE_DEFAULT );
00174                      if(rs->sr_ref) {
00175                             rs->sr_flags |= REP_REF_MUSTBEFREED;
00176                      } else {
00177                             rs->sr_ref = defref;
00178                      }
00179                      rc = LDAP_REFERRAL;
00180                      goto error_return;
00181 
00182               }
00183 
00184               rs->sr_text = "shadow context; no update referral";
00185               rc = LDAP_UNWILLING_TO_PERFORM;
00186               goto error_return;
00187        }
00188 
00189        /* generate a new password if none was provided */
00190        if ( qpw->rs_new.bv_len == 0 ) {
00191               slap_passwd_generate( &qpw->rs_new );
00192               if ( qpw->rs_new.bv_len ) {
00193                      rsp = slap_passwd_return( &qpw->rs_new );
00194                      freenewpw = 1;
00195               }
00196        }
00197        if ( qpw->rs_new.bv_len == 0 ) {
00198               rs->sr_text = "password generation failed";
00199               rc = LDAP_OTHER;
00200               goto error_return;
00201        }
00202 
00203        op->o_bd = op_be;
00204 
00205        /* Give the backend a chance to handle this itself */
00206        if ( op->o_bd->be_extended ) {
00207               rs->sr_err = op->o_bd->be_extended( op, rs );
00208               if ( rs->sr_err != LDAP_UNWILLING_TO_PERFORM &&
00209                      rs->sr_err != SLAP_CB_CONTINUE )
00210               {
00211                      rc = rs->sr_err;
00212                      if ( rsp ) {
00213                             rs->sr_rspdata = rsp;
00214                             rsp = NULL;
00215                      }
00216                      goto error_return;
00217               }
00218        }
00219 
00220        /* The backend didn't handle it, so try it here */
00221        if( op->o_bd && !op->o_bd->be_modify ) {
00222               rs->sr_text = "operation not supported for current user";
00223               rc = LDAP_UNWILLING_TO_PERFORM;
00224               goto error_return;
00225        }
00226 
00227        if ( qpw->rs_old.bv_val != NULL ) {
00228               Entry *e = NULL;
00229 
00230               rc = be_entry_get_rw( op, &op->o_req_ndn, NULL,
00231                      slap_schema.si_ad_userPassword, 0, &e );
00232               if ( rc == LDAP_SUCCESS && e ) {
00233                      Attribute *a = attr_find( e->e_attrs,
00234                             slap_schema.si_ad_userPassword );
00235                      if ( a )
00236                             rc = slap_passwd_check( op, e, a, &qpw->rs_old, &rs->sr_text );
00237                      else
00238                             rc = 1;
00239                      be_entry_release_r( op, e );
00240                      if ( rc == LDAP_SUCCESS )
00241                             goto old_good;
00242               }
00243               rs->sr_text = "unwilling to verify old password";
00244               rc = LDAP_UNWILLING_TO_PERFORM;
00245               goto error_return;
00246        }
00247 
00248 old_good:
00249        ml = ch_malloc( sizeof(Modifications) );
00250        if ( !qpw->rs_modtail ) qpw->rs_modtail = &ml->sml_next;
00251 
00252        if ( default_passwd_hash ) {
00253               for ( nhash = 0; default_passwd_hash[nhash]; nhash++ );
00254               hashes = default_passwd_hash;
00255        } else {
00256               nhash = 1;
00257               hashes = (char **)defhash;
00258        }
00259        ml->sml_numvals = nhash;
00260        ml->sml_values = ch_malloc( (nhash+1)*sizeof(struct berval) );
00261        for ( i=0; hashes[i]; i++ ) {
00262               slap_passwd_hash_type( &qpw->rs_new, &hash, hashes[i], &rs->sr_text );
00263               if ( hash.bv_len == 0 ) {
00264                      if ( !rs->sr_text ) {
00265                             rs->sr_text = "password hash failed";
00266                      }
00267                      break;
00268               }
00269               ml->sml_values[i] = hash;
00270        }
00271        ml->sml_values[i].bv_val = NULL;
00272        ml->sml_nvalues = NULL;
00273        ml->sml_desc = slap_schema.si_ad_userPassword;
00274        ml->sml_type = ml->sml_desc->ad_cname;
00275        ml->sml_op = LDAP_MOD_REPLACE;
00276        ml->sml_flags = 0;
00277        ml->sml_next = qpw->rs_mods;
00278        qpw->rs_mods = ml;
00279 
00280        if ( hashes[i] ) {
00281               rs->sr_err = LDAP_OTHER;
00282 
00283        } else {
00284               slap_callback *sc = op->o_callback;
00285 
00286               op->o_tag = LDAP_REQ_MODIFY;
00287               op->o_callback = &cb;
00288               op->orm_modlist = qpw->rs_mods;
00289               op->orm_no_opattrs = 0;
00290               
00291               cb.sc_private = qpw; /* let Modify know this was pwdMod,
00292                                     * if it cares... */
00293 
00294               rs->sr_err = op->o_bd->be_modify( op, rs );
00295 
00296               /* be_modify() might have shuffled modifications */
00297               qpw->rs_mods = op->orm_modlist;
00298 
00299               if ( rs->sr_err == LDAP_SUCCESS ) {
00300                      rs->sr_rspdata = rsp;
00301 
00302               } else if ( rsp ) {
00303                      ber_bvfree( rsp );
00304                      rsp = NULL;
00305               }
00306               op->o_tag = LDAP_REQ_EXTENDED;
00307               op->o_callback = sc;
00308        }
00309 
00310        rc = rs->sr_err;
00311        op->oq_extended = qext;
00312 
00313 error_return:;
00314        if ( qpw->rs_mods ) {
00315               slap_mods_free( qpw->rs_mods, 1 );
00316        }
00317        if ( freenewpw ) {
00318               free( qpw->rs_new.bv_val );
00319        }
00320        if ( !BER_BVISNULL( &dn ) ) {
00321               op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
00322               BER_BVZERO( &op->o_req_dn );
00323        }
00324        if ( !BER_BVISNULL( &ndn ) ) {
00325               op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
00326               BER_BVZERO( &op->o_req_ndn );
00327        }
00328 
00329        return rc;
00330 }
00331 
00332 /* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will
00333  * reject it in this condition, the caller must NUL-terminate it.
00334  * FIXME: should dnNormalize still be complaining about that?
00335  */
00336 int slap_passwd_parse( struct berval *reqdata,
00337        struct berval *id,
00338        struct berval *oldpass,
00339        struct berval *newpass,
00340        const char **text )
00341 {
00342        int rc = LDAP_SUCCESS;
00343        ber_tag_t tag;
00344        ber_len_t len = -1;
00345        BerElementBuffer berbuf;
00346        BerElement *ber = (BerElement *)&berbuf;
00347 
00348        if( reqdata == NULL ) {
00349               return LDAP_SUCCESS;
00350        }
00351 
00352        if( reqdata->bv_len == 0 ) {
00353               *text = "empty request data field";
00354               return LDAP_PROTOCOL_ERROR;
00355        }
00356 
00357        /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
00358        ber_init2( ber, reqdata, 0 );
00359 
00360        tag = ber_skip_tag( ber, &len );
00361 
00362        if( tag != LBER_SEQUENCE ) {
00363               Debug( LDAP_DEBUG_TRACE,
00364                      "slap_passwd_parse: decoding error\n", 0, 0, 0 );
00365               rc = LDAP_PROTOCOL_ERROR;
00366               goto done;
00367        }
00368 
00369        tag = ber_peek_tag( ber, &len );
00370        if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
00371               if( id == NULL ) {
00372                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n",
00373                             0, 0, 0 );
00374 
00375                      *text = "user must change own password";
00376                      rc = LDAP_UNWILLING_TO_PERFORM;
00377                      goto done;
00378               }
00379 
00380               tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM );
00381 
00382               if( tag == LBER_ERROR ) {
00383                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n",
00384                             0, 0, 0 );
00385 
00386                      goto decoding_error;
00387               }
00388 
00389               tag = ber_peek_tag( ber, &len );
00390        }
00391 
00392        if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) {
00393               if( oldpass == NULL ) {
00394                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n",
00395                             0, 0, 0 );
00396 
00397                      *text = "use bind to verify old password";
00398                      rc = LDAP_UNWILLING_TO_PERFORM;
00399                      goto done;
00400               }
00401 
00402               tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM );
00403 
00404               if( tag == LBER_ERROR ) {
00405                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n",
00406                             0, 0, 0 );
00407 
00408                      goto decoding_error;
00409               }
00410 
00411               if( oldpass->bv_len == 0 ) {
00412                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n",
00413                             0, 0, 0 );
00414 
00415                      *text = "old password value is empty";
00416                      rc = LDAP_UNWILLING_TO_PERFORM;
00417                      goto done;
00418               }
00419 
00420               tag = ber_peek_tag( ber, &len );
00421        }
00422 
00423        if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) {
00424               if( newpass == NULL ) {
00425                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n",
00426                             0, 0, 0 );
00427 
00428                      *text = "user specified passwords disallowed";
00429                      rc = LDAP_UNWILLING_TO_PERFORM;
00430                      goto done;
00431               }
00432 
00433               tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM );
00434 
00435               if( tag == LBER_ERROR ) {
00436                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n",
00437                             0, 0, 0 );
00438 
00439                      goto decoding_error;
00440               }
00441 
00442               if( newpass->bv_len == 0 ) {
00443                      Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n",
00444                             0, 0, 0 );
00445 
00446                      *text = "new password value is empty";
00447                      rc = LDAP_UNWILLING_TO_PERFORM;
00448                      goto done;
00449               }
00450 
00451               tag = ber_peek_tag( ber, &len );
00452        }
00453 
00454        if( len != 0 ) {
00455 decoding_error:
00456               Debug( LDAP_DEBUG_TRACE,
00457                      "slap_passwd_parse: decoding error, len=%ld\n",
00458                      (long) len, 0, 0 );
00459 
00460               *text = "data decoding error";
00461               rc = LDAP_PROTOCOL_ERROR;
00462        }
00463 
00464 done:
00465        return rc;
00466 }
00467 
00468 struct berval * slap_passwd_return(
00469        struct berval        *cred )
00470 {
00471        int rc;
00472        struct berval *bv = NULL;
00473        BerElementBuffer berbuf;
00474        /* opaque structure, size unknown but smaller than berbuf */
00475        BerElement *ber = (BerElement *)&berbuf;
00476 
00477        assert( cred != NULL );
00478 
00479        Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n",
00480               (long) cred->bv_len, 0, 0 );
00481        
00482        ber_init_w_nullc( ber, LBER_USE_DER );
00483 
00484        rc = ber_printf( ber, "{tON}",
00485               LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred );
00486 
00487        if( rc >= 0 ) {
00488               (void) ber_flatten( ber, &bv );
00489        }
00490 
00491        ber_free_buf( ber );
00492 
00493        return bv;
00494 }
00495 
00496 /*
00497  * if "e" is provided, access to each value of the password is checked first
00498  */
00499 int
00500 slap_passwd_check(
00501        Operation     *op,
00502        Entry         *e,
00503        Attribute     *a,
00504        struct berval *cred,
00505        const char    **text )
00506 {
00507        int                  result = 1;
00508        struct berval        *bv;
00509        AccessControlState   acl_state = ACL_STATE_INIT;
00510        char          credNul = cred->bv_val[cred->bv_len];
00511 
00512 #ifdef SLAPD_SPASSWD
00513        void          *old_authctx = NULL;
00514 
00515        ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
00516               op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL );
00517 #endif
00518 
00519        if ( credNul ) cred->bv_val[cred->bv_len] = 0;
00520 
00521        for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) {
00522               /* if e is provided, check access */
00523               if ( e && access_allowed( op, e, a->a_desc, bv,
00524                                    ACL_AUTH, &acl_state ) == 0 )
00525               {
00526                      continue;
00527               }
00528               
00529               if ( !lutil_passwd( bv, cred, NULL, text ) ) {
00530                      result = 0;
00531                      break;
00532               }
00533        }
00534 
00535        if ( credNul ) cred->bv_val[cred->bv_len] = credNul;
00536 
00537 #ifdef SLAPD_SPASSWD
00538        ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
00539               old_authctx, 0, NULL, NULL );
00540 #endif
00541 
00542        return result;
00543 }
00544 
00545 void
00546 slap_passwd_generate( struct berval *pass )
00547 {
00548        Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n", 0, 0, 0 );
00549        BER_BVZERO( pass );
00550 
00551        /*
00552         * generate passwords of only 8 characters as some getpass(3)
00553         * implementations truncate at 8 characters.
00554         */
00555        lutil_passwd_generate( pass, 8 );
00556 }
00557 
00558 void
00559 slap_passwd_hash_type(
00560        struct berval * cred,
00561        struct berval * new,
00562        char *hash,
00563        const char **text )
00564 {
00565        new->bv_len = 0;
00566        new->bv_val = NULL;
00567 
00568        assert( hash != NULL );
00569 
00570        lutil_passwd_hash( cred , hash, new, text );
00571 }
00572 void
00573 slap_passwd_hash(
00574        struct berval * cred,
00575        struct berval * new,
00576        const char **text )
00577 {
00578        char *hash = NULL;
00579        if ( default_passwd_hash ) {
00580               hash = default_passwd_hash[0];
00581        }
00582        if ( !hash ) {
00583               hash = (char *)defhash[0];
00584        }
00585 
00586        slap_passwd_hash_type( cred, new, hash, text );
00587 }
00588 
00589 #ifdef SLAPD_CRYPT
00590 static ldap_pvt_thread_mutex_t passwd_mutex;
00591 static lutil_cryptfunc slapd_crypt;
00592 
00593 static int slapd_crypt( const char *key, const char *salt, char **hash )
00594 {
00595        char *cr;
00596        int rc;
00597 
00598        ldap_pvt_thread_mutex_lock( &passwd_mutex );
00599 
00600        cr = crypt( key, salt );
00601        if ( cr == NULL || cr[0] == '\0' ) {
00602               /* salt must have been invalid */
00603               rc = LUTIL_PASSWD_ERR;
00604        } else {
00605               if ( hash ) {
00606                      *hash = ber_strdup( cr );
00607                      rc = LUTIL_PASSWD_OK;
00608 
00609               } else {
00610                      rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
00611               }
00612        }
00613 
00614        ldap_pvt_thread_mutex_unlock( &passwd_mutex );
00615        return rc;
00616 }
00617 #endif /* SLAPD_CRYPT */
00618 
00619 void slap_passwd_init()
00620 {
00621 #ifdef SLAPD_CRYPT
00622        ldap_pvt_thread_mutex_init( &passwd_mutex );
00623        lutil_cryptptr = slapd_crypt;
00624 #endif
00625 }
00626