Page 1 of 15 in the PowerShell category Next Page

Как известно, когда запрос попадает в папку Pending Requests, администратор CA должен что-то с ним явно сделать — или одобрить или отклонить. Это можно сделать при помощи оснастки CetSrv.msc или при помощи PowerShell. Ещё можно через certutil, но речь сегодня не о нём. Если вспомнить предыдущие посты посвящённые CryptoAPI и PowerShell, можно вспомнить какие-то основные принципы. Как обычно, мы будем использовать интерфейс ICertAdmin2. Для аппрува соответствующих запросов необходимо воспользоваться методом ResubmitRequest(). Как вы видите, метод принимает 2 аргумента:

HRESULT ResubmitRequest(
   
[in] const BSTR strConfig,
   
[in] LONG RequestId,
    [out, retval] LONG *pDisposition
);
 

это конфигурационная строка CA вида: CAComputerName\CAName и номер запроса. И в ответ метод возвращает результат выполнения операции. Вот как это можно аккуратно сделать в PowerShell:

function Issue-PendingRequest {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFomPipeline = $true)]
        [string]$CAConfig,
        [Parameter(Mandatory = $true)]
        [int]$RequestID
    )
    try {$CertAdmin = New-Object -ComObject CertificateAuthority.Admin}
    catch {Write-Warning "Unable to instantiate ICertAdmin2 object!"; return}
    try {
        $status = switch ($CertAdmin.ResubmitRequest($CAConfig,$RequestID)) {
            0 {"The request was not completed."}
            1 {"The request failed."}
            2 {"The request was denied"}
            3 {"The certificate was issued."}
            4 {"The certificate was issued separately."}
            5 {"The request was taken under submission."}
            6 {"The certificate is revoked."}
        }
    }
    catch {$_; return}
    Write-Host "Operation status for the request '$RequestID': $ststus"
}

Для отклонения запроса, следует воспользоваться методом DenyRequest(). Как и метод ResubmitRequest тоже принимает всего 2 аргумента, но кроме ошибок ничего не возвращает:

HRESULT DenyRequest(
    [in] const BSTR strConfig,
    [in] Long RequestId
);

И код будет очень похож на предыдущий:

function Deny-PendingRequest {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFomPipeline = $true)]
        [string]$CAConfig,
        [Parameter(Mandatory = $true)]
        [int]$RequestID
    )
    try {$CertAdmin = New-Object -ComObject CertificateAuthority.Admin}
    catch {Write-Warning "Unable to instantiate ICertAdmin2 object!"; return}
    try {$CertAdmin.DenyRequest($CAConfig,$RequestID)}
    catch {$_; return}
    Write-Host "Successfully denied request '$RequestID'"
}

вот так легко можно программным способом управлять реквестами из папки Pending Requests без графических оснасток или certutil. Что касается certutil, я не уверен, что он сможет заапрувить или отклонить реквест на удалённом CA. У него есть параметр –config, но я не уверен, что он работает в данном случае. Плюс, когда я выложу в общий доступ свой PS модуль для PKI, эта операция будет ещё проще. Вам не придётся вручную набивать конфигурационную строку, а просто воспользоваться командой Get-CertificationAuthority.

Saturday, July 03, 2010 10:48:17 PM (FLE Daylight Time, UTC+03:00)   Comments [2]    

 

Недавно в мессенджер упал интересный вопрос:

в Active Directory у пользователя есть атрибут msRADIUSFramedIPAddress в котором храниться статический IP адрес, который назначается на вкладке Dual In в свойствах пользователя. Если смотреть через LDAP то значение мало похоже на введённый IP адрес. Хотелось бы на выходе получить работающий скрипт пишущий в файл соответсвие пользователь - IP адрес. Как я понял проблему, идёт двойная конвертация.

Действительно, если смотреть на это свойство через провайдер ADSI (не путать с adsiedit.msc), мы увидим число мало похожее на IP адрес. Например, 168846386. Для приведения данного числа в формат IP адреса нужно преобразовать его в двоичный формат, а после — разбить на октеты по 8 бит и каждый октет преобразовать обратно в десятичную форму. В .NET есть целый класс конверторов System.Convert, который позволяет конвертировать между собой множество форматов. Мы воспользуемся вот таким методом: Convert::ToString Method (Int32, Int32), в котором первый аргумент отвечает за исходное число, а второй — выходной формат. Поскольку нам нужно получить двоичное представение, указываем формат 2:

[↓] [vPodans] [convert]::ToString(168846386,2) 1010000100000110010000110010

Строка содержит неполные 32 бита. Чтобы их дополнить мы должны добавить слева недостающие нули:

[↓] [vPodans] $bin = [convert]::ToString(168846386,2) [↓] [vPodans] $bin 1010000100000110010000110010 [↓] [vPodans] $bin.length 28 [↓] [vPodans] $fullBin = "0" * (32 - $bin.length) + $bin [↓] [vPodans] $fullbin 00001010000100000110010000110010

Мы знаем, что 4 октета это 32 бита, следовательно вычитаем из 32 длину актуальной строки и получаем количество добавочных нулей. И эти нули добавляем в начало строки. Теперь эту строку надо разбить на 4 октета по 8 бит или по 1 байту:

[↓] [vPodans] 0,8,16,24 | %{$fullbin.substring($_,8)} 00001010 00010000 01100100 00110010

Я воспользовался методом String::Substring Method (Int32, Int32). Первый аргумент указывает стартовый символ, откуда начинать вырезать, а второй аргумент указывает количество символов, которые нужно вырезать. Вот и получили 4 байта. Преобразовать обратно в десятичный формат их так же просто, но с использованием Convert::ToInt32 Method (String, Int32):

[↓] [vPodans] 0,8,16,24 | %{[convert]::ToInt32($fullbin.Substring($_,8),2)} 10 16 100 50

Теперь это осталось собрать в строку и расставить точки между октетами. Для этого мы вопспользуемся статическим методом Join() класса System.String:

[↓] [vPodans] [string]::Join(".",$(0,8,16,24 | %{[convert]::ToInt32($fullbin.substring($_,8),2)})) 10.16.100.50

вот мы и получили наш заветный IP-адрес. И вот весь код:

$bin = [Convert]::ToString(168846386,2)
$fullBin = "0" * (32 - $bin.Length) + $bin
[string]::Join(".", $(0,8,16,24 | %{[Convert]::ToInt32($fullBin.Substring($_,8),2)}))

Надеюсь, поможет ещё кому-нибудь :)

Sunday, May 30, 2010 1:52:39 PM (FLE Daylight Time, UTC+03:00)   Comments [1]    

 

Что делать, что делать, одна продажная роботиха за 300 долларов или 300 продажных роботих по доллару?

Да, вот такой непростой выбор. А точнее чему отдать преимущество: этому BPA или PowerShell'у? Как известно, Best Practices Analyzer (BPA) в Windows Server 2008 R2 основан на скриптах PowerShell. Я не знаю, как оно там внутри происходит, но есть мнение, что как-то оно происходит. С этой проблемой сталкиваются все те, кто управляет политикой исполнения (ExecutionPolicy) через групповые политики. И вот как это выглядит, если в групповой политике задано значение AllSigned или RemoteSigned и вы пытаетесь просканировать какую-то роль:

Т.е. как я понимаю, этот BPA тянет скрипты с серверов Microsoft'а, либо одно из двух. При этом, он запускает консоль с изменением текущей политики исполнения. Однако тут есть одна хитрость. Если вы не управляете политикой исполнения скриптов из политик, а вручную набираете команду:

Set-ExecutionPolicy RemoteSigned

то BPA ВНЕЗАПНО начинает работать. Т.е. по сути всё равно RemoteSigned будь то вручную, будь то через политику, будут иметь одинаковый эффект. Но в первом случае BPA работает, во втором — нет. Единственная разница здесь в том, что если политика исполнения скриптов задана командлетом Set-ExecutionPolicy (не через групповые политики) её можно спокойно переопределить для конкретного сеанса. Вот как это делается (только PowerShell V2):

PS C:\> Get-ExecutionPolicy AllSigned PS C:\> powershell -executionpolicy remotesigned Windows PowerShell Copyright (C) 2009 Microsoft Corporation. All rights reserved. PS C:\> Get-ExecutionPolicy RemoteSigned PS C:\> PS C:\> Exit PS C:\> Get-ExecutionPolicy AllSigned

Запустили пошик с нужным ключиком и для текущей сессии получили нужную политику исполнения. Вышли из сессии и снова вернулись в AllSigned. Если же политика исполнения управляется через групповые политики, то такой финт ушами уже не пройдёт:

PS C:\> Get-ExecutionPolicy AllSigned PS C:\> powershell -executionpolicy remotesigned Windows PowerShell Copyright (C) 2009 Microsoft Corporation. All rights reserved. PS C:\> Get-ExecutionPolicy AllSigned PS C:\>

Как AllSigned был, так и остался.

Собственно и вопрос: как сделать так, чтобы работал BPA и политика исполнения скриптов назначалась через групповые политики?

Monday, May 03, 2010 5:48:04 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

MSDN и Microsoft меня однажды доканает и я уйду проповедовать православныйбогомерзкий линукс.

А дело в том, что одна моя задумка с треском проваливалась из-за непонятного и тупого бага в интерфейсе ICertEncodeAltName. Это CryptoAPI интерфейс, который позволяет кодировать строковые значения для расширения Subject Alternative Name в ASN.1 DER encoded строку. На возню с ним я потратил почти неделю (чуть меньше, наверное) и только сегодня я смог найти все ответы. Хотя я это должен был сделать много раньше, но дико тупил на ровном месте. Итак, давайте посмотрим, что из себя представляет этот баг.

Для начала я покажу как собирается этот интерфейс:

$san = New-Object -ComObject CertificateAuthority.EncodeAltName

Далее, его надо инициализировать методом Reset() и в качестве аргумента метода указать размер объекта. Т.е. количество элементов, которые должны содержаться в расширении Subject Alternative Name. Если только один элемент, то и указываете число 1:

$san.Reset(1)

и теперь можно в него заряжать данные методом SetNameEntry():

$san.SetNameEntry(0,0x3,"Custom-Name")

Где 0 — индекс массива, в который надо записать строку, 0x3 — тип строки. В данном случае это DNS и последний аргумент указывает само значение строки. Полный список типов можно получить вот здесь: http://msdn.microsoft.com/en-us/library/aa374981(VS.85).aspx. Только учтите, что реально типы начинаются не с нуля (как по ссылке), а с единицы. т.е. 0x3 в нашем случае это DNS (2+1).

После того как мы загнали туда данные, их можно кодировать:

$san.Encode()

И давайте посмотрим, что у нас получилось:

[↓] [vPodans] $san = New-Object -ComObject CertificateAuthority.EncodeAltName [↓] [vPodans] $san.Reset(1) [↓] [vPodans] $san.SetNameEntry(0,0x3,"Custom-Name") [↓] [vPodans] $str = $san.Encode() [↓] [vPodans] $str ???????

выглядит прикольно. Но на самом деле это строка из символов, которые описаны не одним, а двумя байтами:

[↓] [vPodans] $str.ToCharArray() | %{[int][char]$_} 3376 2946 30019 29811 28015 20013 28001

Чтобы убедиться, что в каждом символе по 2 байта, мы преобразуем эти числа в их шестнадцатиричное (hex) представление:

[↓] [vPodans] $str.ToCharArray() | %{"{0:X4}" -f [int][char]$_} 0D30 0B82 7543 7473 6D6F 4E2D 6D61

видите, в каждом символе по 2 байта и все они записаны в little-endian последовательности. Т.е. первый байт находится справа, а не слева, как обычно. Давайте соберём эти байты в последовательную строку, переставив байты местами в каждой строке:

30 0D 82 0B 43 75 73 74 6F 6D 2D 4E 61 6D

И что же означают эти циферки-букавки? А означают они следующее:

  • 30 (48) — тип кодировки. 30 или 48 (в десятичном представлении) означает Constructed encoding.
  • 0D (13) — длину строки начиная со следующего байта. Остаточная длина должна быть 13 байт. Но моя математика говорит, что дальше не 13, а только 12 байт.
  • 82 (130) — тип закодированной строки. 130 означает DNS. 80 (128) — OtherName, 81 (129) — RFC822Name и т.д.
  • 0B (11) — размер строки начиная со следующего байта. Остаточная длина должна быть 11 байт. Но моя математика говорит, что их там не 11, а только 10 байт.
  • Остальные байты — сама строка, только в hex'е.

Куда-то пропал один очень важный байт, из-за чего сервер CA начинал доставлять шлакоблоки при виде такой строки. В принципе, мы можем посмотреть, что у нас потерялось по дороге:

[↓] [vPodans] $str1 = "43 75 73 74 6F 6D 2D 4E 61 6D".split(" ") [↓] [vPodans] $str1 | %{$a += invoke-expression [char]0x$_} [↓] [vPodans] $a Custom-Nam

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

Что можно сделать? Вариантов куча. Как вы видите ASN.1 DER кодировка достаточно простая, её можно сделать самому. Либо использовать православные интерфейсы CertEnroll — IX509ExtensionAlternativeNames. На первый взгляд там багов не обнаружено :). Примеры использования данных интерфейсов можно посмотреть (а заодно и почитать) в моём буржуйском бложике: How to add FQDN to HP iLO request.

На сегодня всё. Спокночи.

Sunday, May 02, 2010 1:46:05 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Сегодня пришло письмо, где человек спрашивал о том, можно ли сделать скриншот средствами PowerShell. Я вообще не очень понял зачем это, но это уже не моё дело. Если я пишу этот пост, значит можно :)

Для этого существует класс Drawing.Graphics у которого есть замечательный метод CopyFromScreen(). Данный метод принимает в качестве аргументов различные параметры, которые определяют координаты области, которую нужно сфоткать. В принципе можно забивать фиксированные значения (см. конструкторы с Int, как этот — CopyFromScreen(Int32, Int32, Int32, Int32, Size)), а можно эти размеры выставлять динамически, в зависимости от размеров экрана. Для этого лучше воспользоваться самым первым методом: CopyFromScreen(Point, Point, Size).

Как получить размеры экрана? Очень просто, достаточно воспользоваться статическим свойством VirtualScreen класса Windows.Forms.SystemInformation:

[Windows.Forms.SystemInformation]::VirtualScreen

[↓] [vPodans] [Windows.Forms.SystemInformation]::VirtualScreen Location : {X=0,Y=0} Size : {Width=1280, Height=800} X : 0 Y : 0 Width : 1280 Height : 800 Left : 0 Top : 0 Right : 1280 Bottom : 800 IsEmpty : False [↓] [vPodans]

Вот размеры экрана на моём нотебуке. Готично. Теперь нужно создать объект Drawing.Graphics. Однако, этот объект нельзя создать через New-Object, поскольку для этого нет ни одного конструктора. Следовательно объект нужно создавать используя статические методы. Статические методы можно посмотреть на странице Drawing.Graphics Members. И там мы можем найти красивый метод — FromImage. В качестве аргумента этого метода нужно указать картинку — http://msdn.microsoft.com/en-us/library/system.drawing.image.aspx. Это может быть файл или объект класса Drawing.Bitmap. Давайте создадим объект класса Drawing.Bitmap с использованием следующего конструктора — Bitmap Constructor (Int32, Int32):

[↓] [vPodans] [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [↓] [vPodans] $size = [Windows.Forms.SystemInformation]::VirtualScreen [↓] [vPodans] $bitmap = new-object Drawing.Bitmap $size.width, $size.height [↓] [vPodans] $bitmap Tag : PhysicalDimension : {Width=1280, Height=800} Size : {Width=1280, Height=800} Width : 1280 Height : 800 HorizontalResolution : 96 VerticalResolution : 96 Flags : 2 RawFormat : [ImageFormat: b96b3caa-0728-11d3-9d7b-0000f81ef32e] PixelFormat : Format32bppArgb Palette : System.Drawing.Imaging.ColorPalette FrameDimensionsList : {7462dc86-6180-4c7e-8e3f-ee7333a7a483} PropertyIdList : {} PropertyItems : {} [↓] [vPodans]

Примечание: WinForms по умолчанию не загружаются вместе с PowerShell, поэтому мы их подгружаем в консоль в ходе работы. Мы создали Bitmap и теперь его можем использовать для создания объекта Drawing.Graphics:

$graphics = [Drawing.Graphics]::FromImage($bitmap)

А дальше уже вызывать метод CopyFromScreen(). При этом указываем первым аргументом начало координат (верхний левый угол или 0,0) и в аргумент правый нижний угол. Т.е. максимум точек по ширине и высоте. Их мы можем получить из свойств Width и Height класса Windows.Forms.SystemInformation. Средний аргумент опускаем и делаем его пустым:

[↓] [vPodans] $graphics = [Drawing.Graphics]::FromImage($bitmap) [↓] [vPodans] $graphics.CopyFromScreen($size.Location,[Drawing.Point]::Empty, $size.size)

Всё, мы сфоткали наш экран и загнали картинку обратно в Bitmap. Теперь нужно выгрузить картинку куда-то в файл. Для этого у Bitmap есть метод Save(), в котором достаточно указать путь к файлу:

[↓] [vPodans] $bitmap.save(".\screenshot.jpg") [↓] [vPodans] gi .\screenshot.jpg Directory: C:\Users\vPodans Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 22.04.2010 22:37 286319 screenshot.jpg [↓] [vPodans]

Всё :)

Примечание: после сохранения картинка остаётся в памяти, поэтому после работы следует освобождать оперативную память от ненужных пруфпиков используя метод Dispose() без аргументов.

И код:

[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$size = [Windows.Forms.SystemInformation]::VirtualScreen
$bitmap = new-object Drawing.Bitmap $size.width, $size.height
$graphics = [Drawing.Graphics]::FromImage($bitmap)
$graphics.CopyFromScreen($size.location,[Drawing.Point]::Empty, $size.size)
$graphics.Dispose()
$bitmap.Save(".\screenshot.jpg")
$bitmap.Dispose()
Thursday, April 22, 2010 11:43:41 PM (FLE Daylight Time, UTC+03:00)   Comments [4]    

 

Page 1 of 15 in the PowerShell category Next Page
 · 
All content © 2008 - 2010, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
My former blog -
For english language visitors
Библиотека
Календарик
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

Карта расположения посетителей
Favorites





Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.