| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | |
| 6 | // Exit macros |
| 7 | #define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 8 | #define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 9 | #define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 10 | #define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 11 | #define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 12 | #define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) |
| 13 | #define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) |
| 14 | #define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) |
| 15 | #define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) |
| 16 | #define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) |
| 17 | #define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) |
| 18 | #define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) |
| 19 | #define OsExitOnPathFailure(x, b, s, ...) ExitOnPathFailureSource(DUTIL_SOURCE_OSUTIL, x, b, s, __VA_ARGS__) |
| 20 | |
| 21 | typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); |
| 22 | |
| 23 | OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; |
| 24 | DWORD vdwOsServicePack = 0; |
| 25 | RTL_OSVERSIONINFOEXW vovix = { }; |
| 26 | |
| 27 | /******************************************************************** |
| 28 | OsGetVersion |
| 29 | |
| 30 | ********************************************************************/ |
| 31 | extern "C" void DAPI OsGetVersion( |
| 32 | __out OS_VERSION* pVersion, |
| 33 | __out DWORD* pdwServicePack |
| 34 | ) |
| 35 | { |
| 36 | OSVERSIONINFOEXW ovi = { }; |
| 37 | |
| 38 | if (OS_VERSION_UNKNOWN == vOsVersion) |
| 39 | { |
| 40 | ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); |
| 41 | |
| 42 | #pragma warning (push) |
| 43 | #pragma warning(suppress: 4996) // deprecated |
| 44 | #pragma warning (push) |
| 45 | #pragma warning(suppress: 28159)// deprecated, use other function instead |
| 46 | ::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&ovi)); // only fails if version info size is set incorrectly. |
| 47 | #pragma warning (pop) |
| 48 | #pragma warning (pop) |
| 49 | |
| 50 | vdwOsServicePack = static_cast<DWORD>(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; |
| 51 | if (4 == ovi.dwMajorVersion) |
| 52 | { |
| 53 | vOsVersion = OS_VERSION_WINNT; |
| 54 | } |
| 55 | else if (5 == ovi.dwMajorVersion) |
| 56 | { |
| 57 | if (0 == ovi.dwMinorVersion) |
| 58 | { |
| 59 | vOsVersion = OS_VERSION_WIN2000; |
| 60 | } |
| 61 | else if (1 == ovi.dwMinorVersion) |
| 62 | { |
| 63 | vOsVersion = OS_VERSION_WINXP; |
| 64 | } |
| 65 | else if (2 == ovi.dwMinorVersion) |
| 66 | { |
| 67 | vOsVersion = OS_VERSION_WIN2003; |
| 68 | } |
| 69 | else |
| 70 | { |
| 71 | vOsVersion = OS_VERSION_FUTURE; |
| 72 | } |
| 73 | } |
| 74 | else if (6 == ovi.dwMajorVersion) |
| 75 | { |
| 76 | if (0 == ovi.dwMinorVersion) |
| 77 | { |
| 78 | vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; |
| 79 | } |
| 80 | else if (1 == ovi.dwMinorVersion) |
| 81 | { |
| 82 | vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; |
| 83 | } |
| 84 | else |
| 85 | { |
| 86 | vOsVersion = OS_VERSION_FUTURE; |
| 87 | } |
| 88 | } |
| 89 | else |
| 90 | { |
| 91 | vOsVersion = OS_VERSION_FUTURE; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | *pVersion = vOsVersion; |
| 96 | *pdwServicePack = vdwOsServicePack; |
| 97 | } |
| 98 | |
| 99 | extern "C" HRESULT DAPI OsCouldRunPrivileged( |
| 100 | __out BOOL* pfPrivileged |
| 101 | ) |
| 102 | { |
| 103 | HRESULT hr = S_OK; |
| 104 | BOOL fUacEnabled = FALSE; |
| 105 | SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; |
| 106 | PSID AdministratorsGroup = NULL; |
| 107 | |
| 108 | // Do a best effort check to see if UAC is enabled on this machine. |
| 109 | OsIsUacEnabled(&fUacEnabled); |
| 110 | |
| 111 | // If UAC is enabled then the process could run privileged by asking to elevate. |
| 112 | if (fUacEnabled) |
| 113 | { |
| 114 | *pfPrivileged = TRUE; |
| 115 | } |
| 116 | else // no UAC so only privilged if user is in administrators group. |
| 117 | { |
| 118 | *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); |
| 119 | if (*pfPrivileged) |
| 120 | { |
| 121 | if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) |
| 122 | { |
| 123 | *pfPrivileged = FALSE; |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | ReleaseSid(AdministratorsGroup); |
| 129 | return hr; |
| 130 | } |
| 131 | |
| 132 | extern "C" HRESULT DAPI OsIsRunningPrivileged( |
| 133 | __out BOOL* pfPrivileged |
| 134 | ) |
| 135 | { |
| 136 | HRESULT hr = S_OK; |
| 137 | UINT er = ERROR_SUCCESS; |
| 138 | HANDLE hToken = NULL; |
| 139 | TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; |
| 140 | DWORD dwSize = 0; |
| 141 | SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; |
| 142 | PSID AdministratorsGroup = NULL; |
| 143 | |
| 144 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) |
| 145 | { |
| 146 | OsExitOnLastError(hr, "Failed to open process token."); |
| 147 | } |
| 148 | |
| 149 | if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) |
| 150 | { |
| 151 | *pfPrivileged = (TokenElevationTypeFull == elevationType); |
| 152 | ExitFunction1(hr = S_OK); |
| 153 | } |
| 154 | |
| 155 | // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check |
| 156 | er = ::GetLastError(); |
| 157 | if (ERROR_INVALID_FUNCTION == er) |
| 158 | { |
| 159 | er = ERROR_SUCCESS; |
| 160 | } |
| 161 | OsExitOnWin32Error(er, hr, "Failed to get process token information."); |
| 162 | |
| 163 | // Fallback to this check for some OS's (like XP) |
| 164 | *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); |
| 165 | if (*pfPrivileged) |
| 166 | { |
| 167 | if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) |
| 168 | { |
| 169 | *pfPrivileged = FALSE; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | LExit: |
| 174 | ReleaseSid(AdministratorsGroup); |
| 175 | |
| 176 | if (hToken) |
| 177 | { |
| 178 | ::CloseHandle(hToken); |
| 179 | } |
| 180 | |
| 181 | return hr; |
| 182 | } |
| 183 | |
| 184 | extern "C" HRESULT DAPI OsIsUacEnabled( |
| 185 | __out BOOL* pfUacEnabled |
| 186 | ) |
| 187 | { |
| 188 | HRESULT hr = S_OK; |
| 189 | HKEY hk = NULL; |
| 190 | BOOL fExists = FALSE; |
| 191 | DWORD dwUacEnabled = 0; |
| 192 | |
| 193 | *pfUacEnabled = FALSE; // assume UAC not enabled. |
| 194 | |
| 195 | hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); |
| 196 | OsExitOnPathFailure(hr, fExists, "Failed to open system policy key to detect UAC."); |
| 197 | |
| 198 | if (!fExists) |
| 199 | { |
| 200 | ExitFunction(); |
| 201 | } |
| 202 | |
| 203 | hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); |
| 204 | OsExitOnPathFailure(hr, fExists, "Failed to read registry value to detect UAC."); |
| 205 | |
| 206 | if (!fExists) |
| 207 | { |
| 208 | ExitFunction(); |
| 209 | } |
| 210 | |
| 211 | *pfUacEnabled = (0 != dwUacEnabled); |
| 212 | |
| 213 | LExit: |
| 214 | ReleaseRegKey(hk); |
| 215 | |
| 216 | return hr; |
| 217 | } |
| 218 | |
| 219 | HRESULT DAPI OsRtlGetVersion( |
| 220 | __inout RTL_OSVERSIONINFOEXW* pOvix |
| 221 | ) |
| 222 | { |
| 223 | HRESULT hr = S_OK; |
| 224 | HMODULE hNtdll = NULL; |
| 225 | PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; |
| 226 | |
| 227 | if (vovix.dwOSVersionInfoSize) |
| 228 | { |
| 229 | ExitFunction(); |
| 230 | } |
| 231 | |
| 232 | vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); |
| 233 | |
| 234 | hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); |
| 235 | if (E_MODNOTFOUND == hr) |
| 236 | { |
| 237 | OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); |
| 238 | } |
| 239 | OsExitOnFailure(hr, "Failed to load ntdll.dll."); |
| 240 | |
| 241 | pfnRtlGetVersion = reinterpret_cast<PFN_RTL_GET_VERSION>(::GetProcAddress(hNtdll, "RtlGetVersion")); |
| 242 | OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); |
| 243 | |
| 244 | hr = static_cast<HRESULT>(pfnRtlGetVersion(&vovix)); |
| 245 | |
| 246 | LExit: |
| 247 | memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); |
| 248 | |
| 249 | if (hNtdll) |
| 250 | { |
| 251 | ::FreeLibrary(hNtdll); |
| 252 | } |
| 253 | |
| 254 | return hr; |
| 255 | } |