Blame SOURCES/0036-Ticket-47950-Bind-DN-tracking-unable-to-write-to-int.patch

f92ce9
From 7ad3f7283e41f3d690626db5b38a9fb63fa8e005 Mon Sep 17 00:00:00 2001
f92ce9
From: Mark Reynolds <mreynolds@redhat.com>
f92ce9
Date: Tue, 18 Nov 2014 09:24:21 -0500
f92ce9
Subject: [PATCH 36/53] Ticket 47950 - Bind DN tracking unable to write to
f92ce9
 internalModifiersName without special permissions
f92ce9
f92ce9
Bug Description:  When a non-rootDN entry makes an update when plugin bind dn tracking
f92ce9
                  is enabled, it is incorrectly rejected with an error 50.
f92ce9
f92ce9
Fix Description:  Create a function to check if an attribute is one of last mod attributes,
f92ce9
                  including the internalModfieresname/internalModifytimestamp.  Use this
f92ce9
                  new function wherever we are checking for last mod attributes.
f92ce9
f92ce9
https://fedorahosted.org/389/ticket/47950
f92ce9
f92ce9
Reviewed by: rmeggins(Thanks!)
f92ce9
f92ce9
(cherry picked from commit c973e7150cf1e7fb3f61a76cf1baf3c0fa91f756)
f92ce9
(cherry picked from commit fa8f7dc3d1ab508c762fc2e18de0cb860ed84657)
f92ce9
---
f92ce9
 dirsrvtests/tickets/ticket47950_test.py            | 273 +++++++++++++++++++++
f92ce9
 ldap/servers/plugins/acl/acl.c                     |   4 +-
f92ce9
 ldap/servers/plugins/replication/cl5_config.c      |   9 +-
f92ce9
 ldap/servers/plugins/replication/repl5_agmtlist.c  |   3 +-
f92ce9
 .../plugins/replication/repl5_replica_config.c     |   3 +-
f92ce9
 ldap/servers/slapd/add.c                           |   4 +-
f92ce9
 ldap/servers/slapd/back-ldbm/ldbm_config.c         |   3 +-
f92ce9
 ldap/servers/slapd/configdse.c                     |   5 +-
f92ce9
 ldap/servers/slapd/opshared.c                      |  17 ++
f92ce9
 ldap/servers/slapd/result.c                        |   2 +
f92ce9
 ldap/servers/slapd/slapi-plugin.h                  |   2 +
f92ce9
 ldap/servers/slapd/task.c                          |   4 +-
f92ce9
 ldap/servers/slapd/tools/mmldif.c                  |  12 +-
f92ce9
 13 files changed, 309 insertions(+), 32 deletions(-)
f92ce9
 create mode 100644 dirsrvtests/tickets/ticket47950_test.py
f92ce9
f92ce9
diff --git a/dirsrvtests/tickets/ticket47950_test.py b/dirsrvtests/tickets/ticket47950_test.py
f92ce9
new file mode 100644
f92ce9
index 0000000..976f964
f92ce9
--- /dev/null
f92ce9
+++ b/dirsrvtests/tickets/ticket47950_test.py
f92ce9
@@ -0,0 +1,273 @@
f92ce9
+import os
f92ce9
+import sys
f92ce9
+import time
f92ce9
+import ldap
f92ce9
+import logging
f92ce9
+import socket
f92ce9
+import pytest
f92ce9
+from lib389 import DirSrv, Entry, tools, tasks
f92ce9
+from lib389.tools import DirSrvTools
f92ce9
+from lib389._constants import *
f92ce9
+from lib389.properties import *
f92ce9
+from lib389.tasks import *
f92ce9
+from constants import *
f92ce9
+
f92ce9
+log = logging.getLogger(__name__)
f92ce9
+
f92ce9
+installation_prefix = None
f92ce9
+
f92ce9
+USER1_DN = "uid=user1,%s" % DEFAULT_SUFFIX
f92ce9
+USER2_DN = "uid=user2,%s" % DEFAULT_SUFFIX
f92ce9
+
f92ce9
+
f92ce9
+class TopologyStandalone(object):
f92ce9
+    def __init__(self, standalone):
f92ce9
+        standalone.open()
f92ce9
+        self.standalone = standalone
f92ce9
+
f92ce9
+
f92ce9
+@pytest.fixture(scope="module")
f92ce9
+def topology(request):
f92ce9
+    '''
f92ce9
+        This fixture is used to standalone topology for the 'module'.
f92ce9
+        At the beginning, It may exists a standalone instance.
f92ce9
+        It may also exists a backup for the standalone instance.
f92ce9
+
f92ce9
+        Principle:
f92ce9
+            If standalone instance exists:
f92ce9
+                restart it
f92ce9
+            If backup of standalone exists:
f92ce9
+                create/rebind to standalone
f92ce9
+
f92ce9
+                restore standalone instance from backup
f92ce9
+            else:
f92ce9
+                Cleanup everything
f92ce9
+                    remove instance
f92ce9
+                    remove backup
f92ce9
+                Create instance
f92ce9
+                Create backup
f92ce9
+    '''
f92ce9
+    global installation_prefix
f92ce9
+
f92ce9
+    if installation_prefix:
f92ce9
+        args_instance[SER_DEPLOYED_DIR] = installation_prefix
f92ce9
+
f92ce9
+    standalone = DirSrv(verbose=False)
f92ce9
+
f92ce9
+    # Args for the standalone instance
f92ce9
+    args_instance[SER_HOST] = HOST_STANDALONE
f92ce9
+    args_instance[SER_PORT] = PORT_STANDALONE
f92ce9
+    args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
f92ce9
+    args_standalone = args_instance.copy()
f92ce9
+    standalone.allocate(args_standalone)
f92ce9
+
f92ce9
+    # Get the status of the backups
f92ce9
+    backup_standalone = standalone.checkBackupFS()
f92ce9
+
f92ce9
+    # Get the status of the instance and restart it if it exists
f92ce9
+    instance_standalone = standalone.exists()
f92ce9
+    if instance_standalone:
f92ce9
+        # assuming the instance is already stopped, just wait 5 sec max
f92ce9
+        standalone.stop(timeout=5)
f92ce9
+        standalone.start(timeout=10)
f92ce9
+
f92ce9
+    if backup_standalone:
f92ce9
+        # The backup exist, assuming it is correct
f92ce9
+        # we just re-init the instance with it
f92ce9
+        if not instance_standalone:
f92ce9
+            standalone.create()
f92ce9
+            # Used to retrieve configuration information (dbdir, confdir...)
f92ce9
+            standalone.open()
f92ce9
+
f92ce9
+        # restore standalone instance from backup
f92ce9
+        standalone.stop(timeout=10)
f92ce9
+        standalone.restoreFS(backup_standalone)
f92ce9
+        standalone.start(timeout=10)
f92ce9
+
f92ce9
+    else:
f92ce9
+        # We should be here only in two conditions
f92ce9
+        #      - This is the first time a test involve standalone instance
f92ce9
+        #      - Something weird happened (instance/backup destroyed)
f92ce9
+        #        so we discard everything and recreate all
f92ce9
+
f92ce9
+        # Remove the backup. So even if we have a specific backup file
f92ce9
+        # (e.g backup_standalone) we clear backup that an instance may have created
f92ce9
+        if backup_standalone:
f92ce9
+            standalone.clearBackupFS()
f92ce9
+
f92ce9
+        # Remove the instance
f92ce9
+        if instance_standalone:
f92ce9
+            standalone.delete()
f92ce9
+
f92ce9
+        # Create the instance
f92ce9
+        standalone.create()
f92ce9
+
f92ce9
+        # Used to retrieve configuration information (dbdir, confdir...)
f92ce9
+        standalone.open()
f92ce9
+
f92ce9
+        # Time to create the backups
f92ce9
+        standalone.stop(timeout=10)
f92ce9
+        standalone.backupfile = standalone.backupFS()
f92ce9
+        standalone.start(timeout=10)
f92ce9
+
f92ce9
+    # clear the tmp directory
f92ce9
+    standalone.clearTmpDir(__file__)
f92ce9
+
f92ce9
+    #
f92ce9
+    # Here we have standalone instance up and running
f92ce9
+    # Either coming from a backup recovery
f92ce9
+    # or from a fresh (re)init
f92ce9
+    # Time to return the topology
f92ce9
+    return TopologyStandalone(standalone)
f92ce9
+
f92ce9
+
f92ce9
+def test_ticket47950(topology):
f92ce9
+    """
f92ce9
+        Testing nsslapd-plugin-binddn-tracking does not cause issues around
f92ce9
+        access control and reconfiguring replication/repl agmt.
f92ce9
+    """
f92ce9
+
f92ce9
+    log.info('Testing Ticket 47950 - Testing nsslapd-plugin-binddn-tracking')
f92ce9
+
f92ce9
+    #
f92ce9
+    # Turn on bind dn tracking
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        topology.standalone.modify_s("cn=config", [(ldap.MOD_REPLACE, 'nsslapd-plugin-binddn-tracking', 'on')])
f92ce9
+        log.info('nsslapd-plugin-binddn-tracking enabled.')
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to enable bind dn tracking: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # Add two users
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        topology.standalone.add_s(Entry((USER1_DN, {
f92ce9
+                                        'objectclass': "top person inetuser".split(),
f92ce9
+                                        'userpassword': "password",
f92ce9
+                                        'sn': "1",
f92ce9
+                                        'cn': "user 1"})))
f92ce9
+        log.info('Added test user %s' % USER1_DN)
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to add %s: %s' % (USER1_DN, e.message['desc']))
f92ce9
+        assert False
f92ce9
+
f92ce9
+    try:
f92ce9
+        topology.standalone.add_s(Entry((USER2_DN, {
f92ce9
+                                        'objectclass': "top person inetuser".split(),
f92ce9
+                                        'sn': "2",
f92ce9
+                                        'cn': "user 2"})))
f92ce9
+        log.info('Added test user %s' % USER2_DN)
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to add user1: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # Add an aci
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        acival = '(targetattr ="cn")(version 3.0;acl "Test bind dn tracking"' + \
f92ce9
+             ';allow (all) (userdn = "ldap:///%s");)' % USER1_DN
f92ce9
+
f92ce9
+        topology.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', acival)])
f92ce9
+        log.info('Added aci')
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to add aci: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # Make modification as user
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        topology.standalone.simple_bind_s(USER1_DN, "password")
f92ce9
+        log.info('Bind as user %s successful' % USER1_DN)
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to bind as user1: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    try:
f92ce9
+        topology.standalone.modify_s(USER2_DN, [(ldap.MOD_REPLACE, 'cn', 'new value')])
f92ce9
+        log.info('%s successfully modified user %s' % (USER1_DN, USER2_DN))
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to update user2: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # Setup replica and create a repl agmt
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        topology.standalone.simple_bind_s(DN_DM, PASSWORD)
f92ce9
+        log.info('Bind as %s successful' % DN_DM)
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to bind as rootDN: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    try:
f92ce9
+        topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
f92ce9
+                                                  replicaId=REPLICAID_MASTER)
f92ce9
+        log.info('Successfully enabled replication.')
f92ce9
+    except ValueError:
f92ce9
+        log.error('Failed to enable replication')
f92ce9
+        assert False
f92ce9
+
f92ce9
+    properties = {RA_NAME: r'test plugin internal bind dn',
f92ce9
+                  RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
f92ce9
+                  RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
f92ce9
+                  RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
f92ce9
+                  RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
f92ce9
+
f92ce9
+    try:
f92ce9
+        repl_agreement = topology.standalone.agreement.create(suffix=DEFAULT_SUFFIX, host="127.0.0.1",
f92ce9
+                                                          port="7777", properties=properties)
f92ce9
+        log.info('Successfully created replication agreement')
f92ce9
+    except InvalidArgumentError, e:
f92ce9
+        log.error('Failed to create replication agreement: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # modify replica
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        properties = {REPLICA_ID: "7"}
f92ce9
+        topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, properties)
f92ce9
+        log.info('Successfully modified replica')
f92ce9
+    except ldap.LDAPError, e:
f92ce9
+        log.error('Failed to update replica config: ' + e.message['desc'])
f92ce9
+        assert False
f92ce9
+
f92ce9
+    #
f92ce9
+    # modify repl agmt
f92ce9
+    #
f92ce9
+    try:
f92ce9
+        properties = {RA_CONSUMER_PORT: "8888"}
f92ce9
+        topology.standalone.agreement.setProperties(None, repl_agreement, None, properties)
f92ce9
+        log.info('Successfully modified replication agreement')
f92ce9
+    except ValueError:
f92ce9
+        log.error('Failed to update replica agreement: ' + repl_agreement)
f92ce9
+        assert False
f92ce9
+
f92ce9
+    # We passed
f92ce9
+    log.info("Test Passed.")
f92ce9
+
f92ce9
+
f92ce9
+def test_ticket47953_final(topology):
f92ce9
+    topology.standalone.stop(timeout=10)
f92ce9
+
f92ce9
+
f92ce9
+def run_isolated():
f92ce9
+    '''
f92ce9
+        run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
f92ce9
+        To run isolated without py.test, you need to
f92ce9
+            - edit this file and comment '@pytest.fixture' line before 'topology' function.
f92ce9
+            - set the installation prefix
f92ce9
+            - run this program
f92ce9
+    '''
f92ce9
+    global installation_prefix
f92ce9
+    installation_prefix = None
f92ce9
+
f92ce9
+    topo = topology(True)
f92ce9
+    test_ticket47950(topo)
f92ce9
+
f92ce9
+if __name__ == '__main__':
f92ce9
+    run_isolated()
f92ce9
\ No newline at end of file
f92ce9
diff --git a/ldap/servers/plugins/acl/acl.c b/ldap/servers/plugins/acl/acl.c
f92ce9
index 5416330..403c5b3 100644
f92ce9
--- a/ldap/servers/plugins/acl/acl.c
f92ce9
+++ b/ldap/servers/plugins/acl/acl.c
f92ce9
@@ -1450,9 +1450,7 @@ acl_check_mods(
f92ce9
 			    if (be != NULL)
f92ce9
 				slapi_pblock_get ( pb, SLAPI_BE_LASTMOD, &lastmod );
f92ce9
 			}
f92ce9
-			if (lastmod &&
f92ce9
-			    (strcmp (mod->mod_type, "modifiersname")== 0 ||
f92ce9
-			     strcmp (mod->mod_type, "modifytimestamp")== 0)) {
f92ce9
+			if (lastmod && slapi_attr_is_last_mod(mod->mod_type)) {
f92ce9
 				/* skip pseudo attr(s)  */
f92ce9
 				continue; 
f92ce9
 			}
f92ce9
diff --git a/ldap/servers/plugins/replication/cl5_config.c b/ldap/servers/plugins/replication/cl5_config.c
f92ce9
index 55727c2..a3a0fb3 100644
f92ce9
--- a/ldap/servers/plugins/replication/cl5_config.c
f92ce9
+++ b/ldap/servers/plugins/replication/cl5_config.c
f92ce9
@@ -357,15 +357,10 @@ changelog5_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entr
f92ce9
                 config_attr = (char *) mods[i]->mod_type; 
f92ce9
                 config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
f92ce9
 
f92ce9
-#define ATTR_MODIFIERSNAME	"modifiersname"
f92ce9
-#define ATTR_MODIFYTIMESTAMP	"modifytimestamp"
f92ce9
-
f92ce9
-				if ( strcasecmp ( config_attr, ATTR_MODIFIERSNAME ) == 0 ) {
f92ce9
-                    continue;
f92ce9
-                }
f92ce9
-				if ( strcasecmp ( config_attr, ATTR_MODIFYTIMESTAMP ) == 0 ) {
f92ce9
+				if ( slapi_attr_is_last_mod(config_attr)){
f92ce9
 					continue;
f92ce9
                 }
f92ce9
+
f92ce9
                 /* replace existing value */
f92ce9
                 if ( strcasecmp (config_attr, CONFIG_CHANGELOG_DIR_ATTRIBUTE ) == 0 )
f92ce9
 				{
f92ce9
diff --git a/ldap/servers/plugins/replication/repl5_agmtlist.c b/ldap/servers/plugins/replication/repl5_agmtlist.c
f92ce9
index 5eead07..4a1ff5d 100644
f92ce9
--- a/ldap/servers/plugins/replication/repl5_agmtlist.c
f92ce9
+++ b/ldap/servers/plugins/replication/repl5_agmtlist.c
f92ce9
@@ -524,8 +524,7 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry
f92ce9
 			repl5_set_debug_timeout(val);
f92ce9
 			slapi_ch_free_string(&val;;
f92ce9
 		}
f92ce9
-        else if (strcasecmp (mods[i]->mod_type, "modifytimestamp") == 0 ||
f92ce9
-                 strcasecmp (mods[i]->mod_type, "modifiersname") == 0 ||
f92ce9
+        else if (slapi_attr_is_last_mod(mods[i]->mod_type) ||
f92ce9
                  strcasecmp (mods[i]->mod_type, "description") == 0)
f92ce9
         {
f92ce9
             /* ignore modifier's name and timestamp attributes and the description. */
f92ce9
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
f92ce9
index 2810b11..3bc3916 100644
f92ce9
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
f92ce9
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
f92ce9
@@ -534,8 +534,7 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
f92ce9
                     }
f92ce9
                 }
f92ce9
                 /* ignore modifiers attributes added by the server */
f92ce9
-                else if (strcasecmp (config_attr, "modifytimestamp") == 0 ||
f92ce9
-                         strcasecmp (config_attr, "modifiersname") == 0)
f92ce9
+                else if (slapi_attr_is_last_mod(config_attr))
f92ce9
                 {
f92ce9
                     *returncode = LDAP_SUCCESS;
f92ce9
                 }
f92ce9
diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c
f92ce9
index 875ad22..28fae32 100644
f92ce9
--- a/ldap/servers/slapd/add.c
f92ce9
+++ b/ldap/servers/slapd/add.c
f92ce9
@@ -911,8 +911,8 @@ static int check_rdn_for_created_attrs(Slapi_Entry *e)
f92ce9
     int i, rc = 0;
f92ce9
     Slapi_RDN *rdn = NULL;
f92ce9
     char *value = NULL;
f92ce9
-    char *type[] = {SLAPI_ATTR_UNIQUEID, "modifytimestamp", "createtimestamp",
f92ce9
-                   "creatorsname", "modifiersname", 0};
f92ce9
+    char *type[] = {SLAPI_ATTR_UNIQUEID, "modifytimestamp", "modifiersname", "internalmodifytimestamp",
f92ce9
+                    "internalmodifiersname", "createtimestamp", "creatorsname", 0};
f92ce9
 
f92ce9
     if ((rdn = slapi_rdn_new())) {
f92ce9
         slapi_rdn_init_dn(rdn, slapi_entry_get_dn_const(e));
f92ce9
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.c b/ldap/servers/slapd/back-ldbm/ldbm_config.c
f92ce9
index 0f8cc76..294999c 100644
f92ce9
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.c
f92ce9
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.c
f92ce9
@@ -1849,10 +1849,9 @@ int ldbm_config_ignored_attr(char *attr_name)
f92ce9
     if (!strcasecmp("objectclass", attr_name) ||
f92ce9
         !strcasecmp("cn", attr_name) ||
f92ce9
         !strcasecmp("creatorsname", attr_name) ||
f92ce9
-        !strcasecmp("modifiersname", attr_name) ||
f92ce9
         !strcasecmp("createtimestamp", attr_name) ||
f92ce9
         !strcasecmp(LDBM_NUMSUBORDINATES_STR, attr_name) ||
f92ce9
-        !strcasecmp("modifytimestamp", attr_name)) {
f92ce9
+        slapi_attr_is_last_mod(attr_name)){
f92ce9
         return 1;
f92ce9
     } else {
f92ce9
         return 0;
f92ce9
diff --git a/ldap/servers/slapd/configdse.c b/ldap/servers/slapd/configdse.c
f92ce9
index 339be42..e70e340 100644
f92ce9
--- a/ldap/servers/slapd/configdse.c
f92ce9
+++ b/ldap/servers/slapd/configdse.c
f92ce9
@@ -116,10 +116,9 @@ ignore_attr_type(const char *attr_type)
f92ce9
 		 (strcasecmp (attr_type, "aci") == 0) ||
f92ce9
 		 (strcasecmp (attr_type, "objectclass") == 0) ||
f92ce9
 		 (strcasecmp (attr_type, "numsubordinates") == 0) ||
f92ce9
-		 (strcasecmp (attr_type, "internalModifiersname") == 0) ||
f92ce9
 		 (strcasecmp (attr_type, "internalCreatorsname") == 0) ||
f92ce9
-		 (strcasecmp (attr_type, "modifytimestamp") == 0) ||
f92ce9
-		 (strcasecmp (attr_type, "modifiersname") == 0)) {
f92ce9
+		 slapi_attr_is_last_mod((char *)attr_type))
f92ce9
+	{
f92ce9
 		return 1;
f92ce9
 	}
f92ce9
 
f92ce9
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
f92ce9
index 4e06652..ebd4fdf 100644
f92ce9
--- a/ldap/servers/slapd/opshared.c
f92ce9
+++ b/ldap/servers/slapd/opshared.c
f92ce9
@@ -218,6 +218,23 @@ modify_update_last_modified_attr(Slapi_PBlock *pb, Slapi_Mods *smods)
f92ce9
 }
f92ce9
 
f92ce9
 /*
f92ce9
+ * If the attribute is one of the last mod attributes return 1,
f92ce9
+ * otherwise return 0;
f92ce9
+ */
f92ce9
+int
f92ce9
+slapi_attr_is_last_mod(char *attr)
f92ce9
+{
f92ce9
+    if(strcasecmp (attr, "modifytimestamp") == 0 ||
f92ce9
+       strcasecmp (attr, "modifiersname") == 0 ||
f92ce9
+       strcasecmp (attr, "internalmodifytimestamp") == 0 ||
f92ce9
+       strcasecmp (attr, "internalmodifiersname") == 0)
f92ce9
+    {
f92ce9
+        return 1;
f92ce9
+    }
f92ce9
+    return 0;
f92ce9
+}
f92ce9
+
f92ce9
+/*
f92ce9
  * Returns: 0    - if the operation is successful
f92ce9
  *        < 0    - if operation fails. 
f92ce9
  * Note that an operation is considered "failed" if a result is sent 
f92ce9
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
f92ce9
index 92573d5..ca2fa43 100644
f92ce9
--- a/ldap/servers/slapd/result.c
f92ce9
+++ b/ldap/servers/slapd/result.c
f92ce9
@@ -1059,6 +1059,8 @@ encode_attr(
f92ce9
 
f92ce9
 #define LASTMODATTR( x )	(strcasecmp( x, "modifytimestamp" ) == 0 \
f92ce9
 				    || strcasecmp( x, "modifiersname" ) == 0 \
f92ce9
+				    || strcasecmp( x, "internalmodifytimestamp" ) == 0 \
f92ce9
+				    || strcasecmp( x, "internalmodifiersname" ) == 0 \
f92ce9
 				    || strcasecmp( x, "createtimestamp" ) == 0 \
f92ce9
 				    || strcasecmp( x, "creatorsname" ) == 0)
f92ce9
 
f92ce9
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
f92ce9
index f1ecfe8..8ffc582 100644
f92ce9
--- a/ldap/servers/slapd/slapi-plugin.h
f92ce9
+++ b/ldap/servers/slapd/slapi-plugin.h
f92ce9
@@ -5278,6 +5278,8 @@ int slapi_filter_compare(struct slapi_filter *f1, struct slapi_filter *f2);
f92ce9
 Slapi_Filter *slapi_filter_dup(Slapi_Filter *f);
f92ce9
 int slapi_filter_changetype(Slapi_Filter *f, const char *newtype);
f92ce9
 
f92ce9
+int slapi_attr_is_last_mod(char *attr);
f92ce9
+
f92ce9
 /**
f92ce9
  * Normalize in-place the given filter.  Normalizes the attribute types always.
f92ce9
  * If norm_values is true, will also normalize the values.
f92ce9
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
f92ce9
index a4d85a8..2a0cb82 100644
f92ce9
--- a/ldap/servers/slapd/task.c
f92ce9
+++ b/ldap/servers/slapd/task.c
f92ce9
@@ -802,8 +802,8 @@ static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e,
f92ce9
          * stuck in by the server */
f92ce9
         if ((strcasecmp(mods[i]->mod_type, "ttl") != 0) &&
f92ce9
             (strcasecmp(mods[i]->mod_type, "nsTaskCancel") != 0) &&
f92ce9
-            (strcasecmp(mods[i]->mod_type, "modifiersName") != 0) &&
f92ce9
-            (strcasecmp(mods[i]->mod_type, "modifyTimestamp") != 0)) {
f92ce9
+            !slapi_attr_is_last_mod(mods[i]->mod_type))
f92ce9
+        {
f92ce9
             /* you aren't allowed to change this! */
f92ce9
             *returncode = LDAP_UNWILLING_TO_PERFORM;
f92ce9
             return SLAPI_DSE_CALLBACK_ERROR;
f92ce9
diff --git a/ldap/servers/slapd/tools/mmldif.c b/ldap/servers/slapd/tools/mmldif.c
f92ce9
index bf20945..156d892 100644
f92ce9
--- a/ldap/servers/slapd/tools/mmldif.c
f92ce9
+++ b/ldap/servers/slapd/tools/mmldif.c
f92ce9
@@ -1009,9 +1009,7 @@ addnew(FILE * edf3, const char *changetype, record_t * first)
f92ce9
     for (attnum = 1, att = &first->data;
f92ce9
          attnum <= first->nattrs;
f92ce9
          attnum++, att = attribnext(att)) {
f92ce9
-        if (!stricmp(attribname(att), "modifytimestamp"))
f92ce9
-            continue;
f92ce9
-        if (!stricmp(attribname(att), "modifiersname"))
f92ce9
+        if (slapi_attr_is_last_mod(attribname(att)))
f92ce9
             continue;
f92ce9
         if (!putvalue(edf3, NULL, attribname(att), att->namelen,
f92ce9
                       attribvalue(att), att->valuelen)) {
f92ce9
@@ -1066,15 +1064,11 @@ addmodified(FILE * edf3, attrib1_t * attrib, record_t * first)
f92ce9
      */
f92ce9
     while (a != NULL || num_b <= tot_b) {
f92ce9
         /* ignore operational attrs */
f92ce9
-        if (num_b <= tot_b &&
f92ce9
-            (stricmp(attribname(b), "modifytimestamp") == 0 ||
f92ce9
-             stricmp(attribname(b), "modifiersname") == 0)) {
f92ce9
+        if ( num_b <= tot_b && slapi_attr_is_last_mod(attribname(b)) ){
f92ce9
             b = attribnext(b); num_b++;
f92ce9
             continue;
f92ce9
         }
f92ce9
-        if (a != NULL &&
f92ce9
-            (stricmp(a->name, "modifytimestamp") == 0 ||
f92ce9
-             stricmp(a->name, "modifiersname") == 0)) {
f92ce9
+        if (a != NULL && slapi_attr_is_last_mod(a->name)) {
f92ce9
             a = a->next;
f92ce9
             continue;
f92ce9
         }
f92ce9
-- 
f92ce9
1.9.3
f92ce9