by Marcin Policht
In the third article of this series, I will look closer into managing
permissions on the Windows Registry. Unfortunately, WMI functionality in
this area is fairly limited. The CheckAccess method of the Standard Registry
Provider allows only checking the level of permissinons for the current
account. The type of permission is determined by hexadecimal values, which I
have declared as the following constants:
In his latest article, Marcin Policht takes an in-depth look at managing permissions in the Windows Registry using customized scripting methods.
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const KEY_ENUMERATE_SUB_KEYS = &H0008
Const KEY_NOTIFY = &H0010
Const KEY_CREATE_LINK = &H0020
Const DELETE = &H00010000
Const READ_CONTROL = &H00020000
Const WRITE_DAC = &H00040000
Const WRITE_OWNER = &H00080000
As before (in all of the scripts in the previous two articles about
registry management), I will also define five constants determining the
target registry tree.
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_CURRENT_CONFIG = &H80000005
The remaining portion of the script is fairly standard. First, we create
the oRegistry object, then provide the method name and its parameters. One of
them is the type of registry access that is being checked for the current
user, which can take one of the following values:
- query – ability to query registry value
- set – ability to set registry value
- createsub – ability to create subkey
- enumsub – ability to enumerate subkey
- notify – ability to audit changes to the key
- createlink – ability to create a symbolic link to the key (example of
such a link is the one that exists between HKEY_CLASSES_ROOT and
HKEY_LOCAL_MACHINESOFTWAREClasses) - delete – ability to delete current key
- readcontrol – ability to read permissions on the current key
- writedac – ability to modify permissions on the current key
- writeowner – ability to take ownership of the current key
The method returns -1 if the current user does not have appropriate
permissions and 0 otherwise.
For example, if you save the script as CheckAccess.vbs, then you can find
out whether the current user has permissions to set registry values by
running the following:
cscript /nologo CheckAccess.vbs set
The returned value (non-zero or zero) indicates the permissions (granted
or not granted, respectively).
sComputer = "USLT-NYPZ0010"
sMethod = "CheckAccess"
hTree = HKEY_LOCAL_MACHINE
sKey = "SOFTWAREMicrosoftWindows NTCurrentVersionWinlogontest"
Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}//" & _
sComputer & "/root/default:StdRegProv")
Set oMethod = oRegistry.Methods_(sMethod)
Set oInParam = oMethod.inParameters.SpawnInstance_()
oInParam.hDefKey = hTree
oInParam.sSubKeyName = sKey
sAccess = WScript.Arguments(0)
Select Case sAccess
Case "query" hAccess = KEY_QUERY_VALUE
Case "set" hAccess = KEY_SET_VALUE
Case "createsub" hAccess = KEY_CREATE_SUB_KEY
Case "enumsub" hAccess = KEY_ENUMERATE_SUB_KEYS
Case "notify" hAccess = KEY_NOTIFY
Case "createlink" hAccess = KEY_CREATE_LINK
Case "delete" hAccess = DELETE
Case "readcontrol" hAccess = READ_CONTROL
Case "writedac" hAccess = WRITE_DAC
Case "writeowner" hAccess = WRITE_OWNER
End Select
oInParam.uRequired = hAccess
Set oOutParam = oRegistry.ExecMethod_(sMethod, oInParam)
If oOutParam.Properties_("bGranted") = 0 Then
WScript.Echo "Current user does not have " & UCase(sAccess) & " registry
permissions"
Else
WScript.Echo "Current user has " & UCase(sAccess) & " registry permissions"
End If
In order to fully manage registry permissions we will need to resort to
use of custom Dynamic Link Libraries. I have demonstrated the
functionality of the first of them — ADsSecurity.DLL — in the series of
articles on Managing Permissions of the File System. It is part of ADSI 2.5
Software Development Kit and can be downloaded from the Microsoft Web site.
The second one, RegObj.DLL is also available for download at http://msdn.microsoft.com/vbasic/downloads/addins.asp.
Each of them needs to be registered — you can copy them to the system32
subfolder of your Windows folder and run the following at the Command
Prompt:
REGSVR32 ADsSecurity.DLL
followed by
REGSVR32 RegObj.DLL
You should receive the confirmation that both components have been
successfully registered.
The script below allows you to display contents of the Access Control
List for a particular registry key (I strongly recommend you refer to
another series of articles I published, which describe
Managing Permissions on the File System. This should get you familiar with
all the necessary terminology used here). If you save the script as
ShowRegACLs.vbs, then in order to display permissions on the
HKEY_LOCAL_MACHINESOFTWAREAdobe key, you would run the following at the
command prompt from the folder where the script resides:
cscript /nologo ShowRegACLs.vbs
"HKEY_LOCAL_MACHINESOFTWAREAdobe"
Note the leading backslash character (“”) in the registry path. It is
required.
The script might look a bit intimidating, but note that roughly one third
of it contains definition of constants. Since different characteristics of
registry permissions are stored as numeric values, we need to know how to
translate these values into more meaningful format. This is the purpose of
the constants.
You are already familiar (from the script above) with the constants
indicating the level of permissions (read, write, create key, etc.). Two other
groups of constants indicate access type (whether access is allowed or
denied) and inheritance properties (e.g. whether permissions will propagate
deeper into key hierarchy or whether permission is inherited, etc.).
If the level of permissions matches one of the “generic” types (read, write,
full control), then appropriate information is displayed. Otherwise, each
individual permission is listed.
The results also contain the name of the trustee (the account for which the
permissions are set), access type (allowed or denied), and inheritance
flags.
Unfortunately, Microsoft documentation on the subject is rather limited,
so some of the permission level values do not match any of the defined
variables. In such cases, these values are displayed in the numeric
format.
Option Explicit
On Error Resume Next
'**************************************************
'*** Constant Declarations
'**************************************************
'*** Access Control Entry Inheritance Flags
'*** Possible values for the IADsAccessControlEntry::AceFlags property.
const ADS_ACEFLAG_UNKNOWN = &h1
'*** child objects will inherit ACE of current object
const ADS_ACEFLAG_INHERIT_ACE = &h2
'*** prevents ACE inherited by the object from further propagation
const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE = &h4
'*** indicates ACE used only for inheritance (it does not affect permissions
on object itself)
const ADS_ACEFLAG_INHERIT_ONLY_ACE = &h8
'*** indicates that ACE was inherited
const ADS_ACEFLAG_INHERITED_ACE = &h10
'*** indicates that inherit flags are valid (provides confirmation of valid
settings)
const ADS_ACEFLAG_VALID_INHERIT_FLAGS = &h1f
'*** for auditing success in system audit ACE
const ADS_ACEFLAG_SUCCESSFUL_ACCESS = &h40
'*** for auditing failure in system audit ACE
const ADS_ACEFLAG_FAILED_ACCESS = &h80
'**************************************************
'*** Access Control Entry Type Values
'*** Possible values for the IADsAccessContronEntry::AceType property.
const ADS_ACETYPE_ACCESS_ALLOWED = 0
const ADS_ACETYPE_ACCESS_DENIED = &h1
const ADS_ACETYPE_SYSTEM_AUDIT = &h2
const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &h5
const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &h6
const ADS_ACETYPE_SYSTEM_AUDIT_OBJECT = &h7
'**************************************************
'*** Registry Permission Type Values
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const KEY_ENUMERATE_SUB_KEYS = &H0008
Const KEY_NOTIFY = &H0010
Const KEY_CREATE_LINK = &H0020
Const DELETE = &H00010000
Const READ_CONTROL = &H00020000
Const WRITE_DAC = &H00040000
Const WRITE_OWNER = &H00080000
Dim KEY_READ 'represents access mask designating read access to registry
key
Dim KEY_WRITE 'represents access mask designating write access to registry
key
Dim KEY_ALL_ACCESS 'represents access mask designating full access to
registry key
Dim iOffset 'value used for display only (left justifying displayed values)
Dim oADSSecurity 'object representing ADsSecurity class
Dim oReg 'object representing RegObj class
Dim oRegKey 'object representing target registry key
Dim sRegPath 'string representing path to target registry key
'*** hRights_KEY_READ is a combination of KEY_QUERY_VALUE,
'*** KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and READ_CONTROL access.
KEY_READ = KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY +
READ_CONTROL
'*** hRights_KEY_WRITE is a combination of KEY_SET_VALUE and
KEY_CREATE_SUB_KEY access.
KEY_WRITE = KEY_SET_VALUE + KEY_CREATE_SUB_KEY + READ_CONTROL
'*** hRights_KEY_FULL_ACCESS is a combination of KEY_QUERY_VALUE,
KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
' KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY and KEY_CREATE_LINK, DELETE,
' READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.
KEY_ALL_ACCESS = KEY_QUERY_VALUE + KEY_SET_VALUE + KEY_CREATE_SUB_KEY + _
KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + KEY_CREATE_LINK + _
DELETE + READ_CONTROL + WRITE_DAC + WRITE_OWNER
iOffset = 20
sRegPath = WScript.Arguments(0)
Set oADSSecurity = CreateObject("ADsSecurity")
Set oReg = CreateObject("RegObj.Registry")
Set oRegKey = oReg.RegKeyFromString(sRegPath)
Call DisplayACLs(oRegKey)
Sub DisplayACLs(oRegKey)
Dim oTargetSD 'object representing security descriptor of target file or
folder
Dim oDACL 'object representing Discretionary Access Control List
Dim oACE 'object representing individual ACE
Dim sMsg, sAccessMask 'strings containing message to be displayed
Dim hAccessMask 'number representing Access Mask value
Set oTargetSD = oADsSecurity.GetSecurityDescriptor("RGY://" &
oRegKey.FullName)
Set oDACL = oTargetSD.DiscretionaryACL
WScript.Echo "Permissions on " & oRegKey.FullName
For Each oACE in oDACL
sMsg = vbCrLf & "Trustee:" & String(iOffset - Len("Trustee:"), Chr(32)) &
oACE.Trustee & vbCrLf
sMsg = sMsg & "ACE Type:" & String(iOffset - Len("ACE Type:"), Chr(32))
Select Case oACE.AceType
Case ADS_ACETYPE_ACCESS_ALLOWED
'Implicit Allow ACE
sMsg = sMsg & "ACCESS_ALLOWED"
Case ADS_ACETYPE_ACCESS_DENIED
'Implicit Deny ACE
sMsg = sMsg & "ACCESS_DENIED"
Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
'Object Allowed ACE
sMsg = sMsg & "ACCESS_ALLOWED_OBJECT"
Case ADS_ACETYPE_ACCESS_DENIED_OBJECT
'Object Deny ACE
sMsg = sMsg & "ACCESS_DENIED_OBJECT"
End Select
Wscript.Echo sMsg
sAccessMask = ""
hAccessMask = 0
If (oACE.AccessMask AND KEY_QUERY_VALUE) Then
sAccessMask = String(iOffset, Chr(32)) & "KEY_QUERY_VALUE" & vbCrLf
hAccessMask = hAccessMask + KEY_QUERY_VALUE
End If
If (oACE.AccessMask AND KEY_SET_VALUE) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "KEY_SET_VALUE" &
vbCrLf
hAccessMask = hAccessMask + KEY_SET_VALUE
End If
If (oACE.AccessMask AND KEY_CREATE_SUB_KEY) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) &
"KEY_CREATE_SUB_KEY" & vbCrLf
hAccessMask = hAccessMask + KEY_CREATE_SUB_KEY
End If
If (oACE.AccessMask AND KEY_ENUMERATE_SUB_KEYS) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) &
"KEY_ENUMERATE_SUB_KEYS" & vbCrLf
hAccessMask = hAccessMask + KEY_ENUMERATE_SUB_KEYS
End If
If (oACE.AccessMask AND KEY_NOTIFY) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_EA" &
vbCrLf
hAccessMask = hAccessMask + KEY_NOTIFY
End If
If (oACE.AccessMask AND KEY_CREATE_LINK) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "KEY_CREATE_LINK"
& vbCrLf
hAccessMask = hAccessMask + KEY_CREATE_LINK
End If
If (oACE.AccessMask AND DELETE) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "DELETE" & vbCrLf
hAccessMask = hAccessMask + DELETE
End If
If (oACE.AccessMask AND READ_CONTROL) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "READ_CONTROL" &
vbCrLf
hAccessMask = hAccessMask + READ_CONTROL
End If
If (oACE.AccessMask AND WRITE_DAC) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_DAC" &
vbCrLf
hAccessMask = hAccessMask + WRITE_DAC
End If
If (oACE.AccessMask AND WRITE_OWNER) Then
sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_OWNER" &
vbCrLf
hAccessMask = hAccessMask + WRITE_OWNER
End If
sMsg = "ACE Permissions:" & String(iOffset - Len("ACE Permissions:"),
Chr(32))
Select Case hAccessMask
Case KEY_ALL_ACCESS Wscript.Echo sMsg & "FULL CONTROL"
Case KEY_WRITE Wscript.Echo sMsg & "WRITE"
Case KEY_READ Wscript.Echo sMsg & "READ"
Case Else WScript.Echo sMsg & oACE.AccessMask
WScript.Echo sAccessMask
End Select
sMsg = "ACE Flags:" & String(iOffset - Len("ACE Flags:"), Chr(32))
If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ACE) Then
WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ACE"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE) Then
WScript.Echo sMsg & "ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ONLY_ACE) Then
WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ONLY_ACE"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_INHERITED_ACE) Then
WScript.Echo sMsg & "ADS_ACEFLAG_INHERITED_ACE"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_VALID_INHERIT_FLAGS) Then
WScript.Echo sMsg & "ADS_ACEFLAG_VALID_INHERIT_FLAGS"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_SUCCESSFUL_ACCESS) Then
WScript.Echo sMsg & "ADS_ACEFLAG_SUCCESSFUL_ACCESS"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_FAILED_ACCESS) Then
WScript.Echo sMsg & "ADS_ACEFLAG_FAILED_ACCESS"
End If
If (oACE.AceFlags AND ADS_ACEFLAG_UNKNOWN) Then
WScript.Echo sMsg & "ADS_ACEFLAG_UNKNOWN"
End If
Next
End Sub