// Resets the enumerator to before the first result in the set. This potentially can be an expensive // operation, e.g., if doing a paged search, may need to re-retrieve the first page of results. // As a special case, if the ResultSet is already at the very beginning, this is guaranteed to be // a no-op. internal override void Reset() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "Reset"); if (!_atBeginning) { _groupsToVisit.Clear(); string originalGroupPath = _groupsVisited[0]; _groupsVisited.Clear(); _groupsVisited.Add(originalGroupPath); _group = _originalGroup; UnsafeNativeMethods.IADsMembers iADsMembers = _group.Members(); _membersEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); _current = null; _currentFakePrincipal = null; _currentForeign = null; _foreignMembers.Clear(); _foreignGroups.Clear(); if (_foreignResultSet != null) { _foreignResultSet.Dispose(); _foreignResultSet = null; } _atBeginning = true; } }
internal SAMGroupsSet(UnsafeNativeMethods.IADsMembers iADsMembers, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { this.atBeginning = true; this.groupsEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); this.storeCtx = storeCtx; this.ctxBase = ctxBase; }
internal override ResultSetBookmark BookmarkAndReset() { SAMMembersSetBookmark sAMMembersSetBookmark = new SAMMembersSetBookmark(); sAMMembersSetBookmark.groupsToVisit = this.groupsToVisit; this.groupsToVisit = new List <string>(); string item = this.groupsVisited[0]; sAMMembersSetBookmark.groupsVisited = this.groupsVisited; this.groupsVisited = new List <string>(); this.groupsVisited.Add(item); sAMMembersSetBookmark.@group = this.@group; sAMMembersSetBookmark.membersEnumerator = this.membersEnumerator; this.@group = this.originalGroup; UnsafeNativeMethods.IADsMembers aDsMember = [email protected](); this.membersEnumerator = ((IEnumerable)aDsMember).GetEnumerator(); sAMMembersSetBookmark.current = this.current; sAMMembersSetBookmark.currentFakePrincipal = this.currentFakePrincipal; sAMMembersSetBookmark.currentForeign = this.currentForeign; this.current = null; this.currentFakePrincipal = null; this.currentForeign = null; sAMMembersSetBookmark.foreignMembers = this.foreignMembers; sAMMembersSetBookmark.foreignGroups = this.foreignGroups; sAMMembersSetBookmark.foreignResultSet = this.foreignResultSet; this.foreignMembers = new List <DirectoryEntry>(); this.foreignGroups = new List <GroupPrincipal>(); this.foreignResultSet = null; sAMMembersSetBookmark.atBeginning = this.atBeginning; this.atBeginning = true; return(sAMMembersSetBookmark); }
internal SAMGroupsSet(UnsafeNativeMethods.IADsMembers iADsMembers, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMGroupsSet", "SAMGroupsSet: creating for path={0}", ctxBase.Path); _groupsEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); _storeCtx = storeCtx; _ctxBase = ctxBase; }
internal SAMMembersSet(string groupPath, UnsafeNativeMethods.IADsGroup group, bool recursive, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { this.atBeginning = true; this.groupsVisited = new List <string>(); this.groupsToVisit = new List <string>(); this.foreignMembers = new List <DirectoryEntry>(); this.foreignGroups = new List <GroupPrincipal>(); this.storeCtx = storeCtx; this.ctxBase = ctxBase; this.@group = group; this.originalGroup = group; this.recursive = recursive; this.groupsVisited.Add(groupPath); UnsafeNativeMethods.IADsMembers aDsMember = group.Members(); this.membersEnumerator = ((IEnumerable)aDsMember).GetEnumerator(); }
internal override bool Matches(DirectoryEntry groupDE) { // If it has no SID, it's not a security principal, and we're not interested in it. // (In reg-SAM, computers don't have accounts and therefore don't have SIDs, but ADSI // creates fake Computer objects for them. In LSAM, computers CAN have accounts, and thus // SIDs). if (groupDE.Properties["objectSid"] == null || groupDE.Properties["objectSid"].Count == 0) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMQuerySet", "GroupMemberMatcher: Matches: skipping no-SID group={0}", groupDE.Path); return(false); } // Enumerate the members of the group, looking for a match UnsafeNativeMethods.IADsGroup iADsGroup = (UnsafeNativeMethods.IADsGroup)groupDE.NativeObject; UnsafeNativeMethods.IADsMembers iADsMembers = iADsGroup.Members(); foreach (UnsafeNativeMethods.IADs nativeMember in ((IEnumerable)iADsMembers)) { // Wrap the DirectoryEntry around the native ADSI object // (which already has the correct credentials) DirectoryEntry memberDE = new DirectoryEntry(nativeMember); // No SID --> not interesting if (memberDE.Properties["objectSid"] == null || memberDE.Properties["objectSid"].Count == 0) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMQuerySet", "GroupMemberMatcher: Matches: skipping member no-SID member={0}", memberDE.Path); continue; } byte[] memberSid = (byte[])memberDE.Properties["objectSid"].Value; // Did we find a matching member in the group? if (Utils.AreBytesEqual(memberSid, _memberSidToMatch)) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMQuerySet", "GroupMemberMatcher: Matches: match member={0}, group={1)", memberDE.Path, groupDE.Path); return(true); } } // We tried all the members in the group and didn't get a match on any GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMQuerySet", "SamMatcher: Matches: no match, group={0}", groupDE.Path); return(false); }
internal override bool Matches(DirectoryEntry groupDE) { bool flag; if (groupDE.Properties["objectSid"] == null || groupDE.Properties["objectSid"].Count == 0) { return(false); } else { UnsafeNativeMethods.IADsGroup nativeObject = (UnsafeNativeMethods.IADsGroup)groupDE.NativeObject; UnsafeNativeMethods.IADsMembers aDsMember = nativeObject.Members(); IEnumerator enumerator = ((IEnumerable)aDsMember).GetEnumerator(); try { while (enumerator.MoveNext()) { UnsafeNativeMethods.IADs current = (UnsafeNativeMethods.IADs)enumerator.Current; DirectoryEntry directoryEntry = new DirectoryEntry(current); if (directoryEntry.Properties["objectSid"] == null || directoryEntry.Properties["objectSid"].Count == 0) { continue; } byte[] value = (byte[])directoryEntry.Properties["objectSid"].Value; if (!Utils.AreBytesEqual(value, this.memberSidToMatch)) { continue; } flag = true; return(flag); } return(false); } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } return(flag); } }
internal SAMMembersSet(string groupPath, UnsafeNativeMethods.IADsGroup group, bool recursive, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "SAMMembersSet: groupPath={0}, recursive={1}, base={2}", groupPath, recursive, ctxBase.Path); _storeCtx = storeCtx; _group = group; _originalGroup = group; _recursive = recursive; _groupsVisited.Add(groupPath); // so we don't revisit it UnsafeNativeMethods.IADsMembers iADsMembers = group.Members(); _membersEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); }
internal override ResultSetBookmark BookmarkAndReset() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "Bookmarking"); SAMMembersSetBookmark bookmark = new SAMMembersSetBookmark(); bookmark.groupsToVisit = _groupsToVisit; _groupsToVisit = new List <string>(); string originalGroupPath = _groupsVisited[0]; bookmark.groupsVisited = _groupsVisited; _groupsVisited = new List <string>(); _groupsVisited.Add(originalGroupPath); bookmark.group = _group; bookmark.membersEnumerator = _membersEnumerator; _group = _originalGroup; UnsafeNativeMethods.IADsMembers iADsMembers = _group.Members(); _membersEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); bookmark.current = _current; bookmark.currentFakePrincipal = _currentFakePrincipal; bookmark.currentForeign = _currentForeign; _current = null; _currentFakePrincipal = null; _currentForeign = null; bookmark.foreignMembers = _foreignMembers; bookmark.foreignGroups = _foreignGroups; bookmark.foreignResultSet = _foreignResultSet; _foreignMembers = new List <DirectoryEntry>(); _foreignGroups = new List <GroupPrincipal>(); _foreignResultSet = null; bookmark.atBeginning = _atBeginning; _atBeginning = true; return(bookmark); }
internal override void Reset() { if (!this.atBeginning) { this.groupsToVisit.Clear(); string item = this.groupsVisited[0]; this.groupsVisited.Clear(); this.groupsVisited.Add(item); this.@group = this.originalGroup; UnsafeNativeMethods.IADsMembers aDsMember = [email protected](); this.membersEnumerator = ((IEnumerable)aDsMember).GetEnumerator(); this.current = null; this.currentFakePrincipal = null; this.currentForeign = null; this.foreignMembers.Clear(); this.foreignGroups.Clear(); if (this.foreignResultSet != null) { this.foreignResultSet.Dispose(); this.foreignResultSet = null; } this.atBeginning = true; } }
// Get groups of which p is a direct member internal override ResultSet GetGroupsMemberOf(Principal p) { // Enforced by the methods that call us Debug.Assert(p.unpersisted == false); if (!p.fakePrincipal) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "GetGroupsMemberOf: is real principal"); // No nested groups or computers as members of groups in SAM if (!(p is UserPrincipal)) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMStoreCtx", "GetGroupsMemberOf: not a user, returning empty set"); return(new EmptySet()); } Debug.Assert(p.UnderlyingObject != null); DirectoryEntry userDE = (DirectoryEntry)p.UnderlyingObject; UnsafeNativeMethods.IADsMembers iadsMembers = (UnsafeNativeMethods.IADsMembers)userDE.Invoke("Groups"); ResultSet resultSet = new SAMGroupsSet(iadsMembers, this, _ctxBase); return(resultSet); } else { // ADSI's IADsGroups doesn't work for fake principals like NT AUTHORITY\NETWORK SERVICE // We use the same SAMQuery set that we use for query-by-example, but with a different // SAMMatcher class to match groups which contain the specified principal as a member // Get the entries we'll iterate over. Write access to Children is controlled through the // ctxBaseLock, but we don't want to have to hold that lock while we're iterating over all // the child entries. So we have to clone the ctxBase --- not ideal, but it prevents // multithreading issues. DirectoryEntries entries = SDSUtils.BuildDirectoryEntry(_ctxBase.Path, _credentials, _authTypes).Children; Debug.Assert(entries != null); // The SAMQuerySet will use this to restrict the types of DirectoryEntry objects returned. List <string> schemaTypes = GetSchemaFilter(typeof(GroupPrincipal)); SecurityIdentifier principalSid = p.Sid; byte[] SidB = new byte[principalSid.BinaryLength]; principalSid.GetBinaryForm(SidB, 0); if (principalSid == null) { GlobalDebug.WriteLineIf(GlobalDebug.Warn, "SAMStoreCtx", "GetGroupsMemberOf: bad SID IC"); throw new InvalidOperationException(SR.StoreCtxNeedValueSecurityIdentityClaimToQuery); } // Create the ResultSet that will perform the client-side filtering SAMQuerySet resultSet = new SAMQuerySet( schemaTypes, entries, _ctxBase, -1, // no size limit this, new GroupMemberMatcher(SidB)); return(resultSet); } }
private bool MoveNextLocal() { bool needToRetry; do { needToRetry = false; bool f = _membersEnumerator.MoveNext(); if (f) // got a value { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: got a value from the enumerator"); UnsafeNativeMethods.IADs nativeMember = (UnsafeNativeMethods.IADs)_membersEnumerator.Current; // If we encountered a group member corresponding to a fake principal such as // NT AUTHORITY/NETWORK SERVICE, construct and prepare to return the fake principal. byte[] sid = (byte[])nativeMember.Get("objectSid"); SidType sidType = Utils.ClassifySID(sid); if (sidType == SidType.FakeObject) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: fake principal, sid={0}", Utils.ByteArrayToString(sid)); _currentFakePrincipal = _storeCtx.ConstructFakePrincipalFromSID(sid); _current = null; _currentForeign = null; if (_foreignResultSet != null) { _foreignResultSet.Dispose(); } _foreignResultSet = null; return(true); } // We do this, rather than using the DirectoryEntry constructor that takes a native IADs object, // is so the credentials get transferred to the new DirectoryEntry. If we just use the native // object constructor, the native object will have the right credentials, but the DirectoryEntry // will have default (null) credentials, which it'll use anytime it needs to use credentials. DirectoryEntry de = SDSUtils.BuildDirectoryEntry( _storeCtx.Credentials, _storeCtx.AuthTypes); if (sidType == SidType.RealObjectFakeDomain) { // Transform the "WinNT://BUILTIN/foo" path to "WinNT://machineName/foo" string builtinADsPath = nativeMember.ADsPath; UnsafeNativeMethods.Pathname pathCracker = new UnsafeNativeMethods.Pathname(); UnsafeNativeMethods.IADsPathname pathName = (UnsafeNativeMethods.IADsPathname)pathCracker; pathName.Set(builtinADsPath, 1 /* ADS_SETTYPE_FULL */); // Build the "WinNT://" portion of the new path StringBuilder adsPath = new StringBuilder(); adsPath.Append("WinNT://"); //adsPath.Append(pathName.Retrieve(9 /*ADS_FORMAT_SERVER */)); // Build the "WinNT://machineName/" portion of the new path adsPath.Append(_storeCtx.MachineUserSuppliedName); adsPath.Append('/'); // Build the "WinNT://machineName/foo" portion of the new path int cElements = pathName.GetNumElements(); Debug.Assert(cElements >= 2); // "WinNT://BUILTIN/foo" == 2 elements // Note that the ADSI WinNT provider indexes them backwards, e.g., in // "WinNT://BUILTIN/A/B", BUILTIN == 2, A == 1, B == 0. for (int i = cElements - 2; i >= 0; i--) { adsPath.Append(pathName.GetElement(i)); adsPath.Append('/'); } adsPath.Remove(adsPath.Length - 1, 1); // remove the trailing "/" de.Path = adsPath.ToString(); GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: fake domain: {0} --> {1}", builtinADsPath, adsPath); } else { Debug.Assert(sidType == SidType.RealObject); de.Path = nativeMember.ADsPath; GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: real domain {0}", de.Path); } // Debug.Assert(Utils.AreBytesEqual(sid, (byte[]) de.Properties["objectSid"].Value)); if (IsLocalMember(sid)) { // If we're processing recursively, and the member is a group, // we don't return it but instead treat it as something to recursively // visit (expand) later. if (!_recursive || !SAMUtils.IsOfObjectClass(de, "Group")) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: setting current to {0}", de.Path); // Not recursive, or not a group. Return the principal. _current = de; _currentFakePrincipal = null; _currentForeign = null; if (_foreignResultSet != null) { _foreignResultSet.Dispose(); } _foreignResultSet = null; return(true); } else { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: adding {0} to groupsToVisit", de.Path); // Save off for later, if we haven't done so already. if (!_groupsVisited.Contains(de.Path) && !_groupsToVisit.Contains(de.Path)) { _groupsToVisit.Add(de.Path); } needToRetry = true; continue; } } else { // It's a foreign principal (e..g, an AD user or group). // Save it off for later. GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: adding {0} to foreignMembers", de.Path); _foreignMembers.Add(de); needToRetry = true; continue; } } else { // We reached the end of this group's membership. // If we're supposed to be recursively expanding, we need to expand // any remaining non-foreign groups we earlier visited. if (_recursive) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: recursive processing, groupsToVisit={0}", _groupsToVisit.Count); if (_groupsToVisit.Count > 0) { // Pull off the next group to visit string groupPath = _groupsToVisit[0]; GlobalDebug.WriteLineIf(GlobalDebug.Info, "SAMMembersSet", "MoveNextLocal: recursively processing {0}", groupPath); _groupsToVisit.RemoveAt(0); _groupsVisited.Add(groupPath); // Set up for the next round of enumeration DirectoryEntry de = SDSUtils.BuildDirectoryEntry( groupPath, _storeCtx.Credentials, _storeCtx.AuthTypes); _group = (UnsafeNativeMethods.IADsGroup)de.NativeObject; UnsafeNativeMethods.IADsMembers iADsMembers = _group.Members(); _membersEnumerator = ((IEnumerable)iADsMembers).GetEnumerator(); // and go on to the first member of this new group needToRetry = true; continue; } } } }while (needToRetry); return(false); }
private bool MoveNextLocal() { bool flag; do { flag = false; bool flag1 = this.membersEnumerator.MoveNext(); if (!flag1) { if (!this.recursive || this.groupsToVisit.Count <= 0) { continue; } string item = this.groupsToVisit[0]; this.groupsToVisit.RemoveAt(0); this.groupsVisited.Add(item); DirectoryEntry directoryEntry = SDSUtils.BuildDirectoryEntry(item, this.storeCtx.Credentials, this.storeCtx.AuthTypes); this.@group = (UnsafeNativeMethods.IADsGroup)directoryEntry.NativeObject; UnsafeNativeMethods.IADsMembers aDsMember = [email protected](); this.membersEnumerator = ((IEnumerable)aDsMember).GetEnumerator(); flag = true; } else { UnsafeNativeMethods.IADs current = (UnsafeNativeMethods.IADs) this.membersEnumerator.Current; byte[] numArray = (byte[])current.Get("objectSid"); SidType sidType = Utils.ClassifySID(numArray); if (sidType != SidType.FakeObject) { DirectoryEntry aDsPath = SDSUtils.BuildDirectoryEntry(this.storeCtx.Credentials, this.storeCtx.AuthTypes); if (sidType != SidType.RealObjectFakeDomain) { aDsPath.Path = current.ADsPath; } else { string str = current.ADsPath; UnsafeNativeMethods.Pathname pathname = new UnsafeNativeMethods.Pathname(); UnsafeNativeMethods.IADsPathname aDsPathname = (UnsafeNativeMethods.IADsPathname)pathname; aDsPathname.Set(str, 1); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("WinNT://"); stringBuilder.Append(this.storeCtx.MachineUserSuppliedName); stringBuilder.Append("/"); int numElements = aDsPathname.GetNumElements(); for (int i = numElements - 2; i >= 0; i--) { stringBuilder.Append(aDsPathname.GetElement(i)); stringBuilder.Append("/"); } stringBuilder.Remove(stringBuilder.Length - 1, 1); aDsPath.Path = stringBuilder.ToString(); } if (!this.IsLocalMember(numArray)) { this.foreignMembers.Add(aDsPath); flag = true; } else { if (!this.recursive || !SAMUtils.IsOfObjectClass(aDsPath, "Group")) { this.current = aDsPath; this.currentFakePrincipal = null; this.currentForeign = null; if (this.foreignResultSet != null) { this.foreignResultSet.Dispose(); } this.foreignResultSet = null; return(true); } else { if (!this.groupsVisited.Contains(aDsPath.Path) && !this.groupsToVisit.Contains(aDsPath.Path)) { this.groupsToVisit.Add(aDsPath.Path); } flag = true; } } } else { this.currentFakePrincipal = this.storeCtx.ConstructFakePrincipalFromSID(numArray); this.current = null; this.currentForeign = null; if (this.foreignResultSet != null) { this.foreignResultSet.Dispose(); } this.foreignResultSet = null; return(true); } } }while (flag); return(false); }