//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
//  
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.Text;

namespace CLRUnleashed
{
	/// <summary>
	/// Podsumowanie dla WindowsImpersonation.
	/// </summary>
	public class WindowsImpersonation : System.Windows.Forms.Form
	{
		private IntPtr userToken;
		private WindowsImpersonationContext impersonationContext;
		private System.Windows.Forms.Label userNameLabel;
		private System.Windows.Forms.Label tokenLabel;
		private System.Windows.Forms.Label adminLabel;
		private System.Windows.Forms.Button impersonateButton;
		private System.Windows.Forms.TextBox userName;
		private System.Windows.Forms.TextBox token;
		private System.Windows.Forms.TextBox impersonateUser;
		private System.Windows.Forms.TextBox impersonatePassword;
		private System.Windows.Forms.ErrorProvider errorProvider;
		private System.Windows.Forms.Label impersonateUserLabel;
		private System.Windows.Forms.Label impersonatePasswordLabel;
		private System.Windows.Forms.Label impersonateDomainLabel;
		private System.Windows.Forms.TextBox impersonateDomain;
		private System.Windows.Forms.Label authenticationLabel;
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		private System.ComponentModel.Container components = null;

		private void SetCurrentUser()
		{
			WindowsIdentity identity = WindowsIdentity.GetCurrent();
			userName.Text = identity.Name;
			token.Text = string.Format("0x{0:X8}", identity.Token);
			WindowsPrincipal principal = new WindowsPrincipal(identity);
			authenticationLabel.Text = identity.AuthenticationType;
			adminLabel.Text = principal.IsInRole(WindowsBuiltInRole.Administrator) ? "Administrator" : "";
		}

		public WindowsImpersonation()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();

			SetCurrentUser();

			userToken = (IntPtr)0;
			impersonationContext = null;

			errorProvider = new ErrorProvider();
		}

		/// <summary>
		/// Oczyszczenie używanych zasobów.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Metoda wymagana do obsługi Designera - nie należy modyfikować
		/// zawartości tej metody przy użyciu edytora kodu.
		/// </summary>
		private void InitializeComponent()
		{
			this.userNameLabel = new System.Windows.Forms.Label();
			this.tokenLabel = new System.Windows.Forms.Label();
			this.adminLabel = new System.Windows.Forms.Label();
			this.impersonateUserLabel = new System.Windows.Forms.Label();
			this.impersonateDomainLabel = new System.Windows.Forms.Label();
			this.impersonatePasswordLabel = new System.Windows.Forms.Label();
			this.impersonateButton = new System.Windows.Forms.Button();
			this.userName = new System.Windows.Forms.TextBox();
			this.token = new System.Windows.Forms.TextBox();
			this.impersonateDomain = new System.Windows.Forms.TextBox();
			this.impersonateUser = new System.Windows.Forms.TextBox();
			this.impersonatePassword = new System.Windows.Forms.TextBox();
			this.errorProvider = new System.Windows.Forms.ErrorProvider();
			this.authenticationLabel = new System.Windows.Forms.Label();
			this.SuspendLayout();
			// 
			// userNameLabel
			// 
			this.userNameLabel.Location = new System.Drawing.Point(8, 16);
			this.userNameLabel.Name = "userNameLabel";
			this.userNameLabel.Size = new System.Drawing.Size(104, 23);
			this.userNameLabel.TabIndex = 0;
			this.userNameLabel.Text = "Nazwa użytkownika";
			// 
			// tokenLabel
			// 
			this.tokenLabel.Location = new System.Drawing.Point(8, 48);
			this.tokenLabel.Name = "tokenLabel";
			this.tokenLabel.Size = new System.Drawing.Size(104, 23);
			this.tokenLabel.TabIndex = 1;
			this.tokenLabel.Text = "Żeton";
			// 
			// adminLabel
			// 
			this.adminLabel.Location = new System.Drawing.Point(8, 112);
			this.adminLabel.Name = "adminLabel";
			this.adminLabel.Size = new System.Drawing.Size(200, 23);
			this.adminLabel.TabIndex = 2;
			// 
			// impersonateUserLabel
			// 
			this.impersonateUserLabel.Location = new System.Drawing.Point(360, 48);
			this.impersonateUserLabel.Name = "impersonateUserLabel";
			this.impersonateUserLabel.Size = new System.Drawing.Size(104, 23);
			this.impersonateUserLabel.TabIndex = 3;
			this.impersonateUserLabel.Text = "Nazwa użytkownika";
			// 
			// impersonateDomainLabel
			// 
			this.impersonateDomainLabel.Location = new System.Drawing.Point(360, 16);
			this.impersonateDomainLabel.Name = "impersonateDomainLabel";
			this.impersonateDomainLabel.Size = new System.Drawing.Size(104, 23);
			this.impersonateDomainLabel.TabIndex = 4;
			this.impersonateDomainLabel.Text = "Domena";
			// 
			// impersonatePasswordLabel
			// 
			this.impersonatePasswordLabel.Location = new System.Drawing.Point(360, 80);
			this.impersonatePasswordLabel.Name = "impersonatePasswordLabel";
			this.impersonatePasswordLabel.Size = new System.Drawing.Size(104, 23);
			this.impersonatePasswordLabel.TabIndex = 5;
			this.impersonatePasswordLabel.Text = "Hasło";
			// 
			// impersonateButton
			// 
			this.impersonateButton.Location = new System.Drawing.Point(472, 112);
			this.impersonateButton.Name = "impersonateButton";
			this.impersonateButton.Size = new System.Drawing.Size(128, 40);
			this.impersonateButton.TabIndex = 6;
			this.impersonateButton.Text = "Logowanie";
			this.impersonateButton.Click += new System.EventHandler(this.OnUserAction);
			// 
			// userName
			// 
			this.userName.Location = new System.Drawing.Point(120, 8);
			this.userName.Name = "userName";
			this.userName.ReadOnly = true;
			this.userName.Size = new System.Drawing.Size(232, 30);
			this.userName.TabIndex = 7;
			this.userName.Text = "";
			// 
			// token
			// 
			this.token.Location = new System.Drawing.Point(120, 40);
			this.token.Name = "token";
			this.token.ReadOnly = true;
			this.token.Size = new System.Drawing.Size(232, 30);
			this.token.TabIndex = 8;
			this.token.Text = "";
			// 
			// impersonateDomain
			// 
			this.impersonateDomain.Location = new System.Drawing.Point(472, 8);
			this.impersonateDomain.Name = "impersonateDomain";
			this.impersonateDomain.Size = new System.Drawing.Size(232, 30);
			this.impersonateDomain.TabIndex = 9;
			this.impersonateDomain.Text = ".";
			// 
			// impersonateUser
			// 
			this.impersonateUser.Location = new System.Drawing.Point(472, 40);
			this.impersonateUser.Name = "impersonateUser";
			this.impersonateUser.Size = new System.Drawing.Size(232, 30);
			this.impersonateUser.TabIndex = 10;
			this.impersonateUser.Text = "";
			// 
			// impersonatePassword
			// 
			this.impersonatePassword.Location = new System.Drawing.Point(472, 72);
			this.impersonatePassword.Name = "impersonatePassword";
			this.impersonatePassword.PasswordChar = '*';
			this.impersonatePassword.Size = new System.Drawing.Size(232, 30);
			this.impersonatePassword.TabIndex = 11;
			this.impersonatePassword.Text = "";
			// 
			// errorProvider
			// 
			this.errorProvider.DataMember = null;
			// 
			// authenticationLabel
			// 
			this.authenticationLabel.Location = new System.Drawing.Point(8, 80);
			this.authenticationLabel.Name = "authenticationLabel";
			this.authenticationLabel.Size = new System.Drawing.Size(200, 23);
			this.authenticationLabel.TabIndex = 12;
			// 
			// WindowsImpersonation
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(9, 23);
			this.ClientSize = new System.Drawing.Size(720, 174);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.authenticationLabel,
																		  this.impersonatePassword,
																		  this.impersonateUser,
																		  this.impersonateDomain,
																		  this.token,
																		  this.userName,
																		  this.impersonateButton,
																		  this.impersonatePasswordLabel,
																		  this.impersonateDomainLabel,
																		  this.impersonateUserLabel,
																		  this.adminLabel,
																		  this.tokenLabel,
																		  this.userNameLabel});
			this.Font = new System.Drawing.Font("Trebuchet MS", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.Name = "WindowsImpersonation";
			this.Text = "Podszywanie w Windows";
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// Główny punkt wejścia dla aplikacji.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new WindowsImpersonation());
		}

		enum LogonType
		{
			LOGON32_LOGON_INTERACTIVE = 2,
			LOGON32_LOGON_NETWORK = 3,
			LOGON32_LOGON_BATCH = 4,
			LOGON32_LOGON_SERVICE = 5,
			LOGON32_LOGON_UNLOCK = 7,
			LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
			LOGON32_LOGON_NEW_CREDENTIALS = 9
		}
		enum LogonProvider
		{
			LOGON32_PROVIDER_DEFAULT = 0,
			LOGON32_PROVIDER_WINNT35 = 1,
			LOGON32_PROVIDER_WINNT40 = 2,
			LOGON32_PROVIDER_WINNT50 = 3
		}
		enum FormatMessageOptions
		{
			FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
			FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
			FORMAT_MESSAGE_FROM_STRING = 0x00000400,
			FORMAT_MESSAGE_FROM_HMODULE = 0x00000800,
			FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
			FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000,
			FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
		}
		struct NativeMethods
		{
			[DllImport("advapi32.dll", SetLastError=true)]
			public static extern bool LogonUser(string lpszUsername,
				                                string lpszDomain,
				                                string lpszPassword, 
				                                int dwLogonType,
				                                int dwLogonProvider,
				                                out IntPtr phToken);
			[DllImport("Kernel32.dll")]
			public static extern uint FormatMessage(
				uint dwFlags,		// opcje źródła i przetwarzania
				IntPtr lpSource,	// źródło wiadomości
				uint dwMessageId,	// identyfikator wiadomości
				uint dwLanguageId,	// identyfikator języka
				StringBuilder lpBuffer,    // bufor wiadomości
				uint nSize,         // maksymalna wielkość bufora wiadomości
				IntPtr arguments	// tablica argumentów wiadomości
				);
			[DllImport("Kernel32.dll")]
			public static extern int CloseHandle(IntPtr token);
		}
		private void OnUserAction(object sender, System.EventArgs e)
		{
			if(!(sender is Button))
				return;
			Button senderButton = (Button)sender;
			if(senderButton.Text == "Login")
			{
				// Żeton użytkownika Windows NT.
				IntPtr token;                     
				// Uzyskanie żetonu dla podanego użytkownika, komputera lub hasła przy użyciu niezarządzanej metody LogonUser.
				bool loggedOn = NativeMethods.LogonUser(
					// Nazwa użytkownika.
					impersonateUser.Text,    
					// Nazwa komputera.
					impersonateDomain.Text,    
					// Hasło.
					impersonatePassword.Text,   
					// Typ logowania = LOGON32_LOGON_NETWORK_CLEARTEXT.
					(int)LogonType.LOGON32_LOGON_NETWORK_CLEARTEXT,   
					// Dostawca logowania = LOGON32_PROVIDER_DEFAULT.
					(int)LogonProvider.LOGON32_PROVIDER_DEFAULT,    
					// Tutaj zwracany jest żeton dla podanego użytkownika.
					out token);            
				if(!loggedOn)
				{
					int error = Marshal.GetLastWin32Error();
					StringBuilder buffer = new StringBuilder( 256 );
					NativeMethods.FormatMessage((uint)FormatMessageOptions.FORMAT_MESSAGE_FROM_SYSTEM,
						                        IntPtr.Zero, (uint)error, 0,
						                        buffer, (uint)buffer.Capacity,
						                        IntPtr.Zero);
					errorProvider.SetError(impersonateUser,
						                   string.Format("Nie można zalogować się jako {0} ze względu na błąd 0x{1:X8}:\r\n{2}",
						                                 impersonateUser.Text, error,
						                                 buffer.ToString()));
				}
				else
				{
					errorProvider.SetError(impersonateUser, "");
					senderButton.Text = "Podszywanie";
					userToken = token;
				}
				return;
			}
			if(senderButton.Text == "Podszywanie")
			{
				Debug.WriteLine("Utworzono nową tożsamość:");
				WindowsIdentity loggedonUser = new WindowsIdentity(userToken);
				Debug.WriteLine(loggedonUser.Name);
				Debug.WriteLine(string.Format("0x{0:X8}", loggedonUser.Token));

				// Podszycie się pod użytkownika.
				impersonationContext = loggedonUser.Impersonate();

				SetCurrentUser();

				senderButton.Text = "Przywróć";
				return;
			}
			if(senderButton.Text == "Przywróć")
			{
				impersonationContext.Undo();
				NativeMethods.CloseHandle(userToken);
				impersonationContext = null;
				userToken = (IntPtr)0;

				SetCurrentUser();

				senderButton.Text = "Logowanie";
				return;
			}
		}
	}
}
