2.4.16 password policy enhancements 2.4.16
authoraravind@localhost
Sat, 30 May 2009 07:50:59 -0700
changeset 2 b27b1a60ca5e
parent 1 656a462522b1
child 3 b45815c09a33
push id1
push useraravind@mozilla.com
push date2009-05-30 14:51 +0000
bugs2
2.4.16 password policy enhancements
README
ppolicy.c
slapo-ppolicy.5
--- a/README
+++ b/README
@@ -1,12 +1,12 @@
 ppolicy enhancement
 ===================
 
-OpenLDAP Verion 2.4.11
+OpenLDAP Verion 2.4.16
 
 Description:
 ============
 
 The OpenLDAP ppolicy overlay provides many powerful features to LDAP 
 administrators that significantly enhance security including password aging 
 which forces users to regularly change passwords. 
 
@@ -38,58 +38,59 @@ Contents:
 
 This enhancement consists of the following items:
 
 1. replacement ppolicy.c
 2. replacement ppolicy.schema
 3. replacement man page slapo-ppolicy.5
 4. draft-mozilla-ldap-password-policy-05.doc
 5. draft-mozilla-ldap-password-policy-05.txt
+6. README
 
 Installation:
 =============
 
 The enhancement may be installed to either a source distribution (tarball) or 
 BSD port as follows:
 
 If using a tarball:
 -------------------
 1. Unpack the source to a suitable directly 
 2. Replace the following:
-  a. openldap-2.4.11/servers/slapd/overlays/ppolicy.c
-  b. openldap-2.4.11/servers/slapd/schema/ppolicy.schema
-  c. openldap-2.4.11/doc/man/man5/slapo-ppolicy.5
+  a. openldap-2.4.16/servers/slapd/overlays/ppolicy.c
+  b. openldap-2.4.16/servers/slapd/schema/ppolicy.schema
+  c. openldap-2.4.16/doc/man/man5/slapo-ppolicy.5
 3. Configure, build and install OpenLDAP in the normal manner
 
 If using a BSD Port:
 --------------------
 
 1. cd to the port directory (normally /usr/ports/net/openldap24-server)
-2. make configure
+2. make patch
 3. when the configure has stopped replace the following modules with those 
    supplied with this enhancement
-  a. work/openldap-2.4.11/servers/slapd/overlays/ppolicy.c
-  b. work/openldap-2.4.11/servers/slapd/schema/ppolicy.schema
-  c. work/openldap-2.4.11/doc/mam/man5/slapo-ppolicy.5
-4. make install clean 
+  a. work/openldap-2.4.16/servers/slapd/overlays/ppolicy.c
+  b. work/openldap-2.4.16/servers/slapd/schema/ppolicy.schema
+  c. work/openldap-2.4.16/doc/mam/man5/slapo-ppolicy.5
+4. make install [clean] 
 
 If enhancing an existing installation you can either:
 
  a. repeat the full install process as above using any special flags necessary 
     to override any package management utilities.
  b. Complete the build but do not intstall the full OpenLDAP system:
     i. if using dynamic overlays copy ppolicy.la (and/or ppolicy.so) from 
-        openldap-2.4.11/servers/slapd/.libs/ppolicy.la to the appropriate 
+        openldap-2.4.16/servers/slapd/.libs/ppolicy.la to the appropriate 
        location
     ii. if using static (compiled in) overlays copy the openldap application 
-		    from openldap-2.4.11/servers/slapd/.libs/slapd to the executable 
+		    from openldap-2.4.16/servers/slapd/.libs/slapd to the executable 
         location (use locate slapd to find)
     iii. copy ppolicy.schema to your normal schema location
     iv.  tar the updated man file from 
-         openldap-2.4.111/doc/man/man5/slapo-ppolicy.5.tmp to your normal man5 
+         openldap-2.4.16/doc/man/man5/slapo-ppolicy.5.tmp to your normal man5 
          location (typical Linux = /usr/man/man5 BSD = /usr/local/man/man5) 
          using a command like:
          tar -czvf man/man5/slapo-ppolicy.5.gz doc/man/man5/slapo-ppolicy.5.tmp
          
 To invoke repeat password checking:
 ====================================
 
 The user attribute pwdMaxTotalAttempts (in objectclass pwdPolicy) defines what, 
@@ -100,12 +101,12 @@ pwdMaxTotalAttempts is -1 repeat passwor
 unlimited number of attempts with any number (up to the limit defined by 
 pwdMaxFailure) of repeat passwords are allowed. It shuld be noted that allowing
 an unlimited number of repeat password attempts may increase the effectivness 
 of a DoS attack using large numbers of unsuccessful bind attempts. If 
 pwdMaxTotalAttempts is set to any positive number then this number defines the 
 maximum number of unique plus repeat password attempts allowed before the 
 account is locked.
 
-The operationalal attribute pwdUniqueAttempts is used to store all unique 
+The operational attribute pwdUniqueAttempts is used to store all unique 
 failed password attempts and will appear only if pwdMaxTotalAttempts is either 
 -1 or a positive number. The failed password is maintained in hashed format in 
 this attribute.
--- a/ppolicy.c
+++ b/ppolicy.c
@@ -1,29 +1,30 @@
-/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/ppolicy.c,v 1.75.2.14 2008/07/10 00:55:07 quanah Exp $ */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/ppolicy.c,v 1.75.2.20 2009/01/22 00:01:12 kurt Exp $ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2004-2008 The OpenLDAP Foundation.
+ * Copyright 2004-2009 The OpenLDAP Foundation.
  * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
  * Portions Copyright 2004 Hewlett-Packard Company.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
  * <http://www.OpenLDAP.org/license.html>.
  */
 /* ACKNOWLEDGEMENTS:
  * This work was developed by Howard Chu for inclusion in
  * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
  * This work was sponsored by the Hewlett-Packard Company.
- * April 2009 Enhanced to allow for multiple attempts with repeated passwords 
+ * PPOLICY ENHANCEMENT
+ * April 2009 Enhanced to allow for multiple bind attempts with repeated passwords 
  * a. code written by Ron Aitchison
  * b. work sponsored by Mozilla Corporation
  * v.0.2 - 2.4.11
 */
 
 #include "portable.h"
 
 /* This file implements "Password Policy for LDAP Directories",
@@ -381,30 +382,24 @@ account_locked( Operation *op, Entry *e,
 #define PPOLICY_ERROR 0x81L		/* primitive + 1 */
  
 #define PPOLICY_EXPIRE 0x80L	/* primitive + 0 */
 #define PPOLICY_GRACE  0x81L	/* primitive + 1 */
 
 static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
 
 static LDAPControl *
-create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err )
+create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
 {
-	char berbuf[LBER_ELEMENT_SIZEOF], bb2[LBER_ELEMENT_SIZEOF];
-	BerElement *ber = (BerElement *)berbuf, *b2 = (BerElement *)bb2;
-	LDAPControl *c;
+	BerElementBuffer berbuf, bb2;
+	BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
+	LDAPControl c = { 0 }, *cp;
 	struct berval bv;
 
-	c = ch_calloc( sizeof( LDAPControl ), 1 );
-	if ( c == NULL ) {
-		return NULL;
-	}
-	c->ldctl_oid = (char *)ppolicy_ctrl_oid;
-	c->ldctl_iscritical = 0;
-	BER_BVZERO( &c->ldctl_value );
+	BER_BVZERO( &c.ldctl_value );
 
 	ber_init2( ber, NULL, LBER_USE_DER );
 	ber_printf( ber, "{" /*}*/ );
 
 	if ( exptime >= 0 ) {
 		ber_init2( b2, NULL, LBER_USE_DER );
 		ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
 		ber_flatten2( b2, &bv, 1 );
@@ -420,22 +415,28 @@ create_passcontrol( int exptime, int gra
 		ch_free( bv.bv_val );
 	}
 
 	if (err != PP_noError ) {
 		ber_printf( ber, "te", PPOLICY_ERROR, err );
 	}
 	ber_printf( ber, /*{*/ "N}" );
 
-	if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) {
-		ch_free(c);
-		c = NULL;
+	if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
+		return NULL;
 	}
+	cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
+	cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
+	cp->ldctl_iscritical = 0;
+	cp->ldctl_value.bv_val = (char *)&cp[1];
+	cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
+	AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
 	(void)ber_free_buf(ber);
-	return c;
+	
+	return cp;
 }
 
 static LDAPControl **
 add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
 {
 	LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
 	int n;
 
@@ -504,18 +505,16 @@ ppolicy_get( Operation *op, Entry *e, Pa
 
 	if ( rc ) goto defaultpol;
 
 #if 0	/* Only worry about userPassword for now */
 	if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
 		slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
 #endif
 
-	Debug( LDAP_DEBUG_TRACE,
-					"ppolicy_get: begin\n", 0, 0, 0 ); /* TESTTTEST */
 	pp->pwdMaxTotalAttempts = 0; /* RGFA just default safely to zero - no repeat checking 
 	                                override if we have an explicit value 
 	                              We do this here because we can drop out with any failure */
 	if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) )
 			&& lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 )
 		goto defaultpol;
 	if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) )
 			&& lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 )
@@ -714,17 +713,17 @@ typedef struct ppbind {
 	PassPolicy pp;
 } ppbind;
 
 static int
 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
 {
 	char *ptr;
 	struct berval nv, npw;
-	int i, j;
+	ber_len_t i, j;
 	
 	assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
 
 	if ( oid ) {
 		*oid = 0;
 	}
 	*oldtime = (time_t)-1;
 	BER_BVZERO( oldpw );
@@ -1104,29 +1103,27 @@ free_pwd_history_list( pw_hist **l )
 		free(p->bv.bv_val);
 		free(p);
 		p = pp;
 	}
 	*l = NULL;
 }
 
 
-
 static void
 ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
 {
 	int n;
 
 	assert( rs->sr_ctrls != NULL );
 	assert( rs->sr_ctrls[0] != NULL );
 
 	for ( n = 0; rs->sr_ctrls[n]; n++ ) {
 		if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) {
-			ch_free( rs->sr_ctrls[n]->ldctl_value.bv_val );
-			ch_free( rs->sr_ctrls[n] );
+			op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
 			rs->sr_ctrls[n] = (LDAPControl *)(-1);
 			break;
 		}
 	}
 
 	if ( rs->sr_ctrls[n] == NULL ) {
 		/* missed? */
 	}
@@ -1147,17 +1144,17 @@ ppolicy_ctrls_cleanup( Operation *op, Sl
 }
 
 static int
 ppolicy_bind_response( Operation *op, SlapReply *rs )
 {
 	ppbind *ppb = op->o_callback->sc_private;
 	slap_overinst *on = ppb->on;
 	Modifications *mod = ppb->mod, *m;
-	int pwExpired = 0; 
+	int pwExpired = 0;
 	int ngut = -1, warn = -1, age, rc;
 	Attribute *a;
 	time_t now, pwtime = (time_t)-1;
 	char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
 	struct berval timestamp;
 	BackendInfo *bi = op->o_bd->bd_info;
 	Entry *e;
 	pw_unique *pull = NULL;
@@ -1505,17 +1502,17 @@ locked:
 	if ( ppb->send_ctrl ) {
 		LDAPControl *ctrl = NULL;
 		pp_info *pi = on->on_bi.bi_private;
 
 		/* Do we really want to tell that the account is locked? */
 		if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
 			ppb->pErr = PP_noError;
 		}
-		ctrl = create_passcontrol( warn, ngut, ppb->pErr );
+		ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
 		ppb->oldctrls = add_passcontrol( op, rs, ctrl );
 		op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
 	}
 	op->o_bd->bd_info = bi;
 	return SLAP_CB_CONTINUE;
 }
 
 static int
@@ -1624,17 +1621,17 @@ ppolicy_restrict(
 			BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
 			return SLAP_CB_CONTINUE;
 		}
 
 		Debug( LDAP_DEBUG_TRACE,
 			"connection restricted to password changing only\n", 0, 0, 0);
 		if ( send_ctrl ) {
 			LDAPControl *ctrl = NULL;
-			ctrl = create_passcontrol( -1, -1, PP_changeAfterReset );
+			ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
 			oldctrls = add_passcontrol( op, rs, ctrl );
 		}
 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
 		send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
 			"Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
 		if ( send_ctrl ) {
 			ctrls_cleanup( op, rs, oldctrls );
 		}
@@ -1690,17 +1687,17 @@ ppolicy_add(
 				send_ctrl = 1;
 			}
 			rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
 			if (rc != LDAP_SUCCESS) {
 				LDAPControl **oldctrls = NULL;
 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
 				if ( send_ctrl ) {
 					LDAPControl *ctrl = NULL;
-					ctrl = create_passcontrol( -1, -1, pErr );
+					ctrl = create_passcontrol( op, -1, -1, pErr );
 					oldctrls = add_passcontrol( op, rs, ctrl );
 				}
 				send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
 				if ( send_ctrl ) {
 					ctrls_cleanup( op, rs, oldctrls );
 				}
 				return rs->sr_err;
 			}
@@ -1779,17 +1776,19 @@ ppolicy_modify( Operation *op, SlapReply
 	Attribute		*pa, *ha, at;
 	const char		*txt;
 	pw_hist			*tl = NULL, *p;
 	int			zapReset, send_ctrl = 0;
 	Entry			*e;
 	struct berval		newpw = BER_BVNULL, oldpw = BER_BVNULL,
 				*bv, cr[2];
 	LDAPPasswordPolicyError pErr = PP_noError;
+	LDAPControl		*ctrl = NULL;
 	LDAPControl 		**oldctrls = NULL;
+	int			is_pwdexop = 0;
 
 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
 	rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
 	op->o_bd->bd_info = (BackendInfo *)on;
 
 	if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
 
 	/* If this is a replica, we may need to tweak some of the
@@ -1910,16 +1909,17 @@ ppolicy_modify( Operation *op, SlapReply
 		slap_callback *sc;
 
 		for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
 			if ( sc->sc_response == slap_null_cb &&
 				sc->sc_private ) {
 				req_pwdexop_s *qpw = sc->sc_private;
 				newpw = qpw->rs_new;
 				oldpw = qpw->rs_old;
+				is_pwdexop = 1;
 			   	break;
 			}
 		}
 	}
 
 	ppolicy_get( op, e, &pp );
 
 	for ( ml = op->orm_modlist,
@@ -2404,24 +2404,31 @@ do_modify:
 	be_entry_release_r( op, e );
 	return SLAP_CB_CONTINUE;
 
 return_results:
 	free_pwd_history_list( &tl );
 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
 	be_entry_release_r( op, e );
 	if ( send_ctrl ) {
-		LDAPControl *ctrl = NULL;
-
-		ctrl = create_passcontrol( -1, -1, pErr );
+		ctrl = create_passcontrol( op, -1, -1, pErr );
 		oldctrls = add_passcontrol( op, rs, ctrl );
 	}
 	send_ldap_result( op, rs );
 	if ( send_ctrl ) {
-		ctrls_cleanup( op, rs, oldctrls );
+		if ( is_pwdexop ) {
+			if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+				op->o_tmpfree( oldctrls, op->o_tmpmemctx );
+			}
+			oldctrls = NULL;
+			rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+		} else {
+			ctrls_cleanup( op, rs, oldctrls );
+		}
 	}
 	return rs->sr_err;
 }
 
 static int
 ppolicy_parseCtrl(
 	Operation *op,
 	SlapReply *rs,
@@ -2479,16 +2486,26 @@ attrNormalize(
 static int
 ppolicy_db_init(
 	BackendDB *be,
 	ConfigReply *cr
 )
 {
 	slap_overinst *on = (slap_overinst *) be->bd_info;
 
+	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+		/* do not allow slapo-ppolicy to be global by now (ITS#5858) */
+		if ( cr ){
+			snprintf( cr->msg, sizeof(cr->msg), 
+				"slapo-ppolicy cannot be global" );
+			fprintf( stderr, "%s\n", cr->msg );
+		}
+		return 1;
+	}
+
 	/* Has User Schema been initialized yet? */
 	if ( !pwd_UsSchema[0].ad[0] ) {
 		const char *err;
 		int i, code;
 
 		for (i=0; pwd_UsSchema[i].def; i++) {
 			code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
 			if ( code ) {
--- a/slapo-ppolicy.5
+++ b/slapo-ppolicy.5
@@ -1,11 +1,12 @@
-.\" $OpenLDAP: pkg/ldap/doc/man/man5/slapo-ppolicy.5,v 1.12.2.7 2008/04/24 08:15:34 hyc Exp $
-.\" Copyright 2004-2008 The OpenLDAP Foundation All Rights Reserved.
+.TH SLAPO_PPOLICY 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2009 The OpenLDAP Foundation All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.\" $OpenLDAP: pkg/ldap/doc/man/man5/slapo-ppolicy.5,v 1.12.2.10 2009/01/30 20:13:42 quanah Exp $
 .TH SLAPO_PPOLICY 5 "RELEASEDATE" "OpenLDAP LDVERSION"
 .SH NAME
 slapo-ppolicy \- Password Policy overlay to slapd
 .SH SYNOPSIS
 ETCDIR/slapd.conf
 .SH DESCRIPTION
 .LP
 The 
@@ -842,16 +843,17 @@ suffix dc=example,dc=com
 overlay ppolicy
 ppolicy_default "cn=Standard,ou=Policies,dc=example,dc=com"
 .fi
 .RE
 
 .SH SEE ALSO
 .BR ldap (3),
 .BR slapd.conf (5),
+.BR slapd\-config (5).
 .LP
 "OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
 .LP
 IETF LDAP password policy proposal by P. Behera, L.  Poitou and J.
 Sermersheim:  documented in IETF document
 "draft-behera-ldap-password-policy-09.txt".
 
 .SH BUGS