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