s4-dsdb: operational handle modifyTimeStamp on the CN=aggregate DN
authorMatthieu Patou <mat@matws.net>
Fri, 18 May 2012 06:58:36 +0000 (23:58 -0700)
committerMatthieu Patou <mat@matws.net>
Sat, 23 Jun 2012 06:42:08 +0000 (23:42 -0700)
modifyTimeStamp is a generated attribute, for most object it's generated
directly from the whenChanged attribute. But for the CN=aggregate object
in the schema we have to handle it in a different way, that's because
for this object whenChanged!=modifyTimeStamp (as checked against Windows
2003R2 DCs) instead the modifyTimeStamp reflect the timestamp of the
most recently modified and loaded schema object (that is to the one with
the highest USN before the schema was reload due to timeout or by the
reloadSchemaNow command).
Some third party are using this information to know if they have to
update their schema cache and also to check that schema updates have
been correctly reloaded by the DC, a good example of this behavior is
exchange 2010.

source4/dsdb/samdb/ldb_modules/operational.c

index 04b746108983d1bf3aa66fa7dc097729da383c3e..79a1d6f2def1b67be5bbfed8ee89bcb3e6b8982a 100644 (file)
@@ -332,6 +332,42 @@ static int construct_parent_guid(struct ldb_module *module,
        return ret;
 }
 
+static int construct_modifyTimeStamp(struct ldb_module *module,
+                                       struct ldb_message *msg, enum ldb_scope scope,
+                                       struct ldb_request *parent)
+{
+       struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+
+       /* We may be being called before the init function has finished */
+       if (!data) {
+               return LDB_SUCCESS;
+       }
+
+       /* Try and set this value up, if possible.  Don't worry if it
+        * fails, we may not have the DB set up yet.
+        */
+       if (!data->aggregate_dn) {
+               data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
+       }
+
+       if (data->aggregate_dn && ldb_dn_compare(data->aggregate_dn, msg->dn) == 0) {
+               /*
+                * If we have the DN for the object with common name = Aggregate and
+                * the request is for this DN then let's do the following:
+                * 1) search the object which changedUSN correspond to the one of the loaded
+                * schema.
+                * 2) Get the whenChanged attribute
+                * 3) Generate the modifyTimestamp out of the whenChanged attribute
+                */
+               const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
+               char *value = ldb_timestring(msg, schema->ts_last_change);
+
+               return ldb_msg_add_string(msg, "modifyTimeStamp", value);
+       }
+       return ldb_msg_copy_attr(msg, "whenChanged", "modifyTimeStamp");
+}
+
 /*
   construct a subSchemaSubEntry
 */
@@ -606,7 +642,7 @@ static const struct {
        int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
 } search_sub[] = {
        { "createTimeStamp", "whenCreated", NULL , NULL },
-       { "modifyTimeStamp", "whenChanged", NULL , NULL },
+       { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
        { "structuralObjectClass", "objectClass", NULL , NULL },
        { "canonicalName", NULL, NULL , construct_canonical_name },
        { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },