# encoding: ascii
# api: powershell
# title: Impersonation
# description: A Module to solve fileshare permission issues once and for all
# version: 0.1
# type: class
# author: Joel Bennett
# license: CC0
# function: Push-ImpersonationContext
# x-poshcode-id: 1856
# x-archived: 2017-04-08T23:45:40
# x-published: 2011-05-17T11:23:00
#
#
$global:ImpContextStack = new-object System.Collections.Generic.Stack[System.Security.Principal.WindowsImpersonationContext]
$global:IdStack = new-object System.Collections.Generic.Stack[System.Security.Principal.WindowsIdentity]


$global:UserToysClass = Add-Type -Namespace Huddled -Name UserToys -MemberDefinition @"
   // http://msdn.microsoft.com/en-us/library/aa378184.aspx
   [DllImport("advapi32.dll", SetLastError = true)]
   public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

   // http://msdn.microsoft.com/en-us/library/aa379317.aspx
   [DllImport("advapi32.dll", SetLastError=true)]
   public static extern bool RevertToSelf();
"@ -passthru


function Push-ImpersonationContext {
[CmdletBinding(DefaultParameterSetName="Credential")]
Param(
[Parameter(Position=0,Mandatory=$true,ParameterSetName="Credential")]
[System.Management.Automation.PSCredential]$Credential, 
[Parameter(Position=0,Mandatory=$true,ParameterSetName="Password")]
[string]$name,
[Parameter(Position=1,Mandatory=$true,ParameterSetName="Password")]
$password = (Read-Host "Password" -AsSecureString),
[Parameter(Position=2,Mandatory=$false,ParameterSetName="Password")]
[string]$domain
)
if(!$Credential) {
   if($password -is [string]) {
      $secure = New-Object System.Security.SecureString
      $password.GetEnumerator() | %{ $secure.AppendChar( $_ ) }
      $password = $secure
   }
   if($domain) {
      $user = "${name}@${domain}"
   }
   $Credential = new-object System.Management.Automation.PSCredential $user, $password
}

   Write-Verbose ([Security.Principal.WindowsIdentity]::GetCurrent() | Format-Table Name, Token, User, Groups -Auto | Out-String)

   [IntPtr]$userToken = [Security.Principal.WindowsIdentity]::GetCurrent().Token
   if(!$UserToysClass::LogonUser( 
         $Credential.GetNetworkCredential().UserName, 
         $Credential.GetNetworkCredential().Domain, 
         $Credential.GetNetworkCredential().Password, 9, 0, [ref]$userToken)
   ) {
      throw (new-object System.ComponentModel.Win32Exception( [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() ) )
   }

   $identity = New-Object Security.Principal.WindowsIdentity $userToken
   $global:IdStack.Push( $identity )
   
   $context = $Identity.Impersonate()
   $global:ImpContextStack.Push( $context )

   Write-Verbose ([Security.Principal.WindowsIdentity]::GetCurrent() | Format-Table Name, Token, User, Groups -Auto | Out-String)
   
   return $global:ImpContextStack.Peek()
}


function Pop-ImpersonationContext {
   $context = $global:ImpContextStack.Pop()
   $context.Undo();
   $context.Dispose();
}

function Get-ImpersonationContext {
   Write-Host "There are $($global:ImpContextStack.Count) contexts on the stack"
   $global:ImpContextStack.Peek()
}
