diff options
Diffstat (limited to 'ldap')
32 files changed, 2313 insertions, 55 deletions
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 |