Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Advapi32Util accessCheck method to verify file permissions #301

Merged
merged 4 commits into from
Jan 22, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Features
* [#250](https://github.com/twall/jna/pull/250): Added `com.sun.jna.platform.win32.Kernel32.GetPrivateProfileSection`, `GetPrivateProfileSectionNames` and `WritePrivateProfileSection` and corresponding `Kernel32Util` helpers - [@quipsy-karg](https://github.com/quipsy-karg).
* [#287](https://github.com/twall/jna/pull/287): Added `DBTF_MEDIA` and `DBTF_NET` to `com.sun.jna.platform.win32.DBT` - [@daifei4321](https://github.com/daifei4321).
* [#295](https://github.com/twall/jna/pull/295): Added `com.sun.jna.platform.win32.Kernel32.ResetEvent` - [@manithree](https://github.com/manithree).
* [#301](https://github.com/twall/jna/pull/301): Added `accessCheck` to `com.sun.jna.platform.win32.Advapi32Util`, `MapGenericMask` and `AccessCheck` to `com.sun.jna.platform.win32.Advapi32`, `PRIVILEGE_SET` and `GENERIC_MAPPING` to `com.sun.jna.platform.win32.WinNT` - [@BusyByte](https://github.com/BusyByte).

Bug Fixes
---------
Expand Down
35 changes: 35 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

import static com.sun.jna.platform.win32.WinDef.BOOLByReference;
import static com.sun.jna.platform.win32.WinDef.DWORD;
import static com.sun.jna.platform.win32.WinDef.DWORDByReference;
import static com.sun.jna.platform.win32.WinNT.GENERIC_MAPPING;
import static com.sun.jna.platform.win32.WinNT.PRIVILEGE_SET;

/**
* Advapi32.dll Interface.
*
Expand Down Expand Up @@ -1523,4 +1529,33 @@ public boolean GetFileSecurity(WString lpFileName,
int RequestedInformation, Pointer pointer, int nLength,
IntByReference lpnLengthNeeded);


/**
* Applies the given mapping of generic access rights to the given access mask.
* @param AccessMask [in, out] A pointer to an access mask.
* @param GenericMapping [in] A pointer to a GENERIC_MAPPING structure specifying a mapping of generic access types to specific and standard access types.
*/
public void MapGenericMask(DWORDByReference AccessMask, GENERIC_MAPPING GenericMapping);


/**
* Check if the if the security descriptor grants access to the given client token.
*
* @param pSecurityDescriptor [in] A pointer to a SECURITY_DESCRIPTOR structure against which access is checked.
* @param ClientToken [in] A handle to an impersonation token that represents the client that is attempting to gain access. The handle must have TOKEN_QUERY access to the token; otherwise, the function fails with ERROR_ACCESS_DENIED.
* @param DesiredAccess [in] Access mask that specifies the access rights to check. This mask must have been mapped by the MapGenericMask function to contain no generic access rights.<br>
* If this parameter is MAXIMUM_ALLOWED, the function sets the GrantedAccess access mask to indicate the maximum access rights the security descriptor allows the client.
* @param GenericMapping [in] A pointer to the GENERIC_MAPPING structure associated with the object for which access is being checked.
* @param PrivilegeSet [out, optional] A pointer to a PRIVILEGE_SET structure that receives the privileges used to perform the access validation. If no privileges were used, the function sets the PrivilegeCount member to zero.
* @param PrivilegeSetLength [in, out] Specifies the size, in bytes, of the buffer pointed to by the PrivilegeSet parameter.
* @param GrantedAccess [out] A pointer to an access mask that receives the granted access rights. If AccessStatus is set to FALSE, the function sets the access mask to zero. If the function fails, it does not set the access mask.
* @param AccessStatus [out] A pointer to a variable that receives the results of the access check. If the security descriptor allows the requested access rights to the client identified by the access token, AccessStatus is set to TRUE. Otherwise, AccessStatus is set to FALSE, and you can call GetLastError to get extended error information.
* @return true on success; false on failure (use GetLastError to get extended error information)
*/
public boolean AccessCheck(Pointer pSecurityDescriptor,
HANDLE ClientToken, DWORD DesiredAccess,
GENERIC_MAPPING GenericMapping,
PRIVILEGE_SET PrivilegeSet,
DWORDByReference PrivilegeSetLength,
DWORDByReference GrantedAccess, BOOLByReference AccessStatus);
}
127 changes: 125 additions & 2 deletions contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package com.sun.jna.platform.win32;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -40,6 +41,12 @@
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.PointerByReference;

import static com.sun.jna.platform.win32.WinDef.BOOLByReference;
import static com.sun.jna.platform.win32.WinDef.DWORD;
import static com.sun.jna.platform.win32.WinDef.DWORDByReference;
import static com.sun.jna.platform.win32.WinNT.*;


/**
* Advapi32 utility API.
*
Expand Down Expand Up @@ -81,6 +88,7 @@ public static class Account {
public String fqn;
}


/**
* Retrieves the name of the user associated with the current thread.
*
Expand Down Expand Up @@ -430,14 +438,14 @@ public static Account[] getCurrentUserGroups() {
// open thread or process token
HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
if (!Advapi32.INSTANCE.OpenThreadToken(threadHandle,
WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, true, phToken)) {
TOKEN_DUPLICATE | TOKEN_QUERY, true, phToken)) {
if (W32Errors.ERROR_NO_TOKEN != Kernel32.INSTANCE
.GetLastError()) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
if (!Advapi32.INSTANCE.OpenProcessToken(processHandle,
WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken)) {
TOKEN_DUPLICATE | TOKEN_QUERY, phToken)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
Expand Down Expand Up @@ -2062,4 +2070,119 @@ public static ACCESS_ACEStructure[] getFileSecurity(String fileName,
}
return aceStructures;
}

public static enum AccessCheckPermission {
READ(GENERIC_READ),
WRITE(GENERIC_WRITE),
EXECUTE(GENERIC_EXECUTE);

final int code;

AccessCheckPermission(int code) {
this.code = code;
}

public int getCode() {
return code;
}
}


private static Memory getSecurityDescriptorForFile(final String absoluteFilePath) {
final int infoType = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION;

final IntByReference lpnSize = new IntByReference();
boolean succeeded = Advapi32.INSTANCE.GetFileSecurity(
new WString(absoluteFilePath),
infoType,
null,
0, lpnSize);

if (!succeeded) {
final int lastError = Kernel32.INSTANCE.GetLastError();
if (W32Errors.ERROR_INSUFFICIENT_BUFFER != lastError) {
throw new Win32Exception(lastError);
}
}

final int nLength = lpnSize.getValue();
final Memory securityDescriptorMemoryPointer = new Memory(nLength);
succeeded = Advapi32.INSTANCE.GetFileSecurity(new WString(
absoluteFilePath), infoType, securityDescriptorMemoryPointer, nLength, lpnSize);

if (!succeeded) {
securityDescriptorMemoryPointer.clear();
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

return securityDescriptorMemoryPointer;
}

/**
* Checks if the current process has the given permission for the file.
* @param file the file to check
* @param permissionToCheck the permission to check for the file
* @return true if has access, otherwise false
*/
public static boolean accessCheck(File file, AccessCheckPermission permissionToCheck) {
boolean hasAccess = false;
final Memory securityDescriptorMemoryPointer = getSecurityDescriptorForFile(file.getAbsolutePath().replaceAll("/", "\\"));

HANDLEByReference openedAccessToken = null;
final HANDLEByReference duplicatedToken = new HANDLEByReference();
try{
openedAccessToken = new HANDLEByReference();

final int desireAccess = TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ;
if(!Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), desireAccess, openedAccessToken)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

if(!Advapi32.INSTANCE.DuplicateToken(openedAccessToken.getValue(), SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, duplicatedToken)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

final DWORDByReference rights = new DWORDByReference(new DWORD(permissionToCheck.getCode()));
Advapi32.INSTANCE.MapGenericMask(rights, mapping);

final PRIVILEGE_SET privileges = new PRIVILEGE_SET(1);
privileges.PrivilegeCount = new DWORD(0);
final DWORDByReference privilegeLength = new DWORDByReference(new DWORD(privileges.size()));

final DWORDByReference grantedAccess = new DWORDByReference();
final BOOLByReference result = new BOOLByReference();
if(!Advapi32.INSTANCE.AccessCheck(securityDescriptorMemoryPointer,
duplicatedToken.getValue(),
rights.getValue(),
mapping,
privileges, privilegeLength, grantedAccess, result)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

hasAccess = result.getValue().booleanValue();

} finally {

if(openedAccessToken != null && openedAccessToken.getValue() != null) {
Kernel32.INSTANCE.CloseHandle(openedAccessToken.getValue());
}

if(duplicatedToken != null && duplicatedToken.getValue() != null) {
Kernel32.INSTANCE.CloseHandle(duplicatedToken.getValue());
}

if(securityDescriptorMemoryPointer != null) {
securityDescriptorMemoryPointer.clear();
}
}

return hasAccess;
}
}
59 changes: 59 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/WinNT.java
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,46 @@ public SID_AND_ATTRIBUTES[] getGroups() {
}
}

/**
* Specifies a set of privileges. <br>
* It is also used to indicate which, if any, privileges are held by a user or group requesting access to an object.
*/
public static class PRIVILEGE_SET extends Structure {
public DWORD PrivilegeCount;
public DWORD Control;
public LUID_AND_ATTRIBUTES Privileges[];

@Override
protected List getFieldOrder() {
return Arrays.asList("PrivilegeCount", "Control", "Privileges");
}

public PRIVILEGE_SET() {
this(0);
}
/**
* @param nbOfPrivileges
* Desired size of the Privileges array
*/
public PRIVILEGE_SET(int nbOfPrivileges) {
PrivilegeCount = new DWORD(nbOfPrivileges);
if(nbOfPrivileges > 0) {
Privileges = new LUID_AND_ATTRIBUTES[nbOfPrivileges];
}
}

/** Initialize a TOKEN_PRIVILEGES instance from initialized memory. */
public PRIVILEGE_SET(Pointer p) {
super(p);
final int count = p.getInt(0);
PrivilegeCount = new DWORD(count);
if(count > 0) {
Privileges = new LUID_AND_ATTRIBUTES[count];
}
read();
}
}

/**
* The TOKEN_PRIVILEGES structure contains information about a set of
* privileges for an access token.
Expand Down Expand Up @@ -2176,6 +2216,25 @@ void callback(int errorCode, int nBytesTransferred,
WinBase.OVERLAPPED overlapped);
}


/**
* Defines the mapping of generic access rights to specific and standard access rights for an object
*/
public static class GENERIC_MAPPING extends Structure {
public static class ByReference extends GENERIC_MAPPING implements Structure.ByReference {
}

public DWORD genericRead;
public DWORD genericWrite;
public DWORD genericExecute;
public DWORD genericAll;

@Override
protected List getFieldOrder() {
return Arrays.asList("genericRead", "genericWrite", "genericExecute", "genericAll");
}
}

/**
* Describes the relationship between the specified processor set. This structure is used with the
* {@link Kernel32#GetLogicalProcessorInformation} function.
Expand Down
80 changes: 80 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

import static com.sun.jna.platform.win32.WinNT.*;

/**
* @author dblock[at]dblock[dot]org
*/
Expand Down Expand Up @@ -826,4 +828,82 @@ public void testImpersonateSelf() {
assertTrue(Advapi32.INSTANCE.ImpersonateSelf(WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityAnonymous));
assertTrue(Advapi32.INSTANCE.RevertToSelf());
}

public void testMapGenericReadMask() {
final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_READ));
Advapi32.INSTANCE.MapGenericMask(rights, mapping);

assertEquals(FILE_GENERIC_READ, rights.getValue().intValue());
assertTrue(GENERIC_READ != (rights.getValue().intValue() & GENERIC_READ));
}

public void testMapGenericWriteMask() {
final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_WRITE));
Advapi32.INSTANCE.MapGenericMask(rights, mapping);

assertEquals(FILE_GENERIC_WRITE, rights.getValue().intValue());
assertTrue(GENERIC_WRITE != (rights.getValue().intValue() & GENERIC_WRITE));
}

public void testMapGenericExecuteMask() {
final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_EXECUTE));
Advapi32.INSTANCE.MapGenericMask(rights, mapping);

assertEquals(FILE_GENERIC_EXECUTE, rights.getValue().intValue());
assertTrue(GENERIC_EXECUTE != (rights.getValue().intValue() & GENERIC_EXECUTE));
}

public void testMapGenericAllMask() {
final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_ALL));
Advapi32.INSTANCE.MapGenericMask(rights, mapping);

assertEquals(FILE_ALL_ACCESS, rights.getValue().intValue());
assertTrue(GENERIC_ALL != (rights.getValue().intValue() & GENERIC_ALL));
}

public void testAccessCheck() {
final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
mapping.genericRead = new DWORD(FILE_GENERIC_READ);
mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
mapping.genericAll = new DWORD(FILE_ALL_ACCESS);
final Memory securityDescriptorMemoryPointer = new Memory(1);

final PRIVILEGE_SET privileges = new PRIVILEGE_SET(1);
privileges.PrivilegeCount = new DWORD(0);
final DWORDByReference privilegeLength = new DWORDByReference(new DWORD(privileges.size()));
final DWORDByReference grantedAccess = new DWORDByReference();
final BOOLByReference result = new BOOLByReference();

final boolean status = Advapi32.INSTANCE.AccessCheck(securityDescriptorMemoryPointer, null, new DWORD(FILE_GENERIC_READ), mapping, privileges, privilegeLength, grantedAccess, result);
assertFalse(status);
assertFalse(result.getValue().booleanValue());

assertEquals(WinError.ERROR_INVALID_HANDLE, Kernel32.INSTANCE.GetLastError());
}

}
Loading