Introducing windows-acl: working with ACLs in Rust

Access Control Lists (ACLs) are an integral part of the Microsoft Windows security model. In addition to controlling access to secured resources, they are also used in sandboxing, event auditing, and specifying mandatory integrity levels. They are also exceedingly painful to programmatically manipulate, especially in Rust. Today, help has arrived — we released windows-acl, a Rust crate that simplifies the manipulation of access control lists on Windows.

A short background on Windows ACLs

Windows has two types of ACLs: discretionary (DACL) and system (SACL). Every securable object in Windows (such as files, registry keys, events, etc.) has an associated security descriptor with a DACL and SACL.

Security Descriptor Diagram (1)

The difference between DACL and SACL lies in the type of held entries. DACLs are used to control an entity’s access to a resource. For instance, to deny a user from reading a registry key, the registry key’s DACL needs to contain an access denied entry for the user. SACLs are used for managing the types of actions required to generate an audit event and for setting mandatory integrity labels on resources. As an example, to audit access failures by a user group on a specific file, the specified file’s SACL must contain an audit entry for the user group.

Working with ACLs today

Adding and removing ACL entries from an existing ACL requires the creation of a new ACL. The removal process is fairly simple — copy all of the existing ACL entries over except the target to be deleted. The insertion process is a bit more difficult for discretionary ACLs. The access rights that a DACL allows a user depend on the ordering of the ACEs in the DACL. Correctly inserting a new access control entry (ACE) at the preferred location is the responsibility of the developer. Furthermore, the new DACL must ensure that the to-be-inserted ACE must not have a conflict with existing ACE entries. For instance, if the existing DACL has an access allowed entry for a user with read/write privileges and the to-be added ACE is an access allowed entry for a user with only read privileges, the existing entry must not be copied into the new DACL. No one wants to deal with this complexity, especially in Rust.

How windows-acl simplifies the task

Let’s take a look at appjaillauncher-rs for a demonstration of windows-acl’s simplicity. Last year, I ported the original C++ AppJailLauncher to Rust. appjaillauncher-rs sandboxes Windows applications using AppContainers. For sandboxing to work correctly, I needed to modify ACLs on specific resources — it was an arduous task without the help of Active Template Library classes (see CDacl and CSacl), the .NET framework, or PowerShell. I ended up with a solution that was both imperfect and complicated. After implementing windows-acl, I went back and updated appjaillauncher-rs to use windows-acl. With windows-acl, I have a modular library that handles the difficulties of Windows ACL programming. It provides an interface that simplifies the act of adding and removing DACL and SACL entries.

For example, adding a DACL access allow entry requires the following code:

match ACL::from_file_path(string_path, false) {
    Ok(mut acl) => {
        let sid = string_to_sid(string_sid).unwrap_or(Vec::new());
        if sid.capacity() == 0 {
            return false;
        }
        acl.remove(
            sid.as_ptr() as PSID, 
            Some(AceType::AccessAllow), None
        ).unwrap_or(0);
        if !acl.allow(sid.as_ptr() as PSID, true, mask).unwrap_or_else(|code| {
               false
           }) {
            return false;
        }
    },
    ...
}

Similarly for removing a DACL access allow entry:

match ACL::from_file_path(string_path, false) {
    Ok(mut acl) => {
        let sid = string_to_sid(string_sid).unwrap_or(Vec::new());
        if sid.capacity() == 0 {
            return false;
        }
        let result = acl.remove(
                         sid.as_ptr() as PSID, 
                         Some(AceType::AccessAllow), 
                         None);
        if result.is_err() {
            return false;
        }
    },
    ...
}

Potential Applications and Future Work with windows-acl

The windows-acl crate opens up new possibilities for writing Windows security tools in Rust. The ability to manipulate SACLs allows us to harness the full power of the Windows Event auditing engine. The Windows Event auditing engine is instrumental in providing information to detect endpoint compromise. We hope this work contributes to the Windows Rust developers community and inspires the creation of more Rust-based security tools!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s