Set permissions for registry, files and folders

Post your working scripts, libraries and tools
Ferry
Posts: 8
Joined: 10 Jul 2014, 15:55

Set permissions for registry, files and folders

22 Sep 2014, 09:21

Just something i would like to share with you all :)

Code: Select all

;ComObjError(false)
;AccessMask: 2032127 = FULL | 1179817 = READ | 1179958 = WRITE | 1245631 = CHANGE
;Flags: 0 = This folder only | 1 = This folder and files | 2 = This folder and subfolders | 3 = This folder, subfolders and files 

;Examples
SetSecurityRegister("HKLM\Software\Test", "S-1-1-0", "2032127")
SetSecurityFile("C:\Temp", "S-1-1-0", "1179817", "3")

ExitApp

SetSecurityRegister(RegKey, Trustee, AccessMask)
	{
	dacl := ComObjCreate("AccessControlList")
	sd := ComObjCreate("SecurityDescriptor")
	newAce := ComObjCreate("AccessControlEntry")
	sdutil := ComObjCreate("ADsSecurityUtility")
	sd := sdUtil.GetSecurityDescriptor(RegKey, 3, 1)
	dacl := sd.DiscretionaryAcl
	newAce.Trustee := Trustee
	newAce.AccessMask := AccessMask	
	newAce.AceType := 0
	dacl.AddAce(newAce)
	sdutil.SetSecurityDescriptor(RegKey, 3, sd, 1)
	Return A_LastError
	}

SetSecurityFile(File, Trustee, AccessMask, Flags)
	{
	dacl := ComObjCreate("AccessControlList")
	sd := ComObjCreate("SecurityDescriptor")
	newAce := ComObjCreate("AccessControlEntry")
	sdutil := ComObjCreate("ADsSecurityUtility")
	sd := sdUtil.GetSecurityDescriptor(File, 1, 1)
	dacl := sd.DiscretionaryAcl
	newAce.Trustee := Trustee
	newAce.AccessMask := AccessMask
	newAce.AceFlags := Flags
	newAce.AceType := 0
	dacl.AddAce(newAce)
	sdutil.SetSecurityDescriptor(File, 1, sd, 1)
	Return A_LastError
	}
Kudos
Posts: 6
Joined: 23 Aug 2015, 05:52

Re: Set permissions for registry, files and folders

05 Sep 2015, 16:40

Great! Thanks for sharing :)
Argimko
Posts: 1
Joined: 07 Mar 2016, 06:22

Re: Set permissions for registry, files and folders

07 Mar 2016, 06:24

Sometimes script don't have necessary ownership, privileges or permissions for changing registry values. My decision is more complicated and more powerfull – it can write to registry, even if Ferry's code can't helps you. It uses powershell call, but without external files – all in one script. It also don't needs admin privileges – it asks you, if you dont' have it. It takes permissions recursively for all subkeys. This is also good example how to execute powershell script from autohotkey (ahk)

Code: Select all

; group BULTIN\Users takes full control recursively
takePermissions("HKLM", "SOFTWARE\test")

; group Everyone takes full control recursively
takePermissions("HKLM", "SOFTWARE\test", "S-1-1-0")

; group Everyone takes full control not recursively and show powershell console for debug purposes
takePermissions("HKLM", "SOFTWARE\test", "S-1-1-0", false, true)


; default SID is BULTIN\Users, full SIDs list - https://support.microsoft.com/en-us/kb/243330
takePermissions(rootKey, key, sid = "S-1-5-32-545", recurse = true, debug = false) {
	psScript =
	(
		# Developed for PowerShell v4.0
		# Links:
		#	http://shrekpoint.blogspot.ru/2012/08/taking-ownership-of-dcom-registry.html
		#	http://www.remkoweijnen.nl/blog/2012/01/16/take-ownership-of-a-registry-key-in-powershell/
		#	https://powertoe.wordpress.com/2010/08/28/controlling-registry-acl-permissions-with-powershell/

		param($rootKey, $key, [System.Security.Principal.SecurityIdentifier]$sid, $recurse)
		
		switch -regex ($rootKey) {
			'HKCU|HKEY_CURRENT_USER'	{ $rootKey = 'CurrentUser' }
			'HKLM|HKEY_LOCAL_MACHINE'	{ $rootKey = 'LocalMachine' }
			'HKCR|HKEY_CLASSES_ROOT'	{ $rootKey = 'ClassesRoot' }
			'HKCC|HKEY_CURRENT_CONFIG'	{ $rootKey = 'CurrentConfig' }
			'HKU|HKEY_USERS'			{ $rootKey = 'Users' }
		}

		### Step 1 - escalate current process's privilege
		# get SeTakeOwnership, SeBackup and SeRestore privileges before executes next lines, script needs Admin privilege
		$import = '[DllImport(\"ntdll.dll\")] public static extern int RtlAdjustPrivilege(ulong a, bool b, bool c, ref bool d);'
		$ntdll = Add-Type -Member $import -Name NtDll -PassThru
		$privileges = @{ SeTakeOwnership = 9; SeBackup =  17; SeRestore = 18 }
		foreach ($i in $privileges.Values) {
			$null = $ntdll::RtlAdjustPrivilege($i, 1, 0, [ref]0)
		}

		function Take-KeyPermissions {
			param($rootKey, $key, $sid, $recurse, $recurseLevel = 0)

			### Step 2 - get ownerships of key - it works only for current key
			$regKey = [Microsoft.Win32.Registry]::$rootKey.OpenSubKey($key, 'ReadWriteSubTree', 'TakeOwnership')
			$acl = New-Object System.Security.AccessControl.RegistrySecurity
			$acl.SetOwner($sid)
			$regKey.SetAccessControl($acl)

			### Step 3 - enable inheritance of permissions (not ownership) for current key from parent
			$acl.SetAccessRuleProtection($false, $false)
			$regKey.SetAccessControl($acl)

			### Step 4 - only for top-level key, change permissions for current key and propagate it for subkeys
			# to enable propagations for subkeys, it needs to execute Steps 2-3 for each subkey (Step 5)
			if ($recurseLevel -eq 0) {
				$regKey = $regKey.OpenSubKey('', 'ReadWriteSubTree', 'ChangePermissions')
				$rule = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'FullControl', 'ContainerInherit', 'None', 'Allow')
				$acl.ResetAccessRule($rule)
				$regKey.SetAccessControl($acl)
			}

			### Step 5 - recursively repeat steps 2-5 for subkeys
			if ($recurse) {
				foreach($subKey in $regKey.OpenSubKey('').GetSubKeyNames()) {
					Take-KeyPermissions $rootKey ($key+'\'+$subKey) $sid $recurse ($recurseLevel+1)
				}
			}
		}

		Take-KeyPermissions $rootKey $key $sid $recurse
	)
	
	If (debug)
		RunWait *RunAs PowerShell.exe -NoExit -Command &{%psScript%} '%rootKey%' '%key%' '%sid%' '%recurse%'
	Else
		RunWait *RunAs PowerShell.exe -Command &{%psScript%} '%rootKey%' '%key%' '%sid%' '%recurse%',, hide
}

Daniel Beardsmore
Posts: 13
Joined: 23 Jul 2016, 15:57

Re: Set permissions for registry, files and folders

16 Aug 2018, 13:12

Note the following correction to the OP’s code:

Code: Select all

+ ; Because the powers that be refuse to permit constants
+ global ADS_SECURITY_INFO_DACL := 4

  ⋮
+ ; Only update the DACL, otherwise it will try to change the ownership!
+ sdutil.SecurityMask := ADS_SECURITY_INFO_DACL
  sdutil.SetSecurityDescriptor(File, 1, sd, 1)
GetSecurityDescriptor + SetSecurityDescriptor reads and then replaces the security descriptor. The file owner is included in the security descriptor, and if the file owner is not you, you will be helpfully notified with this delightful piece of gibberish:

0x8007051B - This security ID may not be assigned as the owner of this object.

Sure, but I didn’t ask to change the owner! Oh, but apparently even if the new SD contains the same owner as the existing SD, you have violated security policy by trying to pass away file ownership to someone else. The fact that the owner hasn’t changed from the old SD to the new SD, Windows doesn’t care.

The solution is to tell Windows that you are not changing the owner (and only the discretionary ACL), using SecurityMask. This was soon made clear on a Stack Overflow post. The excruciating part was finding out how the COM version of this catastrophe implements this request. What you have to take away from IADsSecurityUtility::put_SecurityMask method is that from a COM perspective there are no put_SecurityMask or get_SecurityMask methods, but in fact you just reference .SecurityMask directly. (On Stack Overflow, you're told to set Group and Owner to NULL and ControlFlags to DiscretionaryAclPresent (same value, 4) but this is a whole other API and trying to use this here doesn’t work. Other documentation from Microsoft refers to IADsObjectOptions, but that's not applicable when using ADsSecurityUtility. Fun. Windows, an API for every day of the week year.)

Hopefully this will save someone else a lot of the senseless torment that is drowning in Microsoft’s APIs and what they class as documentation.

Note that the OP’s code bears a striking resemblance to this — https://docs.microsoft.com/en-gb/window ... -to-a-file — so at least we have the reassurance that even Microsoft cannot give correct examples of their own tortuous API. They likewise forget to set SecurityMask. (I suspect the OP’s code is a port of Microsoft’s code.)

Return to “Scripts and Functions”

Who is online

Users browsing this forum: DizzyH, hasantr and 57 guests