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