No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / openldap / dist / servers / slapd / overlays / ppolicy.c
blobe4dd1fa4ace5d73de664d394369789609fc96520
1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/ppolicy.c,v 1.75.2.14 2008/07/10 00:55:07 quanah Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2004-2008 The OpenLDAP Foundation.
5 * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
6 * Portions Copyright 2004 Hewlett-Packard Company.
7 * All rights reserved.
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.
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>.
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.
23 #include "portable.h"
25 /* This file implements "Password Policy for LDAP Directories",
26 * based on draft behera-ldap-password-policy-09
29 #ifdef SLAPD_OVER_PPOLICY
31 #include <ldap.h>
32 #include "lutil.h"
33 #include "slap.h"
34 #ifdef SLAPD_MODULES
35 #define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */
36 #include <ltdl.h>
37 #endif
38 #include <ac/errno.h>
39 #include <ac/time.h>
40 #include <ac/string.h>
41 #include <ac/ctype.h>
42 #include "config.h"
44 #ifndef MODULE_NAME_SZ
45 #define MODULE_NAME_SZ 256
46 #endif
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 int hash_passwords; /* transparently hash cleartext pwds */
53 } pp_info;
55 /* Our per-connection info - note, it is not per-instance, it is
56 * used by all instances
58 typedef struct pw_conn {
59 struct berval dn; /* DN of restricted user */
60 } pw_conn;
62 static pw_conn *pwcons;
63 static int ppolicy_cid;
64 static int ov_count;
66 typedef struct pass_policy {
67 AttributeDescription *ad; /* attribute to which the policy applies */
68 int pwdMinAge; /* minimum time (seconds) until passwd can change */
69 int pwdMaxAge; /* time in seconds until pwd will expire after change */
70 int pwdInHistory; /* number of previous passwords kept */
71 int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
72 2 = check mandatory; fail if not possible */
73 int pwdMinLength; /* minimum number of chars in password */
74 int pwdExpireWarning; /* number of seconds that warning controls are
75 sent before a password expires */
76 int pwdGraceAuthNLimit; /* number of times you can log in with an
77 expired password */
78 int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
79 int pwdLockoutDuration; /* time in seconds a password is locked out for */
80 int pwdMaxFailure; /* number of failed binds allowed before lockout */
81 int pwdFailureCountInterval; /* number of seconds before failure
82 counts are zeroed */
83 int pwdMustChange; /* 0 = users can use admin set password
84 1 = users must change password after admin set */
85 int pwdAllowUserChange; /* 0 = users cannot change their passwords
86 1 = users can change them */
87 int pwdSafeModify; /* 0 = old password doesn't need to come
88 with password change request
89 1 = password change must supply existing pwd */
90 char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
91 load to check password */
92 } PassPolicy;
94 typedef struct pw_hist {
95 time_t t; /* timestamp of history entry */
96 struct berval pw; /* old password hash */
97 struct berval bv; /* text of entire entry */
98 struct pw_hist *next;
99 } pw_hist;
101 /* Operational attributes */
102 static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
103 *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
104 *ad_pwdPolicySubentry;
106 static struct schema_info {
107 char *def;
108 AttributeDescription **ad;
109 } pwd_OpSchema[] = {
110 { "( 1.3.6.1.4.1.42.2.27.8.1.16 "
111 "NAME ( 'pwdChangedTime' ) "
112 "DESC 'The time the password was last changed' "
113 "EQUALITY generalizedTimeMatch "
114 "ORDERING generalizedTimeOrderingMatch "
115 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
116 "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
117 &ad_pwdChangedTime },
118 { "( 1.3.6.1.4.1.42.2.27.8.1.17 "
119 "NAME ( 'pwdAccountLockedTime' ) "
120 "DESC 'The time an user account was locked' "
121 "EQUALITY generalizedTimeMatch "
122 "ORDERING generalizedTimeOrderingMatch "
123 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
124 "SINGLE-VALUE "
125 #if 0
126 /* Not until Relax control is released */
127 "NO-USER-MODIFICATION "
128 #endif
129 "USAGE directoryOperation )",
130 &ad_pwdAccountLockedTime },
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 "NO-USER-MODIFICATION 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 "NO-USER-MODIFICATION 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 "NO-USER-MODIFICATION 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 "
166 #if 0
167 /* Not until Relax control is released */
168 "NO-USER-MODIFICATION "
169 #endif
170 "USAGE directoryOperation )",
171 &ad_pwdPolicySubentry },
172 { NULL, NULL }
175 /* User attributes */
176 static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory,
177 *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure,
178 *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration,
179 *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout,
180 *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
181 *ad_pwdAttribute;
183 #define TAB(name) { #name, &ad_##name }
185 static struct schema_info pwd_UsSchema[] = {
186 TAB(pwdAttribute),
187 TAB(pwdMinAge),
188 TAB(pwdMaxAge),
189 TAB(pwdInHistory),
190 TAB(pwdCheckQuality),
191 TAB(pwdMinLength),
192 TAB(pwdMaxFailure),
193 TAB(pwdGraceAuthNLimit),
194 TAB(pwdExpireWarning),
195 TAB(pwdLockout),
196 TAB(pwdLockoutDuration),
197 TAB(pwdFailureCountInterval),
198 TAB(pwdCheckModule),
199 TAB(pwdMustChange),
200 TAB(pwdAllowUserChange),
201 TAB(pwdSafeModify),
202 { NULL, NULL }
205 static ldap_pvt_thread_mutex_t chk_syntax_mutex;
207 enum {
208 PPOLICY_DEFAULT = 1,
209 PPOLICY_HASH_CLEARTEXT,
210 PPOLICY_USE_LOCKOUT
213 static ConfigDriver ppolicy_cf_default;
215 static ConfigTable ppolicycfg[] = {
216 { "ppolicy_default", "policyDN", 2, 2, 0,
217 ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
218 "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
219 "DESC 'DN of a pwdPolicy object for uncustomized objects' "
220 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
221 { "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
222 ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
223 (void *)offsetof(pp_info,hash_passwords),
224 "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
225 "DESC 'Hash passwords on add or modify' "
226 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
227 { "ppolicy_use_lockout", "on|off", 1, 2, 0,
228 ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
229 (void *)offsetof(pp_info,use_lockout),
230 "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
231 "DESC 'Warn clients with AccountLocked' "
232 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
233 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
236 static ConfigOCs ppolicyocs[] = {
237 { "( OLcfgOvOc:12.1 "
238 "NAME 'olcPPolicyConfig' "
239 "DESC 'Password Policy configuration' "
240 "SUP olcOverlayConfig "
241 "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
242 "olcPPolicyUseLockout ) )",
243 Cft_Overlay, ppolicycfg },
244 { NULL, 0, NULL }
247 static int
248 ppolicy_cf_default( ConfigArgs *c )
250 slap_overinst *on = (slap_overinst *)c->bi;
251 pp_info *pi = (pp_info *)on->on_bi.bi_private;
252 int rc = ARG_BAD_CONF;
254 assert ( c->type == PPOLICY_DEFAULT );
255 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n", 0, 0, 0);
257 switch ( c->op ) {
258 case SLAP_CONFIG_EMIT:
259 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n", 0, 0, 0);
260 rc = 0;
261 if ( !BER_BVISEMPTY( &pi->def_policy )) {
262 rc = value_add_one( &c->rvalue_vals,
263 &pi->def_policy );
264 if ( rc ) return rc;
265 rc = value_add_one( &c->rvalue_nvals,
266 &pi->def_policy );
268 break;
269 case LDAP_MOD_DELETE:
270 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n", 0, 0, 0);
271 if ( pi->def_policy.bv_val ) {
272 ber_memfree ( pi->def_policy.bv_val );
273 pi->def_policy.bv_val = NULL;
275 pi->def_policy.bv_len = 0;
276 rc = 0;
277 break;
278 case SLAP_CONFIG_ADD:
279 /* fallthrough to LDAP_MOD_ADD */
280 case LDAP_MOD_ADD:
281 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n", 0, 0, 0);
282 if ( pi->def_policy.bv_val ) {
283 ber_memfree ( pi->def_policy.bv_val );
285 pi->def_policy = c->value_ndn;
286 ber_memfree( c->value_dn.bv_val );
287 BER_BVZERO( &c->value_dn );
288 BER_BVZERO( &c->value_ndn );
289 rc = 0;
290 break;
291 default:
292 abort ();
295 return rc;
298 static time_t
299 parse_time( char *atm )
301 struct lutil_tm tm;
302 struct lutil_timet tt;
303 time_t ret = (time_t)-1;
305 if ( lutil_parsetime( atm, &tm ) == 0) {
306 lutil_tm2time( &tm, &tt );
307 ret = tt.tt_sec;
309 return ret;
312 static int
313 account_locked( Operation *op, Entry *e,
314 PassPolicy *pp, Modifications **mod )
316 Attribute *la;
318 assert(mod != NULL);
320 if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
321 BerVarray vals = la->a_nvals;
324 * there is a lockout stamp - we now need to know if it's
325 * a valid one.
327 if (vals[0].bv_val != NULL) {
328 time_t then, now;
329 Modifications *m;
331 if (!pp->pwdLockoutDuration)
332 return 1;
334 if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
335 return 1;
337 now = slap_get_time();
339 if (now < then + pp->pwdLockoutDuration)
340 return 1;
342 m = ch_calloc( sizeof(Modifications), 1 );
343 m->sml_op = LDAP_MOD_DELETE;
344 m->sml_flags = 0;
345 m->sml_type = ad_pwdAccountLockedTime->ad_cname;
346 m->sml_desc = ad_pwdAccountLockedTime;
347 m->sml_next = *mod;
348 *mod = m;
352 return 0;
355 /* IMPLICIT TAGS, all context-specific */
356 #define PPOLICY_WARNING 0xa0L /* constructed + 0 */
357 #define PPOLICY_ERROR 0x81L /* primitive + 1 */
359 #define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
360 #define PPOLICY_GRACE 0x81L /* primitive + 1 */
362 static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
364 static LDAPControl *
365 create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err )
367 char berbuf[LBER_ELEMENT_SIZEOF], bb2[LBER_ELEMENT_SIZEOF];
368 BerElement *ber = (BerElement *)berbuf, *b2 = (BerElement *)bb2;
369 LDAPControl *c;
370 struct berval bv;
372 c = ch_calloc( sizeof( LDAPControl ), 1 );
373 if ( c == NULL ) {
374 return NULL;
376 c->ldctl_oid = (char *)ppolicy_ctrl_oid;
377 c->ldctl_iscritical = 0;
378 BER_BVZERO( &c->ldctl_value );
380 ber_init2( ber, NULL, LBER_USE_DER );
381 ber_printf( ber, "{" /*}*/ );
383 if ( exptime >= 0 ) {
384 ber_init2( b2, NULL, LBER_USE_DER );
385 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
386 ber_flatten2( b2, &bv, 1 );
387 (void)ber_free_buf(b2);
388 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
389 ch_free( bv.bv_val );
390 } else if ( grace > 0 ) {
391 ber_init2( b2, NULL, LBER_USE_DER );
392 ber_printf( b2, "ti", PPOLICY_GRACE, grace );
393 ber_flatten2( b2, &bv, 1 );
394 (void)ber_free_buf(b2);
395 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
396 ch_free( bv.bv_val );
399 if (err != PP_noError ) {
400 ber_printf( ber, "te", PPOLICY_ERROR, err );
402 ber_printf( ber, /*{*/ "N}" );
404 if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) {
405 ch_free(c);
406 c = NULL;
408 (void)ber_free_buf(ber);
409 return c;
412 static LDAPControl **
413 add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
415 LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
416 int n;
418 n = 0;
419 if ( oldctrls ) {
420 for ( ; oldctrls[n]; n++ )
423 n += 2;
425 ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
427 n = 0;
428 if ( oldctrls ) {
429 for ( ; oldctrls[n]; n++ ) {
430 ctrls[n] = oldctrls[n];
433 ctrls[n] = ctrl;
434 ctrls[n+1] = NULL;
436 rs->sr_ctrls = ctrls;
438 return oldctrls;
441 static void
442 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
444 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
445 pp_info *pi = on->on_bi.bi_private;
446 Attribute *a;
447 BerVarray vals;
448 int rc;
449 Entry *pe = NULL;
450 #if 0
451 const char *text;
452 #endif
454 memset( pp, 0, sizeof(PassPolicy) );
456 pp->ad = slap_schema.si_ad_userPassword;
458 /* Users can change their own password by default */
459 pp->pwdAllowUserChange = 1;
461 if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
463 * entry has no password policy assigned - use default
465 vals = &pi->def_policy;
466 if ( !vals->bv_val )
467 goto defaultpol;
468 } else {
469 vals = a->a_nvals;
470 if (vals[0].bv_val == NULL) {
471 Debug( LDAP_DEBUG_ANY,
472 "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
473 goto defaultpol;
477 op->o_bd->bd_info = (BackendInfo *)on->on_info;
478 rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
479 op->o_bd->bd_info = (BackendInfo *)on;
481 if ( rc ) goto defaultpol;
483 #if 0 /* Only worry about userPassword for now */
484 if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
485 slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
486 #endif
488 if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) )
489 && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 )
490 goto defaultpol;
491 if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) )
492 && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 )
493 goto defaultpol;
494 if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) )
495 && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 )
496 goto defaultpol;
497 if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckQuality ) )
498 && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 )
499 goto defaultpol;
500 if ( ( a = attr_find( pe->e_attrs, ad_pwdMinLength ) )
501 && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 )
502 goto defaultpol;
503 if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) )
504 && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 )
505 goto defaultpol;
506 if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) )
507 && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 )
508 goto defaultpol;
509 if ( ( a = attr_find( pe->e_attrs, ad_pwdExpireWarning ) )
510 && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 )
511 goto defaultpol;
512 if ( ( a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ) )
513 && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 )
514 goto defaultpol;
515 if ( ( a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ) )
516 && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 )
517 goto defaultpol;
519 if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckModule ) ) ) {
520 strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val,
521 sizeof(pp->pwdCheckModule) );
522 pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
525 if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
526 pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
527 if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
528 pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
529 if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
530 pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
531 if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
532 pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
534 op->o_bd->bd_info = (BackendInfo *)on->on_info;
535 be_entry_release_r( op, pe );
536 op->o_bd->bd_info = (BackendInfo *)on;
538 return;
540 defaultpol:
541 Debug( LDAP_DEBUG_TRACE,
542 "ppolicy_get: using default policy\n", 0, 0, 0 );
543 return;
546 static int
547 password_scheme( struct berval *cred, struct berval *sch )
549 int e;
551 assert( cred != NULL );
553 if (sch) {
554 sch->bv_val = NULL;
555 sch->bv_len = 0;
558 if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
559 (cred->bv_val[0] != '{')) return LDAP_OTHER;
561 for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
562 if (cred->bv_val[e]) {
563 int rc;
564 rc = lutil_passwd_scheme( cred->bv_val );
565 if (rc) {
566 if (sch) {
567 sch->bv_val = cred->bv_val;
568 sch->bv_len = e;
570 return LDAP_SUCCESS;
573 return LDAP_OTHER;
576 static int
577 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e )
579 int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
580 char *ptr = cred->bv_val;
581 struct berval sch;
583 assert( cred != NULL );
584 assert( pp != NULL );
586 if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
587 rc = LDAP_CONSTRAINT_VIOLATION;
588 if ( err ) *err = PP_passwordTooShort;
589 return rc;
593 * We need to know if the password is already hashed - if so
594 * what scheme is it. The reason being that the "hash" of
595 * {cleartext} still allows us to check the password.
597 rc = password_scheme( cred, &sch );
598 if (rc == LDAP_SUCCESS) {
599 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
600 sch.bv_len ) == 0)) {
602 * We can check the cleartext "hash"
604 ptr = cred->bv_val + sch.bv_len;
605 } else {
606 /* everything else, we can't check */
607 if (pp->pwdCheckQuality == 2) {
608 rc = LDAP_CONSTRAINT_VIOLATION;
609 if (err) *err = PP_insufficientPasswordQuality;
610 return rc;
613 * We can't check the syntax of the password, but it's not
614 * mandatory (according to the policy), so we return success.
617 return LDAP_SUCCESS;
621 rc = LDAP_SUCCESS;
623 if (pp->pwdCheckModule[0]) {
624 #ifdef SLAPD_MODULES
625 lt_dlhandle mod;
626 const char *err;
628 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
629 err = lt_dlerror();
631 Debug(LDAP_DEBUG_ANY,
632 "check_password_quality: lt_dlopen failed: (%s) %s.\n",
633 pp->pwdCheckModule, err, 0 );
634 ok = LDAP_OTHER; /* internal error */
635 } else {
636 int (*prog)( char *passwd, char **text, Entry *ent );
638 if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
639 err = lt_dlerror();
641 Debug(LDAP_DEBUG_ANY,
642 "check_password_quality: lt_dlsym failed: (%s) %s.\n",
643 pp->pwdCheckModule, err, 0 );
644 ok = LDAP_OTHER;
645 } else {
646 char *txt = NULL;
648 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
649 ok = prog( cred->bv_val, &txt, e );
650 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
651 if (ok != LDAP_SUCCESS) {
652 Debug(LDAP_DEBUG_ANY,
653 "check_password_quality: module error: (%s) %s.[%d]\n",
654 pp->pwdCheckModule, txt ? txt : "", ok );
655 free(txt);
659 lt_dlclose( mod );
661 #else
662 Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
663 "supported. pwdCheckModule ignored.\n", 0, 0, 0);
664 #endif /* SLAPD_MODULES */
668 if (ok != LDAP_SUCCESS) {
669 rc = LDAP_CONSTRAINT_VIOLATION;
670 if (err) *err = PP_insufficientPasswordQuality;
673 return rc;
676 static int
677 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
679 char *ptr;
680 struct berval nv, npw;
681 int i, j;
683 assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
685 if ( oid ) {
686 *oid = 0;
688 *oldtime = (time_t)-1;
689 BER_BVZERO( oldpw );
691 ber_dupbv( &nv, bv );
693 /* first get the time field */
694 for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
696 if ( i == nv.bv_len ) {
697 goto exit_failure; /* couldn't locate the '#' separator */
699 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
700 ptr = nv.bv_val;
701 *oldtime = parse_time( ptr );
702 if (*oldtime == (time_t)-1) {
703 goto exit_failure;
706 /* get the OID field */
707 for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
709 if ( i == nv.bv_len ) {
710 goto exit_failure; /* couldn't locate the '#' separator */
712 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
713 if ( oid ) {
714 *oid = ber_strdup( ptr );
717 /* get the length field */
718 for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
720 if ( i == nv.bv_len ) {
721 goto exit_failure; /* couldn't locate the '#' separator */
723 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
724 oldpw->bv_len = strtol( ptr, NULL, 10 );
725 if (errno == ERANGE) {
726 goto exit_failure;
729 /* lastly, get the octets of the string */
730 for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
732 if ( i - j != oldpw->bv_len) {
733 goto exit_failure; /* length is wrong */
736 npw.bv_val = ptr;
737 npw.bv_len = oldpw->bv_len;
738 ber_dupbv( oldpw, &npw );
739 ber_memfree( nv.bv_val );
741 return LDAP_SUCCESS;
743 exit_failure:;
744 if ( oid && *oid ) {
745 ber_memfree(*oid);
746 *oid = NULL;
748 if ( oldpw->bv_val ) {
749 ber_memfree( oldpw->bv_val);
750 BER_BVZERO( oldpw );
752 ber_memfree( nv.bv_val );
754 return LDAP_OTHER;
757 static void
758 add_to_pwd_history( pw_hist **l, time_t t,
759 struct berval *oldpw, struct berval *bv )
761 pw_hist *p, *p1, *p2;
763 if (!l) return;
765 p = ch_malloc( sizeof( pw_hist ));
766 p->pw = *oldpw;
767 ber_dupbv( &p->bv, bv );
768 p->t = t;
769 p->next = NULL;
771 if (*l == NULL) {
772 /* degenerate case */
773 *l = p;
774 return;
777 * advance p1 and p2 such that p1 is the node before the
778 * new one, and p2 is the node after it
780 for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
781 p->next = p2;
782 if (p1 == NULL) { *l = p; return; }
783 p1->next = p;
786 #ifndef MAX_PWD_HISTORY_SZ
787 #define MAX_PWD_HISTORY_SZ 1024
788 #endif /* MAX_PWD_HISTORY_SZ */
790 static void
791 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
793 char str[ MAX_PWD_HISTORY_SZ ];
794 int nlen;
796 snprintf( str, MAX_PWD_HISTORY_SZ,
797 "%s#%s#%lu#", timebuf,
798 pa->a_desc->ad_type->sat_syntax->ssyn_oid,
799 (unsigned long) pa->a_nvals[0].bv_len );
800 str[MAX_PWD_HISTORY_SZ-1] = 0;
801 nlen = strlen(str);
804 * We have to assume that the string is a string of octets,
805 * not readable characters. In reality, yes, it probably is
806 * a readable (ie, base64) string, but we can't count on that
807 * Hence, while the first 3 fields of the password history
808 * are definitely readable (a timestamp, an OID and an integer
809 * length), the remaining octets of the actual password
810 * are deemed to be binary data.
812 AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
813 nlen += pa->a_nvals[0].bv_len;
814 bv->bv_val = ch_malloc( nlen + 1 );
815 AC_MEMCPY( bv->bv_val, str, nlen );
816 bv->bv_val[nlen] = '\0';
817 bv->bv_len = nlen;
820 static void
821 free_pwd_history_list( pw_hist **l )
823 pw_hist *p;
825 if (!l) return;
826 p = *l;
827 while (p) {
828 pw_hist *pp = p->next;
830 free(p->pw.bv_val);
831 free(p->bv.bv_val);
832 free(p);
833 p = pp;
835 *l = NULL;
838 typedef struct ppbind {
839 slap_overinst *on;
840 int send_ctrl;
841 LDAPControl **oldctrls;
842 Modifications *mod;
843 LDAPPasswordPolicyError pErr;
844 PassPolicy pp;
845 } ppbind;
847 static void
848 ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
850 int n;
852 assert( rs->sr_ctrls != NULL );
853 assert( rs->sr_ctrls[0] != NULL );
855 for ( n = 0; rs->sr_ctrls[n]; n++ ) {
856 if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) {
857 ch_free( rs->sr_ctrls[n]->ldctl_value.bv_val );
858 ch_free( rs->sr_ctrls[n] );
859 rs->sr_ctrls[n] = (LDAPControl *)(-1);
860 break;
864 if ( rs->sr_ctrls[n] == NULL ) {
865 /* missed? */
868 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
870 rs->sr_ctrls = oldctrls;
873 static int
874 ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
876 ppbind *ppb = op->o_callback->sc_private;
877 if ( ppb->send_ctrl ) {
878 ctrls_cleanup( op, rs, ppb->oldctrls );
880 return SLAP_CB_CONTINUE;
883 static int
884 ppolicy_bind_response( Operation *op, SlapReply *rs )
886 ppbind *ppb = op->o_callback->sc_private;
887 slap_overinst *on = ppb->on;
888 Modifications *mod = ppb->mod, *m;
889 int pwExpired = 0;
890 int ngut = -1, warn = -1, age, rc;
891 Attribute *a;
892 time_t now, pwtime = (time_t)-1;
893 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
894 struct berval timestamp;
895 BackendInfo *bi = op->o_bd->bd_info;
896 Entry *e;
898 /* If we already know it's locked, just get on with it */
899 if ( ppb->pErr != PP_noError ) {
900 goto locked;
903 op->o_bd->bd_info = (BackendInfo *)on->on_info;
904 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
905 op->o_bd->bd_info = bi;
907 if ( rc != LDAP_SUCCESS ) {
908 return SLAP_CB_CONTINUE;
911 now = slap_get_time(); /* stored for later consideration */
912 timestamp.bv_val = nowstr;
913 timestamp.bv_len = sizeof(nowstr);
914 slap_timestamp( &now, &timestamp );
916 if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) {
917 int i = 0, fc = 0;
919 m = ch_calloc( sizeof(Modifications), 1 );
920 m->sml_op = LDAP_MOD_ADD;
921 m->sml_flags = 0;
922 m->sml_type = ad_pwdFailureTime->ad_cname;
923 m->sml_desc = ad_pwdFailureTime;
924 m->sml_numvals = 1;
925 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
926 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
928 ber_dupbv( &m->sml_values[0], &timestamp );
929 ber_dupbv( &m->sml_nvalues[0], &timestamp );
930 m->sml_next = mod;
931 mod = m;
934 * Count the pwdFailureTimes - if it's
935 * greater than the policy pwdMaxFailure,
936 * then lock the account.
938 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
939 for(i=0; a->a_nvals[i].bv_val; i++) {
942 * If the interval is 0, then failures
943 * stay on the record until explicitly
944 * reset by successful authentication.
946 if (ppb->pp.pwdFailureCountInterval == 0) {
947 fc++;
948 } else if (now <=
949 parse_time(a->a_nvals[i].bv_val) +
950 ppb->pp.pwdFailureCountInterval) {
952 fc++;
955 * We only count those failures
956 * which are not due to expire.
961 if ((ppb->pp.pwdMaxFailure > 0) &&
962 (fc >= ppb->pp.pwdMaxFailure - 1)) {
965 * We subtract 1 from the failure max
966 * because the new failure entry hasn't
967 * made it to the entry yet.
969 m = ch_calloc( sizeof(Modifications), 1 );
970 m->sml_op = LDAP_MOD_REPLACE;
971 m->sml_flags = 0;
972 m->sml_type = ad_pwdAccountLockedTime->ad_cname;
973 m->sml_desc = ad_pwdAccountLockedTime;
974 m->sml_numvals = 1;
975 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
976 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
977 ber_dupbv( &m->sml_values[0], &timestamp );
978 ber_dupbv( &m->sml_nvalues[0], &timestamp );
979 m->sml_next = mod;
980 mod = m;
982 } else if ( rs->sr_err == LDAP_SUCCESS ) {
983 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
984 pwtime = parse_time( a->a_nvals[0].bv_val );
986 /* delete all pwdFailureTimes */
987 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
988 m = ch_calloc( sizeof(Modifications), 1 );
989 m->sml_op = LDAP_MOD_DELETE;
990 m->sml_flags = 0;
991 m->sml_type = ad_pwdFailureTime->ad_cname;
992 m->sml_desc = ad_pwdFailureTime;
993 m->sml_next = mod;
994 mod = m;
998 * check to see if the password must be changed
1000 if ( ppb->pp.pwdMustChange &&
1001 (a = attr_find( e->e_attrs, ad_pwdReset )) &&
1002 bvmatch( &a->a_nvals[0], &slap_true_bv ) )
1005 * need to inject client controls here to give
1006 * more information. For the moment, we ensure
1007 * that we are disallowed from doing anything
1008 * other than change password.
1010 ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
1011 &op->o_conn->c_ndn );
1013 ppb->pErr = PP_changeAfterReset;
1015 } else {
1017 * the password does not need to be changed, so
1018 * we now check whether the password has expired.
1020 * We can skip this bit if passwords don't age in
1021 * the policy. Also, if there was no pwdChangedTime
1022 * attribute in the entry, the password never expires.
1024 if (ppb->pp.pwdMaxAge == 0) goto grace;
1026 if (pwtime != (time_t)-1) {
1028 * Check: was the last change time of
1029 * the password older than the maximum age
1030 * allowed. (Ignore case 2 from I-D, it's just silly.)
1032 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
1036 grace:
1037 if (!pwExpired) goto check_expiring_password;
1039 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
1040 ngut = ppb->pp.pwdGraceAuthNLimit;
1041 else {
1042 for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
1043 ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
1047 * ngut is the number of remaining grace logins
1049 Debug( LDAP_DEBUG_ANY,
1050 "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
1051 e->e_name.bv_val, ngut, 0);
1053 if (ngut < 1) {
1054 ppb->pErr = PP_passwordExpired;
1055 rs->sr_err = LDAP_INVALID_CREDENTIALS;
1056 goto done;
1060 * Add a grace user time to the entry
1062 m = ch_calloc( sizeof(Modifications), 1 );
1063 m->sml_op = LDAP_MOD_ADD;
1064 m->sml_flags = 0;
1065 m->sml_type = ad_pwdGraceUseTime->ad_cname;
1066 m->sml_desc = ad_pwdGraceUseTime;
1067 m->sml_numvals = 1;
1068 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1069 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1070 ber_dupbv( &m->sml_values[0], &timestamp );
1071 ber_dupbv( &m->sml_nvalues[0], &timestamp );
1072 m->sml_next = mod;
1073 mod = m;
1075 check_expiring_password:
1077 * Now we need to check to see
1078 * if it is about to expire, and if so, should the user
1079 * be warned about it in the password policy control.
1081 * If the password has expired, and we're in the grace period, then
1082 * we don't need to do this bit. Similarly, if we don't have password
1083 * aging, then there's no need to do this bit either.
1085 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
1086 goto done;
1088 age = (int)(now - pwtime);
1091 * We know that there is a password Change Time attribute - if
1092 * there wasn't, then the pwdExpired value would be true, unless
1093 * there is no password aging - and if there is no password aging,
1094 * then this section isn't called anyway - you can't have an
1095 * expiring password if there's no limit to expire.
1097 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
1099 * Set the warning value.
1101 warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
1102 if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
1104 Debug( LDAP_DEBUG_ANY,
1105 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
1106 op->o_req_dn.bv_val, warn, 0 );
1110 done:
1111 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1112 be_entry_release_r( op, e );
1114 locked:
1115 if ( mod ) {
1116 Operation op2 = *op;
1117 SlapReply r2 = { REP_RESULT };
1118 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
1120 /* FIXME: Need to handle replication of some (but not all)
1121 * of the operational attributes...
1123 op2.o_tag = LDAP_REQ_MODIFY;
1124 op2.o_callback = &cb;
1125 op2.orm_modlist = mod;
1126 op2.o_dn = op->o_bd->be_rootdn;
1127 op2.o_ndn = op->o_bd->be_rootndn;
1128 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
1129 rc = op->o_bd->be_modify( &op2, &r2 );
1130 slap_mods_free( mod, 1 );
1133 if ( ppb->send_ctrl ) {
1134 LDAPControl *ctrl = NULL;
1135 pp_info *pi = on->on_bi.bi_private;
1137 /* Do we really want to tell that the account is locked? */
1138 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
1139 ppb->pErr = PP_noError;
1141 ctrl = create_passcontrol( warn, ngut, ppb->pErr );
1142 ppb->oldctrls = add_passcontrol( op, rs, ctrl );
1143 op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
1145 op->o_bd->bd_info = bi;
1146 return SLAP_CB_CONTINUE;
1149 static int
1150 ppolicy_bind( Operation *op, SlapReply *rs )
1152 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1154 /* Reset lockout status on all Bind requests */
1155 if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1156 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1157 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1160 /* Root bypasses policy */
1161 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
1162 Entry *e;
1163 int rc;
1164 ppbind *ppb;
1165 slap_callback *cb;
1167 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1168 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1170 if ( rc != LDAP_SUCCESS ) {
1171 return SLAP_CB_CONTINUE;
1174 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
1175 1, op->o_tmpmemctx );
1176 ppb = (ppbind *)(cb+1);
1177 ppb->on = on;
1178 ppb->pErr = PP_noError;
1180 /* Setup a callback so we can munge the result */
1182 cb->sc_response = ppolicy_bind_response;
1183 cb->sc_next = op->o_callback->sc_next;
1184 cb->sc_private = ppb;
1185 op->o_callback->sc_next = cb;
1187 /* Did we receive a password policy request control? */
1188 if ( op->o_ctrlflag[ppolicy_cid] ) {
1189 ppb->send_ctrl = 1;
1192 op->o_bd->bd_info = (BackendInfo *)on;
1193 ppolicy_get( op, e, &ppb->pp );
1195 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1197 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1198 be_entry_release_r( op, e );
1200 if ( rc ) {
1201 /* This will be the Draft 8 response, Unwilling is bogus */
1202 ppb->pErr = PP_accountLocked;
1203 send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1204 return rs->sr_err;
1209 return SLAP_CB_CONTINUE;
1212 /* Reset the restricted info for the next session on this connection */
1213 static int
1214 ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
1216 if ( !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
1217 ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
1218 BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
1220 return SLAP_CB_CONTINUE;
1223 /* Check if this connection is restricted */
1224 static int
1225 ppolicy_restrict(
1226 Operation *op,
1227 SlapReply *rs )
1229 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1230 int send_ctrl = 0;
1232 /* Did we receive a password policy request control? */
1233 if ( op->o_ctrlflag[ppolicy_cid] ) {
1234 send_ctrl = 1;
1237 if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1238 LDAPControl **oldctrls;
1239 /* if the current authcDN doesn't match the one we recorded,
1240 * then an intervening Bind has succeeded and the restriction
1241 * no longer applies. (ITS#4516)
1243 if ( !dn_match( &op->o_conn->c_ndn,
1244 &pwcons[op->o_conn->c_conn_idx].dn )) {
1245 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1246 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1247 return SLAP_CB_CONTINUE;
1250 Debug( LDAP_DEBUG_TRACE,
1251 "connection restricted to password changing only\n", 0, 0, 0);
1252 if ( send_ctrl ) {
1253 LDAPControl *ctrl = NULL;
1254 ctrl = create_passcontrol( -1, -1, PP_changeAfterReset );
1255 oldctrls = add_passcontrol( op, rs, ctrl );
1257 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1258 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
1259 "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1260 if ( send_ctrl ) {
1261 ctrls_cleanup( op, rs, oldctrls );
1263 return rs->sr_err;
1266 return SLAP_CB_CONTINUE;
1269 static int
1270 ppolicy_add(
1271 Operation *op,
1272 SlapReply *rs )
1274 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1275 pp_info *pi = on->on_bi.bi_private;
1276 PassPolicy pp;
1277 Attribute *pa;
1278 const char *txt;
1280 if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1281 return rs->sr_err;
1283 /* If this is a replica, assume the master checked everything */
1284 if ( be_shadow_update( op ))
1285 return SLAP_CB_CONTINUE;
1287 /* Check for password in entry */
1288 if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1289 slap_schema.si_ad_userPassword )))
1291 assert( pa->a_vals != NULL );
1292 assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
1294 if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
1295 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
1296 return rs->sr_err;
1300 * new entry contains a password - if we're not the root user
1301 * then we need to check that the password fits in with the
1302 * security policy for the new entry.
1304 ppolicy_get( op, op->ora_e, &pp );
1305 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1306 struct berval *bv = &(pa->a_vals[0]);
1307 int rc, send_ctrl = 0;
1308 LDAPPasswordPolicyError pErr = PP_noError;
1310 /* Did we receive a password policy request control? */
1311 if ( op->o_ctrlflag[ppolicy_cid] ) {
1312 send_ctrl = 1;
1314 rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1315 if (rc != LDAP_SUCCESS) {
1316 LDAPControl **oldctrls = NULL;
1317 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1318 if ( send_ctrl ) {
1319 LDAPControl *ctrl = NULL;
1320 ctrl = create_passcontrol( -1, -1, pErr );
1321 oldctrls = add_passcontrol( op, rs, ctrl );
1323 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1324 if ( send_ctrl ) {
1325 ctrls_cleanup( op, rs, oldctrls );
1327 return rs->sr_err;
1331 * A controversial bit. We hash cleartext
1332 * passwords provided via add and modify operations
1333 * You're not really supposed to do this, since
1334 * the X.500 model says "store attributes" as they
1335 * get provided. By default, this is what we do
1337 * But if the hash_passwords flag is set, we hash
1338 * any cleartext password attribute values via the
1339 * default password hashing scheme.
1341 if ((pi->hash_passwords) &&
1342 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1343 struct berval hpw;
1345 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1346 if (hpw.bv_val == NULL) {
1348 * hashing didn't work. Emit an error.
1350 rs->sr_err = LDAP_OTHER;
1351 rs->sr_text = txt;
1352 send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1353 return rs->sr_err;
1356 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1357 ber_memfree( pa->a_vals[0].bv_val );
1358 pa->a_vals[0].bv_val = hpw.bv_val;
1359 pa->a_vals[0].bv_len = hpw.bv_len;
1362 /* If password aging is in effect, set the pwdChangedTime */
1363 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1364 struct berval timestamp;
1365 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1366 time_t now = slap_get_time();
1368 timestamp.bv_val = timebuf;
1369 timestamp.bv_len = sizeof(timebuf);
1370 slap_timestamp( &now, &timestamp );
1372 attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
1375 return SLAP_CB_CONTINUE;
1378 static int
1379 ppolicy_mod_cb( Operation *op, SlapReply *rs )
1381 slap_callback *sc = op->o_callback;
1382 op->o_callback = sc->sc_next;
1383 if ( rs->sr_err == LDAP_SUCCESS ) {
1384 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1385 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1387 op->o_tmpfree( sc, op->o_tmpmemctx );
1388 return SLAP_CB_CONTINUE;
1391 static int
1392 ppolicy_modify( Operation *op, SlapReply *rs )
1394 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1395 pp_info *pi = on->on_bi.bi_private;
1396 int i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
1397 hsize = 0;
1398 PassPolicy pp;
1399 Modifications *mods = NULL, *modtail = NULL,
1400 *ml, *delmod, *addmod;
1401 Attribute *pa, *ha, at;
1402 const char *txt;
1403 pw_hist *tl = NULL, *p;
1404 int zapReset, send_ctrl = 0;
1405 Entry *e;
1406 struct berval newpw = BER_BVNULL, oldpw = BER_BVNULL,
1407 *bv, cr[2];
1408 LDAPPasswordPolicyError pErr = PP_noError;
1409 LDAPControl **oldctrls = NULL;
1411 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1412 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1413 op->o_bd->bd_info = (BackendInfo *)on;
1415 if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1417 /* If this is a replica, we may need to tweak some of the
1418 * master's modifications. Otherwise, just pass it through.
1420 if ( be_shadow_update( op )) {
1421 Modifications **prev;
1422 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
1423 Attribute *a_grace, *a_lock, *a_fail;
1425 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
1426 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
1427 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
1429 for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
1431 if ( ml->sml_desc == slap_schema.si_ad_userPassword )
1432 got_pw = 1;
1434 /* If we're deleting an attr that didn't exist,
1435 * drop this delete op
1437 if ( ml->sml_op == LDAP_MOD_DELETE ) {
1438 int drop = 0;
1440 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1441 got_del_grace = 1;
1442 if ( !a_grace )
1443 drop = 1;
1444 } else
1445 if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1446 got_del_lock = 1;
1447 if ( !a_lock )
1448 drop = 1;
1449 } else
1450 if ( ml->sml_desc == ad_pwdFailureTime ) {
1451 got_del_fail = 1;
1452 if ( !a_fail )
1453 drop = 1;
1455 if ( drop ) {
1456 *prev = ml->sml_next;
1457 ml->sml_next = NULL;
1458 slap_mods_free( ml, 1 );
1459 continue;
1462 prev = &ml->sml_next;
1465 /* If we're resetting the password, make sure grace, accountlock,
1466 * and failure also get removed.
1468 if ( got_pw ) {
1469 if ( a_grace && !got_del_grace ) {
1470 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1471 ml->sml_op = LDAP_MOD_DELETE;
1472 ml->sml_flags = SLAP_MOD_INTERNAL;
1473 ml->sml_type.bv_val = NULL;
1474 ml->sml_desc = ad_pwdGraceUseTime;
1475 ml->sml_numvals = 0;
1476 ml->sml_values = NULL;
1477 ml->sml_nvalues = NULL;
1478 ml->sml_next = NULL;
1479 *prev = ml;
1480 prev = &ml->sml_next;
1482 if ( a_lock && !got_del_lock ) {
1483 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1484 ml->sml_op = LDAP_MOD_DELETE;
1485 ml->sml_flags = SLAP_MOD_INTERNAL;
1486 ml->sml_type.bv_val = NULL;
1487 ml->sml_desc = ad_pwdAccountLockedTime;
1488 ml->sml_numvals = 0;
1489 ml->sml_values = NULL;
1490 ml->sml_nvalues = NULL;
1491 ml->sml_next = NULL;
1492 *prev = ml;
1494 if ( a_fail && !got_del_fail ) {
1495 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1496 ml->sml_op = LDAP_MOD_DELETE;
1497 ml->sml_flags = SLAP_MOD_INTERNAL;
1498 ml->sml_type.bv_val = NULL;
1499 ml->sml_desc = ad_pwdFailureTime;
1500 ml->sml_numvals = 0;
1501 ml->sml_values = NULL;
1502 ml->sml_nvalues = NULL;
1503 ml->sml_next = NULL;
1504 *prev = ml;
1507 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1508 be_entry_release_r( op, e );
1509 return SLAP_CB_CONTINUE;
1512 /* Did we receive a password policy request control? */
1513 if ( op->o_ctrlflag[ppolicy_cid] ) {
1514 send_ctrl = 1;
1517 /* See if this is a pwdModify exop. If so, we can
1518 * access the plaintext passwords from that request.
1521 slap_callback *sc;
1523 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1524 if ( sc->sc_response == slap_null_cb &&
1525 sc->sc_private ) {
1526 req_pwdexop_s *qpw = sc->sc_private;
1527 newpw = qpw->rs_new;
1528 oldpw = qpw->rs_old;
1529 break;
1534 ppolicy_get( op, e, &pp );
1536 for ( ml = op->orm_modlist,
1537 pwmod = 0, mod_pw_only = 1,
1538 deladd = 0, delmod = NULL,
1539 addmod = NULL,
1540 zapReset = 1;
1541 ml != NULL; modtail = ml, ml = ml->sml_next )
1543 if ( ml->sml_desc == pp.ad ) {
1544 pwmod = 1;
1545 pwmop = ml->sml_op;
1546 if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1547 (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
1549 deladd = 1;
1550 delmod = ml;
1553 if ((ml->sml_op == LDAP_MOD_ADD) ||
1554 (ml->sml_op == LDAP_MOD_REPLACE))
1556 if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
1557 if ( deladd == 1 )
1558 deladd = 2;
1560 /* FIXME: there's no easy way to ensure
1561 * that add does not cause multiple
1562 * userPassword values; one way (that
1563 * would be consistent with the single
1564 * password constraint) would be to turn
1565 * add into replace); another would be
1566 * to disallow add.
1568 * Let's check at least that a single value
1569 * is being added
1571 if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
1572 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1573 rs->sr_text = "Password policy only allows one password value";
1574 goto return_results;
1577 addmod = ml;
1578 } else {
1579 /* replace can have no values, add cannot */
1580 assert( ml->sml_op == LDAP_MOD_REPLACE );
1584 } else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
1585 mod_pw_only = 0;
1586 /* modifying something other than password */
1590 * If there is a request to explicitly add a pwdReset
1591 * attribute, then we suppress the normal behaviour on
1592 * password change, which is to remove the pwdReset
1593 * attribute.
1595 * This enables an administrator to assign a new password
1596 * and place a "must reset" flag on the entry, which will
1597 * stay until the user explicitly changes his/her password.
1599 if (ml->sml_desc == ad_pwdReset ) {
1600 if ((ml->sml_op == LDAP_MOD_ADD) ||
1601 (ml->sml_op == LDAP_MOD_REPLACE))
1602 zapReset = 0;
1606 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
1607 if ( dn_match( &op->o_conn->c_ndn,
1608 &pwcons[op->o_conn->c_conn_idx].dn )) {
1609 Debug( LDAP_DEBUG_TRACE,
1610 "connection restricted to password changing only\n", 0, 0, 0 );
1611 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1612 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1613 pErr = PP_changeAfterReset;
1614 goto return_results;
1615 } else {
1616 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1617 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1622 * if we have a "safe password modify policy", then we need to check if we're doing
1623 * a delete (with the old password), followed by an add (with the new password).
1625 * If we got just a delete with nothing else, just let it go. We also skip all the checks if
1626 * the root user is bound. Root can do anything, including avoid the policies.
1629 if (!pwmod) goto do_modify;
1632 * Build the password history list in ascending time order
1633 * We need this, even if the user is root, in order to maintain
1634 * the pwdHistory operational attributes properly.
1636 if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1637 struct berval oldpw;
1638 time_t oldtime;
1640 for(i=0; ha->a_nvals[i].bv_val; i++) {
1641 rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1642 &oldtime, &oldpw );
1644 if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1646 if (oldpw.bv_val) {
1647 add_to_pwd_history( &tl, oldtime, &oldpw,
1648 &(ha->a_nvals[i]) );
1649 oldpw.bv_val = NULL;
1650 oldpw.bv_len = 0;
1653 for(p=tl; p; p=p->next, hsize++); /* count history size */
1656 if (be_isroot( op )) goto do_modify;
1658 if (!pp.pwdAllowUserChange) {
1659 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1660 rs->sr_text = "User alteration of password is not allowed";
1661 pErr = PP_passwordModNotAllowed;
1662 goto return_results;
1665 /* Just deleting? */
1666 if (!addmod) {
1667 /* skip everything else */
1668 pwmod = 0;
1669 goto do_modify;
1672 /* This is a pwdModify exop that provided the old pw.
1673 * We need to create a Delete mod for this old pw and
1674 * let the matching value get found later
1676 if (pp.pwdSafeModify && oldpw.bv_val ) {
1677 ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
1678 ml->sml_op = LDAP_MOD_DELETE;
1679 ml->sml_flags = SLAP_MOD_INTERNAL;
1680 ml->sml_desc = pp.ad;
1681 ml->sml_type = pp.ad->ad_cname;
1682 ml->sml_numvals = 1;
1683 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1684 ber_dupbv( &ml->sml_values[0], &oldpw );
1685 BER_BVZERO( &ml->sml_values[1] );
1686 ml->sml_next = op->orm_modlist;
1687 op->orm_modlist = ml;
1688 delmod = ml;
1689 deladd = 2;
1692 if (pp.pwdSafeModify && deladd != 2) {
1693 Debug( LDAP_DEBUG_TRACE,
1694 "change password must use DELETE followed by ADD/REPLACE\n",
1695 0, 0, 0 );
1696 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1697 rs->sr_text = "Must supply old password to be changed as well as new one";
1698 pErr = PP_mustSupplyOldPassword;
1699 goto return_results;
1702 /* Check age, but only if pwdReset is not TRUE */
1703 pa = attr_find( e->e_attrs, ad_pwdReset );
1704 if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
1705 pp.pwdMinAge > 0) {
1706 time_t pwtime = (time_t)-1, now;
1707 int age;
1709 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1710 pwtime = parse_time( pa->a_nvals[0].bv_val );
1711 now = slap_get_time();
1712 age = (int)(now - pwtime);
1713 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1714 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1715 rs->sr_text = "Password is too young to change";
1716 pErr = PP_passwordTooYoung;
1717 goto return_results;
1721 /* pa is used in password history check below, be sure it's set */
1722 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1724 * we have a password to check
1726 const char *txt;
1728 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1729 /* FIXME: no access checking? */
1730 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1731 if (rc != LDAP_SUCCESS) {
1732 Debug( LDAP_DEBUG_TRACE,
1733 "old password check failed: %s\n", txt, 0, 0 );
1735 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1736 rs->sr_text = "Must supply correct old password to change to new one";
1737 pErr = PP_mustSupplyOldPassword;
1738 goto return_results;
1740 } else {
1741 int i;
1744 * replace the delete value with the (possibly hashed)
1745 * value which is currently in the password.
1747 for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
1748 free( delmod->sml_values[i].bv_val );
1749 BER_BVZERO( &delmod->sml_values[i] );
1751 free( delmod->sml_values );
1752 delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1753 BER_BVZERO( &delmod->sml_values[1] );
1754 ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) );
1758 bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
1759 if (pp.pwdCheckQuality > 0) {
1761 rc = check_password_quality( bv, &pp, &pErr, e );
1762 if (rc != LDAP_SUCCESS) {
1763 rs->sr_err = rc;
1764 rs->sr_text = "Password fails quality checking policy";
1765 goto return_results;
1769 /* If pwdInHistory is zero, passwords may be reused */
1770 if (pa && pp.pwdInHistory > 0) {
1772 * Last check - the password history.
1774 /* FIXME: no access checking? */
1775 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1777 * This is bad - it means that the user is attempting
1778 * to set the password to the same as the old one.
1780 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1781 rs->sr_text = "Password is not being changed from existing value";
1782 pErr = PP_passwordInHistory;
1783 goto return_results;
1787 * Iterate through the password history, and fail on any
1788 * password matches.
1790 at = *pa;
1791 at.a_vals = cr;
1792 cr[1].bv_val = NULL;
1793 for(p=tl; p; p=p->next) {
1794 cr[0] = p->pw;
1795 /* FIXME: no access checking? */
1796 rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1798 if (rc != LDAP_SUCCESS) continue;
1800 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1801 rs->sr_text = "Password is in history of old passwords";
1802 pErr = PP_passwordInHistory;
1803 goto return_results;
1807 do_modify:
1808 if (pwmod) {
1809 struct berval timestamp;
1810 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1811 time_t now = slap_get_time();
1813 /* If the conn is restricted, set a callback to clear it
1814 * if the pwmod succeeds
1816 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1817 slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
1818 op->o_tmpmemctx );
1819 sc->sc_next = op->o_callback;
1820 /* Must use sc_response to insure we reset on success, before
1821 * the client sees the response. Must use sc_cleanup to insure
1822 * that it gets cleaned up if sc_response is not called.
1824 sc->sc_response = ppolicy_mod_cb;
1825 sc->sc_cleanup = ppolicy_mod_cb;
1826 op->o_callback = sc;
1830 * keep the necessary pwd.. operational attributes
1831 * up to date.
1834 timestamp.bv_val = timebuf;
1835 timestamp.bv_len = sizeof(timebuf);
1836 slap_timestamp( &now, &timestamp );
1838 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1839 mods->sml_desc = ad_pwdChangedTime;
1840 if (pwmop != LDAP_MOD_DELETE) {
1841 mods->sml_op = LDAP_MOD_REPLACE;
1842 mods->sml_numvals = 1;
1843 mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1844 ber_dupbv( &mods->sml_values[0], &timestamp );
1845 BER_BVZERO( &mods->sml_values[1] );
1846 assert( !BER_BVISNULL( &mods->sml_values[0] ) );
1848 } else {
1849 mods->sml_op = LDAP_MOD_DELETE;
1851 mods->sml_flags = SLAP_MOD_INTERNAL;
1852 mods->sml_next = NULL;
1853 modtail->sml_next = mods;
1854 modtail = mods;
1856 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1857 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1858 mods->sml_op = LDAP_MOD_DELETE;
1859 mods->sml_desc = ad_pwdGraceUseTime;
1860 mods->sml_flags = SLAP_MOD_INTERNAL;
1861 mods->sml_next = NULL;
1862 modtail->sml_next = mods;
1863 modtail = mods;
1866 if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
1867 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1868 mods->sml_op = LDAP_MOD_DELETE;
1869 mods->sml_desc = ad_pwdAccountLockedTime;
1870 mods->sml_flags = SLAP_MOD_INTERNAL;
1871 mods->sml_next = NULL;
1872 modtail->sml_next = mods;
1873 modtail = mods;
1876 if (attr_find(e->e_attrs, ad_pwdFailureTime )) {
1877 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1878 mods->sml_op = LDAP_MOD_DELETE;
1879 mods->sml_desc = ad_pwdFailureTime;
1880 mods->sml_flags = SLAP_MOD_INTERNAL;
1881 mods->sml_next = NULL;
1882 modtail->sml_next = mods;
1883 modtail = mods;
1886 /* Delete the pwdReset attribute, since it's being reset */
1887 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1888 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1889 mods->sml_op = LDAP_MOD_DELETE;
1890 mods->sml_desc = ad_pwdReset;
1891 mods->sml_flags = SLAP_MOD_INTERNAL;
1892 mods->sml_next = NULL;
1893 modtail->sml_next = mods;
1894 modtail = mods;
1897 if (pp.pwdInHistory > 0) {
1898 if (hsize >= pp.pwdInHistory) {
1900 * We use the >= operator, since we are going to add
1901 * the existing password attribute value into the
1902 * history - thus the cardinality of history values is
1903 * about to rise by one.
1905 * If this would push it over the limit of history
1906 * values (remembering - the password policy could have
1907 * changed since the password was last altered), we must
1908 * delete at least 1 value from the pwdHistory list.
1910 * In fact, we delete '(#pwdHistory attrs - max pwd
1911 * history length) + 1' values, starting with the oldest.
1912 * This is easily evaluated, since the linked list is
1913 * created in ascending time order.
1915 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
1916 mods->sml_op = LDAP_MOD_DELETE;
1917 mods->sml_flags = SLAP_MOD_INTERNAL;
1918 mods->sml_desc = ad_pwdHistory;
1919 mods->sml_numvals = hsize - pp.pwdInHistory + 1;
1920 mods->sml_values = ch_calloc( sizeof( struct berval ),
1921 hsize - pp.pwdInHistory + 2 );
1922 BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
1923 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1924 BER_BVZERO( &mods->sml_values[i] );
1925 ber_dupbv( &(mods->sml_values[i]), &p->bv );
1927 mods->sml_next = NULL;
1928 modtail->sml_next = mods;
1929 modtail = mods;
1931 free_pwd_history_list( &tl );
1934 * Now add the existing password into the history list.
1935 * This will be executed even if the operation is to delete
1936 * the password entirely.
1938 * This isn't in the spec explicitly, but it seems to make
1939 * sense that the password history list is the list of all
1940 * previous passwords - even if they were deleted. Thus, if
1941 * someone tries to add a historical password at some future
1942 * point, it will fail.
1944 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1945 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1946 mods->sml_op = LDAP_MOD_ADD;
1947 mods->sml_flags = SLAP_MOD_INTERNAL;
1948 mods->sml_type.bv_val = NULL;
1949 mods->sml_desc = ad_pwdHistory;
1950 mods->sml_nvalues = NULL;
1951 mods->sml_numvals = 1;
1952 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1953 mods->sml_values[ 1 ].bv_val = NULL;
1954 mods->sml_values[ 1 ].bv_len = 0;
1955 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1956 mods->sml_next = NULL;
1957 modtail->sml_next = mods;
1958 modtail = mods;
1960 } else {
1961 Debug( LDAP_DEBUG_TRACE,
1962 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1967 * Controversial bit here. If the new password isn't hashed
1968 * (ie, is cleartext), we probably should hash it according
1969 * to the default hash. The reason for this is that we want
1970 * to use the policy if possible, but if we hash the password
1971 * before, then we're going to run into trouble when it
1972 * comes time to check the password.
1974 * Now, the right thing to do is to use the extended password
1975 * modify operation, but not all software can do this,
1976 * therefore it makes sense to hash the new password, now
1977 * we know it passes the policy requirements.
1979 * Of course, if the password is already hashed, then we
1980 * leave it alone.
1983 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val &&
1984 (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
1986 struct berval hpw, bv;
1988 slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1989 if (hpw.bv_val == NULL) {
1991 * hashing didn't work. Emit an error.
1993 rs->sr_err = LDAP_OTHER;
1994 rs->sr_text = txt;
1995 goto return_results;
1997 bv = addmod->sml_values[0];
1998 /* clear and discard the clear password */
1999 memset(bv.bv_val, 0, bv.bv_len);
2000 ber_memfree(bv.bv_val);
2001 addmod->sml_values[0] = hpw;
2004 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2005 be_entry_release_r( op, e );
2006 return SLAP_CB_CONTINUE;
2008 return_results:
2009 free_pwd_history_list( &tl );
2010 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2011 be_entry_release_r( op, e );
2012 if ( send_ctrl ) {
2013 LDAPControl *ctrl = NULL;
2015 ctrl = create_passcontrol( -1, -1, pErr );
2016 oldctrls = add_passcontrol( op, rs, ctrl );
2018 send_ldap_result( op, rs );
2019 if ( send_ctrl ) {
2020 ctrls_cleanup( op, rs, oldctrls );
2022 return rs->sr_err;
2025 static int
2026 ppolicy_parseCtrl(
2027 Operation *op,
2028 SlapReply *rs,
2029 LDAPControl *ctrl )
2031 if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
2032 rs->sr_text = "passwordPolicyRequest control value not absent";
2033 return LDAP_PROTOCOL_ERROR;
2035 op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
2036 ? SLAP_CONTROL_CRITICAL
2037 : SLAP_CONTROL_NONCRITICAL;
2039 return LDAP_SUCCESS;
2042 static int
2043 attrPretty(
2044 Syntax *syntax,
2045 struct berval *val,
2046 struct berval *out,
2047 void *ctx )
2049 AttributeDescription *ad = NULL;
2050 const char *err;
2051 int code;
2053 code = slap_bv2ad( val, &ad, &err );
2054 if ( !code ) {
2055 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
2057 return code;
2060 static int
2061 attrNormalize(
2062 slap_mask_t use,
2063 Syntax *syntax,
2064 MatchingRule *mr,
2065 struct berval *val,
2066 struct berval *out,
2067 void *ctx )
2069 AttributeDescription *ad = NULL;
2070 const char *err;
2071 int code;
2073 code = slap_bv2ad( val, &ad, &err );
2074 if ( !code ) {
2075 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
2077 return code;
2080 static int
2081 ppolicy_db_init(
2082 BackendDB *be,
2083 ConfigReply *cr
2086 slap_overinst *on = (slap_overinst *) be->bd_info;
2088 /* Has User Schema been initialized yet? */
2089 if ( !pwd_UsSchema[0].ad[0] ) {
2090 const char *err;
2091 int i, code;
2093 for (i=0; pwd_UsSchema[i].def; i++) {
2094 code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
2095 if ( code ) {
2096 if ( cr ){
2097 snprintf( cr->msg, sizeof(cr->msg),
2098 "User Schema load failed for attribute \"%s\". Error code %d: %s",
2099 pwd_UsSchema[i].def, code, err );
2100 fprintf( stderr, "%s\n", cr->msg );
2102 return code;
2106 Syntax *syn;
2107 MatchingRule *mr;
2109 syn = ch_malloc( sizeof( Syntax ));
2110 *syn = *ad_pwdAttribute->ad_type->sat_syntax;
2111 syn->ssyn_pretty = attrPretty;
2112 ad_pwdAttribute->ad_type->sat_syntax = syn;
2114 mr = ch_malloc( sizeof( MatchingRule ));
2115 *mr = *ad_pwdAttribute->ad_type->sat_equality;
2116 mr->smr_normalize = attrNormalize;
2117 ad_pwdAttribute->ad_type->sat_equality = mr;
2121 on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
2123 if ( dtblsize && !pwcons ) {
2124 /* accommodate for c_conn_idx == -1 */
2125 pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
2126 pwcons++;
2129 return 0;
2132 static int
2133 ppolicy_db_open(
2134 BackendDB *be,
2135 ConfigReply *cr
2138 ov_count++;
2139 return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
2142 static int
2143 ppolicy_close(
2144 BackendDB *be,
2145 ConfigReply *cr
2148 slap_overinst *on = (slap_overinst *) be->bd_info;
2149 pp_info *pi = on->on_bi.bi_private;
2151 /* Perhaps backover should provide bi_destroy hooks... */
2152 ov_count--;
2153 if ( ov_count <=0 && pwcons ) {
2154 pwcons--;
2155 free( pwcons );
2156 pwcons = NULL;
2158 free( pi->def_policy.bv_val );
2159 free( pi );
2161 return 0;
2164 static char *extops[] = {
2165 LDAP_EXOP_MODIFY_PASSWD,
2166 NULL
2169 static slap_overinst ppolicy;
2171 int ppolicy_initialize()
2173 int i, code;
2175 for (i=0; pwd_OpSchema[i].def; i++) {
2176 code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
2177 if ( code ) {
2178 Debug( LDAP_DEBUG_ANY,
2179 "ppolicy_initialize: register_at failed\n", 0, 0, 0 );
2180 return code;
2182 /* Allow Manager to set these as needed */
2183 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
2184 (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
2185 SLAP_AT_MANAGEABLE;
2189 code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
2190 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
2191 ppolicy_parseCtrl, &ppolicy_cid );
2192 if ( code != LDAP_SUCCESS ) {
2193 fprintf( stderr, "Failed to register control %d\n", code );
2194 return code;
2197 ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
2199 ppolicy.on_bi.bi_type = "ppolicy";
2200 ppolicy.on_bi.bi_db_init = ppolicy_db_init;
2201 ppolicy.on_bi.bi_db_open = ppolicy_db_open;
2202 ppolicy.on_bi.bi_db_close = ppolicy_close;
2204 ppolicy.on_bi.bi_op_add = ppolicy_add;
2205 ppolicy.on_bi.bi_op_bind = ppolicy_bind;
2206 ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
2207 ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
2208 ppolicy.on_bi.bi_op_modify = ppolicy_modify;
2209 ppolicy.on_bi.bi_op_search = ppolicy_restrict;
2210 ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
2212 ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
2213 code = config_register_schema( ppolicycfg, ppolicyocs );
2214 if ( code ) return code;
2216 return overlay_register( &ppolicy );
2219 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
2220 int init_module(int argc, char *argv[]) {
2221 return ppolicy_initialize();
2223 #endif
2225 #endif /* defined(SLAPD_OVER_PPOLICY) */