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