From 820c1c3f018c5fdb8e547740edb58bc1c0a558de Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
Date: Mon, 22 Aug 2011 10:25:21 -0700
Subject: [PATCH] Bug 611438 - Add Account Usability Control support

This adds support for the account usability request and response
controls that are used by Solaris clients.  This control allows
one to check if an account is usable without actually performing
a bind attempt as that entry.  The OpenDS documentation describes
the control in greater detail.

Various information about the password policy is returned in the
response control.  To allow the new plug-in to get the password
policy info (and to make password policy checks), I added some
new SLAPI functions around password policies.  I did this in such
a way that we can add support for password policy plug-ins in the
future if we expand upon this new API.  The password policy is
represented by an opaque Slapi_PWPolicy struct, which is currently
just our internal pwpolicy struct.  This will allow password policy
plug-ins to have their own struct that meets their needs if we add
support for constructor and destructor callbacks.

By default, we only allow the root DN to use this control, though
one can set an aci in the control entry under cn=features to allow
others to use the control.
---
 Makefile.am                                        |   12 +-
 Makefile.in                                        |   56 +++-
 ldap/admin/src/scripts/50acctusabilityplugin.ldif  |   21 +
 ldap/ldif/template-dse.ldif.in                     |   26 +-
 .../plugins/acct_usability/acct_usability.c        |  466 ++++++++++++++++++++
 .../plugins/acct_usability/acct_usability.h        |   63 +++
 ldap/servers/slapd/fedse.c                         |    5 -
 ldap/servers/slapd/pw.c                            |  162 +++++++-
 ldap/servers/slapd/pw.h                            |    2 +-
 ldap/servers/slapd/pw_retry.c                      |   20 +-
 ldap/servers/slapd/slapi-plugin.h                  |   69 +++
 11 files changed, 877 insertions(+), 25 deletions(-)
 create mode 100644 ldap/admin/src/scripts/50acctusabilityplugin.ldif
 create mode 100644 ldap/servers/plugins/acct_usability/acct_usability.c
 create mode 100644 ldap/servers/plugins/acct_usability/acct_usability.h

diff --git a/Makefile.am b/Makefile.am
index 4bf1966..e5bf736 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -197,7 +197,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
 	libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
 	libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
 	libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \
-	$(LIBACCTPOLICY_PLUGIN) \
+	libacctusability-plugin.la $(LIBACCTPOLICY_PLUGIN) \
 	$(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) \
 	$(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN)
 
@@ -443,6 +443,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
 	ldap/admin/src/scripts/10delautodnsuffix.pl \
 	ldap/admin/src/scripts/10fixrundir.pl \
 	ldap/admin/src/scripts/50addchainingsaslpwroles.ldif \
+        ldap/admin/src/scripts/50acctusabilityplugin.ldif \
         ldap/admin/src/scripts/50automemberplugin.ldif \
 	ldap/admin/src/scripts/50memberofindex.ldif \
 	ldap/admin/src/scripts/50bitstringsyntaxplugin.ldif \
@@ -775,6 +776,15 @@ libacctpolicy_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
 libacctpolicy_plugin_la_LDFLAGS = -avoid-version
 
 #------------------------
+# libacctusability-plugin
+#------------------------
+libacctusability_plugin_la_SOURCES = ldap/servers/plugins/acct_usability/acct_usability.c
+
+libacctusability_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libacctusability_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
+libacctusability_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
 # libacl-plugin
 #------------------------
 libacl_plugin_la_SOURCES = ldap/servers/plugins/acl/acl.c \
diff --git a/Makefile.in b/Makefile.in
index 0112e30..75e138c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -139,6 +139,15 @@ libacctpolicy_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(libacctpolicy_plugin_la_LDFLAGS) $(LDFLAGS) -o $@
 @enable_acctpolicy_TRUE@am_libacctpolicy_plugin_la_rpath = -rpath \
 @enable_acctpolicy_TRUE@	$(serverplugindir)
+libacctusability_plugin_la_DEPENDENCIES = libslapd.la \
+	$(am__DEPENDENCIES_1)
+am_libacctusability_plugin_la_OBJECTS = ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo
+libacctusability_plugin_la_OBJECTS =  \
+	$(am_libacctusability_plugin_la_OBJECTS)
+libacctusability_plugin_la_LINK = $(LIBTOOL) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) $(libacctusability_plugin_la_LDFLAGS) \
+	$(LDFLAGS) -o $@
 libacl_plugin_la_DEPENDENCIES = libslapd.la libns-dshttpd.la \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
@@ -971,7 +980,9 @@ CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
 	--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
 SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
-	$(libacctpolicy_plugin_la_SOURCES) $(libacl_plugin_la_SOURCES) \
+	$(libacctpolicy_plugin_la_SOURCES) \
+	$(libacctusability_plugin_la_SOURCES) \
+	$(libacl_plugin_la_SOURCES) \
 	$(libattr_unique_plugin_la_SOURCES) \
 	$(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \
 	$(libbitwise_plugin_la_SOURCES) \
@@ -1001,7 +1012,9 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
 	$(ns_slapd_SOURCES) $(pwdhash_bin_SOURCES) \
 	$(rsearch_bin_SOURCES)
 DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
-	$(libacctpolicy_plugin_la_SOURCES) $(libacl_plugin_la_SOURCES) \
+	$(libacctpolicy_plugin_la_SOURCES) \
+	$(libacctusability_plugin_la_SOURCES) \
+	$(libacl_plugin_la_SOURCES) \
 	$(libattr_unique_plugin_la_SOURCES) \
 	$(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \
 	$(libbitwise_plugin_la_SOURCES) \
@@ -1387,7 +1400,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
 	libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
 	libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
 	libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \
-	$(LIBACCTPOLICY_PLUGIN) \
+	libacctusability-plugin.la $(LIBACCTPOLICY_PLUGIN) \
 	$(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) \
 	$(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN)
 
@@ -1633,6 +1646,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
 	ldap/admin/src/scripts/10delautodnsuffix.pl \
 	ldap/admin/src/scripts/10fixrundir.pl \
 	ldap/admin/src/scripts/50addchainingsaslpwroles.ldif \
+        ldap/admin/src/scripts/50acctusabilityplugin.ldif \
         ldap/admin/src/scripts/50automemberplugin.ldif \
 	ldap/admin/src/scripts/50memberofindex.ldif \
 	ldap/admin/src/scripts/50bitstringsyntaxplugin.ldif \
@@ -1911,6 +1925,14 @@ libacctpolicy_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
 libacctpolicy_plugin_la_LDFLAGS = -avoid-version
 
 #------------------------
+# libacctusability-plugin
+#------------------------
+libacctusability_plugin_la_SOURCES = ldap/servers/plugins/acct_usability/acct_usability.c
+libacctusability_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libacctusability_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
+libacctusability_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
 # libacl-plugin
 #------------------------
 libacl_plugin_la_SOURCES = ldap/servers/plugins/acl/acl.c \
@@ -2767,6 +2789,17 @@ ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_util.lo:  \
 	ldap/servers/plugins/acctpolicy/$(DEPDIR)/$(am__dirstamp)
 libacctpolicy-plugin.la: $(libacctpolicy_plugin_la_OBJECTS) $(libacctpolicy_plugin_la_DEPENDENCIES) 
 	$(libacctpolicy_plugin_la_LINK) $(am_libacctpolicy_plugin_la_rpath) $(libacctpolicy_plugin_la_OBJECTS) $(libacctpolicy_plugin_la_LIBADD) $(LIBS)
+ldap/servers/plugins/acct_usability/$(am__dirstamp):
+	@$(MKDIR_P) ldap/servers/plugins/acct_usability
+	@: > ldap/servers/plugins/acct_usability/$(am__dirstamp)
+ldap/servers/plugins/acct_usability/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) ldap/servers/plugins/acct_usability/$(DEPDIR)
+	@: > ldap/servers/plugins/acct_usability/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo:  \
+	ldap/servers/plugins/acct_usability/$(am__dirstamp) \
+	ldap/servers/plugins/acct_usability/$(DEPDIR)/$(am__dirstamp)
+libacctusability-plugin.la: $(libacctusability_plugin_la_OBJECTS) $(libacctusability_plugin_la_DEPENDENCIES) 
+	$(libacctusability_plugin_la_LINK) -rpath $(serverplugindir) $(libacctusability_plugin_la_OBJECTS) $(libacctusability_plugin_la_LIBADD) $(LIBS)
 ldap/servers/plugins/acl/$(am__dirstamp):
 	@$(MKDIR_P) ldap/servers/plugins/acl
 	@: > ldap/servers/plugins/acl/$(am__dirstamp)
@@ -4673,6 +4706,8 @@ mostlyclean-compile:
 	-rm -f ldap/libraries/libavl/avl.$(OBJEXT)
 	-rm -f ldap/libraries/libavl/libslapd_la-avl.$(OBJEXT)
 	-rm -f ldap/libraries/libavl/libslapd_la-avl.lo
+	-rm -f ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.$(OBJEXT)
+	-rm -f ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo
 	-rm -f ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_config.$(OBJEXT)
 	-rm -f ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_config.lo
 	-rm -f ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_init.$(OBJEXT)
@@ -5516,6 +5551,7 @@ distclean-compile:
 
 @AMDEP_TRUE@@am__include@ @am__quote@ldap/libraries/libavl/$(DEPDIR)/avl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@ldap/libraries/libavl/$(DEPDIR)/libslapd_la-avl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/acct_usability/$(DEPDIR)/libacctusability_plugin_la-acct_usability.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/acctpolicy/$(DEPDIR)/libacctpolicy_plugin_la-acct_config.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/acctpolicy/$(DEPDIR)/libacctpolicy_plugin_la-acct_init.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/acctpolicy/$(DEPDIR)/libacctpolicy_plugin_la-acct_plugin.Plo@am__quote@
@@ -6166,6 +6202,13 @@ ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_util.lo: ldap/serve
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacctpolicy_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/acctpolicy/libacctpolicy_plugin_la-acct_util.lo `test -f 'ldap/servers/plugins/acctpolicy/acct_util.c' || echo '$(srcdir)/'`ldap/servers/plugins/acctpolicy/acct_util.c
 
+ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo: ldap/servers/plugins/acct_usability/acct_usability.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacctusability_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo -MD -MP -MF ldap/servers/plugins/acct_usability/$(DEPDIR)/libacctusability_plugin_la-acct_usability.Tpo -c -o ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo `test -f 'ldap/servers/plugins/acct_usability/acct_usability.c' || echo '$(srcdir)/'`ldap/servers/plugins/acct_usability/acct_usability.c
+@am__fastdepCC_TRUE@	$(am__mv) ldap/servers/plugins/acct_usability/$(DEPDIR)/libacctusability_plugin_la-acct_usability.Tpo ldap/servers/plugins/acct_usability/$(DEPDIR)/libacctusability_plugin_la-acct_usability.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='ldap/servers/plugins/acct_usability/acct_usability.c' object='ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacctusability_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/acct_usability/libacctusability_plugin_la-acct_usability.lo `test -f 'ldap/servers/plugins/acct_usability/acct_usability.c' || echo '$(srcdir)/'`ldap/servers/plugins/acct_usability/acct_usability.c
+
 ldap/servers/plugins/acl/libacl_plugin_la-acl.lo: ldap/servers/plugins/acl/acl.c
 @am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacl_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/acl/libacl_plugin_la-acl.lo -MD -MP -MF ldap/servers/plugins/acl/$(DEPDIR)/libacl_plugin_la-acl.Tpo -c -o ldap/servers/plugins/acl/libacl_plugin_la-acl.lo `test -f 'ldap/servers/plugins/acl/acl.c' || echo '$(srcdir)/'`ldap/servers/plugins/acl/acl.c
 @am__fastdepCC_TRUE@	$(am__mv) ldap/servers/plugins/acl/$(DEPDIR)/libacl_plugin_la-acl.Tpo ldap/servers/plugins/acl/$(DEPDIR)/libacl_plugin_la-acl.Plo
@@ -9703,6 +9746,7 @@ mostlyclean-libtool:
 clean-libtool:
 	-rm -rf .libs _libs
 	-rm -rf ldap/libraries/libavl/.libs ldap/libraries/libavl/_libs
+	-rm -rf ldap/servers/plugins/acct_usability/.libs ldap/servers/plugins/acct_usability/_libs
 	-rm -rf ldap/servers/plugins/acctpolicy/.libs ldap/servers/plugins/acctpolicy/_libs
 	-rm -rf ldap/servers/plugins/acl/.libs ldap/servers/plugins/acl/_libs
 	-rm -rf ldap/servers/plugins/automember/.libs ldap/servers/plugins/automember/_libs
@@ -10304,6 +10348,8 @@ distclean-generic:
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
 	-rm -f ldap/libraries/libavl/$(DEPDIR)/$(am__dirstamp)
 	-rm -f ldap/libraries/libavl/$(am__dirstamp)
+	-rm -f ldap/servers/plugins/acct_usability/$(DEPDIR)/$(am__dirstamp)
+	-rm -f ldap/servers/plugins/acct_usability/$(am__dirstamp)
 	-rm -f ldap/servers/plugins/acctpolicy/$(DEPDIR)/$(am__dirstamp)
 	-rm -f ldap/servers/plugins/acctpolicy/$(am__dirstamp)
 	-rm -f ldap/servers/plugins/acl/$(DEPDIR)/$(am__dirstamp)
@@ -10400,7 +10446,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool clean-local \
 
 distclean: distclean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
+	-rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-libtool distclean-tags
@@ -10456,7 +10502,7 @@ installcheck-am:
 maintainer-clean: maintainer-clean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
+	-rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
diff --git a/ldap/admin/src/scripts/50acctusabilityplugin.ldif b/ldap/admin/src/scripts/50acctusabilityplugin.ldif
new file mode 100644
index 0000000..fa72c4f
--- /dev/null
+++ b/ldap/admin/src/scripts/50acctusabilityplugin.ldif
@@ -0,0 +1,21 @@
+dn: cn=Account Usability Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Account Usability Plugin
+nsslapd-pluginpath: libacctusability-plugin
+nsslapd-plugininitfunc: auc_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+# these will be replaced when the server loads the plugin
+nsslapd-pluginId: ID
+nsslapd-pluginVersion: PACKAGE_VERSION
+nsslapd-pluginVendor: VENDOR
+nsslapd-pluginDescription: DESC
+
+dn: oid=1.3.6.1.4.1.42.2.27.9.5.8,cn=features,cn=config
+objectClass: top
+objectClass: directoryServerFeature
+oid: 1.3.6.1.4.1.42.2.27.9.5.8
+cn: Account Usable Request Control
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index fde894b..d8b8965 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -56,12 +56,23 @@ nsslapd-maxdescriptors: 1024
 nsslapd-max-filter-nest-level: 40
 nsslapd-rootpw: %ds_passwd%
 
-dn: cn=plugins, cn=config
+dn: cn=features,cn=config
+objectclass: top
+objectclass: nsContainer
+cn: features
+
+dn: oid=1.3.6.1.4.1.42.2.27.9.5.8,cn=features,cn=config
+objectClass: top
+objectClass: directoryServerFeature
+oid: 1.3.6.1.4.1.42.2.27.9.5.8
+cn: Account Usable Request Control
+
+dn: cn=plugins,cn=config
 objectclass: top
 objectclass: nsContainer
 cn: plugins
 
-dn: cn=Password Storage Schemes,cn=plugins, cn=config
+dn: cn=Password Storage Schemes,cn=plugins,cn=config
 objectclass: top
 objectclass: nsContainer
 cn: Password Storage Schemes
@@ -620,6 +631,17 @@ nsslapd-pluginarg3: ,
 nsslapd-pluginarg4: %ds_suffix%
 nsslapd-plugin-depends-on-type: database
 
+dn: cn=Account Usability Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Account Usability Plugin
+nsslapd-pluginpath: libacctusability-plugin
+nsslapd-plugininitfunc: auc_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+
 dn: cn=Auto Membership Plugin,cn=plugins,cn=config
 objectclass: top
 objectclass: nsSlapdPlugin
diff --git a/ldap/servers/plugins/acct_usability/acct_usability.c b/ldap/servers/plugins/acct_usability/acct_usability.c
new file mode 100644
index 0000000..2371f03
--- /dev/null
+++ b/ldap/servers/plugins/acct_usability/acct_usability.c
@@ -0,0 +1,466 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/*
+ * Account Usability Control plug-in
+ */
+#include <string.h>
+#include "acct_usability.h"
+#include <nspr.h>
+#include <time.h>
+
+
+/*
+ * Plug-in globals
+ */
+static void *_PluginID = NULL;
+static char *_PluginDN = NULL;
+static int g_plugin_started = 0;
+
+static Slapi_PluginDesc pdesc = { AUC_FEATURE_DESC,
+                                  VENDOR,
+                                  DS_PACKAGE_VERSION,
+                                  AUC_PLUGIN_DESC };
+
+/*
+ * Plug-in management functions
+ */
+int auc_init(Slapi_PBlock * pb);
+static int auc_start(Slapi_PBlock * pb);
+static int auc_close(Slapi_PBlock * pb);
+
+/*
+ * Operation callbacks (where the real work is done)
+ */
+static int auc_pre_search(Slapi_PBlock * pb);
+static int auc_pre_entry(Slapi_PBlock *pb);
+
+/*
+ * Plugin identity functions
+ */
+void
+auc_set_plugin_id(void *pluginID)
+{
+    _PluginID = pluginID;
+}
+
+void *
+auc_get_plugin_id()
+{
+    return _PluginID;
+}
+
+void
+auc_set_plugin_dn(char *pluginDN)
+{
+    _PluginDN = pluginDN;
+}
+
+char *
+auc_get_plugin_dn()
+{
+    return _PluginDN;
+}
+
+/*
+ * Plug-in initialization functions
+ */
+int
+auc_init(Slapi_PBlock *pb)
+{
+    int status = 0;
+    char *plugin_identity = NULL;
+
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "--> auc_init\n");
+
+    /* Store the plugin identity for later use.
+     * Used for internal operations. */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+    PR_ASSERT(plugin_identity);
+    auc_set_plugin_id(plugin_identity);
+
+    /* Register callbacks */
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+                         SLAPI_PLUGIN_VERSION_01) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+                         (void *) auc_start) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+                         (void *) auc_close) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+                         (void *) &pdesc) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN,
+                         (void *) auc_pre_search) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ENTRY_FN,
+                         (void *) auc_pre_entry) != 0
+        ) {
+        slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                        "auc_init: failed to register plugin\n");
+        status = -1;
+    }
+
+    if (status == 0) {
+        slapi_register_supported_control(AUC_OID, SLAPI_OPERATION_SEARCH);
+    }
+
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "<-- auc_init\n");
+    return status;
+}
+
+
+/*
+ * auc_start()
+ */
+static int
+auc_start(Slapi_PBlock * pb)
+{
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "--> auc_start\n");
+
+    /* Check if we're already started */
+    if (g_plugin_started) {
+        goto done;
+    }
+
+    g_plugin_started = 1;
+    slapi_log_error(SLAPI_LOG_PLUGIN, AUC_PLUGIN_SUBSYSTEM,
+                    "account usability control plug-in: ready for service\n");
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "<-- auc_start\n");
+
+done:
+    return 0;
+}
+
+/*
+ * auc_close()
+ */
+static int
+auc_close(Slapi_PBlock * pb)
+{
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "--> auc_close\n");
+
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "<-- auc_close\n");
+
+    return 0;
+}
+
+
+/*
+ * Helper Functions
+ */
+
+/*
+ * auc_incompatible_ctrl()
+ *
+ * Check if control oid is incompatible with the account
+ * usability control.
+ */
+static int
+auc_incompatible_ctrl(const char *oid)
+{
+    return 0; /* no known incompatible ctrls yet */
+}
+
+/*
+ * auc_create_response_ctrl()
+ *
+ * Generates the response control for the passed in DN.
+ *
+ *     ACCOUNT_USABLE_RESPONSE ::= CHOICE {
+ *       is_available           [0] INTEGER, -- Seconds before expiration --
+ *       is_not_available       [1] MORE_INFO }
+ *
+ *     MORE_INFO ::= SEQUENCE {
+ *       inactive               [0] BOOLEAN DEFAULT FALSE,
+ *       reset                  [1] BOOLEAN DEFAULT FALSE,
+ *       expired                [2] BOOLEAN DEFAULT_FALSE,
+ *       remaining_grace        [3] INTEGER OPTIONAL,
+ *       seconds_before_unlock  [4] INTEGER OPTIONAL } 
+ */
+static LDAPControl *auc_create_response_ctrl(Slapi_Entry *e)
+{
+    BerElement *ctrlber = NULL;
+    LDAPControl *ctrl = NULL;
+    int is_available = 0;
+    int seconds_before_expiration = 0;
+    int inactive = 0;
+    int reset = 0;
+    int expired = 0;
+    int remaining_grace = 0;
+    int seconds_before_unlock = 0;
+    Slapi_PWPolicy *pwpolicy = NULL;
+    time_t expire_time = (time_t)0;
+    time_t unlock_time = (time_t)0;
+    time_t now = time(NULL);
+
+    if (!e) {
+        slapi_log_error(SLAPI_LOG_PLUGIN, AUC_PLUGIN_SUBSYSTEM,
+                        "auc_create_response_ctrl: NULL entry specified.\n");
+        goto bail;
+    }
+
+    /* Fetch password policy info */
+    pwpolicy = slapi_get_pwpolicy(slapi_entry_get_ndn(e));
+    if (pwpolicy) {
+        expired = slapi_pwpolicy_is_expired(pwpolicy, e, &expire_time, &remaining_grace);
+        inactive = slapi_pwpolicy_is_locked(pwpolicy, e, &unlock_time);
+        reset = slapi_pwpolicy_is_reset(pwpolicy, e);
+
+        slapi_pwpolicy_free(pwpolicy);
+    }
+
+    /* Calculate the seconds before expiration or unlock if needed. */
+    if (!expired && !inactive && !reset) {
+        is_available = 1;
+        if (expire_time > 0) {
+            seconds_before_expiration = expire_time - now;
+        }
+    } else if (inactive && unlock_time) {
+        if (unlock_time > 0) {
+            seconds_before_unlock = unlock_time - now;
+        }
+    }
+
+    /* Create the control value */
+    ctrlber = ber_alloc();
+
+    if (is_available) {
+        ber_printf(ctrlber, "ti", AUC_TAG_AVAILABLE, seconds_before_expiration);
+    } else {
+        /* Fill in reason account is not available */
+        ber_printf(ctrlber, "t{", AUC_TAG_NOT_AVAILABLE);
+        ber_printf(ctrlber, "tb", AUC_TAG_INACTIVE, inactive);
+        ber_printf(ctrlber, "tb", AUC_TAG_RESET, reset); 
+        ber_printf(ctrlber, "tb", AUC_TAG_EXPIRED, expired);
+
+        if (expired) {
+            ber_printf(ctrlber, "ti", AUC_TAG_GRACE, remaining_grace);
+        }
+
+        if (inactive) {
+            ber_printf(ctrlber, "ti", AUC_TAG_UNLOCK, seconds_before_unlock);
+        }
+
+        ber_printf(ctrlber, "}");
+    }
+
+    slapi_build_control(AUC_OID, ctrlber, 0, &ctrl);
+    ber_free(ctrlber, 1);
+
+bail:
+    return ctrl;
+}
+
+/*
+ * Operation callback functions
+ */
+
+/*
+ * auc_pre_search()
+ *
+ * See if the account usability control has been specified.
+ * If so, parse it, and check to make sure it meets the
+ * protocol specification (no duplicate attributes, etc.).
+ * We also check to see if the requestor is allowed to use
+ * the control.
+ */
+static int
+auc_pre_search(Slapi_PBlock *pb)
+{
+    int ldapcode = LDAP_SUCCESS;
+    const LDAPControl **reqctrls = NULL;
+    const LDAPControl *aucctrl = NULL;
+    const char *ldaperrtext;
+    const char *incompatible = NULL;
+    int isroot = 0;
+    int ii;
+
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "--> auc_pre_search\n");
+
+    /* See if the requestor is the root DN. */
+    slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+
+    /* see if the auc request control is in the list of 
+       controls - if so, validate it */
+    slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
+    for (ii = 0; (ldapcode == LDAP_SUCCESS) && reqctrls && reqctrls[ii]; ++ii) {
+        const LDAPControl *ctrl = reqctrls[ii];
+        if (!strcmp(ctrl->ldctl_oid, AUC_OID)) {
+            if (aucctrl) { /* already specified */
+                slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                                "The account usability control was specified more than "
+                                "once - it must be specified only once in the search request\n");
+                ldapcode = LDAP_PROTOCOL_ERROR;
+                ldaperrtext = "The account usability control cannot be specified more than once";
+                aucctrl = NULL;
+            } else if (ctrl->ldctl_value.bv_len > 0) {
+                slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                                "Non-null control value specified for account usability control\n");
+                ldapcode = LDAP_PROTOCOL_ERROR;
+                ldaperrtext = "The account usability control must not have a value";
+            } else {
+                aucctrl = ctrl;
+            }
+        } else if (auc_incompatible_ctrl(ctrl->ldctl_oid)) {
+            incompatible = ctrl->ldctl_oid;
+        }
+    }
+
+    if (aucctrl && incompatible) {
+        slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                        "Cannot use the account usability control and control [%s] for the same search operation\n",
+                        incompatible);
+        /* not sure if this is a hard failure - the current spec says:
+           The semantics of the criticality field are specified in [RFC4511].
+           In detail, the criticality of the control determines whether the
+           control will or will not be used, and if it will not be used, whether
+           the operation will continue without returning the control in the
+           response, or fail, returning unavailableCriticalExtension.  If the
+           control is appropriate for an operation and, for any reason, it
+           cannot be applied in its entirety to a single SearchResultEntry
+           response, it MUST NOT be applied to that specific SearchResultEntry
+           response, without affecting its application to any subsequent
+           SearchResultEntry response.
+        */
+        /* so for now, just return LDAP_SUCCESS and don't do anything else */
+        aucctrl = NULL;
+    }
+
+    /* Check access control if all the above parsing went OK.
+     * Skip this for the root DN. */
+    if (aucctrl && (ldapcode == LDAP_SUCCESS) && !isroot) {
+        char dn[128];
+        Slapi_Entry *feature = NULL;
+
+        /* Fetch the feature entry and see if the requestor is allowed access. */
+        PR_snprintf(dn, sizeof(dn), "dn: oid=%s,cn=features,cn=config", AUC_OID);
+        if ((feature = slapi_str2entry(dn,0)) != NULL) {
+            char *dummyAttr = "dummy#attr";
+            char *dummyAttrs[2] = { NULL, NULL };
+
+            dummyAttrs[0] = dummyAttr;
+            ldapcode = plugin_call_acl_plugin (pb, feature, dummyAttrs, NULL, SLAPI_ACL_READ, ACLPLUGIN_ACCESS_DEFAULT, NULL);
+        }
+
+        /* If the feature entry does not exist, deny use of the control.  Only
+         * the root DN will be allowed to use the control in this case. */
+        if ((feature == NULL) || (ldapcode != LDAP_SUCCESS)) {
+            ldapcode = LDAP_INSUFFICIENT_ACCESS;
+            ldaperrtext = "Insufficient access rights to use the account usability request control";
+        }
+
+        slapi_entry_free(feature);
+    }
+
+    if (ldapcode != LDAP_SUCCESS) {
+        slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &ldapcode);
+        slapi_send_ldap_result(pb, ldapcode, NULL, (char *)ldaperrtext, 0, NULL);
+    }
+
+    slapi_log_error(SLAPI_LOG_TRACE, AUC_PLUGIN_SUBSYSTEM,
+                    "<-- auc_pre_op\n");
+
+    return ldapcode;
+}
+
+static int
+auc_pre_entry(Slapi_PBlock *pb)
+{
+    int ii = 0;
+    int need_response = 0;
+    LDAPControl *ctrl = NULL;
+    const LDAPControl **reqctrls = NULL;
+    const LDAPControl **searchctrls = NULL;
+    LDAPControl **newsearchctrls = NULL;
+
+    /* See if the account usability request control was specified. */
+    slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
+    for (ii = 0; reqctrls && reqctrls[ii]; ++ii) {
+        if (!strcmp(reqctrls[ii]->ldctl_oid, AUC_OID)) {
+            need_response = 1;
+            break;
+        }
+    }
+
+    /* Generate the response control if requested. */
+    if (need_response) {
+        Slapi_Entry *e = NULL;
+
+        /* grab the entry to be returned */
+        slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_ORIG, &e);
+        if (!e) {
+            slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                            "auc_pre_entry: Unable to fetch entry.\n");
+            goto bail;
+        }
+
+        /* create the respose control */
+        ctrl = auc_create_response_ctrl(e);
+        if (!ctrl) {
+            slapi_log_error(SLAPI_LOG_FATAL, AUC_PLUGIN_SUBSYSTEM,
+                "auc_pre_entry: Error creating response control for entry \"%s\".\n",
+                slapi_entry_get_ndn(e) ? slapi_entry_get_ndn(e) : "null");
+            goto bail;
+        }
+
+        /* get the list of controls */
+        slapi_pblock_get(pb, SLAPI_SEARCH_CTRLS, &searchctrls);
+
+        /* dup them */
+        slapi_add_controls(&newsearchctrls, (LDAPControl **)searchctrls, 1);
+
+        /* add our control */
+        slapi_add_control_ext(&newsearchctrls, ctrl, 0);
+        ctrl = NULL; /* newsearchctrls owns it now */
+
+        /* set the controls in the pblock */
+        slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, newsearchctrls);
+    }
+
+bail:
+    return 0;
+}
+
diff --git a/ldap/servers/plugins/acct_usability/acct_usability.h b/ldap/servers/plugins/acct_usability/acct_usability.h
new file mode 100644
index 0000000..a10a9a2
--- /dev/null
+++ b/ldap/servers/plugins/acct_usability/acct_usability.h
@@ -0,0 +1,63 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/*
+ * Account Usability Control plug-in header file
+ */
+#include "slapi-plugin.h"
+
+/*
+ * Plug-in defines
+ */
+#define AUC_PLUGIN_SUBSYSTEM  "account-usability-plugin"
+#define AUC_FEATURE_DESC      "Account Usability Control"
+#define AUC_PLUGIN_DESC       "Account Usability Control plugin"
+#define AUC_PREOP_DESC        "Account Usability Control preop plugin"
+
+#define	AUC_OID               "1.3.6.1.4.1.42.2.27.9.5.8"
+
+#define AUC_TAG_AVAILABLE     0x80L   /* context specific + primitive */
+#define AUC_TAG_NOT_AVAILABLE 0xA1L   /* context specific + constructed + 1 */
+#define AUC_TAG_INACTIVE      0x80L   /* context specific + primitive */
+#define AUC_TAG_RESET         0x81L   /* context specific + primitive + 1 */
+#define AUC_TAG_EXPIRED       0x82L   /* context specific + primitive + 2 */
+#define AUC_TAG_GRACE         0x83L   /* context specific + primitive + 3 */
+#define AUC_TAG_UNLOCK        0x84L   /* context specific + primitive + 4 */
diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c
index 1417954..8a61e72 100644
--- a/ldap/servers/slapd/fedse.c
+++ b/ldap/servers/slapd/fedse.c
@@ -83,11 +83,6 @@ static const char *internal_entries[] =
     "objectclass: top\n"
 	"aci: (targetattr != \"aci\")(version 3.0; aci \"rootdse anon read access\"; allow(read,search,compare) userdn=\"ldap:///anyone\";)\n",
 
-    "dn:cn=features,cn=config\n"
-    "objectclass:top\n"
-    "objectclass:nsContainer\n"
-    "cn:features\n",
-
     "dn:oid=2.16.840.1.113730.3.4.9,cn=features,cn=config\n"
     "objectclass:top\n"
     "objectclass:directoryServerFeature\n"
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 7c6210b..61d01d9 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -1505,7 +1505,7 @@ pw_add_allowchange_aci(Slapi_Entry *e, int pw_prohibit_change) {
  * returns the structure.
  */
 passwdPolicy *
-new_passwdPolicy(Slapi_PBlock *pb, char *dn)
+new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
 {
 	Slapi_ValueSet *values = NULL;
 	Slapi_Entry *e = NULL, *pw_entry = NULL;
@@ -1519,7 +1519,6 @@ new_passwdPolicy(Slapi_PBlock *pb, char *dn)
 	char *attr_name;
 	Slapi_Value **sval;
 	slapdFrontendConfig_t *slapdFrontendConfig;
-	Slapi_Operation *op;
 	char ebuf[ BUFSIZ ];
 	int optype = -1;
 
@@ -1527,14 +1526,15 @@ new_passwdPolicy(Slapi_PBlock *pb, char *dn)
 	pwdpolicy = (passwdPolicy *)slapi_ch_calloc(1, sizeof(passwdPolicy));
 
 	if (pb) {
-		slapi_pblock_get( pb, SLAPI_OPERATION, &op);
 		slapi_pblock_get( pb, SLAPI_OPERATION_TYPE, &optype );
 	}
 
-	if (pb && dn && (slapdFrontendConfig->pwpolicy_local == 1)) {
+	if (dn && (slapdFrontendConfig->pwpolicy_local == 1)) {
 		/*  If we're doing an add, COS does not apply yet so we check
 			parents for the pwdpolicysubentry.  We look only for virtual
 			attributes, because real ones are for single-target policy. */
+		/* NGK - is there a way to make this work for non-existent entries when we don't pass in pb?  We'll
+		 * need to do this if we add support for password policy plug-ins. */
 		if (optype == SLAPI_OPERATION_ADD) {
 			char *parentdn = slapi_ch_strdup(dn);
 			char *nextdn = NULL;
@@ -2153,3 +2153,157 @@ locked:
 	return (1);
 
 }
+
+/* The idea here is that these functions could allow us to have password
+ * policy plugins in the future.  The plugins would register callbacks for these
+ * slapi functions that would be used here if any pwpolicy plugin is configured to
+ * be used.  Right now, we just use the normal server password policy code since
+ * we don't have a pwpolicy plugin type. */
+Slapi_PWPolicy *
+slapi_get_pwpolicy(Slapi_DN *dn)
+{
+    return ((Slapi_PWPolicy *)new_passwdPolicy(NULL, slapi_sdn_get_ndn(dn)));
+}
+
+void
+slapi_pwpolicy_free(Slapi_PWPolicy *pwpolicy)
+{
+    delete_passwdPolicy((passwdPolicy **)&pwpolicy);
+}
+
+int
+slapi_pwpolicy_is_expired(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e, time_t *expire_time, int *remaining_grace)
+{
+    int is_expired = 0;
+    time_t now = current_time();
+
+    if (pwpolicy && e) {
+        /* If password expiration is enabled in the policy,
+         * check if the password has expired. */
+        if (pwpolicy->pw_exp == 1) {
+            char *expiration_val = NULL;
+            time_t _expire_time;
+            double diff_t = 0;
+            char *cur_time_str = NULL;
+            time_t cur_time;
+
+            expiration_val = slapi_entry_attr_get_charptr(e, "passwordExpirationTime");
+            if (expiration_val) {
+                _expire_time = parse_genTime(expiration_val);
+
+                cur_time = current_time();
+                cur_time_str = format_genTime(cur_time);
+
+                if ((_expire_time != NO_TIME) && (_expire_time != NOT_FIRST_TIME) &&
+                    ((diff_t = difftime (_expire_time, parse_genTime(cur_time_str))) <= 0)) {
+                    is_expired = 1;
+                }
+
+                if (is_expired) {
+                    if (remaining_grace) {
+                        /* Fill in the number of remaining grace logins */
+                        int grace_attempts = 0;
+
+                        grace_attempts = slapi_entry_attr_get_int(e, "passwordGraceUserTime");
+                        if (pwpolicy->pw_gracelimit > grace_attempts) {
+                            *remaining_grace = pwpolicy->pw_gracelimit - grace_attempts;
+                        } else {
+                            *remaining_grace = 0;
+                        }
+                    }
+                } else if (expire_time) {
+                    /* Fill in the expiration time */
+                    if ((_expire_time != NO_TIME) && (_expire_time != NOT_FIRST_TIME)) {
+                        *expire_time = _expire_time;
+                    } else {
+                        *expire_time = (time_t)0;
+                    }
+                }
+
+                slapi_ch_free_string(&cur_time_str);
+            }
+        } else if (expire_time) {
+            /* Passwords never expire */
+            *expire_time = (time_t)0;
+        }
+    }
+
+    return is_expired;
+}
+
+int
+slapi_pwpolicy_is_locked(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e, time_t *unlock_time)
+{
+    int is_locked = 0;
+
+    if (pwpolicy && e) {
+        /* Check if account is locked */
+        if ( pwpolicy->pw_lockout == 1) {
+            if (slapi_entry_attr_get_uint(e, "passwordRetryCount") >= pwpolicy->pw_maxfailure) {
+                is_locked = 1;
+            }
+        }
+
+        if (is_locked) {
+            /* See if it's time for the account to be unlocked */
+            char *unlock_time_str = NULL;
+            char *cur_time_str = NULL;
+            time_t _unlock_time = (time_t)0;
+            time_t cur_time;
+
+            unlock_time_str = slapi_entry_attr_get_charptr(e, "accountUnlockTime");
+            if (unlock_time_str) {
+                _unlock_time = parse_genTime(unlock_time_str);
+            }
+
+            if ((pwpolicy->pw_unlock == 0) && (_unlock_time == NO_TIME)) {
+                /* Account is locked forever */
+                if (unlock_time) {
+                    *unlock_time = (time_t)0;
+                }
+            } else {
+                cur_time = current_time();
+                cur_time_str = format_genTime(cur_time);
+
+                if (difftime(parse_genTime(cur_time_str), _unlock_time)  < 0) {
+                    /* Account is not due to be unlocked yet.
+                     * Fill in the unlock time. */
+                    if (unlock_time) {
+                        *unlock_time = _unlock_time;
+                    }
+                } else {
+                    /* Account is due to be unlocked */
+                    is_locked = 0;
+                }
+
+                slapi_ch_free_string(&cur_time_str);
+            }
+        }
+    }
+
+    return is_locked;
+}
+
+int
+slapi_pwpolicy_is_reset(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e)
+{
+    int is_reset = 0;
+
+    if (pwpolicy && e) {
+        /* Check if password was reset and needs to be changed */
+        if (pwpolicy->pw_must_change) {
+            char *expiration_val = 0;
+            time_t expire_time = (time_t)0;
+
+            expiration_val = slapi_entry_attr_get_charptr(e, "passwordExpirationTime");
+            if (expiration_val) {
+                expire_time = parse_genTime(expiration_val);
+                if (expire_time == NO_TIME) {
+                    is_reset = 1;
+                }
+            }
+        }
+    }
+
+    return is_reset;
+}
diff --git a/ldap/servers/slapd/pw.h b/ldap/servers/slapd/pw.h
index b911e3e..83f0003 100644
--- a/ldap/servers/slapd/pw.h
+++ b/ldap/servers/slapd/pw.h
@@ -84,7 +84,7 @@ struct pw_scheme *pw_val2scheme( char *val, char **valpwdp, int first_is_default
 int pw_encodevals( Slapi_Value **vals );
 int pw_encodevals_ext( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals );
 int checkPrefix(char *cipher, char *schemaName, char **encrypt);
-struct passwordpolicyarray *new_passwdPolicy ( Slapi_PBlock *pb, char *dn );
+struct passwordpolicyarray *new_passwdPolicy ( Slapi_PBlock *pb, const char *dn );
 void delete_passwdPolicy( struct passwordpolicyarray **pwpolicy);
 
 /* function for checking the values of fine grained password policy attributes */
diff --git a/ldap/servers/slapd/pw_retry.c b/ldap/servers/slapd/pw_retry.c
index 6c31441..573046f 100644
--- a/ldap/servers/slapd/pw_retry.c
+++ b/ldap/servers/slapd/pw_retry.c
@@ -199,15 +199,20 @@ void set_retry_cnt ( Slapi_PBlock *pb, int count) {
 
 Slapi_Entry *get_entry ( Slapi_PBlock *pb, const char *dn)
 {
-    int             search_result = 0;
-    Slapi_Entry     *retentry = NULL;
+	int             search_result = 0;
+	Slapi_Entry     *retentry = NULL;
 	Slapi_DN		sdn;
 
-    if ( dn == NULL ) {
-    	char *t;
-        slapi_pblock_get( pb, SLAPI_TARGET_DN, &t );
+	if ((dn == NULL) && pb) {
+		char *t;
+		slapi_pblock_get( pb, SLAPI_TARGET_DN, &t );
 		dn= t;
-    }
+	}
+
+	if (dn == NULL) {
+		LDAPDebug (LDAP_DEBUG_TRACE, "WARNING: 'get_entry' - no dn specified.\n", 0, 0, 0);
+		goto bail;
+	}
 
 	slapi_sdn_init_dn_byref(&sdn, dn);
 
@@ -215,7 +220,8 @@ Slapi_Entry *get_entry ( Slapi_PBlock *pb, const char *dn)
 		LDAPDebug (LDAP_DEBUG_TRACE, "WARNING: 'get_entry' can't find entry '%s', err %d\n", dn, search_result, 0);
 	}
 	slapi_sdn_done(&sdn);
-    return retentry;
+bail:
+	return retentry;
 }
 
 void pw_apply_mods(const char *dn, Slapi_Mods *mods) 
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 28e10ac..57f7322 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -550,6 +550,8 @@ typedef struct slapi_counter		Slapi_Counter;
  */
 typedef struct slapi_task		Slapi_Task;
 
+typedef struct passwordpolicyarray	Slapi_PWPolicy;
+
 /**
  * Defines a callback used specifically by Slapi_Task structure cancel and
  * destructor functions.
@@ -4896,6 +4898,73 @@ int slapi_pwpolicy_make_response_control (Slapi_PBlock *pb, int seconds, int log
 #define LDAP_PWPOLICY_PWDTOOYOUNG		7
 #define LDAP_PWPOLICY_PWDINHISTORY		8
 
+/*
+ * Password Policy API
+ */
+/**
+ * Gets the password policy object for a given entry
+ *
+ * \param dn The dn of the entry whose password policy object you
+ *        want returned
+ *
+ * \return A pointer to an opaque password policy object
+ * \return \c NULL if there is a problem getting the policy object
+ * \warning The caller should free the returned password policy
+ *          object when finished by calling the slapi_pwpolicy_free()
+ *          function.
+ * \see slapi_pwpolicy_free()
+ */
+Slapi_PWPolicy *slapi_get_pwpolicy(Slapi_DN *dn);
+
+/**
+ * Free a password policy object from memory
+ *
+ * \param pwpolicy The password policy object that you want to free
+ * \see slapi_get_pwpolicy()
+ */
+void slapi_pwpolicy_free(Slapi_PWPolicy *pwpolicy);
+
+/**
+ * Checks a password policy object to see if the password is expired
+ *
+ * \param pwpolicy The password policy object that you want to check
+ * \param e The entry that you want to check
+ * \param expire_time Fills in the expiration time if the password has not expired yet.
+ *        You can pass \c NULL if you are not interested in the expiration time.
+ * \param remaining_grace Fills in the number of grace logins remaining if the password
+ *        has already expired.  You can pass \c NULL if you are not interested in the
+ *        number of remaining grace logins.
+ *
+ * \return \c 1 if the password has expired
+ * \return \c 0 if the password has not expired
+ */
+int slapi_pwpolicy_is_expired(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e, time_t *expire_time, int *remaining_grace);
+
+/**
+ * Checks a password policy to see if an account is locked
+ *
+ * \param pwpolicy The password policy object that you want to check
+ * \param e The entry that you want to check
+ * \param unlock_time Fills in the time the account will be unlocked if the account
+ *        is currently locked.  You can pass \c NULL if you are not interested in the
+ *        unlock time.
+ *
+ * \return \c 1 if the account is locked
+ * \return \c 0 if the account is not locked
+ */
+int slapi_pwpolicy_is_locked(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e, time_t *unlock_time);
+
+/**
+ * Checks a password policy to see if a password has been reset
+ *
+ * \param pwpolicy The password policy object that you want to check
+ * \param e The entry that you want to check
+ *
+ * \return \c 1 if the password has been reset
+ * \return \c 0 if the password has not been reset
+ */
+int slapi_pwpolicy_is_reset(Slapi_PWPolicy *pwpolicy, Slapi_Entry *e);
+
 /**
  * Free an array of strings from memory
  *
-- 
1.7.4.4

