| File: System\Security\Principal\NTAccount.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Principal.Windows\src\System.Security.Principal.Windows.csproj (System.Security.Principal.Windows) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; namespace System.Security.Principal { public sealed class NTAccount : IdentityReference { #region Private members private readonly string _name; // // Limit for nt account names for users is 20 while that for groups is 256 // internal const int MaximumAccountNameLength = 256; // // Limit for dns domain names is 255 // internal const int MaximumDomainNameLength = 255; #endregion #region Constructors public NTAccount(string domainName, string accountName) { ArgumentException.ThrowIfNullOrEmpty(accountName); if (accountName.Length > MaximumAccountNameLength) { throw new ArgumentException(SR.IdentityReference_AccountNameTooLong, nameof(accountName)); } if (domainName != null && domainName.Length > MaximumDomainNameLength) { throw new ArgumentException(SR.IdentityReference_DomainNameTooLong, nameof(domainName)); } if (string.IsNullOrEmpty(domainName)) { _name = accountName; } else { _name = domainName + "\\" + accountName; } } public NTAccount(string name) { ArgumentException.ThrowIfNullOrEmpty(name); if (name.Length > (MaximumDomainNameLength + 1 /* '\' */ + MaximumAccountNameLength)) { throw new ArgumentException(SR.IdentityReference_AccountNameTooLong, nameof(name)); } _name = name; } #endregion #region Inherited properties and methods public override string Value { get { return ToString(); } } public override bool IsValidTargetType(Type targetType) { if (targetType == typeof(SecurityIdentifier)) { return true; } else if (targetType == typeof(NTAccount)) { return true; } else { return false; } } public override IdentityReference Translate(Type targetType) { ArgumentNullException.ThrowIfNull(targetType); if (targetType == typeof(NTAccount)) { return this; // assumes that NTAccount objects are immutable } else if (targetType == typeof(SecurityIdentifier)) { IdentityReferenceCollection irSource = new IdentityReferenceCollection(1); irSource.Add(this); IdentityReferenceCollection irTarget; irTarget = NTAccount.Translate(irSource, targetType, true); return irTarget[0]; } else { throw new ArgumentException(SR.IdentityReference_MustBeIdentityReference, nameof(targetType)); } } public override bool Equals([NotNullWhen(true)] object? o) { return (this == o as NTAccount); // invokes operator== } public override int GetHashCode() { return StringComparer.OrdinalIgnoreCase.GetHashCode(_name); } public override string ToString() { return _name; } internal static IdentityReferenceCollection Translate(IdentityReferenceCollection sourceAccounts, Type targetType, bool forceSuccess) { IdentityReferenceCollection result = Translate(sourceAccounts, targetType, out bool someFailed); if (forceSuccess && someFailed) { IdentityReferenceCollection UnmappedIdentities = new IdentityReferenceCollection(); foreach (IdentityReference id in result) { if (id.GetType() != targetType) { UnmappedIdentities.Add(id); } } throw new IdentityNotMappedException(SR.IdentityReference_IdentityNotMapped, UnmappedIdentities); } return result; } internal static IdentityReferenceCollection Translate(IdentityReferenceCollection sourceAccounts, Type targetType, out bool someFailed) { ArgumentNullException.ThrowIfNull(sourceAccounts); if (targetType == typeof(SecurityIdentifier)) { return TranslateToSids(sourceAccounts, out someFailed); } throw new ArgumentException(SR.IdentityReference_MustBeIdentityReference, nameof(targetType)); } #endregion #region Operators public static bool operator ==(NTAccount? left, NTAccount? right) { object? l = left; object? r = right; if (l == r) { return true; } else if (l == null || r == null) { return false; } else { return (left!.ToString().Equals(right!.ToString(), StringComparison.OrdinalIgnoreCase)); } } public static bool operator !=(NTAccount? left, NTAccount? right) { return !(left == right); // invoke operator== } #endregion #region Private methods private static unsafe IdentityReferenceCollection TranslateToSids(IdentityReferenceCollection sourceAccounts, out bool someFailed) { ArgumentNullException.ThrowIfNull(sourceAccounts); if (sourceAccounts.Count == 0) { throw new ArgumentException(SR.Arg_EmptyCollection, nameof(sourceAccounts)); } SafeLsaPolicyHandle? LsaHandle = null; SafeLsaMemoryHandle? ReferencedDomainsPtr = null; SafeLsaMemoryHandle? SidsPtr = null; try { // // Construct an array of unicode strings // Interop.Advapi32.MARSHALLED_UNICODE_STRING[] Names = new Interop.Advapi32.MARSHALLED_UNICODE_STRING[sourceAccounts.Count]; int currentName = 0; foreach (IdentityReference id in sourceAccounts) { if (!(id is NTAccount nta)) { throw new ArgumentException(SR.Argument_ImproperType, nameof(sourceAccounts)); } Names[currentName].Buffer = nta.ToString(); if (Names[currentName].Buffer.Length * 2 + 2 > ushort.MaxValue) { // this should never happen since we are already validating account name length in constructor and // it is less than this limit Debug.Fail("NTAccount::TranslateToSids - source account name is too long."); throw new InvalidOperationException(); } Names[currentName].Length = (ushort)(Names[currentName].Buffer.Length * 2); Names[currentName].MaximumLength = (ushort)(Names[currentName].Length + 2); currentName++; } // // Open LSA policy (for lookup requires it) // LsaHandle = Win32.LsaOpenPolicy(null, Interop.Advapi32.PolicyRights.POLICY_LOOKUP_NAMES); // // Now perform the actual lookup // someFailed = false; uint ReturnCode; ReturnCode = Interop.Advapi32.LsaLookupNames2(LsaHandle, 0, sourceAccounts.Count, Names, out ReferencedDomainsPtr, out SidsPtr); // // 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); if (unchecked((int)win32ErrorCode) != Interop.Errors.ERROR_TRUSTED_RELATIONSHIP_FAILURE) { Debug.Fail($"Interop.LsaLookupNames(2) returned unrecognized error {win32ErrorCode}"); } throw new Win32Exception(unchecked((int)win32ErrorCode)); } // // Interpret the results and generate SID objects // IdentityReferenceCollection Result = new IdentityReferenceCollection(sourceAccounts.Count); if (ReturnCode == 0 || ReturnCode == Interop.StatusOptions.STATUS_SOME_NOT_MAPPED) { SidsPtr.Initialize((uint)sourceAccounts.Count, (uint)sizeof(Interop.LSA_TRANSLATED_SID2)); ReferencedDomainsPtr.InitializeReferencedDomainsList(); Interop.LSA_TRANSLATED_SID2[] translatedSids = new Interop.LSA_TRANSLATED_SID2[sourceAccounts.Count]; SidsPtr.ReadArray(0, translatedSids, 0, translatedSids.Length); for (int i = 0; i < sourceAccounts.Count; i++) { Interop.LSA_TRANSLATED_SID2 Lts = translatedSids[i]; // // Only some names are recognized as NTAccount objects // switch ((SidNameUse)Lts.Use) { case SidNameUse.User: case SidNameUse.Group: case SidNameUse.Alias: case SidNameUse.Computer: case SidNameUse.WellKnownGroup: Result.Add(new SecurityIdentifier(Lts.Sid)); break; default: someFailed = true; Result.Add(sourceAccounts[i]); break; } } } else { for (int i = 0; i < sourceAccounts.Count; i++) { Result.Add(sourceAccounts[i]); } } return Result; } finally { LsaHandle?.Dispose(); ReferencedDomainsPtr?.Dispose(); SidsPtr?.Dispose(); } } #endregion } }