views:

268

answers:

1

I am having trouble using impersonation to delete a PerformanceCounterCategory from an MVC website. I have a static class and when the application starts it checks whether or not a PerformanceCounterCategory exists, and if it contains the correct counters. If not, it deletes the category and creates it again with the required counters.

It works fine when running under the built in webserver Cassini, but when i try run it through IIS7 (Vista) i get the following error:

Access is denied
Description:
An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.ComponentModel.Win32Exception: Access is denied

My use of the code:

var username = "user";
var password = "password";
var domain = "tempuri.org";

WindowsImpersonationContext impersonationContext;

// if impersonation fails - return
if (!ImpersonateValidUser(username, password, domain, out impersonationContext))
{
    throw new AuthenticationException("Impersonation failed");
}

PerformanceCounterCategory.Delete(PerfCategory);
UndoImpersonation(impersonationContext);

The impersonation code from an MS article...

private static bool ImpersonateValidUser(string username, string password, 
    string domain, out WindowsImpersonationContext impersonationContext)
{
    const int LOGON32_LOGON_INTERACTIVE = 2;
    const int LOGON32_PROVIDER_DEFAULT = 0;
    WindowsIdentity tempWindowsIdentity;
    var token = IntPtr.Zero;
    var tokenDuplicate = IntPtr.Zero;

    if (RevertToSelf())
    {
        if (LogonUserA(username, domain, password, 
             LOGON32_LOGON_INTERACTIVE, 
             LOGON32_PROVIDER_DEFAULT, ref token) != 0)
        {
            if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
            {
                tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                impersonationContext = tempWindowsIdentity.Impersonate();

                if (impersonationContext != null)
                {
                    CloseHandle(token);
                    CloseHandle(tokenDuplicate);
                    return true;
                }
            }
        }
    }

    if (token != IntPtr.Zero)
        CloseHandle(token);
    if (tokenDuplicate != IntPtr.Zero)
        CloseHandle(tokenDuplicate);

    impersonationContext = null;
    return false;
}



[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName, String lpszDomain, 
    String lpszPassword, int dwLogonType, int dwLogonProvider, 
    ref IntPtr phToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, 
    ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);

The error is thrown when processing tries to execute the PerformanceCounterCategory.Delete command.

Update
In response to David's answer i tried the following:

  1. Created a new local user named PerfMonUser
  2. Added this user to the "Performance Monitor Users" group

Modified the code so it now reads :

var username = "PerfMonUser";
var password = "password";
var domain = Environment.MachineName;

WindowsImpersonationContext impersonationContext;

// if impersonation fails - return
if (!ImpersonateValidUser(username, password, domain, out impersonationContext))
{
    throw new AuthenticationException("Impersonation failed");
}

PerformanceCounterCategory.Delete(PerfCategory);
UndoImpersonation(impersonationContext);

... but i'm still getting the error:

Exception Details: System.ComponentModel.Win32Exception: Access is denied

...on the line:

PerformanceCounterCategory.Delete(PerfCategory);
A: 

This is because PerformanceCounterCategory.Delete requires you to have either Administrator privileges or be a member of the Performance Monitor Users group. See MSDN for details.

Cassini runs under the NT AUTHORITY\SYSTEM user account by default, which is obviously Admin. IIS however runs under a limited user account, so it won't have access to PerformanceCounter calls. You'll need to make your "user" user either a member of Performance Monitor Users or an Administrator.

David