Vibrationz
Vibrationz

Reputation: 31

CreateProcessAsUser user context

I've already been searching long time but couldn't find a working solution yet :-(

I have created a window service that launches a client on every user logged on to a machine using CreateProcessAsUser (http://www.pinvoke.net/default.aspx/advapi32/createprocessasuser.html), WTSEnumerateSessions and so on...

This works fine already. The client starts in the user's session, shows its taskbar icon and communication with the service is working fine.

The problem I have is that I need to have that client store temporary files in the user's profile. I tried starting with a small log file so that I could keep track of any errors that my users could eventually experience. Unfortunately, I can not save to the user's temp folder because the client somehow seems to be running in LocalSystem's context although WindowsIdentity shows the correct user: System.IO.Path.GetTempPath() always returns 'C:\Windows\Temp' but my users don't have administrative rights so they are not able to write there... furthermore, I planned to store settings in the current user's registry which is not working, too. I think this is related to the wrong temp path in some way.

I also tried CreateEnvironmentBlock (http://www.pinvoke.net/default.aspx/userenv/CreateEnvironmentBlock.html) but I could not make it work and somewhere I found an article saying that this won't work anymore on Vista or higher so I stopped researching on that one.

For testing, I have created a small test form just by doing this:

MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "Before impersonation");

WindowsIdentity currentUserId = WindowsIdentity.GetCurrent();
WindowsImpersonationContext impersonatedUser = currentUserId.Impersonate();

MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "After impersonation");

This one always shows the same results before and after impersonation: Temp: C:\Windows\Temp User:testdomain\testuser :-(

If it helps here's my function to start a process (user token is delivered by WTSEnumerateSessions) - of course this only works under LocalSystem's context:

public static Process StartProcessAsUser(IntPtr UserToken, string App, string AppPath, string AppParameters)
{
    Process ResultProcess = null;

    IntPtr hDupedToken = IntPtr.Zero;
    NativeProcessAPI.PROCESS_INFORMATION oProcessInformation = new NativeProcessAPI.PROCESS_INFORMATION();

    try
    {
        NativeProcessAPI.SECURITY_ATTRIBUTES oSecurityAttributes = new NativeProcessAPI.SECURITY_ATTRIBUTES();
        oSecurityAttributes.Length = Marshal.SizeOf(oSecurityAttributes);

        bool result = NativeProcessAPI.DuplicateTokenEx(
              UserToken,
              NativeProcessAPI.GENERIC_ALL_ACCESS,
              ref oSecurityAttributes,
              (int)NativeProcessAPI.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
              (int)NativeProcessAPI.TOKEN_TYPE.TokenPrimary,
              ref hDupedToken
           );

        if (!result)
        {
            return null;
        }

        NativeProcessAPI.STARTUPINFO oStartupInfo = new NativeProcessAPI.STARTUPINFO();
        oStartupInfo.cb = Marshal.SizeOf(oStartupInfo);
        oStartupInfo.lpDesktop = String.Empty;

        result = NativeProcessAPI.CreateProcessAsUser(
                             hDupedToken,
                             null,
                             App + " " + AppParameters,
                             ref oSecurityAttributes, ref oSecurityAttributes,
                             false, 0, IntPtr.Zero,
                             AppPath, ref oStartupInfo, ref oProcessInformation
                       );

        if (result)
        {
            try
            {
                int ProcessID = oProcessInformation.dwProcessID;

                try
                {
                    ResultProcess = System.Diagnostics.Process.GetProcessById(ProcessID);
                }
                catch
                {
                    ResultProcess = null;
                }
            }
            catch (Exception ex)
            {
                ResultProcess = null;
            }
        }
    }
    catch
    {
        ResultProcess = null;
    }
    finally
    {
        if (oProcessInformation.hProcess != IntPtr.Zero)
            NativeProcessAPI.CloseHandle(oProcessInformation.hProcess);
        if (oProcessInformation.hThread != IntPtr.Zero)
            NativeProcessAPI.CloseHandle(oProcessInformation.hThread);
        if (hDupedToken != IntPtr.Zero)
            NativeProcessAPI.CloseHandle(hDupedToken);
    }

    return ResultProcess;
}

Any ideas on how I could start my processes in the user's context and not in the context of LocalSystem?

Thanks a lot!

Upvotes: 2

Views: 2979

Answers (2)

JMH
JMH

Reputation: 131

Leaving this here for anyone else wondering how to do this: CreateEnvironmentBlock is what you need to use.

DuplicateTokenEx(userToken, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, IntPtr.Zero, SecurityIdentification, TokenPrimary, out dupUserToken);
CreateEnvironmentBlock(out envBlock, dupUserToken, false);
CreateProcessAsUserW(dupUserToken, null, cmdLine, IntPtr.Zero, IntPtr.Zero, false,
            (uint)(CreateProcessFlags.CREATE_NEW_CONSOLE | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT),
            envBlock, processDir, ref startupInfo, out procInfo);

Upvotes: 1

Vibrationz
Vibrationz

Reputation: 31

Ok I have found a workaround: I switched to using the USERS hive instead of the CURRENT_USER hive by using the SID provided by WindowsIdentity:

Microsoft.Win32.Registry.Users.OpenSubKey(System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString() + ..., true)

This works perfectly although it feels a bit uncomfortable to get environment variables from the user's "environment" and "volatile environment" registry paths instead of just using .Net's built-in functions...

But thanks a lot for your help ;-)

EDIT: I will not mark this as an answer because it is a) my own solution and b) just a workaround

Upvotes: 0

Related Questions