/* schema.c - routines to enforce schema definitions */
+/*
+ * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
#include "portable.h"
#include <stdio.h>
+#include <ac/ctype.h>
#include <ac/string.h>
#include <ac/socket.h>
-#include "ldapconfig.h"
+#include "ldap_defaults.h"
#include "slap.h"
static char * oc_check_required(Entry *e, char *ocname);
static int oc_check_allowed(char *type, struct berval **ocl);
+
/*
* oc_check - check that entry e conforms to the schema required by
* its object class(es). returns 0 if so, non-zero otherwise.
if ( (aoc = attr_find( e->e_attrs, "objectclass" )) == NULL ) {
Debug( LDAP_DEBUG_ANY, "No object class for entry (%s)\n",
e->e_dn, 0, 0 );
- return( 0 );
+ return( 1 );
}
/* check that the entry has required attrs for each oc */
return( NULL );
}
+static char *oc_usermod_attrs[] = {
+ /*
+ * OpenLDAP doesn't support any user modification of
+ * operational attributes.
+ */
+ NULL
+};
+
+static char *oc_operational_attrs[] = {
+ /*
+ * these are operational attributes
+ * most could be user modifiable
+ */
+ "objectClasses",
+ "attributeTypes",
+ "matchingRules",
+ "matchingRuleUse",
+ "dITStructureRules",
+ "dITContentRules",
+ "nameForms",
+ "ldapSyntaxes",
+ "namingContexts",
+ "supportedExtension",
+ "supportedControl",
+ "supportedSASLMechanisms",
+ "supportedLDAPversion",
+ "subschemaSubentry", /* NO USER MOD */
+ NULL
+
+};
+
+/* this list should be extensible */
+static char *oc_no_usermod_attrs[] = {
+ /*
+ * Operational and 'no user modification' attributes
+ * which are STORED in the directory server.
+ */
+
+ /* RFC2252, 3.2.1 */
+ "creatorsName",
+ "createTimestamp",
+ "modifiersName",
+ "modifyTimestamp",
+
+ NULL
+};
+
+
/*
* check to see if attribute is 'operational' or not.
- * this function should be externalized...
*/
-static int
-oc_check_operational( char *type )
+int
+oc_check_operational_attr( char *type )
+{
+ return charray_inlist( oc_operational_attrs, type )
+ || charray_inlist( oc_usermod_attrs, type )
+ || charray_inlist( oc_no_usermod_attrs, type );
+}
+
+/*
+ * check to see if attribute can be user modified or not.
+ */
+int
+oc_check_usermod_attr( char *type )
{
- return ( strcasecmp( type, "modifiersname" ) == 0 ||
- strcasecmp( type, "modifytimestamp" ) == 0 ||
- strcasecmp( type, "creatorsname" ) == 0 ||
- strcasecmp( type, "createtimestamp" ) == 0 )
- ? 1 : 0;
+ return charray_inlist( oc_usermod_attrs, type );
}
+/*
+ * check to see if attribute is 'no user modification' or not.
+ */
+int
+oc_check_no_usermod_attr( char *type )
+{
+ return charray_inlist( oc_no_usermod_attrs, type );
+}
+
+
static int
oc_check_allowed( char *type, struct berval **ocl )
{
return( 0 );
}
- if ( oc_check_operational( type ) ) {
+ /*
+ * All operational attributions are allowed by schema rules.
+ * However, we only check attributions which are stored in the
+ * the directory regardless if they are user or non-user modified.
+ */
+ if ( oc_check_usermod_attr( type ) || oc_check_no_usermod_attr( type ) ) {
return( 0 );
}
return code;
}
+static int
+case_exact_normalize(
+ struct berval *val,
+ struct berval **normalized
+)
+{
+ struct berval *newval;
+ char *p, *q;
+
+ newval = ber_bvdup( val );
+ p = q = newval->bv_val;
+ /* Ignore initial whitespace */
+ while ( isspace( *p++ ) )
+ ;
+ while ( *p ) {
+ if ( isspace( *p ) ) {
+ *q++ = *p++;
+ /* Ignore the extra whitespace */
+ while ( isspace(*p++) )
+ ;
+ } else {
+ *q++ = *p++;
+ }
+ }
+ /*
+ * If the string ended in space, backup the pointer one
+ * position. One is enough because the above loop collapsed
+ * all whitespace to a single space.
+ */
+ if ( p != newval->bv_val && isspace( *(p-1) ) ) {
+ *(q-1) = '\0';
+ }
+ newval->bv_len = strlen( newval->bv_val );
+ normalized = &newval;
+
+ return 0;
+}
+
+static int
+case_exact_compare(
+ struct berval *val1,
+ struct berval *val2
+)
+{
+ return strcmp( val1->bv_val, val2->bv_val );
+}
+
+int
+case_ignore_normalize(
+ struct berval *val,
+ struct berval **normalized
+)
+{
+ struct berval *newval;
+ char *p, *q;
+
+ newval = ber_bvdup( val );
+ p = q = newval->bv_val;
+ /* Ignore initial whitespace */
+ while ( isspace( *p++ ) )
+ ;
+ while ( *p ) {
+ if ( isspace( *p ) ) {
+ *q++ = *p++;
+ /* Ignore the extra whitespace */
+ while ( isspace(*p++) )
+ ;
+ } else {
+ *q++ = TOUPPER( *p++ );
+ }
+ }
+ /*
+ * If the string ended in space, backup the pointer one
+ * position. One is enough because the above loop collapsed
+ * all whitespace to a single space.
+ */
+ if ( p != newval->bv_val && isspace( *(p-1) ) ) {
+ *(q-1) = '\0';
+ }
+ newval->bv_len = strlen( newval->bv_val );
+ normalized = &newval;
+
+ return 0;
+}
+
+static int
+case_ignore_compare(
+ struct berval *val1,
+ struct berval *val2
+)
+{
+ return strcasecmp( val1->bv_val, val2->bv_val );
+}
+
int
register_syntax(
char * desc,
slap_mr_compare_func *mrd_compare;
};
+/*
+ * Other matching rules in X.520 that we do not use:
+ *
+ * 2.5.13.9 numericStringOrderingMatch
+ * 2.5.13.12 caseIgnoreListSubstringsMatch
+ * 2.5.13.13 booleanMatch
+ * 2.5.13.15 integerOrderingMatch
+ * 2.5.13.18 octetStringOrderingMatch
+ * 2.5.13.19 octetStringSubstringsMatch
+ * 2.5.13.25 uTCTimeMatch
+ * 2.5.13.26 uTCTimeOrderingMatch
+ * 2.5.13.31 directoryStringFirstComponentMatch
+ * 2.5.13.32 wordMatch
+ * 2.5.13.33 keywordMatch
+ * 2.5.13.34 certificateExactMatch
+ * 2.5.13.35 certificateMatch
+ * 2.5.13.36 certificatePairExactMatch
+ * 2.5.13.37 certificatePairMatch
+ * 2.5.13.38 certificateListExactMatch
+ * 2.5.13.39 certificateListMatch
+ * 2.5.13.40 algorithmIdentifierMatch
+ * 2.5.13.41 storedPrefixMatch
+ * 2.5.13.42 attributeCertificateMatch
+ * 2.5.13.43 readerAndKeyIDMatch
+ * 2.5.13.44 attributeIntegrityMatch
+ */
+
struct mrule_defs_rec mrule_defs[] = {
{"( 2.5.13.0 NAME 'objectIdentifierMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", NULL, NULL},
{"( 2.5.13.1 NAME 'distinguishedNameMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", NULL, NULL},
- {"( 2.5.13.2 NAME 'caseIgnoreMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", NULL, NULL},
- {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", NULL, NULL},
- {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", NULL, NULL},
+ {"( 2.5.13.2 NAME 'caseIgnoreMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ case_ignore_normalize, case_ignore_compare},
+ {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ case_ignore_normalize, case_ignore_compare},
+ {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+ case_ignore_normalize, case_ignore_compare},
+ /* Next three are not in the RFC's, but are needed for compatibility */
+ {"( 2.5.13.5 NAME 'caseExactMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ case_exact_normalize, case_exact_compare},
+ {"( 2.5.13.6 NAME 'caseExactOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ case_exact_normalize, case_exact_compare},
+ {"( 2.5.13.7 NAME 'caseExactSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+ case_exact_normalize, case_exact_compare},
{"( 2.5.13.8 NAME 'numericStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )", NULL, NULL},
{"( 2.5.13.10 NAME 'numericStringSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", NULL, NULL},
{"( 2.5.13.11 NAME 'caseIgnoreListMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", NULL, NULL},
{"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )", NULL, NULL},
{"( 2.5.13.29 NAME 'integerFirstComponentMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", NULL, NULL},
{"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", NULL, NULL},
- {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", NULL, NULL},
- {"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ case_exact_normalize, case_exact_compare},
+ {"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ case_ignore_normalize, case_ignore_compare},
{NULL, NULL, NULL}
};
schema_init( void )
{
int res;
- int code;
- const char *err;
int i;
static int schema_init_done = 0;
if ( res ) {
fprintf( stderr, "schema_init: Error registering syntax %s\n",
syntax_defs[i].sd_desc );
- exit( 1 );
+ exit( EXIT_FAILURE );
}
}
for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) {
res = register_matching_rule( mrule_defs[i].mrd_desc,
- mrule_defs[i].mrd_normalize,
- mrule_defs[i].mrd_compare );
+ ( mrule_defs[i].mrd_normalize ?
+ mrule_defs[i].mrd_normalize : case_ignore_normalize ),
+ ( mrule_defs[i].mrd_compare ?
+ mrule_defs[i].mrd_compare : case_ignore_compare ) );
if ( res ) {
fprintf( stderr, "schema_init: Error registering matching rule %s\n",
mrule_defs[i].mrd_desc );
- exit( 1 );
+ exit( EXIT_FAILURE );
}
}
schema_init_done = 1;
e->e_attrs = NULL;
e->e_dn = ch_strdup( SLAPD_SCHEMA_DN );
- e->e_ndn = dn_normalize_case( ch_strdup( SLAPD_SCHEMA_DN ));
+ e->e_ndn = ch_strdup( SLAPD_SCHEMA_DN );
+ (void) dn_normalize_case( e->e_ndn );
e->e_private = NULL;
- val.bv_val = ch_strdup( "top" );
- val.bv_len = strlen( val.bv_val );
- attr_merge( e, "objectclass", vals );
- ldap_memfree( val.bv_val );
+ {
+ char *rdn = ch_strdup( SLAPD_SCHEMA_DN );
+ val.bv_val = strchr( rdn, '=' );
+
+ if( val.bv_val != NULL ) {
+ *val.bv_val = '\0';
+ val.bv_len = strlen( ++val.bv_val );
- val.bv_val = ch_strdup( "subschema" );
- val.bv_len = strlen( val.bv_val );
- attr_merge( e, "objectclass", vals );
- ldap_memfree( val.bv_val );
+ attr_merge( e, rdn, vals );
+ }
+
+ free( rdn );
+ }
if ( syn_schema_info( e ) ) {
/* Out of memory, do something about it */
return;
}
- send_search_entry( &backends[0], conn, op, e, attrs, attrsonly );
- send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL, 1 );
+ val.bv_val = "top";
+ val.bv_len = sizeof("top")-1;
+ attr_merge( e, "objectClass", vals );
+
+ val.bv_val = "subschema";
+ val.bv_len = sizeof("subschema")-1;
+ attr_merge( e, "objectClass", vals );
+
+ val.bv_val = "extensibleObject";
+ val.bv_len = sizeof("extensibleObject")-1;
+ attr_merge( e, "objectClass", vals );
+
+ send_search_entry( &backends[0], conn, op,
+ e, attrs, attrsonly, NULL );
+ send_search_result( conn, op, LDAP_SUCCESS,
+ NULL, NULL, NULL, NULL, 1 );
entry_free( e );
}
}
#endif
+
+
+int is_entry_objectclass(
+ Entry* e,
+ char* oc)
+{
+ Attribute *attr;
+ struct berval bv;
+
+ if( e == NULL || oc == NULL || *oc == '\0' )
+ return 0;
+
+ /*
+ * find objectClass attribute
+ */
+ attr = attr_find(e->e_attrs, "objectclass");
+
+ if( attr == NULL ) {
+ /* no objectClass attribute */
+ return 0;
+ }
+
+ bv.bv_val = oc;
+ bv.bv_len = strlen( bv.bv_val );
+
+ if( value_find(attr->a_vals, &bv, attr->a_syntax, 1) != 0) {
+ /* entry is not of this objectclass */
+ return 0;
+ }
+
+ return 1;
+}