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