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