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