1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.DirectoryServices; 7 using System.Collections.Generic; 8 using System.Collections; 9 using System.Diagnostics; 10 using System.Security.Principal; 11 12 namespace System.DirectoryServices.AccountManagement 13 { 14 internal class ADDNLinkedAttrSet : BookmarkableResultSet 15 { 16 // This class can be used to either enumerate the members of a group, or the groups 17 // to which a principal belongs. If being used to enumerate the members of a group: 18 // * groupDN --- the DN of the group we're enumerating 19 // * members --- array of enumerators containing the DNs of the members of the group we're enumerating (the "member" attribute) 20 // * primaryGroupDN --- should be null 21 // * recursive --- whether or not to recursively enumerate group membership 22 // 23 // If being used to enumerate the groups to which a principal belongs: 24 // * groupDN --- the DN of the principal (i.e., the user) 25 // * members --- the DNs of the groups to which that principal belongs (e.g, the "memberOf" attribute) 26 // * primaryGroupDN --- the DN of the principal's primary group (constructed from the "primaryGroupID" attribute) 27 // * recursive --- should be false 28 // 29 // Note that the variables in this class are generally named in accord with the "enumerating the members 30 // of a group" case. 31 // 32 // It is assumed that recursive enumeration will only be performed for the "enumerating the members of a group" 33 // case, not the "groups to which a principal belongs" case, thus, this.recursive == true implies the former 34 // (but this.recursive == false could imply either case). 35 ADDNLinkedAttrSet( string groupDN, IEnumerable[] members, string primaryGroupDN, DirectorySearcher primaryGroupMembersSearcher, bool recursive, ADStoreCtx storeCtx)36 internal ADDNLinkedAttrSet( 37 string groupDN, 38 IEnumerable[] members, 39 string primaryGroupDN, 40 DirectorySearcher primaryGroupMembersSearcher, 41 bool recursive, 42 ADStoreCtx storeCtx) 43 { 44 GlobalDebug.WriteLineIf(GlobalDebug.Info, 45 "ADDNLinkedAttrSet", 46 "ADDNLinkedAttrSet: groupDN={0}, primaryGroupDN={1}, recursive={2}, PG queryFilter={3}, PG queryBase={4}", 47 groupDN, 48 (primaryGroupDN != null ? primaryGroupDN : "NULL"), 49 recursive, 50 (primaryGroupMembersSearcher != null ? primaryGroupMembersSearcher.Filter : "NULL"), 51 (primaryGroupMembersSearcher != null ? primaryGroupMembersSearcher.SearchRoot.Path : "NULL")); 52 53 _groupsVisited.Add(groupDN); // so we don't revisit it 54 _recursive = recursive; 55 _storeCtx = storeCtx; 56 _originalStoreCtx = storeCtx; 57 58 if (null != members) 59 { 60 foreach (IEnumerable enumerator in members) 61 { 62 _membersQueue.Enqueue(enumerator); 63 _originalMembers.Enqueue(enumerator); 64 } 65 } 66 67 _members = null; 68 69 _currentMembersSearcher = null; 70 _primaryGroupDN = primaryGroupDN; 71 if (primaryGroupDN == null) 72 _returnedPrimaryGroup = true; // so we don't bother trying to return the primary group 73 74 _primaryGroupMembersSearcher = primaryGroupMembersSearcher; 75 76 _expansionMode = ExpansionMode.Enum; 77 _originalExpansionMode = _expansionMode; 78 } 79 ADDNLinkedAttrSet( string groupDN, DirectorySearcher[] membersSearcher, string primaryGroupDN, DirectorySearcher primaryGroupMembersSearcher, bool recursive, ADStoreCtx storeCtx)80 internal ADDNLinkedAttrSet( 81 string groupDN, 82 DirectorySearcher[] membersSearcher, 83 string primaryGroupDN, 84 DirectorySearcher primaryGroupMembersSearcher, 85 bool recursive, 86 ADStoreCtx storeCtx) 87 88 { 89 GlobalDebug.WriteLineIf(GlobalDebug.Info, 90 "ADDNLinkedAttrSet", 91 "ADDNLinkedAttrSet: groupDN={0}, primaryGroupDN={1}, recursive={2}, M queryFilter={3}, M queryBase={4}, PG queryFilter={5}, PG queryBase={6}", 92 groupDN, 93 (primaryGroupDN != null ? primaryGroupDN : "NULL"), 94 recursive, 95 (membersSearcher != null ? membersSearcher[0].Filter : "NULL"), 96 (membersSearcher != null ? membersSearcher[0].SearchRoot.Path : "NULL"), 97 (primaryGroupMembersSearcher != null ? primaryGroupMembersSearcher.Filter : "NULL"), 98 (primaryGroupMembersSearcher != null ? primaryGroupMembersSearcher.SearchRoot.Path : "NULL")); 99 100 _groupsVisited.Add(groupDN); // so we don't revisit it 101 _recursive = recursive; 102 _storeCtx = storeCtx; 103 _originalStoreCtx = storeCtx; 104 105 _members = null; 106 _originalMembers = null; 107 _membersEnum = null; 108 109 _primaryGroupDN = primaryGroupDN; 110 if (primaryGroupDN == null) 111 _returnedPrimaryGroup = true; // so we don't bother trying to return the primary group 112 113 if (null != membersSearcher) 114 { 115 foreach (DirectorySearcher ds in membersSearcher) 116 { 117 _memberSearchersQueue.Enqueue(ds); 118 _memberSearchersQueueOriginal.Enqueue(ds); 119 } 120 } 121 122 _currentMembersSearcher = null; 123 124 _primaryGroupMembersSearcher = primaryGroupMembersSearcher; 125 126 _expansionMode = ExpansionMode.ASQ; 127 _originalExpansionMode = _expansionMode; 128 } 129 130 // Return the principal we're positioned at as a Principal object. 131 // Need to use our StoreCtx's GetAsPrincipal to convert the native object to a Principal 132 override internal object CurrentAsPrincipal 133 { 134 get 135 { 136 if (this.current != null) 137 { 138 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "CurrentAsPrincipal: using current"); 139 if (this.current is DirectoryEntry) 140 return ADUtils.DirectoryEntryAsPrincipal((DirectoryEntry)this.current, _storeCtx); 141 else 142 { 143 return ADUtils.SearchResultAsPrincipal((SearchResult)this.current, _storeCtx, null); 144 } 145 } 146 else 147 { 148 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "CurrentAsPrincipal: using currentForeignPrincipal"); 149 Debug.Assert(_currentForeignPrincipal != null); 150 return _currentForeignPrincipal; 151 } 152 } 153 } 154 155 // Advance the enumerator to the next principal in the result set, pulling in additional pages 156 // of results (or ranges of attribute values) as needed. 157 // Returns true if successful, false if no more results to return. MoveNext()158 override internal bool MoveNext() 159 { 160 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Entering MoveNext"); 161 162 _atBeginning = false; 163 164 bool needToRetry; 165 bool f = false; 166 167 do 168 { 169 needToRetry = false; 170 // reset our found state. If we are restarting the loop we don't have a current principal yet. 171 f = false; 172 173 if (!_returnedPrimaryGroup) 174 { 175 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNext: trying PrimaryGroup DN"); 176 f = MoveNextPrimaryGroupDN(); 177 } 178 179 if (!f) 180 { 181 if (_expansionMode == ExpansionMode.ASQ) 182 { 183 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNext: trying member searcher"); 184 f = MoveNextMemberSearcher(); 185 } 186 else 187 { 188 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNext: trying member enum"); 189 f = MoveNextMemberEnum(); 190 } 191 } 192 193 if (!f) 194 { 195 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNext: trying foreign"); 196 f = MoveNextForeign(ref needToRetry); 197 } 198 199 if (!f) 200 { 201 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNext: trying primary group search"); 202 f = MoveNextQueryPrimaryGroupMember(); 203 } 204 } 205 while (needToRetry); 206 207 return f; 208 } 209 MoveNextPrimaryGroupDN()210 private bool MoveNextPrimaryGroupDN() 211 { 212 // Do the special primary group ID processing if we haven't yet returned the primary group. 213 Debug.Assert(_primaryGroupDN != null); 214 215 this.current = SDSUtils.BuildDirectoryEntry( 216 BuildPathFromDN(_primaryGroupDN), 217 _storeCtx.Credentials, 218 _storeCtx.AuthTypes); 219 220 _storeCtx.InitializeNewDirectoryOptions((DirectoryEntry)this.current); 221 222 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: returning primary group {0}", ((DirectoryEntry)this.current).Path); 223 224 _currentForeignDE = null; 225 _currentForeignPrincipal = null; 226 227 _returnedPrimaryGroup = true; 228 return true; 229 } 230 GetNextSearchResult()231 private bool GetNextSearchResult() 232 { 233 bool memberFound = false; 234 235 do 236 { 237 if (_currentMembersSearcher == null) 238 { 239 Debug.Assert(_memberSearchersQueue != null); 240 241 if (_memberSearchersQueue.Count == 0) 242 { 243 // We are out of searchers in the queue. 244 return false; 245 } 246 else 247 { 248 // Remove the next searcher from the queue and place it in the current search variable. 249 _currentMembersSearcher = _memberSearchersQueue.Dequeue(); 250 _memberSearchResults = _currentMembersSearcher.FindAll(); 251 Debug.Assert(_memberSearchResults != null); 252 _memberSearchResultsEnumerator = _memberSearchResults.GetEnumerator(); 253 } 254 } 255 256 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextQueryMember: have a searcher"); 257 258 memberFound = _memberSearchResultsEnumerator.MoveNext(); 259 260 // The search is complete. 261 // Dipose the searcher and search results. 262 if (!memberFound) 263 { 264 _currentMembersSearcher.Dispose(); 265 _currentMembersSearcher = null; 266 _memberSearchResults.Dispose(); 267 _memberSearchResults = null; 268 } 269 } while (!memberFound); 270 271 return memberFound; 272 } 273 MoveNextMemberSearcher()274 private bool MoveNextMemberSearcher() 275 { 276 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Entering MoveNextMemberSearcher"); 277 278 bool needToRetry = false; 279 bool f = false; 280 281 do 282 { 283 f = GetNextSearchResult(); 284 needToRetry = false; 285 286 if (f) 287 { 288 SearchResult currentSR = (SearchResult)_memberSearchResultsEnumerator.Current; 289 290 // Got a member from this group (or, got a group of which we're a member). 291 // Create a DirectoryEntry for it. 292 string memberDN = (string)currentSR.Properties["distinguishedName"][0]; 293 294 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: got a value from the enumerator: {0}", memberDN); 295 296 // Make sure the member is a principal 297 if ((!ADUtils.IsOfObjectClass(currentSR, "group")) && 298 (!ADUtils.IsOfObjectClass(currentSR, "user")) && // includes computer as well 299 (!ADUtils.IsOfObjectClass(currentSR, "foreignSecurityPrincipal"))) 300 { 301 // We found a member, but it's not a principal type. Skip it. 302 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: not a principal, skipping"); 303 needToRetry = true; 304 } 305 // If we're processing recursively, and the member is a group, we DON'T return it, 306 // but rather treat it as something to recursively visit later 307 // (unless we've already visited the group previously) 308 else if (_recursive && ADUtils.IsOfObjectClass(currentSR, "group")) 309 { 310 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: adding to groupsToVisit"); 311 312 if (!_groupsVisited.Contains(memberDN) && !_groupsToVisit.Contains(memberDN)) 313 _groupsToVisit.Add(memberDN); 314 315 // and go on to the next member.... 316 needToRetry = true; 317 } 318 else if (_recursive && ADUtils.IsOfObjectClass(currentSR, "foreignSecurityPrincipal")) 319 { 320 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: foreign principal, adding to foreignMembers"); 321 322 // If we haven't seen this FPO yet then add it to the seen user database. 323 if (!_usersVisited.ContainsKey(currentSR.Properties["distinguishedName"][0].ToString())) 324 { 325 // The FPO might represent a group, in which case we should recursively enumerate its 326 // membership. So save it off for later processing. 327 _foreignMembersCurrentGroup.Add(currentSR.GetDirectoryEntry()); 328 _usersVisited.Add(currentSR.Properties["distinguishedName"][0].ToString(), true); 329 } 330 331 // and go on to the next member.... 332 needToRetry = true; 333 } 334 else 335 { 336 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: using as current"); 337 338 // Check to see if we have already seen this user during the enumeration 339 // If so then move on to the next user. If not then return it as current. 340 if (!_usersVisited.ContainsKey(currentSR.Properties["distinguishedName"][0].ToString())) 341 { 342 this.current = currentSR; 343 _currentForeignDE = null; 344 _currentForeignPrincipal = null; 345 _usersVisited.Add(currentSR.Properties["distinguishedName"][0].ToString(), true); 346 } 347 else 348 { 349 needToRetry = true; 350 } 351 } 352 } 353 else 354 { 355 // We reached the end of this group's membership. If we're not processing recursively, 356 // we're done. Otherwise, go on to the next group to visit. 357 // First create a DE that points to the group we want to expand, Using that as a search root run 358 // an ASQ search against member and start enumerting those results. 359 if (_recursive) 360 { 361 GlobalDebug.WriteLineIf(GlobalDebug.Info, 362 "ADDNLinkedAttrSet", 363 "MoveNextMemberSearcher: recursive processing, groupsToVisit={0}", 364 _groupsToVisit.Count); 365 366 if (_groupsToVisit.Count > 0) 367 { 368 // Pull off the next group to visit 369 string groupDN = _groupsToVisit[0]; 370 _groupsToVisit.RemoveAt(0); 371 _groupsVisited.Add(groupDN); 372 373 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberSearcher: recursively processing {0}", groupDN); 374 375 // get the membership of this new group 376 DirectoryEntry groupDE = SDSUtils.BuildDirectoryEntry(BuildPathFromDN(groupDN), _storeCtx.Credentials, _storeCtx.AuthTypes); 377 378 _storeCtx.InitializeNewDirectoryOptions(groupDE); 379 380 // Queue up a searcher for the new group expansion. 381 DirectorySearcher ds = SDSUtils.ConstructSearcher(groupDE); 382 ds.Filter = "(objectClass=*)"; 383 ds.SearchScope = SearchScope.Base; 384 ds.AttributeScopeQuery = "member"; 385 ds.CacheResults = false; 386 387 _memberSearchersQueue.Enqueue(ds); 388 389 // and go on to the first member of this new group. 390 needToRetry = true; 391 } 392 } 393 } 394 } 395 while (needToRetry); 396 397 return f; 398 } 399 GetNextEnum()400 private bool GetNextEnum() 401 { 402 bool memberFound = false; 403 404 do 405 { 406 if (null == _members) 407 { 408 if (_membersQueue.Count == 0) 409 { 410 return false; 411 } 412 413 _members = _membersQueue.Dequeue(); 414 _membersEnum = _members.GetEnumerator(); 415 } 416 417 memberFound = _membersEnum.MoveNext(); 418 419 if (!memberFound) 420 { 421 IDisposable disposableMembers = _members as IDisposable; 422 if (disposableMembers != null) 423 { 424 disposableMembers.Dispose(); 425 } 426 IDisposable disposableMembersEnum = _membersEnum as IDisposable; 427 if (disposableMembersEnum != null) 428 { 429 disposableMembersEnum.Dispose(); 430 } 431 _members = null; 432 _membersEnum = null; 433 } 434 } while (!memberFound); 435 436 return memberFound; 437 } 438 MoveNextMemberEnum()439 private bool MoveNextMemberEnum() 440 { 441 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Entering MoveNextMemberEnum"); 442 443 bool needToRetry = false; 444 bool disposeMemberDE = false; 445 bool f; 446 447 do 448 { 449 f = GetNextEnum(); 450 needToRetry = false; 451 disposeMemberDE = false; 452 453 if (f) 454 { 455 DirectoryEntry memberDE = null; 456 try 457 { 458 // Got a member from this group (or, got a group of which we're a member). 459 // Create a DirectoryEntry for it. 460 string memberDN = (string)_membersEnum.Current; 461 462 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: got a value from the enumerator: {0}", memberDN); 463 464 memberDE = SDSUtils.BuildDirectoryEntry( 465 BuildPathFromDN(memberDN), 466 _storeCtx.Credentials, 467 _storeCtx.AuthTypes); 468 469 _storeCtx.InitializeNewDirectoryOptions(memberDE); 470 471 _storeCtx.LoadDirectoryEntryAttributes(memberDE); 472 473 // Make sure the member is a principal 474 if ((!ADUtils.IsOfObjectClass(memberDE, "group")) && 475 (!ADUtils.IsOfObjectClass(memberDE, "user")) && // includes computer as well 476 (!ADUtils.IsOfObjectClass(memberDE, "foreignSecurityPrincipal"))) 477 { 478 // We found a member, but it's not a principal type. Skip it. 479 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: not a principal, skipping"); 480 needToRetry = true; 481 disposeMemberDE = true; //Since member is not principal we don't return it. So mark it for dispose. 482 } 483 // If we're processing recursively, and the member is a group, we DON'T return it, 484 // but rather treat it as something to recursively visit later 485 // (unless we've already visited the group previously) 486 else if (_recursive && ADUtils.IsOfObjectClass(memberDE, "group")) 487 { 488 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: adding to groupsToVisit"); 489 490 if (!_groupsVisited.Contains(memberDN) && !_groupsToVisit.Contains(memberDN)) 491 _groupsToVisit.Add(memberDN); 492 493 // and go on to the next member.... 494 needToRetry = true; 495 disposeMemberDE = true; //Since recursive is set to true, we do not return groups. So mark it for dispose. 496 } 497 else if (_recursive && ADUtils.IsOfObjectClass(memberDE, "foreignSecurityPrincipal")) 498 { 499 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: foreign principal, adding to foreignMembers"); 500 501 // If we haven't seen this FPO yet then add it to the seen user database. 502 if (!_usersVisited.ContainsKey(memberDE.Properties["distinguishedName"][0].ToString())) 503 { 504 // The FPO might represent a group, in which case we should recursively enumerate its 505 // membership. So save it off for later processing. 506 _foreignMembersCurrentGroup.Add(memberDE); 507 _usersVisited.Add(memberDE.Properties["distinguishedName"][0].ToString(), true); 508 disposeMemberDE = false; //We store the FPO DirectoryEntry objects for further processing. So do NOT dispose it. 509 } 510 511 // and go on to the next member.... 512 needToRetry = true; 513 } 514 else 515 { 516 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: using as current"); 517 518 // Check to see if we have already seen this user during the enumeration 519 // If so then move on to the next user. If not then return it as current. 520 if (!_usersVisited.ContainsKey(memberDE.Properties["distinguishedName"][0].ToString())) 521 { 522 this.current = memberDE; 523 _currentForeignDE = null; 524 _currentForeignPrincipal = null; 525 _usersVisited.Add(memberDE.Properties["distinguishedName"][0].ToString(), true); 526 disposeMemberDE = false; //memberDE will be set in the Principal object we return. So do NOT dispose it. 527 } 528 else 529 { 530 needToRetry = true; 531 } 532 } 533 } 534 finally 535 { 536 if (disposeMemberDE && memberDE != null) 537 { 538 //This means the constructed member is not used in the new principal 539 memberDE.Dispose(); 540 } 541 } 542 } 543 else 544 { 545 // We reached the end of this group's membership. If we're not processing recursively, 546 // we're done. Otherwise, go on to the next group to visit. 547 if (_recursive) 548 { 549 GlobalDebug.WriteLineIf(GlobalDebug.Info, 550 "ADDNLinkedAttrSet", 551 "MoveNextLocal: recursive processing, groupsToVisit={0}", 552 _groupsToVisit.Count); 553 554 if (_groupsToVisit.Count > 0) 555 { 556 // Pull off the next group to visit 557 string groupDN = _groupsToVisit[0]; 558 _groupsToVisit.RemoveAt(0); 559 _groupsVisited.Add(groupDN); 560 561 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextMemberEnum: recursively processing {0}", groupDN); 562 563 // get the membership of this new group 564 DirectoryEntry groupDE = SDSUtils.BuildDirectoryEntry( 565 BuildPathFromDN(groupDN), 566 _storeCtx.Credentials, 567 _storeCtx.AuthTypes); 568 569 _storeCtx.InitializeNewDirectoryOptions(groupDE); 570 571 // set up for the next round of enumeration 572 //Here a new DirectoryEntry object is created and passed 573 //to RangeRetriever object. Hence, configure 574 //RangeRetriever to dispose the DirEntry on its dispose. 575 _membersQueue.Enqueue(new RangeRetriever(groupDE, "member", true)); 576 577 // and go on to the first member of this new group.... 578 needToRetry = true; 579 } 580 } 581 } 582 } 583 while (needToRetry); 584 585 return f; 586 } 587 TranslateForeignMembers()588 private void TranslateForeignMembers() 589 { 590 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "ADDNLinkedAttrSet", "TranslateForeignMembers: Translating foreign members"); 591 592 List<Byte[]> sidList = new List<Byte[]>(_foreignMembersCurrentGroup.Count); 593 594 // Foreach foreign principal retrive the sid. 595 // If the SID is for a fake object we have to track it separately. If we were attempt to translate it 596 // it would fail and not be returned and we would lose it. 597 // Once we have a list of sids then translate them against the target store in one call. 598 foreach (DirectoryEntry de in _foreignMembersCurrentGroup) 599 { 600 // Get the SID of the foreign principal 601 if (de.Properties["objectSid"].Count == 0) 602 { 603 throw new PrincipalOperationException(SR.ADStoreCtxCantRetrieveObjectSidForCrossStore); 604 } 605 606 Byte[] sid = (Byte[])de.Properties["objectSid"].Value; 607 608 // What type of SID is it? 609 SidType sidType = Utils.ClassifySID(sid); 610 611 if (sidType == SidType.FakeObject) 612 { 613 //Add the foreign member DirectoryEntry to fakePrincipalMembers list for further translation 614 //This de will be disposed after completing the translation by another code block. 615 _fakePrincipalMembers.Add(de); 616 617 // It's a FPO for something like NT AUTHORITY\NETWORK SERVICE. 618 // There's no real store object corresponding to this FPO, so 619 // fake a Principal. 620 GlobalDebug.WriteLineIf(GlobalDebug.Info, 621 "ADDNLinkedAttrSet", 622 "TranslateForeignMembers: fake principal, SID={0}", 623 Utils.ByteArrayToString(sid)); 624 } 625 else 626 { 627 GlobalDebug.WriteLineIf(GlobalDebug.Info, 628 "ADDNLinkedAttrSet", 629 "TranslateForeignMembers: standard principal, SID={0}", 630 Utils.ByteArrayToString(sid)); 631 632 sidList.Add(sid); 633 //We do NOT need the Foreign member DirectoryEntry object once it has been translated and added to sidList. 634 //So disposing it off now 635 de.Dispose(); 636 } 637 } 638 639 // This call will perform a bulk sid translate to the name + issuer domain. 640 _foreignMembersToReturn = new SidList(sidList, _storeCtx.DnsHostName, _storeCtx.Credentials); 641 642 // We have translated the sids so clear the group now. 643 _foreignMembersCurrentGroup.Clear(); 644 } 645 MoveNextForeign(ref bool outerNeedToRetry)646 private bool MoveNextForeign(ref bool outerNeedToRetry) 647 { 648 outerNeedToRetry = false; 649 bool needToRetry; 650 Principal foreignPrincipal; 651 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Entering MoveNextForeign"); 652 653 do 654 { 655 needToRetry = false; 656 657 if (_foreignMembersCurrentGroup.Count > 0) 658 { 659 TranslateForeignMembers(); 660 } 661 662 if (_fakePrincipalMembers.Count > 0) 663 { 664 foreignPrincipal = _storeCtx.ConstructFakePrincipalFromSID((Byte[])_fakePrincipalMembers[0].Properties["objectSid"].Value); 665 _fakePrincipalMembers[0].Dispose(); 666 _fakePrincipalMembers.RemoveAt(0); 667 } 668 else if ((_foreignMembersToReturn != null) && (_foreignMembersToReturn.Length > 0)) 669 { 670 StoreCtx foreignStoreCtx; 671 672 SidListEntry foreignSid = _foreignMembersToReturn[0]; 673 674 // sidIssuerName is null only if SID was not resolved 675 // return a unknown principal back 676 if (null == foreignSid.sidIssuerName) 677 { 678 // create and return the unknown principal if it is not yet present in usersVisited 679 if (!_usersVisited.ContainsKey(foreignSid.name)) 680 { 681 byte[] sid = Utils.ConvertNativeSidToByteArray(foreignSid.pSid); 682 UnknownPrincipal unknownPrincipal = UnknownPrincipal.CreateUnknownPrincipal(_storeCtx.OwningContext, sid, foreignSid.name); 683 _usersVisited.Add(foreignSid.name, true); 684 this.current = null; 685 _currentForeignDE = null; 686 _currentForeignPrincipal = unknownPrincipal; 687 // remove the current member 688 _foreignMembersToReturn.RemoveAt(0); 689 return true; 690 } 691 692 // remove the current member 693 _foreignMembersToReturn.RemoveAt(0); 694 695 needToRetry = true; 696 continue; 697 } 698 699 SidType sidType = Utils.ClassifySID(foreignSid.pSid); 700 701 if (sidType == SidType.RealObjectFakeDomain) 702 { 703 // This is a BUILTIN object. It's a real object on the store we're connected to, but LookupSid 704 // will tell us it's a member of the BUILTIN domain. Resolve it as a principal on our store. 705 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "ADDNLinkedAttrSet", "MoveNextForeign: builtin principal"); 706 foreignStoreCtx = _storeCtx; 707 } 708 else 709 { 710 ContextOptions remoteOptions = DefaultContextOptions.ADDefaultContextOption; 711 712 #if USE_CTX_CACHE 713 PrincipalContext remoteCtx = SDSCache.Domain.GetContext(foreignSid.sidIssuerName, _storeCtx.Credentials, remoteOptions); 714 #else 715 PrincipalContext remoteCtx = new PrincipalContext( 716 ContextType.Domain, 717 foreignSid.sidIssuerName, 718 null, 719 (this.storeCtx.Credentials != null ? this.storeCtx.Credentials.UserName : null), 720 (this.storeCtx.Credentials != null ? storeCtx.storeCtx.Credentials.Password : null), 721 remoteOptions); 722 723 #endif 724 foreignStoreCtx = remoteCtx.QueryCtx; 725 } 726 727 foreignPrincipal = foreignStoreCtx.FindPrincipalByIdentRef( 728 typeof(Principal), 729 UrnScheme.SidScheme, 730 (new SecurityIdentifier(Utils.ConvertNativeSidToByteArray(_foreignMembersToReturn[0].pSid), 0)).ToString(), 731 DateTime.UtcNow); 732 733 if (null == foreignPrincipal) 734 { 735 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "ADDNLinkedAttrSet", "MoveNextForeign: no matching principal"); 736 throw new PrincipalOperationException(SR.ADStoreCtxFailedFindCrossStoreTarget); 737 } 738 739 _foreignMembersToReturn.RemoveAt(0); 740 } 741 else 742 { 743 // We don't have any more foreign principals to return so start with the foreign groups 744 if (_foreignGroups.Count > 0) 745 { 746 outerNeedToRetry = true; 747 748 // Determine the domainFunctionalityMode of the foreign domain. If they are W2k or not a global group then we can't use ASQ. 749 if (_foreignGroups[0].Context.ServerInformation.OsVersion == DomainControllerMode.Win2k || 750 _foreignGroups[0].GroupScope != GroupScope.Global) 751 { 752 _expansionMode = ExpansionMode.Enum; 753 return ExpandForeignGroupEnumerator(); 754 } 755 else 756 { 757 _expansionMode = ExpansionMode.ASQ; 758 return ExpandForeignGroupSearcher(); 759 } 760 } 761 else 762 { 763 // We are done with foreign principals and groups.. 764 return false; 765 } 766 } 767 768 if (foreignPrincipal is GroupPrincipal) 769 { 770 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextForeign: foreign member is a group"); 771 772 // A group, need to recursively expand it (unless it's a fake group, 773 // in which case it is by definition empty and so contains nothing to expand, or unless 774 // we've already or will visit it). 775 // Postpone to later. 776 if (!foreignPrincipal.fakePrincipal) 777 { 778 string groupDN = (string)((DirectoryEntry)foreignPrincipal.UnderlyingObject).Properties["distinguishedName"].Value; 779 780 GlobalDebug.WriteLineIf(GlobalDebug.Info, 781 "ADDNLinkedAttrSet", 782 "MoveNextForeign: not a fake group, adding {0} to foreignGroups", 783 groupDN); 784 785 if (!_groupsVisited.Contains(groupDN) && !_groupsToVisit.Contains(groupDN)) 786 { 787 _foreignGroups.Add((GroupPrincipal)foreignPrincipal); 788 } 789 else 790 { 791 foreignPrincipal.Dispose(); 792 } 793 } 794 795 needToRetry = true; 796 continue; 797 } 798 else 799 { 800 // Not a group, nothing to recursively expand, so just return it. 801 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextForeign: using as currentForeignDE/currentForeignPrincipal"); 802 803 DirectoryEntry foreignDE = (DirectoryEntry)foreignPrincipal.GetUnderlyingObject(); 804 805 _storeCtx.LoadDirectoryEntryAttributes(foreignDE); 806 807 if (!_usersVisited.ContainsKey(foreignDE.Properties["distinguishedName"][0].ToString())) 808 { 809 _usersVisited.Add(foreignDE.Properties["distinguishedName"][0].ToString(), true); 810 this.current = null; 811 _currentForeignDE = null; 812 _currentForeignPrincipal = foreignPrincipal; 813 return true; 814 } 815 else 816 { 817 foreignPrincipal.Dispose(); 818 } 819 820 needToRetry = true; 821 continue; 822 } 823 } 824 while (needToRetry); 825 826 return false; 827 } 828 ExpandForeignGroupEnumerator()829 private bool ExpandForeignGroupEnumerator() 830 { 831 Debug.Assert(_recursive == true); 832 GlobalDebug.WriteLineIf(GlobalDebug.Info, 833 "ADDNLinkedAttrSet", 834 "ExpandForeignGroupEnumerator: there are {0} foreignGroups", 835 _foreignGroups.Count); 836 837 GroupPrincipal foreignGroup = _foreignGroups[0]; 838 _foreignGroups.RemoveAt(0); 839 840 // Since members of AD groups must be AD objects 841 Debug.Assert(foreignGroup.Context.QueryCtx is ADStoreCtx); 842 Debug.Assert(foreignGroup.UnderlyingObject is DirectoryEntry); 843 Debug.Assert(((DirectoryEntry)foreignGroup.UnderlyingObject).Path.StartsWith("LDAP:", StringComparison.Ordinal)); 844 845 _storeCtx = (ADStoreCtx)foreignGroup.Context.QueryCtx; 846 847 //Here the foreignGroup object is removed from the foreignGroups collection. 848 //and not used anymore. Hence, configure RangeRetriever to dispose the DirEntry on its dispose. 849 _membersQueue.Enqueue(new RangeRetriever((DirectoryEntry)foreignGroup.UnderlyingObject, "member", true)); 850 851 string groupDN = (string)((DirectoryEntry)foreignGroup.UnderlyingObject).Properties["distinguishedName"].Value; 852 _groupsVisited.Add(groupDN); 853 854 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "ExpandForeignGroupEnumerator: recursively processing {0}", groupDN); 855 856 return true; 857 } 858 ExpandForeignGroupSearcher()859 private bool ExpandForeignGroupSearcher() 860 { 861 Debug.Assert(_recursive == true); 862 GlobalDebug.WriteLineIf(GlobalDebug.Info, 863 "ADDNLinkedAttrSet", 864 "ExpandForeignGroupSearcher: there are {0} foreignGroups", 865 _foreignGroups.Count); 866 867 GroupPrincipal foreignGroup = _foreignGroups[0]; 868 _foreignGroups.RemoveAt(0); 869 870 // Since members of AD groups must be AD objects 871 Debug.Assert(foreignGroup.Context.QueryCtx is ADStoreCtx); 872 Debug.Assert(foreignGroup.UnderlyingObject is DirectoryEntry); 873 Debug.Assert(((DirectoryEntry)foreignGroup.UnderlyingObject).Path.StartsWith("LDAP:", StringComparison.Ordinal)); 874 875 _storeCtx = (ADStoreCtx)foreignGroup.Context.QueryCtx; 876 877 // Queue up a searcher for the new group expansion. 878 DirectorySearcher ds = SDSUtils.ConstructSearcher((DirectoryEntry)foreignGroup.UnderlyingObject); 879 ds.Filter = "(objectClass=*)"; 880 ds.SearchScope = SearchScope.Base; 881 ds.AttributeScopeQuery = "member"; 882 ds.CacheResults = false; 883 884 _memberSearchersQueue.Enqueue(ds); 885 886 string groupDN = (string)((DirectoryEntry)foreignGroup.UnderlyingObject).Properties["distinguishedName"].Value; 887 _groupsVisited.Add(groupDN); 888 889 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "ExpandForeignGroupSearcher: recursively processing {0}", groupDN); 890 891 return true; 892 } 893 MoveNextQueryPrimaryGroupMember()894 private bool MoveNextQueryPrimaryGroupMember() 895 { 896 bool f = false; 897 898 if (_primaryGroupMembersSearcher != null) 899 { 900 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextQueryMember: have a searcher"); 901 902 if (_queryMembersResults == null) 903 { 904 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "MoveNextQueryMember: issuing query"); 905 906 _queryMembersResults = _primaryGroupMembersSearcher.FindAll(); 907 908 Debug.Assert(_queryMembersResults != null); 909 910 _queryMembersResultEnumerator = _queryMembersResults.GetEnumerator(); 911 } 912 913 f = _queryMembersResultEnumerator.MoveNext(); 914 915 if (f) 916 { 917 this.current = (SearchResult)_queryMembersResultEnumerator.Current; 918 Debug.Assert(this.current != null); 919 920 _currentForeignDE = null; 921 _currentForeignPrincipal = null; 922 923 GlobalDebug.WriteLineIf(GlobalDebug.Info, 924 "ADDNLinkedAttrSet", 925 "MoveNextQueryMember: got a result, using as current {0}", 926 ((SearchResult)this.current).Path); 927 } 928 } 929 930 return f; 931 } 932 933 // Resets the enumerator to before the first result in the set. This potentially can be an expensive 934 // operation, e.g., if doing a paged search, may need to re-retrieve the first page of results. 935 // As a special case, if the ResultSet is already at the very beginning, this is guaranteed to be 936 // a no-op. Reset()937 override internal void Reset() 938 { 939 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Reset"); 940 941 if (!_atBeginning) 942 { 943 _usersVisited.Clear(); 944 _groupsToVisit.Clear(); 945 string originalGroupDN = _groupsVisited[0]; 946 _groupsVisited.Clear(); 947 _groupsVisited.Add(originalGroupDN); 948 949 // clear the current enumerator 950 _members = null; 951 _membersEnum = null; 952 953 // replace all items in the queue with the originals and reset them. 954 if (null != _originalMembers) 955 { 956 _membersQueue.Clear(); 957 foreach (IEnumerable ie in _originalMembers) 958 { 959 _membersQueue.Enqueue(ie); 960 IEnumerator enumerator = ie.GetEnumerator(); 961 enumerator.Reset(); 962 } 963 } 964 965 _expansionMode = _originalExpansionMode; 966 967 _storeCtx = _originalStoreCtx; 968 969 this.current = null; 970 if (_primaryGroupDN != null) 971 _returnedPrimaryGroup = false; 972 973 _foreignMembersCurrentGroup.Clear(); 974 _fakePrincipalMembers.Clear(); 975 976 if (null != _foreignMembersToReturn) 977 _foreignMembersToReturn.Clear(); 978 979 _currentForeignPrincipal = null; 980 _currentForeignDE = null; 981 982 _foreignGroups.Clear(); 983 984 _queryMembersResultEnumerator = null; 985 if (_queryMembersResults != null) 986 { 987 _queryMembersResults.Dispose(); 988 _queryMembersResults = null; 989 } 990 991 if (null != _currentMembersSearcher) 992 { 993 _currentMembersSearcher.Dispose(); 994 _currentMembersSearcher = null; 995 } 996 997 _memberSearchResultsEnumerator = null; 998 if (_memberSearchResults != null) 999 { 1000 _memberSearchResults.Dispose(); 1001 _memberSearchResults = null; 1002 } 1003 1004 if (null != _memberSearchersQueue) 1005 { 1006 foreach (DirectorySearcher ds in _memberSearchersQueue) 1007 { 1008 ds.Dispose(); 1009 } 1010 1011 _memberSearchersQueue.Clear(); 1012 1013 if (null != _memberSearchersQueueOriginal) 1014 { 1015 foreach (DirectorySearcher ds in _memberSearchersQueueOriginal) 1016 { 1017 _memberSearchersQueue.Enqueue(ds); 1018 } 1019 } 1020 } 1021 1022 _atBeginning = true; 1023 } 1024 } 1025 BookmarkAndReset()1026 override internal ResultSetBookmark BookmarkAndReset() 1027 { 1028 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Bookmarking"); 1029 1030 ADDNLinkedAttrSetBookmark bookmark = new ADDNLinkedAttrSetBookmark(); 1031 1032 bookmark.usersVisited = _usersVisited; 1033 _usersVisited = new Dictionary<string, bool>(); 1034 1035 bookmark.groupsToVisit = _groupsToVisit; 1036 _groupsToVisit = new List<string>(); 1037 1038 string originalGroupDN = _groupsVisited[0]; 1039 bookmark.groupsVisited = _groupsVisited; 1040 _groupsVisited = new List<string>(); 1041 _groupsVisited.Add(originalGroupDN); 1042 1043 bookmark.expansionMode = _expansionMode; 1044 1045 // bookmark the current enumerators 1046 bookmark.members = _members; 1047 bookmark.membersEnum = _membersEnum; 1048 1049 // Clear the current enumerators for reset 1050 _members = null; 1051 _membersEnum = null; 1052 1053 // Copy all enumerators in the queue over to the bookmark queue. 1054 if (null != _membersQueue) 1055 { 1056 bookmark.membersQueue = new Queue<IEnumerable>(_membersQueue.Count); 1057 foreach (IEnumerable ie in _membersQueue) 1058 { 1059 bookmark.membersQueue.Enqueue(ie); 1060 } 1061 } 1062 1063 // Refill the original queue with the original enumerators and reset them 1064 if (null != _membersQueue) 1065 { 1066 _membersQueue.Clear(); 1067 1068 if (_originalMembers != null) 1069 { 1070 foreach (IEnumerable ie in _originalMembers) 1071 { 1072 _membersQueue.Enqueue(ie); 1073 IEnumerator enumerator = ie.GetEnumerator(); 1074 enumerator.Reset(); 1075 } 1076 } 1077 } 1078 1079 bookmark.storeCtx = _storeCtx; 1080 1081 _expansionMode = _originalExpansionMode; 1082 1083 if (null != _currentMembersSearcher) 1084 { 1085 _currentMembersSearcher.Dispose(); 1086 _currentMembersSearcher = null; 1087 } 1088 1089 _storeCtx = _originalStoreCtx; 1090 1091 bookmark.current = this.current; 1092 bookmark.returnedPrimaryGroup = _returnedPrimaryGroup; 1093 this.current = null; 1094 if (_primaryGroupDN != null) 1095 _returnedPrimaryGroup = false; 1096 1097 bookmark.foreignMembersCurrentGroup = _foreignMembersCurrentGroup; 1098 bookmark.fakePrincipalMembers = _fakePrincipalMembers; 1099 bookmark.foreignMembersToReturn = _foreignMembersToReturn; 1100 bookmark.currentForeignPrincipal = _currentForeignPrincipal; 1101 bookmark.currentForeignDE = _currentForeignDE; 1102 _foreignMembersCurrentGroup = new List<DirectoryEntry>(); 1103 _fakePrincipalMembers = new List<DirectoryEntry>(); 1104 _currentForeignDE = null; 1105 1106 bookmark.foreignGroups = _foreignGroups; 1107 _foreignGroups = new List<GroupPrincipal>(); 1108 1109 bookmark.queryMembersResults = _queryMembersResults; 1110 bookmark.queryMembersResultEnumerator = _queryMembersResultEnumerator; 1111 _queryMembersResults = null; 1112 _queryMembersResultEnumerator = null; 1113 1114 bookmark.memberSearchResults = _memberSearchResults; 1115 bookmark.memberSearchResultsEnumerator = _memberSearchResultsEnumerator; 1116 _memberSearchResults = null; 1117 _memberSearchResultsEnumerator = null; 1118 1119 if (null != _memberSearchersQueue) 1120 { 1121 bookmark.memberSearcherQueue = new Queue<DirectorySearcher>(_memberSearchersQueue.Count); 1122 1123 foreach (DirectorySearcher ds in _memberSearchersQueue) 1124 { 1125 bookmark.memberSearcherQueue.Enqueue(ds); 1126 } 1127 } 1128 1129 if (null != _memberSearchersQueueOriginal) 1130 { 1131 _memberSearchersQueue.Clear(); 1132 1133 foreach (DirectorySearcher ds in _memberSearchersQueueOriginal) 1134 { 1135 _memberSearchersQueue.Enqueue(ds); 1136 } 1137 } 1138 1139 bookmark.atBeginning = _atBeginning; 1140 _atBeginning = true; 1141 1142 return bookmark; 1143 } 1144 RestoreBookmark(ResultSetBookmark bookmark)1145 override internal void RestoreBookmark(ResultSetBookmark bookmark) 1146 { 1147 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Restoring from bookmark"); 1148 1149 Debug.Assert(bookmark is ADDNLinkedAttrSetBookmark); 1150 ADDNLinkedAttrSetBookmark adBookmark = (ADDNLinkedAttrSetBookmark)bookmark; 1151 1152 _usersVisited = adBookmark.usersVisited; 1153 _groupsToVisit = adBookmark.groupsToVisit; 1154 _groupsVisited = adBookmark.groupsVisited; 1155 _storeCtx = adBookmark.storeCtx; 1156 this.current = adBookmark.current; 1157 _returnedPrimaryGroup = adBookmark.returnedPrimaryGroup; 1158 _foreignMembersCurrentGroup = adBookmark.foreignMembersCurrentGroup; 1159 _fakePrincipalMembers = adBookmark.fakePrincipalMembers; 1160 _foreignMembersToReturn = adBookmark.foreignMembersToReturn; 1161 _currentForeignPrincipal = adBookmark.currentForeignPrincipal; 1162 _currentForeignDE = adBookmark.currentForeignDE; 1163 _foreignGroups = adBookmark.foreignGroups; 1164 if (_queryMembersResults != null) 1165 _queryMembersResults.Dispose(); 1166 _queryMembersResults = adBookmark.queryMembersResults; 1167 _queryMembersResultEnumerator = adBookmark.queryMembersResultEnumerator; 1168 _memberSearchResults = adBookmark.memberSearchResults; 1169 _memberSearchResultsEnumerator = adBookmark.memberSearchResultsEnumerator; 1170 _atBeginning = adBookmark.atBeginning; 1171 _expansionMode = adBookmark.expansionMode; 1172 1173 // Replace enumerators 1174 _members = adBookmark.members; 1175 _membersEnum = adBookmark.membersEnum; 1176 1177 // Replace the enumerator queue elements 1178 if (null != _membersQueue) 1179 { 1180 _membersQueue.Clear(); 1181 1182 if (null != adBookmark.membersQueue) 1183 { 1184 foreach (IEnumerable ie in adBookmark.membersQueue) 1185 { 1186 _membersQueue.Enqueue(ie); 1187 } 1188 } 1189 } 1190 1191 if (null != _memberSearchersQueue) 1192 { 1193 foreach (DirectorySearcher ds in _memberSearchersQueue) 1194 { 1195 ds.Dispose(); 1196 } 1197 1198 _memberSearchersQueue.Clear(); 1199 1200 if (null != adBookmark.memberSearcherQueue) 1201 { 1202 foreach (DirectorySearcher ds in adBookmark.memberSearcherQueue) 1203 { 1204 _memberSearchersQueue.Enqueue(ds); 1205 } 1206 } 1207 } 1208 } 1209 1210 // IDisposable implementation Dispose()1211 public override void Dispose() 1212 { 1213 try 1214 { 1215 if (!_disposed) 1216 { 1217 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing"); 1218 1219 if (_primaryGroupMembersSearcher != null) 1220 { 1221 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing primaryGroupMembersSearcher"); 1222 _primaryGroupMembersSearcher.Dispose(); 1223 } 1224 1225 if (_queryMembersResults != null) 1226 { 1227 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing queryMembersResults"); 1228 _queryMembersResults.Dispose(); 1229 } 1230 1231 if (_currentMembersSearcher != null) 1232 { 1233 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing membersSearcher"); 1234 _currentMembersSearcher.Dispose(); 1235 } 1236 1237 if (_memberSearchResults != null) 1238 { 1239 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing memberSearchResults"); 1240 _memberSearchResults.Dispose(); 1241 } 1242 1243 if (_memberSearchersQueue != null) 1244 { 1245 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing memberSearchersQueue"); 1246 foreach (DirectorySearcher ds in _memberSearchersQueue) 1247 { 1248 ds.Dispose(); 1249 } 1250 1251 _memberSearchersQueue.Clear(); 1252 } 1253 IDisposable disposableMembers = _members as IDisposable; 1254 if (disposableMembers != null) 1255 { 1256 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing members Enumerable"); 1257 disposableMembers.Dispose(); 1258 } 1259 IDisposable disposableMembersEnum = _membersEnum as IDisposable; 1260 if (disposableMembersEnum != null) 1261 { 1262 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing membersEnum Enumerator"); 1263 disposableMembersEnum.Dispose(); 1264 } 1265 if (_membersQueue != null) 1266 { 1267 GlobalDebug.WriteLineIf(GlobalDebug.Info, "ADDNLinkedAttrSet", "Dispose: disposing membersQueue"); 1268 foreach (IEnumerable enumerable in _membersQueue) 1269 { 1270 IDisposable disposableEnum = enumerable as IDisposable; 1271 if (disposableEnum != null) 1272 { 1273 disposableEnum.Dispose(); 1274 } 1275 } 1276 } 1277 if (_foreignGroups != null) 1278 { 1279 foreach (GroupPrincipal gp in _foreignGroups) 1280 { 1281 gp.Dispose(); 1282 } 1283 } 1284 1285 _disposed = true; 1286 } 1287 } 1288 finally 1289 { 1290 base.Dispose(); 1291 } 1292 } 1293 1294 // 1295 // 1296 // 1297 1298 private UnsafeNativeMethods.IADsPathname _pathCracker = null; 1299 private object _pathLock = new object(); 1300 private Dictionary<string, bool> _usersVisited = new Dictionary<string, bool>(); 1301 1302 // The 0th entry in this list is always the DN of the original group/user whose membership we're querying 1303 private List<string> _groupsVisited = new List<string>(); 1304 1305 private List<string> _groupsToVisit = new List<string>(); 1306 1307 protected Object current = null; // current member of the group (or current group of the user) 1308 1309 private bool _returnedPrimaryGroup = false; 1310 private string _primaryGroupDN; // the DN of the user's PrimaryGroup (not included in this.members/originalMembers) 1311 1312 private bool _recursive; 1313 1314 private Queue<IEnumerable> _membersQueue = new Queue<IEnumerable>(); 1315 private IEnumerable _members; // the membership we're currently enumerating over 1316 private Queue<IEnumerable> _originalMembers = new Queue<IEnumerable>(); // the membership we started off with (before recursing) 1317 1318 private IEnumerator _membersEnum = null; 1319 1320 private ADStoreCtx _storeCtx; 1321 private ADStoreCtx _originalStoreCtx; 1322 1323 private bool _atBeginning = true; 1324 1325 private bool _disposed = false; 1326 1327 // foreign 1328 // This contains a list of employees built while enumerating the current group. These are FSP objects in the current domain and need to 1329 // be translated to find out the domain that holds the actual object. 1330 private List<DirectoryEntry> _foreignMembersCurrentGroup = new List<DirectoryEntry>(); 1331 // List of objects from the group tha are actual fake group objects. 1332 private List<DirectoryEntry> _fakePrincipalMembers = new List<DirectoryEntry>(); 1333 // list of SIDs + store that have been translated. These could be any principal object 1334 private SidList _foreignMembersToReturn = null; 1335 1336 private Principal _currentForeignPrincipal = null; 1337 private DirectoryEntry _currentForeignDE = null; 1338 1339 private List<GroupPrincipal> _foreignGroups = new List<GroupPrincipal>(); 1340 1341 // members based on a query (used for users who are group members by virtue of their primaryGroupId pointing to the group) 1342 private DirectorySearcher _primaryGroupMembersSearcher; 1343 private SearchResultCollection _queryMembersResults = null; 1344 private IEnumerator _queryMembersResultEnumerator = null; 1345 1346 private DirectorySearcher _currentMembersSearcher = null; 1347 1348 private Queue<DirectorySearcher> _memberSearchersQueue = new Queue<DirectorySearcher>(); 1349 private Queue<DirectorySearcher> _memberSearchersQueueOriginal = new Queue<DirectorySearcher>(); 1350 1351 private SearchResultCollection _memberSearchResults = null; 1352 private IEnumerator _memberSearchResultsEnumerator = null; 1353 1354 private ExpansionMode _expansionMode; 1355 private ExpansionMode _originalExpansionMode; 1356 BuildPathFromDN(string dn)1357 private string BuildPathFromDN(string dn) 1358 { 1359 string userSuppliedServername = _storeCtx.UserSuppliedServerName; 1360 1361 if (null == _pathCracker) 1362 { 1363 lock (_pathLock) 1364 { 1365 if (null == _pathCracker) 1366 { 1367 UnsafeNativeMethods.Pathname pathNameObj = new UnsafeNativeMethods.Pathname(); 1368 _pathCracker = (UnsafeNativeMethods.IADsPathname)pathNameObj; 1369 _pathCracker.EscapedMode = 2 /* ADS_ESCAPEDMODE_ON */; 1370 } 1371 } 1372 } 1373 1374 _pathCracker.Set(dn, 4 /* ADS_SETTYPE_DN */); 1375 1376 string escapedDn = _pathCracker.Retrieve(7 /* ADS_FORMAT_X500_DN */); 1377 1378 if (userSuppliedServername.Length > 0) 1379 return "LDAP://" + _storeCtx.UserSuppliedServerName + "/" + escapedDn; 1380 else 1381 return "LDAP://" + escapedDn; 1382 } 1383 } 1384 1385 internal enum ExpansionMode 1386 { 1387 Enum = 0, 1388 ASQ = 1, 1389 } 1390 1391 internal class ADDNLinkedAttrSetBookmark : ResultSetBookmark 1392 { 1393 public Dictionary<string, bool> usersVisited; 1394 public List<string> groupsToVisit; 1395 public List<string> groupsVisited; 1396 public IEnumerable members; 1397 public IEnumerator membersEnum = null; 1398 public Queue<IEnumerable> membersQueue; 1399 public ADStoreCtx storeCtx; 1400 public Object current; 1401 public bool returnedPrimaryGroup; 1402 public List<DirectoryEntry> foreignMembersCurrentGroup; 1403 public List<DirectoryEntry> fakePrincipalMembers; 1404 public SidList foreignMembersToReturn; 1405 public Principal currentForeignPrincipal; 1406 public DirectoryEntry currentForeignDE; 1407 public List<GroupPrincipal> foreignGroups; 1408 public SearchResultCollection queryMembersResults; 1409 public IEnumerator queryMembersResultEnumerator; 1410 public SearchResultCollection memberSearchResults; 1411 public IEnumerator memberSearchResultsEnumerator; 1412 public bool atBeginning; 1413 public ExpansionMode expansionMode; 1414 public Queue<DirectorySearcher> memberSearcherQueue; 1415 } 1416 } 1417 1418 // #endif 1419