Contents of this directory is archived and no longer updated.

Итак, как я и обещал, я вернулся к вопросу изменения ACL принтеров в PowerShell. В первой части (Странности метода SetSecurityDescriptor класса Win32_Printer) я изложил проблематику вопроса. В конечном итоге я сегодня смог найти решение, которое оказалось не совсем понятным, но относительно предсказуемым.

Вернёмся снова к документации MSDN: SetSecurityDescriptor Method of the Win32_Printer Class

Это, конечно же, было моим попустительством, что не указал флаг управления SE_DACL_PRESENT и не включил привилегии SeSecurityPrivilege ("умение читать - первое умение системного администратора" (c) Peter.G). Понимание этого факта пришло после очередного прочтения поста о смене владельца папки (Смена владельца папки или файла в PowerShell (часть 2)). Что касается флагов управления, то выложу здесь значения флагов:

SE_OWNER_DEFAULTED       = 0x0001
SE_GROUP_DEFAULTED       = 0x0002
SE_DACL_PRESENT          = 0x0004
SE_DACL_DEFAULTED        = 0x0008
SE_SACL_PRESENT          = 0x0010
SE_SACL_DEFAULTED        = 0x0020
SE_DACL_AUTO_INHERIT_REQ = 0x0100
SE_SACL_AUTO_INHERIT_REQ = 0x0200
SE_DACL_AUTO_INHERITED   = 0x0400
SE_SACL_AUTO_INHERITED   = 0x0800
SE_DACL_PROTECTED        = 0x1000
SE_SACL_PROTECTED        = 0x2000
SE_RM_CONTROL_VALID      = 0x4000
SE_SELF_RELATIVE         = 0x8000

Здесь я выделил жирным тот флаг, который нам нужен.

Добавим эту строчку к скрипту и добавим включение привилегий. И мы должны будем получить примерно такой скрипт:

$user = "everyone"
$SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
$ace = ([WMIClass] "Win32_Ace").CreateInstance()
$Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
$SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
[byte[]] $SIDArray = ,0 * $SID.BinaryLength
$SID.GetBinaryForm($SIDArray,0)
$Trustee.Name = $user
$Trustee.SID = $SIDArray
$ace.AccessMask = 393224
$ace.AceType = 0
$ace.AceFlags = 0
$ace.Trustee = $Trustee
$SD.DACL = $ace
$SD.ControlFlags = 0x0004
$Printer = gwmi win32_printer -filter "name='CutePDF Writer'"
$Printer.psbase.Scope.Options.EnablePrivileges = $true
$inParams = $Printer.psbase.GetMethodParameters("SetSecurityDescriptor")
$inParams.Descriptor = $SD
$Printer.SetSecurityDescriptor($inParams)

Пробуем запустить его:

PS C:\Windows\System32> $user = "everyone"
PS C:\Windows\System32> $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
PS C:\Windows\System32> $ace = ([WMIClass] "Win32_Ace").CreateInstance()
PS C:\Windows\System32> $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
PS C:\Windows\System32> $SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
PS C:\Windows\System32> [byte[]] $SIDArray = ,0 * $SID.BinaryLength
PS C:\Windows\System32> $SID.GetBinaryForm($SIDArray,0)
PS C:\Windows\System32> $Trustee.Name = $user
PS C:\Windows\System32> $Trustee.SID = $SIDArray
PS C:\Windows\System32> $ace.AccessMask = 393224
PS C:\Windows\System32> $ace.AceType = 0
PS C:\Windows\System32> $ace.AceFlags = 0
PS C:\Windows\System32> $ace.Trustee = $Trustee
PS C:\Windows\System32> $SD.DACL = $ace
PS C:\Windows\System32> $SD.ControlFlags = 0x0004
PS C:\Windows\System32> $Printer = gwmi win32_printer -filter "name='CutePDF Writer'"
PS C:\Windows\System32> $Printer.psbase.Scope.Options.EnablePrivileges = $true
PS C:\Windows\System32> $inParams = $Printer.psbase.GetMethodParameters("SetSecurityDescriptor")
PS C:\Windows\System32> $inParams.Descriptor = $SD
PS C:\Windows\System32> $Printer.SetSecurityDescriptor($inParams)


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 2147749896



PS C:\Windows\System32>

И снова мы получаем ошибку, которая указывает, что какой-то параметр вызова неверный. Снова посмотрев предыдущую статью, я стал понимать в чём тут дело. А дело в том, что метод SetSecurityDescriptor в этом классе не принимает параметр Descriptor (хотя он есть в выдаче команды GetSecurityDescriptor) и в MSDN чётко указано:

uint32 SetSecurityDescriptor(
    [in] Win32_SecurityDescriptor Descriptor
);

При этом, в методе SetShareInfo класса Win32_Share параметр Access я указывал и всё проходило на "ура". Здесь мы сталкиваемся с отсутствием единого формата использования метода SetSecurityDescriptor (отсутствие единого формата параметров я уже указывал в первой части) для различных WMI классов. И если один приём работает для одного класса, то, увы, далеко не гарантия, что этот же приём сработает для другого. Поэтому выкинем 2 предпоследние строчки из скрипта и в последней строке вместо аргумента $inParams заменим на $SD:

PS C:\Windows\System32> $user = "everyone"
PS C:\Windows\System32> $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
PS C:\Windows\System32> $ace = ([WMIClass] "Win32_Ace").CreateInstance()
PS C:\Windows\System32> $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
PS C:\Windows\System32> $SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
PS C:\Windows\System32> [byte[]] $SIDArray = ,0 * $SID.BinaryLength
PS C:\Windows\System32> $SID.GetBinaryForm($SIDArray,0)
PS C:\Windows\System32> $Trustee.Name = $user
PS C:\Windows\System32> $Trustee.SID = $SIDArray
PS C:\Windows\System32> $ace.AccessMask = 393224
PS C:\Windows\System32> $ace.AceType = 0
PS C:\Windows\System32> $ace.AceFlags = 0
PS C:\Windows\System32> $ace.Trustee = $Trustee
PS C:\Windows\System32> $SD.DACL = $ace
PS C:\Windows\System32> $SD.ControlFlags = 0x0004
PS C:\Windows\System32> $Printer = gwmi win32_printer -filter "name='CutePDF Writer'"
PS C:\Windows\System32> $Printer.psbase.Scope.Options.EnablePrivileges = $true
PS C:\Windows\System32> $Printer.SetSecurityDescriptor($SD)


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



PS C:\Windows\System32>

Вуаля! Мы получили ReturnValue=0, что означает успешное выполнение команды. Давайте убедимся, что группа Everyone имеет маску доступа 393224 (это ReadPermissions, ChangePermissions и Print). Для этого снова вызовем метод GetSecurityDescriptor для нового образца класса Win32_Printer:

PS C:\Windows\System32> $a = (gwmi win32_Printer -filter "name='cutepdf writer'").getsecuritydescriptor()
PS C:\Windows\System32> $a.descriptor.dacl[0]


__GENUS                 : 2
__CLASS                 : Win32_ACE
__SUPERCLASS            : __ACE
__DYNASTY               : __SecurityRelatedClass
__RELPATH               :
__PROPERTY_COUNT        : 7
__DERIVATION            : {__ACE, __SecurityRelatedClass}
__SERVER                :
__NAMESPACE             :
__PATH                  :
AccessMask              : 393224
AceFlags                : 0
AceType                 : 0
GuidInheritedObjectType :
GuidObjectType          :
TIME_CREATED            :
Trustee                 : System.Management.ManagementBaseObject



PS C:\Windows\System32> $a.descriptor.dacl[0].trustee


__GENUS          : 2
__CLASS          : Win32_Trustee
__SUPERCLASS     : __Trustee
__DYNASTY        : __SecurityRelatedClass
__RELPATH        :
__PROPERTY_COUNT : 6
__DERIVATION     : {__Trustee, __SecurityRelatedClass}
__SERVER         :
__NAMESPACE      :
__PATH           :
Domain           :
Name             : Everyone
SID              : {1, 1, 0, 0...}
SidLength        : 12
SIDString        : S-1-1-0
TIME_CREATED     :



PS C:\Windows\System32>

Вот теперь мы видим нашу маску доступа (AccessMask=393224) в корне DACL и нашего уважаемого everyone в Trustee. :rock:

Примечание: данный скрипт удаляет все имеющиеся ACE из ACL принтера и записывает новый единственный ACE. Не забывайте об этом.

Теперь я закрываю этот вопрос. Что же дальше? А дальше будет вот что: я напишу готовый набор функций для управления ACL списками принтеров. Какой это будет набор - я пока в процессе разработки его структуры, но однозначно будут функции вида Get- Set- Add- Remove- PrinterPermission. Возможно будут добавлены функции для владельцев. Но об этом уже в следующий раз, так что продолжение следует однозначно :happy:


Share this article:

Comments:

Igor

Хорошая статья, вот только как сделать подобый скрипт под XP :(

Vadims Podāns

Этот метод не поддерживается в XP, поэтому посмотрите в сторону subinacl.

Comments are closed.