[Fedora-directory-devel] Please Review: Syntax Validation feature implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here's the implementation of syntax validation support for values being added to the database. It does not deal with validation of assertion values.

For details on the implementation, see the design document: http://directory.fedoraproject.org/wiki/Syntax_Validation_Design

I also added support for the "numericString" syntax.

-NGK
>From d19eafcd211d89cffdac1b2c3432087443e7d122 Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@xxxxxxxxxx>
Date: Fri, 8 May 2009 09:14:42 -0700
Subject: [PATCH] Added capability to validate syntax of values being added to the database.  Also added numericstring syntax support.

For more details, see the design doc at http://directory.fedoraproject.org/wiki/Syntax_Validation_Design
---
 Makefile.am                                        |   28 +-
 config.h.in                                        |    3 +
 configure.ac                                       |   15 +
 .../src/scripts/template-syntax-validate.pl.in     |  163 +++++++
 ldap/ldif/template-dse.ldif.in                     |   25 +-
 ldap/schema/60mozilla.ldif                         |    4 +-
 ldap/servers/plugins/syntaxes/bin.c                |    6 +-
 ldap/servers/plugins/syntaxes/ces.c                |   43 ++-
 ldap/servers/plugins/syntaxes/cis.c                |  482 +++++++++++++++++++-
 ldap/servers/plugins/syntaxes/dn.c                 |  215 +++++++++
 ldap/servers/plugins/syntaxes/int.c                |   56 +++
 ldap/servers/plugins/syntaxes/numericstring.c      |  188 ++++++++
 ldap/servers/plugins/syntaxes/sicis.c              |    3 +
 ldap/servers/plugins/syntaxes/syntax.h             |   45 ++
 ldap/servers/plugins/syntaxes/tel.c                |   35 ++
 ldap/servers/plugins/syntaxes/validate.c           |  352 ++++++++++++++
 ldap/servers/plugins/syntaxes/validate_task.c      |  303 ++++++++++++
 ldap/servers/slapd/add.c                           |   10 +
 ldap/servers/slapd/back-ldbm/import-threads.c      |   22 +-
 ldap/servers/slapd/back-ldbm/ldbm_add.c            |    9 +
 ldap/servers/slapd/back-ldbm/ldbm_modify.c         |   14 +-
 ldap/servers/slapd/back-ldbm/ldbm_modrdn.c         |   11 +
 ldap/servers/slapd/back-ldif/add.c                 |    7 +
 ldap/servers/slapd/back-ldif/modify.c              |    7 +
 ldap/servers/slapd/config.c                        |   34 ++-
 ldap/servers/slapd/dse.c                           |   23 +
 ldap/servers/slapd/fedse.c                         |   11 +-
 ldap/servers/slapd/libglobs.c                      |   59 +++
 ldap/servers/slapd/pblock.c                        |   12 +
 ldap/servers/slapd/plugin.c                        |   16 +-
 ldap/servers/slapd/plugin_syntax.c                 |  177 +++++++
 ldap/servers/slapd/proto-slap.h                    |    4 +
 ldap/servers/slapd/schema.c                        |    4 +-
 ldap/servers/slapd/slap.h                          |   22 +-
 ldap/servers/slapd/slapi-plugin.h                  |    6 +-
 35 files changed, 2352 insertions(+), 62 deletions(-)
 create mode 100644 ldap/admin/src/scripts/template-syntax-validate.pl.in
 create mode 100644 ldap/servers/plugins/syntaxes/numericstring.c
 create mode 100644 ldap/servers/plugins/syntaxes/validate.c
 create mode 100644 ldap/servers/plugins/syntaxes/validate_task.c

diff --git a/Makefile.am b/Makefile.am
index ddfe011..b9cdc18 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -138,14 +138,22 @@ LIBBITWISE_PLUGIN = libbitwise-plugin.la
 enable_bitwise = 1
 endif
 
+if enable_presence
+LIBPRESENCE_PLUGIN = libpresence-plugin.la
+LIBPRESENCE_SCHEMA = $(srcdir)/ldap/schema/10presence.ldif
+enable_presence = on
+else
+enable_presence = off
+endif
+
 serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
 	libback-ldbm.la libchainingdb-plugin.la libcos-plugin.la libdes-plugin.la \
 	libdistrib-plugin.la libhttp-client-plugin.la libcollation-plugin.la \
-	libmemberof-plugin.la libpassthru-plugin.la libpresence-plugin.la \
-	libpwdstorage-plugin.la libreferint-plugin.la libreplication-plugin.la \
-	libretrocl-plugin.la libroles-plugin.la libstatechange-plugin.la \
-	libsyntax-plugin.la libviews-plugin.la libschemareload-plugin.la \
-	$(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) 
+	libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \
+	libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
+	libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
+	libviews-plugin.la libschemareload-plugin.la $(LIBPAM_PASSTHRU_PLUGIN) \
+	$(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN)
 
 nodist_property_DATA = ns-slapd.properties
 
@@ -200,13 +208,13 @@ sampledata_DATA = $(srcdir)/ldap/ldif/Ace.ldif \
 	$(srcdir)/ldap/schema/60radius.ldif \
 	$(srcdir)/ldap/schema/60rfc4876.ldif \
 	$(srcdir)/ldap/schema/60samba.ldif \
-	$(srcdir)/ldap/schema/60samba3.ldif
+	$(srcdir)/ldap/schema/60samba3.ldif \
+	$(LIBPRESENCE_SCHEMA)
 
 schema_DATA = $(srcdir)/ldap/schema/00core.ldif \
 	$(srcdir)/ldap/schema/01common.ldif \
 	$(srcdir)/ldap/schema/05rfc2247.ldif \
 	$(srcdir)/ldap/schema/05rfc2927.ldif \
-	$(srcdir)/ldap/schema/10presence.ldif \
 	$(srcdir)/ldap/schema/10rfc2307.ldif \
 	$(srcdir)/ldap/schema/20subscriber.ldif \
 	$(srcdir)/ldap/schema/25java-object.ldif \
@@ -295,6 +303,7 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \
 	ldap/admin/src/scripts/template-ns-inactivate.pl \
 	ldap/admin/src/scripts/template-ns-newpwpolicy.pl \
 	ldap/admin/src/scripts/template-schema-reload.pl \
+	ldap/admin/src/scripts/template-syntax-validate.pl \
 	ldap/admin/src/scripts/template-verify-db.pl \
 	ldap/admin/src/scripts/template-dbverify
 
@@ -894,10 +903,13 @@ libsyntax_plugin_la_SOURCES = ldap/servers/plugins/syntaxes/bin.c \
 	ldap/servers/plugins/syntaxes/debug.c \
 	ldap/servers/plugins/syntaxes/dn.c \
 	ldap/servers/plugins/syntaxes/int.c \
+	ldap/servers/plugins/syntaxes/numericstring.c \
 	ldap/servers/plugins/syntaxes/phonetic.c \
 	ldap/servers/plugins/syntaxes/sicis.c \
 	ldap/servers/plugins/syntaxes/string.c \
 	ldap/servers/plugins/syntaxes/tel.c \
+	ldap/servers/plugins/syntaxes/validate.c \
+	ldap/servers/plugins/syntaxes/validate_task.c \
 	ldap/servers/plugins/syntaxes/value.c
 
 libsyntax_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
@@ -1149,6 +1161,7 @@ fixupcmd = sed \
 	-e 's,@enable_dna\@,$(enable_dna),g' \
 	-e 's,@enable_autobind\@,$(enable_autobind),g' \
 	-e 's,@enable_auto_dn_suffix\@,$(enable_auto_dn_suffix),g' \
+	-e 's,@enable_presence\@,$(enable_presence),g' \
 	-e 's,@ECHO_N\@,$(ECHO_N),g' \
 	-e 's,@ECHO_C\@,$(ECHO_C),g' \
 	-e 's,@brand\@,$(brand),g' \
@@ -1199,6 +1212,7 @@ fixupcmd = sed \
 	-e 's,@enable_dna\@,$(enable_dna),g' \
 	-e 's,@enable_autobind\@,$(enable_autobind),g' \
 	-e 's,@enable_auto_dn_suffix\@,$(enable_auto_dn_suffix),g' \
+	-e 's,@enable_presence\@,$(enable_presence),g' \
 	-e 's,@ECHO_N\@,$(ECHO_N),g' \
 	-e 's,@ECHO_C\@,$(ECHO_C),g' \
 	-e 's,@brand\@,$(brand),g' \
diff --git a/config.h.in b/config.h.in
index e3175ca..981e815 100644
--- a/config.h.in
+++ b/config.h.in
@@ -39,6 +39,9 @@
 /* enable the pam passthru auth plugin */
 #undef ENABLE_PAM_PASSTHRU
 
+/* enable the presence plugin */
+#undef ENABLE_PRESENCE
+
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
diff --git a/configure.ac b/configure.ac
index 9626172..57dd54d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -167,6 +167,21 @@ else
 fi
 AM_CONDITIONAL(enable_bitwise,test "$enable_bitwise" = "yes")
 
+if test -z "$enable_presence" ; then
+   enable_presence=no # if not set on cmdline, set default
+fi
+AC_MSG_CHECKING(for --enable-presence)
+AC_ARG_ENABLE(presence,
+        AS_HELP_STRING([--enable-presence],
+                       [enable the presence plugin (default: no)]))
+if test "$enable_presence" = yes ; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE([ENABLE_PRESENCE], [1], [enable the presence plugin])
+else
+  AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(enable_presence,test "$enable_presence" = "yes")
+
 # the default prefix - override with --prefix or --with-fhs
 AC_PREFIX_DEFAULT([/opt/$PACKAGE_NAME])
 
diff --git a/ldap/admin/src/scripts/template-syntax-validate.pl.in b/ldap/admin/src/scripts/template-syntax-validate.pl.in
new file mode 100644
index 0000000..4e4fa74
--- /dev/null
+++ b/ldap/admin/src/scripts/template-syntax-validate.pl.in
@@ -0,0 +1,163 @@
+#{{PERL-EXEC}}
+#
+# BEGIN COPYRIGHT BLOCK
+# This Program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; version 2 of the License.
+# 
+# This Program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along with
+# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA.
+# 
+# In addition, as a special exception, Red Hat, Inc. gives You the additional
+# right to link the code of this Program with code not covered under the GNU
+# General Public License ("Non-GPL Code") and to distribute linked combinations
+# including the two, subject to the limitations in this paragraph. Non-GPL Code
+# permitted under this exception must only link to the code of this Program
+# through those well defined interfaces identified in the file named EXCEPTION
+# found in the source code files (the "Approved Interfaces"). The files of
+# Non-GPL Code may instantiate templates or use macros or inline functions from
+# the Approved Interfaces without causing the resulting work to be covered by
+# the GNU General Public License. Only Red Hat, Inc. may make changes or
+# additions to the list of Approved Interfaces. You must obey the GNU General
+# Public License in all respects for all of the Program code and other code used
+# in conjunction with the Program except the Non-GPL Code covered by this
+# exception. If you modify this file, you may extend this exception to your
+# version of the file, but you are not obligated to do so. If you do not wish to
+# provide this exception without modification, you must delete this exception
+# statement from your version and license this file solely under the GPL without
+# exception. 
+# 
+# 
+# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+sub usage {
+    print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n");
+    print(STDERR "        -b baseDN [-f filter]\n");
+    print(STDERR " Opts: -D rootdn           - Directory Manager.\n");
+    print(STDERR "     : -w password         - Directory Manager's password.\n");
+    print(STDERR "     : -w -                - Prompt for Directory Manager's password.\n");
+    print(STDERR "     : -j filename         - Read Directory Manager's password from file.\n");
+    print(STDERR "     : -b baseDN           - Base DN that contains entries to validate.\n");
+    print(STDERR "     : -f filter           - Filter for entries to validate.\n");
+    print(STDERR "                             If omitted, all entries under the specified\n");
+    print(STDERR "                             base will have their attribute values\n");
+    print(STDERR "                             validated.\n");
+    print(STDERR "     : -v                  - Verbose.\n");
+}
+
+$rootdn = "";
+$passwd = "";
+$passwdfile = "";
+$basedn_arg = "";
+$filter_arg = "";
+$filter = "";
+$verbose = 0;
+
+$prefix = "{{DS-ROOT}}";
+
+$ENV{'PATH'} = "$prefix@ldapsdk_bindir@:$prefix/usr/bin:@ldapsdk_bindir@:/usr/bin";
+$ENV{'LD_LIBRARY_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib";
+$ENV{'SHLIB_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib";
+
+$i = 0;
+while ($i <= $#ARGV) 
+{
+    if ("$ARGV[$i]" eq "-b")
+    {
+        # base DN
+        $i++; $basedn_arg = $ARGV[$i];
+    }
+    elsif ("$ARGV[$i]" eq "-f")
+    {    
+        # filter 
+        $i++; $filter_arg = $ARGV[$i];
+    }
+    elsif ("$ARGV[$i]" eq "-D") 
+    {    
+        # Directory Manager
+        $i++; $rootdn = $ARGV[$i];
+    }
+    elsif ("$ARGV[$i]" eq "-w") 
+    {    
+        # Directory Manager's password
+        $i++; $passwd = $ARGV[$i];
+    } 
+    elsif ("$ARGV[$i]" eq "-j")
+    {
+         # Read Directory Manager's password from a file
+        $i++; $passwdfile = $ARGV[$i];
+    }
+    elsif ("$ARGV[$i]" eq "-v") 
+    {    
+        # verbose
+        $verbose = 1;
+    }
+    else
+    {
+        &usage; exit(1);
+    }
+    $i++;
+}
+
+if ($passwdfile ne ""){
+# Open file and get the password
+    unless (open (RPASS, $passwdfile)) {
+        die "Error, cannot open password file $passwdfile\n";
+    }
+    $passwd = <RPASS>;
+    chomp($passwd);
+    close(RPASS);
+} elsif ($passwd eq "-"){
+# Read the password from terminal
+	print "Bind Password: ";
+	# Disable console echo
+	system("stty -echo");
+	# read the answer
+	$passwd = <STDIN>;
+	# Enable console echo
+	system("stty echo");
+	print "\n";
+	chop($passwd); # trim trailing newline
+}
+
+if ( $rootdn eq "" || $passwd eq "" || $basedn_arg eq "" ) 
+{ 
+    &usage; 
+    exit(1); 
+}
+
+$vstr = "";
+if ($verbose != 0) 
+{ 
+    $vstr = "-v"; 
+}
+
+# Use a timestamp as part of the task entry name
+($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time);
+$mn++; $yr += 1900;
+$taskname = "syntax_validate_${yr}_${mn}_${dy}_${h}_${m}_${s}";
+
+# Build the task entry to add
+$dn = "dn: cn=$taskname, cn=syntax validate, cn=tasks, cn=config\n";
+$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n";
+$cn =  "cn: $taskname\n";
+$basedn = "basedn: $basedn_arg\n";
+
+if ( $filter_arg ne "" )
+{
+    $filter = "filter: $filter_arg\n";
+}
+
+$entry = "${dn}${misc}${cn}${basedn}${filter}";
+open(FOO, "| ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" );
+print(FOO "$entry");
+close(FOO);
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 36a5566..232d9f2 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -24,6 +24,7 @@ nsslapd-accesslog: %log_dir%/access
 nsslapd-enquote-sup-oc: off
 nsslapd-localhost: %fqdn%
 nsslapd-schemacheck: on
+nsslapd-syntaxcheck: on
 nsslapd-rewrite-rfc1274: off
 nsslapd-return-exact-case: on
 nsslapd-ssl-check-hostname: on
@@ -181,6 +182,16 @@ nsslapd-pluginarg0: nsmultiplexorcredentials
 nsslapd-pluginarg1: nsds5ReplicaCredentials
 nsslapd-pluginid: des-storage-scheme
 
+dn: cn=Syntax Validation Task,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Syntax Validation Task
+nsslapd-pluginpath: libsyntax-plugin
+nsslapd-plugininitfunc: syntax_validate_task_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: on
+
 dn: cn=Case Ignore String Syntax,cn=plugins,cn=config
 objectclass: top
 objectclass: nsSlapdPlugin
@@ -219,7 +230,7 @@ cn: Space Insensitive String Syntax
 nsslapd-pluginpath: libsyntax-plugin
 nsslapd-plugininitfunc: sicis_init
 nsslapd-plugintype: syntax
-nsslapd-pluginenabled: on
+nsslapd-pluginenabled: @enable_presence@
 
 dn: cn=Binary Syntax,cn=plugins,cn=config
 objectclass: top
@@ -309,7 +320,7 @@ cn: URI Syntax
 nsslapd-pluginpath: libsyntax-plugin
 nsslapd-plugininitfunc: uri_init
 nsslapd-plugintype: syntax
-nsslapd-pluginenabled: on
+nsslapd-pluginenabled: off
 
 dn: cn=JPEG Syntax,cn=plugins,cn=config
 objectclass: top
@@ -341,6 +352,16 @@ nsslapd-plugininitfunc: postal_init
 nsslapd-plugintype: syntax
 nsslapd-pluginenabled: on
 
+dn: cn=Numeric String Syntax,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Numeric String Syntax
+nsslapd-pluginpath: libsyntax-plugin
+nsslapd-plugininitfunc: numstr_init
+nsslapd-plugintype: syntax
+nsslapd-pluginenabled: on
+
 dn: cn=State Change Plugin,cn=plugins,cn=config
 objectclass: top
 objectclass: nsSlapdPlugin
diff --git a/ldap/schema/60mozilla.ldif b/ldap/schema/60mozilla.ldif
index f01c3d6..e53e442 100644
--- a/ldap/schema/60mozilla.ldif
+++ b/ldap/schema/60mozilla.ldif
@@ -200,10 +200,10 @@ attributeTypes: (
   )
 #
 ################################################################################
-# nsAIMid is already defined in 10presence.ldif as 2.16.840.1.113730.3.1.2013
+#
 attributeTypes: (
   1.3.6.1.4.1.13769.2.4
-  NAME ( 'nscpaimscreenname' )
+  NAME ( 'nsAIMid' 'nscpaimscreenname' )
   EQUALITY telephoneNumberMatch
   SUBSTR telephoneNumberSubstringsMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.50
diff --git a/ldap/servers/plugins/syntaxes/bin.c b/ldap/servers/plugins/syntaxes/bin.c
index 6d6c763..b7be0d1 100644
--- a/ldap/servers/plugins/syntaxes/bin.c
+++ b/ldap/servers/plugins/syntaxes/bin.c
@@ -43,8 +43,7 @@
 /* bin.c - bin syntax routines */
 
 /*
- * This file actually implements two syntax plugins: OctetString and Binary.
- * We treat them identically for now.  XXXmcs: check if that is correct.
+ * This file actually implements three syntax plugins: OctetString, JPEG, and Binary.
  */
 
 #include <stdio.h>
@@ -73,6 +72,9 @@ static char *octetstring_names[] = { "OctetString", OCTETSTRING_SYNTAX_OID, 0 };
 static char *jpeg_names[] = { "JPEG", JPEG_SYNTAX_OID, 0 };
 
 
+/* This syntax has "gone away" in RFC 4517, however we still use it for
+ * a number of attributes in our default schema.  We should try to eliminate
+ * it's use and remove support for it. */
 static Slapi_PluginDesc bin_pdesc = {
 	"bin-syntax", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
 	"binary attribute syntax plugin"
diff --git a/ldap/servers/plugins/syntaxes/ces.c b/ldap/servers/plugins/syntaxes/ces.c
index a7ffee5..68b642f 100644
--- a/ldap/servers/plugins/syntaxes/ces.c
+++ b/ldap/servers/plugins/syntaxes/ces.c
@@ -40,7 +40,9 @@
 #  include <config.h>
 #endif
 
-/* ces.c - caseexactstring syntax routines */
+/* ces.c - caseexactstring syntax routines.  Implements support for:
+ * 	- IA5String
+ * 	- URI (DEPRECATED - This is non-standard and isn't used in the default schema.) */
 
 #include <stdio.h>
 #include <string.h>
@@ -58,6 +60,7 @@ static int ces_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
 static int ces_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
 		char *final, Slapi_Value ***ivals );
 static int ces_compare(struct berval	*v1, struct berval	*v2);
+static int ia5_validate(struct berval *val);
 
 /* the first name is the official one from RFC 2252 */
 static char *ia5_names[] = { "IA5String", "ces", "caseexactstring",
@@ -78,7 +81,7 @@ static Slapi_PluginDesc uri_pdesc = { "uri-syntax", PLUGIN_MAGIC_VENDOR_STR,
  */
 static int
 register_ces_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
-		char **names, char *oid )
+		char **names, char *oid, void *validate_fn )
 {
 	int	rc, flags;
 
@@ -105,6 +108,10 @@ register_ces_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
 	    (void *) oid );
 	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
 	    (void *) ces_compare );
+	if (validate_fn != NULL) {
+		rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+		    (void *)validate_fn );
+	}
 
 	return( rc );
 }
@@ -116,7 +123,7 @@ ces_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> ces_init\n", 0, 0, 0 );
 
-	rc = register_ces_like_plugin(pb,&ia5_pdesc,ia5_names,IA5STRING_SYNTAX_OID);
+	rc = register_ces_like_plugin(pb,&ia5_pdesc,ia5_names,IA5STRING_SYNTAX_OID, ia5_validate);
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= ces_init %d\n", rc, 0, 0 );
 	return( rc );
@@ -130,7 +137,7 @@ uri_init( Slapi_PBlock *pb )
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> uri_init\n", 0, 0, 0 );
 
 	rc = register_ces_like_plugin(pb,&uri_pdesc,uri_names,
-				      "1.3.6.1.4.1.4401.1.1.1");
+				      "1.3.6.1.4.1.4401.1.1.1", NULL);
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= uri_init %d\n", rc, 0, 0 );
 	return( rc );
@@ -203,3 +210,31 @@ static int ces_compare(
 {
 	return value_cmp(v1,v2,SYNTAX_CES,3 /* Normalise both values */);
 }
+
+static int
+ia5_validate(
+    struct berval *val
+)
+{
+	int	rc = 0;    /* assume the value is valid */
+	int	i = 0;
+
+	if (val == NULL) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Per RFC 4517:
+	 *
+	 * IA5String = *(%x00-7F)
+	 */
+	for (i=0; i < val->bv_len; i++) {
+		if (!IS_UTF1(val->bv_val[i])) {
+			rc = 1;
+			goto exit;
+		}
+	}
+
+exit:
+	return rc;
+}
diff --git a/ldap/servers/plugins/syntaxes/cis.c b/ldap/servers/plugins/syntaxes/cis.c
index 20b990d..f20ae5e 100644
--- a/ldap/servers/plugins/syntaxes/cis.c
+++ b/ldap/servers/plugins/syntaxes/cis.c
@@ -43,13 +43,15 @@
 /* cis.c - caseignorestring syntax routines */
 
 /*
- * This file actually implements three syntax plugins:
- *		DirectoryString
+ * This file actually implements numerous syntax plugins:
+ *
  *		Boolean
+ *		CountryString
+ *		DirectoryString
  *		GeneralizedTime
+ *		OID
+ *		PostalAddress
  *
- * We treat them identically for now.  XXXmcs: we could do some validation on
- *		Boolean and GeneralizedTime values (someday, maybe).
  */
 
 #include <stdio.h>
@@ -68,6 +70,12 @@ static int cis_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
 static int cis_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
 		char *final, Slapi_Value ***ivals );
 static int cis_compare(struct berval	*v1, struct berval	*v2);
+static int dirstring_validate(struct berval *val);
+static int boolean_validate(struct berval *val);
+static int time_validate(struct berval *val);
+static int country_validate(struct berval *val);
+static int postal_validate(struct berval *val);
+static int oid_validate(struct berval *val);
 
 /*
  * Attribute syntaxes. We treat all of these the same for now, even though
@@ -170,7 +178,7 @@ static Slapi_PluginDesc oid_pdesc = { "oid-syntax",
  */
 static int
 register_cis_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
-		char **names, char *oid )
+		char **names, char *oid, void *validate_fn )
 {
 	int	rc, flags;
 
@@ -197,11 +205,14 @@ register_cis_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
 	    (void *) oid );
 	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
 	    (void *) cis_compare );
+	if (validate_fn != NULL) {
+		rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+		    (void *)validate_fn );
+	}
 
 	return( rc );
 }
 
-
 int
 cis_init( Slapi_PBlock *pb )
 {
@@ -209,7 +220,7 @@ cis_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> cis_init\n", 0, 0, 0 );
 	rc = register_cis_like_plugin( pb, &dirstring_pdesc, dirstring_names,
-		 	DIRSTRING_SYNTAX_OID );
+		 	DIRSTRING_SYNTAX_OID, dirstring_validate );
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= cis_init %d\n", rc, 0, 0 );
 	return( rc );
 }
@@ -222,12 +233,11 @@ boolean_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> boolean_init\n", 0, 0, 0 );
 	rc = register_cis_like_plugin( pb, &boolean_pdesc, boolean_names,
-			BOOLEAN_SYNTAX_OID );
+			BOOLEAN_SYNTAX_OID, boolean_validate );
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= boolean_init %d\n", rc, 0, 0 );
 	return( rc );
 }
 
-
 int
 time_init( Slapi_PBlock *pb )
 {
@@ -235,7 +245,7 @@ time_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> time_init\n", 0, 0, 0 );
 	rc = register_cis_like_plugin( pb, &time_pdesc, time_names,
-			GENERALIZEDTIME_SYNTAX_OID );
+			GENERALIZEDTIME_SYNTAX_OID, time_validate );
 	/* also register this plugin for matching rules */
 	rc |= slapi_matchingrule_register(&generalizedTimeMatch);
 	rc |= slapi_matchingrule_register(&generalizedTimeOrderingMatch);
@@ -250,7 +260,7 @@ country_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> country_init\n", 0, 0, 0 );
 	rc = register_cis_like_plugin( pb, &country_pdesc, country_names,
-				       COUNTRYSTRING_SYNTAX_OID );
+				       COUNTRYSTRING_SYNTAX_OID, country_validate );
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= country_init %d\n", rc, 0, 0 );
 	return( rc );
 }
@@ -262,7 +272,7 @@ postal_init( Slapi_PBlock *pb )
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> postal_init\n", 0, 0, 0 );
 	rc = register_cis_like_plugin( pb, &postal_pdesc, postal_names,
-				       POSTALADDRESS_SYNTAX_OID );
+				       POSTALADDRESS_SYNTAX_OID, postal_validate );
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= postal_init %d\n", rc, 0, 0 );
 	return( rc );
 }
@@ -274,7 +284,7 @@ oid_init( Slapi_PBlock *pb )
 	int	rc;
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> oid_init\n", 0, 0, 0 );
-	rc = register_cis_like_plugin( pb, &oid_pdesc, oid_names, OID_SYNTAX_OID );
+	rc = register_cis_like_plugin( pb, &oid_pdesc, oid_names, OID_SYNTAX_OID, oid_validate );
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= oid_init %d\n", rc, 0, 0 );
 	return( rc );
 }
@@ -349,3 +359,449 @@ static int cis_compare(
 {
 	return value_cmp(v1,v2,SYNTAX_CIS,3 /* Normalise both values */);
 }
+
+static int dirstring_validate(
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	char *p = NULL;
+	char *end = NULL;
+
+	/* Per RFC4517:
+	 *
+	 * DirectoryString = 1*UTF8
+	 */
+	if ((val != NULL) && (val->bv_len > 0)) {
+		p = val->bv_val;
+		end = &(val->bv_val[val->bv_len - 1]);
+		rc = utf8string_validate(p, end, NULL);
+	} else {
+		rc = 1;
+		goto exit;
+	}
+
+exit:
+	return( rc );
+}
+
+static int boolean_validate(
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+
+	/* Per RFC4517:
+	 *
+	 * Boolean =  "TRUE" / "FALSE"
+	 */
+	if (val != NULL) {
+		if (val->bv_len == 4) {
+			if (strncmp(val->bv_val, "TRUE", 4) != 0) {
+				rc = 1;
+				goto exit;
+			}
+		} else if (val->bv_len == 5) {
+			if (strncmp(val->bv_val, "FALSE", 5) != 0) {
+				rc = 1;
+				goto exit;
+			}
+		} else {
+			rc = 1;
+			goto exit;
+		}
+	} else {
+		rc = 1;
+	}
+
+exit:
+	return(rc);
+}
+
+static int time_validate(
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	int	i = 0;
+        const char *p = NULL;
+	char *end = NULL;
+
+	/* Per RFC4517:
+	 *
+	 * GeneralizedTime = century year month day hour
+	 *                      [ minute [ second / leap-second ] ]
+	 *                      [ fraction ]
+	 *                      g-time-zone
+	 *
+	 * century = 2(%x30-39) ; "00" to "99"
+	 * year    = 2(%x30-39) ; "00" to "99"
+	 * month   =   ( %x30 %x31-39 ) ; "01" (January) to "09"
+	 *           / ( %x31 %x30-32 ) ; "10 to "12"
+	 * day     =   ( %x30 %x31-39 )     ; "01" to "09"
+	 *           / ( %x31-x32 %x30-39 ) ; "10" to "29"
+	 *           / ( %x33 %x30-31 )     ; "30" to "31"
+	 * hour    = ( %x30-31 %x30-39 ) / ( %x32 %x30-33 ) ; "00" to "23"
+	 * minute  = %x30-35 %x30-39                        ; "00" to "59"
+	 *
+	 * second      = ( %x30-35 - %x30-39 ) ; "00" to "59"
+	 * leap-second = ( %x36 %x30 )         ; "60"
+	 *
+	 * fraction        = ( DOT / COMMA ) 1*(%x30-39)
+	 * g-time-zone     = %x5A  ; "Z"
+	 *                   / g-differential
+	 * g-differential  = ( MINUS / PLUS ) hour [ minute ]
+	 */
+	if (val != NULL) {
+		/* A valid GeneralizedTime should be at least 11 characters.  There
+		 * is no upper bound due to the variable length of "fraction". */
+		if (val->bv_len < 11) {
+			rc = 1;
+			goto exit;
+		}
+
+		/* We're guaranteed that the value is at least 11 characters, so we
+		 * don't need to bother checking if we're at the end of the value
+		 * until we start processing the "minute" part of the value. */
+		p = val->bv_val;
+		end = &(val->bv_val[val->bv_len - 1]);
+
+		/* Process "century year". First 4 characters can be any valid digit. */
+		for (i=0; i<4; i++) {
+			if (!isdigit(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		}
+
+		/* Process "month". Next character can be "0" or "1". */
+		if (*p == '0') {
+			p++;
+			/* any LDIGIT is valid now */
+			if (!IS_LDIGIT(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else if (*p == '1') {
+			p++;
+			/* only "0"-"2" are valid now */
+			if ((*p < '0') || (*p > '2')) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else {
+			rc = 1;
+			goto exit;
+		}
+
+		/* Process "day".  Next character can be "0"-"3". */
+		if (*p == '0') {
+			p++;
+			/* any LDIGIT is valid now */
+			if (!IS_LDIGIT(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else if ((*p == '1') || (*p == '2')) {
+			p++;
+			/* any digit is valid now */
+			if (!isdigit(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else if (*p == '3') {
+			p++;
+			/* only "0"-"1" are valid now */
+			if ((*p != '0') && (*p != '1')) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else {
+			rc = 1;
+			goto exit;
+		}
+
+		/* Process "hour".  Next character can be "0"-"2". */
+		if ((*p == '0') || (*p == '1')) {
+			p++;
+			/* any digit is valid now */
+			if (!isdigit(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else if (*p == '2') {
+			p++;
+			/* only "0"-"3" are valid now */
+			if ((*p < '0') || (*p > '3')) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else {
+			rc = 1;
+			goto exit;
+		}
+
+		/* Time for the optional stuff.  We know we have at least one character here, but
+		 * we need to start checking for the end of the string afterwards.
+		 *
+		 * See if a "minute" was specified. */
+		if ((*p >= '0') && (*p <= '5')) {
+			p++;
+			/* any digit is valid for the second char of a minute */
+			if ((p > end) || (!isdigit(*p))) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+
+			/* At this point, there has to at least be a "g-time-zone" left.
+			 * Make sure we're not at the end of the string. */
+			if (p > end) {
+				rc = 1;
+				goto exit;
+			}
+
+			/* See if a "second" or "leap-second" was specified. */
+			if ((*p >= '0') && (*p <= '5')) {
+				p++;
+				/* any digit is valid now */
+				if ((p > end) || (!isdigit(*p))) {
+					rc = 1;
+					goto exit;
+				}
+				p++;
+			} else if (*p == '6') {
+				p++;
+				/* only a '0' is valid now */
+				if ((p > end) || (*p != '0')) {
+					rc = 1;
+					goto exit;
+				}
+				p++;
+			}
+
+			/* At this point, there has to at least be a "g-time-zone" left.
+			 * Make sure we're not at the end of the string. */
+			if (p > end) {
+				rc = 1;
+				goto exit;
+			}
+		}
+
+		/* See if a fraction was specified. */
+		if ((*p == '.') || (*p == ',')) {
+			p++;
+			/* An arbitrary length string of digit chars is allowed here.
+			 * Ensure we have at least one digit character. */
+			if ((p >= end) || (!isdigit(*p))) {
+				rc = 1;
+				goto exit;
+			}
+
+			/* Just loop through the rest of the fraction until we encounter a non-digit */
+			p++;
+			while ((p < end) && (isdigit(*p))) {
+				p++;
+			}
+		}
+
+		/* Process "g-time-zone".  We either end with 'Z', or have a differential. */
+		if (p == end) {
+			if (*p != 'Z') {
+				rc = 1;
+				goto exit;
+			}
+		} else if (p < end) {
+			if ((*p != '-') && (*p != '+')) {
+				rc = 1;
+				goto exit;
+			} else {
+				/* A "g-differential" was specified. An "hour" must be present now. */
+				p++;
+				if ((*p == '0') || (*p == '1')) {
+					p++;
+					/* any digit is valid now */
+					if ((p > end) || !isdigit(*p)) {
+						rc = 1;
+						goto exit;
+					}
+					p++;
+				} else if (*p == '2') {
+					p++;
+					/* only "0"-"3" are valid now */
+					if ((p > end) || (*p < '0') || (*p > '3')) {
+						rc = 1;
+						goto exit;
+					}
+					p++;
+				} else {
+					rc = 1;
+					goto exit;
+				}
+
+				/* See if an optional minute is present ("00"-"59"). */
+				if (p <= end) {
+					/* "0"-"5" are valid now */
+					if ((*p < '0') || (*p > '5')) {
+						rc = 1;
+						goto exit;
+					}
+					p++;
+
+					/* We should be at the last character of the string
+					 * now, which must be a valid digit. */
+					if ((p != end) || !isdigit(*p)) {
+						rc = 1;
+						goto exit;
+					}
+				}
+			}
+		} else {
+			/* Premature end of string */
+			rc = 1;
+			goto exit;
+		}
+	} else {
+		rc = 1;
+		goto exit;
+	}
+
+exit:
+	return( rc );
+}
+
+static int country_validate(
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+
+	/* Per RFC4517:
+	 *
+	 *   CountryString = 2(PrintableCharacter)
+	 */
+	if (val != NULL) {
+		if ((val->bv_len != 2) || !IS_PRINTABLE(val->bv_val[0]) || !IS_PRINTABLE(val->bv_val[1])) {
+			rc = 1;
+			goto exit;
+		}
+
+
+	} else {
+		rc = 1;
+	}
+
+exit:
+	return(rc);
+}
+
+static int postal_validate( 
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	const char *p = NULL;
+	const char *start = NULL;
+	char *end = NULL;
+
+	/* Per RFC4517:
+	 *   PostalAddress = line *( DOLLAR line )
+	 *   line          = 1*line-char
+	 *   line-char     = %x00-23
+	 *                   / (%x5C "24")  ; escaped "$"
+	 *                   / %x25-5B
+	 *                   / (%x5C "5C")  ; escaped "\"
+	 *                   / %x5D-7F
+	 *                   / UTFMB
+	 */
+	if (val != NULL) {
+		start = val->bv_val;
+		end = &(val->bv_val[val->bv_len - 1]);
+		for (p = start; p <= end; p++) {
+			/* look for a '\' and make sure it's only used to escape a '$' or a '\' */
+			if (*p == '\\') {
+				p++;
+				/* ensure that we're not at the end of the value */
+				if ((p > end) || (strncmp(p, "24", 2) != 0) && (strncasecmp(p, "5C", 2) != 0)) {
+					rc = 1;
+					goto exit;
+				} else {
+					/* advance the pointer to point to the end
+					 * of the hex code for the escaped character */
+					p++;
+				}
+			} else if (*p == '$') {
+				/* This signifies the end of a line.  We need
+				 * to ensure that the line is not empty. */
+				if (p == start) {
+					rc = 1;
+					goto exit;
+				}
+
+				/* make sure the value doesn't end with a '$' */
+				if (p == end) {
+					rc = 1;
+					goto exit;
+				}
+
+				/* Make sure the line (start to p) is valid UTF-8. */
+				if ((rc = utf8string_validate(start, p, NULL)) != 0) {
+					goto exit;
+				}
+
+				/* make the start pointer point to the
+				 * beginning of the next line */
+				start = p + 1;
+			}
+		}
+	} else {
+		rc = 1;
+	}
+
+exit:
+	return(rc);
+}
+
+static int oid_validate(
+	struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	const char *p = NULL;
+	char *end = NULL;
+
+	/* Per RFC4512:
+	 *
+	 *   oid = descr / numericoid
+	 *   descr = keystring
+	 */
+	if ((val != NULL) && (val->bv_len > 0)) {
+		p = val->bv_val;
+		end = &(val->bv_val[val->bv_len - 1]);
+
+		/* check if the value matches the descr form */
+		if (IS_LEADKEYCHAR(*p)) {
+			rc = keystring_validate(p, end);
+		/* check if the value matches the numericoid form */
+		} else if (isdigit(*p)) {
+			rc = numericoid_validate(p, end);
+		} else {
+			rc = 1;
+			goto exit;
+		}
+	} else {
+		rc = 1;
+	}
+
+exit:
+	return( rc );
+}
+
diff --git a/ldap/servers/plugins/syntaxes/dn.c b/ldap/servers/plugins/syntaxes/dn.c
index c7d3475..a6dcced 100644
--- a/ldap/servers/plugins/syntaxes/dn.c
+++ b/ldap/servers/plugins/syntaxes/dn.c
@@ -57,6 +57,8 @@ static int dn_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
 	Slapi_Value ***ivals, int ftype );
 static int dn_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
 	char *final, Slapi_Value ***ivals );
+static int dn_validate( struct berval *val );
+static int rdn_validate( char *begin, char *end, char **last );
 
 /* the first name is the official one from RFC 2252 */
 static char *names[] = { "DN", DN_SYNTAX_OID, 0 };
@@ -89,6 +91,8 @@ dn_init( Slapi_PBlock *pb )
 	    (void *) names );
 	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
 	    (void *) DN_SYNTAX_OID );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+	    (void *) dn_validate );
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= dn_init %d\n", rc, 0, 0 );
 	return( rc );
@@ -133,3 +137,214 @@ dn_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,
 	return( string_assertion2keys_sub( pb, initial, any, final, ivals,
 	    SYNTAX_CIS | SYNTAX_DN ) );
 }
+
+static int dn_validate( struct berval *val )
+{
+	int rc = 0; /* Assume value is valid */
+
+	if (val != NULL) {
+		/* Per RFC 4514:
+		 *
+		 * distinguishedName = [ relativeDistinguishedName
+		 *     *( COMMA relativeDistinguishedName ) ]
+		 * relativeDistinguishedName = attributeTypeAndValue
+		 *     *( PLUS attributeTypeAndValue )
+		 * attributeTypeAndValue = attribyteType EQUALS attributeValue
+		 * attributeType = descr / numericoid
+		 * attributeValue = string / hexstring
+		 */
+		if (val->bv_len > 0) {
+			char *p = val->bv_val;
+			char *end = &(val->bv_val[val->bv_len - 1]);
+			char *last = NULL;
+
+			/* Validate one RDN at a time in a loop. */
+			while (p <= end) {
+				if ((rc = rdn_validate(p, end, &last)) != 0) {
+					goto exit;
+				}
+				p = last + 1;
+
+				/* p should be pointing at a comma, or one past
+				 * the end of the entire dn value.  If we have
+				 * not reached the end, ensure that the next
+				 * character is a comma and that there is at
+				 * least another character after the comma. */
+				if ((p <= end) && ((p == end) || (*p != ','))) {
+					rc = 1;
+					goto exit;
+				}
+
+				/* Advance the pointer past the comma so it
+				 * points at the beginning of the next RDN
+				 * (if there is one). */
+				p++;
+			}
+		}
+	} else {
+		rc = 1;
+		goto exit;
+	}
+exit:
+	return rc;
+}
+
+/*
+ * Helper function for validating a DN.  This function will validate
+ * a single RDN.  If the RDN is valid, 0 will be returned, otherwise
+ * non-zero will be returned. A pointer to the last character processed
+ * will be set in the "last parameter.  This will be the end of the RDN
+ * in the valid case, and the illegal character in the invalid case.
+ */
+static int rdn_validate( char *begin, char *end, char **last )
+{
+	int rc = 0; /* Assume RDN is valid */
+	int numericform = 0;
+	char *separator = NULL;
+	char *p = begin;
+
+	/* Find the '=', then use the helpers for descr and numericoid */
+	if ((separator = PL_strnchr(p, '=', end - begin + 1)) == NULL) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Process an attribute type. The 'descr'
+	 * form must start with a 'leadkeychar'. */
+	if (IS_LEADKEYCHAR(*p)) {
+		if (rc = keystring_validate(p, separator - 1)) {
+			goto exit;
+		}
+	/* See if the 'numericoid' form is being used */
+	} else if (isdigit(*p)) {
+		numericform = 1;
+		if (rc = numericoid_validate(p, separator - 1)) {
+			goto exit;
+		}
+	} else {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Advance the pointer past the '=' and make sure
+	 * we're not past the end of the string. */
+	p = separator + 1;
+	if (p > end) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* The value must be a 'hexstring' if the 'numericoid'
+	 * form of 'attributeType' is used.  Per RFC 4514:
+	 *
+	 *   hexstring = SHARP 1*hexpair
+	 *   hexpair = HEX HEX
+	 */
+	if (numericform) {
+		if ((p == end) || !IS_SHARP(*p)) {
+			rc = 1;
+			goto exit;
+		}
+		p++;
+	/* The value must be a 'string' when the 'descr' form
+	 * of 'attributeType' is used.  Per RFC 4514:
+	 *
+	 *   string = [ ( leadchar / pair ) [ *( stringchar / pair )
+	 *      ( trailchar / pair ) ] ]
+	 *
+	 *   leadchar   = LUTF1 / UTFMB
+	 *   trailchar  = TUTF1 / UTFMB
+	 *   stringchar = SUTF1 / UTFMB
+	 *
+	 *   pair = ESC (ESC / special / hexpair )
+	 *   special = escaped / SPACE / SHARP / EQUALS
+	 *   escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
+	 *   hexpair = HEX HEX
+	 */
+	} else {
+		/* Check the leadchar to see if anything illegal
+		 * is there.  We need to allow a 'pair' to get
+		 * through, so we'll assume that a '\' is the
+		 * start of a 'pair' for now. */
+		if (IS_UTF1(*p) && !IS_ESC(*p) && !IS_LUTF1(*p)) {
+			rc = 1;
+			goto exit;
+		}
+	}
+
+	/* Loop through string until we find the ',' separator, a '+'
+	 * char indicating a multi-value RDN, or we reach the end.  */
+	while ((p <= end) && (*p != ',') && (*p != '+')) {
+		if (numericform) {
+			/* Process a single 'hexpair' */
+			if ((p == end) || !isxdigit(*p) || !isxdigit(*p + 1)) {
+				rc = 1;
+				goto exit;
+			}
+			p = p + 2;
+		} else {
+			/* Check for a valid 'stringchar'.  We handle
+			 * multi-byte characters separately. */
+			if (IS_UTF1(*p)) {
+				/* If we're at the end, check if we have
+				 * a valid 'trailchar'. */
+				if ((p == end) && !IS_TUTF1(*p)) {
+					rc = 1;
+					goto exit;
+				/* Check for a 'pair'. */
+				} else if (IS_ESC(*p)) {
+					/* We're guaranteed to still have at
+					 * least one more character, so lets
+					 * take a look at it. */
+					p++;
+					if (!IS_ESC(*p) && !IS_SPECIAL(*p)) {
+						/* The only thing valid now
+						 * is a 'hexpair'. */
+						if ((p == end) || !isxdigit(*p) ||!isxdigit(*p + 1)) {
+							rc = 1;
+							goto exit;
+						}
+						p++;
+					}
+					p++;
+				/* Only allow 'SUTF1' chars now. */
+				} else if (!IS_SUTF1(*p)) {
+					rc = 1;
+					goto exit;
+				}
+
+				p++;
+			} else {
+				/* Validate a single 'UTFMB' (multi-byte) character. */
+				if (utf8char_validate(p, end, &p ) != 0) {
+					rc = 1;
+					goto exit;
+				}
+
+				/* Advance the pointer past the multi-byte char. */
+				p++;
+			}
+		}
+	}
+
+	/* We'll end up either at the comma, a '+', or one past end.
+	 * If we are processing a multi-valued RDN, we recurse to
+	 * process the next 'attributeTypeAndValue'. */
+	if ((p <= end) && (*p == '+')) {
+		/* Make sure that there is something after the '+'. */
+		if (p == end) {
+			rc = 1;
+			goto exit;
+		}
+		p++;
+
+		/* Recurse to process the next value.  We need to reset p to
+		 * ensure that last is set correctly for the original caller. */
+		rc = rdn_validate( p, end, last );
+		p = *last + 1;
+	}
+
+exit:
+	*last = p - 1;
+	return rc;
+}
diff --git a/ldap/servers/plugins/syntaxes/int.c b/ldap/servers/plugins/syntaxes/int.c
index 73c879a..0372d3a 100644
--- a/ldap/servers/plugins/syntaxes/int.c
+++ b/ldap/servers/plugins/syntaxes/int.c
@@ -54,6 +54,7 @@ static int int_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
 static int int_assertion2keys( Slapi_PBlock *pb, Slapi_Value *val,
 		Slapi_Value ***ivals, int ftype );
 static int int_compare(struct berval	*v1, struct berval	*v2);
+static int int_validate(struct berval *val);
 
 /* the first name is the official one from RFC 2252 */
 static char *names[] = { "INTEGER", "int", INTEGER_SYNTAX_OID, 0 };
@@ -101,6 +102,8 @@ int_init( Slapi_PBlock *pb )
 	    (void *) INTEGER_SYNTAX_OID );
 	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
 	    (void *) int_compare );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+	    (void *) int_validate );
 
 	/* also register this plugin for matching rules */
 	rc |= slapi_matchingrule_register(&integerMatch);
@@ -139,3 +142,56 @@ static int int_compare(
 {
 	return value_cmp(v1, v2, SYNTAX_INT|SYNTAX_CES, 3 /* Normalise both values */);
 }
+
+/* return 0 if valid, non-0 if invalid */
+static int int_validate(
+        struct berval *val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	char    *p = NULL;
+	char	*end = NULL;
+
+	/* Per RFC4517:
+	 *
+	 *   Integer = (HYPHEN LDIGIT *DIGIT) / number
+	 *   number  = DIGIT / (LDIGIT 1*DIGIT)
+	 */
+        if ((val != NULL) && (val->bv_len > 0)) {
+		p = val->bv_val;
+		end = &(val->bv_val[val->bv_len - 1]);
+
+		/* If the first character is HYPHEN, we need
+		 * to make sure the next char is a LDIGIT. */
+		if (*p == '-') {
+			p++;
+			if ((p > end) || !IS_LDIGIT(*p)) {
+				rc = 1;
+				goto exit;
+			}
+			p++;
+		} else if (*p == '0') {
+			/* 0 is allowed by itself, but not as
+			 * a leading 0 before other digits */
+			if (p != end) {
+				rc = 1;
+			}
+
+			/* We're done here */
+			goto exit;
+		}
+
+		/* Now we can simply allow the rest to be DIGIT */
+                for (; p <= end; p++) {
+                        if (!isdigit(*p)) {
+                                rc = 1;
+                                goto exit;
+                        }
+                }
+        } else {
+                rc = 1;
+        }
+
+exit:
+        return(rc);
+}
diff --git a/ldap/servers/plugins/syntaxes/numericstring.c b/ldap/servers/plugins/syntaxes/numericstring.c
new file mode 100644
index 0000000..180f8f7
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/numericstring.c
@@ -0,0 +1,188 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ * 
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ * 
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception. 
+ * 
+ * 
+ * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
+ * Copyright (C) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/* numericstring.c - Numeric String syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int numstr_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+		Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int numstr_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+		Slapi_Value ***ivals, int ftype );
+static int numstr_assertion2keys( Slapi_PBlock *pb, Slapi_Value *val,
+		Slapi_Value ***ivals, int ftype );
+static int numstr_compare(struct berval	*v1, struct berval	*v2);
+static int numstr_validate(struct berval *val);
+
+/* the first name is the official one from RFC 4517 */
+static char *names[] = { "Numeric String", "numstr", NUMERICSTRING_SYNTAX_OID, 0 };
+
+#define NUMERICSTRINGMATCH_OID		"2.5.13.8"
+#define NUMERICSTRINGORDERINGMATCH_OID	"2.5.13.9"
+#define NUMERICSTRINGSUBSTRINGMATCH_OID	"2.5.13.10"
+
+static Slapi_PluginDesc pdesc = { "numstr-syntax", PLUGIN_MAGIC_VENDOR_STR,
+	PRODUCTTEXT, "numeric string attribute syntax plugin" };
+
+static Slapi_MatchingRuleEntry
+numericStringMatch = { NUMERICSTRINGMATCH_OID, NULL /* no alias? */,
+                 "numericStringMatch", "The rule evaluates to TRUE if and only if the prepared "
+                 "attribute value character string and the prepared assertion value character "
+                 "string have the same number of characters and corresponding characters have "
+                 "the same code point.",
+                 NUMERICSTRING_SYNTAX_OID, 0 /* not obsolete */ };
+
+static Slapi_MatchingRuleEntry
+numericStringOrderingMatch = { NUMERICSTRINGORDERINGMATCH_OID, NULL /* no alias? */,
+                 "numericStringOrderingMatch", "The rule evaluates to TRUE if and only if, "
+                 "in the code point collation order, the prepared attribute value character "
+                 "string appears earlier than the prepared assertion value character string; "
+                 "i.e., the attribute value is less than the assertion value.",
+                 NUMERICSTRING_SYNTAX_OID, 0 /* not obsolete */ };
+
+static Slapi_MatchingRuleEntry
+numericStringSubstringMatch = { NUMERICSTRINGSUBSTRINGMATCH_OID, NULL /* no alias? */,
+                 "numericStringSubstringMatch", "The rule evaluates to TRUE if and only if (1) "
+                 "the prepared substrings of the assertion value match disjoint portions of "
+                 "the prepared attribute value, (2) an initial substring, if present, matches "
+                 "the beginning of the prepared attribute value character string, and (3) a "
+                 "final substring, if present, matches the end of the prepared attribute value "
+                 "character string.",
+                 NUMERICSTRING_SYNTAX_OID, 0 /* not obsolete */ };
+
+int
+numstr_init( Slapi_PBlock *pb )
+{
+	int	rc, flags;
+
+	LDAPDebug( LDAP_DEBUG_PLUGIN, "=> numstr_init\n", 0, 0, 0 );
+
+	rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+	    (void *) SLAPI_PLUGIN_VERSION_01 );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+	    (void *)&pdesc );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+	    (void *) numstr_filter_ava );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+	    (void *) numstr_values2keys );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+	    (void *) numstr_assertion2keys );
+	flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+	    (void *) &flags );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+	    (void *) names );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+	    (void *) INTEGER_SYNTAX_OID );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+	    (void *) numstr_compare );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+	    (void *) numstr_validate );
+
+	/* also register this plugin for matching rules */
+	rc |= slapi_matchingrule_register(&numericStringMatch);
+	rc |= slapi_matchingrule_register(&numericStringOrderingMatch);
+	rc |= slapi_matchingrule_register(&numericStringSubstringMatch);
+
+	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= numstr_init %d\n", rc, 0, 0 );
+	return( rc );
+}
+
+static int
+numstr_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+	Slapi_Value **bvals, int ftype, Slapi_Value **retVal )
+{
+	return( string_filter_ava( bvfilter, bvals, SYNTAX_SI | SYNTAX_CES,
+                               ftype, retVal ) );
+}
+
+static int
+numstr_values2keys( Slapi_PBlock *pb, Slapi_Value **vals, Slapi_Value ***ivals, int ftype )
+{
+	return( string_values2keys( pb, vals, ivals, SYNTAX_SI | SYNTAX_CES,
+                                ftype ) );
+}
+
+static int
+numstr_assertion2keys( Slapi_PBlock *pb, Slapi_Value *val, Slapi_Value ***ivals, int ftype )
+{
+	return(string_assertion2keys_ava( pb, val, ivals,
+                                      SYNTAX_SI | SYNTAX_CES, ftype ));
+}
+
+static int numstr_compare(    
+	struct berval	*v1,
+	struct berval	*v2
+)
+{
+	return value_cmp(v1, v2, SYNTAX_SI | SYNTAX_CES, 3 /* Normalise both values */);
+}
+
+/* return 0 if valid, non-0 if invalid */
+static int numstr_validate(
+	struct berval *val
+)
+{
+	int	rc = 0;    /* assume the value is valid */
+	const char	*p = NULL;
+
+	/* Per RFC4517:
+	 *
+	 *   NumericString = 1*(DIGIT / SPACE)
+	 */
+	if (val != NULL) {
+		for (p = val->bv_val; p < &(val->bv_val[val->bv_len]); p++) {
+			if (!isdigit(*p) && !IS_SPACE(*p)) {
+				rc = 1;
+				goto exit;
+			}
+		}
+	} else {
+		rc = 1;
+	}
+
+exit:
+	return(rc);
+}
diff --git a/ldap/servers/plugins/syntaxes/sicis.c b/ldap/servers/plugins/syntaxes/sicis.c
index fe7188c..07fee06 100644
--- a/ldap/servers/plugins/syntaxes/sicis.c
+++ b/ldap/servers/plugins/syntaxes/sicis.c
@@ -43,6 +43,9 @@
 /*
  * sicis.c - space insensitive string syntax routines.
  *           these strings are also case insensitive.
+ *
+ * This is a non-standard syntax.  It is only used by the presence plug-in.
+ * It will be disabled by default unless the presence plug-in is compiled.
  */
 #include <stdio.h>
 #include <string.h>
diff --git a/ldap/servers/plugins/syntaxes/syntax.h b/ldap/servers/plugins/syntaxes/syntax.h
index fc7a2db..b9a0137 100644
--- a/ldap/servers/plugins/syntaxes/syntax.h
+++ b/ldap/servers/plugins/syntaxes/syntax.h
@@ -66,6 +66,46 @@
 #define MIN( a, b )	(a < b ? a : b )
 #endif
 
+#define SYNTAX_PLUGIN_SUBSYSTEM "syntax-plugin"
+
+/* The following are derived from RFC 4512, section 1.4. */
+#define IS_LEADKEYCHAR(c)	( isalpha(c) )
+#define IS_KEYCHAR(c)		( isalnum(c) || (c == '-') )
+#define IS_SPACE(c)		( (c == ' ') )
+#define IS_LDIGIT(c)		( (c != '0') && isdigit(c) )
+#define IS_SHARP(c)		( (c == '#') )
+#define IS_ESC(c)		( (c == '\\') )
+#define IS_UTF0(c)		( (c >= '\x80') && (c <= '\xBF') )
+#define IS_UTF1(c)		( !(c & 128) )
+/* These are only checking the first byte of the multibyte character.  They
+ * do not verify that the entire multibyte character is correct. */
+#define IS_UTF2(c)		( (c >= '\xC2') && (c <= '\xDF') )
+#define IS_UTF3(c)		( (c >= '\xE0') && (c <= '\xEF') )
+#define IS_UTF4(c)		( (c >= '\xF0') && (c <= '\xF4') )
+#define IS_UTFMB(c)		( IS_UTF2(c) || IS_UTF3(c) || IS_UTF4(c) )
+#define IS_UTF8(c)		( IS_UTF1(c) || IS_UTFMB(c) )
+
+/* The following are derived from RFC 4514, section 3. */
+#define IS_ESCAPED(c)		( (c == '"') || (c == '+') || (c == ',') || \
+	(c == ';') || (c == '<') || (c == '>') )
+#define IS_SPECIAL(c)		( IS_ESCAPED(c) || IS_SPACE(c) || \
+	IS_SHARP(c) || (c == '=') )
+#define IS_LUTF1(c)		( IS_UTF1(c) && !IS_ESCAPED(c) && !IS_SPACE(c) && \
+	!IS_SHARP(c) && !IS_ESC(c) )
+#define IS_TUTF1(c)		( IS_UTF1(c) && !IS_ESCAPED(c) && !IS_SPACE(c) && \
+	!IS_ESC(c) )
+#define IS_SUTF1(c)		( IS_UTF1(c) && !IS_ESCAPED(c) && !IS_ESC(c) )
+
+/* Per RFC 4517:
+ *
+ *   PrintableCharacter = ALPHA / DIGIT / SQUOTE / LPAREN / RPAREN /
+ *                        PLUS / COMMA / HYPHEN / DOT / EQUALS /
+ *                        SLASH / COLON / QUESTION / SPACE
+ */
+#define IS_PRINTABLE(c)	( isalnum(c) || (c == '\'') || (c == '(') || \
+	(c == ')') || (c == '+') || (c == ',') || (c == '-') || (c == '.') || \
+	(c == '=') || (c == '/') || (c == ':') || (c == '?') || IS_SPACE(c) )
+
 int string_filter_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,Slapi_Value **bvals, int syntax );
 int string_filter_ava( struct berval *bvfilter, Slapi_Value **bvals, int syntax,int ftype, Slapi_Value **retVal );
 int string_values2keys( Slapi_PBlock *pb, Slapi_Value **bvals,Slapi_Value ***ivals, int syntax, int ftype );
@@ -78,5 +118,10 @@ char *first_word( char *s );
 char *next_word( char *s );
 char *phonetic( char *s );
 
+/* Validation helper functions */
+int keystring_validate( char *begin, char *end );
+int numericoid_validate( char *begin, char *end );
+int utf8char_validate( char *begin, char *end, char **last );
+int utf8string_validate( char *begin, char *end, char **last );
 
 #endif
diff --git a/ldap/servers/plugins/syntaxes/tel.c b/ldap/servers/plugins/syntaxes/tel.c
index b67fb78..3a2edd6 100644
--- a/ldap/servers/plugins/syntaxes/tel.c
+++ b/ldap/servers/plugins/syntaxes/tel.c
@@ -58,6 +58,7 @@ static int tel_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
 static int tel_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
 		char *final, Slapi_Value ***ivals );
 static int tel_compare(struct berval	*v1, struct berval	*v2);
+static int tel_validate(struct berval *val);
 
 /* the first name is the official one from RFC 2252 */
 static char *names[] = { "TelephoneNumber", "tel", TELEPHONE_SYNTAX_OID, 0 };
@@ -95,6 +96,8 @@ tel_init( Slapi_PBlock *pb )
 	    (void *) TELEPHONE_SYNTAX_OID );
 	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
 	    (void *) tel_compare );
+	rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALIDATE,
+	    (void *) tel_validate );
 
 	LDAPDebug( LDAP_DEBUG_PLUGIN, "<= tel_init %d\n", rc, 0, 0 );
 	return( rc );
@@ -170,3 +173,35 @@ static int tel_compare(
 {
 	return value_cmp(v1, v2, SYNTAX_TEL|SYNTAX_CIS, 3 /* Normalise both values */);
 }
+
+static int
+tel_validate(
+	struct berval	*val
+)
+{
+	int     rc = 0;    /* assume the value is valid */
+	int	i = 0;
+
+	/* Per RFC4517:
+	 *
+	 * TelephoneNumber = PrintableString
+	 * PrintableString = 1*PrintableCharacter
+	 */
+
+	/* Don't allow a 0 length string */
+	if ((val == NULL) || (val->bv_len == 0)) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Make sure all chars are a PrintableCharacter */
+	for (i=0; i < val->bv_len; i++) {
+		if (!IS_PRINTABLE(val->bv_val[i])) {
+			rc = 1;
+			goto exit;
+		}
+	}
+
+exit:
+	return rc;
+}
diff --git a/ldap/servers/plugins/syntaxes/validate.c b/ldap/servers/plugins/syntaxes/validate.c
new file mode 100644
index 0000000..8367e08
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/validate.c
@@ -0,0 +1,352 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ * 
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ * 
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception. 
+ * 
+ * 
+ * Copyright (C) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/* validate.c - syntax validation helper functions */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+/* Helper function for processing a 'keystring'.
+ *
+ * Returns 0 is the value between begin and end is a valid 'keystring'.
+ * Returns non-zero if the value is not a valide 'keystring'.
+ */
+int keystring_validate(
+	char *begin,
+	char *end
+)
+{
+	int rc = 0;    /* assume the value is valid */
+	const char *p = begin;
+	
+	if ((begin == NULL) || (end == NULL)) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Per RFC4512:
+	 *
+	 *   keystring = leadkeychar *keychar
+	 */
+	if (IS_LEADKEYCHAR(*p)) {
+		for (p++; p <= end; p++) {
+			if (!IS_KEYCHAR(*p)) {
+				rc = 1;
+				goto exit;
+			}
+		}
+	} else {
+		rc = 1;
+		goto exit;
+	}
+
+exit:
+	return( rc );
+}
+
+/* Helper function for processing a 'numericoid'.
+ *
+ * Returns 0 is the value between begin and end is a valid 'numericoid'.
+ * Returns non-zero if the value is not a valide 'numericoid'.
+ */
+int numericoid_validate(
+	char *begin,
+	char *end
+)
+{
+	int rc = 0; /* assume the value is valid */
+	int found_separator = 0;
+	char *p = NULL;
+
+	if ((begin == NULL) || (end == NULL)) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Per RFC 4512:
+	 *
+	 *   numericoid = number 1*( DOT number )
+	 */
+
+	/* one pass of this loop should process one element of the oid (number DOT) */
+	for (p = begin; p <= end; p++) {
+		if (IS_LDIGIT(*p)) {
+			/* loop until we get to a separator char */
+			while(*p != '.') {
+				p++;
+				if (p > end) {
+					/* ensure we got at least 2 elements */
+					if (!found_separator) {
+						rc = 1;
+						goto exit;
+					} else {
+						/* looks like a valid numericoid */
+						goto exit;
+					}
+				} else if (*p == '.') {
+					/* we can not end with a '.' */
+					if (p == end) {
+						rc = 1;
+						goto exit;
+					} else {
+						found_separator = 1;
+					}
+				} else if (!isdigit(*p)) {
+					rc = 1;
+					goto exit;
+				}
+			}
+		} else if (*p == '0') {
+			p++;
+			if (p > end) {
+				/* ensure we got at least 2 elements */
+				if (!found_separator) {
+					rc = 1;
+					goto exit;
+				} else {
+					/* looks like a valid numericoid */
+					goto exit;
+				}
+			} else if (*p != '.') {
+				/* a leading 0 is not allowed unless the entire element is simply 0 */
+				rc = 1;
+				goto exit;
+			}
+
+			/* At this point, *p is '.'.  We can not end with a '.' */
+			if (p == end) {
+				rc = 1;
+				goto exit;
+			} else {
+				found_separator = 1;
+			}
+		} else {
+			rc = 1;
+			goto exit;
+		}
+	}
+
+exit:
+	return(rc);
+}
+
+/* Helper to validate a single UTF-8 character.
+ * It is assumed that the first byte of the character
+ * is pointed to by begin.  This function will not read
+ * past the byte pointed to by the end parameter.  The
+ * last pointer will be filled in the the address of
+ * the last byte of the validated character if the
+ * character is valid, or the last byte processed
+ * in the invalid case.
+ *
+ * Returns 0 if it is valid and non-zero otherwise. */
+int utf8char_validate(
+	char *begin,
+	char *end,
+	char **last
+)
+{
+	int rc = 0; /* Assume char is valid */
+	char *p = begin;
+
+	if ((begin == NULL) || (end == NULL)) {
+		rc = 1;
+		goto exit;
+	}
+
+	/* Per RFC 4512:
+	 *
+	 *   UTF8  = UTF1 / UTFMB
+	 *   UTFMB = UTF2 / UTF3 / UTF4
+	 *   UTF0  = %x80-BF
+	 *   UTF1  = %x00-7F
+	 *   UTF2  = %xC2-DF UTF0
+	 *   UTF3  = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
+	 *           %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+	 *   UTF4  = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
+	 *           %xF4 %x80-8F 2(UTF0)
+	 */
+
+	/* If we have a single byte (ASCII) character, we
+	 * don't really have any work to do. */
+	if (IS_UTF1(*p)) {
+		goto exit;
+	} else if (IS_UTF2(*p)) {
+		/* Ensure that there is another byte
+		 * and that is is 'UTF0'. */
+		if ((p == end) || !IS_UTF0(*(p + 1))) {
+			rc = 1;
+			goto exit;
+		}
+
+		/* Advance p so last is set correctly */
+		p++;
+	} else if (IS_UTF3(*p)) {
+		/* Ensure that there are at least 2 more bytes. */
+		if (end - p < 2) {
+			rc = 1;
+			goto exit;
+		}
+
+		/* The first byte determines what is legal for
+		 * the second byte. */
+		if (*p == '\xE0') {
+			/* The next byte must be %xA0-BF. */
+			p++;
+			if ((*p < '\xA0') || (*p > '\xBF')) {
+				rc = 1;
+				goto exit;
+			}
+		} else if (*p == '\xED') {
+			/* The next byte must be %x80-9F. */
+			p++;
+			if ((*p < '\x80') || (*p > '\x9F')) {
+				rc = 1;
+				goto exit;
+			}
+		} else {
+			/* The next byte must each be 'UTF0'. */
+			p++;
+			if (!IS_UTF0(*p)) {
+				rc = 1;
+				goto exit;
+			}
+		}
+
+		/* The last byte must be 'UTF0'. */
+		p++;
+		if (!IS_UTF0(*p)) {
+			rc = 1;
+			goto exit;
+		}
+	} else if (IS_UTF4(*p)) {
+		/* Ensure that there are at least 3 more bytes. */
+		if (end - p < 3) {
+			rc = 1;
+			goto exit;
+		}
+
+		/* The first byte determines what is legal for
+		 * the second byte. */
+		if (*p == '\xF0') {
+			/* The next byte must be %x90-BF. */
+			if ((*p < '\x90') || (*p > '\xBF')) {
+				rc = 1;
+				goto exit;
+			}
+		} else if (*p == '\xF4') {
+			/* The next byte must be %x80-BF. */
+			if ((*p < '\x80') || (*p > '\xBF')) {
+				rc = 1;
+				goto exit;
+			}
+		} else {
+			/* The next byte must each be 'UTF0'. */
+			p++;
+			if (!IS_UTF0(*p)) {
+				rc = 1;
+				goto exit;
+			}
+		}
+
+		/* The last 2 bytes must be 'UTF0'. */
+		p++;
+		if (!IS_UTF0(*p) || !IS_UTF0(*(p + 1))) {
+			rc = 1;
+			goto exit;
+		}
+
+		/* Advance the pointer so last is set correctly
+		 * when we return. */
+		p++;
+	} else {
+		/* We found an illegal first byte. */
+		rc = 1;
+		goto exit;
+	}
+
+exit:
+	if (last) {
+		*last = p;
+	}
+	return(rc);
+}
+
+/* Validates that a non '\0' terminated string is UTF8.  This
+ * function will not read past the byte pointed to by the end
+ * parameter.  The last pointer will be filled in to point to
+ * the address of the last byte of the last validated character
+ * if the string is valid, or the last byte processed in the
+ * invalid case.
+ *
+ * Returns 0 if it is valid and non-zero otherwise. */
+int utf8string_validate(
+        char *begin,
+        char *end,
+        char **last
+)
+{
+        int rc = 0; /* Assume string is valid */
+        char *p = NULL;
+
+        if ((begin == NULL) || (end == NULL)) {
+                rc = 1;
+                goto exit;
+        }
+
+	for (p = begin; p <= end; p++) {
+		if ((rc = utf8char_validate(p, end, &p)) != 0) {
+			goto exit;
+		}
+	}
+
+	/* Adjust the pointer so last is set correctly for caller. */
+	p--;
+
+exit:
+	if (last) {
+		*last = p;
+	}
+	return(rc);
+}
+
diff --git a/ldap/servers/plugins/syntaxes/validate_task.c b/ldap/servers/plugins/syntaxes/validate_task.c
new file mode 100644
index 0000000..d469ccd
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/validate_task.c
@@ -0,0 +1,303 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ * 
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ * 
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception. 
+ * 
+ * 
+ * Copyright (C) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/* validate_task.c - syntax validation task */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+/*
+ * Globals
+ */
+static Slapi_PluginDesc pdesc = { "syntax-validate-task", PLUGIN_MAGIC_VENDOR_STR,
+        PRODUCTTEXT, "syntax validation task plugin" };
+static void* _PluginID = NULL;
+
+
+/*
+ * Data Structures
+ */
+typedef struct _task_data
+{
+	char *dn;
+	char *filter_str;
+	Slapi_Counter *invalid_entries;
+} task_data;
+
+
+/*
+ * Function Prototypes
+ */
+int syntax_validate_task_init(Slapi_PBlock *pb);
+static int syntax_validate_task_start(Slapi_PBlock *pb);
+static int syntax_validate_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
+                           Slapi_Entry *eAfter, int *returncode,
+                           char *returntext, void *arg);
+static void syntax_validate_task_destructor(Slapi_Task *task);
+static void syntax_validate_task_thread(void *arg);
+static int syntax_validate_task_callback(Slapi_Entry *e, void *callback_data);
+static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
+                              const char *default_val);
+static void syntax_validate_set_plugin_id(void * plugin_id);
+static void *syntax_validate_get_plugin_id();
+
+
+/*
+ * Function Implementations
+ */
+int
+syntax_validate_task_init(Slapi_PBlock *pb)
+{
+	int rc = 0;
+	char *syntax_validate_plugin_identity = NULL;
+
+	/* Save plugin ID. */
+	slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &syntax_validate_plugin_identity);
+	PR_ASSERT (syntax_validate_plugin_identity);
+	syntax_validate_set_plugin_id(syntax_validate_plugin_identity);
+
+	/* Register task callback. */
+	rc = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+	                      (void *) SLAPI_PLUGIN_VERSION_03 );
+	rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+	                       (void *) syntax_validate_task_start );
+
+	return rc;
+}
+
+static int
+syntax_validate_task_start(Slapi_PBlock *pb)
+{
+	int rc = slapi_task_register_handler("syntax validate", syntax_validate_task_add);
+	return rc;
+}
+
+static int
+syntax_validate_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
+                Slapi_Entry *eAfter, int *returncode,
+                char *returntext, void *arg)
+{
+	PRThread *thread = NULL;
+	int rv = SLAPI_DSE_CALLBACK_OK;
+	task_data *mytaskdata = NULL;
+	Slapi_Task *task = NULL;
+	const char *filter;
+	const char *dn = 0;
+
+	*returncode = LDAP_SUCCESS;
+	/* get arg(s) */
+	if ((dn = fetch_attr(e, "basedn", 0)) == NULL) {
+		*returncode = LDAP_OBJECT_CLASS_VIOLATION;
+		rv = SLAPI_DSE_CALLBACK_ERROR;
+		goto out;
+	}
+
+	if ((filter = fetch_attr(e, "filter", "(objectclass=*)")) == NULL) {
+		*returncode = LDAP_OBJECT_CLASS_VIOLATION;
+		rv = SLAPI_DSE_CALLBACK_ERROR;
+		goto out;
+	}
+
+	/* setup our task data */
+	mytaskdata = (task_data*)slapi_ch_malloc(sizeof(task_data));
+	if (mytaskdata == NULL) {
+		*returncode = LDAP_OPERATIONS_ERROR;
+		rv = SLAPI_DSE_CALLBACK_ERROR;
+		goto out;
+	}
+	mytaskdata->dn = slapi_ch_strdup(dn);
+	mytaskdata->filter_str = slapi_ch_strdup(filter);
+	mytaskdata->invalid_entries = slapi_counter_new();
+
+	/* allocate new task now */
+	task = slapi_new_task(slapi_entry_get_ndn(e));
+
+	/* register our destructor for cleaning up our private data */
+	slapi_task_set_destructor_fn(task, syntax_validate_task_destructor);
+
+	/* Stash a pointer to our data in the task */
+	slapi_task_set_data(task, mytaskdata);
+
+	/* start the sample task as a separate thread */
+	thread = PR_CreateThread(PR_USER_THREAD, syntax_validate_task_thread,
+		(void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+		PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+	if (thread == NULL) {
+		slapi_log_error( SLAPI_LOG_FATAL, SYNTAX_PLUGIN_SUBSYSTEM,
+			"unable to create task thread!\n");
+		*returncode = LDAP_OPERATIONS_ERROR;
+		rv = SLAPI_DSE_CALLBACK_ERROR;
+		slapi_task_finish(task, *returncode);
+	} else {
+		rv = SLAPI_DSE_CALLBACK_OK;
+	}
+
+out:
+	return rv;
+}
+
+static void
+syntax_validate_task_destructor(Slapi_Task *task)
+{
+	if (task) {
+		task_data *mydata = (task_data *)slapi_task_get_data(task);
+		if (mydata) {
+			slapi_ch_free_string(&mydata->dn);
+			slapi_ch_free_string(&mydata->filter_str);
+			slapi_counter_destroy(&mydata->invalid_entries);
+			/* Need to cast to avoid a compiler warning */
+			slapi_ch_free((void **)&mydata);
+		}
+	}
+}
+
+static void
+syntax_validate_task_thread(void *arg)
+{
+	int rc = 0;
+	Slapi_Task *task = (Slapi_Task *)arg;
+	task_data *td = NULL;
+	Slapi_PBlock *search_pb = slapi_pblock_new();
+
+	/* Fetch our task data from the task */
+	td = (task_data *)slapi_task_get_data(task);
+
+	/* Log started message. */
+	slapi_task_begin(task, 1);
+	slapi_task_log_notice(task, "Syntax validation task starting (arg: %s) ...\n",
+	                      td->filter_str);
+	slapi_log_error(SLAPI_LOG_FATAL, SYNTAX_PLUGIN_SUBSYSTEM,
+	                "Syntax validate task starting (base: \"%s\", filter: \"%s\") ...\n",
+	                td->dn, td->filter_str);
+
+	/* Perform the search and use a callback
+	 * to validate each matching entry. */
+        slapi_search_internal_set_pb(search_pb, td->dn,
+                LDAP_SCOPE_SUBTREE, td->filter_str, 0, 0,
+                0, 0, syntax_validate_get_plugin_id(), 0);
+
+        rc = slapi_search_internal_callback_pb(search_pb,
+                td, 0, syntax_validate_task_callback, 0);
+
+        slapi_pblock_destroy(search_pb);
+
+	/* Log finished message. */
+	slapi_task_log_notice(task, "Syntax validate task complete.  Found %" NSPRIu64
+	                      " invalid entries.\n", slapi_counter_get_value(td->invalid_entries));
+	slapi_task_log_status(task, "Syntax validate task complete.  Found %" NSPRIu64
+	                      " invalid entries.\n", slapi_counter_get_value(td->invalid_entries));
+	slapi_log_error(SLAPI_LOG_FATAL, SYNTAX_PLUGIN_SUBSYSTEM, "Syntax validate task complete."
+	                "  Found %" NSPRIu64 " invalid entries.\n",
+	                slapi_counter_get_value(td->invalid_entries));
+	slapi_task_inc_progress(task);
+
+	/* this will queue the destruction of the task */
+	slapi_task_finish(task, rc);
+}
+
+static int
+syntax_validate_task_callback(Slapi_Entry *e, void *callback_data)
+{
+        int rc = 0;
+        char *dn = slapi_entry_get_dn(e);
+	task_data *td = (task_data *)callback_data;
+	Slapi_PBlock *pb = NULL;
+
+	/* Override the syntax checking config to force syntax checking. */
+	if (slapi_entry_syntax_check(NULL, e, 1) != 0) {
+		char *error_text = NULL;
+
+		/* We need a pblock to get more details on the syntax violation,
+		 * but we don't want to allocate a pblock unless we need it for
+		 * performance reasons.  This means that we will actually call
+		 * slapi_entry_syntax_check() twice for entries that have a
+		 * syntax violation. */
+		pb = slapi_pblock_new();
+		slapi_entry_syntax_check(pb, e, 1);
+		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &error_text);
+		slapi_log_error(SLAPI_LOG_FATAL, SYNTAX_PLUGIN_SUBSYSTEM,
+		                "Entry \"%s\" violates syntax.\n%s",
+		                dn, error_text);
+		slapi_pblock_destroy(pb);
+
+		/* Keep a tally of the number of invalid entries found. */
+		slapi_counter_increment(td->invalid_entries);
+	}
+
+	return rc;
+}
+
+/* extract a single value from the entry (as a string) -- if it's not in the
+ * entry, the default will be returned (which can be NULL).
+ * you do not need to free anything returned by this.
+ */
+static const char *
+fetch_attr(Slapi_Entry *e, const char *attrname,
+           const char *default_val)
+{
+Slapi_Attr *attr;
+Slapi_Value *val = NULL;
+
+	if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
+		return default_val;
+	}
+
+	slapi_attr_first_value(attr, &val);
+
+	return slapi_value_get_string(val);
+}
+
+/*
+ * Plug-in identity management helper functions
+ */
+static void
+syntax_validate_set_plugin_id(void * plugin_id)
+{
+        _PluginID=plugin_id;
+}
+
+static void *
+syntax_validate_get_plugin_id()
+{
+        return _PluginID;
+}
diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c
index b69da2e..6607eff 100644
--- a/ldap/servers/slapd/add.c
+++ b/ldap/servers/slapd/add.c
@@ -800,6 +800,16 @@ static void handle_fast_add(Slapi_PBlock *pb, Slapi_Entry *entry)
         return;
     }
 
+    /* syntax check */
+    if (slapi_entry_syntax_check(pb, entry, 0) != 0) {
+        char *errtext;
+        LDAPDebug(LDAP_DEBUG_TRACE, "entry failed syntax check\n", 0, 0, 0);
+        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+        send_ldap_result(pb, LDAP_INVALID_SYNTAX, NULL, errtext, 0, NULL);
+        slapi_entry_free(entry);
+        return;
+    }
+
     /* Check if the entry being added is a Tombstone. Could be if we are
      * doing a replica init. */
     if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS,
diff --git a/ldap/servers/slapd/back-ldbm/import-threads.c b/ldap/servers/slapd/back-ldbm/import-threads.c
index 7a1bcba..7cde2bf 100644
--- a/ldap/servers/slapd/back-ldbm/import-threads.c
+++ b/ldap/servers/slapd/back-ldbm/import-threads.c
@@ -534,9 +534,27 @@ void import_producer(void *param)
                               "violates schema, ending line %d of file "
                               "\"%s\"", escape_string(slapi_entry_get_dn(e), ebuf),
                               curr_lineno, curr_filename);
-            if (e)
+            if (e) {
+                slapi_entry_free(e);
+	    }
+
+            job->skipped++;
+            continue;
+        }
+
+        /* Check attribute syntax */
+        if (slapi_entry_syntax_check(NULL, e, 0) != 0)
+        {
+            char ebuf[BUFSIZ];
+            import_log_notice(job, "WARNING: skipping entry \"%s\" which "
+                              "violates attribute syntax, ending line %d of "
+                              "file \"%s\"", escape_string(slapi_entry_get_dn(e), ebuf),
+                              curr_lineno, curr_filename);
+            if (e) {
                 slapi_entry_free(e);
-                job->skipped++;
+            }
+
+            job->skipped++;
             continue;
         }
 
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
index 764cff9..b9f573a 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -305,6 +305,15 @@ ldbm_back_add( Slapi_PBlock *pb )
 		goto error_return;
 	} 
 
+	/* Check attribute syntax */
+	if (slapi_entry_syntax_check(pb, e, 0) != 0)
+	{
+		LDAPDebug(LDAP_DEBUG_TRACE, "entry failed syntax check\n", 0, 0, 0);
+		ldap_result_code = LDAP_INVALID_SYNTAX;
+		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+		goto error_return;
+	}
+
 	opcsn = operation_get_csn (operation);
 	if(is_resurect_operation)
 	{
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
index c169e9e..1cbe92d 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -188,6 +188,7 @@ ldbm_back_modify( Slapi_PBlock *pb )
 	struct backentry	*e, *ec = NULL;
 	Slapi_Entry		*postentry = NULL;
 	LDAPMod			**mods;
+	Slapi_Mods smods;
 	back_txn txn;
 	back_txnid		parent_txn;
 	int			retval = -1;
@@ -279,11 +280,10 @@ ldbm_back_modify( Slapi_PBlock *pb )
 	slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
 	/* The Plugin may have messed about with some of the PBlock parameters... ie. mods */
 	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+	slapi_mods_init_byref(&smods,mods);
 
 	{
-		Slapi_Mods smods;
 		CSN *csn = operation_get_csn(operation);
-		slapi_mods_init_byref(&smods,mods);
 		if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) {
 			ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn, operation_is_flag_set(operation,OP_FLAG_REPLICATED));
 			/*
@@ -301,7 +301,6 @@ ldbm_back_modify( Slapi_PBlock *pb )
 			slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, postentry );
 			postentry = NULL;	/* avoid removal/free in error_return code */
 		}
-		slapi_mods_done(&smods);
 		if ( !change_entry || ldap_result_code != 0 ) {
 			/* change_entry == 0 is not an error, but we need to free lock etc */
 			goto error_return;
@@ -340,6 +339,14 @@ ldbm_back_modify( Slapi_PBlock *pb )
 		goto error_return;
 	}
 
+	/* check attribute syntax for the new values */
+	if (slapi_mods_syntax_check(pb, mods, 0) != 0)
+	{
+		ldap_result_code = LDAP_INVALID_SYNTAX;
+		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+		goto error_return;
+	}
+
 	/*
 	 * make sure the entry contains all values in the RDN.
 	 * if not, the modification must have removed them.
@@ -506,6 +513,7 @@ error_return:
 
 	
 common_return:
+	slapi_mods_done(&smods);
 	
 	if (ec_in_cache)
 	{
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
index 1cb35ab..c71dd8e 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
@@ -530,6 +530,17 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
 		goto error_return;
 	}
 
+	/* Check attribute syntax if any new values are being added for the new RDN */
+	if (slapi_mods_get_num_mods(&smods_operation_wsi)>0)
+	{
+		if (slapi_mods_syntax_check(pb, smods_generated_wsi.mods, 0) != 0)
+		{
+			ldap_result_code = LDAP_INVALID_SYNTAX;
+			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+			goto error_return;
+		}
+	}
+
 	/*
 	 * Update the DN CSN of the entry.
 	 */
diff --git a/ldap/servers/slapd/back-ldif/add.c b/ldap/servers/slapd/back-ldif/add.c
index 231f548..2779997 100644
--- a/ldap/servers/slapd/back-ldif/add.c
+++ b/ldap/servers/slapd/back-ldif/add.c
@@ -92,6 +92,13 @@ ldif_back_add( Slapi_PBlock *pb )
     return( -1 );
   }
 
+  /* Check if the attribute values in the entry obey the syntaxes */
+  if ( slapi_entry_syntax_check( pb, e, 0 ) != 0 ) {
+    LDAPDebug( LDAP_DEBUG_TRACE, "entry failed syntax_check\n", 0, 0, 0 );
+    slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, NULL, 0, NULL );
+    return( -1 );
+  }
+
   prev = NULL;
   
   /*Lock the database*/
diff --git a/ldap/servers/slapd/back-ldif/modify.c b/ldap/servers/slapd/back-ldif/modify.c
index 58229ec..7fff067 100644
--- a/ldap/servers/slapd/back-ldif/modify.c
+++ b/ldap/servers/slapd/back-ldif/modify.c
@@ -140,6 +140,13 @@ ldif_back_modify( Slapi_PBlock *pb )
     PR_Unlock( db->ldif_lock );
     goto error_return;
   }
+
+  /* Check if the attribute values in the mods obey the syntaxes */
+  if ( slapi_mods_syntax_check( pb, mods, 0 ) != 0 ) {
+    slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, NULL, 0, NULL );
+    PR_Unlock( db->ldif_lock );
+    goto error_return;
+  }
   
   /* Check for abandon again */
   if ( slapi_op_abandoned( pb ) ) {
diff --git a/ldap/servers/slapd/config.c b/ldap/servers/slapd/config.c
index 9cf56dd..1af1b77 100644
--- a/ldap/servers/slapd/config.c
+++ b/ldap/servers/slapd/config.c
@@ -239,11 +239,13 @@ slapd_bootstrap_config(const char *configdir)
 			char _localuser[BUFSIZ];
 			char logenabled[BUFSIZ];
 			char schemacheck[BUFSIZ];
+			char syntaxcheck[BUFSIZ];
+			char syntaxlogging[BUFSIZ];
 			Slapi_DN plug_dn;
 
 			workpath[0] = loglevel[0] = maxdescriptors[0] = '\0';
-			val[0] = logenabled[0] = schemacheck[0] = '\0';
-			_localuser[0] = '\0';
+			val[0] = logenabled[0] = schemacheck[0] = syntaxcheck[0] = '\0';
+			syntaxlogging[0] = _localuser[0] = '\0';
 
 			/* Convert LDIF to entry structures */
 			slapi_sdn_init_dn_byref(&plug_dn, PLUGIN_BASE_DN);
@@ -460,6 +462,34 @@ slapd_bootstrap_config(const char *configdir)
 					}
 				}
 
+				/* see if we need to enable syntax checking */
+				if (!syntaxcheck[0] &&
+				    entry_has_attr_and_value(e, CONFIG_SYNTAXCHECK_ATTRIBUTE,
+				    syntaxcheck, sizeof(syntaxcheck)))
+				{
+					if (config_set_syntaxcheck(CONFIG_SYNTAXCHECK_ATTRIBUTE,
+					                           syntaxcheck, errorbuf, CONFIG_APPLY)
+						                   != LDAP_SUCCESS)
+					{
+						LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+						          CONFIG_SYNTAXCHECK_ATTRIBUTE, errorbuf);
+					}
+				}
+
+				/* see if we need to enable syntax warnings */
+				if (!syntaxlogging[0] &&
+				    entry_has_attr_and_value(e, CONFIG_SYNTAXLOGGING_ATTRIBUTE,
+				    syntaxlogging, sizeof(syntaxlogging)))
+				{
+					if (config_set_syntaxlogging(CONFIG_SYNTAXLOGGING_ATTRIBUTE,
+					                          syntaxlogging, errorbuf, CONFIG_APPLY)
+					                          != LDAP_SUCCESS)
+					{
+						LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+						          CONFIG_SYNTAXLOGGING_ATTRIBUTE, errorbuf);
+					}
+				}
+
 				/* see if we need to expect quoted schema values */
 				if (entry_has_attr_and_value(e, CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE,
 											 val, sizeof(val)))
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
index 4d59350..956c29d 100644
--- a/ldap/servers/slapd/dse.c
+++ b/ldap/servers/slapd/dse.c
@@ -1864,6 +1864,17 @@ dse_modify(Slapi_PBlock *pb) /* JCM There should only be one exit point from thi
         return dse_modify_return( -1, ec, ecc );
     }
 
+    /* Check if the attribute values in the mods obey the syntaxes */
+    if ( slapi_mods_syntax_check( pb, mods, 0 ) != 0 )
+    {
+        char *errtext;
+
+        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+        slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, errtext, 0, NULL );
+        slapi_sdn_done(&sdn);
+        return dse_modify_return( -1, ec, ecc );
+    }
+
     /* Change the entry itself both on disk and in the AVL tree */
     /* dse_replace_entry free's the existing entry. */
     if (dse_replace_entry( pdse, ecc, !dont_write_file, DSE_USE_LOCK )!=0 )
@@ -1941,6 +1952,18 @@ dse_add(Slapi_PBlock *pb) /* JCM There should only be one exit point from this f
         return error;
     }
 
+    /* Check if the attribute values in the entry obey the syntaxes */
+    if ( slapi_entry_syntax_check( pb, e, 0 ) != 0 )
+    {
+        char *errtext;
+        LDAPDebug( SLAPI_DSE_TRACELEVEL,
+                                "dse_add: entry failed syntax check\n", 0, 0, 0 );
+        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+        slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, errtext, 0, NULL );
+        slapi_sdn_done(&sdn);
+        return error;
+    }
+
     /*
      * Attempt to find this dn.
      */
diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c
index f71b7fd..beec7d5 100644
--- a/ldap/servers/slapd/fedse.c
+++ b/ldap/servers/slapd/fedse.c
@@ -143,14 +143,7 @@ static const char *internal_entries[] =
 	"objectclass:top\n"
 	"objectclass:nsSNMP\n"
 	"cn:SNMP\n"
-	"nsSNMPEnabled:on\n"
-	"nsSNMPName:\n"
-	"nsSNMPOrganization:\n"
-	"nsSNMPLocation:\n"
-	"nsSNMPContact:\n"
-	"nsSNMPDescription:\n"
-	"nsSNMPMasterHost:\n"
-	"nsSNMPMasterPort:\n"
+	"nsSNMPEnabled: on\n"
     "aci:(target=\"ldap:///cn=SNMP,cn=config\";)(targetattr !=\"aci\")(version 3.0;acl \"snmp\";allow (read, search, compare)(userdn = \"ldap:///anyone\";);)\n",
 };
 
@@ -161,7 +154,7 @@ static char *easter_egg_entry=
 "1E14405A150F47341F0E09191B0A1F5A3E13081F190E1508035A2E1F1B1756191447171514"
 "130E1508701518101F190E39161B0909405A0E150A701518101F190E39161B0909405A1508"
 "1D1B1413001B0E1315141B162F14130E701518101F190E39161B0909405A1E13081F190E15"
-"0803040E1F1B17041F020E1F14091318161F041518101F190E70150F405A341F0E09191B0A"
+"0803570E1F1B17571F020E1F14091318161F571518101F190E70150F405A341F0E09191B0A"
 "1F5A291F190F08130E035A2915160F0E1315140970150F405A341F0E09191B0A1F5A3E1308"
 "1F190E1508035A2E1F1B17701E1F091908130A0E131514405A3E1B0C131E5A3815081F121B"
 "17565A301B190B0F1F1613141F5A3815081F121B17565A3B140E121514035A3C15020D1508"
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index e473663..30ad5f3 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -321,6 +321,12 @@ static struct config_get_and_set {
 	{CONFIG_SCHEMACHECK_ATTRIBUTE, config_set_schemacheck,
 		NULL, 0,
 		(void**)&global_slapdFrontendConfig.schemacheck, CONFIG_ON_OFF, NULL},
+	{CONFIG_SYNTAXCHECK_ATTRIBUTE, config_set_syntaxcheck,
+		NULL, 0,
+		(void**)&global_slapdFrontendConfig.syntaxcheck, CONFIG_ON_OFF, NULL},
+	{CONFIG_SYNTAXLOGGING_ATTRIBUTE, config_set_syntaxlogging,
+		NULL, 0,
+		(void**)&global_slapdFrontendConfig.syntaxlogging, CONFIG_ON_OFF, NULL},
 	{CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE, config_set_ds4_compatible_schema,
 		NULL, 0,
 		(void**)&global_slapdFrontendConfig.ds4_compatible_schema,
@@ -891,6 +897,8 @@ FrontendConfig_init () {
   cfg->sizelimit = SLAPD_DEFAULT_SIZELIMIT;
   cfg->timelimit = SLAPD_DEFAULT_TIMELIMIT;
   cfg->schemacheck = LDAP_ON;
+  cfg->syntaxcheck = LDAP_OFF;
+  cfg->syntaxlogging = LDAP_OFF;
   cfg->ds4_compatible_schema = LDAP_OFF;
   cfg->enquote_sup_oc = LDAP_OFF;
   cfg->lastmod = LDAP_ON;
@@ -2422,6 +2430,33 @@ config_set_schemacheck( const char *attrname, char *value, char *errorbuf, int a
   return retVal;
 }
 
+int
+config_set_syntaxcheck( const char *attrname, char *value, char *errorbuf, int apply ) {
+  int retVal = LDAP_SUCCESS;
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+  retVal = config_set_onoff ( attrname,
+                              value,
+                              &(slapdFrontendConfig->syntaxcheck),
+                              errorbuf,
+                              apply);
+
+  return retVal;
+} 
+
+int
+config_set_syntaxlogging( const char *attrname, char *value, char *errorbuf, int apply ) {
+  int retVal = LDAP_SUCCESS;
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+  retVal = config_set_onoff ( attrname,
+                              value,
+                              &(slapdFrontendConfig->syntaxlogging),
+                              errorbuf,
+                              apply);
+
+  return retVal;
+}
 
 int
 config_set_ds4_compatible_schema( const char *attrname, char *value, char *errorbuf, int apply ) {
@@ -4034,6 +4069,30 @@ config_get_schemacheck() {
  }
 
 int
+config_get_syntaxcheck() {
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+  int retVal;
+
+  CFG_LOCK_READ(slapdFrontendConfig);
+  retVal = slapdFrontendConfig->syntaxcheck;
+  CFG_UNLOCK_READ(slapdFrontendConfig);
+
+  return retVal;
+}
+
+int
+config_get_syntaxlogging() {
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+  int retVal;
+
+  CFG_LOCK_READ(slapdFrontendConfig);
+  retVal = slapdFrontendConfig->syntaxlogging;
+  CFG_UNLOCK_READ(slapdFrontendConfig);
+
+  return retVal;
+}
+
+int
 config_get_ds4_compatible_schema() {
   slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
   int retVal;
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
index 6ac6aa8..062a87f 100644
--- a/ldap/servers/slapd/pblock.c
+++ b/ldap/servers/slapd/pblock.c
@@ -1072,6 +1072,12 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value )
 	case SLAPI_SYNTAX_SUBSTRLENS:
 		(*(int **)value) = pblock->pb_substrlens;
 		break;
+	case SLAPI_PLUGIN_SYNTAX_VALIDATE:
+		if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+			return( -1 );
+		}
+		(*(int *)value) = pblock->pb_plugin->plg_syntax_validate;
+		break;
 
 	/* controls we know about */
 	case SLAPI_MANAGEDSAIT:
@@ -2314,6 +2320,12 @@ slapi_pblock_set( Slapi_PBlock *pblock, int arg, void *value )
 	case SLAPI_SYNTAX_SUBSTRLENS:
 		pblock->pb_substrlens = (int *) value;
 		break;
+	case SLAPI_PLUGIN_SYNTAX_VALIDATE:
+		if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+			return( -1 );
+		}
+		pblock->pb_plugin->plg_syntax_validate = (IFP) value;
+		break;
 	case SLAPI_ENTRY_PRE_OP:
 		pblock->pb_pre_op_entry = (Slapi_Entry *) value;
 		break;
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index a7ad7df..5ae6356 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -1878,35 +1878,37 @@ plugin_add_descriptive_attributes( Slapi_Entry *e, struct slapdplugin *plugin )
 
 		if ( NULL == plugin )
 		{
+			/* This can happen for things such as disabled syntax plug-ins.  We
+			 * just treat this as a warning to allow the description attributes
+			 * to be set to a default value to avoid an objectclass violation. */
 			LDAPDebug(LDAP_DEBUG_PLUGIN,
-					"Error: failed to add descriptive values for plugin %s"
-					" (could not find plugin entry)\n",
+					"Warning: couldn't find plugin %s in global list. "
+					"Adding default descriptive values.\n",
 					slapi_entry_get_dn_const(e), 0, 0 );
-			return 1;		/* failure */
 		}
 	}
 
 
 	if (add_plugin_description(e, ATTR_PLUGIN_PLUGINID,
-							   plugin->plg_desc.spd_id))
+	                           plugin ? plugin->plg_desc.spd_id : NULL))
 	{
 		status = 1;
 	}
 
 	if (add_plugin_description(e, ATTR_PLUGIN_VERSION,
-							   plugin->plg_desc.spd_version))
+	                           plugin ? plugin->plg_desc.spd_version : NULL))
 	{
 		status = 1;
 	}
 
 	if (add_plugin_description(e, ATTR_PLUGIN_VENDOR,
-							   plugin->plg_desc.spd_vendor))
+	                           plugin ? plugin->plg_desc.spd_vendor: NULL))
 	{
 		status = 1;
 	}
 
 	if (add_plugin_description(e, ATTR_PLUGIN_DESC,
-							   plugin->plg_desc.spd_description))
+	                           plugin ? plugin->plg_desc.spd_description : NULL))
 	{
 		status = 1;
 	}
diff --git a/ldap/servers/slapd/plugin_syntax.c b/ldap/servers/slapd/plugin_syntax.c
index cb3cde9..3290a95 100644
--- a/ldap/servers/slapd/plugin_syntax.c
+++ b/ldap/servers/slapd/plugin_syntax.c
@@ -261,6 +261,183 @@ plugin_call_syntax_filter_sub_sv(
 	return( rc );
 }
 
+/* Checks if the values of all attributes in an entry are valid for the
+ * syntax specified for the attribute in question.  Setting override to
+ * 1 will force syntax checking to be performed, even if syntax checking
+ * is disabled in the config.  Setting override to 0 will obey the config
+ * settings.
+ *
+ * Returns 1 if there is a syntax violation and sets the error message
+ * appropriately.  Returns 0 if everything checks out fine.
+ */
+int
+slapi_entry_syntax_check(
+	Slapi_PBlock *pb, Slapi_Entry *e, int override
+)
+{
+	int ret = 0;
+	int i = 0;
+	int is_replicated_operation = 0;
+	int badval = 0;
+	int syntaxcheck = config_get_syntaxcheck();
+	int syntaxlogging = config_get_syntaxlogging();
+	Slapi_Attr *prevattr = NULL;
+	Slapi_Attr *a = NULL;
+	char errtext[ BUFSIZ ];
+	char *errp = &errtext[0];
+	size_t err_remaining = sizeof(errtext);
+
+	if (pb != NULL) {
+		slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+	}
+
+	/* If syntax checking and logging are off, or if this is a
+         * replicated operation, just return that the syntax is OK. */
+	if (((syntaxcheck == 0) && (syntaxlogging == 0) && (override == 0)) ||
+	    is_replicated_operation) {
+		goto exit;
+	}
+
+	i = slapi_entry_first_attr(e, &a);
+
+	while ((-1 != i) && a && (a->a_plugin != NULL)) {
+		/* If no validate function is available for this type, just
+		 * assume that the value is valid. */
+		if ( a->a_plugin->plg_syntax_validate != NULL ) {
+			int numvals = 0;
+
+			slapi_attr_get_numvalues(a, &numvals);
+			if ( numvals > 0 ) {
+				Slapi_Value *val = NULL;
+				const struct berval *bval = NULL;
+				int hint = slapi_attr_first_value(a, &val);
+
+				/* iterate through each value to check if it's valid */
+				while (val != NULL) {
+					bval = slapi_value_get_berval(val);
+					if ((a->a_plugin->plg_syntax_validate( bval )) != 0) {
+						if (syntaxlogging) {
+							slapi_log_error( SLAPI_LOG_FATAL, "Syntax Check",
+							                "\"%s\": (%s) value #%d invalid per syntax\n",
+							                slapi_entry_get_dn(e), a->a_type, hint );
+						}
+
+						if (syntaxcheck || override) {
+							if (pb) {
+								/* Append new text to any existing text. */
+								errp += PR_snprintf( errp, err_remaining,
+								    "%s: value #%d invalid per syntax\n", a->a_type, hint );
+								err_remaining -= errp - &errtext[0];
+							}
+							ret = 1;
+						}
+					}
+
+					hint = slapi_attr_next_value(a, hint, &val);
+				}
+			}
+		}
+
+		prevattr = a;
+		i = slapi_entry_next_attr(e, prevattr, &a);
+	}
+
+	/* See if we need to set the error text in the pblock. */
+	if (errp != &errtext[0]) {
+		slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+	}
+
+exit:
+	return( ret );
+}
+
+/* Checks if the values of all attributes being added in a Slapi_Mods
+ * are valid for the syntax specified for the attribute in question.
+ * The new values in an add or replace modify operation and the newrdn
+ * value for a modrdn operation will be checked.
+ * Returns 1 if there is a syntax violation and sets the error message
+ * appropriately.  Returns 0 if everything checks out fine.
+ */
+int
+slapi_mods_syntax_check(
+	Slapi_PBlock *pb, LDAPMod **mods, int override
+)
+{
+	int ret = 0;
+	int i, j = 0;
+	int is_replicated_operation = 0;
+	int badval = 0;
+	int syntaxcheck = config_get_syntaxcheck();
+	int syntaxlogging = config_get_syntaxlogging();
+	char errtext[ BUFSIZ ];
+	char *errp = &errtext[0];
+	size_t err_remaining = sizeof(errtext);
+	char *dn = NULL;
+	LDAPMod *mod = NULL;
+
+	if (mods == NULL) {
+		ret = 1;
+		goto exit;
+	}
+
+	if (pb != NULL) {
+		slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+		slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
+	}
+
+	/* If syntax checking and logging are  off, or if this is a
+	 * replicated operation, just return that the syntax is OK. */
+	if (((syntaxcheck == 0) && (syntaxlogging == 0) && (override == 0)) ||
+	    is_replicated_operation) {
+		goto exit;
+	}
+
+	/* Loop through mods */
+	for (i = 0; mods[i] != NULL; i++) {
+		mod = mods[i];
+
+		/* We only care about replace and add modify operations that
+		 * are truly adding new values to the entry. */
+		if ((SLAPI_IS_MOD_REPLACE(mod->mod_op) || SLAPI_IS_MOD_ADD(mod->mod_op)) &&
+		    (mod->mod_bvalues != NULL)) {
+			struct slapdplugin *syntax_plugin = NULL;
+
+			/* Find the plug-in for this type, then call it's
+			 * validate function.*/
+			slapi_attr_type2plugin(mod->mod_type, (void **)&syntax_plugin);
+			if ((syntax_plugin != NULL) && (syntax_plugin->plg_syntax_validate != NULL)) {
+				/* Loop through the values and validate each one */
+				for (j = 0; mod->mod_bvalues[j] != NULL; j++) {
+					if (syntax_plugin->plg_syntax_validate(mod->mod_bvalues[j]) != 0) {
+						if (syntaxlogging) {
+							slapi_log_error( SLAPI_LOG_FATAL, "Syntax Check", "\"%s\": (%s) value #%d invalid per syntax\n", 
+							    dn ? dn : "NULL", mod->mod_type, j );
+						}
+
+						if (syntaxcheck || override) {
+							if (pb) {
+								/* Append new text to any existing text. */
+								errp += PR_snprintf( errp, err_remaining,
+								    "%s: value #%d invalid per syntax\n", mod->mod_type, j );
+								err_remaining -= errp - &errtext[0];
+							}
+							ret = 1;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/* See if we need to set the error text in the pblock. */
+	if (errp != &errtext[0]) {
+		slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+	}
+
+exit:
+	return( ret );
+}
+
 SLAPI_DEPRECATED int
 slapi_call_syntax_values2keys( /* JCM SLOW FUNCTION */
     void		*vpi,
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 7c25b18..c561196 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -264,6 +264,8 @@ int config_set_accesscontrol( const char *attrname, char *value, char *errorbuf,
 int config_set_security( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_readonly( const char *attrname, char *value, 	char *errorbuf, int apply );
 int config_set_schemacheck( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_syntaxcheck( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_syntaxlogging( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_ds4_compatible_schema( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_schema_ignore_trailing_spaces( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_rootdn( const char *attrname, char *value, char *errorbuf, int apply );
@@ -406,6 +408,8 @@ int config_get_return_exact_case();
 int config_get_result_tweak();
 int config_get_security();
 int config_get_schemacheck();
+int config_get_syntaxcheck();
+int config_get_syntaxlogging();
 int config_get_ds4_compatible_schema();
 int config_get_schema_ignore_trailing_spaces();
 char *config_get_rootdn();
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
index 04b13d0..e331a94 100644
--- a/ldap/servers/slapd/schema.c
+++ b/ldap/servers/slapd/schema.c
@@ -3415,7 +3415,9 @@ read_at_ldif(const char *input, struct asyntaxinfo **asipp, char *errorbuf,
                 schema_errprefix_at, first_attr_name,
                 "Missing parent attribute syntax OID");
             status = invalid_syntax_error;
-        } else {
+        /* We only want to use the parent syntax if a SYNTAX
+         * wasn't explicitly specified for this attribute. */
+        } else if (NULL == pSyntax) {
             char *pso = plugin_syntax2oid(asi_parent->asi_plugin);
             
             if (pso) {
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index ceeb11e..cec186f 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -287,8 +287,8 @@ typedef void	(*VFP0)();
 #define SLAPD_SCHEMA_DN			"cn=schema"
 #define SLAPD_CONFIG_DN			"cn=config"
 
-#define EGG_OBJECT_CLASS		"directory~team~extensible~object"
-#define EGG_FILTER				"(objectclass=directory~team~extensible~object)"
+#define EGG_OBJECT_CLASS		"directory-team-extensible-object"
+#define EGG_FILTER				"(objectclass=directory-team-extensible-object)"
 
 #define BE_LIST_SIZE 100 /* used by mapping tree code to hold be_list stuff */
 
@@ -501,16 +501,17 @@ typedef int (*SyntaxEnumFunc)(char **names, Slapi_PluginDesc *plugindesc,
 
 /* OIDs for some commonly used syntaxes */
 #define BINARY_SYNTAX_OID    		"1.3.6.1.4.1.1466.115.121.1.5"
-#define BOOLEAN_SYNTAX_OID			"1.3.6.1.4.1.1466.115.121.1.7"
+#define BOOLEAN_SYNTAX_OID		"1.3.6.1.4.1.1466.115.121.1.7"
 #define COUNTRYSTRING_SYNTAX_OID	"1.3.6.1.4.1.1466.115.121.1.11"
 #define DN_SYNTAX_OID        		"1.3.6.1.4.1.1466.115.121.1.12"
 #define DIRSTRING_SYNTAX_OID		"1.3.6.1.4.1.1466.115.121.1.15"
 #define GENERALIZEDTIME_SYNTAX_OID	"1.3.6.1.4.1.1466.115.121.1.24"
 #define IA5STRING_SYNTAX_OID		"1.3.6.1.4.1.1466.115.121.1.26"
 #define INTEGER_SYNTAX_OID   		"1.3.6.1.4.1.1466.115.121.1.27"
-#define JPEG_SYNTAX_OID				"1.3.6.1.4.1.1466.115.121.1.28"
+#define JPEG_SYNTAX_OID			"1.3.6.1.4.1.1466.115.121.1.28"
+#define NUMERICSTRING_SYNTAX_OID	"1.3.6.1.4.1.1466.115.121.1.36"
+#define OID_SYNTAX_OID			"1.3.6.1.4.1.1466.115.121.1.38"
 #define OCTETSTRING_SYNTAX_OID		"1.3.6.1.4.1.1466.115.121.1.40"
-#define OID_SYNTAX_OID				"1.3.6.1.4.1.1466.115.121.1.38"
 #define POSTALADDRESS_SYNTAX_OID	"1.3.6.1.4.1.1466.115.121.1.41"
 #define TELEPHONE_SYNTAX_OID		"1.3.6.1.4.1.1466.115.121.1.50"
 #define SPACE_INSENSITIVE_STRING_SYNTAX_OID	"2.16.840.1.113730.3.7.1"
@@ -967,6 +968,7 @@ struct slapdplugin {
 			char	**plg_un_syntax_names;
 			char	*plg_un_syntax_oid;
 			IFP	plg_un_syntax_compare;
+			IFP	plg_un_syntax_validate;
 		} plg_un_syntax;
 #define plg_syntax_filter_ava		plg_un.plg_un_syntax.plg_un_syntax_filter_ava
 #define plg_syntax_filter_sub		plg_un.plg_un_syntax.plg_un_syntax_filter_sub
@@ -976,7 +978,8 @@ struct slapdplugin {
 #define plg_syntax_flags		plg_un.plg_un_syntax.plg_un_syntax_flags
 #define plg_syntax_names		plg_un.plg_un_syntax.plg_un_syntax_names
 #define plg_syntax_oid			plg_un.plg_un_syntax.plg_un_syntax_oid
-#define plg_syntax_compare			plg_un.plg_un_syntax.plg_un_syntax_compare
+#define plg_syntax_compare		plg_un.plg_un_syntax.plg_un_syntax_compare
+#define plg_syntax_validate		plg_un.plg_un_syntax.plg_un_syntax_validate
 
 		struct plg_un_acl_struct {
 			IFP	plg_un_acl_init;
@@ -1519,6 +1522,9 @@ typedef struct daemon_ports_s {
 /* Definition for plugin syntax compare routine */
 typedef int (*value_compare_fn_type)(const struct berval *,const struct berval *);
 
+/* Definition for plugin syntax validate routine */
+typedef int (*value_validate_fn_type)(const struct berval *);
+
 #include "pw.h"
 
 #include "proto-slap.h"
@@ -1631,6 +1637,8 @@ typedef struct _slapdEntryPoints {
 #define CONFIG_OBJECTCLASS_ATTRIBUTE    "nsslapd-objectclass"
 #define CONFIG_ATTRIBUTE_ATTRIBUTE      "nsslapd-attribute"
 #define CONFIG_SCHEMACHECK_ATTRIBUTE    "nsslapd-schemacheck"
+#define CONFIG_SYNTAXCHECK_ATTRIBUTE	"nsslapd-syntaxcheck"
+#define CONFIG_SYNTAXLOGGING_ATTRIBUTE	"nsslapd-syntaxlogging"
 #define CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE    "nsslapd-ds4-compatible-schema"
 #define CONFIG_SCHEMA_IGNORE_TRAILING_SPACES    "nsslapd-schema-ignore-trailing-spaces"
 #define CONFIG_SCHEMAREPLACE_ATTRIBUTE	"nsslapd-schemareplace"
@@ -1846,6 +1854,8 @@ typedef struct _slapdFrontendConfig {
   int readonly;
   int reservedescriptors;
   int schemacheck;
+  int syntaxcheck;
+  int syntaxlogging;
   int ds4_compatible_schema;
   int schema_ignore_trailing_spaces;
   int secureport;
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 3c0cf72..70556e9 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -280,6 +280,8 @@ int slapi_entry_next_attr( const Slapi_Entry *e, Slapi_Attr *prevattr, Slapi_Att
 const char *slapi_entry_get_uniqueid( const Slapi_Entry *e );
 void slapi_entry_set_uniqueid( Slapi_Entry *e, char *uniqueid );
 int slapi_entry_schema_check( Slapi_PBlock *pb, Slapi_Entry *e );
+int slapi_entry_syntax_check( Slapi_PBlock *pb, Slapi_Entry *e, int override );
+int slapi_mods_syntax_check( Slapi_PBlock *pb, LDAPMod **mods, int override );
 int slapi_entry_rdn_values_present( const Slapi_Entry *e );
 int slapi_entry_add_rdn_values( Slapi_Entry *e );
 int slapi_entry_attr_delete( Slapi_Entry *e, const char *type );
@@ -1702,9 +1704,9 @@ typedef struct slapi_plugindesc {
 #define SLAPI_PLUGIN_SYNTAX_OID			706
 #define SLAPI_PLUGIN_SYNTAX_FLAGS		707
 #define SLAPI_PLUGIN_SYNTAX_COMPARE		708
-
 /* user defined substrlen; not stored in slapdplugin, but pblock itself */
-#define SLAPI_SYNTAX_SUBSTRLENS	709
+#define SLAPI_SYNTAX_SUBSTRLENS			709
+#define SLAPI_PLUGIN_SYNTAX_VALIDATE		710
 
 /* ACL plugin functions and arguments */
 #define SLAPI_PLUGIN_ACL_INIT			730
-- 
1.5.5.1

--
Fedora-directory-devel mailing list
Fedora-directory-devel@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-directory-devel

[Index of Archives]     [Fedora Directory Announce]     [Fedora Users]     [Older Fedora Users Mail]     [Fedora Advisory Board]     [Fedora Security]     [Fedora Devel Java]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Mentors]     [Fedora Package Review]     [Fedora Art]     [Fedora Music]     [Fedora Packaging]     [CentOS]     [Fedora SELinux]     [Big List of Linux Books]     [KDE Users]     [Fedora Art]     [Fedora Docs]

  Powered by Linux