Web-Role/Worker-Role Startup Tasks with Impersonation

In some scenarios you want to kick an exe with Administrator Account privilege in Azure VM. The following article will help you to handle such requirements.

Please note adding Startup Task with Elevated mode will run only in SYSTEM account not in Administrator Account. This below mentioned method can be followed incase you want to run Task in ADMIN account.

Executing Startup Tasks as Administrator Account

You MUST use the OnStart() method of the class that descends from RoleEntryPoint. This class is typically created for you when you add a new WebRole or WorkerRole to your Azure project.

The point is that you can not use (for some reason) batch files or executable files (and configure your Startup>Tasks section in your ServiceDefinition.csdef to execute them. Attempting to impersonate an administrator in these tasks does not throw exceptions, but if your task requires administrative privileges, it will not work. However, using the OnStart() method along with impersonation works!

WorkerRole.cs

public class WorkerRole : RoleEntryPoint
{
   public override bool OnStart()
   { 
      try
      {
           // Add code here
            using (var impersonation = new MsImpersonation("", "adminUser", "adminPassword", LogonSessionType.LOGON32_LOGON_BATCH))
            {
                // Do your impersonated work here
            }
      } 
     catch (Exception e)
      { 
       Trace.WriteLine("Exception during OnStart: " + e.ToString()); 
       //Take other action as needed. 
      } 
    return base.OnStart(); 
    }
}

The RoleEntryPoint descendant class

The code listing above shows such a class. In the overridden OnStart() method, you’ll see that I’m impersonating an administrator and then doing some work that needs administrative privileges.

Impersonating Administrator in a WorkerRole EntryPoint

The class presented below helps with impersonating a Windows user. You can see it in use in the code listing above.

But the question is, how do you get the name and password of an administrator in a Azure built Virtual Machine? Well, we use a little trick here. When you publish your project to Azure, you’ll need to “Enable Remote Desktop” for all roles in the Publish Windows Azure Application wizard (screen shot show after the code listing below).

MsImpersonation.cs

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.ComponentModel;

namespace Matlus.Security
{
    public enum LogonSessionType
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
         
        /// Only valid in Windows 2000 and higher
        /// 
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
         
        /// Only valid in Windows 2000 and higher
        /// 
        LOGON32_LOGON_NEW_CREDENTIALS = 9
    }

    public enum LogonProvider
    {
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    }

    public enum ImpersonationLevel
    {
        SecurityAnonymous = 0,
        SecurityIdentification = 1,
        SecurityImpersonation = 2,
        SecurityDelegation = 3
    }

     
    /// This class enables Loggin On and impersonating a specific user.
    /// Assembly level attributes are required in order to use this class. These attributes
    /// are typically added in the AssemblyInfo.cs file
    /// [assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum, UnmanagedCode=true)]
    /// [assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]
    /// 
    public class MsImpersonation : IDisposable
    {
        private IntPtr tokenHandle = new IntPtr(0);
        private IntPtr dupeTokenHandle = new IntPtr(0);
        private WindowsImpersonationContext windowsImpersonationContext;

        private bool disposed;

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
          int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

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

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
          int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

        public MsImpersonation(string domain, string userName, string password, LogonSessionType logOnSessionType)
        {
            LogOnAndImpersonateUser(domain, userName, password, logOnSessionType);
        }

        public MsImpersonation(string domain, string userName, string password, LogonSessionType logOnSessionType, LogonProvider logonProvider)
        {
            LogOnAndImpersonateUser(domain, userName, password, logOnSessionType, logonProvider);
        }

        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        private void LogOnAndImpersonateUser(string domain, string userName, string password, LogonSessionType logOnSessionType)
        {
            LogOnAndImpersonateUser(domain, userName, password, logOnSessionType, LogonProvider.LOGON32_PROVIDER_DEFAULT);
        }

        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        private void LogOnAndImpersonateUser(string domain, string userName, string password, LogonSessionType logOnSessionType, LogonProvider logonProvider)
        {
            tokenHandle = IntPtr.Zero;
            dupeTokenHandle = IntPtr.Zero;

            RevertToSelf();

            bool returnValue = LogonUser(userName, domain, password, (int)logOnSessionType, (int)logonProvider, ref tokenHandle);
            if (!returnValue)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            try
            {
                bool retVal = DuplicateToken(tokenHandle, (int)ImpersonationLevel.SecurityImpersonation, ref dupeTokenHandle);
                if (!retVal)
                {
                    CloseHandle(tokenHandle);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                WindowsIdentity windowsIdentity = new WindowsIdentity(dupeTokenHandle);
                windowsImpersonationContext = windowsIdentity.Impersonate();
            }
            catch
            {
                if (tokenHandle != IntPtr.Zero)
                    CloseHandle(tokenHandle);
            }
            finally
            {
                if (tokenHandle != IntPtr.Zero)
                    CloseHandle(tokenHandle);
                if (dupeTokenHandle != IntPtr.Zero)
                    CloseHandle(dupeTokenHandle);
            }
        }

        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        private void RevertToSelf()
        {
            if (windowsImpersonationContext != null)
            {
                windowsImpersonationContext.Undo();
                windowsImpersonationContext.Dispose();
            }
        }

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

        private void Dispose(bool disposing)
        {
            if (!disposed && disposing)
            {
                RevertToSelf();
                disposed = true;
            }
        }
    }
}

Replace the User name and Password of the Administrator account in the VM. The easy way to get this account credential is using teh same RDP credentials you use to open your VM!

Happy Programming.
Advertisements

Technical Lead for Microsoft Technologies especially in Azure, ASP.Net and in Angular JS.

Posted in Azure
One comment on “Web-Role/Worker-Role Startup Tasks with Impersonation
  1. Krishnanand says:

    Equally useful for beginners and those who have already gone through these steps for revise..Waiting for the next blog.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s