2

I am using LogonUser to get primary user token and then CreateProcessAsUser APIs to create process. But I am getting error code 6. Not sure what the problem is. Below is the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LogOnUserTestWindows
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Principal;

    class Program
    {
        // Define the Windows LogonUser and CloseHandle functions.
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool LogonUser(String username, String domain, IntPtr password,
                int logonType, int logonProvider, ref IntPtr token);

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

        private enum SW
        {
            SW_HIDE = 0,
            SW_SHOWNORMAL = 1,
            SW_NORMAL = 1,
            SW_SHOWMINIMIZED = 2,
            SW_SHOWMAXIMIZED = 3,
            SW_MAXIMIZE = 3,
            SW_SHOWNOACTIVATE = 4,
            SW_SHOW = 5,
            SW_MINIMIZE = 6,
            SW_SHOWMINNOACTIVE = 7,
            SW_SHOWNA = 8,
            SW_RESTORE = 9,
            SW_SHOWDEFAULT = 10,
            SW_MAX = 10
        }


        [StructLayout(LayoutKind.Sequential)]
        private struct STARTUPINFO
        {
            public int cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }


        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        private static extern bool CreateProcessAsUser(
                IntPtr hToken,
                String lpApplicationName,
                String lpCommandLine,
                IntPtr lpProcessAttributes,
                IntPtr lpThreadAttributes,
                bool bInheritHandle,
                uint dwCreationFlags,
                IntPtr lpEnvironment,
                String lpCurrentDirectory,
                ref STARTUPINFO lpStartupInfo,
                out PROCESS_INFORMATION lpProcessInformation);
        // Define the required LogonUser enumerations.
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_INTERACTIVE = 2;

        private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
        private const int CREATE_NO_WINDOW = 0x08000000;

        private const int CREATE_NEW_CONSOLE = 0x00000010;


        static void Main()
        {
            // Display the current user before impersonation.
            Console.WriteLine("Before impersonation: {0}",
                              WindowsIdentity.GetCurrent().Name);

            // Ask the user for a network domain.
            Console.Write("Please enter your domain: ");
            string domain = Console.ReadLine();

            // Ask the user for a user name.
            Console.Write("Please enter your user name: ");
            string username = Console.ReadLine();

            // Ask the user for a password.
            Console.Write("Please enter your password: ");
            SecureString passWord = GetPassword();

            // Impersonate the account provided by the user.
            try
            {
                //WindowsImpersonationContext userContext = ImpersonateUser(passWord, username, domain);
                IntPtr token = ImpersonateUser(passWord, username, domain);

                // Display the current user after impersonation.
                Console.WriteLine("After impersonation: {0}",
                                  WindowsIdentity.GetCurrent().Name);
            }
            catch (ArgumentException e)
            {
                Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
            }
            catch (Win32Exception e)
            {
                Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
            }
            finally
            {
                passWord.Dispose();
            }
        }

        public static SecureString GetPassword()
        {
            SecureString password = new SecureString();

            // get the first character of the password
            ConsoleKeyInfo nextKey = Console.ReadKey(true);

            while (nextKey.Key != ConsoleKey.Enter)
            {
                if (nextKey.Key == ConsoleKey.Backspace)
                {
                    if (password.Length > 0)
                    {
                        password.RemoveAt(password.Length - 1);

                        // erase the last * as well
                        Console.Write(nextKey.KeyChar);
                        Console.Write(" ");
                        Console.Write(nextKey.KeyChar);
                    }
                }
                else
                {
                    password.AppendChar(nextKey.KeyChar);
                    Console.Write("*");
                }

                nextKey = Console.ReadKey(true);
            }

            Console.WriteLine();

            // lock the password down
            password.MakeReadOnly();
            return password;
        }

        public static IntPtr ImpersonateUser(SecureString password, string userName, string domainName)
        {
            IntPtr tokenHandle = IntPtr.Zero;
            IntPtr passwordPtr = IntPtr.Zero;
            bool returnValue = false;
            int error = 0;

            // Marshal the SecureString to unmanaged memory.
            passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password);

            // Pass LogonUser the unmanaged (and decrypted) copy of the password.
            returnValue = LogonUser(userName, domainName, passwordPtr,
                                    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                                    ref tokenHandle);
            if (!returnValue && tokenHandle == IntPtr.Zero)
                error = Marshal.GetLastWin32Error();

            // Perform cleanup whether or not the call succeeded.
            // Zero-out and free the unmanaged string reference.
            Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
            // Close the token handle.
            CloseHandle(tokenHandle);

            // Throw an exception if an error occurred.
            if (error != 0)
            {
                throw new System.ComponentModel.Win32Exception(error);
            }
            // The token that is passed to the following constructor must 
            // be a primary token in order to use it for impersonation.
            //WindowsIdentity newId = new WindowsIdentity(tokenHandle);

            //String workgroup;
            string cmdLine = null;
            string workDir = null;
            bool visible = true;
            var pEnv = IntPtr.Zero;
            var startInfo = new STARTUPINFO();
            var procInfo = new PROCESS_INFORMATION();
            int iResultOfCreateProcessAsUser;

            uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
            startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
            startInfo.lpDesktop = "winsta0\\default";




            if (!CreateProcessAsUser(
                tokenHandle,
                "C:/Windows/System32/notepad.exe", // Application Name
                cmdLine, // Command Line
                IntPtr.Zero,
                IntPtr.Zero,
                false,
                dwCreationFlags,
                pEnv,
                workDir, // Working directory
                ref startInfo,
                out procInfo))
            {
                iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();

                throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.  Error Code -" + iResultOfCreateProcessAsUser);
            }
            return tokenHandle;
        }
    }
}

Below is the error

System.Exception
  HResult=0x80131500
  Message=StartProcessAsCurrentUser: CreateProcessAsUser failed.  Error Code -6
  Source=LogOnUserTestWindows
  StackTrace:
   at LogOnUserTestWindows.Program.ImpersonateUser(SecureString password, String userName, String domainName) in C:\Users\santosh\source\repos\LogOnUserTestWindows\LogOnUserTestWindows\Program.cs:line 242
   at LogOnUserTestWindows.Program.Main() in C:\Users\santosh\source\repos\LogOnUserTestWindows\LogOnUserTestWindows\Program.cs:line 122

I am not able to find any proper documentation for both logonUser and CreateProcessAsUser APIs. I am trying to run this code on my machine which has multiple usets. I am logged in from one user and trying to create process from another user. It would be really great if someone can point me to proper documentation or examples. Please help. Thanks in Advance.

  • See [`LogonUser`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184.aspx) and [`CreateProcessAsUser`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682429.aspx) on MSDN – Remy Lebeau Jun 08 '18 at 08:34

2 Answers2

5

You closed tokenHandle before you passed it to CreateProcessAsUser.

Result: ERROR_INVALID_HANDLE (= 6).

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • Thanks Paul. I changed the code. Now I am getting Error Code 1314. I followed below link and modified local security policy based on first answer. But still getting error code 1314. Any Suggestions? https://stackoverflow.com/questions/1475577/createprocessasuser-error-1314 – uttam.aaseri Jun 09 '18 at 12:33
  • Not sure you needed to go that far. As you no doubt are aware, that error code translates to ERROR_PRIVILEGE_NOT_HELD. Just run your service under the Local System account. That should fix it. Also (for next time), tiny nitpick: ERROR_PRIVILEGE_NOT_HELD from what function call? It helps to know these things. Thx. – Paul Sanders Jun 09 '18 at 13:31
-1

Please try CreateProcessWithToken instead, although I don't know why the function returned error code -6, but a few days ago I tried to start up an application with specified user token, but CreateProcessAsUser didn't work, but CreateProcessWithToken did. Maybe it will also work for you, good luck.

jinchx
  • 31
  • 4