UAC, Elevation and the default Administrator account

28 08 2012

These days I was struggling with Windows 7 UAC (User Account Control). We have an (.NET) app where we want to detect if we are running as an elevated process (if UAC is turned on), or if we are a member of the Administrators group (fallback solution when UAC is turned off). There is a lot of good information including C# sample code around (see e. g. here). They recommend to check the registry for the EnableLUA flag to determine if UAC is turned on, and then call OpenProcessToken and GetTokenInformation(…, TokenElevationType, …) to retrieve the elevation type of the current process.

The problem with all these solutions is that they assume that – when UAC is turned on – the elevation will be either TokenElevationTypeLimited (when not running elevated) or TokenElevationTypeFull (when running elevated). But this is not true! There are times when you receive TokenElevationTypeDefault even though you might expect the value to be TokenElevationTypeFull.

You will receive TokenElevationTypeDefault even when UAC is turned on when the following preconditions are met:

  1. The default Administrator account is enabled (default: disabled), and
  2. The security policy „User Account Control: Use Admin Approval Mode for the built-in Administrator account“ is disabled. This is the default.

You can use the Local Security Policy Editor (secpol.msc) and navigate to Security Settings – Local Policies – Security Options to verify this option.

When the above prerequisites are met, you will receive TokenElevationTypeDefault when one of the following conditions is true:

  1. You are running as the default Administrator, or
  2. You started the process through the „Run as administrator“ Explorer context menu and selected the default Administrator account.

In other words: When the security option „User Account Control: Use Admin Approval Mode for the built-in Administrator account“ is disabled, the default Administrator account behaves as if UAC was also disabled.

To make our app behave the same no matter which Administrator account is used (the default one, or any of the other existing accounts which are a member of the Administrators group), I enhanced the solution for determining process elevation as follows:

/// <summary>
/// Base on code found here:
/// http://stackoverflow.com/questions/1220213/c-detect-if-running-with-elevated-privileges
/// </summary>
public static class UacHelper
{
  private const string uacRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
  private const string uacRegistryValue = "EnableLUA";

  private const uint STANDARD_RIGHTS_READ = 0x00020000;
  private const uint TOKEN_QUERY = 0x0008;
  private const uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

  [DllImport("advapi32.dll", SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

  [DllImport("advapi32.dll", SetLastError = true)]
  public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, 
                                                IntPtr TokenInformation, uint TokenInformationLength, 
                                                out uint ReturnLength);

  public enum TOKEN_INFORMATION_CLASS
  {
    TokenUser = 1,
    TokenGroups,
    TokenPrivileges,
    TokenOwner,
    TokenPrimaryGroup,
    TokenDefaultDacl,
    TokenSource,
    TokenType,
    TokenImpersonationLevel,
    TokenStatistics,
    TokenRestrictedSids,
    TokenSessionId,
    TokenGroupsAndPrivileges,
    TokenSessionReference,
    TokenSandBoxInert,
    TokenAuditPolicy,
    TokenOrigin,
    TokenElevationType,
    TokenLinkedToken,
    TokenElevation,
    TokenHasRestrictions,
    TokenAccessInformation,
    TokenVirtualizationAllowed,
    TokenVirtualizationEnabled,
    TokenIntegrityLevel,
    TokenUIAccess,
    TokenMandatoryPolicy,
    TokenLogonSid,
    MaxTokenInfoClass
  }

  public enum TOKEN_ELEVATION_TYPE
  {
    TokenElevationTypeDefault = 1,
    TokenElevationTypeFull,
    TokenElevationTypeLimited
  }

  private static bool? _isUacEnabled;
  public static bool IsUacEnabled
  {
    get
    {
    if (_isUacEnabled == null)
    {
      var uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
      if (uacKey == null)
      {
      _isUacEnabled = false;
      }
      else
      {
      var enableLua = uacKey.GetValue(uacRegistryValue);
      _isUacEnabled = enableLua.Equals(1);
      }
    }
    return _isUacEnabled.Value;
    }
  }

  private static bool? _isAdministrator;
  public static bool IsAdministrator
  {
    get
    {
    if (_isAdministrator == null)
    {
      var identity = WindowsIdentity.GetCurrent();
      Debug.Assert(identity != null);
      var principal = new WindowsPrincipal(identity);
      _isAdministrator = principal.IsInRole(WindowsBuiltInRole.Administrator);
    }
    return _isAdministrator.Value;
    }
  }

  private static bool? _isProcessElevated; 
  public static bool IsProcessElevated
  {
    get
    {
    if (_isProcessElevated == null)
    {
      if (IsUacEnabled)
      {
      var process = Process.GetCurrentProcess();

      IntPtr tokenHandle;
      if (!OpenProcessToken(process.Handle, TOKEN_READ, out tokenHandle))
      {
        throw new ApplicationException("Could not get process token.  Win32 Error Code: " + Marshal.GetLastWin32Error());
      }

      var elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;

      var elevationResultSize = Marshal.SizeOf((int)elevationResult);
      uint returnedSize;
      var elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);

      var success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
      if (!success)
      {
        Marshal.FreeHGlobal(elevationTypePtr);
        throw new ApplicationException("Unable to determine the current elevation.");
      }

      elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
      Marshal.FreeHGlobal(elevationTypePtr);

      // Special test for TokenElevationTypeDefault.
      // If the current user is the default Administrator, then the
      // process is also assumed to run elevated. This is assumed 
      // because by default the default Administrator (which is disabled by default) 
      //  gets all access rights even without showing a UAC prompt.
      switch (elevationResult)
      {
        case TOKEN_ELEVATION_TYPE.TokenElevationTypeFull:
        _isProcessElevated = true;
        break;
        case TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited:
        _isProcessElevated = false;
        break;
        default:
        // Will come here if either
        // 1. We are running as the default Administrator.
        // 2. We were started using "Run as administrator" from a non-admin
        //    account and logged on as the default Administrator account from
        //    the list of available Administrator accounts.
        //
        // Note: By default the default Administrator account always behaves 
        //       as if UAC was turned off. 
        //
        // This can be controlled through the Local Security Policy editor 
        // (secpol.msc) using the 
        // "User Account Control: Use Admin Approval Mode for the built-in Administrator account"
        // option of the Security Settings\Local Policies\Security Options branch.
        _isProcessElevated = IsAdministrator;
        break;
      }
      }
      else
      {
      _isProcessElevated = IsAdministrator;
      }
      }
      return _isProcessElevated.Value;
    }
  }
}
Advertisements




WPF button in WPF Bing Maps MapItemsControl not reacting to touch events

15 03 2012

Today I a solved problem with buttons placed on a MapItemsControl inside a WPF Bing Maps control not reacting to touch events. The full description of the problem and also the solution (custom TouchButton class with OnTouchDown, OnTouchMove and OnTouchUp overrides) can be found in the Bing Maps support forum.

Here’s the code:

 public class TouchButton : Button
  {
    protected override void OnTouchDown(TouchEventArgs e)
    {
      if (e.TouchDevice.Capture(this))
      {
        IsPressed = true;
        e.Handled = true;
      }
      base.OnTouchDown(e);
    }

    protected override void OnTouchMove(TouchEventArgs e)
    {
      if (e.TouchDevice.Captured == this)
      {
        // Update IsPressed property to indicate if the
        // current touch position is within the element's
        // visuals.
        IsPressed = IsHitTest(e.GetTouchPoint(this).Position);
        e.Handled = true;
      }
      base.OnTouchMove(e);
    }

    protected override void OnTouchUp(TouchEventArgs e)
    {
      if (e.TouchDevice.Captured == this)
      {
        ReleaseTouchCapture(e.TouchDevice);
        IsPressed = false;

        // Raise Click event if touch up occurs within
        // the element's visuals.
        if (IsHitTest(e.GetTouchPoint(this).Position))
        {
          OnClick();
        }

        e.Handled = true;
      }
      base.OnTouchUp(e);
    }

    private bool IsHitTest(Point point)
    {
      var hitTestResult = VisualTreeHelper.HitTest(this, point);
      return hitTestResult != null && hitTestResult.VisualHit != null;
    }




WPF Application Shutdown on Windows 7 x64

3 02 2012

In WPF applications, the Application.Exit event is not (reliably) called – at least on Windows 7 x64 – when the user logs off from or shuts down or restarts the Windows machine. I found that my Application_Exit handler which performs important cleanup was not called.

I tried to remedy this first by hooking into the SystemEvents.SessionEnding event and calling Application.Shutdown from there. After doing this, I found that my Application_Exit handler was called, but then the process seemed to be killed in the middle of the shutdown processing (log output stopped suddenly).

Then I removed the SystemEvents.SessionEnding event handler again and tried to override the Application.OnSessionEnding virtual method. This resulted in the same behavior. The Application_Exit handler was sometimes called, sometimes not, but when it was called, the process died during shutdown processing.

I finally solved this by overwriting the Application.OnSessionEnding method, calling the base class implementation first, then setting SessionEndingCancelEventArgs.Cancel=true to avoid my process being killed by the OS, and finally explicitly calling Application.Shutdown to initiate the shutdown process. The end result is a clean application shutdown while Windows shows the „Application not responding“ screen for several seconds while my application shuts down.

Here’s the code:

public partial class App : Application
{
  protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
  {
    base.OnSessionEnding(e);

    // Cancel application shutdown to prevent Windows 7 killing
    // the process
    e.Cancel = true;

    // Terminate myself
    Shutdown();
  }
}




WinHTTP Proxy Settings

11 08 2011

To enable certain network services like the „Windows Update“ service, BITS or the ServerXMLHttpRequest object on hosts behind a HTTP proxy, you need to configure the WinHTTP proxy settings. On Windows XP this was done using the proxycfg.exe utility. On Windows 7 this has been replaced with netsh commands.

Windows XP

Set Proxy Settings

Syntax:

proxycfg –p {proxy address:port} {bypass list}

Example:

proxycfg –p "172.29.240.193:4480" "<local>;172.29.*"

Delete Proxy Settings

proxycfg –d

Verify Proxy Settings

Check the following registry key

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\WinHttpSettings

Windows 7

Set Proxy Settings

Syntax:

netsh winhttp set proxy {proxy address:port} {bypass list}

Example:

netsh winhttp set proxy "172.29.240.193:4480" "<local>;172.29.*"

Delete Proxy Settings

netsh winhttp reset proxy

Verify Proxy Settings

netsh winhttp show proxy

This information has been copied from:

http://www.bohack.com/2010/08/windows-server-2008-replacement-of-proxycfg-exe/





.NET Framework Source Code unter Windows 7 RC

13 05 2009

Ich habe gerade versucht, unter Windows 7 RC (Build 7100) mit VS2008 SP1 in den .NET Framework Source Code zu debugging. Ich habe dazu alles eingerichtet wie in Shawn Burke’s Blog beschrieben. Auch den Visual Studio 2008 QFE KB944899 habe ich installiert. Die Symbole (.pdb Dateien) wurden auch herunter geladen, allerdings fehlte der Quellcode.

Anhand der Datei PESymbolList.xml konnte ich feststellen, dass unter Windows 7 RC eine neuere Version des .NET Frameworks installiert ist, z. B. System.dll, Version 2.0.50727.4918 (NetFXspW7.050727-4900). Für diese Version steht offensichtlich (noch) keine Quellcode zur Verfügung.