Jump to content
Sign in to follow this  
badgerbeard

Setting DACL using WMI

Recommended Posts

badgerbeard

Hi all, hope someone can help...

I'm converting a VB Script to Autoit.

The VB Script is something I came across and adapted a few years ago that sets ACL on a network folder.

The first part checks if the specified user already is listed, it alters the permissions, and that works in autoit.

Here's that bit of code...

$objWMI = ObjGet("winmgmts:{(Security,Restore)}\\work\"&$Server&"\CIMV2")

Dim $objFolderSecuritySettings = $objWMI.get("Win32_LogicalFileSecuritySetting='"&$realPath&"'")

Dim $objSD

Dim $intRetVal = $objFolderSecuritySettings.GetSecurityDescriptor ($objSD)

Dim $intControlFlags = $objSD.ControlFlags

Dim $ACLUser

If $intControlFlags And $SE_DACL_PRESENT Then

Dim $arrACEs = $objSD.DACL

For $objACE In $arrACEs

if StringCompare($objace.Trustee.Name,$username,0)=0 Then

$objACE.AccessMask = $PermissionBitMask

endIf

Next

Else

$strMsg = "No DACL present in security descriptor"

EndIf

;ConsoleWrite($strMsg)

$intRetVal = $objFolderSecuritySettings.SetSecurityDescriptor ($objSD)

Share this post


Link to post
Share on other sites
badgerbeard

Hi all, hope someone can help...

I'm converting a VB Script to Autoit.

The VB Script is something I came across and adapted a few years ago that sets ACL on a network folder.

The first part checks if the specified user already is listed, it alters the permissions, and that works in autoit.

Here's that bit of code...

#Include <Security.au3>

Dim $SE_DACL_PRESENT = 0x4

Dim $server="SERVERNAME"

Dim $username="AB12345"

Dim $realPath="D:\USERS\"&"$username

Dim $PermissionBitMask=1245695 ;like modify, plus allow delete folders and files

$objWMI = ObjGet("winmgmts:{(Security,Restore)}\\"&$Server&"\root\CIMV2")

Dim $objFolderSecuritySettings = $objWMI.get("Win32_LogicalFileSecuritySetting='"&$realPath&"'")

Dim $objSD

Dim $intRetVal = $objFolderSecuritySettings.GetSecurityDescriptor ($objSD)

Dim $intControlFlags = $objSD.ControlFlags

Dim $ACLUser

If $intControlFlags And $SE_DACL_PRESENT Then

Dim $arrACEs = $objSD.DACL

For $objACE In $arrACEs

if StringCompare($objace.Trustee.Name,$username,0)=0 Then

$objACE.AccessMask = $PermissionBitMask

endIf

Next

EndIf

$intRetVal = $objFolderSecuritySettings.SetSecurityDescriptor ($objSD)

If they aren't found the original VB script creates a new array using a dictionary object to copy the existing DACL explicit entries, then adds an entry for the new user. This works fine in the VB script, but I can't quite get it to work in autoit.

Here's my attempt at the autoit conversion.

$aAccount = _Security__LookupAccountName($domain&"\"&$username)

$SID=$aAccount[0]

$objWMI = ObjGet("winmgmts:") ;local connection, trusted

$objSID=$objwmi.get("Win32_SID.SID='"&$SID&"'")

$objTrusteeClass=$objwmi.get("Win32_Trustee")

$objTrustee=$objTrusteeClass.SpawnInstance_()

;Set the properties

$objTrustee.Domain = $objSID.ReferencedDomainName

$objTrustee.Name = $objSID.AccountName

$objTrustee.SID = $objSID.BinaryRepresentation

$objTrustee.SidLength = $objSID.SidLength

$objTrustee.SIDString = $objSID.SID

;Create a new blank Win32_ACE object

$objACEClass = $objWMI.Get("Win32_ACE")

$objACE2 = $objACEClass.SpawnInstance_()

;Set the properties

$objACE2.AccessMask = $PermissionBitMask

$objACE2.Trustee = $objTrustee

$objACE2.AceType = 0 ;Allow

$objACE2.AceFlags = 3 ;Inheritance to sub-folders and files

$oDictionary =ObjCreate("Scripting.Dictionary")

$key=0

For $objACE In $arrACEs

If Not BitAND($objACE.AceFlags,16) Then ;not inherited, explicitly set

$oDictionary.Add($key, $objACE) ;**

$key=$key+1

EndIf

Next

$oDictionary.Add($key, $objACE2) ;**

$objSD.DACL = $oDictionary.items ;***

$intRetVal = $objFolderSecuritySettings.SetSecurityDescriptor ($objSD)

The problem is either with the way I'm adding to the dictionary array ( marked with **) or the assigning of the dictionary array to the DACL (***)

And here's the original vb, (1) Get the SID has been replaced with _Security__LookupAccountName...

'(1) Get the SID of the user or group account.

'Create a WMI object on the local computer (Assuming the local computer is in the

'same domain (or in a trusted domain) as the one specified

Set objWMI = GetObject("winmgmts:\\.\root\cimv2")

'Get a collection of Users objects, using ExecQuery

Set colUsers = objWMI.ExecQuery("SELECT * FROM WIN32_ACCOUNT WHERE Domain = '" & _

Domain & "' AND Name = '" & Username & "'")

'The collection should only have one user in it. Loop through the one user collection

'and create a user object

For Each UserName in colUsers

Set objUserName = Username

Next

'Next, we need both the binary and string representation of the user's SID to create a new

'Win32_Trustee. objUser.SID only returns the string version. Create a Win32_SID object by

'referencing the objUser's SID

Set objSID = objWMI.Get("Win32_SID.SID='" & objUserName.SID & "'")

'(2) Create a new blank Win32_Trustee object, and set it's properties to the appropriate

' values

'Create a new blank Win32_Trustee object

Set objTrusteeClass = objWMI.Get("Win32_Trustee")

Set objTrustee = objTrusteeClass.SpawnInstance_()

'Set the properties

objTrustee.Domain = objSID.ReferencedDomainName

objTrustee.Name = objSID.AccountName

objTrustee.SID = objSID.BinaryRepresentation

objTrustee.SidLength = objSID.SidLength

objTrustee.SIDString = objSID.SID

'(3) Create a new blank Win32_ACE object, and set it's properties to the appropriate

' values. The Trustee property should point to the newly created Win32_Trustee

'Create a new blank Win32_ACE object

Set objACEClass = objWMI.Get("Win32_ACE")

Set objACE = objACEClass.SpawnInstance_()

'Set the properties

objACE.AccessMask = PermissionBitMask

objACE.Trustee = objTrustee

objACE.AceType = 0 'Allow

objACE.AceFlags = 3 'Inheritance to sub-folders and files

'(4) Add the ACE to the DACL. This is the hard part, because to keep the existing

' ACEs means we have to resize the array. The DACL array is not a dynamic array, so

' the solution is to create a new array of the right size, copy the explicit contents

' of the existing DACL (and the new ACE) into it, and then replace the existing

' DACL with the new one.

'Create a Dictionary object (a hash). We're using a dictionary object, because it

'can be dynamically resized

Set objDictionary = CreateObject("Scripting.Dictionary")

'Loop through the DACL array and populate the Dictionary object only with ACE objects

'that have EXPLICIT PERMISSIONS. This can be tested by checking the AceFlags property.

'If the 5th bit (16) is set to 1 then, the permission is inherited (See AceFlags

'description above.

'Doesn't matter what the Dictionary key is, The corresponding item is all that matters,

'being the ACE of the existing DACL. We just use i as the key here

For i = LBound(objSD.DACL) to UBound(objSD.DACL)

If NOT objSD.DACL(i).AceFlags AND 16 Then

objDictionary.Add i, objSD.DACL(i)

End If

Next

'Add the new ACE.

objDictionary.Add "Empty Key", objACE

'Use the items method to return a list of just the items, and overwrite the existing

'DACL with the new list of ACEs

objSD.DACL = objDictionary.Items

'(5) Finally, write back the Security Descriptor

If objSecurity.SetSecurityDescriptor(objSD) = 0 Then

SetPermissions = 0

Else

SetPermissions = 1

Err.Clear

End If

Edited by badgerbeard

Share this post


Link to post
Share on other sites
Jos

Can you stop making new topics and stick to the original one you created?

Will merge this with the first one and the duplicate has been removed.

Jos


Visit the SciTE4AutoIt3 Download page for the latest versions  - Beta files                                How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites
badgerbeard

Can you stop making new topics and stick to the original one you created?

Will merge this with the first one and the duplicate has been removed.

Jos

Sorry, not sure how the first part-post happened! I was tidying the code, removing consolewrites etc and a few times hit tab to indent, but that jumps to the post/preview/cancel links, so must have hit one of them by accident then clicked on the back button without realising I'd posted.

Share this post


Link to post
Share on other sites
badgerbeard

AAAAAaaaaaand there's a question in there...?

:mellow:

Eventually.... :(

Share this post


Link to post
Share on other sites
badgerbeard

I've figured out a way of doing this if anyone's interested...

Instead of spawning instances of the Win32_ACE and Win32_trustee, I just copy the existing objACE values to the dictionary object, then alter the last one to have the user name, SID etc that I want and tag that on the end of the dictionary, then overwrite the full DACL with the values in the dictionary array.

Works like a charm!

;$realPath is local to server eg d:\home\username

$objWMI = ObjGet("winmgmts:{(Security,Restore)}\\yourserver\root\CIMV2")
Dim $objFolderSecuritySettings = $objWMI.get("Win32_LogicalFileSecuritySetting='"&$realPath&"'")

Dim $objSD
Dim $intRetVal = $objFolderSecuritySettings.GetSecurityDescriptor ($objSD)
Dim $intControlFlags = $objSD.ControlFlags
Dim $ACLUser
DIM $PermissionBitMask = 1245695 ;<<<<like modify
If $intControlFlags And $SE_DACL_PRESENT Then
    Dim $strMsg = "", $objACE
    Dim $arrACEs = $objSD.DACL
$changed=0
    For $objACE In $arrACEs
        $ACLUser = $objACE.Trustee.Domain & "\" & $objACE.Trustee.Name
        $strMsg &= $objACE.Trustee.Domain & "\" & $objACE.Trustee.Name & ", " & $objACE.AceFlags
        If $objACE.AceType = $ACCESS_ALLOWED_ACE_TYPE Then
            $strMsg &= @TAB & "Allowed:" & @CRLF
        ElseIf $objACE.AceType = $ACCESS_DENIED_ACE_TYPE Then
            $strMsg &= @TAB & "Denied:" & @CRLF
        EndIf
        if StringCompare($objace.Trustee.Name,$username,0)=0 Then   ;user already in DACL
            $objACE.AccessMask = $PermissionBitMask                 ;change permissions
            $changed=$changed+1
        endIf
    Next
Else
    $strMsg = "No DACL present in security descriptor"
EndIf

$oDictionary =ObjCreate("Scripting.Dictionary")
$key=0
    For $objACE In $arrACEs
        If Not BitAND($objACE.AceFlags,16) Then
            $oDictionary.Add($key, $objACE)
            $key=$key+1
            ;ConsoleWrite(">>>>>>"&$objace.Trustee.Domain & "\" & $objACE.trustee.name & " " & $objACE.Trustee.SIDString&@CRLF)
        EndIf
;           ConsoleWrite(">>>>>>"&$objace.Trustee.Domain & "\" & $objACE.trustee.name & " " & $objACE.Trustee.SIDString&@CRLF)
;           ConsoleWrite(">>>>>>"&vargettype($objACE.Trustee.SID)&@CRLF)

    Next

if $changed = 0 Then    ;user not found, need to add to the DACL
    $aAccount = _Security__LookupAccountName($domain&"\"&$username)
    $SID=$aAccount[0]

    $objWMI = ObjGet("winmgmts:\\.\root\cimv2") ;local connection, trusted
    $objSID=$objWMI.get("Win32_SID.SID='"&$SID&"'")

;Set the properties, overwrite last accessed ACE from above loop....
    $objACE.AccessMask = $PermissionBitMask
    $objACE.Trustee.Domain = $objSID.ReferencedDomainName
    $objACE.Trustee.Name = $objSID.AccountName
    $objACE.Trustee.SID = $objSID.BinaryRepresentation
    $objACE.Trustee.SIDLength = $objSID.SidLength
    $objACE.Trustee.SIDString = $objSID.SID
    $objACE.AceType = 0 ;Allow
    $objACE.AceFlags = 3 ;Inheritance to sub-folders and files

    $oDictionary.Add($oDictionary.count, $objACE)
EndIf

;overwrite current DACL with dictionary, will either amend existing user or add them if not already in DACL.
$objSD.DACL = $oDictionary.items
$intRetVal = $objFolderSecuritySettings.SetSecurityDescriptor ($objSD)
Edited by badgerbeard

Share this post


Link to post
Share on other sites
Igzter

I've figured out a way of doing this if anyone's interested...

Instead of spawning instances of the Win32_ACE and Win32_trustee, I just copy the existing objACE values to the dictionary object, then alter the last one to have the user name, SID etc that I want and tag that on the end of the dictionary, then overwrite the full DACL with the values in the dictionary array.

Works like a charm!

;$realPath is local to server eg d:\home\username

$objWMI = ObjGet("winmgmts:{(Security,Restore)}\\yourserver\root\CIMV2")
Dim $objFolderSecuritySettings = $objWMI.get("Win32_LogicalFileSecuritySetting='"&$realPath&"'")

Dim $objSD
Dim $intRetVal = $objFolderSecuritySettings.GetSecurityDescriptor ($objSD)
Dim $intControlFlags = $objSD.ControlFlags
Dim $ACLUser
DIM $PermissionBitMask = 1245695 ;<<<<like modify
If $intControlFlags And $SE_DACL_PRESENT Then
    Dim $strMsg = "", $objACE
    Dim $arrACEs = $objSD.DACL
$changed=0
    For $objACE In $arrACEs
        $ACLUser = $objACE.Trustee.Domain & "\" & $objACE.Trustee.Name
        $strMsg &= $objACE.Trustee.Domain & "\" & $objACE.Trustee.Name & ", " & $objACE.AceFlags
        If $objACE.AceType = $ACCESS_ALLOWED_ACE_TYPE Then
            $strMsg &= @TAB & "Allowed:" & @CRLF
        ElseIf $objACE.AceType = $ACCESS_DENIED_ACE_TYPE Then
            $strMsg &= @TAB & "Denied:" & @CRLF
        EndIf
        if StringCompare($objace.Trustee.Name,$username,0)=0 Then   ;user already in DACL
            $objACE.AccessMask = $PermissionBitMask                 ;change permissions
            $changed=$changed+1
        endIf
    Next
Else
    $strMsg = "No DACL present in security descriptor"
EndIf

$oDictionary =ObjCreate("Scripting.Dictionary")
$key=0
    For $objACE In $arrACEs
        If Not BitAND($objACE.AceFlags,16) Then
            $oDictionary.Add($key, $objACE)
            $key=$key+1
            ;ConsoleWrite(">>>>>>"&$objace.Trustee.Domain & "\" & $objACE.trustee.name & " " & $objACE.Trustee.SIDString&@CRLF)
        EndIf
;           ConsoleWrite(">>>>>>"&$objace.Trustee.Domain & "\" & $objACE.trustee.name & " " & $objACE.Trustee.SIDString&@CRLF)
;           ConsoleWrite(">>>>>>"&vargettype($objACE.Trustee.SID)&@CRLF)

    Next

if $changed = 0 Then    ;user not found, need to add to the DACL
    $aAccount = _Security__LookupAccountName($domain&"\"&$username)
    $SID=$aAccount[0]

    $objWMI = ObjGet("winmgmts:\\.\root\cimv2") ;local connection, trusted
    $objSID=$objWMI.get("Win32_SID.SID='"&$SID&"'")

;Set the properties, overwrite last accessed ACE from above loop....
    $objACE.AccessMask = $PermissionBitMask
    $objACE.Trustee.Domain = $objSID.ReferencedDomainName
    $objACE.Trustee.Name = $objSID.AccountName
    $objACE.Trustee.SID = $objSID.BinaryRepresentation
    $objACE.Trustee.SIDLength = $objSID.SidLength
    $objACE.Trustee.SIDString = $objSID.SID
    $objACE.AceType = 0 ;Allow
    $objACE.AceFlags = 3 ;Inheritance to sub-folders and files

    $oDictionary.Add($oDictionary.count, $objACE)
EndIf

;overwrite current DACL with dictionary, will either amend existing user or add them if not already in DACL.
$objSD.DACL = $oDictionary.items
$intRetVal = $objFolderSecuritySettings.SetSecurityDescriptor ($objSD)

That is extremely dangerous on a live filesystem with multiple users/applications if you do not make a filelock.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.