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