diff options
Diffstat (limited to 'ldap/servers/plugins/syntaxes/cis.c')
-rw-r--r-- | ldap/servers/plugins/syntaxes/cis.c | 482 |
1 files changed, 469 insertions, 13 deletions
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 ); +} + |