📜 ⬆️ ⬇️

Running code under another user in Windows from Java

Good day! Now I will tell you how to run the code under the account of another user in Windows from Java using JNA.

Practical value


In general, this may be necessary when you need to access resources that are available to any one user, and the user under which the current thread is running does not have permissions to access these resources. But there is a login, domain and password of the desired user.
For example - there is a server that allows you to give files to the client from certain folders. Access to folders is restricted at the level of NTFS access rights. And the server itself does not have access to these folders (which is correct - the server is started under a separate account, which has access only to the server directory and the temp folder). But the client knows the credentials of users who are allowed access to these folders. Question for small - log in under the desired user and access.

Theory


First you need to know that in order to take advantage of the rights of the user, you need to start the process or stream under the credentials of the desired user, and then from this thread / process to access the necessary resources. MSDN and forums advise us the following:
1) CreateProcessWithLogonW - immediately launches the specified application with the necessary credentials. The easiest option and the most optimal is that in order to execute a couple of three lines of code we will start the whole application.
2) A bunch of functions - LogonUser, CreateThread, SetThreadToken, ResumeThread. In the first function, we login to the system under the specified credentials and get a token. In the second function, create a sleep thread. The third indicates the token for the created thread. In the fourth run the created sleep stream.
Let's stop on the second option. Although it requires some additional lines of code, it is the most optimal.

Implementation


First you need to choose with which we will run the native code from Java. I know two ways - JNI and JNA. The first is “native” for Java and the fastest (according to some authors). But it requires writing an additional native library with a description of the functions being called. And for this you need some outside body movements. Moreover, writing a native code, albeit a simple one, is not a trivial task for a Java programmer. In the second case, you only need to describe the called functions in the interface and everything can be used. Especially since many types and functions of Windows in JNA are already described.
To use JNA, we need jna.jar itself, which contains the main JNA code and platform.jar, which describes some functions and types for different platforms - Mac, Win, * nix.
We now turn to coding. First, we describe the called functions — we create two interfaces — MoreKernel32, which inherits the Kernel32 interface from platform.jar, and MoreAdvapi32, which inherits Advapi32 from the same jar.
Let's write in the following methods in these interfaces:
public interface MoreAdvapi32 extends Advapi32 { static final MoreAdvapi32 instance = (MoreAdvapi32) Native.loadLibrary("advapi32", MoreAdvapi32.class, W32APIOptions.DEFAULT_OPTIONS); Boolean SetThreadToken(HANDLEByReference pointer, HANDLE Token); } 

')
 public interface MoreKernel32 extends Kernel32 { static final MoreKernel32 instance = (MoreKernel32) Native.loadLibrary("kernel32", MoreKernel32.class,W32APIOptions.DEFAULT_OPTIONS); DWORD ResumeThread(WinNT.HANDLE hThread); WinNT.HANDLE CreateThread(Pointer lpThreadAttributes, Pointer dwStackSize, Callback lpStartAddress, Pointer lpParameter, DWORD dwCreationFlags, Pointer lpThreadId); } 


The LogonUser method is already in the Advapi32 interface, it is not necessary to describe it.
A few words about how JNA learns what functions to call on the native library. To do this, you need to describe the interface with these functions so that their names and parameters match. Then you need to call Native.loadLibrary with the following parameters - the name of the loaded library, the name of the interface to which the library will be mapped, and the loading options. As a result, we will get a link to the interface from which it will be possible to use these functions.
Now I will tell you in more detail what we have done:
1) Described the functions in the interfaces.
2) We loaded the kernel32 and advapi32 libraries, and zamapili them on the created interfaces by calling the Native.loadLibrary function. The link to the loaded libraries was saved in the instance variable in each of the interfaces.
Now consider two classes. The first class ThreadFunc is designed to run in a user thread. The code is very simple - here you just open the file for reading and check for the availability of the file. The most important thing is that the class implements the Callback interface, and the callback () function was created in it. This will allow JNA to determine if the class is intended to be called. If you look at the Callback interface code, you can see that it describes the name of the function being called and describes the name of the functions that will be ignored.
 package jna; import java.io.FileInputStream; import java.io.FileNotFoundException; import com.sun.jna.Callback; public class ThreadFunc implements Callback { private String fileName; private FileInputStream in; public FileInputStream getIn() { return in; } public String getFile() { return fileName; } public void setFile(String file) { this.fileName = file; } public void callback() { File testAccess = new File(fileName); if (testAccess.canRead()) { System.out.println("     "); try { in = new FileInputStream(fileName); } catch (FileNotFoundException e) { e.printStackTrace(); } }else{ System.out.println("     "); } } } 

The second class of JnaTest is basic - a stream is created here, a user logs into the system, a token is set up, and the most important thing for which all this hastily happened is reading a file.
 package jna; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; import com.sun.jna.platform.win32.Advapi32; import com.sun.jna.platform.win32.WinBase; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; public class JnaTest { private static final String fileName = "D:\\user1\\hitman.jpg"; public static void main(String[] args) { FileInputStream in = null; FileOutputStream out = null; FileChannel fcin = null; FileChannel fcout = null; try { File testAccess = new File(fileName); if (testAccess.canRead()) { System.out.println("     "); }else{ System.out.println("     "); } //  // CREATE_SUSPENDED DWORD flag = new DWORD(4L); ThreadFunc func = new ThreadFunc(); func.setFile(fileName); HANDLE hThread = MoreKernel32.instance.CreateThread(null, null, func, null, flag, null); if (hThread == null) { System.out.println("  "); return; } //    HANDLEByReference phToken = new HANDLEByReference(); Boolean logonResult = Advapi32.INSTANCE.LogonUser("user1", "HORROR", "1234567q-",WinBase.LOGON32_LOGON_NETWORK, WinBase.LOGON32_PROVIDER_DEFAULT, phToken); if (logonResult == false) { System.out.println("  "); return; } //   HANDLEByReference pThread = new HANDLEByReference(hThread); Boolean setTokenResult = MoreAdvapi32.instance.SetThreadToken(pThread, phToken.getValue()); if (setTokenResult == false) { System.out.println("  "); return; } //     MoreKernel32.instance.ResumeThread(hThread); MoreKernel32.instance.WaitForSingleObject(hThread, -1); MoreKernel32.instance.CloseHandle(hThread); MoreKernel32.instance.CloseHandle(phToken.getValue()); //  in = func.getIn(); if (in == null) { return; } out = new FileOutputStream("D:\\test.zip"); fcin = in.getChannel(); fcout = out.getChannel(); fcin.transferTo(0, fcin.size(), fcout); } catch (Exception e) { e.printStackTrace(); } finally { //  if (fcin != null) { try { fcin.close(); } catch (IOException e) { e.printStackTrace(); } } if (fcout != null) { try { fcout.close(); } catch (IOException e) { e.printStackTrace(); } } if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 

Consider in more detail this code.
First, create a class for the stream and specify which file to open. Then we execute the CreateThread function with two parameters — a reference to the class for opening the file and the creation flag CREATE_SUSPENDED — indicating to the system that the stream should be created “asleep”. After execution, we get the handle of the thread. Then we check if the handle is null, then the thread is not created and we need to complete the work.
The second block of code is designed to perform the login. Here, before launching the LogonUser function, you need to create a link to the handle, which will then be written to the user's token. We specify the login, the domain, the password, the memory pointer where the token and additional parameters of the user’s login will be written as parameters to start the function. As a result of executing the function, we get either true and the user's token or false. Next comes the check for the return value and, if it is false, exit from the program.
The next block is quite simple - there we start the thread, wait until it finishes, and close the tokens.
Reading a file is also not difficult - we get file channels from FileInputStream and FileOutputStream and copy the file.
Resource closing is standard in Java - in the finally block.
Now a few comments:
1) When creating a stream, you need to keep a reference to the class that will be called in the stream, for the entire life of the stream. Even if external access to this class is not needed. Otherwise, the class may be deleted by the garbage collector and there will be an attempt to access memory filled with incorrect data. Actually, this is written on the JNA website, but I learned that later, when I caught several Access error.
2) If you open a resource and transfer it to another thread, the access rights are saved. Here we can observe this - the file is open in a stream with the access rights of one user, and the file itself is read in another stream, with other rights.

Execution Example


Now check the program execution. First create a folder and file.


Then assign permissions to this folder:


Fig.1 Folder user1


Fig.2 Allow access for user user1


Fig.3 Allowing access for user Tim

Run the program:
The file can be read from the main stream.
The file can be read from the additional stream.

Now we deny the user Tim access to the folder.


Fig. 4 Denying rights to access the folder for the user Tim

Let's try to open a folder in Explorer. We get the error message:


Fig. 5 Error message when opening a folder

Now run the program:
The file cannot be read from the main stream.
The file can be read from the additional stream.

Links to resources


Jna

Logonuser

CreateThread

ResumeThread

SetThreadToken

Source: https://habr.com/ru/post/123945/


All Articles