Как выдать себя за пользователя в управляемом коде? - PullRequest
6 голосов
/ 11 мая 2010

С учетом имени пользователя и пароля, как мне выдать себя за этого пользователя и запустить некоторый код от имени этого пользователя.

И под управлением я подразумеваю без шуток и дллимпортов

Ответы [ 5 ]

13 голосов
/ 12 мая 2010

Это созданный нами класс-оболочка, работающий на нескольких разных платформах Windows:

public class Impersonator
{
    // constants from winbase.h
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_LOGON_NETWORK = 3;
    public const int LOGON32_LOGON_BATCH = 4;
    public const int LOGON32_LOGON_SERVICE = 5;
    public const int LOGON32_LOGON_UNLOCK = 7;
    public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
    public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

    public const int LOGON32_PROVIDER_DEFAULT = 0;
    public const int LOGON32_PROVIDER_WINNT35 = 1;
    public const int LOGON32_PROVIDER_WINNT40 = 2;
    public const int LOGON32_PROVIDER_WINNT50 = 3;

    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern int LogonUserA(String lpszUserName, 
        String lpszDomain,
        String lpszPassword,
        int dwLogonType, 
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    public static extern int DuplicateToken(IntPtr hToken, 
        int impersonationLevel,  
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
    public static extern  bool CloseHandle(IntPtr handle);

    public static WindowsImpersonationContext LogOn(string userName, string password)
    {
        return LogOn(userName, password, "");
    }

    public static WindowsImpersonationContext LogOn(string userName, string password, string domain)
    {
        WindowsIdentity tempWindowsIdentity;
        WindowsImpersonationContext impersonationContext;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if(RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return impersonationContext;
                    }
                }
            }
            else
            {
                var win32 = new Win32Exception(Marshal.GetLastWin32Error());
                //throw new Exception(string.Format("{0}, Domain:{1}, User:{2}, Password:{3}",
                //    win32.Message, domain, userName, password));
                throw new Exception(win32.Message);
            }
        }
        if(token!= IntPtr.Zero)
            CloseHandle(token);
        if(tokenDuplicate!=IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return null; // Failed to impersonate
    }

    public static bool LogOff(WindowsImpersonationContext context)
    {
        bool result = false;
        try
        {
            if (context != null)
            {
                context.Undo();
                result = true;
            }
        }
        catch
        {
            result = false;
        }
        return result;
    }
}
2 голосов
/ 27 июля 2012

Все примеры, которые я видел, не учитывают тот факт, что тип входа в систему не подходит для всех решений.

Например, это будет работать только в том случае, если пользователь, который вы олицетворяете, имеет разрешение на вход в целевую систему. Не всегда так при доступе к удаленному серверу SQL Server. LOGON32_LOGON_INTERACTIVE

NetworkClearText - единственный, который работает последовательно для использования с соединениями SQL Server. - Отсутствие открытого текста не означает, что учетные данные передаются небезопасным образом.

Когда в рабочей группе вам нужно выдать себя за пользователя домена, то работает NewCredentials. (не проверено с подключениями к SQL Server)

2 голосов
/ 11 мая 2010

Я свел его к двум простым методам:

public bool ImpersonateValidUser(String userName, String domain, String password)
public void UndoImpersonation()

Вы можете напрямую скопировать / вставить класс ниже и использовать его в своем проекте:

    class ImpersonationContext
    {
        [DllImport("advapi32.dll")]
        public static extern int LogonUserA(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern bool CloseHandle(IntPtr handle);

        public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
        public const int LOGON32_LOGON_INTERACTIVE = 2;
        public const int LOGON32_PROVIDER_DEFAULT = 0;
        public const int LOGON32_PROVIDER_WINNT50 = 3;
        WindowsImpersonationContext impersonationContext;

        public bool ImpersonateValidUser(String userName, String domain, String password)
        {
            WindowsIdentity tempWindowsIdentity;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
                    LOGON32_PROVIDER_WINNT50, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return true;
                        }
                    }
                }
            }
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);
            return false;
        }

        public void UndoImpersonation()
        {
            impersonationContext.Undo();
        }
    }
1 голос
/ 11 мая 2010

Есть похожий вопрос с отличным ответом здесь .

Посмотрите на WindowsImpersonationContext для получения дополнительной информации (также есть еще один отличный пример кода)

0 голосов
/ 11 мая 2010

См. ImpersonationHelper из Можно ли безопасно получить значение SecureString из VB .NET? . Код готов к работе и надежен.

Он поддерживает IDisposable, содержит метод RunAs (который неоценим), пароли обрабатываются как SecureString и другие полезные мелочи. Я также предоставил тестовый код для класса ImpersonationHelper, который чрезвычайно полезен для устранения неполадок, и их методы расширения SecureString, которые пригодятся.

...