none
Разадача прав учетной записи компьютера в АД через скрипт RRS feed

  • Вопрос

  • Как через cmd или vbs (wmi?) задавать права на учетную запись компьютера?
    Чтобы было более понятно, на скрине свойства учетной записи компьютера, вкладка безопасность где и раздаются права. Надо "учетке" SELF (то есть сам же комп) дать права читать и писать открытые сведения.

    С объектами файловой системы проще, есть утилита icacls, например. А вот как быть с объектом АДа?

    http://s52.radikal.ru/i137/1103/1c/a53952aa07f6.jpg

     

    Глобальная суть вот в чем. Есть скрипт, который запускается от компьютера при загрузке windows. Этот скрипт записывает в описание учетной записи компьютера в АД фамилия имя и отчество залогиненного юзера (данные берет из реестра). И чтобы ему мочь записать эти данные в свою учетку в АДу, надо чтобы SELF'у были даны права читать и писать открытые сведения. По умолчанию при создании компьютера этих прав не дано.

    Скрипт будет типа робота, должен запускаться на контроллере, например, каждый день и пробегаться по всему контейнеру с компьютерами и раздавать SELF'у соответствующие права.

    15 марта 2011 г. 10:29

Ответы

  • Могу только предложить вариант на PowerShell,возможно натолкнет на мысль,как сделать на vbs.

    $Guid = [Guid]'e48d0154-bcf8-11d1-8702-00c04fb96050'
    $Self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10' 
    $SelfAllowRead = new-object System.DirectoryServices.ActiveDirectoryAccessRule($Self,'ReadProperty','Allow',$Guid) 
    $SelfAllowWrite = new-object System.DirectoryServices.ActiveDirectoryAccessRule($Self,'WriteProperty','Allow',$Guid)
    
    $strFilter = "(&(objectCategory=computer)(objectClass=computer))"
    
    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
    
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.PageSize = 1000
    $objSearcher.Filter = $strFilter
    $objSearcher.SearchScope = "Subtree"
    
    $colResults = $objSearcher.FindAll()
    
    foreach ($objResult in $colResults)
    {
    	$computer = [ADSI]$objResult.Path
    	if ($computer.distinguishedName -notmatch "Domain Controllers")
    	{
    		$computer.ObjectSecurity.Access | where {!($_.IdentityReference -match "SELF" -and $_.ObjectType -eq $Guid.guid)} | `
    		foreach {
    			$computer.ObjectSecurity.AddAccessRule($selfAllowRead)
    			$computer.ObjectSecurity.AddAccessRule($selfAllowWrite)
    			$computer.CommitChanges()
    		}
    	}
    }
    

    • Помечено в качестве ответа unholydiver 18 марта 2011 г. 9:49
    15 марта 2011 г. 13:28
    Отвечающий

Все ответы

  • Могу только предложить вариант на PowerShell,возможно натолкнет на мысль,как сделать на vbs.

    $Guid = [Guid]'e48d0154-bcf8-11d1-8702-00c04fb96050'
    $Self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10' 
    $SelfAllowRead = new-object System.DirectoryServices.ActiveDirectoryAccessRule($Self,'ReadProperty','Allow',$Guid) 
    $SelfAllowWrite = new-object System.DirectoryServices.ActiveDirectoryAccessRule($Self,'WriteProperty','Allow',$Guid)
    
    $strFilter = "(&(objectCategory=computer)(objectClass=computer))"
    
    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
    
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.PageSize = 1000
    $objSearcher.Filter = $strFilter
    $objSearcher.SearchScope = "Subtree"
    
    $colResults = $objSearcher.FindAll()
    
    foreach ($objResult in $colResults)
    {
    	$computer = [ADSI]$objResult.Path
    	if ($computer.distinguishedName -notmatch "Domain Controllers")
    	{
    		$computer.ObjectSecurity.Access | where {!($_.IdentityReference -match "SELF" -and $_.ObjectType -eq $Guid.guid)} | `
    		foreach {
    			$computer.ObjectSecurity.AddAccessRule($selfAllowRead)
    			$computer.ObjectSecurity.AddAccessRule($selfAllowWrite)
    			$computer.CommitChanges()
    		}
    	}
    }
    

    • Помечено в качестве ответа unholydiver 18 марта 2011 г. 9:49
    15 марта 2011 г. 13:28
    Отвечающий
  • У объектов класса COMPUTER имеется атрибут nTSecurityDescriptor. Вот по этому ключу и ищите материал.

    Несколько замечаний:

    - при загрузке ОС никаких данных об открытом сеансе пользователя ещё нет и быть не может (разве только о предыдущем сеансе, который, разумеется, уже закрыт);

    - начиная с версии XP, для надёжного определения интерактивного сеанса пользователя не требуется "заглядывать" в реестр - достаточно получить значение свойства UserName класса Win32_ComputerSystem (разве только для исключения возможных сетевых сеансов);

    - есть способ, позволяющий решить указанную задачу без раздачи дополнительных полномочий (впрочем, со своими недостатками, конечно).

    15 марта 2011 г. 14:34
  • Вот пример простенького сценария, просматривающего DACL указанного ADSI-объекта:

     

    Dim objRoot, objContainer, objSD, objACE
    Dim strDomain, strList
    Set objRoot = GetObject("LDAP://RootDSE")
    strDomain = objRoot.Get("defaultNamingContext")
    Set objRoot = Nothing
    Set objContainer = GetObject("LDAP://cn=comp,cn=Computers," & strDomain)
    Set objSD = objContainer.Get("ntSecurityDescriptor")
    For Each objACE In objSD.DiscretionaryAcl
    	If StrComp(objACE.Trustee, "NT AUTHORITY\SELF", vbTextCompare) = 0 Then
    		strList = strList & "Режим наследования: " & CBool(objACE.AceFlags And &h10 ) & vbNewLine & _
    					"Область действия: " & objACE.AceFlags & vbNewLine & _
    					"Тип доступа: " & objACE.AccessMask & vbNewLine & _
    					"Маска доступа: " & objACE.AccessMask & vbNewLine & _
    					"GUID свойства|объекта: " & objACE.ObjectType & vbNewLine & "======" & vbNewLine
    	End If
    Next
    Set objSD = Nothing
    Set objContainer = Nothing
    If Len(strList) > 0 Then
    	WScript.Echo strList
    Else
    	WScript.Echo "Не найдено ни одной записи для субъекта NT AUTHORITY\SELF."
    End If
    WScript.Quit 0

     

    16 марта 2011 г. 8:55
  • DmitriiV , да, так и есть, данные берутся из реестра и получается, что о предыдущем сеансе.

    А какой есть еще способ решения данной задачи?

     

    Спасибо за коды скриптов, еще не пробовал, сегодня обязательно проверю.

    17 марта 2011 г. 2:50
  • ну варианты могут быть разными, например логон скрипт будет записывать эти данные, но тогда надо давать права всем юзерам, либо логон скрипт записывает пару комп-юзер куда нить (файл, база и т.д.) а другой скрипт от компа или специальной учетки эти данные вписывает в ад

    17 марта 2011 г. 8:45
  • ... так и есть, данные берутся из реестра и получается, что о предыдущем сеансе.

    А какой есть еще способ решения данной задачи?


    О чём именно речь: об определении владельца текущего сеанса или о записи этой информации в AD?
    17 марта 2011 г. 8:53
  • О чём именно речь: об определении владельца текущего сеанса или о записи этой информации в AD?

    О записи информации в AD.

    Я опытным путем вычислял какие права нужно дать SELF'у, чтобы мочь записывать информацию в учетку компа.

    17 марта 2011 г. 10:08
  • О записи информации в AD.


    Способ 1: не требует раздачи (никому и никаких) дополнительных полномочий.

    1.1. Пишем сценарий для регистрации временной (или постоянной) подписки на событие появления в журнале безопасности контроллера домена записи о регистрации нового сеанса (у Win 2008 код события "Вход в систему" - 4624).
    1.2. Запускаем этот сценарий на каждом контроллере.
    1.3. Сценарий, получив сигнал о наступлении события, делает следующее:
    - анализирует текст записи и "выуживает" из него имя пользователя и IP-адрес станции;
    - сопоставляет IP-адресу имя станции;
    - выполняет привязку к нужному объекту AD и проверяет факт отсутствия значения у атрибута Description;
    - если значение отсутствует, то вносит в описание соответствующие изменения;
    - если уже имеется какое-то значение, то делает какие-либо дополнительные проверки и по их результатам выполняет соответствующие операции.

    Этот способ неплох для одноконтроллерного домена. В мультиконтроллерном он становится слишком сложным.


    Способ 2: требует раздачи дополнительных полномочий, но в минимальном количестве и только для файловых операций.

    2.1. Создаём папку общего доступа, с правами для рядовых пользователей только на запись (значение маски либо 0x100116, либо 0x40000000).
    2.2. Пишем "LogOn"-сценарий для групповой политики пользователей, который будет создавать в этой папке некий сигнальный файл. Например, это может быть пустой текстовый файл, имя которого состоит из пары: имя станции + имя пользователя.
    2.3. Пишем сценарий для регистрации временной (или постоянной) подписки на событие появления в папке общего доступа сигнальных файлов от пользователей и запускаем его с нужными полномочиями.
    2.4. Сценарий, получив сигнал о наступлении события, делает следующее:
    - анализирует имя сигнального файла и выполняет привязку к соответствующему объекту AD;
    - проверяет факт отсутствия значения у атрибута Description;
    - если уже имеется какое-то значение, то делает какие-либо дополнительные проверки и по их результатам выполняет соответствующие операции.
    - удаляет сигнальный файл.

    Таким образом, пользователи, имея лишь свои базовые права, могут инициировать процесс внесения изменений в AD.
    Этот способ проще и одинаково применим как для одноконтроллерного, так и для мультиконтроллерного домена, но требует наличия "расшаренной" папки и выполнения достаточно медленных файловых операций.

    Подобным же образом решается и вопрос регистрации события завершения сеанса пользователя (у Win 2008 код события "Выход из системы" - 4634).

    17 марта 2011 г. 14:33
  • DmitriiV, если мне не изменяет память, то этот способ называется проксирование, и Вы мне его уже объясняли на другом форуме :) Идея мне понятна, но как реализовать сие - пока не очень. А решение, как обычно, надо как можно скорее. Но в любом случае разобраться надо бы, ведь это, я так понимаю, какой универсальный способ для многих операций.
    18 марта 2011 г. 3:05
  • ... этот способ называется проксирование...

    "Проксированием" можно назвать лишь второй способ.

    ... в любом случае разобраться надо бы...

    Тогда попробуем разобраться.
    Предполагаются следующие исходные условия:
    - на одном из компьютеров с ОС серверного типа имеется каталог общего доступа;
    - SMB-разрешения этого каталога для всех - "Полный доступ";
    - NTFS-разрешения этого каталога для не привелегированных пользователей - "Запись";
    - каталог содержит два подкаталога, один из которых используется сценарием входа,
       другой - сценарием выхода (NTFS-разрешения унаследованы от "родителя");
    - за содержимым каждого подкаталога наблюдает отдельный сценарий мониторинга;
    - сценарии мониторинга запускаются по расписанию (с помощью планировщика) перед началом рабочего дня и завершаются после его окончания.

    Ниже - заготовка сценария, регистрирующего события появления в заданном каталоге новых файлов, создаваемых "LogOn"-сценарием при начале сеанса.
    Предполагается, что это пустые файлы, имена которых предполагают формат:
    Станция@Пользователь
    (разумеется, разделитель имён можно подобрать и другой).
    Расширение отсутствует.
    Думается, что разбирать код сценария входа|выхода, создающего эти файлы излишне, т.к. он элементарен.

     

    Dim objFS, strBaseFolder, strFile, arrTemp, strTemp
    Dim objWMI, objCollection, objItem
    Dim objRoot, strRoot, objComputer

    strBaseFolder = "c:\test\logon"
    strTemp = """" & Replace(strBaseFolder, "\", "\\\\", 1, -1) & """"
    Set objRoot = GetObject("LDAP://RootDSE")
    strRoot = objRoot.Get("DefaultNamingContext")
    Set objRoot = Nothing
    Set objFS = CreateObject("Scripting.FileSystemObject")
    If objFS.FolderExists(strBaseFolder) Then
    On Error Resume Next
    Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
    Set objCollection = objWMI.ExecNotificationQuery _
    ("SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DirectoryContainsFile'" _
    & " AND TargetInstance.GroupComponent='Win32_Directory.Name=" & strTemp & "'")
    If Err.Number = 0 Then
    Do
    Set objItem = objCollection.NextEvent
    x = InStrRev(objItem.TargetInstance.PartComponent, "\")
    y = Len(Mid(objItem.TargetInstance.PartComponent, x + 1)) - 1
    strFile = Mid(objItem.TargetInstance.PartComponent, x + 1, y)
    arrTemp = Split(strFile, "@")
    Call Get_ADsPath(strRoot, arrTemp(0))
    If Not IsNull(arrTemp(0)) Then
    Set objComputer = GetObject(arrTemp(0))
    If Err.Number = 0 Then
    If IsEmpty(objComputer.Description) Then
    objComputer.Put "Description", arrTemp(1)
    objComputer.SetInfo
    Else
    'WScript.Echo "Имеет смысл сделать дополнительные проверки."
    End If
    objFS.DeleteFile strBaseFolder & "\" & strFile, True
    If Err.Number <> 0 Then
    Err.Clear
    Exit Do
    End If
    Else
    Err.Clear
    End If
    Else
    Err.Clear
    Exit Do
    End If
    Loop
    Set objComputer = Nothing
    Set objItem = Nothing
    Else
    Err.Clear
    End If
    Set objCollection = Nothing
    Set objWMI = Nothing
    End If
    Set objFS = Nothing
    WScript.Quit 0

    '======

    Function Get_ADsPath(strDomain, strComputer)
    Dim objConnection, objCommand, objRSet
    Const ADS_SCOPE_SUBTREE = 2

    Set objConnection = CreateObject("ADODB.Connection")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand = CreateObject("ADODB.Command")
    Set objCommand.ActiveConnection = objConnection
    objCommand.CommandText = "SELECT ADsPath FROM 'LDAP://" & strDomain & _
    "' WHERE objectCategory='Computer' AND cn='" & strComputer & "'"
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Timeout") = 30
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
    On Error Resume Next
    Set objRSet = objCommand.Execute
    If Err.Number = 0 Then
    strComputer = objRSet.Fields("ADsPath").Value
    Else
    strComputer = NULL
    Err.Clear
    End If
    On Error GoTo 0
    Set objRSet = Nothing
    Set objCommand = Nothing
    objConnection.Close
    Set objConnection = Nothing
    End Function

     

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

    - наличие в заголовке сценария оператора Const ADS_PROPERTY_CLEAR = 1
    - иное значение переменной strBaseFolder (например, "c:\test\logoff")
    - вместо фрагмента

    If IsEmpty(objComputer.Description) Then
    objComputer.Put "Description", arrTemp(1)
    objComputer.SetInfo
    Else

    используется фрагмент

    If StrComp(objComputer.Description, arrTemp(1)) = 0 Then
    objComputer.PutEx ADS_PROPERTY_CLEAR, "Description", ""
    objComputer.SetInfo
    Else

    И последнее: если не реализовывать никаких дополнительных проверок и процедуры ведения журнала ошибок, то заготовки можно использовать как готовые к работе сценарии.


    18 марта 2011 г. 21:06
  • Совсем было забыл о таком удобном для данной задачи LDAP-интерфейсе как IADsADSystemInfo.

    С его помощью можно получить имя станции в формате Distinguished name, что, в свою очередь, позволит в сценариях мониторинга каталогов обойтись без функции Get_ADsPath().

    Таким образом, в сценариях входа и выхода должен появиться фрагмент подобный такому:

    Set objSysInfo = CreateObject("ADSystemInfo")
    strComputer = objSysInfo.ComputerName
    arrTemp = Split(objSysInfo.UserName, ",")
    strUser = Mid(arrTemp(0), 4)
    Set objSysInfo = Nothing
    

    В сценариях мониторинга:
    - станут ненужными функция Get_ADsPath() и её вызов

    - станет ненужным условный оператор

    If Not IsNull(arrTemp(0)) Then
    

    - оператор привязки к объекту-компьютеру примет вид:
    Set objComputer = GetObject("LDAP://" & arrTemp(0))
    


    21 марта 2011 г. 12:39