|
41 | 41 | import static com.sun.jna.platform.win32.WinNT.SE_DACL_PROTECTED;
|
42 | 42 | import static com.sun.jna.platform.win32.WinNT.SE_SACL_PROTECTED;
|
43 | 43 | import static com.sun.jna.platform.win32.WinNT.STANDARD_RIGHTS_READ;
|
| 44 | +import static com.sun.jna.platform.win32.WinNT.TOKEN_ADJUST_PRIVILEGES; |
44 | 45 | import static com.sun.jna.platform.win32.WinNT.TOKEN_DUPLICATE;
|
45 | 46 | import static com.sun.jna.platform.win32.WinNT.TOKEN_IMPERSONATE;
|
46 | 47 | import static com.sun.jna.platform.win32.WinNT.TOKEN_QUERY;
|
47 | 48 | import static com.sun.jna.platform.win32.WinNT.UNPROTECTED_DACL_SECURITY_INFORMATION;
|
48 | 49 | import static com.sun.jna.platform.win32.WinNT.UNPROTECTED_SACL_SECURITY_INFORMATION;
|
49 | 50 |
|
50 | 51 | import java.io.ByteArrayOutputStream;
|
| 52 | +import java.io.Closeable; |
51 | 53 | import java.io.File;
|
52 | 54 | import java.io.IOException;
|
53 | 55 | import java.util.ArrayList;
|
|
81 | 83 | import com.sun.jna.platform.win32.WinNT.SECURITY_IMPERSONATION_LEVEL;
|
82 | 84 | import com.sun.jna.platform.win32.WinNT.SID_AND_ATTRIBUTES;
|
83 | 85 | import com.sun.jna.platform.win32.WinNT.SID_NAME_USE;
|
| 86 | +import com.sun.jna.platform.win32.WinNT.TOKEN_TYPE; |
84 | 87 | import com.sun.jna.platform.win32.WinReg.HKEY;
|
85 | 88 | import com.sun.jna.platform.win32.WinReg.HKEYByReference;
|
86 | 89 | import com.sun.jna.ptr.IntByReference;
|
@@ -2636,4 +2639,207 @@ public DWORD callback(Pointer pbData, Pointer pvCallbackContext,
|
2636 | 2639 | // close
|
2637 | 2640 | Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue());
|
2638 | 2641 | }
|
| 2642 | + |
| 2643 | + /** |
| 2644 | + * Convenience class to enable certain Windows process privileges |
| 2645 | + */ |
| 2646 | + public static class Privilege implements Closeable { |
| 2647 | + /** |
| 2648 | + * If true, the thread is currently impersonating |
| 2649 | + */ |
| 2650 | + private boolean currentlyImpersonating = false; |
| 2651 | + |
| 2652 | + /** |
| 2653 | + * If true, the privileges have been enabled |
| 2654 | + */ |
| 2655 | + private boolean privilegesEnabled = false; |
| 2656 | + |
| 2657 | + /** |
| 2658 | + * LUID form of the privileges |
| 2659 | + */ |
| 2660 | + private final WinNT.LUID[] pLuids; |
| 2661 | + |
| 2662 | + /** |
| 2663 | + * Construct and enable a set of privileges |
| 2664 | + * @param privileges the names of the privileges in the form of SE_* from Advapi32.java |
| 2665 | + * @throws IllegalArgumentException |
| 2666 | + */ |
| 2667 | + public Privilege(String... privileges) throws IllegalArgumentException, Win32Exception { |
| 2668 | + pLuids = new WinNT.LUID[privileges.length]; |
| 2669 | + int i = 0; |
| 2670 | + for (String p : privileges) { |
| 2671 | + pLuids[i] = new WinNT.LUID(); |
| 2672 | + if (!Advapi32.INSTANCE.LookupPrivilegeValue(null, p, pLuids[i])) { |
| 2673 | + throw new IllegalArgumentException("Failed to find privilege \"" + privileges[i] + "\" - " + Kernel32.INSTANCE.GetLastError()); |
| 2674 | + } |
| 2675 | + i++; |
| 2676 | + } |
| 2677 | + } |
| 2678 | + |
| 2679 | + /** |
| 2680 | + * Calls disable() to remove the privileges |
| 2681 | + * @see java.io.Closeable#close() |
| 2682 | + */ |
| 2683 | + @Override |
| 2684 | + public void close() { |
| 2685 | + this.disable(); |
| 2686 | + } |
| 2687 | + |
| 2688 | + /** |
| 2689 | + * Enables the given privileges. If required, it will duplicate the process token. No resources are left open when this completes. That is, it is |
| 2690 | + * NOT required to drop the privileges, although it is considered a best practice if you do not need it. This class is state full. It keeps track |
| 2691 | + * of whether it has enabled the privileges. Multiple calls to enable() without a drop() in between have no affect. |
| 2692 | + * @return pointer to self (Privilege) as a convenience for try with resources statements |
| 2693 | + * @throws Win32Exception |
| 2694 | + */ |
| 2695 | + public Privilege enable() throws Win32Exception { |
| 2696 | + // Ignore if already enabled. |
| 2697 | + if (privilegesEnabled) |
| 2698 | + return this; |
| 2699 | + |
| 2700 | + // Get thread token |
| 2701 | + final HANDLEByReference phThreadToken = new HANDLEByReference(); |
| 2702 | + |
| 2703 | + try { |
| 2704 | + phThreadToken.setValue(getThreadToken()); |
| 2705 | + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); |
| 2706 | + for (int i = 0; i < pLuids.length; i++) { |
| 2707 | + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); |
| 2708 | + } |
| 2709 | + if (!Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null)) { |
| 2710 | + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); |
| 2711 | + } |
| 2712 | + privilegesEnabled = true; |
| 2713 | + } |
| 2714 | + catch (Win32Exception ex) { |
| 2715 | + // If fails, clean up |
| 2716 | + if (currentlyImpersonating) { |
| 2717 | + Advapi32.INSTANCE.SetThreadToken(null, null); |
| 2718 | + currentlyImpersonating = false; |
| 2719 | + } |
| 2720 | + else { |
| 2721 | + if (privilegesEnabled) { |
| 2722 | + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); |
| 2723 | + for (int i = 0; i < pLuids.length; i++) { |
| 2724 | + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(0)); |
| 2725 | + } |
| 2726 | + Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null); |
| 2727 | + privilegesEnabled = false; |
| 2728 | + } |
| 2729 | + } |
| 2730 | + throw ex; |
| 2731 | + } |
| 2732 | + finally { |
| 2733 | + // Always close the thread token |
| 2734 | + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) |
| 2735 | + && (phThreadToken.getValue() != null)) { |
| 2736 | + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); |
| 2737 | + phThreadToken.setValue(null); |
| 2738 | + } |
| 2739 | + } |
| 2740 | + return this; |
| 2741 | + } |
| 2742 | + |
| 2743 | + /** |
| 2744 | + * Disabled the prior enabled privilege |
| 2745 | + * @throws Win32Exception |
| 2746 | + */ |
| 2747 | + public void disable() throws Win32Exception { |
| 2748 | + // Get thread token |
| 2749 | + final HANDLEByReference phThreadToken = new HANDLEByReference(); |
| 2750 | + |
| 2751 | + try { |
| 2752 | + phThreadToken.setValue(getThreadToken()); |
| 2753 | + if (currentlyImpersonating) { |
| 2754 | + Advapi32.INSTANCE.SetThreadToken(null, null); |
| 2755 | + } |
| 2756 | + else |
| 2757 | + { |
| 2758 | + if (privilegesEnabled) { |
| 2759 | + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); |
| 2760 | + for (int i = 0; i < pLuids.length; i++) { |
| 2761 | + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(0)); |
| 2762 | + } |
| 2763 | + Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null); |
| 2764 | + privilegesEnabled = false; |
| 2765 | + } |
| 2766 | + } |
| 2767 | + } |
| 2768 | + finally { |
| 2769 | + // Close the thread token |
| 2770 | + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) |
| 2771 | + && (phThreadToken.getValue() != null)) { |
| 2772 | + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); |
| 2773 | + phThreadToken.setValue(null); |
| 2774 | + } |
| 2775 | + } |
| 2776 | + } |
| 2777 | + |
| 2778 | + /** |
| 2779 | + * Get a handle to the thread token. May duplicate the process token |
| 2780 | + * and set as the thread token if ther thread has no token. |
| 2781 | + * @return HANDLE to the thread token |
| 2782 | + * @throws Win32Exception |
| 2783 | + */ |
| 2784 | + private HANDLE getThreadToken() throws Win32Exception { |
| 2785 | + // we need to create a new token here for the duplicate |
| 2786 | + final HANDLEByReference phThreadToken = new HANDLEByReference(); |
| 2787 | + final HANDLEByReference phProcessToken = new HANDLEByReference(); |
| 2788 | + |
| 2789 | + try { |
| 2790 | + // open thread token |
| 2791 | + if (!Advapi32.INSTANCE.OpenThreadToken(Kernel32.INSTANCE.GetCurrentThread(), |
| 2792 | + TOKEN_ADJUST_PRIVILEGES, |
| 2793 | + false, |
| 2794 | + phThreadToken)) { |
| 2795 | + // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous. Check for that condition here. If not, throw an error. |
| 2796 | + int lastError = Kernel32.INSTANCE.GetLastError(); |
| 2797 | + if (W32Errors.ERROR_NO_TOKEN != lastError) { |
| 2798 | + throw new Win32Exception(lastError); |
| 2799 | + } |
| 2800 | + |
| 2801 | + // Due to ERROR_NO_TOKEN, we need to open the process token to duplicate it, then set our thread token. |
| 2802 | + if (!Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phProcessToken)) { |
| 2803 | + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); |
| 2804 | + } |
| 2805 | + |
| 2806 | + // Process token opened, now duplicate |
| 2807 | + if (!Advapi32.INSTANCE.DuplicateTokenEx(phProcessToken.getValue(), |
| 2808 | + TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE, |
| 2809 | + null, |
| 2810 | + SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, |
| 2811 | + TOKEN_TYPE.TokenImpersonation, |
| 2812 | + phThreadToken)) { |
| 2813 | + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); |
| 2814 | + } |
| 2815 | + |
| 2816 | + // And set thread token. |
| 2817 | + if (!Advapi32.INSTANCE.SetThreadToken(null, phThreadToken.getValue())) { |
| 2818 | + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); |
| 2819 | + } |
| 2820 | + currentlyImpersonating = true; |
| 2821 | + } |
| 2822 | + } |
| 2823 | + catch (Win32Exception ex) { |
| 2824 | + // Close the thread token |
| 2825 | + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) |
| 2826 | + && (phThreadToken.getValue() != null)) { |
| 2827 | + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); |
| 2828 | + phThreadToken.setValue(null); |
| 2829 | + } |
| 2830 | + throw ex; |
| 2831 | + } |
| 2832 | + finally |
| 2833 | + { |
| 2834 | + // Always close the process token |
| 2835 | + if ((phProcessToken.getValue() != WinBase.INVALID_HANDLE_VALUE) |
| 2836 | + && (phProcessToken.getValue() != null)) { |
| 2837 | + Kernel32.INSTANCE.CloseHandle(phProcessToken.getValue()); |
| 2838 | + phProcessToken.setValue(null); |
| 2839 | + } |
| 2840 | + } |
| 2841 | + |
| 2842 | + return phThreadToken.getValue(); |
| 2843 | + } |
| 2844 | + } |
2639 | 2845 | }
|
0 commit comments