فهرست منبع

Ticket 47541 - Replication of the schema may overwrite consumer 'attributetypes' even
if consumer definition is a superset

Bug Description: Need to check consumer attributetypes to make sure it doesn't get
overwritten if it is a superset.

Fix Description: First, extended the attribute syntax struct to make it a double
linked list. This allows us to easily look through all the attributeTypes.
Then check for supersets by looking at single vs multi-valued, and
syntax oid's. Matching rules are not being evalauated at this time.

https://fedorahosted.org/389/ticket/47541

Reveiwed by: rmeggins(Thanks!)

Mark Reynolds 12 سال پیش
والد
کامیت
42221a161f

+ 52 - 28
ldap/servers/plugins/replication/repl5_connection.c

@@ -98,7 +98,7 @@ typedef struct repl_connection
 
 /*** from proto-slap.h ***/
 int schema_objectclasses_superset_check(struct berval **remote_schema, char *type);
-
+int schema_attributetypes_superset_check(struct berval **remote_schema, char *type);
 /* Controls we add on every outbound operation */
 
 static LDAPControl manageDSAITControl = {LDAP_CONTROL_MANAGEDSAIT, {0, ""}, '\0'};
@@ -1579,33 +1579,57 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
 					/* Need to free the remote_schema_csn_bervals */
 					ber_bvecfree(remote_schema_csn_bervals);
 				}
-                                if (return_value != CONN_SCHEMA_NO_UPDATE_NEEDED) {
-                                        struct berval **remote_schema_objectclasses_bervals;
-                                        /* before pushing the schema do some checking */
-
-                                        /* First objectclasses */
-                                        return_value = conn_read_entry_attribute(conn, "cn=schema", "objectclasses", &remote_schema_objectclasses_bervals);
-                                        if (return_value == CONN_OPERATION_SUCCESS) {
-                                                /* Check if the consumer objectclasses are a superset of the local supplier schema */
-                                                if (schema_objectclasses_superset_check(remote_schema_objectclasses_bervals, OC_SUPPLIER)) {
-                                                        slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
-                                                                "Schema %s must not be overwritten (set replication log for additional info)\n",
-                                                                agmt_get_long_name(conn->agmt));
-                                                        return_value = CONN_OPERATION_FAILED;
-                                                }
-                                        } else {
-                                                slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
-                                                        "%s: Fail to retrieve the remote schema objectclasses\n",
-                                                        agmt_get_long_name(conn->agmt));
-                                        }
-                                        
-                                        /* In case of success, possibly log a message */
-                                        if (return_value == CONN_OPERATION_SUCCESS) {
-                                                slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, 
-                                                        "Schema checking successful: ok to push the schema (%s)\n", agmt_get_long_name(conn->agmt));
-                                        }
-                                }
-
+				if (return_value != CONN_SCHEMA_NO_UPDATE_NEEDED) {
+					struct berval **remote_schema_objectclasses_bervals = NULL;
+					struct berval **remote_schema_attributetypes_bervals = NULL;
+					/* before pushing the schema do some checking */
+
+					/* First objectclasses */
+					return_value = conn_read_entry_attribute(conn, "cn=schema", "objectclasses",
+															&remote_schema_objectclasses_bervals);
+					if (return_value == CONN_OPERATION_SUCCESS) {
+						/* Check if the consumer objectclasses are a superset of the local supplier schema */
+						if (schema_objectclasses_superset_check(remote_schema_objectclasses_bervals, OC_SUPPLIER)) {
+							slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+									"Schema %s must not be overwritten (set replication log for additional info)\n",
+									agmt_get_long_name(conn->agmt));
+							return_value = CONN_OPERATION_FAILED;
+						}
+						if(remote_schema_objectclasses_bervals){
+							ber_bvecfree(remote_schema_objectclasses_bervals);
+						}
+					} else {
+						slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+								"%s: Fail to retrieve the remote schema objectclasses\n",
+								agmt_get_long_name(conn->agmt));
+					}
+					if (return_value == CONN_OPERATION_SUCCESS) {
+						/* Next attribute types */
+						return_value = conn_read_entry_attribute(conn, "cn=schema", "attributetypes",
+																&remote_schema_attributetypes_bervals);
+						if (return_value == CONN_OPERATION_SUCCESS) {
+							/* Check if the consumer attributes are a superset of the local supplier schema */
+							if (schema_attributetypes_superset_check(remote_schema_attributetypes_bervals, OC_SUPPLIER)) {
+								slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+										"Schema %s must not be overwritten (set replication log for additional info)\n",
+										agmt_get_long_name(conn->agmt));
+								return_value = CONN_OPERATION_FAILED;
+							}
+							if(remote_schema_attributetypes_bervals){
+								ber_bvecfree(remote_schema_attributetypes_bervals);
+							}
+						} else {
+							slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+									"%s: Fail to retrieve the remote schema attribute types\n",
+									agmt_get_long_name(conn->agmt));
+						}
+					}
+					/* In case of success, possibly log a message */
+					if (return_value == CONN_OPERATION_SUCCESS) {
+						slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+								"Schema checking successful: ok to push the schema (%s)\n", agmt_get_long_name(conn->agmt));
+					}
+				}
 			}
 		}
 	}

+ 79 - 7
ldap/servers/slapd/attrsyntax.c

@@ -58,6 +58,9 @@ static PLHashTable *oid2asi = NULL;
 static Slapi_RWLock *oid2asi_lock = NULL;
 static PLHashTable *internalasi = NULL;
 
+/* global attribute linked list */
+static asyntaxinfo *global_at = NULL;
+
 /*
  * This hashtable maps the name or alias of the attribute to the
  * syntax info structure for that attribute.  An attribute type has as
@@ -82,12 +85,20 @@ static void attr_syntax_delete_no_lock( struct asyntaxinfo *asip,
 		PRBool remove_from_oid_table );
 static struct asyntaxinfo *attr_syntax_get_by_oid_locking_optional( const
 		char *oid, PRBool use_lock);
+static void attr_syntax_insert( struct asyntaxinfo *asip );
+static void attr_syntax_remove( struct asyntaxinfo *asip );
 
 #ifdef ATTR_LDAP_DEBUG
 static void attr_syntax_print();
 #endif
 static int attr_syntax_init(void);
 
+struct asyntaxinfo *
+attr_syntax_get_global_at()
+{
+	return global_at;
+}
+
 void
 attr_syntax_read_lock(void)
 {
@@ -202,13 +213,14 @@ attr_syntax_free( struct asyntaxinfo *a )
 	PR_ASSERT( a->asi_refcnt == 0 );
 
 	cool_charray_free( a->asi_aliases );
-	slapi_ch_free( (void**)&a->asi_name );
-	slapi_ch_free( (void **)&a->asi_desc );
-	slapi_ch_free( (void **)&a->asi_oid );
-	slapi_ch_free( (void **)&a->asi_superior );
-	slapi_ch_free( (void **)&a->asi_mr_equality );
-	slapi_ch_free( (void **)&a->asi_mr_ordering );
-	slapi_ch_free( (void **)&a->asi_mr_substring );
+	slapi_ch_free_string(&a->asi_name );
+	slapi_ch_free_string(&a->asi_desc );
+	slapi_ch_free_string(&a->asi_oid );
+	slapi_ch_free_string(&a->asi_superior );
+	slapi_ch_free_string(&a->asi_mr_equality );
+	slapi_ch_free_string(&a->asi_mr_ordering );
+	slapi_ch_free_string(&a->asi_mr_substring );
+	slapi_ch_free_string(&a->asi_syntax_oid);
 	schema_free_extensions(a->asi_extensions);
 	slapi_ch_free( (void **) &a );
 }
@@ -380,6 +392,7 @@ attr_syntax_return_locking_optional(struct asyntaxinfo *asi, PRBool use_lock)
 				}
 				/* ref count is 0 and it's flagged for
 				 * deletion, so it's safe to free now */
+				attr_syntax_remove(asi);
 				attr_syntax_free(asi);
 				if(use_lock) {
 					AS_UNLOCK_WRITE(name2asi_lock);
@@ -411,6 +424,9 @@ attr_syntax_add_by_name(struct asyntaxinfo *a, int lock)
 		AS_LOCK_WRITE(name2asi_lock);
 	}
 
+	/* insert the attr into the global linked list */
+	attr_syntax_insert(a);
+
 	PL_HashTableAdd(name2asi, a->asi_name, a);
 	if ( a->asi_aliases != NULL ) {
 		int		i;
@@ -475,6 +491,7 @@ attr_syntax_delete_no_lock( struct asyntaxinfo *asi,
 			 * then to call return.  The last return will then take care of
 			 * the free.  The only way this free would happen here is if
 			 * you return the syntax before calling delete. */
+			attr_syntax_remove(asi);
 			attr_syntax_free(asi);
 		}
 	}
@@ -764,10 +781,46 @@ attr_syntax_dup( struct asyntaxinfo *a )
 	newas->asi_mr_eq_plugin = a->asi_mr_eq_plugin;
 	newas->asi_mr_ord_plugin = a->asi_mr_ord_plugin;
 	newas->asi_mr_sub_plugin = a->asi_mr_sub_plugin;
+	newas->asi_syntax_oid = slapi_ch_strdup(a->asi_syntax_oid);
+	newas->asi_next = NULL;
+	newas->asi_prev = NULL;
 
 	return( newas );
 }
 
+static void
+attr_syntax_insert(struct asyntaxinfo *asip )
+{
+    /* Insert at top of list */
+    asip->asi_prev = NULL;
+    asip->asi_next = global_at;
+    if(global_at){
+        global_at->asi_prev = asip;
+        global_at = asip;
+    } else {
+        global_at = asip;
+    }
+}
+
+static void
+attr_syntax_remove(struct asyntaxinfo *asip )
+{
+    struct asyntaxinfo *prev, *next;
+
+    prev = asip->asi_prev;
+    next = asip->asi_next;
+    if(prev){
+        prev->asi_next = next;
+        if(next){
+            next->asi_prev = prev;
+        }
+    } else {
+        if(next){
+            next->asi_prev = NULL;
+        }
+        global_at = next;
+    }
+}
 
 /*
  * Add a new attribute type to the schema.
@@ -918,6 +971,7 @@ attr_syntax_create(
 	a.asi_mr_substring = (char*)mr_substring;
 	a.asi_extensions = extensions;
 	a.asi_plugin = plugin_syntax_find( attr_syntax );
+	a.asi_syntax_oid = (char *)attr_syntax ;
 	a.asi_syntaxlength = syntaxlength;
 	/* ideally, we would report an error and fail to start if there was some problem
 	   with the matching rule - but since this functionality is new, and we might
@@ -1503,3 +1557,21 @@ slapi_reload_internal_attr_syntax()
 	attr_syntax_enumerate_attrs_ext(internalasi, attr_syntax_internal_asi_add, NULL);
 	return rc;
 }
+
+/*
+ * See if the attribute at1 is in the list of at2.  Change by name, and oid(if necessary).
+ */
+struct asyntaxinfo *
+attr_syntax_find(struct asyntaxinfo *at1, struct asyntaxinfo *at2)
+{
+	struct asyntaxinfo *asi;
+
+	for(asi = at2; asi != NULL; asi = asi->asi_next){
+		if(strcasecmp(at1->asi_name, asi->asi_name) == 0 || strcmp(at1->asi_oid, asi->asi_oid) == 0){
+			/* found it */
+			return asi;
+		}
+	}
+
+	return NULL;
+}

+ 0 - 1
ldap/servers/slapd/log.c

@@ -2031,7 +2031,6 @@ slapi_log_error_ext(int severity, char *subsystem, char *fmt, va_list varg1, va_
 int
 slapi_is_loglevel_set ( const int loglevel )
 {
-
     return ( 
 #ifdef _WIN32
     *module_ldap_debug

+ 3 - 0
ldap/servers/slapd/proto-slap.h

@@ -136,6 +136,8 @@ struct asyntaxinfo *attr_syntax_get_by_oid ( const char *oid );
 struct asyntaxinfo *attr_syntax_get_by_name ( const char *name );
 struct asyntaxinfo *attr_syntax_get_by_name_with_default ( const char *name );
 struct asyntaxinfo *attr_syntax_get_by_name_locking_optional ( const char *name, PRBool use_lock );
+struct asyntaxinfo *attr_syntax_get_global_at();
+struct asyntaxinfo *attr_syntax_find(struct asyntaxinfo *at1, struct asyntaxinfo *at2);
 /*
  * Call attr_syntax_return() when you are done using a value returned
  * by attr_syntax_get_by_oid() or attr_syntax_get_by_name().
@@ -1002,6 +1004,7 @@ int slapi_reload_schema_files(char *schemadir);
 void schema_free_extensions(schemaext *extensions);
 schemaext *schema_copy_extensions(schemaext *extensions);
 int schema_objectclasses_superset_check(struct berval **remote_schema, char *type);
+int schema_attributypes_superset_check(struct berval **remote_schema, char *type);
 
 /*
  * schemaparse.c

+ 526 - 16
ldap/servers/slapd/schema.c

@@ -145,6 +145,9 @@ static void schema_create_errormsg( char *errorbuf, size_t errorbufsize,
 #else
         ;
 #endif
+static int schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_list2, char *message);
+static int schema_at_superset_check_syntax_oids(char *oid1, char *oid2);
+static int schema_at_superset_check_mr(struct asyntaxinfo *a1, struct asyntaxinfo *a2, char *info);
 static int parse_at_str(const char *input, struct asyntaxinfo **asipp, char *errorbuf, size_t errorbufsize,
         PRUint32 schema_flags, int is_user_defined, int schema_ds4x_compat, int is_remote);
 static int extension_is_user_defined( schemaext *extensions );
@@ -1889,13 +1892,34 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
 		  rc = SLAPI_DSE_CALLBACK_ERROR;
 	  } else {
 		  if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
-			/* 
-			 * Replace all attribute types
-			 */
-			*returncode = schema_replace_attributes( pb, mods[i], returntext,
-					SLAPI_DSE_RETURNTEXT_SIZE );
+			  if (is_replicated_operation) {
+			      /*
+			       * before accepting the schema checks if the local consumer schema is not
+			       * a superset of the supplier schema
+			       */
+			      if (schema_attributetypes_superset_check(mods[i]->mod_bvalues, OC_CONSUMER)) {
+			    	  schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+			                  schema_errprefix_generic, mods[i]->mod_type,
+			                  "Replace is not possible, local consumer schema is a superset of the supplier" );
+			          slapi_log_error(SLAPI_LOG_FATAL, "schema",
+			                  "Local %s must not be overwritten (set replication log for additional info)\n",
+			                  mods[i]->mod_type);
+			          *returncode = LDAP_UNWILLING_TO_PERFORM;
+			      } else {
+			          /*
+			           * Replace all attributes
+			           */
+			    	  *returncode = schema_replace_attributes( pb, mods[i], returntext,
+			    			  SLAPI_DSE_RETURNTEXT_SIZE );
+			      }
+			  } else {
+			      /*
+			       * Replace all objectclasses
+			       */
+			      *returncode = schema_replace_attributes( pb, mods[i], returntext,
+				          SLAPI_DSE_RETURNTEXT_SIZE );
+			  }
 		  } else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
-                          
                           if (is_replicated_operation) {
                                   /* before accepting the schema checks if the local consumer schema is not
                                    * a superset of the supplier schema
@@ -5870,6 +5894,7 @@ static int
 schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, char *message) {
         struct objclass *oc_1, *oc_2;
         char *description;
+        int debug_logging = 0;
         int rc, i, j;
         int found;
 
@@ -5882,6 +5907,11 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
         /* by default assum oc_list1 == oc_list2 */
         rc = 0;
 
+        /* Are we doing replication logging */
+        if(slapi_is_loglevel_set(SLAPI_LOG_REPL)){
+        	debug_logging = 1;
+        }
+
         /* Check if all objectclass in oc_list1
          *   - exists in oc_list2
          *   - required attributes are also required in oc_2
@@ -5903,8 +5933,12 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
 
                         /* The oc_1 objectclasses is supperset */
                         rc = 1;
-
-                        continue; /* we continue to check all the objectclass */
+                        if(debug_logging){
+                            /* we continue to check all the objectclasses so we log what is wrong */
+                            continue;
+                        } else {
+                            break;
+                        }
                 }
 
                 /* First check the MUST */
@@ -5933,8 +5967,12 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
 
                                         /* The oc_1 objectclasses is supperset */
                                         rc = 1;
-                                                
-                                        continue; /* we continue to check all attributes */
+                                        if(debug_logging){
+                                            /* we continue to check all attributes so we log what is wrong */
+                                            continue;
+                                        } else {
+                                            break;
+                                        }
                                 }
                         }
                 }
@@ -5963,10 +6001,14 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
                                                 oc_1->oc_name,
                                                 description);
 
-                                        /* The oc_1 objectclasses is supperset */
+                                        /* The oc_1 objectclasses is superset */
                                         rc = 1;
-                                        
-                                        continue; /* we continue to check all attributes */
+                                        if(debug_logging){
+                                            /* we continue to check all attributes so we log what is wrong */
+                                            continue;
+                                        } else {
+                                            break;
+                                        }
                                 }
                         }
                 }
@@ -5975,6 +6017,385 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
         return rc;
 }
 
+static int
+schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_list2, char *message)
+{
+    struct asyntaxinfo *at_1, *at_2;
+    char *info = NULL;
+    int debug_logging = 0;
+    int found = 0;
+    int rc = 0;
+
+    if(at_list1 == NULL || at_list2 == NULL){
+        return 0;
+    }
+
+    /* Are we doing replication logging */
+    if(slapi_is_loglevel_set(SLAPI_LOG_REPL)){
+       	debug_logging = 1;
+    }
+
+    for (at_1 = at_list1; at_1 != NULL; at_1 = at_1->asi_next){
+
+        /* check if at_1 exists in at_list2 */
+        if((at_2 = attr_syntax_find(at_1, at_list2))){
+            /*
+             *  Check for single vs. multi value
+             */
+            if(!(at_1->asi_flags & SLAPI_ATTR_FLAG_SINGLE) && (at_2->asi_flags & SLAPI_ATTR_FLAG_SINGLE)){
+                /* at_list 1 is a superset */
+                rc = 1;
+                if(debug_logging){
+                	slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] is not "
+                	        "\"single-valued\" \n",message, at_1->asi_name);
+                    continue;
+                } else {
+                    break;
+                }
+            }
+
+            /*
+             *  Check the syntaxes
+             */
+            if(schema_at_superset_check_syntax_oids(at_1->asi_syntax_oid, at_2->asi_syntax_oid)){
+                 /* at_list 1 is a superset */
+                 rc = 1;
+                 if(debug_logging){
+                     slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] syntax "
+                             "can not be overwritten\n",message, at_1->asi_name);
+                     continue;
+                 } else {
+                     break;
+                 }
+             }
+             /*
+              *  Check some matching rules - not finished yet...
+              *
+             if(schema_at_superset_check_mr(at_1, at_2, info)){
+                 rc = 1;
+                 if(debug_logging){
+                     slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] matching "
+                             "rule can not be overwritten\n",message, at_1->asi_name);
+                     continue;
+                 } else {
+                     break;
+                 }
+             }
+             */
+        } else {
+            rc = 1;
+            if(debug_logging){
+                /* we continue to check all attributes so we log what is wrong */
+                slapi_log_error(SLAPI_LOG_REPL, "schema", "Fail to retrieve in the %s schema [%s or %s]\n",
+        	            message, at_1->asi_name, at_1->asi_oid);
+                continue;
+            } else {
+                break;
+            }
+        }
+    }
+
+
+    return rc;
+}
+
+/*
+ * Return 1 if a1's matching rules are superset(not to be overwritten).  If just one of
+ * the matching rules should not be overwritten, even if one should, we can not allow it.
+ */
+static int
+schema_at_superset_check_mr(struct asyntaxinfo *a1, struct asyntaxinfo *a2, char *info)
+{
+    char *a1_mrtype[3] = { a1->asi_mr_equality, a1->asi_mr_substring, a1->asi_mr_ordering };
+    char *a2_mrtype[3] = { a2->asi_mr_equality, a2->asi_mr_substring, a2->asi_mr_ordering };
+    int rc = 0, i;
+
+    /*
+     * Loop over the three matching rule types
+     */
+    for(i = 0; i < 3; i++){
+        if(a1_mrtype[i]){
+            if(a2_mrtype[i]){
+                /*
+                 *  Future action item - determine matching rule precedence:
+                 *
+                    ces
+                    "caseExactIA5Match", "1.3.6.1.4.1.1466.109.114.1"
+                    "caseExactMatch", "2.5.13.5"
+                    "caseExactOrderingMatch", "2.5.13.6"
+                    "caseExactSubstringsMatch", "2.5.13.7"
+                    "caseExactIA5SubstringsMatch", "2.16.840.1.113730.3.3.1"
+
+                    cis
+                    "generalizedTimeMatch", "2.5.13.27"
+                    "generalizedTimeOrderingMatch", "2.5.13.28"
+                    "booleanMatch", "2.5.13.13"
+                    "caseIgnoreIA5Match", "1.3.6.1.4.1.1466.109.114.2"
+                    "caseIgnoreIA5SubstringsMatch", "1.3.6.1.4.1.1466.109.114.3"
+                    "caseIgnoreListMatch", "2.5.13.11"
+                    "caseIgnoreListSubstringsMatch", "2.5.13.12"
+                    "caseIgnoreMatch", "2.5.13.2" -------------------------------
+                    "caseIgnoreOrderingMatch", "2.5.13.3" ----------------------->  can have lang options
+                    "caseIgnoreSubstringsMatch", "2.5.13.4" ---------------------   (as seen in the console)!
+                    "directoryStringFirstComponentMatch", "2.5.13.31"
+                    "objectIdentifierMatch", "2.5.13.0"
+                    "objectIdentifierFirstComponentMatch", "2.5.13.30"
+
+                    bitstring
+                    "bitStringMatch", "2.5.13.16","2.16.840.1.113730.3.3.1"
+
+                    bin
+                    "octetStringMatch", "2.5.13.17"
+                    "octetStringOrderingMatch", "2.5.13.18"
+
+                    DN
+                    "distinguishedNameMatch", "2.5.13.1"
+
+                    Int
+                    "integerMatch", "2.5.13.14"
+                    "integerOrderingMatch", "2.5.13.15"
+                    "integerFirstComponentMatch", "2.5.13.29"
+
+                    NameAndOptUID
+                    "uniqueMemberMatch", "2.5.13.23"
+
+                    NumericString
+                    "numericStringMatch", "2.5.13.8"
+                    "numericStringOrderingMatch", "2.5.13.9"
+                    "numericStringSubstringsMatch", "2.5.13.10"
+
+                    Telephone
+                    "telephoneNumberMatch", "2.5.13.20"
+                    "telephoneNumberSubstringsMatch", "2.5.13.21"
+                 */
+            }
+        }
+    }
+
+    return rc;
+}
+
+/*
+ * Return 1 if oid1 is a superset(oid1 is not to be overwritten)
+ */
+static int
+schema_at_superset_check_syntax_oids(char *oid1, char *oid2)
+{
+    if(oid1 == NULL && oid2 == NULL){
+        return 0;
+    } else if (oid2 == NULL){
+        return 0;
+    } else if (oid1 == NULL){
+        return 1;
+    }
+
+    if(strcmp(oid1, BINARY_SYNTAX_OID) == 0){
+        if(strcmp(oid2, BINARY_SYNTAX_OID) &&
+           strcmp(oid2, INTEGER_SYNTAX_OID) &&
+           strcmp(oid2, NUMERICSTRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, FACSIMILE_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, TELEPHONE_SYNTAX_OID) &&
+           strcmp(oid2, TELETEXTERMID_SYNTAX_OID) &&
+           strcmp(oid2, TELEXNUMBER_SYNTAX_OID))
+
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, BITSTRING_SYNTAX_OID) == 0){
+        if(strcmp(oid2, BINARY_SYNTAX_OID) &&
+           strcmp(oid2, BITSTRING_SYNTAX_OID) &&
+           strcmp(oid2, INTEGER_SYNTAX_OID) &&
+           strcmp(oid2, NUMERICSTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID) &&
+           strcmp(oid2, FACSIMILE_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, TELEPHONE_SYNTAX_OID) &&
+           strcmp(oid2, TELETEXTERMID_SYNTAX_OID) &&
+           strcmp(oid2, TELEXNUMBER_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, BOOLEAN_SYNTAX_OID) == 0){
+        if(strcmp(oid2, BOOLEAN_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, COUNTRYSTRING_SYNTAX_OID) ==0){
+        if(strcmp(oid2, COUNTRYSTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, DN_SYNTAX_OID) == 0){
+        if(strcmp(oid2, DN_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) )
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, DELIVERYMETHOD_SYNTAX_OID) ==0){
+        if(strcmp(oid2, DELIVERYMETHOD_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, DIRSTRING_SYNTAX_OID) == 0){
+        if(strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID)){
+            return 1;
+        }
+    } else if(strcmp(oid1, ENHANCEDGUIDE_SYNTAX_OID) == 0){
+        if(strcmp(oid2, ENHANCEDGUIDE_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, IA5STRING_SYNTAX_OID) == 0){
+        if(strcmp(oid2, IA5STRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID))
+        {
+           return 1;
+        }
+    } else if(strcmp(oid1, INTEGER_SYNTAX_OID) == 0){
+        if(strcmp(oid2, INTEGER_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, NUMERICSTRING_SYNTAX_OID) &&
+           strcmp(oid2, TELEPHONE_SYNTAX_OID) &&
+           strcmp(oid2, TELETEXTERMID_SYNTAX_OID) &&
+           strcmp(oid2, TELEXNUMBER_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID) )
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, JPEG_SYNTAX_OID) == 0){
+        if(strcmp(oid2, JPEG_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, NAMEANDOPTIONALUID_SYNTAX_OID) == 0){
+    	if(strcmp(oid2, NAMEANDOPTIONALUID_SYNTAX_OID) &&
+    	   strcmp(oid2, NAMEANDOPTIONALUID_SYNTAX_OID) &&
+    	   strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, NUMERICSTRING_SYNTAX_OID) == 0){
+    	if(strcmp(oid2, NUMERICSTRING_SYNTAX_OID) &&
+    	   strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, OID_SYNTAX_OID) == 0){
+        if(strcmp(oid2, OID_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if (strcmp(oid1, OCTETSTRING_SYNTAX_OID) == 0){
+        if(strcmp(oid2, OCTETSTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, POSTALADDRESS_SYNTAX_OID) ==0){
+        if(strcmp(oid2, POSTALADDRESS_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, PRINTABLESTRING_SYNTAX_OID) == 0){
+        if(strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, TELEPHONE_SYNTAX_OID) == 0){
+        if(strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, TELEPHONE_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, TELETEXTERMID_SYNTAX_OID) == 0){
+    	if(strcmp(oid2, TELETEXTERMID_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if(strcmp(oid1, TELEXNUMBER_SYNTAX_OID) == 0){
+        if(strcmp(oid2, TELEXNUMBER_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    } else if (strcmp(oid1, SPACE_INSENSITIVE_STRING_SYNTAX_OID) == 0){
+    	if(strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, PRINTABLESTRING_SYNTAX_OID) &&
+           strcmp(oid2, DIRSTRING_SYNTAX_OID) &&
+           strcmp(oid2, SPACE_INSENSITIVE_STRING_SYNTAX_OID) &&
+           strcmp(oid2, IA5STRING_SYNTAX_OID))
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 static void
 schema_oclist_free(struct objclass *oc_list)
 {
@@ -5986,8 +6407,20 @@ schema_oclist_free(struct objclass *oc_list)
         }
 }
 
-static
-struct objclass *schema_berval_to_oclist(struct berval **oc_berval) {
+static void
+schema_atlist_free(struct asyntaxinfo *at_list)
+{
+    struct asyntaxinfo *at, *at_next;
+
+    for (at = at_list; at != NULL; at = at_next) {
+        at_next = at->asi_next;
+        attr_syntax_free(at);
+    }
+}
+
+static struct objclass *
+schema_berval_to_oclist(struct berval **oc_berval)
+{
         struct objclass *oc, *oc_list, *oc_tail;
         char errorbuf[BUFSIZ];
         int schema_ds4x_compat, rc;
@@ -6027,8 +6460,43 @@ struct objclass *schema_berval_to_oclist(struct berval **oc_berval) {
         return oc_list;
 }
 
+static struct asyntaxinfo *
+schema_berval_to_atlist(struct berval **at_berval)
+{
+    struct asyntaxinfo *at, *head = NULL, *prev, *at_list = NULL;
+    char errorbuf[BUFSIZ];
+    int schema_ds4x_compat, rc = 0, i;
+
+    schema_ds4x_compat = config_get_ds4_compatible_schema();
+
+    if (at_berval != NULL) {
+        for (i = 0; at_berval[i] != NULL; i++) {
+            /* parse the objectclass value */
+            rc = parse_at_str(at_berval[i]->bv_val, &at, errorbuf, sizeof (errorbuf),
+                    DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_USE_PRIV_SCHEMA, 0, schema_ds4x_compat, 0);
+            if(rc){
+                attr_syntax_free(at);
+                break;
+            }
+            if(!head){
+                head = at_list = at;
+            } else {
+                at_list->asi_next = at;
+                at->asi_prev = at_list;
+                at_list = at;
+            }
+        }
+    }
+    if (rc) {
+        schema_atlist_free(head);
+    }
+
+    return head;
+}
+
 int
-schema_objectclasses_superset_check(struct berval **remote_schema, char *type) {
+schema_objectclasses_superset_check(struct berval **remote_schema, char *type)
+{
         int  rc;
         struct objclass *remote_oc_list;
 
@@ -6072,3 +6540,45 @@ schema_objectclasses_superset_check(struct berval **remote_schema, char *type) {
         }
         return rc;
 }
+
+int
+schema_attributetypes_superset_check(struct berval **remote_schema, char *type)
+{
+    struct asyntaxinfo *remote_at_list = NULL;
+    int rc = 0;
+
+    if (remote_schema != NULL) {
+        /* First build an attribute list from the remote schema */
+        if ((remote_at_list = schema_berval_to_atlist(remote_schema)) == NULL) {
+            rc = 1;
+            return rc;
+        }
+
+        /*
+         * Check that for each object from the remote schema
+         *         - MUST attributes are also MUST in local schema
+         *         - ALLOWED attributes are also ALLOWED in local schema
+         */
+        if (remote_at_list) {
+            attr_syntax_read_lock();
+            if (strcmp(type, OC_SUPPLIER) == 0) {
+                /*
+                 * Check if the remote_at_list from a consumer are or not
+                 * a superset of the attributetypes of the local supplier schema
+                 */
+                rc = schema_at_superset_check(remote_at_list, attr_syntax_get_global_at(), "local supplier" );
+            } else {
+                /*
+                 * Check if the attributeypes of the local consumer schema are or not
+                 * a superset of the remote_at_list from a supplier
+                 */
+                rc = schema_at_superset_check(attr_syntax_get_global_at(), remote_at_list, "remote supplier");
+            }
+            attr_syntax_unlock_read();
+        }
+
+        /* Free the remote schema list */
+        schema_atlist_free(remote_at_list);
+    }
+    return rc;
+}

+ 1 - 5
ldap/servers/slapd/schemaparse.c

@@ -51,15 +51,11 @@
 /* global_oc and global_schema_csn are both protected by oc locks */
 struct objclass		*global_oc;
 CSN *global_schema_csn = NULL; /* Timestamp for last update CSN. NULL = epoch */
+static Slapi_RWLock	*oc_lock = NULL;
 
 static int      is_duplicate( char *target, char **list, int list_max );
 static void     normalize_list( char **list );
 
-
-
-/* R/W lock used to protect the global objclass linked list. */
-static Slapi_RWLock	*oc_lock = NULL;
-
 /*
  * The oc_init_lock_callonce structure is used by NSPR to ensure
  * that oc_init_lock() is called at most once.

+ 3 - 0
ldap/servers/slapd/slap.h

@@ -517,6 +517,7 @@ typedef struct asyntaxinfo {
 	char				*asi_mr_substring;	/* substring matching rule */
 	schemaext			*asi_extensions;	/* schema extensions (X-ORIGIN, X-?????, ...) */
 	struct slapdplugin	*asi_plugin;		/* syntax */
+	char				*asi_syntax_oid;	/* syntax oid */
 	unsigned long		asi_flags;			/* SLAPI_ATTR_FLAG_... */
 	int					asi_syntaxlength;	/* length associated w/syntax */
 	int					asi_refcnt;			/* outstanding references */
@@ -524,6 +525,8 @@ typedef struct asyntaxinfo {
 	struct slapdplugin	*asi_mr_eq_plugin;	/* EQUALITY matching rule plugin */
 	struct slapdplugin	*asi_mr_sub_plugin;	/* SUBSTR matching rule plugin */
 	struct slapdplugin	*asi_mr_ord_plugin;	/* ORDERING matching rule plugin */
+	struct asyntaxinfo	*asi_next;
+	struct asyntaxinfo	*asi_prev;
 } asyntaxinfo;
 
 /*