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