# encoding: ascii
# api: powershell
# title: Get-LogonSessions
# description: Depends on “steroid” library (see https://github.com/gregzakharov/ps/tree/master/steroid for details).
# version: 0.1
# type: function
# author: greg zakharov
# license: CC0
# function: Get-LogonSessions
# x-poshcode-id: 5713
# x-archived: 2016-09-09T23:54:17
# x-published: 2016-01-25T19:04:00
#
#
function Get-LogonSessions {
  <#
    .SYNOPSIS
        Describes the logon session or sessions associated with a user logged (
        instead Win32_LogonSession class).
    .NOTES
        Author: greg zakharov
  #>
  
  begin {
    [accelerators]::Add('marshal', [Runtime.InteropServices.Marshal])
    
    $LUID = struct LUID {
      UInt32 'LowPart';
      UInt32 'HighPart';
    }
    
    struct LSA_UNICODE_STRING {
      UInt16 'Length';
      UInt16 'MaximumLength';
      IntPtr 'Buffer';
    } | Out-Null
    
    struct LSA_LAST_INTER_LOGON_INFO {
      Int64  'LastSuccessfulLogon';
      Int64  'LastFailedLogon';
      UInt32 'FailedAttemptCountSinceLastSuccessfulLogon';
    } | Out-Null
    
    enum SECURITY_LOGON_TYPE UInt32 {
      Interactive             = 2
      Network                 = 3
      Batch                   = 4
      Serivce                 = 5
      Proxy                   = 6
      Unlock                  = 7
      NetworkCleartext        = 8
      NewCredentials          = 9
      RemoteInteractive       = 10
      CachedInteractive       = 11
      CachedRemoteInteractive = 12
      CachedUnlcok            = 13
    } | Out-Null
    
    $slsd = struct SECURITY_LOGON_SESSION_DATA {
      UInt32                    'Size';
      LUID                      'LogonId';
      LSA_UNICODE_STRING        'UserName';
      LSA_UNICODE_STRING        'LogonDomain';
      LSA_UNICODE_STRING        'AuthenticationPackage';
      SECURITY_LOGON_TYPE       'LogonType';
      UInt32                    'Session';
      IntPtr                    'Sid';
      Int64                     'LogonTime';
      LSA_UNICODE_STRING        'LogonServer';
      LSA_UNICODE_STRING        'DnsDomainName';
      LSA_UNICODE_STRING        'Upn';
      LSA_LAST_INTER_LOGON_INFO 'LastLogonInfo';
      LSA_UNICODE_STRING        'LogonScript';
      LSA_UNICODE_STRING        'ProfilePath';
      LSA_UNICODE_STRING        'HomeDirectory';
      LSA_UNICODE_STRING        'HomeDirectoryDrive';
      Int64                     'LogoffTime';
      Int64                     'KickOffTime';
      Int64                     'PassworkLastSet';
      Int64                     'PasswordCanChange';
      Int64                     'PasswordMustChange';
    }
    
    $LsaFreeReturnBuffer = delegate secur32.dll LsaFreeReturnBuffer Int32 @(
      [IntPtr]
    )
    $LsaEnumerateLogonSessions = delegate secur32.dll LsaEnumerateLogonSessions Int32 @(
      [UInt32].MakeByRefType(), [IntPtr].MakeByRefType()
    )
    $LsaGetLogonSessionData = delegate secur32.dll LsaGetLogonSessionData Int32 @(
      [IntPtr], [IntPtr].MakeByRefType()
    )
    
    $STATUS_SUCCESS       = 0x00000000
    $STATUS_ACCESS_DENIED = 0xC0000022
  }
  process {
    try {
      [UInt32]$count = 0 #sessions counter
      [IntPtr]$first = [IntPtr]::Zero #pointer to first entry
      
      if (($ret = $LsaEnumerateLogonSessions.Invoke([ref]$count, [ref]$first)) -ne $STATUS_SUCCESS) {
        throw (New-Object Exception($('Could not enumerate logon sessions. Error 0x{0:X}' -f $ret)))
      }
      
      $ptr = $first #iteration pointer
      $str = $slsd
      for ($i = 0; $i -lt $count; $i++) {
        [IntPtr]$out = [IntPtr]::Zero
        if ($LsaGetLogonSessionData.Invoke($ptr, [ref]$out) -eq $STATUS_ACCESS_DENIED) {
          "You have $(if(!(IsAdmin)){'not'}) administrator rights."
          break
        }
        
        $str = $out -as $str
        $SECURITY_LOGON_SESSION_DATA = New-Object PSObject -Property @{
          UserName       = ($str.LogonDomain.Buffer, $str.UserName.Buffer | % {
            [Marshal]::PtrToStringUni($_)
          }) -join '\'
          LogonType      = $str.LogonType
          Session        = $str.Session
          SID            = $(try{New-Object Security.Principal.SecurityIdentifier($str.Sid)}catch{})
          Authentication = [Marshal]::PtrToStringUni($str.AuthenticationPackage.Buffer)
          LogonTime      = [DateTime]::FromFileTime($str.LogonTime)
        }
        $SECURITY_LOGON_SESSION_DATA.PSObject.TypeNames.Insert(0, 'SECURITY_LOGON_SESSION_DATA')
        $SECURITY_LOGON_SESSION_DATA
        
        $ptr = [IntPtr]($ptr.ToInt64() + $LUID::GetSize())
        [void]$LsaFreeReturnBuffer.Invoke($out)
        $str = $slsd
      } #for
    }
    catch { $_.Exception }
    finally {
      [void]$LsaFreeReturnBuffer.Invoke($first)
    }
  }
  end {
    [void][accelerators]::Remove('marshal')
  }
}
