1

Тема: VBS & WMI: замена объекта класса Win32_Truste в DACL каталога

Продолжение темы VBS & WMI: безопасность NTFS для каталога, DACL (чтение, изменение).

Сценарий 6.
Замена объекта класса "Win32_Trustee" в списке управления доступом NTFS (DACL) заданного каталога текущего компьютера с сохранением настроек, унаследованных от "родителя".

Смысловое назначение сценария состоит в том, чтобы удалить текущую привязку записи DACL к некоторой пользовательской учётной записи, а затем привязать эту же запись DACL к другой пользовательской учётной записи. При этом тип, область действия и маска доступа записи DACL не изменяются.
Результат работы сценария аналогичен результату процедуры удаления некоторой записи DACL, а затем создания такой же по типу, области действия и маске доступа записи DACL, но соответствующей иной учётной записи пользователя.

Предполагается, что участвующие в процедуре пользовательские учётные записи, во-первых, являются доменными, во-вторых, принадлежат одному и тому же домену.
Сценарий способен работать и в графическом, и в консольном режимах.

Dim objWsNet, objFS, objWMI
Dim strDomain, strBaseFolder, blnContinue, xResult
Dim strAccountOld, strSIDOld, strAccountNew, strSIDNew

strAccountOld = "user1": strAccountNew = "user2"
strBaseFolder = "C:\Temp"
Set objWsNet = CreateObject("WScript.Network")
strDomain = objWsNet.UserDomain
Set objWsNet = Nothing
Set objFS = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
If Err.Number = 0 Then
    If objFS.FolderExists(strBaseFolder) Then
        If StrComp(strDomain & "\" & strAccountOld, strDomain & "\" & strAccountNew, vbTextCompare) <> 0 Then
            Set objAccount = objWMI.Get("Win32_UserAccount.Domain='" & strDomain & "',Name='" & strAccountOld & "'")
            If Err.Number = 0 Then
                strSIDOld = UCase(objAccount.SID)
                Set objAccount = objWMI.Get("Win32_UserAccount.Domain='" & strDomain & "',Name='" & strAccountNew & "'")
                If Err.Number = 0 Then
                    strSIDNew = UCase(objAccount.SID)
                    xResult = Change_Trustee(objWMI, strDomain, strAccountOld, strSIDOld, strAccountNew, strSIDNew, strBaseFolder)
                    WScript.Echo strAccountOld & " <-> " & strAccountNew & ": " & xResult
                Else
                    WScript.Echo strAccountNew & " -> не найдена учётная запись объекта"
                End If
            Else
                WScript.Echo strAccountOld & " -> не найдена учётная запись объекта"
                Err.Clear
            End If
            Set objAccount = Nothing
        Else
            WScript.Echo strAccountOld & " <-> " & strAccountNew & ": бессмысленная операция"
        End If
    Else
        WScript.Echo "Не найден путь " & UCase(strBaseFolder)
    End If
Else
    WScript.Echo "Ошибка " & Err.Number & " при подключении к WMI-пространству" & vbNewLine & Err.Description
    Err.Clear
End If
Set objWMI = Nothing
Set objFS = Nothing
WScript.Quit 0

'======

Function Change_Trustee(objWMIServ, strDom, strSAN1, strSID1, strSAN2, strSID2, strDir)
Dim objSecSettings, objSD, objItem, blnHasInherited
Dim objSID, objTrustee, arrACE, arrLines, xRes, i, j
Const SE_DACL_PROTECTED = 4096 'Флаг-признак отключенного режима наследования управляемым каталогом безопасности NTFS от "родителя"
Const INHERITED_ACE = 16 'Флаг-признак того, что текущая запись DACL унаследована от "родителя"

On Error Resume Next
xRes = 0
Set objSecSettings = objWMIServ.Get("Win32_LogicalFileSecuritySetting.Path='" & strDir & "'")
If Err.Number = 0 Then
    If objSecSettings.GetSecurityDescriptor(objSD) = 0 Then
        If Not IsNull(objSD.DACL) Then
            If Not CBool(objSD.ControlFlags And SE_DACL_PROTECTED) Then blnHasInherited = True
            arrACE = Array(): i = -1: arrLines = Array(): j = -1
            '--- Выборка из исходного DACL записей, не унаследованных от "родителя",
            'и поиск среди них (по SID) тех, которые привязаны к заменяемой пользовательской "учётке"
            For Each objItem In objSD.DACL
                If Not CBool(objItem.AceFlags And INHERITED_ACE) Then
                    If UCase(objItem.Trustee.SIDString) = strSID1 Then
                        j = j + 1
                        ReDim Preserve arrLines(j)
                        arrLines(j) = i + 1
                    End If
                    i = i + 1
                    ReDim Preserve arrACE(i)
                    Set arrACE(i) = objItem
                End If
            Next
            Set objItem = Nothing
            '------
            If j >= 0 Then
                If blnHasInherited Then
                    '--- Отключение наследования настроек безопасности от "родителя"
                    objSD.ControlFlags = objSD.ControlFlags + SE_DACL_PROTECTED
                    xRes = objSecSettings.SetSecurityDescriptor(objSD)
                    '------
                End If
                If xRes = 0 Then
                    '--- Создание экземпляра класса "Win32_Trustee",
                    'привязанного к заменяющей пользовательской "учётке"
                    Set objSID = objWMI.Get("Win32_SID.SID='" & strSID2 & "'")
                    Set objTrustee = objWMIServ.Get("Win32_Trustee").Spawninstance_
                    objTrustee.Domain = strDom
                    objTrustee.Name = strSAN2
                    objTrustee.SID = objSID.BinaryRepresentation
                    objTrustee.SidLength = objSID.SidLength
                    objTrustee.SIDString = strSID2
                    Set objSID = Nothing
                    '------
                    '--- Замена объекта класса "Win32_Trustee" у обрабатываемых записей DACL
                    For j = 0 To UBound(arrLines)
                        arrACE(arrLines(j)).Trustee = objTrustee
                    Next
                    '------
                    objSD.DACL = arrACE 'собственно изменение DACL
                    Erase arrACE: Erase arrLines
                    '--- Включение наследования настроек безопасности от "родителя", если первоначально оно было включено
                    If blnHasInherited Then objSD.ControlFlags = objSD.ControlFlags - SE_DACL_PROTECTED
                    '------
                    '--- Итоговое сохраненение изменений, внесённых в дескриптор безопасности
                    xRes = objSecSettings.SetSecurityDescriptor(objSD)
                    Select Case xRes
                        Case 0: xRes = "успешное завершение"
                        Case 2: xRes = "не удалось сохранить изменения DACL (доступ запрещён)"
                        Case 5, 9: xRes = "не удалось сохранить изменения DACL (для выполнения операции недостаточно полномочий)"
                        Case 21: xRes = "не удалось сохранить изменения DACL (заданы недопустимые значения параметров)"
                        Case Else: xRes = "не удалось сохранить изменения DACL (неизвестная ошибка)"
                    End Select
                    '------
                Else
                    xRes = "не удалось отключить наследование безопасности"
                End If
            Else
                xRes = "не обнаружено не унаследованных записей исходного объекта"
            End If
            Set objTrustee = Nothing
        Else
            xRes = "список управления доступом пуст"
        End If
    Else
        xRes = "не удалось прочитать дескриптор безопасности объекта"
    End If
    Set objSD = Nothing
    Set objSecSettings = Nothing
Else
    xRes = "ошибка " & CStr(Err.Number) & vbNewLine & Err.Description
    Err.Clear
End If
On Error GoTo 0
Change_Trustee = xRes
End Function

Примечания.
1. Работа сценария проверена в 32-битных версиях: 2000 Pro. + SP4/XP Pro. + SP3/2008 Std. + SP2/7 Pro. + SP1
2. Сценарий ориентирован на использование в русифицированных ОС.