| /* |
| * Copyright (c) 2008, 2009, 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 sun.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<Long> 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<AclEntry> acl) throws IOException { |
| boolean initialized = false; |
| |
| // SECURITY: need to copy list in case size changes during processing |
| acl = new ArrayList<AclEntry>(acl); |
| |
| // list of SIDs |
| sidList = new ArrayList<Long>(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<AclEntryFlag> flags = new HashSet<AclEntryFlag>(); |
| 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<AclEntryPermission> perms = new HashSet<AclEntryPermission>(); |
| 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<AclEntryPermission> 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<AclEntryFlag> 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<AclEntry> 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<AclEntry> acl = (List<AclEntry>)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<AclEntry> 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<AclEntry> result = new ArrayList<AclEntry>(aceCount); |
| |
| // decode each of the ACEs to AclEntry objects |
| for (int i=0; i<aceCount; i++) { |
| long aceAddress = GetAce(aclAddress, i); |
| AclEntry entry = decode(aceAddress); |
| if (entry != null) |
| result.add(entry); |
| } |
| return result; |
| } |
| } |