![JAR search and dependency download from the Maven repository](/logo.png)
jcifs.smb.SID Maven / Gradle / Ivy
Show all versions of jcifs-krb5-jdk7 Show documentation
/* jcifs smb client library in Java
* Copyright (C) 2006 "Michael B. Allen"
* "Eric Glass"
*
* 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
*/
package jcifs.smb;
import java.util.*;
import java.io.IOException;
import jcifs.util.Hexdump;
import jcifs.dcerpc.*;
import jcifs.dcerpc.msrpc.*;
/**
* 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 extends rpc.sid_t {
public static final int SID_TYPE_USE_NONE = lsarpc.SID_NAME_USE_NONE;
public static final int SID_TYPE_USER = lsarpc.SID_NAME_USER;
public static final int SID_TYPE_DOM_GRP = lsarpc.SID_NAME_DOM_GRP;
public static final int SID_TYPE_DOMAIN = lsarpc.SID_NAME_DOMAIN;
public static final int SID_TYPE_ALIAS = lsarpc.SID_NAME_ALIAS;
public static final int SID_TYPE_WKN_GRP = lsarpc.SID_NAME_WKN_GRP;
public static final int SID_TYPE_DELETED = lsarpc.SID_NAME_DELETED;
public static final int SID_TYPE_INVALID = lsarpc.SID_NAME_INVALID;
public static final int SID_TYPE_UNKNOWN = lsarpc.SID_NAME_UNKNOWN;
static final String[] SID_TYPE_NAMES = {
"0",
"User",
"Domain group",
"Domain",
"Local group",
"Builtin group",
"Deleted",
"Invalid",
"Unknown"
};
public static final int SID_FLAG_RESOLVE_SIDS = 0x0001;
public static SID EVERYONE = null;
public static SID CREATOR_OWNER = null;
public static SID SYSTEM = null;
static {
try {
EVERYONE = new SID("S-1-1-0");
CREATOR_OWNER = new SID("S-1-3-0");
SYSTEM = new SID("S-1-5-18");
} catch (SmbException se) {
}
}
static Map sid_cache = new HashMap();
static void resolveSids(DcerpcHandle handle,
LsaPolicyHandle policyHandle,
SID[] sids) throws IOException {
MsrpcLookupSids rpc = new MsrpcLookupSids(policyHandle, sids);
handle.sendrecv(rpc);
switch (rpc.retval) {
case 0:
case NtStatus.NT_STATUS_NONE_MAPPED:
case 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].sid_type;
sids[si].domainName = null;
switch (sids[si].type) {
case SID_TYPE_USER:
case SID_TYPE_DOM_GRP:
case SID_TYPE_DOMAIN:
case SID_TYPE_ALIAS:
case SID_TYPE_WKN_GRP:
int sid_index = rpc.names.names[si].sid_index;
rpc.unicode_string ustr = rpc.domains.domains[sid_index].name;
sids[si].domainName = (new UnicodeString(ustr, false)).toString();
break;
}
sids[si].acctName = (new UnicodeString(rpc.names.names[si].name, false)).toString();
sids[si].origin_server = null;
sids[si].origin_auth = null;
}
}
static void resolveSids0(String authorityServerName,
NtlmPasswordAuthentication auth,
SID[] sids) throws IOException {
DcerpcHandle handle = null;
LsaPolicyHandle policyHandle = null;
synchronized (sid_cache) {
try {
handle = DcerpcHandle.getHandle("ncacn_np:" + authorityServerName +
"[\\PIPE\\lsarpc]", auth);
String server = authorityServerName;
int dot = server.indexOf('.');
if (dot > 0 && Character.isDigit(server.charAt(0)) == false)
server = server.substring(0, dot);
policyHandle = new LsaPolicyHandle(handle, "\\\\" + server, 0x00000800);
SID.resolveSids(handle, policyHandle, sids);
} finally {
if (handle != null) {
if (policyHandle != null) {
policyHandle.close();
}
handle.close();
}
}
}
}
static public void resolveSids(String authorityServerName,
NtlmPasswordAuthentication auth,
SID[] sids,
int offset,
int length) throws IOException {
ArrayList list = new ArrayList(sids.length);
int si;
synchronized (sid_cache) {
for (si = 0; si < length; si++) {
SID sid = (SID)sid_cache.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.size() > 0) {
sids = (SID[])list.toArray(new SID[0]);
SID.resolveSids0(authorityServerName, auth, sids);
for (si = 0; si < sids.length; si++) {
sid_cache.put(sids[si], sids[si]);
}
}
}
}
/**
* 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.
*
* @param authorityServerName 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.
* @param auth The credentials that should be used to communicate with the named server. As usual, null indicates that default credentials should be used.
* @param sids 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.
*/
static public void resolveSids(String authorityServerName,
NtlmPasswordAuthentication auth,
SID[] sids) throws IOException {
ArrayList list = new ArrayList(sids.length);
int si;
synchronized (sid_cache) {
for (si = 0; si < sids.length; si++) {
SID sid = (SID)sid_cache.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.size() > 0) {
sids = (SID[])list.toArray(new SID[0]);
SID.resolveSids0(authorityServerName, auth, sids);
for (si = 0; si < sids.length; si++) {
sid_cache.put(sids[si], sids[si]);
}
}
}
}
public static SID getServerSid(String server,
NtlmPasswordAuthentication auth) throws IOException {
DcerpcHandle handle = null;
LsaPolicyHandle policyHandle = null;
lsarpc.LsarDomainInfo info = new lsarpc.LsarDomainInfo();
MsrpcQueryInformationPolicy rpc;
synchronized (sid_cache) {
try {
handle = DcerpcHandle.getHandle("ncacn_np:" + server +
"[\\PIPE\\lsarpc]", auth);
// NetApp doesn't like the 'generic' access mask values
policyHandle = new LsaPolicyHandle(handle, null, 0x00000001);
rpc = new MsrpcQueryInformationPolicy(policyHandle,
(short)lsarpc.POLICY_INFO_ACCOUNT_DOMAIN,
info);
handle.sendrecv(rpc);
if (rpc.retval != 0)
throw new SmbException(rpc.retval, false);
return new SID(info.sid,
SID.SID_TYPE_DOMAIN,
(new UnicodeString(info.name, false)).toString(),
null,
false);
} finally {
if (handle != null) {
if (policyHandle != null) {
policyHandle.close();
}
handle.close();
}
}
}
}
public static byte[] toByteArray(rpc.sid_t sid) {
byte[] dst = new byte[1 + 1 + 6 + sid.sub_authority_count * 4];
int di = 0;
dst[di++] = sid.revision;
dst[di++] = sid.sub_authority_count;
System.arraycopy(sid.identifier_authority, 0, dst, di, 6);
di += 6;
for (int ii = 0; ii < sid.sub_authority_count; ii++) {
jcifs.util.Encdec.enc_uint32le(sid.sub_authority[ii], dst, di);
di += 4;
}
return dst;
}
int type;
String domainName = null;
String acctName = null;
String origin_server = null;
NtlmPasswordAuthentication origin_auth = null;
/*
* Construct a SID from it's binary representation.
*/
public SID(byte[] src, int si) {
revision = src[si++];
sub_authority_count = src[si++];
identifier_authority = new byte[6];
System.arraycopy(src, si, identifier_authority, 0, 6);
si += 6;
if (sub_authority_count > 100)
throw new RuntimeException( "Invalid SID sub_authority_count" );
sub_authority = new int[sub_authority_count];
for (int i = 0; i < sub_authority_count; i++) {
sub_authority[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.
*/
public SID(String textual) throws SmbException {
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);
this.revision = Byte.parseByte(st.nextToken());
String tmp = st.nextToken();
long id = 0;
if (tmp.startsWith("0x"))
id = Long.parseLong(tmp.substring(2), 16);
else
id = Long.parseLong(tmp);
this.identifier_authority = new byte[6];
for (int i = 5; id > 0; i--) {
this.identifier_authority[i] = (byte) (id % 256);
id >>= 8;
}
this.sub_authority_count = (byte) st.countTokens();
if (this.sub_authority_count > 0) {
this.sub_authority = new int[this.sub_authority_count];
for (int i = 0; i < this.sub_authority_count; i++)
this.sub_authority[i] = (int)(Long.parseLong(st.nextToken()) & 0xFFFFFFFFL);
}
}
/**
* 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) {
this.revision = domsid.revision;
this.identifier_authority = domsid.identifier_authority;
this.sub_authority_count = (byte)(domsid.sub_authority_count + 1);
this.sub_authority = new int[this.sub_authority_count];
int i;
for (i = 0; i < domsid.sub_authority_count; i++) {
this.sub_authority[i] = domsid.sub_authority[i];
}
this.sub_authority[i] = rid;
}
public SID(rpc.sid_t sid,
int type,
String domainName,
String acctName,
boolean decrementAuthority) {
this.revision = sid.revision;
this.sub_authority_count = sid.sub_authority_count;
this.identifier_authority = sid.identifier_authority;
this.sub_authority = sid.sub_authority;
this.type = type;
this.domainName = domainName;
this.acctName = acctName;
if (decrementAuthority) {
this.sub_authority_count--;
this.sub_authority = new int[sub_authority_count];
for (int i = 0; i < this.sub_authority_count; i++) {
this.sub_authority[i] = sid.sub_authority[i];
}
}
}
public SID getDomainSid() {
return new SID(this,
SID_TYPE_DOMAIN,
this.domainName,
null,
getType() != SID_TYPE_DOMAIN);
}
public int getRid() {
if (getType() == SID_TYPE_DOMAIN)
throw new IllegalArgumentException("This SID is a domain sid");
return sub_authority[sub_authority_count - 1];
}
/**
* Returns the type of this SID indicating the state or type of account.
*
* SID types are described in the following table.
*
*
* Type Name
* SID_TYPE_USE_NONE 0
* SID_TYPE_USER User
* SID_TYPE_DOM_GRP Domain group
* SID_TYPE_DOMAIN Domain
* SID_TYPE_ALIAS Local group
* SID_TYPE_WKN_GRP Builtin group
* SID_TYPE_DELETED Deleted
* SID_TYPE_INVALID Invalid
* SID_TYPE_UNKNOWN Unknown
*
*
*/
public int getType() {
if (origin_server != null)
resolveWeak();
return type;
}
/**
* Return text represeting the SID type suitable for display to
* users. Text includes 'User', 'Domain group', 'Local group', etc.
*/
public String getTypeText() {
if (origin_server != null)
resolveWeak();
return SID_TYPE_NAMES[type];
}
/**
* Return the domain name of this SID unless it could not be
* resolved in which case the numeric representation is returned.
*/
public String getDomainName() {
if (origin_server != null)
resolveWeak();
if (type == SID_TYPE_UNKNOWN) {
String full = toString();
return full.substring(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. If this
* SID is a domain SID, this method will return an empty String.
*/
public String getAccountName() {
if (origin_server != null)
resolveWeak();
if (type == SID_TYPE_UNKNOWN)
return "" + sub_authority[sub_authority_count - 1];
if (type == SID_TYPE_DOMAIN)
return "";
return acctName;
}
public int hashCode() {
int hcode = identifier_authority[5];
for (int i = 0; i < sub_authority_count; i++) {
hcode += 65599 * sub_authority[i];
}
return hcode;
}
public boolean equals(Object obj) {
if (obj instanceof SID) {
SID sid = (SID)obj;
if (sid == this)
return true;
if (sid.sub_authority_count == sub_authority_count) {
int i = sub_authority_count;
while (i-- > 0) {
if (sid.sub_authority[i] != sub_authority[i]) {
return false;
}
}
for (i = 0; i < 6; i++) {
if (sid.identifier_authority[i] != identifier_authority[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.
*/
public String toString() {
String ret = "S-" + (revision & 0xFF) + "-";
if (identifier_authority[0] != (byte)0 || identifier_authority[1] != (byte)0) {
ret += "0x";
ret += Hexdump.toHexString(identifier_authority, 0, 6);
} else {
long shift = 0;
long id = 0;
for (int i = 5; i > 1; i--) {
id += (identifier_authority[i] & 0xFFL) << shift;
shift += 8;
}
ret += id;
}
for (int i = 0; i < sub_authority_count ; i++)
ret += "-" + (sub_authority[i] & 0xFFFFFFFFL);
return ret;
}
/**
* 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 String toDisplayString() {
if (origin_server != null)
resolveWeak();
if (domainName != null) {
String str;
if (type == SID_TYPE_DOMAIN) {
str = domainName;
} else if (type == SID_TYPE_WKN_GRP ||
domainName.equals("BUILTIN")) {
if (type == SID_TYPE_UNKNOWN) {
str = toString();
} else {
str = acctName;
}
} else {
str = domainName + "\\" + acctName;
}
return str;
}
return toString();
}
/**
* 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.
*
* @param authorityServerName The FQDN of the server that is an authority for the SID.
* @param auth Credentials suitable for accessing the SID's information.
*/
public void resolve(String authorityServerName,
NtlmPasswordAuthentication auth) throws IOException {
SID[] sids = new SID[1];
sids[0] = this;
SID.resolveSids(authorityServerName, auth, sids);
}
void resolveWeak() {
if (origin_server != null) {
try {
resolve(origin_server, origin_auth);
} catch(IOException ioe) {
} finally {
origin_server = null;
origin_auth = null;
}
}
}
static SID[] getGroupMemberSids0(DcerpcHandle handle,
SamrDomainHandle domainHandle,
SID domsid,
int rid,
int flags) throws IOException {
SamrAliasHandle aliasHandle = null;
lsarpc.LsarSidArray sidarray = new lsarpc.LsarSidArray();
MsrpcGetMembersInAlias rpc = null;
try {
aliasHandle = new SamrAliasHandle(handle, domainHandle, 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.num_sids];
String origin_server = handle.getServer();
NtlmPasswordAuthentication origin_auth =
(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].origin_server = origin_server;
sids[i].origin_auth = origin_auth;
}
if (sids.length > 0 && (flags & SID_FLAG_RESOLVE_SIDS) != 0) {
SID.resolveSids(origin_server, origin_auth, sids);
}
return sids;
} finally {
if (aliasHandle != null) {
aliasHandle.close();
}
}
}
public SID[] getGroupMemberSids(String authorityServerName,
NtlmPasswordAuthentication auth,
int flags) throws IOException {
if (type != SID_TYPE_DOM_GRP && type != SID_TYPE_ALIAS)
return new SID[0];
DcerpcHandle handle = null;
SamrPolicyHandle policyHandle = null;
SamrDomainHandle domainHandle = null;
SID domsid = getDomainSid();
synchronized (sid_cache) {
try {
handle = DcerpcHandle.getHandle("ncacn_np:" + authorityServerName +
"[\\PIPE\\samr]", auth);
policyHandle = new SamrPolicyHandle(handle, authorityServerName, 0x00000030);
domainHandle = new SamrDomainHandle(handle, policyHandle, 0x00000200, domsid);
return SID.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 ArrayList 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.
*
* @param authorityServerName The server from which the local groups will be queried.
* @param auth The credentials required to query groups and group members.
* @param flags 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.
*/
static Map getLocalGroupsMap(String authorityServerName,
NtlmPasswordAuthentication auth,
int flags) throws IOException {
SID domsid = SID.getServerSid(authorityServerName, auth);
DcerpcHandle handle = null;
SamrPolicyHandle policyHandle = null;
SamrDomainHandle domainHandle = null;
samr.SamrSamArray sam = new samr.SamrSamArray();
MsrpcEnumerateAliasesInDomain rpc;
synchronized (sid_cache) {
try {
handle = DcerpcHandle.getHandle("ncacn_np:" + authorityServerName +
"[\\PIPE\\samr]", auth);
policyHandle = new SamrPolicyHandle(handle, authorityServerName, 0x02000000);
domainHandle = new SamrDomainHandle(handle, policyHandle, 0x02000000, domsid);
rpc = new MsrpcEnumerateAliasesInDomain(domainHandle, 0xFFFF, sam);
handle.sendrecv(rpc);
if (rpc.retval != 0)
throw new SmbException(rpc.retval, false);
Map map = new HashMap();
for (int ei = 0; ei < rpc.sam.count; ei++) {
samr.SamrSamEntry entry = rpc.sam.entries[ei];
SID[] mems = SID.getGroupMemberSids0(handle,
domainHandle,
domsid,
entry.idx,
flags);
SID groupSid = new SID(domsid, entry.idx);
groupSid.type = SID_TYPE_ALIAS;
groupSid.domainName = domsid.getDomainName();
groupSid.acctName = (new UnicodeString(entry.name, false)).toString();
for (int mi = 0; mi < mems.length; mi++) {
ArrayList groups = (ArrayList)map.get(mems[mi]);
if (groups == null) {
groups = new ArrayList();
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();
}
}
}
}
}