Scheduled Task – Status, Start, Stop of Windows Service

So yesterday I created a task scheduler, today I wanted to be able to figure out the status of service and be able to start/stop it. I thought this would be a great time to just implement a new task in my project for task schedule and be on my way.

I found the code for accessing services using System.ServiceProcess quite easily and in my test project it worked great. Infact too great, I was even concerned at how easy it was to start/stop a service from visual studio further reflection on this I assumed since I was debugging a test project it was most likely running under my admin rights (proved true.) So I dropped it into actual ASP.NET and of course was issued security exceptions.

This led me down needing to figure out how to assume the identity of a user that can start/stop the service and then use their credientials to handle it. With a little bit of shameless stealing from MSDN and code clean up I was able to create my new task.

#region Using Statements

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceProcess;

#endregion

namespace Tasks
{
    [assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode = true)]
    [assembly: PermissionSet(SecurityAction.RequestMinimum, Name = "FullTrust")]
    public class ServiceUpTask : Task
    {
        public string ServiceName { get; set; }

        #region Overrides of Task

        public override void Run()
        {
            if (!string.IsNullOrEmpty(ServiceName))
            {
                WindowsImpersonationContext impersonationContext = null;

                try
                {
                    impersonationContext = AssumeIdentity("userName", "someDomain", "password");

                    using (var service = new ServiceController(ServiceName))
                    {
                        Debug.Print(string.Format("Service {0}, Status {1}", service.ServiceName, service.Status));

                        if (service.CanStop)
                        {
                            service.Stop();
                        }
                    }

                    using (var service = new ServiceController(ServiceName))
                    {
                        if (service.Status == ServiceControllerStatus.Stopped)
                        {
                            service.Start();
                        }
                    }
                }
                finally
                {
                    //Make sure we release the identity in case of an exception no matter what.
                    StopImpersonating(impersonationContext);
                }
            }
        }

        ///<summary>
        ///Assumes the identity. Steal shamelessly from MSDN.
        ///</summary>
        ///<returns></returns>
        private static WindowsImpersonationContext AssumeIdentity(string userName, string domain, string password)
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int dwLogonProvider = LOGON32_PROVIDER_DEFAULT;

            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;
            const int dwLogonType = LOGON32_LOGON_INTERACTIVE;

            var phToken = new IntPtr();
            if (LogonUser(userName, domain, password, dwLogonType, dwLogonProvider, ref phToken))
            {
                if (phToken != IntPtr.Zero)
                {
                    var windowsIdentity = new WindowsIdentity(phToken);
                    var impersonationContext = windowsIdentity.Impersonate();
                    CloseHandle(phToken);
                    return impersonationContext;
                }
            }

            return null;
        }

        /// <summary>
        /// Stops the WindowsIdentity impersonating.
        /// </summary>
        /// <param name="impersonationContext">The impersonation context.</param>
        private static void StopImpersonating(WindowsImpersonationContext impersonationContext)
        {
            //Stop impersonating the user.
            if (impersonationContext != null) impersonationContext.Undo();

            //Check the identity name.
            //Name of the identity after performing an Undo on the impersonation:
            //Uncomment for debugging if to check identity if needed
            //var identity = WindowsIdentity.GetCurrent();
        }

        #endregion

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        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);
    }
}

These changes are all checked into my SVN for access see the Assembla page or the post below.

BloggingContext.ApplicationInstance.CompleteRequest();

Advertisements

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