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