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