0

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();
            }
        }
    }
}
timmkrause
  • 3,367
  • 4
  • 32
  • 59
  • Please give your question a more meaningful title! – abatishchev Jun 28 '12 at 09:02
  • Thank you for making SO a better place :) – abatishchev Jun 28 '12 at 10:36
  • LogonUser is correct in returning true even for invalid credentials, because with the NEW_CREDENTIALS logon type it does not actually log on the supplied user! It just stores the credentials in a copy of the calling user's token for use in network operations (think `net use /U`). If you want to log on a user onto the local machine, use the INTERACTIVE logon type as suggested by rdkleine. – Anton Tykhyy Jun 28 '12 at 11:16
  • Based on your comments I must downvote this question. You don't explain the situation with the users in detail. – Security Hound Jun 28 '12 at 11:36
  • But I described what I WANT but it seems nobody cares and other ways are described. I´m aware of the security concerns but it is the actual setup out there. – timmkrause Jun 28 '12 at 13:49

3 Answers3

3

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.

Community
  • 1
  • 1
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • No option. The users don´t have administrator rights and no access to any administrator account. I have to "elevate" it programmatically. – timmkrause Jun 28 '12 at 13:46
  • 1
    You cannot elevate programmatically without user confirmation (the UAC prompt). That's what UAC is for. Any workaround is a security hole and will be patched ASAP by Microsoft. – Anton Tykhyy Jun 28 '12 at 14:56
  • So you say full admin-elevation can be achieved with the technique you mentioned? – timmkrause Jul 06 '12 at 06:35
  • "start another copy of itself with ShellExecuteEx using the runas verb" - But this will pop-up the UAC window which asks for eleveation, right? So the user has to enter username and password again, if they have no administrator rights and simply click "Yes" if they have them. Is this correct? So it is the same behavior as applying a manifest file. – timmkrause Jul 06 '12 at 06:55
  • 1
    You'll have to try it. Since the process calling ShellExecuteEx will be running as (non-elevated) administrator, it is possible that the user will not have to enter credentials. – Anton Tykhyy Jul 06 '12 at 07:12
  • Ah okay. So truly running in elevated mode is not possible. Okay. – timmkrause Jul 06 '12 at 09:38
0

Use Group Policy to roll out the MSI or EXE installer.

Alternatively use Task Scheduler to run the installer as the Local System account.

Ben
  • 34,935
  • 6
  • 74
  • 113
  • Not possible. The devices are domainless. The second advice is also not possible. We will send a USB stick to our users and it should be as easy as "click our setup.exe". – timmkrause Jun 28 '12 at 09:07
  • So who has Admin logon? The user? You? The user's sysadmin? The correct answer is to mark your setup as requiring admin rights, then let the user's sysadmin take care of that. – Ben Jun 28 '12 at 09:15
  • In other words: ***Do Not Do That***. You are basically asking your users to turn off security. Any recipient of the thumb drives is being given the admin password to their machine. Yes, security is a pain. – Ben Jun 28 '12 at 09:16
  • I have no influence of changing that. The situation is as described above. – timmkrause Jun 28 '12 at 09:23
  • The password is not given to them in clear text and the code that holds it will be obfuscated. – timmkrause Jun 28 '12 at 09:26
  • 1
    "the code that holds it will be obfuscated" : right... like that *ever* works. If the prize is worth, is enough for *one* person to crack it and then Google will take care of *everyone* find out how to do it. Security has to be done right. You requirements scream 'malware' and 'scam'. – Remus Rusanu Jun 28 '12 at 10:58
  • @tkrause - You are also using encryption to store the password which can be broken if the key is known which must be the cast, if you are using the password to authenticate into a Windows User Account, which means the password more or less is in plain text. – Security Hound Jun 28 '12 at 11:35
0

Have you tried setting dwLogonType to 2 instead of 9?

http://msdn.microsoft.com/en-us/library/windows/desktop/bb540756(v=vs.85).aspx

Here's a code sample which works for me:

    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
Ralf de Kleine
  • 11,464
  • 5
  • 45
  • 87