В первой и второй части я рассказал про основные моменты управления принтерами в PowerShell и теперь хочу поговорить о правах на принтеры. Т.к. принтеры управляются с помощью классов WMI, то управление правами доступа к ним будет превращаться в очередную эпохальную эпопею, которую я исследовал при изучении безопасности Share Permissions (вот ссылка на эти статьи в моём прежнем блоге: http://vpodans.spaces.live.com/lists/cns!BB1419A2CFC1E008!178). Однако, с принтерами оказалось всё печальней Мне так и не удалось заставить работать метод SetSecurityDescriptor. Итак, я расскажу о своих кратких исследованиях и в чём же мы имеем проблему.
Для чтения прав доступа принтера потребуется метод GetSecurityDescriptor:
[System32] $a=(gwmi win32_printer -filter "name='cutepdf writer'").getsecuritydescriptor() [System32] $a __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 2 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : Descriptor : System.Management.ManagementBaseObject ReturnValue : 0 [System32] $a.Descriptor | fl [a-z]* ControlFlags : 32780 DACL : {System.Management.ManagementBaseObject, System.Management.ManagementBaseObject,System.Mana gement.ManagementBaseObject, System.Management.ManagementBaseObject...} Group : System.Management.ManagementBaseObject Owner : System.Management.ManagementBaseObject SACL : TIME_CREATED : [System32] $a.descriptor.dacl[0] | fl [a-z]* AccessMask : 983052 AceFlags : 0 AceType : 0 GuidInheritedObjectType : GuidObjectType : TIME_CREATED : Trustee : System.Management.ManagementBaseObject [System32] $a.descriptor.dacl[0].trustee | fl [a-z]* Domain : Thor Name : Admin SID : {1, 5, 0, 0...} SidLength : 28 SIDString : S-1-5-21-3020384060-3247076327-363933757-1000 TIME_CREATED : [System32]
Как видите, метод GetSecurityDescriptor вернул нам единственный параметр - Descriptor. Заглянув в Descriptor, нам нужно было найти параметр DACL, который уже содержит все пермишены. Подробнее материал изложен тут: WMI Security Descriptor Objects.
Примечание: методы GetSecurityDescriptor и SetSecurityDescriptor доступны только в ОС начиная от Windows Vista/Windows Server 2008 и выше.
Структура DACL содержит в себе как права доступа (AccessMask), так и сведения о пользователе, который имеет указаную AccessMask маску доступа (Trustee). Я попробовал создать идентичную структуру SecurityDescriptor (как описано в ссылке: WMI Security Descriptor Objects) и получил вот такой скрипт:
# задаём пользователя, которому хотим предоставить доступ$user = "everyone"# объявляем необходимые классы, которые описывают дескриптор безопасности$SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()$ace = ([WMIClass] "Win32_Ace").CreateInstance()$Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()# преобразовываем имя пользователя в строковый SID и массив байтов для Win32_Trustee$SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])[byte[]] $SIDArray = ,0 * $SID.BinaryLength$SID.GetBinaryForm($SIDArray,0)# заполняем необходимыми данными класс Win32_Trustee$Trustee.Name = $user$Trustee.SID = $SIDArray# заполняем поля класса Win32_Ace, которые описывают права доступа и заворачиваем# пользователя в лице класса Win32_Trustee# AccessMask в контексте принтера может принимать значения:# 524288 - Take ownership# 131072 - read permissions# 262144 - change permissions# 983088 - manage documents# 983052 - manage printers# 131080 - print + read permissions$ace.AccessMask = 983052$ace.AceType = 0$ace.AceFlags = 0$ace.Trustee = $Trustee# заворачиваем полученный объект Win32_Ace в параметр DACL класса Win32_SecurityDescriptor$SD.DACL = $ace# получаем объект принтера, с которым собираемся работать$Printer = gwmi win32_printer -filter "name='CutePDF Writer'"# получаем свойства метода SetSecurityDescriptor, чтобы в соответствии с ними# завернуть туда SecurityDescriptor$inParams = $Printer.psbase.GetMethodParameters("SetSecurityDescriptor")# заворачиваем SecurityDescriptor в параметр Descriptor метода SetSecurityDescriptor$inParams.Descriptor = $SD# применяем метод SetSecurityDescriptor$Printer.SetSecurityDescriptor($inParams)
И попробуем его запустить:
[System32] $user = "everyone" [System32] $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance() [System32] $ace = ([WMIClass] "Win32_Ace").CreateInstance() [System32] $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance() [System32] $SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier]) [System32] [byte[]] $SIDArray = ,0 * $SID.BinaryLength[System32] $SID.GetBinaryForm($SIDArray,0) [System32] $Trustee.Name = $user [System32] $Trustee.SID = $SIDArray [System32] $ace.AccessMask = 983052 [System32] $ace.AceType = 0 [System32] $ace.AceFlags = 0 [System32] $ace.Trustee = $Trustee [System32] $SD.DACL = $ace [System32] $Printer = gwmi win32_printer -filter "name='CutePDF Writer'" [System32] $inParams = $Printer.psbase.GetMethodParameters("SetSecurityDescriptor") [System32] $inParams.Descriptor = $SD [System32] $Printer.SetSecurityDescriptor($inParams) __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ReturnValue : 2147749896 [System32]
Как видно, ни одна строка не вернула ошибок, а последняя строка вернула значение 2147749896. Прогулявшись по MSDN нашёл описание этой ошибки: Win32SDToSDDL Method of the Win32_SecurityDescriptorHelper Class (практически везде данное значение в контексте SecurityDescriptor интерпретируются так):
один из параметров вызова указан неверно. Давайте попробуем проанализировать, что мы сформировали и сравним с данными, которые получили методом GetSecurityDescriptor:
[System32] $inparams __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : Descriptor : System.Management.ManagementBaseObject [System32] $inparams.Descriptor | fl [a-z]* ControlFlags : DACL : {System.Management.ManagementBaseObject} Group : Owner : SACL : TIME_CREATED : [System32] $inparams.Descriptor.dacl[0] | fl [a-z]* AccessMask : 983052 AceFlags : 0 AceType : 0 GuidInheritedObjectType : GuidObjectType : TIME_CREATED : Trustee : System.Management.ManagementBaseObject [System32] $inparams.Descriptor.dacl[0].trustee | fl [a-z]* Domain : Name : everyone SID : {1, 1, 0, 0...} SidLength : SIDString : TIME_CREATED : [System32]
Если сравнивать с первым листингом, где мы получали текущий SecurityDescriptor, то мы сохранили структуру SecurityDescriptor и в соответствующие поля записали нужные данные. При этом я также пытался для Trustee записать строковый SIDString и SIDLength:
$Trustee.SIDString = $SID.Value $Trustee.SIDLength = $SID.BinaryLength
и получил уже такой Trustee:
[System32] $inparams.Descriptor.dacl[0].trustee | fl [a-z]* Domain : Name : everyone SID : {1, 1, 0, 0...} SidLength : 12 SIDString : S-1-1-0 TIME_CREATED : [System32]
От этого результат не изменился. Попробовал вызвать несколько иначе метод SetSecurityDescriptor:
$Printer.psbase.invokemethod("SetSecurityDescriptor", $inParams, $null)
и всё по старому. Я попытался нагуглить этот вопрос поисковой фразой "setsecuritydescriptor win32_printer powershell" (да-да, каюсь, я пользуюсь гуглем, как это ни прискорбно). И он мне выдал меньше одной страницы и 2 вменяемые ссылки, одна из которых ведёт на MSDN, а вторая на какой-то французский сайт (в котором я без переводчика плохо понимаю, а жаль) - http://powershell-scripting.com/index.php?option=com_joomlaboard&Itemid=76&func=view&id=2292&catid=6, но человек там творил что-то страшное и я понял, что там помощи не ждать.
Вобщем, этот вопрос пока остаётся открытым. Что ему не нравится, я пока не могу понять. Управление SecurityDescriptor в WMI - это не самая удачная модель управления безопасностью в WMI, но пока это почти единственный способ добиться результата - приходится с этим работать. Хотя, на первый взгляд может показаться, что там всё страшно и ужасно, но на самом деле, если разобрать предметно вопрос и поупражняться, всё оказывается вполне понятным, хоть и не совсем логичным и не всегда это хочет работать как положено :)
Remember Me