Managing Windows Registry with Scripting (Part 3)

By ServerWatch Staff (Send Email)
Posted Aug 19, 2002


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_MACHINE\SOFTWARE\Classes)
  • 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		= "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\test"

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_MACHINE\SOFTWARE\Adobe key, you would run the following at the command prompt from the folder where the script resides:

cscript /nologo ShowRegACLs.vbs 
"\HKEY_LOCAL_MACHINE\SOFTWARE\Adobe"

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

Page 1 of 1


Comment and Contribute

Your name/nickname

Your email

(Maximum characters: 1200). You have characters left.