# encoding: ascii
# api: powershell
# title: Add-Type extends
# description: Arise you ever a wish to call CPython code via Add-Type cmdlets? Now you have this ability! Note that you need “morphine” library (https://github.com/gregzakh/morphine/blob/master/lib/morphine.ps1) and Python stored into $env:path
# version: 0.1
# type: function
# author: Ernst Schlee
# license: CC0
# function: Get-LogonSessions
# x-poshcode-id: 6654
# x-archived: 2017-01-01T19:28:35
# x-published: 2017-12-19T12:27:00
#
#
function Get-LogonSessions {
  Set-Content function:Add-Type (Unlock-Python)
Add-Type -Language Python -TypeDefinition @'
from calendar import timegm
from ctypes   import (
   POINTER, Structure, byref, cast, c_long, c_ulong, c_longlong,
   c_ushort, c_void_p, c_wchar_p, sizeof, windll
)
from datetime import datetime, timedelta
from enum     import IntEnum
from sys      import exit

SECURITY_LOGON_TYPE = IntEnum('SECURITY_LOGON_TYPE', ' \
   Unknown Interactive Network Batch Service Proxy Unlock \
   NetworkCleartext NewCredentials RemoteInteractive \
   CachedInteractive CachedRemoteInteractive CachedUnlock \
')

class LUID(Structure):
   _fields_ = [
      ('LowPart',  c_ulong),
      ('HighPart', c_long),
   ]

class LSA_UNICODE_STRING(Structure):
   _fields_ = [
      ('Length',        c_ushort),
      ('MaximumLength', c_ushort),
      ('Buffer',        c_wchar_p),
   ]

class LSA_LAST_INTER_LOGON_INFO(Structure):
   _fields_ = [
      ('LastSuccessfulLogon',                        c_longlong),
      ('LastFailedLogon',                            c_longlong),
      ('FailedAttemptCountSinceLastSuccessfulLogon', c_ulong),
   ]

class SECURITY_LOGON_SESSION_DATA(Structure):
   _fields_ = [
      ('Size',                  c_ulong),
      ('LogonId',               LUID),
      ('UserName',              LSA_UNICODE_STRING),
      ('LogonDomain',           LSA_UNICODE_STRING),
      ('AuthenticationPackage', LSA_UNICODE_STRING),
      ('_LogonType',            c_ulong),
      ('Session',               c_ulong),
      ('_Sid',                  c_void_p),
      ('_LogonTime',            c_longlong),
      ('LogonServer',           LSA_UNICODE_STRING),
      ('DnsDomainName',         LSA_UNICODE_STRING),
      ('Upn',                   LSA_UNICODE_STRING),
      ('UserFlags',             c_ulong),
      ('LastLogonInfo',         LSA_LAST_INTER_LOGON_INFO),
      ('LogonScript',           LSA_UNICODE_STRING),
      ('ProfilePath',           LSA_UNICODE_STRING),
      ('HomeDirectory',         LSA_UNICODE_STRING),
      ('HomeDirectoryDrive',    LSA_UNICODE_STRING),
      ('LogoffTime',            c_longlong),
      ('KickOffTime',           c_longlong),
      ('PasswordLastSet',       c_longlong),
      ('PasswordCanChange',     c_longlong),
      ('PasswordMustChange',    c_longlong),
   ]
   @property
   def LogonType(self):
      return SECURITY_LOGON_TYPE(self._LogonType).name if self._LogonType else None
   @property
   def Sid(self):
      ptr = c_wchar_p()
      if windll.advapi32.ConvertSidToStringSidW(self._Sid, byref(ptr)):
         sid = ptr.value
         windll.kernel32.LocalFree(ptr)
         return sid
   @property
   def LogonTime(self):
      lt = datetime(1601, 1, 1) + timedelta(microseconds=self._LogonTime / 10)
      return datetime.fromtimestamp(timegm(lt.timetuple()))

def printlsaerror(nts):
   msg = c_void_p()
   windll.kernel32.FormatMessageW(
      0x00001100, None, windll.advapi32.LsaNtStatusToWinError(nts),
      1024, byref(msg), 0, None
   )
   err = c_wchar_p(msg.value).value
   windll.kernel32.LocalFree(msg)
   print(err.strip() if err else 'Unknown error has been occured.')

if __name__ == '__main__':
   cnt, lsl = c_ulong(), c_void_p()
   nts = windll.secur32.LsaEnumerateLogonSessions(byref(cnt), byref(lsl))
   if nts:
      printlsaerror(nts)
      exit(1)
   itr = c_void_p(lsl.value)
   for i in range(0, cnt.value):
      lsd = c_void_p()
      nts = windll.secur32.LsaGetLogonSessionData(itr, byref(lsd))
      if nts:
         printlsaerror(nts)
         break
      slsd = cast(lsd, POINTER(SECURITY_LOGON_SESSION_DATA)).contents
      print('Logon type   : %s' % slsd.LogonType)
      print('User name    : %s\\%s' % (slsd.LogonServer.Buffer, slsd.UserName.Buffer))
      print('Auth package : %s' % slsd.AuthenticationPackage.Buffer)
      print('Session      : %u' % slsd.Session)
      print('Sid          : %s' % slsd.Sid)
      print('Logon time   : %s\n' % slsd.LogonTime)
      itr = c_void_p(itr.value + sizeof(LUID))
      windll.secur32.LsaFreeReturnBuffer(lsd)
   windll.secur32.LsaFreeReturnBuffer(lsl)
'@
}
