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
 Monday, April 06, 2009

I was putting together a WinForms utility application today that consists of a TabStrip control on the main dialog box.  On a themed system the TabStrip displays a nice gradual gradient on the page.  As part of this application I wanted to output statistical and detailed information about the user's selections in a readonly fashion, but I wanted to have the text appear as though it were written directly on the TabStrip page rather than within a control.

Of course my first thought was to use a Label control, but I almost immediately wrote that off as the amount of information to be displayed was prohibitive and variable.  The data being displayed might exceed the space on the TabStrip, so scrolling the information is a must.

Next I thought of using a TextBox control, but without some pretty intense subclassing and overriding, I wouldn't be able to achieve the 'written-on' look I was after.  The TextBox will always render an opaque background color.  Sure, there are ways of faking transparency, like overriding TextBox and adding a PictureBox and telling the parent control to WM_PRINT into it, but that seems to be a kludgy way of getting the results I wanted.

Additionally, there are mechanisms to use a RichTextBox (v5.0) to achieve transparency that are well documented online, but try as I might with these implementations, I found them to be quite buggy.  In particular, the appearance of the scrollbars was erratic and sometimes didn't render properly with the control was resized.  After battling with it for a few hours I gave up.  It wasn't for lack of desire, it just wasn't worth it given the scope of the project I was working on.

So this morning I went back to the drawing board and came up with a solution in about 45 minutes that seems to do exactly what I wanted.  I've called this control the ScrollableLabel.  Essentially, it offers the readonlyness of a Label control and the scrollability of a TextBox.  This implementation here is very fundamental.  I plan on enhancing it some more to incorporate user-defined colors, headers, and much more.

I made some executive decisions about how this control should render.  For instance, it will always use your system's colors for text and I set the BackColor property of the UserControl to Transparent via the designer.  For both of these I plan on making them user-selectable choices, but for not, they're the look I was after.

public partial class ScrollableLabel : UserControl {
   public ScrollableLabel() {
      InitializeComponent();
      SetStyle(ControlStyles.Selectable, false);
      // the preferred mechanism for enabling double-buffering is to set the DoubleBuffered property
      // of the control rather than the equivalent SetStyle(DoubleBuffer | UserPaint | AllPaintingInWmPaint, true).
      DoubleBuffered = true;
      AutoScroll = true;
      AutoScrollMinSize = Size.Empty;
   }

   private SizeF _actualTextSize;

   public override string Text {
      get { return base.Text; }
      set {
         base.Text = value;
         calcLabelDimensions();
         Invalidate();
      }
   }

   protected override void OnScroll(ScrollEventArgs se) {
      base.OnScroll(se);
      Invalidate();
   }

   public void Clear() {
      Text = string.Empty;
   }

   #region Hidden design-time properties
   [Browsable(false)]
   public override bool AutoScroll {
      get { return true; }
      set { /* do nothing for now; control is always AutoScroll */ }
   }


   [Browsable(false)]
   public override Color ForeColor {
      get { return SystemColors.ControlText; }
      set { /* do nothing for now; only ControlText is currently supported */ }
   }
   #endregion

   private void calcLabelDimensions() {
      using ( Graphics g = CreateGraphics() ) {
         // assume that no text should wrap
         _actualTextSize = g.MeasureString(Text, Font);
         AutoScrollMinSize = Size.Round(_actualTextSize);
      }
   }

   protected override void OnPaint(PaintEventArgs e) {
      RectangleF rct = new RectangleF(AutoScrollPosition, _actualTextSize);
     
Brush br = Enabled
                    ? SystemBrushes.ControlText
                    : SystemBrushes.GrayText;
      e.Graphics.DrawString(Text, Font, br, rct);
   }
}

When this control is added to a Form it will automatically add scrollbars if the contents exceed the dimensions of the control.  At design-time, however I wanted to make sure that I could see where the ScrollableLabel was positioned so I created a simple Designer.

public sealed class BorderlessBorderDesigner : ControlDesigner {
   protected override void OnPaintAdornments(PaintEventArgs pe) {
      base.OnPaintAdornments(pe);
      if ( BorderStyle.None == getBorderStyle() ) {
         Rectangle rct = Control.ClientRectangle;
         rct.Width -= 1;
         rct.Height -= 1;
         using ( Pen p = new Pen(SystemColors.ControlDark) ) {
            p.DashStyle = DashStyle.Dash;
            pe.Graphics.DrawRectangle(p, rct);
         }
      }
   }

   private BorderStyle getBorderStyle() {
      UserControl ctl = Control as UserControl;
      if ( null != ctl ) return ctl.BorderStyle;
      return BorderStyle.None;
   }
}

Then, I added [Designer(typeof(BorderlessBorderDesigner)] to my ScrollableLabel class declaration.

It's a simple control that achieves a simple goal.  I'll post my updates to it as I get a chance to modify it and make it all the more flexible and powerful.

Enjoy!

.NET | C# | WinForms | Controls
Monday, April 06, 2009 9:19:13 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, December 23, 2008

A few weeks ago I made some updates to my Regular Expression Assembly Builder that I wrote some time back.  These updates have long been requested by users and I finally carved out some time to get them implemented.

These are the new features in verion 2.0.0.3:

  • NEW: Added the ability to assign a description to each regular expression.  These are stored in the RegexDescriptionAttribute class.
  • UPD: Rearranged the RegexEditor to accommodate the new description, changing some labels in the process, and tab order.
  • UPD: Made the validation textboxes 'public' so their references aren't be continually lost each time the editor is opened in the designer.
  • UPD: Updated About box link to point directly to http://blog.devstone.com/aaron.
  • UPD: Added the regular expression description to the regular expression grid.

Though perhaps a small update, I hope the changes are found to be useful.

NOTE: The File menu has two 'Save' options: Save Project and Save Release Version.  These were added a few versions ago, but I'd like to clarify their usage.  Save Project will save your .dll (because the application works natively with a .dll as its project).  Save Release Version will create a \Release directory and save your .dll there as well.  However, the Release Version has a few things (like the regular expression descriptions) stripped out.  It's the Release version you'll want to reference in your applications if you plan to distribute the .dll; otherwise, you'll end up with a dependency on RegexAssemblyBuilder.exe and you wouldn't want to distribute that :).  If you source control your .dll, I'd recommend storing both .dll files so you have your project and your release version on hand.

Also, partially due to my migration to dasBlog, I've not gotten around to updating my little downloader application.  Therefore, you can simply download the application directly here.  Let me know if you have any issues with it.

For a history of the application, please check out this post and its links as well.

Tuesday, December 23, 2008 10:21:32 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, December 09, 2008

Alright, this has been a downright stumper and I'm currently at a loss.  For this reason, therefore, I'm opening up to the community to see if anyone out there has any recommendations to this dilemma.

The issue revolves around running a 32-bit application on a 64-bit version of Windows and the application needs to pull values from the Registry.  One thing that 64-bit Windows will do, to help achieve compatibility with 32-bit applications by transparency, is remap (redirect) registry calls.  In other words, when I request "HKLM\SOFTWARE\Test Company\Application Name\Key Name" it actually redirects this to "HKLM\SOFTWARE\Wow6432Node\Test Company\Application Name\Key Name" for storage and retrieval.  So far so good.

Well, I have an old 32-bit application written in VB 6.0 that writes a value to the registry that my .NET application must read.  For argument sake, let's say that the value is ultimately written to the HKLM\SOFTWARE\Wow6432Node\Company\Application\Settings key with a name of Value01.  This is precisely what I'd expect.  However, when I attempt to read this value in my .NET application (I'm using VS 2008, .NET 3.5) I'm getting erratic results.

This is what I have:

My application is explicitly compiled as a 32-bit application.  To do this, I've set the Platform target project property to x86.  I've also confirmed this using the CorFlags.exe, even going to far as using the /32BIT+ switch to ensure the setting.

I have a few varieties of code that seem like they should successfully read the value:

Take 01:

private static string getValue01() {
   return getCompanySetting("Application", "Settings", "Value01", "Not found");
}

private static string getCompanySetting(string appName, string section, string key, string defaultValue) {
   string subKey = string.Format(@"SOFTWARE\Company\{0}\{1}", appName, section);
   return getRegValue(subKey, key, defaultValue);
}

private static string getRegValue(string subKey, string key, string defaultValue) {
   RegistryKey key = Registry.LocalMachine.OpenSubKey(subKey);
   if ( null != key ) {
      object val = key.GetValue(key);
      return ( string.IsNullOrEmpty(val) ? string.Empty : val.ToString();
   }
   else {
      return new Win32Exception(Marshal.GetLastWin32Error()).Message;
   }
}

Take 02:  This is almost the same except that I use the WinAPI directly to read the value rather than the managed objects.

private static string getValue01() {
   return getCompanySetting("Application", "Settings", "Value01", "Not found");
}

private static string getCompanySetting(string appName, string section, string key, string defaultValue) {
   FieldInfo fi = Registry.LocalMachine.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)[0];
   SafeHandle hKey = ( SafeHandle )fi.GetValue(Registry.LocalMachine);
   string subKey = string.Format(@"SOFTWARE\Company\{0}\{1}", appName, section);
   return getRegValue(hKey, subKey, key, defaultValue);
}

private static string getRegValue(SafeHandle hKey, string lpszSubKey, string szKey, string szDefaultValue) {
   StringBuilder szBuffer = new StringBuilder(255);
   uint lBufferSize = ( uint )szBuffer.Capacity;

   int lResult;
   IntPtr phkResult;
   uint lpType;

   lResult = WinApi.RegOpenKeyEx(hKey, lpszSubKey, 0, 1, out phkResult);
   if ( WinApi.ERROR_SUCCESS != lResult )
      return new Win32Exception(Marshal.GetLastWin32Error()).Message;

   lResult = WinApi.RegQueryValueEx(phkResult, szKey, 0, out lpType, szBuffer, ref lBufferSize);
   WinApi.RegCloseKey(phkResult);

   return ( WinApi.ERROR_SUCCESS == lResult )
               ? szBuffer.ToString()
               : szDefault;
}

internal static class WinApi {
   internal const int ERROR_SUCCESS = 0;
   internal const uint KEY_WOW64_64KEY = 0x0100;
   internal const uint KEY_WOW64_32KEY = 0x0200;

   [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegOpenKeyEx")]
   internal static extern int RegOpenKeyEx(SafeHandle hKey, string subKey, uint options, uint sam, out IntPtr phkResult);

   [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
   internal static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, out uint lpType, StringBuilder lpData, ref uint lpcbData);
 
   [DllImport("advapi32.dll", SetLastError = true)]
   internal static extern int RegCloseKey(IntPtr hKey);
}

In both of these cases, the code runs flawlessly when I run it from Visual Studio with the debugger attached (e.g., F5).  However (and here's the rub) it fails to retrieve the value when I run it directly within Windows or in VS without the debugger (e.g., CTRL+F5).

In all my investigations, it appears that the application IS indeed a 32-bit image (Process Explorer confirms it), but it will simply not redirect to the \Wow6432Node unless it's running with the debugger attached.

Any thoughts of how to address it?  It's driving me nuts.

Tuesday, December 09, 2008 7:37:44 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  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
 Monday, September 01, 2008

I ran across some interesting (read "unexpected") behavior the other day when writing a .NET WinForms control.  This particular control has some logic that executes if and only if the host OS has visual themes enabled.

In order to check for the existence of enabled themes, I call a method called 'IsVisualStylesEnabled()' which is defined as below:

public static bool IsVisualStylesEnabled() {
   bool isEnabled = false;
   try {
      OperatingSystem os = Environment.OSVersion;
      // decide whether the OS even supports visual styles
      bool isSupported = ( os.Platform == PlatformID.Win32NT )
                         && ( ( ( os.Version.Major == 5 ) && ( os.Version.Minor >= 1 ) )
                              || ( os.Version.Major > 5 ) );
      if ( isSupported ) {
         int majorDllVersion = getComCtlMajorVersion();
         isEnabled = ( majorDllVersion > 5 ) && IsThemeActive() && IsAppThemed();
      }
   }
   catch ( Exception ) {
      // do nothing explicitly
   }
   return isEnabled;
}


private static int getComCtlMajorVersion() {
   DLLVERSIONINFO dllVersion = new DLLVERSIONINFO { cbSize = Marshal.SizeOf(typeof( DLLVERSIONINFO )) };
   DllGetVersion(ref dllVersion);
   return dllVersion.dwMajorVersion;
}

 

[StructLayout(LayoutKind.Sequential)]
private struct DLLVERSIONINFO {
   public int cbSize;
   public int dwMajorVersion;
   public int dwMinorVersion;
   public int dwBuildNumber;
   public int dwPlatformID;
}

 

[DllImport("comctl32.dll", CharSet = CharSet.Auto)]
private static extern int DllGetVersion(ref DLLVERSIONINFO version);


[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
private static extern bool IsThemeActive();


[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
private static extern bool IsAppThemed();

This code is pretty standard.

At design-time (i.e., within Visual Studio) my control would render properly.  The problem, however, was evident when I executed the code - it would think that visual styles were disabled and degrade gracefully to the non-themed output.

After a little bit of debugging I discovered that the comctl32.dll version being loaded at design-time was version 6 (the desired version) whereas at runtime I was getting version 5.  The first thing I tried was embedding a manifest into my executable to explicitly load version 6.  No dice - I was still getting version 5.

Ultimately, however, I did discover the source of the issue: I was calling the IsVisualStylesEnabled() too early in the load process of my application.  This was made apparent by placing my control on a form other than the start-up form - everything worked!

When initialized, my control began setting up UI components such as brushes, pens, etc and some of this logic was based on whether the themes were enabled.  Most, if not all, of the setup logic occurs in the InitializeComponent() method which is called from the .ctor() of the control.

In order to guarantee that the proper version of comctl32.dll is loaded, you cannot call the GetDllVersion() method until at least the OnHandleCreated() method of your control.  If you call it earlier, you effectively lock your application (and consequently the host application if your application is a visual component) into using version 5 whether or not the manifest dictates otherwise.

For my purposes, I had to wait until the OnPaintBackground() method to initialize the brushes and other auxiliary objects, but that at least happens after the OnHandleCreated().

Monday, September 01, 2008 10:50:27 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, August 28, 2008

I've been putting together a nice little WinForms Wizard control recently that had a few twist and turns to its development.  In particular, I wanted to make the design-time experience a good one for the end user.  I wanted to support design-time navigation of the wizard pages, drag/drop of control to each child page, reordering of the pages, and much more.  In fact, as soon as I tidy up the code a bit and flesh it out a bit, I'll be posting the component here on my blog for general use.  But enough of that...

The wizard control has a rich design-time experience.  I owe some of the thanks for the functionality to control's ControlDesigner and DesignerActionList.  Among many other things, these classes provides a mechanism for a developer to associate custom verbs and actions with the control at design-time.  These actions are made available to the developer through the smart-tag menu when the control is selected on its design surface.

It is through these custom actions that I've given the programmer the ability to manage pages on the wizard.  Internally, these pages are represented not only as controls on the wizard itself, but also in a collection of pages that the wizard uses to properly navigate the sequence of pages.  It is this collection that gets persisted (serialized) in the form's InitializeComponent() method when saving changes made to the wizard at design-time.

I quickly discovered, however, that all wasn't green grass and blue skies.  I wanted to support reordering the pages through the design-time smart-tags.  While I was able to easily reorder the elements in the page collection and even update the control's display at design-time to reflect the changes, the Visual Studio environment didn't register the change.  Unless I then went ahead and further edited a property through Visual Studio itself (effectively to cause a change in the environment), the reordering of the pages never persisted.  So I had to figure out how to notify Visual Studio that a change had been made.

Fortunately, this isn't difficult, but it's not very intuitive at the same time.

The ControlDesigner's GetService() method provides access to the services available to the control at design time.  Among these is the IComponentChangeService.  It is through this service that you can notify the designer of a change.  To do so, you need a property to update.  I, for one, don't really like the idea of utilizing an existing property for this purpose (though it could be done).

What I did in my solution was create a design-time-only property in the ControlDesigner.  Then, when a change is made that requires the change notification I call a method which 'updates' the property.  The property doesn't really get updated, but at least the code is clearer.

In the control designer:

protected override void PreFilterProperties(IDictionary properties) {
   base.PreFilterProperties(properties);

   Attribute[] attribs = {
                            new BrowsableAttribute(false),
                            new DesignOnlyAttribute(true)
                         };
   PropertyDescriptor prop = TypeDescriptor.CreateProperty(GetType(), "DesignTimeChange", typeof(string), attribs);
   properties.Add("DesignTimeChange", prop);
}

/// <summary>
/// Property created exclusively for the purpose of notifying the
/// designer of changes to properties made through the designer
/// (such as reordering the pages).
/// </summary>
public string DesignTimeChange { get; set; }

Then, also in the Designer, a method that causes a change to be acknowledged:

private void notifyOfChange() {
   PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["DesignTimeChange"];
   getComponentChangeService().OnComponentChanged(this, prop, null, null);
}

That's all there is to it.  When an action occurs in the designer that would not otherwise register a change in the environment, I simply have it call the notifyOfChange() method.

A little more work would be involved if the change were coming from outside the designer.  For instance, if the change was made within the collection or within the control, you'd first have to evaluate whether it's running in design mode, and then retrieve a reference to the ControlDesigner, invoking the method (it'd probably have to be made internal (and it's name appropriately Pascal-cased) rather than private).

Maybe something like this (NOTE: untested, but seemingly approximately accurate):

if ( DesignMode ) {
   IDesignerHost host = ( IDesignerHost )Site.Container;
   ControlDesigner designer = ( ControlDesigner )host.GetDesigner(this);
   designer.NotifyOfChange();
}

Thursday, August 28, 2008 6:47:20 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, July 13, 2008

I've recently been thinking about database authentication in an ASP.NET application.  While this concept is most definitely unoriginal - a great many web applications must perform data access of some sort - it is easy to get confused by the many variations of data access.

To summarize in brief, there are two primary methods for authenticating to a database: Windows Authentication (also called Integrated Security), and Sql Authentication.  All SQL Servers support Windows Authentication and it's the natural choice.  Enabling Sql Authentication, on the other hand, requires that the server be configured to support it; this can be accomplished either during installation or after the fact.  What's more, not all organizations will support Sql Authentication.

Despite the aforementioned, perhaps he simplest technique to authenticate a user in the database is to use Sql Authentication.  It requires that 1) a login be created in the SQL Server and mapped to the database and 2) the connection string contain the appropriate User ID and Password parameters.  It is the simplest also because the database can be located locally or remotely and the authentication will succeed.

As soon as you use Windows Authentication in a web application the level of difficulty raises, even if only slightly.  To properly implement Windows Authentication in a web application, the identity of the process must be determined.  For purposes of this article, as it is not directly pertinent and can be quite large in scope, I will refrain from exploring the topic of ASP.NET process identity.  Suffice it to say that on a Windows Server 2003/2008 the default identity for an IIS application pool is NT AUTHORITY\NETWORK SERVICE.  On other platforms this identity will vary.

ASP.NET Application - Anonymous or Integrated Windows Authentication, No Impersonation

If the requesting user is not being impersonated, IIS will access resources using the identity of the application pool (e.g., NT AUTHORITY\NETWORK SERVICE).

ASP.NET Application - Anonymous Authentication, Impersonation Enabled

If your web application is using anonymous access and the user is being impersonated a couple of things can happen.  First, if, in your web.config file, you specify <identity impersonate="true" />, IIS will impersonate the anonymous user specified for your web site (e.g., IUSR_MACHINENAME, IUSR, etc.).  A user may be specified in the web.config file as well via <identity impersonate="true" userName="DOMAIN\User" password="xxx" />.  In this case, the web server will impersonate the user on the server.  NOTE: This user needs, at a minimum, Write access to the \Temporary ASP.NET Files folder.

ASP.NET Application - Integrated Windows Authentication, Impersonation Enabled

This is functionally similar to the previous item except if you use the simple form of the identity element (i.e., <identity impersonate="true" />), the user account that is performing the request is impersonated.  This is quite helpful especially if you need to control access to a server-side resource by ACL.

When connecting to a SQL Server database that is local to the web server, that is, it is physically on the same machine, you can grant access to the identity under which the web application is running (ASPNET, NT AUTHORITY\NETWORK SERVICE, IUSR_MACHINENAME, or the impersonated user for instance).

When the database is physically remote, however, care must be taken to properly flow user credentials to remote server.  That is, in fact, something of a misnomer.  Credentials aren't actually flowing to the remote server, but rather an authentication token.  This token is generated on the computer where the user identity is authenticated.

A user token generated on the web server will be able to flow to the remote machine without any extra work.  This token is created in the following scenarios:

  1. Using Basic authentication - the user is actually logged-in on the server.
  2. Using no impersonation - the website is locally authenticated as ASPNET or NT AUTHORITY\NETWORK SERVICE.
  3. The impersonation identity is manually set in the web.config's <identity /> element.

NTLM will permit the token a 'single hop' to the remote server.  Provided the identity in question has access to the database, the connection will be successfully established, and the requested information returned.

If, on the other hand, the web server is impersonating the requesting user, the solution is not so cut-and-dry.  The user's token is created on the client computer and makes a 'single hop' to the web server.  When making a request to a remote server a 'double hop' must be performed.  NTLM will prohibit the token from being passed to the server and you will encounter an error resembling "Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'".

This error may be confusing and disheartening to a developer who has not seen it before.  This is often the result of having tested the website on a development machine (which is usually local) - "It works on my machine."  Well, it works because the token is created locally and only needs a 'single hop' to get to the database server.

There are a few solutions that may help to address the oft-times confusing and frustrating 'double hop' conundrum.

  1. Setup Constrained Delegation
    Less far-sweeping than full delegation, Kerberos constrained delegation allows tokens to flow against a limited set of services.  This option is only available on Windows Server 2003+.  To properly implement constrained delegation, you must set up a Service Principal Name (SPN), identifying the service and the machine trusted for delegating the tokens.  While simple in principle, I've found this solution to be tempermental and very sensitive.  You may seemingly have everything set up correctly and still not get it to work.
  2. Fall back (a.k.a., revert) to the base IIS process's identity
    Having fought the 'double hop' issue more times than I care to admit, and for more hours than are healthy, this idea came to me last week while discussing the 'problem' with a friend and associate at Microsoft.  While this idea is not revolutionary nor original, it is definitely useful.  This technique allows your website to retain its ability to identify the calling user while deferring to the process's identity (e.g., NT AUTHORITY\NETWORK SERVICE) to access remote databases.

To make this (#2) work we need the ability to 'undo' the impersonation that ASP.NET performs and then reimpersonate when we're finished.  Unfortunately, this functionality is not native to the .NET Framework as far as I'm aware - it is, however, accessible via the RevertToSelf() Window API function.  In order to encapsulate the logic of reverting to the base process's identity and restoring impersonation, I've created a simple disposable class which is presented below:

/// <summary>
/// Represents a disposable class that, for the lifetime of the object, runs using the
/// underlying identity of the process.  This class is useful within an ASP.NET application
/// that is impersonating the caller, but needs to access network or directory resources
/// that would otherwise be prohibited without setting up constrained delegation in Active
/// Directory.
/// </summary>

public sealed class RevertImpersonator : IDisposable {
  public RevertImpersonator() {
     // acquire the identity of the current user (the user being impersonated)
     _userIdentity = WindowsIdentity.GetCurrent();

     // revert to the underlying process' identity
     // for ASP.NET applications that impersonate, this will be the identity of the IIS process
     // (e.g., the identity of the application pool which, by default, is NETWORK SERVICE).
     // NOTE: the NETWORK SERVICE account will access network resources as the MACHINE$ account, local resources as NT AUTHORITY\NETWORK SERVICE.

     RevertedIdentity = ( 0 != RevertToSelf() );
  }

  private readonly WindowsIdentity _userIdentity;

  
  [DllImport("advapi32.dll")]
  private static extern int RevertToSelf();

  ~RevertImpersonator() {
     restore();
  }


  /// <summary>
  /// Returns whether the user's identity was successfully reverted on initialization.
  /// </summary>
  public bool RevertedIdentity { get; private set; }


  public void Dispose() {
     GC.SuppressFinalize(this);
     restore();
  }


  private void restore() {
     // re-impersonate the user during cleanup
     if ( RevertedIdentity )
        _userIdentity.Impersonate();
  }
}

Effectively, this class allows you to encapsulate database calls thus:

using ( new RevertImpersonator() ) {
   // perform data access here
}

An important note is warranted.  As previously mentioned, using this class will revert the identity on the thread to the process's identity.  You can set your IIS Application Pool to use a domain account rather than the default NT AUTHORITY\NETWORK SERVICE.  Doing so will require that the domain user have access to the database in question.  If, however, you decide to use the default, you must be aware of a few items.  First, a local database will be accessed with the NT AUTHORITY\NETWORK SERVICE account as expected.  A remote database will be accessed with the MACHINE$ account - this is how the NT AUTHORITY\NETWORK SERVICE account is authenticated remotely.

Sunday, July 13, 2008 9:02:49 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, June 27, 2008

I was putting together a small application today that presents data in a TextBox.  Now while this isn't too uncommon :), the text gets added to the TextBox a line at a time via the TextBox's .AppendText() method.  I needed to do this repeatedly and in succession.  As it turns out, an unwelcome side effect of AppendText is that it scrolls the TextBox as the text is appended.  This resulted in the TextBox getting cleared and then 'wiping' down as the text was added.  Not only was this visually unappealing, but I wanted the insertion point to stay at the top of the contents.

To my knowledge, which is quite flawed and limited, there's not a built-in mechanism in .NET that provides this functionality.  Sure, I could override the TextBox control and manage the WndProc method and/or use SetStyle to make the control user drawn.  But I didn't want to do that.  Well, accomplishing this is quite easy.

There are a few ways we can do it:

  1. Use the LockWindowUpdate() API function.
  2. Use SendMessage() API function with the WM_SETREDRAW message.

LockWindowUpdate() is slick and arguably easier to use, but it's intended purpose isn't to suppress the redrawing of controls in this manner.  Plus, you can only have one window locked at a time.  So I threw that one out.

I wrapped the logic to lock and unlock the redrawing of the control in an IDisposable object so I wouldn't have to worry about remembering to clean up after myself.  My class is as follows:

public class LockVisualUpdate : IDisposable {
   public LockVisualUpdate(IWin32Window control) {
      _hWnd = control.Handle;
      SendMessage(control.Handle, WM_SETREDRAW, 0, 0);
   }


   private readonly IntPtr _hWnd;
   private const int WM_SETREDRAW = 0x000B;


   [DllImport("user32.dll")]
   private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);


   [DllImport("user32.dll")]
   private static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);


   public void Dispose() {
      SendMessage(_hWnd, WM_SETREDRAW, 1, 0);
      InvalidateRect(_hWnd, IntPtr.Zero, false);
   }
}

Really simple to use.  To consume it, I simply have to do the following:

using ( new LockVisualUpdate(textBox1) ) {
   textBox1.AppendText(...);
   // ...repeated as often as necessary to populate the control

   // when finished, position the insertion point to the top of the control
   textBox1.SelectionStart = 0;
   textBox1.ScrollToCaret();
}

.NET | C#
Friday, June 27, 2008 1:28:14 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback