Thursday, October 22, 2009

I had the need today to create an application must run with administrative privileges.  Fortunately, this is a pretty straightforward endeavor.

On Windows Vista, Windows 7, or Windows Server 2008 (and beyond) I wanted to take advange of the Admin Approval Mode (AAM) or Over The Shoulder (OST) UAC elevation prompt automatically (and as a side-effect have the little shield icon accompanying my app's icon).  This wouldn't be possible on a previous OS (such as Windows Server 2003 or XP), so I needed to add an explicit permission check.

To get the UAC prompt all you need to do is embed an application manifest.  Vista+, when displaying the application icon, will probe the app and, upon finding the manifest requiring admin rights, will display the icon properly.

Create a manifest file (e.g., app.exe.manifest) in your project.  This is what mine looked like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity version="1.0.0.0" name="APPLICATION_NAME"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

By default (if you don't create a manifest), a .NET application will have the <requestedExecutionLevel /> set to 'asInvoker' and run the application as the user invoking it.  You can set this to one of three values: asInvoker, highestAvailable, and requireAdministrator.  Also, don't forget to set the name to match your application's name.

Then, to embed the application you can do a few things.

One technique is to use the mt.exe (ManifestTool) from the Visual Studio Command Prompt:

mt.exe -manifest app.exe.manifest -updateresource:app.exe,#1

But I don't believe this would work if your application is signed with a strong name unless you were to delay-sign it.

The easier (and better) approach is to use the Project Properties --> Application tab.  Simply select your app.exe.manifest file in the Manifest dropdown.  It will automatically be embedded.

NOTE: In order to debug your application that requires admin rights you'll need to run Visual Studio from an elevated token.  Otherwise you won't have privileges enough to attach to the process.

Next, I wanted to ensure that the user runs the application with administrative rights.  This check was placed particularly for the pre-Vista operating systems which didn't have UAC.  To do this I added the following code:

[STAThread()]
private static void Main() {
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);

   // check to make sure that the user is running with administrative rights.
   // in Vista/Win7/Svr2008 this is automatically taken care of via the
   // embedded manifest (which ensures administrative rights) via UAC.

   try {
      if ( !userHasAdminRights() ) {
         MessageBox.Show("This application requires administrative privileges.",
                         "Unauthorized Access",
                         MessageBoxButtons.OK, MessageBoxIcon.Information);
         return;
      }
   }
   catch ( Exception er ) {
      MessageBox.Show(er.ToString());  // Of course you'd handle this differently
   }

   Application.Run(new MainForm());
}


private static bool userHasAdminRights() {
   WindowsIdentity identity = WindowsIdentity.GetCurrent();
   return ( null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator) );
}

Pretty simple, really.

C# | Security
Thursday, October 22, 2009 7:28:57 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, September 07, 2008

When granting database permissions to built-in security accounts you need to be aware of potentially localized account names.

Just this past week I ran into an issue when attempting to execute a SQL Server database script on a German server OS.  The database script in question had hard-coded the usage of 'NT AUTHORITY\NETWORK SERVICE' as the account name for granting database permissions.  As it turns out, however, this account doesn't exist in the German version of Windows.  Instead, the account is 'NT-AUTORITÄT\NETZWERKDIENST'.  I found it interesting that the account name is not localized on other non-English versions of Windows.

Unless I misunderstanding something, the documentation indicates that "the name of the account in all locales is NT AUTHORITY\NETWORK SERVICE" so I was initially confused by this.

In this particular case my script file is, conveniently, not executed 100% verbatim.  Via a little script engine that I wrote, I have the ability to update variables with runtime values.  In this case, rather than use the exact string 'NT AUTHORITY\NETWORK SERVICE', I replaced it with a variable and determined the value to use at runtime.

Resolving to the properly localized account name is pretty easy:

private static string getNetworkServiceAcctName() {
   SecurityIdentifier id = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
   NTAccount acct = ( NTAccount )id.Translate(typeof( NTAccount ));
   return acct.Value;
}

What's quite interesting is after researching the issue and updating the application to properly resolve the account name, we stumbled upon a blog post made a couple of years ago that resolved the exact same issue.

Sunday, September 07, 2008 6:08:42 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback