Bug 699458

Summary: windows sync can lose old multi-valued attribute values when a new value is added
Product: Red Hat Enterprise Linux 6 Reporter: Nathan Kinder <nkinder>
Component: 389-ds-baseAssignee: Rich Megginson <rmeggins>
Status: CLOSED ERRATA QA Contact: Chandrasekar Kannan <ckannan>
Severity: urgent Docs Contact:
Priority: urgent    
Version: 6.1CC: amsharma, benl, dpal, jgalipea, jwest, kevinu, nkinder, rmeggins, roysjosh
Target Milestone: rcKeywords: ZStream
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Values could be lost when group memberships were synchronized between 389 Directory Server and Active Directory with the Windows Sync feature. The synchronization and modify operations have been altered to prevent this issue, allowing group updates to synchronize with Active Directory.
Story Points: ---
Clone Of: 695779 Environment:
Last Closed: 2011-12-06 17:48:26 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 695779, 699460    
Bug Blocks: 701556    

Description Nathan Kinder 2011-04-25 18:09:26 UTC
+++ This bug was initially created as a clone of Bug #695779 +++

Description of problem:
A group is sync'd between AD and 389.  Members come over fine.  Certain LDAP clients, when adding a new "uniqueMember" in 389, cause all old members to be immediately deleted on AD (and shortly after deleted from 389 when the dirsync runs).

Some LDAP clients are "dumb" and simply delete the attr and re-add it with all the new values (rather than adding only the ones that are missing).  windows_map_mods_for_replay calls mod_already_made on each mod that is about to be sync'd.  The "dumb" clients have a mod list like [ "delete": "member", "add": "member": [ $existingDN1, $existingDN2, ..., $newDN ] ].  mod_already_made prunes out the adds that already exist in AD, leaving only the new value.  Now we have: [ "delete": "member", "add": "member": [ $newDN ] ].
If I do a ldapmodify by hand and only add the new values (changetype: modify, add: uniqueMember) it works fine.
windows_map_mods_for_replay needs some logic to not prune "add" values from a just deleted attribute.


Version-Release number of selected component (if applicable):
1.2.7.5


How reproducible:
Use any python ldap client (e.g. luma) to add a uniqueMember to a group that is sync'd between AD and 389.  Watch the immediate removal of all old members from the group in AD (with only the new remaining) and the eventual removal of all old group members from 389 as well (when the dirsync hits).


Actual results:
Old members (values) are deleted.


Expected results:
Old values are preserved.


Additional info:
See /usr/lib64/python2.6/site-packages/ldap/modlist.py (python-ldap), modifyModlist.  Right near the end it does:

      if replace_attr_value:
        modlist.append((ldap.MOD_DELETE,attrtype,None))
        modlist.append((ldap.MOD_ADD,attrtype,new_value))

--- Additional comment from nkinder on 2011-04-21 17:24:21 EDT ---

For changes that are sent from DS->AD, we keep a changelog of operations to replay.  What we are dealing with here is essentially 2 mods in one modify operation.  Here's what the operation looks like in LDIF update format:

===========================================
dn: cn=group,dc=example,dc=com
changetype: modify
delete: uniquemember
-
add: uniquemember
uniquemember: uid=user,dc=example,dc=com
===========================================

This is saying to delete all uniquemember values, then add a single value.  It is the same thing as a replace modify operation.  This operation should be played back as is to AD, but we try to skip sending mods that have already been made (this is done to prevent looping mods).  This individual processing of mods to skip stuff already in AD is causing problems since it it done prior to sending any of the mods.  If the first mod makes a change that affects the results of a second mod in the same modify operation, the results could be incorrect.

It seems like we need to process through all of the mods in a single modify operation to determine what exactly needs to be sent to AD.  We may need to create some sort of "resulting entry" in memory to show what the AD entry should look like to determine what mods to make.

--- Additional comment from nkinder on 2011-04-21 18:48:20 EDT ---

Created attachment 494021 [details]
Patch

--- Additional comment from rmeggins on 2011-04-21 18:59:21 EDT ---

Comment on attachment 494021 [details]
Patch

Ok - but note that this changes the server code and the slapi api too.  You could do it without any of those changes by doing 
LDAPMod *modary[2] = {slapi_mod_get_ldapmod_byref(smod), NULL};
slapi_entry_apply_mods(ad_entry, modary);

--- Additional comment from nkinder on 2011-04-25 10:58:11 EDT ---

(In reply to comment #3)
> Comment on attachment 494021 [details]
> Patch
> 
> Ok - but note that this changes the server code and the slapi api too.  You
> could do it without any of those changes by doing 
> LDAPMod *modary[2] = {slapi_mod_get_ldapmod_byref(smod), NULL};
> slapi_entry_apply_mods(ad_entry, modary);

Yes, I added slapi_entry_apply_mod(), as it seemed useful to expose via slapi.  If you feel we should not expse that function, I can use slapi_entry_apply_mods() instead.

--- Additional comment from rmeggins on 2011-04-25 12:05:29 EDT ---

(In reply to comment #4)
> (In reply to comment #3)
> > Comment on attachment 494021 [details]
> > Patch
> > 
> > Ok - but note that this changes the server code and the slapi api too.  You
> > could do it without any of those changes by doing 
> > LDAPMod *modary[2] = {slapi_mod_get_ldapmod_byref(smod), NULL};
> > slapi_entry_apply_mods(ad_entry, modary);
> 
> Yes, I added slapi_entry_apply_mod(), as it seemed useful to expose via slapi. 
> If you feel we should not expse that function, I can use
> slapi_entry_apply_mods() instead.

I guess since we have to release 389-ds-base too, we can go ahead and do this.

--- Additional comment from nkinder on 2011-04-25 13:47:42 EDT ---

Pushed to master.  Thanks to Rich for his review!

Counting objects: 19, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (10/10), 1.70 KiB, done.
Total 10 (delta 8), reused 0 (delta 0)
To ssh://git.fedorahosted.org/git/389/ds.git
   11c8bf1..fb7aee0  master -> master

--- Additional comment from nkinder on 2011-04-25 13:58:18 EDT ---

Pushed to 389-ds-base-1.2.8 branch.

Counting objects: 19, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (10/10), 1.69 KiB, done.
Total 10 (delta 8), reused 0 (delta 0)
Auto packing the repository for optimum performance.
To ssh://git.fedorahosted.org/git/389/ds.git
   c51c77b..2be27d3  128-local -> 389-ds-base-1.2.8

Comment 1 Nathan Kinder 2011-04-25 18:11:24 UTC
Part of this bug fix is in the 389-ds-base package.  We need to expose the new slapi_entry_apply_mod() function for use by the ds-replication package.

Comment 6 Amita Sharma 2011-05-04 07:19:55 UTC
Bug is verified Successfully with below steps:

1. Created a group at AD - add few members to it.
2. Checked they are replicated to RHDS.
3. Then added a new member to that group from DS console.
4. Checked AD, It got reflected properly without deleting the old members of the group.

Marking the bug as VERIFIED.

Comment 7 Laura Bailey 2011-05-17 04:50:42 UTC
    Technical note added. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    New Contents:
Values could be lost when group memberships were synchronized between 389 Directory Server and Active Directory with the Windows Sync feature. The synchronization and modify operations have been altered to prevent this issue, allowing group updates to synchronize with Active Directory.

Comment 8 errata-xmlrpc 2011-12-06 17:48:26 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

http://rhn.redhat.com/errata/RHEA-2011-1711.html