LoadUserProfile实际上没有加载配置文件?

时间:2014-12-11 22:24:04

标签: c# winapi iis com-interop

此代码从应用程序池帐户下的IIS运行,可以运行像notepad.exe或某些自定义简单.NET应用程序的可执行文件。我甚至可以从来宾帐户/应用程序写入文件。但是从应用程序访问注册表(例如Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main", "Local Page", null);)会导致通用的.NET APP CRASH(0xC0000142)。

我很确定LoadUserProfile()是没有效果的。

我尝试了几种变体,包括SetUserObjectSecurity用于“当前”窗口站和桌面(分别用于带有WINSTA_ALL_ACCESS | READ_CONTROL和GENERIC_ALL的EXPLICT_ACCESS,但这没有明显的效果)。我在其他地方看到了对窗口站和桌面的访问权限。

我尝试过使用LogonUserEx()CreateProcessWithLogon()(而不是LogonUser()LoadProfile())和CreateProcessWithToken(...LOGON_WITH_PROFILE...)CreateProcessAsUser()的变体。

有一点我不明白为什么LoadUserProfile()似乎与登录/会话/窗口站/桌面/进程没有任何关联。我们将个人资料加载到什么位置?也许我正在加载配置文件,但没有加载到目标可以访问它的位置?

我已登录为目标帐户的批量权限以及服务帐户的各种权限,包括管理员,作为操作系统的一部分,调整内存配额,替换令牌,备份和还原。我怀疑UAC或类似的机制在创建时从应用程序池帐户中删除了部分权限。

无论如何,最大的问题是我们如何从服务中成功加载配置文件?

目标应用程序在由IIS调用以使用内置System.Diagnostics.Process由同一用户帐户执行时运行正常,但在指定其他用户帐户时失败。 Apparently“当指定凭据时,Process.Start在内部调用CreateProcessWithLogonW(CPLW),无法从Windows服务环境调用。”

uint exitCode;
IntPtr userToken = IntPtr.Zero;
IntPtr userProfile = IntPtr.Zero;

try
{
    if (!Native.LogonUser(
        username,
        domain,
        password,
        Native.LOGON32_LOGON_BATCH,
        Native.LOGON32_PROVIDER_DEFAULT,
        ref userToken))
    {
        var win32Ex = new Win32Exception(Marshal.GetLastWin32Error());
        throw new Exception("LogonUser failed: " + win32Ex.Message, win32Ex);
    }

    Native.PROFILEINFO profileInfo = new Native.PROFILEINFO();
    profileInfo.dwSize = Marshal.SizeOf(profileInfo);
    profileInfo.lpUserName = username;

    if (!Native.LoadUserProfile(userToken, ref profileInfo))
    {
        var win32Ex = new Win32Exception(Marshal.GetLastWin32Error());
        throw new Exception("LoadUserProfile failed: " + win32Ex.Message, win32Ex);
    }

    Native.STARTUPINFO startUpInfo = default(Native.STARTUPINFO);
    startUpInfo.cb = Marshal.SizeOf(startUpInfo);
    startUpInfo.lpDesktop = string.Empty;

    Native.PROCESS_INFORMATION processInfo = default(Native.PROCESS_INFORMATION);

    try
    {
        if (!Native.CreateProcessAsUserW(
            userToken,
            command,
            // CreateProcessAsUser() doesn't include the executable name in the args as other mechanisms do,
            // and so when you read them in on the other side (which skips args[0] by convention) you'll be missing
            // your expected first argument!
            string.Format("\"{0}\" {1}", command, arguments),
            IntPtr.Zero,
            IntPtr.Zero,
            true,
            0,
            IntPtr.Zero,
            null,
            ref startUpInfo,
            out processInfo))
        {
            var win32Ex = new Win32Exception(Marshal.GetLastWin32Error());
            throw new Exception("CreateProcessAsUserW failed: " + win32Ex.Message, win32Ex);
        }

        Native.WaitForSingleObject(processInfo.hProcess, Native.INFINITE);

        if (!Native.GetExitCodeProcess(processInfo.hProcess, out exitCode))
        {
            var win32Ex = new Win32Exception(Marshal.GetLastWin32Error());
            throw new Exception("GetExitCodeProcess failed: " + win32Ex.Message, win32Ex);
        }
    }
    finally
    {
        Native.CloseHandle(processInfo.hThread);
        Native.CloseHandle(processInfo.hProcess);
    }
}
finally
{
    if (userProfile != IntPtr.Zero) Native.UnloadUserProfile(userToken, userProfile);
    if (userToken != IntPtr.Zero) Native.CloseHandle(userToken);
}

ViewBag.Message = string.Format("VersionB ran to the end with exit code ({0})", exitCode);

return View("Index");

LogonUserEx使用“访问冲突异常”严重崩溃IIS,我试过的每个变体都表明pinvoke签名是非常错误的。 (也许我曾经把它关闭过一次,或者说它的工作只是(非)幸运)。

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] // to invoke the 'W' variant, I tried some variations there AND specifed MarshallAs on the strings.
internal static extern bool LogonUserEx(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    ref IntPtr phToken,
    ref IntPtr ppLogonSid, // I tried these as out and no decoration 
    ref IntPtr ppProfileBuffer, // I tried null, IntPtr.zero
    ref IntPtr pdwProfileLength, //
    ref IntPtr pQuotaLimits
);

1 个答案:

答案 0 :(得分:1)

我知道你的问题,因为我/在服务上面临同样奇怪的行为。我创建了一些代码,如果用户尝试模拟,有管理员权限(没有其他请求)

必须以LocalSystem帐户启动服务才能获得模拟用户的权限。像

  

作为系统的一部分   重复令牌

public class ImpersonateUserClass
{
    public static IntPtr ImpersonateUser(string sUsername, string sDomain, string sPassword)
    {
        // initialize tokens
        var pExistingTokenHandle = new IntPtr(0);
        pExistingTokenHandle = IntPtr.Zero;
        IntPtr token = IntPtr.Zero;

        // if domain name was blank, assume local machine
        if (sDomain == "")
        {
            sDomain = Environment.MachineName;
        }

        try
        {
            unsafe
            {
                const int LOGON32_PROVIDER_DEFAULT = 0;
                bool bImpersonated = NativMethodes.LogonUser(sUsername, sDomain, sPassword,
                                     (int) NativMethodes.LOGON_TYPE.LOGON32_LOGON_BATCH, 
                                     LOGON32_PROVIDER_DEFAULT,
                                     out pExistingTokenHandle);

                // did impersonation fail?
                if (false == bImpersonated)
                {
                    throw new Win32Exception("bImpersonated");
                }

                bool bRetVal = NativMethodes.DuplicateTokenEx(pExistingTokenHandle,
                    0,
                    null,
                    NativMethodes.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                    NativMethodes.TOKEN_TYPE.TokenPrimary,
                    out token);

                // did DuplicateToken fail?
                if (false == bRetVal)
                {
                    int nErrorCode = Marshal.GetLastWin32Error();
                    // close existing handle
                    NativMethodes.CloseHandle(pExistingTokenHandle);

                    throw new Win32Exception("bRetVal");
                }
                else
                    return token;
            }
        }
        catch (Exception ex)
        {

            throw ex;
        }
        finally
        {
            // close handle(s)
            if (pExistingTokenHandle != IntPtr.Zero)
                NativMethodes.CloseHandle(pExistingTokenHandle);
        }
    }

        IntPtr pDuplicateTokenHandle = IntPtr.Zero;
        try
        {
            pDuplicateTokenHandle = ImpersonateUserClass.ImpersonateUser(user.UserName, user.Domain, user.Password);
            string @path = Path.GetDirectoryName(strProcessFilename);

            var sec = new NativMethodes.SECURITY_ATTRIBUTES();
            var si = new NativMethodes.STARTUPINFO();
            var pi = new NativMethodes.PROCESS_INFORMATION();

            /*
                Click Start, Run. 
                Type gpedit.msc and click ok. 
                In the group policy editor: 
                Expand Windows Settings 
                Expand Security Settings 
                Expand Local Policies 
                Click on User Rights Assignment                  
             */

            if (NativMethodes.CreateProcessAsUser(pDuplicateTokenHandle, strProcessFilename,
                string.Format("{0} {1}", 0, strCommand), ref sec, ref sec, false,
                (uint)NativMethodes.CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT, IntPtr.Zero,
                @path, ref si, out pi))
            {
                int err = Marshal.GetLastWin32Error();
                if (err != 0)
                    throw new Exception("Failed CreateProcessAsUser error: " + new Win32Exception());
                try
                {
                    _process = Process.GetProcessById(pi.dwProcessId);
                    if (_process != null)
                    {
                        _process.WaitForExit();
                    }
                    else
                    {
                        NativMethodes.WaitForSingleObject(pDuplicateTokenHandle, NativMethodes.INFINITE);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Not able to wait for the program", ex);
                }
            }
            else
                throw new Exception("Failed CreateProcessAsUser error: " + new Win32Exception());
        }
        finally
        {
            if (pDuplicateTokenHandle != IntPtr.Zero)
                NativMethodes.CloseHandle(pDuplicateTokenHandle);
        }

        return "";