openldap/servers/slapd/schema.c

1555 lines
33 KiB
C
Raw Normal View History

1998-08-09 08:43:13 +08:00
/* schema.c - routines to enforce schema definitions */
/* $OpenLDAP$ */
1999-08-07 07:07:46 +08:00
/*
* Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
*/
1998-08-09 08:43:13 +08:00
1998-10-25 09:41:42 +08:00
#include "portable.h"
1998-08-09 08:43:13 +08:00
#include <stdio.h>
1998-10-25 09:41:42 +08:00
#include <ac/ctype.h>
1998-10-25 09:41:42 +08:00
#include <ac/string.h>
#include <ac/socket.h>
1998-08-09 08:43:13 +08:00
#include "slap.h"
#include "ldap_pvt.h"
1998-08-09 08:43:13 +08:00
static char * oc_check_required(Entry *e, char *ocname);
static int oc_check_allowed(char *type, struct berval **ocl);
1998-08-09 08:43:13 +08:00
/*
* oc_check - check that entry e conforms to the schema required by
* its object class(es). returns 0 if so, non-zero otherwise.
*/
int
oc_schema_check( Entry *e )
{
Attribute *a, *aoc;
ObjectClass *oc;
1998-08-09 08:43:13 +08:00
int i;
int ret = 0;
1998-08-09 08:43:13 +08:00
/* find the object class attribute - could error out here */
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( 1 );
1998-08-09 08:43:13 +08:00
}
/* check that the entry has required attrs for each oc */
for ( i = 0; aoc->a_vals[i] != NULL; i++ ) {
if ( (oc = oc_find( aoc->a_vals[i]->bv_val )) == NULL ) {
1998-08-09 08:43:13 +08:00
Debug( LDAP_DEBUG_ANY,
2000-01-02 03:40:06 +08:00
"Objectclass \"%s\" not defined\n",
aoc->a_vals[i]->bv_val, 0, 0 );
1998-08-09 08:43:13 +08:00
}
else
{
char *s = oc_check_required( e, aoc->a_vals[i]->bv_val );
if (s != NULL) {
Debug( LDAP_DEBUG_ANY,
"Entry (%s), oc \"%s\" requires attr \"%s\"\n",
e->e_dn, aoc->a_vals[i]->bv_val, s );
ret = 1;
}
}
1998-08-09 08:43:13 +08:00
}
if ( ret != 0 ) {
return( ret );
}
/* check that each attr in the entry is allowed by some oc */
for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
if ( oc_check_allowed( a->a_type, aoc->a_vals ) != 0 ) {
Debug( LDAP_DEBUG_ANY,
"Entry (%s), attr \"%s\" not allowed\n",
1998-08-09 08:43:13 +08:00
e->e_dn, a->a_type, 0 );
ret = 1;
}
}
return( ret );
}
static char *
1998-08-09 08:43:13 +08:00
oc_check_required( Entry *e, char *ocname )
{
1999-05-28 22:27:07 +08:00
ObjectClass *oc;
AttributeType *at;
int i;
1998-08-09 08:43:13 +08:00
Attribute *a;
1999-05-28 22:27:07 +08:00
char **pp;
Debug( LDAP_DEBUG_TRACE,
"oc_check_required entry (%s), objectclass \"%s\"\n",
e->e_dn, ocname, 0 );
1998-08-09 08:43:13 +08:00
/* find global oc defn. it we don't know about it assume it's ok */
if ( (oc = oc_find( ocname )) == NULL ) {
return( 0 );
}
1998-08-19 01:51:53 +08:00
/* check for empty oc_required */
1999-05-28 22:27:07 +08:00
if(oc->soc_required == NULL) {
1998-08-19 01:51:53 +08:00
return( 0 );
}
1998-08-09 08:43:13 +08:00
/* for each required attribute */
1999-05-28 22:27:07 +08:00
for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
at = oc->soc_required[i];
1998-08-09 08:43:13 +08:00
/* see if it's in the entry */
for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
if ( at->sat_oid &&
strcmp( a->a_type, at->sat_oid ) == 0 ) {
1999-05-28 22:27:07 +08:00
break;
}
pp = at->sat_names;
1999-05-28 22:27:07 +08:00
if ( pp == NULL ) {
/* Empty name list => not found */
a = NULL;
break;
}
while ( *pp ) {
if ( strcasecmp( a->a_type, *pp ) == 0 ) {
break;
}
pp++;
}
if ( *pp ) {
1998-08-09 08:43:13 +08:00
break;
}
}
/* not there => schema violation */
if ( a == NULL ) {
if ( at->sat_names && at->sat_names[0] ) {
return at->sat_names[0];
1999-05-28 22:27:07 +08:00
} else {
return at->sat_oid;
1999-05-28 22:27:07 +08:00
}
1998-08-09 08:43:13 +08:00
}
}
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",
"supportedACIMechanisms",
"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.
*/
int
1999-08-21 03:00:44 +08:00
oc_check_operational_attr( const char *type )
{
return charray_inlist( oc_operational_attrs, type )
|| charray_inlist( oc_usermod_attrs, type )
|| charray_inlist( oc_no_usermod_attrs, type );
1998-08-09 08:43:13 +08:00
}
/*
* check to see if attribute can be user modified or not.
*/
int
1999-08-21 03:00:44 +08:00
oc_check_usermod_attr( const char *type )
{
return charray_inlist( oc_usermod_attrs, type );
}
/*
* check to see if attribute is 'no user modification' or not.
*/
int
1999-08-21 03:00:44 +08:00
oc_check_no_usermod_attr( const char *type )
{
return charray_inlist( oc_no_usermod_attrs, type );
}
1998-08-09 08:43:13 +08:00
static int
oc_check_allowed( char *type, struct berval **ocl )
{
1999-05-28 22:27:07 +08:00
ObjectClass *oc;
AttributeType *at;
1998-08-09 08:43:13 +08:00
int i, j;
1999-05-28 22:27:07 +08:00
char **pp;
char *p, *t;
1999-05-28 22:27:07 +08:00
Debug( LDAP_DEBUG_TRACE,
"oc_check_allowed type \"%s\"\n", type, 0, 0 );
1998-08-09 08:43:13 +08:00
/* always allow objectclass attribute */
if ( strcasecmp( type, "objectclass" ) == 0 ) {
return( 0 );
}
/*
* 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 );
}
/*
* The "type" we have received is actually an AttributeDescription.
* Let's find out the corresponding type.
*/
p = strchr( type, ';' );
if ( p ) {
t = ch_malloc( p-type+1 );
strncpy( t, type, p-type );
t[p-type] = '\0';
Debug( LDAP_DEBUG_TRACE,
"oc_check_allowed type \"%s\" from \"%s\"\n",
t, type, 0 );
} else {
t = type;
}
1998-08-09 08:43:13 +08:00
/* check that the type appears as req or opt in at least one oc */
for ( i = 0; ocl[i] != NULL; i++ ) {
/* if we know about the oc */
if ( (oc = oc_find( ocl[i]->bv_val )) != NULL ) {
/* does it require the type? */
1999-05-28 22:27:07 +08:00
for ( j = 0; oc->soc_required != NULL &&
oc->soc_required[j] != NULL; j++ ) {
at = oc->soc_required[j];
if ( at->sat_oid &&
strcmp(at->sat_oid, t ) == 0 ) {
if ( t != type )
ldap_memfree( t );
1998-08-09 08:43:13 +08:00
return( 0 );
}
pp = at->sat_names;
1999-05-28 22:27:07 +08:00
if ( pp == NULL )
continue;
while ( *pp ) {
if ( strcasecmp( *pp, t ) == 0 ) {
if ( t != type )
ldap_memfree( t );
1999-05-28 22:27:07 +08:00
return( 0 );
}
pp++;
}
1998-08-09 08:43:13 +08:00
}
/* does it allow the type? */
1999-05-28 22:27:07 +08:00
for ( j = 0; oc->soc_allowed != NULL &&
oc->soc_allowed[j] != NULL; j++ ) {
at = oc->soc_allowed[j];
if ( at->sat_oid &&
strcmp( at->sat_oid, t ) == 0 ) {
if ( t != type )
ldap_memfree( t );
1998-08-09 08:43:13 +08:00
return( 0 );
}
pp = at->sat_names;
1999-05-28 22:27:07 +08:00
if ( pp == NULL )
continue;
while ( *pp ) {
if ( strcasecmp( *pp, t ) == 0 ||
1999-05-28 22:27:07 +08:00
strcmp( *pp, "*" ) == 0 ) {
if ( t != type )
ldap_memfree( t );
1999-05-28 22:27:07 +08:00
return( 0 );
}
pp++;
}
1998-08-09 08:43:13 +08:00
}
/* maybe the next oc allows it */
#ifdef OC_UNDEFINED_IMPLES_EXTENSIBLE
1998-08-09 08:43:13 +08:00
/* we don't know about the oc. assume it allows it */
} else {
if ( t != type )
ldap_memfree( t );
1998-08-09 08:43:13 +08:00
return( 0 );
#endif
1998-08-09 08:43:13 +08:00
}
}
if ( t != type )
ldap_memfree( t );
1998-08-09 08:43:13 +08:00
/* not allowed by any oc */
return( 1 );
}
1999-05-28 22:27:07 +08:00
struct oindexrec {
char *oir_name;
ObjectClass *oir_oc;
};
static Avlnode *oc_index = NULL;
static ObjectClass *oc_list = NULL;
1999-05-28 22:27:07 +08:00
static int
oc_index_cmp(
struct oindexrec *oir1,
struct oindexrec *oir2
)
{
return (strcasecmp( oir1->oir_name, oir2->oir_name ));
}
static int
oc_index_name_cmp(
char *name,
1999-05-28 22:27:07 +08:00
struct oindexrec *oir
)
{
return (strcasecmp( name, oir->oir_name ));
1999-05-28 22:27:07 +08:00
}
ObjectClass *
oc_find( const char *ocname )
1998-08-09 08:43:13 +08:00
{
1999-05-28 22:27:07 +08:00
struct oindexrec *oir = NULL;
if ( (oir = (struct oindexrec *) avl_find( oc_index, ocname,
(AVL_CMP) oc_index_name_cmp )) != NULL ) {
return( oir->oir_oc );
}
return( NULL );
}
static int
oc_create_required(
ObjectClass *soc,
char **attrs,
const char **err
1999-05-28 22:27:07 +08:00
)
{
char **attrs1;
AttributeType *sat;
AttributeType **satp;
int i;
if ( attrs ) {
attrs1 = attrs;
while ( *attrs1 ) {
sat = at_find(*attrs1);
if ( !sat ) {
*err = *attrs1;
return SLAP_SCHERR_ATTR_NOT_FOUND;
}
if ( at_find_in_list(sat, soc->soc_required) < 0) {
if ( at_append_to_list(sat, &soc->soc_required) ) {
*err = *attrs1;
return SLAP_SCHERR_OUTOFMEM;
}
}
attrs1++;
}
/* Now delete duplicates from the allowed list */
for ( satp = soc->soc_required; *satp; satp++ ) {
i = at_find_in_list(*satp,soc->soc_allowed);
if ( i >= 0 ) {
at_delete_from_list(i, &soc->soc_allowed);
}
}
}
return 0;
}
static int
oc_create_allowed(
ObjectClass *soc,
char **attrs,
const char **err
1999-05-28 22:27:07 +08:00
)
{
char **attrs1;
AttributeType *sat;
1998-08-09 08:43:13 +08:00
1999-05-28 22:27:07 +08:00
if ( attrs ) {
attrs1 = attrs;
while ( *attrs1 ) {
sat = at_find(*attrs1);
if ( !sat ) {
*err = *attrs1;
return SLAP_SCHERR_ATTR_NOT_FOUND;
}
if ( at_find_in_list(sat, soc->soc_required) < 0 &&
at_find_in_list(sat, soc->soc_allowed) < 0 ) {
if ( at_append_to_list(sat, &soc->soc_allowed) ) {
*err = *attrs1;
return SLAP_SCHERR_OUTOFMEM;
}
}
attrs1++;
1998-08-09 08:43:13 +08:00
}
}
1999-05-28 22:27:07 +08:00
return 0;
}
1998-08-09 08:43:13 +08:00
1999-05-28 22:27:07 +08:00
static int
oc_add_sups(
ObjectClass *soc,
char **sups,
const char **err
1999-05-28 22:27:07 +08:00
)
{
int code;
ObjectClass *soc1;
int nsups;
char **sups1;
int add_sups = 0;
if ( sups ) {
if ( !soc->soc_sups ) {
/* We are at the first recursive level */
add_sups = 1;
nsups = 0;
sups1 = sups;
while ( *sups1 ) {
nsups++;
sups1++;
}
nsups++;
soc->soc_sups = (ObjectClass **)ch_calloc(1,
nsups*sizeof(ObjectClass *));
}
nsups = 0;
sups1 = sups;
while ( *sups1 ) {
soc1 = oc_find(*sups1);
if ( !soc1 ) {
*err = *sups1;
return SLAP_SCHERR_CLASS_NOT_FOUND;
}
if ( add_sups )
soc->soc_sups[nsups] = soc1;
code = oc_add_sups(soc,soc1->soc_sup_oids, err);
1999-05-28 22:27:07 +08:00
if ( code )
return code;
1999-09-06 12:42:20 +08:00
code = oc_create_required(soc,soc1->soc_at_oids_must,err);
if ( code )
1999-05-28 22:27:07 +08:00
return code;
1999-09-06 12:42:20 +08:00
code = oc_create_allowed(soc,soc1->soc_at_oids_may,err);
if ( code )
1999-05-28 22:27:07 +08:00
return code;
1999-09-06 12:42:20 +08:00
1999-05-28 22:27:07 +08:00
nsups++;
sups1++;
}
}
return 0;
}
static int
oc_insert(
ObjectClass *soc,
const char **err
1999-05-28 22:27:07 +08:00
)
{
ObjectClass **ocp;
struct oindexrec *oir;
char **names;
ocp = &oc_list;
1999-05-28 22:27:07 +08:00
while ( *ocp != NULL ) {
ocp = &(*ocp)->soc_next;
}
*ocp = soc;
if ( soc->soc_oid ) {
1999-05-28 22:27:07 +08:00
oir = (struct oindexrec *)
ch_calloc( 1, sizeof(struct oindexrec) );
oir->oir_name = soc->soc_oid;
1999-05-28 22:27:07 +08:00
oir->oir_oc = soc;
if ( avl_insert( &oc_index, (caddr_t) oir,
(AVL_CMP) oc_index_cmp,
(AVL_DUP) avl_dup_error ) ) {
*err = soc->soc_oid;
1999-05-28 22:27:07 +08:00
ldap_memfree(oir);
return SLAP_SCHERR_DUP_CLASS;
}
/* FIX: temporal consistency check */
oc_find(oir->oir_name);
}
if ( (names = soc->soc_names) ) {
1999-05-28 22:27:07 +08:00
while ( *names ) {
oir = (struct oindexrec *)
ch_calloc( 1, sizeof(struct oindexrec) );
oir->oir_name = ch_strdup(*names);
oir->oir_oc = soc;
if ( avl_insert( &oc_index, (caddr_t) oir,
(AVL_CMP) oc_index_cmp,
(AVL_DUP) avl_dup_error ) ) {
*err = *names;
ldap_memfree(oir);
return SLAP_SCHERR_DUP_CLASS;
}
/* FIX: temporal consistency check */
oc_find(oir->oir_name);
names++;
}
}
return 0;
}
int
oc_add(
LDAP_OBJECT_CLASS *oc,
const char **err
1999-05-28 22:27:07 +08:00
)
{
ObjectClass *soc;
int code;
soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
memcpy( &soc->soc_oclass, oc, sizeof(LDAP_OBJECT_CLASS));
1999-09-06 12:42:20 +08:00
if ( (code = oc_add_sups(soc,soc->soc_sup_oids,err)) != 0 )
1999-05-28 22:27:07 +08:00
return code;
1999-09-06 12:42:20 +08:00
if ( (code = oc_create_required(soc,soc->soc_at_oids_must,err)) != 0 )
1999-05-28 22:27:07 +08:00
return code;
1999-09-06 12:42:20 +08:00
if ( (code = oc_create_allowed(soc,soc->soc_at_oids_may,err)) != 0 )
1999-05-28 22:27:07 +08:00
return code;
code = oc_insert(soc,err);
return code;
1998-08-09 08:43:13 +08:00
}
struct sindexrec {
char *sir_name;
Syntax *sir_syn;
};
static Avlnode *syn_index = NULL;
static Syntax *syn_list = NULL;
static int
syn_index_cmp(
struct sindexrec *sir1,
struct sindexrec *sir2
)
{
return (strcmp( sir1->sir_name, sir2->sir_name ));
}
static int
syn_index_name_cmp(
char *name,
struct sindexrec *sir
)
{
return (strcmp( name, sir->sir_name ));
}
Syntax *
syn_find( const char *synname )
{
struct sindexrec *sir = NULL;
if ( (sir = (struct sindexrec *) avl_find( syn_index, synname,
(AVL_CMP) syn_index_name_cmp )) != NULL ) {
return( sir->sir_syn );
}
return( NULL );
}
Syntax *
syn_find_desc( const char *syndesc, int *len )
{
Syntax *synp;
for (synp = syn_list; synp; synp = synp->ssyn_next)
1999-08-21 03:00:44 +08:00
if ((*len = dscompare( synp->ssyn_syn.syn_desc, syndesc, '{')))
return synp;
return( NULL );
}
static int
syn_insert(
Syntax *ssyn,
const char **err
)
{
Syntax **synp;
struct sindexrec *sir;
synp = &syn_list;
while ( *synp != NULL ) {
synp = &(*synp)->ssyn_next;
}
*synp = ssyn;
if ( ssyn->ssyn_oid ) {
sir = (struct sindexrec *)
ch_calloc( 1, sizeof(struct sindexrec) );
sir->sir_name = ssyn->ssyn_oid;
sir->sir_syn = ssyn;
if ( avl_insert( &syn_index, (caddr_t) sir,
(AVL_CMP) syn_index_cmp,
(AVL_DUP) avl_dup_error ) ) {
*err = ssyn->ssyn_oid;
ldap_memfree(sir);
return SLAP_SCHERR_DUP_SYNTAX;
}
/* FIX: temporal consistency check */
syn_find(sir->sir_name);
}
return 0;
}
int
syn_add(
LDAP_SYNTAX *syn,
slap_syntax_validate_func *validate,
slap_syntax_normalize_func *normalize,
const char **err
)
{
Syntax *ssyn;
int code;
ssyn = (Syntax *) ch_calloc( 1, sizeof(Syntax) );
memcpy( &ssyn->ssyn_syn, syn, sizeof(LDAP_SYNTAX));
ssyn->ssyn_validate = validate;
ssyn->ssyn_normalize = normalize;
code = syn_insert(ssyn,err);
return code;
}
struct mindexrec {
char *mir_name;
MatchingRule *mir_mr;
};
static Avlnode *mr_index = NULL;
static MatchingRule *mr_list = NULL;
static int
mr_index_cmp(
struct mindexrec *mir1,
struct mindexrec *mir2
)
{
return (strcmp( mir1->mir_name, mir2->mir_name ));
}
static int
mr_index_name_cmp(
char *name,
struct mindexrec *mir
)
{
return (strcmp( name, mir->mir_name ));
}
MatchingRule *
mr_find( const char *mrname )
{
struct mindexrec *mir = NULL;
if ( (mir = (struct mindexrec *) avl_find( mr_index, mrname,
(AVL_CMP) mr_index_name_cmp )) != NULL ) {
return( mir->mir_mr );
}
return( NULL );
}
static int
mr_insert(
MatchingRule *smr,
const char **err
)
{
MatchingRule **mrp;
struct mindexrec *mir;
char **names;
mrp = &mr_list;
while ( *mrp != NULL ) {
mrp = &(*mrp)->smr_next;
}
*mrp = smr;
if ( smr->smr_oid ) {
mir = (struct mindexrec *)
ch_calloc( 1, sizeof(struct mindexrec) );
mir->mir_name = smr->smr_oid;
mir->mir_mr = smr;
if ( avl_insert( &mr_index, (caddr_t) mir,
(AVL_CMP) mr_index_cmp,
(AVL_DUP) avl_dup_error ) ) {
*err = smr->smr_oid;
ldap_memfree(mir);
return SLAP_SCHERR_DUP_RULE;
}
/* FIX: temporal consistency check */
mr_find(mir->mir_name);
}
if ( (names = smr->smr_names) ) {
while ( *names ) {
mir = (struct mindexrec *)
ch_calloc( 1, sizeof(struct mindexrec) );
mir->mir_name = ch_strdup(*names);
mir->mir_mr = smr;
if ( avl_insert( &mr_index, (caddr_t) mir,
(AVL_CMP) mr_index_cmp,
(AVL_DUP) avl_dup_error ) ) {
*err = *names;
ldap_memfree(mir);
return SLAP_SCHERR_DUP_RULE;
}
/* FIX: temporal consistency check */
mr_find(mir->mir_name);
names++;
}
}
return 0;
}
int
mr_add(
LDAP_MATCHING_RULE *mr,
slap_mr_match_func *match,
const char **err
)
{
MatchingRule *smr;
Syntax *syn;
int code;
smr = (MatchingRule *) ch_calloc( 1, sizeof(MatchingRule) );
memcpy( &smr->smr_mrule, mr, sizeof(LDAP_MATCHING_RULE));
smr->smr_match = match;
if ( smr->smr_syntax_oid ) {
if ( (syn = syn_find(smr->smr_syntax_oid)) ) {
smr->smr_syntax = syn;
} else {
*err = smr->smr_syntax_oid;
return SLAP_SCHERR_SYN_NOT_FOUND;
}
} else {
*err = "";
return SLAP_SCHERR_MR_INCOMPLETE;
}
code = mr_insert(smr,err);
return code;
}
static int
octetStringValidate( struct berval *val )
{
/* any value allowed */
return 0;
}
static int
UTF8StringValidate( struct berval *val )
{
ber_len_t count;
int len;
unsigned char *u = val->bv_val;
for( count = val->bv_len; count > 0; count+=len, u+=len ) {
/* get the length indicated by the first byte */
len = LDAP_UTF8_CHARLEN( u );
/* should not be zero */
if( len == 0 ) return -1;
/* make sure len corresponds with the offset
to the next character */
if( LDAP_UTF8_OFFSET( u ) != len ) return -1;
}
if( count != 0 ) return -1;
return 0;
}
static int
UTF8StringNormalize(
struct berval *val,
struct berval **normalized
)
{
struct berval *newval;
char *p, *q, *s;
newval = ch_malloc( sizeof( struct berval ) );
p = val->bv_val;
/* Ignore initial whitespace */
while ( ldap_utf8_isspace( p ) ) {
LDAP_UTF8_INCR( p );
}
if( *p ) {
ch_free( newval );
return 1;
}
newval->bv_val = ch_strdup( p );
p = q = newval->bv_val;
s = NULL;
while ( *p ) {
int len;
if ( ldap_utf8_isspace( p ) ) {
len = LDAP_UTF8_COPY(q,p);
s=q;
p+=len;
q+=len;
/* Ignore the extra whitespace */
while ( ldap_utf8_isspace( p ) ) {
LDAP_UTF8_INCR( p );
}
} else {
len = LDAP_UTF8_COPY(q,p);
s=NULL;
p+=len;
q+=len;
}
}
assert( *newval->bv_val );
assert( newval->bv_val < p );
assert( p <= q );
/* cannot start with a space */
assert( !ldap_utf8_isspace(newval->bv_val) );
/*
* 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 ( s != NULL ) {
q = s;
}
/* cannot end with a space */
assert( !ldap_utf8_isspace( LDAP_UTF8_PREV(q) ) );
/* null terminate */
*q = '\0';
newval->bv_len = q - newval->bv_val;
normalized = &newval;
return 0;
}
static int
IA5StringValidate( struct berval *val )
{
int i;
for(i=0; i < val->bv_len; i++) {
if( !isascii(val->bv_val[i]) ) return -1;
}
return 0;
}
static int
IA5StringNormalize(
struct berval *val,
struct berval **normalized
)
{
struct berval *newval;
char *p, *q;
newval = ch_malloc( sizeof( struct berval ) );
p = val->bv_val;
/* Ignore initial whitespace */
while ( isspace( *p++ ) ) {
/* EMPTY */ ;
}
if( *p ) {
ch_free( newval );
return 1;
}
newval->bv_val = ch_strdup( p );
p = q = newval->bv_val;
while ( *p ) {
if ( isspace( *p ) ) {
*q++ = *p++;
/* Ignore the extra whitespace */
while ( isspace( *p++ ) ) {
/* EMPTY */ ;
}
} else {
*q++ = *p++;
}
}
assert( *newval->bv_val );
assert( newval->bv_val < p );
assert( p <= q );
/* cannot start with a space */
assert( !isspace(*newval->bv_val) );
/*
* 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 ( isspace( q[-1] ) ) {
--q;
}
/* cannot end with a space */
assert( !isspace( q[-1] ) );
/* null terminate */
*q = '\0';
newval->bv_len = q - newval->bv_val;
normalized = &newval;
return 0;
}
static int
caseExactIA5Match(
struct berval *val1,
struct berval *val2
)
{
return strcmp( val1->bv_val, val2->bv_val );
}
static int
caseIgnoreIA5Match(
struct berval *val1,
struct berval *val2
)
{
return strcasecmp( val1->bv_val, val2->bv_val );
}
int
register_syntax(
char * desc,
slap_syntax_validate_func *validate,
slap_syntax_normalize_func *normalize )
{
LDAP_SYNTAX *syn;
int code;
const char *err;
syn = ldap_str2syntax( desc, &code, &err);
if ( !syn ) {
Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s before %s in %s\n",
ldap_scherr2str(code), err, desc );
return( -1 );
}
code = syn_add( syn, validate, normalize, &err );
if ( code ) {
Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s %s in %s\n",
scherr2str(code), err, desc );
return( -1 );
}
return( 0 );
}
int
register_matching_rule(
char * desc,
slap_mr_match_func *match )
{
LDAP_MATCHING_RULE *mr;
int code;
const char *err;
mr = ldap_str2matchingrule( desc, &code, &err);
if ( !mr ) {
Debug( LDAP_DEBUG_ANY, "Error in register_matching_rule: %s before %s in %s\n",
ldap_scherr2str(code), err, desc );
return( -1 );
}
code = mr_add( mr, match, &err );
if ( code ) {
Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s for %s in %s\n",
scherr2str(code), err, desc );
return( -1 );
}
return( 0 );
}
struct syntax_defs_rec {
char *sd_desc;
slap_syntax_validate_func *sd_validate;
slap_syntax_normalize_func *sd_normalize;
};
struct syntax_defs_rec syntax_defs[] = {
{"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'AttributeTypeDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'BitString' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'CertificateList' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'CertificatePair' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'DN' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'DeliveryMethod' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'DirectoryString' )",
UTF8StringValidate, UTF8StringNormalize},
{"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DITContentRuleDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DITStructureRuleDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'EnhancedGuide' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'FacsimileTelephoneNumber' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'GeneralizedTime' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5String' )",
IA5StringValidate, IA5StringNormalize},
{"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'MatchingRuleDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'MatchingRuleUseDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'MailPreference' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'NameAndOptionalUID' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'NameFormDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'NumericString' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'ObjectClassDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'OtherMailbox' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'OctetString' )",
octetStringValidate, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'PostalAddress' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'ProtocolInformation' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'PresentationAddress' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'PrintableString' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'SupportedAlgorithm' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'TelephoneNumber' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'TeletexTerminalIdentifier' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'TelexNumber' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTCTime' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAPSyntaxDescription' )",
NULL, NULL},
{"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'SubstringAssertion' )",
NULL, NULL},
{NULL, NULL, NULL}
};
struct mrule_defs_rec {
char *mrd_desc;
slap_mr_match_func *mrd_match;
};
/*
* 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
*/
/* recycled matching functions */
#define caseIgnoreMatch caseIgnoreIA5Match
#define caseExactMatch caseExactIA5Match
/* unimplemented matching functions */
#define objectIdentifierMatch NULL
#define distinguishedNameMatch NULL
#define caseIgnoreOrderingMatch NULL
#define caseIgnoreSubstringsMatch NULL
#define caseExactOrderingMatch NULL
#define caseExactSubstringsMatch NULL
#define numericStringMatch NULL
#define numericStringSubstringsMatch NULL
#define caseIgnoreListMatch NULL
#define integerMatch NULL
#define bitStringMatch NULL
#define octetStringMatch NULL
#define telephoneNumberMatch NULL
#define telephoneNumberSubstringsMatch NULL
#define presentationAddressMatch NULL
#define uniqueMemberMatch NULL
#define protocolInformationMatch NULL
#define generalizedTimeMatch NULL
#define generalizedTimeOrderingMatch NULL
#define integerFirstComponentMatch NULL
#define objectIdentifierFirstComponentMatch NULL
#define caseIgnoreIA5SubstringsMatch NULL
struct mrule_defs_rec mrule_defs[] = {
{"( 2.5.13.0 NAME 'objectIdentifierMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
objectIdentifierMatch},
{"( 2.5.13.1 NAME 'distinguishedNameMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
distinguishedNameMatch},
{"( 2.5.13.2 NAME 'caseIgnoreMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
caseIgnoreMatch},
{"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
caseIgnoreOrderingMatch},
{"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
caseIgnoreSubstringsMatch},
/* 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 )",
caseExactMatch},
{"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
caseExactOrderingMatch},
{"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
caseExactSubstringsMatch},
{"( 2.5.13.8 NAME 'numericStringMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
numericStringMatch},
{"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
numericStringSubstringsMatch},
{"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )",
caseIgnoreListMatch},
{"( 2.5.13.14 NAME 'integerMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
integerMatch},
{"( 2.5.13.16 NAME 'bitStringMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
bitStringMatch},
{"( 2.5.13.17 NAME 'octetStringMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
octetStringMatch},
{"( 2.5.13.20 NAME 'telephoneNumberMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
telephoneNumberMatch},
{"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
telephoneNumberSubstringsMatch},
{"( 2.5.13.22 NAME 'presentationAddressMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )",
presentationAddressMatch},
{"( 2.5.13.23 NAME 'uniqueMemberMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )",
uniqueMemberMatch},
{"( 2.5.13.24 NAME 'protocolInformationMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )",
protocolInformationMatch},
{"( 2.5.13.27 NAME 'generalizedTimeMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
generalizedTimeMatch},
{"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
generalizedTimeOrderingMatch},
{"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
integerFirstComponentMatch},
{"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
objectIdentifierFirstComponentMatch},
{"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
caseExactIA5Match},
{"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
caseIgnoreIA5Match},
{"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
caseIgnoreIA5SubstringsMatch},
{NULL, NULL}
};
int
schema_init( void )
{
int res;
int i;
static int schema_init_done = 0;
/* We are called from read_config that is recursive */
if ( schema_init_done )
return( 0 );
for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) {
res = register_syntax( syntax_defs[i].sd_desc,
syntax_defs[i].sd_validate,
syntax_defs[i].sd_normalize );
if ( res ) {
fprintf( stderr, "schema_init: Error registering syntax %s\n",
syntax_defs[i].sd_desc );
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_match );
if ( res ) {
fprintf( stderr, "schema_init: Error registering matching rule %s\n",
mrule_defs[i].mrd_desc );
exit( EXIT_FAILURE );
}
}
schema_init_done = 1;
return( 0 );
}
#if defined( SLAPD_SCHEMA_DN )
static int
syn_schema_info( Entry *e )
{
struct berval val;
struct berval *vals[2];
Syntax *syn;
vals[0] = &val;
vals[1] = NULL;
for ( syn = syn_list; syn; syn = syn->ssyn_next ) {
val.bv_val = ldap_syntax2str( &syn->ssyn_syn );
if ( val.bv_val ) {
val.bv_len = strlen( val.bv_val );
1999-09-02 16:11:54 +08:00
Debug( LDAP_DEBUG_TRACE, "Merging syn [%ld] %s\n",
(long) val.bv_len, val.bv_val, 0 );
attr_merge( e, "ldapSyntaxes", vals );
ldap_memfree( val.bv_val );
} else {
return -1;
}
}
return 0;
}
static int
mr_schema_info( Entry *e )
{
struct berval val;
struct berval *vals[2];
MatchingRule *mr;
vals[0] = &val;
vals[1] = NULL;
for ( mr = mr_list; mr; mr = mr->smr_next ) {
val.bv_val = ldap_matchingrule2str( &mr->smr_mrule );
if ( val.bv_val ) {
val.bv_len = strlen( val.bv_val );
1999-09-02 16:11:54 +08:00
Debug( LDAP_DEBUG_TRACE, "Merging mr [%ld] %s\n",
(long) val.bv_len, val.bv_val, 0 );
attr_merge( e, "matchingRules", vals );
ldap_memfree( val.bv_val );
} else {
return -1;
}
}
return 0;
}
static int
oc_schema_info( Entry *e )
{
struct berval val;
struct berval *vals[2];
ObjectClass *oc;
vals[0] = &val;
vals[1] = NULL;
for ( oc = oc_list; oc; oc = oc->soc_next ) {
val.bv_val = ldap_objectclass2str( &oc->soc_oclass );
if ( val.bv_val ) {
val.bv_len = strlen( val.bv_val );
1999-09-02 16:11:54 +08:00
Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s\n",
(long) val.bv_len, val.bv_val, 0 );
attr_merge( e, "objectClasses", vals );
ldap_memfree( val.bv_val );
} else {
return -1;
}
}
return 0;
}
void
schema_info( Connection *conn, Operation *op, char **attrs, int attrsonly )
{
Entry *e;
struct berval val;
struct berval *vals[2];
vals[0] = &val;
vals[1] = NULL;
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
e->e_attrs = NULL;
e->e_dn = ch_strdup( SLAPD_SCHEMA_DN );
e->e_ndn = ch_strdup( SLAPD_SCHEMA_DN );
(void) dn_normalize( e->e_ndn );
e->e_private = NULL;
{
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 );
attr_merge( e, rdn, vals );
}
free( rdn );
}
if ( syn_schema_info( e ) ) {
/* Out of memory, do something about it */
entry_free( e );
return;
}
if ( mr_schema_info( e ) ) {
/* Out of memory, do something about it */
entry_free( e );
return;
}
if ( at_schema_info( e ) ) {
/* Out of memory, do something about it */
entry_free( e );
return;
}
if ( oc_schema_info( e ) ) {
/* Out of memory, do something about it */
entry_free( e );
return;
}
val.bv_val = "top";
val.bv_len = sizeof("top")-1;
attr_merge( e, "objectClass", vals );
val.bv_val = "LDAPsubentry";
val.bv_len = sizeof("LDAPsubentry")-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
1998-08-09 08:43:13 +08:00
#ifdef LDAP_DEBUG
static void
1999-05-28 22:27:07 +08:00
oc_print( ObjectClass *oc )
1998-08-09 08:43:13 +08:00
{
int i;
const char *mid;
1998-08-09 08:43:13 +08:00
printf( "objectclass %s\n", ldap_objectclass2name( &oc->soc_oclass ) );
1999-05-28 22:27:07 +08:00
if ( oc->soc_required != NULL ) {
mid = "\trequires ";
for ( i = 0; oc->soc_required[i] != NULL; i++, mid = "," )
printf( "%s%s", mid,
ldap_attributetype2name( &oc->soc_required[i]->sat_atype ) );
1998-08-09 08:43:13 +08:00
printf( "\n" );
}
1999-05-28 22:27:07 +08:00
if ( oc->soc_allowed != NULL ) {
mid = "\tallows ";
for ( i = 0; oc->soc_allowed[i] != NULL; i++, mid = "," )
printf( "%s%s", mid,
ldap_attributetype2name( &oc->soc_allowed[i]->sat_atype ) );
1998-08-09 08:43:13 +08:00
printf( "\n" );
}
}
#endif
int is_entry_objectclass(
Entry* e,
1999-08-21 03:00:44 +08:00
const 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;
}
1999-08-21 03:00:44 +08:00
bv.bv_val = (char *) 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;
1999-07-24 09:27:32 +08:00
}