LoadUserProfile not loading profile?

This code, run from IIS under the application pool account, can run an executable such as notepad.exe or some common simple .NET application. I can even write a file from the guest account / app. But accessing the registry (for example Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main", "Local Page", null);

) from the application results in the generation of .NET APP CRASH ( 0xC0000142

).

I'm sure LoadUserProfile()

this is what doesn't work.

I've tried several options, including SetUserObjectSecurity

for the "current" window station and desktop (for EXPLICT_ACCESS with WINSTA_ALL_ACCESS | READ_CONTROL and GENERIC_ALL respectively, but this has no discernible effect). I've seen these window station and desktop permissions suggested elsewhere.

I tried with options LogonUserEx()

, or CreateProcessWithLogon()

(instead of LogonUser()

, and LoadProfile()

), and CreateProcessWithToken(...LOGON_WITH_PROFILE...)

, CreateProcessAsUser()

.

One thing I don't understand is why LoadUserProfile()

it doesn't seem to have any connection to the login / session / window / desktop / process system. What do we upload a profile to? Maybe I am loading the profile but not where the target can access it?

I have logged in as batch rights for the target account, and all kinds of rights for the service account, including administrator, act as part of the OS, adjust memory quotas, replace tokens, backup and restore. I suspect UAC or similar mechanism strips some of these rights from the application pool account when it is created.

Anyway, the big question is, how do we successfully load the profile from the service?

The target applications work fine when IIS is called to execute the same user account using the built-in System.Diagnostics.Process, but fails when using a different user account. Obviously . Process.Start internally calls CreateProcessWithLogonW (CPLW) when credentials are specified that cannot be called from Windows Servicing.

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 severely crashes IIS with an "Access Violation Exception" with every variation changed, which indicates that the pinvoke signature is very bad. (maybe I closed it sometime, or maybe it worked just (un) luck).

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

      

+3


source to share


1 answer


I am aware of your problems because I / faced the same odd behavior on the Service. I created some code that does the trick if the user you are trying to use has admin rights (nothing else is required)

The service must be running as the LocalSystem account to be able to impersonate users. as

Act as part of the
Duplicate Token system



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

      

and

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

      

+1


source







All Articles