diff --git a/clients/tools/ldappasswd.c b/clients/tools/ldappasswd.c index d87730bf98..5e97373479 100644 --- a/clients/tools/ldappasswd.c +++ b/clients/tools/ldappasswd.c @@ -27,8 +27,8 @@ static void usage(const char *s) { fprintf(stderr, - "Usage: %s [options]\n" - " -D binddn\tbind dn\tREQUIRED\n" + "Usage: %s [options] dn\n" + " -D binddn\tbind dn\n" " -d level\tdebugging level\n" " -h host\tldap server (default: localhost)\n" " -n\t\tmake no modifications\n" @@ -46,6 +46,7 @@ int main( int argc, char *argv[] ) { int rc; + char *dn = NULL; char *binddn = NULL; char *bindpw = NULL; char *ldaphost = NULL; @@ -119,6 +120,12 @@ main( int argc, char *argv[] ) } } + if( argc - optind != 1 ) { + usage( argv[0] ); + } + + dn = strdup( argv[optind] ); + if( newpw == NULL ) { /* prompt for new password */ char *cknewpw; @@ -132,8 +139,8 @@ main( int argc, char *argv[] ) } if( binddn == NULL ) { - fprintf( stderr, "no bind DN specified\n" ); - return EXIT_FAILURE; + binddn = dn; + dn = NULL; } /* handle bind password */ @@ -188,9 +195,19 @@ main( int argc, char *argv[] ) return EXIT_FAILURE; } - ber_printf( ber, "{es}", - (ber_int_t) 0, - newpw ); + if( dn != NULL ) { + ber_printf( ber, "{tsts}", + LDAP_TAG_EXOP_X_MODIFY_PASSWD_ID, dn, + LDAP_TAG_EXOP_X_MODIFY_PASSWD_NEW, newpw ); + + free(dn); + + } else { + ber_printf( ber, "{ts}", + LDAP_TAG_EXOP_X_MODIFY_PASSWD_NEW, newpw ); + } + + free(newpw); rc = ber_flatten( ber, &bv ); diff --git a/libraries/liblber/decode.c b/libraries/liblber/decode.c index 4af3a38d51..57d33b9e54 100644 --- a/libraries/liblber/decode.c +++ b/libraries/liblber/decode.c @@ -96,6 +96,8 @@ ber_skip_tag( BerElement *ber, ber_len_t *len ) * 2) primitive encodings used whenever possible */ + *len = 0; + /* * First, we read the tag. */ @@ -110,8 +112,6 @@ ber_skip_tag( BerElement *ber, ber_len_t *len ) * greater than what we can hold in a ber_len_t. */ - *len = 0; - if ( ber_read( ber, (char *) &lc, 1 ) != 1 ) return( LBER_DEFAULT ); @@ -149,6 +149,8 @@ ber_peek_tag( assert( ber_in != NULL ); assert( BER_VALID( ber_in ) ); + *len = 0; + ber = ber_dup( ber_in ); if( ber == NULL ) { diff --git a/servers/slapd/back-ldbm/passwd.c b/servers/slapd/back-ldbm/passwd.c index 355405bbda..c8980768b6 100644 --- a/servers/slapd/back-ldbm/passwd.c +++ b/servers/slapd/back-ldbm/passwd.c @@ -28,30 +28,61 @@ ldbm_back_exop_passwd( ) { struct ldbminfo *li = (struct ldbminfo *) be->be_private; - int rc = LDAP_OPERATIONS_ERROR; - Entry *e; - struct berval *cred = NULL; + int rc; + Entry *e = NULL; + struct berval *hash = NULL; + + struct berval *id = NULL; + struct berval *new = NULL; + + char *dn; assert( oid != NULL ); assert( strcmp( LDAP_EXOP_X_MODIFY_PASSWD, oid ) == 0 ); - Debug( LDAP_DEBUG_ARGS, "==> ldbm_back_exop_passwd: dn: %s\n", - op->o_dn, 0, 0 ); + rc = slap_passwd_parse( reqdata, + &id, NULL, &new, text ); + Debug( LDAP_DEBUG_ARGS, "==> ldbm_back_exop_passwd: \"%s\"\n", + id ? id->bv_val : "", 0, 0 ); - cred = slap_passwd_generate( reqdata ); - if( cred == NULL || cred->bv_len == 0 ) { - *text = ch_strdup("password generation failed"); - return LDAP_OPERATIONS_ERROR; + if( rc != LDAP_SUCCESS ) { + goto done; } - Debug( LDAP_DEBUG_TRACE, "passwd: %s\n", cred->bv_val, 0, 0 ); + if( new == NULL || new->bv_len == 0 ) { + *text = ch_strdup("no password provided"); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } - e = dn2entry_w( be, op->o_ndn, NULL ); + hash = slap_passwd_generate( new ); + + if( hash == NULL || hash->bv_len == 0 ) { + *text = ch_strdup("password generation failed"); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + + dn = id ? id->bv_val : op->o_dn; + + Debug( LDAP_DEBUG_TRACE, "passwd: \"%s\"%s\n", + dn, id ? " (proxy)" : "", 0 ); + + if( dn == NULL || dn[0] == NULL ) { + *text = ch_strdup("No password is associated with the Root DSE"); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + + e = dn2entry_w( be, + id ? id->bv_val : op->o_dn, + NULL ); if( e == NULL ) { *text = ch_strdup("could not locate authorization entry"); - return LDAP_OPERATIONS_ERROR; + rc = LDAP_OPERATIONS_ERROR; + goto done; } if( ! access_allowed( be, conn, op, e, "entry", NULL, ACL_WRITE ) ) { @@ -67,6 +98,8 @@ ldbm_back_exop_passwd( goto done; } + rc = LDAP_OPERATIONS_ERROR; + if( is_entry_referral( e ) ) { /* entry is an referral, don't allow operation */ *text = ch_strdup("authorization entry is referral"); @@ -77,7 +110,7 @@ ldbm_back_exop_passwd( LDAPModList ml; struct berval *vals[2]; - vals[0] = cred; + vals[0] = hash; vals[1] = NULL; ml.ml_type = ch_strdup("userPassword"); @@ -99,10 +132,20 @@ ldbm_back_exop_passwd( } done: - cache_return_entry_w( &li->li_cache, e ); + if( e != NULL ) { + cache_return_entry_w( &li->li_cache, e ); + } - if( cred != NULL ) { - ber_bvfree( cred ); + if( id != NULL ) { + ber_bvfree( id ); + } + + if( new != NULL ) { + ber_bvfree( new ); + } + + if( hash != NULL ) { + ber_bvfree( hash ); } return rc; diff --git a/servers/slapd/passwd.c b/servers/slapd/passwd.c index 1152e81b0f..bfeec38bf9 100644 --- a/servers/slapd/passwd.c +++ b/servers/slapd/passwd.c @@ -24,9 +24,6 @@ static int passwd_main( struct berval *reqdata, struct berval **rspdata, char **text ) { int rc; - BerElement *ber; - struct berval *cred = NULL; - ber_int_t type; assert( oid != NULL ); assert( strcmp( LDAP_EXOP_X_MODIFY_PASSWD, oid ) == 0 ); @@ -37,33 +34,7 @@ static int passwd_main( } if( reqdata == NULL || reqdata->bv_len == 0 ) { - *text = ch_strdup("data missing"); - return LDAP_PROTOCOL_ERROR; - } - - ber = ber_init( reqdata ); - - if( ber == NULL ) { - *text = ch_strdup("password decoding error"); - return LDAP_PROTOCOL_ERROR; - } - - rc = ber_scanf(ber, "{iO}", &type, &cred ); - ber_free( ber, 1 ); - - if( rc == LBER_ERROR ) { - *text = ch_strdup("data decoding error"); - return LDAP_PROTOCOL_ERROR; - } - - if( cred == NULL || cred->bv_len == 0 ) { - *text = ch_strdup("password missing"); - return LDAP_PROTOCOL_ERROR; - } - - if( type != 0 ) { - ber_bvfree( cred ); - *text = ch_strdup("password type unknown"); + *text = ch_strdup("request data missing"); return LDAP_PROTOCOL_ERROR; } @@ -72,15 +43,135 @@ static int passwd_main( { rc = conn->c_authz_backend->be_extended( conn->c_authz_backend, - conn, op, - oid, cred, rspdata, text ); + conn, op, oid, reqdata, rspdata, text ); } else { *text = ch_strdup("operation not supported for current user"); rc = LDAP_UNWILLING_TO_PERFORM; } - ber_bvfree( cred ); + return rc; +} + +int slap_passwd_parse( struct berval *reqdata, + struct berval **id, + struct berval **old, + struct berval **new, + char **text ) +{ + int rc = LDAP_SUCCESS; + ber_tag_t tag; + ber_len_t len; + BerElement *ber; + + assert( reqdata != NULL ); + + ber = ber_init( reqdata ); + + if( ber == NULL ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ber_init failed\n", + 0, 0, 0 ); + *text = ch_strdup("password decoding error"); + return LDAP_PROTOCOL_ERROR; + } + + tag = ber_scanf(ber, "{" /*}*/); + + if( tag == LBER_ERROR ) { + goto decoding_error; + } + + tag = ber_peek_tag( ber, &len ); + + if( tag == LDAP_TAG_EXOP_X_MODIFY_PASSWD_ID ) { + if( id == NULL ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n", + 0, 0, 0 ); + *text = "user must change own password"; + rc = LDAP_UNWILLING_TO_PERFORM; + goto done; + } + + tag = ber_scanf( ber, "O", id ); + + if( tag == LBER_ERROR ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n", + 0, 0, 0 ); + goto decoding_error; + } + + tag = ber_peek_tag( ber, &len); + } + + if( tag == LDAP_TAG_EXOP_X_MODIFY_PASSWD_OLD ) { + if( old == NULL ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n", + 0, 0, 0 ); + *text = "use bind to verify old password"; + rc = LDAP_UNWILLING_TO_PERFORM; + goto done; + } + + tag = ber_scanf( ber, "O", old ); + + if( tag == LBER_ERROR ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n", + 0, 0, 0 ); + goto decoding_error; + } + + tag = ber_peek_tag( ber, &len); + } + + if( tag == LDAP_TAG_EXOP_X_MODIFY_PASSWD_NEW ) { + if( new == NULL ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n", + 0, 0, 0 ); + *text = "user specified passwords disallowed"; + rc = LDAP_UNWILLING_TO_PERFORM; + goto done; + } + + tag = ber_scanf( ber, "O", new ); + + if( tag == LBER_ERROR ) { + Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n", + 0, 0, 0 ); + goto decoding_error; + } + + tag = ber_peek_tag( ber, &len ); + } + + if( len != 0 ) { +decoding_error: + Debug( LDAP_DEBUG_TRACE, + "slap_passwd_parse: decoding error, len=%ld\n", + (long) len, 0, 0 ); + + *text = ch_strdup("data decoding error"); + rc = LDAP_PROTOCOL_ERROR; + } + +done: + if( rc != LDAP_SUCCESS ) { + if( id != NULL ) { + ber_bvfree( *id ); + *id = NULL; + } + + if( old != NULL ) { + ber_bvfree( *old ); + *old = NULL; + } + + if( new != NULL ) { + ber_bvfree( *new ); + *new = NULL; + } + } + + ber_free( ber, 1 ); return rc; } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index a07804d754..07ea38c315 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -480,12 +480,22 @@ LIBSLAPD_F (void) slap_init_user LDAP_P(( char *username, char *groupname )); /* * passwd.c */ +LIBSLAPD_F (int) slap_passwd_init( void ); + LIBSLAPD_F (int) slap_passwd_check( Attribute *attr, struct berval *cred ); + LIBSLAPD_F (struct berval *) slap_passwd_generate( struct berval *cred ); -LIBSLAPD_F (int) slap_passwd_init( void ); + +LIBSLAPD_F (int) slap_passwd_parse( + struct berval *reqdata, + struct berval **id, + struct berval **old, + struct berval **new, + char **text ); + /* * kerberos.c */