Browse Source

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
Nathan Kinder 16 years ago
parent
commit
d19eafcd21
35 changed files with 2352 additions and 62 deletions
  1. 21 7
      Makefile.am
  2. 3 0
      config.h.in
  3. 15 0
      configure.ac
  4. 163 0
      ldap/admin/src/scripts/template-syntax-validate.pl.in
  5. 23 2
      ldap/ldif/template-dse.ldif.in
  6. 2 2
      ldap/schema/60mozilla.ldif
  7. 4 2
      ldap/servers/plugins/syntaxes/bin.c
  8. 39 4
      ldap/servers/plugins/syntaxes/ces.c
  9. 469 13
      ldap/servers/plugins/syntaxes/cis.c
  10. 215 0
      ldap/servers/plugins/syntaxes/dn.c
  11. 56 0
      ldap/servers/plugins/syntaxes/int.c
  12. 188 0
      ldap/servers/plugins/syntaxes/numericstring.c
  13. 3 0
      ldap/servers/plugins/syntaxes/sicis.c
  14. 45 0
      ldap/servers/plugins/syntaxes/syntax.h
  15. 35 0
      ldap/servers/plugins/syntaxes/tel.c
  16. 352 0
      ldap/servers/plugins/syntaxes/validate.c
  17. 303 0
      ldap/servers/plugins/syntaxes/validate_task.c
  18. 10 0
      ldap/servers/slapd/add.c
  19. 20 2
      ldap/servers/slapd/back-ldbm/import-threads.c
  20. 9 0
      ldap/servers/slapd/back-ldbm/ldbm_add.c
  21. 11 3
      ldap/servers/slapd/back-ldbm/ldbm_modify.c
  22. 11 0
      ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
  23. 7 0
      ldap/servers/slapd/back-ldif/add.c
  24. 7 0
      ldap/servers/slapd/back-ldif/modify.c
  25. 32 2
      ldap/servers/slapd/config.c
  26. 23 0
      ldap/servers/slapd/dse.c
  27. 2 9
      ldap/servers/slapd/fedse.c
  28. 59 0
      ldap/servers/slapd/libglobs.c
  29. 12 0
      ldap/servers/slapd/pblock.c
  30. 9 7
      ldap/servers/slapd/plugin.c
  31. 177 0
      ldap/servers/slapd/plugin_syntax.c
  32. 4 0
      ldap/servers/slapd/proto-slap.h
  33. 3 1
      ldap/servers/slapd/schema.c
  34. 16 6
      ldap/servers/slapd/slap.h
  35. 4 2
      ldap/servers/slapd/slapi-plugin.h

+ 21 - 7
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' \

+ 3 - 0
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
 

+ 15 - 0
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])
 

+ 163 - 0
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);

+ 23 - 2
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

+ 2 - 2
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

+ 4 - 2
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"

+ 39 - 4
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;
+}

+ 469 - 13
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 );
+}
+

+ 215 - 0
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;
+}

+ 56 - 0
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);
+}

+ 188 - 0
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);
+}

+ 3 - 0
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>

+ 45 - 0
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

+ 35 - 0
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;
+}

+ 352 - 0
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);
+}
+

+ 303 - 0
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;
+}

+ 10 - 0
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,

+ 20 - 2
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;
         }
 

+ 9 - 0
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)
 	{

+ 11 - 3
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)
 	{

+ 11 - 0
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.
 	 */

+ 7 - 0
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*/

+ 7 - 0
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 ) ) {

+ 32 - 2
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)))

+ 23 - 0
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.
      */

+ 2 - 9
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"

+ 59 - 0
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 ) {
@@ -4033,6 +4068,30 @@ config_get_schemacheck() {
   return retVal;
  }
 
+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();

+ 12 - 0
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;

+ 9 - 7
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;
 	}

+ 177 - 0
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,

+ 4 - 0
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();

+ 3 - 1
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) {

+ 16 - 6
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;

+ 4 - 2
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