Pergunta

Eu tenho tentado criar um novo processo sob o contexto de um usuário específico usando a função CreateProcessAsUser da API do Windows, mas parecem estar a correr em um problema de segurança bastante desagradável ...

Antes de me explicar mais longe, aqui está o código que estou usando atualmente para iniciar o novo processo (um processo de console - PowerShell para ser específico, embora não deve importar)

.
    private void StartProcess()
    {
        bool retValue;

        // Create startup info for new console process.
        var startupInfo = new STARTUPINFO();
        startupInfo.cb = Marshal.SizeOf(startupInfo);
        startupInfo.dwFlags = StartFlags.STARTF_USESHOWWINDOW;
        startupInfo.wShowWindow = _consoleVisible ? WindowShowStyle.Show : WindowShowStyle.Hide;
        startupInfo.lpTitle = this.ConsoleTitle ?? "Console";

        var procAttrs = new SECURITY_ATTRIBUTES();
        var threadAttrs = new SECURITY_ATTRIBUTES();
        procAttrs.nLength = Marshal.SizeOf(procAttrs);
        threadAttrs.nLength = Marshal.SizeOf(threadAttrs);

        // Log on user temporarily in order to start console process in its security context.
        var hUserToken = IntPtr.Zero;
        var hUserTokenDuplicate = IntPtr.Zero;
        var pEnvironmentBlock = IntPtr.Zero;
        var pNewEnvironmentBlock = IntPtr.Zero;

        if (!WinApi.LogonUser("UserName", null, "Password",
            LogonType.Interactive, LogonProvider.Default, out hUserToken))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error logging on user.");

        var duplicateTokenAttrs = new SECURITY_ATTRIBUTES();
        duplicateTokenAttrs.nLength = Marshal.SizeOf(duplicateTokenAttrs);
        if (!WinApi.DuplicateTokenEx(hUserToken, 0, ref duplicateTokenAttrs,
            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary,
            out hUserTokenDuplicate))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error duplicating user token.");

        try
        {
            // Get block of environment vars for logged on user.
            if (!WinApi.CreateEnvironmentBlock(out pEnvironmentBlock, hUserToken, false))
                throw new Win32Exception(Marshal.GetLastWin32Error(),
                    "Error getting block of environment variables for user.");

            // Read block as array of strings, one per variable.
            var envVars = ReadEnvironmentVariables(pEnvironmentBlock);

            // Append custom environment variables to list.
            foreach (var var in this.EnvironmentVariables)
                envVars.Add(var.Key + "=" + var.Value);

            // Recreate environment block from array of variables.
            var newEnvironmentBlock = string.Join("\0", envVars.ToArray()) + "\0";
            pNewEnvironmentBlock = Marshal.StringToHGlobalUni(newEnvironmentBlock);

            // Start new console process.
            retValue = WinApi.CreateProcessAsUser(hUserTokenDuplicate, null, this.CommandLine,
                ref procAttrs, ref threadAttrs, false, CreationFlags.CREATE_NEW_CONSOLE |
                CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
                pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);
            if (!retValue) throw new Win32Exception(Marshal.GetLastWin32Error(),
                "Unable to create new console process.");
        }
        catch
        {
            // Catch any exception thrown here so as to prevent any malicious program operating
            // within the security context of the logged in user.

            // Clean up.
            if (hUserToken != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserToken);
                hUserToken = IntPtr.Zero;
            }

            if (hUserTokenDuplicate != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserTokenDuplicate);
                hUserTokenDuplicate = IntPtr.Zero;
            }

            if (pEnvironmentBlock != IntPtr.Zero)
            {
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);
                pEnvironmentBlock = IntPtr.Zero;
            }

            if (pNewEnvironmentBlock != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
                pNewEnvironmentBlock = IntPtr.Zero;
            }

            throw;
        }
        finally
        {
            // Clean up.
            if (hUserToken != IntPtr.Zero)
                WinApi.CloseHandle(hUserToken);

            if (hUserTokenDuplicate != IntPtr.Zero)
                WinApi.CloseHandle(hUserTokenDuplicate);

            if (pEnvironmentBlock != IntPtr.Zero)
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);

            if (pNewEnvironmentBlock != IntPtr.Zero)
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
        }

        _process = Process.GetProcessById(_processInfo.dwProcessId);
    }

Por uma questão da questão aqui, ignorar o código de lidar com as variáveis ??de ambiente (Eu testei essa seção de forma independente e parece funcionar.)

Agora, o erro que eu vejo é o seguinte (jogado na linha após a chamada para CreateProcessAsUSer):

"Um exigido privilégio não é mantido pelo cliente" (código de erro 1314)

(A mensagem de erro foi descoberto, removendo o parâmetro de mensagem do construtor Win32Exception. Na verdade, o meu erro de manipulação código aqui pode não ser a melhor, mas isso é uma questão um pouco irrelevante. Você está convidado a comentar sobre ele, se desejar , no entanto.) Eu estou realmente muito confuso quanto à causa desta vaga de erro nesta situação. documentação MSDN e vários tópicos do fórum só têm me dado tantos conselhos, e especialmente tendo em conta que as causas para esses erros parecem ser amplamente variada, não tenho idéia de qual parte do código que eu preciso modificar. Talvez seja simplesmente um único parâmetro que preciso mudar, mas eu poderia estar fazendo o errado / não o suficiente chamadas de WinAPI pelo que sei. O que me confunde muito é que a versão anterior do código que usa a função CreateProcess simples (equivalente exceto para o parâmetro de token de usuário) funcionou perfeitamente bem. Como eu entendo, é necessário apenas para chamar a função de usuário de logon para receber o identificador de token apropriado e, em seguida, duplicá-lo para que ele possa ser passado para CreateProcessAsUser.

Todas as sugestões para modificações no código, bem como explicações seria muito bem-vindos.

Notas

Eu estive referindo principalmente para a documentação do MSDN (bem como PInvoke.net para o C # função / suporte / declarações enum). As páginas seguintes, em particular, parecem ter um monte de informações nas seções Observações, alguns dos quais podem ser importantes e me iludindo:

Editar

Eu apenas tentei a sugestão de Mitch, mas infelizmente o erro de idade acaba de ser substituída por uma nova: "O sistema não pode encontrar o arquivo especificado." (Código de erro 2)

A chamada anterior para CreateProcessAsUser foi substituído com o seguinte:

retValue = WinApi.CreateProcessWithTokenW(hUserToken, LogonFlags.WithProfile, null,
    this.CommandLine, CreationFlags.CREATE_NEW_CONSOLE |
    CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
    pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);

Note que este código não usa mais o duplicado simbólica, mas sim o original, como a documentação do MSDN parecem sugerir.

E aqui está outra tentativa usando CreateProcessWithLogonW. O erro desta vez é "Falha de logon: nome de usuário desconhecido ou senha incorreta" (código de erro 1326)

retValue = WinApi.CreateProcessWithLogonW("Alex", null, "password",
    LogonFlags.WithProfile, null, this.CommandLine,
    CreationFlags.CREATE_NEW_CONSOLE | CreationFlags.CREATE_SUSPENDED |
    CreationFlags.CREATE_UNICODE_ENVIRONMENT, pNewEnvironmentBlock,
    null, ref startupInfo, out _processInfo);

Eu também tentei especificando o nome de usuário no formato UPN ( "Alex @ Alex-PC") e passando o domínio de forma independente como o segundo argumento, todos sem sucesso (erro idêntico).

Foi útil?

Solução

Ahh ... parece liker Fui pego por um dos maiores armadilhas na programação WinAPI interop. Além disso, postar o código para minhas declarações de função teria sido uma boa idéia neste caso.

De qualquer forma, tudo o que eu precisava fazer era adicionar um argumento para o atributo DllImport do CharSet = CharSet.Unicode função especificando. Isso fez o truque para ambas as funções CreateProcessWithLogonW e CreateProcessWithTokenW. Eu acho que finalmente bateu-me apenas que o sufixo W dos nomes das funções a que se refere Unicode e que eu precisava para especificar isso explicitamente em C #! Aqui estão o correta declarações de função em caso alguém estiver interessado:

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(string principal, string authority,
    string password, LogonFlags logonFlags, string appName, string cmdLine,
    CreationFlags creationFlags, IntPtr environmentBlock, string currentDirectory,
    ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInfo);

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags,
    string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags,
    IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);

Outras dicas

A partir aqui :

Normalmente, o processo que chama a função CreateProcessAsUser deve ter o SE_ASSIGNPRIMARYTOKEN_NAME e privilégios SE_INCREASE_QUOTA_NAME. E se essa função falha com ERROR_PRIVILEGE_NOT_HELD (1314), uso a função CreateProcessWithLogonW em vez de. CreateProcessWithLogonW não requer privilégios especiais, mas a conta de usuário especificado deve ser permissão para fazer logon interativamente. Geralmente, é melhor usar CreateProcessWithLogonW para criar uma processo com credenciais alternativas.

Veja este post Como chamar CreateProcessWithLogonW & CreateProcessAsUser em .NET

Jonathan Pimentas desde que esta grande pedaço de código que fixa os meus problemas

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb?ppud=4

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top