C#에서 Windows Impersonation 하는 방법
Windows OS에 로그인한 사용한 자는 자신의 고유한 Identity를 가지는데 만약 일시적으로 다른 사람의 로그인 계정 하에서 어떤 작업을 수행할 필요가 있다면, 이를 프로그래밍을 통해 구현할 수 있다. 하나의 프로세스에서 다른 사람의 계정으로 둔갑하는 것을 Impersonation 이라 하는데, 물론 이 경우 다른 계정에 대한 로그인 정보를 가지고 있어야 한다.
아래 예제는 Impersonation하는 과정을 표현한 것인데, 먼저 다른 계정의 로그인 정보를 사용하여 Win32 API인 LogonUser() 함수를 호출하고 이때 얻은 사용자 토큰(handle)을 복제한 후, .NET의 WindowsIdentity 클래스로부터 (토큰과 함께) 객체를 생성하고 WindowsIdentity의 Impersonate() 메서드를 호출한 것이다. Impersonate()를 호출하기 전까지는 원래 계정으로 사용하는 것이고, Impersonate()을 호출한 이후부터 다른 계정을 사용하게 된다. Impersonation한 후 그 사용자를 이용한 작업을 완료한 후, 다시 원래 사용자로 돌아기 위해서는 WindowsIdentity의 Undo() 메서드를 호출하면 된다.
여기서 WindowsIdentity 클래스는 System.Security.Principal 네임스페이스에 있는 클래스로서 윈도우즈 사용자를 표현하는 클래스인데, 현재 로그인 사용자를 구하거나 다른 계정으로 Impersonation 하는 등의 기능을 가지고 있다.
아래 예제는 Impersonation하는 과정을 표현한 것인데, 먼저 다른 계정의 로그인 정보를 사용하여 Win32 API인 LogonUser() 함수를 호출하고 이때 얻은 사용자 토큰(handle)을 복제한 후, .NET의 WindowsIdentity 클래스로부터 (토큰과 함께) 객체를 생성하고 WindowsIdentity의 Impersonate() 메서드를 호출한 것이다. Impersonate()를 호출하기 전까지는 원래 계정으로 사용하는 것이고, Impersonate()을 호출한 이후부터 다른 계정을 사용하게 된다. Impersonation한 후 그 사용자를 이용한 작업을 완료한 후, 다시 원래 사용자로 돌아기 위해서는 WindowsIdentity의 Undo() 메서드를 호출하면 된다.
여기서 WindowsIdentity 클래스는 System.Security.Principal 네임스페이스에 있는 클래스로서 윈도우즈 사용자를 표현하는 클래스인데, 현재 로그인 사용자를 구하거나 다른 계정으로 Impersonation 하는 등의 기능을 가지고 있다.
예제
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
class Program
{
static void Main(string[] args)
{
IntPtr token = IntPtr.Zero;
IntPtr dupToken = IntPtr.Zero;
WindowsIdentity impUser;
WindowsImpersonationContext impCtx;
// 다른 사용자 계정으로 로그인
if (!LogonUser("User1", "Domain1", "Pwd1",
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref token))
{
Console.WriteLine("Logon failed");
return;
}
if (!DuplicateToken(token, 2, ref dupToken))
{
Console.WriteLine("Dup failed");
return;
}
// WindowsIdentity 객체 생성
impUser = new WindowsIdentity(dupToken);
// Impersonation :
// 여기서부터 다른 계정으로 사용됨
impCtx = impUser.Impersonate();
//... Impersonation 사용자 작업
var mydoc = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
foreach(var fn in Directory.GetFiles(mydoc))
{
Console.WriteLine(fn);
}
//... Imp 사용자 작업 완료
// 원래 사용자로 복귀
impCtx.Undo();
CloseHandle(dupToken);
CloseHandle(token);
}
const int LOGON32_LOGON_INTERACTIVE = 2; // dwLogonType
const int LOGON32_PROVIDER_DEFAULT = 0; // dwLogonProvider
const int SecurityImpersonation = 2; // SECURITY_IMPERSONATION_LEVEL
[DllImport("advapi32.dll")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr handle);
}
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
class Program
{
static void Main(string[] args)
{
IntPtr token = IntPtr.Zero;
IntPtr dupToken = IntPtr.Zero;
WindowsIdentity impUser;
WindowsImpersonationContext impCtx;
// 다른 사용자 계정으로 로그인
if (!LogonUser("User1", "Domain1", "Pwd1",
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref token))
{
Console.WriteLine("Logon failed");
return;
}
if (!DuplicateToken(token, 2, ref dupToken))
{
Console.WriteLine("Dup failed");
return;
}
// WindowsIdentity 객체 생성
impUser = new WindowsIdentity(dupToken);
// Impersonation :
// 여기서부터 다른 계정으로 사용됨
impCtx = impUser.Impersonate();
//... Impersonation 사용자 작업
var mydoc = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
foreach(var fn in Directory.GetFiles(mydoc))
{
Console.WriteLine(fn);
}
//... Imp 사용자 작업 완료
// 원래 사용자로 복귀
impCtx.Undo();
CloseHandle(dupToken);
CloseHandle(token);
}
const int LOGON32_LOGON_INTERACTIVE = 2; // dwLogonType
const int LOGON32_PROVIDER_DEFAULT = 0; // dwLogonProvider
const int SecurityImpersonation = 2; // SECURITY_IMPERSONATION_LEVEL
[DllImport("advapi32.dll")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr handle);
}