I'm using Java JNA in order to access a process' memory on Windows. My code looks like this:
static WinNT.HANDLE openProcessHandle(int processId)
{
val processAccessRights = PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_TERMINATE
| PROCESS_NAME_NATIVE | PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION;
val processHandle = Kernel32.INSTANCE.OpenProcess(processAccessRights, false, processId);
if (processHandle == null)
{
val lastError = Native.getLastError();
val formatMessageFromLastErrorCode = Kernel32Util.formatMessageFromLastErrorCode(lastError);
val message = "OpenProcess() failed to open process id "
+ processId + ": " + formatMessageFromLastErrorCode;
throw new IllegalStateException(message);
}
return processHandle;
}
I'm already not using PROCESS_ALL_ACCESS due to the problems it causes.
According to this answer, I also need to enable the debug privilege for my process. However, despite calling this code successfully (e.g. all return values indicate success) before OpenProcess(), some users still get the error message OpenProcess() failed to open process id xxxx: Access denied. My application is not running as administrator. Why does it work for me and most users without administrator rights but not all users? What exactly causes this inconsistency? I would prefer to understand and tackle this problem specifically rather than making all users run my software as administrator since my software generally doesn't need those extra rights.
I authored the answer you cited in 2017, and learned last year that I had missed something key. I probably should edit that older answer to update it, but I'd forgotten about it. See this answer for a corrected version of the code that I'll be editing into the previous one you cited.
The API for AdjustTokenPrivileges() is somewhat broken in that it will give a false indication of success:
If the function succeeds, the return value is nonzero. To determine whether the function adjusted all of the specified privileges, call GetLastError, which returns one of ... ERROR_SUCCESS ... ERROR_NOT_ALL_ASSIGNED.
Given you are attempting to adjust the privileges with a non-admin user, you are not able to actually do so, and the other users will not be able to OpenProcess() for other users' processes. I don't think there is a workaround other than running your program with the necessary administrative power (SE_DEBUG_PRIVILEGE), which must be assigned by an administrator at some point.
It is likely you are still getting a misleading indication of success, but need to check GetLastError() to see if it actually worked.
Related
I am rewriting my Discord bot from Discord4J v2 to v3 that's based on Reactor. I am new to reactive programming and Java Reactor in particular. I'd like to implement a commands system with commands of three types:
DM commands — commands that users send as direct messages to the bot,
public guild commands — commands that users send in a special channel #bot-commands in my Discord server — everyone with some particular role X can use these commands, and
admin guild commands — similar to public, but users must have some role Y (which is higher than X).
If a user tries to execute some particular command but they haven't got the role required for it, the bot should tell them about that. Otherwise the command should be executed (taking into account the sender (message author), the channel this command was sent, and the command itself (the message)).
I am not sure how to implement this all reactively, and I couldn't find the necessary information on the Discord4J wiki. This is what I currently have:
private final Set<DiscordCommand> discordGuildCommands = new HashSet<>();
private final Set<DiscordCommand> discordDmCommands = new HashSet<>();
// ...
gateway = DiscordClient.create(discordToken).login().block();
// ...
gateway.getEventDispatcher()
.on(MessageCreateEvent.class)
.filter(event -> !(event.getMessage().getChannel().block() instanceof PrivateChannel))
.filterWhen(event -> event.getMember().get().hasHigherRoles(Roles.REFLEXBOT_CTRL_SINGLETON))
.flatMap(event -> Mono.just(event.getMessage().getContent())
.flatMap(content -> Flux.fromIterable(discordGuildCommands)
.filter(cmd -> content.startsWith(DISCORD_COMMANDS_PREFIX + cmd.getName()))
.flatMap(cmd -> cmd.process(event))
.next()
)
)
.subscribe();
This code appears to work fine and allows me to process admin guild commands, that is, commands sent in my Discord server's text channels by members whose "permission level" (highest role level) is at least one I specified.
But how do I implement the rest of the commands properly (reactively and nicely)? I haven't found anything like if/else chaining in Reactor, and I don't really want to duplicate the above code several times with only 1-2 filter(...) lines changed.
I'm not familiar with Discord4J, but I think you should inverse flow and add some settings for your commands.
Add hash map [command name => channel/private]
Add hash map [command name => minimum role]
Add hash map [command name => command]
Then build your flow:
Get command from first map (or return error "I dont know that command")
Using first map check if command matches with request type (private/channel) (or return error "I dont know that command")
Using second map check user role (or return error "Access denied")
Get command instance from third map
Run the command
I want to know whether the user launched our Java-based application from a read-only file system like from a .dmg, so functions like auto-update will be able to show meaningful information instead of aborting with an error. I first thought checking the .app's path would be sufficient (when launched from a .dmg it is something like /Volumes/MyApp 1.2.3/MyApp.app, but this won't work, because the user might have installed the application on a different partition. What other things may I check?
You can use -[NSURL getResourceValue:forKey:error:] with the key NSURLVolumeIsReadOnlyKey. You would apply this to the app bundle URL as returned by [[NSBundle mainBundle] bundleURL]. So:
NSBundle* bundle = [NSBundle mainBundle];
NSURL* bundleURL = bundle.bundleURL;
NSNumber* readOnly;
NSError* error;
if ([bundleURL getResourceValue:&readOnly forKey:NSURLVolumeIsReadOnlyKey error:&error])
{
BOOL isReadOnly = [readOnly boolValue];
// act on isReadOnly value
}
else
{
// Handle error
}
If OSX is POSIX compliant, to determine if filesystem is mounted R/O, You can use statvfs() or fstatvfs(), returned struct statvfs field f_flag should have ST_RDONLY bit set for R/O filesystem.
As it was pointed in comments, check if this information is correctly provided by OS.
JNA and this question may be usefull for Java.
A few more ideas, which may be usefull here (access(), open(), utime() ).
OS X specific statfs() may be used too, but this function is not portable (Linux and *BSD have slightly different statfs() functions).
You can also check directly from Java whether a certain path points to something within a read-only directory by querying the FileStore associated with your path:
File classpathRoot = new File(MyClass.class.getClassLoader().getResource("").getPath());
/* getPath() actually returns a String instead of a Path object,
* so we need to take this little detour */
Path yourAppPath = classpathRoot.toPath();
boolean isReadOnly = Files.getFileStore(yourAppPath).isReadOnly();
As above.
I have scoured the web, i also rang mac support and annoyed a mac (OSX Lion) genius (out of desperation).
I have no idea how to do this, I really don't want to have to sit on top of a terminal and give it commands.
Has any one encountered this or got a solution?
Try looking at Greg Guerin's AuthKit library. It is a Mac-specific library that wraps Mac OS X Authorization Services.
Here is an example:
import glguerin.authkit.*;
Privilege priv = new Privilege("system.privilege.admin");
Authorization auth = new MacOSXAuthorization();
try
{
// This will cause an authentication prompt to be
// shown to the user, requesting the "system.privilege.admin"
// privilege.
auth.authorize(priv, true);
// If we reach this point, we can execute privileged programs.
// Load the secured file.
Process proc = auth.execPrivileged(new String[] { "/bin/cat", "/root/securefile" });
InputStream inputStream = proc.getInputStream();
// Use standard I/O mechanisms to read the input.
}
catch (UnauthorizedCancellation e)
{
// User chose not to authorize the application.
// Handle appropriately.
}
The auth.authorize() call will cause the standard "Please enter your password to allow program X to make changes" dialog. The user can cancel if desired, causing glguerin.authkit.UnauthorizedCancellation to be thrown.
This solution has a huge advantage over using sudo or setuid: it only runs the necessary tasks as root.
One last gotcha: the default JNI loader for AuthKit uses the Cocoa/Java bridge, which was removed from Mac OS X as of Snow Leopard. So on recent versions of Mac OS X, the code above will fail with UnsatisfiedLinkError. To work around this, use the following:
// Put this class somewhere:
public class AuthKitLibLoader extends LibLoader
{
#Override
protected File makeFallbackDir()
{
return new File(".");
}
}
// Then, before calling AuthKit (using the above example), do this:
// Hook in our "Snow Leopard-safe" extension to AuthKit (see below).
System.setProperty("glguerin.util.LibLoader.imp", AuthKitLibLoader.class.getName());
Finally, be sure to read the AuthKit documentation for more detail.
If you run the application as the root user, the application will have full access to everything.
This is a dangerous operation however because it gives the application full privileges.
Another option would be to run it as a user that has the needed permissions to the files in question. This can be done by putting the user or the files in the appropriate group.
You probably need to SETUID the application to root.
> su
Enter password:
> chown root:wheel myJavaApp
> chmod ug+s myJavaApp
> exit
Now whenever someone in the wheel group runs myJavaApp, it will run as its owner (root). Just make sure you're in the wheel group (or whatever other group)
Alternatively, you could chmod a+s myJavaApp ... but that would let ANYONE AT ALL run the program as root. I would think carefully about that.
I want to grab a hardware ID with a java webapplet. Is this doable? If not, is there any web language I can do this with to help with a more secure authentication?
You can use any one approach as described below:
Use Signed Java Applet to load some JNI compatible shared library and get your job done.
public NativeHelloApplet() {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
System.load(System.getProperty("user.home") +"/libhello.so");
displayHelloWorld();
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
});
}
Use VB Script embedded in your web-page. Here is a sample:
Use the Win32_SystemEnclosure class, and the properties SerialNumber and SMBIOSAssetTag.
strComputer = "." Set objWMIService =
GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\"
_
& strComputer & "\root\cimv2") Set colSMBIOS = objWMIService.ExecQuery _
("Select * from Win32_SystemEnclosure") For Each
objSMBIOS in colSMBIOS
Wscript.Echo "Part Number: " & objSMBIOS.PartNumber
Wscript.Echo "Serial Number: " _
& objSMBIOS.SerialNumber
Wscript.Echo "Asset Tag: " _
& objSMBIOS.SMBIOSAssetTag Next
Design plug-in for every browser of your interest and collect data using them. MS uses this for Authenticated Software checking with FireFox.
Feel comfortable to let me know if you want to know more. That case, I shall write on my blog at http://puspendu.wordpress.com/
You can get a general idea of What Applets Can and Cannot Do, including access to certain system properties. In particular, unsigned applets "cannot load native libraries," which would probably be required for any kind of hardware identification.
If not, is there any web language I can do this with to help with a more secure authentication?
The issue is not strength of authentication. Rather it is whether a web language running in the user's browser should allow remote web services to access the user's hardware, files, etc. The answer is an emphatic "NO IT SHOULD NOT" ... unless the user implicitly grants permission by either installing a trusted plugin or a certificate.
I hava a Java program that needs to monitor a directory tree for changes. I have JNI code that uses ReadDirectoryChangesW(). The directory is opened like:
HANDLE dirHandle = CreateFile(
path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
);
and then I pass dirHandle to ReadDirectoryChangesW(). All of that works just fine.
The problem is that other parts of the code (on the Java side) use File.setLastModified() to "touch" files or directories (update their timestamps to be "now"). This generally works; however, it fails when it tried to "touch" the directory that was opened using CreateFile().
To see what Windows error is actually occurring, I looked at the JDK source for File.setLastModified() and reimplemented it in my own code with the addition of printing the error from GetLastError(); the error is:
ERROR_SHARING_VIOLATION (error 32)
"The process cannot access the file because it is being used by another process."
WTF? It's the same process. I even passed FILE_SHARE_READ and FILE_SHARE_WRITE to CreateFile().
Is there a way to make this work?
More Info
The native code implementation of File.setLastModified() in the JDK does a:
h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
If I change the first 0 to FILE_SHARE_READ | FILE_SHARE_WRITE, it all works. So it seems that the JDK implementation is a little broken. :(
So my question now becomes: Is there a way to make this work without having to use my own (re)implementation of File.setLastModified()?
Although the error message is a bit misleading in this case, what you're seeing is normal behaviour.
By opening the directory with dwShareMode set to zero, the JDK is, in effect, asking for exclusive access, which will cause any other access attempt to fail with a sharing violation error. This applies equally to accesses
from other processes and from within your own process.
The CreateFile documentation describes the dwShareMode parameter:
If this parameter is zero and CreateFile succeeds, the file or device cannot be shared and cannot be opened again until the handle to the file or device is closed.
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION.
So, it seems you've answered your own question: you need a custom setLastModified function that specifies FILE_SHARE_READ | FILE_SHARE_WRITE when accessing the directory.