// This code is derived from jcifs smb client library // Ported by J. Arturo // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Collections; using System.Collections.Generic; using System.IO; using SharpCifs.Dcerpc; using SharpCifs.Dcerpc.Msrpc; using SharpCifs.Util; using SharpCifs.Util.Sharpen; using Hashtable = SharpCifs.Util.Sharpen.Hashtable; //not System.Collections.Hashtable namespace SharpCifs.Smb { /// /// A Windows SID is a numeric identifier used to represent Windows /// accounts. /// /// /// A Windows SID is a numeric identifier used to represent Windows /// accounts. SIDs are commonly represented using a textual format such as /// S-1-5-21-1496946806-2192648263-3843101252-1029 but they may /// also be resolved to yield the name of the associated Windows account /// such as Administrators or MYDOM\alice. ///

/// Consider the following output of examples/SidLookup.java: ///

    /// toString: S-1-5-21-4133388617-793952518-2001621813-512
    /// toDisplayString: WNET\Domain Admins
    /// getType: 2
    /// getTypeText: Domain group
    /// getDomainName: WNET
    /// getAccountName: Domain Admins
    /// 
///
public class Sid : Rpc.SidT { public const int SidTypeUseNone = Lsarpc.SidNameUseNone; public const int SidTypeUser = Lsarpc.SidNameUser; public const int SidTypeDomGrp = Lsarpc.SidNameDomGrp; public const int SidTypeDomain = Lsarpc.SidNameDomain; public const int SidTypeAlias = Lsarpc.SidNameAlias; public const int SidTypeWknGrp = Lsarpc.SidNameWknGrp; public const int SidTypeDeleted = Lsarpc.SidNameDeleted; public const int SidTypeInvalid = Lsarpc.SidNameInvalid; public const int SidTypeUnknown = Lsarpc.SidNameUnknown; internal static readonly string[] SidTypeNames = { "0", "User", "Domain group", "Domain", "Local group", "Builtin group", "Deleted", "Invalid", "Unknown" }; public const int SidFlagResolveSids = unchecked(0x0001); public static Sid Everyone; public static Sid CreatorOwner; public static Sid SYSTEM; static Sid() { try { Everyone = new Sid("S-1-1-0"); CreatorOwner = new Sid("S-1-3-0"); SYSTEM = new Sid("S-1-5-18"); } catch (SmbException) { } } internal static Hashtable SidCache = new Hashtable(); /// internal static void ResolveSids(DcerpcHandle handle, LsaPolicyHandle policyHandle, Sid[] sids) { MsrpcLookupSids rpc = new MsrpcLookupSids(policyHandle, sids); handle.Sendrecv(rpc); switch (rpc.Retval) { case 0: case NtStatus.NtStatusNoneMapped: case unchecked(0x00000107): { // NT_STATUS_SOME_NOT_MAPPED break; } default: { throw new SmbException(rpc.Retval, false); } } for (int si = 0; si < sids.Length; si++) { sids[si].Type = rpc.Names.Names[si].SidType; sids[si].DomainName = null; switch (sids[si].Type) { case SidTypeUser: case SidTypeDomGrp: case SidTypeDomain: case SidTypeAlias: case SidTypeWknGrp: { int sidIndex = rpc.Names.Names[si].SidIndex; Rpc.Unicode_string ustr = rpc.Domains.Domains[sidIndex].Name; sids[si].DomainName = (new UnicodeString(ustr, false)).ToString(); break; } } sids[si].AcctName = (new UnicodeString(rpc.Names.Names[si].Name, false)).ToString(); sids[si].OriginServer = null; sids[si].OriginAuth = null; } } /// internal static void ResolveSids0(string authorityServerName, NtlmPasswordAuthentication auth, Sid[] sids) { DcerpcHandle handle = null; LsaPolicyHandle policyHandle = null; lock (SidCache) { try { handle = DcerpcHandle.GetHandle("ncacn_np:" + authorityServerName + "[\\PIPE\\lsarpc]", auth); string server = authorityServerName; int dot = server.IndexOf('.'); if (dot > 0 && char.IsDigit(server[0]) == false) { server = Runtime.Substring(server, 0, dot); } policyHandle = new LsaPolicyHandle(handle, "\\\\" + server, unchecked(0x00000800)); ResolveSids(handle, policyHandle, sids); } finally { if (handle != null) { if (policyHandle != null) { policyHandle.Close(); } handle.Close(); } } } } /// public static void ResolveSids(string authorityServerName, NtlmPasswordAuthentication auth, Sid[] sids, int offset, int length) { List list = new List(); //new List(sids.Length); int si; lock (SidCache) { for (si = 0; si < length; si++) { Sid sid = (Sid)SidCache.Get(sids[offset + si]); if (sid != null) { sids[offset + si].Type = sid.Type; sids[offset + si].DomainName = sid.DomainName; sids[offset + si].AcctName = sid.AcctName; } else { list.Add(sids[offset + si]); } } if (list.Count > 0) { //sids = (Jcifs.Smb.SID[])Sharpen.Collections.ToArray(list, new Jcifs.Smb.SID[0]); sids = (Sid[])list.ToArray(); ResolveSids0(authorityServerName, auth, sids); for (si = 0; si < sids.Length; si++) { SidCache.Put(sids[si], sids[si]); } } } } /// Resolve an array of SIDs using a cache and at most one MSRPC request. /// /// Resolve an array of SIDs using a cache and at most one MSRPC request. /// /// This method will attempt /// to resolve SIDs using a cache and cache the results of any SIDs that /// required resolving with the authority. SID cache entries are currently not /// expired because under normal circumstances SID information never changes. /// /// /// The hostname of the server that should be queried. For maximum efficiency this should be the hostname of a domain controller however a member server will work as well and a domain controller may not return names for SIDs corresponding to local accounts for which the domain controller is not an authority. /// /// /// The credentials that should be used to communicate with the named server. As usual, null indicates that default credentials should be used. /// /// /// The SIDs that should be resolved. After this function is called, the names associated with the SIDs may be queried with the toDisplayString, getDomainName, and getAccountName methods. /// /// public static void ResolveSids(string authorityServerName, NtlmPasswordAuthentication auth, Sid[] sids) { List list = new List(); //new List(sids.Length); int si; lock (SidCache) { for (si = 0; si < sids.Length; si++) { Sid sid = (Sid)SidCache.Get(sids[si]); if (sid != null) { sids[si].Type = sid.Type; sids[si].DomainName = sid.DomainName; sids[si].AcctName = sid.AcctName; } else { list.Add(sids[si]); } } if (list.Count > 0) { //sids = (Jcifs.Smb.SID[])Sharpen.Collections.ToArray(list, new Jcifs.Smb.SID[0]); sids = (Sid[])list.ToArray(); ResolveSids0(authorityServerName, auth, sids); for (si = 0; si < sids.Length; si++) { SidCache.Put(sids[si], sids[si]); } } } } /// public static Sid GetServerSid(string server, NtlmPasswordAuthentication auth) { DcerpcHandle handle = null; LsaPolicyHandle policyHandle = null; Lsarpc.LsarDomainInfo info = new Lsarpc.LsarDomainInfo(); MsrpcQueryInformationPolicy rpc; lock (SidCache) { try { handle = DcerpcHandle.GetHandle("ncacn_np:" + server + "[\\PIPE\\lsarpc]", auth); // NetApp doesn't like the 'generic' access mask values policyHandle = new LsaPolicyHandle(handle, null, unchecked(0x00000001)); rpc = new MsrpcQueryInformationPolicy(policyHandle, Lsarpc.PolicyInfoAccountDomain, info); handle.Sendrecv(rpc); if (rpc.Retval != 0) { throw new SmbException(rpc.Retval, false); } return new Sid(info.Sid, SidTypeDomain, (new UnicodeString(info.Name, false)).ToString(), null, false); } finally { if (handle != null) { if (policyHandle != null) { policyHandle.Close(); } handle.Close(); } } } } public static byte[] ToByteArray(Rpc.SidT sid) { byte[] dst = new byte[1 + 1 + 6 + sid.SubAuthorityCount * 4]; int di = 0; dst[di++] = sid.Revision; dst[di++] = sid.SubAuthorityCount; Array.Copy(sid.IdentifierAuthority, 0, dst, di, 6); di += 6; for (int ii = 0; ii < sid.SubAuthorityCount; ii++) { Encdec.Enc_uint32le(sid.SubAuthority[ii], dst, di); di += 4; } return dst; } internal int Type; internal string DomainName; internal string AcctName; internal string OriginServer; internal NtlmPasswordAuthentication OriginAuth; public Sid(byte[] src, int si) { Revision = src[si++]; SubAuthorityCount = src[si++]; IdentifierAuthority = new byte[6]; Array.Copy(src, si, IdentifierAuthority, 0, 6); si += 6; if (SubAuthorityCount > 100) { throw new RuntimeException("Invalid SID sub_authority_count"); } SubAuthority = new int[SubAuthorityCount]; for (int i = 0; i < SubAuthorityCount; i++) { SubAuthority[i] = ServerMessageBlock.ReadInt4(src, si); si += 4; } } /// /// Construct a SID from it's textual representation such as /// S-1-5-21-1496946806-2192648263-3843101252-1029. /// /// /// Construct a SID from it's textual representation such as /// S-1-5-21-1496946806-2192648263-3843101252-1029. /// /// public Sid(string textual) { StringTokenizer st = new StringTokenizer(textual, "-"); if (st.CountTokens() < 3 || !st.NextToken().Equals("S")) { // need S-N-M throw new SmbException("Bad textual SID format: " + textual); } Revision = byte.Parse(st.NextToken()); string tmp = st.NextToken(); long id = 0; if (tmp.StartsWith("0x")) { //id = long.Parse(Sharpen.Runtime.Substring(tmp, 2), 16); id = long.Parse(Runtime.Substring(tmp, 2)); } else { id = long.Parse(tmp); } IdentifierAuthority = new byte[6]; for (int i = 5; id > 0; i--) { IdentifierAuthority[i] = unchecked((byte)(id % 256)); id >>= 8; } SubAuthorityCount = unchecked((byte)st.CountTokens()); if (SubAuthorityCount > 0) { SubAuthority = new int[SubAuthorityCount]; for (int i1 = 0; i1 < SubAuthorityCount; i1++) { SubAuthority[i1] = (int)(long.Parse(st.NextToken()) & unchecked(0xFFFFFFFFL)); } } } /// /// Construct a SID from a domain SID and an RID /// (relative identifier). /// /// /// Construct a SID from a domain SID and an RID /// (relative identifier). For example, a domain SID /// S-1-5-21-1496946806-2192648263-3843101252 and RID 1029 would /// yield the SID S-1-5-21-1496946806-2192648263-3843101252-1029. /// public Sid(Sid domsid, int rid) { Revision = domsid.Revision; IdentifierAuthority = domsid.IdentifierAuthority; SubAuthorityCount = unchecked((byte)(domsid.SubAuthorityCount + 1)); SubAuthority = new int[SubAuthorityCount]; int i; for (i = 0; i < domsid.SubAuthorityCount; i++) { SubAuthority[i] = domsid.SubAuthority[i]; } SubAuthority[i] = rid; } public Sid(Rpc.SidT sid, int type, string domainName, string acctName, bool decrementAuthority) { Revision = sid.Revision; SubAuthorityCount = sid.SubAuthorityCount; IdentifierAuthority = sid.IdentifierAuthority; SubAuthority = sid.SubAuthority; this.Type = type; this.DomainName = domainName; this.AcctName = acctName; if (decrementAuthority) { SubAuthorityCount--; SubAuthority = new int[SubAuthorityCount]; for (int i = 0; i < SubAuthorityCount; i++) { SubAuthority[i] = sid.SubAuthority[i]; } } } public virtual Sid GetDomainSid() { return new Sid(this, SidTypeDomain, DomainName, null, GetType() != SidTypeDomain); } public virtual int GetRid() { if (GetType() == SidTypeDomain) { throw new ArgumentException("This SID is a domain sid"); } return SubAuthority[SubAuthorityCount - 1]; } /// Returns the type of this SID indicating the state or type of account. /// /// Returns the type of this SID indicating the state or type of account. ///

/// SID types are described in the following table. /// /// /// /// /// /// /// /// /// /// /// /// ///
TypeName
SID_TYPE_USE_NONE0
SID_TYPE_USERUser
SID_TYPE_DOM_GRPDomain group
SID_TYPE_DOMAINDomain
SID_TYPE_ALIASLocal group
SID_TYPE_WKN_GRPBuiltin group
SID_TYPE_DELETEDDeleted
SID_TYPE_INVALIDInvalid
SID_TYPE_UNKNOWNUnknown
///
/// public virtual int GetType() { if (OriginServer != null) { ResolveWeak(); } return Type; } ///

/// Return text represeting the SID type suitable for display to /// users. /// /// /// Return text represeting the SID type suitable for display to /// users. Text includes 'User', 'Domain group', 'Local group', etc. /// public virtual string GetTypeText() { if (OriginServer != null) { ResolveWeak(); } return SidTypeNames[Type]; } /// /// Return the domain name of this SID unless it could not be /// resolved in which case the numeric representation is returned. /// /// /// Return the domain name of this SID unless it could not be /// resolved in which case the numeric representation is returned. /// public virtual string GetDomainName() { if (OriginServer != null) { ResolveWeak(); } if (Type == SidTypeUnknown) { string full = ToString(); return Runtime.Substring(full, 0, full.Length - GetAccountName().Length - 1); } return DomainName; } /// /// Return the sAMAccountName of this SID unless it could not /// be resolved in which case the numeric RID is returned. /// /// /// Return the sAMAccountName of this SID unless it could not /// be resolved in which case the numeric RID is returned. If this /// SID is a domain SID, this method will return an empty String. /// public virtual string GetAccountName() { if (OriginServer != null) { ResolveWeak(); } if (Type == SidTypeUnknown) { return string.Empty + SubAuthority[SubAuthorityCount - 1]; } if (Type == SidTypeDomain) { return string.Empty; } return AcctName; } public override int GetHashCode() { int hcode = IdentifierAuthority[5]; for (int i = 0; i < SubAuthorityCount; i++) { hcode += 65599 * SubAuthority[i]; } return hcode; } public override bool Equals(object obj) { if (obj is Sid) { Sid sid = (Sid)obj; if (sid == this) { return true; } if (sid.SubAuthorityCount == SubAuthorityCount) { int i = SubAuthorityCount; while (i-- > 0) { if (sid.SubAuthority[i] != SubAuthority[i]) { return false; } } for (i = 0; i < 6; i++) { if (sid.IdentifierAuthority[i] != IdentifierAuthority[i]) { return false; } } return sid.Revision == Revision; } } return false; } /// /// Return the numeric representation of this sid such as /// S-1-5-21-1496946806-2192648263-3843101252-1029. /// /// /// Return the numeric representation of this sid such as /// S-1-5-21-1496946806-2192648263-3843101252-1029. /// public override string ToString() { string ret = "S-" + (Revision & unchecked(0xFF)) + "-"; if (IdentifierAuthority[0] != unchecked(0) || IdentifierAuthority[1] != unchecked(0)) { ret += "0x"; ret += Hexdump.ToHexString(IdentifierAuthority, 0, 6); } else { int shift = 0; long id = 0; for (int i = 5; i > 1; i--) { id += (IdentifierAuthority[i] & unchecked(0xFFL)) << shift; shift += 8; } ret += id; } for (int i1 = 0; i1 < SubAuthorityCount; i1++) { ret += "-" + (SubAuthority[i1] & unchecked(0xFFFFFFFFL)); } return ret; } /// /// Return a String representing this SID ideal for display to /// users. /// /// /// Return a String representing this SID ideal for display to /// users. This method should return the same text that the ACL /// editor in Windows would display. ///

/// Specifically, if the SID has /// been resolved and it is not a domain SID or builtin account, /// the full DOMAIN\name form of the account will be /// returned (e.g. MYDOM\alice or MYDOM\Domain Users). /// If the SID has been resolved but it is is a domain SID, /// only the domain name will be returned (e.g. MYDOM). /// If the SID has been resolved but it is a builtin account, /// only the name component will be returned (e.g. SYSTEM). /// If the sid cannot be resolved the numeric representation from /// toString() is returned. /// public virtual string ToDisplayString() { if (OriginServer != null) { ResolveWeak(); } if (DomainName != null) { string str; if (Type == SidTypeDomain) { str = DomainName; } else { if (Type == SidTypeWknGrp || DomainName.Equals("BUILTIN")) { if (Type == SidTypeUnknown) { str = ToString(); } else { str = AcctName; } } else { str = DomainName + "\\" + AcctName; } } return str; } return ToString(); } ///

Manually resolve this SID. /// /// Manually resolve this SID. Normally SIDs are automatically /// resolved. However, if a SID is constructed explicitly using a SID /// constructor, JCIFS will have no knowledge of the server that created the /// SID and therefore cannot possibly resolve it automatically. In this case, /// this method will be necessary. /// /// The FQDN of the server that is an authority for the SID. /// /// Credentials suitable for accessing the SID's information. /// public virtual void Resolve(string authorityServerName, NtlmPasswordAuthentication auth) { Sid[] sids = new Sid[1]; sids[0] = this; ResolveSids(authorityServerName, auth, sids); } internal virtual void ResolveWeak() { if (OriginServer != null) { try { Resolve(OriginServer, OriginAuth); } catch (IOException) { } finally { OriginServer = null; OriginAuth = null; } } } /// internal static Sid[] GetGroupMemberSids0(DcerpcHandle handle, SamrDomainHandle domainHandle, Sid domsid, int rid, int flags) { SamrAliasHandle aliasHandle = null; Lsarpc.LsarSidArray sidarray = new Lsarpc.LsarSidArray(); MsrpcGetMembersInAlias rpc = null; try { aliasHandle = new SamrAliasHandle(handle, domainHandle, unchecked(0x0002000c), rid); rpc = new MsrpcGetMembersInAlias(aliasHandle, sidarray); handle.Sendrecv(rpc); if (rpc.Retval != 0) { throw new SmbException(rpc.Retval, false); } Sid[] sids = new Sid[rpc.Sids.NumSids]; string originServer = handle.GetServer(); NtlmPasswordAuthentication originAuth = (NtlmPasswordAuthentication)handle.GetPrincipal(); for (int i = 0; i < sids.Length; i++) { sids[i] = new Sid(rpc.Sids.Sids[i].Sid, 0, null, null, false); sids[i].OriginServer = originServer; sids[i].OriginAuth = originAuth; } if (sids.Length > 0 && (flags & SidFlagResolveSids) != 0) { ResolveSids(originServer, originAuth, sids); } return sids; } finally { if (aliasHandle != null) { aliasHandle.Close(); } } } /// public virtual Sid[] GetGroupMemberSids(string authorityServerName, NtlmPasswordAuthentication auth, int flags) { if (Type != SidTypeDomGrp && Type != SidTypeAlias) { return new Sid[0]; } DcerpcHandle handle = null; SamrPolicyHandle policyHandle = null; SamrDomainHandle domainHandle = null; Sid domsid = GetDomainSid(); lock (SidCache) { try { handle = DcerpcHandle.GetHandle("ncacn_np:" + authorityServerName + "[\\PIPE\\samr]", auth); policyHandle = new SamrPolicyHandle(handle, authorityServerName, unchecked(0x00000030)); domainHandle = new SamrDomainHandle(handle, policyHandle, unchecked(0x00000200), domsid); return GetGroupMemberSids0(handle, domainHandle, domsid, GetRid(), flags); } finally { if (handle != null) { if (policyHandle != null) { if (domainHandle != null) { domainHandle.Close(); } policyHandle.Close(); } handle.Close(); } } } } /// /// This specialized method returns a Map of users and local groups for the /// target server where keys are SIDs representing an account and each value /// is an List of SIDs represents the local groups that the account is /// a member of. /// /// /// This specialized method returns a Map of users and local groups for the /// target server where keys are SIDs representing an account and each value /// is an List of SIDs represents the local groups that the account is /// a member of. ///

/// This method is designed to assist with computing access control for a /// given user when the target object's ACL has local groups. Local groups /// are not listed in a user's group membership (e.g. as represented by the /// tokenGroups constructed attribute retrived via LDAP). ///

/// Domain groups nested inside a local group are currently not expanded. In /// this case the key (SID) type will be SID_TYPE_DOM_GRP rather than /// SID_TYPE_USER. /// /// The server from which the local groups will be queried. /// /// The credentials required to query groups and group members. /// /// Flags that control the behavior of the operation. When all /// name associated with SIDs will be required, the SID_FLAG_RESOLVE_SIDS /// flag should be used which causes all group member SIDs to be resolved /// together in a single more efficient operation. /// /// internal static Hashtable GetLocalGroupsMap(string authorityServerName, NtlmPasswordAuthentication auth, int flags) { Sid domsid = GetServerSid(authorityServerName, auth); DcerpcHandle handle = null; SamrPolicyHandle policyHandle = null; SamrDomainHandle domainHandle = null; Samr.SamrSamArray sam = new Samr.SamrSamArray(); MsrpcEnumerateAliasesInDomain rpc; lock (SidCache) { try { handle = DcerpcHandle.GetHandle("ncacn_np:" + authorityServerName + "[\\PIPE\\samr]", auth); policyHandle = new SamrPolicyHandle(handle, authorityServerName, unchecked(0x02000000)); domainHandle = new SamrDomainHandle(handle, policyHandle, unchecked(0x02000000), domsid); rpc = new MsrpcEnumerateAliasesInDomain(domainHandle, unchecked(0xFFFF), sam); handle.Sendrecv(rpc); if (rpc.Retval != 0) { throw new SmbException(rpc.Retval, false); } Hashtable map = new Hashtable(); for (int ei = 0; ei < rpc.Sam.Count; ei++) { Samr.SamrSamEntry entry = rpc.Sam.Entries[ei]; Sid[] mems = GetGroupMemberSids0(handle, domainHandle, domsid, entry.Idx, flags); Sid groupSid = new Sid(domsid, entry.Idx); groupSid.Type = SidTypeAlias; groupSid.DomainName = domsid.GetDomainName(); groupSid.AcctName = (new UnicodeString(entry.Name, false)).ToString(); for (int mi = 0; mi < mems.Length; mi++) { List groups = (List)map.Get(mems[mi]); if (groups == null) { groups = new List(); map.Put(mems[mi], groups); } if (!groups.Contains(groupSid)) { groups.Add(groupSid); } } } return map; } finally { if (handle != null) { if (policyHandle != null) { if (domainHandle != null) { domainHandle.Close(); } policyHandle.Close(); } handle.Close(); } } } } } }