Run a winforms application with elevated permissions

dodgerfan
dodgerfan used Ask the Experts™
on
I have a winforms application (c#). It will be deployed to specific users' desktops. When it is run, I need it to run with elevated permissions. Specifically I want it to run with the permissions of an account I specify without having to enter the username and password. What is the best way to implement this? I'm working with visual studio 2017 in a Windows 8 environment.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Chinmay PatelChief Technology Ninja
Distinguished Expert 2018

Commented:
Hi dodgerfan,

This technique is called user impersonation.
Here is a code sample

// The following example demonstrates the use of the WindowsIdentity class to impersonate a user.   
// IMPORTANT NOTE:   
// This sample asks the user to enter a password on the console screen.   
// The password will be visible on the screen, because the console window   
// does not support masked input natively.  

using System;  
using System.Runtime.InteropServices;  
using System.Security;  
using System.Security.Principal;  
using Microsoft.Win32.SafeHandles;  

public class ImpersonationDemo  
{  
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,  
        int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);  

    public static void Main()  
    {  
        // Get the user token for the specified user, domain, and password using the   
        // unmanaged LogonUser method.   
        // The local machine name can be used for the domain name to impersonate a user on this machine.  
        Console.Write("Enter the name of the domain on which to log on: ");  
        string domainName = Console.ReadLine();  

        Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);  
        string userName = Console.ReadLine();  

        Console.Write("Enter the password for {0}: ", userName);  

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

        // Call LogonUser to obtain a handle to an access token.   
        SafeAccessTokenHandle safeAccessTokenHandle;  
        bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),  
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,  
            out safeAccessTokenHandle);  

        if (false == returnValue)  
        {  
            int ret = Marshal.GetLastWin32Error();  
            Console.WriteLine("LogonUser failed with error code : {0}", ret);  
            throw new System.ComponentModel.Win32Exception(ret);  
        }  

        Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));  
        // Check the identity.  
        Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);  

        // Note: if you want to run as unimpersonated, pass  
        //       'SafeAccessTokenHandle.InvalidHandle' instead of variable 'safeAccessTokenHandle'  
        WindowsIdentity.RunImpersonated(  
            safeAccessTokenHandle,  
            // User action  
            () =>  
            {  
                // Check the identity.  
                Console.WriteLine("During impersonation: " + WindowsIdentity.GetCurrent().Name);  
            }  
            );  

        // Check the identity again.  
        Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);  
    }  
}

Open in new window


Please refer to the documentation for more details:
https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated?view=netframework-4.7.2

Regards,
Chinmay.
Most Valuable Expert 2012
Top Expert 2014

Commented:
Specifically I want it to run with the permissions of an account I specify without having to enter the username and password.
You'd have to enter the credentials at some point, otherwise you'd not be able to log in as that account. Do you mean that you're going to embed the credentials into your code, as Chinmay has suggested above?
Chinmay PatelChief Technology Ninja
Distinguished Expert 2018

Commented:
I think he meant to say manual entry of the credentials.

@dodgerfan,
Please modify the sample above per your requirements. If I were to do it I'd store credentials in a config file and encrypt it using DPAPI.
JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

Author

Commented:
Yes, I want to avoid having the user manually enter a username and password. I've using this example:
https://www.daoudisamir.com/impersonate-users-in-c/
which I think comes from the one you put up here. I'm using it, and I've put in the click event of a button. The button is returns a list of files from a selected directory. I'm getting an error message that says: Could not find a part of the path. I'm usually selecting folders from mapped drives. Would that cause this issue? If it does, is there a fix?
Chief Technology Ninja
Distinguished Expert 2018
Commented:
Hi dodgerfan,

That method is seriously dated ( I used it extensively in 2003 , If today I have to do impersonation, I would use the latest recommendations by Microsoft. The latest method used for impersonation only uses one Pinvoke call, rest of the stuff is managed.

Now coming to the question, yes I think it is due to mapped drives. I think for the folders which are giving you an error - you can do a retry(time consuming and can be a performance bottleneck) OR highlight it to the end user that the following paths are not accessible - so they can take corrective measures.

Regards,
Chinmay.

Author

Commented:
Chinmay, I switched my code to use what you referenced first. I have it working now. But I got an error at first. "The user has not been granted the requested logon type at this computer". After some searching, I changed
const int LOGON32_LOGON_INTERACTIVE = 2; 

Open in new window

to
const int LOGON32_LOGON_INTERACTIVE = 8; 

Open in new window

I'm not sure I understand why that worked. I have not resolved my mapped drive issue yet, but after looking at it it feels like a sparate question. Thank you for your input. If I cannot get it resolved I open a  new question. In the meantime I'm going to close this one.
Chinmay PatelChief Technology Ninja
Distinguished Expert 2018

Commented:
Whenever in doubt w.r.t. PInvoke, refer to: Pinvoke.net http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html

You are to supply plain-text credentials for your scenario to work. It is strange, I wonder what kind of account you are using?
Please use the proper constants and their values from the Pinvoke.net

nst int LOGON32_LOGON_INTERACTIVE       = 2;
const int LOGON32_LOGON_NETWORK       = 3;
const int LOGON32_LOGON_BATCH         = 4;
const int LOGON32_LOGON_SERVICE       = 5;
const int LOGON32_LOGON_UNLOCK        = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS   = 9;

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial