Question

Je suis tenté de créer un nouveau processus dans le contexte d'un utilisateur spécifique en utilisant la fonction de l'API CreateProcessAsUser Windows, mais semble être en cours d'exécution dans un problème de sécurité plutôt désagréable ...

Avant d'expliquer plus loin, voici le code que je suis actuellement à l'aide pour lancer le nouveau processus. (Un processus de console - PowerShell pour être précis, mais il ne devrait pas d'importance)

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

Par souci de la question ici, ignorer le code traitant des variables d'environnement (je l'ai testé cette section indépendante et il semble fonctionner.)

Maintenant, l'erreur que je reçois est la suivante (jeté à la ligne suite à l'appel à CreateProcessAsUSer):

  

"Un privilège requis n'est pas tenu par le client" (code d'erreur 1314)

(Le message d'erreur a été découverte en supprimant le paramètre de message du code de gestion constructeur Win32Exception. Il est vrai que mon erreur ici peut ne pas être le meilleur, mais c'est une question un peu hors de propos. Nous vous invitons à commenter si vous le souhaitez , cependant.) Je suis vraiment confus quant à la cause de cette erreur vague dans cette situation. documentation MSDN et diverses discussions du forum me ont seulement donné tant de conseils, et surtout étant donné que les causes de ces erreurs semblent être très variées, je ne sais pas quelle partie du code que je dois modifier. Peut-être qu'il est tout simplement un seul paramètre que je dois changer, mais je peux faire le mauvais / pas assez WinAPI appelle à tout ce que je sais. Ce qui me confond est grandement que la version précédente du code qui utilise la fonction ordinaire CreateProcess (équivalent à l'exception du paramètre jeton utilisateur) a parfaitement fonctionné très bien. Si je comprends bien, il suffit d'appeler la fonction d'ouverture de session utilisateur pour recevoir la poignée de jeton approprié, puis le dupliquer afin qu'il puisse être transmis à CreateProcessWithLogonW.

Toutes les suggestions de modifications au code ainsi que des explications seraient bienvenus.

Remarques

J'ai réfère principalement à la documentation MSDN (ainsi que PInvoke.net pour la fonction C # / jambe de force / déclarations de ENUM). en particulier les pages suivantes semblent avoir beaucoup d'informations dans les sections Remarques, dont certains peuvent être importants et me éludant:

Modifier

Je viens d'essayer sur la suggestion de Mitch, mais malheureusement, l'ancienne erreur vient d'être remplacé par un nouveau: « Le système ne peut pas trouver le fichier spécifié. » (Code d'erreur 2)

L'appel précédent à été remplacé par <=> ce qui suit:

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

Notez que ce code n'utilise le jeton en double mais l'original, comme les docs MSDN semblent indiquer.

Et voici une autre tentative à l'aide <=>. L'erreur cette fois-ci est « échec: nom d'utilisateur inconnu ou mot de passe incorrect » (code d'erreur 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);

J'ai aussi essayé spécifier le nom d'utilisateur au format UPN ( "Alex @ Alex-PC") et en passant le domaine indépendamment comme second argument, en vain (erreur identique).

Était-ce utile?

La solution

Ahh ... semble liker j'ai été pris par l'un des plus grands gotchas dans la programmation WinAPI Interop. En outre, le code d'affichage pour mes déclarations de fonction aurait été une bonne idée dans ce cas.

Quoi qu'il en soit, tout ce que je devais faire était d'ajouter un argument à l'attribut DllImport de la fonction spécifiant CharSet = CharSet.Unicode. Cela a fait le tour pour les deux fonctions CreateProcessWithLogonW et CreateProcessWithTokenW. Je suppose que finalement vient de me frapper que le suffixe W des noms de fonction appelée Unicode et que je devais préciser explicitement en C #! Voici les correcte déclarations de fonction au cas où quelqu'un est intéressé:

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

Autres conseils

De :

  

En règle générale, le processus qui appelle la   fonction CreateProcessAsUser doit avoir   le SE_ASSIGNPRIMARYTOKEN_NAME et   privilèges SE_INCREASE_QUOTA_NAME. Si   cette fonction échoue avec   ERROR_PRIVILEGE_NOT_HELD (1314), l'utilisation   la fonction CreateProcessWithLogonW   au lieu. CreateProcessWithLogonW   ne nécessite pas de privilèges spéciaux, mais   le compte d'utilisateur spécifié doit être   autorisés à se connecter de manière interactive.   En général, il est préférable d'utiliser   CreateProcessWithLogonW pour créer un   processus avec d'autres références.

Voir ce billet de blog Comment appeler CreateProcessWithLogonW & CreateProcessAsUser .NET

Peppers Jonathan à condition que cette grande partie du code qui fixe mes questions

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top