1/* passwd.c - password extended operation routines */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2011 The OpenLDAP Foundation.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
11 *
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>.
15 */
16
17#include "portable.h"
18
19#include <stdio.h>
20
21#include <ac/socket.h>
22#include <ac/string.h>
23#include <ac/unistd.h>
24
25#ifdef SLAPD_CRYPT
26#include <ac/crypt.h>
27#endif
28
29#include "slap.h"
30#ifdef __APPLE__
31#include "applehelpers.h"
32#include "psauth.h"
33#endif
34
35#include <lber_pvt.h>
36#include <lutil.h>
37#include <lutil_sha1.h>
38
39const struct berval slap_EXOP_MODIFY_PASSWD = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
40
41static const char *defhash[] = {
42#ifdef LUTIL_SHA1_BYTES
43	"{SSHA}",
44#else
45	"{SMD5}",
46#endif
47	NULL
48};
49
50int passwd_extop(
51	Operation *op,
52	SlapReply *rs )
53{
54	struct berval id = {0, NULL}, hash, *rsp = NULL;
55	req_pwdexop_s *qpw = &op->oq_pwdexop;
56	req_extended_s qext = op->oq_extended;
57	Modifications *ml;
58	slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
59	int i, nhash;
60	char **hashes, idNul;
61	int rc;
62	BackendDB *op_be;
63	int freenewpw = 0;
64	struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
65#ifdef __APPLE__
66	CFDictionaryRef policy = NULL;
67	int isChangingOwnPassword = 0;
68	bool isldapi = false;
69	if((op->o_conn->c_listener->sl_url.bv_len == strlen("ldapi://%2Fvar%2Frun%2Fldapi")) && (strncmp(op->o_conn->c_listener->sl_url.bv_val, "ldapi://%2Fvar%2Frun%2Fldapi", op->o_conn->c_listener->sl_url.bv_len) == 0)) {
70		isldapi = true;
71	}
72#endif
73
74	assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
75
76#ifndef __APPLE__
77	if( op->o_dn.bv_len == 0 ) {
78		Statslog( LDAP_DEBUG_STATS, "%s PASSMOD\n",
79			op->o_log_prefix, 0, 0, 0, 0 );
80		rs->sr_text = "only authenticated users may change passwords";
81		return LDAP_STRONG_AUTH_REQUIRED;
82	}
83#endif
84
85	qpw->rs_old.bv_len = 0;
86	qpw->rs_old.bv_val = NULL;
87	qpw->rs_new.bv_len = 0;
88	qpw->rs_new.bv_val = NULL;
89	qpw->rs_mods = NULL;
90	qpw->rs_modtail = NULL;
91
92	rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
93		&qpw->rs_old, &qpw->rs_new, &rs->sr_text );
94
95	if ( !BER_BVISNULL( &id )) {
96		idNul = id.bv_val[id.bv_len];
97		id.bv_val[id.bv_len] = '\0';
98	}
99	if ( rs->sr_err == LDAP_SUCCESS && !BER_BVISEMPTY( &id ) ) {
100		Statslog( LDAP_DEBUG_STATS, "%s PASSMOD id=\"%s\"%s%s\n",
101			op->o_log_prefix, id.bv_val,
102			qpw->rs_old.bv_val ? " old" : "",
103			qpw->rs_new.bv_val ? " new" : "", 0 );
104	} else {
105		Statslog( LDAP_DEBUG_STATS, "%s PASSMOD%s%s\n",
106			op->o_log_prefix,
107			qpw->rs_old.bv_val ? " old" : "",
108			qpw->rs_new.bv_val ? " new" : "", 0, 0 );
109	}
110
111	if ( rs->sr_err != LDAP_SUCCESS ) {
112		if ( !BER_BVISNULL( &id ))
113			id.bv_val[id.bv_len] = idNul;
114		return rs->sr_err;
115	}
116
117	if ( !BER_BVISEMPTY( &id ) ) {
118		rs->sr_err = dnPrettyNormal( NULL, &id, &dn, &ndn, op->o_tmpmemctx );
119		id.bv_val[id.bv_len] = idNul;
120		if ( rs->sr_err != LDAP_SUCCESS ) {
121			rs->sr_text = "Invalid DN";
122			rc = rs->sr_err;
123			goto error_return;
124		}
125		op->o_req_dn = dn;
126		op->o_req_ndn = ndn;
127		op->o_bd = select_backend( &op->o_req_ndn, 1 );
128
129	} else {
130		ber_dupbv_x( &dn, &op->o_dn, op->o_tmpmemctx );
131		ber_dupbv_x( &ndn, &op->o_ndn, op->o_tmpmemctx );
132		op->o_req_dn = dn;
133		op->o_req_ndn = ndn;
134		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
135		op->o_bd = op->o_conn->c_authz_backend;
136		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
137	}
138
139	if( op->o_bd == NULL ) {
140		if ( qpw->rs_old.bv_val != NULL ) {
141			rs->sr_text = "unwilling to verify old password";
142			rc = LDAP_UNWILLING_TO_PERFORM;
143			goto error_return;
144		}
145
146#ifdef HAVE_CYRUS_SASL
147		rc = slap_sasl_setpass( op, rs );
148#else
149		rs->sr_text = "no authz backend";
150		rc = LDAP_OTHER;
151#endif
152		goto error_return;
153	}
154
155	if ( op->o_req_ndn.bv_len == 0 ) {
156		rs->sr_text = "no password is associated with the Root DSE";
157		rc = LDAP_UNWILLING_TO_PERFORM;
158		goto error_return;
159	}
160
161	/* If we've got a glued backend, check the real backend */
162	op_be = op->o_bd;
163	if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
164		op->o_bd = select_backend( &op->o_req_ndn, 0 );
165	}
166
167	if (backend_check_restrictions( op, rs,
168			(struct berval *)&slap_EXOP_MODIFY_PASSWD ) != LDAP_SUCCESS) {
169		rc = rs->sr_err;
170		goto error_return;
171	}
172
173	/* check for referrals */
174	if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
175		rc = rs->sr_err;
176		goto error_return;
177	}
178
179	/* This does not apply to multi-master case */
180	if(!( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ))) {
181		/* we SHOULD return a referral in this case */
182		BerVarray defref = op->o_bd->be_update_refs
183			? op->o_bd->be_update_refs : default_referral;
184
185		if( defref != NULL ) {
186			rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
187				NULL, NULL, LDAP_SCOPE_DEFAULT );
188			if(rs->sr_ref) {
189				rs->sr_flags |= REP_REF_MUSTBEFREED;
190			} else {
191				rs->sr_ref = defref;
192			}
193			rc = LDAP_REFERRAL;
194			goto error_return;
195
196		}
197
198		rs->sr_text = "shadow context; no update referral";
199		rc = LDAP_UNWILLING_TO_PERFORM;
200		goto error_return;
201	}
202
203	/* generate a new password if none was provided */
204	if ( qpw->rs_new.bv_len == 0 ) {
205		slap_passwd_generate( &qpw->rs_new );
206		if ( qpw->rs_new.bv_len ) {
207			rsp = slap_passwd_return( &qpw->rs_new );
208			freenewpw = 1;
209		}
210	}
211	if ( qpw->rs_new.bv_len == 0 ) {
212		rs->sr_text = "password generation failed";
213		rc = LDAP_OTHER;
214		goto error_return;
215	}
216
217	op->o_bd = op_be;
218
219	/* Give the backend a chance to handle this itself */
220	if ( op->o_bd->be_extended ) {
221		rs->sr_err = op->o_bd->be_extended( op, rs );
222		if ( rs->sr_err != LDAP_UNWILLING_TO_PERFORM &&
223			rs->sr_err != SLAP_CB_CONTINUE )
224		{
225			rc = rs->sr_err;
226			if ( rsp ) {
227				rs->sr_rspdata = rsp;
228				rsp = NULL;
229			}
230			goto error_return;
231		}
232	}
233
234	/* The backend didn't handle it, so try it here */
235	if( op->o_bd && !op->o_bd->be_modify ) {
236		rs->sr_text = "operation not supported for current user";
237		rc = LDAP_UNWILLING_TO_PERFORM;
238		goto error_return;
239	}
240
241#ifndef __APPLE__
242	if ( qpw->rs_old.bv_val != NULL ) {
243		Entry *e = NULL;
244
245		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL,
246			slap_schema.si_ad_userPassword, 0, &e );
247		if ( rc == LDAP_SUCCESS && e ) {
248			Attribute *a = attr_find( e->e_attrs,
249				slap_schema.si_ad_userPassword );
250			if ( a )
251				rc = slap_passwd_check( op, e, a, &qpw->rs_old, &rs->sr_text );
252			else
253				rc = 1;
254			be_entry_release_r( op, e );
255			if ( rc == LDAP_SUCCESS )
256				goto old_good;
257		}
258		rs->sr_text = "unwilling to verify old password";
259		rc = LDAP_UNWILLING_TO_PERFORM;
260		goto error_return;
261	}
262#else
263	if( !isldapi && op->o_dn.bv_len == 0 ) {
264		if ( qpw->rs_old.bv_val != NULL ) {
265			char *recname = odusers_copy_recname(op);
266			// nul terminated version of the old password
267			char *tmpoldpass = ch_calloc(qpw->rs_old.bv_len + 1, 1);
268			memcpy(tmpoldpass, qpw->rs_old.bv_val, qpw->rs_old.bv_len);
269			op->o_conn->c_sasl_bindop = op;
270			rc = (DoPSAuth(recname, tmpoldpass, NULL, op->o_conn, op->o_req_dn.bv_val) == kAuthNoError) ? LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS;
271			op->o_conn->c_sasl_bindop = NULL;
272			free(tmpoldpass);
273			free(recname);
274			if(rc == LDAP_SUCCESS) {
275				isChangingOwnPassword = 1;
276				goto old_good;
277			}
278			rs->sr_text = "unwilling to verify old password";
279			rc = LDAP_UNWILLING_TO_PERFORM;
280			goto error_return;
281		}
282
283		rs->sr_text = "only authenticated users may change passwords";
284		rc = LDAP_STRONG_AUTH_REQUIRED;
285		goto error_return;
286	} else {
287		if(ber_bvstrcasecmp(&op->o_req_dn, &op->o_dn) == 0) {
288			isChangingOwnPassword = 1;
289		}
290	}
291#endif
292
293old_good:;
294#ifndef __APPLE__
295	ml = ch_malloc( sizeof(Modifications) );
296	if ( !qpw->rs_modtail ) qpw->rs_modtail = &ml->sml_next;
297
298	if ( default_passwd_hash ) {
299		for ( nhash = 0; default_passwd_hash[nhash]; nhash++ );
300		hashes = default_passwd_hash;
301	} else {
302		nhash = 1;
303		hashes = (char **)defhash;
304	}
305	ml->sml_numvals = nhash;
306	ml->sml_values = ch_malloc( (nhash+1)*sizeof(struct berval) );
307	for ( i=0; hashes[i]; i++ ) {
308		slap_passwd_hash_type( &qpw->rs_new, &hash, hashes[i], &rs->sr_text );
309		if ( hash.bv_len == 0 ) {
310			if ( !rs->sr_text ) {
311				rs->sr_text = "password hash failed";
312			}
313			break;
314		}
315		ml->sml_values[i] = hash;
316	}
317	ml->sml_values[i].bv_val = NULL;
318	ml->sml_nvalues = NULL;
319	ml->sml_desc = slap_schema.si_ad_userPassword;
320	ml->sml_type = ml->sml_desc->ad_cname;
321	ml->sml_op = LDAP_MOD_REPLACE;
322	ml->sml_flags = 0;
323	ml->sml_next = qpw->rs_mods;
324	qpw->rs_mods = ml;
325
326	if ( hashes[i] ) {
327		rs->sr_err = LDAP_OTHER;
328
329	} else {
330		slap_callback *sc = op->o_callback;
331
332		op->o_tag = LDAP_REQ_MODIFY;
333		op->o_callback = &cb;
334		op->orm_modlist = qpw->rs_mods;
335		op->orm_no_opattrs = 0;
336
337		cb.sc_private = qpw;	/* let Modify know this was pwdMod,
338					 * if it cares... */
339
340		rs->sr_err = op->o_bd->be_modify( op, rs );
341
342		/* be_modify() might have shuffled modifications */
343		qpw->rs_mods = op->orm_modlist;
344
345		if ( rs->sr_err == LDAP_SUCCESS ) {
346			rs->sr_rspdata = rsp;
347
348		} else if ( rsp ) {
349			ber_bvfree( rsp );
350			rsp = NULL;
351		}
352		op->o_tag = LDAP_REQ_EXTENDED;
353		op->o_callback = sc;
354	}
355	rc = rs->sr_err;
356#else
357	bool isadmin = false;
358	bool isowner = false;
359	bool iscomputer = false;
360	char *tmppass = ch_calloc(qpw->rs_new.bv_len + 1, 1);
361	memcpy(tmppass, qpw->rs_new.bv_val, qpw->rs_new.bv_len);
362
363	if(strnstr(op->o_req_dn.bv_val, "cn=computer", op->o_req_dn.bv_len) != NULL) {
364		iscomputer = true;
365	}
366
367	// Always get the policy so odusers_store_history() can decide if it
368	// should store the password.  But in general, policy is only checked
369	// for non-ldapi connections.
370	//
371	// If the connection isn't authenticated (they supplied the old password),
372	// then they are changing their own password.
373	if(op->o_dn.bv_len) {
374		policy = odusers_copy_effectiveuserpoldict(&op->o_conn->c_dn);
375	} else {
376		policy = odusers_copy_effectiveuserpoldict(&op->o_req_dn);
377	}
378
379	if(isldapi == false) {
380		if(!policy) {
381			Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve policy for %s, failing password change for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val);
382			rs->sr_text = "unable to retrieve policy";
383			rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
384			free(tmppass);
385			goto error_return;
386		}
387
388		int disableReason = odusers_isdisabled(policy);
389		if(disableReason && (disableReason != kDisabledNewPasswordRequired)) {
390			Debug(LDAP_DEBUG_ANY, "%s: User %s is disabled, denying password change for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val);
391			rs->sr_text = "user is disabled";
392			rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
393			free(tmppass);
394			goto error_return;
395		}
396
397		if( op->o_dn.bv_len && (ber_bvcmp(&op->o_req_ndn, &op->o_conn->c_dn) != 0) ) {
398			if(!odusers_isadmin(policy)) {
399				char *ownername = odusers_copy_owner(&op->o_req_ndn);
400				if(ownername && strncmp(ownername, op->o_conn->c_dn.bv_val, op->o_conn->c_dn.bv_len) == 0) {
401					isowner = true;
402				} else {
403					Debug(LDAP_DEBUG_ANY, "%s: non-admin user %s denied attempt to change password for %s.\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val);
404					rs->sr_text = "permission denied";
405					rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
406					free(tmppass);
407					free(ownername);
408					goto error_return;
409				}
410				free(ownername);
411			} else {
412				isadmin = true;
413			}
414		} else {
415			CFNumberRef canchange = CFDictionaryGetValue(policy, CFSTR("canModifyPasswordforSelf"));
416			if(canchange) {
417				short tmpshort = 0;
418				CFNumberGetValue(canchange, kCFNumberShortType, &tmpshort);
419				if(!tmpshort) {
420					Debug(LDAP_DEBUG_ANY, "%s: canModifyPasswordforSelf on %s denied attempt to change password for %s.\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val);
421					rs->sr_text = "policy violation";
422					rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
423					free(tmppass);
424					goto error_return;
425				}
426			} else {
427				Debug(LDAP_DEBUG_ANY, "%s: No canModifyPasswordforSelf policy found in user %s dictionary", __func__, op->o_req_ndn.bv_val, 0);
428			}
429		}
430	}
431
432	/* ldapi and admins and computers skip policy checks */
433	if(!isldapi && !isadmin && !isowner && !iscomputer) {
434		char *recname = odusers_copy_recname(op);
435		if(!recname) {
436			Debug(LDAP_DEBUG_ANY, "%s: Could not locate record name for %s\n", __func__, op->o_req_ndn.bv_val, 0);
437			rs->sr_text = "Couldn't locate recname";
438			rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
439			free(tmppass);
440			goto error_return;
441		}
442
443		if(odusers_verify_passwordquality(tmppass, recname, policy, rs) != 0) {
444			Debug(LDAP_DEBUG_ANY, "%s: password quality check failed for %s\n", __func__, op->o_req_ndn.bv_val, 0);
445			free(recname);
446			free(tmppass);
447			rc = rs->sr_err;
448			goto error_return;
449		}
450		free(recname);
451
452		if(odusers_check_history(&op->o_req_dn, tmppass, policy) != 0) {
453			Debug(LDAP_DEBUG_ANY, "%s: password history check failed for %s\n", __func__, op->o_req_dn.bv_val, 0);
454			free(tmppass);
455			rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
456			goto error_return;
457		}
458	}
459
460
461	if(odusers_set_password(&op->o_req_dn, tmppass, isChangingOwnPassword) != 0) {
462		Debug(LDAP_DEBUG_ANY, "%s: set password for user %s failed\n", __func__, op->o_req_ndn.bv_val, 0);
463		rs->sr_err = rc = LDAP_UNWILLING_TO_PERFORM;
464		rs->sr_text = "Error setting password";
465		free(tmppass);
466		goto error_return;
467	}
468
469	// Will only store if the policy has been set
470	odusers_store_history(&op->o_req_dn, tmppass, policy);
471
472	Debug(LDAP_DEBUG_ANY, "%s: %s changed password for %s\n", __func__, op->o_conn->c_dn.bv_val, op->o_req_ndn.bv_val);
473	free(tmppass);
474	rs->sr_err = rc = LDAP_SUCCESS;
475	rs->sr_text = NULL;
476
477#endif
478
479	op->oq_extended = qext;
480
481error_return:;
482	if(policy) CFRelease(policy);
483	if ( qpw->rs_mods ) {
484		slap_mods_free( qpw->rs_mods, 1 );
485	}
486	if ( freenewpw ) {
487		free( qpw->rs_new.bv_val );
488	}
489	if ( !BER_BVISNULL( &dn ) ) {
490		op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
491		BER_BVZERO( &op->o_req_dn );
492	}
493	if ( !BER_BVISNULL( &ndn ) ) {
494		op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
495		BER_BVZERO( &op->o_req_ndn );
496	}
497
498	return rc;
499}
500
501/* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will
502 * reject it in this condition, the caller must NUL-terminate it.
503 * FIXME: should dnNormalize still be complaining about that?
504 */
505int slap_passwd_parse( struct berval *reqdata,
506	struct berval *id,
507	struct berval *oldpass,
508	struct berval *newpass,
509	const char **text )
510{
511	int rc = LDAP_SUCCESS;
512	ber_tag_t tag;
513	ber_len_t len = -1;
514	BerElementBuffer berbuf;
515	BerElement *ber = (BerElement *)&berbuf;
516
517	if( reqdata == NULL ) {
518		return LDAP_SUCCESS;
519	}
520
521	if( reqdata->bv_len == 0 ) {
522		*text = "empty request data field";
523		return LDAP_PROTOCOL_ERROR;
524	}
525
526	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
527	ber_init2( ber, reqdata, 0 );
528
529	tag = ber_skip_tag( ber, &len );
530
531	if( tag != LBER_SEQUENCE ) {
532		Debug( LDAP_DEBUG_TRACE,
533			"slap_passwd_parse: decoding error\n", 0, 0, 0 );
534		rc = LDAP_PROTOCOL_ERROR;
535		goto done;
536	}
537
538	tag = ber_peek_tag( ber, &len );
539	if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
540		if( id == NULL ) {
541			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n",
542				0, 0, 0 );
543
544			*text = "user must change own password";
545			rc = LDAP_UNWILLING_TO_PERFORM;
546			goto done;
547		}
548
549		tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM );
550
551		if( tag == LBER_ERROR ) {
552			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n",
553				0, 0, 0 );
554
555			goto decoding_error;
556		}
557
558		tag = ber_peek_tag( ber, &len );
559	}
560
561	if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) {
562		if( oldpass == NULL ) {
563			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n",
564				0, 0, 0 );
565
566			*text = "use bind to verify old password";
567			rc = LDAP_UNWILLING_TO_PERFORM;
568			goto done;
569		}
570
571		tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM );
572
573		if( tag == LBER_ERROR ) {
574			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n",
575				0, 0, 0 );
576
577			goto decoding_error;
578		}
579
580		if( oldpass->bv_len == 0 ) {
581			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n",
582				0, 0, 0 );
583
584			*text = "old password value is empty";
585			rc = LDAP_UNWILLING_TO_PERFORM;
586			goto done;
587		}
588
589		tag = ber_peek_tag( ber, &len );
590	}
591
592	if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) {
593		if( newpass == NULL ) {
594			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n",
595				0, 0, 0 );
596
597			*text = "user specified passwords disallowed";
598			rc = LDAP_UNWILLING_TO_PERFORM;
599			goto done;
600		}
601
602		tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM );
603
604		if( tag == LBER_ERROR ) {
605			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n",
606				0, 0, 0 );
607
608			goto decoding_error;
609		}
610
611		if( newpass->bv_len == 0 ) {
612			Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n",
613				0, 0, 0 );
614
615			*text = "new password value is empty";
616			rc = LDAP_UNWILLING_TO_PERFORM;
617			goto done;
618		}
619
620		tag = ber_peek_tag( ber, &len );
621	}
622
623	if( len != 0 ) {
624decoding_error:
625		Debug( LDAP_DEBUG_TRACE,
626			"slap_passwd_parse: decoding error, len=%ld\n",
627			(long) len, 0, 0 );
628
629		*text = "data decoding error";
630		rc = LDAP_PROTOCOL_ERROR;
631	}
632
633done:
634	return rc;
635}
636
637struct berval * slap_passwd_return(
638	struct berval		*cred )
639{
640	int rc;
641	struct berval *bv = NULL;
642	BerElementBuffer berbuf;
643	/* opaque structure, size unknown but smaller than berbuf */
644	BerElement *ber = (BerElement *)&berbuf;
645
646	assert( cred != NULL );
647
648	Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n",
649		(long) cred->bv_len, 0, 0 );
650
651	ber_init_w_nullc( ber, LBER_USE_DER );
652
653	rc = ber_printf( ber, "{tON}",
654		LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred );
655
656	if( rc >= 0 ) {
657		(void) ber_flatten( ber, &bv );
658	}
659
660	ber_free_buf( ber );
661
662	return bv;
663}
664
665/*
666 * if "e" is provided, access to each value of the password is checked first
667 */
668int
669slap_passwd_check(
670	Operation	*op,
671	Entry		*e,
672	Attribute	*a,
673	struct berval	*cred,
674	const char	**text )
675{
676	int			result = 1;
677	struct berval		*bv;
678	AccessControlState	acl_state = ACL_STATE_INIT;
679	char		credNul = cred->bv_val[cred->bv_len];
680
681#ifdef SLAPD_SPASSWD
682	void		*old_authctx = NULL;
683
684	ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
685		op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL );
686#endif
687
688	if ( credNul ) cred->bv_val[cred->bv_len] = 0;
689
690	for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) {
691		/* if e is provided, check access */
692		if ( e && access_allowed( op, e, a->a_desc, bv,
693					ACL_AUTH, &acl_state ) == 0 )
694		{
695			continue;
696		}
697
698		if ( !lutil_passwd( bv, cred, NULL, text ) ) {
699			result = 0;
700			break;
701		}
702	}
703
704	if ( credNul ) cred->bv_val[cred->bv_len] = credNul;
705
706#ifdef SLAPD_SPASSWD
707	ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
708		old_authctx, 0, NULL, NULL );
709#endif
710
711	return result;
712}
713
714void
715slap_passwd_generate( struct berval *pass )
716{
717	Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n", 0, 0, 0 );
718	BER_BVZERO( pass );
719
720	/*
721	 * generate passwords of only 8 characters as some getpass(3)
722	 * implementations truncate at 8 characters.
723	 */
724	lutil_passwd_generate( pass, 8 );
725}
726
727void
728slap_passwd_hash_type(
729	struct berval * cred,
730	struct berval * new,
731	char *hash,
732	const char **text )
733{
734	new->bv_len = 0;
735	new->bv_val = NULL;
736
737	assert( hash != NULL );
738
739	lutil_passwd_hash( cred , hash, new, text );
740}
741void
742slap_passwd_hash(
743	struct berval * cred,
744	struct berval * new,
745	const char **text )
746{
747	char *hash = NULL;
748	if ( default_passwd_hash ) {
749		hash = default_passwd_hash[0];
750	}
751	if ( !hash ) {
752		hash = (char *)defhash[0];
753	}
754
755	slap_passwd_hash_type( cred, new, hash, text );
756}
757
758#ifdef SLAPD_CRYPT
759static ldap_pvt_thread_mutex_t passwd_mutex;
760static lutil_cryptfunc slapd_crypt;
761
762static int slapd_crypt( const char *key, const char *salt, char **hash )
763{
764	char *cr;
765	int rc;
766
767	ldap_pvt_thread_mutex_lock( &passwd_mutex );
768
769	cr = crypt( key, salt );
770	if ( cr == NULL || cr[0] == '\0' ) {
771		/* salt must have been invalid */
772		rc = LUTIL_PASSWD_ERR;
773	} else {
774		if ( hash ) {
775			*hash = ber_strdup( cr );
776			rc = LUTIL_PASSWD_OK;
777
778		} else {
779			rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
780		}
781	}
782
783	ldap_pvt_thread_mutex_unlock( &passwd_mutex );
784	return rc;
785}
786#endif /* SLAPD_CRYPT */
787
788void slap_passwd_init()
789{
790#ifdef SLAPD_CRYPT
791	ldap_pvt_thread_mutex_init( &passwd_mutex );
792	lutil_cryptptr = slapd_crypt;
793#endif
794}
795
796