public class ACE {

    /**
     * Logger.
     */
    protected static final Logger LOG = LoggerFactory.getLogger(ACE.class);

    /**
     * @see AceType
     */
    private AceType type;

    /**
     * @see AceFlag
     */
    private List<AceFlag> flags;

    /**
     * @see AceRights
     */
    private AceRights rights;

    /**
     * @see AceObjectFlags
     */
    private AceObjectFlags objectFlags;

    /**
     * A GUID (16 bytes) that identifies a property set, property, extended right, or type of child object.
     */
    private byte[] objectType;

    /**
     * A GUID (16 bytes) that identifies the type of child object that can inherit the ACE.
     */
    private byte[] inheritedObjectType;

    /**
     * Optional application data.
     */
    private byte[] applicationData;

    /**
     * The SID of a trustee.
     */
    private SID sid;

    /**
     * Default constructor.
     */
    ACE() {
    }

    /**
     * Creates a new ACE instance.
     *
     * @param type ACE type.
     * @return ACE.
     */
    public static ACE newInstance(final AceType type) {
        final ACE ace = new ACE();
        ace.setType(type);
        return ace;
    }

    /**
     * Load the ACE from the buffer returning the last ACE segment position into the buffer.
     *
     * @param buff source buffer.
     * @param start start loading position.
     * @return last loading position.
     */
    int parse(final IntBuffer buff, final int start) {
        int pos = start;

        byte[] bytes = NumberFacility.getBytes(buff.get(pos));
        type = AceType.parseValue(bytes[0]);
        flags = AceFlag.parseValue(bytes[1]);

        int size = NumberFacility.getInt(bytes[3], bytes[2]);

        pos++;
        rights = AceRights.parseValue(NumberFacility.getReverseInt(buff.get(pos)));

        if (type == AceType.ACCESS_ALLOWED_OBJECT_ACE_TYPE || type == AceType.ACCESS_DENIED_OBJECT_ACE_TYPE) {
            pos++;
            objectFlags = AceObjectFlags.parseValue(NumberFacility.getReverseInt(buff.get(pos)));

            if (objectFlags.getFlags().contains(AceObjectFlags.Flag.ACE_OBJECT_TYPE_PRESENT)) {
                objectType = new byte[16];
                for (int j = 0; j < 4; j++) {
                    pos++;
                    System.arraycopy(NumberFacility.getBytes(buff.get(pos)), 0, objectType, j * 4, 4);
                }
            }

            if (objectFlags.getFlags().contains(AceObjectFlags.Flag.ACE_INHERITED_OBJECT_TYPE_PRESENT)) {
                inheritedObjectType = new byte[16];
                for (int j = 0; j < 4; j++) {
                    pos++;
                    System.arraycopy(NumberFacility.getBytes(buff.get(pos)), 0, inheritedObjectType, j * 4, 4);
                }
            }
        }

        pos++;
        sid = new SID();
        pos = sid.parse(buff, pos);

        int lastPos = start + (size / 4) - 1;
        applicationData = new byte[4 * (lastPos - pos)];

        int index = 0;
        while (pos < lastPos) {
            pos++;
            System.arraycopy(NumberFacility.getBytes(buff.get(pos)), 0, applicationData, index, 4);
            index += 4;
        }

        return pos;
    }

    /**
     * Gets ACE type.
     *
     * @see AceType
     * @return ACE type.
     */
    public AceType getType() {
        return type;
    }

    /**
     * Gets ACE flags.
     *
     * @see AceFlag
     * @return ACE flags; empty list if no flag has been specified.
     */
    public List<AceFlag> getFlags() {
        return this.flags == null ? new ArrayList<AceFlag>() : this.flags;
    }

    /**
     * Optional application data. The size of the application data is determined by the AceSize field.
     *
     * @return application data; null if not available.
     */
    public byte[] getApplicationData() {
        return this.applicationData == null || this.applicationData.length == 0
                ? null
                : Arrays.copyOf(this.applicationData, this.applicationData.length);
    }

    /**
     * Sets application data.
     *
     * @param applicationData application data.
     */
    public void setApplicationData(final byte[] applicationData) {
        this.applicationData = applicationData == null || applicationData.length == 0
                ? null
                : Arrays.copyOf(applicationData, applicationData.length);
    }

    /**
     * An ACCESS_MASK that specifies the user rights allowed by this ACE.
     *
     * @see AceRights
     * @return ACE rights.
     */
    public AceRights getRights() {
        return rights;
    }

    /**
     * A 32-bit unsigned integer that specifies a set of bit flags that indicate whether the ObjectType and
     * InheritedObjectType fields contain valid data. This parameter can be one or more of the following values.
     *
     * @see AceObjectFlags
     *
     * @return Flags.
     */
    public AceObjectFlags getObjectFlags() {
        return objectFlags;
    }

    /**
     * A GUID (16 bytes) that identifies a property set, property, extended right, or type of child object. The purpose
     * of this GUID depends on the user rights specified in the Mask field. This field is valid only if the ACE
     * _OBJECT_TYPE_PRESENT bit is set in the Flags field. Otherwise, the ObjectType field is ignored. For information
     * on access rights and for a mapping of the control access rights to the corresponding GUID value that identifies
     * each right, see [MS-ADTS] sections 5.1.3.2 and 5.1.3.2.1.
     *
     * ACCESS_MASK bits are not mutually exclusive. Therefore, the ObjectType field can be set in an ACE with any
     * ACCESS_MASK. If the AccessCheck algorithm calls this ACE and does not find an appropriate GUID, then that ACE
     * will be ignored. For more information on access checks and object access, see [MS-ADTS] section 5.1.3.3.3.
     *
     * @return ObjectType; null if not available.
     */
    public byte[] getObjectType() {
        return this.objectType == null || this.objectType.length == 0
                ? null
                : Arrays.copyOf(this.objectType, this.objectType.length);
    }

    /**
     * A GUID (16 bytes) that identifies the type of child object that can inherit the ACE. Inheritance is also
     * controlled by the inheritance flags in the ACE_HEADER, as well as by any protection against inheritance placed on
     * the child objects. This field is valid only if the ACE_INHERITED_OBJECT_TYPE_PRESENT bit is set in the Flags
     * member. Otherwise, the InheritedObjectType field is ignored.
     *
     * @return InheritedObjectType; null if not available.
     */
    public byte[] getInheritedObjectType() {
        return this.inheritedObjectType == null || this.inheritedObjectType.length == 0
                ? null
                : Arrays.copyOf(this.inheritedObjectType, this.inheritedObjectType.length);
    }

    /**
     * The SID of a trustee. The length of the SID MUST be a multiple of 4.
     *
     * @see SID
     * @return SID of the trustee.
     */
    public SID getSid() {
        return sid;
    }

    /**
     * An unsigned 16-bit integer that specifies the size, in bytes, of the ACE. The AceSize field can be greater than
     * the sum of the individual fields, but MUST be a multiple of 4 to ensure alignment on a DWORD boundary. In cases
     * where the AceSize field encompasses additional data for the callback ACEs types, that data is
     * implementation-specific. Otherwise, this additional data is not interpreted and MUST be ignored.
     *
     * @return ACE size.
     */
    public int getSize() {
        return 8 + (objectFlags == null ? 0 : 4)
                + (objectType == null ? 0 : 16)
                + (inheritedObjectType == null ? 0 : 16)
                + (sid == null ? 0 : sid.getSize())
                + (applicationData == null ? 0 : applicationData.length);
    }

    /**
     * Sets ACE type.
     *
     * @param type ACE type.
     * @see AceType
     */
    public void setType(final AceType type) {
        this.type = type;
    }

    /**
     * Adds ACE flag.
     *
     * @param flag ACE flag.
     * @see AceFlag
     */
    public void addFlag(final AceFlag flag) {
        this.flags.add(flag);
    }

    /**
     * Sets ACE rights.
     *
     * @param rights ACE rights.
     * @see AceRights
     */
    public void setRights(final AceRights rights) {
        this.rights = rights;
    }

    /**
     * Sets object flags.
     *
     * @param objectFlags ACE object flags.
     * @see AceObjectFlags
     */
    public void setObjectFlags(final AceObjectFlags objectFlags) {
        this.objectFlags = objectFlags;
    }

    /**
     * Sets object type, a GUID (16 bytes) that identifies a property set, property, extended right, or type of child
     * object.
     *
     * @param objectType ACE object type.
     */
    public void setObjectType(final byte[] objectType) {
        this.objectType = objectType == null || objectType.length == 0
                ? null
                : Arrays.copyOf(objectType, objectType.length);
    }

    /**
     * Sets inherited object type, a GUID (16 bytes) that identifies the type of child object that can inherit the ACE.
     *
     * @param inheritedObjectType Inherited object type.
     */
    public void setInheritedObjectType(final byte[] inheritedObjectType) {
        this.inheritedObjectType = inheritedObjectType == null || inheritedObjectType.length == 0
                ? null
                : Arrays.copyOf(inheritedObjectType, inheritedObjectType.length);
    }

    /**
     * Sets the SID of a trustee.
     *
     * @param sid SID of the trustee.
     * @see SID
     */
    public void setSid(final SID sid) {
        this.sid = sid;
    }

    /**
     * Serializes to byte array.
     *
     * @return serialized ACE.
     */
    public byte[] toByteArray() {
        final int size = getSize();

        final ByteBuffer buff = ByteBuffer.allocate(size);

        // Add type byte
        buff.put(type.getValue());

        // add flags byte
        byte flagSRC = 0x00;
        for (AceFlag flag : getFlags()) {
            flagSRC |= flag.getValue();
        }
        buff.put(flagSRC);

        // add size bytes (2 reversed)
        byte[] sizeSRC = NumberFacility.getBytes(size);
        buff.put(sizeSRC[3]);
        buff.put(sizeSRC[2]);

        // add right mask
        buff.put(Hex.reverse(NumberFacility.getUIntBytes(rights.asUInt())));

        // add object flags (from int to byte[] + reversed)
        if (objectFlags != null) {
            buff.put(Hex.reverse(NumberFacility.getUIntBytes(objectFlags.asUInt())));
        }

        // add object type
        if (objectType != null) {
            buff.put(objectType);
        }

        // add inherited object type
        if (inheritedObjectType != null) {
            buff.put(inheritedObjectType);
        }

        // add sid
        buff.put(sid.toByteArray());

        // add application data
        if (applicationData != null) {
            buff.put(applicationData);
        }

        return buff.array();
    }

    /**
     * {@inheritDoc }
     *
     * @param ace ACE to be compared with.
     * @return <tt>true</tt> if equals; <tt>false</tt> otherwise.
     */
    @Override
    public boolean equals(final Object ace) {
        if (!(ace instanceof ACE)) {
            return false;
        }

        final ACE ext = ACE.class.cast(ace);

        if (getSize() != ext.getSize()) {
            LOG.debug("Different size");
            return false;
        }

        if (getType() != ext.getType()) {
            LOG.debug("Different type");
            return false;
        }

        if (!Arrays.equals(getApplicationData(), ext.getApplicationData())) {
            LOG.debug("Different application data");
            return false;
        }

        if (!getSid().equals(ext.getSid())) {
            LOG.debug("Different SID");
            return false;
        }

        if ((getObjectFlags() == null && ext.getObjectFlags() != null)
                || (getObjectFlags() != null && ext.getObjectFlags() == null)
                || (getObjectFlags() != null && ext.getObjectFlags() != null
                && getObjectFlags().asUInt() != ext.getObjectFlags().asUInt())) {
            LOG.debug("Different object flags");
            return false;
        }

        if ((getObjectType() != null && ext.getObjectType() == null)
                || (getObjectType() == null && ext.getObjectType() != null)
                || (getObjectType() != null && ext.getObjectType() != null
                && !Arrays.equals(getObjectType(), ext.getObjectType()))) {
            LOG.debug("Different object type");
            return false;
        }

        if ((getInheritedObjectType() != null && ext.getInheritedObjectType() == null)
                || (getInheritedObjectType() == null && ext.getInheritedObjectType() != null)
                || (getInheritedObjectType() != null && ext.getInheritedObjectType() != null
                && !Arrays.equals(getInheritedObjectType(), ext.getInheritedObjectType()))) {
            LOG.debug("Different inherited object type");
            return false;
        }

        if (getRights().asUInt() != ext.getRights().asUInt()) {
            LOG.debug("Different rights");
            return false;
        }

        return new HashSet<>(getFlags()).equals(new HashSet<>(ext.getFlags()));
    }

    /**
     * Serializes to string.
     *
     * @return serialized ACE.
     */
    @Override
    public String toString() {
        final StringBuilder bld = new StringBuilder();
        bld.append('(');
        bld.append(type.toString());
        bld.append(';');

        for (AceFlag flag : flags) {
            bld.append(flag);
        }

        bld.append(';');

        for (AceRights.ObjectRight right : rights.getObjectRights()) {
            bld.append(right.name());
        }

        if (rights.getOthers() != 0) {
            bld.append('[');
            bld.append(rights.getOthers());
            bld.append(']');
        }

        bld.append(';');

        if (objectType != null) {
            bld.append(GUID.getGuidAsString(objectType));
        }

        bld.append(';');

        if (inheritedObjectType != null) {
            bld.append(GUID.getGuidAsString(inheritedObjectType));
        }

        bld.append(';');

        bld.append(sid.toString());

        bld.append(')');

        return bld.toString();
    }

    /**
     * {@inheritDoc }
     *
     * @return hashcode.
     */
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 53 * hash + Objects.hashCode(this.type);
        hash = 53 * hash + Objects.hashCode(this.flags);
        hash = 53 * hash + Objects.hashCode(this.rights);
        hash = 53 * hash + Objects.hashCode(this.objectFlags);
        hash = 53 * hash + Arrays.hashCode(this.objectType);
        hash = 53 * hash + Arrays.hashCode(this.inheritedObjectType);
        hash = 53 * hash + Arrays.hashCode(this.applicationData);
        hash = 53 * hash + Objects.hashCode(this.sid);
        return hash;
    }

} public class ACL {

    /**
     * Logger.
     */
    protected static final Logger LOG = LoggerFactory.getLogger(ACL.class);

    /**
     * An unsigned 8-bit value that specifies the revision of the ACL. The only two legitimate forms of ACLs supported
     * for on-the-wire management or manipulation are type 2 and type 4. No other form is valid for manipulation on the
     * wire. Therefore this field MUST be set to one of the following values.
     *
     * ACL_REVISION (0x02) - When set to 0x02, only AceTypes 0x00, 0x01, 0x02, 0x03, and 0x11 can be present in the ACL.
     * An AceType of 0x11 is used for SACLs but not for DACLs. For more information about ACE types.
     *
     * ACL_REVISION_DS (0x04) - When set to 0x04, AceTypes 0x05, 0x06, 0x07, 0x08, and 0x11 are allowed. ACLs of
     * revision 0x04 are applicable only to directory service objects. An AceType of 0x11 is used for SACLs but not for
     * DACLs.
     */
    private AclRevision revision;

    private final List<ACE> aces = new ArrayList<>();

    /**
     * Protected constructor.
     */
    ACL() {
    }

    /**
     * Load the ACL from the buffer returning the last ACL segment position into the buffer.
     *
     * @param buff source buffer.
     * @param start start loading position.
     * @return last loading position.
     */
    int parse(final IntBuffer buff, final int start) {
        int pos = start;
        // read for Dacl
        byte[] bytes = NumberFacility.getBytes(buff.get(pos));
        revision = AclRevision.parseValue(bytes[0]);

        pos++;
        bytes = NumberFacility.getBytes(buff.get(pos));
        final int aceCount = NumberFacility.getInt(bytes[1], bytes[0]);

        for (int i = 0; i < aceCount; i++) {
            pos++;

            final ACE ace = new ACE();
            aces.add(ace);

            pos = ace.parse(buff, pos);
        }

        return pos;
    }

    /**
     * Gets ACL revision.
     *
     * @return revision.
     */
    public AclRevision getRevision() {
        return revision;
    }

    /**
     * Gets ACL size in bytes.
     *
     * @return ACL size in bytes.
     */
    public int getSize() {
        int size = 8;

        // add aces
        for (ACE ace : aces) {
            size += ace.getSize();
        }

        return size;
    }

    /**
     * Gets ACE number: an unsigned 16-bit integer that specifies the count of the number of ACE records in the ACL.
     *
     * @return ACEs' number.
     */
    public int getAceCount() {
        return aces.size();
    }

    /**
     * Gets ACL ACEs.
     *
     * @return list of ACEs.
     * 
     * @see ACE
     */
    public List<ACE> getAces() {
        return aces;
    }

    /**
     * Gets ACL ACE at the given position.
     *
     * @param i position.
     * @return ACL ACE.
     *
     * @see ACE
     */
    public ACE getAce(final int i) {
        return aces.get(i);
    }

    /**
     * Serializes to byte array.
     *
     * @return serialized ACL.
     */
    public byte[] toByteArray() {

        int size = getSize();

        final ByteBuffer buff = ByteBuffer.allocate(size);

        // add revision
        buff.put(revision.getValue());

        // add reserved
        buff.put((byte) 0x00);

        // add size (2 bytes reversed)
        byte[] sizeSRC = NumberFacility.getBytes(size);
        buff.put(sizeSRC[3]);
        buff.put(sizeSRC[2]);

        // add ace count (2 bytes reversed)
        byte[] aceCountSRC = NumberFacility.getBytes(getAceCount());
        buff.put(aceCountSRC[3]);
        buff.put(aceCountSRC[2]);

        // add reserved (2 bytes)
        buff.put((byte) 0x00);
        buff.put((byte) 0x00);

        // add aces
        for (ACE ace : aces) {
            buff.put(ace.toByteArray());
        }

        return buff.array();
    }

    /**
     * {@inheritDoc }
     *
     * @param acl ACL to be compared with.
     * @return <tt>true</tt> if equals; <tt>false</tt> otherwise.
     */
    @Override
    public boolean equals(final Object acl) {
        if (!(acl instanceof ACL)) {
            return false;
        }

        final ACL ext = ACL.class.cast(acl);

        if (getSize() != ext.getSize()) {
            LOG.debug("Different size");
            return false;
        }

        if (getAceCount() != ext.getAceCount()) {
            LOG.debug("Different ace count");
            return false;
        }

        for (int i = 0; i < aces.size(); i++) {
            if (!getAce(i).equals(ext.getAce(i))) {
                LOG.debug("Different ace: {}-{}", getAce(i), ext.getAce(i));
                return false;
            }
        }

        return true;
    }

    /**
     * Serializes to string.
     *
     * @return serialized ACL.
     */
    @Override
    public String toString() {
        final StringBuilder bld = new StringBuilder();
        bld.append('P');

        for (ACE ace : aces) {
            bld.append(ace.toString());
        }

        return bld.toString();
    }

    /**
     * {@inheritDoc }
     *
     * @return hashcode.
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 43 * hash + Objects.hashCode(this.aces);
        return hash;
    }

} public class SDDL {

    /**
     * Logger.
     */
    protected static final Logger LOG = LoggerFactory.getLogger(SDDL.class);

    /**
     * An unsigned 8-bit value that specifies the revision of the SECURITY_DESCRIPTOR structure.
     * This field MUST be set to one.
     */
    private byte revision;

    /**
     * An unsigned 16-bit field that specifies control access bit flags. The Self Relative (SR) bit MUST be set when the
     * security descriptor is in self-relative format.
     */
    private byte[] controlFlags;

    /**
     * An unsigned 32-bit integer that specifies the offset to the SID. This SID specifies the owner of the object to
     * which the security descriptor is associated. This must be a valid offset if the OD flag is not set. If this field
     * is set to zero, the OwnerSid field MUST not be present.
     */
    private long offsetOwner;

    /**
     * An unsigned 32-bit integer that specifies the offset to the SID. This SID specifies the group of the object to
     * which the security descriptor is associated. This must be a valid offset if the GD flag is not set. If this field
     * is set to zero, the GroupSid field MUST not be present.
     */
    private long offsetGroup;

    /**
     * An unsigned 32-bit integer that specifies the offset to the ACL that contains system ACEs. Typically, the system
     * ACL contains auditing ACEs (such as SYSTEM_AUDIT_ACE, SYSTEM_AUDIT_CALLBACK_ACE, or
     * SYSTEM_AUDIT_CALLBACK_OBJECT_ACE), and at most one Label ACE. This must be a valid offset if the SP flag is set;
     * if the SP flag is not set, this field MUST be set to zero. If this field is set to zero, the Sacl field MUST not
     * be present.
     */
    private long offsetSACL;

    /**
     * An unsigned 32-bit integer that specifies the offset to the ACL that contains ACEs that control access.
     * Typically, the DACL contains ACEs that grant or deny access to principals or groups. This must be a valid offset
     * if the DP flag is set; if the DP flag is not set, this field MUST be set to zero. If this field is set to zero,
     * the Dacl field MUST not be present.
     */
    private long offsetDACL;

    /**
     * The SID of the owner of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if
     * the OffsetOwner field is not zero.
     */
    private SID owner;

    /**
     * The SID of the group of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if
     * the GroupOwner field is not zero.
     */
    private SID group;

    /**
     * The SACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the SP flag
     * is set.
     */
    private ACL dacl;

    /**
     * The DACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the DP flag
     * is set.
     */
    private ACL sacl;

    /**
     * Constructor.
     *
     * @param src source as byte array.
     */
    public SDDL(byte[] src) {
        final ByteBuffer sddlBuffer = ByteBuffer.wrap(src);
        parse(sddlBuffer.asIntBuffer(), 0);
    }

    /**
     * Load the SDDL from the buffer returning the last SDDL segment position into the buffer.
     *
     * @param buff source buffer.
     * @param start start loading position.
     * @return last loading position.
     */
    private int parse(final IntBuffer buff, final int start) {
        int pos = start;

        /**
         * Revision (1 byte): An unsigned 8-bit value that specifies the revision of the SECURITY_DESCRIPTOR
         * structure. This field MUST be set to one.
         */
        final byte[] header = NumberFacility.getBytes(buff.get(pos));
        revision = header[0];

        /**
         * Control (2 bytes): An unsigned 16-bit field that specifies control access bit flags. The Self Relative
         * (SR) bit MUST be set when the security descriptor is in self-relative format.
         */
        controlFlags = new byte[] { header[3], header[2] };
        final boolean[] controlFlag = NumberFacility.getBits(controlFlags);

        pos++;
        /**
         * OffsetOwner (4 bytes): An unsigned 32-bit integer that specifies the offset to the SID. This SID
         * specifies the owner of the object to which the security descriptor is associated. This must be a valid
         * offset if the OD flag is not set. If this field is set to zero, the OwnerSid field MUST not be present.
         */
        if (!controlFlag[15]) {
            offsetOwner = NumberFacility.getReverseUInt(buff.get(pos));
        } else {
            offsetOwner = 0;
        }

        pos++;

        /**
         * OffsetGroup (4 bytes): An unsigned 32-bit integer that specifies the offset to the SID. This SID
         * specifies the group of the object to which the security descriptor is associated. This must be a valid
         * offset if the GD flag is not set. If this field is set to zero, the GroupSid field MUST not be present.
         */
        if (!controlFlag[14]) {
            offsetGroup = NumberFacility.getReverseUInt(buff.get(pos));
        } else {
            offsetGroup = 0;
        }

        pos++;

        /**
         * OffsetSacl (4 bytes): An unsigned 32-bit integer that specifies the offset to the ACL that contains
         * system ACEs. Typically, the system ACL contains auditing ACEs (such as SYSTEM_AUDIT_ACE,
         * SYSTEM_AUDIT_CALLBACK_ACE, or SYSTEM_AUDIT_CALLBACK_OBJECT_ACE), and at most one Label ACE (as specified
         * in section 2.4.4.13). This must be a valid offset if the SP flag is set; if the SP flag is not set, this
         * field MUST be set to zero. If this field is set to zero, the Sacl field MUST not be present.
         */
        if (controlFlag[11]) {
            offsetSACL = NumberFacility.getReverseUInt(buff.get(pos));
        } else {
            offsetSACL = 0;
        }

        pos++;

        /**
         * OffsetDacl (4 bytes): An unsigned 32-bit integer that specifies the offset to the ACL that contains ACEs
         * that control access. Typically, the DACL contains ACEs that grant or deny access to principals or groups.
         * This must be a valid offset if the DP flag is set; if the DP flag is not set, this field MUST be set to
         * zero. If this field is set to zero, the Dacl field MUST not be present.
         */
        if (controlFlag[13]) {
            offsetDACL = NumberFacility.getReverseUInt(buff.get(pos));
        } else {
            offsetDACL = 0;
        }

        /**
         * OwnerSid (variable): The SID of the owner of the object. The length of the SID MUST be a multiple of 4.
         * This field MUST be present if the OffsetOwner field is not zero.
         */
        if (offsetOwner > 0) {
            // read for OwnerSid
            pos = (int) (offsetOwner / 4);
            owner = new SID();
            pos = owner.parse(buff, pos);
        }

        /**
         * GroupSid (variable): The SID of the group of the object. The length of the SID MUST be a multiple of 4.
         * This field MUST be present if the GroupOwner field is not zero.
         */
        if (offsetGroup > 0) {
            // read for GroupSid
            pos = (int) (offsetGroup / 4);
            group = new SID();
            pos = group.parse(buff, pos);
        }

        /**
         * Sacl (variable): The SACL of the object. The length of the SID MUST be a multiple of 4. This field MUST
         * be present if the SP flag is set.
         */
        if (offsetSACL > 0) {
            // read for Sacl
            pos = (int) (offsetSACL / 4);
            sacl = new ACL();
            pos = sacl.parse(buff, pos);
        }

        /**
         * Dacl (variable): The DACL of the object. The length of the SID MUST be a multiple of 4. This field MUST
         * be present if the DP flag is set.
         */
        if (offsetDACL > 0) {
            pos = (int) (offsetDACL / 4);
            dacl = new ACL();
            pos = dacl.parse(buff, pos);
        }

        return pos;
    }

    /**
     * Gets size in terms of number of bytes.
     *
     * @return size.
     */
    public int getSize() {
        return 20 + (sacl == null ? 0 : sacl.getSize())
                + (dacl == null ? 0 : dacl.getSize())
                + (owner == null ? 0 : owner.getSize())
                + (group == null ? 0 : group.getSize());
    }

    /**
     * Get revison.
     *
     * @return An unsigned 8-bit value that specifies the revision of the SECURITY_DESCRIPTOR structure..
     */
    public byte getRevision() {
        return revision;
    }

    /**
     * Gets control.
     *
     * @return An unsigned 16-bit field that specifies control access bit flags.
     */
    public byte[] getControlFlags() {
        return controlFlags;
    }

    /**
     * Gets owner.
     *
     * @return The SID of the owner of the object.
     */
    public SID getOwner() {
        return owner;
    }

    /**
     * Gets group.
     *
     * @return The SID of the group of the object.
     */
    public SID getGroup() {
        return group;
    }

    /**
     * Gets DACL.
     *
     * @return The DACL of the object.
     */
    public ACL getDacl() {
        return dacl;
    }

    /**
     * Gets SACL.
     *
     * @return The SACL of the object.
     */
    public ACL getSacl() {
        return sacl;
    }

    /**
     * Serializes SDDL as byte array.
     *
     * @return SDL as byte array.
     */
    public byte[] toByteArray() {
        final ByteBuffer buff = ByteBuffer.allocate(getSize());

        // add revision
        buff.put(revision);

        // add reserved
        buff.put((byte) 0x00);

        // add contro flags
        buff.put(controlFlags[1]);
        buff.put(controlFlags[0]);

        // add offset owner
        buff.position(4);

        int nextAvailablePosition = 20;
        // add owner SID
        if (owner == null) {
            buff.putInt(0);
        } else {
            buff.put(Hex.reverse(NumberFacility.getBytes(nextAvailablePosition)));
            buff.position(nextAvailablePosition);
            buff.put(owner.toByteArray());
            nextAvailablePosition += owner.getSize();
        }

        // add offset group
        buff.position(8);

        // add group SID
        if (group == null) {
            buff.putInt(0);
        } else {
            buff.put(Hex.reverse(NumberFacility.getBytes(nextAvailablePosition)));
            buff.position(nextAvailablePosition);
            buff.put(group.toByteArray());
            nextAvailablePosition += group.getSize();
        }

        // add offset sacl
        buff.position(12);

        // add SACL
        if (sacl == null) {
            buff.putInt(0);
        } else {
            buff.put(Hex.reverse(NumberFacility.getBytes(nextAvailablePosition)));
            buff.position(nextAvailablePosition);
            buff.put(sacl.toByteArray());
            nextAvailablePosition += sacl.getSize();
        }

        // add offset dacl
        buff.position(16);

        // add DACL
        if (dacl == null) {
            buff.putInt(0);
        } else {
            buff.put(Hex.reverse(NumberFacility.getBytes(nextAvailablePosition)));
            buff.position(nextAvailablePosition);
            buff.put(dacl.toByteArray());
        }

        return buff.array();
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public boolean equals(final Object o) {
        if (!(o instanceof SDDL)) {
            return false;
        }

        final SDDL ext = SDDL.class.cast(o);

        if (getSize() != ext.getSize()) {
            LOG.debug("Different size");
            return false;
        }

        if (!Arrays.equals(getControlFlags(), ext.getControlFlags())) {
            LOG.debug("Different control flags");
            return false;
        }

        if (!getOwner().equals(ext.getOwner())) {
            LOG.debug("Different owner ....\nEspected; {}\nActual: {}",
                    getOwner().toString(), ext.getOwner().toString());
            return false;
        }

        if (!getGroup().equals(ext.getGroup())) {
            LOG.debug("Different group ....\nEspected; {}\nActual: {}",
                    getGroup().toString(), ext.getGroup().toString());
            return false;
        }

        if (!getDacl().equals(ext.getDacl())) {
            LOG.debug("Different dacl");
            return false;
        }

        if (!getSacl().equals(ext.getSacl())) {
            LOG.debug("Different sacl");
            return false;
        }
        return true;
    }

    /**
     * Serializes SDDL as string.
     *
     * @return SDDL string representation.
     *
     * @see <a href="https://msdn.microsoft.com/en-us/library/hh877835.aspx" target="_top">hh877835</a>
     */
    @Override
    public String toString() {
        final StringBuilder bld = new StringBuilder();

        if (owner != null) {
            bld.append("O:");
            bld.append(owner.toString());
        }
        if (group != null) {
            bld.append("G:");
            bld.append(group.toString());
        }

        if (dacl != null) {
            bld.append("D:");
            bld.append(dacl.toString());
        }

        if (sacl != null) {
            bld.append("S:");
            bld.append(sacl.toString());
        }

        return bld.toString();
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public int hashCode() {
        int hash = 5;
        hash = 71 * hash + Arrays.hashCode(this.controlFlags);
        hash = 71 * hash + Objects.hashCode(this.owner);
        hash = 71 * hash + Objects.hashCode(this.group);
        hash = 71 * hash + Objects.hashCode(this.dacl);
        hash = 71 * hash + Objects.hashCode(this.sacl);
        return hash;
    }
} public class SID {

    /**
     * Logger.
     */
    protected static final Logger LOG = LoggerFactory.getLogger(SID.class);

    /**
     * An 8-bit unsigned integer that specifies the revision level of the SID. This value MUST be set to 0x01.
     */
    private byte revision;

    /**
     * A SID_IDENTIFIER_AUTHORITY (6 bytes) structure that indicates the authority under which the SID was created.
     * It describes the entity that created the SID. The Identifier Authority value {0,0,0,0,0,5} denotes SIDs created
     * by the NT SID authority.
     */
    private byte[] identifierAuthority;

    /**
     * A variable length list of unsigned 32-bit integers that uniquely identifies a principal relative to the
     * IdentifierAuthority.
     */
    private final List<byte[]> subAuthorities;

    SID() {
        subAuthorities = new ArrayList<>();
    }

    /**
     * Instances a new SID with the given identifier authority.
     *
     * @param identifier identifier authority (6 bytes only).
     * @return the SID instance.
     */
    public static SID newInstance(final byte[] identifier) {
        final SID sid = new SID();
        sid.setRevision((byte) 0x01);
        sid.setIdentifierAuthority(identifier);
        return sid;
    }

    /**
     * Instances a SID instance of the given byte array.
     *
     * @param src SID as byte array.
     * @return SID instance.
     */
    public static SID parse(final byte[] src) {
        final ByteBuffer sddlBuffer = ByteBuffer.wrap(src);
        final SID sid = new SID();
        sid.parse(sddlBuffer.asIntBuffer(), 0);
        return sid;
    }

    /**
     * Load the SID from the buffer returning the last SID segment position into the buffer.
     *
     * @param buff source buffer.
     * @param start start loading position.
     * @return last loading position.
     */
    int parse(final IntBuffer buff, final int start) {
        int pos = start;

        // Check for a SID (http://msdn.microsoft.com/en-us/library/cc230371.aspx)
        final byte[] sidHeader = NumberFacility.getBytes(buff.get(pos));

        // Revision(1 byte): An 8-bit unsigned integer that specifies the revision level of the SID.
        // This value MUST be set to 0x01.
        revision = sidHeader[0];

        //SubAuthorityCount (1 byte): An 8-bit unsigned integer that specifies the number of elements 
        //in the SubAuthority array. The maximum number of elements allowed is 15.
        int subAuthorityCount = NumberFacility.getInt(sidHeader[1]);

        // IdentifierAuthority (6 bytes): A SID_IDENTIFIER_AUTHORITY structure that indicates the 
        // authority under which the SID was created. It describes the entity that created the SID. 
        // The Identifier Authority value {0,0,0,0,0,5} denotes SIDs created by the NT SID authority.
        identifierAuthority = new byte[6];

        System.arraycopy(sidHeader, 2, identifierAuthority, 0, 2);

        pos++;
        System.arraycopy(NumberFacility.getBytes(buff.get(pos)), 0, identifierAuthority, 2, 4);

        // SubAuthority (variable): A variable length array of unsigned 32-bit integers that uniquely 
        // identifies a principal relative to the IdentifierAuthority. Its length is determined by 
        // SubAuthorityCount.
        for (int j = 0; j < subAuthorityCount; j++) {
            pos++;
            subAuthorities.add(Hex.reverse(NumberFacility.getBytes(buff.get(pos))));
        }

        return pos;
    }

    /**
     * Gets revision level of the SID.
     *
     * @return revision.
     */
    public byte getRevision() {
        return revision;
    }

    /**
     * Gets sub-authority number: an 8-bit unsigned integer that specifies the number of elements in the SubAuthority
     * array. The maximum number of elements allowed is 15.
     *
     * @return sub-authorities number.
     */
    public int getSubAuthorityCount() {
        return subAuthorities == null ? 0 : subAuthorities.size() > 15 ? 15 : subAuthorities.size();
    }

    /**
     * Gets identifier authority: 6 bytes describing the entity that created the SID.
     *
     * @return identifier authority.
     */
    public byte[] getIdentifierAuthority() {
        return identifierAuthority == null ? null : Arrays.copyOf(identifierAuthority, identifierAuthority.length);
    }

    /**
     * Gets sub-authorities: a list of unsigned 32-bit integers that uniquely identifies a principal
     * relative to the IdentifierAuthority.
     *
     * @return sub-authorities.
     */
    public List<byte[]> getSubAuthorities() {
        final List<byte[]> res = new ArrayList<>(getSubAuthorityCount());
        for (byte[] sub : subAuthorities) {
            if (sub != null) {
                res.add(Arrays.copyOf(sub, sub.length));
            }
        }
        return Collections.unmodifiableList(res);
    }

    /**
     * Gets size of the SID byte array form.
     *
     * @return size of SID byte aray form.
     */
    public int getSize() {
        return 8 + subAuthorities.size() * 4;
    }

    /**
     * Sets revision level of the SID.
     *
     * @param revision revision.
     * @return the current SID instance.
     */
    public SID setRevision(byte revision) {
        this.revision = revision;
        return this;
    }

    /**
     * Sets idetifier authority: 6 bytes describing the entity that created the SID.
     *
     * @param identifierAuthority identifier authority.
     * @return the current SID instance.
     */
    public SID setIdentifierAuthority(byte[] identifierAuthority) {
        if (identifierAuthority == null || identifierAuthority.length != 6) {
            throw new IllegalArgumentException("Invalid identifier authority");
        }

        this.identifierAuthority = Arrays.copyOf(identifierAuthority, identifierAuthority.length);
        return this;
    }

    /**
     * Adds sub-authority:a principal relative to the IdentifierAuthority.
     *
     * @param sub sub-authority.
     * @return the current SID instance.
     */
    public SID addSubAuthority(byte[] sub) {
        if (sub == null || sub.length != 4) {
            throw new IllegalArgumentException("Invalid sub-authority to be added");
        }

        this.subAuthorities.add(Arrays.copyOf(sub, sub.length));
        return this;
    }

    /**
     * Serializes to byte array.
     *
     * @return serialized SID.
     */
    public byte[] toByteArray() {
        // variable content size depending on sub authorities number
        final ByteBuffer buff = ByteBuffer.allocate(getSize());
        buff.put(revision);
        buff.put(NumberFacility.getBytes(subAuthorities.size())[3]);
        buff.put(identifierAuthority);
        for (byte[] sub : subAuthorities) {
            buff.put(Hex.reverse(sub));
        }
        return buff.array();
    }

    /**
     * Serializes to string.
     *
     * @return serialized SID.
     */
    @Override
    public String toString() {
        final StringBuilder bld = new StringBuilder();
        bld.append("S-1-");

        if (identifierAuthority[0] == 0x00 && identifierAuthority[1] == 0x00) {
            bld.append(NumberFacility.getUInt(
                    identifierAuthority[2], identifierAuthority[3], identifierAuthority[4], identifierAuthority[5]));
        } else {
            bld.append(Hex.get(identifierAuthority));
        }

        if (subAuthorities.isEmpty()) {
            bld.append("-0");
        } else {
            for (byte[] sub : subAuthorities) {
                bld.append("-");
                bld.append(NumberFacility.getUInt(sub));
            }
        }

        return bld.toString();
    }

    /**
     * {@inheritDoc }
     *
     * @param sid SID to be compared with.
     * @return <tt>true</tt> if equals; <tt>false</tt> otherwise.
     */
    @Override
    public boolean equals(final Object sid) {
        if (!(sid instanceof SID)) {
            return false;
        }

        final SID ext = SID.class.cast(sid);

        if (getSize() != ext.getSize()) {
            LOG.debug("Different size");
            return false;
        }

        if (getSubAuthorityCount() != ext.getSubAuthorityCount()) {
            LOG.debug("Different sub authorities");
            return false;
        }

        if (!Arrays.equals(getIdentifierAuthority(), ext.getIdentifierAuthority())) {
            LOG.debug("Different identifier authority: {}-{}",
                    Hex.get(identifierAuthority), Hex.get(ext.getIdentifierAuthority()));
            return false;
        }

        for (int i = 0; i < subAuthorities.size(); i++) {
            if (!Arrays.equals(subAuthorities.get(i), ext.getSubAuthorities().get(i))) {
                LOG.debug("Different sub authority: {}-{}",
                        Hex.get(subAuthorities.get(i)), Hex.get(ext.getSubAuthorities().get(i)));
                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritDoc }
     *
     * @return hashcode.
     */
    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + Arrays.hashCode(this.identifierAuthority);
        hash = 97 * hash + Objects.hashCode(this.subAuthorities);
        return hash;
    }

} public enum AceFlag {

    /**
     * 0x02 - Child objects that are containers, such as directories, inherit the ACE as an effective ACE. The inherited
     * ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
     */
    CONTAINER_INHERIT_ACE((byte) 0x02, "CI"),
    /**
     * 0x80 - Used with system-audit ACEs in a system access control list (SACL) to generate audit messages for failed
     * access attempts.
     */
    FAILED_ACCESS_ACE_FLAG((byte) 0x80, "FA"),
    /**
     * 0x08 - Indicates an inherit-only ACE, which does not control access to the object to which it is attached. If
     * this flag is not set, the ACE is an effective ACE that controls access to the object to which it is attached.
     *
     * Both effective and inherit-only ACEs can be inherited depending on the state of the other inheritance flags.
     */
    INHERIT_ONLY_ACE((byte) 0x08, "IO"),
    /**
     * 0x10 - Indicates that the ACE was inherited. The system sets this bit when it propagates an inherited ACE to a
     * child object.
     */
    INHERITED_ACE((byte) 0x10, "ID"),
    /**
     * 0x04 - If the ACE is inherited by a child object, the system clears the OBJECT_INHERIT_ACE and
     * CONTAINER_INHERIT_ACE flags in the inherited ACE. This prevents the ACE from being inherited by subsequent
     * generations of objects.
     */
    NO_PROPAGATE_INHERIT_ACE((byte) 0x04, "NP"),
    /**
     * 0x01 - Noncontainer child objects inherit the ACE as an effective ACE.
     *
     * For child objects that are containers, the ACE is inherited as an inherit-only ACE unless the
     * NO_PROPAGATE_INHERIT_ACE bit flag is also set.
     */
    OBJECT_INHERIT_ACE((byte) 0x01, "OI"),
    /**
     * 0x40 - Used with system-audit ACEs in a SACL to generate audit messages for successful access attempts.
     */
    SUCCESSFUL_ACCESS_ACE_FLAG((byte) 0x40, "SA");

    private final byte value;

    private final String str;

    /**
     * Private constructor.
     *
     * @param value byte value.
     * @param str string value.
     */
    private AceFlag(final byte value, final String str) {
        this.value = value;
        this.str = str;
    }

    /**
     * Gets byte value.
     *
     * @return byte value.
     */
    public byte getValue() {
        return value;
    }

    /**
     * {@inheritDoc}
     *
     * @return string value.
     */
    @Override
    public String toString() {
        return str;
    }

    /**
     * Parse byte value.
     *
     * @param value byte value.
     * @return ACE flags.
     */
    public static List<AceFlag> parseValue(final byte value) {
        final List<AceFlag> res = new ArrayList<>();

        for (AceFlag type : AceFlag.values()) {
            if ((value & type.getValue()) == type.getValue()) {
                res.add(type);
            }
        }

        return res;
    }
} public class AceObjectFlags {

    /**
     * ACE object flag.
     */
    public static enum Flag {

        /**
         * 0x00000001 - ObjectType is valid.
         */
        ACE_OBJECT_TYPE_PRESENT(0x00000001),
        /**
         * 0x00000002 - InheritedObjectType is valid. If this value is not specified, all types of child objects can
         * inherit the ACE.
         */
        ACE_INHERITED_OBJECT_TYPE_PRESENT(0x00000002);

        /**
         * Int value.
         */
        private final int value;

        /**
         * Private constructor.
         *
         * @param value int value.
         */
        private Flag(int value) {
            this.value = value;
        }

        /**
         * Gets int value.
         *
         * @return int value.
         */
        public int getValue() {
            return value;
        }
    }

    /**
     * Standard flags.
     */
    private final List<Flag> flags = new ArrayList<>();

    /**
     * Custom/Other flags.
     */
    private int others = 0;

    /**
     * Constructor.
     *
     * @param fls ACE object flags.
     */
    public AceObjectFlags(final Flag... fls) {
        for (Flag flag : fls) {
            if (!flags.contains(flag)) {
                flags.add(flag);
            }
        }
    }

    /**
     * Parse flags given as int value.
     *
     * @param value flags given as int value.
     * @return ACE object flags.
     */
    public static AceObjectFlags parseValue(final int value) {
        final AceObjectFlags res = new AceObjectFlags();

        res.others = value;

        for (AceObjectFlags.Flag type : AceObjectFlags.Flag.values()) {
            if ((value & type.getValue()) == type.getValue()) {
                res.flags.add(type);
                res.others ^= type.getValue();
            }
        }

        return res;
    }

    /**
     * Gets standard ACE object flags.
     *
     * @return stabdatd ACE object flags.
     */
    public List<Flag> getFlags() {
        return flags;
    }

    /**
     * Adds standard ACE object flag.
     *
     * @param flag standard ACE object flag.
     * @return the current ACE object flags.
     */
    public AceObjectFlags addFlag(final Flag flag) {
        if (!flags.contains(flag)) {
            flags.add(flag);
        }
        return this;
    }

    /**
     * Gets custom/other ACE object flags.
     *
     * @return custom/other ACE object flags as long value.
     */
    public long getOthers() {
        return others;
    }

    /**
     * Sets custom/others ACE object flags.
     *
     * @param others custom/other ACE object flags given as int value..
     * @return the current ACE object flags.
     */
    public AceObjectFlags setOthers(int others) {
        this.others = others;
        return this;
    }

    /**
     * Gets custom/other ACE object flags as long value.
     *
     * @return custom/other ACE object flags as long value.
     */
    public long asUInt() {
        long res = others;

        for (AceObjectFlags.Flag flag : flags) {
            res += flag.getValue();
        }

        return res;
    }
} public class AceRights {

    /**
     * Standard ACE rights.
     */
    public static enum ObjectRight {

        /**
         * GENERIC_READ - When read access to an object is requested, this bit is translated to a combination of bits.
         * These are most often set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) The bits that are set are implementation dependent. During this
         * translation, the GR bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * checked against the ACE structures in the security descriptor that attached to the object.
         *
         * When the GR bit is set in an ACE that is to be attached to an object, it is translated into a combination of
         * bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) The bits that are set are implementation dependent. During this
         * translation, the GR bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * granted by this ACE.
         */
        GR(0x80000000),
        /**
         * GENERIC_WRITE - When write access to an object is requested, this bit is translated to a combination of bits,
         * which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) The bits that are set are implementation dependent. During this
         * translation, the GW bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * checked against the ACE structures in the security descriptor that attached to the object.
         *
         * When the GW bit is set in an ACE that is to be
         * attached to an object, it is translated into a combination of bits, which are usually set in the lower 16
         * bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits
         * that are set are implementation dependent. During this translation, the GW bit is cleared. The resulting
         * ACCESS_MASK bits are the actual permissions that are granted by this ACE.
         */
        GW(0x40000000),
        /**
         * GENERIC_EXECUTE - When execute access to an object is requested, this bit is translated to a combination of
         * bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) The bits that are set are implementation dependent. During this
         * translation, the GX bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * checked against the ACE structures in the security descriptor that attached to the object.
         *
         * When the GX bit is set in an ACE that is to be attached to an object, it is translated into a combination of
         * bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) The bits that are set are implementation dependent. During this
         * translation, the GX bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * granted by this ACE.
         */
        GX(0x20000000),
        /**
         * GENERIC_ALL - When all access permissions to an object are requested, this bit is translated to a combination
         * of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications
         * MAY specify a different configuration.) Objects are free to include bits from the upper 16 bits in that
         * translation as required by the objects semantics. The bits that are set are implementation dependent. During
         * this translation, the GA bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are
         * checked against the ACE structures in the security descriptor that attached to the object.
         *
         * When the GA bit is set in an ACE that is to be attached to an object, it is translated into a combination of
         * bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY
         * specify a different configuration.) Objects are free to include bits from the upper 16 bits in that
         * translation, if required by the objects semantics. The bits that are set are implementation dependent.
         * During this translation, the GA bit is cleared. The resulting ACCESS_MASK bits are the actual permissions
         * that are granted by this ACE.
         */
        GA(0x10000000),
        /**
         * MAXIMUM_ALLOWED - When requested, this bit grants the requestor the maximum permissions allowed to the
         * object through the Access Check Algorithm. This bit can only be requested; it cannot be set in an ACE.
         *
         * Specifying the Maximum Allowed bit in the SECURITY_DESCRIPTOR has no meaning. The MA bit SHOULD NOT be set
         * and SHOULD be ignored when part of a SECURITY_DESCRIPTOR structure.
         */
        MA(0x02000000),
        /**
         * ACCESS_SYSTEM_SECURITY - When requested, this bit grants the requestor the maximum permissions allowed to the
         * object through the Access Check Algorithm. This bit can only be requested; it cannot be set in an ACE.
         *
         * Specifying the Maximum Allowed bit in the SECURITY_DESCRIPTOR has no meaning. The MA bit SHOULD NOT be set
         * and SHOULD be ignored when part of a SECURITY_DESCRIPTOR structure.
         */
        AS(0x01000000),
        /**
         * SYNCHRONIZE - Specifies access to the object sufficient to synchronize or wait on the object.
         */
        SY(0x00100000),
        /**
         * WRITE_OWNER - Specifies access to change the owner of the object as listed in the security descriptor.
         */
        WO(0x00080000),
        /**
         * WRITE_DACL - Specifies access to change the discretionary access control list of the security descriptor of
         * an object.
         */
        WD(0x00040000),
        /**
         * READ_CONTROL - Specifies access to read the security descriptor of an object.
         */
        RC(0x00020000),
        /**
         * DELETE - Specifies access to delete an object.
         */
        SD(0x00010000),
        /**
         * ADS_RIGHT_DS_CONTROL_ACCESS - The ObjectType GUID identifies an extended access right.
         */
        CR(0x00000100),
        /**
         * ADS_RIGHT_DS_WRITE_PROP - The ObjectType GUID identifies a property set or property of the object.
         * The ACE controls the trustee's right to write the property or property set.
         */
        WP(0x00000020);
        
        // FA(0x001F01FF),
        // FX(0x001200A0),
        // FW(0x00100116),
        // FR(0x00120089),
        // KA(0x00000019),
        // KR(0x0000003F),
        // KX(0x00000019),
        // KW(0x00000006),
        // LO(0x00000080),
        // DT(0x00000040),
        // RP(0x00000010),
        // SW(0x00000008),
        // LC(0x00000004),
        // DC(0x00000002),
        // CC(0x00000001);

        private final int value;

        /**
         * Private constructor.
         *
         * @param value int value.
         */
        private ObjectRight(int value) {
            this.value = value;
        }

        /**
         * Gest int value.
         *
         * @return int value.
         */
        public int getValue() {
            return value;
        }
    }

    /**
     * Custom/Other rights.
     */
    private long others = 0l;

    /**
     * Standard ACE rights.
     */
    private final List<AceRights.ObjectRight> rights = new ArrayList<>();

    /**
     * Default constructor.
     */
    public AceRights() {

    }

    /**
     * Parse ACE rights.
     *
     * @param value int value representing rights.
     * @return ACE rights.
     */
    public static AceRights parseValue(final int value) {
        final AceRights res = new AceRights();
        if (value == 0) {
            return res;
        }

        res.others = value;

        for (ObjectRight type : ObjectRight.values()) {
            if ((value & type.getValue()) == type.getValue()) {
                res.rights.add(type);
                res.others ^= type.getValue();
            }
        }

        return res;
    }

    /**
     * Gets custom/other rights.
     *
     * @return custom/other rights.
     */
    public long getOthers() {
        return others;
    }

    /**
     * Sets custom/other rights.
     *
     * @param others custom/other rights.
     * @return the current ACE rights.
     */
    public AceRights setOthers(final long others) {
        this.others = others;
        return this;
    }

    /**
     * Gets standard ACE rights.
     *
     * @return standard ACE rights.
     */
    public List<ObjectRight> getObjectRights() {
        return rights;
    }

    /**
     * Adds standard ACE right.
     *
     * @param right Object right.
     * @return the carrent ACE rights.
     */
    public AceRights addOjectRight(final ObjectRight right) {
        rights.add(right);
        return this;
    }

    /**
     * Gets rights as unsigned int.
     *
     * @return rights as unsigned int.
     */
    public long asUInt() {
        long res = others;

        for (ObjectRight right : rights) {
            res += right.getValue();
        }

        return res;
    }
} public enum AceType {

    /**
     * Unexpected value.
     */
    UNEXPECTED((byte) 0xFF),
    /**
     * 0x00 - Access-allowed ACE that uses the ACCESS_ALLOWED_ACE structure.
     */
    ACCESS_ALLOWED_ACE_TYPE((byte) 0x00, "A"),
    /**
     * 0x01 - Access-denied ACE that uses the ACCESS_DENIED_ACE structure.
     */
    ACCESS_DENIED_ACE_TYPE((byte) 0x01, "D"),
    /**
     * 0x02 - System-audit ACE that uses the SYSTEM_AUDIT_ACE structure.
     */
    SYSTEM_AUDIT_ACE_TYPE((byte) 0x02, "AU"),
    /**
     * 0x03 - Reserved for future use.
     */
    SYSTEM_ALARM_ACE_TYPE((byte) 0x03, "AL"),
    /**
     * 0x04 - Reserved for future use.
     */
    ACCESS_ALLOWED_COMPOUND_ACE_TYPE((byte) 0x04),
    /**
     * 0x05 - Object-specific access-allowed ACE that uses the ACCESS_ALLOWED_OBJECT_ACE structure.
     */
    ACCESS_ALLOWED_OBJECT_ACE_TYPE((byte) 0x05, "OA"),
    /**
     * 0x06 - Object-specific access-denied ACE that uses the ACCESS_DENIED_OBJECT_ACE structure.
     */
    ACCESS_DENIED_OBJECT_ACE_TYPE((byte) 0x06, "OD"),
    /**
     * 0x07 - Object-specific system-audit ACE that uses the SYSTEM_AUDIT_OBJECT_ACE structure.
     */
    SYSTEM_AUDIT_OBJECT_ACE_TYPE((byte) 0x07, "OU"),
    /**
     * 0x09 - Reserved for future use.
     */
    SYSTEM_ALARM_OBJECT_ACE_TYPE((byte) 0x08, "OL"),
    /**
     * 0x09 - Access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_ACE structure.
     */
    ACCESS_ALLOWED_CALLBACK_ACE_TYPE((byte) 0x09, "XA"),
    /**
     * 0x0A - Access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_ACE structure.
     */
    ACCESS_DENIED_CALLBACK_ACE_TYPE((byte) 0x0A, "XD"),
    /**
     * 0x0B - Object-specific access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_OBJECT_ACE structure.
     */
    ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0B, "ZA"),
    /**
     * 0x0C - Object-specific access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_OBJECT_ACE structure.
     */
    ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0C, "ZD"),
    /**
     * 0x0D - System-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_ACE structure.
     */
    SYSTEM_AUDIT_CALLBACK_ACE_TYPE((byte) 0x0D, "XU"),
    /**
     * 0x0E - Reserved for future use.
     */
    SYSTEM_ALARM_CALLBACK_ACE_TYPE((byte) 0x0E, "XL"),
    /**
     * 0x0F - Object-specific system-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_OBJECT_ACE structure.
     */
    SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0F, "ZU"),
    /**
     * 0x10 - Reserved for future use.
     */
    SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE((byte) 0x10, "ZL"),
    /**
     * 0x11 - Mandatory label ACE that uses the SYSTEM_MANDATORY_LABEL_ACE structure.
     */
    SYSTEM_MANDATORY_LABEL_ACE_TYPE((byte) 0x11, "ML"),
    /**
     * 0x12 - Resource attribute ACE that uses the SYSTEM_RESOURCE_ATTRIBUTE_ACE.
     */
    SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE((byte) 0x12, "RA"),
    /**
     * 0x13 - A central policy ID ACE that uses the SYSTEM_SCOPED_POLICY_ID_ACE.
     */
    SYSTEM_SCOPED_POLICY_ID_ACE_TYPE((byte) 0x13, "SP");

    private final String str;

    private final byte value;

    /**
     * Private constructor.
     *
     * @param value byte value.
     */
    private AceType(byte value) {
        this(value, "");
    }

    /**
     * Private constructor.
     *
     * @param value byte value.
     * @param str string representation.
     */
    private AceType(byte value, final String str) {
        this.value = value;
        this.str = str;
    }

    /**
     * Gets byte value.
     *
     * @return byte value.
     */
    public byte getValue() {
        return value;
    }

    /**
     * Return string representation.
     * @return string value.
     */
    @Override
    public String toString() {
        return str.isEmpty() ? name() : str;
    }

    /**
     * Parses byte value.
     *
     * @param value byte value.
     * @return ACE type.
     */
    public static AceType parseValue(final byte value) {
        for (AceType type : AceType.values()) {
            if (type.getValue() == value) {
                return type;
            }
        }

        return null;
    }
} public class GUID {

    /**
     * Gets GUID as string.
     *
     * @param GUID GUID.
     * @return GUID as string.
     */
    public static String getGuidAsString(byte[] GUID) {
        final StringBuilder res = new StringBuilder();

        res.append(AddLeadingZero((int) GUID[3] & 0xFF));
        res.append(AddLeadingZero((int) GUID[2] & 0xFF));
        res.append(AddLeadingZero((int) GUID[1] & 0xFF));
        res.append(AddLeadingZero((int) GUID[0] & 0xFF));
        res.append("-");
        res.append(AddLeadingZero((int) GUID[5] & 0xFF));
        res.append(AddLeadingZero((int) GUID[4] & 0xFF));
        res.append("-");
        res.append(AddLeadingZero((int) GUID[7] & 0xFF));
        res.append(AddLeadingZero((int) GUID[6] & 0xFF));
        res.append("-");
        res.append(AddLeadingZero((int) GUID[8] & 0xFF));
        res.append(AddLeadingZero((int) GUID[9] & 0xFF));
        res.append("-");
        res.append(AddLeadingZero((int) GUID[10] & 0xFF));
        res.append(AddLeadingZero((int) GUID[11] & 0xFF));
        res.append(AddLeadingZero((int) GUID[12] & 0xFF));
        res.append(AddLeadingZero((int) GUID[13] & 0xFF));
        res.append(AddLeadingZero((int) GUID[14] & 0xFF));
        res.append(AddLeadingZero((int) GUID[15] & 0xFF));

        return res.toString();

    }

    /**
     * Gets GUID as byte array.
     *
     * @param GUID GUID.
     * @return GUID as byte array.
     */
    public static byte[] getGuidAsByteArray(final String GUID) {
        final UUID uuid = UUID.fromString(GUID);

        final ByteBuffer buff = ByteBuffer.wrap(new byte[16]);
        buff.putLong(uuid.getMostSignificantBits());
        buff.putLong(uuid.getLeastSignificantBits());

        byte[] res = new byte[] {
            buff.get(3),
            buff.get(2),
            buff.get(1),
            buff.get(0),
            buff.get(5),
            buff.get(4),
            buff.get(7),
            buff.get(6),
            buff.get(8),
            buff.get(9),
            buff.get(10),
            buff.get(11),
            buff.get(12),
            buff.get(13),
            buff.get(14),
            buff.get(15), };

        return res;
    }

    private static String AddLeadingZero(int k) {
        return (k <= 0xF) ? "0" + Integer.toHexString(k) : Integer.toHexString(k);
    }
} public class Hex {

    /**
     * Gets hex string corresponding to the given byte array from "<tt>from</tt>" position to "<tt>to's</tt>"
     *
     * @param bytes bytes.
     * @param from from position.
     * @param to to position.
     * @return hex string.
     */
    public static String get(byte[] bytes, int from, int to) {
        final StringBuilder bld = new StringBuilder();
        for (int i = from; i < to; i++) {
            bld.append(Hex.get(bytes[i]));
        }
        return bld.toString();
    }

    /**
     * Gets hex string corresponding to the given bytes.
     *
     * @param bytes bytes.
     * @return hex string.
     */
    public static String get(byte... bytes) {
        final StringBuilder bld = new StringBuilder();
        for (byte b : bytes) {
            bld.append(Hex.get(b));
        }
        return bld.toString();
    }

    /**
     * Gets escaped hex string corresponding to the given bytes.
     *
     * @param bytes bytes.
     * @return escaped hex string
     */
    public static String getEscaped(byte... bytes) {
        final StringBuilder bld = new StringBuilder();
        for (byte b : bytes) {
            bld.append("\\").append(Hex.get(b));
        }
        return bld.toString();
    }

    /**
     * Gets hex string corresponding to the given byte.
     *
     * @param b byte.
     * @return hex string.
     */
    public static String get(byte b) {
        return String.format("%02X", b);
    }

    /**
     * Reverses bytes.
     *
     * @param bytes bytes.
     * @return reversed byte array.
     */
    public static byte[] reverse(byte... bytes) {
        byte[] res = new byte[bytes.length];
        int j = 0;
        for (int i = bytes.length - 1; i >= 0; i--) {
            res[j] = bytes[i];
            j++;
        }
        return res;
    }
} public class NumberFacility {

    /**
     * Gets byte array corresponding to a given unsigned integer.
     *
     * @param value unsigned integer.
     * @return byte array.
     */
    public static byte[] getUIntBytes(final long value) {
        return Arrays.copyOfRange(ByteBuffer.allocate(8).putLong(value).array(), 4, 8);
    }

    /**
     * Gets byte array from integer.
     *
     * @param value integer.
     * @return byte array.
     */
    public static byte[] getBytes(final int value) {
        return ByteBuffer.allocate(4).putInt(value).array();
    }
    
    /**
     * Gets byte array from integer.
     *
     * @param value integer.
     * @param length array size.
     * @return byte array.
     */
    public static byte[] getBytes(final int value, final int length) {
        return ByteBuffer.allocate(length).putInt(value).array();
    }

    /**
     * Remove 0x00 bytes from left side.
     *
     * @param bytes source array.
     * @return trimmed array.
     */
    @SuppressWarnings("empty-statement")
    public static byte[] leftTrim(final byte... bytes) {
        int pos = 0;
        for (; pos < bytes.length && bytes[pos] == 0x00; pos++);

        if (pos < bytes.length) {
            return Arrays.copyOfRange(bytes, pos, bytes.length);
        } else {
            return new byte[] { 0x00 };
        }
    }

    /**
     * Remove 0x00 bytes from right side.
     *
     * @param bytes source array.
     * @return trimmed array.
     */
    public static byte[] rightTrim(final byte... bytes) {
        return Hex.reverse(leftTrim(Hex.reverse(bytes)));
    }

    /**
     * Gets bits as boolean array from a given byte array.
     *
     * @param bytes bytes.
     * @return bits.
     */
    public static boolean[] getBits(final byte... bytes) {
        if (bytes.length > 4) {
            throw new InvalidParameterException("Invalid number of bytes");
        }

        final boolean[] res = new boolean[bytes.length * 8];

        int pos = 0;

        for (byte b : bytes) {
            for (boolean bool : getBits(b)) {
                res[pos] = bool;
                pos++;
            }
        }

        return res;
    }

    /**
     * Gets bits as boolean array from a given byte.
     *
     * @param b byte.
     * @return bits.
     */
    public static boolean[] getBits(final byte b) {
        final boolean[] res = new boolean[8];
        for (int i = 0; i < 8; i++) {
            res[7 - i] = (b & (1 << i)) != 0;
        }
        return res;
    }

    /**
     * Reverts bytes and retrieves the corresponding integer value.
     *
     * @param bytes bytes.
     * @return integer.
     */
    public static int getReverseInt(final byte... bytes) {
        return (int) getReverseUInt(bytes);
    }

    /**
     * Reverses bytes and retrieves the corresponding unsigned integer value.
     *
     * @param bytes bytes.
     * @return unsigned integer.
     */
    public static long getReverseUInt(final byte... bytes) {
        return getUInt(Hex.reverse(bytes));
    }

    /**
     * Gets byte array corresponding to the given integer value, reverses obtained byte array and retrieves the new
     * integer value.
     *
     * @param value integer value.
     * @return reversed integer value.
     */
    public static int getReverseInt(final int value) {
        return (int) getReverseUInt(value);
    }

    /**
     * Gets byte array corresponding to the given integer value, reverses obtained byte array and retrieves the new
     * unsigned integer value.
     *
     * @param value integer value.
     * @return reversed unsigned integer value.
     */
    public static long getReverseUInt(final int value) {
        return getReverseUInt(getBytes(value));
    }

    /**
     * Gets integer value corresponding to the given bytes.
     *
     * @param bytes bytes.
     * @return integer.
     */
    public static int getInt(final byte... bytes) {
        return (int) getUInt(bytes);
    }

    /**
     * Gets unsigned integer value corresponding to the given bytes.
     *
     * @param bytes bytes.
     * @return unsigned integer.
     */
    public static long getUInt(final byte... bytes) {
        if (bytes.length > 4) {
            throw new InvalidParameterException("Invalid number of bytes");
        }

        long res = 0;
        for (int i = 0; i < bytes.length; i++) {
            res |= bytes[i] & 0xFF;
            if (i < bytes.length - 1) {
                res <<= 8;
            }
        }

        return res;
    }
} public class SDDLHelper {

    /**
     * User cannot change password GUID.
     */
    public static final String UCP_OBJECT_GUID = "ab721a53-1e2f-11d0-9819-00aa0040529b";

    /**
     * Check if user canot change password.
     *
     * @param sddl SSDL.
     * @return <tt>true</tt> if user cannot change password: <tt>false</tt> otherwise.
     */
    public static boolean isUserCannotChangePassword(final SDDL sddl) {
        boolean res = false;

        final List<ACE> aces = sddl.getDacl().getAces();
        for (int i = 0; !res && i < aces.size(); i++) {
            final ACE ace = aces.get(i);

            if (ace.getType() == AceType.ACCESS_DENIED_OBJECT_ACE_TYPE
                    && ace.getObjectFlags().getFlags().contains(AceObjectFlags.Flag.ACE_OBJECT_TYPE_PRESENT)) {
                if (GUID.getGuidAsString(ace.getObjectType()).equals(UCP_OBJECT_GUID)) {

                    final SID sid = ace.getSid();
                    if (sid.getSubAuthorities().size() == 1) {
                        if ((Arrays.equals(
                                sid.getIdentifierAuthority(), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })
                                && Arrays.equals(
                                        sid.getSubAuthorities().get(0), new byte[] { 0x00, 0x00, 0x00, 0x00 }))
                                || (Arrays.equals(
                                        sid.getIdentifierAuthority(), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 })
                                && Arrays.equals(
                                        sid.getSubAuthorities().get(0), new byte[] { 0x00, 0x00, 0x00, 0x0a }))) {
                            res = true;
                        }
                    }
                }
            }
        }

        return res;
    }

    /**
     * Set "User Cannot Change Password ACL".
     *
     * @param sddl SDDL.
     * @param cannot <tt>true</tt> to set the ACL; <tt>false</tt> to unset.
     * @return updated SDDL.
     */
    public static SDDL userCannotChangePassword(final SDDL sddl, final Boolean cannot) {
        final AceType type = cannot ? AceType.ACCESS_DENIED_OBJECT_ACE_TYPE : AceType.ACCESS_ALLOWED_OBJECT_ACE_TYPE;

        ACE self = null;
        ACE all = null;

        final List<ACE> aces = sddl.getDacl().getAces();
        for (int i = 0; (all == null || self == null) && i < aces.size(); i++) {
            final ACE ace = aces.get(i);

            if ((ace.getType() == AceType.ACCESS_ALLOWED_OBJECT_ACE_TYPE
                    || ace.getType() == AceType.ACCESS_DENIED_OBJECT_ACE_TYPE)
                    && ace.getObjectFlags().getFlags().contains(AceObjectFlags.Flag.ACE_OBJECT_TYPE_PRESENT)) {
                if (GUID.getGuidAsString(ace.getObjectType()).equals(UCP_OBJECT_GUID)) {

                    final SID sid = ace.getSid();
                    if (sid.getSubAuthorities().size() == 1) {
                        if (self == null && Arrays.equals(
                                sid.getIdentifierAuthority(), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })
                                && Arrays.equals(
                                        sid.getSubAuthorities().get(0), new byte[] { 0x00, 0x00, 0x00, 0x00 })) {
                            self = ace;
                            self.setType(type);
                        } else if (all == null && Arrays.equals(
                                sid.getIdentifierAuthority(), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 })
                                && Arrays.equals(
                                        sid.getSubAuthorities().get(0), new byte[] { 0x00, 0x00, 0x00, 0x0a })) {
                            all = ace;
                            all.setType(type);
                        }
                    }
                }
            }
        }

        if (self == null) {
            // prepare aces
            self = ACE.newInstance(type);
            self.setObjectFlags(new AceObjectFlags(AceObjectFlags.Flag.ACE_OBJECT_TYPE_PRESENT));
            self.setObjectType(GUID.getGuidAsByteArray(UCP_OBJECT_GUID));
            self.setRights(new AceRights().addOjectRight(AceRights.ObjectRight.CR));
            SID sid = SID.newInstance(NumberFacility.getBytes(0x000000000001, 6));
            sid.addSubAuthority(NumberFacility.getBytes(0));
            self.setSid(sid);
            sddl.getDacl().getAces().add(self);
        }

        if (all == null) {
            all = ACE.newInstance(type);
            all.setObjectFlags(new AceObjectFlags(AceObjectFlags.Flag.ACE_OBJECT_TYPE_PRESENT));
            all.setObjectType(GUID.getGuidAsByteArray(UCP_OBJECT_GUID));
            all.setRights(new AceRights().addOjectRight(AceRights.ObjectRight.CR));
            final SID sid = SID.newInstance(NumberFacility.getBytes(0x000000000005, 6));
            sid.addSubAuthority(NumberFacility.getBytes(0x0A));
            all.setSid(sid);
            sddl.getDacl().getAces().add(all);
        }

        return sddl;
    }
}
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import net.tirasa.adsddl.ntsd.ACE;
import net.tirasa.adsddl.ntsd.ACL;
import net.tirasa.adsddl.ntsd.SDDL;
import net.tirasa.adsddl.ntsd.SID;
import net.tirasa.adsddl.ntsd.data.AceFlag;
import net.tirasa.adsddl.ntsd.data.AceObjectFlags;
import net.tirasa.adsddl.ntsd.data.AceRights;
import net.tirasa.adsddl.ntsd.data.AceType;
import net.tirasa.adsddl.ntsd.utils.GUID;
import net.tirasa.adsddl.ntsd.utils.Hex;
import net.tirasa.adsddl.ntsd.utils.NumberFacility;
import net.tirasa.adsddl.ntsd.utils.SDDLHelper;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
 * Copyright (C) 2015 Tirasa (info@tirasa.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.tirasa.adsddl.unit;



public abstract class AbstractTest implements Serializable {

    private static final long serialVersionUID = 1L;

    protected static final Logger LOG = LoggerFactory.getLogger(AbstractTest.class);

    protected static final String SDDL_ALL_SAMPLE = "/sddlSample.bin";

    protected static final String DACL_ONLY_SAMPLE = "/daclOnlySample.bin";

    protected static String[] control = {
        "SR", "RM", "PS", "PD", "SI", "DI", "SC", "DC", "DT", "SS", "SD", "SP", "DD", "DP", "GD", "OD" };

    @BeforeClass
    @SuppressWarnings("unchecked")
    public static void setUpConnection() throws IOException {
        // nothing to set up
    }

    protected void UnMarshall(final byte[] src) throws Exception {
        final SDDL sddl = new SDDL(src);

        if (LOG.isDebugEnabled()) {
            printSDDL(sddl);
            LOG.info(sddl.toString());
        }

        assertTrue(sddl.equals(new SDDL(sddl.toByteArray())));
    }

    protected void UserChangePassword(final byte[] src) throws Exception {
        final SDDL sddl = new SDDL(src);

        if (LOG.isDebugEnabled()) {
            printSDDL(sddl);
            LOG.info(sddl.toString());
        }
        
        // Assert.assertArrayEquals(src, sddl.toByteArray());

        assertFalse(sddl.getDacl().getAces().isEmpty());
        boolean found = false;
        for (ACE ace : sddl.getDacl().getAces()) {