mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
73276e84ae
Includes support for update referral for each replicated backend. Reworked replication test to use update referral. Includes major rewrite of response encoding codes (result.c). Includes reworked alias support and eliminates old suffix alias codes (can be emulated using named alias). Includes (untested) support for the Manage DSA IT control. Works in LDAPv2 world. Still testing in LDAPv3 world. Added default referral (test009) test.
1215 lines
28 KiB
C
1215 lines
28 KiB
C
/* schema.c - routines to enforce schema definitions */
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/ctype.h>
|
|
#include <ac/string.h>
|
|
#include <ac/socket.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.
|
|
*/
|
|
|
|
int
|
|
oc_schema_check( Entry *e )
|
|
{
|
|
Attribute *a, *aoc;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
/* check that the entry has required attrs for each oc */
|
|
for ( i = 0; aoc->a_vals[i] != NULL; i++ ) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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",
|
|
e->e_dn, a->a_type, 0 );
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
return( ret );
|
|
}
|
|
|
|
static char *
|
|
oc_check_required( Entry *e, char *ocname )
|
|
{
|
|
ObjectClass *oc;
|
|
AttributeType *at;
|
|
int i;
|
|
Attribute *a;
|
|
char **pp;
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"oc_check_required entry (%s), objectclass \"%s\"\n",
|
|
e->e_dn, ocname, 0 );
|
|
|
|
/* find global oc defn. it we don't know about it assume it's ok */
|
|
if ( (oc = oc_find( ocname )) == NULL ) {
|
|
return( 0 );
|
|
}
|
|
|
|
/* check for empty oc_required */
|
|
if(oc->soc_required == NULL) {
|
|
return( 0 );
|
|
}
|
|
|
|
/* for each required attribute */
|
|
for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
|
|
at = oc->soc_required[i];
|
|
/* 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 ) {
|
|
break;
|
|
}
|
|
pp = at->sat_names;
|
|
if ( pp == NULL ) {
|
|
/* Empty name list => not found */
|
|
a = NULL;
|
|
break;
|
|
}
|
|
while ( *pp ) {
|
|
if ( strcasecmp( a->a_type, *pp ) == 0 ) {
|
|
break;
|
|
}
|
|
pp++;
|
|
}
|
|
if ( *pp ) {
|
|
break;
|
|
}
|
|
}
|
|
/* not there => schema violation */
|
|
if ( a == NULL ) {
|
|
if ( at->sat_names && at->sat_names[0] ) {
|
|
return at->sat_names[0];
|
|
} else {
|
|
return at->sat_oid;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 that *could* be
|
|
* modified by users if we supported such.
|
|
*/
|
|
"objectClasses",
|
|
"attributeTypes",
|
|
"matchingRules",
|
|
"matchingRuleUse",
|
|
"dITStructureRules",
|
|
"dITContentRules",
|
|
"nameForms",
|
|
"ldapSyntaxes",
|
|
NULL
|
|
|
|
};
|
|
|
|
/* this list should be extensible */
|
|
static char *oc_no_usermod_attrs[] = {
|
|
/*
|
|
* Operational and 'no user modification' attributes
|
|
*/
|
|
|
|
/* RFC2252, 3.2.1 */
|
|
"creatorsName",
|
|
"createTimestamp",
|
|
"modifiersName",
|
|
"modifyTimestamp",
|
|
"subschemaSubentry",
|
|
|
|
NULL
|
|
};
|
|
|
|
|
|
/*
|
|
* check to see if attribute is 'operational' or not.
|
|
*/
|
|
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 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 )
|
|
{
|
|
ObjectClass *oc;
|
|
AttributeType *at;
|
|
int i, j;
|
|
char **pp;
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"oc_check_allowed type \"%s\"\n", type, 0, 0 );
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
/* 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? */
|
|
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, type ) == 0 ) {
|
|
return( 0 );
|
|
}
|
|
pp = at->sat_names;
|
|
if ( pp == NULL )
|
|
continue;
|
|
while ( *pp ) {
|
|
if ( strcasecmp( *pp, type ) == 0 ) {
|
|
return( 0 );
|
|
}
|
|
pp++;
|
|
}
|
|
}
|
|
/* does it allow the type? */
|
|
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, type ) == 0 ) {
|
|
return( 0 );
|
|
}
|
|
pp = at->sat_names;
|
|
if ( pp == NULL )
|
|
continue;
|
|
while ( *pp ) {
|
|
if ( strcasecmp( *pp, type ) == 0 ||
|
|
strcmp( *pp, "*" ) == 0 ) {
|
|
return( 0 );
|
|
}
|
|
pp++;
|
|
}
|
|
}
|
|
/* maybe the next oc allows it */
|
|
|
|
/* we don't know about the oc. assume it allows it */
|
|
} else {
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
/* not allowed by any oc */
|
|
return( 1 );
|
|
}
|
|
|
|
struct oindexrec {
|
|
char *oir_name;
|
|
ObjectClass *oir_oc;
|
|
};
|
|
|
|
static Avlnode *oc_index = NULL;
|
|
static ObjectClass *oc_list = NULL;
|
|
|
|
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,
|
|
struct oindexrec *oir
|
|
)
|
|
{
|
|
return (strcasecmp( name, oir->oir_name ));
|
|
}
|
|
|
|
ObjectClass *
|
|
oc_find( const char *ocname )
|
|
{
|
|
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
|
|
)
|
|
{
|
|
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
|
|
)
|
|
{
|
|
char **attrs1;
|
|
AttributeType *sat;
|
|
|
|
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++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
oc_add_sups(
|
|
ObjectClass *soc,
|
|
char **sups,
|
|
const char **err
|
|
)
|
|
{
|
|
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);
|
|
if ( code )
|
|
return code;
|
|
|
|
if ( code = oc_create_required(soc,
|
|
soc1->soc_at_oids_must,err) )
|
|
return code;
|
|
if ( code = oc_create_allowed(soc,
|
|
soc1->soc_at_oids_may,err) )
|
|
return code;
|
|
nsups++;
|
|
sups1++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
oc_insert(
|
|
ObjectClass *soc,
|
|
const char **err
|
|
)
|
|
{
|
|
ObjectClass **ocp;
|
|
struct oindexrec *oir;
|
|
char **names;
|
|
|
|
ocp = &oc_list;
|
|
while ( *ocp != NULL ) {
|
|
ocp = &(*ocp)->soc_next;
|
|
}
|
|
*ocp = soc;
|
|
|
|
if ( soc->soc_oid ) {
|
|
oir = (struct oindexrec *)
|
|
ch_calloc( 1, sizeof(struct oindexrec) );
|
|
oir->oir_name = soc->soc_oid;
|
|
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;
|
|
ldap_memfree(oir);
|
|
return SLAP_SCHERR_DUP_CLASS;
|
|
}
|
|
/* FIX: temporal consistency check */
|
|
oc_find(oir->oir_name);
|
|
}
|
|
if ( (names = soc->soc_names) ) {
|
|
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
|
|
)
|
|
{
|
|
ObjectClass *soc;
|
|
int code;
|
|
|
|
soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
|
|
memcpy( &soc->soc_oclass, oc, sizeof(LDAP_OBJECT_CLASS));
|
|
if ( code = oc_add_sups(soc,soc->soc_sup_oids,err) )
|
|
return code;
|
|
if ( code = oc_create_required(soc,soc->soc_at_oids_must,err) )
|
|
return code;
|
|
if ( code = oc_create_allowed(soc,soc->soc_at_oids_may,err) )
|
|
return code;
|
|
code = oc_insert(soc,err);
|
|
return code;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
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_check_func *check,
|
|
const char **err
|
|
)
|
|
{
|
|
Syntax *ssyn;
|
|
int code;
|
|
|
|
ssyn = (Syntax *) ch_calloc( 1, sizeof(Syntax) );
|
|
memcpy( &ssyn->ssyn_syn, syn, sizeof(LDAP_SYNTAX));
|
|
ssyn->ssyn_check = check;
|
|
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_normalize_func *normalize,
|
|
slap_mr_compare_func *compare,
|
|
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_normalize = normalize;
|
|
smr->smr_compare = compare;
|
|
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
|
|
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_syntax_check_func *check )
|
|
{
|
|
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, check, &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_normalize_func *normalize,
|
|
slap_mr_compare_func *compare )
|
|
{
|
|
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, normalize, compare, &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_check_func *sd_check;
|
|
};
|
|
|
|
struct syntax_defs_rec syntax_defs[] = {
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'DN' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'INTEGER' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )", NULL},
|
|
{"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )", NULL},
|
|
{"( 1.3.6.1.1.1.0.0 DESC 'NIS netgroup triple' )", NULL},
|
|
{"( 1.3.6.1.1.1.0.1 DESC 'Boot parameter' )", NULL},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
struct mrule_defs_rec {
|
|
char *mrd_desc;
|
|
slap_mr_normalize_func *mrd_normalize;
|
|
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 )",
|
|
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.14 NAME 'integerMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", NULL, NULL},
|
|
{"( 2.5.13.16 NAME 'bitStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )", NULL, NULL},
|
|
{"( 2.5.13.17 NAME 'octetStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", NULL, NULL},
|
|
{"( 2.5.13.20 NAME 'telephoneNumberMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )", NULL, NULL},
|
|
{"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", NULL, NULL},
|
|
{"( 2.5.13.22 NAME 'presentationAddressMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )", NULL, NULL},
|
|
{"( 2.5.13.23 NAME 'uniqueMemberMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", NULL, NULL},
|
|
{"( 2.5.13.24 NAME 'protocolInformationMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )", NULL, NULL},
|
|
{"( 2.5.13.27 NAME 'generalizedTimeMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )", 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 )",
|
|
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}
|
|
};
|
|
|
|
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_check );
|
|
if ( res ) {
|
|
fprintf( stderr, "schema_init: Error registering syntax %s\n",
|
|
syntax_defs[i].sd_desc );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
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_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 );
|
|
}
|
|
}
|
|
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 );
|
|
Debug( LDAP_DEBUG_TRACE, "Merging syn [%d] %s\n",
|
|
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 );
|
|
Debug( LDAP_DEBUG_TRACE, "Merging mr [%d] %s\n",
|
|
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 );
|
|
Debug( LDAP_DEBUG_TRACE, "Merging oc [%d] %s\n",
|
|
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 = dn_normalize_case( ch_strdup( SLAPD_SCHEMA_DN ));
|
|
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 );
|
|
|
|
val.bv_val = ch_strdup( "subschema" );
|
|
val.bv_len = strlen( val.bv_val );
|
|
attr_merge( e, "objectClass", vals );
|
|
ldap_memfree( val.bv_val );
|
|
|
|
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;
|
|
}
|
|
|
|
send_search_entry( &backends[0], conn, op,
|
|
e, attrs, attrsonly, 0, NULL );
|
|
send_search_result( conn, op, LDAP_SUCCESS,
|
|
NULL, NULL, NULL, NULL, 1 );
|
|
|
|
entry_free( e );
|
|
}
|
|
#endif
|
|
|
|
#ifdef LDAP_DEBUG
|
|
|
|
static void
|
|
oc_print( ObjectClass *oc )
|
|
{
|
|
int i;
|
|
|
|
if ( oc->soc_names && oc->soc_names[0] ) {
|
|
printf( "objectclass %s\n", oc->soc_names[0] );
|
|
} else {
|
|
printf( "objectclass %s\n", oc->soc_oid );
|
|
}
|
|
if ( oc->soc_required != NULL ) {
|
|
printf( "\trequires %s", oc->soc_required[0] );
|
|
for ( i = 1; oc->soc_required[i] != NULL; i++ ) {
|
|
printf( ",%s", oc->soc_required[i] );
|
|
}
|
|
printf( "\n" );
|
|
}
|
|
if ( oc->soc_allowed != NULL ) {
|
|
printf( "\tallows %s", oc->soc_allowed[0] );
|
|
for ( i = 1; oc->soc_allowed[i] != NULL; i++ ) {
|
|
printf( ",%s", oc->soc_allowed[i] );
|
|
}
|
|
printf( "\n" );
|
|
}
|
|
}
|
|
|
|
#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;
|
|
} |