public DirectoryEntry() { this.path = ""; this.useCache = true; this.authenticationType = AuthenticationTypes.Secure; this.options = new DirectoryEntryConfiguration(this); }
public DirectoryEntry(string path, string username, string password, AuthenticationTypes authenticationType) : this(path) { this.credentials = new NetworkCredential(username, password); if (username == null) { this.userNameIsNull = true; } if (password == null) { this.passwordIsNull = true; } this.authenticationType = authenticationType; }
internal DirectoryEntry(string path, bool useCache, string username, string password, AuthenticationTypes authenticationType) { this.path = ""; this.useCache = true; this.authenticationType = AuthenticationTypes.Secure; this.path = path; this.useCache = useCache; this.credentials = new NetworkCredential(username, password); if (username == null) { this.userNameIsNull = true; } if (password == null) { this.passwordIsNull = true; } this.authenticationType = authenticationType; this.options = new DirectoryEntryConfiguration(this); }
static Utils() { Utils.LOGON32_LOGON_NEW_CREDENTIALS = 9; Utils.LOGON32_PROVIDER_WINNT50 = 3; Utils.POLICY_VIEW_LOCAL_INFORMATION = 1; Utils.STANDARD_RIGHTS_REQUIRED = 0xf0000; Utils.SYNCHRONIZE = 0x100000; Utils.THREAD_ALL_ACCESS = Utils.STANDARD_RIGHTS_REQUIRED | Utils.SYNCHRONIZE | 0x3ff; Utils.DefaultAuthType = AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing; Utils.LANG_ENGLISH = 9; Utils.SUBLANG_ENGLISH_US = 1; Utils.SORT_DEFAULT = 0; Utils.LANGID = Convert.ToUInt32 ((ushort)Utils.SUBLANG_ENGLISH_US << 10 | (ushort)Utils.LANG_ENGLISH); Utils.LCID = Convert.ToUInt32 ((ushort)Utils.SORT_DEFAULT << 16 | (ushort)Utils.LANGID); Utils.NORM_IGNORECASE = 1; Utils.NORM_IGNORENONSPACE = 2; Utils.NORM_IGNOREKANATYPE = 0x10000; Utils.NORM_IGNOREWIDTH = 0x20000; Utils.SORT_STRINGSORT = 0x1000; Utils.DEFAULT_CMP_FLAGS = Utils.NORM_IGNORECASE | Utils.NORM_IGNOREKANATYPE | Utils.NORM_IGNORENONSPACE | Utils.NORM_IGNOREWIDTH | Utils.SORT_STRINGSORT; Utils.NTAuthorityString = null; }
internal DirectoryEntry(object adsObject, bool useCache, string username, string password, AuthenticationTypes authenticationType, bool AdsObjIsExternal) { _adsObject = adsObject as UnsafeNativeMethods.IAds; if (_adsObject == null) throw new ArgumentException(Res.GetString(Res.DSDoesNotImplementIADs)); // GetInfo is not needed here. ADSI executes an implicit GetInfo when GetEx // is called on the PropertyValueCollection. 0x800704BC error might be returned // on some WinNT entries, when iterating through 'Users' group members. // if (forceBind) // this.adsObject.GetInfo(); _path = _adsObject.ADsPath; _useCache = useCache; _authenticationType = authenticationType; _credentials = new NetworkCredential(username, password); if (username == null) _userNameIsNull = true; if (password == null) _passwordIsNull = true; if (!useCache) CommitChanges(); _options = new DirectoryEntryConfiguration(this); // We are starting from an already bound connection so make sure the options are set properly. // If this is an externallly managed com object then we don't want to change it's current behavior if (!AdsObjIsExternal) { InitADsObjectOptions(); } }
internal DirectoryEntry(object adsObject, bool useCache, string username, string password, AuthenticationTypes authenticationType) : this(adsObject, useCache, username, password, authenticationType, false) { }
internal DirectoryEntry(string path, bool useCache, string username, string password, AuthenticationTypes authenticationType) { _path = path; _useCache = useCache; _credentials = new NetworkCredential(username, password); if (username == null) _userNameIsNull = true; if (password == null) _passwordIsNull = true; _authenticationType = authenticationType; _options = new DirectoryEntryConfiguration(this); }
private static void UpdateGroupMembership(Principal group, DirectoryEntry de, NetCred credentials, AuthenticationTypes authTypes) { Debug.Assert(group.fakePrincipal == false); PrincipalCollection members = (PrincipalCollection)group.GetValueForProperty(PropertyNames.GroupMembers); UnsafeNativeMethods.IADsGroup iADsGroup = (UnsafeNativeMethods.IADsGroup)de.NativeObject; try { // // Process clear // if (members.Cleared) { // Unfortunately, there's no quick way to clear a group's membership in SAM. // So we remove each member in turn, by enumerating over the group membership // and calling remove on each. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "UpdateGroupMembership: clearing {0}", de.Path); // Prepare the COM Interopt enumeration UnsafeNativeMethods.IADsMembers iADsMembers = iADsGroup.Members(); IEnumVARIANT enumerator = (IEnumVARIANT)iADsMembers._NewEnum; object[] nativeMembers = new object[1]; int hr; do { hr = enumerator.Next(1, nativeMembers, IntPtr.Zero); if (hr == 0) // S_OK { // Found a member, remove it. UnsafeNativeMethods.IADs iADs = (UnsafeNativeMethods.IADs)nativeMembers[0]; iADsGroup.Remove(iADs.ADsPath); } } while (hr == 0); // Either hr == S_FALSE (1), which means we completed the enumerator, // or we encountered an error if (hr != 1) { // Error occurred. GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "UpdateGroupMembership: error while clearing, hr={0}", hr); throw new PrincipalOperationException( String.Format( CultureInfo.CurrentCulture, StringResources.SAMStoreCtxFailedToClearGroup, hr.ToString(CultureInfo.InvariantCulture))); } } // // Process inserted members // List<Principal> insertedMembers = members.Inserted; // First, validate the members to be added foreach (Principal member in insertedMembers) { Type memberType = member.GetType(); if ((memberType != typeof(UserPrincipal)) && (!memberType.IsSubclassOf(typeof(UserPrincipal))) && (memberType != typeof(ComputerPrincipal)) && (!memberType.IsSubclassOf(typeof(ComputerPrincipal))) && (memberType != typeof(GroupPrincipal)) && (!memberType.IsSubclassOf(typeof(GroupPrincipal)))) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, StringResources.StoreCtxUnsupportedPrincipalTypeForGroupInsert, memberType.ToString())); } // Can't inserted unpersisted principal if (member.unpersisted) throw new InvalidOperationException(StringResources.StoreCtxGroupHasUnpersistedInsertedPrincipal); Debug.Assert(member.Context != null); // There's no restriction on the type of principal to be inserted (AD/reg-SAM/MSAM), but it needs // to have a SID IdentityClaim. We'll check that as we go. } // Now add each member to the group foreach (Principal member in insertedMembers) { // We'll add the member via its SID. This works for both MSAM members and AD members. // Build a SID ADsPath string memberSidPath = GetSidADsPathFromPrincipal(member); if (memberSidPath == null) throw new InvalidOperationException(StringResources.SAMStoreCtxCouldntGetSIDForGroupMember); GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "UpdateGroupMembership: inserting {0}", memberSidPath); // Add the member to the group iADsGroup.Add(memberSidPath); } // // Process removed members // List<Principal> removedMembers = members.Removed; foreach (Principal member in removedMembers) { // Since we don't allow any of these to be inserted, none of them should ever // show up in the removal list Debug.Assert(member.unpersisted == false); // If the collection was cleared, there should be no original members to remove Debug.Assert(members.Cleared == false); // Like insertion, we'll remove by SID. // Build a SID ADsPath string memberSidPath = GetSidADsPathFromPrincipal(member); if (memberSidPath == null) throw new InvalidOperationException(StringResources.SAMStoreCtxCouldntGetSIDForGroupMember); GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "UpdateGroupMembership: removing {0}", memberSidPath); // Remove the member from the group iADsGroup.Remove(memberSidPath); } } catch (System.Runtime.InteropServices.COMException e) { GlobalDebug.WriteLineIf(GlobalDebug.Error, "SAMStoreCtx", "UpdateGroupMembership: caught COMException, message={0}, code={1}", e.Message, e.ErrorCode); // ADSI threw an exception trying to update the group membership throw ExceptionHelper.GetExceptionFromCOMException(e); } }
internal DirectoryEntry(object adsObject, bool useCache, string username, string password, AuthenticationTypes authenticationType, bool AdsObjIsExternal) { this.path = ""; this.useCache = true; this.authenticationType = AuthenticationTypes.Secure; this.adsObject = adsObject as System.DirectoryServices.Interop.UnsafeNativeMethods.IAds; if (this.adsObject == null) { throw new ArgumentException(Res.GetString("DSDoesNotImplementIADs")); } this.path = this.adsObject.ADsPath; this.useCache = useCache; this.authenticationType = authenticationType; this.credentials = new NetworkCredential(username, password); if (username == null) { this.userNameIsNull = true; } if (password == null) { this.passwordIsNull = true; } if (!useCache) { this.CommitChanges(); } this.options = new DirectoryEntryConfiguration(this); if (!AdsObjIsExternal) { this.InitADsObjectOptions(); } }
internal DirectoryInformation(string adspath, NetworkCredential credentials, string connProtection, int clientSearchTimeout, int serverSearchTimeout, bool enablePasswordReset) { // // all parameters have already been validated at this point // this.adspath = adspath; this.credentials = credentials; this.clientSearchTimeout = clientSearchTimeout; this.serverSearchTimeout = serverSearchTimeout; Debug.Assert(adspath != null); Debug.Assert(adspath.Length > 0); // // Provider must be LDAP // if (!(adspath.StartsWith("LDAP", StringComparison.Ordinal))) throw new ProviderException(SR.GetString(SR.ADMembership_OnlyLdap_supported)); // // Parse out the server/domain information // NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname(); try { pathCracker.Set(adspath, NativeComInterfaces.ADS_SETTYPE_FULL); } catch (COMException e) { if (e.ErrorCode == unchecked((int) 0x80005000)) throw new ProviderException(SR.GetString(SR.ADMembership_invalid_path)); else throw; } // Get the server and container names try { serverName = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_SERVER); } catch (COMException e) { if (e.ErrorCode == unchecked((int) 0x80005000)) throw new ProviderException(SR.GetString(SR.ADMembership_ServerlessADsPath_not_supported)); else throw; } Debug.Assert(serverName != null); creationContainerDN = containerDN = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_X500_DN); // // Parse out the port number if specified // int index = serverName.IndexOf(':'); if (index != -1) { string tempStr = serverName; serverName = tempStr.Substring(0, index); Debug.Assert(tempStr.Length > index); port = Int32.Parse(tempStr.Substring(index + 1), NumberFormatInfo.InvariantInfo); portSpecified = true; } if (String.Compare(connProtection, "Secure", StringComparison.Ordinal) == 0) { // // The logic is as follows: // 1. Try Ssl first and check if concurrent binds are possible for validating users // 2. If Ssl is not supported, try signing and sealing // 3. If both the above are not supported, then we will fail // bool trySignAndSeal = false; bool trySslWithSecureAuth = false; // first try with simple bind if (!IsDefaultCredential()) { authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows); ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows); try { rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); // this will force a bind rootdse.RefreshCache(); this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl; if (!portSpecified) { port = SSL_PORT; portSpecified = true; } } catch (COMException ce) { if (ce.ErrorCode == unchecked((int) 0x8007052e)) { // // this could be an ADAM target with windows user (in that case simple bind will not work) // trySslWithSecureAuth = true; } else if (ce.ErrorCode == unchecked((int) 0x8007203a)) { // server is not operational error, do nothing, we need to fall back to SignAndSeal trySignAndSeal = true; } else throw; } } else { // default credentials, so we have to do secure bind trySslWithSecureAuth = true; } if (trySslWithSecureAuth) { authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); try { rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); // this will force a bind rootdse.RefreshCache(); this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl; if (!portSpecified) { port = SSL_PORT; portSpecified = true; } } catch (COMException ce) { if (ce.ErrorCode == unchecked((int) 0x8007203a)) { // server is not operational error, do nothing, we need to fall back to SignAndSeal trySignAndSeal = true; } else throw; } } if (trySignAndSeal) { authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows); ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows); try { rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); rootdse.RefreshCache(); this.connectionProtection = ActiveDirectoryConnectionProtection.SignAndSeal; } catch (COMException e) { throw new ProviderException(SR.GetString(SR.ADMembership_Secure_connection_not_established, e.Message), e); } } } else { // // No connection protection // // // we will do a simple bind but we must ensure that the credentials are explicitly specified // since in the case of default credentials we cannot honor it (default credentials become anonymous in the case of // simple bind) // if (IsDefaultCredential()) throw new NotSupportedException(SR.GetString(SR.ADMembership_Default_Creds_not_supported)); // simple bind authenticationType = GetAuthenticationTypes(connectionProtection, CredentialsType.NonWindows); ldapAuthType = GetLdapAuthenticationTypes(connectionProtection, CredentialsType.NonWindows); rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); } // // Determine whether this is AD or ADAM by binding to the rootdse and // checking the supported capabilities // if (rootdse == null) rootdse = new DirectoryEntry(GetADsPath("RootDSE"), GetUsername(), GetPassword(), authenticationType); directoryType = GetDirectoryType(); // // if the directory type is ADAM and the conntectionProtection was selected // as sign and seal, then we should throw an ProviderException. This is becuase validate user will always fail for ADAM // because ADAM does not support secure authentication for ADAM users. // if ((directoryType == DirectoryType.ADAM) && (this.connectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal)) throw new ProviderException(SR.GetString(SR.ADMembership_Ssl_connection_not_established)); // // for AD, we need to block the GC ports // if ((directoryType == DirectoryType.AD) && ((port == GC_PORT) || (port == GC_SSL_PORT))) throw new ProviderException(SR.GetString(SR.ADMembership_GCPortsNotSupported)); // // if container dn is null, we need to get the default naming context // (containerDN cannot be null for ADAM) // if (String.IsNullOrEmpty(containerDN)) { if (directoryType == DirectoryType.AD) { containerDN = (string)rootdse.Properties["defaultNamingContext"].Value; if (containerDN == null) throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_not_specified)); // // we will create users in the default users container, check that it exists // string wkUsersContainerPath = GetADsPath("<WKGUID=" + GUID_USERS_CONTAINER_W + "," + containerDN + ">"); DirectoryEntry containerEntry = new DirectoryEntry(wkUsersContainerPath, GetUsername(), GetPassword(), authenticationType); try { creationContainerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName"); } catch (COMException ce) { if (ce.ErrorCode == unchecked((int) 0x80072030)) throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_does_not_exist)); else throw; } } else { // container must be specified for ADAM throw new ProviderException(SR.GetString(SR.ADMembership_Container_must_be_specified)); } } else { // // Normalize the container name (incase it was specified as GUID or WKGUID) // DirectoryEntry containerEntry = new DirectoryEntry(GetADsPath(containerDN), GetUsername(), GetPassword(), authenticationType); try { creationContainerDN = containerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName"); } catch (COMException ce) { if (ce.ErrorCode == unchecked((int) 0x80072030)) throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist)); else throw; } } // // Check if the specified path(container) exists on the specified server/domain // (NOTE: We need to do this using S.DS.Protocols rather than S.DS because we need to // bypass the referral chasing which is automatic in S.DS) // LdapConnection tempConnection = new LdapConnection(new LdapDirectoryIdentifier(serverName + ":" + port), GetCredentialsWithDomain(credentials), ldapAuthType); tempConnection.SessionOptions.ProtocolVersion = 3; try { tempConnection.SessionOptions.ReferralChasing = System.DirectoryServices.Protocols.ReferralChasingOptions.None; SetSessionOptionsForSecureConnection(tempConnection, false /*useConcurrentBind */); tempConnection.Bind(); SearchRequest request = new SearchRequest(); request.DistinguishedName = containerDN; request.Filter = "(objectClass=*)"; request.Scope = System.DirectoryServices.Protocols.SearchScope.Base; request.Attributes.Add("distinguishedName"); request.Attributes.Add("objectClass"); if (ServerSearchTimeout != -1) request.TimeLimit = new TimeSpan(0, ServerSearchTimeout, 0); SearchResponse response; try { response = (SearchResponse) tempConnection.SendRequest(request); if (response.ResultCode == ResultCode.Referral || response.ResultCode == ResultCode.NoSuchObject) throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist)); else if (response.ResultCode != ResultCode.Success) throw new ProviderException(response.ErrorMessage); } catch (DirectoryOperationException oe) { SearchResponse errorResponse = (SearchResponse) oe.Response; if (errorResponse.ResultCode == ResultCode.NoSuchObject) throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist)); else throw; } // // check that the container is of an object type that can be a superior of a user object // DirectoryAttribute objectClass = response.Entries[0].Attributes["objectClass"]; if (!ContainerIsSuperiorOfUser(objectClass)) throw new ProviderException(SR.GetString(SR.ADMembership_Container_not_superior)); // // Determine whether concurrent bind is supported // if ((connectionProtection == ActiveDirectoryConnectionProtection.None) || (connectionProtection == ActiveDirectoryConnectionProtection.Ssl)) { this.concurrentBindSupported = IsConcurrentBindSupported(tempConnection); } } finally { tempConnection.Dispose(); } // // if this is ADAM, get the partition DN // if (directoryType == DirectoryType.ADAM) { adamPartitionDN = GetADAMPartitionFromContainer(); } else { if (enablePasswordReset) { // for AD, get the lockout duration for user account auto unlock DirectoryEntry de = new DirectoryEntry(GetADsPath((string) PropertyManager.GetPropertyValue(rootdse, "defaultNamingContext")), GetUsername(), GetPassword(), AuthenticationTypes); NativeComInterfaces.IAdsLargeInteger largeIntValue = (NativeComInterfaces.IAdsLargeInteger) PropertyManager.GetPropertyValue(de, "lockoutDuration"); Int64 int64Value = largeIntValue.HighPart * 0x100000000 + (uint) largeIntValue.LowPart; // int64Value is the negative of the number of 100 nanoseconds interval that makes up the lockout duration adLockoutDuration = new TimeSpan(-int64Value); } } }
internal SearchResult(NetworkCredential parentCredentials, AuthenticationTypes parentAuthenticationType) { this.parentCredentials = parentCredentials; this.parentAuthenticationType = parentAuthenticationType; }
// Throws exception if ctxBase is not a computer object public SAMStoreCtx(DirectoryEntry ctxBase, bool ownCtxBase, string username, string password, ContextOptions options) { Debug.Assert(ctxBase != null); GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "Constructing SAMStoreCtx for {0}", ctxBase.Path); Debug.Assert(SAMUtils.IsOfObjectClass(ctxBase, "Computer")); _ctxBase = ctxBase; _ownCtxBase = ownCtxBase; if (username != null && password != null) _credentials = new NetCred(username, password); _contextOptions = options; _authTypes = SDSUtils.MapOptionsToAuthTypes(options); }
internal ResultsEnumerator(SearchResultCollection results, string parentUserName, string parentPassword, AuthenticationTypes parentAuthenticationType) { if (parentUserName != null && parentPassword != null) _parentCredentials = new NetworkCredential(parentUserName, parentPassword); _parentAuthenticationType = parentAuthenticationType; _results = results; _initialized = false; // get the app configuration information object o = PrivilegedConfigurationManager.GetSection("system.directoryservices"); if (o != null && o is bool) { _waitForResult = (bool)o; } }
// {PropertyNames.GroupMembers, "members", null, new ToLdapConverterDelegate(GroupMembersToLdapConverter)}, protected static void UpdateGroupMembership(Principal group, DirectoryEntry de, NetCred credentials, AuthenticationTypes authTypes) { Debug.Assert(group.fakePrincipal == false); PrincipalCollection members = (PrincipalCollection)group.GetValueForProperty(PropertyNames.GroupMembers); DirectoryEntry groupDe = null; try { // // Process clear // if (members.Cleared) { DirectoryEntry copyOfDe = null; try { GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "UpdateGroupMembership: clearing {0}", de.Path); copyOfDe = SDSUtils.BuildDirectoryEntry( de.Path, credentials, authTypes); Debug.Assert(copyOfDe != null); copyOfDe.Properties["member"].Clear(); copyOfDe.CommitChanges(); } finally { if (copyOfDe != null) copyOfDe.Dispose(); } } // // Process inserted members // List<Principal> insertedMembers = members.Inserted; List<Principal> removedMembers = members.Removed; if (insertedMembers.Count > 0 || removedMembers.Count > 0) { groupDe = SDSUtils.BuildDirectoryEntry( de.Path, credentials, authTypes); } // First, validate the members to be added foreach (Principal member in insertedMembers) { Type memberType = member.GetType(); if ((memberType != typeof(UserPrincipal)) && (!memberType.IsSubclassOf(typeof(UserPrincipal))) && (memberType != typeof(ComputerPrincipal)) && (!memberType.IsSubclassOf(typeof(ComputerPrincipal))) && (memberType != typeof(GroupPrincipal)) && (!memberType.IsSubclassOf(typeof(GroupPrincipal))) && (!memberType.IsSubclassOf(typeof(AuthenticablePrincipal)))) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, StringResources.StoreCtxUnsupportedPrincipalTypeForGroupInsert, memberType.ToString())); } // Can't inserted unpersisted principal if (member.unpersisted) throw new InvalidOperationException(StringResources.StoreCtxGroupHasUnpersistedInsertedPrincipal); Debug.Assert(member.Context != null); // Can only insert AD principals (no reg-SAM/MSAM principals) if (member.ContextType == ContextType.Machine) throw new InvalidOperationException(StringResources.ADStoreCtxUnsupportedPrincipalContextForGroupInsert); } // Now add each member to the group foreach (Principal member in insertedMembers) { // For objects in the current domain or any other domains in the forest we need to use the objects DN // SID path would work for current domain but would not work for child or parent domains. // For foreign objects we must use SID path e.g. "<SID=...>" so that the necessary FPO gets autocreated by AD. // It also works in the "fake principal" case (which are always represented as FPOs). if (!member.fakePrincipal && ADUtils.ArePrincipalsInSameForest(group, member)) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "UpdateGroupMembership: add {0}", member.DistinguishedName); groupDe.Properties["member"].Add(member.DistinguishedName); } else { // Build a SID DN. This needs to be a DN path not an ADSI sid path with the LDAP prefix. string memberSidDN = GetSidPathFromPrincipal(member); if (memberSidDN == null) throw new PrincipalOperationException(StringResources.ADStoreCtxCouldntGetSIDForGroupMember); GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "UpdateGroupMembership: add {0}", memberSidDN); // Add the member to the group groupDe.Properties["member"].Add(memberSidDN); } } // If we had any members then commit them. if (insertedMembers.Count > 0) groupDe.CommitChanges(); // // Process removed members // foreach (Principal member in removedMembers) { // Since we don't allow any of these to be inserted, none of them should ever // show up in the removal list Debug.Assert(member.unpersisted == false); Debug.Assert(member.ContextType == ContextType.Domain || member.ContextType == ContextType.ApplicationDirectory); // If the collection was cleared, there should be no original members to remove Debug.Assert(members.Cleared == false); // Since we are using PropertyValueCollection to do the item removal we are constrainted to items that are in the collection // For principals that are in the same forest just use their DN to do the removal. This is how they are represented in the member attr. // For foreign principals we must represent them with their SID binding string since they are locally represented by an FSP object. if (!member.fakePrincipal && ADUtils.ArePrincipalsInSameForest(group, member)) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "UpdateGroupMembership: remove via DN {0}", member.DistinguishedName); // Remove the member from the group groupDe.Properties["member"].Remove(member.DistinguishedName); } else { // SID DN case // Build a SID DN. string memberSidDN = GetSidPathFromPrincipal(member); if (memberSidDN == null) throw new PrincipalOperationException(StringResources.ADStoreCtxCouldntGetSIDForGroupMember); GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADStoreCtx", "UpdateGroupMembership: remove via SID {0}", memberSidDN); // Remove the member from the group groupDe.Properties["member"].Remove(memberSidDN); } } // If we used the collection to do a modification then commit it. if (removedMembers.Count > 0) groupDe.CommitChanges(); } catch (System.Runtime.InteropServices.COMException e) { throw ExceptionHelper.GetExceptionFromCOMException(e); } finally { if (null != groupDe) groupDe.Dispose(); } }