Search code examples
c#.netimpersonationrunas

C#: Start process as the "real" administrator with hard coded privileges


I need to install different setups silently with administrator privileges. I have to hard code the privileges because the users don´t know username and password to install the setups themselfes.

I have tried two different approaches.

  1. ProcessStartInfo with UserName, Password and UseShellExecute = false.
  2. User Impersonation with

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(...);
    

In both scenarios windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator) returns false and my setups do not run because of insufficient rights.

Strange behavior: LogonUser always returns true, even with invalid credentials.

Here is the impersonation class:

namespace BlackBlade.Utilities
{
    /// <summary>
    /// Quelle: http://www.blackbladeinc.com/en-us/community/blogs/archive/2009/08/10/runas-in-c.aspx
    /// </summary>
    public class SecurityUtilities
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

        public delegate void RunAsDelegate();

        public static void RunAs(RunAsDelegate methodToRunAs, string username, string password)
        {
            string userName;

            string domain;
            if (username.IndexOf('\\') > 0)
            {
                //a domain name was supplied
                string[] usernameArray = username.Split('\\');
                userName = usernameArray[1];
                domain = usernameArray[0];
            }
            else
            {
                //there was no domain name supplied
                userName = username;
                domain = ".";
            }
            RunAs(methodToRunAs, userName, password, domain);
        }

        public static void RunAs(RunAsDelegate methodToRunAs, string username, string password, string domain)
        {
            IntPtr userToken;
            WindowsIdentity adminIdentity = null;
            WindowsImpersonationContext adminImpersonationContext = null;

            try
            {
                if (LogonUser(username, string.IsNullOrEmpty(domain) ? "." : domain, password, 9, 0, out userToken))
                {
                    //the impersonation suceeded
                    adminIdentity = new WindowsIdentity(userToken);
                    adminImpersonationContext = adminIdentity.Impersonate();

                    // todo: Entfernen.
                    WindowsPrincipal p = new WindowsPrincipal(adminIdentity);
                    MessageBox.Show(p.IsInRole(WindowsBuiltInRole.Administrator).ToString());

                    //run the delegate method
                    //methodToRunAs();
                }
                else
                    throw new Exception(string.Format("Could not impersonate user {0} in domain {1} with the specified password.", username, domain));
            }
            catch (Exception se)
            {
                int ret = Marshal.GetLastWin32Error();
                if (adminImpersonationContext != null)
                    adminImpersonationContext.Undo();
                throw new Exception("Error code: " + ret.ToString(), se);
            }
            finally
            {
                //revert to self
                if (adminImpersonationContext != null)
                    adminImpersonationContext.Undo();
            }
        }
    }
}

Solution

  • Add a manifest to the process you are starting with RunAs to request elevation.

    Edit: First, start a process using your known administrator credentials, either with LogonUser/CreateProcessAsUser or with CreateProcessWithLogon. Then check for real admin rights (maybe UAC is turned off) and if necessary, have this process (running as non-elevated administrator) start another copy of itself with ShellExecuteEx using the runas verb. This is the only way. UAC was explicitly designed to prohibit elevation without user confirmation.

    Users will have to confirm the elevation, unless UAC is turned off. For better user experience (less scary message box) get a code signing certificate and sign this executable.