ppolicy.c revision 1.1.1.8
1/*	$NetBSD: ppolicy.c,v 1.1.1.8 2019/08/08 13:31:40 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2004-2019 The OpenLDAP Foundation.
7 * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
8 * Portions Copyright 2004 Hewlett-Packard Company.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted only as authorized by the OpenLDAP
13 * Public License.
14 *
15 * A copy of this license is available in the file LICENSE in the
16 * top-level directory of the distribution or, alternatively, at
17 * <http://www.OpenLDAP.org/license.html>.
18 */
19/* ACKNOWLEDGEMENTS:
20 * This work was developed by Howard Chu for inclusion in
21 * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
22 * This work was sponsored by the Hewlett-Packard Company.
23 */
24
25#include <sys/cdefs.h>
26__RCSID("$NetBSD: ppolicy.c,v 1.1.1.8 2019/08/08 13:31:40 christos Exp $");
27
28#include "portable.h"
29
30/* This file implements "Password Policy for LDAP Directories",
31 * based on draft behera-ldap-password-policy-09
32 */
33
34#ifdef SLAPD_OVER_PPOLICY
35
36#include <ldap.h>
37#include "lutil.h"
38#include "slap.h"
39#ifdef SLAPD_MODULES
40#define LIBLTDL_DLL_IMPORT	/* Win32: don't re-export libltdl's symbols */
41#include <ltdl.h>
42#endif
43#include <ac/errno.h>
44#include <ac/time.h>
45#include <ac/string.h>
46#include <ac/ctype.h>
47#include "config.h"
48
49#ifndef MODULE_NAME_SZ
50#define MODULE_NAME_SZ 256
51#endif
52
53#ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE
54#define PPOLICY_DEFAULT_MAXRECORDED_FAILURE	5
55#endif
56
57/* Per-instance configuration information */
58typedef struct pp_info {
59	struct berval def_policy;	/* DN of default policy subentry */
60	int use_lockout;		/* send AccountLocked result? */
61	int hash_passwords;		/* transparently hash cleartext pwds */
62	int forward_updates;	/* use frontend for policy state updates */
63} pp_info;
64
65/* Our per-connection info - note, it is not per-instance, it is
66 * used by all instances
67 */
68typedef struct pw_conn {
69	struct berval dn;	/* DN of restricted user */
70} pw_conn;
71
72static pw_conn *pwcons;
73static int ppolicy_cid;
74static int ov_count;
75
76typedef struct pass_policy {
77	AttributeDescription *ad; /* attribute to which the policy applies */
78	int pwdMinAge; /* minimum time (seconds) until passwd can change */
79	int pwdMaxAge; /* time in seconds until pwd will expire after change */
80	int pwdInHistory; /* number of previous passwords kept */
81	int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
82						   2 = check mandatory; fail if not possible */
83	int pwdMinLength; /* minimum number of chars in password */
84	int pwdExpireWarning; /* number of seconds that warning controls are
85							sent before a password expires */
86	int pwdGraceAuthNLimit; /* number of times you can log in with an
87							expired password */
88	int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
89	int pwdLockoutDuration; /* time in seconds a password is locked out for */
90	int pwdMaxFailure; /* number of failed binds allowed before lockout */
91	int pwdMaxRecordedFailure;	/* number of failed binds to store */
92	int pwdFailureCountInterval; /* number of seconds before failure
93									counts are zeroed */
94	int pwdMustChange; /* 0 = users can use admin set password
95							1 = users must change password after admin set */
96	int pwdAllowUserChange; /* 0 = users cannot change their passwords
97								1 = users can change them */
98	int pwdSafeModify; /* 0 = old password doesn't need to come
99								with password change request
100							1 = password change must supply existing pwd */
101	char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
102										    load to check password */
103} PassPolicy;
104
105typedef struct pw_hist {
106	time_t t;	/* timestamp of history entry */
107	struct berval pw;	/* old password hash */
108	struct berval bv;	/* text of entire entry */
109	struct pw_hist *next;
110} pw_hist;
111
112/* Operational attributes */
113static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
114	*ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
115	*ad_pwdPolicySubentry;
116
117static struct schema_info {
118	char *def;
119	AttributeDescription **ad;
120} pwd_OpSchema[] = {
121	{	"( 1.3.6.1.4.1.42.2.27.8.1.16 "
122		"NAME ( 'pwdChangedTime' ) "
123		"DESC 'The time the password was last changed' "
124		"EQUALITY generalizedTimeMatch "
125		"ORDERING generalizedTimeOrderingMatch "
126		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
127		"SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
128		&ad_pwdChangedTime },
129	{	"( 1.3.6.1.4.1.42.2.27.8.1.17 "
130		"NAME ( 'pwdAccountLockedTime' ) "
131		"DESC 'The time an user account was locked' "
132		"EQUALITY generalizedTimeMatch "
133		"ORDERING generalizedTimeOrderingMatch "
134		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
135		"SINGLE-VALUE "
136#if 0
137		/* Not until Relax control is released */
138		"NO-USER-MODIFICATION "
139#endif
140		"USAGE directoryOperation )",
141		&ad_pwdAccountLockedTime },
142	{	"( 1.3.6.1.4.1.42.2.27.8.1.19 "
143		"NAME ( 'pwdFailureTime' ) "
144		"DESC 'The timestamps of the last consecutive authentication failures' "
145		"EQUALITY generalizedTimeMatch "
146		"ORDERING generalizedTimeOrderingMatch "
147		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
148		"NO-USER-MODIFICATION USAGE directoryOperation )",
149		&ad_pwdFailureTime },
150	{	"( 1.3.6.1.4.1.42.2.27.8.1.20 "
151		"NAME ( 'pwdHistory' ) "
152		"DESC 'The history of users passwords' "
153		"EQUALITY octetStringMatch "
154		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
155		"NO-USER-MODIFICATION USAGE directoryOperation )",
156		&ad_pwdHistory },
157	{	"( 1.3.6.1.4.1.42.2.27.8.1.21 "
158		"NAME ( 'pwdGraceUseTime' ) "
159		"DESC 'The timestamps of the grace login once the password has expired' "
160		"EQUALITY generalizedTimeMatch "
161		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
162		"NO-USER-MODIFICATION USAGE directoryOperation )",
163		&ad_pwdGraceUseTime },
164	{	"( 1.3.6.1.4.1.42.2.27.8.1.22 "
165		"NAME ( 'pwdReset' ) "
166		"DESC 'The indication that the password has been reset' "
167		"EQUALITY booleanMatch "
168		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
169		"SINGLE-VALUE USAGE directoryOperation )",
170		&ad_pwdReset },
171	{	"( 1.3.6.1.4.1.42.2.27.8.1.23 "
172		"NAME ( 'pwdPolicySubentry' ) "
173		"DESC 'The pwdPolicy subentry in effect for this object' "
174		"EQUALITY distinguishedNameMatch "
175		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
176		"SINGLE-VALUE "
177#if 0
178		/* Not until Relax control is released */
179		"NO-USER-MODIFICATION "
180#endif
181		"USAGE directoryOperation )",
182		&ad_pwdPolicySubentry },
183	{ NULL, NULL }
184};
185
186/* User attributes */
187static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory,
188	*ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure,
189	*ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration,
190	*ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout,
191	*ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
192	*ad_pwdAttribute, *ad_pwdMaxRecordedFailure;
193
194#define TAB(name)	{ #name, &ad_##name }
195
196static struct schema_info pwd_UsSchema[] = {
197	TAB(pwdAttribute),
198	TAB(pwdMinAge),
199	TAB(pwdMaxAge),
200	TAB(pwdInHistory),
201	TAB(pwdCheckQuality),
202	TAB(pwdMinLength),
203	TAB(pwdMaxFailure),
204	TAB(pwdMaxRecordedFailure),
205	TAB(pwdGraceAuthNLimit),
206	TAB(pwdExpireWarning),
207	TAB(pwdLockout),
208	TAB(pwdLockoutDuration),
209	TAB(pwdFailureCountInterval),
210	TAB(pwdCheckModule),
211	TAB(pwdMustChange),
212	TAB(pwdAllowUserChange),
213	TAB(pwdSafeModify),
214	{ NULL, NULL }
215};
216
217static ldap_pvt_thread_mutex_t chk_syntax_mutex;
218
219enum {
220	PPOLICY_DEFAULT = 1,
221	PPOLICY_HASH_CLEARTEXT,
222	PPOLICY_USE_LOCKOUT
223};
224
225static ConfigDriver ppolicy_cf_default;
226
227static ConfigTable ppolicycfg[] = {
228	{ "ppolicy_default", "policyDN", 2, 2, 0,
229	  ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
230	  "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
231	  "DESC 'DN of a pwdPolicy object for uncustomized objects' "
232	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
233	{ "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
234	  ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
235	  (void *)offsetof(pp_info,hash_passwords),
236	  "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
237	  "DESC 'Hash passwords on add or modify' "
238	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
239	{ "ppolicy_forward_updates", "on|off", 1, 2, 0,
240	  ARG_ON_OFF|ARG_OFFSET,
241	  (void *)offsetof(pp_info,forward_updates),
242	  "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' "
243	  "DESC 'Allow policy state updates to be forwarded via updateref' "
244	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
245	{ "ppolicy_use_lockout", "on|off", 1, 2, 0,
246	  ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
247	  (void *)offsetof(pp_info,use_lockout),
248	  "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
249	  "DESC 'Warn clients with AccountLocked' "
250	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
251	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
252};
253
254static ConfigOCs ppolicyocs[] = {
255	{ "( OLcfgOvOc:12.1 "
256	  "NAME 'olcPPolicyConfig' "
257	  "DESC 'Password Policy configuration' "
258	  "SUP olcOverlayConfig "
259	  "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
260	  "olcPPolicyUseLockout $ olcPPolicyForwardUpdates ) )",
261	  Cft_Overlay, ppolicycfg },
262	{ NULL, 0, NULL }
263};
264
265static int
266ppolicy_cf_default( ConfigArgs *c )
267{
268	slap_overinst *on = (slap_overinst *)c->bi;
269	pp_info *pi = (pp_info *)on->on_bi.bi_private;
270	int rc = ARG_BAD_CONF;
271
272	assert ( c->type == PPOLICY_DEFAULT );
273	Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n", 0, 0, 0);
274
275	switch ( c->op ) {
276	case SLAP_CONFIG_EMIT:
277		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n", 0, 0, 0);
278		rc = 0;
279		if ( !BER_BVISEMPTY( &pi->def_policy )) {
280			rc = value_add_one( &c->rvalue_vals,
281					    &pi->def_policy );
282			if ( rc ) return rc;
283			rc = value_add_one( &c->rvalue_nvals,
284					    &pi->def_policy );
285		}
286		break;
287	case LDAP_MOD_DELETE:
288		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n", 0, 0, 0);
289		if ( pi->def_policy.bv_val ) {
290			ber_memfree ( pi->def_policy.bv_val );
291			pi->def_policy.bv_val = NULL;
292		}
293		pi->def_policy.bv_len = 0;
294		rc = 0;
295		break;
296	case SLAP_CONFIG_ADD:
297		/* fallthrough to LDAP_MOD_ADD */
298	case LDAP_MOD_ADD:
299		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n", 0, 0, 0);
300		if ( pi->def_policy.bv_val ) {
301			ber_memfree ( pi->def_policy.bv_val );
302		}
303		pi->def_policy = c->value_ndn;
304		ber_memfree( c->value_dn.bv_val );
305		BER_BVZERO( &c->value_dn );
306		BER_BVZERO( &c->value_ndn );
307		rc = 0;
308		break;
309	default:
310		abort ();
311	}
312
313	return rc;
314}
315
316static time_t
317parse_time( char *atm )
318{
319	struct lutil_tm tm;
320	struct lutil_timet tt;
321	time_t ret = (time_t)-1;
322
323	if ( lutil_parsetime( atm, &tm ) == 0) {
324		lutil_tm2time( &tm, &tt );
325		ret = tt.tt_sec;
326	}
327	return ret;
328}
329
330static int
331account_locked( Operation *op, Entry *e,
332		PassPolicy *pp, Modifications **mod )
333{
334	Attribute       *la;
335
336	assert(mod != NULL);
337
338	if ( !pp->pwdLockout )
339		return 0;
340
341	if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
342		BerVarray vals = la->a_nvals;
343
344		/*
345		 * there is a lockout stamp - we now need to know if it's
346		 * a valid one.
347		 */
348		if (vals[0].bv_val != NULL) {
349			time_t then, now;
350			Modifications *m;
351
352			if (!pp->pwdLockoutDuration)
353				return 1;
354
355			if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
356				return 1;
357
358			now = slap_get_time();
359
360			if (now < then + pp->pwdLockoutDuration)
361				return 1;
362
363			m = ch_calloc( sizeof(Modifications), 1 );
364			m->sml_op = LDAP_MOD_DELETE;
365			m->sml_flags = 0;
366			m->sml_type = ad_pwdAccountLockedTime->ad_cname;
367			m->sml_desc = ad_pwdAccountLockedTime;
368			m->sml_next = *mod;
369			*mod = m;
370		}
371	}
372
373	return 0;
374}
375
376/* IMPLICIT TAGS, all context-specific */
377#define PPOLICY_WARNING 0xa0L	/* constructed + 0 */
378#define PPOLICY_ERROR 0x81L		/* primitive + 1 */
379
380#define PPOLICY_EXPIRE 0x80L	/* primitive + 0 */
381#define PPOLICY_GRACE  0x81L	/* primitive + 1 */
382
383static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
384
385static LDAPControl *
386create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
387{
388	BerElementBuffer berbuf, bb2;
389	BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
390	LDAPControl c = { 0 }, *cp;
391	struct berval bv;
392	int rc;
393
394	BER_BVZERO( &c.ldctl_value );
395
396	ber_init2( ber, NULL, LBER_USE_DER );
397	ber_printf( ber, "{" /*}*/ );
398
399	if ( exptime >= 0 ) {
400		ber_init2( b2, NULL, LBER_USE_DER );
401		ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
402		rc = ber_flatten2( b2, &bv, 1 );
403		(void)ber_free_buf(b2);
404		if (rc == -1) {
405			cp = NULL;
406			goto fail;
407		}
408		ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
409		ch_free( bv.bv_val );
410	} else if ( grace > 0 ) {
411		ber_init2( b2, NULL, LBER_USE_DER );
412		ber_printf( b2, "ti", PPOLICY_GRACE, grace );
413		rc = ber_flatten2( b2, &bv, 1 );
414		(void)ber_free_buf(b2);
415		if (rc == -1) {
416			cp = NULL;
417			goto fail;
418		}
419		ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
420		ch_free( bv.bv_val );
421	}
422
423	if (err != PP_noError ) {
424		ber_printf( ber, "te", PPOLICY_ERROR, err );
425	}
426	ber_printf( ber, /*{*/ "N}" );
427
428	if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
429		return NULL;
430	}
431	cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
432	cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
433	cp->ldctl_iscritical = 0;
434	cp->ldctl_value.bv_val = (char *)&cp[1];
435	cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
436	AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
437fail:
438	(void)ber_free_buf(ber);
439
440	return cp;
441}
442
443static LDAPControl **
444add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
445{
446	LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
447	int n;
448
449	n = 0;
450	if ( oldctrls ) {
451		for ( ; oldctrls[n]; n++ )
452			;
453	}
454	n += 2;
455
456	ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
457
458	n = 0;
459	if ( oldctrls ) {
460		for ( ; oldctrls[n]; n++ ) {
461			ctrls[n] = oldctrls[n];
462		}
463	}
464	ctrls[n] = ctrl;
465	ctrls[n+1] = NULL;
466
467	rs->sr_ctrls = ctrls;
468
469	return oldctrls;
470}
471
472static void
473ppolicy_get_default( PassPolicy *pp )
474{
475	memset( pp, 0, sizeof(PassPolicy) );
476
477	pp->ad = slap_schema.si_ad_userPassword;
478
479	/* Users can change their own password by default */
480	pp->pwdAllowUserChange = 1;
481	if ( !pp->pwdMaxRecordedFailure )
482		pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE;
483}
484
485
486static void
487ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
488{
489	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
490	pp_info *pi = on->on_bi.bi_private;
491	Attribute *a;
492	BerVarray vals;
493	int rc;
494	Entry *pe = NULL;
495#if 0
496	const char *text;
497#endif
498
499	ppolicy_get_default( pp );
500
501	if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
502		/*
503		 * entry has no password policy assigned - use default
504		 */
505		vals = &pi->def_policy;
506		if ( !vals->bv_val )
507			goto defaultpol;
508	} else {
509		vals = a->a_nvals;
510		if (vals[0].bv_val == NULL) {
511			Debug( LDAP_DEBUG_ANY,
512				"ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
513			goto defaultpol;
514		}
515	}
516
517	op->o_bd->bd_info = (BackendInfo *)on->on_info;
518	rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
519	op->o_bd->bd_info = (BackendInfo *)on;
520
521	if ( rc ) goto defaultpol;
522
523#if 0	/* Only worry about userPassword for now */
524	if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
525		slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
526#endif
527
528	if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) )
529			&& lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 )
530		goto defaultpol;
531	if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) )
532			&& lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 )
533		goto defaultpol;
534	if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) )
535			&& lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 )
536		goto defaultpol;
537	if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckQuality ) )
538			&& lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 )
539		goto defaultpol;
540	if ( ( a = attr_find( pe->e_attrs, ad_pwdMinLength ) )
541			&& lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 )
542		goto defaultpol;
543	if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) )
544			&& lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 )
545		goto defaultpol;
546	if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxRecordedFailure ) )
547			&& lutil_atoi( &pp->pwdMaxRecordedFailure, a->a_vals[0].bv_val ) != 0 )
548		goto defaultpol;
549	if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) )
550			&& lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 )
551		goto defaultpol;
552	if ( ( a = attr_find( pe->e_attrs, ad_pwdExpireWarning ) )
553			&& lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 )
554		goto defaultpol;
555	if ( ( a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ) )
556			&& lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 )
557		goto defaultpol;
558	if ( ( a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ) )
559			&& lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 )
560		goto defaultpol;
561
562	if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckModule ) ) ) {
563		strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val,
564			sizeof(pp->pwdCheckModule) );
565		pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
566	}
567
568	if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
569    		pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
570	if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
571    		pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
572	if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
573	    	pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
574	if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
575	    	pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
576
577	if ( pp->pwdMaxRecordedFailure < pp->pwdMaxFailure )
578		pp->pwdMaxRecordedFailure = pp->pwdMaxFailure;
579	if ( !pp->pwdMaxRecordedFailure )
580		pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE;
581
582	op->o_bd->bd_info = (BackendInfo *)on->on_info;
583	be_entry_release_r( op, pe );
584	op->o_bd->bd_info = (BackendInfo *)on;
585
586	return;
587
588defaultpol:
589	if ( pe ) {
590		op->o_bd->bd_info = (BackendInfo *)on->on_info;
591		be_entry_release_r( op, pe );
592		op->o_bd->bd_info = (BackendInfo *)on;
593	}
594
595	Debug( LDAP_DEBUG_TRACE,
596		"ppolicy_get: using default policy\n", 0, 0, 0 );
597
598	ppolicy_get_default( pp );
599
600	return;
601}
602
603static int
604password_scheme( struct berval *cred, struct berval *sch )
605{
606	int e;
607
608	assert( cred != NULL );
609
610	if (sch) {
611		sch->bv_val = NULL;
612		sch->bv_len = 0;
613	}
614
615	if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
616		(cred->bv_val[0] != '{')) return LDAP_OTHER;
617
618	for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
619	if (cred->bv_val[e]) {
620		int rc;
621		rc = lutil_passwd_scheme( cred->bv_val );
622		if (rc) {
623			if (sch) {
624				sch->bv_val = cred->bv_val;
625				sch->bv_len = e;
626			}
627			return LDAP_SUCCESS;
628		}
629	}
630	return LDAP_OTHER;
631}
632
633static int
634check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e, char **txt )
635{
636	int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
637	char *ptr;
638	struct berval sch;
639
640	assert( cred != NULL );
641	assert( pp != NULL );
642	assert( txt != NULL );
643
644	ptr = cred->bv_val;
645
646	*txt = NULL;
647
648	if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
649		rc = LDAP_CONSTRAINT_VIOLATION;
650		if ( err ) *err = PP_passwordTooShort;
651		return rc;
652	}
653
654        /*
655         * We need to know if the password is already hashed - if so
656         * what scheme is it. The reason being that the "hash" of
657         * {cleartext} still allows us to check the password.
658         */
659	rc = password_scheme( cred, &sch );
660	if (rc == LDAP_SUCCESS) {
661		if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
662			sch.bv_len ) == 0)) {
663			/*
664			 * We can check the cleartext "hash"
665			 */
666			ptr = cred->bv_val + sch.bv_len;
667		} else {
668			/* everything else, we can't check */
669			if (pp->pwdCheckQuality == 2) {
670				rc = LDAP_CONSTRAINT_VIOLATION;
671				if (err) *err = PP_insufficientPasswordQuality;
672				return rc;
673			}
674			/*
675			 * We can't check the syntax of the password, but it's not
676			 * mandatory (according to the policy), so we return success.
677			 */
678
679			return LDAP_SUCCESS;
680		}
681	}
682
683	rc = LDAP_SUCCESS;
684
685	if (pp->pwdCheckModule[0]) {
686#ifdef SLAPD_MODULES
687		lt_dlhandle mod;
688		const char *err;
689
690		if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
691			err = lt_dlerror();
692
693			Debug(LDAP_DEBUG_ANY,
694			"check_password_quality: lt_dlopen failed: (%s) %s.\n",
695				pp->pwdCheckModule, err, 0 );
696			ok = LDAP_OTHER; /* internal error */
697		} else {
698			/* FIXME: the error message ought to be passed thru a
699			 * struct berval, with preallocated buffer and size
700			 * passed in. Module can still allocate a buffer for
701			 * it if the provided one is too small.
702			 */
703			int (*prog)( char *passwd, char **text, Entry *ent );
704
705			if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
706				err = lt_dlerror();
707
708				Debug(LDAP_DEBUG_ANY,
709					"check_password_quality: lt_dlsym failed: (%s) %s.\n",
710					pp->pwdCheckModule, err, 0 );
711				ok = LDAP_OTHER;
712			} else {
713				ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
714				ok = prog( ptr, txt, e );
715				ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
716				if (ok != LDAP_SUCCESS) {
717					Debug(LDAP_DEBUG_ANY,
718						"check_password_quality: module error: (%s) %s.[%d]\n",
719						pp->pwdCheckModule, *txt ? *txt : "", ok );
720				}
721			}
722
723			lt_dlclose( mod );
724		}
725#else
726	Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
727		"supported. pwdCheckModule ignored.\n", 0, 0, 0);
728#endif /* SLAPD_MODULES */
729	}
730
731
732	if (ok != LDAP_SUCCESS) {
733		rc = LDAP_CONSTRAINT_VIOLATION;
734		if (err) *err = PP_insufficientPasswordQuality;
735	}
736
737	return rc;
738}
739
740static int
741parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
742{
743	char *ptr;
744	struct berval nv, npw;
745	ber_len_t i, j;
746
747	assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
748
749	if ( oid ) {
750		*oid = 0;
751	}
752	*oldtime = (time_t)-1;
753	BER_BVZERO( oldpw );
754
755	ber_dupbv( &nv, bv );
756
757	/* first get the time field */
758	for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
759		;
760	if ( i == nv.bv_len ) {
761		goto exit_failure; /* couldn't locate the '#' separator */
762	}
763	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
764	ptr = nv.bv_val;
765	*oldtime = parse_time( ptr );
766	if (*oldtime == (time_t)-1) {
767		goto exit_failure;
768	}
769
770	/* get the OID field */
771	for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
772		;
773	if ( i == nv.bv_len ) {
774		goto exit_failure; /* couldn't locate the '#' separator */
775	}
776	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
777	if ( oid ) {
778		*oid = ber_strdup( ptr );
779	}
780
781	/* get the length field */
782	for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
783		;
784	if ( i == nv.bv_len ) {
785		goto exit_failure; /* couldn't locate the '#' separator */
786	}
787	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
788	oldpw->bv_len = strtol( ptr, NULL, 10 );
789	if (errno == ERANGE) {
790		goto exit_failure;
791	}
792
793	/* lastly, get the octets of the string */
794	for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
795		;
796	if ( i - j != oldpw->bv_len) {
797		goto exit_failure; /* length is wrong */
798	}
799
800	npw.bv_val = ptr;
801	npw.bv_len = oldpw->bv_len;
802	ber_dupbv( oldpw, &npw );
803	ber_memfree( nv.bv_val );
804
805	return LDAP_SUCCESS;
806
807exit_failure:;
808	if ( oid && *oid ) {
809		ber_memfree(*oid);
810		*oid = NULL;
811	}
812	if ( oldpw->bv_val ) {
813		ber_memfree( oldpw->bv_val);
814		BER_BVZERO( oldpw );
815	}
816	ber_memfree( nv.bv_val );
817
818	return LDAP_OTHER;
819}
820
821static void
822add_to_pwd_history( pw_hist **l, time_t t,
823                    struct berval *oldpw, struct berval *bv )
824{
825	pw_hist *p, *p1, *p2;
826
827	if (!l) return;
828
829	p = ch_malloc( sizeof( pw_hist ));
830	p->pw = *oldpw;
831	ber_dupbv( &p->bv, bv );
832	p->t = t;
833	p->next = NULL;
834
835	if (*l == NULL) {
836		/* degenerate case */
837		*l = p;
838		return;
839	}
840	/*
841	 * advance p1 and p2 such that p1 is the node before the
842	 * new one, and p2 is the node after it
843	 */
844	for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
845	p->next = p2;
846	if (p1 == NULL) { *l = p; return; }
847	p1->next = p;
848}
849
850#ifndef MAX_PWD_HISTORY_SZ
851#define MAX_PWD_HISTORY_SZ 1024
852#endif /* MAX_PWD_HISTORY_SZ */
853
854static void
855make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
856{
857	char str[ MAX_PWD_HISTORY_SZ ];
858	int nlen;
859
860	snprintf( str, MAX_PWD_HISTORY_SZ,
861		  "%s#%s#%lu#", timebuf,
862		  pa->a_desc->ad_type->sat_syntax->ssyn_oid,
863		  (unsigned long) pa->a_nvals[0].bv_len );
864	str[MAX_PWD_HISTORY_SZ-1] = 0;
865	nlen = strlen(str);
866
867        /*
868         * We have to assume that the string is a string of octets,
869         * not readable characters. In reality, yes, it probably is
870         * a readable (ie, base64) string, but we can't count on that
871         * Hence, while the first 3 fields of the password history
872         * are definitely readable (a timestamp, an OID and an integer
873         * length), the remaining octets of the actual password
874         * are deemed to be binary data.
875         */
876	AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
877	nlen += pa->a_nvals[0].bv_len;
878	bv->bv_val = ch_malloc( nlen + 1 );
879	AC_MEMCPY( bv->bv_val, str, nlen );
880	bv->bv_val[nlen] = '\0';
881	bv->bv_len = nlen;
882}
883
884static void
885free_pwd_history_list( pw_hist **l )
886{
887	pw_hist *p;
888
889	if (!l) return;
890	p = *l;
891	while (p) {
892		pw_hist *pp = p->next;
893
894		free(p->pw.bv_val);
895		free(p->bv.bv_val);
896		free(p);
897		p = pp;
898	}
899	*l = NULL;
900}
901
902typedef struct ppbind {
903	slap_overinst *on;
904	int send_ctrl;
905	int set_restrict;
906	LDAPControl **oldctrls;
907	Modifications *mod;
908	LDAPPasswordPolicyError pErr;
909	PassPolicy pp;
910} ppbind;
911
912static void
913ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
914{
915	int n;
916
917	assert( rs->sr_ctrls != NULL );
918	assert( rs->sr_ctrls[0] != NULL );
919
920	for ( n = 0; rs->sr_ctrls[n]; n++ ) {
921		if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) {
922			op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
923			rs->sr_ctrls[n] = (LDAPControl *)(-1);
924			break;
925		}
926	}
927
928	if ( rs->sr_ctrls[n] == NULL ) {
929		/* missed? */
930	}
931
932	op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
933
934	rs->sr_ctrls = oldctrls;
935}
936
937static int
938ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
939{
940	ppbind *ppb = op->o_callback->sc_private;
941	if ( ppb->send_ctrl ) {
942		ctrls_cleanup( op, rs, ppb->oldctrls );
943	}
944	return SLAP_CB_CONTINUE;
945}
946
947static int
948ppolicy_bind_response( Operation *op, SlapReply *rs )
949{
950	ppbind *ppb = op->o_callback->sc_private;
951	slap_overinst *on = ppb->on;
952	Modifications *mod = ppb->mod, *m;
953	int pwExpired = 0;
954	int ngut = -1, warn = -1, age, rc;
955	Attribute *a;
956	time_t now, pwtime = (time_t)-1;
957	struct lutil_tm now_tm;
958	struct lutil_timet now_usec;
959	char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
960	char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ];
961	struct berval timestamp, timestamp_usec;
962	BackendInfo *bi = op->o_bd->bd_info;
963	Entry *e;
964
965	/* If we already know it's locked, just get on with it */
966	if ( ppb->pErr != PP_noError ) {
967		goto locked;
968	}
969
970	op->o_bd->bd_info = (BackendInfo *)on->on_info;
971	rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
972	op->o_bd->bd_info = bi;
973
974	if ( rc != LDAP_SUCCESS ) {
975		return SLAP_CB_CONTINUE;
976	}
977
978	ldap_pvt_gettime(&now_tm); /* stored for later consideration */
979	lutil_tm2time(&now_tm, &now_usec);
980	now = now_usec.tt_sec;
981	timestamp.bv_val = nowstr;
982	timestamp.bv_len = sizeof(nowstr);
983	slap_timestamp( &now, &timestamp );
984
985	/* Separate timestamp for pwdFailureTime with microsecond granularity */
986	strcpy(nowstr_usec, nowstr);
987	timestamp_usec.bv_val = nowstr_usec;
988	timestamp_usec.bv_len = timestamp.bv_len;
989	snprintf( timestamp_usec.bv_val + timestamp_usec.bv_len-1, sizeof(".123456Z"), ".%06dZ", now_usec.tt_usec );
990	timestamp_usec.bv_len += STRLENOF(".123456");
991
992	if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) {
993		int i = 0, fc = 0;
994
995		m = ch_calloc( sizeof(Modifications), 1 );
996		m->sml_op = LDAP_MOD_ADD;
997		m->sml_flags = 0;
998		m->sml_type = ad_pwdFailureTime->ad_cname;
999		m->sml_desc = ad_pwdFailureTime;
1000		m->sml_numvals = 1;
1001		m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1002		m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1003
1004		ber_dupbv( &m->sml_values[0], &timestamp_usec );
1005		ber_dupbv( &m->sml_nvalues[0], &timestamp_usec );
1006		m->sml_next = mod;
1007		mod = m;
1008
1009		/*
1010		 * Count the pwdFailureTimes - if it's
1011		 * greater than the policy pwdMaxFailure,
1012		 * then lock the account.
1013		 */
1014		if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
1015			for(i=0; a->a_nvals[i].bv_val; i++) {
1016
1017				/*
1018				 * If the interval is 0, then failures
1019				 * stay on the record until explicitly
1020				 * reset by successful authentication.
1021				 */
1022				if (ppb->pp.pwdFailureCountInterval == 0) {
1023					fc++;
1024				} else if (now <=
1025							parse_time(a->a_nvals[i].bv_val) +
1026							ppb->pp.pwdFailureCountInterval) {
1027
1028					fc++;
1029				}
1030				/*
1031				 * We only count those failures
1032				 * which are not due to expire.
1033				 */
1034			}
1035			/* Do we have too many timestamps? If so, delete some values.
1036			 * We don't bother to sort the values here. OpenLDAP keeps the
1037			 * values in order by default. Fundamentally, relying on the
1038			 * information here is wrong anyway; monitoring systems should
1039			 * be tracking Bind failures in syslog, not here.
1040			 */
1041			if (a->a_numvals >= ppb->pp.pwdMaxRecordedFailure) {
1042				int j = ppb->pp.pwdMaxRecordedFailure-1;
1043				/* If more than 2x, cheaper to perform a Replace */
1044				if (a->a_numvals >= 2 * ppb->pp.pwdMaxRecordedFailure) {
1045					struct berval v, nv;
1046
1047					/* Change the mod we constructed above */
1048					m->sml_op = LDAP_MOD_REPLACE;
1049					m->sml_numvals = ppb->pp.pwdMaxRecordedFailure;
1050					v = m->sml_values[0];
1051					nv = m->sml_nvalues[0];
1052					ch_free(m->sml_values);
1053					ch_free(m->sml_nvalues);
1054					m->sml_values = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
1055					m->sml_nvalues = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
1056					for (i=0; i<j; i++) {
1057						ber_dupbv(&m->sml_values[i], &a->a_vals[a->a_numvals-j+i]);
1058						ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[a->a_numvals-j+i]);
1059					}
1060					m->sml_values[i] = v;
1061					m->sml_nvalues[i] = nv;
1062				} else {
1063				/* else just delete some */
1064					m = ch_calloc( sizeof(Modifications), 1 );
1065					m->sml_op = LDAP_MOD_DELETE;
1066					m->sml_type = ad_pwdFailureTime->ad_cname;
1067					m->sml_desc = ad_pwdFailureTime;
1068					m->sml_numvals = a->a_numvals - j;
1069					m->sml_values = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
1070					m->sml_nvalues = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
1071					for (i=0; i<m->sml_numvals; i++) {
1072						ber_dupbv(&m->sml_values[i], &a->a_vals[i]);
1073						ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[i]);
1074					}
1075					m->sml_next = mod;
1076					mod = m;
1077				}
1078			}
1079		}
1080
1081		if ((ppb->pp.pwdMaxFailure > 0) &&
1082			(fc >= ppb->pp.pwdMaxFailure - 1)) {
1083
1084			/*
1085			 * We subtract 1 from the failure max
1086			 * because the new failure entry hasn't
1087			 * made it to the entry yet.
1088			 */
1089			m = ch_calloc( sizeof(Modifications), 1 );
1090			m->sml_op = LDAP_MOD_REPLACE;
1091			m->sml_flags = 0;
1092			m->sml_type = ad_pwdAccountLockedTime->ad_cname;
1093			m->sml_desc = ad_pwdAccountLockedTime;
1094			m->sml_numvals = 1;
1095			m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1096			m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1097			ber_dupbv( &m->sml_values[0], &timestamp );
1098			ber_dupbv( &m->sml_nvalues[0], &timestamp );
1099			m->sml_next = mod;
1100			mod = m;
1101		}
1102	} else if ( rs->sr_err == LDAP_SUCCESS ) {
1103		if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1104			pwtime = parse_time( a->a_nvals[0].bv_val );
1105
1106		/* delete all pwdFailureTimes */
1107		if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
1108			m = ch_calloc( sizeof(Modifications), 1 );
1109			m->sml_op = LDAP_MOD_DELETE;
1110			m->sml_flags = 0;
1111			m->sml_type = ad_pwdFailureTime->ad_cname;
1112			m->sml_desc = ad_pwdFailureTime;
1113			m->sml_next = mod;
1114			mod = m;
1115		}
1116
1117		/*
1118		 * check to see if the password must be changed
1119		 */
1120		if ( ppb->pp.pwdMustChange &&
1121			(a = attr_find( e->e_attrs, ad_pwdReset )) &&
1122			bvmatch( &a->a_nvals[0], &slap_true_bv ) )
1123		{
1124			/*
1125			 * need to inject client controls here to give
1126			 * more information. For the moment, we ensure
1127			 * that we are disallowed from doing anything
1128			 * other than change password.
1129			 */
1130			if ( ppb->set_restrict ) {
1131				ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
1132					&op->o_conn->c_ndn );
1133			}
1134
1135			ppb->pErr = PP_changeAfterReset;
1136
1137		} else {
1138			/*
1139			 * the password does not need to be changed, so
1140			 * we now check whether the password has expired.
1141			 *
1142			 * We can skip this bit if passwords don't age in
1143			 * the policy. Also, if there was no pwdChangedTime
1144			 * attribute in the entry, the password never expires.
1145			 */
1146			if (ppb->pp.pwdMaxAge == 0) goto grace;
1147
1148			if (pwtime != (time_t)-1) {
1149				/*
1150				 * Check: was the last change time of
1151				 * the password older than the maximum age
1152				 * allowed. (Ignore case 2 from I-D, it's just silly.)
1153				 */
1154				if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
1155			}
1156		}
1157
1158grace:
1159		if (!pwExpired) goto check_expiring_password;
1160
1161		if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
1162			ngut = ppb->pp.pwdGraceAuthNLimit;
1163		else {
1164			for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
1165			ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
1166		}
1167
1168		/*
1169		 * ngut is the number of remaining grace logins
1170		 */
1171		Debug( LDAP_DEBUG_ANY,
1172			"ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
1173			e->e_name.bv_val, ngut, 0);
1174
1175		if (ngut < 1) {
1176			ppb->pErr = PP_passwordExpired;
1177			rs->sr_err = LDAP_INVALID_CREDENTIALS;
1178			goto done;
1179		}
1180
1181		/*
1182		 * Add a grace user time to the entry
1183		 */
1184		m = ch_calloc( sizeof(Modifications), 1 );
1185		m->sml_op = LDAP_MOD_ADD;
1186		m->sml_flags = 0;
1187		m->sml_type = ad_pwdGraceUseTime->ad_cname;
1188		m->sml_desc = ad_pwdGraceUseTime;
1189		m->sml_numvals = 1;
1190		m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1191		m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1192		ber_dupbv( &m->sml_values[0], &timestamp );
1193		ber_dupbv( &m->sml_nvalues[0], &timestamp );
1194		m->sml_next = mod;
1195		mod = m;
1196
1197check_expiring_password:
1198		/*
1199		 * Now we need to check to see
1200		 * if it is about to expire, and if so, should the user
1201		 * be warned about it in the password policy control.
1202		 *
1203		 * If the password has expired, and we're in the grace period, then
1204		 * we don't need to do this bit. Similarly, if we don't have password
1205		 * aging, then there's no need to do this bit either.
1206		 */
1207		if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
1208			goto done;
1209
1210		age = (int)(now - pwtime);
1211
1212		/*
1213		 * We know that there is a password Change Time attribute - if
1214		 * there wasn't, then the pwdExpired value would be true, unless
1215		 * there is no password aging - and if there is no password aging,
1216		 * then this section isn't called anyway - you can't have an
1217		 * expiring password if there's no limit to expire.
1218		 */
1219		if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
1220			/*
1221			 * Set the warning value.
1222			 */
1223			warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
1224			if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
1225
1226			Debug( LDAP_DEBUG_ANY,
1227				"ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
1228				op->o_req_dn.bv_val, warn, 0 );
1229		}
1230	}
1231
1232done:
1233	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1234	be_entry_release_r( op, e );
1235
1236locked:
1237	if ( mod ) {
1238		Operation op2 = *op;
1239		SlapReply r2 = { REP_RESULT };
1240		slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
1241		pp_info *pi = on->on_bi.bi_private;
1242		LDAPControl c, *ca[2];
1243
1244		op2.o_tag = LDAP_REQ_MODIFY;
1245		op2.o_callback = &cb;
1246		op2.orm_modlist = mod;
1247		op2.orm_no_opattrs = 0;
1248		op2.o_dn = op->o_bd->be_rootdn;
1249		op2.o_ndn = op->o_bd->be_rootndn;
1250
1251		/* If this server is a shadow and forward_updates is true,
1252		 * use the frontend to perform this modify. That will trigger
1253		 * the update referral, which can then be forwarded by the
1254		 * chain overlay. Obviously the updateref and chain overlay
1255		 * must be configured appropriately for this to be useful.
1256		 */
1257		if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) {
1258			op2.o_bd = frontendDB;
1259
1260			/* Must use Relax control since these are no-user-mod */
1261			op2.o_relax = SLAP_CONTROL_CRITICAL;
1262			op2.o_ctrls = ca;
1263			ca[0] = &c;
1264			ca[1] = NULL;
1265			BER_BVZERO( &c.ldctl_value );
1266			c.ldctl_iscritical = 1;
1267			c.ldctl_oid = LDAP_CONTROL_RELAX;
1268		} else {
1269			/* If not forwarding, don't update opattrs and don't replicate */
1270			if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
1271				op2.orm_no_opattrs = 1;
1272				op2.o_dont_replicate = 1;
1273			}
1274			op2.o_bd->bd_info = (BackendInfo *)on->on_info;
1275		}
1276		rc = op2.o_bd->be_modify( &op2, &r2 );
1277		slap_mods_free( mod, 1 );
1278	}
1279
1280	if ( ppb->send_ctrl ) {
1281		LDAPControl *ctrl = NULL;
1282		pp_info *pi = on->on_bi.bi_private;
1283
1284		/* Do we really want to tell that the account is locked? */
1285		if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
1286			ppb->pErr = PP_noError;
1287		}
1288		ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
1289		ppb->oldctrls = add_passcontrol( op, rs, ctrl );
1290		op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
1291	}
1292	op->o_bd->bd_info = bi;
1293	return SLAP_CB_CONTINUE;
1294}
1295
1296static int
1297ppolicy_bind( Operation *op, SlapReply *rs )
1298{
1299	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1300
1301	/* Reset lockout status on all Bind requests */
1302	if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1303		ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1304		BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1305	}
1306
1307	/* Root bypasses policy */
1308	if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
1309		Entry *e;
1310		int rc;
1311		ppbind *ppb;
1312		slap_callback *cb;
1313
1314		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1315		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1316
1317		if ( rc != LDAP_SUCCESS ) {
1318			return SLAP_CB_CONTINUE;
1319		}
1320
1321		cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
1322			1, op->o_tmpmemctx );
1323		ppb = (ppbind *)(cb+1);
1324		ppb->on = on;
1325		ppb->pErr = PP_noError;
1326		ppb->set_restrict = 1;
1327
1328		/* Setup a callback so we can munge the result */
1329
1330		cb->sc_response = ppolicy_bind_response;
1331		cb->sc_next = op->o_callback->sc_next;
1332		cb->sc_private = ppb;
1333		op->o_callback->sc_next = cb;
1334
1335		/* Did we receive a password policy request control? */
1336		if ( op->o_ctrlflag[ppolicy_cid] ) {
1337			ppb->send_ctrl = 1;
1338		}
1339
1340		op->o_bd->bd_info = (BackendInfo *)on;
1341		ppolicy_get( op, e, &ppb->pp );
1342
1343		rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1344
1345		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1346		be_entry_release_r( op, e );
1347
1348		if ( rc ) {
1349			ppb->pErr = PP_accountLocked;
1350			send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1351			return rs->sr_err;
1352		}
1353
1354	}
1355
1356	return SLAP_CB_CONTINUE;
1357}
1358
1359/* Reset the restricted info for the next session on this connection */
1360static int
1361ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
1362{
1363	if ( pwcons && !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
1364		ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
1365		BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
1366	}
1367	return SLAP_CB_CONTINUE;
1368}
1369
1370/* Check if this connection is restricted */
1371static int
1372ppolicy_restrict(
1373	Operation *op,
1374	SlapReply *rs )
1375{
1376	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1377	int send_ctrl = 0;
1378
1379	/* Did we receive a password policy request control? */
1380	if ( op->o_ctrlflag[ppolicy_cid] ) {
1381		send_ctrl = 1;
1382	}
1383
1384	if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1385		LDAPControl **oldctrls;
1386		/* if the current authcDN doesn't match the one we recorded,
1387		 * then an intervening Bind has succeeded and the restriction
1388		 * no longer applies. (ITS#4516)
1389		 */
1390		if ( !dn_match( &op->o_conn->c_ndn,
1391				&pwcons[op->o_conn->c_conn_idx].dn )) {
1392			ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1393			BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1394			return SLAP_CB_CONTINUE;
1395		}
1396
1397		Debug( LDAP_DEBUG_TRACE,
1398			"connection restricted to password changing only\n", 0, 0, 0);
1399		if ( send_ctrl ) {
1400			LDAPControl *ctrl = NULL;
1401			ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
1402			oldctrls = add_passcontrol( op, rs, ctrl );
1403		}
1404		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1405		send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
1406			"Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1407		if ( send_ctrl ) {
1408			ctrls_cleanup( op, rs, oldctrls );
1409		}
1410		return rs->sr_err;
1411	}
1412
1413	return SLAP_CB_CONTINUE;
1414}
1415
1416static int
1417ppolicy_compare_response(
1418	Operation *op,
1419	SlapReply *rs )
1420{
1421	/* map compare responses to bind responses */
1422	if ( rs->sr_err == LDAP_COMPARE_TRUE )
1423		rs->sr_err = LDAP_SUCCESS;
1424	else if ( rs->sr_err == LDAP_COMPARE_FALSE )
1425		rs->sr_err = LDAP_INVALID_CREDENTIALS;
1426
1427	ppolicy_bind_response( op, rs );
1428
1429	/* map back to compare */
1430	if ( rs->sr_err == LDAP_SUCCESS )
1431		rs->sr_err = LDAP_COMPARE_TRUE;
1432	else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS )
1433		rs->sr_err = LDAP_COMPARE_FALSE;
1434
1435	return SLAP_CB_CONTINUE;
1436}
1437
1438static int
1439ppolicy_compare(
1440	Operation *op,
1441	SlapReply *rs )
1442{
1443	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1444
1445	if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1446		return rs->sr_err;
1447
1448	/* Did we receive a password policy request control?
1449	 * Are we testing the userPassword?
1450	 */
1451	if ( op->o_ctrlflag[ppolicy_cid] &&
1452		op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) {
1453		Entry *e;
1454		int rc;
1455		ppbind *ppb;
1456		slap_callback *cb;
1457
1458		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1459		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1460
1461		if ( rc != LDAP_SUCCESS ) {
1462			return SLAP_CB_CONTINUE;
1463		}
1464
1465		cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
1466			1, op->o_tmpmemctx );
1467		ppb = (ppbind *)(cb+1);
1468		ppb->on = on;
1469		ppb->pErr = PP_noError;
1470		ppb->send_ctrl = 1;
1471		/* failures here don't lockout the connection */
1472		ppb->set_restrict = 0;
1473
1474		/* Setup a callback so we can munge the result */
1475
1476		cb->sc_response = ppolicy_compare_response;
1477		cb->sc_next = op->o_callback->sc_next;
1478		cb->sc_private = ppb;
1479		op->o_callback->sc_next = cb;
1480
1481		op->o_bd->bd_info = (BackendInfo *)on;
1482		ppolicy_get( op, e, &ppb->pp );
1483
1484		rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1485
1486		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1487		be_entry_release_r( op, e );
1488
1489		if ( rc ) {
1490			ppb->pErr = PP_accountLocked;
1491			send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL );
1492			return rs->sr_err;
1493		}
1494	}
1495	return SLAP_CB_CONTINUE;
1496}
1497
1498static int
1499ppolicy_add(
1500	Operation *op,
1501	SlapReply *rs )
1502{
1503	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1504	pp_info *pi = on->on_bi.bi_private;
1505	PassPolicy pp;
1506	Attribute *pa;
1507	const char *txt;
1508
1509	if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1510		return rs->sr_err;
1511
1512	/* If this is a replica, assume the master checked everything */
1513	if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) )
1514		return SLAP_CB_CONTINUE;
1515
1516	/* Check for password in entry */
1517	if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1518		slap_schema.si_ad_userPassword )))
1519	{
1520		assert( pa->a_vals != NULL );
1521		assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
1522
1523		if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
1524			send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
1525			return rs->sr_err;
1526		}
1527
1528		/*
1529		 * new entry contains a password - if we're not the root user
1530		 * then we need to check that the password fits in with the
1531		 * security policy for the new entry.
1532		 */
1533		ppolicy_get( op, op->ora_e, &pp );
1534		if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1535			struct berval *bv = &(pa->a_vals[0]);
1536			int rc, send_ctrl = 0;
1537			LDAPPasswordPolicyError pErr = PP_noError;
1538			char *txt;
1539
1540			/* Did we receive a password policy request control? */
1541			if ( op->o_ctrlflag[ppolicy_cid] ) {
1542				send_ctrl = 1;
1543			}
1544			rc = check_password_quality( bv, &pp, &pErr, op->ora_e, &txt );
1545			if (rc != LDAP_SUCCESS) {
1546				LDAPControl **oldctrls = NULL;
1547				op->o_bd->bd_info = (BackendInfo *)on->on_info;
1548				if ( send_ctrl ) {
1549					LDAPControl *ctrl = NULL;
1550					ctrl = create_passcontrol( op, -1, -1, pErr );
1551					oldctrls = add_passcontrol( op, rs, ctrl );
1552				}
1553				send_ldap_error( op, rs, rc, txt ? txt : "Password fails quality checking policy" );
1554				if ( txt ) {
1555					free( txt );
1556				}
1557				if ( send_ctrl ) {
1558					ctrls_cleanup( op, rs, oldctrls );
1559				}
1560				return rs->sr_err;
1561			}
1562		}
1563			/*
1564			 * A controversial bit. We hash cleartext
1565			 * passwords provided via add and modify operations
1566			 * You're not really supposed to do this, since
1567			 * the X.500 model says "store attributes" as they
1568			 * get provided. By default, this is what we do
1569			 *
1570			 * But if the hash_passwords flag is set, we hash
1571			 * any cleartext password attribute values via the
1572			 * default password hashing scheme.
1573			 */
1574		if ((pi->hash_passwords) &&
1575			(password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1576			struct berval hpw;
1577
1578			slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1579			if (hpw.bv_val == NULL) {
1580				/*
1581				 * hashing didn't work. Emit an error.
1582				 */
1583				rs->sr_err = LDAP_OTHER;
1584				rs->sr_text = txt;
1585				send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1586				return rs->sr_err;
1587			}
1588
1589			memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1590			ber_memfree( pa->a_vals[0].bv_val );
1591			pa->a_vals[0].bv_val = hpw.bv_val;
1592			pa->a_vals[0].bv_len = hpw.bv_len;
1593		}
1594
1595		/* If password aging is in effect, set the pwdChangedTime */
1596		if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1597			struct berval timestamp;
1598			char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1599			time_t now = slap_get_time();
1600
1601			timestamp.bv_val = timebuf;
1602			timestamp.bv_len = sizeof(timebuf);
1603			slap_timestamp( &now, &timestamp );
1604
1605			attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
1606		}
1607	}
1608	return SLAP_CB_CONTINUE;
1609}
1610
1611static int
1612ppolicy_mod_cb( Operation *op, SlapReply *rs )
1613{
1614	slap_callback *sc = op->o_callback;
1615	op->o_callback = sc->sc_next;
1616	if ( rs->sr_err == LDAP_SUCCESS ) {
1617		ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1618		BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1619	}
1620	op->o_tmpfree( sc, op->o_tmpmemctx );
1621	return SLAP_CB_CONTINUE;
1622}
1623
1624static int
1625ppolicy_modify( Operation *op, SlapReply *rs )
1626{
1627	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1628	pp_info			*pi = on->on_bi.bi_private;
1629	int			i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
1630				hsize = 0, hskip;
1631	PassPolicy		pp;
1632	Modifications		*mods = NULL, *modtail = NULL,
1633				*ml, *delmod, *addmod;
1634	Attribute		*pa, *ha, at;
1635	const char		*txt;
1636	pw_hist			*tl = NULL, *p;
1637	int			zapReset, send_ctrl = 0, free_txt = 0;
1638	Entry			*e;
1639	struct berval		newpw = BER_BVNULL, oldpw = BER_BVNULL,
1640				*bv, cr[2];
1641	LDAPPasswordPolicyError pErr = PP_noError;
1642	LDAPControl		*ctrl = NULL;
1643	LDAPControl 		**oldctrls = NULL;
1644	int			is_pwdexop = 0;
1645	int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
1646	int got_changed = 0, got_history = 0;
1647
1648	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1649	rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1650	op->o_bd->bd_info = (BackendInfo *)on;
1651
1652	if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1653
1654	/* If this is a replica, we may need to tweak some of the
1655	 * master's modifications. Otherwise, just pass it through.
1656	 */
1657	if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
1658		Modifications **prev;
1659		Attribute *a_grace, *a_lock, *a_fail;
1660
1661		a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
1662		a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
1663		a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
1664
1665		for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
1666
1667			if ( ml->sml_desc == slap_schema.si_ad_userPassword )
1668				got_pw = 1;
1669
1670			/* If we're deleting an attr that didn't exist,
1671			 * drop this delete op
1672			 */
1673			if ( ml->sml_op == LDAP_MOD_DELETE ||
1674					ml->sml_op == SLAP_MOD_SOFTDEL ) {
1675				int drop = 0;
1676
1677				if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1678					if ( !a_grace || got_del_grace ) {
1679						drop = ml->sml_op == LDAP_MOD_DELETE;
1680					} else {
1681						got_del_grace = 1;
1682					}
1683				} else
1684				if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1685					if ( !a_lock || got_del_lock ) {
1686						drop = ml->sml_op == LDAP_MOD_DELETE;
1687					} else {
1688						got_del_lock = 1;
1689					}
1690				} else
1691				if ( ml->sml_desc == ad_pwdFailureTime ) {
1692					if ( !a_fail || got_del_fail ) {
1693						drop = ml->sml_op == LDAP_MOD_DELETE;
1694					} else {
1695						got_del_fail = 1;
1696					}
1697				}
1698				if ( drop ) {
1699					*prev = ml->sml_next;
1700					ml->sml_next = NULL;
1701					slap_mods_free( ml, 1 );
1702					continue;
1703				}
1704			}
1705			prev = &ml->sml_next;
1706		}
1707
1708		/* If we're resetting the password, make sure grace, accountlock,
1709		 * and failure also get removed.
1710		 */
1711		if ( got_pw ) {
1712			if ( a_grace && !got_del_grace ) {
1713				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1714				ml->sml_op = LDAP_MOD_DELETE;
1715				ml->sml_flags = SLAP_MOD_INTERNAL;
1716				ml->sml_type.bv_val = NULL;
1717				ml->sml_desc = ad_pwdGraceUseTime;
1718				ml->sml_numvals = 0;
1719				ml->sml_values = NULL;
1720				ml->sml_nvalues = NULL;
1721				ml->sml_next = NULL;
1722				*prev = ml;
1723				prev = &ml->sml_next;
1724			}
1725			if ( a_lock && !got_del_lock ) {
1726				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1727				ml->sml_op = LDAP_MOD_DELETE;
1728				ml->sml_flags = SLAP_MOD_INTERNAL;
1729				ml->sml_type.bv_val = NULL;
1730				ml->sml_desc = ad_pwdAccountLockedTime;
1731				ml->sml_numvals = 0;
1732				ml->sml_values = NULL;
1733				ml->sml_nvalues = NULL;
1734				ml->sml_next = NULL;
1735				*prev = ml;
1736			}
1737			if ( a_fail && !got_del_fail ) {
1738				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1739				ml->sml_op = LDAP_MOD_DELETE;
1740				ml->sml_flags = SLAP_MOD_INTERNAL;
1741				ml->sml_type.bv_val = NULL;
1742				ml->sml_desc = ad_pwdFailureTime;
1743				ml->sml_numvals = 0;
1744				ml->sml_values = NULL;
1745				ml->sml_nvalues = NULL;
1746				ml->sml_next = NULL;
1747				*prev = ml;
1748			}
1749		}
1750		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1751		be_entry_release_r( op, e );
1752		return SLAP_CB_CONTINUE;
1753	}
1754
1755	/* Did we receive a password policy request control? */
1756	if ( op->o_ctrlflag[ppolicy_cid] ) {
1757		send_ctrl = 1;
1758	}
1759
1760	/* See if this is a pwdModify exop. If so, we can
1761	 * access the plaintext passwords from that request.
1762	 */
1763	{
1764		slap_callback *sc;
1765
1766		for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1767			if ( sc->sc_response == slap_null_cb &&
1768				sc->sc_private ) {
1769				req_pwdexop_s *qpw = sc->sc_private;
1770				newpw = qpw->rs_new;
1771				oldpw = qpw->rs_old;
1772				is_pwdexop = 1;
1773			   	break;
1774			}
1775		}
1776	}
1777
1778	ppolicy_get( op, e, &pp );
1779
1780	for ( ml = op->orm_modlist,
1781			pwmod = 0, mod_pw_only = 1,
1782			deladd = 0, delmod = NULL,
1783			addmod = NULL,
1784			zapReset = 1;
1785		ml != NULL; modtail = ml, ml = ml->sml_next )
1786	{
1787		if ( ml->sml_desc == pp.ad ) {
1788			pwmod = 1;
1789			pwmop = ml->sml_op;
1790			if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1791				(ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
1792			{
1793				deladd = 1;
1794				delmod = ml;
1795			}
1796
1797			if ((ml->sml_op == LDAP_MOD_ADD) ||
1798				(ml->sml_op == LDAP_MOD_REPLACE))
1799			{
1800				if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
1801					if ( deladd == 1 )
1802						deladd = 2;
1803
1804					/* FIXME: there's no easy way to ensure
1805					 * that add does not cause multiple
1806					 * userPassword values; one way (that
1807					 * would be consistent with the single
1808					 * password constraint) would be to turn
1809					 * add into replace); another would be
1810					 * to disallow add.
1811					 *
1812					 * Let's check at least that a single value
1813					 * is being added
1814					 */
1815					if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
1816						rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1817						rs->sr_text = "Password policy only allows one password value";
1818						goto return_results;
1819					}
1820
1821					addmod = ml;
1822				} else {
1823					/* replace can have no values, add cannot */
1824					assert( ml->sml_op == LDAP_MOD_REPLACE );
1825				}
1826			}
1827
1828		} else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
1829			mod_pw_only = 0;
1830			/* modifying something other than password */
1831		}
1832
1833		/*
1834		 * If there is a request to explicitly add a pwdReset
1835		 * attribute, then we suppress the normal behaviour on
1836		 * password change, which is to remove the pwdReset
1837		 * attribute.
1838		 *
1839		 * This enables an administrator to assign a new password
1840		 * and place a "must reset" flag on the entry, which will
1841		 * stay until the user explicitly changes his/her password.
1842		 */
1843		if (ml->sml_desc == ad_pwdReset ) {
1844			if ((ml->sml_op == LDAP_MOD_ADD) ||
1845				(ml->sml_op == LDAP_MOD_REPLACE))
1846				zapReset = 0;
1847		}
1848		if ( ml->sml_op == LDAP_MOD_DELETE ) {
1849			if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1850				got_del_grace = 1;
1851			} else if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1852				got_del_lock = 1;
1853			} else if ( ml->sml_desc == ad_pwdFailureTime ) {
1854				got_del_fail = 1;
1855			}
1856		}
1857		if ( ml->sml_desc == ad_pwdChangedTime ) {
1858			got_changed = 1;
1859		} else if (ml->sml_desc == ad_pwdHistory ) {
1860			got_history = 1;
1861		}
1862	}
1863
1864	if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
1865		if ( dn_match( &op->o_conn->c_ndn,
1866				&pwcons[op->o_conn->c_conn_idx].dn )) {
1867			Debug( LDAP_DEBUG_TRACE,
1868				"connection restricted to password changing only\n", 0, 0, 0 );
1869			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1870			rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1871			pErr = PP_changeAfterReset;
1872			goto return_results;
1873		} else {
1874			ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1875			BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1876		}
1877	}
1878
1879	/*
1880	 * if we have a "safe password modify policy", then we need to check if we're doing
1881	 * a delete (with the old password), followed by an add (with the new password).
1882	 *
1883	 * If we got just a delete with nothing else, just let it go. We also skip all the checks if
1884	 * the root user is bound. Root can do anything, including avoid the policies.
1885	 */
1886
1887	if (!pwmod) goto do_modify;
1888
1889	/*
1890	 * Build the password history list in ascending time order
1891	 * We need this, even if the user is root, in order to maintain
1892	 * the pwdHistory operational attributes properly.
1893	 */
1894	if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1895		struct berval oldpw;
1896		time_t oldtime;
1897
1898		for(i=0; ha->a_nvals[i].bv_val; i++) {
1899			rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1900				&oldtime, &oldpw );
1901
1902			if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1903
1904			if (oldpw.bv_val) {
1905				add_to_pwd_history( &tl, oldtime, &oldpw,
1906					&(ha->a_nvals[i]) );
1907				oldpw.bv_val = NULL;
1908				oldpw.bv_len = 0;
1909			}
1910		}
1911		for(p=tl; p; p=p->next, hsize++); /* count history size */
1912	}
1913
1914	if (be_isroot( op )) goto do_modify;
1915
1916	/* NOTE: according to draft-behera-ldap-password-policy
1917	 * pwdAllowUserChange == FALSE must only prevent pwd changes
1918	 * by the user the pwd belongs to (ITS#7021) */
1919	if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) {
1920		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1921		rs->sr_text = "User alteration of password is not allowed";
1922		pErr = PP_passwordModNotAllowed;
1923		goto return_results;
1924	}
1925
1926	/* Just deleting? */
1927	if (!addmod) {
1928		/* skip everything else */
1929		pwmod = 0;
1930		goto do_modify;
1931	}
1932
1933	/* This is a pwdModify exop that provided the old pw.
1934	 * We need to create a Delete mod for this old pw and
1935	 * let the matching value get found later
1936	 */
1937	if (pp.pwdSafeModify && oldpw.bv_val ) {
1938		ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
1939		ml->sml_op = LDAP_MOD_DELETE;
1940		ml->sml_flags = SLAP_MOD_INTERNAL;
1941		ml->sml_desc = pp.ad;
1942		ml->sml_type = pp.ad->ad_cname;
1943		ml->sml_numvals = 1;
1944		ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1945		ber_dupbv( &ml->sml_values[0], &oldpw );
1946		BER_BVZERO( &ml->sml_values[1] );
1947		ml->sml_next = op->orm_modlist;
1948		op->orm_modlist = ml;
1949		delmod = ml;
1950		deladd = 2;
1951	}
1952
1953	if (pp.pwdSafeModify && deladd != 2) {
1954		Debug( LDAP_DEBUG_TRACE,
1955			"change password must use DELETE followed by ADD/REPLACE\n",
1956			0, 0, 0 );
1957		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1958		rs->sr_text = "Must supply old password to be changed as well as new one";
1959		pErr = PP_mustSupplyOldPassword;
1960		goto return_results;
1961	}
1962
1963	/* Check age, but only if pwdReset is not TRUE */
1964	pa = attr_find( e->e_attrs, ad_pwdReset );
1965	if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
1966		pp.pwdMinAge > 0) {
1967		time_t pwtime = (time_t)-1, now;
1968		int age;
1969
1970		if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1971			pwtime = parse_time( pa->a_nvals[0].bv_val );
1972		now = slap_get_time();
1973		age = (int)(now - pwtime);
1974		if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1975			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1976			rs->sr_text = "Password is too young to change";
1977			pErr = PP_passwordTooYoung;
1978			goto return_results;
1979		}
1980	}
1981
1982	/* pa is used in password history check below, be sure it's set */
1983	if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1984		/*
1985		 * we have a password to check
1986		 */
1987		bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1988		/* FIXME: no access checking? */
1989		rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1990		if (rc != LDAP_SUCCESS) {
1991			Debug( LDAP_DEBUG_TRACE,
1992				"old password check failed: %s\n", txt, 0, 0 );
1993
1994			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1995			rs->sr_text = "Must supply correct old password to change to new one";
1996			pErr = PP_mustSupplyOldPassword;
1997			goto return_results;
1998
1999		} else {
2000			int i;
2001
2002			/*
2003			 * replace the delete value with the (possibly hashed)
2004			 * value which is currently in the password.
2005			 */
2006			for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
2007				free( delmod->sml_values[i].bv_val );
2008				BER_BVZERO( &delmod->sml_values[i] );
2009			}
2010			free( delmod->sml_values );
2011			delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
2012			BER_BVZERO( &delmod->sml_values[1] );
2013			ber_dupbv( &(delmod->sml_values[0]),  &(pa->a_nvals[0]) );
2014		}
2015	}
2016
2017	bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
2018	if (pp.pwdCheckQuality > 0) {
2019
2020		rc = check_password_quality( bv, &pp, &pErr, e, (char **)&txt );
2021		if (rc != LDAP_SUCCESS) {
2022			rs->sr_err = rc;
2023			if ( txt ) {
2024				rs->sr_text = txt;
2025				free_txt = 1;
2026			} else {
2027				rs->sr_text = "Password fails quality checking policy";
2028			}
2029			goto return_results;
2030		}
2031	}
2032
2033	/* If pwdInHistory is zero, passwords may be reused */
2034	if (pa && pp.pwdInHistory > 0) {
2035		/*
2036		 * Last check - the password history.
2037		 */
2038		/* FIXME: no access checking? */
2039		if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
2040			/*
2041			 * This is bad - it means that the user is attempting
2042			 * to set the password to the same as the old one.
2043			 */
2044			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2045			rs->sr_text = "Password is not being changed from existing value";
2046			pErr = PP_passwordInHistory;
2047			goto return_results;
2048		}
2049
2050		/* We need this when reduce pwdInHistory */
2051		hskip = hsize - pp.pwdInHistory;
2052
2053		/*
2054		 * Iterate through the password history, and fail on any
2055		 * password matches.
2056		 */
2057		at = *pa;
2058		at.a_vals = cr;
2059		cr[1].bv_val = NULL;
2060		for(p=tl; p; p=p->next) {
2061			if(hskip > 0){
2062				hskip--;
2063				continue;
2064			}
2065			cr[0] = p->pw;
2066			/* FIXME: no access checking? */
2067			rc = slap_passwd_check( op, NULL, &at, bv, &txt );
2068
2069			if (rc != LDAP_SUCCESS) continue;
2070
2071			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2072			rs->sr_text = "Password is in history of old passwords";
2073			pErr = PP_passwordInHistory;
2074			goto return_results;
2075		}
2076	}
2077
2078do_modify:
2079	if (pwmod) {
2080		struct berval timestamp;
2081		char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
2082		time_t now = slap_get_time();
2083
2084		/* If the conn is restricted, set a callback to clear it
2085		 * if the pwmod succeeds
2086		 */
2087		if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
2088			slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
2089				op->o_tmpmemctx );
2090			sc->sc_next = op->o_callback;
2091			/* Must use sc_response to insure we reset on success, before
2092			 * the client sees the response. Must use sc_cleanup to insure
2093			 * that it gets cleaned up if sc_response is not called.
2094			 */
2095			sc->sc_response = ppolicy_mod_cb;
2096			sc->sc_cleanup = ppolicy_mod_cb;
2097			op->o_callback = sc;
2098		}
2099
2100		/*
2101		 * keep the necessary pwd.. operational attributes
2102		 * up to date.
2103		 */
2104
2105		if (!got_changed) {
2106			timestamp.bv_val = timebuf;
2107			timestamp.bv_len = sizeof(timebuf);
2108			slap_timestamp( &now, &timestamp );
2109
2110			mods = NULL;
2111			if (pwmop != LDAP_MOD_DELETE) {
2112				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2113				mods->sml_op = LDAP_MOD_REPLACE;
2114				mods->sml_numvals = 1;
2115				mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
2116				ber_dupbv( &mods->sml_values[0], &timestamp );
2117				BER_BVZERO( &mods->sml_values[1] );
2118				assert( !BER_BVISNULL( &mods->sml_values[0] ) );
2119			} else if (attr_find(e->e_attrs, ad_pwdChangedTime )) {
2120				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2121				mods->sml_op = LDAP_MOD_DELETE;
2122			}
2123			if (mods) {
2124				mods->sml_desc = ad_pwdChangedTime;
2125				mods->sml_flags = SLAP_MOD_INTERNAL;
2126				mods->sml_next = NULL;
2127				modtail->sml_next = mods;
2128				modtail = mods;
2129			}
2130		}
2131
2132		if (!got_del_grace && attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
2133			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2134			mods->sml_op = LDAP_MOD_DELETE;
2135			mods->sml_desc = ad_pwdGraceUseTime;
2136			mods->sml_flags = SLAP_MOD_INTERNAL;
2137			mods->sml_next = NULL;
2138			modtail->sml_next = mods;
2139			modtail = mods;
2140		}
2141
2142		if (!got_del_lock && attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
2143			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2144			mods->sml_op = LDAP_MOD_DELETE;
2145			mods->sml_desc = ad_pwdAccountLockedTime;
2146			mods->sml_flags = SLAP_MOD_INTERNAL;
2147			mods->sml_next = NULL;
2148			modtail->sml_next = mods;
2149			modtail = mods;
2150		}
2151
2152		if (!got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
2153			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2154			mods->sml_op = LDAP_MOD_DELETE;
2155			mods->sml_desc = ad_pwdFailureTime;
2156			mods->sml_flags = SLAP_MOD_INTERNAL;
2157			mods->sml_next = NULL;
2158			modtail->sml_next = mods;
2159			modtail = mods;
2160		}
2161
2162		/* Delete the pwdReset attribute, since it's being reset */
2163		if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
2164			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2165			mods->sml_op = LDAP_MOD_DELETE;
2166			mods->sml_desc = ad_pwdReset;
2167			mods->sml_flags = SLAP_MOD_INTERNAL;
2168			mods->sml_next = NULL;
2169			modtail->sml_next = mods;
2170			modtail = mods;
2171		}
2172
2173		/* Delete all pwdInHistory attribute */
2174		if (!got_history && pp.pwdInHistory == 0 &&
2175            attr_find(e->e_attrs, ad_pwdHistory )){
2176			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2177			mods->sml_op = LDAP_MOD_DELETE;
2178			mods->sml_flags = SLAP_MOD_INTERNAL;
2179			mods->sml_desc = ad_pwdHistory;
2180			mods->sml_next = NULL;
2181			modtail->sml_next = mods;
2182			modtail = mods;
2183		}
2184
2185		if (!got_history && pp.pwdInHistory > 0){
2186			if (hsize >= pp.pwdInHistory) {
2187				/*
2188				 * We use the >= operator, since we are going to add
2189				 * the existing password attribute value into the
2190				 * history - thus the cardinality of history values is
2191				 * about to rise by one.
2192				 *
2193				 * If this would push it over the limit of history
2194				 * values (remembering - the password policy could have
2195				 * changed since the password was last altered), we must
2196				 * delete at least 1 value from the pwdHistory list.
2197				 *
2198				 * In fact, we delete '(#pwdHistory attrs - max pwd
2199				 * history length) + 1' values, starting with the oldest.
2200				 * This is easily evaluated, since the linked list is
2201				 * created in ascending time order.
2202				 */
2203				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2204				mods->sml_op = LDAP_MOD_DELETE;
2205				mods->sml_flags = SLAP_MOD_INTERNAL;
2206				mods->sml_desc = ad_pwdHistory;
2207				mods->sml_numvals = hsize - pp.pwdInHistory + 1;
2208				mods->sml_values = ch_calloc( sizeof( struct berval ),
2209					hsize - pp.pwdInHistory + 2 );
2210				BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
2211				for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
2212					BER_BVZERO( &mods->sml_values[i] );
2213					ber_dupbv( &(mods->sml_values[i]), &p->bv );
2214				}
2215				mods->sml_next = NULL;
2216				modtail->sml_next = mods;
2217				modtail = mods;
2218			}
2219			free_pwd_history_list( &tl );
2220
2221			/*
2222			 * Now add the existing password into the history list.
2223			 * This will be executed even if the operation is to delete
2224			 * the password entirely.
2225			 *
2226			 * This isn't in the spec explicitly, but it seems to make
2227			 * sense that the password history list is the list of all
2228			 * previous passwords - even if they were deleted. Thus, if
2229			 * someone tries to add a historical password at some future
2230			 * point, it will fail.
2231			 */
2232			if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
2233				mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
2234				mods->sml_op = LDAP_MOD_ADD;
2235				mods->sml_flags = SLAP_MOD_INTERNAL;
2236				mods->sml_type.bv_val = NULL;
2237				mods->sml_desc = ad_pwdHistory;
2238				mods->sml_nvalues = NULL;
2239				mods->sml_numvals = 1;
2240				mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
2241				mods->sml_values[ 1 ].bv_val = NULL;
2242				mods->sml_values[ 1 ].bv_len = 0;
2243				make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
2244				mods->sml_next = NULL;
2245				modtail->sml_next = mods;
2246				modtail = mods;
2247
2248			} else {
2249				Debug( LDAP_DEBUG_TRACE,
2250				"ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
2251			}
2252		}
2253
2254		/*
2255		 * Controversial bit here. If the new password isn't hashed
2256		 * (ie, is cleartext), we probably should hash it according
2257		 * to the default hash. The reason for this is that we want
2258		 * to use the policy if possible, but if we hash the password
2259		 * before, then we're going to run into trouble when it
2260		 * comes time to check the password.
2261		 *
2262		 * Now, the right thing to do is to use the extended password
2263		 * modify operation, but not all software can do this,
2264		 * therefore it makes sense to hash the new password, now
2265		 * we know it passes the policy requirements.
2266		 *
2267		 * Of course, if the password is already hashed, then we
2268		 * leave it alone.
2269		 */
2270
2271		if ((pi->hash_passwords) && (addmod) && !newpw.bv_val &&
2272			(password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
2273		{
2274			struct berval hpw, bv;
2275
2276			slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
2277			if (hpw.bv_val == NULL) {
2278					/*
2279					 * hashing didn't work. Emit an error.
2280					 */
2281				rs->sr_err = LDAP_OTHER;
2282				rs->sr_text = txt;
2283				goto return_results;
2284			}
2285			bv = addmod->sml_values[0];
2286				/* clear and discard the clear password */
2287			memset(bv.bv_val, 0, bv.bv_len);
2288			ber_memfree(bv.bv_val);
2289			addmod->sml_values[0] = hpw;
2290		}
2291	}
2292	op->o_bd->bd_info = (BackendInfo *)on->on_info;
2293	be_entry_release_r( op, e );
2294	return SLAP_CB_CONTINUE;
2295
2296return_results:
2297	free_pwd_history_list( &tl );
2298	op->o_bd->bd_info = (BackendInfo *)on->on_info;
2299	be_entry_release_r( op, e );
2300	if ( send_ctrl ) {
2301		ctrl = create_passcontrol( op, -1, -1, pErr );
2302		oldctrls = add_passcontrol( op, rs, ctrl );
2303	}
2304	send_ldap_result( op, rs );
2305	if ( free_txt ) {
2306		free( (char *)txt );
2307		rs->sr_text = NULL;
2308	}
2309	if ( send_ctrl ) {
2310		if ( is_pwdexop ) {
2311			if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
2312				op->o_tmpfree( oldctrls, op->o_tmpmemctx );
2313			}
2314			oldctrls = NULL;
2315			rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
2316
2317		} else {
2318			ctrls_cleanup( op, rs, oldctrls );
2319		}
2320	}
2321	return rs->sr_err;
2322}
2323
2324static int
2325ppolicy_parseCtrl(
2326	Operation *op,
2327	SlapReply *rs,
2328	LDAPControl *ctrl )
2329{
2330	if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
2331		rs->sr_text = "passwordPolicyRequest control value not absent";
2332		return LDAP_PROTOCOL_ERROR;
2333	}
2334	op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
2335		? SLAP_CONTROL_CRITICAL
2336		: SLAP_CONTROL_NONCRITICAL;
2337
2338	return LDAP_SUCCESS;
2339}
2340
2341static int
2342attrPretty(
2343	Syntax *syntax,
2344	struct berval *val,
2345	struct berval *out,
2346	void *ctx )
2347{
2348	AttributeDescription *ad = NULL;
2349	const char *err;
2350	int code;
2351
2352	code = slap_bv2ad( val, &ad, &err );
2353	if ( !code ) {
2354		ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
2355	}
2356	return code;
2357}
2358
2359static int
2360attrNormalize(
2361	slap_mask_t use,
2362	Syntax *syntax,
2363	MatchingRule *mr,
2364	struct berval *val,
2365	struct berval *out,
2366	void *ctx )
2367{
2368	AttributeDescription *ad = NULL;
2369	const char *err;
2370	int code;
2371
2372	code = slap_bv2ad( val, &ad, &err );
2373	if ( !code ) {
2374		ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
2375	}
2376	return code;
2377}
2378
2379static int
2380ppolicy_db_init(
2381	BackendDB *be,
2382	ConfigReply *cr
2383)
2384{
2385	slap_overinst *on = (slap_overinst *) be->bd_info;
2386
2387	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
2388		/* do not allow slapo-ppolicy to be global by now (ITS#5858) */
2389		if ( cr ){
2390			snprintf( cr->msg, sizeof(cr->msg),
2391				"slapo-ppolicy cannot be global" );
2392			Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 );
2393		}
2394		return 1;
2395	}
2396
2397	/* Has User Schema been initialized yet? */
2398	if ( !pwd_UsSchema[0].ad[0] ) {
2399		const char *err;
2400		int i, code;
2401
2402		for (i=0; pwd_UsSchema[i].def; i++) {
2403			code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
2404			if ( code ) {
2405				if ( cr ){
2406					snprintf( cr->msg, sizeof(cr->msg),
2407						"User Schema load failed for attribute \"%s\". Error code %d: %s",
2408						pwd_UsSchema[i].def, code, err );
2409					Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 );
2410				}
2411				return code;
2412			}
2413		}
2414		{
2415			Syntax *syn;
2416			MatchingRule *mr;
2417
2418			syn = ch_malloc( sizeof( Syntax ));
2419			*syn = *ad_pwdAttribute->ad_type->sat_syntax;
2420			syn->ssyn_pretty = attrPretty;
2421			ad_pwdAttribute->ad_type->sat_syntax = syn;
2422
2423			mr = ch_malloc( sizeof( MatchingRule ));
2424			*mr = *ad_pwdAttribute->ad_type->sat_equality;
2425			mr->smr_normalize = attrNormalize;
2426			ad_pwdAttribute->ad_type->sat_equality = mr;
2427		}
2428	}
2429
2430	on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
2431
2432	if ( dtblsize && !pwcons ) {
2433		/* accommodate for c_conn_idx == -1 */
2434		pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
2435		pwcons++;
2436	}
2437
2438	ov_count++;
2439
2440	return 0;
2441}
2442
2443static int
2444ppolicy_db_open(
2445	BackendDB *be,
2446	ConfigReply *cr
2447)
2448{
2449	return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
2450}
2451
2452static int
2453ppolicy_db_close(
2454	BackendDB *be,
2455	ConfigReply *cr
2456)
2457{
2458#ifdef SLAP_CONFIG_DELETE
2459	overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
2460#endif /* SLAP_CONFIG_DELETE */
2461
2462	return 0;
2463}
2464
2465static int
2466ppolicy_db_destroy(
2467	BackendDB *be,
2468	ConfigReply *cr
2469)
2470{
2471	slap_overinst *on = (slap_overinst *) be->bd_info;
2472	pp_info *pi = on->on_bi.bi_private;
2473
2474	on->on_bi.bi_private = NULL;
2475	free( pi->def_policy.bv_val );
2476	free( pi );
2477
2478	ov_count--;
2479	if ( ov_count <=0 && pwcons ) {
2480		pw_conn *pwc = pwcons;
2481		pwcons = NULL;
2482		pwc--;
2483		ch_free( pwc );
2484	}
2485	return 0;
2486}
2487
2488static char *extops[] = {
2489	LDAP_EXOP_MODIFY_PASSWD,
2490	NULL
2491};
2492
2493static slap_overinst ppolicy;
2494
2495int ppolicy_initialize()
2496{
2497	int i, code;
2498
2499	for (i=0; pwd_OpSchema[i].def; i++) {
2500		code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
2501		if ( code ) {
2502			Debug( LDAP_DEBUG_ANY,
2503				"ppolicy_initialize: register_at failed\n", 0, 0, 0 );
2504			return code;
2505		}
2506		/* Allow Manager to set these as needed */
2507		if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
2508			(*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
2509				SLAP_AT_MANAGEABLE;
2510		}
2511	}
2512
2513	code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
2514		SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
2515		ppolicy_parseCtrl, &ppolicy_cid );
2516	if ( code != LDAP_SUCCESS ) {
2517		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code, 0, 0 );
2518		return code;
2519	}
2520
2521	ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
2522
2523	ppolicy.on_bi.bi_type = "ppolicy";
2524	ppolicy.on_bi.bi_db_init = ppolicy_db_init;
2525	ppolicy.on_bi.bi_db_open = ppolicy_db_open;
2526	ppolicy.on_bi.bi_db_close = ppolicy_db_close;
2527	ppolicy.on_bi.bi_db_destroy = ppolicy_db_destroy;
2528
2529	ppolicy.on_bi.bi_op_add = ppolicy_add;
2530	ppolicy.on_bi.bi_op_bind = ppolicy_bind;
2531	ppolicy.on_bi.bi_op_compare = ppolicy_compare;
2532	ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
2533	ppolicy.on_bi.bi_op_modify = ppolicy_modify;
2534	ppolicy.on_bi.bi_op_search = ppolicy_restrict;
2535	ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
2536
2537	ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
2538	code = config_register_schema( ppolicycfg, ppolicyocs );
2539	if ( code ) return code;
2540
2541	return overlay_register( &ppolicy );
2542}
2543
2544#if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
2545int init_module(int argc, char *argv[]) {
2546	return ppolicy_initialize();
2547}
2548#endif
2549
2550#endif	/* defined(SLAPD_OVER_PPOLICY) */
2551