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