Start a .Net Process as a Different User
Asked Answered
C

3

31

I want to start a Process with Admin rights. When I run the code below the Process complains saying it needs Admin rights:

public class ImpersonationHelper : IDisposable
{
    IntPtr m_tokenHandle = new IntPtr(0);
    WindowsImpersonationContext m_impersonatedUser;

    #region Win32 API Declarations

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

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

    #endregion

    /// <summary>
    /// Constructor. Impersonates the requested user. Impersonation lasts until
    /// the instance is disposed.
    /// </summary>
    public ImpersonationHelper(string domain, string user, string password)
    {
        // Call LogonUser to obtain a handle to an access token.
        bool returnValue = LogonUser(user, domain, password,
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
            ref m_tokenHandle);
        if (false == returnValue)
        {
            int ret = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(ret);
        }

        // Impersonate
        m_impersonatedUser = new WindowsIdentity(m_tokenHandle).Impersonate();
    }

    #region IDisposable Pattern

    /// <summary>
    /// Revert to original user and cleanup.
    /// </summary>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Revert to original user identity
            if (m_impersonatedUser != null)
                m_impersonatedUser.Undo();
        }

        // Free the tokens.
        if (m_tokenHandle != IntPtr.Zero)
            CloseHandle(m_tokenHandle);
    }

    /// <summary>
    /// Explicit dispose.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Destructor
    /// </summary>
    ~ImpersonationHelper()
    {
        Dispose(false);
    }

    #endregion
}

using (new ImpersonationHelper("xxx.blabla.com", "xxxx", "xxxx"))
{
    if (!string.IsNullOrEmpty(txtFilename.Text))
        Process.Start(txtFilename.Text);
}
Cyclops answered 7/1, 2011 at 9:32 Comment(0)
L
50

Can you try something like this: Start a new Process as another user

Code sample:

System.Diagnostics.Process proc = new System.Diagnostics.Process();
System.Security.SecureString ssPwd = new System.Security.SecureString();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "filename";
proc.StartInfo.Arguments = "args...";
proc.StartInfo.Domain = "domainname";
proc.StartInfo.UserName = "username";
string password = "user entered password";
for (int x = 0; x < password.Length; x++)
{
    ssPwd.AppendChar(password[x]);
}
password = "";
proc.StartInfo.Password = ssPwd;
proc.Start();
Leukas answered 7/1, 2011 at 10:9 Comment(11)
What's about UAC - Verb runas ? #13839454Forgat
WRONG usage of SecureString. You defeated all the purpose of it by storing it in passwordSachs
it did not make me feel better actually because string password = "user entered password"; is still there and assigning it to "" doesn't mean so much.Sachs
@sotn: Your example is not any different than my example. You are using char and I'm using string (array of chars)...Leukas
They are not the same. The point is you should not store that string in a variable since peeking in memory could reveal that variable's value easily. By appending it char by char, the stored value will be encrypted each time so that peeking will not reveal the password.Sachs
Check linkSachs
@sotn: Chars are still in memory (as well as string) until they are garbage collected. By decompiling binaries where you have your password hardcoded no matter what way ( string or char by char ) it's easily to read out that information.Leukas
does anyone know if this will work under fast user switching? if you use an admin account for the start up will it start on the current user or will it show up for every user? Thanks.Effusive
@sotn: How useful is the SecureString anyway if the TextBox doesn't provide a way to get it in the first place without transiting by a String anyway? Even TextBox.GetCharFromPosition uses Text, I checked in the reference source.Spirant
Verb = "runas" ?Forgat
This doesn't work if parent process is running under 'Local System' account contextBehalf
S
12

Correct usage of SecureString and a few extras:

//You should use SecureString like the following
SecureString password = new SecureString();
password.AppendChar('p');
password.AppendChar('a');
password.AppendChar('s');
password.AppendChar('s');

Process process = new Process();
process.StartInfo.UseShellExecute = false;
//Set the working directory if you don't execute something like calc or iisreset but your own exe in which you want to access some files etc..
process.StartInfo.WorkingDirectory = "workingDirectory";
//Full path (e.g. it can be @"C:\Windows\System32\iisreset.exe" OR you can use only file name if the path is included in Environment Variables..)
process.StartInfo.FileName = @"fileName";
process.StartInfo.Domain = "domain";
process.StartInfo.UserName = "userName";
process.StartInfo.Password = password;
process.Start();

EDIT: I don't know why this answer is voted below 0, maybe a little more explanation is needed. If you'll use this in non-interactive environment (like a web application) and you want to run a process with a user, then you have a few options to use the user's password. You can either read password from a storage or from code. A better way; you can store it encrypted. However, if you plan to use it in plain form (maybe temporarily or just to test something etc.), you can use SecureString in a way I described. The accepted answer doesn't use SecureString in a right way. Reading password into a string from console and then putting it into a SecureString is simply WRONG. The accepted answer does NOT secure that string or something but only cheating it. That was the main motivation for me to add this answer. Check link.

Sachs answered 20/12, 2016 at 13:49 Comment(6)
Not sure how smart is to hardcode password in the code. If it's not hardcoded, where do you get it from? How this differs from my example?Leukas
my example was just a POC. I'm not getting why are you making such a big deal out of it. Whatever way you hardcode your password (string or char by char) it can be easily read from decompiled binaries and memory as well. At certain point when your code runs in memory, it gives instruction what to append to SecureString. You can easily put a breakpoint in WinDbg and read what's passed in. My string will eventually be garbage collected and there will no be trace of it. You chars are in memory as well, it's just harder to put them together. They'll be garbage collected later as well.Leukas
I'm not sure if you are aware but SecureString isn't really a secure string. It's merely a way to decrease the time window in which someone can inspect your memory and successfully get the sensitive data. This isn't bulletproof and it wasn't intended to be.Leukas
"...correct usage of secure string..." sees password hard coded as a series of characters .... I'm pretty sure the "correct" usage involves an encrypted version of the password somewhere, although even that is iffy since the key must then be known.Pasia
Verb = "runas" ?Forgat
@Leukas Garbage collected doesn't necessarily mean "no trace of it". There is no way for you to reference it again in the code, but there most certainly will be a trace in memory, unless it was overwriten by something else. The normal (unsecure) strings containing sensitive information like keys and passwords should always exists for minimal amount of time and be wiped clean char by char after use.Mordred
J
3

Don't forget to add LoadUserProfile=true since without this access denied issues might occur if your application is doing some read write operations

Juror answered 14/6, 2022 at 10:19 Comment(2)
This parameter only works on Windows.Lifegiving
This was exactly what I needed - I was starting a process as another user on the local machine but it didn't have environment variables loaded. Adding LoadUserProfile=true to the ProcessStartInfo fixed thisRecept

© 2022 - 2024 — McMap. All rights reserved.