On Sat, 16 May 2026 00:19:02 GMT, Ashay Rane <[email protected]> wrote:

> Prior to this patch, when `userOnly` was true, the conflicting "allow"
> entry (for the owner) and "deny" entry (for groups that the owner is a
> member of) resulted in the owner being denied access since the "deny"
> entry takes precedence.  This resulted in RmiBootstrapTest and
> RmiSslNoKeyStoreTest tests failing with an "Access Denied" error.
> 
> In reality, the "deny" entry is not required, since Windows grants
> access only when an explicit "allow" entry matches the requesting
> principal.  So this patch fixes the ACLs so that when `userOnly` is
> true, only the owner has "allow" access.  Principals without a matching
> "allow" entry are denied access, thus restricting access without risking
> a group "deny" entry overriding the owner's access.
> 
> This patch also fixes the case when `userOnly` is false so that it
> doesn't inadvertently deny access when a principal didn't already have
> an ACL entry for the file.
> 
> ---------
> - [x] I confirm that I make this contribution in accordance with the [OpenJDK 
> Interim AI Policy](https://openjdk.org/legal/ai).

The following standalone program demonstrates the problem more succintly:


import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

public class AclDemo {

    public static void main(String[] args) throws Exception {
        Path file = Files.createTempFile("acl-demo-", ".txt");
        AclFileAttributeView view = Files.getFileAttributeView(file, 
AclFileAttributeView.class);
        System.out.println("Owner: " + view.getOwner().getName());

        List<AclEntry> originalAcl = view.getAcl();
        System.out.println("\nOriginal ACL:");
        printAcl(originalAcl);

        System.out.println("\nACL after updating using old code:");
        List<AclEntry> buggyAcl = oldUpdate(originalAcl, view.getOwner());
        view.setAcl(buggyAcl);
        printAcl(view.getAcl());

        tryRead(file);

        List<AclEntry> revisedAcl = newUpdate(originalAcl, view.getOwner());
        System.out.println("\nACL after updating using new code:");
        view.setAcl(revisedAcl);
        printAcl(view.getAcl());

        tryRead(file);
        Files.deleteIfExists(file);
    }

    static List<AclEntry> oldUpdate(List<AclEntry> oldAcl, UserPrincipal owner) 
{
        List<AclEntry> newAcl = new ArrayList<>();
        for (AclEntry entry : oldAcl) {
            String principalName = entry.principal().getName();
            boolean isOwner = principalName.equals(owner.getName());

            if (isOwner) {
                AclEntry allowed = 
AclEntry.newBuilder(entry).setType(AclEntryType.ALLOW).build();
                newAcl.add(allowed);
            } else if (entry.type() == AclEntryType.ALLOW) {
                AclEntry denied = 
AclEntry.newBuilder(entry).setType(AclEntryType.DENY).build();
                newAcl.add(denied);
            } else {
                newAcl.add(entry);
            }
        }

        return newAcl;
    }

    static List<AclEntry> newUpdate(List<AclEntry> oldAcl, UserPrincipal owner) 
{
        List<AclEntry> newAcl = new ArrayList<>();
        newAcl.add(AclEntry.newBuilder()
                .setType(AclEntryType.ALLOW)
                .setPrincipal(owner)
                .setPermissions(EnumSet.allOf(AclEntryPermission.class))
                .build());
        return newAcl;
    }

    static void printAcl(List<AclEntry> acl) {
        for (int i = 0; i < acl.size(); i++) {
            AclEntry entry = acl.get(i);
            System.out.println("[" + i + "] principal=" + 
entry.principal().getName() + ", type=" + entry.type());
        }
    }

    static void tryRead(Path file) {
        try (FileInputStream fis = new FileInputStream(file.toFile())) {
            byte[] data = fis.readAllBytes();
            System.out.println("\nSUCCESS: Read " + data.length + " bytes");
        } catch (IOException e) {
            System.out.println("\nFAILED: " + e.getClass().getName() + ": " + 
e.getMessage());
        }
    }
}


which results in the following output on my machine:


>_ java AclDemo.java
Owner: REDMOND\raneashay

Original ACL:
[0] principal=NT AUTHORITY\SYSTEM, type=ALLOW
[1] principal=BUILTIN\Administrators, type=ALLOW
[2] principal=REDMOND\raneashay, type=ALLOW

ACL after updating using old code:
[0] principal=NT AUTHORITY\SYSTEM, type=DENY
[1] principal=BUILTIN\Administrators, type=DENY
[2] principal=REDMOND\raneashay, type=ALLOW

FAILED: java.io.FileNotFoundException: 
C:\Users\RANEAS~1\AppData\Local\Temp\acl-demo-1845852012895386055.txt (Access 
is denied)

ACL after updating using new code:
[0] principal=REDMOND\raneashay, type=ALLOW

SUCCESS: Read 0 bytes

-------------

PR Comment: https://git.openjdk.org/jdk/pull/31179#issuecomment-4464712606

Reply via email to