diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2016-09-16 01:47:56 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2016-09-16 01:47:56 +0300 |
commit | fbd6445c68745d484d5df8c75b0c12054185958f (patch) | |
tree | c8eb6da6b89d88f530647f408fd2c47669e1bb27 /src | |
parent | .hpp instead of .h for C++ headers (diff) | |
download | privilege-check-fbd6445c68745d484d5df8c75b0c12054185958f.tar.gz privilege-check-fbd6445c68745d484d5df8c75b0c12054185958f.zip |
move source files to src/
Diffstat (limited to 'src')
-rw-r--r-- | src/error.hpp | 26 | ||||
-rw-r--r-- | src/handle.hpp | 64 | ||||
-rw-r--r-- | src/main.cpp | 210 | ||||
-rw-r--r-- | src/main.rc | 112 | ||||
-rw-r--r-- | src/os.hpp | 30 | ||||
-rw-r--r-- | src/process.hpp | 40 | ||||
-rw-r--r-- | src/resource_ids.h | 23 | ||||
-rw-r--r-- | src/sid.hpp | 68 | ||||
-rw-r--r-- | src/token.hpp | 175 |
9 files changed, 748 insertions, 0 deletions
diff --git a/src/error.hpp b/src/error.hpp new file mode 100644 index 0000000..2ea7f85 --- /dev/null +++ b/src/error.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <Windows.h> + +#include <system_error> + +typedef std::system_error Error; + +namespace error +{ + inline void raise(const char* function_name) + { + const auto ec = GetLastError(); + throw std::system_error(ec, std::system_category(), function_name); + } + + void report(const Error& e) + { + MessageBoxA(NULL, e.what(), NULL, MB_OK); + } + + int get_code(const Error& e) + { + return e.code().value(); + } +} diff --git a/src/handle.hpp b/src/handle.hpp new file mode 100644 index 0000000..5f83f24 --- /dev/null +++ b/src/handle.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include <Windows.h> + +#include <cassert> + +#include <memory> +#include <utility> + +class Handle +{ +public: + Handle() = default; + + explicit Handle(HANDLE raw) + : impl{raw} + { } + + Handle(Handle&& other) noexcept + { + swap(other); + } + + Handle& operator=(Handle other) noexcept + { + swap(other); + return *this; + } + + void swap(Handle& other) noexcept + { + using std::swap; + swap(impl, other.impl); + } + + operator HANDLE() const + { + return impl.get(); + } + +private: + struct Close + { + void operator()(HANDLE raw) const + { + if (raw == NULL || raw == INVALID_HANDLE_VALUE) + return; + const auto ret = CloseHandle(raw); + assert(ret); + } + }; + + std::unique_ptr<void, Close> impl; + + Handle(const Handle&) = delete; +}; + +namespace std +{ + void swap(Handle& a, Handle& b) noexcept + { + a.swap(b); + } +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..1777d79 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,210 @@ +#include "error.hpp" +#include "os.hpp" +#include "process.hpp" +#include "resource_ids.h" +#include "sid.hpp" +#include "token.hpp" + +#include <Windows.h> +#include <CommCtrl.h> +#include <windowsx.h> + +#include <string> + +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +bool is_user_in_administrators() +{ + const auto token = token::impersonate( + token::get_linked( + token::open_for_current_process(token::permissions::query() | token::permissions::duplicate()))); + + return token::belongs(token, sid::builtin_administrators()); +} + +bool is_run_as_administrator() +{ + return token::belongs(token::dumb(), sid::builtin_administrators()); +} + +bool is_elevated() +{ + return token::is_elevated(token::open_for_current_process()); +} + +void set_label(HWND root, int id, bool val) +{ + const auto label = GetDlgItem(root, id); + SetWindowTextW(label, val ? L"True" : L"False"); +} + +void set_label(HWND root, int id, const wchar_t* s) +{ + const auto label = GetDlgItem(root, id); + SetWindowTextW(label, s); +} + +void set_label(HWND root, int id, const std::wstring& s) +{ + const auto label = GetDlgItem(root, id); + SetWindowTextW(label, s.c_str()); +} + +BOOL on_init_dialog(HWND wnd, HWND, LPARAM) +{ + try + { + set_label(wnd, IDC_ADMINISTRATOR, is_user_in_administrators()); + } + catch (const Error& e) + { + set_label(wnd, IDC_ADMINISTRATOR, L"N/A"); + error::report(e); + } + + try + { + set_label(wnd, IDC_RUN_AS_ADMINISTRATOR, is_run_as_administrator()); + } + catch (const Error& e) + { + set_label(wnd, IDC_RUN_AS_ADMINISTRATOR, L"N/A"); + error::report(e); + } + + if (os::is_vista_or_later()) + { + try + { + const auto elevated = is_elevated(); + set_label(wnd, IDC_ELEVATED, elevated); + + const auto elevate_button = GetDlgItem(wnd, IDC_BUTTON_ELEVATE); + Button_SetElevationRequiredState(elevate_button, !elevated); + } + catch (const Error& e) + { + set_label(wnd, IDC_ELEVATED, L"N/A"); + error::report(e); + } + + try + { + set_label(wnd, IDC_INTEGRITY_LEVEL, token::integrity_level_to_string( + token::query_integrity_level(token::open_for_current_process()))); + } + catch (const Error& e) + { + set_label(wnd, IDC_INTEGRITY_LEVEL, L"N/A"); + error::report(e); + } + } + else + { + set_label(wnd, IDC_ELEVATED, L"N/A"); + set_label(wnd, IDC_INTEGRITY_LEVEL, L"N/A"); + } + + return TRUE; +} + +void on_button_elevate_click(HWND wnd) +{ + bool as_admin = false; + + try + { + as_admin = is_run_as_administrator(); + } + catch (const Error& e) + { + error::report(e); + return; + } + + if (as_admin) + { + MessageBoxW(wnd, L"Already elevated!", L"Elevation", MB_OK); + return; + } + + try + { + process::runas(process::get_executable_path()); + } + catch (const Error& e) + { + if (error::get_code(e) != ERROR_CANCELLED) + error::report(e); + return; + } + + EndDialog(wnd, 1); +} + +void on_command(HWND wnd, int id, HWND, unsigned int) +{ + switch (id) + { + case IDC_BUTTON_ELEVATE: + on_button_elevate_click(wnd); + break; + + case IDOK: + case IDCANCEL: + EndDialog(wnd, 0); + break; + } +} + +void on_close(HWND wnd) +{ + EndDialog(wnd, 0); +} + +INT_PTR CALLBACK dialog_main( + HWND wnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + switch (msg) + { + HANDLE_MSG(wnd, WM_INITDIALOG, on_init_dialog); + HANDLE_MSG(wnd, WM_COMMAND, on_command); + HANDLE_MSG(wnd, WM_CLOSE, on_close); + + default: + return FALSE; + } +} + +int APIENTRY wWinMain( + HINSTANCE instance, + HINSTANCE, + wchar_t*, + int) +{ + const auto ret = DialogBoxW( + instance, + MAKEINTRESOURCE(IDD_MAINDIALOG), + NULL, + dialog_main); + + switch (ret) + { + case -1: + try + { + error::raise("DialogBoxW"); + } + catch (const Error& e) + { + error::report(e); + return 1; + } + + default: + return static_cast<int>(ret); + } +} diff --git a/src/main.rc b/src/main.rc new file mode 100644 index 0000000..70c04e5 --- /dev/null +++ b/src/main.rc @@ -0,0 +1,112 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource_ids.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAINDIALOG DIALOGEX 0, 0, 120, 79 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Privilege test" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Administrator?",IDC_STATIC,7,7,75,12 + RTEXT "",IDC_ADMINISTRATOR,85,7,28,12 + LTEXT "Run as administrator?",IDC_STATIC,7,19,75,12 + RTEXT "",IDC_RUN_AS_ADMINISTRATOR,85,19,28,12 + LTEXT "Elevated?",IDC_STATIC,7,31,75,12 + RTEXT "",IDC_ELEVATED,85,31,28,12 + LTEXT "Integrity level:",IDC_STATIC,7,43,75,12 + RTEXT "",IDC_INTEGRITY_LEVEL,85,43,28,15 + PUSHBUTTON "Elevate",IDC_BUTTON_ELEVATE,63,58,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_MAINDIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 113 + VERTGUIDE, 82 + VERTGUIDE, 85 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + HORZGUIDE, 19 + HORZGUIDE, 31 + HORZGUIDE, 43 + HORZGUIDE, 58 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource_ids.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/os.hpp b/src/os.hpp new file mode 100644 index 0000000..1a3c52d --- /dev/null +++ b/src/os.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "error.hpp" + +#include <Windows.h> + +namespace os +{ + OSVERSIONINFOW get_version_info() + { + OSVERSIONINFOW info; + ZeroMemory(&info, sizeof(info)); + info.dwOSVersionInfoSize = sizeof(info); + + if (!GetVersionExW(&info)) + error::raise("GetVersionExW"); + + return info; + } + + bool is_vista_or_later(const OSVERSIONINFOW& info) + { + return info.dwMajorVersion >= 6; + } + + bool is_vista_or_later() + { + return is_vista_or_later(get_version_info()); + } +} diff --git a/src/process.hpp b/src/process.hpp new file mode 100644 index 0000000..864d452 --- /dev/null +++ b/src/process.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "error.hpp" + +#include <Windows.h> +#include <shellapi.h> + +#include <array> +#include <string> + +namespace process +{ + std::wstring get_executable_path() + { + static constexpr DWORD max_path = MAX_PATH; + + std::array<wchar_t, max_path> buf; + + const auto ret = GetModuleFileNameW(NULL, buf.data(), max_path); + + if (GetLastError() != ERROR_SUCCESS) + error::raise("GetModuleFileNameW"); + + return buf.data(); + } + + void runas(const std::wstring& exe_path, HWND hwnd = NULL, int nShow = SW_NORMAL) + { + SHELLEXECUTEINFOW info; + ZeroMemory(&info, sizeof(info)); + info.cbSize = sizeof(info); + info.lpVerb = L"runas"; + info.lpFile = exe_path.c_str(); + info.hwnd = hwnd; + info.nShow = nShow; + + if (!ShellExecuteExW(&info)) + error::raise("ShellExecuteExW"); + } +} diff --git a/src/resource_ids.h b/src/resource_ids.h new file mode 100644 index 0000000..4c6d90f --- /dev/null +++ b/src/resource_ids.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDD_MAINDIALOG 100 +#define IDC_BUTTON_ELEVATE 1000 +#define IDC_ADMINISTRATOR 1001 +#define IDC_RUN_AS_ADMINISTRATOR 1002 +#define IDC_ELEVATED 1003 +#define IDC_INTEGRITY_LEVEL 1004 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/src/sid.hpp b/src/sid.hpp new file mode 100644 index 0000000..15ce73b --- /dev/null +++ b/src/sid.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "error.hpp" + +#include <Windows.h> +#include <sddl.h> + +#include <array> +#include <memory> +#include <string> + +constexpr DWORD max_sid_size = SECURITY_MAX_SID_SIZE; +typedef std::array<unsigned char, max_sid_size> SidBuffer; + +namespace sid +{ + SidBuffer well_known(WELL_KNOWN_SID_TYPE type) + { + SidBuffer buffer; + DWORD cb = static_cast<DWORD>(buffer.size()); + + if (!CreateWellKnownSid(type, NULL, buffer.data(), &cb)) + error::raise("CreateWellKnownSid"); + + return buffer; + } + + SidBuffer builtin_administrators() + { + /* + void* sid = nullptr; + SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY; + + if (!AllocateAndInitializeSid( + &authority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &sid)) + { + error::raise("AllocateAndInitializeSid"); + } + + return std::unique_ptr<void, FreeSid>{sid}; + */ + + return well_known(WinBuiltinAdministratorsSid); + } + + struct DeleteSidString + { + void operator()(wchar_t* s) const + { + LocalFree(s); + } + }; + + std::wstring to_string(const SidBuffer& sid) + { + wchar_t* s = nullptr; + + if (!ConvertSidToStringSidW(const_cast<unsigned char*>(sid.data()), &s)) + error::raise("ConvertSidToStringSidW"); + + return std::unique_ptr<wchar_t, DeleteSidString>{s}.get(); + } +} diff --git a/src/token.hpp b/src/token.hpp new file mode 100644 index 0000000..5669970 --- /dev/null +++ b/src/token.hpp @@ -0,0 +1,175 @@ +#pragma once + +#include "error.hpp" +#include "handle.hpp" +#include "os.hpp" +#include "sid.hpp" + +#include <Windows.h> + +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace token +{ + namespace permissions + { + constexpr DWORD query() { return TOKEN_QUERY; } + + constexpr DWORD duplicate() { return TOKEN_DUPLICATE; } + } + + constexpr DWORD default_permissions = permissions::query(); + + // Current thread's primary impersonation token. + Handle dumb() { return Handle{NULL}; } + + Handle open_for_process( + const Handle& process, + DWORD permissions = default_permissions) + { + HANDLE raw; + + if (!OpenProcessToken(process, permissions, &raw)) + error::raise("OpenProcessToken"); + + return Handle{raw}; + } + + Handle open_for_current_process( + DWORD permissions = default_permissions) + { + return open_for_process(Handle{GetCurrentProcess()}, permissions); + } + + Handle get_linked(Handle&& token) + { + if (!os::is_vista_or_later()) + return std::move(token); + + auto type = TokenElevationTypeDefault; + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenElevationType, &type, sizeof(type), &cb)) + error::raise("GetTokenInformation"); + + if (type != TokenElevationTypeLimited) + return std::move(token); + + HANDLE raw; + + if (!GetTokenInformation(token, TokenLinkedToken, &raw, sizeof(raw), &cb)) + error::raise("GetTokenInformation"); + + return Handle{raw}; + } + + Handle impersonate(const Handle& token) + { + HANDLE raw; + + if (!DuplicateToken(token, SecurityIdentification, &raw)) + error::raise("DuplicateToken"); + + return Handle{raw}; + } + + // + // FUNCTION: query_integrity_level() + // + // RETURN VALUE: Returns the integrity level of the current process. It is + // usually one of these values: + // + // SECURITY_MANDATORY_UNTRUSTED_RID (SID: S-1-16-0x0) + // Means untrusted level. It is used by processes started by the + // Anonymous group. Blocks most write access. + // + // SECURITY_MANDATORY_LOW_RID (SID: S-1-16-0x1000) + // Means low integrity level. It is used by Protected Mode Internet + // Explorer. Blocks write acess to most objects (such as files and + // registry keys) on the system. + // + // SECURITY_MANDATORY_MEDIUM_RID (SID: S-1-16-0x2000) + // Means medium integrity level. It is used by normal applications + // being launched while UAC is enabled. + // + // SECURITY_MANDATORY_HIGH_RID (SID: S-1-16-0x3000) + // Means high integrity level. It is used by administrative applications + // launched through elevation when UAC is enabled, or normal + // applications if UAC is disabled and the user is an administrator. + // + // SECURITY_MANDATORY_SYSTEM_RID (SID: S-1-16-0x4000) + // Means system integrity level. It is used by services and other + // system-level applications (such as Wininit, Winlogon, Smss, etc.) + // + DWORD query_integrity_level(const Handle& token) + { + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &cb)) + { + switch (GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + break; + default: + error::raise("GetTokenInformation"); + } + } + + std::vector<unsigned char> buf(cb); + const auto token_level = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buf.data()); + + if (!GetTokenInformation(token, TokenIntegrityLevel, token_level, cb, &cb)) + error::raise("GetTokenInformation"); + + // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. + // S-1-16-0x1000 stands for low integrity level SID). There is one and + // only one subauthority. + return *GetSidSubAuthority(token_level->Label.Sid, 0); + } + + std::wstring integrity_level_to_string(DWORD level) + { + static const std::unordered_map<DWORD, const wchar_t*> names = + { + {SECURITY_MANDATORY_UNTRUSTED_RID, L"Untrusted"}, + {SECURITY_MANDATORY_LOW_RID, L"Low"}, + {SECURITY_MANDATORY_MEDIUM_RID, L"Medium"}, + {SECURITY_MANDATORY_HIGH_RID, L"High"}, + {SECURITY_MANDATORY_SYSTEM_RID, L"System"}, + }; + + static constexpr auto unknown_name = L"Unknown"; + + const auto it = names.find(level); + + if (it == names.cend()) + return unknown_name; + + return it->second; + } + + bool belongs(const Handle& token, const SidBuffer& sid) + { + BOOL b = FALSE; + + if (!CheckTokenMembership(token, const_cast<unsigned char*>(sid.data()), &b)) + error::raise("CheckTokenMembership"); + + return b != FALSE; + } + + bool is_elevated(const Handle& token) + { + TOKEN_ELEVATION elevation; + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &cb)) + error::raise("GetTokenInformation"); + + return elevation.TokenIsElevated != 0; + } +} |