diff options
| author | Nathan Kinder <nkinder@redhat.com> | 2009-05-08 09:14:42 -0700 |
|---|---|---|
| committer | Nathan Kinder <nkinder@redhat.com> | 2009-05-08 09:14:42 -0700 |
| commit | d19eafcd211d89cffdac1b2c3432087443e7d122 (patch) | |
| tree | 26d2b7f956c2ceaa3f605a42552a113e156b5b30 /ldap/servers/plugins/syntaxes | |
| parent | 5d3d883251dd15cf719181e33fb6954454869822 (diff) | |
| download | ds-d19eafcd211d89cffdac1b2c3432087443e7d122.tar.gz ds-d19eafcd211d89cffdac1b2c3432087443e7d122.tar.xz ds-d19eafcd211d89cffdac1b2c3432087443e7d122.zip | |
Added capability to validate syntax of values being added to the database. Also added numericstring syntax support.
For more details, see the design doc at http://directory.fedoraproject.org/wiki/Syntax_Validation_Design
Diffstat (limited to 'ldap/servers/plugins/syntaxes')
| -rw-r--r-- | ldap/servers/plugins/syntaxes/bin.c | 6 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/ces.c | 43 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/cis.c | 482 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/dn.c | 215 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/int.c | 56 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/numericstring.c | 188 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/sicis.c | 3 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/syntax.h | 45 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/tel.c | 35 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/validate.c | 352 | ||||
| -rw-r--r-- | ldap/servers/plugins/syntaxes/validate_task.c | 303 |
11 files changed, 1709 insertions, 19 deletions
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; +} |
