|
| 1 | +{% if flag?(:win32) %} |
| 2 | + lib LibC |
| 3 | + struct LUID |
| 4 | + lowPart : DWORD |
| 5 | + highPart : Long |
| 6 | + end |
| 7 | + |
| 8 | + struct LUID_AND_ATTRIBUTES |
| 9 | + luid : LUID |
| 10 | + attributes : DWORD |
| 11 | + end |
| 12 | + |
| 13 | + struct TOKEN_PRIVILEGES |
| 14 | + privilegeCount : DWORD |
| 15 | + privileges : LUID_AND_ATTRIBUTES[1] |
| 16 | + end |
| 17 | + |
| 18 | + TOKEN_QUERY = 0x0008 |
| 19 | + TOKEN_ADJUST_PRIVILEGES = 0x0020 |
| 20 | + |
| 21 | + TokenPrivileges = 3 |
| 22 | + |
| 23 | + SE_PRIVILEGE_ENABLED = 0x00000002_u32 |
| 24 | + |
| 25 | + fun OpenProcessToken(processHandle : HANDLE, desiredAccess : DWORD, tokenHandle : HANDLE*) : BOOL |
| 26 | + fun GetTokenInformation(tokenHandle : HANDLE, tokenInformationClass : Int, tokenInformation : Void*, tokenInformationLength : DWORD, returnLength : DWORD*) : BOOL |
| 27 | + fun LookupPrivilegeValueW(lpSystemName : LPWSTR, lpName : LPWSTR, lpLuid : LUID*) : BOOL |
| 28 | + fun AdjustTokenPrivileges(tokenHandle : HANDLE, disableAllPrivileges : BOOL, newState : TOKEN_PRIVILEGES*, bufferLength : DWORD, previousState : TOKEN_PRIVILEGES*, returnLength : DWORD*) : BOOL |
| 29 | + end |
| 30 | +{% end %} |
| 31 | + |
1 | 32 | module Shards::Helpers
|
2 | 33 | def self.rm_rf(path : String) : Nil
|
3 | 34 | # TODO: delete this and use https://github.com/crystal-lang/crystal/pull/9903
|
@@ -32,4 +63,64 @@ module Shards::Helpers
|
32 | 63 | name
|
33 | 64 | {% end %}
|
34 | 65 | end
|
| 66 | + |
| 67 | + def self.privilege_enabled?(privilege_name : String) : Bool |
| 68 | + {% if flag?(:win32) %} |
| 69 | + if LibC.LookupPrivilegeValueW(nil, privilege_name.to_utf16, out privilege_luid) == 0 |
| 70 | + return false |
| 71 | + end |
| 72 | + |
| 73 | + # if the process token already has the privilege, and the privilege is already enabled, |
| 74 | + # we don't need to do anything else |
| 75 | + if LibC.OpenProcessToken(LibC.GetCurrentProcess, LibC::TOKEN_QUERY, out token) != 0 |
| 76 | + begin |
| 77 | + LibC.GetTokenInformation(token, LibC::TokenPrivileges, nil, 0, out len) |
| 78 | + buf = Pointer(UInt8).malloc(len).as(LibC::TOKEN_PRIVILEGES*) |
| 79 | + LibC.GetTokenInformation(token, LibC::TokenPrivileges, buf, len, out _) |
| 80 | + privileges = Slice.new(pointerof(buf.value.@privileges).as(LibC::LUID_AND_ATTRIBUTES*), buf.value.privilegeCount) |
| 81 | + # if the process token doesn't have the privilege, there is no way |
| 82 | + # `AdjustTokenPrivileges` could grant or enable it |
| 83 | + privilege = privileges.find(&.luid.== privilege_luid) |
| 84 | + return false unless privilege |
| 85 | + return true if privilege.attributes.bits_set?(LibC::SE_PRIVILEGE_ENABLED) |
| 86 | + ensure |
| 87 | + LibC.CloseHandle(token) |
| 88 | + end |
| 89 | + end |
| 90 | + |
| 91 | + if LibC.OpenProcessToken(LibC.GetCurrentProcess, LibC::TOKEN_ADJUST_PRIVILEGES, out adjust_token) != 0 |
| 92 | + new_privileges = LibC::TOKEN_PRIVILEGES.new( |
| 93 | + privilegeCount: 1, |
| 94 | + privileges: StaticArray[ |
| 95 | + LibC::LUID_AND_ATTRIBUTES.new( |
| 96 | + luid: privilege_luid, |
| 97 | + attributes: LibC::SE_PRIVILEGE_ENABLED, |
| 98 | + ), |
| 99 | + ], |
| 100 | + ) |
| 101 | + if LibC.AdjustTokenPrivileges(adjust_token, 0, pointerof(new_privileges), 0, nil, nil) != 0 |
| 102 | + return true if WinError.value.error_success? |
| 103 | + end |
| 104 | + end |
| 105 | + |
| 106 | + false |
| 107 | + {% else %} |
| 108 | + raise NotImplementedError.new("Shards::Helpers.privilege_enabled?") |
| 109 | + {% end %} |
| 110 | + end |
| 111 | + |
| 112 | + def self.developer_mode? : Bool |
| 113 | + {% if flag?(:win32) %} |
| 114 | + key = %q(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock).to_utf16 |
| 115 | + !!Crystal::System::WindowsRegistry.open?(LibC::HKEY_LOCAL_MACHINE, key) do |handle| |
| 116 | + value = uninitialized LibC::DWORD |
| 117 | + name = "AllowDevelopmentWithoutDevLicense".to_utf16 |
| 118 | + bytes = Slice.new(pointerof(value), 1).to_unsafe_bytes |
| 119 | + type, len = Crystal::System::WindowsRegistry.get_raw(handle, name, bytes) || return false |
| 120 | + return type.dword? && len == sizeof(typeof(value)) && value != 0 |
| 121 | + end |
| 122 | + {% else %} |
| 123 | + raise NotImplementedError.new("Shards::Helpers.developer_mode?") |
| 124 | + {% end %} |
| 125 | + end |
35 | 126 | end
|
0 commit comments