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