1 /* passwd.c - password extended operation routines */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/passwd.c,v 1.128.2.10 2008/02/11 23:34:15 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
21 #include <ac/socket.h>
22 #include <ac/string.h>
23 #include <ac/unistd.h>
33 #include <lutil_sha1.h>
35 const struct berval slap_EXOP_MODIFY_PASSWD
= BER_BVC(LDAP_EXOP_MODIFY_PASSWD
);
37 static const char *defhash
[] = {
38 #ifdef LUTIL_SHA1_BYTES
50 struct berval id
= {0, NULL
}, hash
, *rsp
= NULL
;
51 req_pwdexop_s
*qpw
= &op
->oq_pwdexop
;
52 req_extended_s qext
= op
->oq_extended
;
54 slap_callback cb
= { NULL
, slap_null_cb
, NULL
, NULL
};
61 assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD
, &op
->ore_reqoid
) == 0 );
63 if( op
->o_dn
.bv_len
== 0 ) {
64 Statslog( LDAP_DEBUG_STATS
, "%s PASSMOD\n",
65 op
->o_log_prefix
, 0, 0, 0, 0 );
66 rs
->sr_text
= "only authenticated users may change passwords";
67 return LDAP_STRONG_AUTH_REQUIRED
;
70 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
;
75 qpw
->rs_modtail
= NULL
;
77 rs
->sr_err
= slap_passwd_parse( op
->ore_reqdata
, &id
,
78 &qpw
->rs_old
, &qpw
->rs_new
, &rs
->sr_text
);
80 if ( !BER_BVISNULL( &id
)) {
81 idNul
= id
.bv_val
[id
.bv_len
];
82 id
.bv_val
[id
.bv_len
] = '\0';
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 );
90 Statslog( LDAP_DEBUG_STATS
, "%s PASSMOD%s%s\n",
92 qpw
->rs_old
.bv_val
? " old" : "",
93 qpw
->rs_new
.bv_val
? " new" : "", 0, 0 );
96 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
97 if ( !BER_BVISNULL( &id
))
98 id
.bv_val
[id
.bv_len
] = idNul
;
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";
111 op
->o_bd
= select_backend( &op
->o_req_ndn
, 1 );
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
);
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
;
128 #ifdef HAVE_CYRUS_SASL
129 rc
= slap_sasl_setpass( op
, rs
);
131 rs
->sr_text
= "no authz backend";
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
;
143 /* If we've got a glued backend, check the real backend */
145 if ( SLAP_GLUE_INSTANCE( op
->o_bd
)) {
146 op
->o_bd
= select_backend( &op
->o_req_ndn
, 0 );
149 if (backend_check_restrictions( op
, rs
,
150 (struct berval
*)&slap_EXOP_MODIFY_PASSWD
) != LDAP_SUCCESS
) {
155 /* check for referrals */
156 if ( backend_check_referrals( op
, rs
) != LDAP_SUCCESS
) {
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
;
167 if( defref
!= NULL
) {
168 rs
->sr_ref
= referral_rewrite( op
->o_bd
->be_update_refs
,
169 NULL
, NULL
, LDAP_SCOPE_DEFAULT
);
171 rs
->sr_flags
|= REP_REF_MUSTBEFREED
;
180 rs
->sr_text
= "shadow context; no update referral";
181 rc
= LDAP_UNWILLING_TO_PERFORM
;
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
);
193 if ( qpw
->rs_new
.bv_len
== 0 ) {
194 rs
->sr_text
= "password generation failed";
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
)
209 rs
->sr_rspdata
= rsp
;
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
;
223 if ( qpw
->rs_old
.bv_val
!= NULL
) {
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
);
232 rc
= slap_passwd_check( op
, e
, a
, &qpw
->rs_old
, &rs
->sr_text
);
235 be_entry_release_r( op
, e
);
236 if ( rc
== LDAP_SUCCESS
)
239 rs
->sr_text
= "unwilling to verify old password";
240 rc
= LDAP_UNWILLING_TO_PERFORM
;
245 ml
= ch_malloc( sizeof(Modifications
) );
246 if ( !qpw
->rs_modtail
) qpw
->rs_modtail
= &ml
->sml_next
;
248 if ( default_passwd_hash
) {
249 for ( nhash
= 0; default_passwd_hash
[nhash
]; nhash
++ );
250 hashes
= default_passwd_hash
;
253 hashes
= (char **)defhash
;
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";
265 ml
->sml_values
[i
] = hash
;
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
;
273 ml
->sml_next
= qpw
->rs_mods
;
277 rs
->sr_err
= LDAP_OTHER
;
280 slap_callback
*sc
= op
->o_callback
;
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;
287 cb
.sc_private
= qpw
; /* let Modify know this was pwdMod,
290 rs
->sr_err
= op
->o_bd
->be_modify( op
, rs
);
292 /* be_modify() might have shuffled modifications */
293 qpw
->rs_mods
= op
->orm_modlist
;
295 if ( rs
->sr_err
== LDAP_SUCCESS
) {
296 rs
->sr_rspdata
= rsp
;
302 op
->o_tag
= LDAP_REQ_EXTENDED
;
307 op
->oq_extended
= qext
;
310 if ( qpw
->rs_mods
) {
311 slap_mods_free( qpw
->rs_mods
, 1 );
314 free( qpw
->rs_new
.bv_val
);
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
);
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
);
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?
332 int slap_passwd_parse( struct berval
*reqdata
,
334 struct berval
*oldpass
,
335 struct berval
*newpass
,
338 int rc
= LDAP_SUCCESS
;
341 BerElementBuffer berbuf
;
342 BerElement
*ber
= (BerElement
*)&berbuf
;
344 if( reqdata
== NULL
) {
348 if( reqdata
->bv_len
== 0 ) {
349 *text
= "empty request data field";
350 return LDAP_PROTOCOL_ERROR
;
353 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
354 ber_init2( ber
, reqdata
, 0 );
356 tag
= ber_skip_tag( ber
, &len
);
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
;
365 tag
= ber_peek_tag( ber
, &len
);
366 if( tag
== LDAP_TAG_EXOP_MODIFY_PASSWD_ID
) {
368 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: ID not allowed.\n",
371 *text
= "user must change own password";
372 rc
= LDAP_UNWILLING_TO_PERFORM
;
376 tag
= ber_get_stringbv( ber
, id
, LBER_BV_NOTERM
);
378 if( tag
== LBER_ERROR
) {
379 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: ID parse failed.\n",
385 tag
= ber_peek_tag( ber
, &len
);
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",
393 *text
= "use bind to verify old password";
394 rc
= LDAP_UNWILLING_TO_PERFORM
;
398 tag
= ber_get_stringbv( ber
, oldpass
, LBER_BV_NOTERM
);
400 if( tag
== LBER_ERROR
) {
401 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: OLD parse failed.\n",
407 if( oldpass
->bv_len
== 0 ) {
408 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: OLD empty.\n",
411 *text
= "old password value is empty";
412 rc
= LDAP_UNWILLING_TO_PERFORM
;
416 tag
= ber_peek_tag( ber
, &len
);
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",
424 *text
= "user specified passwords disallowed";
425 rc
= LDAP_UNWILLING_TO_PERFORM
;
429 tag
= ber_get_stringbv( ber
, newpass
, LBER_BV_NOTERM
);
431 if( tag
== LBER_ERROR
) {
432 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: NEW parse failed.\n",
438 if( newpass
->bv_len
== 0 ) {
439 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_parse: NEW empty.\n",
442 *text
= "new password value is empty";
443 rc
= LDAP_UNWILLING_TO_PERFORM
;
447 tag
= ber_peek_tag( ber
, &len
);
452 Debug( LDAP_DEBUG_TRACE
,
453 "slap_passwd_parse: decoding error, len=%ld\n",
456 *text
= "data decoding error";
457 rc
= LDAP_PROTOCOL_ERROR
;
464 struct berval
* slap_passwd_return(
465 struct berval
*cred
)
468 struct berval
*bv
= NULL
;
469 BerElementBuffer berbuf
;
470 /* opaque structure, size unknown but smaller than berbuf */
471 BerElement
*ber
= (BerElement
*)&berbuf
;
473 assert( cred
!= NULL
);
475 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_return: %ld\n",
476 (long) cred
->bv_len
, 0, 0 );
478 ber_init_w_nullc( ber
, LBER_USE_DER
);
480 rc
= ber_printf( ber
, "{tON}",
481 LDAP_TAG_EXOP_MODIFY_PASSWD_GEN
, cred
);
484 (void) ber_flatten( ber
, &bv
);
493 * if "e" is provided, access to each value of the password is checked first
505 AccessControlState acl_state
= ACL_STATE_INIT
;
508 void *old_authctx
= NULL
;
510 ldap_pvt_thread_pool_setkey( op
->o_threadctx
, (void *)slap_sasl_bind
,
511 op
->o_conn
->c_sasl_authctx
, 0, &old_authctx
, NULL
);
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 )
522 if ( !lutil_passwd( bv
, cred
, NULL
, text
) ) {
529 ldap_pvt_thread_pool_setkey( op
->o_threadctx
, (void *)slap_sasl_bind
,
530 old_authctx
, 0, NULL
, NULL
);
537 slap_passwd_generate( struct berval
*pass
)
539 Debug( LDAP_DEBUG_TRACE
, "slap_passwd_generate\n", 0, 0, 0 );
543 * generate passwords of only 8 characters as some getpass(3)
544 * implementations truncate at 8 characters.
546 lutil_passwd_generate( pass
, 8 );
550 slap_passwd_hash_type(
551 struct berval
* cred
,
559 assert( hash
!= NULL
);
561 lutil_passwd_hash( cred
, hash
, new, text
);
565 struct berval
* cred
,
570 if ( default_passwd_hash
) {
571 hash
= default_passwd_hash
[0];
574 hash
= (char *)defhash
[0];
577 slap_passwd_hash_type( cred
, new, hash
, text
);
581 static ldap_pvt_thread_mutex_t passwd_mutex
;
582 static lutil_cryptfunc slapd_crypt
;
584 static int slapd_crypt( const char *key
, const char *salt
, char **hash
)
589 ldap_pvt_thread_mutex_lock( &passwd_mutex
);
591 cr
= crypt( key
, salt
);
592 if ( cr
== NULL
|| cr
[0] == '\0' ) {
593 /* salt must have been invalid */
594 rc
= LUTIL_PASSWD_ERR
;
597 *hash
= ber_strdup( cr
);
598 rc
= LUTIL_PASSWD_OK
;
601 rc
= strcmp( salt
, cr
) ? LUTIL_PASSWD_ERR
: LUTIL_PASSWD_OK
;
605 ldap_pvt_thread_mutex_unlock( &passwd_mutex
);
608 #endif /* SLAPD_CRYPT */
610 void slap_passwd_init()
613 ldap_pvt_thread_mutex_init( &passwd_mutex
);
614 lutil_cryptptr
= slapd_crypt
;