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