UacHelpers.CppLibrary/UserAccountControl.cpp
author StephaneLenclud
Thu, 28 Jan 2016 20:22:46 +0100
branchMiniDisplay
changeset 453 017874772461
permissions -rw-r--r--
Migration to Visual Studio 2015.
SharpLibDisplay: Now closing client upon server request.
StephaneLenclud@436
     1
#include "UserAccountControl.h"
StephaneLenclud@436
     2
StephaneLenclud@436
     3
#include <windows.h>
StephaneLenclud@436
     4
#pragma comment (lib, "kernel32.lib")
StephaneLenclud@436
     5
#pragma comment (lib, "advapi32.lib")
StephaneLenclud@436
     6
StephaneLenclud@436
     7
#include <vcclr.h>
StephaneLenclud@436
     8
#include <msclr\marshal.h>
StephaneLenclud@436
     9
#include <msclr\marshal_windows.h>
StephaneLenclud@436
    10
StephaneLenclud@436
    11
using namespace msclr::interop;
StephaneLenclud@436
    12
StephaneLenclud@436
    13
using namespace System::ComponentModel;
StephaneLenclud@436
    14
using namespace Microsoft::Win32;
StephaneLenclud@436
    15
StephaneLenclud@436
    16
namespace UacHelpers {
StephaneLenclud@436
    17
StephaneLenclud@436
    18
	Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments)
StephaneLenclud@436
    19
    {
StephaneLenclud@436
    20
        ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments);
StephaneLenclud@436
    21
        psi->UseShellExecute = true;
StephaneLenclud@436
    22
        psi->Verb = "runas";
StephaneLenclud@436
    23
		return Process::Start(psi);
StephaneLenclud@436
    24
    }
StephaneLenclud@436
    25
StephaneLenclud@436
    26
	Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments)
StephaneLenclud@436
    27
	{
StephaneLenclud@436
    28
		marshal_context context;
StephaneLenclud@436
    29
StephaneLenclud@436
    30
		//If the current process is not elevated, then there's no reason to go through the hassle --
StephaneLenclud@436
    31
		//just use the standard System.Diagnostics.Process facilities.
StephaneLenclud@436
    32
		if (!IsCurrentProcessElevated)
StephaneLenclud@436
    33
		{
StephaneLenclud@436
    34
			return Process::Start(exePath, arguments);
StephaneLenclud@436
    35
		}
StephaneLenclud@436
    36
StephaneLenclud@436
    37
		//The following implementation is roughly based on Aaron Margosis' post:
StephaneLenclud@436
    38
		//http://blogs.msdn.com/aaron_margosis/archive/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.aspx
StephaneLenclud@436
    39
StephaneLenclud@436
    40
		//Enable SeIncreaseQuotaPrivilege in this process.  (This requires administrative privileges.)
StephaneLenclud@436
    41
		HANDLE hProcessToken = NULL;
StephaneLenclud@436
    42
		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
StephaneLenclud@436
    43
		{
StephaneLenclud@436
    44
			throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
    45
		}
StephaneLenclud@436
    46
		else
StephaneLenclud@436
    47
		{
StephaneLenclud@436
    48
			TOKEN_PRIVILEGES tkp;
StephaneLenclud@436
    49
			tkp.PrivilegeCount = 1;
StephaneLenclud@436
    50
			LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid);
StephaneLenclud@436
    51
			tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
StephaneLenclud@436
    52
			AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL);
StephaneLenclud@436
    53
			DWORD dwLastErr = GetLastError();
StephaneLenclud@436
    54
			CloseHandle(hProcessToken);
StephaneLenclud@436
    55
			if (ERROR_SUCCESS != dwLastErr)
StephaneLenclud@436
    56
			{
StephaneLenclud@436
    57
				throw gcnew Win32Exception(dwLastErr);
StephaneLenclud@436
    58
			}
StephaneLenclud@436
    59
		}
StephaneLenclud@436
    60
StephaneLenclud@436
    61
		//Get window handle representing the desktop shell.  This might not work if there is no shell window, or when
StephaneLenclud@436
    62
		//using a custom shell.  Also note that we're assuming that the shell is not running elevated.
StephaneLenclud@436
    63
		HWND hShellWnd = GetShellWindow();
StephaneLenclud@436
    64
		if (hShellWnd == NULL)
StephaneLenclud@436
    65
		{
StephaneLenclud@436
    66
			throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell");
StephaneLenclud@436
    67
		}
StephaneLenclud@436
    68
StephaneLenclud@436
    69
		//Get the ID of the desktop shell process.
StephaneLenclud@436
    70
		DWORD dwShellPID;
StephaneLenclud@436
    71
		GetWindowThreadProcessId(hShellWnd, &dwShellPID);
StephaneLenclud@436
    72
		if (dwShellPID == 0)
StephaneLenclud@436
    73
		{
StephaneLenclud@436
    74
			throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
    75
		}
StephaneLenclud@436
    76
StephaneLenclud@436
    77
		//Open the desktop shell process in order to get the process token.
StephaneLenclud@436
    78
		HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID);
StephaneLenclud@436
    79
		if (hShellProcess == NULL)
StephaneLenclud@436
    80
		{
StephaneLenclud@436
    81
			throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
    82
		}
StephaneLenclud@436
    83
StephaneLenclud@436
    84
		HANDLE hShellProcessToken = NULL;
StephaneLenclud@436
    85
		HANDLE hPrimaryToken = NULL;
StephaneLenclud@436
    86
		try
StephaneLenclud@436
    87
		{
StephaneLenclud@436
    88
			//Get the process token of the desktop shell.
StephaneLenclud@436
    89
			if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken))
StephaneLenclud@436
    90
			{
StephaneLenclud@436
    91
				throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
    92
			}
StephaneLenclud@436
    93
StephaneLenclud@436
    94
			//Duplicate the shell's process token to get a primary token.
StephaneLenclud@436
    95
			const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
StephaneLenclud@436
    96
			if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
StephaneLenclud@436
    97
			{
StephaneLenclud@436
    98
				throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
    99
			}
StephaneLenclud@436
   100
StephaneLenclud@436
   101
			//Start the target process with the new token.
StephaneLenclud@436
   102
			STARTUPINFO si = {0}; si.cb = sizeof(si);
StephaneLenclud@436
   103
			PROCESS_INFORMATION pi = {0};
StephaneLenclud@436
   104
			if (!CreateProcessWithTokenW(hPrimaryToken, 0,
StephaneLenclud@436
   105
				context.marshal_as<LPCWSTR>(exePath), context.marshal_as<LPWSTR>(exePath + " " + arguments),
StephaneLenclud@436
   106
				0, NULL, NULL, &si, &pi))
StephaneLenclud@436
   107
			{
StephaneLenclud@436
   108
				throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
   109
			}
StephaneLenclud@436
   110
			CloseHandle(pi.hProcess);
StephaneLenclud@436
   111
			CloseHandle(pi.hThread);
StephaneLenclud@436
   112
StephaneLenclud@436
   113
			return Process::GetProcessById(pi.dwProcessId);
StephaneLenclud@436
   114
		}
StephaneLenclud@436
   115
		finally
StephaneLenclud@436
   116
		{
StephaneLenclud@436
   117
			if (hShellProcessToken != NULL)
StephaneLenclud@436
   118
				CloseHandle(hShellProcessToken);
StephaneLenclud@436
   119
StephaneLenclud@436
   120
			if (hPrimaryToken != NULL)
StephaneLenclud@436
   121
				CloseHandle(hPrimaryToken);
StephaneLenclud@436
   122
StephaneLenclud@436
   123
			if (hShellProcess != NULL)
StephaneLenclud@436
   124
				CloseHandle(hShellProcess);
StephaneLenclud@436
   125
		}
StephaneLenclud@436
   126
	}
StephaneLenclud@436
   127
StephaneLenclud@436
   128
	bool UserAccountControl::IsUserAdmin::get()
StephaneLenclud@436
   129
	{
StephaneLenclud@436
   130
		if (UserAccountControl::IsUacEnabled)
StephaneLenclud@436
   131
			return GetProcessTokenElevationType() != TokenElevationTypeDefault;	//split token
StephaneLenclud@436
   132
StephaneLenclud@436
   133
		//If UAC is off, we can't rely on the token; check for Admin group.
StephaneLenclud@436
   134
		return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators");
StephaneLenclud@436
   135
	}
StephaneLenclud@436
   136
StephaneLenclud@436
   137
	bool UserAccountControl::IsUacEnabled::get()
StephaneLenclud@436
   138
	{
StephaneLenclud@436
   139
		//Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value.
StephaneLenclud@436
   140
		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false);
StephaneLenclud@436
   141
		return key->GetValue(UacRegistryValue)->Equals(1);
StephaneLenclud@436
   142
    }
StephaneLenclud@436
   143
StephaneLenclud@436
   144
	void UserAccountControl::DisableUac()
StephaneLenclud@436
   145
	{
StephaneLenclud@436
   146
		SetUacRegistryValue(false);
StephaneLenclud@436
   147
	}
StephaneLenclud@436
   148
StephaneLenclud@436
   149
	void UserAccountControl::DisableUacAndRestartWindows()
StephaneLenclud@436
   150
	{
StephaneLenclud@436
   151
		DisableUac();
StephaneLenclud@436
   152
		RestartWindows();
StephaneLenclud@436
   153
	}
StephaneLenclud@436
   154
StephaneLenclud@436
   155
	void UserAccountControl::EnableUac()
StephaneLenclud@436
   156
	{
StephaneLenclud@436
   157
		SetUacRegistryValue(true);
StephaneLenclud@436
   158
	}
StephaneLenclud@436
   159
StephaneLenclud@436
   160
	void UserAccountControl::EnableUacAndRestartWindows()
StephaneLenclud@436
   161
	{
StephaneLenclud@436
   162
		EnableUac();
StephaneLenclud@436
   163
		RestartWindows();
StephaneLenclud@436
   164
	}
StephaneLenclud@436
   165
StephaneLenclud@436
   166
	bool UserAccountControl::IsCurrentProcessElevated::get()
StephaneLenclud@436
   167
	{
StephaneLenclud@436
   168
		return GetProcessTokenElevationType() == TokenElevationTypeFull;	//elevated
StephaneLenclud@436
   169
	}
StephaneLenclud@436
   170
StephaneLenclud@436
   171
	bool UserAccountControl::IsCurrentProcessVirtualized::get()
StephaneLenclud@436
   172
	{
StephaneLenclud@436
   173
		HANDLE hToken;
StephaneLenclud@436
   174
        try
StephaneLenclud@436
   175
        {
StephaneLenclud@436
   176
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
StephaneLenclud@436
   177
				throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
   178
StephaneLenclud@436
   179
            DWORD virtualizationEnabled;
StephaneLenclud@436
   180
			DWORD dwSize;
StephaneLenclud@436
   181
			if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize))
StephaneLenclud@436
   182
                throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
   183
StephaneLenclud@436
   184
			return virtualizationEnabled != 0;
StephaneLenclud@436
   185
        }
StephaneLenclud@436
   186
        finally
StephaneLenclud@436
   187
        {
StephaneLenclud@436
   188
            CloseHandle(hToken);
StephaneLenclud@436
   189
        }
StephaneLenclud@436
   190
	}
StephaneLenclud@436
   191
StephaneLenclud@436
   192
	int UserAccountControl::GetProcessTokenElevationType()
StephaneLenclud@436
   193
	{
StephaneLenclud@436
   194
		HANDLE hToken;
StephaneLenclud@436
   195
        try
StephaneLenclud@436
   196
        {
StephaneLenclud@436
   197
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
StephaneLenclud@436
   198
				throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
   199
StephaneLenclud@436
   200
            TOKEN_ELEVATION_TYPE elevationType;
StephaneLenclud@436
   201
			DWORD dwSize;
StephaneLenclud@436
   202
            if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
StephaneLenclud@436
   203
                throw gcnew Win32Exception(GetLastError());
StephaneLenclud@436
   204
StephaneLenclud@436
   205
			return elevationType;
StephaneLenclud@436
   206
        }
StephaneLenclud@436
   207
        finally
StephaneLenclud@436
   208
        {
StephaneLenclud@436
   209
            CloseHandle(hToken);
StephaneLenclud@436
   210
        }
StephaneLenclud@436
   211
	}
StephaneLenclud@436
   212
StephaneLenclud@436
   213
	void UserAccountControl::SetUacRegistryValue(bool enabled)
StephaneLenclud@436
   214
	{
StephaneLenclud@436
   215
		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true);
StephaneLenclud@436
   216
		key->SetValue(UacRegistryValue, enabled ? 1 : 0);
StephaneLenclud@436
   217
	}
StephaneLenclud@436
   218
StephaneLenclud@436
   219
	void UserAccountControl::RestartWindows()
StephaneLenclud@436
   220
	{
StephaneLenclud@436
   221
		InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/,
StephaneLenclud@436
   222
								 TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/,
StephaneLenclud@436
   223
								 SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED);
StephaneLenclud@436
   224
		//This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)".
StephaneLenclud@436
   225
	}
StephaneLenclud@436
   226
}