Пользовательский контекст CreateProcessAsUser

Я уже долго искал, но не смог найти работающего решения:-(

Я создал оконную службу, которая запускает клиента при каждом входе пользователя в систему на компьютере с помощью CreateProcessAsUser ( http://www.pinvoke.net/default.aspx/advapi32/createprocessasuser.html), WTSEnumerateSessions и т. Д....

Это уже отлично работает. Клиент запускается в сеансе пользователя, показывает значок панели задач, и связь с сервисом работает нормально.

У меня проблема в том, что мне нужно, чтобы этот клиент хранил временные файлы в профиле пользователя. Я попытался начать с небольшого файла журнала, чтобы отслеживать все ошибки, с которыми может столкнуться мой пользователь. К сожалению, я не могу сохранить во временную папку пользователя, потому что клиент каким-то образом работает в контексте LocalSystem, хотя WindowsIdentity показывает правильного пользователя: System.IO.Path.GetTempPath() всегда возвращает C:\Windows\Temp, но мой пользователь У меня нет административных прав, поэтому они не могут писать туда. Более того, я планировал сохранить настройки в реестре текущего пользователя, который тоже не работает. Я думаю, что это каким-то образом связано с неправильным временным путем.

Я также попробовал CreateEnvironmentBlock ( http://www.pinvoke.net/default.aspx/userenv/CreateEnvironmentBlock.html), но не смог заставить его работать, и где-то я нашел статью о том, что это больше не будет работать в Vista или выше, поэтому я перестал исследовать это.

Для тестирования я создал небольшую тестовую форму просто так:

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

Этот всегда показывает одинаковые результаты до и после олицетворения: "Temp: C:\Windows\Temp Пользователь:testdomain\testuser":-(

Если это поможет, вот моя функция - запустить процесс (токен пользователя доставляется WTSEnumerateSessions) - конечно, это работает только в контексте LocalSystem:

    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;
    }

Любые идеи, как я мог бы начать свои процессы в контексте пользователя, а не в контексте LocalSystem?

Большое спасибо!

2 ответа

Оставьте это здесь для всех, кто интересуется, как это сделать: CreateEnvironmentBlock - это то, что вам нужно использовать.

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

Хорошо, я нашел обходной путь: я переключился на использование куста USERS вместо куста CURRENT_USER, используя SID, предоставленный WindowsIdentity:

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

Это прекрасно работает, хотя немного неудобно получать переменные окружения из путей реестра пользователя "среда" и "изменчивая среда", а не просто использовать встроенные функции.Net...

Но большое спасибо за вашу помощь;-)

РЕДАКТИРОВАТЬ: я не буду отмечать это как ответ, потому что это а) мое собственное решение и б) просто обходной путь

Другие вопросы по тегам