]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
78ac231e64bad3e50c36b97d5ee6326b919eaa49
[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
993                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
994                 ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr );
995                 ctrls[1] = NULL;
996                 rs->sr_ctrls = ctrls;
997         }
998         op->o_bd->bd_info = bi;
999         return SLAP_CB_CONTINUE;
1000 }
1001
1002 static int
1003 ppolicy_bind( Operation *op, SlapReply *rs )
1004 {
1005         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1006
1007         /* Root bypasses policy */
1008         if ( !be_isroot( op->o_bd, &op->o_req_ndn )) {
1009                 Entry *e;
1010                 int i, rc;
1011                 ppbind *ppb;
1012                 slap_callback *cb;
1013
1014                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1015                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1016
1017                 if ( rc != LDAP_SUCCESS ) {
1018                         return SLAP_CB_CONTINUE;
1019                 }
1020
1021                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
1022                         1, op->o_tmpmemctx );
1023                 ppb = (ppbind *)(cb+1);
1024                 ppb->on = on;
1025                 ppb->pErr = PP_noError;
1026
1027                 /* Setup a callback so we can munge the result */
1028
1029                 cb->sc_response = ppolicy_bind_resp;
1030                 cb->sc_next = op->o_callback->sc_next;
1031                 cb->sc_private = ppb;
1032                 op->o_callback->sc_next = cb;
1033
1034                 /* Did we receive a password policy request control? */
1035                 for ( i=0; op->o_ctrls && op->o_ctrls[i]; i++ ) {
1036                         if ( !strcmp( op->o_ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYREQUEST ) ) {
1037                                 ppb->send_ctrl = 1;
1038                                 break;
1039                         }
1040                 }
1041
1042                 op->o_bd->bd_info = (BackendInfo *)on;
1043                 ppolicy_get( op, e, &ppb->pp );
1044
1045                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1046
1047                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1048                 be_entry_release_r( op, e );
1049
1050                 if ( rc ) {
1051                         pp_info *pi = on->on_bi.bi_private;
1052                         /* This will be the Draft 8 response, Unwilling is bogus */
1053                         if ( pi->use_lockout ) ppb->pErr = PP_accountLocked;
1054                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1055                         return rs->sr_err;
1056                 }
1057
1058         }
1059
1060         return SLAP_CB_CONTINUE;
1061 }
1062
1063 /* Reset the restrict flag for the next session on this connection */
1064 static int
1065 ppolicy_unbind( Operation *op, SlapReply *rs )
1066 {
1067         pwcons[op->o_conn->c_conn_idx].restrict = 0;
1068         return SLAP_CB_CONTINUE;
1069 }
1070
1071 /* Check if this connection is restricted */
1072 static int
1073 ppolicy_restrict(
1074         Operation *op,
1075         SlapReply *rs )
1076 {
1077         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1078         int i, send_ctrl = 0;
1079
1080         /* Did we receive a password policy request control? */
1081         for ( i=0; op->o_ctrls && op->o_ctrls[i]; i++ ) {
1082                 if ( !strcmp( op->o_ctrls[i]->ldctl_oid,
1083                         LDAP_CONTROL_PASSWORDPOLICYREQUEST ) ) {
1084                         send_ctrl = 1;
1085                         break;
1086                 }
1087         }
1088
1089         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restrict ) {
1090 #ifdef NEW_LOGGING
1091                 LDAP_LOG( OPERATION, ENTRY,
1092                         "connection restricted to password changing only\n", 0, 0, 0 );
1093 #else
1094                 Debug( LDAP_DEBUG_TRACE,
1095                         "connection restricted to password changing only\n", 0, 0, 0);
1096 #endif
1097                 if ( send_ctrl ) {
1098                         LDAPControl **ctrls = NULL;
1099
1100                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1101                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1102                         ctrls[1] = NULL;
1103                         rs->sr_ctrls = ctrls;
1104                 }
1105                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1106                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
1107                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1108                 return rs->sr_err;
1109         }
1110
1111         return SLAP_CB_CONTINUE;
1112 }
1113
1114 static int
1115 ppolicy_add(
1116         Operation *op,
1117         SlapReply *rs )
1118 {
1119         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1120         PassPolicy pp;
1121         int pw;
1122         Attribute *pa;
1123
1124         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1125                 return rs->sr_err;
1126
1127         /* Check for password in entry */
1128         if ((pa = attr_find( op->oq_add.rs_e->e_attrs, slap_schema.si_ad_userPassword ))) {
1129                 /*
1130                  * new entry contains a password - if we're not the root user
1131                  * then we need to check that the password fits in with the
1132                  * security policy for the new entry.
1133                  */
1134                 ppolicy_get( op, op->oq_add.rs_e, &pp );
1135                 if (pp.pwdCheckQuality > 0 && !be_isroot( op->o_bd, &op->o_ndn )) {
1136                         struct berval *bv = &(pa->a_vals[0]);
1137                         int rc, i, send_ctrl = 0; 
1138                         LDAPPasswordPolicyError pErr = PP_noError;
1139
1140                         /* Did we receive a password policy request control? */
1141                         for ( i=0; op->o_ctrls && op->o_ctrls[i]; i++ ) {
1142                                 if ( !strcmp( op->o_ctrls[i]->ldctl_oid,
1143                                         LDAP_CONTROL_PASSWORDPOLICYREQUEST ) ) {
1144                                         send_ctrl = 1;
1145                                         break;
1146                                 }
1147                         }
1148                         rc = check_password_quality( bv, &pp, &pErr );
1149                         if (rc != LDAP_SUCCESS) {
1150                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1151                                 if ( send_ctrl ) {
1152                                         LDAPControl **ctrls = NULL;
1153
1154                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1155                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1156                                         ctrls[1] = NULL;
1157                                         rs->sr_ctrls = ctrls;
1158                                 }
1159                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1160                                 return rs->sr_err;
1161                         }
1162                 }
1163                 /* If password aging is in effect, set the pwdChangedTime */
1164                 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1165                         struct berval timestamp;
1166                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1167                         struct tm *ltm;
1168                         time_t now = slap_get_time();
1169
1170                         ldap_pvt_thread_mutex_lock( &gmtime_mutex );
1171                         ltm = gmtime( &now );
1172                         lutil_gentime( timebuf, sizeof(timebuf), ltm );
1173                         ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
1174
1175                         timestamp.bv_val = timebuf;
1176                         timestamp.bv_len = strlen(timebuf);
1177
1178                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1179                 }
1180         }
1181         return SLAP_CB_CONTINUE;
1182 }
1183
1184 static int
1185 ppolicy_modify( Operation *op, SlapReply *rs )
1186 {
1187         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1188         int                             i, rc, mod_pw_only, pwmod, pwmop, deladd, hsize = 0;
1189         PassPolicy              pp;
1190         Modifications   *mods = NULL, *modtail, *ml, *delmod, *addmod;
1191         Attribute               *pa, *ha, *ra, at;
1192         int                             repl_user = be_isupdate( op->o_bd, &op->o_ndn );
1193         const char              *txt;
1194         pw_hist                 *tl = NULL, *p;
1195         int                             zapReset, send_ctrl = 0;
1196         Entry                   *e;
1197         struct berval   newpw = { 0, NULL }, oldpw = { 0, NULL }, *bv, cr[2];
1198         LDAPPasswordPolicyError pErr = PP_noError;
1199
1200         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1201         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1202         op->o_bd->bd_info = (BackendInfo *)on;
1203
1204         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1205
1206         /* Did we receive a password policy request control? */
1207         for ( i=0; op->o_ctrls && op->o_ctrls[i]; i++ ) {
1208                 if ( !strcmp( op->o_ctrls[i]->ldctl_oid,
1209                         LDAP_CONTROL_PASSWORDPOLICYREQUEST ) ) {
1210                         send_ctrl = 1;
1211                         break;
1212                 }
1213         }
1214
1215         /* See if this is a pwdModify exop. If so, we can
1216          * access the plaintext passwords from that request.
1217          */
1218         {
1219                 slap_callback *sc;
1220
1221                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1222                         if ( sc->sc_response == slap_replog_cb &&
1223                                 sc->sc_private ) {
1224                                 req_pwdexop_s *qpw = sc->sc_private;
1225                                 newpw = qpw->rs_new;
1226                                 oldpw = qpw->rs_old;
1227                                 break;
1228                         }
1229                 }
1230         }
1231
1232         ppolicy_get( op, e, &pp );
1233
1234         for(ml = op->oq_modify.rs_modlist,
1235                         pwmod = 0, mod_pw_only = 1,
1236                         deladd = 0, delmod = NULL,
1237                         addmod = NULL,
1238                         zapReset = 1;
1239                 ml != NULL; modtail = ml, ml = ml->sml_next ) {
1240                 if ( ml->sml_desc == pp.ad ) {
1241                         pwmod = 1;
1242                         pwmop = ml->sml_op;
1243                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1244                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1245                                 deladd = 1;
1246                                 delmod = ml;
1247                         }
1248
1249                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1250                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1251                                 deladd = 2;
1252
1253                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1254                                 (ml->sml_op == LDAP_MOD_REPLACE))
1255                                 addmod = ml;
1256                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1257                         mod_pw_only = 0;
1258                         /* modifying something other than password */
1259                 }
1260
1261                 /*
1262                  * If there is a request to explicitly add a pwdReset
1263                  * attribute, then we suppress the normal behaviour on
1264                  * password change, which is to remove the pwdReset
1265                  * attribute.
1266                  *
1267                  * This enables an administrator to assign a new password
1268                  * and place a "must reset" flag on the entry, which will
1269                  * stay until the user explicitly changes his/her password.
1270                  */
1271                 if (ml->sml_desc == ad_pwdReset ) {
1272                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1273                                 (ml->sml_op == LDAP_MOD_REPLACE))
1274                                 zapReset = 0;
1275                 }
1276         }
1277         
1278         if (pwcons[op->o_conn->c_conn_idx].restrict && !mod_pw_only) {
1279 #ifdef NEW_LOGGING
1280                 LDAP_LOG( OPERATION, ENTRY,
1281                         "connection restricted to password changing only\n", 0, 0, 0 );
1282 #else
1283                 Debug( LDAP_DEBUG_TRACE,
1284                         "connection restricted to password changing only\n", 0, 0, 0 );
1285 #endif
1286                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1287                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1288                 pErr = PP_changeAfterReset;
1289                 goto return_results;
1290         }
1291
1292         /*
1293          * if we have a "safe password modify policy", then we need to check if we're doing
1294          * a delete (with the old password), followed by an add (with the new password).
1295          *
1296          * If we don't have this, then we fail with an error. We also skip all the checks if
1297          * the root user is bound. Root can do anything, including avoid the policies.
1298          */
1299
1300         if (!pwmod) goto do_modify;
1301
1302         /*
1303          * Did we get a valid add mod?
1304          */
1305
1306         if (!addmod) {
1307                 rs->sr_err = LDAP_OTHER;
1308                 rs->sr_text = "Internal Error";
1309 #ifdef NEW_LOGGING
1310                 LDAP_LOG( OPERATION, ENTRY,
1311                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1312 #else
1313                 Debug( LDAP_DEBUG_TRACE,
1314                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1315 #endif
1316                 goto return_results;
1317         }
1318
1319         /*
1320          * Build the password history list in ascending time order
1321          * We need this, even if the user is root, in order to maintain
1322          * the pwdHistory operational attributes properly.
1323          */
1324         if (ha = attr_find( e->e_attrs, ad_pwdHistory )) {
1325                 struct berval oldpw;
1326                 time_t oldtime;
1327                 char *oid;
1328
1329                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1330                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1331                                 &oldtime, &oldpw );
1332
1333                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1334
1335                         if (oldpw.bv_val) {
1336                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1337                                         &(ha->a_nvals[i]) );
1338                                 oldpw.bv_val = NULL;
1339                                 oldpw.bv_len = 0;
1340                         }
1341                 }
1342         }
1343         for(p=tl, hsize=0; p; p=p->next, hsize++); /* count history size */
1344
1345         if (be_isroot( op->o_bd, &op->o_ndn)) goto do_modify;
1346
1347         /* This is a pwdModify exop that provided the old pw.
1348          * We need to create a Delete mod for this old pw and 
1349          * let the matching value get found later
1350          */
1351         if (pp.pwdSafeModify && oldpw.bv_val ) {
1352                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1353                 ml->sml_op = LDAP_MOD_DELETE;
1354                 ml->sml_desc = pp.ad;
1355                 ml->sml_type = pp.ad->ad_cname;
1356                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1357                 ber_dupbv( &ml->sml_values[0], &oldpw );
1358                 ml->sml_values[1].bv_len = 0;
1359                 ml->sml_values[1].bv_val = NULL;
1360                 ml->sml_nvalues = NULL;
1361                 ml->sml_next = op->orm_modlist;
1362                 op->orm_modlist = ml;
1363                 delmod = ml;
1364                 deladd = 2;
1365         }
1366
1367         if (pp.pwdSafeModify && deladd != 2) {
1368 #ifdef NEW_LOGGING
1369                 LDAP_LOG( OPERATION, ENTRY,
1370                         "change password must use DELETE followed by ADD/REPLACE\n",
1371                         0, 0, 0 );
1372 #else
1373                 Debug( LDAP_DEBUG_TRACE,
1374                         "change password must use DELETE followed by ADD/REPLACE\n",
1375                         0, 0, 0 );
1376 #endif
1377                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1378                 rs->sr_text = "Must supply old password to be changed as well as new one";
1379                 pErr = PP_mustSupplyOldPassword;
1380                 goto return_results;
1381         }
1382
1383         if (!pp.pwdAllowUserChange) {
1384                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1385                 rs->sr_text = "User alteration of password is not allowed";
1386                 pErr = PP_passwordModNotAllowed;
1387                 goto return_results;
1388         }
1389
1390         if (pp.pwdMinAge > 0) {
1391                 time_t pwtime = (time_t)-1, now;
1392                 int age;
1393
1394                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1395                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1396                 now = slap_get_time();
1397                 age = (int)(now - pwtime);
1398                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1399                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1400                         rs->sr_text = "Password is too young to change";
1401                         pErr = PP_passwordTooYoung;
1402                         goto return_results;
1403                 }
1404         }
1405
1406         if (delmod && (pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1407                 /*
1408                  * we have a password to check
1409                  */
1410                 const char *txt;
1411                 
1412                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1413                 rc = slap_passwd_check( op->o_conn, pa, bv, &txt );
1414                 if (rc != LDAP_SUCCESS) {
1415 #ifdef NEW_LOGGING
1416                         LDAP_LOG( OPERATION, ENTRY,
1417                                 "old password check failed: %s\n", txt, 0, 0 );
1418 #else
1419                         Debug( LDAP_DEBUG_TRACE,
1420                                 "old password check failed: %s\n", txt, 0, 0 );
1421 #endif
1422                         
1423                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1424                         rs->sr_text = "Must supply correct old password to change to new one";
1425                         pErr = PP_mustSupplyOldPassword;
1426                         goto return_results;
1427
1428                 } else {
1429                         int i;
1430                         
1431                         /*
1432                          * replace the delete value with the (possibly hashed)
1433                          * value which is currently in the password.
1434                          */
1435                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1436                                 free(delmod->sml_values[i].bv_val);
1437                                 delmod->sml_values[i].bv_len = 0;
1438                         }
1439                         free(delmod->sml_values);
1440                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1441                         delmod->sml_values[1].bv_len = 0;
1442                         delmod->sml_values[1].bv_val = NULL;
1443                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1444                 }
1445         }
1446
1447         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1448         if (pp.pwdCheckQuality > 0) {
1449
1450                 rc = check_password_quality( bv, &pp, &pErr );
1451                 if (rc != LDAP_SUCCESS) {
1452                         rs->sr_err = rc;
1453                         rs->sr_text = "Password fails quality checking policy";
1454                         goto return_results;
1455                 }
1456         }
1457
1458         /*
1459          * Last check - the password history.
1460          */
1461         if (slap_passwd_check( op->o_conn, pa, bv, &txt ) == LDAP_SUCCESS) {
1462                 /*
1463                  * This is bad - it means that the user is attempting
1464                  * to set the password to the same as the old one.
1465                  */
1466                 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1467                 rs->sr_text = "Password is not being changed from existing value";
1468                 pErr = PP_passwordInHistory;
1469                 goto return_results;
1470         }
1471
1472         if (pp.pwdInHistory < 1) goto do_modify;
1473
1474         /*
1475          * Iterate through the password history, and fail on any
1476          * password matches.
1477          */
1478         at = *pa;
1479         at.a_vals = cr;
1480         cr[1].bv_val = NULL;
1481         for(p=tl; p; p=p->next) {
1482                 cr[0] = p->pw;
1483                 rc = slap_passwd_check( op->o_conn, &at, bv, &txt );
1484                 
1485                 if (rc != LDAP_SUCCESS) continue;
1486                 
1487                 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1488                 rs->sr_text = "Password is in history of old passwords";
1489                 pErr = PP_passwordInHistory;
1490                 goto return_results;
1491         }
1492
1493 do_modify:
1494         if ((pwmod) && (!repl_user)) {
1495                 struct berval timestamp;
1496                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1497                 struct tm *ltm;
1498                 time_t now = slap_get_time();
1499                 Attribute *ga;
1500                 
1501                 /*
1502                  * keep the necessary pwd.. operational attributes
1503                  * up to date.
1504                  */
1505
1506                 ldap_pvt_thread_mutex_lock( &gmtime_mutex );
1507                 ltm = gmtime( &now );
1508                 lutil_gentime( timebuf, sizeof(timebuf), ltm );
1509                 ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
1510
1511                 timestamp.bv_val = timebuf;
1512                 timestamp.bv_len = strlen(timebuf);
1513                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1514                 mods->sml_type.bv_val = NULL;
1515                 mods->sml_desc = ad_pwdChangedTime;
1516                 if (pwmop != LDAP_MOD_DELETE) {
1517                         mods->sml_op = LDAP_MOD_REPLACE;
1518                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1519                         ber_dupbv( &mods->sml_values[0], &timestamp );
1520                         mods->sml_values[1].bv_len = 0;
1521                         mods->sml_values[1].bv_val = NULL;
1522                         assert( mods->sml_values[0].bv_val );
1523                 } else {
1524                         mods->sml_op = LDAP_MOD_DELETE;
1525                         mods->sml_values = NULL;
1526                 }
1527                 mods->sml_nvalues = NULL;
1528                 mods->sml_next = NULL;
1529                 modtail->sml_next = mods;
1530                 modtail = mods;
1531
1532                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1533                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1534                         mods->sml_op = LDAP_MOD_DELETE;
1535                         mods->sml_type.bv_val = NULL;
1536                         mods->sml_desc = ad_pwdGraceUseTime;
1537                         mods->sml_values = NULL;
1538                         mods->sml_nvalues = NULL;
1539                         mods->sml_next = NULL;
1540                         modtail->sml_next = mods;
1541                         modtail = mods;
1542                 }
1543
1544                 if (attr_find(e->e_attrs, ad_pwdExpirationWarned )) {
1545                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1546                         mods->sml_op = LDAP_MOD_DELETE;
1547                         mods->sml_type.bv_val = NULL;
1548                         mods->sml_desc = ad_pwdExpirationWarned;
1549                         mods->sml_values = NULL;
1550                         mods->sml_nvalues = NULL;
1551                         mods->sml_next = NULL;
1552                         modtail->sml_next = mods;
1553                         modtail = mods;
1554                 }
1555
1556                 /* Delete the pwdReset attribute, since it's being reset */
1557                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1558                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1559                         mods->sml_op = LDAP_MOD_DELETE;
1560                         mods->sml_type.bv_val = NULL;
1561                         mods->sml_desc = ad_pwdReset;
1562                         mods->sml_values = NULL;
1563                         mods->sml_nvalues = NULL;
1564                         mods->sml_next = NULL;
1565                         modtail->sml_next = mods;
1566                         modtail = mods;
1567                 }
1568
1569                 if (hsize >= pp.pwdInHistory) {
1570                         /*
1571                          * We use the >= operator, since we are going to add a
1572                          * the existing password attribute value into the
1573                          * history - thus the cardinality of history values is
1574                          * about to rise by one.
1575                          *
1576                          * If this would push it over the limit of history
1577                          * values (remembering - the password policy could have
1578                          * changed since the password was last altered), we must
1579                          * delete at least 1 value from the pwdHistory list.
1580                          *
1581                          * In fact, we delete '(#pwdHistory attrs - max pwd
1582                          * history length) + 1' values, starting with the oldest.
1583                          * This is easily evaluated, since the linked list is
1584                          * created in ascending time order.
1585                          */
1586                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1587                         mods->sml_op = LDAP_MOD_DELETE;
1588                         mods->sml_type.bv_val = NULL;
1589                         mods->sml_desc = ad_pwdHistory;
1590                         mods->sml_nvalues = NULL;
1591                         mods->sml_values = ch_calloc( sizeof( struct berval ),
1592                                                                                    hsize - pp.pwdInHistory + 2 );
1593                         mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1594                         mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1595                         for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1596                                 mods->sml_values[i].bv_val = NULL;
1597                                 mods->sml_values[i].bv_len = 0;
1598                                 ber_dupbv( &(mods->sml_values[i]), &p->bv );
1599                         }
1600                         mods->sml_next = NULL;
1601                         modtail->sml_next = mods;
1602                         modtail = mods;
1603                 }
1604                 free_pwd_history_list( &tl );
1605
1606                 /*
1607                  * Now add the existing password into the history list.
1608                  * This will be executed even if the operation is to delete
1609                  * the password entirely.
1610                  *
1611                  * This isn't in the spec explicitly, but it seems to make
1612                  * sense that the password history list is the list of all
1613                  * previous passwords - even if they were deleted. Thus, if
1614                  * someone tries to add a historical password at some future
1615                  * point, it will fail.
1616                  */
1617                 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1618                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1619                         mods->sml_op = LDAP_MOD_ADD;
1620                         mods->sml_type.bv_val = NULL;
1621                         mods->sml_desc = ad_pwdHistory;
1622                         mods->sml_nvalues = NULL;
1623                         mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1624                         mods->sml_values[ 1 ].bv_val = NULL;
1625                         mods->sml_values[ 1 ].bv_len = 0;
1626                         make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1627                         mods->sml_next = NULL;
1628                         modtail->sml_next = mods;
1629                         modtail = mods;
1630                 } else {
1631 #ifdef NEW_LOGGING
1632         LDAP_LOG ( OPERATION, ERR, 
1633                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1634 #else
1635         Debug( LDAP_DEBUG_TRACE,
1636                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1637 #endif
1638                 }
1639
1640                 /*
1641                  * Controversial bit here. If the new password isn't hashed
1642                  * (ie, is cleartext), we probably should hash it according
1643                  * to the default hash. The reason for this is that we want
1644                  * to use the policy if possible, but if we hash the password
1645                  * before, then we're going to run into trouble when it
1646                  * comes time to check the password.
1647                  *
1648                  * Now, the right thing to do is to use the extended password
1649                  * modify operation, but not all software can do this,
1650                  * therefore it makes sense to hash the new password, now
1651                  * we know it passes the policy requirements.
1652                  *
1653                  * Of course, if the password is already hashed, then we
1654                  * leave it alone.
1655                  */
1656
1657                 if ((addmod) && !newpw.bv_val && 
1658                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1659                         struct berval hpw, bv;
1660                         
1661                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1662                         if (hpw.bv_val == NULL) {
1663                                         /*
1664                                          * hashing didn't work. Emit an error.
1665                                          */
1666                                 rs->sr_err = LDAP_OTHER;
1667                                 rs->sr_text = txt;
1668                                 goto return_results;
1669                         }
1670                         bv.bv_val = addmod->sml_values[0].bv_val;
1671                         bv.bv_len = addmod->sml_values[0].bv_len;
1672                                 /* clear and discard the clear password */
1673                         memset(bv.bv_val, 0, bv.bv_len);
1674                         ber_memfree(bv.bv_val);
1675                         addmod->sml_values[0].bv_val = hpw.bv_val;
1676                         addmod->sml_values[0].bv_len = hpw.bv_len;
1677                 }
1678         }
1679         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1680         be_entry_release_r( op, e );
1681         return SLAP_CB_CONTINUE;
1682
1683 return_results:
1684         free_pwd_history_list( &tl );
1685         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1686         be_entry_release_r( op, e );
1687         if ( send_ctrl ) {
1688                 LDAPControl **ctrls = NULL;
1689
1690                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1691                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1692                 ctrls[1] = NULL;
1693                 rs->sr_ctrls = ctrls;
1694         }
1695         send_ldap_result( op, rs );
1696         return rs->sr_err;
1697 }
1698
1699 static int
1700 ppolicy_parseCtrl(
1701         Operation *op,
1702         SlapReply *rs,
1703         LDAPControl *ctrl )
1704 {
1705         if ( ctrl->ldctl_value.bv_len ) {
1706                 rs->sr_text = "passwordPolicyRequest control value not empty";
1707                 return LDAP_PROTOCOL_ERROR;
1708         }
1709         if ( ctrl->ldctl_iscritical ) {
1710                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1711                 return LDAP_PROTOCOL_ERROR;
1712         }
1713
1714         return LDAP_SUCCESS;
1715 }
1716
1717 static int
1718 ppolicy_db_init(
1719         BackendDB *be
1720 )
1721 {
1722         slap_overinst *on = (slap_overinst *) be->bd_info;
1723
1724         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1725
1726         if ( dtblsize && !pwcons )
1727                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1728
1729         return 0;
1730 }
1731
1732 static int
1733 ppolicy_close(
1734         BackendDB *be
1735 )
1736 {
1737         slap_overinst *on = (slap_overinst *) be->bd_info;
1738         pp_info *pi = on->on_bi.bi_private;
1739         
1740         free( pwcons );
1741         free( pi->def_policy.bv_val );
1742         free( pi );
1743
1744         return 0;
1745 }
1746
1747 static int
1748 ppolicy_config(
1749     BackendDB   *be,
1750     const char  *fname,
1751     int         lineno,
1752     int         argc,
1753     char        **argv
1754 )
1755 {
1756         slap_overinst *on = (slap_overinst *) be->bd_info;
1757         pp_info *pi = on->on_bi.bi_private;
1758         struct berval dn;
1759         
1760
1761         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1762                 if ( argc != 2 ) {
1763                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1764                                 " <policyDN>\n", fname, lineno );
1765                         return ( 1 );
1766                 }
1767                 ber_str2bv( argv[1], 0, 0, &dn );
1768                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1769                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1770                                 fname, lineno );
1771                         return 1;
1772                 }
1773                 return 0;
1774         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1775                 if ( argc != 1 ) {
1776                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1777                                 "takes no arguments\n", fname, lineno );
1778                         return ( 1 );
1779                 }
1780                 pi->use_lockout = 1;
1781         }
1782         return SLAP_CONF_UNKNOWN;
1783 }
1784
1785 static char *extops[] = {
1786         LDAP_EXOP_MODIFY_PASSWD,
1787         NULL
1788 };
1789
1790 static slap_overinst ppolicy;
1791
1792 int ppolicy_init()
1793 {
1794         LDAPAttributeType *at;
1795         int code;
1796         const char *err;
1797         int i;
1798
1799         for (i=0; pwd_OpSchema[i].def; i++) {
1800                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1801                         LDAP_SCHEMA_ALLOW_ALL );
1802                 if ( !at ) {
1803                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1804                                 ldap_scherr2str(code), err );
1805                         return code;
1806                 }
1807                 code = at_add( at, &err );
1808                 if ( !code ) {
1809                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1810                 }
1811                 ldap_memfree( at );
1812                 if ( code ) {
1813                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1814                                 scherr2str(code), err );
1815                         return code;
1816                 }
1817         }
1818
1819         for (i=0; pwd_UsSchema[i].def; i++) {
1820                 code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1821                 if ( code ) {
1822                         fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1823                         return code;
1824                 }
1825         }
1826
1827         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1828                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY, extops,
1829                 ppolicy_parseCtrl );
1830         if ( code != LDAP_SUCCESS ) {
1831                 fprintf( stderr, "Failed to register control %d\n", code );
1832                 return code;
1833         }
1834
1835         /* Find out our offset from GMT since mktime() always assumes
1836          * its input is local time
1837          */
1838         {
1839                 time_t gmt, lcl, now = slap_get_time();
1840                 struct tm *tmp;
1841
1842                 tmp = gmtime( &now );
1843                 gmt = mktime( tmp );
1844
1845                 tmp = localtime( &now );
1846                 tmp->tm_isdst = 0;
1847                 lcl = mktime( tmp );
1848
1849                 gmtoff = gmt - lcl;
1850         }
1851
1852         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1853
1854         ppolicy.on_bi.bi_type = "ppolicy";
1855         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1856         ppolicy.on_bi.bi_db_config = ppolicy_config;
1857         ppolicy.on_bi.bi_db_close = ppolicy_close;
1858
1859         ppolicy.on_bi.bi_op_add = ppolicy_add;
1860         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1861         ppolicy.on_bi.bi_op_unbind = ppolicy_unbind;
1862         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1863         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1864         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1865         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1866
1867         return overlay_register( &ppolicy );
1868 }
1869
1870 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1871 int init_module(int argc, char *argv[]) {
1872         return ppolicy_init();
1873 }
1874 #endif
1875
1876 #endif  /* defined(SLAPD_OVER_PPOLICY) */