package jcifs.smb; import jcifs.util.Hexdump; import java.io.IOException; /** * An Access Control Entry (ACE) is an element in a security descriptor * such as those associated with files and directories. The Windows OS * determines which users have the necessary permissions to access objects * based on these entries. * <p> * To fully understand the information exposed by this class a description * of the access check algorithm used by Windows is required. The following * is a basic description of the algorithm. For a more complete description * we recommend reading the section on Access Control in Keith Brown's * "The .NET Developer's Guide to Windows Security" (which is also * available online). * <p> * Direct ACEs are evaluated first in order. The SID of the user performing * the operation and the desired access bits are compared to the SID * and access mask of each ACE. If the SID matches, the allow/deny flags * and access mask are considered. If the ACE is a "deny" * ACE and <i>any</i> of the desired access bits match bits in the access * mask of the ACE, the whole access check fails. If the ACE is an "allow" * ACE and <i>all</i> of the bits in the desired access bits match bits in * the access mask of the ACE, the access check is successful. Otherwise, * more ACEs are evaluated until all desired access bits (combined) * are "allowed". If all of the desired access bits are not "allowed" * the then same process is repeated for inherited ACEs. * <p> * For example, if user <tt>WNET\alice</tt> tries to open a file * with desired access bits <tt>0x00000003</tt> (<tt>FILE_READ_DATA | * FILE_WRITE_DATA</tt>) and the target file has the following security * descriptor ACEs: * <pre> * Allow WNET\alice 0x001200A9 Direct * Allow Administrators 0x001F01FF Inherited * Allow SYSTEM 0x001F01FF Inherited * </pre> * the access check would fail because the direct ACE has an access mask * of <tt>0x001200A9</tt> which doesn't have the * <tt>FILE_WRITE_DATA</tt> bit on (bit <tt>0x00000002</tt>). Actually, this isn't quite correct. If * <tt>WNET\alice</tt> is in the local <tt>Administrators</tt> group the access check * will succeed because the inherited ACE allows local <tt>Administrators</tt> * both <tt>FILE_READ_DATA</tt> and <tt>FILE_WRITE_DATA</tt> access. */ public class ACE { public static final int FILE_READ_DATA = 0x00000001; // 1 public static final int FILE_WRITE_DATA = 0x00000002; // 2 public static final int FILE_APPEND_DATA = 0x00000004; // 3 public static final int FILE_READ_EA = 0x00000008; // 4 public static final int FILE_WRITE_EA = 0x00000010; // 5 public static final int FILE_EXECUTE = 0x00000020; // 6 public static final int FILE_DELETE = 0x00000040; // 7 public static final int FILE_READ_ATTRIBUTES = 0x00000080; // 8 public static final int FILE_WRITE_ATTRIBUTES = 0x00000100; // 9 public static final int DELETE = 0x00010000; // 16 public static final int READ_CONTROL = 0x00020000; // 17 public static final int WRITE_DAC = 0x00040000; // 18 public static final int WRITE_OWNER = 0x00080000; // 19 public static final int SYNCHRONIZE = 0x00100000; // 20 public static final int GENERIC_ALL = 0x10000000; // 28 public static final int GENERIC_EXECUTE = 0x20000000; // 29 public static final int GENERIC_WRITE = 0x40000000; // 30 public static final int GENERIC_READ = 0x80000000; // 31 public static final int FLAGS_OBJECT_INHERIT = 0x01; public static final int FLAGS_CONTAINER_INHERIT = 0x02; public static final int FLAGS_NO_PROPAGATE = 0x04; public static final int FLAGS_INHERIT_ONLY = 0x08; public static final int FLAGS_INHERITED = 0x10; boolean allow; int flags; int access; SID sid; /** * Returns true if this ACE is an allow ACE and false if it is a deny ACE. */ public boolean isAllow() { return allow; } /** * Returns true if this ACE is an inherited ACE and false if it is a direct ACE. * <p> * Note: For reasons not fully understood, <tt>FLAGS_INHERITED</tt> may * not be set within all security descriptors even though the ACE was in * face inherited. If an inherited ACE is added to a parent the Windows * ACL editor will rebuild all children ACEs and set this flag accordingly. */ public boolean isInherited() { return (flags & FLAGS_INHERITED) != 0; } /** * Returns the flags for this ACE. The </tt>isInherited()</tt> * method checks the <tt>FLAGS_INHERITED</tt> bit in these flags. */ public int getFlags() { return flags; } /** * Returns the 'Apply To' text for inheritance of ACEs on * directories such as 'This folder, subfolder and files'. For * files the text is always 'This object only'. */ public String getApplyToText() { switch (flags & (FLAGS_OBJECT_INHERIT | FLAGS_CONTAINER_INHERIT | FLAGS_INHERIT_ONLY)) { case 0x00: return "This folder only"; case 0x03: return "This folder, subfolders and files"; case 0x0B: return "Subfolders and files only"; case 0x02: return "This folder and subfolders"; case 0x0A: return "Subfolders only"; case 0x01: return "This folder and files"; case 0x09: return "Files only"; } return "Invalid"; } /** * Returns the access mask accociated with this ACE. Use the * constants for <tt>FILE_READ_DATA</tt>, <tt>FILE_WRITE_DATA</tt>, * <tt>READ_CONTROL</tt>, <tt>GENERIC_ALL</tt>, etc with bitwise * operators to determine which bits of the mask are on or off. */ public int getAccessMask() { return access; } /** * Return the SID associated with this ACE. */ public SID getSID() { return sid; } int decode( byte[] buf, int bi ) { allow = buf[bi++] == (byte)0x00; flags = buf[bi++] & 0xFF; int size = ServerMessageBlock.readInt2(buf, bi); bi += 2; access = ServerMessageBlock.readInt4(buf, bi); bi += 4; sid = new SID(buf, bi); return size; } void appendCol(StringBuffer sb, String str, int width) { sb.append(str); int count = width - str.length(); for (int i = 0; i < count; i++) { sb.append(' '); } } /** * Return a string represeting this ACE. * <p> * Note: This function should probably be changed to return SDDL * fragments but currently it does not. */ public String toString() { int count, i; String str; StringBuffer sb = new StringBuffer(); sb.append( isAllow() ? "Allow " : "Deny " ); appendCol(sb, sid.toDisplayString(), 25); sb.append( " 0x" ).append( Hexdump.toHexString( access, 8 )).append(' '); sb.append(isInherited() ? "Inherited " : "Direct "); appendCol(sb, getApplyToText(), 34); return sb.toString(); } }