ppolicy.c revision 1.1.1.5
1/* $NetBSD: ppolicy.c,v 1.1.1.5 2014/05/28 09:58:52 tron Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 2004-2014 The OpenLDAP Foundation. 7 * Portions Copyright 2004-2005 Howard Chu, Symas Corporation. 8 * Portions Copyright 2004 Hewlett-Packard Company. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19/* ACKNOWLEDGEMENTS: 20 * This work was developed by Howard Chu for inclusion in 21 * OpenLDAP Software, based on prior work by Neil Dunbar (HP). 22 * This work was sponsored by the Hewlett-Packard Company. 23 */ 24 25#include "portable.h" 26 27/* This file implements "Password Policy for LDAP Directories", 28 * based on draft behera-ldap-password-policy-09 29 */ 30 31#ifdef SLAPD_OVER_PPOLICY 32 33#include <ldap.h> 34#include "lutil.h" 35#include "slap.h" 36#ifdef SLAPD_MODULES 37#define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */ 38#include <ltdl.h> 39#endif 40#include <ac/errno.h> 41#include <ac/time.h> 42#include <ac/string.h> 43#include <ac/ctype.h> 44#include "config.h" 45 46#ifndef MODULE_NAME_SZ 47#define MODULE_NAME_SZ 256 48#endif 49 50/* Per-instance configuration information */ 51typedef struct pp_info { 52 struct berval def_policy; /* DN of default policy subentry */ 53 int use_lockout; /* send AccountLocked result? */ 54 int hash_passwords; /* transparently hash cleartext pwds */ 55 int forward_updates; /* use frontend for policy state updates */ 56} pp_info; 57 58/* Our per-connection info - note, it is not per-instance, it is 59 * used by all instances 60 */ 61typedef struct pw_conn { 62 struct berval dn; /* DN of restricted user */ 63} pw_conn; 64 65static pw_conn *pwcons; 66static int ppolicy_cid; 67static int ov_count; 68 69typedef struct pass_policy { 70 AttributeDescription *ad; /* attribute to which the policy applies */ 71 int pwdMinAge; /* minimum time (seconds) until passwd can change */ 72 int pwdMaxAge; /* time in seconds until pwd will expire after change */ 73 int pwdInHistory; /* number of previous passwords kept */ 74 int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible, 75 2 = check mandatory; fail if not possible */ 76 int pwdMinLength; /* minimum number of chars in password */ 77 int pwdExpireWarning; /* number of seconds that warning controls are 78 sent before a password expires */ 79 int pwdGraceAuthNLimit; /* number of times you can log in with an 80 expired password */ 81 int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */ 82 int pwdLockoutDuration; /* time in seconds a password is locked out for */ 83 int pwdMaxFailure; /* number of failed binds allowed before lockout */ 84 int pwdFailureCountInterval; /* number of seconds before failure 85 counts are zeroed */ 86 int pwdMustChange; /* 0 = users can use admin set password 87 1 = users must change password after admin set */ 88 int pwdAllowUserChange; /* 0 = users cannot change their passwords 89 1 = users can change them */ 90 int pwdSafeModify; /* 0 = old password doesn't need to come 91 with password change request 92 1 = password change must supply existing pwd */ 93 char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically 94 load to check password */ 95} PassPolicy; 96 97typedef struct pw_hist { 98 time_t t; /* timestamp of history entry */ 99 struct berval pw; /* old password hash */ 100 struct berval bv; /* text of entire entry */ 101 struct pw_hist *next; 102} pw_hist; 103 104/* Operational attributes */ 105static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime, 106 *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset, 107 *ad_pwdPolicySubentry; 108 109static struct schema_info { 110 char *def; 111 AttributeDescription **ad; 112} pwd_OpSchema[] = { 113 { "( 1.3.6.1.4.1.42.2.27.8.1.16 " 114 "NAME ( 'pwdChangedTime' ) " 115 "DESC 'The time the password was last changed' " 116 "EQUALITY generalizedTimeMatch " 117 "ORDERING generalizedTimeOrderingMatch " 118 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 119 "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", 120 &ad_pwdChangedTime }, 121 { "( 1.3.6.1.4.1.42.2.27.8.1.17 " 122 "NAME ( 'pwdAccountLockedTime' ) " 123 "DESC 'The time an user account was locked' " 124 "EQUALITY generalizedTimeMatch " 125 "ORDERING generalizedTimeOrderingMatch " 126 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 127 "SINGLE-VALUE " 128#if 0 129 /* Not until Relax control is released */ 130 "NO-USER-MODIFICATION " 131#endif 132 "USAGE directoryOperation )", 133 &ad_pwdAccountLockedTime }, 134 { "( 1.3.6.1.4.1.42.2.27.8.1.19 " 135 "NAME ( 'pwdFailureTime' ) " 136 "DESC 'The timestamps of the last consecutive authentication failures' " 137 "EQUALITY generalizedTimeMatch " 138 "ORDERING generalizedTimeOrderingMatch " 139 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 140 "NO-USER-MODIFICATION USAGE directoryOperation )", 141 &ad_pwdFailureTime }, 142 { "( 1.3.6.1.4.1.42.2.27.8.1.20 " 143 "NAME ( 'pwdHistory' ) " 144 "DESC 'The history of users passwords' " 145 "EQUALITY octetStringMatch " 146 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 " 147 "NO-USER-MODIFICATION USAGE directoryOperation )", 148 &ad_pwdHistory }, 149 { "( 1.3.6.1.4.1.42.2.27.8.1.21 " 150 "NAME ( 'pwdGraceUseTime' ) " 151 "DESC 'The timestamps of the grace login once the password has expired' " 152 "EQUALITY generalizedTimeMatch " 153 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 154 "NO-USER-MODIFICATION USAGE directoryOperation )", 155 &ad_pwdGraceUseTime }, 156 { "( 1.3.6.1.4.1.42.2.27.8.1.22 " 157 "NAME ( 'pwdReset' ) " 158 "DESC 'The indication that the password has been reset' " 159 "EQUALITY booleanMatch " 160 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 " 161 "SINGLE-VALUE USAGE directoryOperation )", 162 &ad_pwdReset }, 163 { "( 1.3.6.1.4.1.42.2.27.8.1.23 " 164 "NAME ( 'pwdPolicySubentry' ) " 165 "DESC 'The pwdPolicy subentry in effect for this object' " 166 "EQUALITY distinguishedNameMatch " 167 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " 168 "SINGLE-VALUE " 169#if 0 170 /* Not until Relax control is released */ 171 "NO-USER-MODIFICATION " 172#endif 173 "USAGE directoryOperation )", 174 &ad_pwdPolicySubentry }, 175 { NULL, NULL } 176}; 177 178/* User attributes */ 179static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory, 180 *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure, 181 *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration, 182 *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout, 183 *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify, 184 *ad_pwdAttribute; 185 186#define TAB(name) { #name, &ad_##name } 187 188static struct schema_info pwd_UsSchema[] = { 189 TAB(pwdAttribute), 190 TAB(pwdMinAge), 191 TAB(pwdMaxAge), 192 TAB(pwdInHistory), 193 TAB(pwdCheckQuality), 194 TAB(pwdMinLength), 195 TAB(pwdMaxFailure), 196 TAB(pwdGraceAuthNLimit), 197 TAB(pwdExpireWarning), 198 TAB(pwdLockout), 199 TAB(pwdLockoutDuration), 200 TAB(pwdFailureCountInterval), 201 TAB(pwdCheckModule), 202 TAB(pwdMustChange), 203 TAB(pwdAllowUserChange), 204 TAB(pwdSafeModify), 205 { NULL, NULL } 206}; 207 208static ldap_pvt_thread_mutex_t chk_syntax_mutex; 209 210enum { 211 PPOLICY_DEFAULT = 1, 212 PPOLICY_HASH_CLEARTEXT, 213 PPOLICY_USE_LOCKOUT 214}; 215 216static ConfigDriver ppolicy_cf_default; 217 218static ConfigTable ppolicycfg[] = { 219 { "ppolicy_default", "policyDN", 2, 2, 0, 220 ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default, 221 "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' " 222 "DESC 'DN of a pwdPolicy object for uncustomized objects' " 223 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, 224 { "ppolicy_hash_cleartext", "on|off", 1, 2, 0, 225 ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT, 226 (void *)offsetof(pp_info,hash_passwords), 227 "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' " 228 "DESC 'Hash passwords on add or modify' " 229 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 230 { "ppolicy_forward_updates", "on|off", 1, 2, 0, 231 ARG_ON_OFF|ARG_OFFSET, 232 (void *)offsetof(pp_info,forward_updates), 233 "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' " 234 "DESC 'Allow policy state updates to be forwarded via updateref' " 235 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 236 { "ppolicy_use_lockout", "on|off", 1, 2, 0, 237 ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT, 238 (void *)offsetof(pp_info,use_lockout), 239 "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' " 240 "DESC 'Warn clients with AccountLocked' " 241 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 242 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 243}; 244 245static ConfigOCs ppolicyocs[] = { 246 { "( OLcfgOvOc:12.1 " 247 "NAME 'olcPPolicyConfig' " 248 "DESC 'Password Policy configuration' " 249 "SUP olcOverlayConfig " 250 "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ " 251 "olcPPolicyUseLockout $ olcPPolicyForwardUpdates ) )", 252 Cft_Overlay, ppolicycfg }, 253 { NULL, 0, NULL } 254}; 255 256static int 257ppolicy_cf_default( ConfigArgs *c ) 258{ 259 slap_overinst *on = (slap_overinst *)c->bi; 260 pp_info *pi = (pp_info *)on->on_bi.bi_private; 261 int rc = ARG_BAD_CONF; 262 263 assert ( c->type == PPOLICY_DEFAULT ); 264 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n", 0, 0, 0); 265 266 switch ( c->op ) { 267 case SLAP_CONFIG_EMIT: 268 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n", 0, 0, 0); 269 rc = 0; 270 if ( !BER_BVISEMPTY( &pi->def_policy )) { 271 rc = value_add_one( &c->rvalue_vals, 272 &pi->def_policy ); 273 if ( rc ) return rc; 274 rc = value_add_one( &c->rvalue_nvals, 275 &pi->def_policy ); 276 } 277 break; 278 case LDAP_MOD_DELETE: 279 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n", 0, 0, 0); 280 if ( pi->def_policy.bv_val ) { 281 ber_memfree ( pi->def_policy.bv_val ); 282 pi->def_policy.bv_val = NULL; 283 } 284 pi->def_policy.bv_len = 0; 285 rc = 0; 286 break; 287 case SLAP_CONFIG_ADD: 288 /* fallthrough to LDAP_MOD_ADD */ 289 case LDAP_MOD_ADD: 290 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n", 0, 0, 0); 291 if ( pi->def_policy.bv_val ) { 292 ber_memfree ( pi->def_policy.bv_val ); 293 } 294 pi->def_policy = c->value_ndn; 295 ber_memfree( c->value_dn.bv_val ); 296 BER_BVZERO( &c->value_dn ); 297 BER_BVZERO( &c->value_ndn ); 298 rc = 0; 299 break; 300 default: 301 abort (); 302 } 303 304 return rc; 305} 306 307static time_t 308parse_time( char *atm ) 309{ 310 struct lutil_tm tm; 311 struct lutil_timet tt; 312 time_t ret = (time_t)-1; 313 314 if ( lutil_parsetime( atm, &tm ) == 0) { 315 lutil_tm2time( &tm, &tt ); 316 ret = tt.tt_sec; 317 } 318 return ret; 319} 320 321static int 322account_locked( Operation *op, Entry *e, 323 PassPolicy *pp, Modifications **mod ) 324{ 325 Attribute *la; 326 327 assert(mod != NULL); 328 329 if ( !pp->pwdLockout ) 330 return 0; 331 332 if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) { 333 BerVarray vals = la->a_nvals; 334 335 /* 336 * there is a lockout stamp - we now need to know if it's 337 * a valid one. 338 */ 339 if (vals[0].bv_val != NULL) { 340 time_t then, now; 341 Modifications *m; 342 343 if (!pp->pwdLockoutDuration) 344 return 1; 345 346 if ((then = parse_time( vals[0].bv_val )) == (time_t)0) 347 return 1; 348 349 now = slap_get_time(); 350 351 if (now < then + pp->pwdLockoutDuration) 352 return 1; 353 354 m = ch_calloc( sizeof(Modifications), 1 ); 355 m->sml_op = LDAP_MOD_DELETE; 356 m->sml_flags = 0; 357 m->sml_type = ad_pwdAccountLockedTime->ad_cname; 358 m->sml_desc = ad_pwdAccountLockedTime; 359 m->sml_next = *mod; 360 *mod = m; 361 } 362 } 363 364 return 0; 365} 366 367/* IMPLICIT TAGS, all context-specific */ 368#define PPOLICY_WARNING 0xa0L /* constructed + 0 */ 369#define PPOLICY_ERROR 0x81L /* primitive + 1 */ 370 371#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */ 372#define PPOLICY_GRACE 0x81L /* primitive + 1 */ 373 374static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE; 375 376static LDAPControl * 377create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err ) 378{ 379 BerElementBuffer berbuf, bb2; 380 BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2; 381 LDAPControl c = { 0 }, *cp; 382 struct berval bv; 383 384 BER_BVZERO( &c.ldctl_value ); 385 386 ber_init2( ber, NULL, LBER_USE_DER ); 387 ber_printf( ber, "{" /*}*/ ); 388 389 if ( exptime >= 0 ) { 390 ber_init2( b2, NULL, LBER_USE_DER ); 391 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime ); 392 ber_flatten2( b2, &bv, 1 ); 393 (void)ber_free_buf(b2); 394 ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); 395 ch_free( bv.bv_val ); 396 } else if ( grace > 0 ) { 397 ber_init2( b2, NULL, LBER_USE_DER ); 398 ber_printf( b2, "ti", PPOLICY_GRACE, grace ); 399 ber_flatten2( b2, &bv, 1 ); 400 (void)ber_free_buf(b2); 401 ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); 402 ch_free( bv.bv_val ); 403 } 404 405 if (err != PP_noError ) { 406 ber_printf( ber, "te", PPOLICY_ERROR, err ); 407 } 408 ber_printf( ber, /*{*/ "N}" ); 409 410 if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) { 411 return NULL; 412 } 413 cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx ); 414 cp->ldctl_oid = (char *)ppolicy_ctrl_oid; 415 cp->ldctl_iscritical = 0; 416 cp->ldctl_value.bv_val = (char *)&cp[1]; 417 cp->ldctl_value.bv_len = c.ldctl_value.bv_len; 418 AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len ); 419 (void)ber_free_buf(ber); 420 421 return cp; 422} 423 424static LDAPControl ** 425add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl ) 426{ 427 LDAPControl **ctrls, **oldctrls = rs->sr_ctrls; 428 int n; 429 430 n = 0; 431 if ( oldctrls ) { 432 for ( ; oldctrls[n]; n++ ) 433 ; 434 } 435 n += 2; 436 437 ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx ); 438 439 n = 0; 440 if ( oldctrls ) { 441 for ( ; oldctrls[n]; n++ ) { 442 ctrls[n] = oldctrls[n]; 443 } 444 } 445 ctrls[n] = ctrl; 446 ctrls[n+1] = NULL; 447 448 rs->sr_ctrls = ctrls; 449 450 return oldctrls; 451} 452 453static void 454ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) 455{ 456 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 457 pp_info *pi = on->on_bi.bi_private; 458 Attribute *a; 459 BerVarray vals; 460 int rc; 461 Entry *pe = NULL; 462#if 0 463 const char *text; 464#endif 465 466 memset( pp, 0, sizeof(PassPolicy) ); 467 468 pp->ad = slap_schema.si_ad_userPassword; 469 470 /* Users can change their own password by default */ 471 pp->pwdAllowUserChange = 1; 472 473 if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) { 474 /* 475 * entry has no password policy assigned - use default 476 */ 477 vals = &pi->def_policy; 478 if ( !vals->bv_val ) 479 goto defaultpol; 480 } else { 481 vals = a->a_nvals; 482 if (vals[0].bv_val == NULL) { 483 Debug( LDAP_DEBUG_ANY, 484 "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 ); 485 goto defaultpol; 486 } 487 } 488 489 op->o_bd->bd_info = (BackendInfo *)on->on_info; 490 rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe ); 491 op->o_bd->bd_info = (BackendInfo *)on; 492 493 if ( rc ) goto defaultpol; 494 495#if 0 /* Only worry about userPassword for now */ 496 if ((a = attr_find( pe->e_attrs, ad_pwdAttribute ))) 497 slap_bv2ad( &a->a_vals[0], &pp->ad, &text ); 498#endif 499 500 if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) ) 501 && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 ) 502 goto defaultpol; 503 if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) ) 504 && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 ) 505 goto defaultpol; 506 if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) ) 507 && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 ) 508 goto defaultpol; 509 if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckQuality ) ) 510 && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 ) 511 goto defaultpol; 512 if ( ( a = attr_find( pe->e_attrs, ad_pwdMinLength ) ) 513 && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 ) 514 goto defaultpol; 515 if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) ) 516 && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) 517 goto defaultpol; 518 if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) ) 519 && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) 520 goto defaultpol; 521 if ( ( a = attr_find( pe->e_attrs, ad_pwdExpireWarning ) ) 522 && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 ) 523 goto defaultpol; 524 if ( ( a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ) ) 525 && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 ) 526 goto defaultpol; 527 if ( ( a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ) ) 528 && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 ) 529 goto defaultpol; 530 531 if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckModule ) ) ) { 532 strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val, 533 sizeof(pp->pwdCheckModule) ); 534 pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0'; 535 } 536 537 if ((a = attr_find( pe->e_attrs, ad_pwdLockout ))) 538 pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv ); 539 if ((a = attr_find( pe->e_attrs, ad_pwdMustChange ))) 540 pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv ); 541 if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange ))) 542 pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv ); 543 if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify ))) 544 pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv ); 545 546 op->o_bd->bd_info = (BackendInfo *)on->on_info; 547 be_entry_release_r( op, pe ); 548 op->o_bd->bd_info = (BackendInfo *)on; 549 550 return; 551 552defaultpol: 553 Debug( LDAP_DEBUG_TRACE, 554 "ppolicy_get: using default policy\n", 0, 0, 0 ); 555 return; 556} 557 558static int 559password_scheme( struct berval *cred, struct berval *sch ) 560{ 561 int e; 562 563 assert( cred != NULL ); 564 565 if (sch) { 566 sch->bv_val = NULL; 567 sch->bv_len = 0; 568 } 569 570 if ((cred->bv_len == 0) || (cred->bv_val == NULL) || 571 (cred->bv_val[0] != '{')) return LDAP_OTHER; 572 573 for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++); 574 if (cred->bv_val[e]) { 575 int rc; 576 rc = lutil_passwd_scheme( cred->bv_val ); 577 if (rc) { 578 if (sch) { 579 sch->bv_val = cred->bv_val; 580 sch->bv_len = e; 581 } 582 return LDAP_SUCCESS; 583 } 584 } 585 return LDAP_OTHER; 586} 587 588static int 589check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e, char **txt ) 590{ 591 int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS; 592 char *ptr; 593 struct berval sch; 594 595 assert( cred != NULL ); 596 assert( pp != NULL ); 597 assert( txt != NULL ); 598 599 ptr = cred->bv_val; 600 601 *txt = NULL; 602 603 if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) { 604 rc = LDAP_CONSTRAINT_VIOLATION; 605 if ( err ) *err = PP_passwordTooShort; 606 return rc; 607 } 608 609 /* 610 * We need to know if the password is already hashed - if so 611 * what scheme is it. The reason being that the "hash" of 612 * {cleartext} still allows us to check the password. 613 */ 614 rc = password_scheme( cred, &sch ); 615 if (rc == LDAP_SUCCESS) { 616 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}", 617 sch.bv_len ) == 0)) { 618 /* 619 * We can check the cleartext "hash" 620 */ 621 ptr = cred->bv_val + sch.bv_len; 622 } else { 623 /* everything else, we can't check */ 624 if (pp->pwdCheckQuality == 2) { 625 rc = LDAP_CONSTRAINT_VIOLATION; 626 if (err) *err = PP_insufficientPasswordQuality; 627 return rc; 628 } 629 /* 630 * We can't check the syntax of the password, but it's not 631 * mandatory (according to the policy), so we return success. 632 */ 633 634 return LDAP_SUCCESS; 635 } 636 } 637 638 rc = LDAP_SUCCESS; 639 640 if (pp->pwdCheckModule[0]) { 641#ifdef SLAPD_MODULES 642 lt_dlhandle mod; 643 const char *err; 644 645 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) { 646 err = lt_dlerror(); 647 648 Debug(LDAP_DEBUG_ANY, 649 "check_password_quality: lt_dlopen failed: (%s) %s.\n", 650 pp->pwdCheckModule, err, 0 ); 651 ok = LDAP_OTHER; /* internal error */ 652 } else { 653 /* FIXME: the error message ought to be passed thru a 654 * struct berval, with preallocated buffer and size 655 * passed in. Module can still allocate a buffer for 656 * it if the provided one is too small. 657 */ 658 int (*prog)( char *passwd, char **text, Entry *ent ); 659 660 if ((prog = lt_dlsym( mod, "check_password" )) == NULL) { 661 err = lt_dlerror(); 662 663 Debug(LDAP_DEBUG_ANY, 664 "check_password_quality: lt_dlsym failed: (%s) %s.\n", 665 pp->pwdCheckModule, err, 0 ); 666 ok = LDAP_OTHER; 667 } else { 668 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex ); 669 ok = prog( ptr, txt, e ); 670 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex ); 671 if (ok != LDAP_SUCCESS) { 672 Debug(LDAP_DEBUG_ANY, 673 "check_password_quality: module error: (%s) %s.[%d]\n", 674 pp->pwdCheckModule, *txt ? *txt : "", ok ); 675 } 676 } 677 678 lt_dlclose( mod ); 679 } 680#else 681 Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not " 682 "supported. pwdCheckModule ignored.\n", 0, 0, 0); 683#endif /* SLAPD_MODULES */ 684 } 685 686 687 if (ok != LDAP_SUCCESS) { 688 rc = LDAP_CONSTRAINT_VIOLATION; 689 if (err) *err = PP_insufficientPasswordQuality; 690 } 691 692 return rc; 693} 694 695static int 696parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw ) 697{ 698 char *ptr; 699 struct berval nv, npw; 700 ber_len_t i, j; 701 702 assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw ); 703 704 if ( oid ) { 705 *oid = 0; 706 } 707 *oldtime = (time_t)-1; 708 BER_BVZERO( oldpw ); 709 710 ber_dupbv( &nv, bv ); 711 712 /* first get the time field */ 713 for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) 714 ; 715 if ( i == nv.bv_len ) { 716 goto exit_failure; /* couldn't locate the '#' separator */ 717 } 718 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ 719 ptr = nv.bv_val; 720 *oldtime = parse_time( ptr ); 721 if (*oldtime == (time_t)-1) { 722 goto exit_failure; 723 } 724 725 /* get the OID field */ 726 for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) 727 ; 728 if ( i == nv.bv_len ) { 729 goto exit_failure; /* couldn't locate the '#' separator */ 730 } 731 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ 732 if ( oid ) { 733 *oid = ber_strdup( ptr ); 734 } 735 736 /* get the length field */ 737 for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) 738 ; 739 if ( i == nv.bv_len ) { 740 goto exit_failure; /* couldn't locate the '#' separator */ 741 } 742 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ 743 oldpw->bv_len = strtol( ptr, NULL, 10 ); 744 if (errno == ERANGE) { 745 goto exit_failure; 746 } 747 748 /* lastly, get the octets of the string */ 749 for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ ) 750 ; 751 if ( i - j != oldpw->bv_len) { 752 goto exit_failure; /* length is wrong */ 753 } 754 755 npw.bv_val = ptr; 756 npw.bv_len = oldpw->bv_len; 757 ber_dupbv( oldpw, &npw ); 758 ber_memfree( nv.bv_val ); 759 760 return LDAP_SUCCESS; 761 762exit_failure:; 763 if ( oid && *oid ) { 764 ber_memfree(*oid); 765 *oid = NULL; 766 } 767 if ( oldpw->bv_val ) { 768 ber_memfree( oldpw->bv_val); 769 BER_BVZERO( oldpw ); 770 } 771 ber_memfree( nv.bv_val ); 772 773 return LDAP_OTHER; 774} 775 776static void 777add_to_pwd_history( pw_hist **l, time_t t, 778 struct berval *oldpw, struct berval *bv ) 779{ 780 pw_hist *p, *p1, *p2; 781 782 if (!l) return; 783 784 p = ch_malloc( sizeof( pw_hist )); 785 p->pw = *oldpw; 786 ber_dupbv( &p->bv, bv ); 787 p->t = t; 788 p->next = NULL; 789 790 if (*l == NULL) { 791 /* degenerate case */ 792 *l = p; 793 return; 794 } 795 /* 796 * advance p1 and p2 such that p1 is the node before the 797 * new one, and p2 is the node after it 798 */ 799 for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next ); 800 p->next = p2; 801 if (p1 == NULL) { *l = p; return; } 802 p1->next = p; 803} 804 805#ifndef MAX_PWD_HISTORY_SZ 806#define MAX_PWD_HISTORY_SZ 1024 807#endif /* MAX_PWD_HISTORY_SZ */ 808 809static void 810make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa ) 811{ 812 char str[ MAX_PWD_HISTORY_SZ ]; 813 int nlen; 814 815 snprintf( str, MAX_PWD_HISTORY_SZ, 816 "%s#%s#%lu#", timebuf, 817 pa->a_desc->ad_type->sat_syntax->ssyn_oid, 818 (unsigned long) pa->a_nvals[0].bv_len ); 819 str[MAX_PWD_HISTORY_SZ-1] = 0; 820 nlen = strlen(str); 821 822 /* 823 * We have to assume that the string is a string of octets, 824 * not readable characters. In reality, yes, it probably is 825 * a readable (ie, base64) string, but we can't count on that 826 * Hence, while the first 3 fields of the password history 827 * are definitely readable (a timestamp, an OID and an integer 828 * length), the remaining octets of the actual password 829 * are deemed to be binary data. 830 */ 831 AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len ); 832 nlen += pa->a_nvals[0].bv_len; 833 bv->bv_val = ch_malloc( nlen + 1 ); 834 AC_MEMCPY( bv->bv_val, str, nlen ); 835 bv->bv_val[nlen] = '\0'; 836 bv->bv_len = nlen; 837} 838 839static void 840free_pwd_history_list( pw_hist **l ) 841{ 842 pw_hist *p; 843 844 if (!l) return; 845 p = *l; 846 while (p) { 847 pw_hist *pp = p->next; 848 849 free(p->pw.bv_val); 850 free(p->bv.bv_val); 851 free(p); 852 p = pp; 853 } 854 *l = NULL; 855} 856 857typedef struct ppbind { 858 slap_overinst *on; 859 int send_ctrl; 860 int set_restrict; 861 LDAPControl **oldctrls; 862 Modifications *mod; 863 LDAPPasswordPolicyError pErr; 864 PassPolicy pp; 865} ppbind; 866 867static void 868ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls ) 869{ 870 int n; 871 872 assert( rs->sr_ctrls != NULL ); 873 assert( rs->sr_ctrls[0] != NULL ); 874 875 for ( n = 0; rs->sr_ctrls[n]; n++ ) { 876 if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) { 877 op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx ); 878 rs->sr_ctrls[n] = (LDAPControl *)(-1); 879 break; 880 } 881 } 882 883 if ( rs->sr_ctrls[n] == NULL ) { 884 /* missed? */ 885 } 886 887 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx ); 888 889 rs->sr_ctrls = oldctrls; 890} 891 892static int 893ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs ) 894{ 895 ppbind *ppb = op->o_callback->sc_private; 896 if ( ppb->send_ctrl ) { 897 ctrls_cleanup( op, rs, ppb->oldctrls ); 898 } 899 return SLAP_CB_CONTINUE; 900} 901 902static int 903ppolicy_bind_response( Operation *op, SlapReply *rs ) 904{ 905 ppbind *ppb = op->o_callback->sc_private; 906 slap_overinst *on = ppb->on; 907 Modifications *mod = ppb->mod, *m; 908 int pwExpired = 0; 909 int ngut = -1, warn = -1, age, rc; 910 Attribute *a; 911 time_t now, pwtime = (time_t)-1; 912 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 913 struct berval timestamp; 914 BackendInfo *bi = op->o_bd->bd_info; 915 Entry *e; 916 917 /* If we already know it's locked, just get on with it */ 918 if ( ppb->pErr != PP_noError ) { 919 goto locked; 920 } 921 922 op->o_bd->bd_info = (BackendInfo *)on->on_info; 923 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 924 op->o_bd->bd_info = bi; 925 926 if ( rc != LDAP_SUCCESS ) { 927 return SLAP_CB_CONTINUE; 928 } 929 930 now = slap_get_time(); /* stored for later consideration */ 931 timestamp.bv_val = nowstr; 932 timestamp.bv_len = sizeof(nowstr); 933 slap_timestamp( &now, ×tamp ); 934 935 if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) { 936 int i = 0, fc = 0; 937 938 m = ch_calloc( sizeof(Modifications), 1 ); 939 m->sml_op = LDAP_MOD_ADD; 940 m->sml_flags = 0; 941 m->sml_type = ad_pwdFailureTime->ad_cname; 942 m->sml_desc = ad_pwdFailureTime; 943 m->sml_numvals = 1; 944 m->sml_values = ch_calloc( sizeof(struct berval), 2 ); 945 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); 946 947 ber_dupbv( &m->sml_values[0], ×tamp ); 948 ber_dupbv( &m->sml_nvalues[0], ×tamp ); 949 m->sml_next = mod; 950 mod = m; 951 952 /* 953 * Count the pwdFailureTimes - if it's 954 * greater than the policy pwdMaxFailure, 955 * then lock the account. 956 */ 957 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) { 958 for(i=0; a->a_nvals[i].bv_val; i++) { 959 960 /* 961 * If the interval is 0, then failures 962 * stay on the record until explicitly 963 * reset by successful authentication. 964 */ 965 if (ppb->pp.pwdFailureCountInterval == 0) { 966 fc++; 967 } else if (now <= 968 parse_time(a->a_nvals[i].bv_val) + 969 ppb->pp.pwdFailureCountInterval) { 970 971 fc++; 972 } 973 /* 974 * We only count those failures 975 * which are not due to expire. 976 */ 977 } 978 } 979 980 if ((ppb->pp.pwdMaxFailure > 0) && 981 (fc >= ppb->pp.pwdMaxFailure - 1)) { 982 983 /* 984 * We subtract 1 from the failure max 985 * because the new failure entry hasn't 986 * made it to the entry yet. 987 */ 988 m = ch_calloc( sizeof(Modifications), 1 ); 989 m->sml_op = LDAP_MOD_REPLACE; 990 m->sml_flags = 0; 991 m->sml_type = ad_pwdAccountLockedTime->ad_cname; 992 m->sml_desc = ad_pwdAccountLockedTime; 993 m->sml_numvals = 1; 994 m->sml_values = ch_calloc( sizeof(struct berval), 2 ); 995 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); 996 ber_dupbv( &m->sml_values[0], ×tamp ); 997 ber_dupbv( &m->sml_nvalues[0], ×tamp ); 998 m->sml_next = mod; 999 mod = m; 1000 } 1001 } else if ( rs->sr_err == LDAP_SUCCESS ) { 1002 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) 1003 pwtime = parse_time( a->a_nvals[0].bv_val ); 1004 1005 /* delete all pwdFailureTimes */ 1006 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) { 1007 m = ch_calloc( sizeof(Modifications), 1 ); 1008 m->sml_op = LDAP_MOD_DELETE; 1009 m->sml_flags = 0; 1010 m->sml_type = ad_pwdFailureTime->ad_cname; 1011 m->sml_desc = ad_pwdFailureTime; 1012 m->sml_next = mod; 1013 mod = m; 1014 } 1015 1016 /* 1017 * check to see if the password must be changed 1018 */ 1019 if ( ppb->pp.pwdMustChange && 1020 (a = attr_find( e->e_attrs, ad_pwdReset )) && 1021 bvmatch( &a->a_nvals[0], &slap_true_bv ) ) 1022 { 1023 /* 1024 * need to inject client controls here to give 1025 * more information. For the moment, we ensure 1026 * that we are disallowed from doing anything 1027 * other than change password. 1028 */ 1029 if ( ppb->set_restrict ) { 1030 ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn, 1031 &op->o_conn->c_ndn ); 1032 } 1033 1034 ppb->pErr = PP_changeAfterReset; 1035 1036 } else { 1037 /* 1038 * the password does not need to be changed, so 1039 * we now check whether the password has expired. 1040 * 1041 * We can skip this bit if passwords don't age in 1042 * the policy. Also, if there was no pwdChangedTime 1043 * attribute in the entry, the password never expires. 1044 */ 1045 if (ppb->pp.pwdMaxAge == 0) goto grace; 1046 1047 if (pwtime != (time_t)-1) { 1048 /* 1049 * Check: was the last change time of 1050 * the password older than the maximum age 1051 * allowed. (Ignore case 2 from I-D, it's just silly.) 1052 */ 1053 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1; 1054 } 1055 } 1056 1057grace: 1058 if (!pwExpired) goto check_expiring_password; 1059 1060 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL) 1061 ngut = ppb->pp.pwdGraceAuthNLimit; 1062 else { 1063 for(ngut=0; a->a_nvals[ngut].bv_val; ngut++); 1064 ngut = ppb->pp.pwdGraceAuthNLimit - ngut; 1065 } 1066 1067 /* 1068 * ngut is the number of remaining grace logins 1069 */ 1070 Debug( LDAP_DEBUG_ANY, 1071 "ppolicy_bind: Entry %s has an expired password: %d grace logins\n", 1072 e->e_name.bv_val, ngut, 0); 1073 1074 if (ngut < 1) { 1075 ppb->pErr = PP_passwordExpired; 1076 rs->sr_err = LDAP_INVALID_CREDENTIALS; 1077 goto done; 1078 } 1079 1080 /* 1081 * Add a grace user time to the entry 1082 */ 1083 m = ch_calloc( sizeof(Modifications), 1 ); 1084 m->sml_op = LDAP_MOD_ADD; 1085 m->sml_flags = 0; 1086 m->sml_type = ad_pwdGraceUseTime->ad_cname; 1087 m->sml_desc = ad_pwdGraceUseTime; 1088 m->sml_numvals = 1; 1089 m->sml_values = ch_calloc( sizeof(struct berval), 2 ); 1090 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); 1091 ber_dupbv( &m->sml_values[0], ×tamp ); 1092 ber_dupbv( &m->sml_nvalues[0], ×tamp ); 1093 m->sml_next = mod; 1094 mod = m; 1095 1096check_expiring_password: 1097 /* 1098 * Now we need to check to see 1099 * if it is about to expire, and if so, should the user 1100 * be warned about it in the password policy control. 1101 * 1102 * If the password has expired, and we're in the grace period, then 1103 * we don't need to do this bit. Similarly, if we don't have password 1104 * aging, then there's no need to do this bit either. 1105 */ 1106 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1)) 1107 goto done; 1108 1109 age = (int)(now - pwtime); 1110 1111 /* 1112 * We know that there is a password Change Time attribute - if 1113 * there wasn't, then the pwdExpired value would be true, unless 1114 * there is no password aging - and if there is no password aging, 1115 * then this section isn't called anyway - you can't have an 1116 * expiring password if there's no limit to expire. 1117 */ 1118 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) { 1119 /* 1120 * Set the warning value. 1121 */ 1122 warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */ 1123 if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */ 1124 1125 Debug( LDAP_DEBUG_ANY, 1126 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n", 1127 op->o_req_dn.bv_val, warn, 0 ); 1128 } 1129 } 1130 1131done: 1132 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1133 be_entry_release_r( op, e ); 1134 1135locked: 1136 if ( mod ) { 1137 Operation op2 = *op; 1138 SlapReply r2 = { REP_RESULT }; 1139 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 1140 pp_info *pi = on->on_bi.bi_private; 1141 LDAPControl c, *ca[2]; 1142 1143 op2.o_tag = LDAP_REQ_MODIFY; 1144 op2.o_callback = &cb; 1145 op2.orm_modlist = mod; 1146 op2.orm_no_opattrs = 0; 1147 op2.o_dn = op->o_bd->be_rootdn; 1148 op2.o_ndn = op->o_bd->be_rootndn; 1149 1150 /* If this server is a shadow and forward_updates is true, 1151 * use the frontend to perform this modify. That will trigger 1152 * the update referral, which can then be forwarded by the 1153 * chain overlay. Obviously the updateref and chain overlay 1154 * must be configured appropriately for this to be useful. 1155 */ 1156 if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) { 1157 op2.o_bd = frontendDB; 1158 1159 /* Must use Relax control since these are no-user-mod */ 1160 op2.o_relax = SLAP_CONTROL_CRITICAL; 1161 op2.o_ctrls = ca; 1162 ca[0] = &c; 1163 ca[1] = NULL; 1164 BER_BVZERO( &c.ldctl_value ); 1165 c.ldctl_iscritical = 1; 1166 c.ldctl_oid = LDAP_CONTROL_RELAX; 1167 } else { 1168 /* If not forwarding, don't update opattrs and don't replicate */ 1169 if ( SLAP_SINGLE_SHADOW( op->o_bd )) { 1170 op2.orm_no_opattrs = 1; 1171 op2.o_dont_replicate = 1; 1172 } 1173 op2.o_bd->bd_info = (BackendInfo *)on->on_info; 1174 } 1175 rc = op2.o_bd->be_modify( &op2, &r2 ); 1176 slap_mods_free( mod, 1 ); 1177 } 1178 1179 if ( ppb->send_ctrl ) { 1180 LDAPControl *ctrl = NULL; 1181 pp_info *pi = on->on_bi.bi_private; 1182 1183 /* Do we really want to tell that the account is locked? */ 1184 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) { 1185 ppb->pErr = PP_noError; 1186 } 1187 ctrl = create_passcontrol( op, warn, ngut, ppb->pErr ); 1188 ppb->oldctrls = add_passcontrol( op, rs, ctrl ); 1189 op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup; 1190 } 1191 op->o_bd->bd_info = bi; 1192 return SLAP_CB_CONTINUE; 1193} 1194 1195static int 1196ppolicy_bind( Operation *op, SlapReply *rs ) 1197{ 1198 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1199 1200 /* Reset lockout status on all Bind requests */ 1201 if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { 1202 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); 1203 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); 1204 } 1205 1206 /* Root bypasses policy */ 1207 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) { 1208 Entry *e; 1209 int rc; 1210 ppbind *ppb; 1211 slap_callback *cb; 1212 1213 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1214 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 1215 1216 if ( rc != LDAP_SUCCESS ) { 1217 return SLAP_CB_CONTINUE; 1218 } 1219 1220 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback), 1221 1, op->o_tmpmemctx ); 1222 ppb = (ppbind *)(cb+1); 1223 ppb->on = on; 1224 ppb->pErr = PP_noError; 1225 ppb->set_restrict = 1; 1226 1227 /* Setup a callback so we can munge the result */ 1228 1229 cb->sc_response = ppolicy_bind_response; 1230 cb->sc_next = op->o_callback->sc_next; 1231 cb->sc_private = ppb; 1232 op->o_callback->sc_next = cb; 1233 1234 /* Did we receive a password policy request control? */ 1235 if ( op->o_ctrlflag[ppolicy_cid] ) { 1236 ppb->send_ctrl = 1; 1237 } 1238 1239 op->o_bd->bd_info = (BackendInfo *)on; 1240 ppolicy_get( op, e, &ppb->pp ); 1241 1242 rc = account_locked( op, e, &ppb->pp, &ppb->mod ); 1243 1244 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1245 be_entry_release_r( op, e ); 1246 1247 if ( rc ) { 1248 ppb->pErr = PP_accountLocked; 1249 send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL ); 1250 return rs->sr_err; 1251 } 1252 1253 } 1254 1255 return SLAP_CB_CONTINUE; 1256} 1257 1258/* Reset the restricted info for the next session on this connection */ 1259static int 1260ppolicy_connection_destroy( BackendDB *bd, Connection *conn ) 1261{ 1262 if ( !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) { 1263 ch_free( pwcons[conn->c_conn_idx].dn.bv_val ); 1264 BER_BVZERO( &pwcons[conn->c_conn_idx].dn ); 1265 } 1266 return SLAP_CB_CONTINUE; 1267} 1268 1269/* Check if this connection is restricted */ 1270static int 1271ppolicy_restrict( 1272 Operation *op, 1273 SlapReply *rs ) 1274{ 1275 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1276 int send_ctrl = 0; 1277 1278 /* Did we receive a password policy request control? */ 1279 if ( op->o_ctrlflag[ppolicy_cid] ) { 1280 send_ctrl = 1; 1281 } 1282 1283 if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { 1284 LDAPControl **oldctrls; 1285 /* if the current authcDN doesn't match the one we recorded, 1286 * then an intervening Bind has succeeded and the restriction 1287 * no longer applies. (ITS#4516) 1288 */ 1289 if ( !dn_match( &op->o_conn->c_ndn, 1290 &pwcons[op->o_conn->c_conn_idx].dn )) { 1291 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); 1292 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); 1293 return SLAP_CB_CONTINUE; 1294 } 1295 1296 Debug( LDAP_DEBUG_TRACE, 1297 "connection restricted to password changing only\n", 0, 0, 0); 1298 if ( send_ctrl ) { 1299 LDAPControl *ctrl = NULL; 1300 ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset ); 1301 oldctrls = add_passcontrol( op, rs, ctrl ); 1302 } 1303 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1304 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 1305 "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" ); 1306 if ( send_ctrl ) { 1307 ctrls_cleanup( op, rs, oldctrls ); 1308 } 1309 return rs->sr_err; 1310 } 1311 1312 return SLAP_CB_CONTINUE; 1313} 1314 1315static int 1316ppolicy_compare_response( 1317 Operation *op, 1318 SlapReply *rs ) 1319{ 1320 /* map compare responses to bind responses */ 1321 if ( rs->sr_err == LDAP_COMPARE_TRUE ) 1322 rs->sr_err = LDAP_SUCCESS; 1323 else if ( rs->sr_err == LDAP_COMPARE_FALSE ) 1324 rs->sr_err = LDAP_INVALID_CREDENTIALS; 1325 1326 ppolicy_bind_response( op, rs ); 1327 1328 /* map back to compare */ 1329 if ( rs->sr_err == LDAP_SUCCESS ) 1330 rs->sr_err = LDAP_COMPARE_TRUE; 1331 else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) 1332 rs->sr_err = LDAP_COMPARE_FALSE; 1333 1334 return SLAP_CB_CONTINUE; 1335} 1336 1337static int 1338ppolicy_compare( 1339 Operation *op, 1340 SlapReply *rs ) 1341{ 1342 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1343 1344 if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE ) 1345 return rs->sr_err; 1346 1347 /* Did we receive a password policy request control? 1348 * Are we testing the userPassword? 1349 */ 1350 if ( op->o_ctrlflag[ppolicy_cid] && 1351 op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) { 1352 Entry *e; 1353 int rc; 1354 ppbind *ppb; 1355 slap_callback *cb; 1356 1357 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1358 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 1359 1360 if ( rc != LDAP_SUCCESS ) { 1361 return SLAP_CB_CONTINUE; 1362 } 1363 1364 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback), 1365 1, op->o_tmpmemctx ); 1366 ppb = (ppbind *)(cb+1); 1367 ppb->on = on; 1368 ppb->pErr = PP_noError; 1369 ppb->send_ctrl = 1; 1370 /* failures here don't lockout the connection */ 1371 ppb->set_restrict = 0; 1372 1373 /* Setup a callback so we can munge the result */ 1374 1375 cb->sc_response = ppolicy_compare_response; 1376 cb->sc_next = op->o_callback->sc_next; 1377 cb->sc_private = ppb; 1378 op->o_callback->sc_next = cb; 1379 1380 op->o_bd->bd_info = (BackendInfo *)on; 1381 ppolicy_get( op, e, &ppb->pp ); 1382 1383 rc = account_locked( op, e, &ppb->pp, &ppb->mod ); 1384 1385 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1386 be_entry_release_r( op, e ); 1387 1388 if ( rc ) { 1389 ppb->pErr = PP_accountLocked; 1390 send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL ); 1391 return rs->sr_err; 1392 } 1393 } 1394 return SLAP_CB_CONTINUE; 1395} 1396 1397static int 1398ppolicy_add( 1399 Operation *op, 1400 SlapReply *rs ) 1401{ 1402 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1403 pp_info *pi = on->on_bi.bi_private; 1404 PassPolicy pp; 1405 Attribute *pa; 1406 const char *txt; 1407 1408 if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE ) 1409 return rs->sr_err; 1410 1411 /* If this is a replica, assume the master checked everything */ 1412 if ( be_shadow_update( op )) 1413 return SLAP_CB_CONTINUE; 1414 1415 /* Check for password in entry */ 1416 if ((pa = attr_find( op->oq_add.rs_e->e_attrs, 1417 slap_schema.si_ad_userPassword ))) 1418 { 1419 assert( pa->a_vals != NULL ); 1420 assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) ); 1421 1422 if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) { 1423 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" ); 1424 return rs->sr_err; 1425 } 1426 1427 /* 1428 * new entry contains a password - if we're not the root user 1429 * then we need to check that the password fits in with the 1430 * security policy for the new entry. 1431 */ 1432 ppolicy_get( op, op->ora_e, &pp ); 1433 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) { 1434 struct berval *bv = &(pa->a_vals[0]); 1435 int rc, send_ctrl = 0; 1436 LDAPPasswordPolicyError pErr = PP_noError; 1437 char *txt; 1438 1439 /* Did we receive a password policy request control? */ 1440 if ( op->o_ctrlflag[ppolicy_cid] ) { 1441 send_ctrl = 1; 1442 } 1443 rc = check_password_quality( bv, &pp, &pErr, op->ora_e, &txt ); 1444 if (rc != LDAP_SUCCESS) { 1445 LDAPControl **oldctrls = NULL; 1446 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1447 if ( send_ctrl ) { 1448 LDAPControl *ctrl = NULL; 1449 ctrl = create_passcontrol( op, -1, -1, pErr ); 1450 oldctrls = add_passcontrol( op, rs, ctrl ); 1451 } 1452 send_ldap_error( op, rs, rc, txt ? txt : "Password fails quality checking policy" ); 1453 if ( txt ) { 1454 free( txt ); 1455 } 1456 if ( send_ctrl ) { 1457 ctrls_cleanup( op, rs, oldctrls ); 1458 } 1459 return rs->sr_err; 1460 } 1461 } 1462 /* 1463 * A controversial bit. We hash cleartext 1464 * passwords provided via add and modify operations 1465 * You're not really supposed to do this, since 1466 * the X.500 model says "store attributes" as they 1467 * get provided. By default, this is what we do 1468 * 1469 * But if the hash_passwords flag is set, we hash 1470 * any cleartext password attribute values via the 1471 * default password hashing scheme. 1472 */ 1473 if ((pi->hash_passwords) && 1474 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) { 1475 struct berval hpw; 1476 1477 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt ); 1478 if (hpw.bv_val == NULL) { 1479 /* 1480 * hashing didn't work. Emit an error. 1481 */ 1482 rs->sr_err = LDAP_OTHER; 1483 rs->sr_text = txt; 1484 send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" ); 1485 return rs->sr_err; 1486 } 1487 1488 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len); 1489 ber_memfree( pa->a_vals[0].bv_val ); 1490 pa->a_vals[0].bv_val = hpw.bv_val; 1491 pa->a_vals[0].bv_len = hpw.bv_len; 1492 } 1493 1494 /* If password aging is in effect, set the pwdChangedTime */ 1495 if ( pp.pwdMaxAge || pp.pwdMinAge ) { 1496 struct berval timestamp; 1497 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 1498 time_t now = slap_get_time(); 1499 1500 timestamp.bv_val = timebuf; 1501 timestamp.bv_len = sizeof(timebuf); 1502 slap_timestamp( &now, ×tamp ); 1503 1504 attr_merge_one( op->ora_e, ad_pwdChangedTime, ×tamp, ×tamp ); 1505 } 1506 } 1507 return SLAP_CB_CONTINUE; 1508} 1509 1510static int 1511ppolicy_mod_cb( Operation *op, SlapReply *rs ) 1512{ 1513 slap_callback *sc = op->o_callback; 1514 op->o_callback = sc->sc_next; 1515 if ( rs->sr_err == LDAP_SUCCESS ) { 1516 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); 1517 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); 1518 } 1519 op->o_tmpfree( sc, op->o_tmpmemctx ); 1520 return SLAP_CB_CONTINUE; 1521} 1522 1523static int 1524ppolicy_modify( Operation *op, SlapReply *rs ) 1525{ 1526 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1527 pp_info *pi = on->on_bi.bi_private; 1528 int i, rc, mod_pw_only, pwmod, pwmop = -1, deladd, 1529 hsize = 0; 1530 PassPolicy pp; 1531 Modifications *mods = NULL, *modtail = NULL, 1532 *ml, *delmod, *addmod; 1533 Attribute *pa, *ha, at; 1534 const char *txt; 1535 pw_hist *tl = NULL, *p; 1536 int zapReset, send_ctrl = 0, free_txt = 0; 1537 Entry *e; 1538 struct berval newpw = BER_BVNULL, oldpw = BER_BVNULL, 1539 *bv, cr[2]; 1540 LDAPPasswordPolicyError pErr = PP_noError; 1541 LDAPControl *ctrl = NULL; 1542 LDAPControl **oldctrls = NULL; 1543 int is_pwdexop = 0; 1544 1545 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1546 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 1547 op->o_bd->bd_info = (BackendInfo *)on; 1548 1549 if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE; 1550 1551 /* If this is a replica, we may need to tweak some of the 1552 * master's modifications. Otherwise, just pass it through. 1553 */ 1554 if ( be_shadow_update( op )) { 1555 Modifications **prev; 1556 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0; 1557 Attribute *a_grace, *a_lock, *a_fail; 1558 1559 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime ); 1560 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime ); 1561 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime ); 1562 1563 for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) { 1564 1565 if ( ml->sml_desc == slap_schema.si_ad_userPassword ) 1566 got_pw = 1; 1567 1568 /* If we're deleting an attr that didn't exist, 1569 * drop this delete op 1570 */ 1571 if ( ml->sml_op == LDAP_MOD_DELETE ) { 1572 int drop = 0; 1573 1574 if ( ml->sml_desc == ad_pwdGraceUseTime ) { 1575 got_del_grace = 1; 1576 if ( !a_grace ) 1577 drop = 1; 1578 } else 1579 if ( ml->sml_desc == ad_pwdAccountLockedTime ) { 1580 got_del_lock = 1; 1581 if ( !a_lock ) 1582 drop = 1; 1583 } else 1584 if ( ml->sml_desc == ad_pwdFailureTime ) { 1585 got_del_fail = 1; 1586 if ( !a_fail ) 1587 drop = 1; 1588 } 1589 if ( drop ) { 1590 *prev = ml->sml_next; 1591 ml->sml_next = NULL; 1592 slap_mods_free( ml, 1 ); 1593 continue; 1594 } 1595 } 1596 prev = &ml->sml_next; 1597 } 1598 1599 /* If we're resetting the password, make sure grace, accountlock, 1600 * and failure also get removed. 1601 */ 1602 if ( got_pw ) { 1603 if ( a_grace && !got_del_grace ) { 1604 ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); 1605 ml->sml_op = LDAP_MOD_DELETE; 1606 ml->sml_flags = SLAP_MOD_INTERNAL; 1607 ml->sml_type.bv_val = NULL; 1608 ml->sml_desc = ad_pwdGraceUseTime; 1609 ml->sml_numvals = 0; 1610 ml->sml_values = NULL; 1611 ml->sml_nvalues = NULL; 1612 ml->sml_next = NULL; 1613 *prev = ml; 1614 prev = &ml->sml_next; 1615 } 1616 if ( a_lock && !got_del_lock ) { 1617 ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); 1618 ml->sml_op = LDAP_MOD_DELETE; 1619 ml->sml_flags = SLAP_MOD_INTERNAL; 1620 ml->sml_type.bv_val = NULL; 1621 ml->sml_desc = ad_pwdAccountLockedTime; 1622 ml->sml_numvals = 0; 1623 ml->sml_values = NULL; 1624 ml->sml_nvalues = NULL; 1625 ml->sml_next = NULL; 1626 *prev = ml; 1627 } 1628 if ( a_fail && !got_del_fail ) { 1629 ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); 1630 ml->sml_op = LDAP_MOD_DELETE; 1631 ml->sml_flags = SLAP_MOD_INTERNAL; 1632 ml->sml_type.bv_val = NULL; 1633 ml->sml_desc = ad_pwdFailureTime; 1634 ml->sml_numvals = 0; 1635 ml->sml_values = NULL; 1636 ml->sml_nvalues = NULL; 1637 ml->sml_next = NULL; 1638 *prev = ml; 1639 } 1640 } 1641 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1642 be_entry_release_r( op, e ); 1643 return SLAP_CB_CONTINUE; 1644 } 1645 1646 /* Did we receive a password policy request control? */ 1647 if ( op->o_ctrlflag[ppolicy_cid] ) { 1648 send_ctrl = 1; 1649 } 1650 1651 /* See if this is a pwdModify exop. If so, we can 1652 * access the plaintext passwords from that request. 1653 */ 1654 { 1655 slap_callback *sc; 1656 1657 for ( sc = op->o_callback; sc; sc=sc->sc_next ) { 1658 if ( sc->sc_response == slap_null_cb && 1659 sc->sc_private ) { 1660 req_pwdexop_s *qpw = sc->sc_private; 1661 newpw = qpw->rs_new; 1662 oldpw = qpw->rs_old; 1663 is_pwdexop = 1; 1664 break; 1665 } 1666 } 1667 } 1668 1669 ppolicy_get( op, e, &pp ); 1670 1671 for ( ml = op->orm_modlist, 1672 pwmod = 0, mod_pw_only = 1, 1673 deladd = 0, delmod = NULL, 1674 addmod = NULL, 1675 zapReset = 1; 1676 ml != NULL; modtail = ml, ml = ml->sml_next ) 1677 { 1678 if ( ml->sml_desc == pp.ad ) { 1679 pwmod = 1; 1680 pwmop = ml->sml_op; 1681 if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) && 1682 (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] )) 1683 { 1684 deladd = 1; 1685 delmod = ml; 1686 } 1687 1688 if ((ml->sml_op == LDAP_MOD_ADD) || 1689 (ml->sml_op == LDAP_MOD_REPLACE)) 1690 { 1691 if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) { 1692 if ( deladd == 1 ) 1693 deladd = 2; 1694 1695 /* FIXME: there's no easy way to ensure 1696 * that add does not cause multiple 1697 * userPassword values; one way (that 1698 * would be consistent with the single 1699 * password constraint) would be to turn 1700 * add into replace); another would be 1701 * to disallow add. 1702 * 1703 * Let's check at least that a single value 1704 * is being added 1705 */ 1706 if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) { 1707 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 1708 rs->sr_text = "Password policy only allows one password value"; 1709 goto return_results; 1710 } 1711 1712 addmod = ml; 1713 } else { 1714 /* replace can have no values, add cannot */ 1715 assert( ml->sml_op == LDAP_MOD_REPLACE ); 1716 } 1717 } 1718 1719 } else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) { 1720 mod_pw_only = 0; 1721 /* modifying something other than password */ 1722 } 1723 1724 /* 1725 * If there is a request to explicitly add a pwdReset 1726 * attribute, then we suppress the normal behaviour on 1727 * password change, which is to remove the pwdReset 1728 * attribute. 1729 * 1730 * This enables an administrator to assign a new password 1731 * and place a "must reset" flag on the entry, which will 1732 * stay until the user explicitly changes his/her password. 1733 */ 1734 if (ml->sml_desc == ad_pwdReset ) { 1735 if ((ml->sml_op == LDAP_MOD_ADD) || 1736 (ml->sml_op == LDAP_MOD_REPLACE)) 1737 zapReset = 0; 1738 } 1739 } 1740 1741 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) { 1742 if ( dn_match( &op->o_conn->c_ndn, 1743 &pwcons[op->o_conn->c_conn_idx].dn )) { 1744 Debug( LDAP_DEBUG_TRACE, 1745 "connection restricted to password changing only\n", 0, 0, 0 ); 1746 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 1747 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password"; 1748 pErr = PP_changeAfterReset; 1749 goto return_results; 1750 } else { 1751 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); 1752 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); 1753 } 1754 } 1755 1756 /* 1757 * if we have a "safe password modify policy", then we need to check if we're doing 1758 * a delete (with the old password), followed by an add (with the new password). 1759 * 1760 * If we got just a delete with nothing else, just let it go. We also skip all the checks if 1761 * the root user is bound. Root can do anything, including avoid the policies. 1762 */ 1763 1764 if (!pwmod) goto do_modify; 1765 1766 /* 1767 * Build the password history list in ascending time order 1768 * We need this, even if the user is root, in order to maintain 1769 * the pwdHistory operational attributes properly. 1770 */ 1771 if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) { 1772 struct berval oldpw; 1773 time_t oldtime; 1774 1775 for(i=0; ha->a_nvals[i].bv_val; i++) { 1776 rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL, 1777 &oldtime, &oldpw ); 1778 1779 if (rc != LDAP_SUCCESS) continue; /* invalid history entry */ 1780 1781 if (oldpw.bv_val) { 1782 add_to_pwd_history( &tl, oldtime, &oldpw, 1783 &(ha->a_nvals[i]) ); 1784 oldpw.bv_val = NULL; 1785 oldpw.bv_len = 0; 1786 } 1787 } 1788 for(p=tl; p; p=p->next, hsize++); /* count history size */ 1789 } 1790 1791 if (be_isroot( op )) goto do_modify; 1792 1793 /* NOTE: according to draft-behera-ldap-password-policy 1794 * pwdAllowUserChange == FALSE must only prevent pwd changes 1795 * by the user the pwd belongs to (ITS#7021) */ 1796 if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) { 1797 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 1798 rs->sr_text = "User alteration of password is not allowed"; 1799 pErr = PP_passwordModNotAllowed; 1800 goto return_results; 1801 } 1802 1803 /* Just deleting? */ 1804 if (!addmod) { 1805 /* skip everything else */ 1806 pwmod = 0; 1807 goto do_modify; 1808 } 1809 1810 /* This is a pwdModify exop that provided the old pw. 1811 * We need to create a Delete mod for this old pw and 1812 * let the matching value get found later 1813 */ 1814 if (pp.pwdSafeModify && oldpw.bv_val ) { 1815 ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 ); 1816 ml->sml_op = LDAP_MOD_DELETE; 1817 ml->sml_flags = SLAP_MOD_INTERNAL; 1818 ml->sml_desc = pp.ad; 1819 ml->sml_type = pp.ad->ad_cname; 1820 ml->sml_numvals = 1; 1821 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 1822 ber_dupbv( &ml->sml_values[0], &oldpw ); 1823 BER_BVZERO( &ml->sml_values[1] ); 1824 ml->sml_next = op->orm_modlist; 1825 op->orm_modlist = ml; 1826 delmod = ml; 1827 deladd = 2; 1828 } 1829 1830 if (pp.pwdSafeModify && deladd != 2) { 1831 Debug( LDAP_DEBUG_TRACE, 1832 "change password must use DELETE followed by ADD/REPLACE\n", 1833 0, 0, 0 ); 1834 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 1835 rs->sr_text = "Must supply old password to be changed as well as new one"; 1836 pErr = PP_mustSupplyOldPassword; 1837 goto return_results; 1838 } 1839 1840 /* Check age, but only if pwdReset is not TRUE */ 1841 pa = attr_find( e->e_attrs, ad_pwdReset ); 1842 if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) && 1843 pp.pwdMinAge > 0) { 1844 time_t pwtime = (time_t)-1, now; 1845 int age; 1846 1847 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) 1848 pwtime = parse_time( pa->a_nvals[0].bv_val ); 1849 now = slap_get_time(); 1850 age = (int)(now - pwtime); 1851 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) { 1852 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 1853 rs->sr_text = "Password is too young to change"; 1854 pErr = PP_passwordTooYoung; 1855 goto return_results; 1856 } 1857 } 1858 1859 /* pa is used in password history check below, be sure it's set */ 1860 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) { 1861 /* 1862 * we have a password to check 1863 */ 1864 bv = oldpw.bv_val ? &oldpw : delmod->sml_values; 1865 /* FIXME: no access checking? */ 1866 rc = slap_passwd_check( op, NULL, pa, bv, &txt ); 1867 if (rc != LDAP_SUCCESS) { 1868 Debug( LDAP_DEBUG_TRACE, 1869 "old password check failed: %s\n", txt, 0, 0 ); 1870 1871 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 1872 rs->sr_text = "Must supply correct old password to change to new one"; 1873 pErr = PP_mustSupplyOldPassword; 1874 goto return_results; 1875 1876 } else { 1877 int i; 1878 1879 /* 1880 * replace the delete value with the (possibly hashed) 1881 * value which is currently in the password. 1882 */ 1883 for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) { 1884 free( delmod->sml_values[i].bv_val ); 1885 BER_BVZERO( &delmod->sml_values[i] ); 1886 } 1887 free( delmod->sml_values ); 1888 delmod->sml_values = ch_calloc( sizeof(struct berval), 2 ); 1889 BER_BVZERO( &delmod->sml_values[1] ); 1890 ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) ); 1891 } 1892 } 1893 1894 bv = newpw.bv_val ? &newpw : &addmod->sml_values[0]; 1895 if (pp.pwdCheckQuality > 0) { 1896 1897 rc = check_password_quality( bv, &pp, &pErr, e, (char **)&txt ); 1898 if (rc != LDAP_SUCCESS) { 1899 rs->sr_err = rc; 1900 if ( txt ) { 1901 rs->sr_text = txt; 1902 free_txt = 1; 1903 } else { 1904 rs->sr_text = "Password fails quality checking policy"; 1905 } 1906 goto return_results; 1907 } 1908 } 1909 1910 /* If pwdInHistory is zero, passwords may be reused */ 1911 if (pa && pp.pwdInHistory > 0) { 1912 /* 1913 * Last check - the password history. 1914 */ 1915 /* FIXME: no access checking? */ 1916 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) { 1917 /* 1918 * This is bad - it means that the user is attempting 1919 * to set the password to the same as the old one. 1920 */ 1921 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 1922 rs->sr_text = "Password is not being changed from existing value"; 1923 pErr = PP_passwordInHistory; 1924 goto return_results; 1925 } 1926 1927 /* 1928 * Iterate through the password history, and fail on any 1929 * password matches. 1930 */ 1931 at = *pa; 1932 at.a_vals = cr; 1933 cr[1].bv_val = NULL; 1934 for(p=tl; p; p=p->next) { 1935 cr[0] = p->pw; 1936 /* FIXME: no access checking? */ 1937 rc = slap_passwd_check( op, NULL, &at, bv, &txt ); 1938 1939 if (rc != LDAP_SUCCESS) continue; 1940 1941 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 1942 rs->sr_text = "Password is in history of old passwords"; 1943 pErr = PP_passwordInHistory; 1944 goto return_results; 1945 } 1946 } 1947 1948do_modify: 1949 if (pwmod) { 1950 struct berval timestamp; 1951 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 1952 time_t now = slap_get_time(); 1953 1954 /* If the conn is restricted, set a callback to clear it 1955 * if the pwmod succeeds 1956 */ 1957 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { 1958 slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ), 1959 op->o_tmpmemctx ); 1960 sc->sc_next = op->o_callback; 1961 /* Must use sc_response to insure we reset on success, before 1962 * the client sees the response. Must use sc_cleanup to insure 1963 * that it gets cleaned up if sc_response is not called. 1964 */ 1965 sc->sc_response = ppolicy_mod_cb; 1966 sc->sc_cleanup = ppolicy_mod_cb; 1967 op->o_callback = sc; 1968 } 1969 1970 /* 1971 * keep the necessary pwd.. operational attributes 1972 * up to date. 1973 */ 1974 1975 timestamp.bv_val = timebuf; 1976 timestamp.bv_len = sizeof(timebuf); 1977 slap_timestamp( &now, ×tamp ); 1978 1979 mods = NULL; 1980 if (pwmop != LDAP_MOD_DELETE) { 1981 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 1982 mods->sml_op = LDAP_MOD_REPLACE; 1983 mods->sml_numvals = 1; 1984 mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 1985 ber_dupbv( &mods->sml_values[0], ×tamp ); 1986 BER_BVZERO( &mods->sml_values[1] ); 1987 assert( !BER_BVISNULL( &mods->sml_values[0] ) ); 1988 } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) { 1989 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 1990 mods->sml_op = LDAP_MOD_DELETE; 1991 } 1992 if (mods) { 1993 mods->sml_desc = ad_pwdChangedTime; 1994 mods->sml_flags = SLAP_MOD_INTERNAL; 1995 mods->sml_next = NULL; 1996 modtail->sml_next = mods; 1997 modtail = mods; 1998 } 1999 2000 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) { 2001 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 2002 mods->sml_op = LDAP_MOD_DELETE; 2003 mods->sml_desc = ad_pwdGraceUseTime; 2004 mods->sml_flags = SLAP_MOD_INTERNAL; 2005 mods->sml_next = NULL; 2006 modtail->sml_next = mods; 2007 modtail = mods; 2008 } 2009 2010 if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) { 2011 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 2012 mods->sml_op = LDAP_MOD_DELETE; 2013 mods->sml_desc = ad_pwdAccountLockedTime; 2014 mods->sml_flags = SLAP_MOD_INTERNAL; 2015 mods->sml_next = NULL; 2016 modtail->sml_next = mods; 2017 modtail = mods; 2018 } 2019 2020 if (attr_find(e->e_attrs, ad_pwdFailureTime )) { 2021 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 2022 mods->sml_op = LDAP_MOD_DELETE; 2023 mods->sml_desc = ad_pwdFailureTime; 2024 mods->sml_flags = SLAP_MOD_INTERNAL; 2025 mods->sml_next = NULL; 2026 modtail->sml_next = mods; 2027 modtail = mods; 2028 } 2029 2030 /* Delete the pwdReset attribute, since it's being reset */ 2031 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) { 2032 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 2033 mods->sml_op = LDAP_MOD_DELETE; 2034 mods->sml_desc = ad_pwdReset; 2035 mods->sml_flags = SLAP_MOD_INTERNAL; 2036 mods->sml_next = NULL; 2037 modtail->sml_next = mods; 2038 modtail = mods; 2039 } 2040 2041 if (pp.pwdInHistory > 0) { 2042 if (hsize >= pp.pwdInHistory) { 2043 /* 2044 * We use the >= operator, since we are going to add 2045 * the existing password attribute value into the 2046 * history - thus the cardinality of history values is 2047 * about to rise by one. 2048 * 2049 * If this would push it over the limit of history 2050 * values (remembering - the password policy could have 2051 * changed since the password was last altered), we must 2052 * delete at least 1 value from the pwdHistory list. 2053 * 2054 * In fact, we delete '(#pwdHistory attrs - max pwd 2055 * history length) + 1' values, starting with the oldest. 2056 * This is easily evaluated, since the linked list is 2057 * created in ascending time order. 2058 */ 2059 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); 2060 mods->sml_op = LDAP_MOD_DELETE; 2061 mods->sml_flags = SLAP_MOD_INTERNAL; 2062 mods->sml_desc = ad_pwdHistory; 2063 mods->sml_numvals = hsize - pp.pwdInHistory + 1; 2064 mods->sml_values = ch_calloc( sizeof( struct berval ), 2065 hsize - pp.pwdInHistory + 2 ); 2066 BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] ); 2067 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) { 2068 BER_BVZERO( &mods->sml_values[i] ); 2069 ber_dupbv( &(mods->sml_values[i]), &p->bv ); 2070 } 2071 mods->sml_next = NULL; 2072 modtail->sml_next = mods; 2073 modtail = mods; 2074 } 2075 free_pwd_history_list( &tl ); 2076 2077 /* 2078 * Now add the existing password into the history list. 2079 * This will be executed even if the operation is to delete 2080 * the password entirely. 2081 * 2082 * This isn't in the spec explicitly, but it seems to make 2083 * sense that the password history list is the list of all 2084 * previous passwords - even if they were deleted. Thus, if 2085 * someone tries to add a historical password at some future 2086 * point, it will fail. 2087 */ 2088 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) { 2089 mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); 2090 mods->sml_op = LDAP_MOD_ADD; 2091 mods->sml_flags = SLAP_MOD_INTERNAL; 2092 mods->sml_type.bv_val = NULL; 2093 mods->sml_desc = ad_pwdHistory; 2094 mods->sml_nvalues = NULL; 2095 mods->sml_numvals = 1; 2096 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 ); 2097 mods->sml_values[ 1 ].bv_val = NULL; 2098 mods->sml_values[ 1 ].bv_len = 0; 2099 make_pwd_history_value( timebuf, &mods->sml_values[0], pa ); 2100 mods->sml_next = NULL; 2101 modtail->sml_next = mods; 2102 modtail = mods; 2103 2104 } else { 2105 Debug( LDAP_DEBUG_TRACE, 2106 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 ); 2107 } 2108 } 2109 2110 /* 2111 * Controversial bit here. If the new password isn't hashed 2112 * (ie, is cleartext), we probably should hash it according 2113 * to the default hash. The reason for this is that we want 2114 * to use the policy if possible, but if we hash the password 2115 * before, then we're going to run into trouble when it 2116 * comes time to check the password. 2117 * 2118 * Now, the right thing to do is to use the extended password 2119 * modify operation, but not all software can do this, 2120 * therefore it makes sense to hash the new password, now 2121 * we know it passes the policy requirements. 2122 * 2123 * Of course, if the password is already hashed, then we 2124 * leave it alone. 2125 */ 2126 2127 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 2128 (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) 2129 { 2130 struct berval hpw, bv; 2131 2132 slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt ); 2133 if (hpw.bv_val == NULL) { 2134 /* 2135 * hashing didn't work. Emit an error. 2136 */ 2137 rs->sr_err = LDAP_OTHER; 2138 rs->sr_text = txt; 2139 goto return_results; 2140 } 2141 bv = addmod->sml_values[0]; 2142 /* clear and discard the clear password */ 2143 memset(bv.bv_val, 0, bv.bv_len); 2144 ber_memfree(bv.bv_val); 2145 addmod->sml_values[0] = hpw; 2146 } 2147 } 2148 op->o_bd->bd_info = (BackendInfo *)on->on_info; 2149 be_entry_release_r( op, e ); 2150 return SLAP_CB_CONTINUE; 2151 2152return_results: 2153 free_pwd_history_list( &tl ); 2154 op->o_bd->bd_info = (BackendInfo *)on->on_info; 2155 be_entry_release_r( op, e ); 2156 if ( send_ctrl ) { 2157 ctrl = create_passcontrol( op, -1, -1, pErr ); 2158 oldctrls = add_passcontrol( op, rs, ctrl ); 2159 } 2160 send_ldap_result( op, rs ); 2161 if ( free_txt ) { 2162 free( (char *)txt ); 2163 rs->sr_text = NULL; 2164 } 2165 if ( send_ctrl ) { 2166 if ( is_pwdexop ) { 2167 if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) { 2168 op->o_tmpfree( oldctrls, op->o_tmpmemctx ); 2169 } 2170 oldctrls = NULL; 2171 rs->sr_flags |= REP_CTRLS_MUSTBEFREED; 2172 2173 } else { 2174 ctrls_cleanup( op, rs, oldctrls ); 2175 } 2176 } 2177 return rs->sr_err; 2178} 2179 2180static int 2181ppolicy_parseCtrl( 2182 Operation *op, 2183 SlapReply *rs, 2184 LDAPControl *ctrl ) 2185{ 2186 if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) { 2187 rs->sr_text = "passwordPolicyRequest control value not absent"; 2188 return LDAP_PROTOCOL_ERROR; 2189 } 2190 op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical 2191 ? SLAP_CONTROL_CRITICAL 2192 : SLAP_CONTROL_NONCRITICAL; 2193 2194 return LDAP_SUCCESS; 2195} 2196 2197static int 2198attrPretty( 2199 Syntax *syntax, 2200 struct berval *val, 2201 struct berval *out, 2202 void *ctx ) 2203{ 2204 AttributeDescription *ad = NULL; 2205 const char *err; 2206 int code; 2207 2208 code = slap_bv2ad( val, &ad, &err ); 2209 if ( !code ) { 2210 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx ); 2211 } 2212 return code; 2213} 2214 2215static int 2216attrNormalize( 2217 slap_mask_t use, 2218 Syntax *syntax, 2219 MatchingRule *mr, 2220 struct berval *val, 2221 struct berval *out, 2222 void *ctx ) 2223{ 2224 AttributeDescription *ad = NULL; 2225 const char *err; 2226 int code; 2227 2228 code = slap_bv2ad( val, &ad, &err ); 2229 if ( !code ) { 2230 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx ); 2231 } 2232 return code; 2233} 2234 2235static int 2236ppolicy_db_init( 2237 BackendDB *be, 2238 ConfigReply *cr 2239) 2240{ 2241 slap_overinst *on = (slap_overinst *) be->bd_info; 2242 2243 if ( SLAP_ISGLOBALOVERLAY( be ) ) { 2244 /* do not allow slapo-ppolicy to be global by now (ITS#5858) */ 2245 if ( cr ){ 2246 snprintf( cr->msg, sizeof(cr->msg), 2247 "slapo-ppolicy cannot be global" ); 2248 Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 ); 2249 } 2250 return 1; 2251 } 2252 2253 /* Has User Schema been initialized yet? */ 2254 if ( !pwd_UsSchema[0].ad[0] ) { 2255 const char *err; 2256 int i, code; 2257 2258 for (i=0; pwd_UsSchema[i].def; i++) { 2259 code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err ); 2260 if ( code ) { 2261 if ( cr ){ 2262 snprintf( cr->msg, sizeof(cr->msg), 2263 "User Schema load failed for attribute \"%s\". Error code %d: %s", 2264 pwd_UsSchema[i].def, code, err ); 2265 Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 ); 2266 } 2267 return code; 2268 } 2269 } 2270 { 2271 Syntax *syn; 2272 MatchingRule *mr; 2273 2274 syn = ch_malloc( sizeof( Syntax )); 2275 *syn = *ad_pwdAttribute->ad_type->sat_syntax; 2276 syn->ssyn_pretty = attrPretty; 2277 ad_pwdAttribute->ad_type->sat_syntax = syn; 2278 2279 mr = ch_malloc( sizeof( MatchingRule )); 2280 *mr = *ad_pwdAttribute->ad_type->sat_equality; 2281 mr->smr_normalize = attrNormalize; 2282 ad_pwdAttribute->ad_type->sat_equality = mr; 2283 } 2284 } 2285 2286 on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 ); 2287 2288 if ( dtblsize && !pwcons ) { 2289 /* accommodate for c_conn_idx == -1 */ 2290 pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 ); 2291 pwcons++; 2292 } 2293 2294 return 0; 2295} 2296 2297static int 2298ppolicy_db_open( 2299 BackendDB *be, 2300 ConfigReply *cr 2301) 2302{ 2303 ov_count++; 2304 return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST ); 2305} 2306 2307static int 2308ppolicy_close( 2309 BackendDB *be, 2310 ConfigReply *cr 2311) 2312{ 2313 slap_overinst *on = (slap_overinst *) be->bd_info; 2314 pp_info *pi = on->on_bi.bi_private; 2315 2316#ifdef SLAP_CONFIG_DELETE 2317 overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST ); 2318#endif /* SLAP_CONFIG_DELETE */ 2319 2320 /* Perhaps backover should provide bi_destroy hooks... */ 2321 ov_count--; 2322 if ( ov_count <=0 && pwcons ) { 2323 pwcons--; 2324 free( pwcons ); 2325 pwcons = NULL; 2326 } 2327 free( pi->def_policy.bv_val ); 2328 free( pi ); 2329 2330 return 0; 2331} 2332 2333static char *extops[] = { 2334 LDAP_EXOP_MODIFY_PASSWD, 2335 NULL 2336}; 2337 2338static slap_overinst ppolicy; 2339 2340int ppolicy_initialize() 2341{ 2342 int i, code; 2343 2344 for (i=0; pwd_OpSchema[i].def; i++) { 2345 code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 ); 2346 if ( code ) { 2347 Debug( LDAP_DEBUG_ANY, 2348 "ppolicy_initialize: register_at failed\n", 0, 0, 0 ); 2349 return code; 2350 } 2351 /* Allow Manager to set these as needed */ 2352 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) { 2353 (*pwd_OpSchema[i].ad)->ad_type->sat_flags |= 2354 SLAP_AT_MANAGEABLE; 2355 } 2356 } 2357 2358 code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST, 2359 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops, 2360 ppolicy_parseCtrl, &ppolicy_cid ); 2361 if ( code != LDAP_SUCCESS ) { 2362 Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code, 0, 0 ); 2363 return code; 2364 } 2365 2366 ldap_pvt_thread_mutex_init( &chk_syntax_mutex ); 2367 2368 ppolicy.on_bi.bi_type = "ppolicy"; 2369 ppolicy.on_bi.bi_db_init = ppolicy_db_init; 2370 ppolicy.on_bi.bi_db_open = ppolicy_db_open; 2371 ppolicy.on_bi.bi_db_close = ppolicy_close; 2372 2373 ppolicy.on_bi.bi_op_add = ppolicy_add; 2374 ppolicy.on_bi.bi_op_bind = ppolicy_bind; 2375 ppolicy.on_bi.bi_op_compare = ppolicy_compare; 2376 ppolicy.on_bi.bi_op_delete = ppolicy_restrict; 2377 ppolicy.on_bi.bi_op_modify = ppolicy_modify; 2378 ppolicy.on_bi.bi_op_search = ppolicy_restrict; 2379 ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy; 2380 2381 ppolicy.on_bi.bi_cf_ocs = ppolicyocs; 2382 code = config_register_schema( ppolicycfg, ppolicyocs ); 2383 if ( code ) return code; 2384 2385 return overlay_register( &ppolicy ); 2386} 2387 2388#if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC 2389int init_module(int argc, char *argv[]) { 2390 return ppolicy_initialize(); 2391} 2392#endif 2393 2394#endif /* defined(SLAPD_OVER_PPOLICY) */ 2395