LuaOSTools
LuaOSTools provides an interface to various Windows specific APIs. It implements a thin LUA wrapper interface over the Win32 API calls.
Module
The LuaOSTools module provides the following submodules:
- luaostools.security: Functions related to User-Management (like get current user name, check group membership)
- luaostools.scard: Functions related to the WinSCard API (to access PC/SC compatible smart card readers)
All modules are contained in a single LUA C-DLL but are scoped into seperate libraries according to their functionality. To access the libraries, load them as follows:
-- Load the OSTools security library:
local security = require('luaostools.security')
-- Load the OSTools scard library:
local scard = require('luaostools.scard')
luaostools.security
| Function Name | Return Type | Description | Tags |
|---|---|---|---|
GetUserName(format: integer = 8) |
name: string or nil, win32error: integer |
Reads the current user name in the given format (1 = FullyQualifiedDN, 2 = SamCompatible, 3 = DisplayName, 6 = UniqueId, 7 = Canonical, 8 = UserPrincipal, 9 = CanonicalEx, 10, = ServicePrincipal, 12 = DnsDomain, 13 = GivenName, 14 = Surname). Returns the requested property/format or nil and the Win32 API error code. | None |
IsMemberOf(groupsid: string) |
isMember: boolean or nil, win32error: integer |
Checks, if the current user is a member of the given group (identified by the group security identifier). You can use the well known SIDs (e.g. 'S-1-5-32-544' for the Administrators group) to query for group membership or all other SID formats allowed for ConvertStringSidToSid(). Returns true/false (true if the user is a member) or nil and the Win32 API error code. |
None |
For more information, see the Win32 API documentation:
Sample code for checking group membership
local ostools = require('luaostools.security') -- load our library
-- define the user name formats
local UserNameFormat = {
Unknown = 0, -- An unknown name type.
FullyQualifiedDN = 1, -- The fully qualified distinguished name (for example, CN=Jeff Smith,OU=Users,DC=Engineering,DC=Microsoft,DC=Com).
SamCompatible = 2, -- A legacy account name (for example, Engineering\JSmith). The domain-only version includes trailing backslashes (\).
Display = 3, -- A "friendly" display name (for example, Jeff Smith). The display name is not necessarily the defining relative distinguished name (RDN).
UniqueId = 6, -- A GUID string that the IIDFromString function returns (for example, {4fa050f0-f561-11cf-bdd9-00aa003a77b6}).
Canonical = 7, -- The complete canonical name (for example, engineering.microsoft.com/software/someone). The domain-only version includes a trailing forward slash (/).
UserPrincipal = 8, -- The user principal name (for example, someone@example.com).
CanonicalEx = 9, -- The same as NameCanonical except that the rightmost forward slash (/) is replaced with a new line character (\n), even in a domain-only case (for example, engineering.microsoft.com/software\nJSmith).
ServicePrincipal = 10, -- The generalized service principal name (for example, www/www.microsoft.com@microsoft.com).
DnsDomain = 12, -- The DNS domain name followed by a backward-slash and the SAM user name.
GivenName = 13,
Surname = 14,
}
local UserNameFormatNames = {
[UserNameFormat.Unknown] = 'Unknown',
[UserNameFormat.FullyQualifiedDN] = 'FullyQualifiedDN',
[UserNameFormat.SamCompatible] = 'SamCompatible',
[UserNameFormat.Display] = 'Display',
[UserNameFormat.UniqueId] = 'UniqueId',
[UserNameFormat.Canonical] = 'Canonical',
[UserNameFormat.UserPrincipal] = 'UserPrincipal',
[UserNameFormat.CanonicalEx] = 'CanonicalEx',
[UserNameFormat.ServicePrincipal] = 'ServicePrincipal',
[UserNameFormat.DnsDomain] = 'DnsDomain',
[UserNameFormat.GivenName] = 'GivenName',
[UserNameFormat.Surname] = 'Surname',
}
-- define some well-known group sids
local Groups = {
['S-1-1-0'] = 'Everyone',
['S-1-2-0'] = 'LOCAL',
['S-1-5-2'] = 'NETWORK',
['S-1-5-18'] = 'SYSTEM',
['S-1-5-10'] = 'SELF',
['S-1-5-11'] = 'Authenticated Users',
['S-1-5-20'] = 'NETWORK SERVICE',
['S-1-5-32-544'] = 'Administrators',
['S-1-5-32-573'] = 'Event Log Readers',
['S-1-5-32-546'] = 'Guests',
['S-1-5-32-568'] = 'IIS_IUSRS',
['S-1-5-32-559'] = 'Performance Log Users',
['S-1-5-32-558'] = 'Performance Monitor Users',
['S-1-5-32-547'] = 'Power Users',
['S-1-5-32-555'] = 'Remote Desktop Users',
['S-1-5-32-580'] = 'Remote Management Users',
['S-1-5-32-581'] = 'System Managed Accounts Group',
['S-1-5-32-545'] = 'Users',
}
print("Getting user name in all formats")
for k,v in pairs(UserNameFormatNames) do
local un, err = ostools.GetUserName(k)
if un == nil then
print(string.format(' %20.20s: Error %d', v, err))
else
print(string.format(' %20.20s: %s', v, un))
end
end
print("Checking membership")
for k,v in pairs(Groups) do
local isIn, err = ostools.IsMemberOf(k)
if isIn == nil then
print(string.format(' %30.30s: Error %d', v, err))
else
if isIn then
print(string.format(' %30.30s: is member', v))
else
print(string.format(' %30.30s: not a member', v))
end
end
end
luaostools.scard
| Function Name | Return Type | Description | Tags |
|---|---|---|---|
GetReaders() |
readers: array of string or nil, errormessage: string |
Reads the list of currently attached readers. Returns and array of connected readers or nil and some error message string. | None |
GetStatusChange() |
changes: table of changes (see below) or nil, errormessage: string |
Checks, if any of the readers have a change (card removed or card added) and returns a table with details. If a card was detected, then a connection is automatically initiated, so you can call one of the other functions to exchange data with the card. If no change was detected, a empty table is returned. On error nil and an errormessage text is returned. | None |
ReadCardSerial(reader: string) |
status: integer, id: string, data: binarystring or nil, errormessage: string |
Reads the card serial number. Returns the card response status (0x9000 for read ok), the card id (as hex encoded string) and the raw (binary) card response data. On error nil and some error message string is returned. | None |
GetStatusChange() should be called cyclically to detect card changes. The function returns a table with a key for each readers change - the value then contains information about the change in the following structure:
---@class StatusChangeEvent
---@field name string Reader name
---@field state integer The event flags as reported by SCardGetStatusChange()
---@field protocol integer The active protocol code as reported by SCardGetStatusChange()
---@field handle integer The card handle (if ~= 0, this indicates a newly connected card)
The main information here is the handle value - if handle == 0 then the card was disconnected, if handle ~= 0 then a card connect event happened.
Sample code for detecting cards and reading ID
local scard = require('luaostools.scard') -- load our library
local gReaders = nil
local gCards = {}
-- return status, id
local function GetCardId()
if not gReaders then
local readers, err = scard.GetReaders()
if readers == nil then
return nil, 'no reader available'
end
print("Smartcard Readers found:")
for _,reader in pairs(readers) do
print(string.format(' Reader %s', reader))
end
gReaders = readers
end
local data, err = scard.GetStatusChange()
if data then
for k,v in pairs(data) do
if v.handle and v.handle ~= 0 then
-- card is plugged
print(string.format('[%s] Card plugged: State %08Xh', k, v.state))
local status, id, data = scard.ReadCardSerial(k)
if status == 0x9000 then
-- local raw = basexx.to_hex(data)
print(string.format(' ID = "%s" (State %08Xh)', id, status))
gCards[k] = id
return true, id
else
return nil, 'error reading card'
end
else
-- card is unplugged
local oldId = gCards[k] or ''
if oldId == '' then
-- was no card before
return nil, 'initial card remove event'
end
print(string.format('[%s] Card "%s" removed', k, oldId))
return false, oldId
end
end
end
end
-- run an endless test loop...
while true do
GetCardId()
end