View Single Post
  #6  
Old 06-02-2006, 07:30 AM
kinook kinook is online now
Administrator
 
Join Date: 03-06-2001
Location: Colorado
Posts: 6,003
The change that was made ensures that the build thread runs under the same identity as the thread that created it, which is fairly straightforward (see [1] for more details).

However, the thread's impersonated identity does not extend to processes that a thread creates via CreateProcess (which applies to many actions, including Vault), and the Vault client doesn't handle the default context it does get created in very well (and it probably would not have the necessary permissions to perform its task anyway). It turns out that it is not trivial to implement this transparently, and may not even be possible without giving the user account more rights than you probably want to (see [2], [3]).

Also, since the technique you are using waits for the build to complete, the client browser could easily timeout waiting for a response from the server.

An alternative technique would be to instead invoke the VBP console app [4] to build (using CreateProcessWithLogonW with the credentials of the impersonated identity [5]). When a process is started this way, an impersonated identity will flow properly to processes that it in turn creates. This method could be performed synchronously as well (for instance, waiting for the process to complete while populating the response page with stdout) or asynchronously, returning as soon as the process was started.

[1] http://groups.google.com/group/micro...s&rnum=3&hl=en
[2] http://groups.google.com/group/micro...9c440af87bc027
[3] http://groups.google.com/group/micro...b76b88c6b54203
[4] http://www.visualbuild.com/Manual/consoleapp.htm

[5]
Code:
    [StructLayout(LayoutKind.Sequential)]
    internal struct PROCESS_INFORMATION
    {
        internal IntPtr hProcess;
        internal IntPtr hThread;
        internal int dwProcessId;
        internal int dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct STARTUPINFO
    {
        internal int cb;
        [MarshalAs(UnmanagedType.LPTStr)]
        internal string lpReserved;
        [MarshalAs(UnmanagedType.LPTStr)]
        internal string lpDesktop;
        [MarshalAs(UnmanagedType.LPTStr)]
        internal string lpTitle;
        internal int dwX;
        internal int dwY;
        internal int dwXSize;
        internal int dwYSize;
        internal int dwXCountChars;
        internal int dwYCountChars;
        internal int dwFillAttribute;
        internal int dwFlags;
        internal short wShowWindow;
        internal short cbReserved2;
        internal IntPtr lpReserved2;
        internal IntPtr hStdInput;
        internal IntPtr hStdOutput;
        internal IntPtr hStdError;
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool CreateProcessWithLogonW(String lpszUsername,
        String lpszDomain, String lpszPassword, int dwLogonFlags, string applicationName, 
        StringBuilder commandLine, int creationFlags, IntPtr environment, string currentDirectory,
        ref STARTUPINFO sui, out PROCESS_INFORMATION processInfo);

    //dwLogonFlags Specifies the logon option 
    const int LOGON_WITH_PROFILE = 1;
    const int LOGON_NETCREDENTIALS_ONLY = 2;
 
    //dwCreationFlags - Specifies how the process is created 
    const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;

    //dwCreationFlags parameter controls the new process's priority class 
    const int NORMAL_PRIORITY_CLASS = 0x00000020;
    const int IDLE_PRIORITY_CLASS = 0x00000040;
    const int HIGH_PRIORITY_CLASS = 0x00000080;
    const int REALTIME_PRIORITY_CLASS = 0x00000100;
    const int BELOW_NORMAL_PRIORITY_CLASS = 0x00004000;
    const int ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000;


        // retrieve the path+filename of the console app
        string app = Registry.LocalMachine.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\App Paths\VisBuildCmd.exe").GetValue(null).ToString();

        StringBuilder parms = new StringBuilder();

        // pass all macro values to VBP on command-line based on user inputs
//        foreach (DictionaryEntry keypair in macros)
  //          parms.AppendFormat(" \"{0}={1}\"", keypair.Key, keypair.Value);

        // pass the filename to build
        parms.AppendFormat(" /b \"{0}\"", projectFile);

        // retrieve the impersonated identity credentials
        XmlDocument cfg = new XmlDocument();
        cfg.Load(Path.Combine(Server.MapPath("."), "web.config"));
        XmlNode ident = cfg.SelectSingleNode("configuration/system.web/identity");

        PROCESS_INFORMATION processInfo;
        STARTUPINFO startInfo = new STARTUPINFO();
        startInfo.cb = Marshal.SizeOf(startInfo);
        startInfo.lpDesktop = "winsta0\\default";

        // create the process under the credentials of our impersonated identity so that it has
        // the necessary rights for performing a build
        if (CreateProcessWithLogonW(ident.Attributes["userName"].Value, ".", ident.Attributes["password"].Value,
            LOGON_NETCREDENTIALS_ONLY /*LOGON_WITH_PROFILE*/, app, parms,
            NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, IntPtr.Zero, "", ref startInfo, out processInfo))
        {
            Response.Write("Started with Process ID " + processInfo.dwProcessId.ToString());
        }
        else
        {
            Response.Write(Marshal.GetLastWin32Error());
        }
Reply With Quote