mattjohnsonpint/SimpleImpersonation

Add method to load an unload user profile.

Closed this issue · 2 comments

I use something like this to load user profile. It could be added to SimpleImpersonation.

internal class ImpersonationHelper
    {
        /// <summary>
        /// Profile info.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct PROFILEINFO
        {
            public static readonly int SizeOf = Marshal.SizeOf(typeof(PROFILEINFO));

            public int dwSize;                 // Set to sizeof(PROFILEINFO) before calling
            public int dwFlags;                // See PI_ flags defined in userenv.h
            public string lpUserName;          // User name (required)
            public string lpProfilePath;       // Roaming profile path (optional, can be NULL)
            public string lpDefaultPath;       // Default user profile path (optional, can be NULL)
            public string lpServerName;        // Validating domain controller name in netbios format (optional, can be NULL but group NT4 style policy won't be applied)
            public string lpPolicyPath;        // Path to the NT4 style policy file (optional, can be NULL)
            public IntPtr hProfile;            // Filled in by the function.  Registry key handle open to the root.
        }

        /// <summary>
        /// Load user profile.
        /// </summary>
        /// <param name="hToken"></param>
        /// <param name="lpProfileInfo"></param>
        /// <returns></returns>
        [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool LoadUserProfile(
        IntPtr hToken,               // user token
        ref PROFILEINFO lpProfileInfo  // profile
        );

        /// <summary>
        /// Unload user profile.
        /// </summary>
        /// <param name="hToken"></param>
        /// <param name="hProfile"></param>
        /// <returns></returns>
        [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool UnloadUserProfile(
        IntPtr hToken,   // user token
        IntPtr hProfile  // handle to registry key
        );

        /// <summary>
        /// Load user profile.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public static SafeAccessTokenHandle LoadUserProfile(IntPtr token, string userName)
        {
            PROFILEINFO profileInfo = new PROFILEINFO();
            profileInfo.dwSize = PROFILEINFO.SizeOf;
            profileInfo.lpUserName = userName;
            profileInfo.dwFlags = 1;
            bool loadSuccess = LoadUserProfile(token, ref profileInfo);

            if (!loadSuccess)
            {
                int error = Marshal.GetLastWin32Error();
                Console.WriteLine("LoadUserProfile() failed with error code: " +
                                  error);
                throw new Win32Exception(error);
            }

            if (profileInfo.hProfile == IntPtr.Zero)
            {
                int error = Marshal.GetLastWin32Error();
                Console.WriteLine(
                    "LoadUserProfile() failed - HKCU handle was not loaded. Error code: " +
                    error);
                throw new Win32Exception(error);
            }

            return new SafeAccessTokenHandle(profileInfo.hProfile);
        }
    }
UserCredentials credentials = new UserCredentials(userName, password);
Impersonation.RunAsUser(credentials, LogonType.NetworkCleartext, (safeTokenHandle) =>
{
    IntPtr loginHandle = safeTokenHandle.DangerousGetHandle();

    using (SafeAccessTokenHandle profileHandle = ImpersonationHelper.LoadUserProfile(loginHandle, userName))
    {
        try
        {
      
        }
        finally
        {
            ImpersonationHelper.UnloadUserProfile(loginHandle, profileHandle.DangerousGetHandle());
        }
    }
});

Can you also make the internal SafeAccessTokenHandle Impersonate(LogonType logonType) method public. To load user profile, administrative rights is needed. We must login, loaduserprofile with current admin user, then impersonate it seems. A Impersonation.RunAsUser with SafeAccessTokenHandle instead of UserCredential input will also be needed.

I don't want to start adding other Win32 APIs to this library. That seems like a slippery slope. However, per #60, we can expose the SafeTokenHandle so you can do the rest of it yourself.