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();