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