views:

44

answers:

1

Hello

I just spent 2 days reading a bunch of stuff about impersonation in C# (including stackoverflow and codeproject articles) and here is the result of my investigations.

To make it short, I just want to start and stop a service from a winform application running under standard (non privileged) user account. I want to impersonate my user with an admin account. Both accounts are local accounts.

In the code below, I don't get any errors during token retreival (via Interop calls) ,but calls to servicecontroller.Start and servicecontroller.Stop always fail.

All the articles on the web describe the same process to impersonate user, but until now, I wasn't able to get it work or even to get a clue about what's going wrong.

Does anyone have a clue about what is wrong donw there...

Thanks in advance

--Bruno

     public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    [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 extern static bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
        int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
    private void button1_Click(object sender, EventArgs e)
    {


        WindowsImpersonationContext wic = ImpersonateUser(
                 "Administrator",
                 "machinename",
                 "password");

        try
        {
            ServiceController sc = new ServiceController("SERVICE_PERE");
            Process p = new Process();


            sc.Start();
        }
        catch (Exception ex)
        {

            Debugger.Break();
        }
        finally
        {
            wic.Undo();
        }


    }

    private void button2_Click(object sender, EventArgs e)
    {
        WindowsImpersonationContext wic = ImpersonateUser(
                 "Administrator",
                 "machinename",
                 "password");


        try
        {

            ServiceController sc = new ServiceController("SERVICE_PERE");

            sc.Stop();
        }
        catch (Exception ex)
        {
            Debugger.Break();

        }
        finally
        {
            wic.Undo();
            //CodeAccessPermission.RevertAssert();
        }
    }
    public WindowsImpersonationContext ImpersonateUser(string sUsername, string sDomain, string sPassword)
    {
        // initialize tokens

        IntPtr pExistingTokenHandle = new IntPtr(0);
        IntPtr pDuplicateTokenHandle = new IntPtr(0);
        pExistingTokenHandle = IntPtr.Zero;
        pDuplicateTokenHandle = IntPtr.Zero;

        // if domain name was blank, assume local machine

        if (sDomain == "")
            sDomain = System.Environment.MachineName;

        try
        {
            string sResult = null;

            const int LOGON32_PROVIDER_DEFAULT = 0;

            // create token

            const int LOGON32_LOGON_INTERACTIVE = 2;
            //const int SecurityImpersonation = 2;


            // get handle to token

            bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                    ref pExistingTokenHandle);

            // did impersonation fail?

            if (false == bImpersonated)
            {
                int nErrorCode = Marshal.GetLastWin32Error();
                sResult = "LogonUser() failed with error code: " +
                    nErrorCode + "\r\n";

                // show the reason why LogonUser failed

                //MessageBox.Show(this, sResult, "Error",
                //    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Get identity before impersonation

            sResult += "Before impersonation: " +
                WindowsIdentity.GetCurrent().Name + "\r\n";

            bool bRetVal = DuplicateToken(pExistingTokenHandle,
                2, ref pDuplicateTokenHandle);

            // did DuplicateToken fail?

            if (false == bRetVal)
            {
                int nErrorCode = Marshal.GetLastWin32Error();
                // close existing handle

                CloseHandle(pExistingTokenHandle);
                sResult += "DuplicateToken() failed with error code: "
                    + nErrorCode + "\r\n";

                // show the reason why DuplicateToken failed

                //MessageBox.Show(this, sResult, "Error",
                //    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }
            else
            {
                // create new identity using new primary token

                WindowsIdentity newId = new WindowsIdentity
                                            (pDuplicateTokenHandle);

                WindowsImpersonationContext impersonatedUser =
                                            newId.Impersonate();

                // check the identity after impersonation

                sResult += "After impersonation: " +
                    WindowsIdentity.GetCurrent().Name + "\r\n";

                //MessageBox.Show(this, sResult, "Success",
                //    MessageBoxButtons.OK, MessageBoxIcon.Information);
                return impersonatedUser;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            // close handle(s)

            if (pExistingTokenHandle != IntPtr.Zero)
                CloseHandle(pExistingTokenHandle);
            if (pDuplicateTokenHandle != IntPtr.Zero)
                CloseHandle(pDuplicateTokenHandle);
        }
    }
}

public enum LogonType
{
    LOGON32_LOGON_INTERACTIVE = 2,
    LOGON32_LOGON_NETWORK = 3,
    LOGON32_LOGON_BATCH = 4,
    LOGON32_LOGON_SERVICE = 5,
    LOGON32_LOGON_UNLOCK = 7,
    LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
    LOGON32_LOGON_NEW_CREDENTIALS = 9,
}
public enum LogonProvider
{
    LOGON32_PROVIDER_DEFAULT = 0,
}
+1  A: 

Starting and stopping services requires admin privileges. You cannot bypass UAC with impersonation, you have to display the elevation prompt. This answer shows you how.

Hans Passant
Ok, I'm going to check this out. Thnak you.
Bruno