From 2ec25ef8810f15f5aa13ec47b593a18cb96fcbdf Mon Sep 17 00:00:00 2001
From: Kurt Zeilenga <kurt@openldap.org>
Date: Fri, 21 Aug 1998 06:33:42 +0000
Subject: [PATCH] Merged files from branch REGEX_REMOVAL.  Despite name, this
 merge 	adds POSIX RegEx (and removes BSD re_comp/re_exec) support. * POSIX
 RegEx is not currently included in the distribution, however 	we will
 probably add Henry Spencer's REGEX library soon. * ACL Group functionality is
 also included in this merge!

---
 Make-common.dist                          |   3 +
 Make-common.um                            | 200 +++++
 build/Make-append                         |  13 +-
 build/platforms/freebsd-gcc/Make-platform |   1 -
 include/{regex.h => compat_regex.h}       |   0
 include/ldapconfig.h.um                   | 273 +++++++
 libraries/libldap/Make-template           |   4 +-
 libraries/libldap/getfilter.c             |  57 +-
 libraries/libldap/regex.c                 | 905 ---------------------
 libraries/libldbm/Make-template           |   2 +-
 servers/slapd/Make-template               |   9 +-
 servers/slapd/acl.c                       | 388 ++++++---
 servers/slapd/aclparse.c                  | 133 +++-
 servers/slapd/back-ldbm/Make-template     |   6 +-
 servers/slapd/back-ldbm/group.c           |  90 +++
 servers/slapd/back-ldbm/idl.c             |   2 -
 servers/slapd/backend.c                   |  29 +-
 servers/slapd/filterentry.c               |  50 +-
 servers/slapd/init.c                      |   6 -
 servers/slapd/main.c                      |   3 -
 servers/slapd/proto-slap.h                |   7 +-
 servers/slapd/regex.c                     | 908 ----------------------
 servers/slapd/result.c                    |  50 +-
 servers/slapd/slap.h                      |  15 +
 servers/slapd/tools/Make-template         |   2 +-
 tests/data/acl.out.master                 |   3 -
 tests/scripts/acfilter.sh                 |   5 +-
 tests/scripts/test002-populate            |   1 +
 tests/scripts/test006-acls                |   3 +-
 29 files changed, 1096 insertions(+), 2072 deletions(-)
 create mode 100644 Make-common.um
 rename include/{regex.h => compat_regex.h} (100%)
 create mode 100644 include/ldapconfig.h.um
 delete mode 100644 libraries/libldap/regex.c
 create mode 100644 servers/slapd/back-ldbm/group.c
 delete mode 100644 servers/slapd/regex.c

diff --git a/Make-common.dist b/Make-common.dist
index 17a39a9cf7..c45a0b4272 100644
--- a/Make-common.dist
+++ b/Make-common.dist
@@ -232,6 +232,9 @@ LDAP_REVERSE_LOOKUP=-DREVERSE_LOOKUP
 # uncomment this line to enable support for LDAP referrals in libldap
 LDAP_REFERRALS=-DLDAP_REFERRALS
 
+# uncomment this line to enable ACL by groups
+# LDAP_ACLGROUP=-DACLGROUP
+
 # uncomment these lines to enable support for CRYPT passwords in LDBM.
 # LDAP_CRYPT=-DLDAP_CRYPT
 # LDAP_CRYPT_LIB?=
diff --git a/Make-common.um b/Make-common.um
new file mode 100644
index 0000000000..6fa3121e69
--- /dev/null
+++ b/Make-common.um
@@ -0,0 +1,200 @@
+#-----------------------------------------------------------------------------
+# Copyright (c) 1994 Regents of the University of Michigan.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are permitted
+# provided that this notice is preserved and that due credit is given
+# to the University of Michigan at Ann Arbor. The name of the University
+# may not be used to endorse or promote products derived from this
+# software without specific prior written permission. This software
+# is provided ``as is'' without express or implied warranty.
+#
+#       LDAP common Make defines (included in all but top-level Makefile)
+#
+#-----------------------------------------------------------------------------
+
+#############################################################################
+## Edit the following variables to have appropriate values for your system ##
+#############################################################################
+
+#############################################################################
+## LDAP install paths                                                      ##
+#############################################################################
+#
+# by default, everything is installed below INSTROOT
+# servers, config files, etc. are put in ETCDIR
+# include files get put in INCLUDEDIR
+# libraries are put in LIBDIR
+# man pages are put under MANDIR
+# programs end-users will run are put in BINDIR
+#
+INSTROOT=/usr/local
+ETCDIR= $(INSTROOT)/etc
+INCLUDEDIR= $(INSTROOT)/include
+LIBDIR= $(INSTROOT)/lib
+MANDIR= $(INSTROOT)/man
+BINDIR= $(INSTROOT)/bin
+#
+# if you want things to run in a different directory from where they
+# are installed, set this accordingly (this path gets compiled into a
+# few binaries). otherwise, leave it alone.
+RUNTIMEETCDIR= $(ETCDIR)
+
+#############################################################################
+## General compiler options                                                ##
+#############################################################################
+# Passed to every compile (cc or gcc).  This is where you put -O or -g, etc.
+EXTRACFLAGS=-g
+# Passed to every link (ld).  Include -g here if you did in EXTRACFLAGS.
+EXTRALDFLAGS=-g
+
+#############################################################################
+## If you are NOT using Kerberos authentication, you can skip this section.##
+#############################################################################
+#
+# Otherwise, to enable kerberos authentication, uncomment KERBEROS (and
+# AFSKERBEROS if you are running the AFS version of kerberos).  Also
+# uncomment and change the various KRB* lines to point to where the
+# kerberos libraries and include files are installed at your site.
+#
+KERBEROS=-DKERBEROS
+AFSKERBEROS=-DAFSKERBEROS
+KRBINCLUDEFLAG	= -I/usr/local/kerberos/include
+KRBLIBFLAG	= -L/usr/local/kerberos/lib
+KRBLIBS		= -lkrb -ldes
+
+#############################################################################
+## ISODE is required ONLY to build the ldap <-> X.500 server (ldapd)       ##
+## If you don't want to build it, you can skip this section.               ##
+#############################################################################
+#
+# To build the ldap server, uncomment the HAVEISODE line,
+# and the section describing build settings for your version of isode.
+#
+HAVEISODE = yes
+# If you compiled ISODE with TURBO_DISK defined, uncomment this
+#ISODETURBOLIBS	= -lgdbm
+# uncomment these to have ldapd "pretty print" protocol elements w/debugging
+PEPSY_DUMP=-DPEPSY_DUMP
+PEPSY=/usr/local/ic/bin/pepsy
+# uncommment this line to have ldapd load PP syntax handlers
+# you'll also need to add -lpp to ISODEBASELIBS below
+#LDAP_USE_PP=-DLDAP_USE_PP
+# uncomment NO_SETPROCTITLE to have ldapd NOT change its title
+#NO_SETPROCTITLE=-DNOSETPROCTITLE
+#
+# ISODE Consortium release build settings
+# You should change the next line so that ICRELEASE matches the (integer)
+# version number of whatever IC release you have, e.g. 1, 2, or 3 and
+# also uncomment the next 5 lines.
+ICRELEASE=-DICRELEASE=2
+ISODEPACKAGE=-DISODEPACKAGE=IC
+ISODEINCLUDEFLAG= -I/usr/local/ic/include
+ISODELIBFLAG	= -L/usr/local/ic/lib
+ISODEBASELIBS	= -lisode
+#
+# Freely available ISODE 8.0 release build settings (uncomment the next 4 lines)
+#ISODEPACKAGE=-DISODEPACKAGE
+#ISODEINCLUDEFLAG= -I/usr/local/isode/include
+#ISODELIBFLAG	= -L/usr/local/isode/lib
+#ISODEBASELIBS	= -ldsap -lisode
+#
+# NEXOR ISODE release build settings (uncomment the next 4 lines)
+#ISODEPACKAGE=-DISODEPACKAGE=XT
+#ISODEINCLUDEFLAG= -I/usr/include/isode
+#ISODELIBFLAG	= -L/usr/local/lib -L/usr/sunlink/osi/lib
+#ISODEBASELIBS	= -lxtpp -lresolv -lxtdsap -lxtisode -losi
+
+#############################################################################
+## If you don't want to run slapd, skip this section.                      ##
+#############################################################################
+#
+# To build slapd (the stand-alone ldap daemon), uncomment the MAKESLAPD
+# line and select the SLAPD_BACKENDS you want to use. If you enable the
+# LDBM backend, also select one of the LDBM backends.
+MAKESLAPD= yes
+#
+# remove the defines for backends you don't want to enable 
+SLAPD_BACKENDS= -DLDAP_LDBM -DLDAP_SHELL -DLDAP_PASSWD
+#
+# If you have included -DLDAP_LDBM in the SLAPD_BACKENDS line you need
+# to specify which low-level database package to use.  There are
+# four choices: Berkeley db b-tree, Berkeley db hash, GNU dbm, or ndbm.
+#
+# berkeley db btree package
+LDBMBACKEND=-DLDBM_USE_DBBTREE
+LDBMINCLUDE=-I/usr/local/include
+LDBMLIB=-ldb
+# berkeley db hash package
+#LDBMBACKEND=-DLDBM_USE_DBHASH
+#LDBMINCLUDE=-I/usr/local/include
+#LDBMLIB=-ldb
+# gnu dbm (gdbm)
+#LDBMBACKEND=-DLDBM_USE_GDBM
+#LDBMINCLUDE=-I/usr/local/include
+#LDBMLIB=-lgdbm
+# standard unix ndbm
+#LDBMBACKEND=-DLDBM_USE_NDBM
+#
+# if you want to use a non-default threads package change these lines
+#THREADS=-DNO_THREADS
+#THREADSLIB=
+
+#############################################################################
+## The following options are used by the xax500 client.  If you haven't    ##
+## retrieved the xax500 source and dropped it into the "clients"           ##
+## directory, you can skip this section.                                   ##
+#############################################################################
+#
+# location of your X include files
+#XINCLUDES= -I/usr/local/X11/include
+#
+# location of your X libraries
+#XLIBDIRS=-L/usr/local/X11/lib
+#
+# include any extra X libraries you need here
+# the following works with sunos 4 and X11R5
+#XLIBS = $(XLIBDIRS) -lXm -lXt -lX11
+# the following has been known to work with Solaris 2.4 and X11R6
+#XLIBS = $(XLIBDIRS) -lXm -lXext -lSM -lICE -lXpm -lXt -lX11
+
+#############################################################################
+## If you don't want to do auto-translation of character sets, skip this   ##
+#############################################################################
+#
+# Otherwise, uncomment this line and set the following options.
+#STR_TRANSLATION=-DSTR_TRANSLATION
+# 
+# remove the defines for LDAP client library T.61 character translation
+# you do not need.  If you use LDAP_CHARSET_8859, replace the '1' in "88591"
+# with the number of the particular character set you use.  E.g., use "88594"
+# if you use the ISO 8859-4 chracter set.
+#LIBLDAP_CHARSETS=-DLDAP_CHARSET_8859="88591"
+# 
+# uncomment one these lines to enable automatic T.61 translation by default
+#LIBLDAP_DEF_CHARSET=-DLDAP_DEFAULT_CHARSET=LDAP_CHARSET_8859
+
+#############################################################################
+## General options                                                         ##
+#############################################################################
+# uncomment this line to enable debugging code (a good idea)
+LDAP_DEBUG=-DLDAP_DEBUG
+
+# uncomment this line to turn on a few U of Michigan specific things
+UOFM=-DUOFM
+
+# uncomment this line to delete a few printfs in the lber and ldap libraries.
+#NO_USERINTERFACE=-DNO_USERINTERFACE
+
+# uncomment this line to include Connectionless LDAP support
+CLDAP=-DCLDAP
+
+# uncomment this line to eliminate local caching support in the libldap
+#NO_CACHE=-DNO_CACHE
+
+# uncomment this line to enable support for LDAP referrals in libldap
+LDAP_REFERRALS=-DLDAP_REFERRALS
+
+# uncomment this line to use soundex for approximate matches in slapd.
+# the default is to use the metaphone algorithm.
+#PHONETIC=-DSOUNDEX
diff --git a/build/Make-append b/build/Make-append
index a841639e79..7998a98dcf 100644
--- a/build/Make-append
+++ b/build/Make-append
@@ -21,15 +21,18 @@
 # DEFS are included in CFLAGS
 DEFS	= $(PLATFORMCFLAGS) $(LDAP_DEBUG) $(KERBEROS) $(AFSKERBEROS) \
 		$(UOFM) $(UOFA) $(NO_USERINTERFACE) $(CLDAP) $(NO_CACHE) \
-		$(LDAP_REVERSE_LOOKUP) \
 		$(LDAP_REFERRALS) $(LDAP_DNS) $(STR_TRANSLATION) \
-		$(LIBLDAP_CHARSETS) $(LIBLDAP_DEF_CHARSET) \
-		$(SLAPD_BACKENDS) $(LDBMBACKEND) $(LDBMINCLUDE) $(PHONETIC)
+		$(LIBLDAP_CHARSETS) $(LIBLDAP_DEF_CHARSET)
+
+# LDBMDEFS are included by servers (though only slapd only cares)
+# and libldbm 
+LDBMDEFS = $(SLAPD_BACKENDS) $(LDBMBACKEND) $(LDBMINCLUDE)
 
 # SERVERDEFS are added to server builds CFLAGS (in addition to DEFS)
 SERVERDEFS	= $(ISODEPACKAGE) $(ICRELEASE) $(LDAP_USE_PP) \
-			$(NO_SETPROCTITLE) $(PEPSY_DUMP)
-
+			$(NO_SETPROCTITLE) $(PEPSY_DUMP) \
+			$(LDAP_REVERSE_LOOKUP) $(LDAP_CRYPT) $(LDAP_ACLGROUP) \
+			$(PHONETIC) $(LDBMDEFS)
 #
 # ISODELIBS are used in server/ldapd builds
 #
diff --git a/build/platforms/freebsd-gcc/Make-platform b/build/platforms/freebsd-gcc/Make-platform
index 25b326070b..f5750b49ce 100644
--- a/build/platforms/freebsd-gcc/Make-platform
+++ b/build/platforms/freebsd-gcc/Make-platform
@@ -44,4 +44,3 @@ LDAP_CRYPT_LIB= -lcrypt
 # -------------------------------------------------------------------------
 CC	= gcc
 PLATFORMCFLAGS=	-Dfreebsd
-PLATFORMLIBS=-lcompat
diff --git a/include/regex.h b/include/compat_regex.h
similarity index 100%
rename from include/regex.h
rename to include/compat_regex.h
diff --git a/include/ldapconfig.h.um b/include/ldapconfig.h.um
new file mode 100644
index 0000000000..ecaf918af0
--- /dev/null
+++ b/include/ldapconfig.h.um
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/*
+ * config.h for LDAP -- edit this file to customize LDAP client behavior.
+ * NO platform-specific definitions should be placed in this file.
+ * Note that this is NOT used by the LDAP or LBER libraries.
+ */
+
+/*
+ * SHARED DEFINITIONS - things you should change
+ */
+	/* default ldap host */
+#define LDAPHOST	"ldap.itd.umich.edu"
+	/* default place to start searching */
+#define DEFAULT_BASE	"o=University of Michigan, c=US"
+
+/*********************************************************************
+ *                                                                   *
+ * You probably do not need to edit anything below this point        *
+ *                                                                   *
+ *********************************************************************/
+
+/*
+ * SHARED DEFINITIONS - other things you can change
+ */
+	/* default attribute to use when sorting entries, NULL => sort by DN */
+#define SORT_ATTR	NULL
+	/* default count of DN components to show in entry displays */
+#define DEFAULT_RDNCOUNT	2
+	/* default config file locations */
+#define FILTERFILE	"%ETCDIR%/ldapfilter.conf"
+#define TEMPLATEFILE	"%ETCDIR%/ldaptemplates.conf"
+#define SEARCHFILE	"%ETCDIR%/ldapsearchprefs.conf"
+#define FRIENDLYFILE	"%ETCDIR%/ldapfriendly"
+
+/*
+ * FINGER DEFINITIONS
+ */
+	/* who to bind as */
+#define FINGER_BINDDN		"cn=finger, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* where to search */
+#define FINGER_BASE		DEFAULT_BASE
+	/* banner to print */
+#define FINGER_BANNER		"X.500 Finger Service...\r\n"
+	/* who to report errors to */
+#define FINGER_ERRORS		"x500@umich.edu"
+	/* what to say if no matches are found */
+#define FINGER_NOMATCH		\
+"Search failed to find anything.  Currently, this service contains\r\n\
+information only on University of Michigan faculty, staff, and students.\r\n\
+Other likely places to finger are:\r\n\
+        um.cc.umich.edu\r\n\
+        azure.engin.umich.edu\r\n"
+	/* what to say if the service may be unavailable */
+#define FINGER_UNAVAILABLE	\
+"The X.500 service may be temporarily unavailable.\r\n\
+Please try again later.\r\n"
+	/* printed if a match has no email address - for disptmp default */
+#define FINGER_NOEMAIL1	"None registered in this service.  Try fingering at:"
+#define FINGER_NOEMAIL2	"um.cc.umich.edu or azure.engin.umich.edu"
+#define FINGER_NOEMAIL	{ FINGER_NOEMAIL1, FINGER_NOEMAIL2, NULL }
+	/* maximum number of matches returned */
+#define FINGER_SIZELIMIT	50
+	/* max number of hits displayed in full before a list is presented */
+#define FINGER_LISTLIMIT	1
+	/* what to exec for "finger @host" */
+#define FINGER_CMD		"/usr/ucb/finger"
+	/* how to treat aliases when searching */
+#define FINGER_DEREF		LDAP_DEREF_FINDING
+	/* attribute to use when sorting results */
+#define FINGER_SORT_ATTR	SORT_ATTR
+	/* enable ufn support */
+#define FINGER_UFN
+	/* timeout for searches */
+#define FINGER_TIMEOUT		60
+	/* number of DN components to show in entry displays */
+#define FINGER_RDNCOUNT		DEFAULT_RDNCOUNT	
+
+/*
+ * GO500 GOPHER GATEWAY DEFINITIONS
+ */
+	/* who to bind as */
+#define GO500_BINDDN	"cn=go500, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* where to search */
+#define GO500_BASE	DEFAULT_BASE
+	/* port on which to listen */
+#define GO500_PORT	5555
+	/* how to handle aliases */
+#define GO500_DEREF	LDAP_DEREF_FINDING
+	/* attribute to use when sorting results */
+#define GO500_SORT_ATTR	SORT_ATTR
+	/* timeout for searches */
+#define GO500_TIMEOUT	60
+	/* enable ufn support */
+#define GO500_UFN
+	/*
+	 * only set and uncomment this if your hostname() does not return
+	 * a fully qualified hostname
+	 */
+/* #define GO500_HOSTNAME	"fully.qualified.hostname.here" */
+	/* number of DN components to show in entry displays */
+#define GO500_RDNCOUNT		DEFAULT_RDNCOUNT	
+
+/*
+ * GO500GW GOPHER GATEWAY DEFINITIONS
+ */
+	/* who to bind as */
+#define GO500GW_BINDDN		"cn=go500gw, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* where the helpfile lives */
+#define GO500GW_HELPFILE	"%ETCDIR%/go500gw.help"
+	/* port on which to listen */
+#define GO500GW_PORT		7777
+	/* timeout on all searches */
+#define GO500GW_TIMEOUT		180
+	/* enable ufn support */
+#define GO500GW_UFN
+	/* attribute to use when sorting results */
+#define GO500GW_SORT_ATTR	SORT_ATTR
+	/*
+	 * only set and uncomment this if your hostname() does not return
+	 * a fully qualified hostname
+	 */
+/* #define GO500GW_HOSTNAME	"fully.qualified.hostname.here" */
+	/* number of DN components to show in entry displays */
+#define GO500GW_RDNCOUNT	DEFAULT_RDNCOUNT	
+
+/*
+ * RCPT500 MAIL RESPONDER GATEWAY DEFINITIONS
+ */
+	/* who to bind as */
+#define RCPT500_BINDDN		"cn=Rcpt500, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* where the helpfile lives */
+#define RCPT500_HELPFILE	"%ETCDIR%/rcpt500.help"
+	/* maximum number of matches returned */
+#define RCPT500_SIZELIMIT	50
+	/* address replies will appear to come from */
+#define RCPT500_FROM		"\"X.500 Query Program\" <X500-Query>"
+	/* command that will accept an RFC822 message text on standard
+	   input, and send it.  sendmail -t does this nicely. */
+#define RCPT500_PIPEMAILCMD	"/usr/lib/sendmail -t"
+        /* where to search */
+#define RCPT500_BASE             DEFAULT_BASE
+	/* attribute to use when sorting results */
+#define RCPT500_SORT_ATTR	SORT_ATTR
+	/* max number of hits displayed in full before a list is presented */
+#define RCPT500_LISTLIMIT	3
+	/* enable ufn support */
+#define RCPT500_UFN
+	/* number of DN components to show in entry displays */
+#define RCPT500_RDNCOUNT	DEFAULT_RDNCOUNT	
+
+/*
+ * LDAPSEARCH TOOL
+ */
+	/* who to bind as */
+#define LDAPSEARCH_BINDDN	"cn=LDAP Search Tool, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* search base */
+#define LDAPSEARCH_BASE		DEFAULT_BASE
+
+/*
+ * LDAPMODIFY TOOL
+ */
+	/* who to bind as */
+#define LDAPMODIFY_BINDDN	"cn=Manager, o=University of Michigan, c=US"
+	/* search base */
+#define LDAPMODIFY_BASE		DEFAULT_BASE
+
+/*
+ * LDAPDELETE TOOL
+ */
+	/* who to bind as */
+#define LDAPDELETE_BINDDN	"cn=Manager, o=University of Michigan, c=US"
+	/* search base */
+#define LDAPDELETE_BASE		DEFAULT_BASE
+
+/*
+ * LDAPMODRDN TOOL
+ */
+	/* who to bind as */
+#define LDAPMODRDN_BINDDN	"cn=Manager, o=University of Michigan, c=US"
+	/* search base */
+#define LDAPMODRDN_BASE		DEFAULT_BASE
+
+/*
+ * MAIL500 MAILER DEFINITIONS
+ */
+	/* who to bind as */
+#define MAIL500_BINDDN		"cn=mail500, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* max number of ambiguous matches reported */
+#define MAIL500_MAXAMBIGUOUS	10
+	/* max subscribers allowed (size limit when searching for them ) */
+#define MAIL500_MAXGROUPMEMBERS	LDAP_NO_LIMIT
+	/* timeout for all searches */
+#define MAIL500_TIMEOUT		180
+	/* sendmail location - mail500 needs to exec this */
+#define MAIL500_SENDMAIL	"/usr/lib/sendmail"
+
+/*
+ * UD DEFINITIONS
+ */
+	/* ud configuration file */
+#define UD_CONFIG_FILE		"%ETCDIR%/ud.conf"
+	/* default editor */
+#define UD_DEFAULT_EDITOR	"/usr/ucb/vi"
+	/* default bbasename of user config file */
+#define UD_USER_CONFIG_FILE	".udrc"
+	/* default user to bind as */
+#define UD_BINDDN		"cn=ud, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* default password to bind with */
+#define UD_PASSWD		NULL
+	/* default search base */
+#define UD_BASE			DEFAULT_BASE
+	/* default base where groups are created */
+#define UD_WHERE_GROUPS_ARE_CREATED	"ou=User Groups, ou=Groups, o=University of Michigan, c=US"
+	/* default base below which all groups live */
+#define UD_WHERE_ALL_GROUPS_LIVE	"ou=Groups, o=University of Michigan, c=US"
+
+/*
+ * FAX500 DEFINITIONS
+ */
+	/* what to bind as */
+#define FAX_BINDDN	"cn=mail500, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* how long to wait for searches */
+#define FAX_TIMEOUT		180
+	/* maximum number of ambiguous matches reported */
+#define FAX_MAXAMBIGUOUS	10
+	/* maximum number of members allowed */
+#define FAX_MAXMEMBERS		LDAP_NO_LIMIT
+	/* program to send mail */
+#define FAX_SENDMAIL		"/usr/lib/sendmail"
+
+/*
+ * RP500 DEFINITIONS
+ */
+	/* what to bind as */
+#define RP_BINDDN	"cn=rp500, ou=Miscellaneous Servers, o=University of Michigan, c=US"
+	/* prefix to add to non-fully-qualified numbers */
+#define RP_PHONEPREFIX	"1313"
+
+/*
+ * SLAPD DEFINITIONS
+ */
+	/* location of the default slapd config file */
+#define SLAPD_DEFAULT_CONFIGFILE	"%ETCDIR%/slapd.conf"
+	/* default sizelimit on number of entries from a search */
+#define SLAPD_DEFAULT_SIZELIMIT		500
+	/* default timelimit to spend on a search */
+#define SLAPD_DEFAULT_TIMELIMIT		3600
+	/* location of the slapd pid file */
+#define SLAPD_PIDFILE			"%ETCDIR%/slapd.pid"
+	/* location of the slapd args file */
+#define SLAPD_ARGSFILE			"%ETCDIR%/slapd.args"
+	/* dn of the special "monitor" entry */
+#define SLAPD_MONITOR_DN		"cn=monitor"
+	/* dn of the special "config" entry */
+#define SLAPD_CONFIG_DN			"cn=config"
+	/* minimum max ids that a single index entry can map to in ldbm */
+#define SLAPD_LDBM_MIN_MAXIDS		4000
+
+#endif /* _CONFIG_H */
diff --git a/libraries/libldap/Make-template b/libraries/libldap/Make-template
index 41c7b35c3d..a100e22825 100644
--- a/libraries/libldap/Make-template
+++ b/libraries/libldap/Make-template
@@ -17,13 +17,13 @@ LDAPSRC	= ../..
 
 SRCS	= bind.c open.c result.c error.c compare.c search.c \
 	modify.c add.c modrdn.c delete.c abandon.c ufn.c cache.c \
-	getfilter.c regex.c sbind.c kbind.c unbind.c friendly.c cldap.c \
+	getfilter.c sbind.c kbind.c unbind.c friendly.c cldap.c \
 	free.c disptmpl.c srchpref.c dsparse.c tmplout.c sort.c \
 	getdn.c getentry.c getattr.c getvalues.c addentry.c \
 	request.c getdxbyname.c os-ip.c url.c charset.c
 OBJS	= bind.o open.o result.o error.o compare.o search.o \
 	modify.o add.o modrdn.o delete.o abandon.o ufn.o cache.o \
-	getfilter.o regex.o sbind.o kbind.o unbind.o friendly.o cldap.o \
+	getfilter.o sbind.o kbind.o unbind.o friendly.o cldap.o \
 	free.o disptmpl.o srchpref.o dsparse.o tmplout.o sort.o \
 	getdn.o getentry.o getattr.o getvalues.o addentry.o \
 	request.o getdxbyname.o os-ip.o url.o charset.o
diff --git a/libraries/libldap/getfilter.c b/libraries/libldap/getfilter.c
index e9ebc6aa29..a2f9267968 100644
--- a/libraries/libldap/getfilter.c
+++ b/libraries/libldap/getfilter.c
@@ -10,13 +10,13 @@ static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of
 #endif
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#if defined(NeXT)
+#include <sys/types.h>
 #include <regex.h>
-#endif
+
 #ifdef MACOS
-#include <stdlib.h>
 #include "macos.h"
 #else /* MACOS */
 #ifdef DOS
@@ -25,7 +25,6 @@ static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of
 #else /* DOS */
 #include <sys/types.h>
 #include <sys/file.h>
-#include <stdlib.h>
 #include <sys/errno.h>
 #ifndef VMS
 #include <unistd.h>
@@ -35,7 +34,6 @@ static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of
 
 #include "lber.h"
 #include "ldap.h"
-#include "regex.h"
 
 #ifdef NEEDPROTOS
 static int break_into_words( char *str, char *delims, char ***wordsp );
@@ -49,7 +47,6 @@ void free_strarray();
 
 #if !defined( MACOS ) && !defined( DOS )
 extern int	errno;
-extern char	*re_comp();
 #endif
 
 #define FILT_MAX_LINE_LEN	1024
@@ -107,8 +104,10 @@ ldap_init_getfilter_buf( char *buf, long buflen )
     LDAPFiltDesc	*lfdp;
     LDAPFiltList	*flp, *nextflp;
     LDAPFiltInfo	*fip, *nextfip;
-    char		*tag, **tok;
-    int			tokcnt, i;
+    char			*tag, **tok;
+    int				tokcnt, i;
+	int				rc;
+	regex_t			re;
 
     if (( lfdp = (LDAPFiltDesc *)calloc( 1, sizeof( LDAPFiltDesc))) == NULL ) {
 	return( NULL );
@@ -138,11 +137,13 @@ ldap_init_getfilter_buf( char *buf, long buflen )
 	    }
 	    nextflp->lfl_tag = strdup( tag );
 	    nextflp->lfl_pattern = tok[ 0 ];
-	    if ( re_comp( nextflp->lfl_pattern ) != NULL ) {
+	    if ( (rc = regcomp( &re, nextflp->lfl_pattern, 0 )) != 0 ) {
 #ifndef NO_USERINTERFACE
+		char error[512];
+		regerror(rc, &re, error, sizeof(error));
 		ldap_getfilter_free( lfdp );
-		fprintf( stderr, "bad regular expresssion %s\n",
-			nextflp->lfl_pattern );
+		fprintf( stderr, "bad regular expresssion %s, %s\n",
+			nextflp->lfl_pattern, error );
 #if !defined( MACOS ) && !defined( DOS )
 		errno = EINVAL;
 #endif
@@ -150,6 +151,7 @@ ldap_init_getfilter_buf( char *buf, long buflen )
 		free_strarray( tok );
 		return( NULL );
 	    }
+		regfree(&re);
 		
 	    nextflp->lfl_delims = tok[ 1 ];
 	    nextflp->lfl_ilist = NULL;
@@ -247,6 +249,8 @@ LDAPFiltInfo *
 ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat, char *value )
 {
     LDAPFiltList	*flp;
+	int				rc;
+	regex_t			re;
 
     if ( lfdp->lfd_curvalcopy != NULL ) {
 	free( lfdp->lfd_curvalcopy );
@@ -256,13 +260,30 @@ ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat, char *value )
     lfdp->lfd_curval = value;
     lfdp->lfd_curfip = NULL;
 
-    for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = flp->lfl_next ) {
-	if ( re_comp( tagpat ) == NULL && re_exec( flp->lfl_tag ) == 1
-		&& re_comp( flp->lfl_pattern ) == NULL
-		&& re_exec( lfdp->lfd_curval ) == 1 ) {
-	    lfdp->lfd_curfip = flp->lfl_ilist;
-	    break;
-	}
+	for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = flp->lfl_next ) {
+		/* compile tagpat, continue if we fail */
+		if (regcomp(&re, tagpat, 0) != 0)
+			continue;
+
+		/* match tagpatern and tag, continue if we fail */
+		rc = regexec(&re, flp->lfl_tag, 0, NULL, 0);
+		regfree(&re);
+		if (rc != 0)
+			continue;
+
+		/* compile flp->ifl_pattern, continue if we fail */
+		if (regcomp(&re, flp->lfl_pattern, 0) != 0)
+			continue;
+
+		/* match ifl_pattern and lfd_curval, continue if we fail */
+		rc = regexec(&re, lfdp->lfd_curval, 0, NULL, 0);
+		regfree(&re);
+		if (rc != 0)
+			continue;
+
+		/* we successfully compiled both patterns and matched both values */
+		lfdp->lfd_curfip = flp->lfl_ilist;
+		break;
     }
 
     if ( lfdp->lfd_curfip == NULL ) {
diff --git a/libraries/libldap/regex.c b/libraries/libldap/regex.c
deleted file mode 100644
index a0a70b4539..0000000000
--- a/libraries/libldap/regex.c
+++ /dev/null
@@ -1,905 +0,0 @@
-#include "portable.h"
-
-#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
-#include "regex.h"
-
-/*
- * regex - Regular expression pattern matching  and replacement
- *
- * By:  Ozan S. Yigit (oz)
- *      Dept. of Computer Science
- *      York University
- *
- * These routines are the PUBLIC DOMAIN equivalents of regex
- * routines as found in 4.nBSD UN*X, with minor extensions.
- *
- * These routines are derived from various implementations found
- * in software tools books, and Conroy's grep. They are NOT derived
- * from licensed/restricted software.
- * For more interesting/academic/complicated implementations,
- * see Henry Spencer's regexp routines, or GNU Emacs pattern
- * matching module.
- *
- * Vendor Modification history:
- *
- * Revision 1.12  1996/04/25  16:20:59  mcs
- * make re_exec() match "" with ".*" and similar patterns
- * hopefully this change doesn't break anything else!
- *
- * Revision 1.11  1994/12/14  21:33:45  mcs
- * use new NEED_BSDREGEX
- * fix pmatch() prototype
- *
- * Revision 1.10  1994/12/12  18:16:39  mcs
- * use on NetBSD
- *
- * Revision 1.9  1994/11/15  19:16:35  mcs
- * add (CHAR) cast to make VisualC++ happy
- *
- * Revision 1.8  1994/11/08  21:14:32  mcs
- * WIN32 changes
- *
- * Revision 1.7  1994/07/23  19:51:24  mcs
- * use ANSI-style inline function parameters
- *
- * Revision 1.6  1993/10/18  01:52:32  tim
- * include for VMS
- *
- * Revision 1.5  1993/09/28  21:37:54  mcs
- * HP/UX needs the regex we include (not in its libc)
- *
- * Revision 1.4  1993/08/27  15:59:52  mcs
- * use CHAR for deftab
- *
- * Revision 1.3  1993/08/27  15:49:47  mcs
- * added missing 0 to octal constants
- * use unsigned char for CHAR under DOS
- *
- * Revision 1.2  1993/08/27  14:57:48  mcs
- * add proto. for pmatch
- *
- * Revision 1.1  1993/08/18  21:20:02  mcs
- * Initial revision
- *
- * Revision 1.4  1991/10/17  03:56:42  oz
- * miscellaneous changes, small cleanups etc.
- *
- * Revision 1.3  1989/04/01  14:18:09  oz
- * Change all references to a dfa: this is actually an nfa.
- *
- * Revision 1.2  88/08/28  15:36:04  oz
- * Use a complement bitmap to represent NCL.
- * This removes the need to have seperate 
- * code in the pmatch case block - it is 
- * just CCL code now.
- * 
- * Use the actual CCL code in the CLO
- * section of pmatch. No need for a recursive
- * pmatch call.
- * 
- * Use a bitmap table to set char bits in an
- * 8-bit chunk.
- * 
- * Interfaces:
- *      re_comp:        compile a regular expression into a NFA.
- *
- *			char *re_comp(s)
- *			char *s;
- *
- *      re_exec:        execute the NFA to match a pattern.
- *
- *			int re_exec(s)
- *			char *s;
- *
- *	re_modw		change re_exec's understanding of what a "word"
- *			looks like (for \< and \>) by adding into the
- *			hidden word-syntax table.
- *
- *			void re_modw(s)
- *			char *s;
- *
- *      re_subs:	substitute the matched portions in a new string.
- *
- *			int re_subs(src, dst)
- *			char *src;
- *			char *dst;
- *
- *	re_fail:	failure routine for re_exec.
- *
- *			void re_fail(msg, op)
- *			char *msg;
- *			char op;
- *  
- * Regular Expressions:
- *
- *      [1]     char    matches itself, unless it is a special
- *                      character (metachar): . \ [ ] * + ^ $
- *
- *      [2]     .       matches any character.
- *
- *      [3]     \       matches the character following it, except
- *			when followed by a left or right round bracket,
- *			a digit 1 to 9 or a left or right angle bracket. 
- *			(see [7], [8] and [9])
- *			It is used as an escape character for all 
- *			other meta-characters, and itself. When used
- *			in a set ([4]), it is treated as an ordinary
- *			character.
- *
- *      [4]     [set]   matches one of the characters in the set.
- *                      If the first character in the set is "^",
- *                      it matches a character NOT in the set, i.e. 
- *			complements the set. A shorthand S-E is 
- *			used to specify a set of characters S upto 
- *			E, inclusive. The special characters "]" and 
- *			"-" have no special meaning if they appear 
- *			as the first chars in the set.
- *                      examples:        match:
- *
- *                              [a-z]    any lowercase alpha
- *
- *                              [^]-]    any char except ] and -
- *
- *                              [^A-Z]   any char except uppercase
- *                                       alpha
- *
- *                              [a-zA-Z] any alpha
- *
- *      [5]     *       any regular expression form [1] to [4], followed by
- *                      closure char (*) matches zero or more matches of
- *                      that form.
- *
- *      [6]     +       same as [5], except it matches one or more.
- *
- *      [7]             a regular expression in the form [1] to [10], enclosed
- *                      as \(form\) matches what form matches. The enclosure
- *                      creates a set of tags, used for [8] and for
- *                      pattern substution. The tagged forms are numbered
- *			starting from 1.
- *
- *      [8]             a \ followed by a digit 1 to 9 matches whatever a
- *                      previously tagged regular expression ([7]) matched.
- *
- *	[9]	\<	a regular expression starting with a \< construct
- *		\>	and/or ending with a \> construct, restricts the
- *			pattern matching to the beginning of a word, and/or
- *			the end of a word. A word is defined to be a character
- *			string beginning and/or ending with the characters
- *			A-Z a-z 0-9 and _. It must also be preceded and/or
- *			followed by any character outside those mentioned.
- *
- *      [10]            a composite regular expression xy where x and y
- *                      are in the form [1] to [10] matches the longest
- *                      match of x followed by a match for y.
- *
- *      [11]	^	a regular expression starting with a ^ character
- *		$	and/or ending with a $ character, restricts the
- *                      pattern matching to the beginning of the line,
- *                      or the end of line. [anchors] Elsewhere in the
- *			pattern, ^ and $ are treated as ordinary characters.
- *
- *
- * Acknowledgements:
- *
- *	HCR's Hugh Redelmeier has been most helpful in various
- *	stages of development. He convinced me to include BOW
- *	and EOW constructs, originally invented by Rob Pike at
- *	the University of Toronto.
- *
- * References:
- *              Software tools			Kernighan & Plauger
- *              Software tools in Pascal        Kernighan & Plauger
- *              Grep [rsx-11 C dist]            David Conroy
- *		ed - text editor		Un*x Programmer's Manual
- *		Advanced editing on Un*x	B. W. Kernighan
- *		RegExp routines			Henry Spencer
- *
- * Notes:
- *
- *	This implementation uses a bit-set representation for character
- *	classes for speed and compactness. Each character is represented 
- *	by one bit in a 128-bit block. Thus, CCL always takes a 
- *	constant 16 bytes in the internal nfa, and re_exec does a single
- *	bit comparison to locate the character in the set.
- *
- * Examples:
- *
- *	pattern:	foo*.*
- *	compile:	CHR f CHR o CLO CHR o END CLO ANY END END
- *	matches:	fo foo fooo foobar fobar foxx ...
- *
- *	pattern:	fo[ob]a[rz]	
- *	compile:	CHR f CHR o CCL bitset CHR a CCL bitset END
- *	matches:	fobar fooar fobaz fooaz
- *
- *	pattern:	foo\\+
- *	compile:	CHR f CHR o CHR o CHR \ CLO CHR \ END END
- *	matches:	foo\ foo\\ foo\\\  ...
- *
- *	pattern:	\(foo\)[1-3]\1	(same as foo[1-3]foo)
- *	compile:	BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
- *	matches:	foo1foo foo2foo foo3foo
- *
- *	pattern:	\(fo.*\)-\1
- *	compile:	BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
- *	matches:	foo-foo fo-fo fob-fob foobar-foobar ...
- */
-
-#define MAXNFA  1024
-#define MAXTAG  10
-
-#define OKP     1
-#define NOP     0
-
-#define CHR     1
-#define ANY     2
-#define CCL     3
-#define BOL     4
-#define EOL     5
-#define BOT     6
-#define EOT     7
-#define BOW	8
-#define EOW	9
-#define REF     10
-#define CLO     11
-
-#define END     0
-
-/*
- * The following defines are not meant to be changeable.
- * They are for readability only.
- */
-#define MAXCHR	128
-#define CHRBIT	8
-#define BITBLK	MAXCHR/CHRBIT
-#define BLKIND	0170
-#define BITIND	07
-
-#define ASCIIB	0177
-
-#if defined( DOS ) || defined( _WIN32 )
-typedef unsigned char CHAR;
-#else /* DOS */
-typedef /*unsigned*/ char CHAR;
-#endif /* DOS */
-
-static int  tagstk[MAXTAG];             /* subpat tag stack..*/
-static CHAR nfa[MAXNFA];		/* automaton..       */
-static int  sta = NOP;               	/* status of lastpat */
-
-static CHAR bittab[BITBLK];		/* bit table for CCL */
-					/* pre-set bits...   */
-static CHAR bitarr[] = {1,2,4,8,16,32,64,128};
-
-static void
-chset(CHAR c)
-{
-	bittab[((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
-}
-
-#define badpat(x)	(*nfa = END, x)
-#define store(x)	*mp++ = x
- 
-char *     
-re_comp( char *pat )
-{
-	register char *p;               /* pattern pointer   */
-	register CHAR *mp=nfa;          /* nfa pointer       */
-	register CHAR *lp;              /* saved pointer..   */
-	register CHAR *sp=nfa;          /* another one..     */
-
-	register int tagi = 0;          /* tag stack index   */
-	register int tagc = 1;          /* actual tag count  */
-
-	register int n;
-	register CHAR mask;		/* xor mask -CCL/NCL */
-	int c1, c2;
-		
-	if (!pat || !*pat)
-		if (sta)
-			return 0;
-		else
-			return badpat("No previous regular expression");
-	sta = NOP;
-
-	for (p = pat; *p; p++) {
-		lp = mp;
-		switch(*p) {
-
-		case '.':               /* match any char..  */
-			store(ANY);
-			break;
-
-		case '^':               /* match beginning.. */
-			if (p == pat)
-				store(BOL);
-			else {
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		case '$':               /* match endofline.. */
-			if (!*(p+1))
-				store(EOL);
-			else {
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		case '[':               /* match char class..*/
-			store(CCL);
-
-			if (*++p == '^') {
-				mask = 0377;	
-				p++;
-			}
-			else
-				mask = 0;
-
-			if (*p == '-')		/* real dash */
-				chset(*p++);
-			if (*p == ']')		/* real brac */
-				chset(*p++);
-			while (*p && *p != ']') {
-				if (*p == '-' && *(p+1) && *(p+1) != ']') {
-					p++;
-					c1 = *(p-2) + 1;
-					c2 = *p++;
-					while (c1 <= c2)
-						chset((CHAR)c1++);
-				}
-#ifdef EXTEND
-				else if (*p == '\\' && *(p+1)) {
-					p++;
-					chset(*p++);
-				}
-#endif
-				else
-					chset(*p++);
-			}
-			if (!*p)
-				return badpat("Missing ]");
-
-			for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
-				store(mask ^ bittab[n]);
-	
-			break;
-
-		case '*':               /* match 0 or more.. */
-		case '+':               /* match 1 or more.. */
-			if (p == pat)
-				return badpat("Empty closure");
-			lp = sp;		/* previous opcode */
-			if (*lp == CLO)		/* equivalence..   */
-				break;
-			switch(*lp) {
-
-			case BOL:
-			case BOT:
-			case EOT:
-			case BOW:
-			case EOW:
-			case REF:
-				return badpat("Illegal closure");
-			default:
-				break;
-			}
-
-			if (*p == '+')
-				for (sp = mp; lp < sp; lp++)
-					store(*lp);
-
-			store(END);
-			store(END);
-			sp = mp;
-			while (--mp > lp)
-				*mp = mp[-1];
-			store(CLO);
-			mp = sp;
-			break;
-
-		case '\\':              /* tags, backrefs .. */
-			switch(*++p) {
-
-			case '(':
-				if (tagc < MAXTAG) {
-					tagstk[++tagi] = tagc;
-					store(BOT);
-					store(tagc++);
-				}
-				else
-					return badpat("Too many \\(\\) pairs");
-				break;
-			case ')':
-				if (*sp == BOT)
-					return badpat("Null pattern inside \\(\\)");
-				if (tagi > 0) {
-					store(EOT);
-					store(tagstk[tagi--]);
-				}
-				else
-					return badpat("Unmatched \\)");
-				break;
-			case '<':
-				store(BOW);
-				break;
-			case '>':
-				if (*sp == BOW)
-					return badpat("Null pattern inside \\<\\>");
-				store(EOW);
-				break;
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				n = *p-'0';
-				if (tagi > 0 && tagstk[tagi] == n)
-					return badpat("Cyclical reference");
-				if (tagc > n) {
-					store(REF);
-					store(n);
-				}
-				else
-					return badpat("Undetermined reference");
-				break;
-#ifdef EXTEND
-			case 'b':
-				store(CHR);
-				store('\b');
-				break;
-			case 'n':
-				store(CHR);
-				store('\n');
-				break;
-			case 'f':
-				store(CHR);
-				store('\f');
-				break;
-			case 'r':
-				store(CHR);
-				store('\r');
-				break;
-			case 't':
-				store(CHR);
-				store('\t');
-				break;
-#endif
-			default:
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		default :               /* an ordinary char  */
-			store(CHR);
-			store(*p);
-			break;
-		}
-		sp = lp;
-	}
-	if (tagi > 0)
-		return badpat("Unmatched \\(");
-	store(END);
-	sta = OKP;
-	return 0;
-}
-
-
-static char *bol;
-char *bopat[MAXTAG];
-char *eopat[MAXTAG];
-#ifdef NEEDPROTOS
-static char *pmatch( char *lp, CHAR *ap );
-#else /* NEEDPROTOS */
-static char *pmatch();
-#endif /* NEEDPROTOS */
-
-/*
- * re_exec:
- * 	execute nfa to find a match.
- *
- *	special cases: (nfa[0])	
- *		BOL
- *			Match only once, starting from the
- *			beginning.
- *		CHR
- *			First locate the character without
- *			calling pmatch, and if found, call
- *			pmatch for the remaining string.
- *		END
- *			re_comp failed, poor luser did not
- *			check for it. Fail fast.
- *
- *	If a match is found, bopat[0] and eopat[0] are set
- *	to the beginning and the end of the matched fragment,
- *	respectively.
- *
- */
-
-int
-re_exec( char *lp )
-{
-	register char c;
-	register char *ep = 0;
-	register CHAR *ap = nfa;
-
-	bol = lp;
-
-	bopat[0] = 0;
-	bopat[1] = 0;
-	bopat[2] = 0;
-	bopat[3] = 0;
-	bopat[4] = 0;
-	bopat[5] = 0;
-	bopat[6] = 0;
-	bopat[7] = 0;
-	bopat[8] = 0;
-	bopat[9] = 0;
-
-	switch(*ap) {
-
-	case BOL:			/* anchored: match from BOL only */
-		ep = pmatch(lp,ap);
-		break;
-	case CHR:			/* ordinary char: locate it fast */
-		c = *(ap+1);
-		while (*lp && *lp != c)
-			lp++;
-		if (!*lp)		/* if EOS, fail, else fall thru. */
-			return 0;
-	default:			/* regular matching all the way. */
-		do {
-			if ((ep = pmatch(lp,ap)))
-				break;
-			lp++;
-		} while (*lp);
-
-		break;
-	case END:			/* munged automaton. fail always */
-		return 0;
-	}
-	if (!ep)
-		return 0;
-
-	bopat[0] = lp;
-	eopat[0] = ep;
-	return 1;
-}
-
-/* 
- * pmatch: internal routine for the hard part
- *
- * 	This code is partly snarfed from an early grep written by
- *	David Conroy. The backref and tag stuff, and various other
- *	innovations are by oz.
- *
- *	special case optimizations: (nfa[n], nfa[n+1])
- *		CLO ANY
- *			We KNOW .* will match everything upto the
- *			end of line. Thus, directly go to the end of
- *			line, without recursive pmatch calls. As in
- *			the other closure cases, the remaining pattern
- *			must be matched by moving backwards on the
- *			string recursively, to find a match for xy
- *			(x is ".*" and y is the remaining pattern)
- *			where the match satisfies the LONGEST match for
- *			x followed by a match for y.
- *		CLO CHR
- *			We can again scan the string forward for the
- *			single char and at the point of failure, we
- *			execute the remaining nfa recursively, same as
- *			above.
- *
- *	At the end of a successful match, bopat[n] and eopat[n]
- *	are set to the beginning and end of subpatterns matched
- *	by tagged expressions (n = 1 to 9).	
- *
- */
-
-#ifndef re_fail
-extern void re_fail();
-#endif /* re_fail */
-
-/*
- * character classification table for word boundary operators BOW
- * and EOW. the reason for not using ctype macros is that we can
- * let the user add into our own table. see re_modw. This table
- * is not in the bitset form, since we may wish to extend it in the
- * future for other character classifications. 
- *
- *	TRUE for 0-9 A-Z a-z _
- */
-static char chrtyp[MAXCHR] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 
-	0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 0, 0, 0, 0, 0
-	};
-
-#define inascii(x)	(0177&(x))
-#define iswordc(x) 	chrtyp[inascii(x)]
-#define isinset(x,y) 	((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
-
-/*
- * skip values for CLO XXX to skip past the closure
- */
-
-#define ANYSKIP	2 	/* [CLO] ANY END ...	     */
-#define CHRSKIP	3	/* [CLO] CHR chr END ...     */
-#define CCLSKIP 18	/* [CLO] CCL 16bytes END ... */
-
-static char *
-pmatch( char *lp, CHAR *ap)
-{
-	register int op, c, n;
-	register char *e;		/* extra pointer for CLO */
-	register char *bp;		/* beginning of subpat.. */
-	register char *ep;		/* ending of subpat..	 */
-	char *are;			/* to save the line ptr. */
-
-	while ((op = *ap++) != END)
-		switch(op) {
-
-		case CHR:
-			if (*lp++ != *ap++)
-				return 0;
-			break;
-		case ANY:
-			if (!*lp++)
-				return 0;
-			break;
-		case CCL:
-			c = *lp++;
-			if (!isinset(ap,c))
-				return 0;
-			ap += BITBLK;
-			break;
-		case BOL:
-			if (lp != bol)
-				return 0;
-			break;
-		case EOL:
-			if (*lp)
-				return 0;
-			break;
-		case BOT:
-			bopat[*ap++] = lp;
-			break;
-		case EOT:
-			eopat[*ap++] = lp;
-			break;
- 		case BOW:
-			if (lp!=bol && iswordc(lp[-1]) || !iswordc(*lp))
-				return 0;
-			break;
-		case EOW:
-			if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp))
-				return 0;
-			break;
-		case REF:
-			n = *ap++;
-			bp = bopat[n];
-			ep = eopat[n];
-			while (bp < ep)
-				if (*bp++ != *lp++)
-					return 0;
-			break;
-		case CLO:
-			are = lp;
-			switch(*ap) {
-
-			case ANY:
-				while (*lp)
-					lp++;
-				n = ANYSKIP;
-				break;
-			case CHR:
-				c = *(ap+1);
-				while (*lp && c == *lp)
-					lp++;
-				n = CHRSKIP;
-				break;
-			case CCL:
-				while ((c = *lp) && isinset(ap+1,c))
-					lp++;
-				n = CCLSKIP;
-				break;
-			default:
-				re_fail("closure: bad nfa.", *ap);
-				return 0;
-			}
-
-			ap += n;
-
-			while (lp >= are) {
-				if (e = pmatch(lp, ap))
-					return e;
-				--lp;
-			}
-			return 0;
-		default:
-			re_fail("re_exec: bad nfa.", op);
-			return 0;
-		}
-	return lp;
-}
-
-/*
- * re_modw:
- *	add new characters into the word table to change re_exec's
- *	understanding of what a word should look like. Note that we
- *	only accept additions into the word definition.
- *
- *	If the string parameter is 0 or null string, the table is
- *	reset back to the default containing A-Z a-z 0-9 _. [We use
- *	the compact bitset representation for the default table]
- */
-
-static CHAR deftab[16] = {	
-	0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,  
-	0376, 0377, 0377, 007 
-}; 
-
-void
-re_modw( char *s )
-{
-	register int i;
-
-	if (!s || !*s) {
-		for (i = 0; i < MAXCHR; i++)
-			if (!isinset(deftab,i))
-				iswordc(i) = 0;
-	}
-	else
-		while(*s)
-			iswordc(*s++) = 1;
-}
-
-/*
- * re_subs:
- *	substitute the matched portions of the src in dst.
- *
- *	&	substitute the entire matched pattern.
- *
- *	\digit	substitute a subpattern, with the given	tag number.
- *		Tags are numbered from 1 to 9. If the particular
- *		tagged subpattern does not exist, null is substituted.
- */
-int
-re_subs( char *src, char *dst)
-{
-	register char c;
-	register int  pin;
-	register char *bp;
-	register char *ep;
-
-	if (!*src || !bopat[0])
-		return 0;
-
-	while (c = *src++) {
-		switch(c) {
-
-		case '&':
-			pin = 0;
-			break;
-
-		case '\\':
-			c = *src++;
-			if (c >= '0' && c <= '9') {
-				pin = c - '0';
-				break;
-			}
-			
-		default:
-			*dst++ = c;
-			continue;
-		}
-
-		if ((bp = bopat[pin]) && (ep = eopat[pin])) {
-			while (*bp && bp < ep)
-				*dst++ = *bp++;
-			if (bp < ep)
-				return 0;
-		}
-	}
-	*dst = (char) 0;
-	return 1;
-}
-			
-#ifdef DEBUG
-/*
- * symbolic - produce a symbolic dump of the nfa
- */
-symbolic( char *s ) 
-{
-	printf("pattern: %s\n", s);
-	printf("nfacode:\n");
-	nfadump(nfa);
-}
-
-static	
-nfadump( CHAR *ap)
-{
-	register int n;
-
-	while (*ap != END)
-		switch(*ap++) {
-		case CLO:
-			printf("CLOSURE");
-			nfadump(ap);
-			switch(*ap) {
-			case CHR:
-				n = CHRSKIP;
-				break;
-			case ANY:
-				n = ANYSKIP;
-				break;
-			case CCL:
-				n = CCLSKIP;
-				break;
-			}
-			ap += n;
-			break;
-		case CHR:
-			printf("\tCHR %c\n",*ap++);
-			break;
-		case ANY:
-			printf("\tANY .\n");
-			break;
-		case BOL:
-			printf("\tBOL -\n");
-			break;
-		case EOL:
-			printf("\tEOL -\n");
-			break;
-		case BOT:
-			printf("BOT: %d\n",*ap++);
-			break;
-		case EOT:
-			printf("EOT: %d\n",*ap++);
-			break;
-		case BOW:
-			printf("BOW\n");
-			break;
-		case EOW:
-			printf("EOW\n");
-			break;
-		case REF:
-			printf("REF: %d\n",*ap++);
-			break;
-		case CCL:
-			printf("\tCCL [");
-			for (n = 0; n < MAXCHR; n++)
-				if (isinset(ap,(CHAR)n)) {
-					if (n < ' ')
-						printf("^%c", n ^ 0x040);
-					else
-						printf("%c", n);
-				}
-			printf("]\n");
-			ap += BITBLK;
-			break;
-		default:
-			printf("bad nfa. opcode %o\n", ap[-1]);
-			exit(1);
-			break;
-		}
-}
-#endif
-#endif /* MACOS or DOS or NEED_BSDREGEX */
diff --git a/libraries/libldbm/Make-template b/libraries/libldbm/Make-template
index 4ebd4527e5..f91baade9d 100644
--- a/libraries/libldbm/Make-template
+++ b/libraries/libldbm/Make-template
@@ -21,7 +21,7 @@ OBJS	= ldbm.o
 HDIR	= ../../include
 
 INCLUDES= -I$(HDIR)
-DEFINES	= $(DEFS)
+DEFINES	= $(DEFS) $(LDBMDEFS)
 
 CFLAGS	= $(INCLUDES) $(DEFINES) $(ACFLAGS)
 
diff --git a/servers/slapd/Make-template b/servers/slapd/Make-template
index 82e6147d1b..01ef5cc0e0 100644
--- a/servers/slapd/Make-template
+++ b/servers/slapd/Make-template
@@ -21,22 +21,23 @@ SRCS	= main.c daemon.c connection.c search.c filter.c add.c charray.c \
 		attr.c entry.c config.c backend.c result.c operation.c \
 		dn.c compare.c modify.c delete.c modrdn.c ch_malloc.c \
 		value.c ava.c bind.c unbind.c abandon.c filterentry.c \
-		phonetic.c regex.c acl.c str2filter.c aclparse.c init.c \
+		phonetic.c acl.c str2filter.c aclparse.c init.c \
 		detach.c strdup.c tempnam.c repl.c lock.c \
 		schema.c schemaparse.c monitor.c configinfo.c
 OBJS	= main.o daemon.o connection.o search.o filter.o add.o charray.o \
 		attr.o entry.o config.o backend.o result.o operation.o \
 		dn.o compare.o modify.o delete.o modrdn.o ch_malloc.o \
 		value.o ava.o bind.o unbind.o abandon.o filterentry.o \
-		phonetic.o regex.o acl.o str2filter.o aclparse.o init.o \
+		phonetic.o acl.o str2filter.o aclparse.o init.o \
 		detach.o strdup.o tempnam.o repl.o lock.o \
 		schema.o schemaparse.o monitor.o configinfo.o
 
 INCLUDES= -I. -I$(HDIR) $(KRBINCLUDEFLAG)
-DEFINES = $(DEFS) $(LDAP_CRYPT) $(LDAP_TCP_WRAPPERS) $(SERVERDEFS)
+DEFINES = $(DEFS) $(SERVERDEFS)
 CFLAGS	= $(INCLUDES) $(THREADSINCLUDE) $(DEFINES) $(ACFLAGS) $(THREADS)
 LDFLAGS	= -L$(LDIR) $(KRBLIBFLAG)
-LIBS	= $(KRBLIBS) -llber -lldbm -lavl -llthread -lldif $(THREADSLIB) \
+LIBS	= $(KRBLIBS) -llber -lldbm -lavl -llthread -lldif \
+		$(REGEXLIB) $(THREADSLIB) \
 		$(LDBMLIB) $(LDAP_CRYPT_LIB) $(LDAP_TCP_WRAPPERS_LIB) $(ALIBS)
 
 all: FORCE
diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c
index 6c3b22ee85..6917f5e171 100644
--- a/servers/slapd/acl.c
+++ b/servers/slapd/acl.c
@@ -6,15 +6,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-#ifdef sunos5
-#include "regexpr.h"
-#else
-#include "regex.h"
-#endif
+#include <regex.h>
+
 #include "slap.h"
 
 extern Attribute	*attr_find();
-extern char		*re_comp();
 extern struct acl	*global_acl;
 extern int		global_default_access;
 extern char		*access2str();
@@ -26,7 +22,10 @@ struct acl	*acl_get_applicable();
 
 static int	regex_matches();
 
-extern pthread_mutex_t	regex_mutex;
+static string_expand(char *newbuf, int bufsiz, char *pattern,
+	char *match, regmatch_t *matches);
+
+extern Entry * be_dn2entry(Backend *be, char *bdn, char **matched);
 
 /*
  * access_allowed - check whether dn is allowed the requested access
@@ -51,15 +50,57 @@ access_allowed(
     int			access
 )
 {
-	int		rc;
-	struct acl	*a;
+	int				rc;
+	struct acl		*a;
+	char            *edn;
+
+	regmatch_t       matches[MAXREMATCHES];
+	int              i;
+	int              n;
 
 	if ( be == NULL ) {
 		return( 0 );
 	}
 
-	a = acl_get_applicable( be, op, e, attr );
-	rc = acl_access_allowed( a, be, conn, e, val, op, access );
+	edn = dn_normalize_case( strdup( e->e_dn ) );
+	Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n",
+		e->e_dn, attr, 0 );
+
+	/* the lastmod attributes are ignored by ACL checking */
+	if ( strcasecmp( attr, "modifiersname" ) == 0 ||
+		strcasecmp( attr, "modifytimestamp" ) == 0 ||
+		strcasecmp( attr, "creatorsname" ) == 0 ||
+		strcasecmp( attr, "createtimestamp" ) == 0 )
+	{
+ 		Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
+			attr, 0, 0 );
+		free( edn );
+		return(1);
+	}
+
+	memset(matches, 0, sizeof(matches));
+
+	a = acl_get_applicable( be, op, e, attr, edn, MAXREMATCHES, matches );
+
+	if (a) {
+		for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
+			Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ",
+				i, matches[i].rm_so, matches[i].rm_eo );
+
+			if( matches[i].rm_so <= matches[0].rm_eo ) {
+				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
+					Debug( LDAP_DEBUG_ARGS, "%c", edn[n], 0, 0 );
+				}
+			}
+			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
+		}
+	}
+
+	rc = acl_access_allowed( a, be, conn, e, val, op, access, edn, matches );
+	free( edn );
+
+	Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n",
+		e->e_dn, attr, 0);
 
 	return( rc );
 }
@@ -75,15 +116,17 @@ acl_get_applicable(
     Backend		*be,
     Operation		*op,
     Entry		*e,
-    char		*attr
+    char		*attr,
+    char		*edn,
+    int			nmatch,
+    regmatch_t	*matches
 )
 {
-	int		i;
+	int		i, j;
 	struct acl	*a;
-	char		*edn;
 
-	Debug( LDAP_DEBUG_ACL, "=> acl_get: entry (%s) attr (%s)\n", e->e_dn,
-	    attr, 0 );
+	Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n",
+		e->e_dn, attr, 0 );
 
 	if ( be_isroot( be, op->o_dn ) ) {
 		Debug( LDAP_DEBUG_ACL,
@@ -92,55 +135,73 @@ acl_get_applicable(
 		return( NULL );
 	}
 
+	Debug( LDAP_DEBUG_ARGS, "=> acl_get: edn %s\n", edn, 0, 0 );
+
 	/* check for a backend-specific acl that matches the entry */
 	for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
-		if ( a->acl_dnpat != NULL ) {
-			edn = dn_normalize_case( strdup( e->e_dn ) );
-			if ( ! regex_matches( a->acl_dnpat, edn ) ) {
-				free( edn );
+		if (a->acl_dnpat != NULL) {
+			Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
+				i, a->acl_dnpat, a->acl_dnre.re_nsub);
+
+			if (regexec(&a->acl_dnre, edn, nmatch, matches, 0))
 				continue;
-			}
-			free( edn );
+			else
+				Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d]  backend ACL match\n",
+					i, 0, 0);
 		}
+
 		if ( a->acl_filter != NULL ) {
-			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
-			    != 0 ) {
+			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
 				continue;
 			}
 		}
+
+        Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0);
+
 		if ( attr == NULL || a->acl_attrs == NULL ||
-		    charray_inlist( a->acl_attrs, attr ) ) {
-			Debug( LDAP_DEBUG_ACL, "<= acl_get: backend acl #%d\n",
-			    i, e->e_dn, attr );
+			charray_inlist( a->acl_attrs, attr ) )
+		{
+			Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n",
+				i, e->e_dn, attr );
 			return( a );
 		}
+		matches[0].rm_so = matches[0].rm_eo = -1;
 	}
 
 	/* check for a global acl that matches the entry */
 	for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
-		if ( a->acl_dnpat != NULL ) {
-			edn = dn_normalize_case( strdup( e->e_dn ) );
-			if ( ! regex_matches( a->acl_dnpat, edn ) ) {
-				free( edn );
+		if (a->acl_dnpat != NULL) {
+			Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
+				i, a->acl_dnpat, a->acl_dnre.re_nsub);
+
+			if (regexec(&a->acl_dnre, edn, nmatch, matches, 0)) {
 				continue;
+			} else {
+				Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n",
+					i, 0, 0);
 			}
-			free( edn );
 		}
+
 		if ( a->acl_filter != NULL ) {
-			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
-			    != 0 ) {
+			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
 				continue;
 			}
 		}
-		if ( attr == NULL || a->acl_attrs == NULL || charray_inlist(
-		    a->acl_attrs, attr ) ) {
-			Debug( LDAP_DEBUG_ACL, "<= acl_get: global acl #%d\n",
-			    i, e->e_dn, attr );
+
+		Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0);
+
+		if ( attr == NULL || a->acl_attrs == NULL ||
+			charray_inlist( a->acl_attrs, attr ) )
+		{
+			Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n",
+				i, e->e_dn, attr );
 			return( a );
 		}
+
+		matches[0].rm_so = matches[0].rm_eo = -1;
 	}
-	Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
 
+	Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
 	return( NULL );
 }
 
@@ -161,31 +222,40 @@ acl_access_allowed(
     Entry		*e,
     struct berval	*val,
     Operation		*op,
-    int			access
+    int			access,
+	char		*edn,
+	regmatch_t	*matches
 )
 {
 	int		i;
-	char		*edn, *odn;
+	char		*odn;
 	struct access	*b;
 	Attribute	*at;
 	struct berval	bv;
 	int		default_access;
 
-	Debug( LDAP_DEBUG_ACL, "=> acl: %s access to value \"%s\" by \"%s\"\n",
-	    access2str( access ), val ? val->bv_val : "any", op->o_dn ?
-	    op->o_dn : "" );
+	Debug( LDAP_DEBUG_ACL,
+		"\n=> acl_access_allowed: %s access to entry \"%s\"\n",
+		access2str( access ), e->e_dn, 0 );
+
+	Debug( LDAP_DEBUG_ACL,
+		"\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n",
+	    access2str( access ),
+		val ? val->bv_val : "any",
+		op->o_dn ?  op->o_dn : "" );
 
 	if ( be_isroot( be, op->o_dn ) ) {
-		Debug( LDAP_DEBUG_ACL, "<= acl: granted to database root\n",
+		Debug( LDAP_DEBUG_ACL,
+			"<= acl_access_allowed: granted to database root\n",
 		    0, 0, 0 );
 		return( 1 );
 	}
 
-	default_access = be->be_dfltaccess ? be->be_dfltaccess :
-	    global_default_access;
+	default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access;
+
 	if ( a == NULL ) {
 		Debug( LDAP_DEBUG_ACL,
-		    "<= acl: %s by default (no matching to)\n",
+		    "<= acl_access_allowed: %s by default (no matching to)\n",
 		    default_access >= access ? "granted" : "denied", 0, 0 );
 		return( default_access >= access );
 	}
@@ -198,76 +268,78 @@ acl_access_allowed(
 	}
 	for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
 		if ( b->a_dnpat != NULL ) {
+			Debug( LDAP_DEBUG_TRACE, "<= check a_dnpat: %s\n",
+				b->a_dnpat, 0, 0);
 			/*
 			 * if access applies to the entry itself, and the
 			 * user is bound as somebody in the same namespace as
 			 * the entry, OR the given dn matches the dn pattern
 			 */
-			if ( strcasecmp( b->a_dnpat, "self" ) == 0 && op->o_dn
-			    != NULL && *(op->o_dn) && e->e_dn != NULL ) {
-				edn = dn_normalize_case( strdup( e->e_dn ) );
+			if ( strcasecmp( b->a_dnpat, "self" ) == 0 && 
+				op->o_dn != NULL && *(op->o_dn) && e->e_dn != NULL ) 
+			{
 				if ( strcasecmp( edn, op->o_dn ) == 0 ) {
-					free( edn );
-					if ( odn ) free( odn );
 					Debug( LDAP_DEBUG_ACL,
-				    "<= acl: matched by clause #%d access %s\n",
+					"<= acl_access_allowed: matched by clause #%d access %s\n",
 					    i, (b->a_access & ~ACL_SELF) >=
 					    access ? "granted" : "denied", 0 );
 
-					return( (b->a_access & ~ACL_SELF)
-					    >= access );
+					if ( odn ) free( odn );
+					return( (b->a_access & ~ACL_SELF) >= access );
 				}
-				free( edn );
 			} else {
-				if ( regex_matches( b->a_dnpat, odn ) ) {
-					if ( odn ) free( odn );
+				if ( regex_matches( b->a_dnpat, odn, edn, matches ) ) {
 					Debug( LDAP_DEBUG_ACL,
-				    "<= acl: matched by clause #%d access %s\n",
+				    "<= acl_access_allowed: matched by clause #%d access %s\n",
 				    i, (b->a_access & ~ACL_SELF) >= access ?
 					    "granted" : "denied", 0 );
 
-					return( (b->a_access & ~ACL_SELF)
-					    >= access );
+					if ( odn ) free( odn );
+					return( (b->a_access & ~ACL_SELF) >= access );
 				}
 			}
 		}
 		if ( b->a_addrpat != NULL ) {
-			if ( regex_matches( b->a_addrpat, conn->c_addr ) ) {
-				if ( odn ) free( odn );
+			if ( regex_matches( b->a_addrpat, conn->c_addr, edn, matches ) ) {
 				Debug( LDAP_DEBUG_ACL,
-				    "<= acl: matched by clause #%d access %s\n",
+				    "<= acl_access_allowed: matched by clause #%d access %s\n",
 				    i, (b->a_access & ~ACL_SELF) >= access ?
 				    "granted" : "denied", 0 );
 
+				if ( odn ) free( odn );
 				return( (b->a_access & ~ACL_SELF) >= access );
 			}
 		}
 		if ( b->a_domainpat != NULL ) {
-			if ( regex_matches( b->a_domainpat, conn->c_domain ) ) {
-				if ( odn ) free( odn );
+			Debug( LDAP_DEBUG_ARGS, "<= check a_domainpath: %s\n",
+				b->a_domainpat, 0, 0 );
+			if ( regex_matches( b->a_domainpat, conn->c_domain, edn, matches ) ) 
+			{
 				Debug( LDAP_DEBUG_ACL,
-				    "<= acl: matched by clause #%d access %s\n",
+				    "<= acl_access_allowed: matched by clause #%d access %s\n",
 				    i, (b->a_access & ~ACL_SELF) >= access ?
 				    "granted" : "denied", 0 );
 
+				if ( odn ) free( odn );
 				return( (b->a_access & ~ACL_SELF) >= access );
 			}
 		}
 		if ( b->a_dnattr != NULL && op->o_dn != NULL ) {
+			Debug( LDAP_DEBUG_ARGS, "<= check a_dnattr: %s\n",
+				b->a_dnattr, 0, 0);
 			/* see if asker is listed in dnattr */
-			if ( (at = attr_find( e->e_attrs, b->a_dnattr ))
-			    != NULL && value_find( at->a_vals, &bv,
-			    at->a_syntax, 3 ) == 0 )
+			if ( (at = attr_find( e->e_attrs, b->a_dnattr )) != NULL && 
+				value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
 			{
-				if ( (b->a_access & ACL_SELF) && (val == NULL
-				    || value_cmp( &bv, val, at->a_syntax,
-				    2 )) ) {
+				if ( (b->a_access & ACL_SELF) && 
+					(val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) )
+				{
 					continue;
 				}
 
 				if ( odn ) free( odn );
 				Debug( LDAP_DEBUG_ACL,
-				    "<= acl: matched by clause #%d access %s\n",
+				    "<= acl_acces_allowed: matched by clause #%d access %s\n",
 				    i, (b->a_access & ~ACL_SELF) >= access ?
 				    "granted" : "denied", 0 );
 
@@ -276,22 +348,49 @@ acl_access_allowed(
 
 			/* asker not listed in dnattr - check for self access */
 			if ( ! (b->a_access & ACL_SELF) || val == NULL ||
-			    value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) {
+				value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
+			{
 				continue;
 			}
 
 			if ( odn ) free( odn );
 			Debug( LDAP_DEBUG_ACL,
-			    "<= acl: matched by clause #%d (self) access %s\n",
+				"<= acl_access_allowed: matched by clause #%d (self) access %s\n",
 			    i, (b->a_access & ~ACL_SELF) >= access ? "granted"
 			    : "denied", 0 );
 
 			return( (b->a_access & ~ACL_SELF) >= access );
 		}
+#ifdef ACLGROUP
+		if ( b->a_group != NULL && op->o_dn != NULL ) {
+			char buf[512];
+
+			/* b->a_group is an unexpanded entry name, expanded it should be an 
+			 * entry with objectclass group* and we test to see if odn is one of
+			 * the values in the attribute uniquegroup
+			 */
+			Debug( LDAP_DEBUG_ARGS, "<= check a_group: %s\n",
+				b->a_group, 0, 0);
+			Debug( LDAP_DEBUG_ARGS, "<= check a_group: odn: %s\n",
+				odn, 0, 0);
+
+			/* see if asker is listed in dnattr */
+			string_expand(buf, sizeof(buf), b->a_group, edn, matches);
+
+			if (be_group(be, buf, odn) == 0) {
+				Debug( LDAP_DEBUG_ACL,
+					"<= acl_access_allowed: matched by clause #%d (group) access granted\n",
+					i, 0, 0 );
+				if ( odn ) free( odn );
+				return( (b->a_access & ~ACL_SELF) >= access );
+			}
+		}
+#endif /* ACLGROUP */
 	}
 
 	if ( odn ) free( odn );
-	Debug( LDAP_DEBUG_ACL, "<= acl: %s by default (no matching by)\n",
+	Debug( LDAP_DEBUG_ACL,
+		"<= acl_access_allowed: %s by default (no matching by)\n",
 	    default_access >= access ? "granted" : "denied", 0, 0 );
 
 	return( default_access >= access );
@@ -316,14 +415,26 @@ acl_check_mods(
 {
 	int		i;
 	struct acl	*a;
+	char            *edn;
+
+	edn = dn_normalize_case( strdup( e->e_dn ) );
 
 	for ( ; mods != NULL; mods = mods->mod_next ) {
+		regmatch_t       matches[MAXREMATCHES];
+
+		/* the lastmod attributes are ignored by ACL checking */
 		if ( strcasecmp( mods->mod_type, "modifiersname" ) == 0 ||
-		    strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ) {
+			strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ||
+			strcasecmp( mods->mod_type, "creatorsname" ) == 0 ||
+			strcasecmp( mods->mod_type, "createtimestamp" ) == 0 ) 
+		{
+			Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
+				mods->mod_type, 0, 0 );
 			continue;
 		}
 
-		a = acl_get_applicable( be, op, e, mods->mod_type );
+		a = acl_get_applicable( be, op, e, mods->mod_type, edn,
+			MAXREMATCHES, matches );
 
 		switch ( mods->mod_op & ~LDAP_MOD_BVALUES ) {
 		case LDAP_MOD_REPLACE:
@@ -332,8 +443,10 @@ acl_check_mods(
 				break;
 			}
 			for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
-				if ( ! acl_access_allowed( a, be, conn, e,
-				    mods->mod_bvalues[i], op, ACL_WRITE ) ) {
+				if ( ! acl_access_allowed( a, be, conn, e, mods->mod_bvalues[i], 
+					op, ACL_WRITE, edn, matches) ) 
+				{
+					free(edn);
 					return( LDAP_INSUFFICIENT_ACCESS );
 				}
 			}
@@ -342,14 +455,18 @@ acl_check_mods(
 		case LDAP_MOD_DELETE:
 			if ( mods->mod_bvalues == NULL ) {
 				if ( ! acl_access_allowed( a, be, conn, e,
-				    NULL, op, ACL_WRITE ) ) {
+					NULL, op, ACL_WRITE, edn, matches) ) 
+				{
+					free(edn);
 					return( LDAP_INSUFFICIENT_ACCESS );
 				}
 				break;
 			}
 			for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
-				if ( ! acl_access_allowed( a, be, conn, e,
-				    mods->mod_bvalues[i], op, ACL_WRITE ) ) {
+				if ( ! acl_access_allowed( a, be, conn, e, mods->mod_bvalues[i], 
+					op, ACL_WRITE, edn, matches) ) 
+				{
+					free(edn);
 					return( LDAP_INSUFFICIENT_ACCESS );
 				}
 			}
@@ -357,48 +474,95 @@ acl_check_mods(
 		}
 	}
 
+	free(edn);
 	return( LDAP_SUCCESS );
 }
 
-#ifdef sunos5
-
-static int
-regex_matches( char *pat, char *str )
+static string_expand(
+	char *newbuf,
+	int bufsiz,
+	char *pat,
+	char *match,
+	regmatch_t *matches)
 {
-	char	*e;
-	int	rc;
-
-	if ( (e = compile( pat, NULL, NULL )) == NULL ) {
-		Debug( LDAP_DEBUG_ANY,
-		    "compile( \"%s\", \"%s\") failed\n", pat, str, 0 );
-		return( 0 );
+	int     size;
+	char   *sp;
+	char   *dp;
+	int     flag;
+
+	size = 0;
+	newbuf[0] = '\0';
+
+	flag = 0;
+	for ( dp = newbuf, sp = pat; size < 512 && *sp ; sp++) {
+		/* did we previously see a $ */
+		if (flag) {
+			if (*sp == '$') {
+				*dp++ = '$';
+				size++;
+			} else if (*sp >= '0' && *sp <= '9' ) {
+				int     n;
+				int     i;
+				char   *ep;
+				int     l;
+
+				n = *sp - '0';
+				*dp = '\0';
+				i = matches[n].rm_so;
+				l = matches[n].rm_eo; 
+				for ( ; size < 512 && i < l; size++, i++ ) {
+					*dp++ = match[i];
+					size++;
+				}
+				*dp = '\0';
+			}
+			flag = 0;
+		} else {
+			if (*sp == '$') {
+				flag = 1;
+			} else {
+				*dp++ = *sp;
+				size++;
+			}
+		}
 	}
-	rc = step( str ? str : "", e );
-	free( e );
+	*dp = '\0';
 
-	return( rc );
+	Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
+	Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
 }
 
-#else /* sunos5 */
-
 static int
-regex_matches( char *pat, char *str )
+regex_matches(
+	char *pat,				/* pattern to expand and match against */
+	char *str,				/* string to match against pattern */
+	char *buf,				/* buffer with $N expansion variables */
+	regmatch_t *matches		/* offsets in buffer for $N expansion variables */
+)
 {
-	char	*e;
+	regex_t re;
+	char newbuf[512];
 	int	rc;
 
-	pthread_mutex_lock( &regex_mutex );
-	if ( (e = re_comp( pat )) != NULL ) {
+	string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
+	if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
+		char error[512];
+		regerror(rc, &re, error, sizeof(error));
+
 		Debug( LDAP_DEBUG_ANY,
-		    "re_comp( \"%s\", \"%s\") failed because (%s)\n", pat, str,
-		    e );
-		pthread_mutex_unlock( &regex_mutex );
+		    "compile( \"%s\", \"%s\") failed %s\n",
+			pat, str, error );
 		return( 0 );
 	}
-	rc = re_exec( str ? str : "" );
-	pthread_mutex_unlock( &regex_mutex );
 
-	return( rc == 1 );
+	rc = regexec(&re, str, 0, NULL, 0);
+	regfree( &re );
+
+	Debug( LDAP_DEBUG_ANY,
+	    "=> regex_matches: string:   %s\n", str, 0, 0 );
+	Debug( LDAP_DEBUG_ANY,
+	    "=> regex_matches: rc: %d %s\n",
+		rc, !rc ? "matches" : "no matches", 0 );
+	return( !rc );
 }
 
-#endif /* sunos5 */
diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c
index 10fa6a0721..3d853222c6 100644
--- a/servers/slapd/aclparse.c
+++ b/servers/slapd/aclparse.c
@@ -7,12 +7,13 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-#include "regex.h"
+#include <unistd.h>
+#include <regex.h>
+
 #include "slap.h"
 #include "portable.h"
 
 extern Filter		*str2filter();
-extern char		*re_comp();
 extern struct acl	*global_acl;
 extern char		**str2charray();
 extern char		*dn_upcase();
@@ -26,6 +27,62 @@ static void		print_acl();
 static void		print_access();
 #endif
 
+int
+regtest(char *fname, int lineno, char *pat) {
+	int e;
+	regex_t re;
+
+	char buf[512];
+	int size;
+
+	char *sp;
+	char *dp;
+	int  flag;
+
+	sp = pat;
+	dp = buf;
+	size = 0;
+	buf[0] = '\0';
+
+	for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
+		if (flag) {
+			if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
+				*dp++ = *sp;
+				size++;
+			}
+			flag = 0;
+
+		} else {
+			if (*sp == '$') {
+				flag = 1;
+			} else {
+				*dp++ = *sp;
+				size++;
+			}
+		}
+	}
+
+	*dp = '\0';
+	if ( size >= (sizeof(buf)-1) ) {
+		fprintf( stderr,
+			"%s: line %d: regular expression \"%s\" too large\n",
+			fname, lineno, pat, 0 );
+		acl_usage();
+	}
+
+	if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
+		char error[512];
+		regerror(e, &re, error, sizeof(error));
+		fprintf( stderr,
+			"%s: line %d: regular expression \"%s\" bad because of %s\n",
+			fname, lineno, pat, error );
+		acl_usage();
+		return(0);
+	}
+	regfree(&re);
+	return(1);
+}
+
 void
 parse_acl(
     Backend	*be,
@@ -58,6 +115,17 @@ parse_acl(
 				}
 
 				if ( strcasecmp( argv[i], "*" ) == 0 ) {
+					int e;
+					if ((e = regcomp( &a->acl_dnre, ".*",
+						REG_EXTENDED|REG_ICASE)))
+					{
+						char buf[512];
+						regerror(e, &a->acl_dnre, buf, sizeof(buf));
+						fprintf( stderr,
+							"%s: line %d: regular expression \"%s\" bad because of %s\n",
+							fname, lineno, right, buf );
+						acl_usage();
+					}
 					a->acl_dnpat = strdup( ".*" );
 					continue;
 				}
@@ -79,14 +147,19 @@ parse_acl(
 						acl_usage();
 					}
 				} else if ( strcasecmp( left, "dn" ) == 0 ) {
-					if ( (e = re_comp( right )) != NULL ) {
+					int e;
+					if ((e = regcomp(&a->acl_dnre, right,
+						REG_EXTENDED|REG_ICASE))) {
+						char buf[512];
+						regerror(e, &a->acl_dnre, buf, sizeof(buf));
 						fprintf( stderr,
-		"%s: line %d: regular expression \"%s\" bad because of %s\n",
-						    fname, lineno, right, e );
+				"%s: line %d: regular expression \"%s\" bad because of %s\n",
+							fname, lineno, right, buf );
 						acl_usage();
+
+					} else {
+						a->acl_dnpat = dn_upcase(strdup( right ));
 					}
-					a->acl_dnpat = dn_upcase( strdup(
-					    right ) );
 				} else if ( strncasecmp( left, "attr", 4 )
 				    == 0 ) {
 					char	**alist;
@@ -96,7 +169,7 @@ parse_acl(
 					free( alist );
 				} else {
 					fprintf( stderr,
-				"%s: line %d: expecting <what> got \"%s\"\n",
+						"%s: line %d: expecting <what> got \"%s\"\n",
 					    fname, lineno, left );
 					acl_usage();
 				}
@@ -106,7 +179,7 @@ parse_acl(
 		} else if ( strcasecmp( argv[i], "by" ) == 0 ) {
 			if ( a == NULL ) {
 				fprintf( stderr,
-	"%s: line %d: to clause required before by clause in access line\n",
+					"%s: line %d: to clause required before by clause in access line\n",
 				    fname, lineno );
 				acl_usage();
 			}
@@ -131,38 +204,27 @@ parse_acl(
 			} else if ( strcasecmp( argv[i], "self" ) == 0 ) {
 				b->a_dnpat = strdup( "self" );
 			} else if ( strcasecmp( left, "dn" ) == 0 ) {
-				if ( (e = re_comp( right )) != NULL ) {
-					fprintf( stderr,
-			"%s: line %d: regular expression \"%s\" bad: %s\n",
-					    fname, lineno, right, e );
-					acl_usage();
-				}
+				regtest(fname, lineno, right);
 				b->a_dnpat = dn_upcase( strdup( right ) );
-			} else if ( strcasecmp( left, "dnattr" )
-			    == 0 ) {
+			} else if ( strcasecmp( left, "dnattr" ) == 0 ) {
 				b->a_dnattr = strdup( right );
-			} else if ( strcasecmp( left, "domain" )
-			    == 0 ) {
-				char	*s;
 
-				if ( (e = re_comp( right )) != NULL ) {
-					fprintf( stderr,
-			"%s: line %d: regular expression \"%s\" bad: %s\n",
-					    fname, lineno, right, e );
-					acl_usage();
-				}
+#ifdef ACLGROUP
+			} else if ( strcasecmp( left, "group" ) == 0 ) {
+				regtest(fname, lineno, right);
+				b->a_group = dn_upcase(strdup( right ));
+#endif /* ACLGROUP */
+			} else if ( strcasecmp( left, "domain" ) == 0 ) {
+				char	*s;
+				regtest(fname, lineno, right);
 				b->a_domainpat = strdup( right );
+
 				/* normalize the domain */
 				for ( s = b->a_domainpat; *s; s++ ) {
 					*s = TOLOWER( *s );
 				}
 			} else if ( strcasecmp( left, "addr" ) == 0 ) {
-				if ( (e = re_comp( right )) != NULL ) {
-					fprintf( stderr,
-			"%s: line %d: regular expression \"%s\" bad: %s\n",
-					    fname, lineno, right, e );
-					acl_usage();
-				}
+				regtest(fname, lineno, right);
 				b->a_addrpat = strdup( right );
 			} else {
 				fprintf( stderr,
@@ -198,16 +260,15 @@ parse_acl(
 
 	/* if we have no real access clause, complain and do nothing */
 	if ( a == NULL ) {
-	
 			fprintf( stderr,
-		    "%s: line %d: warning: no access clause(s) specified in access line\n",
+				"%s: line %d: warning: no access clause(s) specified in access line\n",
 			    fname, lineno );
 
 	} else {
 	
 		if ( a->acl_access == NULL ) {
 			fprintf( stderr,
-		    "%s: line %d: warning: no by clause(s) specified in access line\n",
+		    	"%s: line %d: warning: no by clause(s) specified in access line\n",
 			    fname, lineno );
 		}
 
@@ -373,4 +434,4 @@ print_acl( struct acl *a )
 	}
 }
 
-#endif
+#endif /* LDAP_DEBUG */
diff --git a/servers/slapd/back-ldbm/Make-template b/servers/slapd/back-ldbm/Make-template
index ae809e5892..4cc4e0a173 100644
--- a/servers/slapd/back-ldbm/Make-template
+++ b/servers/slapd/back-ldbm/Make-template
@@ -20,14 +20,14 @@ VERSIONFILE = $(LDAPSRC)/build/version
 SRCS	= idl.c add.c search.c cache.c dbcache.c dn2id.c id2entry.c \
 		index.c id2children.c nextid.c abandon.c compare.c \
 		modify.c modrdn.c delete.c init.c config.c bind.c attr.c \
-		filterindex.c unbind.c kerberos.c close.c
+		filterindex.c unbind.c kerberos.c close.c group.c
 OBJS	= idl.o add.o search.o cache.o dbcache.o dn2id.o id2entry.o \
 		index.o id2children.o nextid.o abandon.o compare.o \
 		modify.o modrdn.o delete.o init.o config.o bind.o attr.o \
-		filterindex.o unbind.o kerberos.o close.o
+		filterindex.o unbind.o kerberos.o close.o group.o
 
 INCLUDES= -I. -I.. -I$(HDIR) $(KRBINCLUDEFLAG)
-DEFINES = $(DEFS) $(LDAP_CRYPT) $(THREADS)
+DEFINES = $(DEFS) $(SERVERDEFS) $(THREADS)
 CFLAGS	= $(INCLUDES) $(THREADSINCLUDE) $(DEFINES) $(ACFLAGS)
 LDFLAGS	= -L$(LDIR) $(KRBLIBFLAG) $(LDAP_CRYPT_LIB)
 
diff --git a/servers/slapd/back-ldbm/group.c b/servers/slapd/back-ldbm/group.c
new file mode 100644
index 0000000000..49161fc55b
--- /dev/null
+++ b/servers/slapd/back-ldbm/group.c
@@ -0,0 +1,90 @@
+/* compare.c - ldbm backend compare routine */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "slap.h"
+#include "back-ldbm.h"
+
+extern Entry            *dn2entry();
+extern Attribute        *attr_find();
+
+
+#ifdef ACLGROUP
+/* return 0 IFF edn is a value in uniqueMember attribute
+ * of entry with bdn AND that entry has an objectClass
+ * value of groupOfUniqueNames
+ */
+int
+ldbm_back_group(
+	Backend     *be,
+        char        *bdn,
+        char        *edn
+)
+{
+        struct ldbminfo *li = (struct ldbminfo *) be->be_private;    
+        Entry        *e;
+        char        *matched;
+        Attribute   *objectClass;
+        Attribute   *uniqueMember;
+        int          rc;
+
+	Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: bdn: %s\n", bdn, 0, 0 ); 
+	Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: edn: %s\n", edn, 0, 0 ); 
+
+        /* can we find bdn entry */
+        if ((e = dn2entry(be, bdn, &matched )) == NULL) {
+                Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: cannot find bdn: %s matched: %x\n", bdn, matched, 0 ); 
+                if (matched != NULL)
+                        free(matched);
+                return( 1 );
+        }
+        Debug( LDAP_DEBUG_ARGS, "ldbm_back_group: found bdn: %s matched: %x\n", bdn, matched, 0 ); 
+
+
+        /* find it's objectClass and uniqueMember attribute values
+         * make sure this is a group entry
+         * finally test if we can find edn in the uniqueMember attribute value list *
+         */
+        
+        rc = 1;
+        if ((objectClass = attr_find(e->e_attrs, "objectclass")) == NULL)  {
+            Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: failed to find objectClass\n", 0, 0, 0 ); 
+        }
+        else if ((uniqueMember = attr_find(e->e_attrs, "uniquemember")) == NULL) {
+            Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: failed to find uniqueMember\n", 0, 0, 0 ); 
+        }
+        else {
+            struct berval bvObjectClass;
+            struct berval bvUniqueMembers;
+
+            Debug( LDAP_DEBUG_ARGS, "ldbm_back_group: found objectClass and uniqueMembers\n", 0, 0, 0 ); 
+
+            bvObjectClass.bv_val = "groupofuniquenames";
+            bvObjectClass.bv_len = strlen( bvObjectClass.bv_val );         
+            bvUniqueMembers.bv_val = edn;
+            bvUniqueMembers.bv_len = strlen( edn );         
+
+            if (value_find(objectClass->a_vals, &bvObjectClass, SYNTAX_CIS, 1) != 0) {
+                Debug( LDAP_DEBUG_TRACE, "ldbm_back_group: failed to find objectClass in groupOfUniqueNames\n", 
+                        0, 0, 0 ); 
+            }
+            else if (value_find(uniqueMember->a_vals, &bvUniqueMembers, SYNTAX_CIS, 1) != 0) {
+                Debug( LDAP_DEBUG_ACL, "ldbm_back_group: %s not in %s: groupOfUniqueNames\n", 
+                        edn, bdn, 0 ); 
+            }
+            else {
+                Debug( LDAP_DEBUG_ACL, "ldbm_back_group: %s is in %s: groupOfUniqueNames\n", 
+                        edn, bdn, 0 ); 
+                rc = 0;
+            }
+        }
+
+        /* free e */
+        cache_return_entry( &li->li_cache, e );                 
+        Debug( LDAP_DEBUG_ARGS, "ldbm_back_group: rc: %d\n", rc, 0, 0 ); 
+        return(rc);
+}
+#endif
+
diff --git a/servers/slapd/back-ldbm/idl.c b/servers/slapd/back-ldbm/idl.c
index 8e5077afc9..1a76ff8350 100644
--- a/servers/slapd/back-ldbm/idl.c
+++ b/servers/slapd/back-ldbm/idl.c
@@ -2,7 +2,6 @@
 
 #include <stdio.h>
 #include <sys/types.h>
-#include <sys/socket.h>
 #include "slap.h"
 #include "ldapconfig.h"
 #include "back-ldbm.h"
@@ -175,7 +174,6 @@ idl_store(
 	
 	flags = LDBM_REPLACE;
 	if( li->li_flush_wrt ) flags |= LDBM_SYNC;
-
 	rc = ldbm_cache_store( db, key, data, flags );
 
 	/* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c
index 8131b90b77..c0e32b12d1 100644
--- a/servers/slapd/backend.c
+++ b/servers/slapd/backend.c
@@ -21,6 +21,7 @@ extern int	ldbm_back_abandon();
 extern int	ldbm_back_config();
 extern int	ldbm_back_init();
 extern int	ldbm_back_close();
+extern int      ldbm_back_group();
 #endif
 
 #ifdef LDAP_PASSWD
@@ -86,6 +87,9 @@ new_backend(
 		be->be_config = ldbm_back_config;
 		be->be_init = ldbm_back_init;
 		be->be_close = ldbm_back_close;
+#ifdef ACLGROUP
+		be->be_group = ldbm_back_group;
+#endif
 		be->be_type = "ldbm";
 		foundit = 1;
 	}
@@ -105,6 +109,9 @@ new_backend(
 		be->be_config = passwd_back_config;
 		be->be_init = NULL;
 		be->be_close = NULL;
+#ifdef ACLGROUP
+		be->be_group = NULL;
+#endif
 		be->be_type = "passwd";
 		foundit = 1;
 	}
@@ -124,6 +131,9 @@ new_backend(
 		be->be_config = shell_back_config;
 		be->be_init = shell_back_init;
 		be->be_close = NULL;
+#ifdef ACLGROUP
+		be->be_group = NULL;
+#endif
 		be->be_type = "shell";
 		foundit = 1;
 	}
@@ -150,14 +160,6 @@ select_backend( char * dn )
 	for ( i = 0; i < nbackends; i++ ) {
 		for ( j = 0; backends[i].be_suffix != NULL &&
 		    backends[i].be_suffix[j] != NULL; j++ ) {
-			/* Add greg@greg.rim.or.jp
-			 * It's quick hack for cheep client
-			 * Some browser offer a NULL base at ldap_search
-			 */
-			if(dnlen == 0) {
-				Debug( LDAP_DEBUG_TRACE, "select_backend: use default backend\n", 0, 0, 0 );
-				return (&backends[i]);
-			}
 			len = strlen( backends[i].be_suffix[j] );
 
 			if ( len > dnlen ) {
@@ -239,3 +241,14 @@ be_unbind(
 		}
 	}
 }
+
+#ifdef ACLGROUP
+int 
+be_group(Backend *be, char *bdn, char *edn)
+{
+        if (be->be_group)
+                return(be->be_group(be, bdn, edn));
+        else
+                return(1);
+}
+#endif
diff --git a/servers/slapd/filterentry.c b/servers/slapd/filterentry.c
index 4b6807f55a..3aa261751f 100644
--- a/servers/slapd/filterentry.c
+++ b/servers/slapd/filterentry.c
@@ -4,22 +4,13 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#ifdef sunos5
-#include "regexpr.h"
-#else
-#include "regex.h"
-#endif
+#include <regex.h>
 #include "slap.h"
 
 extern Attribute	*attr_find();
 extern char		*first_word();
 extern char		*next_word();
 extern char		*phonetic();
-extern char		*re_comp();
-
-#ifndef sunos5
-extern pthread_mutex_t	regex_mutex;
-#endif
 
 static int	test_filter_list();
 static int	test_substring_filter();
@@ -323,6 +314,7 @@ test_substring_filter(
 	char		pat[BUFSIZ];
 	char		buf[BUFSIZ];
 	struct berval	*val;
+	regex_t		re;
 
 	Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
 
@@ -390,19 +382,16 @@ test_substring_filter(
 	}
 
 	/* compile the regex */
-#ifdef sunos5
-	if ( (p = compile( pat, NULL, NULL )) == NULL ) {
-		Debug( LDAP_DEBUG_ANY, "compile failed (%s)\n", p, 0, 0 );
-		return( -1 );
-	}
-#else /* sunos5 */
-	pthread_mutex_lock( &regex_mutex );
-	if ( (p = re_comp( pat )) != 0 ) {
-		Debug( LDAP_DEBUG_ANY, "re_comp failed (%s)\n", p, 0, 0 );
-		pthread_mutex_unlock( &regex_mutex );
+	Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
+		pat, 0, 0 );
+	if ((rc = regcomp(&re, pat, 0))) {
+		char error[512];
+
+		regerror(rc, &re, error, sizeof(error));
+		Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
+			p, error, 0 );
 		return( -1 );
 	}
-#endif /* sunos5 */
 
 	/* for each value in the attribute see if regex matches */
 	for ( i = 0; a->a_vals[i] != NULL; i++ ) {
@@ -418,29 +407,18 @@ test_substring_filter(
 		}
 		value_normalize( realval, a->a_syntax );
 
-#ifdef sunos5
-		rc = step( realval, p );
-#else /* sunos5 */
-		rc = re_exec( realval );
-#endif /* sunos5 */
+		rc = !regexec(&re, realval, 0, NULL, 0);
 
 		if ( tmp != NULL ) {
 			free( tmp );
 		}
 		if ( rc == 1 ) {
-#ifdef sunos5
-			free( p );
-#else /* sunos5 */
-			pthread_mutex_unlock( &regex_mutex );
-#endif /* sunos5 */
+			regfree(&re);
 			return( 0 );
 		}
 	}
-#ifdef sunos5
-	free( p );
-#else /* sunos5 */
-	pthread_mutex_unlock( &regex_mutex );
-#endif /* sunos5 */
+
+	regfree(&re);
 
 	Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
 	return( 1 );
diff --git a/servers/slapd/init.c b/servers/slapd/init.c
index 03dd850f0e..94c47b4143 100644
--- a/servers/slapd/init.c
+++ b/servers/slapd/init.c
@@ -18,9 +18,6 @@ extern pthread_mutex_t	entry2str_mutex;
 extern pthread_mutex_t	replog_mutex;
 extern pthread_mutex_t	ops_mutex;
 extern pthread_mutex_t	num_sent_mutex;
-#ifndef sunos5
-extern pthread_mutex_t	regex_mutex;
-#endif
 
 init()
 {
@@ -31,7 +28,4 @@ init()
 	pthread_mutex_init( &replog_mutex, pthread_mutexattr_default );
 	pthread_mutex_init( &ops_mutex, pthread_mutexattr_default );
 	pthread_mutex_init( &num_sent_mutex, pthread_mutexattr_default );
-#ifndef sunos5
-	pthread_mutex_init( &regex_mutex, pthread_mutexattr_default );
-#endif
 }
diff --git a/servers/slapd/main.c b/servers/slapd/main.c
index fbbf450844..8229fbf26b 100644
--- a/servers/slapd/main.c
+++ b/servers/slapd/main.c
@@ -56,9 +56,6 @@ pthread_mutex_t	num_sent_mutex;
  */
 pthread_mutex_t	entry2str_mutex;
 pthread_mutex_t	replog_mutex;
-#ifndef sunos5
-pthread_mutex_t	regex_mutex;
-#endif
 
 static
 usage( name )
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index a1ff6e5e75..300b76aa6b 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -7,10 +7,13 @@
 
 int access_allowed( Backend *be, Connection *conn, Operation *op, Entry *e,
 	char *attr, struct berval *val, char *dn, int  access );
+
 struct acl * acl_get_applicable( Backend *be, Operation *op, Entry *e,
-	char *attr );
+	char *attr, char *edn, int nmatches, regmatch_t *matches );
 int acl_access_allowed( struct acl *a, Backend *be, Connection *conn, Entry *e,
-	struct berval *val, Operation *op, int  access );
+	struct berval *val, Operation *op, int  access, char *edn,
+	regmatch_t *matches );
+
 int acl_check_mods( Backend *be, Connection *conn, Operation *op, Entry *e,
 	LDAPMod *mods );
 
diff --git a/servers/slapd/regex.c b/servers/slapd/regex.c
deleted file mode 100644
index 02a3c4c8ff..0000000000
--- a/servers/slapd/regex.c
+++ /dev/null
@@ -1,908 +0,0 @@
-#include "portable.h"
-
-#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
-#include "regex.h"
-
-/*
- * regex - Regular expression pattern matching  and replacement
- *
- * By:  Ozan S. Yigit (oz)
- *      Dept. of Computer Science
- *      York University
- *
- * These routines are the PUBLIC DOMAIN equivalents of regex
- * routines as found in 4.nBSD UN*X, with minor extensions.
- *
- * These routines are derived from various implementations found
- * in software tools books, and Conroy's grep. They are NOT derived
- * from licensed/restricted software.
- * For more interesting/academic/complicated implementations,
- * see Henry Spencer's regexp routines, or GNU Emacs pattern
- * matching module.
- *
- * Vendor Modification history:
- *
- * Revision 1.2  1996/04/25  16:24:11  mcs
- * make re_exec() match "" with ".*" and similar patterns
- * hopefully this change doesn't break anything else!
- *
- * Revision 1.1  1995/02/03  15:56:52  tim
- * Initial revision
- *
- * Revision 1.11  1994/12/14  21:33:45  mcs
- * use new NEED_BSDREGEX
- * fix pmatch() prototype
- *
- * Revision 1.10  1994/12/12  18:16:39  mcs
- * use on NetBSD
- *
- * Revision 1.9  1994/11/15  19:16:35  mcs
- * add (CHAR) cast to make VisualC++ happy
- *
- * Revision 1.8  1994/11/08  21:14:32  mcs
- * WIN32 changes
- *
- * Revision 1.7  1994/07/23  19:51:24  mcs
- * use ANSI-style inline function parameters
- *
- * Revision 1.6  1993/10/18  01:52:32  tim
- * include for VMS
- *
- * Revision 1.5  1993/09/28  21:37:54  mcs
- * HP/UX needs the regex we include (not in its libc)
- *
- * Revision 1.4  1993/08/27  15:59:52  mcs
- * use CHAR for deftab
- *
- * Revision 1.3  1993/08/27  15:49:47  mcs
- * added missing 0 to octal constants
- * use unsigned char for CHAR under DOS
- *
- * Revision 1.2  1993/08/27  14:57:48  mcs
- * add proto. for pmatch
- *
- * Revision 1.1  1993/08/18  21:20:02  mcs
- * Initial revision
- *
- * Revision 1.4  1991/10/17  03:56:42  oz
- * miscellaneous changes, small cleanups etc.
- *
- * Revision 1.3  1989/04/01  14:18:09  oz
- * Change all references to a dfa: this is actually an nfa.
- *
- * Revision 1.2  88/08/28  15:36:04  oz
- * Use a complement bitmap to represent NCL.
- * This removes the need to have seperate 
- * code in the pmatch case block - it is 
- * just CCL code now.
- * 
- * Use the actual CCL code in the CLO
- * section of pmatch. No need for a recursive
- * pmatch call.
- * 
- * Use a bitmap table to set char bits in an
- * 8-bit chunk.
- * 
- * Interfaces:
- *      re_comp:        compile a regular expression into a NFA.
- *
- *			char *re_comp(s)
- *			char *s;
- *
- *      re_exec:        execute the NFA to match a pattern.
- *
- *			int re_exec(s)
- *			char *s;
- *
- *	re_modw		change re_exec's understanding of what a "word"
- *			looks like (for \< and \>) by adding into the
- *			hidden word-syntax table.
- *
- *			void re_modw(s)
- *			char *s;
- *
- *      re_subs:	substitute the matched portions in a new string.
- *
- *			int re_subs(src, dst)
- *			char *src;
- *			char *dst;
- *
- *	re_fail:	failure routine for re_exec.
- *
- *			void re_fail(msg, op)
- *			char *msg;
- *			char op;
- *  
- * Regular Expressions:
- *
- *      [1]     char    matches itself, unless it is a special
- *                      character (metachar): . \ [ ] * + ^ $
- *
- *      [2]     .       matches any character.
- *
- *      [3]     \       matches the character following it, except
- *			when followed by a left or right round bracket,
- *			a digit 1 to 9 or a left or right angle bracket. 
- *			(see [7], [8] and [9])
- *			It is used as an escape character for all 
- *			other meta-characters, and itself. When used
- *			in a set ([4]), it is treated as an ordinary
- *			character.
- *
- *      [4]     [set]   matches one of the characters in the set.
- *                      If the first character in the set is "^",
- *                      it matches a character NOT in the set, i.e. 
- *			complements the set. A shorthand S-E is 
- *			used to specify a set of characters S upto 
- *			E, inclusive. The special characters "]" and 
- *			"-" have no special meaning if they appear 
- *			as the first chars in the set.
- *                      examples:        match:
- *
- *                              [a-z]    any lowercase alpha
- *
- *                              [^]-]    any char except ] and -
- *
- *                              [^A-Z]   any char except uppercase
- *                                       alpha
- *
- *                              [a-zA-Z] any alpha
- *
- *      [5]     *       any regular expression form [1] to [4], followed by
- *                      closure char (*) matches zero or more matches of
- *                      that form.
- *
- *      [6]     +       same as [5], except it matches one or more.
- *
- *      [7]             a regular expression in the form [1] to [10], enclosed
- *                      as \(form\) matches what form matches. The enclosure
- *                      creates a set of tags, used for [8] and for
- *                      pattern substution. The tagged forms are numbered
- *			starting from 1.
- *
- *      [8]             a \ followed by a digit 1 to 9 matches whatever a
- *                      previously tagged regular expression ([7]) matched.
- *
- *	[9]	\<	a regular expression starting with a \< construct
- *		\>	and/or ending with a \> construct, restricts the
- *			pattern matching to the beginning of a word, and/or
- *			the end of a word. A word is defined to be a character
- *			string beginning and/or ending with the characters
- *			A-Z a-z 0-9 and _. It must also be preceded and/or
- *			followed by any character outside those mentioned.
- *
- *      [10]            a composite regular expression xy where x and y
- *                      are in the form [1] to [10] matches the longest
- *                      match of x followed by a match for y.
- *
- *      [11]	^	a regular expression starting with a ^ character
- *		$	and/or ending with a $ character, restricts the
- *                      pattern matching to the beginning of the line,
- *                      or the end of line. [anchors] Elsewhere in the
- *			pattern, ^ and $ are treated as ordinary characters.
- *
- *
- * Acknowledgements:
- *
- *	HCR's Hugh Redelmeier has been most helpful in various
- *	stages of development. He convinced me to include BOW
- *	and EOW constructs, originally invented by Rob Pike at
- *	the University of Toronto.
- *
- * References:
- *              Software tools			Kernighan & Plauger
- *              Software tools in Pascal        Kernighan & Plauger
- *              Grep [rsx-11 C dist]            David Conroy
- *		ed - text editor		Un*x Programmer's Manual
- *		Advanced editing on Un*x	B. W. Kernighan
- *		RegExp routines			Henry Spencer
- *
- * Notes:
- *
- *	This implementation uses a bit-set representation for character
- *	classes for speed and compactness. Each character is represented 
- *	by one bit in a 128-bit block. Thus, CCL always takes a 
- *	constant 16 bytes in the internal nfa, and re_exec does a single
- *	bit comparison to locate the character in the set.
- *
- * Examples:
- *
- *	pattern:	foo*.*
- *	compile:	CHR f CHR o CLO CHR o END CLO ANY END END
- *	matches:	fo foo fooo foobar fobar foxx ...
- *
- *	pattern:	fo[ob]a[rz]	
- *	compile:	CHR f CHR o CCL bitset CHR a CCL bitset END
- *	matches:	fobar fooar fobaz fooaz
- *
- *	pattern:	foo\\+
- *	compile:	CHR f CHR o CHR o CHR \ CLO CHR \ END END
- *	matches:	foo\ foo\\ foo\\\  ...
- *
- *	pattern:	\(foo\)[1-3]\1	(same as foo[1-3]foo)
- *	compile:	BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
- *	matches:	foo1foo foo2foo foo3foo
- *
- *	pattern:	\(fo.*\)-\1
- *	compile:	BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
- *	matches:	foo-foo fo-fo fob-fob foobar-foobar ...
- */
-
-#define MAXNFA  1024
-#define MAXTAG  10
-
-#define OKP     1
-#define NOP     0
-
-#define CHR     1
-#define ANY     2
-#define CCL     3
-#define BOL     4
-#define EOL     5
-#define BOT     6
-#define EOT     7
-#define BOW	8
-#define EOW	9
-#define REF     10
-#define CLO     11
-
-#define END     0
-
-/*
- * The following defines are not meant to be changeable.
- * They are for readability only.
- */
-#define MAXCHR	128
-#define CHRBIT	8
-#define BITBLK	MAXCHR/CHRBIT
-#define BLKIND	0170
-#define BITIND	07
-
-#define ASCIIB	0177
-
-#if defined( DOS ) || defined( _WIN32 )
-typedef unsigned char CHAR;
-#else /* DOS */
-typedef /*unsigned*/ char CHAR;
-#endif /* DOS */
-
-static int  tagstk[MAXTAG];             /* subpat tag stack..*/
-static CHAR nfa[MAXNFA];		/* automaton..       */
-static int  sta = NOP;               	/* status of lastpat */
-
-static CHAR bittab[BITBLK];		/* bit table for CCL */
-					/* pre-set bits...   */
-static CHAR bitarr[] = {1,2,4,8,16,32,64,128};
-
-static void
-chset(CHAR c)
-{
-	bittab[((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
-}
-
-#define badpat(x)	(*nfa = END, x)
-#define store(x)	*mp++ = x
- 
-char *     
-re_comp( char *pat )
-{
-	register char *p;               /* pattern pointer   */
-	register CHAR *mp=nfa;          /* nfa pointer       */
-	register CHAR *lp;              /* saved pointer..   */
-	register CHAR *sp=nfa;          /* another one..     */
-
-	register int tagi = 0;          /* tag stack index   */
-	register int tagc = 1;          /* actual tag count  */
-
-	register int n;
-	register CHAR mask;		/* xor mask -CCL/NCL */
-	int c1, c2;
-		
-	if (!pat || !*pat)
-		if (sta)
-			return 0;
-		else
-			return badpat("No previous regular expression");
-	sta = NOP;
-
-	for (p = pat; *p; p++) {
-		lp = mp;
-		switch(*p) {
-
-		case '.':               /* match any char..  */
-			store(ANY);
-			break;
-
-		case '^':               /* match beginning.. */
-			if (p == pat)
-				store(BOL);
-			else {
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		case '$':               /* match endofline.. */
-			if (!*(p+1))
-				store(EOL);
-			else {
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		case '[':               /* match char class..*/
-			store(CCL);
-
-			if (*++p == '^') {
-				mask = 0377;	
-				p++;
-			}
-			else
-				mask = 0;
-
-			if (*p == '-')		/* real dash */
-				chset(*p++);
-			if (*p == ']')		/* real brac */
-				chset(*p++);
-			while (*p && *p != ']') {
-				if (*p == '-' && *(p+1) && *(p+1) != ']') {
-					p++;
-					c1 = *(p-2) + 1;
-					c2 = *p++;
-					while (c1 <= c2)
-						chset((CHAR)c1++);
-				}
-#ifdef EXTEND
-				else if (*p == '\\' && *(p+1)) {
-					p++;
-					chset(*p++);
-				}
-#endif
-				else
-					chset(*p++);
-			}
-			if (!*p)
-				return badpat("Missing ]");
-
-			for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
-				store(mask ^ bittab[n]);
-	
-			break;
-
-		case '*':               /* match 0 or more.. */
-		case '+':               /* match 1 or more.. */
-			if (p == pat)
-				return badpat("Empty closure");
-			lp = sp;		/* previous opcode */
-			if (*lp == CLO)		/* equivalence..   */
-				break;
-			switch(*lp) {
-
-			case BOL:
-			case BOT:
-			case EOT:
-			case BOW:
-			case EOW:
-			case REF:
-				return badpat("Illegal closure");
-			default:
-				break;
-			}
-
-			if (*p == '+')
-				for (sp = mp; lp < sp; lp++)
-					store(*lp);
-
-			store(END);
-			store(END);
-			sp = mp;
-			while (--mp > lp)
-				*mp = mp[-1];
-			store(CLO);
-			mp = sp;
-			break;
-
-		case '\\':              /* tags, backrefs .. */
-			switch(*++p) {
-
-			case '(':
-				if (tagc < MAXTAG) {
-					tagstk[++tagi] = tagc;
-					store(BOT);
-					store(tagc++);
-				}
-				else
-					return badpat("Too many \\(\\) pairs");
-				break;
-			case ')':
-				if (*sp == BOT)
-					return badpat("Null pattern inside \\(\\)");
-				if (tagi > 0) {
-					store(EOT);
-					store(tagstk[tagi--]);
-				}
-				else
-					return badpat("Unmatched \\)");
-				break;
-			case '<':
-				store(BOW);
-				break;
-			case '>':
-				if (*sp == BOW)
-					return badpat("Null pattern inside \\<\\>");
-				store(EOW);
-				break;
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				n = *p-'0';
-				if (tagi > 0 && tagstk[tagi] == n)
-					return badpat("Cyclical reference");
-				if (tagc > n) {
-					store(REF);
-					store(n);
-				}
-				else
-					return badpat("Undetermined reference");
-				break;
-#ifdef EXTEND
-			case 'b':
-				store(CHR);
-				store('\b');
-				break;
-			case 'n':
-				store(CHR);
-				store('\n');
-				break;
-			case 'f':
-				store(CHR);
-				store('\f');
-				break;
-			case 'r':
-				store(CHR);
-				store('\r');
-				break;
-			case 't':
-				store(CHR);
-				store('\t');
-				break;
-#endif
-			default:
-				store(CHR);
-				store(*p);
-			}
-			break;
-
-		default :               /* an ordinary char  */
-			store(CHR);
-			store(*p);
-			break;
-		}
-		sp = lp;
-	}
-	if (tagi > 0)
-		return badpat("Unmatched \\(");
-	store(END);
-	sta = OKP;
-	return 0;
-}
-
-
-static char *bol;
-char *bopat[MAXTAG];
-char *eopat[MAXTAG];
-#ifdef NEEDPROTOS
-static char *pmatch( char *lp, CHAR *ap );
-#else /* NEEDPROTOS */
-static char *pmatch();
-#endif /* NEEDPROTOS */
-
-/*
- * re_exec:
- * 	execute nfa to find a match.
- *
- *	special cases: (nfa[0])	
- *		BOL
- *			Match only once, starting from the
- *			beginning.
- *		CHR
- *			First locate the character without
- *			calling pmatch, and if found, call
- *			pmatch for the remaining string.
- *		END
- *			re_comp failed, poor luser did not
- *			check for it. Fail fast.
- *
- *	If a match is found, bopat[0] and eopat[0] are set
- *	to the beginning and the end of the matched fragment,
- *	respectively.
- *
- */
-
-int
-re_exec( char *lp )
-{
-	register char c;
-	register char *ep = 0;
-	register CHAR *ap = nfa;
-
-	bol = lp;
-
-	bopat[0] = 0;
-	bopat[1] = 0;
-	bopat[2] = 0;
-	bopat[3] = 0;
-	bopat[4] = 0;
-	bopat[5] = 0;
-	bopat[6] = 0;
-	bopat[7] = 0;
-	bopat[8] = 0;
-	bopat[9] = 0;
-
-	switch(*ap) {
-
-	case BOL:			/* anchored: match from BOL only */
-		ep = pmatch(lp,ap);
-		break;
-	case CHR:			/* ordinary char: locate it fast */
-		c = *(ap+1);
-		while (*lp && *lp != c)
-			lp++;
-		if (!*lp)		/* if EOS, fail, else fall thru. */
-			return 0;
-	default:			/* regular matching all the way. */
-		do {
-			if ((ep = pmatch(lp,ap)))
-				break;
-			lp++;
-		} while (*lp);
-
-		break;
-	case END:			/* munged automaton. fail always */
-		return 0;
-	}
-	if (!ep)
-		return 0;
-
-	bopat[0] = lp;
-	eopat[0] = ep;
-	return 1;
-}
-
-/* 
- * pmatch: internal routine for the hard part
- *
- * 	This code is partly snarfed from an early grep written by
- *	David Conroy. The backref and tag stuff, and various other
- *	innovations are by oz.
- *
- *	special case optimizations: (nfa[n], nfa[n+1])
- *		CLO ANY
- *			We KNOW .* will match everything upto the
- *			end of line. Thus, directly go to the end of
- *			line, without recursive pmatch calls. As in
- *			the other closure cases, the remaining pattern
- *			must be matched by moving backwards on the
- *			string recursively, to find a match for xy
- *			(x is ".*" and y is the remaining pattern)
- *			where the match satisfies the LONGEST match for
- *			x followed by a match for y.
- *		CLO CHR
- *			We can again scan the string forward for the
- *			single char and at the point of failure, we
- *			execute the remaining nfa recursively, same as
- *			above.
- *
- *	At the end of a successful match, bopat[n] and eopat[n]
- *	are set to the beginning and end of subpatterns matched
- *	by tagged expressions (n = 1 to 9).	
- *
- */
-
-#ifndef re_fail
-extern void re_fail();
-#endif /* re_fail */
-
-/*
- * character classification table for word boundary operators BOW
- * and EOW. the reason for not using ctype macros is that we can
- * let the user add into our own table. see re_modw. This table
- * is not in the bitset form, since we may wish to extend it in the
- * future for other character classifications. 
- *
- *	TRUE for 0-9 A-Z a-z _
- */
-static char chrtyp[MAXCHR] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 
-	0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 0, 0, 0, 0, 0
-	};
-
-#define inascii(x)	(0177&(x))
-#define iswordc(x) 	chrtyp[inascii(x)]
-#define isinset(x,y) 	((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
-
-/*
- * skip values for CLO XXX to skip past the closure
- */
-
-#define ANYSKIP	2 	/* [CLO] ANY END ...	     */
-#define CHRSKIP	3	/* [CLO] CHR chr END ...     */
-#define CCLSKIP 18	/* [CLO] CCL 16bytes END ... */
-
-static char *
-pmatch( char *lp, CHAR *ap)
-{
-	register int op, c, n;
-	register char *e;		/* extra pointer for CLO */
-	register char *bp;		/* beginning of subpat.. */
-	register char *ep;		/* ending of subpat..	 */
-	char *are;			/* to save the line ptr. */
-
-	while ((op = *ap++) != END)
-		switch(op) {
-
-		case CHR:
-			if (*lp++ != *ap++)
-				return 0;
-			break;
-		case ANY:
-			if (!*lp++)
-				return 0;
-			break;
-		case CCL:
-			c = *lp++;
-			if (!isinset(ap,c))
-				return 0;
-			ap += BITBLK;
-			break;
-		case BOL:
-			if (lp != bol)
-				return 0;
-			break;
-		case EOL:
-			if (*lp)
-				return 0;
-			break;
-		case BOT:
-			bopat[*ap++] = lp;
-			break;
-		case EOT:
-			eopat[*ap++] = lp;
-			break;
- 		case BOW:
-			if (lp!=bol && iswordc(lp[-1]) || !iswordc(*lp))
-				return 0;
-			break;
-		case EOW:
-			if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp))
-				return 0;
-			break;
-		case REF:
-			n = *ap++;
-			bp = bopat[n];
-			ep = eopat[n];
-			while (bp < ep)
-				if (*bp++ != *lp++)
-					return 0;
-			break;
-		case CLO:
-			are = lp;
-			switch(*ap) {
-
-			case ANY:
-				while (*lp)
-					lp++;
-				n = ANYSKIP;
-				break;
-			case CHR:
-				c = *(ap+1);
-				while (*lp && c == *lp)
-					lp++;
-				n = CHRSKIP;
-				break;
-			case CCL:
-				while ((c = *lp) && isinset(ap+1,c))
-					lp++;
-				n = CCLSKIP;
-				break;
-			default:
-				re_fail("closure: bad nfa.", *ap);
-				return 0;
-			}
-
-			ap += n;
-
-			while (lp >= are) {
-				if (e = pmatch(lp, ap))
-					return e;
-				--lp;
-			}
-			return 0;
-		default:
-			re_fail("re_exec: bad nfa.", op);
-			return 0;
-		}
-	return lp;
-}
-
-/*
- * re_modw:
- *	add new characters into the word table to change re_exec's
- *	understanding of what a word should look like. Note that we
- *	only accept additions into the word definition.
- *
- *	If the string parameter is 0 or null string, the table is
- *	reset back to the default containing A-Z a-z 0-9 _. [We use
- *	the compact bitset representation for the default table]
- */
-
-static CHAR deftab[16] = {	
-	0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,  
-	0376, 0377, 0377, 007 
-}; 
-
-void
-re_modw( char *s )
-{
-	register int i;
-
-	if (!s || !*s) {
-		for (i = 0; i < MAXCHR; i++)
-			if (!isinset(deftab,i))
-				iswordc(i) = 0;
-	}
-	else
-		while(*s)
-			iswordc(*s++) = 1;
-}
-
-/*
- * re_subs:
- *	substitute the matched portions of the src in dst.
- *
- *	&	substitute the entire matched pattern.
- *
- *	\digit	substitute a subpattern, with the given	tag number.
- *		Tags are numbered from 1 to 9. If the particular
- *		tagged subpattern does not exist, null is substituted.
- */
-int
-re_subs( char *src, char *dst)
-{
-	register char c;
-	register int  pin;
-	register char *bp;
-	register char *ep;
-
-	if (!*src || !bopat[0])
-		return 0;
-
-	while (c = *src++) {
-		switch(c) {
-
-		case '&':
-			pin = 0;
-			break;
-
-		case '\\':
-			c = *src++;
-			if (c >= '0' && c <= '9') {
-				pin = c - '0';
-				break;
-			}
-			
-		default:
-			*dst++ = c;
-			continue;
-		}
-
-		if ((bp = bopat[pin]) && (ep = eopat[pin])) {
-			while (*bp && bp < ep)
-				*dst++ = *bp++;
-			if (bp < ep)
-				return 0;
-		}
-	}
-	*dst = (char) 0;
-	return 1;
-}
-			
-#ifdef DEBUG
-/*
- * symbolic - produce a symbolic dump of the nfa
- */
-symbolic( char *s ) 
-{
-	printf("pattern: %s\n", s);
-	printf("nfacode:\n");
-	nfadump(nfa);
-}
-
-static	
-nfadump( CHAR *ap)
-{
-	register int n;
-
-	while (*ap != END)
-		switch(*ap++) {
-		case CLO:
-			printf("CLOSURE");
-			nfadump(ap);
-			switch(*ap) {
-			case CHR:
-				n = CHRSKIP;
-				break;
-			case ANY:
-				n = ANYSKIP;
-				break;
-			case CCL:
-				n = CCLSKIP;
-				break;
-			}
-			ap += n;
-			break;
-		case CHR:
-			printf("\tCHR %c\n",*ap++);
-			break;
-		case ANY:
-			printf("\tANY .\n");
-			break;
-		case BOL:
-			printf("\tBOL -\n");
-			break;
-		case EOL:
-			printf("\tEOL -\n");
-			break;
-		case BOT:
-			printf("BOT: %d\n",*ap++);
-			break;
-		case EOT:
-			printf("EOT: %d\n",*ap++);
-			break;
-		case BOW:
-			printf("BOW\n");
-			break;
-		case EOW:
-			printf("EOW\n");
-			break;
-		case REF:
-			printf("REF: %d\n",*ap++);
-			break;
-		case CCL:
-			printf("\tCCL [");
-			for (n = 0; n < MAXCHR; n++)
-				if (isinset(ap,(CHAR)n)) {
-					if (n < ' ')
-						printf("^%c", n ^ 0x040);
-					else
-						printf("%c", n);
-				}
-			printf("]\n");
-			ap += BITBLK;
-			break;
-		default:
-			printf("bad nfa. opcode %o\n", ap[-1]);
-			exit(1);
-			break;
-		}
-}
-#endif
-#endif /* MACOS or DOS or NEED_BSDREGEX */
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index 3ca0555cee..b8e5c85059 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -121,11 +121,13 @@ send_ldap_result2(
 		pthread_mutex_lock( &active_threads_mutex );
 		active_threads--;
 		conn->c_writewaiter = 1;
+
 #ifdef linux
 		pthread_kill( listener_tid, SIGSTKFLT );
 #else /* !linux */
 		pthread_kill( listener_tid, SIGUSR1 );
 #endif /* !linux */
+
 		pthread_cond_wait( &conn->c_wcv, &active_threads_mutex );
 		pthread_mutex_unlock( &active_threads_mutex );
 
@@ -196,6 +198,7 @@ send_search_entry(
 	Attribute	*a;
 	int		i, rc, bytes, sd;
 	struct acl	*acl;
+	char            *edn;
 
 	Debug( LDAP_DEBUG_TRACE, "=> send_search_entry (%s)\n", e->e_dn, 0, 0 );
 
@@ -206,15 +209,19 @@ send_search_entry(
 		return( 1 );
 	}
 
+	edn = dn_normalize_case( strdup( e->e_dn ) );
+
 #ifdef COMPAT30
 	if ( (ber = ber_alloc_t( conn->c_version == 30 ? 0 : LBER_USE_DER ))
-	    == NULLBER ) {
+		== NULLBER )
 #else
-	if ( (ber = der_alloc()) == NULLBER ) {
+	if ( (ber = der_alloc()) == NULLBER )
 #endif
+	{
 		Debug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
 		send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL,
-		    "ber_alloc" );
+			"ber_alloc" );
+		free(edn);
 		return( 1 );
 	}
 
@@ -224,26 +231,44 @@ send_search_entry(
 		    LDAP_RES_SEARCH_ENTRY, e->e_dn );
 	} else
 #endif
+	{
 		rc = ber_printf( ber, "{it{s{", op->o_msgid,
-		    LDAP_RES_SEARCH_ENTRY, e->e_dn );
+			LDAP_RES_SEARCH_ENTRY, e->e_dn );
+	}
 
 	if ( rc == -1 ) {
 		Debug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
 		ber_free( ber, 1 );
 		send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL,
 		    "ber_printf dn" );
+		free(edn);
 		return( 1 );
 	}
 
 	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+		regmatch_t       matches[MAXREMATCHES];
+
 		if ( attrs != NULL && ! charray_inlist( attrs, a->a_type ) ) {
 			continue;
 		}
 
-		acl = acl_get_applicable( be, op, e, a->a_type );
+		/* the lastmod attributes are ignored by ACL checking */
+		if ( strcasecmp( a->a_type, "modifiersname" ) == 0 ||
+			strcasecmp( a->a_type, "modifytimestamp" ) == 0 ||
+			strcasecmp( a->a_type, "creatorsname" ) == 0 ||
+			strcasecmp( a->a_type, "createtimestamp" ) == 0 ) 
+		{
+			Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access DEFAULT\n",
+				a->a_type, 0, 0 );
+			acl = NULL;
+		} else {
+			acl = acl_get_applicable( be, op, e, a->a_type, edn,
+				MAXREMATCHES, matches );
+		}
 
-		if ( ! acl_access_allowed( acl, be, conn, e, NULL, op,
-		    ACL_READ ) ) {
+		if ( ! acl_access_allowed( acl, be, conn, e, NULL, op, ACL_READ,
+			edn, matches ) ) 
+		{
 			continue;
 		}
 
@@ -252,14 +277,15 @@ send_search_entry(
 			ber_free( ber, 1 );
 			send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
 			    NULL, "ber_printf type" );
+			free(edn);
 			return( 1 );
 		}
 
 		if ( ! attrsonly ) {
 			for ( i = 0; a->a_vals[i] != NULL; i++ ) {
-				if ( a->a_syntax & SYNTAX_DN &&
-				    ! acl_access_allowed( acl, be, conn, e,
-				    a->a_vals[i], op, ACL_READ ) )
+				if ( a->a_syntax & SYNTAX_DN && 
+					! acl_access_allowed( acl, be, conn, e, a->a_vals[i], op,
+						ACL_READ, edn, matches) )
 				{
 					continue;
 				}
@@ -274,6 +300,7 @@ send_search_entry(
 					send_ldap_result( conn, op,
 					    LDAP_OPERATIONS_ERROR, NULL,
 					    "ber_printf value" );
+                                        free(edn);
 					return( 1 );
 				}
 			}
@@ -284,10 +311,13 @@ send_search_entry(
 			ber_free( ber, 1 );
 			send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
 			    NULL, "ber_printf type end" );
+                        free(edn);
 			return( 1 );
 		}
 	}
 
+	free(edn);
+
 #ifdef COMPAT30
 	if ( conn->c_version == 30 ) {
 		rc = ber_printf( ber, "}}}}" );
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index a67e567cbe..a5df09b0e3 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -6,6 +6,9 @@
 #define LDAP_SYSLOG
 
 #include <syslog.h>
+#include <sys/types.h>
+#include <regex.h>
+
 #include "avl.h"
 #include "lber.h"
 #include "ldap.h"
@@ -19,6 +22,8 @@
 #define OFF	(-1)
 #define UNDEFINED 0
 
+#define MAXREMATCHES 10
+
 /*
  * represents an attribute value assertion (i.e., attr=value)
  */
@@ -122,6 +127,11 @@ struct access {
 	char		*a_domainpat;
 	char		*a_dnattr;
 	long		a_access;
+
+#ifdef ACLGROUP
+    char		*a_group;
+#endif
+
 #define ACL_NONE	0x01
 #define ACL_COMPARE	0x02
 #define ACL_SEARCH	0x04
@@ -135,6 +145,7 @@ struct access {
 struct acl {
 	/* "to" part: the entries this acl applies to */
 	Filter		*acl_filter;
+	regex_t		acl_dnre;
 	char		*acl_dnpat;
 	char		**acl_attrs;
 
@@ -188,6 +199,10 @@ typedef struct backend {
 	IFP	be_config;	/* backend config routine	   	   */
 	IFP	be_init;	/* backend init routine			   */
 	IFP	be_close;	/* backend close routine		   */
+
+#ifdef ACLGROUP
+	IFP	be_group;	/* backend group member test               */
+#endif
 } Backend;
 
 /*
diff --git a/servers/slapd/tools/Make-template b/servers/slapd/tools/Make-template
index b4bd65f460..b5c3c17d3e 100644
--- a/servers/slapd/tools/Make-template
+++ b/servers/slapd/tools/Make-template
@@ -24,7 +24,7 @@ OBJS2	= ../config.o ../ch_malloc.o ../backend.o ../charray.o \
 		../aclparse.o ../schema.o ../result.o ../filterentry.o \
 		../acl.o ../phonetic.o ../attr.o ../value.o ../entry.o \
 		../dn.o ../filter.o ../str2filter.o ../ava.o ../init.o \
-		../schemaparse.o ../regex.o ../strdup.o
+		../schemaparse.o ../strdup.o
 
 INCLUDES= -I. -I$(HDIR) $(EXINCLUDES)
 DEFINES = $(DEFS) $(LDAP_CRYPT) $(SERVERDEFS) $(THREADS)
diff --git a/tests/data/acl.out.master b/tests/data/acl.out.master
index 4bef01e521..1a8074d702 100644
--- a/tests/data/acl.out.master
+++ b/tests/data/acl.out.master
@@ -21,8 +21,6 @@ multilinedescription: Outstanding
 title: Mad Cow Researcher, UM Alumni Association
 pager: +1 313 555 3923
 mail: jaj@mail.alumni.umich.edu
-modifytimestamp: 960404171231Z
-modifiersname: cn=Manager,o=University of Michigan,c=US
 facsimiletelephonenumber: +1 313 555 4332
 telephonenumber: +1 313 555 0895
 dn: cn=All Staff,ou=Groups,o=University of Michigan,c=US
@@ -180,7 +178,6 @@ member: cn=John Doe, ou=Information Technology Division, ou=People, o=Universi
 member: cn=James A Jones 1, ou=Alumni Association, ou=People, o=University of 
  Michigan, c=US
 labeledurl: http://www.itd.umich.edu ITD Home Page
- University of Michigan,c=US
 telephonenumber: +1 810 555 1212
 
 dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, o=Unive
diff --git a/tests/scripts/acfilter.sh b/tests/scripts/acfilter.sh
index 11d116d790..a7c6ac5ed8 100755
--- a/tests/scripts/acfilter.sh
+++ b/tests/scripts/acfilter.sh
@@ -1,5 +1,2 @@
 #!/bin/sh
-grep -v "modifiersname" | \
-grep -v "modifytimestamp" |  \
-grep -v "creatorsname" | \
-grep -v "createtimestamp"  
+egrep -iv '^modifiersname:|^modifytimestamp:|^creatorsname:|^createtimestamp'
diff --git a/tests/scripts/test002-populate b/tests/scripts/test002-populate
index e3db47ca25..bf01845fd8 100755
--- a/tests/scripts/test002-populate
+++ b/tests/scripts/test002-populate
@@ -50,6 +50,7 @@ echo "Filtering original ldif used to create database..."
 . scripts/acfilter.sh < $LDIF > $LDIFFLT
 echo "Comparing filter output..."
 cmp $SEARCHFLT $LDIFFLT
+
 if [ $? != 0 ]; then
 	echo "comparison failed - database was not created correctly"
 	exit 1
diff --git a/tests/scripts/test006-acls b/tests/scripts/test006-acls
index d4b8ee3ad1..a9b1b4c519 100755
--- a/tests/scripts/test006-acls
+++ b/tests/scripts/test006-acls
@@ -96,8 +96,7 @@ EOMODS4
 
 echo "Using ldapsearch to retrieve all the entries..."
 $LDAPSEARCH -L -S "" -b "$BASEDN" -h localhost -p $PORT \
-	    'objectClass=*' | egrep -iv '^modifytimestamp|^modifiersname' \
-	    >> $SEARCHOUT 2>&1
+	    'objectClass=*' | . scripts/acfilter.sh >> $SEARCHOUT 2>&1
 RC=$?
 kill -HUP $PID
 if [ $RC != 0 ]; then
-- 
GitLab