Back to index

openldap  2.4.31
schema_check.c
Go to the documentation of this file.
00001 /* schema_check.c - routines to enforce schema definitions */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 
00021 #include <ac/ctype.h>
00022 #include <ac/string.h>
00023 #include <ac/socket.h>
00024 
00025 #include "slap.h"
00026 
00027 static char * oc_check_required(
00028        Entry *e,
00029        ObjectClass *oc,
00030        struct berval *ocname );
00031 
00032 static int entry_naming_check(
00033        Entry *e,
00034        int manage,
00035        int add_naming,
00036        const char** text,
00037        char *textbuf, size_t textlen );
00038 /*
00039  * entry_schema_check - check that entry e conforms to the schema required
00040  * by its object class(es).
00041  *
00042  * returns 0 if so, non-zero otherwise.
00043  */
00044 
00045 int
00046 entry_schema_check( 
00047        Operation *op,
00048        Entry *e,
00049        Attribute *oldattrs,
00050        int manage,
00051        int add,
00052        Attribute **socp,
00053        const char** text,
00054        char *textbuf, size_t textlen )
00055 {
00056        Attribute     *a, *asc = NULL, *aoc = NULL;
00057        ObjectClass *sc, *oc, **socs = NULL;
00058        AttributeType *at;
00059        ContentRule *cr;
00060        int    rc, i;
00061        AttributeDescription *ad_structuralObjectClass
00062               = slap_schema.si_ad_structuralObjectClass;
00063        AttributeDescription *ad_objectClass
00064               = slap_schema.si_ad_objectClass;
00065        int extensible = 0;
00066        int subentry = is_entry_subentry( e );
00067        int collectiveSubentry = 0;
00068 
00069        if ( SLAP_NO_SCHEMA_CHECK( op->o_bd )) {
00070               return LDAP_SUCCESS;
00071        }
00072 
00073        if ( get_no_schema_check( op ) ) {
00074               return LDAP_SUCCESS;
00075        }
00076 
00077        if( subentry ) {
00078               collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
00079        }
00080 
00081        *text = textbuf;
00082 
00083        /* misc attribute checks */
00084        for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
00085               const char *type = a->a_desc->ad_cname.bv_val;
00086 
00087               /* there should be at least one value */
00088               assert( a->a_vals != NULL );
00089               assert( a->a_vals[0].bv_val != NULL ); 
00090 
00091               if( a->a_desc->ad_type->sat_check ) {
00092                      rc = (a->a_desc->ad_type->sat_check)(
00093                             op->o_bd, e, a, text, textbuf, textlen );
00094                      if( rc != LDAP_SUCCESS ) {
00095                             return rc;
00096                      }
00097               }
00098 
00099               if( a->a_desc == ad_structuralObjectClass )
00100                      asc = a;
00101               else if ( a->a_desc == ad_objectClass )
00102                      aoc = a;
00103 
00104               if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
00105                      snprintf( textbuf, textlen,
00106                             "'%s' can only appear in collectiveAttributeSubentry",
00107                             type );
00108                      return LDAP_OBJECT_CLASS_VIOLATION;
00109               }
00110 
00111               /* if single value type, check for multiple values */
00112               if( is_at_single_value( a->a_desc->ad_type ) &&
00113                      a->a_vals[1].bv_val != NULL )
00114               {
00115                      snprintf( textbuf, textlen, 
00116                             "attribute '%s' cannot have multiple values",
00117                             type );
00118 
00119                      Debug( LDAP_DEBUG_ANY,
00120                          "Entry (%s), %s\n",
00121                          e->e_dn, textbuf, 0 );
00122 
00123                      return LDAP_CONSTRAINT_VIOLATION;
00124               }
00125        }
00126 
00127        /* check the object class attribute */
00128        if ( aoc == NULL ) {
00129               Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
00130                   e->e_dn, 0, 0 );
00131 
00132               *text = "no objectClass attribute";
00133               return LDAP_OBJECT_CLASS_VIOLATION;
00134        }
00135 
00136        assert( aoc->a_vals != NULL );
00137        assert( aoc->a_vals[0].bv_val != NULL );
00138 
00139        /* check the structural object class attribute */
00140        if ( asc == NULL && !add ) {
00141               Debug( LDAP_DEBUG_ANY,
00142                      "No structuralObjectClass for entry (%s)\n",
00143                   e->e_dn, 0, 0 );
00144 
00145               *text = "no structuralObjectClass operational attribute";
00146               return LDAP_OTHER;
00147        }
00148 
00149        rc = structural_class( aoc->a_vals, &oc, &socs, text, textbuf, textlen,
00150               op->o_tmpmemctx );
00151        if( rc != LDAP_SUCCESS ) {
00152               return rc;
00153        }
00154 
00155        if ( asc == NULL && add ) {
00156               attr_merge_one( e, ad_structuralObjectClass, &oc->soc_cname, NULL );
00157               asc = attr_find( e->e_attrs, ad_structuralObjectClass );
00158               sc = oc;
00159               goto got_soc;
00160        }
00161 
00162        assert( asc->a_vals != NULL );
00163        assert( asc->a_vals[0].bv_val != NULL );
00164        assert( asc->a_vals[1].bv_val == NULL );
00165 
00166        sc = oc_bvfind( &asc->a_vals[0] );
00167        if( sc == NULL ) {
00168               snprintf( textbuf, textlen, 
00169                      "unrecognized structuralObjectClass '%s'",
00170                      asc->a_vals[0].bv_val );
00171 
00172               Debug( LDAP_DEBUG_ANY,
00173                      "entry_check_schema(%s): %s\n",
00174                      e->e_dn, textbuf, 0 );
00175 
00176               rc = LDAP_OBJECT_CLASS_VIOLATION;
00177               goto done;
00178        }
00179 
00180        if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
00181               snprintf( textbuf, textlen, 
00182                      "structuralObjectClass '%s' is not STRUCTURAL",
00183                      asc->a_vals[0].bv_val );
00184 
00185               Debug( LDAP_DEBUG_ANY,
00186                      "entry_check_schema(%s): %s\n",
00187                      e->e_dn, textbuf, 0 );
00188 
00189               rc = LDAP_OTHER;
00190               goto done;
00191        }
00192 
00193 got_soc:
00194        if( !manage && sc->soc_obsolete ) {
00195               snprintf( textbuf, textlen, 
00196                      "structuralObjectClass '%s' is OBSOLETE",
00197                      asc->a_vals[0].bv_val );
00198 
00199               Debug( LDAP_DEBUG_ANY,
00200                      "entry_check_schema(%s): %s\n",
00201                      e->e_dn, textbuf, 0 );
00202 
00203               rc = LDAP_OBJECT_CLASS_VIOLATION;
00204               goto done;
00205        }
00206 
00207        *text = textbuf;
00208 
00209        if ( oc == NULL ) {
00210               snprintf( textbuf, textlen, 
00211                      "unrecognized objectClass '%s'",
00212                      aoc->a_vals[0].bv_val );
00213               rc = LDAP_OBJECT_CLASS_VIOLATION;
00214               goto done;
00215 
00216        } else if ( sc != oc ) {
00217               if ( !manage && sc != slap_schema.si_oc_glue ) {
00218                      snprintf( textbuf, textlen, 
00219                             "structural object class modification "
00220                             "from '%s' to '%s' not allowed",
00221                             asc->a_vals[0].bv_val, oc->soc_cname.bv_val );
00222                      rc = LDAP_NO_OBJECT_CLASS_MODS;
00223                      goto done;
00224               }
00225 
00226               assert( asc->a_vals != NULL );
00227               assert( !BER_BVISNULL( &asc->a_vals[0] ) );
00228               assert( BER_BVISNULL( &asc->a_vals[1] ) );
00229               assert( asc->a_nvals == asc->a_vals );
00230 
00231               /* draft-zeilenga-ldap-relax: automatically modify
00232                * structuralObjectClass if changed with relax */
00233               sc = oc;
00234               ber_bvreplace( &asc->a_vals[ 0 ], &sc->soc_cname );
00235               if ( socp ) {
00236                      *socp = asc;
00237               }
00238        }
00239 
00240        /* naming check */
00241        if ( !is_entry_glue ( e ) ) {
00242               rc = entry_naming_check( e, manage, add, text, textbuf, textlen );
00243               if( rc != LDAP_SUCCESS ) {
00244                      goto done;
00245               }
00246        } else {
00247               /* Glue Entry */
00248        }
00249 
00250        /* find the content rule for the structural class */
00251        cr = cr_find( sc->soc_oid );
00252 
00253        /* the cr must be same as the structural class */
00254        assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
00255 
00256        /* check that the entry has required attrs of the content rule */
00257        if( cr ) {
00258               if( !manage && cr->scr_obsolete ) {
00259                      snprintf( textbuf, textlen, 
00260                             "content rule '%s' is obsolete",
00261                             ldap_contentrule2name( &cr->scr_crule ));
00262 
00263                      Debug( LDAP_DEBUG_ANY,
00264                             "Entry (%s): %s\n",
00265                             e->e_dn, textbuf, 0 );
00266 
00267                      rc = LDAP_OBJECT_CLASS_VIOLATION;
00268                      goto done;
00269               }
00270 
00271               if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
00272                      at = cr->scr_required[i];
00273 
00274                      for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
00275                             if( a->a_desc->ad_type == at ) {
00276                                    break;
00277                             }
00278                      }
00279 
00280                      /* not there => schema violation */
00281                      if ( a == NULL ) {
00282                             snprintf( textbuf, textlen, 
00283                                    "content rule '%s' requires attribute '%s'",
00284                                    ldap_contentrule2name( &cr->scr_crule ),
00285                                    at->sat_cname.bv_val );
00286 
00287                             Debug( LDAP_DEBUG_ANY,
00288                                    "Entry (%s): %s\n",
00289                                    e->e_dn, textbuf, 0 );
00290 
00291                             rc = LDAP_OBJECT_CLASS_VIOLATION;
00292                             goto done;
00293                      }
00294               }
00295 
00296               if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
00297                      at = cr->scr_precluded[i];
00298 
00299                      for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
00300                             if( a->a_desc->ad_type == at ) {
00301                                    break;
00302                             }
00303                      }
00304 
00305                      /* there => schema violation */
00306                      if ( a != NULL ) {
00307                             snprintf( textbuf, textlen, 
00308                                    "content rule '%s' precluded attribute '%s'",
00309                                    ldap_contentrule2name( &cr->scr_crule ),
00310                                    at->sat_cname.bv_val );
00311 
00312                             Debug( LDAP_DEBUG_ANY,
00313                                    "Entry (%s): %s\n",
00314                                    e->e_dn, textbuf, 0 );
00315 
00316                             rc = LDAP_OBJECT_CLASS_VIOLATION;
00317                             goto done;
00318                      }
00319               }
00320        }
00321 
00322        /* check that the entry has required attrs for each oc */
00323        for ( i = 0; socs[i]; i++ ) {
00324               oc = socs[i];
00325               if ( !manage && oc->soc_obsolete ) {
00326                      /* disallow obsolete classes */
00327                      snprintf( textbuf, textlen, 
00328                             "objectClass '%s' is OBSOLETE",
00329                             aoc->a_vals[i].bv_val );
00330 
00331                      Debug( LDAP_DEBUG_ANY,
00332                             "entry_check_schema(%s): %s\n",
00333                             e->e_dn, textbuf, 0 );
00334 
00335                      rc = LDAP_OBJECT_CLASS_VIOLATION;
00336                      goto done;
00337               }
00338 
00339               if ( oc->soc_check ) {
00340                      rc = (oc->soc_check)( op->o_bd, e, oc,
00341                             text, textbuf, textlen );
00342                      if( rc != LDAP_SUCCESS ) {
00343                             goto done;
00344                      }
00345               }
00346 
00347               if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
00348                      /* object class is abstract */
00349                      if ( oc != slap_schema.si_oc_top &&
00350                             !is_object_subclass( oc, sc ))
00351                      {
00352                             int j;
00353                             ObjectClass *xc = NULL;
00354                             for( j=0; socs[j]; j++ ) {
00355                                    if( i != j ) {
00356                                           xc = socs[j];
00357 
00358                                           /* since we previous check against the
00359                                            * structural object of this entry, the
00360                                            * abstract class must be a (direct or indirect)
00361                                            * superclass of one of the auxiliary classes of
00362                                            * the entry.
00363                                            */
00364                                           if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
00365                                                  is_object_subclass( oc, xc ) )
00366                                           {
00367                                                  xc = NULL;
00368                                                  break;
00369                                           }
00370                                    }
00371                             }
00372 
00373                             if( xc != NULL ) {
00374                                    snprintf( textbuf, textlen, "instantiation of "
00375                                           "abstract objectClass '%s' not allowed",
00376                                           aoc->a_vals[i].bv_val );
00377 
00378                                    Debug( LDAP_DEBUG_ANY,
00379                                           "entry_check_schema(%s): %s\n",
00380                                           e->e_dn, textbuf, 0 );
00381 
00382                                    rc = LDAP_OBJECT_CLASS_VIOLATION;
00383                                    goto done;
00384                             }
00385                      }
00386 
00387               } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
00388                      char *s;
00389 
00390                      if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
00391                             int k;
00392 
00393                             if( cr ) {
00394                                    int j;
00395 
00396                                    k = -1;
00397                                    if( cr->scr_auxiliaries ) {
00398                                           for( j = 0; cr->scr_auxiliaries[j]; j++ ) {
00399                                                  if( cr->scr_auxiliaries[j] == oc ) {
00400                                                         k = 0;
00401                                                         break;
00402                                                  }
00403                                           }
00404                                    }
00405                                    if ( k ) {
00406                                           snprintf( textbuf, textlen, 
00407                                                  "class '%s' not allowed by content rule '%s'",
00408                                                  oc->soc_cname.bv_val,
00409                                                  ldap_contentrule2name( &cr->scr_crule ) );
00410                                    }
00411                             } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
00412                                    k = -1;
00413                                    snprintf( textbuf, textlen, 
00414                                           "class '%s' not allowed by any content rule",
00415                                           oc->soc_cname.bv_val );
00416                             } else {
00417                                    k = 0; 
00418                             }
00419 
00420                             if( k == -1 ) {
00421                                    Debug( LDAP_DEBUG_ANY,
00422                                           "Entry (%s): %s\n",
00423                                           e->e_dn, textbuf, 0 );
00424 
00425                                    rc = LDAP_OBJECT_CLASS_VIOLATION;
00426                                    goto done;
00427                             }
00428                      }
00429 
00430                      s = oc_check_required( e, oc, &aoc->a_vals[i] );
00431                      if (s != NULL) {
00432                             snprintf( textbuf, textlen, 
00433                                    "object class '%s' requires attribute '%s'",
00434                                    aoc->a_vals[i].bv_val, s );
00435 
00436                             Debug( LDAP_DEBUG_ANY,
00437                                    "Entry (%s): %s\n",
00438                                    e->e_dn, textbuf, 0 );
00439 
00440                             rc = LDAP_OBJECT_CLASS_VIOLATION;
00441                             goto done;
00442                      }
00443 
00444                      if( oc == slap_schema.si_oc_extensibleObject ) {
00445                             extensible=1;
00446                      }
00447               }
00448        }
00449 
00450        if( extensible ) {
00451               *text = NULL;
00452               rc = LDAP_SUCCESS;
00453               goto done;
00454        }
00455 
00456        /* check that each attr in the entry is allowed by some oc */
00457        for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
00458               rc = LDAP_OBJECT_CLASS_VIOLATION;
00459 
00460               if( cr && cr->scr_required ) {
00461                      for( i=0; cr->scr_required[i]; i++ ) {
00462                             if( cr->scr_required[i] == a->a_desc->ad_type ) {
00463                                    rc = LDAP_SUCCESS;
00464                                    break;
00465                             }
00466                      }
00467               }
00468 
00469               if( rc != LDAP_SUCCESS && cr && cr->scr_allowed ) {
00470                      for( i=0; cr->scr_allowed[i]; i++ ) {
00471                             if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
00472                                    rc = LDAP_SUCCESS;
00473                                    break;
00474                             }
00475                      }
00476               }
00477 
00478               if( rc != LDAP_SUCCESS ) 
00479               {
00480                      rc = oc_check_allowed( a->a_desc->ad_type, socs, sc );
00481               }
00482 
00483               if ( rc != LDAP_SUCCESS ) {
00484                      char *type = a->a_desc->ad_cname.bv_val;
00485 
00486                      snprintf( textbuf, textlen, 
00487                             "attribute '%s' not allowed",
00488                             type );
00489 
00490                      Debug( LDAP_DEBUG_ANY,
00491                          "Entry (%s), %s\n",
00492                          e->e_dn, textbuf, 0 );
00493 
00494                      goto done;
00495               }
00496        }
00497 
00498        *text = NULL;
00499 done:
00500        slap_sl_free( socs, op->o_tmpmemctx );
00501        return rc;
00502 }
00503 
00504 static char *
00505 oc_check_required(
00506        Entry *e,
00507        ObjectClass *oc,
00508        struct berval *ocname )
00509 {
00510        AttributeType *at;
00511        int           i;
00512        Attribute     *a;
00513 
00514        Debug( LDAP_DEBUG_TRACE,
00515               "oc_check_required entry (%s), objectClass \"%s\"\n",
00516               e->e_dn, ocname->bv_val, 0 );
00517 
00518 
00519        /* check for empty oc_required */
00520        if(oc->soc_required == NULL) {
00521               return NULL;
00522        }
00523 
00524        /* for each required attribute */
00525        for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
00526               at = oc->soc_required[i];
00527               /* see if it's in the entry */
00528               for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
00529                      if( a->a_desc->ad_type == at ) {
00530                             break;
00531                      }
00532               }
00533               /* not there => schema violation */
00534               if ( a == NULL ) {
00535                      return at->sat_cname.bv_val;
00536               }
00537        }
00538 
00539        return( NULL );
00540 }
00541 
00542 int oc_check_allowed(
00543        AttributeType *at,
00544        ObjectClass **socs,
00545        ObjectClass *sc )
00546 {
00547        int           i, j;
00548 
00549        Debug( LDAP_DEBUG_TRACE,
00550               "oc_check_allowed type \"%s\"\n",
00551               at->sat_cname.bv_val, 0, 0 );
00552 
00553        /* always allow objectClass attribute */
00554        if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
00555               return LDAP_SUCCESS;
00556        }
00557 
00558        /*
00559         * All operational attributions are allowed by schema rules.
00560         */
00561        if( is_at_operational(at) ) {
00562               return LDAP_SUCCESS;
00563        }
00564 
00565        /* check to see if its allowed by the structuralObjectClass */
00566        if( sc ) {
00567               /* does it require the type? */
00568               for ( j = 0; sc->soc_required != NULL && 
00569                      sc->soc_required[j] != NULL; j++ )
00570               {
00571                      if( at == sc->soc_required[j] ) {
00572                             return LDAP_SUCCESS;
00573                      }
00574               }
00575 
00576               /* does it allow the type? */
00577               for ( j = 0; sc->soc_allowed != NULL && 
00578                      sc->soc_allowed[j] != NULL; j++ )
00579               {
00580                      if( at == sc->soc_allowed[j] ) {
00581                             return LDAP_SUCCESS;
00582                      }
00583               }
00584        }
00585 
00586        /* check that the type appears as req or opt in at least one oc */
00587        for ( i = 0; socs[i]; i++ ) {
00588               /* if we know about the oc */
00589               ObjectClass   *oc = socs[i];
00590               /* extensibleObject allows all */
00591               if ( oc == slap_schema.si_oc_extensibleObject ) {
00592                      return LDAP_SUCCESS;
00593               }
00594               if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
00595                      ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
00596               {
00597                      /* does it require the type? */
00598                      for ( j = 0; oc->soc_required != NULL && 
00599                             oc->soc_required[j] != NULL; j++ )
00600                      {
00601                             if( at == oc->soc_required[j] ) {
00602                                    return LDAP_SUCCESS;
00603                             }
00604                      }
00605                      /* does it allow the type? */
00606                      for ( j = 0; oc->soc_allowed != NULL && 
00607                             oc->soc_allowed[j] != NULL; j++ )
00608                      {
00609                             if( at == oc->soc_allowed[j] ) {
00610                                    return LDAP_SUCCESS;
00611                             }
00612                      }
00613               }
00614        }
00615 
00616        /* not allowed by any oc */
00617        return LDAP_OBJECT_CLASS_VIOLATION;
00618 }
00619 
00620 /*
00621  * Determine the structural object class from a set of OIDs
00622  */
00623 int structural_class(
00624        BerVarray ocs,
00625        ObjectClass **scp,
00626        ObjectClass ***socsp,
00627        const char **text,
00628        char *textbuf, size_t textlen,
00629        void *ctx )
00630 {
00631        int i, nocs;
00632        ObjectClass *oc, **socs;
00633        ObjectClass *sc = NULL;
00634        int scn = -1;
00635 
00636        *text = "structural_class: internal error";
00637 
00638        /* count them */
00639        for( i=0; ocs[i].bv_val; i++ ) ;
00640        nocs = i;
00641        
00642        socs = slap_sl_malloc( (nocs+1) * sizeof(ObjectClass *), ctx );
00643 
00644        for( i=0; ocs[i].bv_val; i++ ) {
00645               socs[i] = oc_bvfind( &ocs[i] );
00646 
00647               if( socs[i] == NULL ) {
00648                      snprintf( textbuf, textlen,
00649                             "unrecognized objectClass '%s'",
00650                             ocs[i].bv_val );
00651                      *text = textbuf;
00652                      goto fail;
00653               }
00654        }
00655        socs[i] = NULL;
00656 
00657        for( i=0; ocs[i].bv_val; i++ ) {
00658               oc = socs[i];
00659               if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
00660                      if( sc == NULL || is_object_subclass( sc, oc ) ) {
00661                             sc = oc;
00662                             scn = i;
00663 
00664                      } else if ( !is_object_subclass( oc, sc ) ) {
00665                             int j;
00666                             ObjectClass *xc = NULL;
00667 
00668                             /* find common superior */
00669                             for( j=i+1; ocs[j].bv_val; j++ ) {
00670                                    xc = socs[j];
00671 
00672                                    if( xc == NULL ) {
00673                                           snprintf( textbuf, textlen,
00674                                                  "unrecognized objectClass '%s'",
00675                                                  ocs[j].bv_val );
00676                                           *text = textbuf;
00677                                           goto fail;
00678                                    }
00679 
00680                                    if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
00681                                           xc = NULL;
00682                                           continue;
00683                                    }
00684 
00685                                    if( is_object_subclass( sc, xc ) &&
00686                                           is_object_subclass( oc, xc ) )
00687                                    {
00688                                           /* found common subclass */
00689                                           break;
00690                                    }
00691 
00692                                    xc = NULL;
00693                             }
00694 
00695                             if( xc == NULL ) {
00696                                    /* no common subclass */
00697                                    snprintf( textbuf, textlen,
00698                                           "invalid structural object class chain (%s/%s)",
00699                                           ocs[scn].bv_val, ocs[i].bv_val );
00700                                    *text = textbuf;
00701                                    goto fail;
00702                             }
00703                      }
00704               }
00705        }
00706 
00707        if( scp ) {
00708               *scp = sc;
00709        }
00710 
00711        if( sc == NULL ) {
00712               *text = "no structural object class provided";
00713               goto fail;
00714        }
00715 
00716        if( scn < 0 ) {
00717               *text = "invalid structural object class";
00718               goto fail;
00719        }
00720 
00721        if ( socsp ) {
00722               *socsp = socs;
00723        } else {
00724               slap_sl_free( socs, ctx );
00725        }
00726        *text = NULL;
00727 
00728        return LDAP_SUCCESS;
00729 
00730 fail:
00731        slap_sl_free( socs, ctx );
00732        return LDAP_OBJECT_CLASS_VIOLATION;
00733 }
00734 
00735 /*
00736  * Return structural object class from list of modifications
00737  */
00738 int mods_structural_class(
00739        Modifications *mods,
00740        struct berval *sc,
00741        const char **text,
00742        char *textbuf, size_t textlen, void *ctx )
00743 {
00744        Modifications *ocmod = NULL;
00745        ObjectClass *ssc;
00746        int rc;
00747 
00748        for( ; mods != NULL; mods = mods->sml_next ) {
00749               if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
00750                      if( ocmod != NULL ) {
00751                             *text = "entry has multiple objectClass attributes";
00752                             return LDAP_OBJECT_CLASS_VIOLATION;
00753                      }
00754                      ocmod = mods;
00755               }
00756        }
00757 
00758        if( ocmod == NULL ) {
00759               *text = "entry has no objectClass attribute";
00760               return LDAP_OBJECT_CLASS_VIOLATION;
00761        }
00762 
00763        if( ocmod->sml_values == NULL || ocmod->sml_values[0].bv_val == NULL ) {
00764               *text = "objectClass attribute has no values";
00765               return LDAP_OBJECT_CLASS_VIOLATION;
00766        }
00767 
00768        rc = structural_class( ocmod->sml_values, &ssc, NULL,
00769               text, textbuf, textlen, ctx );
00770        if ( rc == LDAP_SUCCESS )
00771               *sc = ssc->soc_cname;
00772        return rc;
00773 }
00774 
00775 
00776 static int
00777 entry_naming_check(
00778        Entry *e,
00779        int manage,
00780        int add_naming,
00781        const char** text,
00782        char *textbuf, size_t textlen )
00783 {
00784        /* naming check */
00785        LDAPRDN              rdn = NULL;
00786        const char    *p = NULL;
00787        ber_len_t     cnt;
00788        int           rc = LDAP_SUCCESS;
00789 
00790        if ( BER_BVISEMPTY( &e->e_name )) {
00791               return LDAP_SUCCESS;
00792        }
00793 
00794        /*
00795         * Get attribute type(s) and attribute value(s) of our RDN
00796         */
00797        if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
00798               LDAP_DN_FORMAT_LDAP ) )
00799        {
00800               *text = "unrecognized attribute type(s) in RDN";
00801               return LDAP_INVALID_DN_SYNTAX;
00802        }
00803 
00804        /* Check that each AVA of the RDN is present in the entry */
00805        /* FIXME: Should also check that each AVA lists a distinct type */
00806        for ( cnt = 0; rdn[cnt]; cnt++ ) {
00807               LDAPAVA *ava = rdn[cnt];
00808               AttributeDescription *desc = NULL;
00809               Attribute *attr;
00810               const char *errtext;
00811               int add = 0;
00812 
00813               if( ava->la_flags & LDAP_AVA_BINARY ) {
00814                      snprintf( textbuf, textlen, 
00815                             "value of naming attribute '%s' in unsupported BER form",
00816                             ava->la_attr.bv_val );
00817                      rc = LDAP_NAMING_VIOLATION;
00818               }
00819 
00820               rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
00821               if ( rc != LDAP_SUCCESS ) {
00822                      snprintf( textbuf, textlen, "%s (in RDN)", errtext );
00823                      break;
00824               }
00825 
00826               if( desc->ad_type->sat_usage ) {
00827                      snprintf( textbuf, textlen, 
00828                             "naming attribute '%s' is operational",
00829                             ava->la_attr.bv_val );
00830                      rc = LDAP_NAMING_VIOLATION;
00831                      break;
00832               }
00833  
00834               if( desc->ad_type->sat_collective ) {
00835                      snprintf( textbuf, textlen, 
00836                             "naming attribute '%s' is collective",
00837                             ava->la_attr.bv_val );
00838                      rc = LDAP_NAMING_VIOLATION;
00839                      break;
00840               }
00841 
00842               if( !manage && desc->ad_type->sat_obsolete ) {
00843                      snprintf( textbuf, textlen, 
00844                             "naming attribute '%s' is obsolete",
00845                             ava->la_attr.bv_val );
00846                      rc = LDAP_NAMING_VIOLATION;
00847                      break;
00848               }
00849 
00850               if( !desc->ad_type->sat_equality ) {
00851                      snprintf( textbuf, textlen, 
00852                             "naming attribute '%s' has no equality matching rule",
00853                             ava->la_attr.bv_val );
00854                      rc = LDAP_NAMING_VIOLATION;
00855                      break;
00856               }
00857 
00858               if( !desc->ad_type->sat_equality->smr_match ) {
00859                      snprintf( textbuf, textlen, 
00860                             "naming attribute '%s' has unsupported equality matching rule",
00861                             ava->la_attr.bv_val );
00862                      rc = LDAP_NAMING_VIOLATION;
00863                      break;
00864               }
00865 
00866               /* find the naming attribute */
00867               attr = attr_find( e->e_attrs, desc );
00868               if ( attr == NULL ) {
00869                      snprintf( textbuf, textlen, 
00870                             "naming attribute '%s' is not present in entry",
00871                             ava->la_attr.bv_val );
00872                      if ( add_naming ) {
00873                             add = 1;
00874 
00875                      } else {
00876                             rc = LDAP_NAMING_VIOLATION;
00877                      }
00878 
00879               } else {
00880                      rc = attr_valfind( attr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|
00881                             SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
00882                             &ava->la_value, NULL, NULL );
00883 
00884                      if ( rc != 0 ) {
00885                             switch( rc ) {
00886                             case LDAP_INAPPROPRIATE_MATCHING:
00887                                    snprintf( textbuf, textlen, 
00888                                           "inappropriate matching for naming attribute '%s'",
00889                                           ava->la_attr.bv_val );
00890                                    break;
00891                             case LDAP_INVALID_SYNTAX:
00892                                    snprintf( textbuf, textlen, 
00893                                           "value of naming attribute '%s' is invalid",
00894                                           ava->la_attr.bv_val );
00895                                    break;
00896                             case LDAP_NO_SUCH_ATTRIBUTE:
00897                                    if ( add_naming ) {
00898                                           if ( is_at_single_value( desc->ad_type ) ) {
00899                                                  snprintf( textbuf, textlen, 
00900                                                         "value of single-valued naming attribute '%s' conflicts with value present in entry",
00901                                                         ava->la_attr.bv_val );
00902 
00903                                           } else {
00904                                                  add = 1;
00905                                                  rc = LDAP_SUCCESS;
00906                                           }
00907 
00908                                    } else {
00909                                           snprintf( textbuf, textlen, 
00910                                                  "value of naming attribute '%s' is not present in entry",
00911                                                  ava->la_attr.bv_val );
00912                                    }
00913                                    break;
00914                             default:
00915                                    snprintf( textbuf, textlen, 
00916                                           "naming attribute '%s' is inappropriate",
00917                                           ava->la_attr.bv_val );
00918                             }
00919 
00920                             if ( !add ) {
00921                                    rc = LDAP_NAMING_VIOLATION;
00922                             }
00923                      }
00924               }
00925 
00926               if ( add ) {
00927                      attr_merge_normalize_one( e, desc, &ava->la_value, NULL );
00928 
00929               } else if ( rc != LDAP_SUCCESS ) {
00930                      break;
00931               }
00932        }
00933 
00934        ldap_rdnfree( rdn );
00935        return rc;
00936 }
00937