/* * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.fs; import java.nio.file.ProviderMismatchException; import java.nio.file.attribute.*; import java.util.*; import java.io.IOException; import jdk.internal.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; /** * A SecurityDescriptor for use when setting a file's ACL or creating a file * with an initial ACL. */ class WindowsSecurityDescriptor { private static final Unsafe unsafe = Unsafe.getUnsafe(); /** * typedef struct _ACL { * BYTE AclRevision; * BYTE Sbz1; * WORD AclSize; * WORD AceCount; * WORD Sbz2; * } ACL; * * typedef struct _ACE_HEADER { * BYTE AceType; * BYTE AceFlags; * WORD AceSize; * } ACE_HEADER; * * typedef struct _ACCESS_ALLOWED_ACE { * ACE_HEADER Header; * ACCESS_MASK Mask; * DWORD SidStart; * } ACCESS_ALLOWED_ACE; * * typedef struct _ACCESS_DENIED_ACE { * ACE_HEADER Header; * ACCESS_MASK Mask; * DWORD SidStart; * } ACCESS_DENIED_ACE; * * typedef struct _SECURITY_DESCRIPTOR { * BYTE Revision; * BYTE Sbz1; * SECURITY_DESCRIPTOR_CONTROL Control; * PSID Owner; * PSID Group; * PACL Sacl; * PACL Dacl; * } SECURITY_DESCRIPTOR; */ private static final short SIZEOF_ACL = 8; private static final short SIZEOF_ACCESS_ALLOWED_ACE = 12; private static final short SIZEOF_ACCESS_DENIED_ACE = 12; private static final short SIZEOF_SECURITY_DESCRIPTOR = 20; private static final short OFFSETOF_TYPE = 0; private static final short OFFSETOF_FLAGS = 1; private static final short OFFSETOF_ACCESS_MASK = 4; private static final short OFFSETOF_SID = 8; // null security descriptor private static final WindowsSecurityDescriptor NULL_DESCRIPTOR = new WindowsSecurityDescriptor(); // native resources private final List sidList; private final NativeBuffer aclBuffer, sdBuffer; /** * Creates the "null" SecurityDescriptor */ private WindowsSecurityDescriptor() { this.sidList = null; this.aclBuffer = null; this.sdBuffer = null; } /** * Creates a SecurityDescriptor from the given ACL */ private WindowsSecurityDescriptor(List acl) throws IOException { boolean initialized = false; // SECURITY: need to copy list in case size changes during processing acl = new ArrayList(acl); // list of SIDs sidList = new ArrayList(acl.size()); try { // initial size of ACL int size = SIZEOF_ACL; // get the SID for each entry for (AclEntry entry: acl) { UserPrincipal user = entry.principal(); if (!(user instanceof WindowsUserPrincipals.User)) throw new ProviderMismatchException(); String sidString = ((WindowsUserPrincipals.User)user).sidString(); try { long pSid = ConvertStringSidToSid(sidString); sidList.add(pSid); // increase size to allow for entry size += GetLengthSid(pSid) + Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE); } catch (WindowsException x) { throw new IOException("Failed to get SID for " + user.getName() + ": " + x.errorString()); } } // allocate memory for the ACL aclBuffer = NativeBuffers.getNativeBuffer(size); sdBuffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR); InitializeAcl(aclBuffer.address(), size); // Add entry ACE to the ACL int i = 0; while (i < acl.size()) { AclEntry entry = acl.get(i); long pSid = sidList.get(i); try { encode(entry, pSid, aclBuffer.address()); } catch (WindowsException x) { throw new IOException("Failed to encode ACE: " + x.errorString()); } i++; } // initialize security descriptor and set DACL InitializeSecurityDescriptor(sdBuffer.address()); SetSecurityDescriptorDacl(sdBuffer.address(), aclBuffer.address()); initialized = true; } catch (WindowsException x) { throw new IOException(x.getMessage()); } finally { // release resources if not completely initialized if (!initialized) release(); } } /** * Releases memory associated with SecurityDescriptor */ void release() { if (sdBuffer != null) sdBuffer.release(); if (aclBuffer != null) aclBuffer.release(); if (sidList != null) { // release memory for SIDs for (Long sid: sidList) { LocalFree(sid); } } } /** * Returns address of SecurityDescriptor */ long address() { return (sdBuffer == null) ? 0L : sdBuffer.address(); } // decode Windows ACE to NFSv4 AclEntry private static AclEntry decode(long aceAddress) throws IOException { // map type byte aceType = unsafe.getByte(aceAddress + OFFSETOF_TYPE); if (aceType != ACCESS_ALLOWED_ACE_TYPE && aceType != ACCESS_DENIED_ACE_TYPE) return null; AclEntryType type; if (aceType == ACCESS_ALLOWED_ACE_TYPE) { type = AclEntryType.ALLOW; } else { type = AclEntryType.DENY; } // map flags byte aceFlags = unsafe.getByte(aceAddress + OFFSETOF_FLAGS); Set flags = EnumSet.noneOf(AclEntryFlag.class); if ((aceFlags & OBJECT_INHERIT_ACE) != 0) flags.add(AclEntryFlag.FILE_INHERIT); if ((aceFlags & CONTAINER_INHERIT_ACE) != 0) flags.add(AclEntryFlag.DIRECTORY_INHERIT); if ((aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0) flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT); if ((aceFlags & INHERIT_ONLY_ACE) != 0) flags.add(AclEntryFlag.INHERIT_ONLY); // map access mask int mask = unsafe.getInt(aceAddress + OFFSETOF_ACCESS_MASK); Set perms = EnumSet.noneOf(AclEntryPermission.class); if ((mask & FILE_READ_DATA) > 0) perms.add(AclEntryPermission.READ_DATA); if ((mask & FILE_WRITE_DATA) > 0) perms.add(AclEntryPermission.WRITE_DATA); if ((mask & FILE_APPEND_DATA ) > 0) perms.add(AclEntryPermission.APPEND_DATA); if ((mask & FILE_READ_EA) > 0) perms.add(AclEntryPermission.READ_NAMED_ATTRS); if ((mask & FILE_WRITE_EA) > 0) perms.add(AclEntryPermission.WRITE_NAMED_ATTRS); if ((mask & FILE_EXECUTE) > 0) perms.add(AclEntryPermission.EXECUTE); if ((mask & FILE_DELETE_CHILD ) > 0) perms.add(AclEntryPermission.DELETE_CHILD); if ((mask & FILE_READ_ATTRIBUTES) > 0) perms.add(AclEntryPermission.READ_ATTRIBUTES); if ((mask & FILE_WRITE_ATTRIBUTES) > 0) perms.add(AclEntryPermission.WRITE_ATTRIBUTES); if ((mask & DELETE) > 0) perms.add(AclEntryPermission.DELETE); if ((mask & READ_CONTROL) > 0) perms.add(AclEntryPermission.READ_ACL); if ((mask & WRITE_DAC) > 0) perms.add(AclEntryPermission.WRITE_ACL); if ((mask & WRITE_OWNER) > 0) perms.add(AclEntryPermission.WRITE_OWNER); if ((mask & SYNCHRONIZE) > 0) perms.add(AclEntryPermission.SYNCHRONIZE); // lookup SID to create UserPrincipal long sidAddress = aceAddress + OFFSETOF_SID; UserPrincipal user = WindowsUserPrincipals.fromSid(sidAddress); return AclEntry.newBuilder() .setType(type) .setPrincipal(user) .setFlags(flags).setPermissions(perms).build(); } // encode NFSv4 AclEntry as Windows ACE to given ACL private static void encode(AclEntry ace, long sidAddress, long aclAddress) throws WindowsException { // ignore non-allow/deny entries for now if (ace.type() != AclEntryType.ALLOW && ace.type() != AclEntryType.DENY) return; boolean allow = (ace.type() == AclEntryType.ALLOW); // map access mask Set aceMask = ace.permissions(); int mask = 0; if (aceMask.contains(AclEntryPermission.READ_DATA)) mask |= FILE_READ_DATA; if (aceMask.contains(AclEntryPermission.WRITE_DATA)) mask |= FILE_WRITE_DATA; if (aceMask.contains(AclEntryPermission.APPEND_DATA)) mask |= FILE_APPEND_DATA; if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS)) mask |= FILE_READ_EA; if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS)) mask |= FILE_WRITE_EA; if (aceMask.contains(AclEntryPermission.EXECUTE)) mask |= FILE_EXECUTE; if (aceMask.contains(AclEntryPermission.DELETE_CHILD)) mask |= FILE_DELETE_CHILD; if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES)) mask |= FILE_READ_ATTRIBUTES; if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES)) mask |= FILE_WRITE_ATTRIBUTES; if (aceMask.contains(AclEntryPermission.DELETE)) mask |= DELETE; if (aceMask.contains(AclEntryPermission.READ_ACL)) mask |= READ_CONTROL; if (aceMask.contains(AclEntryPermission.WRITE_ACL)) mask |= WRITE_DAC; if (aceMask.contains(AclEntryPermission.WRITE_OWNER)) mask |= WRITE_OWNER; if (aceMask.contains(AclEntryPermission.SYNCHRONIZE)) mask |= SYNCHRONIZE; // map flags Set aceFlags = ace.flags(); byte flags = 0; if (aceFlags.contains(AclEntryFlag.FILE_INHERIT)) flags |= OBJECT_INHERIT_ACE; if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT)) flags |= CONTAINER_INHERIT_ACE; if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT)) flags |= NO_PROPAGATE_INHERIT_ACE; if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY)) flags |= INHERIT_ONLY_ACE; if (allow) { AddAccessAllowedAceEx(aclAddress, flags, mask, sidAddress); } else { AddAccessDeniedAceEx(aclAddress, flags, mask, sidAddress); } } /** * Creates a security descriptor with a DACL representing the given ACL. */ static WindowsSecurityDescriptor create(List acl) throws IOException { return new WindowsSecurityDescriptor(acl); } /** * Processes the array of attributes looking for the attribute "acl:acl". * Returns security descriptor representing the ACL or the "null" security * descriptor if the attribute is not in the array. */ @SuppressWarnings("unchecked") static WindowsSecurityDescriptor fromAttribute(FileAttribute... attrs) throws IOException { WindowsSecurityDescriptor sd = NULL_DESCRIPTOR; for (FileAttribute attr: attrs) { // if more than one ACL specified then last one wins if (sd != NULL_DESCRIPTOR) sd.release(); if (attr == null) throw new NullPointerException(); if (attr.name().equals("acl:acl")) { List acl = (List)attr.value(); sd = new WindowsSecurityDescriptor(acl); } else { throw new UnsupportedOperationException("'" + attr.name() + "' not supported as initial attribute"); } } return sd; } /** * Extracts DACL from security descriptor. */ static List getAcl(long pSecurityDescriptor) throws IOException { // get address of DACL long aclAddress = GetSecurityDescriptorDacl(pSecurityDescriptor); // get ACE count int aceCount = 0; if (aclAddress == 0L) { // no ACEs aceCount = 0; } else { AclInformation aclInfo = GetAclInformation(aclAddress); aceCount = aclInfo.aceCount(); } ArrayList result = new ArrayList<>(aceCount); // decode each of the ACEs to AclEntry objects for (int i=0; i