public static unsafe void InitializeReferencedDomainsList(this SafeLsaMemoryHandle referencedDomains) { // We don't know the real size of the referenced domains yet, so we need to set an initial // size based on the LSA_REFERENCED_DOMAIN_LIST structure, then resize it to include all of // the domains. referencedDomains.Initialize((uint)Marshal.SizeOf <Interop.LSA_REFERENCED_DOMAIN_LIST>()); Interop.LSA_REFERENCED_DOMAIN_LIST domainList = referencedDomains.Read <Interop.LSA_REFERENCED_DOMAIN_LIST>(0); byte *pRdl = null; try { referencedDomains.AcquirePointer(ref pRdl); // If there is a trust information list, then the buffer size is the end of that list minus // the beginning of the domain list. Otherwise, then the buffer is just the size of the // referenced domain list structure, which is what we defaulted to. if (domainList.Domains != IntPtr.Zero) { Interop.LSA_TRUST_INFORMATION *pTrustInformation = (Interop.LSA_TRUST_INFORMATION *)domainList.Domains; pTrustInformation += domainList.Entries; long bufferSize = (byte *)pTrustInformation - pRdl; System.Diagnostics.Debug.Assert(bufferSize > 0, "bufferSize > 0"); referencedDomains.Initialize((ulong)bufferSize); } } finally { if (pRdl != null) { referencedDomains.ReleasePointer(); } } }
private static unsafe IdentityReferenceCollection TranslateToNTAccounts(IdentityReferenceCollection sourceSids, out bool someFailed) { ArgumentNullException.ThrowIfNull(sourceSids); if (sourceSids.Count == 0) { throw new ArgumentException(SR.Arg_EmptyCollection, nameof(sourceSids)); } IntPtr[] SidArrayPtr = new IntPtr[sourceSids.Count]; GCHandle[] HandleArray = new GCHandle[sourceSids.Count]; SafeLsaPolicyHandle?LsaHandle = null; SafeLsaMemoryHandle?ReferencedDomainsPtr = null; SafeLsaMemoryHandle?NamesPtr = null; try { // // Pin all elements in the array of SIDs // int currentSid = 0; foreach (IdentityReference id in sourceSids) { if (!(id is SecurityIdentifier sid)) { throw new ArgumentException(SR.Argument_ImproperType, nameof(sourceSids)); } HandleArray[currentSid] = GCHandle.Alloc(sid.BinaryForm, GCHandleType.Pinned); SidArrayPtr[currentSid] = HandleArray[currentSid].AddrOfPinnedObject(); currentSid++; } // // Open LSA policy (for lookup requires it) // LsaHandle = Win32.LsaOpenPolicy(null, Interop.Advapi32.PolicyRights.POLICY_LOOKUP_NAMES); // // Perform the actual lookup // someFailed = false; uint ReturnCode; ReturnCode = Interop.Advapi32.LsaLookupSids(LsaHandle, sourceSids.Count, SidArrayPtr, out ReferencedDomainsPtr, out NamesPtr); // // Make a decision regarding whether it makes sense to proceed // based on the return code and the value of the forceSuccess argument // if (ReturnCode == Interop.StatusOptions.STATUS_NO_MEMORY || ReturnCode == Interop.StatusOptions.STATUS_INSUFFICIENT_RESOURCES) { throw new OutOfMemoryException(); } else if (ReturnCode == Interop.StatusOptions.STATUS_ACCESS_DENIED) { throw new UnauthorizedAccessException(); } else if (ReturnCode == Interop.StatusOptions.STATUS_NONE_MAPPED || ReturnCode == Interop.StatusOptions.STATUS_SOME_NOT_MAPPED) { someFailed = true; } else if (ReturnCode != 0) { uint win32ErrorCode = Interop.Advapi32.LsaNtStatusToWinError(ReturnCode); Debug.Fail($"Interop.LsaLookupSids returned {win32ErrorCode}"); throw new Win32Exception(unchecked ((int)win32ErrorCode)); } NamesPtr.Initialize((uint)sourceSids.Count, (uint)sizeof(Interop.LSA_TRANSLATED_NAME)); ReferencedDomainsPtr.InitializeReferencedDomainsList(); // // Interpret the results and generate NTAccount objects // IdentityReferenceCollection Result = new IdentityReferenceCollection(sourceSids.Count); if (ReturnCode == 0 || ReturnCode == Interop.StatusOptions.STATUS_SOME_NOT_MAPPED) { // // Interpret the results and generate NT Account objects // Interop.LSA_REFERENCED_DOMAIN_LIST rdl = ReferencedDomainsPtr.Read <Interop.LSA_REFERENCED_DOMAIN_LIST>(0); string[] ReferencedDomains = new string[rdl.Entries]; for (int i = 0; i < rdl.Entries; i++) { Interop.LSA_TRUST_INFORMATION *ti = (Interop.LSA_TRUST_INFORMATION *)rdl.Domains + i; ReferencedDomains[i] = Marshal.PtrToStringUni(ti->Name.Buffer, ti->Name.Length / sizeof(char)); } Interop.LSA_TRANSLATED_NAME[] translatedNames = new Interop.LSA_TRANSLATED_NAME[sourceSids.Count]; NamesPtr.ReadArray(0, translatedNames, 0, translatedNames.Length); for (int i = 0; i < sourceSids.Count; i++) { Interop.LSA_TRANSLATED_NAME Ltn = translatedNames[i]; switch ((SidNameUse)Ltn.Use) { case SidNameUse.User: case SidNameUse.Group: case SidNameUse.Alias: case SidNameUse.Computer: case SidNameUse.WellKnownGroup: string account = Marshal.PtrToStringUni(Ltn.Name.Buffer, Ltn.Name.Length / sizeof(char)); string domain = ReferencedDomains[Ltn.DomainIndex]; Result.Add(new NTAccount(domain, account)); break; default: someFailed = true; Result.Add(sourceSids[i]); break; } } } else { for (int i = 0; i < sourceSids.Count; i++) { Result.Add(sourceSids[i]); } } return(Result); } finally { for (int i = 0; i < sourceSids.Count; i++) { if (HandleArray[i].IsAllocated) { HandleArray[i].Free(); } } LsaHandle?.Dispose(); ReferencedDomainsPtr?.Dispose(); NamesPtr?.Dispose(); } }