From ee1f75f65b0efc523a4e9dfe43d6330940c2783e Mon Sep 17 00:00:00 2001 From: Adam Marcionek Date: Tue, 26 May 2015 10:15:18 -0400 Subject: [PATCH] Added `com.sun.jna.platform.win32.Advapi32.GetNamedSecurityInfo`, `SetNamedSecurityInfo`, `GetSecurityDescriptorLength`, `IsValidSecurityDescriptor`, `IsValidAcl` --- CHANGES.md | 3 +- .../com/sun/jna/platform/win32/AccCtrl.java | 126 +++++++ .../com/sun/jna/platform/win32/Advapi32.java | 190 ++++++++++ .../sun/jna/platform/win32/Advapi32Util.java | 207 +++++++++++ .../src/com/sun/jna/platform/win32/WinNT.java | 57 ++- .../sun/jna/platform/win32/Advapi32Test.java | 344 ++++++++++++++++++ .../jna/platform/win32/Advapi32UtilTest.java | 17 + 7 files changed, 936 insertions(+), 8 deletions(-) create mode 100644 contrib/platform/src/com/sun/jna/platform/win32/AccCtrl.java diff --git a/CHANGES.md b/CHANGES.md index b02926d2f3..a155a26d70 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,7 +7,7 @@ Release 4.2 (Next) Features -------- - +* [#446](https://github.com/twall/jna/pull/443): Added `com.sun.jna.platform.win32.Advapi32.GetNamedSecurityInfo`, `SetNamedSecurityInfo`, `GetSecurityDescriptorLength`, `IsValidSecurityDescriptor`, `IsValidAcl` - [@amarcionek](https://github.com/amarcionek). * [#387](https://github.com/twall/jna/pull/397): Use of interfaces and annotations to provide easier implementation of COM interfaces (with `InvocationHandler`) - [@dhakehurst](https://github.com/dhakehurst). * [#387](https://github.com/twall/jna/pull/397): Support for COM event callbacks - [@dhakehurst](https://github.com/dhakehurst). * [#387](https://github.com/twall/jna/pull/397): Support for COM interface discovery by iteration over `RunningObjectTable` - [@dhakehurst](https://github.com/dhakehurst). @@ -765,4 +765,3 @@ Bug Fixes * Properly handle NULL when the return value is a Structure * Proper conversion to wchar_t on linux * Copy full length of Java strings to C strings instead of stopping when a NUL character is encountered - diff --git a/contrib/platform/src/com/sun/jna/platform/win32/AccCtrl.java b/contrib/platform/src/com/sun/jna/platform/win32/AccCtrl.java new file mode 100644 index 0000000000..ead36cfa09 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/AccCtrl.java @@ -0,0 +1,126 @@ +/* Copyright (c) 2015 Adam Marcionek, All Rights Reserved + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +package com.sun.jna.platform.win32; + +import com.sun.jna.win32.StdCallLibrary; + +/** + * Ported from AccCtrl.h. + * Microsoft Windows SDK 7.1 + * @author amarcionek[at]gmail.com + */ + +public abstract class AccCtrl implements StdCallLibrary { + + /** + * The SE_OBJECT_TYPE enumeration contains values that correspond to the + * types of Windows objects that support security. The functions, such as + * GetSecurityInfo and SetSecurityInfo, that set and retrieve the security + * information of an object, use these values to indicate the type of object. + */ + public abstract class SE_OBJECT_TYPE { + /** + * Unknown object type. + */ + public static final int SE_UNKNOWN_OBJECT_TYPE = 0; + + /** + * Indicates a file or directory. The name string that identifies a + * file or directory object can be in one of the following formats: + * + * A relative path, such as FileName.dat or ..\FileName + * An absolute path, such as FileName.dat, C:\DirectoryName\FileName.dat, + * or G:\RemoteDirectoryName\FileName.dat. + * A UNC name, such as \\ComputerName\ShareName\FileName.dat. + */ + public static final int SE_FILE_OBJECT = 1; + + /** + * Indicates a Windows service. A service object can be a local service, + * such as ServiceName, or a remote service, such as + * \\ComputerName\ServiceName. + */ + public static final int SE_SERVICE = 2; + + /** + * Indicates a printer. A printer object can be a local printer, such as + * PrinterName, or a remote printer, such as \\ComputerName\PrinterName. + */ + public static final int SE_PRINTER = 3; + + /** + * Indicates a registry key. A registry key object can be in the local + * registry, such as CLASSES_ROOT\SomePath or in a remote registry, + * such as \\ComputerName\CLASSES_ROOT\SomePath. + * + * The names of registry keys must use the following literal strings to + * identify the predefined registry keys: "CLASSES_ROOT", "CURRENT_USER", + * "MACHINE", and "USERS". + */ + public static final int SE_REGISTRY_KEY = 4; + + /** + * Indicates a network share. A share object can be local, such as ShareName, + * or remote, such as \\ComputerName\ShareName. + */ + public static final int SE_LMSHARE = 5; + + /** + * Indicates a local kernel object. The GetSecurityInfo and + * SetSecurityInfo functions support all types of kernel objects. + * The GetNamedSecurityInfo and SetNamedSecurityInfo functions work + * only with the following kernel objects: semaphore, event, mutex, + * waitable timer, and file mapping. + */ + public static final int SE_KERNEL_OBJECT = 6; + + /** + * Indicates a window station or desktop object on the local computer. + * You cannot use GetNamedSecurityInfo and SetNamedSecurityInfo with + * these objects because the names of window stations or desktops are + * not unique. + */ + public static final int SE_WINDOW_OBJECT = 7; + + /** + * Indicates a directory service object or a property set or property + * of a directory service object. The name string for a directory service + * object must be in X.500 form, for example: + * CN=SomeObject,OU=ou2,OU=ou1,DC=DomainName,DC=CompanyName,DC=com,O=internet + */ + public static final int SE_DS_OBJECT = 8; + + /** + * The server process can impersonate the client's security context on + * remote systems. + */ + public static final int SE_DS_OBJECT_ALL = 9; + + /** + * Indicates a provider-defined object. + */ + public static final int SE_PROVIDER_DEFINED_OBJECT = 10; + + /** + * Indicates a WMI object. + */ + public static final int SE_WMIGUID_OBJECT = 11; + + /** + * Indicates an object for a registry entry under WOW64. + */ + public static final int SE_REGISTRY_WOW64_32KEY = 12; + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java index cb5556bdb7..146bbcfa44 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java @@ -305,6 +305,25 @@ public boolean LogonUser(String lpszUsername, String lpszDomain, public boolean OpenThreadToken(HANDLE ThreadHandle, int DesiredAccess, boolean OpenAsSelf, HANDLEByReference TokenHandle); + /** + * The SetThreadToken function assigns an impersonation token to a thread. + * The function can also cause a thread to stop using an impersonation token. + * @param ThreadHandle [in, optional] + * A pointer to a handle to the thread to which the function + * assigns the impersonation token. If ThreadHandle is NULL, the + * function assigns the impersonation token to the calling thread. + * @param TokenHandle [in, optional] + * A handle to the impersonation token to assign to the thread. + * This handle must have been opened with TOKEN_IMPERSONATE access + * rights. For more information, see Access Rights for Access-Token + * Objects. If Token is NULL, the function causes the + * thread to stop using an impersonation token. + * @return If the function succeeds, the return value is nonzero. If the + * function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + public boolean SetThreadToken(HANDLEByReference ThreadHandle, HANDLE TokenHandle); + /** * The OpenProcessToken function opens the access token associated with a * process. @@ -1532,6 +1551,177 @@ public boolean GetFileSecurity(WString lpFileName, int RequestedInformation, Pointer pointer, int nLength, IntByReference lpnLengthNeeded); + /** + * The GetNamedSecurityInfo function retrieves a copy of the security + * descriptor for an object specified by name + * + * @param pObjectName + * A pointer to a that specifies the name of the object from + * which to retrieve security information. + * For descriptions of the string formats for the different + * object types, see SE_OBJECT_TYPE. + * @param ObjectType + * Specifies a value from the SE_OBJECT_TYPE enumeration that + * indicates the type of object named by the pObjectName parameter. + * @param SecurityInfo + * A set of bit flags that indicate the type of security + * information to retrieve. See WinNT *_SECURITY_INFORMATION + * @param ppsidOwner [out, optional] + * A pointer to a variable that receives a pointer to the owner SID + * in the security descriptor returned in ppSecurityDescriptor + * or NULL if the security descriptor has no owner SID. + * The returned pointer is valid only if you set the + * OWNER_SECURITY_INFORMATION flag. Also, this parameter can be + * NULL if you do not need the owner SID. + * @param ppsidGroup [out, optional] + * A pointer to a variable that receives a pointer to the primary + * group SID in the returned security descriptor or NULL if the + * security descriptor has no group SID. The returned pointer is + * valid only if you set the GROUP_SECURITY_INFORMATION flag. + * Also, this parameter can be NULL if you do not need the group SID. + * @param ppDacl [out, optional] + * A pointer to a variable that receives a pointer to the DACL in + * the returned security descriptor or NULL if the security + * descriptor has no DACL. The returned pointer is valid only if + * you set the DACL_SECURITY_INFORMATION flag. Also, this parameter + * can be NULL if you do not need the DACL. + * @param ppSacl [out, optional] + * A pointer to a variable that receives a pointer to the SACL in + * the returned security descriptor or NULL if the security + * descriptor has no SACL. The returned pointer is valid only if + * you set the SACL_SECURITY_INFORMATION flag. Also, this parameter + * can be NULL if you do not need the SACL. + * @param ppSecurityDescriptor + * A pointer to a variable that receives a pointer to the security + * descriptor of the object. When you have finished using the + * pointer, free the returned buffer by calling the LocalFree + * function. + * + * This parameter is required if any one of the ppsidOwner, + * ppsidGroup, ppDacl, or ppSacl parameters is not NULL. + * @return whether the call succeeded. A nonzero return is a failure. + * + * NOTES: + * 1. To read the owner, group, or DACL from the object's security descriptor, + * the object's DACL must grant READ_CONTROL access to the caller, or the caller + * must be the owner of the object. + * 2. To read the system access control list of the object, the SE_SECURITY_NAME + * privilege must be enabled for the calling process. For information about the + * security implications of enabling privileges, see Running with Special Privileges. + */ + public int GetNamedSecurityInfo( + String pObjectName, + int ObjectType, + int SecurityInfo, + PointerByReference ppsidOwner, + PointerByReference ppsidGroup, + PointerByReference ppDacl, + PointerByReference ppSacl, + PointerByReference ppSecurityDescriptor); + + /** + * The SetNamedSecurityInfo function sets specified security information in + * the security descriptor of a specified object. The caller identifies the + * object by name. + * + * @param pObjectName [in] + * A pointer to a string that specifies the name of the object for + * which to set security information. This can be + * the name of a local or remote file or directory on an NTFS file + * system, network share, registry key, semaphore, event, mutex, + * file mapping, or waitable timer. * + * For descriptions of the string formats for the different + * object types, see SE_OBJECT_TYPE. + * @param ObjectType [in] + * A value of the SE_OBJECT_TYPE enumeration that indicates the type + * of object named by the pObjectName parameter. + * @param SecurityInfo [in] + * A set of bit flags that indicate the type of security + * information to set. See WinNT *_SECURITY_INFORMATION + * @param ppsidOwner [in, optional] + * A pointer to a SID structure that identifies the owner of the object. + * If the caller does not have the SeRestorePrivilege constant + * (see Privilege Constants), this SID must be contained in the + * caller's token, and must have the SE_GROUP_OWNER permission enabled. + * The SecurityInfo parameter must include the OWNER_SECURITY_INFORMATION + * flag. To set the owner, the caller must have WRITE_OWNER access to + * the object or have the SE_TAKE_OWNERSHIP_NAME privilege enabled. + * If you are not setting the owner SID, this parameter can be NULL. + * @param ppsidGroup [in, optional] + * A pointer to a SID that identifies the primary group of the object. + * The SecurityInfo parameter must include the GROUP_SECURITY_INFORMATION + * flag. If you are not setting the primary group SID, this parameter + * can be NULL. + * @param ppDacl [in, optional] + * A pointer to the new DACL for the object. The SecurityInfo parameter + * must include the DACL_SECURITY_INFORMATION flag. The caller must have + * WRITE_DAC access to the object or be the owner of the object. If you + * are not setting the DACL, this parameter can be NULL. + * @param ppSacl [in, optional] + * A pointer to the new SACL for the object. The SecurityInfo parameter + * must include any of the following flags: SACL_SECURITY_INFORMATION, + * LABEL_SECURITY_INFORMATION, ATTRIBUTE_SECURITY_INFORMATION, + * SCOPE_SECURITY_INFORMATION, or BACKUP_SECURITY_INFORMATION. + * If setting SACL_SECURITY_INFORMATION or SCOPE_SECURITY_INFORMATION, + * the caller must have the SE_SECURITY_NAME privilege enabled. If + * you are not setting the SACL, this parameter can be NULL. + * @return whether the call succeeded. A nonzero return is a failure. + * + * NOTES: + * 1. The SetNamedSecurityInfo function does not reorder access-allowed or access-denied + * ACEs based on the preferred order. When propagating inheritable ACEs to existing + * child objects, SetNamedSecurityInfo puts inherited ACEs in order after all of the + * noninherited ACEs in the DACLs of the child objects. + * 2. This function transfers information in plaintext. The information transferred by + * this function is signed unless signing has been turned off for the system, but no + * encryption is performed. + * 3. When you update access rights of a folder indicated by an UNC path, for example + * \\Test\TestFolder, the original inherited ACE is removed and the full volume path + * is not included. + */ + public int SetNamedSecurityInfo( + String pObjectName, + int ObjectType, + int SecurityInfo, + Pointer ppsidOwner, + Pointer ppsidGroup, + Pointer ppDacl, + Pointer ppSacl); + + /** + * The GetSecurityDescriptorLength function returns the length, in bytes, of a structurally + * valid security descriptor. The length includes the length of all associated structures. + * + * @param ppSecurityDescriptor + * A pointer to the SECURITY_DESCRIPTOR structure whose length the function returns. + * The pointer is assumed to be valid. + * @return If the function succeeds, the function returns the length, in bytes, of the SECURITY_DESCRIPTOR structure. + * If the SECURITY_DESCRIPTOR structure is not valid, the return value is undefined. + */ + public int GetSecurityDescriptorLength(Pointer ppSecurityDescriptor); + + /** + * The IsValidSecurityDescriptor function determines whether the components of a security descriptor are valid. + * + * @param ppSecurityDescriptor [in] + * A pointer to a SECURITY_DESCRIPTOR structure that the function validates. + * @return If the components of the security descriptor are valid, the return value is nonzero. + */ + public boolean IsValidSecurityDescriptor(Pointer ppSecurityDescriptor); + + /** + * The IsValidAcl function validates an access control list (ACL). + * + * @param pAcl [in] + * A pointer to an ACL structure validated by this function. This value must not be NULL. + * @return If the ACL is valid, the function returns nonzero. If the ACL is not valid, the function returns zero. + * There is no extended error information for this function; do not call GetLastError. + * + * This function checks the revision level of the ACL and verifies that the number of access control entries + * (ACEs) specified in the AceCount member of the ACL structure fits the space specified by the AclSize member + * of the ACL structure.If pAcl is NULL, the application will fail with an access violation. + */ + public boolean IsValidAcl(Pointer pAcl); /** * Applies the given mapping of generic access rights to the given access mask. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java index a8c45526ef..13e94a9795 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java @@ -2123,6 +2123,164 @@ private static Memory getSecurityDescriptorForFile(final String absoluteFilePath return securityDescriptorMemoryPointer; } + /** + * Get a self relative security descriptor for the given object type. The value is returned in Memory + * @param absoluteObjectPath + * A pointer to a null-terminated string that specifies the name of the object + * from which to retrieve security information. For descriptions of the string + * formats for the different object types, see SE_OBJECT_TYPE in AccCtrl.java + * @param objectType + * Object type referred to by the path. See {@link AccCtrl#SE_OBJECT_TYPE} for valid definitions. + * @param getSACL + * Get SACL of the object. See {@link Advapi32#GetNamedSecurityInfo} for process privilege requirements in getting the SACL. + * @return Memory containing the self relative security descriptor + */ + public static Memory getSecurityDescriptorForObject(final String absoluteObjectPath, int objectType, boolean getSACL) { + + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION + | (getSACL ? SACL_SECURITY_INFORMATION : 0); + + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + int lastError = Advapi32.INSTANCE.GetNamedSecurityInfo( + absoluteObjectPath, + objectType, + infoType, + null, + null, + null, + null, + ppSecurityDescriptor); + + if (lastError != 0) { + throw new Win32Exception(lastError); + } + + int nLength = Advapi32.INSTANCE.GetSecurityDescriptorLength(ppSecurityDescriptor.getValue()); + final Memory memory = new Memory(nLength); + memory.write(0, ppSecurityDescriptor.getValue().getByteArray(0, nLength), 0, nLength); + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + return memory; + } + + /** + * Set a self relative security descriptor for the given object type. + * + * @param absoluteObjectPath + * A pointer to a null-terminated string that specifies the name of the object + * from which to retrieve security information. For descriptions of the string + * formats for the different object types, see SE_OBJECT_TYPE in AccCtrl.java + * @param objectType + * Object type referred to by the path. See {@link AccCtrl#SE_OBJECT_TYPE} for valid definitions. + * @param securityDescriptor + * A security descriptor to set. + * @param setOwner + * Set the owner. The owner is extracted from securityDescriptor and must be valid, + * otherwise IllegalArgumentException is throw. + * See {@link Advapi32#SetNamedSecurityInfo} for process privilege requirements in getting the OWNER. + * @param setGroup + * Set the group. The group is extracted from securityDescriptor and must be valid, + * otherwise IllegalArgumentException is throw. + * @param setDACL + * Set the DACL. The DACL is extracted from securityDescriptor and must be valid, + * otherwise IllegalArgumentException is throw. + * @param setSACL + * Set the SACL. The SACL is extracted from securityDescriptor and must be valid, + * otherwise IllegalArgumentException is throw. + * See {@link Advapi32#SetNamedSecurityInfo} for process privilege requirements in getting the SACL. + * @param setDACLProtectedStatus + * Set DACL protected status as contained within securityDescriptor.control. + * @param setSACLProtectedStatus + * Set SACL protected status as contained within securityDescriptor.control. + */ + public static void setSecurityDescriptorForObject(final String absoluteObjectPath, + int objectType, + SECURITY_DESCRIPTOR_RELATIVE securityDescriptor, + boolean setOwner, + boolean setGroup, + boolean setDACL, + boolean setSACL, + boolean setDACLProtectedStatus, + boolean setSACLProtectedStatus) { + + final PSID psidOwner = securityDescriptor.getOwner(); + final PSID psidGroup = securityDescriptor.getGroup(); + final ACL dacl = securityDescriptor.getDiscretionaryACL(); + final ACL sacl = securityDescriptor.getSystemACL(); + + int infoType = 0; + // Parameter validation and infoType flag setting. + if (setOwner) { + if (psidOwner == null) + throw new IllegalArgumentException("SECURITY_DESCRIPTOR_RELATIVE does not contain owner"); + if (!Advapi32.INSTANCE.IsValidSid(psidOwner)) + throw new IllegalArgumentException("Owner PSID is invalid"); + infoType |= OWNER_SECURITY_INFORMATION; + } + + if (setGroup) { + if (psidGroup == null) + throw new IllegalArgumentException("SECURITY_DESCRIPTOR_RELATIVE does not contain group"); + if (!Advapi32.INSTANCE.IsValidSid(psidGroup)) + throw new IllegalArgumentException("Group PSID is invalid"); + infoType |= GROUP_SECURITY_INFORMATION; + } + + if (setDACL) { + if (dacl == null) + throw new IllegalArgumentException("SECURITY_DESCRIPTOR_RELATIVE does not contain DACL"); + if (!Advapi32.INSTANCE.IsValidAcl(dacl.getPointer())) + throw new IllegalArgumentException("DACL is invalid"); + infoType |= DACL_SECURITY_INFORMATION; + } + + if (setSACL) { + if (sacl == null) + throw new IllegalArgumentException("SECURITY_DESCRIPTOR_RELATIVE does not contain SACL"); + if (!Advapi32.INSTANCE.IsValidAcl(sacl.getPointer())) + throw new IllegalArgumentException("SACL is invalid"); + infoType |= SACL_SECURITY_INFORMATION; + } + + /* + * Control bits SE_DACL_PROTECTED/SE_SACL_PROTECTED indicate the *ACL is protected. The *ACL_SECURITY_INFORMATION flags + * are meta flags for SetNamedSecurityInfo and are not stored in the SD. If either *ACLProtectedStatus is set, + * get the current status from the securityDescriptor and apply as such, otherwise the ACL remains at its default. + */ + if (setDACLProtectedStatus) { + if ((securityDescriptor.Control & SE_DACL_PROTECTED) != 0) { + infoType |= PROTECTED_DACL_SECURITY_INFORMATION; + } + else if ((securityDescriptor.Control & SE_DACL_PROTECTED) == 0) { + infoType |= UNPROTECTED_DACL_SECURITY_INFORMATION; + } + } + + if (setSACLProtectedStatus) { + if ((securityDescriptor.Control & SE_SACL_PROTECTED) != 0) { + infoType |= PROTECTED_SACL_SECURITY_INFORMATION; + } + else if ((securityDescriptor.Control & SE_SACL_PROTECTED) == 0) { + infoType |= UNPROTECTED_SACL_SECURITY_INFORMATION; + } + } + + int lastError = Advapi32.INSTANCE.SetNamedSecurityInfo( + absoluteObjectPath, + objectType, + infoType, + setOwner ? psidOwner.getPointer() : null, + setGroup ? psidGroup.getPointer() : null, + setDACL ? dacl.getPointer() : null, + setSACL ? sacl.getPointer() : null); + + if (lastError != 0) { + throw new Win32Exception(lastError); + } + } + /** * Checks if the current process has the given permission for the file. * @param file the file to check @@ -2189,6 +2347,55 @@ public static boolean accessCheck(File file, AccessCheckPermission permissionToC return hasAccess; } + + /** + * Gets a file's Security Descriptor. Convenience wrapper getSecurityDescriptorForObject. + * + * @param file + * File object containing a path to a file system object. + * @param getSACL + * Get the SACL. See {@link Advapi32#GetNamedSecurityInfo} for process privilege requirements in getting the SACL. + * @return The file's Security Descriptor in self relative format. + */ + public static SECURITY_DESCRIPTOR_RELATIVE getFileSecurityDescriptor(File file, boolean getSACL) + { + SECURITY_DESCRIPTOR_RELATIVE sdr = null; + Memory securityDesc = getSecurityDescriptorForObject(file.getAbsolutePath().replaceAll("/", "\\"), AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, getSACL); + sdr = new SECURITY_DESCRIPTOR_RELATIVE(securityDesc); + return sdr; + } + + /** + * Sets a file's Security Descriptor. Convenience wrapper setSecurityDescriptorForObject. + * @param file + * File object containing a path to a file system object. + * @param securityDescriptor + * The security descriptor to set. + * @param setOwner + * Set the owner. See {@link Advapi32#SetNamedSecurityInfo} for process privilege requirements in setting the owner. + * @param setGroup + * Set the group. + * @param setDACL + * Set the DACL. + * @param setSACL + * Set the SACL. See {@link Advapi32#SetNamedSecurityInfo} for process privilege requirements in setting the SACL. + * @param setDACLProtectedStatus + * Set DACL protected status as contained within securityDescriptor.control. + * @param setSACLProtectedStatus + * Set SACL protected status as contained within securityDescriptor.control. * + */ + public static void setFileSecurityDescriptor( + File file, + SECURITY_DESCRIPTOR_RELATIVE securityDescriptor, + boolean setOwner, + boolean setGroup, + boolean setDACL, + boolean setSACL, + boolean setDACLProtectedStatus, + boolean setSACLProtectedStatus) + { + setSecurityDescriptorForObject(file.getAbsolutePath().replaceAll("/", "\\"), AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, securityDescriptor, setOwner, setGroup, setDACL, setSACL, setDACLProtectedStatus, setSACLProtectedStatus); + } /** * Encrypts a file or directory. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java index 9ed47b13ab..e383d3d6c4 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java @@ -355,6 +355,10 @@ public byte[] getBytes() { int len = Advapi32.INSTANCE.GetLengthSid(this); return getPointer().getByteArray(0, len); } + + public String getSidString() { + return Advapi32Util.convertSidToStringSid(this); + } public Pointer sid; } @@ -2057,7 +2061,24 @@ public EVENTLOGRECORD(Pointer p) { int PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000; int UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000; int UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000; - + + /* Security control bits */ + int SE_OWNER_DEFAULTED = 0x00000001; + int SE_GROUP_DEFAULTED = 0x00000002; + int SE_DACL_PRESENT = 0x00000004; + int SE_DACL_DEFAULTED = 0x00000008; + int SE_SACL_PRESENT = 0x00000010; + int SE_SACL_DEFAULTED = 0x00000020; + int SE_DACL_AUTO_INHERIT_REQ = 0x00000100; + int SE_SACL_AUTO_INHERIT_REQ = 0x00000200; + int SE_DACL_AUTO_INHERITED = 0x00000400; + int SE_SACL_AUTO_INHERITED = 0x00000800; + int SE_DACL_PROTECTED = 0x00001000; + int SE_SACL_PROTECTED = 0x00002000; + int SE_RM_CONTROL_VALID = 0x00004000; + int SE_SELF_RELATIVE = 0x00008000; + + public static class SECURITY_DESCRIPTOR extends Structure { public static class ByReference extends SECURITY_DESCRIPTOR implements Structure.ByReference { @@ -2111,7 +2132,7 @@ public ACL(Pointer pointer) { ace = new ACCESS_DENIED_ACE(share); break; default: - throw new IllegalArgumentException("Unknwon ACE type " + throw new IllegalArgumentException("Unknown ACE type " + aceType); } ACEs[i] = ace; @@ -2150,30 +2171,54 @@ protected List getFieldOrder() { } private ACL DACL; - + private PSID OWNER; + private PSID GROUP; + private ACL SACL; + public SECURITY_DESCRIPTOR_RELATIVE() { } public SECURITY_DESCRIPTOR_RELATIVE(byte[] data) { super(new Memory(data.length)); getPointer().write(0, data, 0, data.length); - setDacl(); + setMembers(); } public SECURITY_DESCRIPTOR_RELATIVE(Pointer p) { super(p); - setDacl(); + setMembers(); + } + + public PSID getOwner() { + return OWNER; + } + + public PSID getGroup() { + return GROUP; } public ACL getDiscretionaryACL() { return DACL; } - private final void setDacl() { + public ACL getSystemACL() { + return SACL; + } + + private final void setMembers() { read(); if (Dacl != 0) { DACL = new ACL(getPointer().share(Dacl)); } + if (Sacl != 0) { + SACL = new ACL(getPointer().share(Sacl)); + } + if (Group != 0) { + GROUP = new PSID(getPointer().share(Group)); + } + if (Owner != 0) { + OWNER = new PSID(getPointer().share(Owner)); + } } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java index c09dbee47e..6f5866a2a4 100755 --- a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java @@ -183,6 +183,68 @@ public void testOpenThreadOrProcessToken() { assertTrue(Kernel32.INSTANCE.CloseHandle(phToken.getValue())); } + public void testSetThreadTokenCurrentThread() { + HANDLEByReference phToken = new HANDLEByReference(); + HANDLEByReference phTokenDup = new HANDLEByReference(); + HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread(); + // See if thread has a token. If not, must duplicate process token and set thread token using that. + if (!Advapi32.INSTANCE.OpenThreadToken(threadHandle, + WinNT.TOKEN_IMPERSONATE | WinNT.TOKEN_QUERY, false, phToken)) { + assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError()); + HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess(); + assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle, WinNT.TOKEN_DUPLICATE, phToken)); + assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(), + WinNT.TOKEN_IMPERSONATE, + null, + WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, + WinNT.TOKEN_TYPE.TokenImpersonation, + phTokenDup)); + // Null sets on current thread + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDup.getValue())); + } + else { + //Null sets on current thread + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phToken.getValue())); + } + // Revert and cleanup + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, null)); + assertTrue(Kernel32.INSTANCE.CloseHandle(phToken.getValue())); + if (phTokenDup.getValue() != null) + assertTrue(Kernel32.INSTANCE.CloseHandle(phTokenDup.getValue())); + } + + public void testSetThreadTokenThisThread() { + HANDLEByReference phToken = new HANDLEByReference(); + HANDLEByReference phTokenDup = new HANDLEByReference(); + HANDLEByReference pthreadHandle = new HANDLEByReference(); + pthreadHandle.setValue(Kernel32.INSTANCE.GetCurrentThread()); + // See if thread has a token. If not, must duplicate process token and set thread token using that. + if (!Advapi32.INSTANCE.OpenThreadToken(pthreadHandle.getValue(), + WinNT.TOKEN_IMPERSONATE | WinNT.TOKEN_QUERY, false, phToken)) { + assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError()); + HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess(); + assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle, + WinNT.TOKEN_DUPLICATE, phToken)); + assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(), + WinNT.TOKEN_IMPERSONATE, + null, + WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, + WinNT.TOKEN_TYPE.TokenImpersonation, + phTokenDup)); + // Use HANDLEByReference on this thread to test, should be good enough for API compatibility. + assertTrue(Advapi32.INSTANCE.SetThreadToken(pthreadHandle, phTokenDup.getValue())); + } + else { + // Use HANDLEByReference on this thread to test, should be good enough for API compatibility. + assertTrue(Advapi32.INSTANCE.SetThreadToken(pthreadHandle, phToken.getValue())); + } + // Revert and cleanup + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, null)); + assertTrue(Kernel32.INSTANCE.CloseHandle(phToken.getValue())); + if (phTokenDup.getValue() != null) + assertTrue(Kernel32.INSTANCE.CloseHandle(phTokenDup.getValue())); + } + public void testDuplicateToken() { HANDLEByReference phToken = new HANDLEByReference(); HANDLEByReference phTokenDup = new HANDLEByReference(); @@ -833,6 +895,288 @@ public void testImpersonateSelf() { assertTrue(Advapi32.INSTANCE.RevertToSelf()); } + + public void testGetNamedSecurityInfoForFileNoSACL() throws Exception { + // create a temp file + File file = createTempFile(); + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + + PointerByReference ppsidOwner = new PointerByReference(); + PointerByReference ppsidGroup = new PointerByReference(); + PointerByReference ppDacl = new PointerByReference(); + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner, + ppsidGroup, + ppDacl, + null, + ppSecurityDescriptor), 0); + + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + } + + public void testGetNamedSecurityInfoForFileWithSACL() throws Exception { + + boolean impersontating = false; + WinNT.LUID pLuid = new WinNT.LUID(); + + assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_SECURITY_NAME, pLuid)); + + final HANDLEByReference phToken = new HANDLEByReference(); + final HANDLEByReference phTokenDuplicate = new HANDLEByReference(); + // open thread or process token, elevate + if (!Advapi32.INSTANCE.OpenThreadToken( + Kernel32.INSTANCE.GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES, + false, + phToken)) + { + assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError()); + // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous. When this happens, + // we need to open the process token to duplicate it, then set our thread token. + assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phToken)); + // Process token opened, now duplicate + assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE, + null, + SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, + TOKEN_TYPE.TokenImpersonation, + phTokenDuplicate)); + // And set thread token. + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDuplicate.getValue())); + impersontating = true; + } + + // Which token to adjust depends on whether we had to impersonate or not. + HANDLE tokenAdjust = impersontating ? phTokenDuplicate.getValue() : phToken.getValue(); + + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1); + tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); + assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null)); + + // create a temp file + File file = createTempFile(); + + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION + | SACL_SECURITY_INFORMATION; + + PointerByReference ppsidOwner = new PointerByReference(); + PointerByReference ppsidGroup = new PointerByReference(); + PointerByReference ppDacl = new PointerByReference(); + PointerByReference ppSacl = new PointerByReference(); + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner, + ppsidGroup, + ppDacl, + ppSacl, + ppSecurityDescriptor), 0); + + // Clean up resources + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + if (impersontating) { + Advapi32.INSTANCE.SetThreadToken(null, null); + } + else { + tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(0)); + Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null); + } + if (phToken.getValue() != null) + Kernel32.INSTANCE.CloseHandle(phToken.getValue()); + if (phTokenDuplicate.getValue() != null) + Kernel32.INSTANCE.CloseHandle(phTokenDuplicate.getValue()); + } + + public void testSetNamedSecurityInfoForFileNoSACL() throws Exception { + // create a temp file + File file = createTempFile(); + + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + + PointerByReference ppsidOwner = new PointerByReference(); + PointerByReference ppsidGroup = new PointerByReference(); + PointerByReference ppDacl = new PointerByReference(); + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner, + ppsidGroup, + ppDacl, + null, + ppSecurityDescriptor), 0); + + assertEquals(Advapi32.INSTANCE.SetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner.getValue(), + ppsidGroup.getValue(), + ppDacl.getValue(), + null), 0); + + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + } + + public void testSetNamedSecurityInfoForFileWithSACL() throws Exception { + boolean impersontating = false; + + final HANDLEByReference phToken = new HANDLEByReference(); + final HANDLEByReference phTokenDuplicate = new HANDLEByReference(); + // open thread or process token, elevate + if (!Advapi32.INSTANCE.OpenThreadToken( + Kernel32.INSTANCE.GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES, + false, + phToken)) + { + assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError()); + // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous. When this happens, + // we need to open the process token to duplicate it, then set our thread token. + assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phToken)); + // Process token opened, now duplicate + assertTrue(Advapi32.INSTANCE.DuplicateTokenEx( + phToken.getValue(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE, + null, + SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, + TOKEN_TYPE.TokenImpersonation, + phTokenDuplicate)); + // And set thread token. + assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDuplicate.getValue())); + impersontating = true; + } + + // Which token to adjust depends on whether we had to impersonate or not. + HANDLE tokenAdjust = impersontating ? phTokenDuplicate.getValue() : phToken.getValue(); + + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1); + WinNT.LUID pLuid = new WinNT.LUID(); + + assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_SECURITY_NAME, pLuid)); + tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); + assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null)); + + assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_RESTORE_NAME, pLuid)); + tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); + assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null)); + + // create a temp file + File file = createTempFile(); + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION + | SACL_SECURITY_INFORMATION; + + PointerByReference ppsidOwner = new PointerByReference(); + PointerByReference ppsidGroup = new PointerByReference(); + PointerByReference ppDacl = new PointerByReference(); + PointerByReference ppSacl = new PointerByReference(); + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner, + ppsidGroup, + ppDacl, + ppSacl, + ppSecurityDescriptor), 0); + + // Send the DACL as a SACL + assertEquals(Advapi32.INSTANCE.SetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + ppsidOwner.getValue(), + ppsidGroup.getValue(), + ppDacl.getValue(), + ppDacl.getValue()), 0); + + // Clean up resources + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + if (impersontating) { + Advapi32.INSTANCE.SetThreadToken(null, null); + } + else { + tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(0)); + Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null); + } + if (phToken.getValue() != null) + Kernel32.INSTANCE.CloseHandle(phToken.getValue()); + if (phTokenDuplicate.getValue() != null) + Kernel32.INSTANCE.CloseHandle(phTokenDuplicate.getValue()); + } + + public void testGetSecurityDescriptorLength() throws Exception { + // create a temp file + File file = createTempFile(); + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + null, + null, + null, + null, + ppSecurityDescriptor), 0); + + assertTrue(Advapi32.INSTANCE.GetSecurityDescriptorLength(ppSecurityDescriptor.getValue()) > 0); + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + } + + public void testIsValidSecurityDescriptor() throws Exception { + // create a temp file + File file = createTempFile(); + int infoType = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + + PointerByReference ppSecurityDescriptor = new PointerByReference(); + + assertEquals(Advapi32.INSTANCE.GetNamedSecurityInfo( + file.getAbsolutePath(), + AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT, + infoType, + null, + null, + null, + null, + ppSecurityDescriptor), 0); + + assertTrue(Advapi32.INSTANCE.IsValidSecurityDescriptor(ppSecurityDescriptor.getValue())); + Kernel32.INSTANCE.LocalFree(ppSecurityDescriptor.getValue()); + file.delete(); + } + public void testMapGenericReadMask() { final GENERIC_MAPPING mapping = new GENERIC_MAPPING(); mapping.genericRead = new DWORD(FILE_GENERIC_READ); diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java index c8eb531046..5f0b38db79 100755 --- a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java @@ -26,6 +26,7 @@ import com.sun.jna.platform.win32.LMAccess.USER_INFO_1; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.platform.win32.WinNT.PSID; +import com.sun.jna.platform.win32.WinNT.SECURITY_DESCRIPTOR_RELATIVE; import com.sun.jna.platform.win32.WinNT.SID_NAME_USE; import com.sun.jna.platform.win32.WinNT.WELL_KNOWN_SID_TYPE; import com.sun.jna.platform.win32.WinReg.HKEY; @@ -487,6 +488,22 @@ public void testGetEnvironmentBlock() { assertEquals("Environment block must comprise key=value pairs separated by NUL characters", expected, block); } + public void testGetFileSecurityDescriptor() throws Exception { + File file = createTempFile(); + SECURITY_DESCRIPTOR_RELATIVE sdr = Advapi32Util.getFileSecurityDescriptor(file, false); + assertTrue(Advapi32.INSTANCE.IsValidSecurityDescriptor(sdr.getPointer())); + file.delete(); + } + + public void testSetFileSecurityDescriptor() throws Exception { + File file = createTempFile(); + SECURITY_DESCRIPTOR_RELATIVE sdr = Advapi32Util.getFileSecurityDescriptor(file, false); + Advapi32Util.setFileSecurityDescriptor(file, sdr, false, true, true, false, true, false); + sdr = Advapi32Util.getFileSecurityDescriptor(file, false); + assertTrue(Advapi32.INSTANCE.IsValidSecurityDescriptor(sdr.getPointer())); + file.delete(); + } + public void testEncryptFile() throws Exception { File file = createTempFile(); assertEquals(FILE_ENCRYPTABLE, Advapi32Util.fileEncryptionStatus(file));