Итак, как я и обещал, я вернулся к вопросу изменения 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:
Хорошая статья, вот только как сделать подобый скрипт под XP :(
Этот метод не поддерживается в XP, поэтому посмотрите в сторону subinacl.
Comments: