Home | History | Annotate | Line # | Download | only in overlays
      1  1.3  christos /*	$NetBSD: ppolicy.c,v 1.4 2025/09/05 21:16:32 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.4  christos  * Copyright 2004-2024 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.3  christos __RCSID("$NetBSD: ppolicy.c,v 1.4 2025/09/05 21:16:32 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.3  christos #include "slap-config.h"
     48  1.1     lukem 
     49  1.2  christos #ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE
     50  1.2  christos #define PPOLICY_DEFAULT_MAXRECORDED_FAILURE	5
     51  1.2  christos #endif
     52  1.2  christos 
     53  1.4  christos 		/* External password quality checking function.
     54  1.4  christos 		 * The error message must have a preallocated buffer and size
     55  1.4  christos 		 * passed in. Module can still allocate a buffer for
     56  1.4  christos 		 * it if the provided one is too small.
     57  1.4  christos 		 */
     58  1.4  christos typedef	int (check_func)( char *passwd, struct berval *errmsg, Entry *ent, struct berval *arg );
     59  1.4  christos #define ERRBUFSIZ	256
     60  1.4  christos 
     61  1.1     lukem /* Per-instance configuration information */
     62  1.1     lukem typedef struct pp_info {
     63  1.1     lukem 	struct berval def_policy;	/* DN of default policy subentry */
     64  1.1     lukem 	int use_lockout;		/* send AccountLocked result? */
     65  1.1     lukem 	int hash_passwords;		/* transparently hash cleartext pwds */
     66  1.2  christos 	int forward_updates;	/* use frontend for policy state updates */
     67  1.3  christos 	int disable_write;
     68  1.3  christos 	int send_netscape_controls;	/* send netscape password controls */
     69  1.4  christos 	char *pwdCheckModule; /* name of module to dynamically
     70  1.4  christos 										    load to check password */
     71  1.4  christos #ifdef SLAPD_MODULES
     72  1.4  christos 	lt_dlhandle	pwdCheckHandle;		/* handle from lt_dlopen */
     73  1.4  christos 	check_func *pwdCheckFunc;
     74  1.4  christos #endif /* SLAPD_MODULES */
     75  1.3  christos 	ldap_pvt_thread_mutex_t pwdFailureTime_mutex;
     76  1.1     lukem } pp_info;
     77  1.1     lukem 
     78  1.1     lukem /* Our per-connection info - note, it is not per-instance, it is
     79  1.1     lukem  * used by all instances
     80  1.1     lukem  */
     81  1.1     lukem typedef struct pw_conn {
     82  1.1     lukem 	struct berval dn;	/* DN of restricted user */
     83  1.1     lukem } pw_conn;
     84  1.1     lukem 
     85  1.1     lukem static pw_conn *pwcons;
     86  1.1     lukem static int ppolicy_cid;
     87  1.3  christos static int account_usability_cid;
     88  1.1     lukem static int ov_count;
     89  1.1     lukem 
     90  1.1     lukem typedef struct pass_policy {
     91  1.1     lukem 	AttributeDescription *ad; /* attribute to which the policy applies */
     92  1.1     lukem 	int pwdMinAge; /* minimum time (seconds) until passwd can change */
     93  1.1     lukem 	int pwdMaxAge; /* time in seconds until pwd will expire after change */
     94  1.3  christos 	int pwdMaxIdle; /* number of seconds since last successful bind before
     95  1.3  christos 					   passwd gets locked out */
     96  1.1     lukem 	int pwdInHistory; /* number of previous passwords kept */
     97  1.1     lukem 	int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
     98  1.1     lukem 						   2 = check mandatory; fail if not possible */
     99  1.1     lukem 	int pwdMinLength; /* minimum number of chars in password */
    100  1.3  christos 	int pwdMaxLength; /* maximum number of chars in password */
    101  1.1     lukem 	int pwdExpireWarning; /* number of seconds that warning controls are
    102  1.1     lukem 							sent before a password expires */
    103  1.3  christos 	int pwdGraceExpiry; /* number of seconds after expiry grace logins are
    104  1.3  christos 						   valid */
    105  1.1     lukem 	int pwdGraceAuthNLimit; /* number of times you can log in with an
    106  1.1     lukem 							expired password */
    107  1.1     lukem 	int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
    108  1.1     lukem 	int pwdLockoutDuration; /* time in seconds a password is locked out for */
    109  1.3  christos 	int pwdMinDelay; /* base bind delay in seconds on failure */
    110  1.3  christos 	int pwdMaxDelay; /* maximum bind delay in seconds */
    111  1.1     lukem 	int pwdMaxFailure; /* number of failed binds allowed before lockout */
    112  1.2  christos 	int pwdMaxRecordedFailure;	/* number of failed binds to store */
    113  1.1     lukem 	int pwdFailureCountInterval; /* number of seconds before failure
    114  1.1     lukem 									counts are zeroed */
    115  1.1     lukem 	int pwdMustChange; /* 0 = users can use admin set password
    116  1.1     lukem 							1 = users must change password after admin set */
    117  1.1     lukem 	int pwdAllowUserChange; /* 0 = users cannot change their passwords
    118  1.1     lukem 								1 = users can change them */
    119  1.1     lukem 	int pwdSafeModify; /* 0 = old password doesn't need to come
    120  1.1     lukem 								with password change request
    121  1.1     lukem 							1 = password change must supply existing pwd */
    122  1.4  christos 	int pwdUseCheckModule; /* 0 = do not use password check module, 1 = use */
    123  1.3  christos 	struct berval pwdCheckModuleArg; /* Optional argument to the password check
    124  1.3  christos 										module */
    125  1.1     lukem } PassPolicy;
    126  1.1     lukem 
    127  1.1     lukem typedef struct pw_hist {
    128  1.1     lukem 	time_t t;	/* timestamp of history entry */
    129  1.1     lukem 	struct berval pw;	/* old password hash */
    130  1.1     lukem 	struct berval bv;	/* text of entire entry */
    131  1.1     lukem 	struct pw_hist *next;
    132  1.1     lukem } pw_hist;
    133  1.1     lukem 
    134  1.1     lukem /* Operational attributes */
    135  1.1     lukem static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
    136  1.1     lukem 	*ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
    137  1.3  christos 	*ad_pwdPolicySubentry, *ad_pwdStartTime, *ad_pwdEndTime,
    138  1.3  christos 	*ad_pwdLastSuccess, *ad_pwdAccountTmpLockoutEnd;
    139  1.3  christos 
    140  1.3  christos /* Policy attributes */
    141  1.3  christos static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdMaxIdle,
    142  1.3  christos 	*ad_pwdInHistory, *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxLength,
    143  1.3  christos 	*ad_pwdMaxFailure, *ad_pwdGraceExpiry, *ad_pwdGraceAuthNLimit,
    144  1.3  christos 	*ad_pwdExpireWarning, *ad_pwdMinDelay, *ad_pwdMaxDelay,
    145  1.3  christos 	*ad_pwdLockoutDuration, *ad_pwdFailureCountInterval,
    146  1.4  christos 	*ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdUseCheckModule, *ad_pwdLockout,
    147  1.3  christos 	*ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
    148  1.3  christos 	*ad_pwdAttribute, *ad_pwdMaxRecordedFailure;
    149  1.1     lukem 
    150  1.1     lukem static struct schema_info {
    151  1.1     lukem 	char *def;
    152  1.1     lukem 	AttributeDescription **ad;
    153  1.1     lukem } pwd_OpSchema[] = {
    154  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.16 "
    155  1.1     lukem 		"NAME ( 'pwdChangedTime' ) "
    156  1.1     lukem 		"DESC 'The time the password was last changed' "
    157  1.1     lukem 		"EQUALITY generalizedTimeMatch "
    158  1.1     lukem 		"ORDERING generalizedTimeOrderingMatch "
    159  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    160  1.3  christos 		"SINGLE-VALUE "
    161  1.3  christos 		"NO-USER-MODIFICATION "
    162  1.3  christos 		"USAGE directoryOperation )",
    163  1.1     lukem 		&ad_pwdChangedTime },
    164  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.17 "
    165  1.1     lukem 		"NAME ( 'pwdAccountLockedTime' ) "
    166  1.1     lukem 		"DESC 'The time an user account was locked' "
    167  1.1     lukem 		"EQUALITY generalizedTimeMatch "
    168  1.1     lukem 		"ORDERING generalizedTimeOrderingMatch "
    169  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    170  1.1     lukem 		"SINGLE-VALUE "
    171  1.4  christos #if 0 /* FIXME: ITS#9671 until we introduce a separate lockout flag? */
    172  1.1     lukem 		"NO-USER-MODIFICATION "
    173  1.4  christos #endif
    174  1.1     lukem 		"USAGE directoryOperation )",
    175  1.1     lukem 		&ad_pwdAccountLockedTime },
    176  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.19 "
    177  1.1     lukem 		"NAME ( 'pwdFailureTime' ) "
    178  1.1     lukem 		"DESC 'The timestamps of the last consecutive authentication failures' "
    179  1.1     lukem 		"EQUALITY generalizedTimeMatch "
    180  1.1     lukem 		"ORDERING generalizedTimeOrderingMatch "
    181  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    182  1.3  christos 		"NO-USER-MODIFICATION "
    183  1.3  christos 		"USAGE directoryOperation )",
    184  1.1     lukem 		&ad_pwdFailureTime },
    185  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.20 "
    186  1.1     lukem 		"NAME ( 'pwdHistory' ) "
    187  1.1     lukem 		"DESC 'The history of users passwords' "
    188  1.1     lukem 		"EQUALITY octetStringMatch "
    189  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
    190  1.3  christos 		"NO-USER-MODIFICATION "
    191  1.3  christos 		"USAGE directoryOperation )",
    192  1.1     lukem 		&ad_pwdHistory },
    193  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.21 "
    194  1.1     lukem 		"NAME ( 'pwdGraceUseTime' ) "
    195  1.1     lukem 		"DESC 'The timestamps of the grace login once the password has expired' "
    196  1.1     lukem 		"EQUALITY generalizedTimeMatch "
    197  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    198  1.3  christos 		"NO-USER-MODIFICATION "
    199  1.3  christos 		"USAGE directoryOperation )",
    200  1.1     lukem 		&ad_pwdGraceUseTime },
    201  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.22 "
    202  1.1     lukem 		"NAME ( 'pwdReset' ) "
    203  1.1     lukem 		"DESC 'The indication that the password has been reset' "
    204  1.1     lukem 		"EQUALITY booleanMatch "
    205  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    206  1.3  christos 		"SINGLE-VALUE "
    207  1.3  christos 		"USAGE directoryOperation )",
    208  1.1     lukem 		&ad_pwdReset },
    209  1.1     lukem 	{	"( 1.3.6.1.4.1.42.2.27.8.1.23 "
    210  1.1     lukem 		"NAME ( 'pwdPolicySubentry' ) "
    211  1.1     lukem 		"DESC 'The pwdPolicy subentry in effect for this object' "
    212  1.1     lukem 		"EQUALITY distinguishedNameMatch "
    213  1.1     lukem 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
    214  1.1     lukem 		"SINGLE-VALUE "
    215  1.4  christos #if 0 /* ITS#9671: until we implement ITS#9343 or similar */
    216  1.1     lukem 		"NO-USER-MODIFICATION "
    217  1.4  christos #endif
    218  1.1     lukem 		"USAGE directoryOperation )",
    219  1.1     lukem 		&ad_pwdPolicySubentry },
    220  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.27 "
    221  1.3  christos 		"NAME ( 'pwdStartTime' ) "
    222  1.3  christos 		"DESC 'The time the password becomes enabled' "
    223  1.3  christos 		"EQUALITY generalizedTimeMatch "
    224  1.3  christos 		"ORDERING generalizedTimeOrderingMatch "
    225  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    226  1.3  christos 		"SINGLE-VALUE "
    227  1.3  christos 		"USAGE directoryOperation )",
    228  1.3  christos 		&ad_pwdStartTime },
    229  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.28 "
    230  1.3  christos 		"NAME ( 'pwdEndTime' ) "
    231  1.3  christos 		"DESC 'The time the password becomes disabled' "
    232  1.3  christos 		"EQUALITY generalizedTimeMatch "
    233  1.3  christos 		"ORDERING generalizedTimeOrderingMatch "
    234  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    235  1.3  christos 		"SINGLE-VALUE "
    236  1.3  christos 		"USAGE directoryOperation )",
    237  1.3  christos 		&ad_pwdEndTime },
    238  1.3  christos 	/* Defined in schema_prep.c now
    239  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.29 "
    240  1.3  christos 		"NAME ( 'pwdLastSuccess' ) "
    241  1.3  christos 		"DESC 'The timestamp of the last successful authentication' "
    242  1.3  christos 		"EQUALITY generalizedTimeMatch "
    243  1.3  christos 		"ORDERING generalizedTimeOrderingMatch "
    244  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    245  1.3  christos 		"SINGLE-VALUE "
    246  1.3  christos 		"NO-USER-MODIFICATION "
    247  1.3  christos 		"USAGE directoryOperation )",
    248  1.3  christos 		&ad_pwdLastSuccess },
    249  1.3  christos 	*/
    250  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.33 "
    251  1.3  christos 		"NAME ( 'pwdAccountTmpLockoutEnd' ) "
    252  1.3  christos 		"DESC 'Temporary lockout end' "
    253  1.3  christos 		"EQUALITY generalizedTimeMatch "
    254  1.3  christos 		"ORDERING generalizedTimeOrderingMatch "
    255  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
    256  1.3  christos 		"SINGLE-VALUE "
    257  1.3  christos 		"NO-USER-MODIFICATION "
    258  1.3  christos 		"USAGE directoryOperation )",
    259  1.3  christos 		&ad_pwdAccountTmpLockoutEnd },
    260  1.3  christos 
    261  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.1 "
    262  1.3  christos 		"NAME ( 'pwdAttribute' ) "
    263  1.3  christos 		"EQUALITY objectIdentifierMatch "
    264  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
    265  1.3  christos 		&ad_pwdAttribute },
    266  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.2 "
    267  1.3  christos 		"NAME ( 'pwdMinAge' ) "
    268  1.3  christos 		"EQUALITY integerMatch "
    269  1.3  christos 		"ORDERING integerOrderingMatch "
    270  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    271  1.3  christos 		"SINGLE-VALUE )",
    272  1.3  christos 		&ad_pwdMinAge },
    273  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.3 "
    274  1.3  christos 		"NAME ( 'pwdMaxAge' ) "
    275  1.3  christos 		"EQUALITY integerMatch "
    276  1.3  christos 		"ORDERING integerOrderingMatch "
    277  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    278  1.3  christos 		"SINGLE-VALUE )",
    279  1.3  christos 		&ad_pwdMaxAge },
    280  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.4 "
    281  1.3  christos 		"NAME ( 'pwdInHistory' ) "
    282  1.3  christos 		"EQUALITY integerMatch "
    283  1.3  christos 		"ORDERING integerOrderingMatch "
    284  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    285  1.3  christos 		"SINGLE-VALUE )",
    286  1.3  christos 		&ad_pwdInHistory },
    287  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.5 "
    288  1.3  christos 		"NAME ( 'pwdCheckQuality' ) "
    289  1.3  christos 		"EQUALITY integerMatch "
    290  1.3  christos 		"ORDERING integerOrderingMatch "
    291  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    292  1.3  christos 		"SINGLE-VALUE )",
    293  1.3  christos 		&ad_pwdCheckQuality },
    294  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.6 "
    295  1.3  christos 		"NAME ( 'pwdMinLength' ) "
    296  1.3  christos 		"EQUALITY integerMatch "
    297  1.3  christos 		"ORDERING integerOrderingMatch "
    298  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    299  1.3  christos 		"SINGLE-VALUE )",
    300  1.3  christos 		&ad_pwdMinLength },
    301  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.31 "
    302  1.3  christos 		"NAME ( 'pwdMaxLength' ) "
    303  1.3  christos 		"EQUALITY integerMatch "
    304  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    305  1.3  christos 		"SINGLE-VALUE )",
    306  1.3  christos 		&ad_pwdMaxLength },
    307  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.7 "
    308  1.3  christos 		"NAME ( 'pwdExpireWarning' ) "
    309  1.3  christos 		"EQUALITY integerMatch "
    310  1.3  christos 		"ORDERING integerOrderingMatch "
    311  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    312  1.3  christos 		"SINGLE-VALUE )",
    313  1.3  christos 		&ad_pwdExpireWarning },
    314  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.8 "
    315  1.3  christos 		"NAME ( 'pwdGraceAuthNLimit' ) "
    316  1.3  christos 		"EQUALITY integerMatch "
    317  1.3  christos 		"ORDERING integerOrderingMatch "
    318  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    319  1.3  christos 		"SINGLE-VALUE )",
    320  1.3  christos 		&ad_pwdGraceAuthNLimit },
    321  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.30 "
    322  1.3  christos 		"NAME ( 'pwdGraceExpiry' ) "
    323  1.3  christos 		"EQUALITY integerMatch "
    324  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    325  1.3  christos 		"SINGLE-VALUE )",
    326  1.3  christos 		&ad_pwdGraceExpiry },
    327  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.9 "
    328  1.3  christos 		"NAME ( 'pwdLockout' ) "
    329  1.3  christos 		"EQUALITY booleanMatch "
    330  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    331  1.3  christos 		"SINGLE-VALUE )",
    332  1.3  christos 		&ad_pwdLockout },
    333  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.10 "
    334  1.3  christos 		"NAME ( 'pwdLockoutDuration' ) "
    335  1.3  christos 		"EQUALITY integerMatch "
    336  1.3  christos 		"ORDERING integerOrderingMatch "
    337  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    338  1.3  christos 		"SINGLE-VALUE )",
    339  1.3  christos 		&ad_pwdLockoutDuration },
    340  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.11 "
    341  1.3  christos 		"NAME ( 'pwdMaxFailure' ) "
    342  1.3  christos 		"EQUALITY integerMatch "
    343  1.3  christos 		"ORDERING integerOrderingMatch "
    344  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    345  1.3  christos 		"SINGLE-VALUE )",
    346  1.3  christos 		&ad_pwdMaxFailure },
    347  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.12 "
    348  1.3  christos 		"NAME ( 'pwdFailureCountInterval' ) "
    349  1.3  christos 		"EQUALITY integerMatch "
    350  1.3  christos 		"ORDERING integerOrderingMatch "
    351  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    352  1.3  christos 		"SINGLE-VALUE )",
    353  1.3  christos 		&ad_pwdFailureCountInterval },
    354  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.13 "
    355  1.3  christos 		"NAME ( 'pwdMustChange' ) "
    356  1.3  christos 		"EQUALITY booleanMatch "
    357  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    358  1.3  christos 		"SINGLE-VALUE )",
    359  1.3  christos 		&ad_pwdMustChange },
    360  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.14 "
    361  1.3  christos 		"NAME ( 'pwdAllowUserChange' ) "
    362  1.3  christos 		"EQUALITY booleanMatch "
    363  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    364  1.3  christos 		"SINGLE-VALUE )",
    365  1.3  christos 		&ad_pwdAllowUserChange },
    366  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.15 "
    367  1.3  christos 		"NAME ( 'pwdSafeModify' ) "
    368  1.3  christos 		"EQUALITY booleanMatch "
    369  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    370  1.3  christos 		"SINGLE-VALUE )",
    371  1.3  christos 		&ad_pwdSafeModify },
    372  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.24 "
    373  1.3  christos 		"NAME ( 'pwdMinDelay' ) "
    374  1.3  christos 		"EQUALITY integerMatch "
    375  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    376  1.3  christos 		"SINGLE-VALUE )",
    377  1.3  christos 		&ad_pwdMinDelay },
    378  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.25 "
    379  1.3  christos 		"NAME ( 'pwdMaxDelay' ) "
    380  1.3  christos 		"EQUALITY integerMatch "
    381  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    382  1.3  christos 		"SINGLE-VALUE )",
    383  1.3  christos 		&ad_pwdMaxDelay },
    384  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.26 "
    385  1.3  christos 		"NAME ( 'pwdMaxIdle' ) "
    386  1.3  christos 		"EQUALITY integerMatch "
    387  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    388  1.3  christos 		"SINGLE-VALUE )",
    389  1.3  christos 		&ad_pwdMaxIdle },
    390  1.3  christos 	{	"( 1.3.6.1.4.1.42.2.27.8.1.32 "
    391  1.3  christos 		"NAME ( 'pwdMaxRecordedFailure' ) "
    392  1.3  christos 		"EQUALITY integerMatch "
    393  1.3  christos 		"ORDERING integerOrderingMatch "
    394  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    395  1.3  christos 		"SINGLE-VALUE )",
    396  1.3  christos 		&ad_pwdMaxRecordedFailure },
    397  1.3  christos 	{	"( 1.3.6.1.4.1.4754.1.99.1 "
    398  1.3  christos 		"NAME ( 'pwdCheckModule' ) "
    399  1.3  christos 		"EQUALITY caseExactIA5Match "
    400  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 "
    401  1.4  christos 		"DESC 'Obsolete, no longer used' "
    402  1.4  christos 		"OBSOLETE "
    403  1.3  christos 		"SINGLE-VALUE )",
    404  1.3  christos 		&ad_pwdCheckModule },
    405  1.3  christos 	{	"( 1.3.6.1.4.1.4754.1.99.2 "
    406  1.3  christos 		"NAME ( 'pwdCheckModuleArg' ) "
    407  1.3  christos 		"EQUALITY octetStringMatch "
    408  1.3  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
    409  1.3  christos 		"DESC 'Argument to pass to check_password() function' "
    410  1.3  christos 		"SINGLE-VALUE )",
    411  1.3  christos 		&ad_pwdCheckModuleArg },
    412  1.4  christos 	{	"( 1.3.6.1.4.1.4754.1.99.3 "
    413  1.4  christos 		"NAME ( 'pwdUseCheckModule' ) "
    414  1.4  christos 		"EQUALITY booleanMatch "
    415  1.4  christos 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
    416  1.4  christos 		"DESC 'Toggle use of the loaded pwdCheckModule' "
    417  1.4  christos 		"SINGLE-VALUE )",
    418  1.4  christos 		&ad_pwdUseCheckModule },
    419  1.3  christos 
    420  1.1     lukem 	{ NULL, NULL }
    421  1.1     lukem };
    422  1.1     lukem 
    423  1.3  christos static char *pwd_ocs[] = {
    424  1.3  christos 	"( 1.3.6.1.4.1.4754.2.99.1 "
    425  1.3  christos 		"NAME 'pwdPolicyChecker' "
    426  1.3  christos 		"SUP top "
    427  1.3  christos 		"AUXILIARY "
    428  1.4  christos 		"MAY ( pwdCheckModule $ pwdCheckModuleArg $ pwdUseCheckModule ) )" ,
    429  1.3  christos 	"( 1.3.6.1.4.1.42.2.27.8.2.1 "
    430  1.3  christos 		"NAME 'pwdPolicy' "
    431  1.3  christos 		"SUP top "
    432  1.3  christos 		"AUXILIARY "
    433  1.3  christos 		"MUST ( pwdAttribute ) "
    434  1.3  christos 		"MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdCheckQuality $ "
    435  1.3  christos 		"pwdMinLength $ pwdMaxLength $ pwdExpireWarning $ "
    436  1.3  christos 		"pwdGraceAuthNLimit $ pwdGraceExpiry $ pwdLockout $ "
    437  1.3  christos 		"pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ "
    438  1.3  christos 		"pwdMustChange $ pwdAllowUserChange $ pwdSafeModify $ "
    439  1.3  christos 		"pwdMinDelay $ pwdMaxDelay $ pwdMaxIdle $ "
    440  1.3  christos 		"pwdMaxRecordedFailure ) )",
    441  1.3  christos 	NULL
    442  1.1     lukem };
    443  1.1     lukem 
    444  1.1     lukem static ldap_pvt_thread_mutex_t chk_syntax_mutex;
    445  1.1     lukem 
    446  1.1     lukem enum {
    447  1.1     lukem 	PPOLICY_DEFAULT = 1,
    448  1.1     lukem 	PPOLICY_HASH_CLEARTEXT,
    449  1.3  christos 	PPOLICY_USE_LOCKOUT,
    450  1.3  christos 	PPOLICY_DISABLE_WRITE,
    451  1.4  christos 	PPOLICY_CHECK_MODULE,
    452  1.1     lukem };
    453  1.1     lukem 
    454  1.4  christos static ConfigDriver ppolicy_cf_default, ppolicy_cf_checkmod;
    455  1.1     lukem 
    456  1.1     lukem static ConfigTable ppolicycfg[] = {
    457  1.1     lukem 	{ "ppolicy_default", "policyDN", 2, 2, 0,
    458  1.2  christos 	  ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
    459  1.1     lukem 	  "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
    460  1.1     lukem 	  "DESC 'DN of a pwdPolicy object for uncustomized objects' "
    461  1.3  christos 	  "EQUALITY distinguishedNameMatch "
    462  1.1     lukem 	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
    463  1.1     lukem 	{ "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
    464  1.1     lukem 	  ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
    465  1.1     lukem 	  (void *)offsetof(pp_info,hash_passwords),
    466  1.1     lukem 	  "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
    467  1.1     lukem 	  "DESC 'Hash passwords on add or modify' "
    468  1.3  christos 	  "EQUALITY booleanMatch "
    469  1.1     lukem 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
    470  1.2  christos 	{ "ppolicy_forward_updates", "on|off", 1, 2, 0,
    471  1.2  christos 	  ARG_ON_OFF|ARG_OFFSET,
    472  1.2  christos 	  (void *)offsetof(pp_info,forward_updates),
    473  1.2  christos 	  "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' "
    474  1.2  christos 	  "DESC 'Allow policy state updates to be forwarded via updateref' "
    475  1.3  christos 	  "EQUALITY booleanMatch "
    476  1.2  christos 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
    477  1.1     lukem 	{ "ppolicy_use_lockout", "on|off", 1, 2, 0,
    478  1.1     lukem 	  ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
    479  1.1     lukem 	  (void *)offsetof(pp_info,use_lockout),
    480  1.1     lukem 	  "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
    481  1.1     lukem 	  "DESC 'Warn clients with AccountLocked' "
    482  1.3  christos 	  "EQUALITY booleanMatch "
    483  1.3  christos 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
    484  1.3  christos 	{ "ppolicy_disable_write", "on|off", 1, 2, 0,
    485  1.3  christos 	  ARG_ON_OFF|ARG_OFFSET|PPOLICY_DISABLE_WRITE,
    486  1.3  christos 	  (void *)offsetof(pp_info,disable_write),
    487  1.3  christos 	  "( OLcfgOvAt:12.5 NAME 'olcPPolicyDisableWrite' "
    488  1.3  christos 	  "DESC 'Prevent all policy overlay writes' "
    489  1.3  christos 	  "EQUALITY booleanMatch "
    490  1.3  christos 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
    491  1.3  christos 	{ "ppolicy_send_netscape_controls", "on|off", 1, 2, 0,
    492  1.3  christos 	  ARG_ON_OFF|ARG_OFFSET,
    493  1.3  christos 	  (void *)offsetof(pp_info,send_netscape_controls),
    494  1.3  christos 	  "( OLcfgOvAt:12.6 NAME 'olcPPolicySendNetscapeControls' "
    495  1.3  christos 	  "DESC 'Send Netscape policy controls' "
    496  1.3  christos 	  "EQUALITY booleanMatch "
    497  1.1     lukem 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
    498  1.4  christos 	{ "ppolicy_check_module", "path", 2, 2, 0,
    499  1.4  christos #ifdef SLAPD_MODULES
    500  1.4  christos 	  ARG_STRING|ARG_MAGIC|PPOLICY_CHECK_MODULE, ppolicy_cf_checkmod,
    501  1.4  christos #else
    502  1.4  christos 	  ARG_IGNORED, NULL,
    503  1.4  christos #endif /* SLAPD_MODULES */
    504  1.4  christos 	  "( OLcfgOvAt:12.7 NAME 'olcPPolicyCheckModule' "
    505  1.4  christos 	  "DESC 'Loadable module that instantiates check_password() function' "
    506  1.4  christos 	  "EQUALITY caseExactIA5Match "
    507  1.4  christos 	  "SYNTAX OMsIA5String "
    508  1.4  christos 	  "SINGLE-VALUE )", NULL, NULL },
    509  1.1     lukem 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
    510  1.1     lukem };
    511  1.1     lukem 
    512  1.1     lukem static ConfigOCs ppolicyocs[] = {
    513  1.1     lukem 	{ "( OLcfgOvOc:12.1 "
    514  1.1     lukem 	  "NAME 'olcPPolicyConfig' "
    515  1.1     lukem 	  "DESC 'Password Policy configuration' "
    516  1.1     lukem 	  "SUP olcOverlayConfig "
    517  1.1     lukem 	  "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
    518  1.3  christos 	  "olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
    519  1.4  christos 	  "olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls $ "
    520  1.4  christos 	  "olcPPolicyCheckModule ) )",
    521  1.1     lukem 	  Cft_Overlay, ppolicycfg },
    522  1.1     lukem 	{ NULL, 0, NULL }
    523  1.1     lukem };
    524  1.1     lukem 
    525  1.1     lukem static int
    526  1.1     lukem ppolicy_cf_default( ConfigArgs *c )
    527  1.1     lukem {
    528  1.1     lukem 	slap_overinst *on = (slap_overinst *)c->bi;
    529  1.1     lukem 	pp_info *pi = (pp_info *)on->on_bi.bi_private;
    530  1.1     lukem 	int rc = ARG_BAD_CONF;
    531  1.1     lukem 
    532  1.1     lukem 	assert ( c->type == PPOLICY_DEFAULT );
    533  1.3  christos 	Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n" );
    534  1.1     lukem 
    535  1.1     lukem 	switch ( c->op ) {
    536  1.1     lukem 	case SLAP_CONFIG_EMIT:
    537  1.3  christos 		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n" );
    538  1.1     lukem 		rc = 0;
    539  1.1     lukem 		if ( !BER_BVISEMPTY( &pi->def_policy )) {
    540  1.1     lukem 			rc = value_add_one( &c->rvalue_vals,
    541  1.1     lukem 					    &pi->def_policy );
    542  1.1     lukem 			if ( rc ) return rc;
    543  1.1     lukem 			rc = value_add_one( &c->rvalue_nvals,
    544  1.1     lukem 					    &pi->def_policy );
    545  1.1     lukem 		}
    546  1.1     lukem 		break;
    547  1.1     lukem 	case LDAP_MOD_DELETE:
    548  1.3  christos 		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n" );
    549  1.1     lukem 		if ( pi->def_policy.bv_val ) {
    550  1.1     lukem 			ber_memfree ( pi->def_policy.bv_val );
    551  1.1     lukem 			pi->def_policy.bv_val = NULL;
    552  1.1     lukem 		}
    553  1.1     lukem 		pi->def_policy.bv_len = 0;
    554  1.1     lukem 		rc = 0;
    555  1.1     lukem 		break;
    556  1.1     lukem 	case SLAP_CONFIG_ADD:
    557  1.3  christos 		/* fallthru to LDAP_MOD_ADD */
    558  1.1     lukem 	case LDAP_MOD_ADD:
    559  1.3  christos 		Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n" );
    560  1.1     lukem 		if ( pi->def_policy.bv_val ) {
    561  1.1     lukem 			ber_memfree ( pi->def_policy.bv_val );
    562  1.1     lukem 		}
    563  1.1     lukem 		pi->def_policy = c->value_ndn;
    564  1.1     lukem 		ber_memfree( c->value_dn.bv_val );
    565  1.1     lukem 		BER_BVZERO( &c->value_dn );
    566  1.1     lukem 		BER_BVZERO( &c->value_ndn );
    567  1.1     lukem 		rc = 0;
    568  1.1     lukem 		break;
    569  1.1     lukem 	default:
    570  1.1     lukem 		abort ();
    571  1.1     lukem 	}
    572  1.1     lukem 
    573  1.1     lukem 	return rc;
    574  1.1     lukem }
    575  1.1     lukem 
    576  1.4  christos #ifdef SLAPD_MODULES
    577  1.4  christos static int
    578  1.4  christos ppolicy_cf_checkmod( ConfigArgs *c )
    579  1.4  christos {
    580  1.4  christos 	slap_overinst *on = (slap_overinst *)c->bi;
    581  1.4  christos 	pp_info *pi = (pp_info *)on->on_bi.bi_private;
    582  1.4  christos 	int rc = ARG_BAD_CONF;
    583  1.4  christos 
    584  1.4  christos 	assert ( c->type == PPOLICY_CHECK_MODULE );
    585  1.4  christos 	Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_checkmod\n" );
    586  1.4  christos 
    587  1.4  christos 	switch ( c->op ) {
    588  1.4  christos 	case SLAP_CONFIG_EMIT:
    589  1.4  christos 		if ( pi->pwdCheckModule ) {
    590  1.4  christos 			c->value_string = ch_strdup( pi->pwdCheckModule );
    591  1.4  christos 			rc = 0;
    592  1.4  christos 		}
    593  1.4  christos 		break;
    594  1.4  christos 	case LDAP_MOD_DELETE:
    595  1.4  christos 		if ( pi->pwdCheckHandle ) {
    596  1.4  christos 			lt_dlclose( pi->pwdCheckHandle );
    597  1.4  christos 			pi->pwdCheckHandle = NULL;
    598  1.4  christos 			pi->pwdCheckFunc = NULL;
    599  1.4  christos 		}
    600  1.4  christos 		ch_free( pi->pwdCheckModule );
    601  1.4  christos 		pi->pwdCheckModule = NULL;
    602  1.4  christos 		rc = 0;
    603  1.4  christos 		break;
    604  1.4  christos 	case SLAP_CONFIG_ADD:
    605  1.4  christos 		/* fallthru to LDAP_MOD_ADD */
    606  1.4  christos 	case LDAP_MOD_ADD:
    607  1.4  christos 		pi->pwdCheckHandle = lt_dlopen( c->value_string );
    608  1.4  christos 		if ( pi->pwdCheckHandle == NULL ) {
    609  1.4  christos 			const char *dlerr = lt_dlerror();
    610  1.4  christos 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlopen(%s) failed: %s",
    611  1.4  christos 				c->argv[0], c->value_string, dlerr );
    612  1.4  christos 			Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
    613  1.4  christos 		} else {
    614  1.4  christos 			if (( pi->pwdCheckFunc = lt_dlsym( pi->pwdCheckHandle, "check_password" )) == NULL) {
    615  1.4  christos 				const char *dlerr = lt_dlerror();
    616  1.4  christos 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlsym(%s) failed: %s",
    617  1.4  christos 					c->argv[0], c->value_string, dlerr );
    618  1.4  christos 				Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
    619  1.4  christos 			} else {
    620  1.4  christos 				pi->pwdCheckModule = c->value_string;
    621  1.4  christos 				rc = 0;
    622  1.4  christos 			}
    623  1.4  christos 		}
    624  1.4  christos 		break;
    625  1.4  christos 	default:
    626  1.4  christos 		abort ();
    627  1.4  christos 	}
    628  1.4  christos 
    629  1.4  christos 	return rc;
    630  1.4  christos }
    631  1.4  christos #endif /* SLAPD_MODULES */
    632  1.4  christos 
    633  1.1     lukem static time_t
    634  1.1     lukem parse_time( char *atm )
    635  1.1     lukem {
    636  1.1     lukem 	struct lutil_tm tm;
    637  1.1     lukem 	struct lutil_timet tt;
    638  1.1     lukem 	time_t ret = (time_t)-1;
    639  1.1     lukem 
    640  1.1     lukem 	if ( lutil_parsetime( atm, &tm ) == 0) {
    641  1.1     lukem 		lutil_tm2time( &tm, &tt );
    642  1.1     lukem 		ret = tt.tt_sec;
    643  1.1     lukem 	}
    644  1.1     lukem 	return ret;
    645  1.1     lukem }
    646  1.1     lukem 
    647  1.1     lukem static int
    648  1.1     lukem account_locked( Operation *op, Entry *e,
    649  1.1     lukem 		PassPolicy *pp, Modifications **mod )
    650  1.1     lukem {
    651  1.1     lukem 	Attribute       *la;
    652  1.1     lukem 
    653  1.3  christos 	if ( (la = attr_find( e->e_attrs, ad_pwdStartTime )) != NULL ) {
    654  1.3  christos 		BerVarray vals = la->a_nvals;
    655  1.3  christos 		time_t then, now = op->o_time;
    656  1.3  christos 
    657  1.3  christos 		/*
    658  1.3  christos 		 * Password has a defined start of validity
    659  1.3  christos 		 */
    660  1.3  christos 		if ( vals[0].bv_val != NULL ) {
    661  1.3  christos 			if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
    662  1.3  christos 				return 1;
    663  1.3  christos 			}
    664  1.3  christos 			if ( now < then ) {
    665  1.3  christos 				return 1;
    666  1.3  christos 			}
    667  1.3  christos 		}
    668  1.3  christos 	}
    669  1.3  christos 
    670  1.3  christos 	if ( (la = attr_find( e->e_attrs, ad_pwdEndTime )) != NULL ) {
    671  1.3  christos 		BerVarray vals = la->a_nvals;
    672  1.3  christos 		time_t then, now = op->o_time;
    673  1.3  christos 
    674  1.3  christos 		/*
    675  1.3  christos 		 * Password has a defined end of validity
    676  1.3  christos 		 */
    677  1.3  christos 		if ( vals[0].bv_val != NULL ) {
    678  1.3  christos 			if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
    679  1.3  christos 				return 1;
    680  1.3  christos 			}
    681  1.3  christos 			if ( then <= now ) {
    682  1.3  christos 				return 1;
    683  1.3  christos 			}
    684  1.3  christos 		}
    685  1.3  christos 	}
    686  1.1     lukem 
    687  1.2  christos 	if ( !pp->pwdLockout )
    688  1.2  christos 		return 0;
    689  1.2  christos 
    690  1.3  christos 	if ( (la = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
    691  1.3  christos 		BerVarray vals = la->a_nvals;
    692  1.3  christos 		time_t then, now = op->o_time;
    693  1.3  christos 
    694  1.3  christos 		/*
    695  1.3  christos 		 * We have temporarily locked the account after a failure
    696  1.3  christos 		 */
    697  1.3  christos 		if ( vals[0].bv_val != NULL ) {
    698  1.3  christos 			if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
    699  1.3  christos 				return 1;
    700  1.3  christos 			}
    701  1.3  christos 			if ( now < then ) {
    702  1.3  christos 				return 1;
    703  1.3  christos 			}
    704  1.3  christos 		}
    705  1.3  christos 	}
    706  1.3  christos 
    707  1.3  christos 	/* Only check if database maintains lastbind */
    708  1.3  christos 	if ( pp->pwdMaxIdle && SLAP_LASTBIND( op->o_bd ) ) {
    709  1.3  christos 		time_t lastbindtime = (time_t)-1;
    710  1.3  christos 
    711  1.3  christos 		la = attr_find( e->e_attrs, ad_pwdLastSuccess );
    712  1.3  christos 		if ( la == NULL ) {
    713  1.3  christos 			la = attr_find( e->e_attrs, ad_pwdChangedTime );
    714  1.3  christos 		}
    715  1.3  christos 		if ( la != NULL ) {
    716  1.3  christos 			lastbindtime = parse_time( la->a_nvals[0].bv_val );
    717  1.3  christos 		}
    718  1.3  christos 
    719  1.3  christos 		if ( lastbindtime != (time_t)-1 &&
    720  1.3  christos 				op->o_time > lastbindtime + pp->pwdMaxIdle ) {
    721  1.3  christos 			return 1;
    722  1.3  christos 		}
    723  1.3  christos 	}
    724  1.3  christos 
    725  1.1     lukem 	if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
    726  1.1     lukem 		BerVarray vals = la->a_nvals;
    727  1.1     lukem 
    728  1.1     lukem 		/*
    729  1.1     lukem 		 * there is a lockout stamp - we now need to know if it's
    730  1.1     lukem 		 * a valid one.
    731  1.1     lukem 		 */
    732  1.1     lukem 		if (vals[0].bv_val != NULL) {
    733  1.1     lukem 			time_t then, now;
    734  1.1     lukem 			Modifications *m;
    735  1.1     lukem 
    736  1.1     lukem 			if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
    737  1.1     lukem 				return 1;
    738  1.1     lukem 
    739  1.1     lukem 			now = slap_get_time();
    740  1.1     lukem 
    741  1.3  christos 			/* Still in the future? not yet in effect */
    742  1.3  christos 			if (now < then)
    743  1.3  christos 				return 0;
    744  1.3  christos 
    745  1.3  christos 			if (!pp->pwdLockoutDuration)
    746  1.3  christos 				return 1;
    747  1.3  christos 
    748  1.1     lukem 			if (now < then + pp->pwdLockoutDuration)
    749  1.1     lukem 				return 1;
    750  1.1     lukem 
    751  1.3  christos 			if ( mod != NULL ) {
    752  1.3  christos 				m = ch_calloc( sizeof(Modifications), 1 );
    753  1.3  christos 				m->sml_op = LDAP_MOD_DELETE;
    754  1.3  christos 				m->sml_flags = 0;
    755  1.3  christos 				m->sml_type = ad_pwdAccountLockedTime->ad_cname;
    756  1.3  christos 				m->sml_desc = ad_pwdAccountLockedTime;
    757  1.3  christos 				m->sml_next = *mod;
    758  1.3  christos 				*mod = m;
    759  1.3  christos 			}
    760  1.1     lukem 		}
    761  1.1     lukem 	}
    762  1.1     lukem 
    763  1.1     lukem 	return 0;
    764  1.1     lukem }
    765  1.1     lukem 
    766  1.1     lukem /* IMPLICIT TAGS, all context-specific */
    767  1.1     lukem #define PPOLICY_WARNING 0xa0L	/* constructed + 0 */
    768  1.1     lukem #define PPOLICY_ERROR 0x81L		/* primitive + 1 */
    769  1.1     lukem 
    770  1.1     lukem #define PPOLICY_EXPIRE 0x80L	/* primitive + 0 */
    771  1.1     lukem #define PPOLICY_GRACE  0x81L	/* primitive + 1 */
    772  1.1     lukem 
    773  1.1     lukem static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
    774  1.3  christos static const char ppolicy_account_ctrl_oid[] = LDAP_CONTROL_X_ACCOUNT_USABILITY;
    775  1.3  christos static const char ppolicy_pwd_expired_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRED;
    776  1.3  christos static const char ppolicy_pwd_expiring_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRING;
    777  1.1     lukem 
    778  1.1     lukem static LDAPControl *
    779  1.2  christos create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
    780  1.1     lukem {
    781  1.2  christos 	BerElementBuffer berbuf, bb2;
    782  1.2  christos 	BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
    783  1.2  christos 	LDAPControl c = { 0 }, *cp;
    784  1.1     lukem 	struct berval bv;
    785  1.2  christos 	int rc;
    786  1.1     lukem 
    787  1.2  christos 	BER_BVZERO( &c.ldctl_value );
    788  1.1     lukem 
    789  1.1     lukem 	ber_init2( ber, NULL, LBER_USE_DER );
    790  1.1     lukem 	ber_printf( ber, "{" /*}*/ );
    791  1.1     lukem 
    792  1.1     lukem 	if ( exptime >= 0 ) {
    793  1.1     lukem 		ber_init2( b2, NULL, LBER_USE_DER );
    794  1.1     lukem 		ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
    795  1.2  christos 		rc = ber_flatten2( b2, &bv, 1 );
    796  1.1     lukem 		(void)ber_free_buf(b2);
    797  1.2  christos 		if (rc == -1) {
    798  1.2  christos 			cp = NULL;
    799  1.2  christos 			goto fail;
    800  1.2  christos 		}
    801  1.1     lukem 		ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
    802  1.1     lukem 		ch_free( bv.bv_val );
    803  1.3  christos 	} else if ( grace >= 0 ) {
    804  1.1     lukem 		ber_init2( b2, NULL, LBER_USE_DER );
    805  1.1     lukem 		ber_printf( b2, "ti", PPOLICY_GRACE, grace );
    806  1.2  christos 		rc = ber_flatten2( b2, &bv, 1 );
    807  1.1     lukem 		(void)ber_free_buf(b2);
    808  1.2  christos 		if (rc == -1) {
    809  1.2  christos 			cp = NULL;
    810  1.2  christos 			goto fail;
    811  1.2  christos 		}
    812  1.1     lukem 		ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
    813  1.1     lukem 		ch_free( bv.bv_val );
    814  1.1     lukem 	}
    815  1.1     lukem 
    816  1.1     lukem 	if (err != PP_noError ) {
    817  1.1     lukem 		ber_printf( ber, "te", PPOLICY_ERROR, err );
    818  1.1     lukem 	}
    819  1.1     lukem 	ber_printf( ber, /*{*/ "N}" );
    820  1.1     lukem 
    821  1.2  christos 	if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
    822  1.2  christos 		return NULL;
    823  1.1     lukem 	}
    824  1.2  christos 	cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
    825  1.2  christos 	cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
    826  1.2  christos 	cp->ldctl_iscritical = 0;
    827  1.2  christos 	cp->ldctl_value.bv_val = (char *)&cp[1];
    828  1.2  christos 	cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
    829  1.2  christos 	AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
    830  1.2  christos fail:
    831  1.1     lukem 	(void)ber_free_buf(ber);
    832  1.2  christos 
    833  1.2  christos 	return cp;
    834  1.1     lukem }
    835  1.1     lukem 
    836  1.3  christos static LDAPControl *
    837  1.3  christos create_passexpiry( Operation *op, int expired, int warn )
    838  1.3  christos {
    839  1.3  christos 	LDAPControl *cp;
    840  1.3  christos 	char buf[sizeof("-2147483648")];
    841  1.3  christos 	struct berval bv = { .bv_val = buf, .bv_len = sizeof(buf) };
    842  1.3  christos 
    843  1.3  christos 	bv.bv_len = snprintf( bv.bv_val, bv.bv_len, "%d", warn );
    844  1.3  christos 
    845  1.3  christos 	cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
    846  1.3  christos 	if ( expired ) {
    847  1.3  christos 		cp->ldctl_oid = (char *)ppolicy_pwd_expired_oid;
    848  1.3  christos 	} else {
    849  1.3  christos 		cp->ldctl_oid = (char *)ppolicy_pwd_expiring_oid;
    850  1.3  christos 	}
    851  1.3  christos 	cp->ldctl_iscritical = 0;
    852  1.3  christos 	cp->ldctl_value.bv_val = (char *)&cp[1];
    853  1.3  christos 	cp->ldctl_value.bv_len = bv.bv_len;
    854  1.3  christos 	AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
    855  1.3  christos 	return cp;
    856  1.3  christos }
    857  1.3  christos 
    858  1.1     lukem static LDAPControl **
    859  1.1     lukem add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
    860  1.1     lukem {
    861  1.1     lukem 	LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
    862  1.1     lukem 	int n;
    863  1.1     lukem 
    864  1.1     lukem 	n = 0;
    865  1.1     lukem 	if ( oldctrls ) {
    866  1.1     lukem 		for ( ; oldctrls[n]; n++ )
    867  1.1     lukem 			;
    868  1.1     lukem 	}
    869  1.1     lukem 	n += 2;
    870  1.1     lukem 
    871  1.1     lukem 	ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
    872  1.1     lukem 
    873  1.1     lukem 	n = 0;
    874  1.1     lukem 	if ( oldctrls ) {
    875  1.1     lukem 		for ( ; oldctrls[n]; n++ ) {
    876  1.1     lukem 			ctrls[n] = oldctrls[n];
    877  1.1     lukem 		}
    878  1.1     lukem 	}
    879  1.1     lukem 	ctrls[n] = ctrl;
    880  1.1     lukem 	ctrls[n+1] = NULL;
    881  1.1     lukem 
    882  1.1     lukem 	rs->sr_ctrls = ctrls;
    883  1.1     lukem 
    884  1.1     lukem 	return oldctrls;
    885  1.1     lukem }
    886  1.1     lukem 
    887  1.1     lukem static void
    888  1.3  christos add_account_control(
    889  1.3  christos 	Operation *op,
    890  1.3  christos 	SlapReply *rs,
    891  1.3  christos 	int available,
    892  1.3  christos 	int remaining,
    893  1.3  christos 	LDAPAccountUsabilityMoreInfo *more_info )
    894  1.3  christos {
    895  1.3  christos 	BerElementBuffer berbuf;
    896  1.3  christos 	BerElement *ber = (BerElement *) &berbuf;
    897  1.3  christos 	LDAPControl c = { 0 }, *cp = NULL, **ctrls;
    898  1.3  christos 	int i = 0;
    899  1.3  christos 
    900  1.3  christos 	BER_BVZERO( &c.ldctl_value );
    901  1.3  christos 
    902  1.3  christos 	ber_init2( ber, NULL, LBER_USE_DER );
    903  1.3  christos 
    904  1.3  christos 	if ( available ) {
    905  1.3  christos 		ber_put_int( ber, remaining, LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE );
    906  1.3  christos 	} else {
    907  1.3  christos 		assert( more_info != NULL );
    908  1.3  christos 
    909  1.3  christos 		ber_start_seq( ber, LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE );
    910  1.3  christos 		ber_put_boolean( ber, more_info->inactive, LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE );
    911  1.3  christos 		ber_put_boolean( ber, more_info->reset, LDAP_TAG_X_ACCOUNT_USABILITY_RESET );
    912  1.3  christos 		ber_put_boolean( ber, more_info->expired, LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED );
    913  1.3  christos 		ber_put_int( ber, more_info->remaining_grace, LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE );
    914  1.3  christos 		ber_put_int( ber, more_info->seconds_before_unlock, LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK );
    915  1.3  christos 		ber_put_seq( ber );
    916  1.3  christos 	}
    917  1.3  christos 
    918  1.3  christos 	if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
    919  1.3  christos 		goto fail;
    920  1.3  christos 	}
    921  1.3  christos 
    922  1.3  christos 	if ( rs->sr_ctrls != NULL ) {
    923  1.3  christos 		for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) /* Count */;
    924  1.3  christos 	}
    925  1.3  christos 
    926  1.3  christos 	ctrls = op->o_tmprealloc( rs->sr_ctrls, sizeof(LDAPControl *)*( i + 2 ), op->o_tmpmemctx );
    927  1.3  christos 	if ( ctrls == NULL ) {
    928  1.3  christos 		goto fail;
    929  1.3  christos 	}
    930  1.3  christos 
    931  1.3  christos 	cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
    932  1.3  christos 	cp->ldctl_oid = (char *)ppolicy_account_ctrl_oid;
    933  1.3  christos 	cp->ldctl_iscritical = 0;
    934  1.3  christos 	cp->ldctl_value.bv_val = (char *)&cp[1];
    935  1.3  christos 	cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
    936  1.3  christos 	AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
    937  1.3  christos 
    938  1.3  christos 	ctrls[ i ] = cp;
    939  1.3  christos 	ctrls[ i + 1 ] = NULL;
    940  1.3  christos 	rs->sr_ctrls = ctrls;
    941  1.3  christos 
    942  1.3  christos fail:
    943  1.3  christos 	(void)ber_free_buf(ber);
    944  1.3  christos }
    945  1.3  christos 
    946  1.3  christos static void
    947  1.2  christos ppolicy_get_default( PassPolicy *pp )
    948  1.2  christos {
    949  1.2  christos 	memset( pp, 0, sizeof(PassPolicy) );
    950  1.2  christos 
    951  1.2  christos 	pp->ad = slap_schema.si_ad_userPassword;
    952  1.2  christos 
    953  1.2  christos 	/* Users can change their own password by default */
    954  1.2  christos 	pp->pwdAllowUserChange = 1;
    955  1.2  christos }
    956  1.2  christos 
    957  1.2  christos 
    958  1.3  christos static int
    959  1.1     lukem ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
    960  1.1     lukem {
    961  1.1     lukem 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
    962  1.1     lukem 	pp_info *pi = on->on_bi.bi_private;
    963  1.3  christos 	BackendDB *bd, *bd_orig = op->o_bd;
    964  1.3  christos 	AttributeDescription *ad = NULL;
    965  1.1     lukem 	Attribute *a;
    966  1.1     lukem 	BerVarray vals;
    967  1.3  christos 	int rc = LDAP_SUCCESS;
    968  1.1     lukem 	Entry *pe = NULL;
    969  1.1     lukem #if 0
    970  1.1     lukem 	const char *text;
    971  1.1     lukem #endif
    972  1.1     lukem 
    973  1.2  christos 	ppolicy_get_default( pp );
    974  1.1     lukem 
    975  1.3  christos 	ad = ad_pwdPolicySubentry;
    976  1.3  christos 	if ( (a = attr_find( e->e_attrs, ad )) == NULL ) {
    977  1.1     lukem 		/*
    978  1.1     lukem 		 * entry has no password policy assigned - use default
    979  1.1     lukem 		 */
    980  1.1     lukem 		vals = &pi->def_policy;
    981  1.1     lukem 		if ( !vals->bv_val )
    982  1.1     lukem 			goto defaultpol;
    983  1.1     lukem 	} else {
    984  1.1     lukem 		vals = a->a_nvals;
    985  1.1     lukem 		if (vals[0].bv_val == NULL) {
    986  1.1     lukem 			Debug( LDAP_DEBUG_ANY,
    987  1.3  christos 				"ppolicy_get: NULL value for policySubEntry\n" );
    988  1.1     lukem 			goto defaultpol;
    989  1.1     lukem 		}
    990  1.1     lukem 	}
    991  1.1     lukem 
    992  1.3  christos 	op->o_bd = bd = select_backend( vals, 0 );
    993  1.3  christos 	if ( op->o_bd == NULL ) {
    994  1.3  christos 		op->o_bd = bd_orig;
    995  1.3  christos 		goto defaultpol;
    996  1.3  christos 	}
    997  1.3  christos 
    998  1.1     lukem 	rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
    999  1.3  christos 	op->o_bd = bd_orig;
   1000  1.1     lukem 
   1001  1.1     lukem 	if ( rc ) goto defaultpol;
   1002  1.1     lukem 
   1003  1.1     lukem #if 0	/* Only worry about userPassword for now */
   1004  1.1     lukem 	if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
   1005  1.1     lukem 		slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
   1006  1.1     lukem #endif
   1007  1.1     lukem 
   1008  1.3  christos 	ad = ad_pwdMinAge;
   1009  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1010  1.3  christos 			&& lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 ) {
   1011  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1012  1.3  christos 		goto defaultpol;
   1013  1.3  christos 	}
   1014  1.3  christos 
   1015  1.3  christos 	ad = ad_pwdMaxAge;
   1016  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1017  1.3  christos 			&& lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 ) {
   1018  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1019  1.3  christos 		goto defaultpol;
   1020  1.3  christos 	}
   1021  1.3  christos 
   1022  1.3  christos 	ad = ad_pwdMaxIdle;
   1023  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1024  1.3  christos 			&& lutil_atoi( &pp->pwdMaxIdle, a->a_vals[0].bv_val ) != 0 ) {
   1025  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1026  1.1     lukem 		goto defaultpol;
   1027  1.3  christos 	}
   1028  1.3  christos 
   1029  1.3  christos 	ad = ad_pwdInHistory;
   1030  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1031  1.3  christos 			&& lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 ) {
   1032  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1033  1.1     lukem 		goto defaultpol;
   1034  1.3  christos 	}
   1035  1.3  christos 
   1036  1.3  christos 	ad = ad_pwdCheckQuality;
   1037  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1038  1.3  christos 			&& lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 ) {
   1039  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1040  1.1     lukem 		goto defaultpol;
   1041  1.3  christos 	}
   1042  1.3  christos 
   1043  1.3  christos 	ad = ad_pwdMinLength;
   1044  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1045  1.3  christos 			&& lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 ) {
   1046  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1047  1.1     lukem 		goto defaultpol;
   1048  1.3  christos 	}
   1049  1.3  christos 
   1050  1.3  christos 	ad = ad_pwdMaxLength;
   1051  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1052  1.3  christos 			&& lutil_atoi( &pp->pwdMaxLength, a->a_vals[0].bv_val ) != 0 ) {
   1053  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1054  1.1     lukem 		goto defaultpol;
   1055  1.3  christos 	}
   1056  1.3  christos 
   1057  1.3  christos 	ad = ad_pwdMaxFailure;
   1058  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1059  1.3  christos 			&& lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) {
   1060  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1061  1.3  christos 		goto defaultpol;
   1062  1.3  christos 	}
   1063  1.3  christos 
   1064  1.3  christos 	ad = ad_pwdMaxRecordedFailure;
   1065  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1066  1.3  christos 			&& lutil_atoi( &pp->pwdMaxRecordedFailure, a->a_vals[0].bv_val ) != 0 ) {
   1067  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1068  1.3  christos 		goto defaultpol;
   1069  1.3  christos 	}
   1070  1.3  christos 
   1071  1.3  christos 	ad = ad_pwdGraceExpiry;
   1072  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1073  1.3  christos 			&& lutil_atoi( &pp->pwdGraceExpiry, a->a_vals[0].bv_val ) != 0 ) {
   1074  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1075  1.1     lukem 		goto defaultpol;
   1076  1.3  christos 	}
   1077  1.3  christos 
   1078  1.3  christos 	ad = ad_pwdGraceAuthNLimit;
   1079  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1080  1.3  christos 			&& lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) {
   1081  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1082  1.3  christos 		goto defaultpol;
   1083  1.3  christos 	}
   1084  1.3  christos 
   1085  1.3  christos 	ad = ad_pwdExpireWarning;
   1086  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1087  1.3  christos 			&& lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 ) {
   1088  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1089  1.2  christos 		goto defaultpol;
   1090  1.3  christos 	}
   1091  1.3  christos 
   1092  1.3  christos 	ad = ad_pwdFailureCountInterval;
   1093  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1094  1.3  christos 			&& lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 ) {
   1095  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1096  1.1     lukem 		goto defaultpol;
   1097  1.3  christos 	}
   1098  1.3  christos 
   1099  1.3  christos 	ad = ad_pwdLockoutDuration;
   1100  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1101  1.3  christos 			&& lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 ) {
   1102  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1103  1.1     lukem 		goto defaultpol;
   1104  1.3  christos 	}
   1105  1.3  christos 
   1106  1.3  christos 	ad = ad_pwdMinDelay;
   1107  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1108  1.3  christos 			&& lutil_atoi( &pp->pwdMinDelay, a->a_vals[0].bv_val ) != 0 ) {
   1109  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1110  1.1     lukem 		goto defaultpol;
   1111  1.3  christos 	}
   1112  1.3  christos 
   1113  1.3  christos 	ad = ad_pwdMaxDelay;
   1114  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad ))
   1115  1.3  christos 			&& lutil_atoi( &pp->pwdMaxDelay, a->a_vals[0].bv_val ) != 0 ) {
   1116  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1117  1.1     lukem 		goto defaultpol;
   1118  1.3  christos 	}
   1119  1.1     lukem 
   1120  1.3  christos 	ad = ad_pwdCheckModule;
   1121  1.4  christos 	if ( attr_find( pe->e_attrs, ad )) {
   1122  1.4  christos 		Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
   1123  1.4  christos 				"WARNING: Ignoring OBSOLETE attribute %s in policy %s.\n",
   1124  1.4  christos 				ad->ad_cname.bv_val, pe->e_name.bv_val );
   1125  1.1     lukem 	}
   1126  1.1     lukem 
   1127  1.4  christos 	ad = ad_pwdUseCheckModule;
   1128  1.4  christos 	if ( (a = attr_find( pe->e_attrs, ad )) )
   1129  1.4  christos 		pp->pwdUseCheckModule = bvmatch( &a->a_nvals[0], &slap_true_bv );
   1130  1.4  christos 
   1131  1.3  christos 	ad = ad_pwdCheckModuleArg;
   1132  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad )) ) {
   1133  1.3  christos 		ber_dupbv_x( &pp->pwdCheckModuleArg, &a->a_vals[0], op->o_tmpmemctx );
   1134  1.3  christos 	}
   1135  1.3  christos 
   1136  1.3  christos 	ad = ad_pwdLockout;
   1137  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad )) )
   1138  1.3  christos 		pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
   1139  1.3  christos 
   1140  1.3  christos 	ad = ad_pwdMustChange;
   1141  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad )) )
   1142  1.3  christos 		pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
   1143  1.3  christos 
   1144  1.3  christos 	ad = ad_pwdAllowUserChange;
   1145  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad )) )
   1146  1.3  christos 		pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
   1147  1.3  christos 
   1148  1.3  christos 	ad = ad_pwdSafeModify;
   1149  1.3  christos 	if ( (a = attr_find( pe->e_attrs, ad )) )
   1150  1.3  christos 		pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
   1151  1.3  christos 
   1152  1.2  christos 	if ( pp->pwdMaxRecordedFailure < pp->pwdMaxFailure )
   1153  1.2  christos 		pp->pwdMaxRecordedFailure = pp->pwdMaxFailure;
   1154  1.3  christos 
   1155  1.3  christos 	if ( !pp->pwdMaxRecordedFailure && pp->pwdMinDelay )
   1156  1.2  christos 		pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE;
   1157  1.2  christos 
   1158  1.3  christos 	if ( pp->pwdMinDelay && !pp->pwdMaxDelay ) {
   1159  1.3  christos 		Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
   1160  1.3  christos 				"pwdMinDelay was set but pwdMaxDelay wasn't, assuming they "
   1161  1.3  christos 				"are equal\n" );
   1162  1.3  christos 		pp->pwdMaxDelay = pp->pwdMinDelay;
   1163  1.3  christos 	}
   1164  1.3  christos 
   1165  1.3  christos 	op->o_bd = bd;
   1166  1.1     lukem 	be_entry_release_r( op, pe );
   1167  1.3  christos 	op->o_bd = bd_orig;
   1168  1.1     lukem 
   1169  1.3  christos 	return LDAP_SUCCESS;
   1170  1.1     lukem 
   1171  1.1     lukem defaultpol:
   1172  1.2  christos 	if ( pe ) {
   1173  1.3  christos 		op->o_bd = bd;
   1174  1.2  christos 		be_entry_release_r( op, pe );
   1175  1.3  christos 		op->o_bd = bd_orig;
   1176  1.2  christos 	}
   1177  1.2  christos 
   1178  1.3  christos 	if ( rc && !BER_BVISNULL( vals ) ) {
   1179  1.3  christos 		Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
   1180  1.3  christos 			"policy subentry %s missing or invalid at '%s', "
   1181  1.3  christos 			"no policy will be applied!\n",
   1182  1.3  christos 			vals->bv_val, ad ? ad->ad_cname.bv_val : "" );
   1183  1.3  christos 	} else {
   1184  1.3  christos 		Debug( LDAP_DEBUG_TRACE,
   1185  1.3  christos 			"ppolicy_get: using default policy\n" );
   1186  1.3  christos 	}
   1187  1.2  christos 
   1188  1.2  christos 	ppolicy_get_default( pp );
   1189  1.2  christos 
   1190  1.3  christos 	return -1;
   1191  1.1     lukem }
   1192  1.1     lukem 
   1193  1.1     lukem static int
   1194  1.1     lukem password_scheme( struct berval *cred, struct berval *sch )
   1195  1.1     lukem {
   1196  1.1     lukem 	int e;
   1197  1.1     lukem 
   1198  1.1     lukem 	assert( cred != NULL );
   1199  1.1     lukem 
   1200  1.1     lukem 	if (sch) {
   1201  1.1     lukem 		sch->bv_val = NULL;
   1202  1.1     lukem 		sch->bv_len = 0;
   1203  1.1     lukem 	}
   1204  1.1     lukem 
   1205  1.1     lukem 	if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
   1206  1.1     lukem 		(cred->bv_val[0] != '{')) return LDAP_OTHER;
   1207  1.1     lukem 
   1208  1.1     lukem 	for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
   1209  1.1     lukem 	if (cred->bv_val[e]) {
   1210  1.1     lukem 		int rc;
   1211  1.1     lukem 		rc = lutil_passwd_scheme( cred->bv_val );
   1212  1.1     lukem 		if (rc) {
   1213  1.1     lukem 			if (sch) {
   1214  1.1     lukem 				sch->bv_val = cred->bv_val;
   1215  1.1     lukem 				sch->bv_len = e;
   1216  1.1     lukem 			}
   1217  1.1     lukem 			return LDAP_SUCCESS;
   1218  1.1     lukem 		}
   1219  1.1     lukem 	}
   1220  1.1     lukem 	return LDAP_OTHER;
   1221  1.1     lukem }
   1222  1.1     lukem 
   1223  1.1     lukem static int
   1224  1.4  christos check_password_quality( struct berval *cred, pp_info *pi, PassPolicy *pp, LDAPPasswordPolicyError *err,
   1225  1.4  christos 	Entry *e, struct berval *errmsg )
   1226  1.1     lukem {
   1227  1.1     lukem 	int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
   1228  1.2  christos 	char *ptr;
   1229  1.1     lukem 	struct berval sch;
   1230  1.1     lukem 
   1231  1.1     lukem 	assert( cred != NULL );
   1232  1.1     lukem 	assert( pp != NULL );
   1233  1.4  christos 	assert( errmsg != NULL );
   1234  1.4  christos 
   1235  1.4  christos 	ptr = errmsg->bv_val;
   1236  1.4  christos 	*ptr = '\0';
   1237  1.2  christos 
   1238  1.2  christos 	ptr = cred->bv_val;
   1239  1.2  christos 
   1240  1.1     lukem 	if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
   1241  1.1     lukem 		rc = LDAP_CONSTRAINT_VIOLATION;
   1242  1.1     lukem 		if ( err ) *err = PP_passwordTooShort;
   1243  1.1     lukem 		return rc;
   1244  1.1     lukem 	}
   1245  1.1     lukem 
   1246  1.3  christos 	if ( pp->pwdMaxLength && cred->bv_len > pp->pwdMaxLength ) {
   1247  1.3  christos 		rc = LDAP_CONSTRAINT_VIOLATION;
   1248  1.3  christos 		if ( err ) *err = PP_passwordTooLong;
   1249  1.3  christos 		return rc;
   1250  1.3  christos 	}
   1251  1.3  christos 
   1252  1.1     lukem         /*
   1253  1.1     lukem          * We need to know if the password is already hashed - if so
   1254  1.1     lukem          * what scheme is it. The reason being that the "hash" of
   1255  1.1     lukem          * {cleartext} still allows us to check the password.
   1256  1.1     lukem          */
   1257  1.1     lukem 	rc = password_scheme( cred, &sch );
   1258  1.1     lukem 	if (rc == LDAP_SUCCESS) {
   1259  1.1     lukem 		if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
   1260  1.1     lukem 			sch.bv_len ) == 0)) {
   1261  1.1     lukem 			/*
   1262  1.1     lukem 			 * We can check the cleartext "hash"
   1263  1.1     lukem 			 */
   1264  1.1     lukem 			ptr = cred->bv_val + sch.bv_len;
   1265  1.1     lukem 		} else {
   1266  1.1     lukem 			/* everything else, we can't check */
   1267  1.1     lukem 			if (pp->pwdCheckQuality == 2) {
   1268  1.1     lukem 				rc = LDAP_CONSTRAINT_VIOLATION;
   1269  1.1     lukem 				if (err) *err = PP_insufficientPasswordQuality;
   1270  1.1     lukem 				return rc;
   1271  1.1     lukem 			}
   1272  1.1     lukem 			/*
   1273  1.1     lukem 			 * We can't check the syntax of the password, but it's not
   1274  1.1     lukem 			 * mandatory (according to the policy), so we return success.
   1275  1.1     lukem 			 */
   1276  1.1     lukem 
   1277  1.1     lukem 			return LDAP_SUCCESS;
   1278  1.1     lukem 		}
   1279  1.1     lukem 	}
   1280  1.1     lukem 
   1281  1.1     lukem 	rc = LDAP_SUCCESS;
   1282  1.1     lukem 
   1283  1.4  christos 	if (pp->pwdUseCheckModule) {
   1284  1.1     lukem #ifdef SLAPD_MODULES
   1285  1.4  christos 		check_func *prog;
   1286  1.1     lukem 
   1287  1.4  christos 		if ( !pi->pwdCheckFunc ) {
   1288  1.1     lukem 			Debug(LDAP_DEBUG_ANY,
   1289  1.4  christos 				"check_password_quality: no CheckModule loaded\n" );
   1290  1.4  christos 			ok = LDAP_OTHER;
   1291  1.1     lukem 		} else {
   1292  1.4  christos 			struct berval *arg = NULL;
   1293  1.4  christos 			if ( !BER_BVISNULL( &pp->pwdCheckModuleArg ) ) {
   1294  1.4  christos 				arg = &pp->pwdCheckModuleArg;
   1295  1.4  christos 			}
   1296  1.3  christos 
   1297  1.4  christos 			ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
   1298  1.4  christos 			ok = pi->pwdCheckFunc( ptr, errmsg, e, arg );
   1299  1.4  christos 			ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
   1300  1.4  christos 			if (ok != LDAP_SUCCESS) {
   1301  1.1     lukem 				Debug(LDAP_DEBUG_ANY,
   1302  1.4  christos 					"check_password_quality: module error: (%s) %s.[%d]\n",
   1303  1.4  christos 					pi->pwdCheckModule, errmsg->bv_val ? errmsg->bv_val : "", ok );
   1304  1.1     lukem 			}
   1305  1.1     lukem 		}
   1306  1.1     lukem #else
   1307  1.4  christos 		Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
   1308  1.4  christos 			"supported. pwdCheckModule ignored.\n" );
   1309  1.1     lukem #endif /* SLAPD_MODULES */
   1310  1.1     lukem 	}
   1311  1.4  christos 
   1312  1.1     lukem 	if (ok != LDAP_SUCCESS) {
   1313  1.1     lukem 		rc = LDAP_CONSTRAINT_VIOLATION;
   1314  1.1     lukem 		if (err) *err = PP_insufficientPasswordQuality;
   1315  1.1     lukem 	}
   1316  1.4  christos 
   1317  1.1     lukem 	return rc;
   1318  1.1     lukem }
   1319  1.1     lukem 
   1320  1.1     lukem static int
   1321  1.1     lukem parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
   1322  1.1     lukem {
   1323  1.1     lukem 	char *ptr;
   1324  1.1     lukem 	struct berval nv, npw;
   1325  1.2  christos 	ber_len_t i, j;
   1326  1.1     lukem 
   1327  1.1     lukem 	assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
   1328  1.1     lukem 
   1329  1.1     lukem 	if ( oid ) {
   1330  1.1     lukem 		*oid = 0;
   1331  1.1     lukem 	}
   1332  1.1     lukem 	*oldtime = (time_t)-1;
   1333  1.1     lukem 	BER_BVZERO( oldpw );
   1334  1.1     lukem 
   1335  1.1     lukem 	ber_dupbv( &nv, bv );
   1336  1.1     lukem 
   1337  1.1     lukem 	/* first get the time field */
   1338  1.1     lukem 	for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
   1339  1.1     lukem 		;
   1340  1.1     lukem 	if ( i == nv.bv_len ) {
   1341  1.1     lukem 		goto exit_failure; /* couldn't locate the '#' separator */
   1342  1.1     lukem 	}
   1343  1.1     lukem 	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
   1344  1.1     lukem 	ptr = nv.bv_val;
   1345  1.1     lukem 	*oldtime = parse_time( ptr );
   1346  1.1     lukem 	if (*oldtime == (time_t)-1) {
   1347  1.1     lukem 		goto exit_failure;
   1348  1.1     lukem 	}
   1349  1.1     lukem 
   1350  1.1     lukem 	/* get the OID field */
   1351  1.1     lukem 	for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
   1352  1.1     lukem 		;
   1353  1.1     lukem 	if ( i == nv.bv_len ) {
   1354  1.1     lukem 		goto exit_failure; /* couldn't locate the '#' separator */
   1355  1.1     lukem 	}
   1356  1.1     lukem 	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
   1357  1.1     lukem 	if ( oid ) {
   1358  1.1     lukem 		*oid = ber_strdup( ptr );
   1359  1.1     lukem 	}
   1360  1.1     lukem 
   1361  1.1     lukem 	/* get the length field */
   1362  1.1     lukem 	for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
   1363  1.1     lukem 		;
   1364  1.1     lukem 	if ( i == nv.bv_len ) {
   1365  1.1     lukem 		goto exit_failure; /* couldn't locate the '#' separator */
   1366  1.1     lukem 	}
   1367  1.1     lukem 	nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
   1368  1.1     lukem 	oldpw->bv_len = strtol( ptr, NULL, 10 );
   1369  1.1     lukem 	if (errno == ERANGE) {
   1370  1.1     lukem 		goto exit_failure;
   1371  1.1     lukem 	}
   1372  1.1     lukem 
   1373  1.1     lukem 	/* lastly, get the octets of the string */
   1374  1.1     lukem 	for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
   1375  1.1     lukem 		;
   1376  1.1     lukem 	if ( i - j != oldpw->bv_len) {
   1377  1.1     lukem 		goto exit_failure; /* length is wrong */
   1378  1.1     lukem 	}
   1379  1.1     lukem 
   1380  1.1     lukem 	npw.bv_val = ptr;
   1381  1.1     lukem 	npw.bv_len = oldpw->bv_len;
   1382  1.1     lukem 	ber_dupbv( oldpw, &npw );
   1383  1.1     lukem 	ber_memfree( nv.bv_val );
   1384  1.1     lukem 
   1385  1.1     lukem 	return LDAP_SUCCESS;
   1386  1.1     lukem 
   1387  1.1     lukem exit_failure:;
   1388  1.1     lukem 	if ( oid && *oid ) {
   1389  1.1     lukem 		ber_memfree(*oid);
   1390  1.1     lukem 		*oid = NULL;
   1391  1.1     lukem 	}
   1392  1.1     lukem 	if ( oldpw->bv_val ) {
   1393  1.1     lukem 		ber_memfree( oldpw->bv_val);
   1394  1.1     lukem 		BER_BVZERO( oldpw );
   1395  1.1     lukem 	}
   1396  1.1     lukem 	ber_memfree( nv.bv_val );
   1397  1.1     lukem 
   1398  1.1     lukem 	return LDAP_OTHER;
   1399  1.1     lukem }
   1400  1.1     lukem 
   1401  1.1     lukem static void
   1402  1.1     lukem add_to_pwd_history( pw_hist **l, time_t t,
   1403  1.1     lukem                     struct berval *oldpw, struct berval *bv )
   1404  1.1     lukem {
   1405  1.1     lukem 	pw_hist *p, *p1, *p2;
   1406  1.1     lukem 
   1407  1.1     lukem 	if (!l) return;
   1408  1.1     lukem 
   1409  1.1     lukem 	p = ch_malloc( sizeof( pw_hist ));
   1410  1.1     lukem 	p->pw = *oldpw;
   1411  1.1     lukem 	ber_dupbv( &p->bv, bv );
   1412  1.1     lukem 	p->t = t;
   1413  1.1     lukem 	p->next = NULL;
   1414  1.1     lukem 
   1415  1.1     lukem 	if (*l == NULL) {
   1416  1.1     lukem 		/* degenerate case */
   1417  1.1     lukem 		*l = p;
   1418  1.1     lukem 		return;
   1419  1.1     lukem 	}
   1420  1.1     lukem 	/*
   1421  1.1     lukem 	 * advance p1 and p2 such that p1 is the node before the
   1422  1.1     lukem 	 * new one, and p2 is the node after it
   1423  1.1     lukem 	 */
   1424  1.1     lukem 	for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
   1425  1.1     lukem 	p->next = p2;
   1426  1.1     lukem 	if (p1 == NULL) { *l = p; return; }
   1427  1.1     lukem 	p1->next = p;
   1428  1.1     lukem }
   1429  1.1     lukem 
   1430  1.1     lukem #ifndef MAX_PWD_HISTORY_SZ
   1431  1.1     lukem #define MAX_PWD_HISTORY_SZ 1024
   1432  1.1     lukem #endif /* MAX_PWD_HISTORY_SZ */
   1433  1.1     lukem 
   1434  1.1     lukem static void
   1435  1.1     lukem make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
   1436  1.1     lukem {
   1437  1.1     lukem 	char str[ MAX_PWD_HISTORY_SZ ];
   1438  1.1     lukem 	int nlen;
   1439  1.1     lukem 
   1440  1.1     lukem 	snprintf( str, MAX_PWD_HISTORY_SZ,
   1441  1.1     lukem 		  "%s#%s#%lu#", timebuf,
   1442  1.1     lukem 		  pa->a_desc->ad_type->sat_syntax->ssyn_oid,
   1443  1.1     lukem 		  (unsigned long) pa->a_nvals[0].bv_len );
   1444  1.1     lukem 	str[MAX_PWD_HISTORY_SZ-1] = 0;
   1445  1.1     lukem 	nlen = strlen(str);
   1446  1.1     lukem 
   1447  1.1     lukem         /*
   1448  1.1     lukem          * We have to assume that the string is a string of octets,
   1449  1.1     lukem          * not readable characters. In reality, yes, it probably is
   1450  1.1     lukem          * a readable (ie, base64) string, but we can't count on that
   1451  1.1     lukem          * Hence, while the first 3 fields of the password history
   1452  1.1     lukem          * are definitely readable (a timestamp, an OID and an integer
   1453  1.1     lukem          * length), the remaining octets of the actual password
   1454  1.1     lukem          * are deemed to be binary data.
   1455  1.1     lukem          */
   1456  1.1     lukem 	AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
   1457  1.1     lukem 	nlen += pa->a_nvals[0].bv_len;
   1458  1.1     lukem 	bv->bv_val = ch_malloc( nlen + 1 );
   1459  1.1     lukem 	AC_MEMCPY( bv->bv_val, str, nlen );
   1460  1.1     lukem 	bv->bv_val[nlen] = '\0';
   1461  1.1     lukem 	bv->bv_len = nlen;
   1462  1.1     lukem }
   1463  1.1     lukem 
   1464  1.1     lukem static void
   1465  1.1     lukem free_pwd_history_list( pw_hist **l )
   1466  1.1     lukem {
   1467  1.1     lukem 	pw_hist *p;
   1468  1.1     lukem 
   1469  1.1     lukem 	if (!l) return;
   1470  1.1     lukem 	p = *l;
   1471  1.1     lukem 	while (p) {
   1472  1.1     lukem 		pw_hist *pp = p->next;
   1473  1.1     lukem 
   1474  1.1     lukem 		free(p->pw.bv_val);
   1475  1.1     lukem 		free(p->bv.bv_val);
   1476  1.1     lukem 		free(p);
   1477  1.1     lukem 		p = pp;
   1478  1.1     lukem 	}
   1479  1.1     lukem 	*l = NULL;
   1480  1.1     lukem }
   1481  1.1     lukem 
   1482  1.1     lukem typedef struct ppbind {
   1483  1.4  christos 	pp_info *pi;
   1484  1.4  christos 	BackendDB *be;
   1485  1.1     lukem 	int send_ctrl;
   1486  1.2  christos 	int set_restrict;
   1487  1.1     lukem 	LDAPControl **oldctrls;
   1488  1.1     lukem 	Modifications *mod;
   1489  1.1     lukem 	LDAPPasswordPolicyError pErr;
   1490  1.1     lukem 	PassPolicy pp;
   1491  1.1     lukem } ppbind;
   1492  1.1     lukem 
   1493  1.1     lukem static void
   1494  1.1     lukem ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
   1495  1.1     lukem {
   1496  1.1     lukem 	int n;
   1497  1.1     lukem 
   1498  1.1     lukem 	assert( rs->sr_ctrls != NULL );
   1499  1.1     lukem 	assert( rs->sr_ctrls[0] != NULL );
   1500  1.1     lukem 
   1501  1.1     lukem 	for ( n = 0; rs->sr_ctrls[n]; n++ ) {
   1502  1.3  christos 		if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ||
   1503  1.3  christos 			rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expired_oid ||
   1504  1.3  christos 			rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expiring_oid ) {
   1505  1.2  christos 			op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
   1506  1.1     lukem 			rs->sr_ctrls[n] = (LDAPControl *)(-1);
   1507  1.1     lukem 			break;
   1508  1.1     lukem 		}
   1509  1.1     lukem 	}
   1510  1.1     lukem 
   1511  1.1     lukem 	if ( rs->sr_ctrls[n] == NULL ) {
   1512  1.1     lukem 		/* missed? */
   1513  1.1     lukem 	}
   1514  1.1     lukem 
   1515  1.1     lukem 	op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
   1516  1.1     lukem 
   1517  1.1     lukem 	rs->sr_ctrls = oldctrls;
   1518  1.1     lukem }
   1519  1.1     lukem 
   1520  1.1     lukem static int
   1521  1.1     lukem ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
   1522  1.1     lukem {
   1523  1.1     lukem 	ppbind *ppb = op->o_callback->sc_private;
   1524  1.1     lukem 	if ( ppb->send_ctrl ) {
   1525  1.1     lukem 		ctrls_cleanup( op, rs, ppb->oldctrls );
   1526  1.1     lukem 	}
   1527  1.1     lukem 	return SLAP_CB_CONTINUE;
   1528  1.1     lukem }
   1529  1.1     lukem 
   1530  1.1     lukem static int
   1531  1.1     lukem ppolicy_bind_response( Operation *op, SlapReply *rs )
   1532  1.1     lukem {
   1533  1.1     lukem 	ppbind *ppb = op->o_callback->sc_private;
   1534  1.4  christos 	pp_info *pi = ppb->pi;
   1535  1.1     lukem 	Modifications *mod = ppb->mod, *m;
   1536  1.1     lukem 	int pwExpired = 0;
   1537  1.3  christos 	int ngut = -1, warn = -1, fc = 0, age, rc;
   1538  1.1     lukem 	Attribute *a;
   1539  1.1     lukem 	time_t now, pwtime = (time_t)-1;
   1540  1.2  christos 	struct lutil_tm now_tm;
   1541  1.2  christos 	struct lutil_timet now_usec;
   1542  1.1     lukem 	char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
   1543  1.2  christos 	char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ];
   1544  1.2  christos 	struct berval timestamp, timestamp_usec;
   1545  1.4  christos 	BackendDB *be = op->o_bd;
   1546  1.3  christos 	LDAPControl *ctrl = NULL;
   1547  1.1     lukem 	Entry *e;
   1548  1.1     lukem 
   1549  1.3  christos 	ldap_pvt_thread_mutex_lock( &pi->pwdFailureTime_mutex );
   1550  1.1     lukem 	/* If we already know it's locked, just get on with it */
   1551  1.1     lukem 	if ( ppb->pErr != PP_noError ) {
   1552  1.1     lukem 		goto locked;
   1553  1.1     lukem 	}
   1554  1.1     lukem 
   1555  1.4  christos 	op->o_bd = ppb->be;
   1556  1.1     lukem 	rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
   1557  1.4  christos 	op->o_bd = be;
   1558  1.1     lukem 
   1559  1.1     lukem 	if ( rc != LDAP_SUCCESS ) {
   1560  1.3  christos 		ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
   1561  1.1     lukem 		return SLAP_CB_CONTINUE;
   1562  1.1     lukem 	}
   1563  1.1     lukem 
   1564  1.3  christos 	/* ITS#7089 Skip lockout checks/modifications if password attribute missing */
   1565  1.3  christos 	if ( attr_find( e->e_attrs, ppb->pp.ad ) == NULL ) {
   1566  1.3  christos 		goto done;
   1567  1.3  christos 	}
   1568  1.3  christos 
   1569  1.2  christos 	ldap_pvt_gettime(&now_tm); /* stored for later consideration */
   1570  1.2  christos 	lutil_tm2time(&now_tm, &now_usec);
   1571  1.2  christos 	now = now_usec.tt_sec;
   1572  1.1     lukem 	timestamp.bv_val = nowstr;
   1573  1.1     lukem 	timestamp.bv_len = sizeof(nowstr);
   1574  1.1     lukem 	slap_timestamp( &now, &timestamp );
   1575  1.1     lukem 
   1576  1.2  christos 	/* Separate timestamp for pwdFailureTime with microsecond granularity */
   1577  1.2  christos 	strcpy(nowstr_usec, nowstr);
   1578  1.2  christos 	timestamp_usec.bv_val = nowstr_usec;
   1579  1.2  christos 	timestamp_usec.bv_len = timestamp.bv_len;
   1580  1.3  christos 	snprintf( timestamp_usec.bv_val + timestamp_usec.bv_len-1, sizeof(".123456Z"), ".%06dZ", now_usec.tt_nsec / 1000 );
   1581  1.2  christos 	timestamp_usec.bv_len += STRLENOF(".123456");
   1582  1.2  christos 
   1583  1.3  christos 	if ( rs->sr_err == LDAP_INVALID_CREDENTIALS && ppb->pp.pwdMaxRecordedFailure ) {
   1584  1.3  christos 		int i = 0;
   1585  1.1     lukem 
   1586  1.1     lukem 		m = ch_calloc( sizeof(Modifications), 1 );
   1587  1.1     lukem 		m->sml_op = LDAP_MOD_ADD;
   1588  1.1     lukem 		m->sml_flags = 0;
   1589  1.1     lukem 		m->sml_type = ad_pwdFailureTime->ad_cname;
   1590  1.1     lukem 		m->sml_desc = ad_pwdFailureTime;
   1591  1.1     lukem 		m->sml_numvals = 1;
   1592  1.1     lukem 		m->sml_values = ch_calloc( sizeof(struct berval), 2 );
   1593  1.1     lukem 		m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
   1594  1.1     lukem 
   1595  1.2  christos 		ber_dupbv( &m->sml_values[0], &timestamp_usec );
   1596  1.2  christos 		ber_dupbv( &m->sml_nvalues[0], &timestamp_usec );
   1597  1.1     lukem 		m->sml_next = mod;
   1598  1.1     lukem 		mod = m;
   1599  1.1     lukem 
   1600  1.1     lukem 		/*
   1601  1.1     lukem 		 * Count the pwdFailureTimes - if it's
   1602  1.1     lukem 		 * greater than the policy pwdMaxFailure,
   1603  1.1     lukem 		 * then lock the account.
   1604  1.1     lukem 		 */
   1605  1.1     lukem 		if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
   1606  1.1     lukem 			for(i=0; a->a_nvals[i].bv_val; i++) {
   1607  1.1     lukem 
   1608  1.1     lukem 				/*
   1609  1.1     lukem 				 * If the interval is 0, then failures
   1610  1.1     lukem 				 * stay on the record until explicitly
   1611  1.1     lukem 				 * reset by successful authentication.
   1612  1.1     lukem 				 */
   1613  1.1     lukem 				if (ppb->pp.pwdFailureCountInterval == 0) {
   1614  1.1     lukem 					fc++;
   1615  1.1     lukem 				} else if (now <=
   1616  1.1     lukem 							parse_time(a->a_nvals[i].bv_val) +
   1617  1.1     lukem 							ppb->pp.pwdFailureCountInterval) {
   1618  1.1     lukem 
   1619  1.1     lukem 					fc++;
   1620  1.1     lukem 				}
   1621  1.1     lukem 				/*
   1622  1.1     lukem 				 * We only count those failures
   1623  1.1     lukem 				 * which are not due to expire.
   1624  1.1     lukem 				 */
   1625  1.1     lukem 			}
   1626  1.2  christos 			/* Do we have too many timestamps? If so, delete some values.
   1627  1.2  christos 			 * We don't bother to sort the values here. OpenLDAP keeps the
   1628  1.2  christos 			 * values in order by default. Fundamentally, relying on the
   1629  1.2  christos 			 * information here is wrong anyway; monitoring systems should
   1630  1.2  christos 			 * be tracking Bind failures in syslog, not here.
   1631  1.2  christos 			 */
   1632  1.2  christos 			if (a->a_numvals >= ppb->pp.pwdMaxRecordedFailure) {
   1633  1.2  christos 				int j = ppb->pp.pwdMaxRecordedFailure-1;
   1634  1.2  christos 				/* If more than 2x, cheaper to perform a Replace */
   1635  1.2  christos 				if (a->a_numvals >= 2 * ppb->pp.pwdMaxRecordedFailure) {
   1636  1.2  christos 					struct berval v, nv;
   1637  1.2  christos 
   1638  1.2  christos 					/* Change the mod we constructed above */
   1639  1.2  christos 					m->sml_op = LDAP_MOD_REPLACE;
   1640  1.2  christos 					m->sml_numvals = ppb->pp.pwdMaxRecordedFailure;
   1641  1.2  christos 					v = m->sml_values[0];
   1642  1.2  christos 					nv = m->sml_nvalues[0];
   1643  1.2  christos 					ch_free(m->sml_values);
   1644  1.2  christos 					ch_free(m->sml_nvalues);
   1645  1.2  christos 					m->sml_values = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
   1646  1.2  christos 					m->sml_nvalues = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
   1647  1.2  christos 					for (i=0; i<j; i++) {
   1648  1.2  christos 						ber_dupbv(&m->sml_values[i], &a->a_vals[a->a_numvals-j+i]);
   1649  1.2  christos 						ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[a->a_numvals-j+i]);
   1650  1.2  christos 					}
   1651  1.2  christos 					m->sml_values[i] = v;
   1652  1.2  christos 					m->sml_nvalues[i] = nv;
   1653  1.2  christos 				} else {
   1654  1.2  christos 				/* else just delete some */
   1655  1.2  christos 					m = ch_calloc( sizeof(Modifications), 1 );
   1656  1.2  christos 					m->sml_op = LDAP_MOD_DELETE;
   1657  1.2  christos 					m->sml_type = ad_pwdFailureTime->ad_cname;
   1658  1.2  christos 					m->sml_desc = ad_pwdFailureTime;
   1659  1.2  christos 					m->sml_numvals = a->a_numvals - j;
   1660  1.2  christos 					m->sml_values = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
   1661  1.2  christos 					m->sml_nvalues = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
   1662  1.2  christos 					for (i=0; i<m->sml_numvals; i++) {
   1663  1.2  christos 						ber_dupbv(&m->sml_values[i], &a->a_vals[i]);
   1664  1.2  christos 						ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[i]);
   1665  1.2  christos 					}
   1666  1.2  christos 					m->sml_next = mod;
   1667  1.2  christos 					mod = m;
   1668  1.2  christos 				}
   1669  1.2  christos 			}
   1670  1.1     lukem 		}
   1671  1.1     lukem 
   1672  1.1     lukem 		if ((ppb->pp.pwdMaxFailure > 0) &&
   1673  1.1     lukem 			(fc >= ppb->pp.pwdMaxFailure - 1)) {
   1674  1.1     lukem 
   1675  1.1     lukem 			/*
   1676  1.1     lukem 			 * We subtract 1 from the failure max
   1677  1.1     lukem 			 * because the new failure entry hasn't
   1678  1.1     lukem 			 * made it to the entry yet.
   1679  1.1     lukem 			 */
   1680  1.1     lukem 			m = ch_calloc( sizeof(Modifications), 1 );
   1681  1.1     lukem 			m->sml_op = LDAP_MOD_REPLACE;
   1682  1.1     lukem 			m->sml_flags = 0;
   1683  1.1     lukem 			m->sml_type = ad_pwdAccountLockedTime->ad_cname;
   1684  1.1     lukem 			m->sml_desc = ad_pwdAccountLockedTime;
   1685  1.1     lukem 			m->sml_numvals = 1;
   1686  1.1     lukem 			m->sml_values = ch_calloc( sizeof(struct berval), 2 );
   1687  1.1     lukem 			m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
   1688  1.1     lukem 			ber_dupbv( &m->sml_values[0], &timestamp );
   1689  1.1     lukem 			ber_dupbv( &m->sml_nvalues[0], &timestamp );
   1690  1.1     lukem 			m->sml_next = mod;
   1691  1.1     lukem 			mod = m;
   1692  1.3  christos 		} else if ( ppb->pp.pwdMinDelay ) {
   1693  1.3  christos 			int waittime = ppb->pp.pwdMinDelay << fc;
   1694  1.3  christos 			time_t wait_end;
   1695  1.4  christos 			char lockout_stamp_buf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
   1696  1.4  christos 			struct berval lockout_stamp = BER_BVC(lockout_stamp_buf);
   1697  1.3  christos 
   1698  1.3  christos 			if ( waittime > ppb->pp.pwdMaxDelay ) {
   1699  1.3  christos 				waittime = ppb->pp.pwdMaxDelay;
   1700  1.3  christos 			}
   1701  1.3  christos 			wait_end = now + waittime;
   1702  1.3  christos 
   1703  1.3  christos 			slap_timestamp( &wait_end, &lockout_stamp );
   1704  1.3  christos 
   1705  1.3  christos 			m = ch_calloc( sizeof(Modifications), 1 );
   1706  1.3  christos 			m->sml_op = LDAP_MOD_REPLACE;
   1707  1.3  christos 			m->sml_flags = 0;
   1708  1.3  christos 			m->sml_type = ad_pwdAccountTmpLockoutEnd->ad_cname;
   1709  1.3  christos 			m->sml_desc = ad_pwdAccountTmpLockoutEnd;
   1710  1.3  christos 			m->sml_numvals = 1;
   1711  1.3  christos 			m->sml_values = ch_calloc( sizeof(struct berval), 2 );
   1712  1.3  christos 			m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
   1713  1.3  christos 			ber_dupbv( &m->sml_values[0], &lockout_stamp );
   1714  1.3  christos 			ber_dupbv( &m->sml_nvalues[0], &lockout_stamp );
   1715  1.3  christos 			m->sml_next = mod;
   1716  1.3  christos 			mod = m;
   1717  1.1     lukem 		}
   1718  1.1     lukem 	} else if ( rs->sr_err == LDAP_SUCCESS ) {
   1719  1.1     lukem 		if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
   1720  1.1     lukem 			pwtime = parse_time( a->a_nvals[0].bv_val );
   1721  1.1     lukem 
   1722  1.1     lukem 		/* delete all pwdFailureTimes */
   1723  1.1     lukem 		if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
   1724  1.1     lukem 			m = ch_calloc( sizeof(Modifications), 1 );
   1725  1.1     lukem 			m->sml_op = LDAP_MOD_DELETE;
   1726  1.1     lukem 			m->sml_flags = 0;
   1727  1.1     lukem 			m->sml_type = ad_pwdFailureTime->ad_cname;
   1728  1.1     lukem 			m->sml_desc = ad_pwdFailureTime;
   1729  1.1     lukem 			m->sml_next = mod;
   1730  1.1     lukem 			mod = m;
   1731  1.1     lukem 		}
   1732  1.1     lukem 
   1733  1.1     lukem 		/*
   1734  1.1     lukem 		 * check to see if the password must be changed
   1735  1.1     lukem 		 */
   1736  1.1     lukem 		if ( ppb->pp.pwdMustChange &&
   1737  1.1     lukem 			(a = attr_find( e->e_attrs, ad_pwdReset )) &&
   1738  1.1     lukem 			bvmatch( &a->a_nvals[0], &slap_true_bv ) )
   1739  1.1     lukem 		{
   1740  1.1     lukem 			/*
   1741  1.1     lukem 			 * need to inject client controls here to give
   1742  1.1     lukem 			 * more information. For the moment, we ensure
   1743  1.1     lukem 			 * that we are disallowed from doing anything
   1744  1.1     lukem 			 * other than change password.
   1745  1.1     lukem 			 */
   1746  1.2  christos 			if ( ppb->set_restrict ) {
   1747  1.2  christos 				ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
   1748  1.2  christos 					&op->o_conn->c_ndn );
   1749  1.2  christos 			}
   1750  1.1     lukem 
   1751  1.1     lukem 			ppb->pErr = PP_changeAfterReset;
   1752  1.1     lukem 
   1753  1.1     lukem 		} else {
   1754  1.1     lukem 			/*
   1755  1.1     lukem 			 * the password does not need to be changed, so
   1756  1.1     lukem 			 * we now check whether the password has expired.
   1757  1.1     lukem 			 *
   1758  1.1     lukem 			 * We can skip this bit if passwords don't age in
   1759  1.1     lukem 			 * the policy. Also, if there was no pwdChangedTime
   1760  1.1     lukem 			 * attribute in the entry, the password never expires.
   1761  1.1     lukem 			 */
   1762  1.1     lukem 			if (ppb->pp.pwdMaxAge == 0) goto grace;
   1763  1.1     lukem 
   1764  1.1     lukem 			if (pwtime != (time_t)-1) {
   1765  1.1     lukem 				/*
   1766  1.1     lukem 				 * Check: was the last change time of
   1767  1.1     lukem 				 * the password older than the maximum age
   1768  1.1     lukem 				 * allowed. (Ignore case 2 from I-D, it's just silly.)
   1769  1.1     lukem 				 */
   1770  1.1     lukem 				if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
   1771  1.1     lukem 			}
   1772  1.1     lukem 		}
   1773  1.1     lukem 
   1774  1.1     lukem grace:
   1775  1.1     lukem 		if (!pwExpired) goto check_expiring_password;
   1776  1.1     lukem 
   1777  1.3  christos 		if ( ppb->pp.pwdGraceExpiry &&
   1778  1.3  christos 				now - pwtime > ppb->pp.pwdMaxAge + ppb->pp.pwdGraceExpiry ) {
   1779  1.3  christos 			/* Grace logins have expired now */
   1780  1.3  christos 			ngut = 0;
   1781  1.3  christos 		} else if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL) {
   1782  1.1     lukem 			ngut = ppb->pp.pwdGraceAuthNLimit;
   1783  1.3  christos 		} else {
   1784  1.1     lukem 			for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
   1785  1.1     lukem 			ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
   1786  1.1     lukem 		}
   1787  1.1     lukem 
   1788  1.1     lukem 		/*
   1789  1.1     lukem 		 * ngut is the number of remaining grace logins
   1790  1.1     lukem 		 */
   1791  1.1     lukem 		Debug( LDAP_DEBUG_ANY,
   1792  1.1     lukem 			"ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
   1793  1.3  christos 			e->e_name.bv_val, ngut );
   1794  1.3  christos 
   1795  1.3  christos 		ngut--;
   1796  1.3  christos 
   1797  1.3  christos 		if (ngut < 0) {
   1798  1.1     lukem 			ppb->pErr = PP_passwordExpired;
   1799  1.1     lukem 			rs->sr_err = LDAP_INVALID_CREDENTIALS;
   1800  1.1     lukem 			goto done;
   1801  1.1     lukem 		}
   1802  1.1     lukem 
   1803  1.1     lukem 		/*
   1804  1.1     lukem 		 * Add a grace user time to the entry
   1805  1.1     lukem 		 */
   1806  1.1     lukem 		m = ch_calloc( sizeof(Modifications), 1 );
   1807  1.1     lukem 		m->sml_op = LDAP_MOD_ADD;
   1808  1.1     lukem 		m->sml_flags = 0;
   1809  1.1     lukem 		m->sml_type = ad_pwdGraceUseTime->ad_cname;
   1810  1.1     lukem 		m->sml_desc = ad_pwdGraceUseTime;
   1811  1.1     lukem 		m->sml_numvals = 1;
   1812  1.1     lukem 		m->sml_values = ch_calloc( sizeof(struct berval), 2 );
   1813  1.1     lukem 		m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
   1814  1.3  christos 		ber_dupbv( &m->sml_values[0], &timestamp_usec );
   1815  1.3  christos 		ber_dupbv( &m->sml_nvalues[0], &timestamp_usec );
   1816  1.1     lukem 		m->sml_next = mod;
   1817  1.1     lukem 		mod = m;
   1818  1.1     lukem 
   1819  1.1     lukem check_expiring_password:
   1820  1.1     lukem 		/*
   1821  1.1     lukem 		 * Now we need to check to see
   1822  1.1     lukem 		 * if it is about to expire, and if so, should the user
   1823  1.1     lukem 		 * be warned about it in the password policy control.
   1824  1.1     lukem 		 *
   1825  1.1     lukem 		 * If the password has expired, and we're in the grace period, then
   1826  1.1     lukem 		 * we don't need to do this bit. Similarly, if we don't have password
   1827  1.1     lukem 		 * aging, then there's no need to do this bit either.
   1828  1.4  christos 		 *
   1829  1.4  christos 		 * If pwdtime is -1 there is no password Change Time attribute on the
   1830  1.4  christos 		 * entry so we skip the expiry check.
   1831  1.4  christos 		 *
   1832  1.1     lukem 		 */
   1833  1.4  christos 		if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1) ||
   1834  1.4  christos 			(pwtime == -1))
   1835  1.1     lukem 			goto done;
   1836  1.1     lukem 
   1837  1.1     lukem 		age = (int)(now - pwtime);
   1838  1.1     lukem 
   1839  1.1     lukem 		/*
   1840  1.1     lukem 		 * We know that there is a password Change Time attribute - if
   1841  1.1     lukem 		 * there wasn't, then the pwdExpired value would be true, unless
   1842  1.1     lukem 		 * there is no password aging - and if there is no password aging,
   1843  1.1     lukem 		 * then this section isn't called anyway - you can't have an
   1844  1.1     lukem 		 * expiring password if there's no limit to expire.
   1845  1.1     lukem 		 */
   1846  1.1     lukem 		if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
   1847  1.1     lukem 			/*
   1848  1.1     lukem 			 * Set the warning value.
   1849  1.1     lukem 			 */
   1850  1.1     lukem 			warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
   1851  1.1     lukem 			if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
   1852  1.1     lukem 
   1853  1.4  christos 			Debug( LDAP_DEBUG_TRACE,
   1854  1.1     lukem 				"ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
   1855  1.3  christos 				op->o_req_dn.bv_val, warn );
   1856  1.1     lukem 		}
   1857  1.1     lukem 	}
   1858  1.1     lukem 
   1859  1.1     lukem done:
   1860  1.4  christos 	op->o_bd = ppb->be;
   1861  1.1     lukem 	be_entry_release_r( op, e );
   1862  1.4  christos 	op->o_bd = be;
   1863  1.1     lukem 
   1864  1.1     lukem locked:
   1865  1.3  christos 	if ( mod && !pi->disable_write ) {
   1866  1.1     lukem 		Operation op2 = *op;
   1867  1.1     lukem 		SlapReply r2 = { REP_RESULT };
   1868  1.1     lukem 		slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
   1869  1.2  christos 		LDAPControl c, *ca[2];
   1870  1.1     lukem 
   1871  1.1     lukem 		op2.o_tag = LDAP_REQ_MODIFY;
   1872  1.1     lukem 		op2.o_callback = &cb;
   1873  1.1     lukem 		op2.orm_modlist = mod;
   1874  1.2  christos 		op2.orm_no_opattrs = 0;
   1875  1.1     lukem 		op2.o_dn = op->o_bd->be_rootdn;
   1876  1.1     lukem 		op2.o_ndn = op->o_bd->be_rootndn;
   1877  1.2  christos 
   1878  1.2  christos 		/* If this server is a shadow and forward_updates is true,
   1879  1.2  christos 		 * use the frontend to perform this modify. That will trigger
   1880  1.2  christos 		 * the update referral, which can then be forwarded by the
   1881  1.2  christos 		 * chain overlay. Obviously the updateref and chain overlay
   1882  1.2  christos 		 * must be configured appropriately for this to be useful.
   1883  1.2  christos 		 */
   1884  1.2  christos 		if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) {
   1885  1.2  christos 			op2.o_bd = frontendDB;
   1886  1.2  christos 
   1887  1.2  christos 			/* Must use Relax control since these are no-user-mod */
   1888  1.2  christos 			op2.o_relax = SLAP_CONTROL_CRITICAL;
   1889  1.2  christos 			op2.o_ctrls = ca;
   1890  1.2  christos 			ca[0] = &c;
   1891  1.2  christos 			ca[1] = NULL;
   1892  1.2  christos 			BER_BVZERO( &c.ldctl_value );
   1893  1.2  christos 			c.ldctl_iscritical = 1;
   1894  1.2  christos 			c.ldctl_oid = LDAP_CONTROL_RELAX;
   1895  1.2  christos 		} else {
   1896  1.2  christos 			/* If not forwarding, don't update opattrs and don't replicate */
   1897  1.2  christos 			if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
   1898  1.2  christos 				op2.orm_no_opattrs = 1;
   1899  1.2  christos 				op2.o_dont_replicate = 1;
   1900  1.2  christos 			}
   1901  1.4  christos 			op2.o_bd = ppb->be;
   1902  1.2  christos 		}
   1903  1.2  christos 		rc = op2.o_bd->be_modify( &op2, &r2 );
   1904  1.3  christos 		if ( rc != LDAP_SUCCESS ) {
   1905  1.3  christos 			Debug( LDAP_DEBUG_ANY, "%s ppolicy_bind_response: "
   1906  1.3  christos 					"ppolicy state change failed with rc=%d text=%s\n",
   1907  1.3  christos 					op->o_log_prefix, rc, r2.sr_text );
   1908  1.3  christos 		}
   1909  1.3  christos 	}
   1910  1.3  christos 	if ( mod ) {
   1911  1.1     lukem 		slap_mods_free( mod, 1 );
   1912  1.1     lukem 	}
   1913  1.1     lukem 
   1914  1.1     lukem 	if ( ppb->send_ctrl ) {
   1915  1.1     lukem 
   1916  1.1     lukem 		/* Do we really want to tell that the account is locked? */
   1917  1.1     lukem 		if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
   1918  1.1     lukem 			ppb->pErr = PP_noError;
   1919  1.1     lukem 		}
   1920  1.2  christos 		ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
   1921  1.3  christos 	} else if ( pi->send_netscape_controls ) {
   1922  1.3  christos 		if ( ppb->pErr != PP_noError || pwExpired ) {
   1923  1.3  christos 			ctrl = create_passexpiry( op, 1, 0 );
   1924  1.3  christos 		} else if ( warn > 0 ) {
   1925  1.3  christos 			ctrl = create_passexpiry( op, 0, warn );
   1926  1.3  christos 		}
   1927  1.3  christos 	}
   1928  1.3  christos 	if ( ctrl ) {
   1929  1.1     lukem 		ppb->oldctrls = add_passcontrol( op, rs, ctrl );
   1930  1.1     lukem 		op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
   1931  1.1     lukem 	}
   1932  1.3  christos 	ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
   1933  1.1     lukem 	return SLAP_CB_CONTINUE;
   1934  1.1     lukem }
   1935  1.1     lukem 
   1936  1.1     lukem static int
   1937  1.1     lukem ppolicy_bind( Operation *op, SlapReply *rs )
   1938  1.1     lukem {
   1939  1.1     lukem 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   1940  1.1     lukem 
   1941  1.1     lukem 	/* Reset lockout status on all Bind requests */
   1942  1.1     lukem 	if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
   1943  1.1     lukem 		ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
   1944  1.1     lukem 		BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
   1945  1.1     lukem 	}
   1946  1.1     lukem 
   1947  1.1     lukem 	/* Root bypasses policy */
   1948  1.1     lukem 	if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
   1949  1.1     lukem 		Entry *e;
   1950  1.1     lukem 		int rc;
   1951  1.1     lukem 		ppbind *ppb;
   1952  1.1     lukem 		slap_callback *cb;
   1953  1.1     lukem 
   1954  1.1     lukem 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   1955  1.1     lukem 		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
   1956  1.1     lukem 
   1957  1.1     lukem 		if ( rc != LDAP_SUCCESS ) {
   1958  1.1     lukem 			return SLAP_CB_CONTINUE;
   1959  1.1     lukem 		}
   1960  1.1     lukem 
   1961  1.1     lukem 		cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
   1962  1.1     lukem 			1, op->o_tmpmemctx );
   1963  1.1     lukem 		ppb = (ppbind *)(cb+1);
   1964  1.4  christos 		ppb->pi = on->on_bi.bi_private;
   1965  1.4  christos 		ppb->be = op->o_bd->bd_self;
   1966  1.1     lukem 		ppb->pErr = PP_noError;
   1967  1.2  christos 		ppb->set_restrict = 1;
   1968  1.1     lukem 
   1969  1.1     lukem 		/* Setup a callback so we can munge the result */
   1970  1.1     lukem 
   1971  1.1     lukem 		cb->sc_response = ppolicy_bind_response;
   1972  1.1     lukem 		cb->sc_private = ppb;
   1973  1.2  christos 		overlay_callback_after_backover( op, cb, 1 );
   1974  1.1     lukem 
   1975  1.1     lukem 		/* Did we receive a password policy request control? */
   1976  1.1     lukem 		if ( op->o_ctrlflag[ppolicy_cid] ) {
   1977  1.1     lukem 			ppb->send_ctrl = 1;
   1978  1.1     lukem 		}
   1979  1.1     lukem 
   1980  1.1     lukem 		op->o_bd->bd_info = (BackendInfo *)on;
   1981  1.1     lukem 
   1982  1.3  christos 		if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
   1983  1.3  christos 			rc = account_locked( op, e, &ppb->pp, &ppb->mod );
   1984  1.3  christos 		}
   1985  1.1     lukem 
   1986  1.1     lukem 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   1987  1.1     lukem 		be_entry_release_r( op, e );
   1988  1.1     lukem 
   1989  1.1     lukem 		if ( rc ) {
   1990  1.1     lukem 			ppb->pErr = PP_accountLocked;
   1991  1.1     lukem 			send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
   1992  1.1     lukem 			return rs->sr_err;
   1993  1.1     lukem 		}
   1994  1.1     lukem 
   1995  1.1     lukem 	}
   1996  1.1     lukem 
   1997  1.1     lukem 	return SLAP_CB_CONTINUE;
   1998  1.1     lukem }
   1999  1.1     lukem 
   2000  1.1     lukem /* Reset the restricted info for the next session on this connection */
   2001  1.1     lukem static int
   2002  1.1     lukem ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
   2003  1.1     lukem {
   2004  1.2  christos 	if ( pwcons && !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
   2005  1.1     lukem 		ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
   2006  1.1     lukem 		BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
   2007  1.1     lukem 	}
   2008  1.1     lukem 	return SLAP_CB_CONTINUE;
   2009  1.1     lukem }
   2010  1.1     lukem 
   2011  1.1     lukem /* Check if this connection is restricted */
   2012  1.1     lukem static int
   2013  1.1     lukem ppolicy_restrict(
   2014  1.1     lukem 	Operation *op,
   2015  1.1     lukem 	SlapReply *rs )
   2016  1.1     lukem {
   2017  1.1     lukem 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   2018  1.1     lukem 	int send_ctrl = 0;
   2019  1.1     lukem 
   2020  1.1     lukem 	/* Did we receive a password policy request control? */
   2021  1.1     lukem 	if ( op->o_ctrlflag[ppolicy_cid] ) {
   2022  1.1     lukem 		send_ctrl = 1;
   2023  1.1     lukem 	}
   2024  1.1     lukem 
   2025  1.1     lukem 	if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
   2026  1.1     lukem 		LDAPControl **oldctrls;
   2027  1.1     lukem 		/* if the current authcDN doesn't match the one we recorded,
   2028  1.1     lukem 		 * then an intervening Bind has succeeded and the restriction
   2029  1.1     lukem 		 * no longer applies. (ITS#4516)
   2030  1.1     lukem 		 */
   2031  1.1     lukem 		if ( !dn_match( &op->o_conn->c_ndn,
   2032  1.1     lukem 				&pwcons[op->o_conn->c_conn_idx].dn )) {
   2033  1.1     lukem 			ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
   2034  1.1     lukem 			BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
   2035  1.1     lukem 			return SLAP_CB_CONTINUE;
   2036  1.1     lukem 		}
   2037  1.1     lukem 
   2038  1.1     lukem 		Debug( LDAP_DEBUG_TRACE,
   2039  1.3  christos 			"connection restricted to password changing only\n" );
   2040  1.1     lukem 		if ( send_ctrl ) {
   2041  1.1     lukem 			LDAPControl *ctrl = NULL;
   2042  1.2  christos 			ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
   2043  1.1     lukem 			oldctrls = add_passcontrol( op, rs, ctrl );
   2044  1.1     lukem 		}
   2045  1.1     lukem 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2046  1.1     lukem 		send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
   2047  1.1     lukem 			"Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
   2048  1.1     lukem 		if ( send_ctrl ) {
   2049  1.1     lukem 			ctrls_cleanup( op, rs, oldctrls );
   2050  1.1     lukem 		}
   2051  1.1     lukem 		return rs->sr_err;
   2052  1.1     lukem 	}
   2053  1.1     lukem 
   2054  1.1     lukem 	return SLAP_CB_CONTINUE;
   2055  1.1     lukem }
   2056  1.1     lukem 
   2057  1.1     lukem static int
   2058  1.3  christos ppolicy_account_usability_entry_cb( Operation *op, SlapReply *rs )
   2059  1.3  christos {
   2060  1.3  christos 	slap_overinst *on = op->o_callback->sc_private;
   2061  1.3  christos 	BackendInfo *bi = op->o_bd->bd_info;
   2062  1.3  christos 	LDAPControl *ctrl = NULL;
   2063  1.3  christos 	PassPolicy pp;
   2064  1.3  christos 	Attribute *a;
   2065  1.3  christos 	Entry *e = NULL;
   2066  1.3  christos 	time_t pwtime = 0, seconds_until_expiry = -1, now = op->o_time;
   2067  1.3  christos 	int isExpired = 0, grace = -1;
   2068  1.3  christos 
   2069  1.3  christos 	if ( rs->sr_type != REP_SEARCH ) {
   2070  1.3  christos 		return SLAP_CB_CONTINUE;
   2071  1.3  christos 	}
   2072  1.3  christos 
   2073  1.3  christos 	if ( be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e ) != LDAP_SUCCESS ) {
   2074  1.3  christos 		goto done;
   2075  1.3  christos 	}
   2076  1.3  christos 
   2077  1.3  christos 	op->o_bd->bd_info = (BackendInfo *)on;
   2078  1.3  christos 
   2079  1.3  christos 	if ( ppolicy_get( op, e, &pp ) != LDAP_SUCCESS ) {
   2080  1.3  christos 		/* TODO: If there is no policy, should we check if */
   2081  1.3  christos 		goto done;
   2082  1.3  christos 	}
   2083  1.3  christos 
   2084  1.3  christos 	if ( !access_allowed( op, e, pp.ad, NULL, ACL_COMPARE, NULL ) ) {
   2085  1.3  christos 		goto done;
   2086  1.3  christos 	}
   2087  1.3  christos 
   2088  1.3  christos 	if ( attr_find( e->e_attrs, pp.ad ) == NULL ) {
   2089  1.3  christos 		goto done;
   2090  1.3  christos 	}
   2091  1.3  christos 
   2092  1.3  christos 	if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) {
   2093  1.3  christos 		pwtime = parse_time( a->a_nvals[0].bv_val );
   2094  1.3  christos 	}
   2095  1.3  christos 
   2096  1.3  christos 	if ( pp.pwdMaxAge && pwtime ) {
   2097  1.3  christos 		seconds_until_expiry = pwtime + pp.pwdMaxAge - now;
   2098  1.3  christos 		if ( seconds_until_expiry <= 0 ) isExpired = 1;
   2099  1.3  christos 		if ( pp.pwdGraceAuthNLimit ) {
   2100  1.3  christos 			if ( !pp.pwdGraceExpiry || seconds_until_expiry + pp.pwdGraceExpiry > 0 ) {
   2101  1.3  christos 				grace = pp.pwdGraceAuthNLimit;
   2102  1.3  christos 				if ( attr_find( e->e_attrs, ad_pwdGraceUseTime ) ) {
   2103  1.3  christos 					grace -= a->a_numvals;
   2104  1.3  christos 				}
   2105  1.3  christos 			}
   2106  1.3  christos 		}
   2107  1.3  christos 	}
   2108  1.3  christos 	if ( !isExpired && pp.pwdMaxIdle && (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) ) {
   2109  1.3  christos 		time_t lastbindtime = pwtime;
   2110  1.3  christos 
   2111  1.3  christos 		if ( (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) != NULL ) {
   2112  1.3  christos 			lastbindtime = parse_time( a->a_nvals[0].bv_val );
   2113  1.3  christos 		}
   2114  1.3  christos 
   2115  1.3  christos 		if ( lastbindtime ) {
   2116  1.3  christos 			int remaining_idle = lastbindtime + pp.pwdMaxIdle - now;
   2117  1.3  christos 			if ( remaining_idle <= 0 ) {
   2118  1.3  christos 				isExpired = 1;
   2119  1.3  christos 			} else if ( seconds_until_expiry == -1 || remaining_idle < seconds_until_expiry ) {
   2120  1.3  christos 				seconds_until_expiry = remaining_idle;
   2121  1.3  christos 			}
   2122  1.3  christos 		}
   2123  1.3  christos 	}
   2124  1.3  christos 
   2125  1.3  christos 	if ( isExpired || account_locked( op, e, &pp, NULL ) ) {
   2126  1.3  christos 		LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 };
   2127  1.3  christos 		time_t then, lockoutEnd = 0;
   2128  1.3  christos 
   2129  1.3  christos 		if ( isExpired ) more_info.remaining_grace = grace;
   2130  1.3  christos 
   2131  1.3  christos 		if ( (a = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
   2132  1.3  christos 			then = parse_time( a->a_vals[0].bv_val );
   2133  1.3  christos 			if ( then == 0 )
   2134  1.3  christos 				lockoutEnd = -1;
   2135  1.3  christos 
   2136  1.3  christos 			/* Still in the future? not yet in effect */
   2137  1.3  christos 			if ( now < then )
   2138  1.3  christos 				then = 0;
   2139  1.3  christos 
   2140  1.3  christos 			if ( !pp.pwdLockoutDuration )
   2141  1.3  christos 				lockoutEnd = -1;
   2142  1.3  christos 
   2143  1.3  christos 			if ( now < then + pp.pwdLockoutDuration )
   2144  1.3  christos 				lockoutEnd = then + pp.pwdLockoutDuration;
   2145  1.3  christos 		}
   2146  1.3  christos 
   2147  1.3  christos 		if ( (a = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
   2148  1.3  christos 			then = parse_time( a->a_vals[0].bv_val );
   2149  1.3  christos 			if ( lockoutEnd != -1 && then > lockoutEnd )
   2150  1.3  christos 				lockoutEnd = then;
   2151  1.3  christos 		}
   2152  1.3  christos 
   2153  1.3  christos 		if ( lockoutEnd > now ) {
   2154  1.3  christos 			more_info.inactive = 1;
   2155  1.3  christos 			more_info.seconds_before_unlock = lockoutEnd - now;
   2156  1.3  christos 		}
   2157  1.3  christos 
   2158  1.3  christos 		if ( pp.pwdMustChange &&
   2159  1.3  christos 			(a = attr_find( e->e_attrs, ad_pwdReset )) &&
   2160  1.3  christos 			bvmatch( &a->a_nvals[0], &slap_true_bv ) )
   2161  1.3  christos 		{
   2162  1.3  christos 			more_info.reset = 1;
   2163  1.3  christos 		}
   2164  1.3  christos 
   2165  1.3  christos 		add_account_control( op, rs, 0, -1, &more_info );
   2166  1.3  christos 	} else {
   2167  1.3  christos 		add_account_control( op, rs, 1, seconds_until_expiry, NULL );
   2168  1.3  christos 	}
   2169  1.3  christos 
   2170  1.3  christos done:
   2171  1.3  christos 	op->o_bd->bd_info = bi;
   2172  1.3  christos 	if ( e ) {
   2173  1.3  christos 		be_entry_release_r( op, e );
   2174  1.3  christos 	}
   2175  1.3  christos 	return SLAP_CB_CONTINUE;
   2176  1.3  christos }
   2177  1.3  christos 
   2178  1.3  christos static int
   2179  1.3  christos ppolicy_search(
   2180  1.3  christos 	Operation *op,
   2181  1.3  christos 	SlapReply *rs )
   2182  1.3  christos {
   2183  1.3  christos 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   2184  1.3  christos 	int rc = ppolicy_restrict( op, rs );
   2185  1.3  christos 
   2186  1.3  christos 	if ( rc != SLAP_CB_CONTINUE ) {
   2187  1.3  christos 		return rc;
   2188  1.3  christos 	}
   2189  1.3  christos 
   2190  1.3  christos 	if ( op->o_ctrlflag[account_usability_cid] ) {
   2191  1.3  christos 		slap_callback *cb;
   2192  1.3  christos 
   2193  1.3  christos 		cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx );
   2194  1.3  christos 
   2195  1.3  christos 		cb->sc_response = ppolicy_account_usability_entry_cb;
   2196  1.3  christos 		cb->sc_private = on;
   2197  1.3  christos 		overlay_callback_after_backover( op, cb, 1 );
   2198  1.3  christos 	}
   2199  1.3  christos 
   2200  1.3  christos 	return SLAP_CB_CONTINUE;
   2201  1.3  christos }
   2202  1.3  christos 
   2203  1.3  christos static int
   2204  1.2  christos ppolicy_compare_response(
   2205  1.2  christos 	Operation *op,
   2206  1.2  christos 	SlapReply *rs )
   2207  1.2  christos {
   2208  1.2  christos 	/* map compare responses to bind responses */
   2209  1.2  christos 	if ( rs->sr_err == LDAP_COMPARE_TRUE )
   2210  1.2  christos 		rs->sr_err = LDAP_SUCCESS;
   2211  1.2  christos 	else if ( rs->sr_err == LDAP_COMPARE_FALSE )
   2212  1.2  christos 		rs->sr_err = LDAP_INVALID_CREDENTIALS;
   2213  1.2  christos 
   2214  1.2  christos 	ppolicy_bind_response( op, rs );
   2215  1.2  christos 
   2216  1.2  christos 	/* map back to compare */
   2217  1.2  christos 	if ( rs->sr_err == LDAP_SUCCESS )
   2218  1.2  christos 		rs->sr_err = LDAP_COMPARE_TRUE;
   2219  1.2  christos 	else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS )
   2220  1.2  christos 		rs->sr_err = LDAP_COMPARE_FALSE;
   2221  1.2  christos 
   2222  1.2  christos 	return SLAP_CB_CONTINUE;
   2223  1.2  christos }
   2224  1.2  christos 
   2225  1.2  christos static int
   2226  1.2  christos ppolicy_compare(
   2227  1.2  christos 	Operation *op,
   2228  1.2  christos 	SlapReply *rs )
   2229  1.2  christos {
   2230  1.2  christos 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   2231  1.2  christos 
   2232  1.2  christos 	if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
   2233  1.2  christos 		return rs->sr_err;
   2234  1.2  christos 
   2235  1.2  christos 	/* Did we receive a password policy request control?
   2236  1.2  christos 	 * Are we testing the userPassword?
   2237  1.2  christos 	 */
   2238  1.2  christos 	if ( op->o_ctrlflag[ppolicy_cid] &&
   2239  1.2  christos 		op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) {
   2240  1.2  christos 		Entry *e;
   2241  1.2  christos 		int rc;
   2242  1.2  christos 		ppbind *ppb;
   2243  1.2  christos 		slap_callback *cb;
   2244  1.2  christos 
   2245  1.2  christos 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2246  1.2  christos 		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
   2247  1.2  christos 
   2248  1.2  christos 		if ( rc != LDAP_SUCCESS ) {
   2249  1.2  christos 			return SLAP_CB_CONTINUE;
   2250  1.2  christos 		}
   2251  1.2  christos 
   2252  1.2  christos 		cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
   2253  1.2  christos 			1, op->o_tmpmemctx );
   2254  1.2  christos 		ppb = (ppbind *)(cb+1);
   2255  1.4  christos 		ppb->pi = on->on_bi.bi_private;
   2256  1.4  christos 		ppb->be = op->o_bd->bd_self;
   2257  1.2  christos 		ppb->pErr = PP_noError;
   2258  1.2  christos 		ppb->send_ctrl = 1;
   2259  1.2  christos 		/* failures here don't lockout the connection */
   2260  1.2  christos 		ppb->set_restrict = 0;
   2261  1.2  christos 
   2262  1.2  christos 		/* Setup a callback so we can munge the result */
   2263  1.2  christos 
   2264  1.2  christos 		cb->sc_response = ppolicy_compare_response;
   2265  1.2  christos 		cb->sc_private = ppb;
   2266  1.2  christos 		overlay_callback_after_backover( op, cb, 1 );
   2267  1.2  christos 
   2268  1.2  christos 		op->o_bd->bd_info = (BackendInfo *)on;
   2269  1.2  christos 
   2270  1.3  christos 		if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
   2271  1.3  christos 			rc = account_locked( op, e, &ppb->pp, &ppb->mod );
   2272  1.3  christos 		}
   2273  1.2  christos 
   2274  1.2  christos 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2275  1.2  christos 		be_entry_release_r( op, e );
   2276  1.2  christos 
   2277  1.2  christos 		if ( rc ) {
   2278  1.2  christos 			ppb->pErr = PP_accountLocked;
   2279  1.2  christos 			send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL );
   2280  1.2  christos 			return rs->sr_err;
   2281  1.2  christos 		}
   2282  1.2  christos 	}
   2283  1.2  christos 	return SLAP_CB_CONTINUE;
   2284  1.2  christos }
   2285  1.2  christos 
   2286  1.2  christos static int
   2287  1.1     lukem ppolicy_add(
   2288  1.1     lukem 	Operation *op,
   2289  1.1     lukem 	SlapReply *rs )
   2290  1.1     lukem {
   2291  1.1     lukem 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   2292  1.1     lukem 	pp_info *pi = on->on_bi.bi_private;
   2293  1.1     lukem 	PassPolicy pp;
   2294  1.1     lukem 	Attribute *pa;
   2295  1.1     lukem 	const char *txt;
   2296  1.4  christos 	int is_pwdadmin = 0;
   2297  1.1     lukem 
   2298  1.1     lukem 	if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
   2299  1.1     lukem 		return rs->sr_err;
   2300  1.1     lukem 
   2301  1.3  christos 	/* If this is a replica, assume the provider checked everything */
   2302  1.4  christos 	if ( be_shadow_update( op ) )
   2303  1.1     lukem 		return SLAP_CB_CONTINUE;
   2304  1.1     lukem 
   2305  1.4  christos 	ppolicy_get( op, op->ora_e, &pp );
   2306  1.4  christos 
   2307  1.4  christos 	if ( access_allowed( op, op->ora_e, pp.ad, NULL, ACL_MANAGE, NULL ) ) {
   2308  1.4  christos 		is_pwdadmin = 1;
   2309  1.4  christos 	}
   2310  1.4  christos 
   2311  1.1     lukem 	/* Check for password in entry */
   2312  1.4  christos 	if ( (pa = attr_find( op->oq_add.rs_e->e_attrs, pp.ad )) ) {
   2313  1.1     lukem 		assert( pa->a_vals != NULL );
   2314  1.1     lukem 		assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
   2315  1.1     lukem 
   2316  1.1     lukem 		if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
   2317  1.1     lukem 			send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
   2318  1.1     lukem 			return rs->sr_err;
   2319  1.1     lukem 		}
   2320  1.1     lukem 
   2321  1.1     lukem 		/*
   2322  1.4  christos 		 * new entry contains a password - if we're not the password admin
   2323  1.1     lukem 		 * then we need to check that the password fits in with the
   2324  1.1     lukem 		 * security policy for the new entry.
   2325  1.1     lukem 		 */
   2326  1.3  christos 
   2327  1.4  christos 		if ( pp.pwdCheckQuality > 0 && !is_pwdadmin ) {
   2328  1.1     lukem 			struct berval *bv = &(pa->a_vals[0]);
   2329  1.1     lukem 			int rc, send_ctrl = 0;
   2330  1.1     lukem 			LDAPPasswordPolicyError pErr = PP_noError;
   2331  1.4  christos 			char errbuf[ERRBUFSIZ];
   2332  1.4  christos 			struct berval errmsg = BER_BVC( errbuf );
   2333  1.1     lukem 
   2334  1.1     lukem 			/* Did we receive a password policy request control? */
   2335  1.1     lukem 			if ( op->o_ctrlflag[ppolicy_cid] ) {
   2336  1.1     lukem 				send_ctrl = 1;
   2337  1.1     lukem 			}
   2338  1.4  christos 			rc = check_password_quality( bv, pi, &pp, &pErr, op->ora_e, &errmsg );
   2339  1.1     lukem 			if (rc != LDAP_SUCCESS) {
   2340  1.4  christos 				char *txt = errmsg.bv_val;
   2341  1.1     lukem 				LDAPControl **oldctrls = NULL;
   2342  1.1     lukem 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2343  1.1     lukem 				if ( send_ctrl ) {
   2344  1.1     lukem 					LDAPControl *ctrl = NULL;
   2345  1.2  christos 					ctrl = create_passcontrol( op, -1, -1, pErr );
   2346  1.1     lukem 					oldctrls = add_passcontrol( op, rs, ctrl );
   2347  1.1     lukem 				}
   2348  1.4  christos 				send_ldap_error( op, rs, rc, txt && txt[0] ? txt : "Password fails quality checking policy" );
   2349  1.4  christos 				if ( txt != errbuf ) {
   2350  1.2  christos 					free( txt );
   2351  1.2  christos 				}
   2352  1.1     lukem 				if ( send_ctrl ) {
   2353  1.1     lukem 					ctrls_cleanup( op, rs, oldctrls );
   2354  1.1     lukem 				}
   2355  1.1     lukem 				return rs->sr_err;
   2356  1.1     lukem 			}
   2357  1.1     lukem 		}
   2358  1.1     lukem 			/*
   2359  1.1     lukem 			 * A controversial bit. We hash cleartext
   2360  1.1     lukem 			 * passwords provided via add and modify operations
   2361  1.1     lukem 			 * You're not really supposed to do this, since
   2362  1.1     lukem 			 * the X.500 model says "store attributes" as they
   2363  1.1     lukem 			 * get provided. By default, this is what we do
   2364  1.1     lukem 			 *
   2365  1.1     lukem 			 * But if the hash_passwords flag is set, we hash
   2366  1.1     lukem 			 * any cleartext password attribute values via the
   2367  1.1     lukem 			 * default password hashing scheme.
   2368  1.1     lukem 			 */
   2369  1.1     lukem 		if ((pi->hash_passwords) &&
   2370  1.1     lukem 			(password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
   2371  1.1     lukem 			struct berval hpw;
   2372  1.1     lukem 
   2373  1.1     lukem 			slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
   2374  1.1     lukem 			if (hpw.bv_val == NULL) {
   2375  1.1     lukem 				/*
   2376  1.1     lukem 				 * hashing didn't work. Emit an error.
   2377  1.1     lukem 				 */
   2378  1.1     lukem 				rs->sr_err = LDAP_OTHER;
   2379  1.1     lukem 				rs->sr_text = txt;
   2380  1.1     lukem 				send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
   2381  1.1     lukem 				return rs->sr_err;
   2382  1.1     lukem 			}
   2383  1.1     lukem 
   2384  1.1     lukem 			memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
   2385  1.1     lukem 			ber_memfree( pa->a_vals[0].bv_val );
   2386  1.1     lukem 			pa->a_vals[0].bv_val = hpw.bv_val;
   2387  1.1     lukem 			pa->a_vals[0].bv_len = hpw.bv_len;
   2388  1.1     lukem 		}
   2389  1.1     lukem 
   2390  1.1     lukem 		/* If password aging is in effect, set the pwdChangedTime */
   2391  1.4  christos 		if ( ( pp.pwdMaxAge || pp.pwdMinAge ) &&
   2392  1.4  christos 				!attr_find( op->ora_e->e_attrs, ad_pwdChangedTime ) ) {
   2393  1.1     lukem 			struct berval timestamp;
   2394  1.1     lukem 			char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
   2395  1.1     lukem 			time_t now = slap_get_time();
   2396  1.1     lukem 
   2397  1.1     lukem 			timestamp.bv_val = timebuf;
   2398  1.1     lukem 			timestamp.bv_len = sizeof(timebuf);
   2399  1.1     lukem 			slap_timestamp( &now, &timestamp );
   2400  1.1     lukem 
   2401  1.1     lukem 			attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
   2402  1.1     lukem 		}
   2403  1.1     lukem 	}
   2404  1.1     lukem 	return SLAP_CB_CONTINUE;
   2405  1.1     lukem }
   2406  1.1     lukem 
   2407  1.1     lukem static int
   2408  1.1     lukem ppolicy_mod_cb( Operation *op, SlapReply *rs )
   2409  1.1     lukem {
   2410  1.1     lukem 	slap_callback *sc = op->o_callback;
   2411  1.1     lukem 	op->o_callback = sc->sc_next;
   2412  1.1     lukem 	if ( rs->sr_err == LDAP_SUCCESS ) {
   2413  1.1     lukem 		ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
   2414  1.1     lukem 		BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
   2415  1.1     lukem 	}
   2416  1.1     lukem 	op->o_tmpfree( sc, op->o_tmpmemctx );
   2417  1.1     lukem 	return SLAP_CB_CONTINUE;
   2418  1.1     lukem }
   2419  1.1     lukem 
   2420  1.1     lukem static int
   2421  1.3  christos ppolicy_text_cleanup( Operation *op, SlapReply *rs )
   2422  1.3  christos {
   2423  1.3  christos 	slap_callback *sc = op->o_callback;
   2424  1.3  christos 
   2425  1.3  christos 	if ( rs->sr_text == sc->sc_private ) {
   2426  1.3  christos 		rs->sr_text = NULL;
   2427  1.3  christos 	}
   2428  1.3  christos 	free( sc->sc_private );
   2429  1.3  christos 
   2430  1.3  christos 	op->o_callback = sc->sc_next;
   2431  1.3  christos 	op->o_tmpfree( sc, op->o_tmpmemctx );
   2432  1.3  christos 	return SLAP_CB_CONTINUE;
   2433  1.3  christos }
   2434  1.3  christos 
   2435  1.3  christos static int
   2436  1.1     lukem ppolicy_modify( Operation *op, SlapReply *rs )
   2437  1.1     lukem {
   2438  1.1     lukem 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
   2439  1.1     lukem 	pp_info			*pi = on->on_bi.bi_private;
   2440  1.3  christos 	int			i, rc, mod_pw_only, pwmod = 0, pwmop = -1, deladd,
   2441  1.2  christos 				hsize = 0, hskip;
   2442  1.1     lukem 	PassPolicy		pp;
   2443  1.1     lukem 	Modifications		*mods = NULL, *modtail = NULL,
   2444  1.1     lukem 				*ml, *delmod, *addmod;
   2445  1.1     lukem 	Attribute		*pa, *ha, at;
   2446  1.1     lukem 	const char		*txt;
   2447  1.4  christos 	char errbuf[ERRBUFSIZ];
   2448  1.1     lukem 	pw_hist			*tl = NULL, *p;
   2449  1.2  christos 	int			zapReset, send_ctrl = 0, free_txt = 0;
   2450  1.1     lukem 	Entry			*e;
   2451  1.1     lukem 	struct berval		newpw = BER_BVNULL, oldpw = BER_BVNULL,
   2452  1.1     lukem 				*bv, cr[2];
   2453  1.1     lukem 	LDAPPasswordPolicyError pErr = PP_noError;
   2454  1.2  christos 	LDAPControl		*ctrl = NULL;
   2455  1.1     lukem 	LDAPControl 		**oldctrls = NULL;
   2456  1.3  christos 	int			is_pwdexop = 0, is_pwdadmin = 0;
   2457  1.3  christos 	int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0,
   2458  1.3  christos 		got_del_success = 0;
   2459  1.2  christos 	int got_changed = 0, got_history = 0;
   2460  1.3  christos 	int have_policy = 0;
   2461  1.1     lukem 
   2462  1.1     lukem 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2463  1.1     lukem 	rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
   2464  1.1     lukem 	op->o_bd->bd_info = (BackendInfo *)on;
   2465  1.1     lukem 
   2466  1.1     lukem 	if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
   2467  1.3  christos 	if ( pi->disable_write ) return SLAP_CB_CONTINUE;
   2468  1.1     lukem 
   2469  1.1     lukem 	/* If this is a replica, we may need to tweak some of the
   2470  1.3  christos 	 * provider's modifications. Otherwise, just pass it through.
   2471  1.1     lukem 	 */
   2472  1.4  christos 	if ( be_shadow_update( op ) ) {
   2473  1.1     lukem 		Modifications **prev;
   2474  1.3  christos 		Attribute *a_grace, *a_lock, *a_fail, *a_success;
   2475  1.1     lukem 
   2476  1.1     lukem 		a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
   2477  1.1     lukem 		a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
   2478  1.1     lukem 		a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
   2479  1.3  christos 		a_success = attr_find( e->e_attrs, ad_pwdLastSuccess );
   2480  1.1     lukem 
   2481  1.1     lukem 		for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
   2482  1.1     lukem 
   2483  1.1     lukem 			if ( ml->sml_desc == slap_schema.si_ad_userPassword )
   2484  1.1     lukem 				got_pw = 1;
   2485  1.1     lukem 
   2486  1.1     lukem 			/* If we're deleting an attr that didn't exist,
   2487  1.1     lukem 			 * drop this delete op
   2488  1.1     lukem 			 */
   2489  1.2  christos 			if ( ml->sml_op == LDAP_MOD_DELETE ||
   2490  1.2  christos 					ml->sml_op == SLAP_MOD_SOFTDEL ) {
   2491  1.1     lukem 				int drop = 0;
   2492  1.1     lukem 
   2493  1.1     lukem 				if ( ml->sml_desc == ad_pwdGraceUseTime ) {
   2494  1.2  christos 					if ( !a_grace || got_del_grace ) {
   2495  1.2  christos 						drop = ml->sml_op == LDAP_MOD_DELETE;
   2496  1.2  christos 					} else {
   2497  1.2  christos 						got_del_grace = 1;
   2498  1.2  christos 					}
   2499  1.1     lukem 				} else
   2500  1.1     lukem 				if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
   2501  1.2  christos 					if ( !a_lock || got_del_lock ) {
   2502  1.2  christos 						drop = ml->sml_op == LDAP_MOD_DELETE;
   2503  1.2  christos 					} else {
   2504  1.2  christos 						got_del_lock = 1;
   2505  1.2  christos 					}
   2506  1.1     lukem 				} else
   2507  1.1     lukem 				if ( ml->sml_desc == ad_pwdFailureTime ) {
   2508  1.2  christos 					if ( !a_fail || got_del_fail ) {
   2509  1.2  christos 						drop = ml->sml_op == LDAP_MOD_DELETE;
   2510  1.2  christos 					} else {
   2511  1.2  christos 						got_del_fail = 1;
   2512  1.2  christos 					}
   2513  1.1     lukem 				}
   2514  1.3  christos 				if ( ml->sml_desc == ad_pwdLastSuccess ) {
   2515  1.3  christos 					if ( !a_success || got_del_success ) {
   2516  1.3  christos 						drop = ml->sml_op == LDAP_MOD_DELETE;
   2517  1.3  christos 					} else {
   2518  1.3  christos 						got_del_success = 1;
   2519  1.3  christos 					}
   2520  1.3  christos 				}
   2521  1.1     lukem 				if ( drop ) {
   2522  1.1     lukem 					*prev = ml->sml_next;
   2523  1.1     lukem 					ml->sml_next = NULL;
   2524  1.1     lukem 					slap_mods_free( ml, 1 );
   2525  1.1     lukem 					continue;
   2526  1.1     lukem 				}
   2527  1.1     lukem 			}
   2528  1.1     lukem 			prev = &ml->sml_next;
   2529  1.1     lukem 		}
   2530  1.1     lukem 
   2531  1.1     lukem 		/* If we're resetting the password, make sure grace, accountlock,
   2532  1.3  christos 		 * success, and failure also get removed.
   2533  1.1     lukem 		 */
   2534  1.1     lukem 		if ( got_pw ) {
   2535  1.1     lukem 			if ( a_grace && !got_del_grace ) {
   2536  1.1     lukem 				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
   2537  1.1     lukem 				ml->sml_op = LDAP_MOD_DELETE;
   2538  1.1     lukem 				ml->sml_flags = SLAP_MOD_INTERNAL;
   2539  1.1     lukem 				ml->sml_type.bv_val = NULL;
   2540  1.1     lukem 				ml->sml_desc = ad_pwdGraceUseTime;
   2541  1.1     lukem 				ml->sml_numvals = 0;
   2542  1.1     lukem 				ml->sml_values = NULL;
   2543  1.1     lukem 				ml->sml_nvalues = NULL;
   2544  1.1     lukem 				ml->sml_next = NULL;
   2545  1.1     lukem 				*prev = ml;
   2546  1.1     lukem 				prev = &ml->sml_next;
   2547  1.1     lukem 			}
   2548  1.1     lukem 			if ( a_lock && !got_del_lock ) {
   2549  1.1     lukem 				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
   2550  1.1     lukem 				ml->sml_op = LDAP_MOD_DELETE;
   2551  1.1     lukem 				ml->sml_flags = SLAP_MOD_INTERNAL;
   2552  1.1     lukem 				ml->sml_type.bv_val = NULL;
   2553  1.1     lukem 				ml->sml_desc = ad_pwdAccountLockedTime;
   2554  1.1     lukem 				ml->sml_numvals = 0;
   2555  1.1     lukem 				ml->sml_values = NULL;
   2556  1.1     lukem 				ml->sml_nvalues = NULL;
   2557  1.1     lukem 				ml->sml_next = NULL;
   2558  1.1     lukem 				*prev = ml;
   2559  1.1     lukem 			}
   2560  1.1     lukem 			if ( a_fail && !got_del_fail ) {
   2561  1.1     lukem 				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
   2562  1.1     lukem 				ml->sml_op = LDAP_MOD_DELETE;
   2563  1.1     lukem 				ml->sml_flags = SLAP_MOD_INTERNAL;
   2564  1.1     lukem 				ml->sml_type.bv_val = NULL;
   2565  1.1     lukem 				ml->sml_desc = ad_pwdFailureTime;
   2566  1.1     lukem 				ml->sml_numvals = 0;
   2567  1.1     lukem 				ml->sml_values = NULL;
   2568  1.1     lukem 				ml->sml_nvalues = NULL;
   2569  1.1     lukem 				ml->sml_next = NULL;
   2570  1.1     lukem 				*prev = ml;
   2571  1.1     lukem 			}
   2572  1.3  christos 			if ( a_success && !got_del_success ) {
   2573  1.3  christos 				ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
   2574  1.3  christos 				ml->sml_op = LDAP_MOD_DELETE;
   2575  1.3  christos 				ml->sml_flags = SLAP_MOD_INTERNAL;
   2576  1.3  christos 				ml->sml_type.bv_val = NULL;
   2577  1.3  christos 				ml->sml_desc = ad_pwdLastSuccess;
   2578  1.3  christos 				ml->sml_numvals = 0;
   2579  1.3  christos 				ml->sml_values = NULL;
   2580  1.3  christos 				ml->sml_nvalues = NULL;
   2581  1.3  christos 				ml->sml_next = NULL;
   2582  1.3  christos 				*prev = ml;
   2583  1.3  christos 			}
   2584  1.1     lukem 		}
   2585  1.1     lukem 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   2586  1.1     lukem 		be_entry_release_r( op, e );
   2587  1.1     lukem 		return SLAP_CB_CONTINUE;
   2588  1.1     lukem 	}
   2589  1.1     lukem 
   2590  1.1     lukem 	/* Did we receive a password policy request control? */
   2591  1.1     lukem 	if ( op->o_ctrlflag[ppolicy_cid] ) {
   2592  1.1     lukem 		send_ctrl = 1;
   2593  1.1     lukem 	}
   2594  1.1     lukem 
   2595  1.1     lukem 	/* See if this is a pwdModify exop. If so, we can
   2596  1.1     lukem 	 * access the plaintext passwords from that request.
   2597  1.1     lukem 	 */
   2598  1.1     lukem 	{
   2599  1.1     lukem 		slap_callback *sc;
   2600  1.1     lukem 
   2601  1.1     lukem 		for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
   2602  1.1     lukem 			if ( sc->sc_response == slap_null_cb &&
   2603  1.1     lukem 				sc->sc_private ) {
   2604  1.1     lukem 				req_pwdexop_s *qpw = sc->sc_private;
   2605  1.1     lukem 				newpw = qpw->rs_new;
   2606  1.1     lukem 				oldpw = qpw->rs_old;
   2607  1.2  christos 				is_pwdexop = 1;
   2608  1.1     lukem 			   	break;
   2609  1.1     lukem 			}
   2610  1.1     lukem 		}
   2611  1.1     lukem 	}
   2612  1.1     lukem 
   2613  1.3  christos 	/* ppolicy_hash_cleartext depends on pwmod being determined first */
   2614  1.3  christos 	if ( ppolicy_get( op, e, &pp ) == LDAP_SUCCESS ) {
   2615  1.3  christos 		have_policy = 1;
   2616  1.3  christos 	}
   2617  1.3  christos 
   2618  1.3  christos 	if ( access_allowed( op, e, pp.ad, NULL, ACL_MANAGE, NULL ) ) {
   2619  1.3  christos 		is_pwdadmin = 1;
   2620  1.3  christos 	}
   2621  1.1     lukem 
   2622  1.1     lukem 	for ( ml = op->orm_modlist,
   2623  1.1     lukem 			pwmod = 0, mod_pw_only = 1,
   2624  1.1     lukem 			deladd = 0, delmod = NULL,
   2625  1.1     lukem 			addmod = NULL,
   2626  1.1     lukem 			zapReset = 1;
   2627  1.1     lukem 		ml != NULL; modtail = ml, ml = ml->sml_next )
   2628  1.1     lukem 	{
   2629  1.1     lukem 		if ( ml->sml_desc == pp.ad ) {
   2630  1.1     lukem 			pwmod = 1;
   2631  1.1     lukem 			pwmop = ml->sml_op;
   2632  1.1     lukem 			if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
   2633  1.1     lukem 				(ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
   2634  1.1     lukem 			{
   2635  1.1     lukem 				deladd = 1;
   2636  1.1     lukem 				delmod = ml;
   2637  1.1     lukem 			}
   2638  1.1     lukem 
   2639  1.1     lukem 			if ((ml->sml_op == LDAP_MOD_ADD) ||
   2640  1.1     lukem 				(ml->sml_op == LDAP_MOD_REPLACE))
   2641  1.1     lukem 			{
   2642  1.1     lukem 				if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
   2643  1.1     lukem 					if ( deladd == 1 )
   2644  1.1     lukem 						deladd = 2;
   2645  1.1     lukem 
   2646  1.1     lukem 					/* FIXME: there's no easy way to ensure
   2647  1.1     lukem 					 * that add does not cause multiple
   2648  1.1     lukem 					 * userPassword values; one way (that
   2649  1.1     lukem 					 * would be consistent with the single
   2650  1.1     lukem 					 * password constraint) would be to turn
   2651  1.1     lukem 					 * add into replace); another would be
   2652  1.1     lukem 					 * to disallow add.
   2653  1.1     lukem 					 *
   2654  1.1     lukem 					 * Let's check at least that a single value
   2655  1.1     lukem 					 * is being added
   2656  1.1     lukem 					 */
   2657  1.1     lukem 					if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
   2658  1.1     lukem 						rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
   2659  1.1     lukem 						rs->sr_text = "Password policy only allows one password value";
   2660  1.1     lukem 						goto return_results;
   2661  1.1     lukem 					}
   2662  1.1     lukem 
   2663  1.1     lukem 					addmod = ml;
   2664  1.1     lukem 				} else {
   2665  1.1     lukem 					/* replace can have no values, add cannot */
   2666  1.1     lukem 					assert( ml->sml_op == LDAP_MOD_REPLACE );
   2667  1.1     lukem 				}
   2668  1.1     lukem 			}
   2669  1.1     lukem 
   2670  1.2  christos 		} else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
   2671  1.1     lukem 			mod_pw_only = 0;
   2672  1.1     lukem 			/* modifying something other than password */
   2673  1.1     lukem 		}
   2674  1.1     lukem 
   2675  1.1     lukem 		/*
   2676  1.1     lukem 		 * If there is a request to explicitly add a pwdReset
   2677  1.1     lukem 		 * attribute, then we suppress the normal behaviour on
   2678  1.1     lukem 		 * password change, which is to remove the pwdReset
   2679  1.1     lukem 		 * attribute.
   2680  1.1     lukem 		 *
   2681  1.1     lukem 		 * This enables an administrator to assign a new password
   2682  1.1     lukem 		 * and place a "must reset" flag on the entry, which will
   2683  1.1     lukem 		 * stay until the user explicitly changes his/her password.
   2684  1.1     lukem 		 */
   2685  1.1     lukem 		if (ml->sml_desc == ad_pwdReset ) {
   2686  1.1     lukem 			if ((ml->sml_op == LDAP_MOD_ADD) ||
   2687  1.1     lukem 				(ml->sml_op == LDAP_MOD_REPLACE))
   2688  1.1     lukem 				zapReset = 0;
   2689  1.1     lukem 		}
   2690  1.2  christos 		if ( ml->sml_op == LDAP_MOD_DELETE ) {
   2691  1.2  christos 			if ( ml->sml_desc == ad_pwdGraceUseTime ) {
   2692  1.2  christos 				got_del_grace = 1;
   2693  1.2  christos 			} else if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
   2694  1.2  christos 				got_del_lock = 1;
   2695  1.2  christos 			} else if ( ml->sml_desc == ad_pwdFailureTime ) {
   2696  1.2  christos 				got_del_fail = 1;
   2697  1.3  christos 			} else if ( ml->sml_desc == ad_pwdLastSuccess ) {
   2698  1.3  christos 				got_del_success = 1;
   2699  1.2  christos 			}
   2700  1.2  christos 		}
   2701  1.2  christos 		if ( ml->sml_desc == ad_pwdChangedTime ) {
   2702  1.2  christos 			got_changed = 1;
   2703  1.2  christos 		} else if (ml->sml_desc == ad_pwdHistory ) {
   2704  1.2  christos 			got_history = 1;
   2705  1.2  christos 		}
   2706  1.1     lukem 	}
   2707  1.1     lukem 
   2708  1.1     lukem 	if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
   2709  1.1     lukem 		if ( dn_match( &op->o_conn->c_ndn,
   2710  1.1     lukem 				&pwcons[op->o_conn->c_conn_idx].dn )) {
   2711  1.1     lukem 			Debug( LDAP_DEBUG_TRACE,
   2712  1.3  christos 				"connection restricted to password changing only\n" );
   2713  1.1     lukem 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
   2714  1.1     lukem 			rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
   2715  1.1     lukem 			pErr = PP_changeAfterReset;
   2716  1.1     lukem 			goto return_results;
   2717  1.1     lukem 		} else {
   2718  1.1     lukem 			ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
   2719  1.1     lukem 			BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
   2720  1.1     lukem 		}
   2721  1.1     lukem 	}
   2722  1.1     lukem 
   2723  1.1     lukem 	/*
   2724  1.1     lukem 	 * if we have a "safe password modify policy", then we need to check if we're doing
   2725  1.1     lukem 	 * a delete (with the old password), followed by an add (with the new password).
   2726  1.1     lukem 	 *
   2727  1.1     lukem 	 * If we got just a delete with nothing else, just let it go. We also skip all the checks if
   2728  1.1     lukem 	 * the root user is bound. Root can do anything, including avoid the policies.
   2729  1.1     lukem 	 */
   2730  1.1     lukem 
   2731  1.3  christos 	if (!have_policy || !pwmod) goto do_modify;
   2732  1.1     lukem 
   2733  1.1     lukem 	/*
   2734  1.1     lukem 	 * Build the password history list in ascending time order
   2735  1.1     lukem 	 * We need this, even if the user is root, in order to maintain
   2736  1.1     lukem 	 * the pwdHistory operational attributes properly.
   2737  1.1     lukem 	 */
   2738  1.1     lukem 	if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
   2739  1.1     lukem 		struct berval oldpw;
   2740  1.1     lukem 		time_t oldtime;
   2741  1.1     lukem 
   2742  1.1     lukem 		for(i=0; ha->a_nvals[i].bv_val; i++) {
   2743  1.1     lukem 			rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
   2744  1.1     lukem 				&oldtime, &oldpw );
   2745  1.1     lukem 
   2746  1.1     lukem 			if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
   2747  1.1     lukem 
   2748  1.1     lukem 			if (oldpw.bv_val) {
   2749  1.1     lukem 				add_to_pwd_history( &tl, oldtime, &oldpw,
   2750  1.1     lukem 					&(ha->a_nvals[i]) );
   2751  1.1     lukem 				oldpw.bv_val = NULL;
   2752  1.1     lukem 				oldpw.bv_len = 0;
   2753  1.1     lukem 			}
   2754  1.1     lukem 		}
   2755  1.1     lukem 		for(p=tl; p; p=p->next, hsize++); /* count history size */
   2756  1.1     lukem 	}
   2757  1.1     lukem 
   2758  1.3  christos 	if (is_pwdadmin) goto do_modify;
   2759  1.1     lukem 
   2760  1.2  christos 	/* NOTE: according to draft-behera-ldap-password-policy
   2761  1.2  christos 	 * pwdAllowUserChange == FALSE must only prevent pwd changes
   2762  1.2  christos 	 * by the user the pwd belongs to (ITS#7021) */
   2763  1.2  christos 	if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) {
   2764  1.1     lukem 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
   2765  1.1     lukem 		rs->sr_text = "User alteration of password is not allowed";
   2766  1.1     lukem 		pErr = PP_passwordModNotAllowed;
   2767  1.1     lukem 		goto return_results;
   2768  1.1     lukem 	}
   2769  1.1     lukem 
   2770  1.1     lukem 	/* Just deleting? */
   2771  1.1     lukem 	if (!addmod) {
   2772  1.1     lukem 		/* skip everything else */
   2773  1.1     lukem 		pwmod = 0;
   2774  1.1     lukem 		goto do_modify;
   2775  1.1     lukem 	}
   2776  1.1     lukem 
   2777  1.1     lukem 	/* This is a pwdModify exop that provided the old pw.
   2778  1.1     lukem 	 * We need to create a Delete mod for this old pw and
   2779  1.1     lukem 	 * let the matching value get found later
   2780  1.1     lukem 	 */
   2781  1.1     lukem 	if (pp.pwdSafeModify && oldpw.bv_val ) {
   2782  1.1     lukem 		ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
   2783  1.1     lukem 		ml->sml_op = LDAP_MOD_DELETE;
   2784  1.1     lukem 		ml->sml_flags = SLAP_MOD_INTERNAL;
   2785  1.1     lukem 		ml->sml_desc = pp.ad;
   2786  1.1     lukem 		ml->sml_type = pp.ad->ad_cname;
   2787  1.1     lukem 		ml->sml_numvals = 1;
   2788  1.1     lukem 		ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
   2789  1.1     lukem 		ber_dupbv( &ml->sml_values[0], &oldpw );
   2790  1.1     lukem 		BER_BVZERO( &ml->sml_values[1] );
   2791  1.1     lukem 		ml->sml_next = op->orm_modlist;
   2792  1.1     lukem 		op->orm_modlist = ml;
   2793  1.1     lukem 		delmod = ml;
   2794  1.1     lukem 		deladd = 2;
   2795  1.1     lukem 	}
   2796  1.1     lukem 
   2797  1.1     lukem 	if (pp.pwdSafeModify && deladd != 2) {
   2798  1.1     lukem 		Debug( LDAP_DEBUG_TRACE,
   2799  1.3  christos 			"change password must use DELETE followed by ADD/REPLACE\n" );
   2800  1.1     lukem 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
   2801  1.1     lukem 		rs->sr_text = "Must supply old password to be changed as well as new one";
   2802  1.1     lukem 		pErr = PP_mustSupplyOldPassword;
   2803  1.1     lukem 		goto return_results;
   2804  1.1     lukem 	}
   2805  1.1     lukem 
   2806  1.1     lukem 	/* Check age, but only if pwdReset is not TRUE */
   2807  1.1     lukem 	pa = attr_find( e->e_attrs, ad_pwdReset );
   2808  1.1     lukem 	if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
   2809  1.1     lukem 		pp.pwdMinAge > 0) {
   2810  1.1     lukem 		time_t pwtime = (time_t)-1, now;
   2811  1.1     lukem 		int age;
   2812  1.1     lukem 
   2813  1.1     lukem 		if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
   2814  1.1     lukem 			pwtime = parse_time( pa->a_nvals[0].bv_val );
   2815  1.1     lukem 		now = slap_get_time();
   2816  1.1     lukem 		age = (int)(now - pwtime);
   2817  1.1     lukem 		if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
   2818  1.1     lukem 			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
   2819  1.1     lukem 			rs->sr_text = "Password is too young to change";
   2820  1.1     lukem 			pErr = PP_passwordTooYoung;
   2821  1.1     lukem 			goto return_results;
   2822  1.1     lukem 		}
   2823  1.1     lukem 	}
   2824  1.1     lukem 
   2825  1.1     lukem 	/* pa is used in password history check below, be sure it's set */
   2826  1.1     lukem 	if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
   2827  1.1     lukem 		/*
   2828  1.1     lukem 		 * we have a password to check
   2829  1.1     lukem 		 */
   2830  1.1     lukem 		bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
   2831  1.1     lukem 		/* FIXME: no access checking? */
   2832  1.1     lukem 		rc = slap_passwd_check( op, NULL, pa, bv, &txt );
   2833  1.1     lukem 		if (rc != LDAP_SUCCESS) {
   2834  1.1     lukem 			Debug( LDAP_DEBUG_TRACE,
   2835  1.3  christos 				"old password check failed: %s\n", txt );
   2836  1.1     lukem 
   2837  1.1     lukem 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
   2838  1.1     lukem 			rs->sr_text = "Must supply correct old password to change to new one";
   2839  1.1     lukem 			pErr = PP_mustSupplyOldPassword;
   2840  1.1     lukem 			goto return_results;
   2841  1.1     lukem 
   2842  1.1     lukem 		} else {
   2843  1.1     lukem 			int i;
   2844  1.1     lukem 
   2845  1.1     lukem 			/*
   2846  1.1     lukem 			 * replace the delete value with the (possibly hashed)
   2847  1.1     lukem 			 * value which is currently in the password.
   2848  1.1     lukem 			 */
   2849  1.1     lukem 			for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
   2850  1.1     lukem 				free( delmod->sml_values[i].bv_val );
   2851  1.1     lukem 				BER_BVZERO( &delmod->sml_values[i] );
   2852  1.1     lukem 			}
   2853  1.1     lukem 			free( delmod->sml_values );
   2854  1.1     lukem 			delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
   2855  1.1     lukem 			BER_BVZERO( &delmod->sml_values[1] );
   2856  1.1     lukem 			ber_dupbv( &(delmod->sml_values[0]),  &(pa->a_nvals[0]) );
   2857  1.1     lukem 		}
   2858  1.1     lukem 	}
   2859  1.1     lukem 
   2860  1.1     lukem 	bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
   2861  1.1     lukem 	if (pp.pwdCheckQuality > 0) {
   2862  1.4  christos 		struct berval errmsg = BER_BVC( errbuf );
   2863  1.1     lukem 
   2864  1.4  christos 		rc = check_password_quality( bv, pi, &pp, &pErr, e, &errmsg );
   2865  1.1     lukem 		if (rc != LDAP_SUCCESS) {
   2866  1.1     lukem 			rs->sr_err = rc;
   2867  1.4  christos 			txt = errmsg.bv_val;
   2868  1.4  christos 			if ( txt && txt[0] ) {
   2869  1.2  christos 				rs->sr_text = txt;
   2870  1.4  christos 				if ( txt != errbuf )
   2871  1.4  christos 					free_txt = 1;
   2872  1.2  christos 			} else {
   2873  1.2  christos 				rs->sr_text = "Password fails quality checking policy";
   2874  1.2  christos 			}
   2875  1.1     lukem 			goto return_results;
   2876  1.1     lukem 		}
   2877  1.1     lukem 	}
   2878  1.1     lukem 
   2879  1.1     lukem 	/* If pwdInHistory is zero, passwords may be reused */
   2880  1.1     lukem 	if (pa && pp.pwdInHistory > 0) {
   2881  1.1     lukem 		/*
   2882  1.1     lukem 		 * Last check - the password history.
   2883  1.1     lukem 		 */
   2884  1.1     lukem 		/* FIXME: no access checking? */
   2885  1.1     lukem 		if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
   2886  1.1     lukem 			/*
   2887  1.1     lukem 			 * This is bad - it means that the user is attempting
   2888  1.1     lukem 			 * to set the password to the same as the old one.
   2889  1.1     lukem 			 */
   2890  1.1     lukem 			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
   2891  1.1     lukem 			rs->sr_text = "Password is not being changed from existing value";
   2892  1.1     lukem 			pErr = PP_passwordInHistory;
   2893  1.1     lukem 			goto return_results;
   2894  1.1     lukem 		}
   2895  1.2  christos 
   2896  1.2  christos 		/* We need this when reduce pwdInHistory */
   2897  1.2  christos 		hskip = hsize - pp.pwdInHistory;
   2898  1.2  christos 
   2899  1.1     lukem 		/*
   2900  1.1     lukem 		 * Iterate through the password history, and fail on any
   2901  1.1     lukem 		 * password matches.
   2902  1.1     lukem 		 */
   2903  1.1     lukem 		at = *pa;
   2904  1.1     lukem 		at.a_vals = cr;
   2905  1.1     lukem 		cr[1].bv_val = NULL;
   2906  1.1     lukem 		for(p=tl; p; p=p->next) {
   2907  1.2  christos 			if(hskip > 0){
   2908  1.2  christos 				hskip--;
   2909  1.2  christos 				continue;
   2910  1.2  christos 			}
   2911  1.1     lukem 			cr[0] = p->pw;
   2912  1.1     lukem 			/* FIXME: no access checking? */
   2913  1.1     lukem 			rc = slap_passwd_check( op, NULL, &at, bv, &txt );
   2914  1.1     lukem 
   2915  1.1     lukem 			if (rc != LDAP_SUCCESS) continue;
   2916  1.1     lukem 
   2917  1.1     lukem 			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
   2918  1.1     lukem 			rs->sr_text = "Password is in history of old passwords";
   2919  1.1     lukem 			pErr = PP_passwordInHistory;
   2920  1.1     lukem 			goto return_results;
   2921  1.1     lukem 		}
   2922  1.1     lukem 	}
   2923  1.1     lukem 
   2924  1.1     lukem do_modify:
   2925  1.1     lukem 	if (pwmod) {
   2926  1.1     lukem 		struct berval timestamp;
   2927  1.1     lukem 		char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
   2928  1.1     lukem 		time_t now = slap_get_time();
   2929  1.1     lukem 
   2930  1.1     lukem 		/* If the conn is restricted, set a callback to clear it
   2931  1.1     lukem 		 * if the pwmod succeeds
   2932  1.1     lukem 		 */
   2933  1.1     lukem 		if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
   2934  1.1     lukem 			slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
   2935  1.1     lukem 				op->o_tmpmemctx );
   2936  1.1     lukem 			sc->sc_next = op->o_callback;
   2937  1.1     lukem 			/* Must use sc_response to insure we reset on success, before
   2938  1.1     lukem 			 * the client sees the response. Must use sc_cleanup to insure
   2939  1.1     lukem 			 * that it gets cleaned up if sc_response is not called.
   2940  1.1     lukem 			 */
   2941  1.1     lukem 			sc->sc_response = ppolicy_mod_cb;
   2942  1.1     lukem 			sc->sc_cleanup = ppolicy_mod_cb;
   2943  1.1     lukem 			op->o_callback = sc;
   2944  1.1     lukem 		}
   2945  1.1     lukem 
   2946  1.1     lukem 		/*
   2947  1.1     lukem 		 * keep the necessary pwd.. operational attributes
   2948  1.1     lukem 		 * up to date.
   2949  1.1     lukem 		 */
   2950  1.1     lukem 
   2951  1.2  christos 		if (!got_changed) {
   2952  1.2  christos 			timestamp.bv_val = timebuf;
   2953  1.2  christos 			timestamp.bv_len = sizeof(timebuf);
   2954  1.2  christos 			slap_timestamp( &now, &timestamp );
   2955  1.1     lukem 
   2956  1.2  christos 			mods = NULL;
   2957  1.2  christos 			if (pwmop != LDAP_MOD_DELETE) {
   2958  1.2  christos 				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   2959  1.2  christos 				mods->sml_op = LDAP_MOD_REPLACE;
   2960  1.2  christos 				mods->sml_numvals = 1;
   2961  1.2  christos 				mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
   2962  1.2  christos 				mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
   2963  1.2  christos 
   2964  1.2  christos 				ber_dupbv( &mods->sml_values[0], &timestamp );
   2965  1.2  christos 				ber_dupbv( &mods->sml_nvalues[0], &timestamp );
   2966  1.2  christos 			} else if (attr_find(e->e_attrs, ad_pwdChangedTime )) {
   2967  1.2  christos 				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   2968  1.2  christos 				mods->sml_op = LDAP_MOD_DELETE;
   2969  1.2  christos 			}
   2970  1.2  christos 			if (mods) {
   2971  1.2  christos 				mods->sml_desc = ad_pwdChangedTime;
   2972  1.2  christos 				mods->sml_flags = SLAP_MOD_INTERNAL;
   2973  1.2  christos 				mods->sml_next = NULL;
   2974  1.2  christos 				modtail->sml_next = mods;
   2975  1.2  christos 				modtail = mods;
   2976  1.2  christos 			}
   2977  1.1     lukem 		}
   2978  1.1     lukem 
   2979  1.2  christos 		if (!got_del_grace && attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
   2980  1.1     lukem 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   2981  1.1     lukem 			mods->sml_op = LDAP_MOD_DELETE;
   2982  1.1     lukem 			mods->sml_desc = ad_pwdGraceUseTime;
   2983  1.1     lukem 			mods->sml_flags = SLAP_MOD_INTERNAL;
   2984  1.1     lukem 			mods->sml_next = NULL;
   2985  1.1     lukem 			modtail->sml_next = mods;
   2986  1.1     lukem 			modtail = mods;
   2987  1.1     lukem 		}
   2988  1.1     lukem 
   2989  1.2  christos 		if (!got_del_lock && attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
   2990  1.1     lukem 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   2991  1.1     lukem 			mods->sml_op = LDAP_MOD_DELETE;
   2992  1.1     lukem 			mods->sml_desc = ad_pwdAccountLockedTime;
   2993  1.1     lukem 			mods->sml_flags = SLAP_MOD_INTERNAL;
   2994  1.1     lukem 			mods->sml_next = NULL;
   2995  1.1     lukem 			modtail->sml_next = mods;
   2996  1.1     lukem 			modtail = mods;
   2997  1.1     lukem 		}
   2998  1.1     lukem 
   2999  1.2  christos 		if (!got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
   3000  1.1     lukem 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3001  1.1     lukem 			mods->sml_op = LDAP_MOD_DELETE;
   3002  1.1     lukem 			mods->sml_desc = ad_pwdFailureTime;
   3003  1.1     lukem 			mods->sml_flags = SLAP_MOD_INTERNAL;
   3004  1.1     lukem 			mods->sml_next = NULL;
   3005  1.1     lukem 			modtail->sml_next = mods;
   3006  1.1     lukem 			modtail = mods;
   3007  1.1     lukem 		}
   3008  1.1     lukem 
   3009  1.3  christos 		if ( zapReset ) {
   3010  1.3  christos 			/*
   3011  1.3  christos 			 * ITS#7084 Is this a modification by the password
   3012  1.3  christos 			 * administrator? Then force a reset if configured.
   3013  1.3  christos 			 * Otherwise clear it.
   3014  1.3  christos 			 */
   3015  1.3  christos 			if ( pp.pwdMustChange && is_pwdadmin ) {
   3016  1.3  christos 				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3017  1.3  christos 				mods->sml_op = LDAP_MOD_REPLACE;
   3018  1.3  christos 				mods->sml_desc = ad_pwdReset;
   3019  1.3  christos 				mods->sml_flags = SLAP_MOD_INTERNAL;
   3020  1.3  christos 				mods->sml_numvals = 1;
   3021  1.3  christos 				mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
   3022  1.3  christos 				mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
   3023  1.3  christos 
   3024  1.3  christos 				ber_dupbv( &mods->sml_values[0], (struct berval *)&slap_true_bv );
   3025  1.3  christos 				ber_dupbv( &mods->sml_nvalues[0], (struct berval *)&slap_true_bv );
   3026  1.3  christos 
   3027  1.3  christos 				mods->sml_next = NULL;
   3028  1.3  christos 				modtail->sml_next = mods;
   3029  1.3  christos 				modtail = mods;
   3030  1.3  christos 			} else if ( attr_find( e->e_attrs, ad_pwdReset ) ) {
   3031  1.3  christos 				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3032  1.3  christos 				mods->sml_op = LDAP_MOD_DELETE;
   3033  1.3  christos 				mods->sml_desc = ad_pwdReset;
   3034  1.3  christos 				mods->sml_flags = SLAP_MOD_INTERNAL;
   3035  1.3  christos 				mods->sml_next = NULL;
   3036  1.3  christos 				modtail->sml_next = mods;
   3037  1.3  christos 				modtail = mods;
   3038  1.3  christos 			}
   3039  1.3  christos 		}
   3040  1.3  christos 
   3041  1.3  christos 		/* TODO: do we remove pwdLastSuccess or set it to 'now'? */
   3042  1.3  christos 		if (!got_del_success && attr_find(e->e_attrs, ad_pwdLastSuccess )){
   3043  1.1     lukem 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3044  1.1     lukem 			mods->sml_op = LDAP_MOD_DELETE;
   3045  1.1     lukem 			mods->sml_flags = SLAP_MOD_INTERNAL;
   3046  1.3  christos 			mods->sml_desc = ad_pwdLastSuccess;
   3047  1.1     lukem 			mods->sml_next = NULL;
   3048  1.1     lukem 			modtail->sml_next = mods;
   3049  1.1     lukem 			modtail = mods;
   3050  1.1     lukem 		}
   3051  1.1     lukem 
   3052  1.2  christos 		/* Delete all pwdInHistory attribute */
   3053  1.2  christos 		if (!got_history && pp.pwdInHistory == 0 &&
   3054  1.2  christos             attr_find(e->e_attrs, ad_pwdHistory )){
   3055  1.2  christos 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3056  1.2  christos 			mods->sml_op = LDAP_MOD_DELETE;
   3057  1.2  christos 			mods->sml_flags = SLAP_MOD_INTERNAL;
   3058  1.2  christos 			mods->sml_desc = ad_pwdHistory;
   3059  1.2  christos 			mods->sml_next = NULL;
   3060  1.2  christos 			modtail->sml_next = mods;
   3061  1.2  christos 			modtail = mods;
   3062  1.2  christos 		}
   3063  1.2  christos 
   3064  1.2  christos 		if (!got_history && pp.pwdInHistory > 0){
   3065  1.1     lukem 			if (hsize >= pp.pwdInHistory) {
   3066  1.1     lukem 				/*
   3067  1.1     lukem 				 * We use the >= operator, since we are going to add
   3068  1.1     lukem 				 * the existing password attribute value into the
   3069  1.1     lukem 				 * history - thus the cardinality of history values is
   3070  1.1     lukem 				 * about to rise by one.
   3071  1.1     lukem 				 *
   3072  1.1     lukem 				 * If this would push it over the limit of history
   3073  1.1     lukem 				 * values (remembering - the password policy could have
   3074  1.1     lukem 				 * changed since the password was last altered), we must
   3075  1.1     lukem 				 * delete at least 1 value from the pwdHistory list.
   3076  1.1     lukem 				 *
   3077  1.1     lukem 				 * In fact, we delete '(#pwdHistory attrs - max pwd
   3078  1.1     lukem 				 * history length) + 1' values, starting with the oldest.
   3079  1.1     lukem 				 * This is easily evaluated, since the linked list is
   3080  1.1     lukem 				 * created in ascending time order.
   3081  1.1     lukem 				 */
   3082  1.1     lukem 				mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3083  1.1     lukem 				mods->sml_op = LDAP_MOD_DELETE;
   3084  1.1     lukem 				mods->sml_flags = SLAP_MOD_INTERNAL;
   3085  1.1     lukem 				mods->sml_desc = ad_pwdHistory;
   3086  1.1     lukem 				mods->sml_numvals = hsize - pp.pwdInHistory + 1;
   3087  1.1     lukem 				mods->sml_values = ch_calloc( sizeof( struct berval ),
   3088  1.1     lukem 					hsize - pp.pwdInHistory + 2 );
   3089  1.1     lukem 				BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
   3090  1.1     lukem 				for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
   3091  1.1     lukem 					BER_BVZERO( &mods->sml_values[i] );
   3092  1.1     lukem 					ber_dupbv( &(mods->sml_values[i]), &p->bv );
   3093  1.1     lukem 				}
   3094  1.1     lukem 				mods->sml_next = NULL;
   3095  1.1     lukem 				modtail->sml_next = mods;
   3096  1.1     lukem 				modtail = mods;
   3097  1.1     lukem 			}
   3098  1.1     lukem 			free_pwd_history_list( &tl );
   3099  1.1     lukem 
   3100  1.1     lukem 			/*
   3101  1.1     lukem 			 * Now add the existing password into the history list.
   3102  1.1     lukem 			 * This will be executed even if the operation is to delete
   3103  1.1     lukem 			 * the password entirely.
   3104  1.1     lukem 			 *
   3105  1.1     lukem 			 * This isn't in the spec explicitly, but it seems to make
   3106  1.1     lukem 			 * sense that the password history list is the list of all
   3107  1.1     lukem 			 * previous passwords - even if they were deleted. Thus, if
   3108  1.1     lukem 			 * someone tries to add a historical password at some future
   3109  1.1     lukem 			 * point, it will fail.
   3110  1.1     lukem 			 */
   3111  1.1     lukem 			if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
   3112  1.1     lukem 				mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
   3113  1.1     lukem 				mods->sml_op = LDAP_MOD_ADD;
   3114  1.1     lukem 				mods->sml_flags = SLAP_MOD_INTERNAL;
   3115  1.1     lukem 				mods->sml_type.bv_val = NULL;
   3116  1.1     lukem 				mods->sml_desc = ad_pwdHistory;
   3117  1.1     lukem 				mods->sml_nvalues = NULL;
   3118  1.1     lukem 				mods->sml_numvals = 1;
   3119  1.1     lukem 				mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
   3120  1.1     lukem 				mods->sml_values[ 1 ].bv_val = NULL;
   3121  1.1     lukem 				mods->sml_values[ 1 ].bv_len = 0;
   3122  1.1     lukem 				make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
   3123  1.1     lukem 				mods->sml_next = NULL;
   3124  1.1     lukem 				modtail->sml_next = mods;
   3125  1.1     lukem 				modtail = mods;
   3126  1.1     lukem 
   3127  1.1     lukem 			} else {
   3128  1.1     lukem 				Debug( LDAP_DEBUG_TRACE,
   3129  1.3  christos 				"ppolicy_modify: password attr lookup failed\n" );
   3130  1.1     lukem 			}
   3131  1.1     lukem 		}
   3132  1.1     lukem 
   3133  1.1     lukem 		/*
   3134  1.1     lukem 		 * Controversial bit here. If the new password isn't hashed
   3135  1.1     lukem 		 * (ie, is cleartext), we probably should hash it according
   3136  1.1     lukem 		 * to the default hash. The reason for this is that we want
   3137  1.1     lukem 		 * to use the policy if possible, but if we hash the password
   3138  1.1     lukem 		 * before, then we're going to run into trouble when it
   3139  1.1     lukem 		 * comes time to check the password.
   3140  1.1     lukem 		 *
   3141  1.1     lukem 		 * Now, the right thing to do is to use the extended password
   3142  1.1     lukem 		 * modify operation, but not all software can do this,
   3143  1.1     lukem 		 * therefore it makes sense to hash the new password, now
   3144  1.1     lukem 		 * we know it passes the policy requirements.
   3145  1.1     lukem 		 *
   3146  1.1     lukem 		 * Of course, if the password is already hashed, then we
   3147  1.1     lukem 		 * leave it alone.
   3148  1.1     lukem 		 */
   3149  1.1     lukem 
   3150  1.1     lukem 		if ((pi->hash_passwords) && (addmod) && !newpw.bv_val &&
   3151  1.1     lukem 			(password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
   3152  1.1     lukem 		{
   3153  1.1     lukem 			struct berval hpw, bv;
   3154  1.1     lukem 
   3155  1.1     lukem 			slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
   3156  1.1     lukem 			if (hpw.bv_val == NULL) {
   3157  1.1     lukem 					/*
   3158  1.1     lukem 					 * hashing didn't work. Emit an error.
   3159  1.1     lukem 					 */
   3160  1.1     lukem 				rs->sr_err = LDAP_OTHER;
   3161  1.1     lukem 				rs->sr_text = txt;
   3162  1.1     lukem 				goto return_results;
   3163  1.1     lukem 			}
   3164  1.1     lukem 			bv = addmod->sml_values[0];
   3165  1.1     lukem 				/* clear and discard the clear password */
   3166  1.1     lukem 			memset(bv.bv_val, 0, bv.bv_len);
   3167  1.1     lukem 			ber_memfree(bv.bv_val);
   3168  1.1     lukem 			addmod->sml_values[0] = hpw;
   3169  1.1     lukem 		}
   3170  1.3  christos 	} else {
   3171  1.3  christos 		/* ITS#8762 Make sure we drop pwdFailureTime if unlocking */
   3172  1.3  christos 		if (got_del_lock && !got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
   3173  1.3  christos 			mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
   3174  1.3  christos 			mods->sml_op = LDAP_MOD_DELETE;
   3175  1.3  christos 			mods->sml_desc = ad_pwdFailureTime;
   3176  1.3  christos 			mods->sml_flags = SLAP_MOD_INTERNAL;
   3177  1.3  christos 			mods->sml_next = NULL;
   3178  1.3  christos 			modtail->sml_next = mods;
   3179  1.3  christos 			modtail = mods;
   3180  1.3  christos 		}
   3181  1.1     lukem 	}
   3182  1.1     lukem 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
   3183  1.1     lukem 	be_entry_release_r( op, e );
   3184  1.1     lukem 	return SLAP_CB_CONTINUE;
   3185  1.1     lukem 
   3186  1.1     lukem return_results:
   3187  1.1     lukem 	free_pwd_history_list( &tl );
   3188  1.1     lukem 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
   3189  1.1     lukem 	be_entry_release_r( op, e );
   3190  1.1     lukem 	if ( send_ctrl ) {
   3191  1.2  christos 		ctrl = create_passcontrol( op, -1, -1, pErr );
   3192  1.1     lukem 		oldctrls = add_passcontrol( op, rs, ctrl );
   3193  1.1     lukem 	}
   3194  1.1     lukem 	send_ldap_result( op, rs );
   3195  1.2  christos 	if ( free_txt ) {
   3196  1.3  christos 		if ( is_pwdexop ) {
   3197  1.3  christos 			slap_callback *cb;
   3198  1.3  christos 			cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
   3199  1.3  christos 				1, op->o_tmpmemctx );
   3200  1.3  christos 
   3201  1.3  christos 			/* Setup a callback so we can free the text when sent */
   3202  1.3  christos 			cb->sc_cleanup = ppolicy_text_cleanup;
   3203  1.3  christos 			cb->sc_private = (void *)txt;
   3204  1.3  christos 			overlay_callback_after_backover( op, cb, 1 );
   3205  1.3  christos 		} else {
   3206  1.3  christos 			if ( rs->sr_text == txt ) {
   3207  1.3  christos 				rs->sr_text = NULL;
   3208  1.3  christos 			}
   3209  1.3  christos 			free( (char *)txt );
   3210  1.3  christos 		}
   3211  1.2  christos 	}
   3212  1.1     lukem 	if ( send_ctrl ) {
   3213  1.2  christos 		if ( is_pwdexop ) {
   3214  1.2  christos 			if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
   3215  1.2  christos 				op->o_tmpfree( oldctrls, op->o_tmpmemctx );
   3216  1.2  christos 			}
   3217  1.2  christos 			oldctrls = NULL;
   3218  1.2  christos 			rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
   3219  1.2  christos 
   3220  1.2  christos 		} else {
   3221  1.2  christos 			ctrls_cleanup( op, rs, oldctrls );
   3222  1.2  christos 		}
   3223  1.1     lukem 	}
   3224  1.1     lukem 	return rs->sr_err;
   3225  1.1     lukem }
   3226  1.1     lukem 
   3227  1.1     lukem static int
   3228  1.1     lukem ppolicy_parseCtrl(
   3229  1.1     lukem 	Operation *op,
   3230  1.1     lukem 	SlapReply *rs,
   3231  1.1     lukem 	LDAPControl *ctrl )
   3232  1.1     lukem {
   3233  1.1     lukem 	if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
   3234  1.1     lukem 		rs->sr_text = "passwordPolicyRequest control value not absent";
   3235  1.1     lukem 		return LDAP_PROTOCOL_ERROR;
   3236  1.1     lukem 	}
   3237  1.1     lukem 	op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
   3238  1.1     lukem 		? SLAP_CONTROL_CRITICAL
   3239  1.1     lukem 		: SLAP_CONTROL_NONCRITICAL;
   3240  1.1     lukem 
   3241  1.1     lukem 	return LDAP_SUCCESS;
   3242  1.1     lukem }
   3243  1.1     lukem 
   3244  1.1     lukem static int
   3245  1.3  christos ppolicy_au_parseCtrl(
   3246  1.3  christos 	Operation *op,
   3247  1.3  christos 	SlapReply *rs,
   3248  1.3  christos 	LDAPControl *ctrl )
   3249  1.3  christos {
   3250  1.3  christos 	if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
   3251  1.3  christos 		rs->sr_text = "account usability control value not absent";
   3252  1.3  christos 		return LDAP_PROTOCOL_ERROR;
   3253  1.3  christos 	}
   3254  1.3  christos 	op->o_ctrlflag[account_usability_cid] = ctrl->ldctl_iscritical
   3255  1.3  christos 		? SLAP_CONTROL_CRITICAL
   3256  1.3  christos 		: SLAP_CONTROL_NONCRITICAL;
   3257  1.3  christos 
   3258  1.3  christos 	return LDAP_SUCCESS;
   3259  1.3  christos }
   3260  1.3  christos 
   3261  1.3  christos static int
   3262  1.1     lukem attrPretty(
   3263  1.1     lukem 	Syntax *syntax,
   3264  1.1     lukem 	struct berval *val,
   3265  1.1     lukem 	struct berval *out,
   3266  1.1     lukem 	void *ctx )
   3267  1.1     lukem {
   3268  1.1     lukem 	AttributeDescription *ad = NULL;
   3269  1.1     lukem 	const char *err;
   3270  1.1     lukem 	int code;
   3271  1.1     lukem 
   3272  1.1     lukem 	code = slap_bv2ad( val, &ad, &err );
   3273  1.1     lukem 	if ( !code ) {
   3274  1.1     lukem 		ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
   3275  1.1     lukem 	}
   3276  1.1     lukem 	return code;
   3277  1.1     lukem }
   3278  1.1     lukem 
   3279  1.1     lukem static int
   3280  1.1     lukem attrNormalize(
   3281  1.1     lukem 	slap_mask_t use,
   3282  1.1     lukem 	Syntax *syntax,
   3283  1.1     lukem 	MatchingRule *mr,
   3284  1.1     lukem 	struct berval *val,
   3285  1.1     lukem 	struct berval *out,
   3286  1.1     lukem 	void *ctx )
   3287  1.1     lukem {
   3288  1.1     lukem 	AttributeDescription *ad = NULL;
   3289  1.1     lukem 	const char *err;
   3290  1.1     lukem 	int code;
   3291  1.1     lukem 
   3292  1.1     lukem 	code = slap_bv2ad( val, &ad, &err );
   3293  1.1     lukem 	if ( !code ) {
   3294  1.1     lukem 		ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
   3295  1.1     lukem 	}
   3296  1.1     lukem 	return code;
   3297  1.1     lukem }
   3298  1.1     lukem 
   3299  1.1     lukem static int
   3300  1.1     lukem ppolicy_db_init(
   3301  1.1     lukem 	BackendDB *be,
   3302  1.1     lukem 	ConfigReply *cr
   3303  1.1     lukem )
   3304  1.1     lukem {
   3305  1.1     lukem 	slap_overinst *on = (slap_overinst *) be->bd_info;
   3306  1.3  christos 	pp_info *pi;
   3307  1.1     lukem 
   3308  1.2  christos 	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
   3309  1.2  christos 		/* do not allow slapo-ppolicy to be global by now (ITS#5858) */
   3310  1.2  christos 		if ( cr ){
   3311  1.2  christos 			snprintf( cr->msg, sizeof(cr->msg),
   3312  1.2  christos 				"slapo-ppolicy cannot be global" );
   3313  1.3  christos 			Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
   3314  1.2  christos 		}
   3315  1.2  christos 		return 1;
   3316  1.2  christos 	}
   3317  1.2  christos 
   3318  1.3  christos 	pi = on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
   3319  1.1     lukem 
   3320  1.2  christos 	if ( !pwcons ) {
   3321  1.1     lukem 		/* accommodate for c_conn_idx == -1 */
   3322  1.1     lukem 		pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
   3323  1.1     lukem 		pwcons++;
   3324  1.1     lukem 	}
   3325  1.1     lukem 
   3326  1.2  christos 	ov_count++;
   3327  1.2  christos 
   3328  1.3  christos 	ldap_pvt_thread_mutex_init( &pi->pwdFailureTime_mutex );
   3329  1.3  christos 
   3330  1.1     lukem 	return 0;
   3331  1.1     lukem }
   3332  1.1     lukem 
   3333  1.1     lukem static int
   3334  1.1     lukem ppolicy_db_open(
   3335  1.1     lukem 	BackendDB *be,
   3336  1.1     lukem 	ConfigReply *cr
   3337  1.1     lukem )
   3338  1.1     lukem {
   3339  1.3  christos 	int rc;
   3340  1.3  christos 
   3341  1.3  christos 	if ( (rc = overlay_register_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY )) != LDAP_SUCCESS ) {
   3342  1.3  christos 		return rc;
   3343  1.3  christos 	}
   3344  1.1     lukem 	return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
   3345  1.1     lukem }
   3346  1.1     lukem 
   3347  1.1     lukem static int
   3348  1.2  christos ppolicy_db_close(
   3349  1.2  christos 	BackendDB *be,
   3350  1.2  christos 	ConfigReply *cr
   3351  1.2  christos )
   3352  1.2  christos {
   3353  1.2  christos #ifdef SLAP_CONFIG_DELETE
   3354  1.2  christos 	overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
   3355  1.3  christos 	overlay_unregister_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY );
   3356  1.2  christos #endif /* SLAP_CONFIG_DELETE */
   3357  1.2  christos 
   3358  1.2  christos 	return 0;
   3359  1.2  christos }
   3360  1.2  christos 
   3361  1.2  christos static int
   3362  1.2  christos ppolicy_db_destroy(
   3363  1.1     lukem 	BackendDB *be,
   3364  1.1     lukem 	ConfigReply *cr
   3365  1.1     lukem )
   3366  1.1     lukem {
   3367  1.1     lukem 	slap_overinst *on = (slap_overinst *) be->bd_info;
   3368  1.1     lukem 	pp_info *pi = on->on_bi.bi_private;
   3369  1.1     lukem 
   3370  1.2  christos 	on->on_bi.bi_private = NULL;
   3371  1.3  christos 	ldap_pvt_thread_mutex_destroy( &pi->pwdFailureTime_mutex );
   3372  1.2  christos 	free( pi->def_policy.bv_val );
   3373  1.2  christos 	free( pi );
   3374  1.2  christos 
   3375  1.1     lukem 	ov_count--;
   3376  1.1     lukem 	if ( ov_count <=0 && pwcons ) {
   3377  1.2  christos 		pw_conn *pwc = pwcons;
   3378  1.1     lukem 		pwcons = NULL;
   3379  1.2  christos 		pwc--;
   3380  1.2  christos 		ch_free( pwc );
   3381  1.1     lukem 	}
   3382  1.1     lukem 	return 0;
   3383  1.1     lukem }
   3384  1.1     lukem 
   3385  1.1     lukem static char *extops[] = {
   3386  1.1     lukem 	LDAP_EXOP_MODIFY_PASSWD,
   3387  1.1     lukem 	NULL
   3388  1.1     lukem };
   3389  1.1     lukem 
   3390  1.1     lukem static slap_overinst ppolicy;
   3391  1.1     lukem 
   3392  1.1     lukem int ppolicy_initialize()
   3393  1.1     lukem {
   3394  1.1     lukem 	int i, code;
   3395  1.1     lukem 
   3396  1.1     lukem 	for (i=0; pwd_OpSchema[i].def; i++) {
   3397  1.1     lukem 		code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
   3398  1.1     lukem 		if ( code ) {
   3399  1.1     lukem 			Debug( LDAP_DEBUG_ANY,
   3400  1.3  christos 				"ppolicy_initialize: register_at failed\n" );
   3401  1.1     lukem 			return code;
   3402  1.1     lukem 		}
   3403  1.1     lukem 		/* Allow Manager to set these as needed */
   3404  1.1     lukem 		if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
   3405  1.1     lukem 			(*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
   3406  1.1     lukem 				SLAP_AT_MANAGEABLE;
   3407  1.1     lukem 		}
   3408  1.1     lukem 	}
   3409  1.3  christos 	ad_pwdLastSuccess = slap_schema.si_ad_pwdLastSuccess;
   3410  1.3  christos 	{
   3411  1.3  christos 		Syntax *syn;
   3412  1.3  christos 		MatchingRule *mr;
   3413  1.3  christos 
   3414  1.3  christos 		syn = ch_malloc( sizeof( Syntax ));
   3415  1.3  christos 		*syn = *ad_pwdAttribute->ad_type->sat_syntax;
   3416  1.3  christos 		syn->ssyn_pretty = attrPretty;
   3417  1.3  christos 		ad_pwdAttribute->ad_type->sat_syntax = syn;
   3418  1.3  christos 
   3419  1.3  christos 		mr = ch_malloc( sizeof( MatchingRule ));
   3420  1.3  christos 		*mr = *ad_pwdAttribute->ad_type->sat_equality;
   3421  1.3  christos 		mr->smr_normalize = attrNormalize;
   3422  1.3  christos 		ad_pwdAttribute->ad_type->sat_equality = mr;
   3423  1.3  christos 	}
   3424  1.3  christos 
   3425  1.3  christos 	for (i=0; pwd_ocs[i]; i++) {
   3426  1.3  christos 		code = register_oc( pwd_ocs[i], NULL, 0 );
   3427  1.3  christos 		if ( code ) {
   3428  1.3  christos 			Debug( LDAP_DEBUG_ANY, "ppolicy_initialize: "
   3429  1.3  christos 				"register_oc failed\n" );
   3430  1.3  christos 			return code;
   3431  1.3  christos 		}
   3432  1.3  christos 	}
   3433  1.1     lukem 
   3434  1.1     lukem 	code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
   3435  1.3  christos 		SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY, extops,
   3436  1.1     lukem 		ppolicy_parseCtrl, &ppolicy_cid );
   3437  1.1     lukem 	if ( code != LDAP_SUCCESS ) {
   3438  1.3  christos 		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
   3439  1.3  christos 		return code;
   3440  1.3  christos 	}
   3441  1.3  christos 
   3442  1.3  christos 	code = register_supported_control( LDAP_CONTROL_X_ACCOUNT_USABILITY,
   3443  1.3  christos 		SLAP_CTRL_SEARCH, NULL,
   3444  1.3  christos 		ppolicy_au_parseCtrl, &account_usability_cid );
   3445  1.3  christos 	if ( code != LDAP_SUCCESS ) {
   3446  1.3  christos 		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
   3447  1.3  christos 		return code;
   3448  1.3  christos 	}
   3449  1.3  christos 
   3450  1.3  christos 	/* We don't expect to receive these controls, only send them */
   3451  1.3  christos 	code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRED,
   3452  1.3  christos 		0, NULL, NULL, NULL );
   3453  1.3  christos 	if ( code != LDAP_SUCCESS ) {
   3454  1.3  christos 		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
   3455  1.3  christos 		return code;
   3456  1.3  christos 	}
   3457  1.3  christos 
   3458  1.3  christos 	code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRING,
   3459  1.3  christos 		0, NULL, NULL, NULL );
   3460  1.3  christos 	if ( code != LDAP_SUCCESS ) {
   3461  1.3  christos 		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
   3462  1.1     lukem 		return code;
   3463  1.1     lukem 	}
   3464  1.1     lukem 
   3465  1.1     lukem 	ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
   3466  1.1     lukem 
   3467  1.1     lukem 	ppolicy.on_bi.bi_type = "ppolicy";
   3468  1.3  christos 	ppolicy.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
   3469  1.1     lukem 	ppolicy.on_bi.bi_db_init = ppolicy_db_init;
   3470  1.1     lukem 	ppolicy.on_bi.bi_db_open = ppolicy_db_open;
   3471  1.2  christos 	ppolicy.on_bi.bi_db_close = ppolicy_db_close;
   3472  1.2  christos 	ppolicy.on_bi.bi_db_destroy = ppolicy_db_destroy;
   3473  1.1     lukem 
   3474  1.1     lukem 	ppolicy.on_bi.bi_op_add = ppolicy_add;
   3475  1.1     lukem 	ppolicy.on_bi.bi_op_bind = ppolicy_bind;
   3476  1.2  christos 	ppolicy.on_bi.bi_op_compare = ppolicy_compare;
   3477  1.1     lukem 	ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
   3478  1.1     lukem 	ppolicy.on_bi.bi_op_modify = ppolicy_modify;
   3479  1.3  christos 	ppolicy.on_bi.bi_op_search = ppolicy_search;
   3480  1.1     lukem 	ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
   3481  1.1     lukem 
   3482  1.1     lukem 	ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
   3483  1.1     lukem 	code = config_register_schema( ppolicycfg, ppolicyocs );
   3484  1.1     lukem 	if ( code ) return code;
   3485  1.1     lukem 
   3486  1.1     lukem 	return overlay_register( &ppolicy );
   3487  1.1     lukem }
   3488  1.1     lukem 
   3489  1.1     lukem #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
   3490  1.1     lukem int init_module(int argc, char *argv[]) {
   3491  1.1     lukem 	return ppolicy_initialize();
   3492  1.1     lukem }
   3493  1.1     lukem #endif
   3494  1.1     lukem 
   3495  1.1     lukem #endif	/* defined(SLAPD_OVER_PPOLICY) */
   3496