Programming Elevated Privilege/UAC

by Weifen Luo (DevZest) 14. August 2009 16:01

 

image

Introduction

Running applications with more privileges than required is against the principle of least privilege, and may have potential security vulnerability. To defend this, Windows Vista introduces User Account Control (UAC), to protect operating system by running applications with reduced privileges (as a normal user), even the current user is signed in as an administrator. More and more XP/2K users are also use normal user account for daily use. [Edit] Read UAC Demystified first to fully understand UAC.

There are two common mistakes that developers tend to make:

  1. Request the end-user to run an application with administrator privilege even though this is not necessary, most of the time because of bad design practices. These applications either scare away end-users, or potentially have security vulnerability.
  2. Do not request the end-user to run the application elevated but try to perform operations that require administrator privilege. These applications simply break under Windows Vista or Windows XP/2K normal user account.

The downloadable sample code demonstrates how to programming elevated privilege/UAC. Both WPF and Windows Forms sample applications are provided. Run the application for the following scenarios to see the difference:

  • Normal user, Windows XP/Windows Vista: the UAC shield icon is displayed. Clicking “Save to C:\” displays “Run As” dialog, asking user to enter administrator password to continue;
  • Administrator, Windows XP/Windows Vista with UAC disabled: the UAC shield icon is hidden. Clicking “Save to C:\” completed without any dialog;
  • Administrator, Windows Vista with UAC enabled: the UAC shield icon is displayed. Clicking “Save to C:\” displays dialog asking user’s permission to continue.

How does it work

Code can only be elevated at process level when startup, which means that a running process cannot be elevated. In order to elevate an existing application, a new instance of the application process must be created, with the verb “runas”:

private static string ElevatedExecute(NameValueCollection parameters)
{
    string tempFile = Path.GetTempFileName();
    File.WriteAllText(tempFile, ConstructQueryString(parameters));

    try
    {
        ProcessStartInfo startInfo = new ProcessStartInfo();
        startInfo.UseShellExecute = true;
        startInfo.WorkingDirectory = Environment.CurrentDirectory;
        Uri uri = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase);
        startInfo.FileName = uri.LocalPath;
        startInfo.Arguments = "\"" + tempFile + "\"";
        startInfo.Verb = "runas";
        Process p = Process.Start(startInfo);
        p.WaitForExit();
        return File.ReadAllText(tempFile);
    }
    catch (Win32Exception exception)
    {
        return exception.Message;
    }
    finally
    {
        File.Delete(tempFile);
    }
}

 

The above code snippet will run the application as if right clicking the application icon in Windows Explorer and select “Run as administrator” (or “Run As…” in Windows XP) from shortcut menu:

Untitled
Windows Vista UAC dialog

Untitled
Windows XP Run As dialog

After end-user confirmed the execution of the program as administrator, another instance of the same application (ElevatedPrivilege.exe) is executed without an user interface. Note there are two ElevatedPrivilege.exe processes executed at the same time: one displays an user interface running without elevated privilege, another one running at background with elevated privilege. The first process waits until the second process finished execution.

 

The main entry of the program distinguishes these two situations by examining the provided argument:

[STAThread()]
static void Main(string[] args)
{
    if (args.Length == 0)
    {
        App.Main();
        return;
    }

    if (args.Length == 1)
    {
        string tempFile = args[0];
        NameValueCollection parameters = HttpUtility.ParseQueryString(File.ReadAllText(tempFile));
        File.WriteAllText(tempFile, string.Empty);

        try
        {
            string text1 = parameters["Text1"];
            string text2 = parameters["Text2"];
            string fileName = parameters["FileName"];
            Window1.DoSaveFile(text1, text2, fileName);
        }
        catch (Exception ex)
        {
            File.WriteAllText(tempFile, ex.Message);
        }
    }
}

Note how the NameValueCollection parameter is serialized as query string and passed through a temp file.

Display the UAC shield icon – your permission, please

If part of your application is going to require an administrator privilege, and current application is not running as administrator, a shield icon should be displayed with the button or menu to let the end-user know you’re asking their permission to continue, like Windows Vista does. The following code determines if the current application is running as an administrator:

internal static bool IsAdmin
{
    get
    {
        WindowsIdentity id = WindowsIdentity.GetCurrent();
        WindowsPrincipal p = new WindowsPrincipal(id);
        return p.IsInRole(WindowsBuiltInRole.Administrator);
    }
}

Based on this value, you can determine whether an UAC shield icon should be displayed; and in your code behind, elevate privilege when necessary:

private void SaveToRootFolder_Click(object sender, RoutedEventArgs e)
{
    string fileName = @"C:\Test.txt";
    if (App.IsAdmin)
        DoSaveFile(textBox1.Text, textBox2.Text, fileName);
    else
    {
        NameValueCollection parameters = new NameValueCollection();
        parameters.Add("Text1", textBox1.Text);
        parameters.Add("Text2", textBox2.Text);
        parameters.Add("FileName", fileName);
        string result = Program.ElevatedExecute(parameters);
        if (!string.IsNullOrEmpty(result))
            MessageBox.Show(result);
    }
}

To display the UAC shield icon is different in WPF and Windows Forms. In WPF, you can define an attached read only property App.IsAdmin, then restyle the button based on the value of this property, as demonstrated in the sample application. In Windows Forms, you can send BCM_SETSHIELD window message if your application is running under Windows Vista or later, or you need to set Button.Image property, as demonstrated in App.SetUacShield method.

Tags: ,

Code Sample | Windows Forms | WPF

Comments

8/14/2009 4:07:25 PM #

Programming Elevated Privilege/UAC

You've been kicked (a good thing) - Trackback from DotNetKicks.com

DotNetKicks.com |

9/24/2009 11:23:20 PM #

This is really useful stuff.  However, testing on XP (I force IsAdmin to false for testing purposes), the spawned process is unable to open the temp file.  According to procmon.exe, it is getting access denied.  Which is strange as it is running as the same user.

Ever come across this?  

Andy H United Kingdom |

4/21/2013 7:13:59 AM #

Pingback from eonlinegratis.com

Elevate A Running Program To Administrator (Windows 7) | Click & Find Answer !

eonlinegratis.com |

6/18/2014 4:31:02 AM #

Pingback from drwindows.de

Speedfan mit eingeschalteter UAC starten - aber wie?

drwindows.de |

Comments are closed

Copyright DevZest, 2008 - 2014