A Quick-Start Guide of Process Mandatory Level Checking and Self-elevation under UAC

Introduction

User Account Control (UAC) is a new security component in Windows Vista and newer operating systems. With UAC fully enabled, interactive administrators normally run with least user privileges. This article and the attached code samples demonstrate these frequently asked coding scenarios related to UAC.

1. How to check if the current process is running as administrator?

2. How to know if the primary access token of the current process belongs to user account that is a member of the local Administrators group, even if it currently is not elevated?

3. How to check if the current process is elevated? The elevation information is available to only Windows Vista and newer operating systems because there was no UAC and “elevation” before Windows Vista.

4. How to get the integrity level of the current process (System/High/Medium/Low/Unknown)? The integrity level information is available to only Windows Vista and newer operating systems because there was no UAC and “integrity level” before Windows Vista.

5. How to show an UAC shield icon on the UI for tasks that requires elevation?

6. How to self-elevate the current process?

7. How to automatically elevate the process when it's started up?

We provide code samples to demonstrate the above how-to scenarios in three programming languages (native VC++, VC#, VB.NET) to meet the needs of different developers.

Language

Sample

VC++

CppUACSelfElevation

VC#

CSUACSelfElevation

VB.NET

VBUACSelfElevation

The code samples are part of Microsoft All-In-One Code Framework, which is a centralized code sample solution from Microsoft. You can download the code from the project’s Download page: https://1code.codeplex.com/releases/.

Background

Microsoft All-In-One Code Framework delineates the framework and skeleton of most Microsoft development techniques (e.g., COM, Data Access, IPC) using typical sample codes in different programming languages (e.g., Visual C#, VB.NET, Visual C++). Each sample is elaborately selected, composed, and documented to demonstrate one frequently-asked, tested or used coding scenario based on Microsoft’s support experience in MSDN newsgroups and forums.

Demo

This is a quick demo of the attached UAC samples.

Step1. After you successfully build the sample project in Visual Studio 2008, you will get an application depending on the programming language that you are using: CppUACSelfElevation.exe / CSUACSelfElevation.exe / VBUACSelfElevation.exe.

Step2. Run the application as a protected administrator on a Windows Vista or Windows 7 system with UAC fully enabled. The application should display the following content on the main dialog. There is a UAC shield icon on the Self-elevate button.

clip_image002

Step3. Click on the Self-elevate button. You will see a Consent UI.

clip_image004

Step4. Click Yes to approve the elevation. The original application will then be started and display the following content on the main dialog.

clip_image006

The Self-elevate button on the dialog does not have the UAC shield icon this time. That is, the application is running as elevated administrator. The elevation succeeds. If you click on the Self-elevate button again, the application will tell you that it is running as administrator.

Step5. Click OK to close the application.

Using the Code

The code introduced in this section is for VC++ developers only. You can find the VC# and VB.NET implementations in the CSUACSelfElevation and VBUACSelfElevation sample packages.

1. How to check if the current process is running as administrator?

   1: // 
  2: //   FUNCTION: IsRunAsAdmin()
  3: //
  4: //   PURPOSE: The function checks whether the current process is run as 
  5: //   administrator. In other words, it dictates whether the primary access 
  6: //   token of the process belongs to user account that is a member of the 
  7: //   local Administrators group and it is elevated.
  8: //
  9: //   RETURN VALUE: Returns TRUE if the primary access token of the process 
 10: //   belongs to user account that is a member of the local Administrators 
 11: //   group and it is elevated. Returns FALSE if the token does not.
 12: //
 13: //   EXCEPTION: If this function fails, it throws a C++ DWORD exception which 
 14: //   contains the Win32 error code of the failure.
 15: //
 16: //   EXAMPLE CALL:
 17: //     try 
 18: //     {
 19: //         if (IsRunAsAdmin())
 20: //             wprintf (L"Process is run as administrator\n");
 21: //         else
 22: //             wprintf (L"Process is not run as administrator\n");
 23: //     }
 24: //     catch (DWORD dwError)
 25: //     {
 26: //         wprintf(L"IsRunAsAdmin failed w/err %lu\n", dwError);
 27: //     }
 28: //
 29: BOOL IsRunAsAdmin()
 30: {
 31:     BOOL fIsRunAsAdmin = FALSE;
 32:     DWORD dwError = ERROR_SUCCESS;
 33:     PSID pAdministratorsGroup = NULL;
 34: 
 35:     // Allocate and initialize a SID of the administrators group.
 36:     SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
 37:     if (!AllocateAndInitializeSid(
 38:         &NtAuthority, 
 39:         2, 
 40:         SECURITY_BUILTIN_DOMAIN_RID, 
 41:         DOMAIN_ALIAS_RID_ADMINS, 
 42:         0, 0, 0, 0, 0, 0, 
 43:         &pAdministratorsGroup))
 44:     {
 45:         dwError = GetLastError();
 46:         goto Cleanup;
 47:     }
 48: 
 49:     // Determine whether the SID of administrators group is enabled in 
 50:     // the primary access token of the process.
 51:     if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
 52:     {
 53:         dwError = GetLastError();
 54:         goto Cleanup;
 55:     }
 56: 
 57: Cleanup:
 58:     // Centralized cleanup for all allocated resources.
 59:     if (pAdministratorsGroup)
 60:     {
 61:         FreeSid(pAdministratorsGroup);
 62:         pAdministratorsGroup = NULL;
 63:     }
 64: 
 65:     // Throw the error if something failed in the function.
 66:     if (ERROR_SUCCESS != dwError)
 67:     {
 68:         throw dwError;
 69:     }
 70: 
 71:     return fIsRunAsAdmin;
 72: }
 73: 

2. How to know if the primary access token of the current process belongs to user account that is a member of the local Administrators group, even if it currently is not elevated?

   1: //
  2: //   FUNCTION: IsUserInAdminGroup()
  3: //
  4: //   PURPOSE: The function checks whether the primary access token of the 
  5: //   process belongs to user account that is a member of the local 
  6: //   Administrators group, even if it currently is not elevated.
  7: //
  8: //   RETURN VALUE: Returns TRUE if the primary access token of the process 
  9: //   belongs to user account that is a member of the local Administrators 
 10: //   group. Returns FALSE if the token does not.
 11: //
 12: //   EXCEPTION: If this function fails, it throws a C++ DWORD exception which 
 13: //   contains the Win32 error code of the failure.
 14: //
 15: //   EXAMPLE CALL:
 16: //     try 
 17: //     {
 18: //         if (IsUserInAdminGroup())
 19: //             wprintf (L"User is a member of the Administrators group\n");
 20: //         else
 21: //             wprintf (L"User is not a member of the Administrators group\n");
 22: //     }
 23: //     catch (DWORD dwError)
 24: //     {
 25: //         wprintf(L"IsUserInAdminGroup failed w/err %lu\n", dwError);
 26: //     }
 27: //
 28: BOOL IsUserInAdminGroup()
 29: {
 30:     BOOL fInAdminGroup = FALSE;
 31:     DWORD dwError = ERROR_SUCCESS;
 32:     HANDLE hToken = NULL;
 33:     HANDLE hTokenToCheck = NULL;
 34:     DWORD cbSize = 0;
 35: 
 36:     // Open the primary access token of the process for query and duplicate.
 37:     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, 
 38:         &hToken))
 39:     {
 40:         dwError = GetLastError();
 41:         goto Cleanup;
 42:     }
 43: 
 44:     // Determine whether system is running Windows Vista or later operating 
 45:     // systems (major version >= 6) because they support linked tokens, but 
 46:     // previous versions (major version < 6) do not.
 47:     OSVERSIONINFO osver = { sizeof(osver) };
 48:     if (!GetVersionEx(&osver))
 49:     {
 50:         dwError = GetLastError();
 51:         goto Cleanup;
 52:     }
 53: 
 54:     if (osver.dwMajorVersion >= 6)
 55:     {
 56:         // Running Windows Vista or later (major version >= 6). 
 57:         // Determine token type: limited, elevated, or default. 
 58:         TOKEN_ELEVATION_TYPE elevType;
 59:         if (!GetTokenInformation(hToken, TokenElevationType, &elevType, 
 60:             sizeof(elevType), &cbSize))
 61:         {
 62:             dwError = GetLastError();
 63:             goto Cleanup;
 64:         }
 65: 
 66:         // If limited, get the linked elevated token for further check.
 67:         if (TokenElevationTypeLimited == elevType)
 68:         {
 69:             if (!GetTokenInformation(hToken, TokenLinkedToken, &hTokenToCheck, 
 70:                 sizeof(hTokenToCheck), &cbSize))
 71:             {
 72:                 dwError = GetLastError();
 73:                 goto Cleanup;
 74:             }
 75:         }
 76:     }
 77:     
 78:     // CheckTokenMembership requires an impersonation token. If we just got a 
 79:     // linked token, it already is an impersonation token.  If we did not get 
 80:     // a linked token, duplicate the original into an impersonation token for 
 81:     // CheckTokenMembership.
 82:     if (!hTokenToCheck)
 83:     {
 84:         if (!DuplicateToken(hToken, SecurityIdentification, &hTokenToCheck))
 85:         {
 86:             dwError = GetLastError();
 87:             goto Cleanup;
 88:         }
 89:     }
 90: 
 91:     // Create the SID corresponding to the Administrators group.
 92:     BYTE adminSID[SECURITY_MAX_SID_SIZE];
 93:     cbSize = sizeof(adminSID);
 94:     if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID,  
 95:         &cbSize))
 96:     {
 97:         dwError = GetLastError();
 98:         goto Cleanup;
 99:     }
100: 

101:     // Check if the token to be checked contains admin SID.

102:     // https://msdn.microsoft.com/en-us/library/aa379596(VS.85).aspx:

103:     // To determine whether a SID is enabled in a token, that is, whether it 

104:     // has the SE_GROUP_ENABLED attribute, call CheckTokenMembership.

105:     if (!CheckTokenMembership(hTokenToCheck, &adminSID, &fInAdminGroup)) 

106:     {

107:         dwError = GetLastError();

108:         goto Cleanup;

109:     }

110: 

111: Cleanup:

112:     // Centralized cleanup for all allocated resources.

113:     if (hToken)

114:     {

115:         CloseHandle(hToken);

116:         hToken = NULL;

117:     }

118:     if (hTokenToCheck)

119:     {

120:         CloseHandle(hTokenToCheck);

121:         hTokenToCheck = NULL;

122:     }

123: 

124:     // Throw the error if something failed in the function.

125:     if (ERROR_SUCCESS != dwError)

126:     {

127:         throw dwError;

128:     }

129: 

130:     return fInAdminGroup;

131: }

132: 

3. How to check if the current process is elevated? The elevation information is available to only Windows Vista and newer operating systems because there was no UAC and “elevation” before Windows Vista.

   1: //
  2: //   FUNCTION: IsProcessElevated()
  3: //
  4: //   PURPOSE: The function gets the elevation information of the current 
  5: //   process. It dictates whether the process is elevated or not. Token 
  6: //   elevation is only available on Windows Vista and newer operating 
  7: //   systems, thus IsProcessElevated throws a C++ exception if it is called 
  8: //   on systems prior to Windows Vista. It is not appropriate to use this 
  9: //   function to determine whether a process is run as administartor.
 10: //
 11: //   RETURN VALUE: Returns TRUE if the process is elevated. Returns FALSE if 
 12: //   it is not.
 13: //
 14: //   EXCEPTION: If this function fails, it throws a C++ DWORD exception 
 15: //   which contains the Win32 error code of the failure. For example, if 
 16: //   IsProcessElevated is called on systems prior to Windows Vista, the error 
 17: //   code will be ERROR_INVALID_PARAMETER.
 18: //
 19: //   NOTE: TOKEN_INFORMATION_CLASS provides TokenElevationType to check the 
 20: //   elevation type (TokenElevationTypeDefault / TokenElevationTypeLimited /
 21: //   TokenElevationTypeFull) of the process. It is different from 
 22: //   TokenElevation in that, when UAC is turned off, elevation type always 
 23: //   returns TokenElevationTypeDefault even though the process is elevated 
 24: //   (Integrity Level == High). In other words, it is not safe to say if the 
 25: //   process is elevated based on elevation type. Instead, we should use 
 26: //   TokenElevation.
 27: //
 28: //   EXAMPLE CALL:
 29: //     try 
 30: //     {
 31: //         if (IsProcessElevated())
 32: //             wprintf (L"Process is elevated\n");
 33: //         else
 34: //             wprintf (L"Process is not elevated\n");
 35: //     }
 36: //     catch (DWORD dwError)
 37: //     {
 38: //         wprintf(L"IsProcessElevated failed w/err %lu\n", dwError);
 39: //     }
 40: //
 41: BOOL IsProcessElevated()
 42: {
 43:     BOOL fIsElevated = FALSE;
 44:     DWORD dwError = ERROR_SUCCESS;
 45:     HANDLE hToken = NULL;
 46: 
 47:     // Open the primary access token of the process with TOKEN_QUERY.
 48:     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
 49:     {
 50:         dwError = GetLastError();
 51:         goto Cleanup;
 52:     }
 53: 
 54:     // Retrieve token elevation information.
 55:     TOKEN_ELEVATION elevation;
 56:     DWORD dwSize;
 57:     if (!GetTokenInformation(hToken, TokenElevation, &elevation, 
 58:         sizeof(elevation), &dwSize))
 59:     {
 60:         // When the process is run on operating systems prior to Windows 
 61:         // Vista, GetTokenInformation returns FALSE with the 
 62:         // ERROR_INVALID_PARAMETER error code because TokenElevation is 
 63:         // not supported on those operating systems.
 64:         dwError = GetLastError();
 65:         goto Cleanup;
 66:     }
 67: 
 68:     fIsElevated = elevation.TokenIsElevated;
 69: 
 70: Cleanup:
 71:     // Centralized cleanup for all allocated resources.
 72:     if (hToken)
 73:     {
 74:         CloseHandle(hToken);
 75:         hToken = NULL;
 76:     }
 77: 
 78:     // Throw the error if something failed in the function.
 79:     if (ERROR_SUCCESS != dwError)
 80:     {
 81:         throw dwError;
 82:     }
 83: 
 84:     return fIsElevated;
 85: }
 86: 

4. How to get the integrity level of the current process (System/High/Medium/Low/Unknown)? The integrity level information is available to only Windows Vista and newer operating systems because there was no UAC and “integrity level” before Windows Vista.

   1: //
  2: //   FUNCTION: GetProcessIntegrityLevel()
  3: //
  4: //   PURPOSE: The function gets the integrity level of the current process. 
  5: //   Integrity level is only available on Windows Vista and newer operating 
  6: //   systems, thus GetProcessIntegrityLevel throws a C++ exception if it is 
  7: //   called on systems prior to Windows Vista.
  8: //
  9: //   RETURN VALUE: Returns the integrity level of the current process. It is 
 10: //   usually one of these values:
 11: //
 12: //     SECURITY_MANDATORY_UNTRUSTED_RID (SID: S-1-16-0x0)
 13: //     Means untrusted level. It is used by processes started by the 
 14: //     Anonymous group. Blocks most write access. 
 15: //
 16: //     SECURITY_MANDATORY_LOW_RID (SID: S-1-16-0x1000)
 17: //     Means low integrity level. It is used by Protected Mode Internet 
 18: //     Explorer. Blocks write acess to most objects (such as files and 
 19: //     registry keys) on the system. 
 20: //
 21: //     SECURITY_MANDATORY_MEDIUM_RID (SID: S-1-16-0x2000)
 22: //     Means medium integrity level. It is used by normal applications 
 23: //     being launched while UAC is enabled. 
 24: //
 25: //     SECURITY_MANDATORY_HIGH_RID (SID: S-1-16-0x3000)
 26: //     Means high integrity level. It is used by administrative applications 
 27: //     launched through elevation when UAC is enabled, or normal 
 28: //     applications if UAC is disabled and the user is an administrator. 
 29: //
 30: //     SECURITY_MANDATORY_SYSTEM_RID (SID: S-1-16-0x4000)
 31: //     Means system integrity level. It is used by services and other 
 32: //     system-level applications (such as Wininit, Winlogon, Smss, etc.)  
 33: //
 34: //   EXCEPTION: If this function fails, it throws a C++ DWORD exception 
 35: //   which contains the Win32 error code of the failure. For example, if 
 36: //   GetProcessIntegrityLevel is called on systems prior to Windows Vista, 
 37: //   the error code will be ERROR_INVALID_PARAMETER.
 38: //
 39: //   EXAMPLE CALL:
 40: //     try 
 41: //     {
 42: //         DWORD dwIntegrityLevel = GetProcessIntegrityLevel();
 43: //     }
 44: //     catch (DWORD dwError)
 45: //     {
 46: //         wprintf(L"GetProcessIntegrityLevel failed w/err %lu\n", dwError);
 47: //     }
 48: //
 49: DWORD GetProcessIntegrityLevel()
 50: {
 51:     DWORD dwIntegrityLevel = 0;
 52:     DWORD dwError = ERROR_SUCCESS;
 53:     HANDLE hToken = NULL;
 54:     DWORD cbTokenIL = 0;
 55:     PTOKEN_MANDATORY_LABEL pTokenIL = NULL;
 56: 
 57:     // Open the primary access token of the process with TOKEN_QUERY.
 58:     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
 59:     {
 60:         dwError = GetLastError();
 61:         goto Cleanup;
 62:     }
 63: 
 64:     // Query the size of the token integrity level information. Note that 
 65:     // we expect a FALSE result and the last error ERROR_INSUFFICIENT_BUFFER
 66:     // from GetTokenInformation because we have given it a NULL buffer. On 
 67:     // exit cbTokenIL will tell the size of the integrity level information.
 68:     if (!GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cbTokenIL))
 69:     {
 70:         if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
 71:         {
 72:             // When the process is run on operating systems prior to Windows 
 73:             // Vista, GetTokenInformation returns FALSE with the 
 74:             // ERROR_INVALID_PARAMETER error code because TokenElevation 
 75:             // is not supported on those operating systems.
 76:             dwError = GetLastError();
 77:             goto Cleanup;
 78:         }
 79:     }
 80: 
 81:     // Now we allocate a buffer for the integrity level information.
 82:     pTokenIL = (TOKEN_MANDATORY_LABEL *)LocalAlloc(LPTR, cbTokenIL);
 83:     if (pTokenIL == NULL)
 84:     {
 85:         dwError = GetLastError();
 86:         goto Cleanup;
 87:     }
 88: 
 89:     // Retrieve token integrity level information.
 90:     if (!GetTokenInformation(hToken, TokenIntegrityLevel, pTokenIL, 
 91:         cbTokenIL, &cbTokenIL))
 92:     {
 93:         dwError = GetLastError();
 94:         goto Cleanup;
 95:     }
 96: 
 97:     // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. 
 98:     // S-1-16-0x1000 stands for low integrity level SID). There is one and 
 99:     // only one subauthority.
100:     dwIntegrityLevel = *GetSidSubAuthority(pTokenIL->Label.Sid, 0);

101: 

102: Cleanup:

103:     // Centralized cleanup for all allocated resources.

104:     if (hToken)

105:     {

106:         CloseHandle(hToken);

107:         hToken = NULL;

108:     }

109:     if (pTokenIL)

110:     {

111:         LocalFree(pTokenIL);

112:         pTokenIL = NULL;

113:         cbTokenIL = 0;

114:     }

115: 

116:     // Throw the error if something failed in the function.

117:     if (ERROR_SUCCESS != dwError)

118:     {

119:         throw dwError;

120:     }

121: 

122:     return dwIntegrityLevel;

123: }

124: 

5. How to show an UAC shield icon on the UI for tasks that requires elevation?

   1: // Get the process elevation information.
  2: BOOL const fIsElevated = IsProcessElevated();
  3: 
  4: // Update the Self-elevate button to show the UAC shield icon on 
  5: // the UI if the process is not elevated. The 
  6: // Button_SetElevationRequiredState macro (declared in Commctrl.h) 
  7: // is used to show or hide the shield icon in a button. You can 
  8: // also get the shield directly as an icon by calling 
  9: // SHGetStockIconInfo with SIID_SHIELD as the parameter.
 10: HWND hElevateBtn = GetDlgItem(hWnd, IDC_ELEVATE_BN);
 11: Button_SetElevationRequiredState(hElevateBtn, !fIsElevated);
 12: 

6. How to self-elevate the current process?

   1: wchar_t szPath[MAX_PATH];
  2: if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
  3: {
  4:     // Launch itself as administrator.
  5:     SHELLEXECUTEINFO sei = { sizeof(sei) };
  6:     sei.lpVerb = L"runas";
  7:     sei.lpFile = szPath;
  8:     sei.hwnd = hWnd;
  9:     sei.nShow = SW_NORMAL;
 10: 
 11:     if (!ShellExecuteEx(&sei))
 12:     {
 13:         DWORD dwError = GetLastError();
 14:         if (dwError == ERROR_CANCELLED)
 15:         {
 16:             // The user refused to allow privileges elevation.
 17:             // Do nothing ...
 18:         }
 19:     }
 20:     else
 21:     {
 22:         EndDialog(hWnd, TRUE);  // Quit itself
 23:     }
 24: }
 25: 

7. How to automatically elevate the process when it's started up?

If your application always requires administrative privileges, such as during an installation step, the operating system can automatically prompt the user for privileges elevation each time your application is invoked.

If a specific kind of resource (RT_MANIFEST) is found embedded within the application executable, the system looks for the <trustInfo> section and parses its contents. Here is an example of this section in the manifest file:

   1:     <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
  2:        <security>
  3:           <requestedPrivileges>
  4:              <requestedExecutionLevel
  5:                 level="requireAdministrator"
  6:              />
  7:           </requestedPrivileges>
  8:        </security>
  9:     </trustInfo>
 10: 

Three different values are possible for the level attribute

a) requireAdministrator
The application must be started with Administrator privileges; it won't run otherwise.

b) highestAvailable
The application is started with the highest possible privileges. If the user is logged on with an Administrator account, an elevation prompt appears. If the user is a Standard User, the application is started (without any elevation prompt) with these standard privileges.

c) asInvoker
The application is started with the same privileges as the calling application.

To configure the elevation level in a VC++ project, open the project's properties dialog, turn to Linker /Manifest File, and select UAC Execution Level.

Jialiang ^JLG , CLR expert, 3 Star Contributor at forum

If you want more information shared by our team, please follow us @ Twitter

Comments

  • Anonymous
    January 01, 2013
    Finally I've found this page. Very good article. This is what I was looking for. Why there is nothing mentioned in MSDN about the differences of being an administrator and being elevated and how to check this?