Set WMI permissions for 'Domain users'

2019-07-22 19:33发布

问题:

I've created two functions in PowerShell, one that creates the Namespace ROOT\CustomCMClasses and one that creates the class Test. This piece works fine:

Param (
    $Namespace = 'CustomCMClasses',
    $Class     = 'Test'
)

Function New-WMINamespaceHC{
    if (Get-WmiObject -Namespace 'root' -Class '__NAMESPACE' | Where-Object {$_.Name -eq $Namespace}) {
        Write-Verbose "WMI Namespace 'root\$Namespace' exists"
    }
    else {
        Write-Verbose "Create WMI namespace 'root\$Namespace'"
        $Ns = [WMICLASS]'root:__Namespace' 
        $NewNamespace = $Ns.createInstance() 
        $NewNamespace.Name = $Namespace 
        $NewNamespace.Put() 
    }
}

Function New-WMIClassHC {
    if (Get-WmiObject -List -Namespace "root\$Namespace" | Where-Object {$_.Name -eq $Class}) {
        Write-Verbose "WMI Class '$Class' exists"
    }
    else {
        Write-Verbose "Create WMI Class '$Class'"
        $NewClass = New-Object System.Management.ManagementClass ("root\$Namespace", [String]::Empty, $Null); 
        $NewClass['__CLASS'] = $Class
        $NewClass.Qualifiers.Add('Static', $true)
        $NewClass.Properties.Add('Key', [System.Management.CimType]::String, $false)
        $NewClass.Properties['Key'].Qualifiers.Add('Key', $true)
        $NewClass.Properties.Add('Value1', [System.Management.CimType]::UInt32, $false)
        $NewClass.Properties.Add('Value2', [System.Management.CimType]::String, $false)
        $NewClass.Put()
    }
}

At this point I need to grant Domain users permissions on the new namespace, so they are able to write data to it on their local client (no remoting needed). This is the point where I find a lot of information but am now stuck.

On the Microsoft blog there is a script I tried to tweak, but it's overly complicated for my needs and it failed so I found and tweaked the following code:

Function Add-WMIPermissions {
    [CmdLetBinding()]
    Param (
        [String]$Principal = 'DOMAIN.NET\Domain users',
        [String]$Namespace = 'CustomCMClasses'
    )

    Function Get-Sid {
        Param (
            $DSIdentity
        )
         $ID = new-object System.Security.Principal.NTAccount($DSIdentity)
         Return $ID.Translate([System.Security.Principal.SecurityIdentifier]).toString()
    }

    $Sid = Get-Sid $Principal

    $WMISDDL = "A;CI;CCWP;;;$Sid" 
    $WMISDDLPartialMatch = "A;\w*;\w+;;;$Sid"

    $security = Get-WmiObject -Namespace root\$Namespace -Class __SystemSecurity
    $binarySD = @($null)
    $result = $security.PsBase.InvokeMethod('GetSD',$binarySD)

    $converter = New-Object system.management.ManagementClass Win32_SecurityDescriptorHelper
    $CurrentWMISDDL = $converter.BinarySDToSDDL($binarySD[0])

    if (($CurrentWMISDDL.SDDL -match $WMISDDLPartialMatch) -and ($CurrentWMISDDL.SDDL -notmatch $WMISDDL)) {
        $NewWMISDDL = $CurrentWMISDDL.SDDL -replace $WMISDDLPartialMatch, $WMISDDL
    }
    else {
        $NewWMISDDL = $CurrentWMISDDL.SDDL += '(' + $WMISDDL + ')'
    }

    $WMIbinarySD = $converter.SDDLToBinarySD($NewWMISDDL)
    $WMIconvertedPermissions = ,$WMIbinarySD.BinarySD

    if ($CurrentWMISDDL.SDDL -match $WMISDDL) {
        Write-Verbose 'Current WMI Permissions matches desired value'
    }
    else {
        $result = $security.PsBase.InvokeMethod('SetSD',$WMIconvertedPermissions) 
        if($result='0'){
            Write-Verbose 'WMI permissions applied'
        }
    }
}

Add-WMIPermissions -Verbose

It's clearly stating that the permissions are correctly applied but the user still can't write data to WMI. Working with WMI is new to me, so any help is greatly appreciated.

The test code to see if a regular user (Domain users) has permissions:

$WMIClass = [WMICLASS]('root\' + $Namespace + ':' + $Class)
$WMIInstance = $WMIClass.CreateInstance()
$WMIInstance.Key =  'Unique value identifier 5'
$WMIInstance.Value1 = 101
$WMIInstance.Value2 = 'Status Ok'
$WMIInstance.Put()

回答1:

Solved the problem for Partial Write:

Function Set-WMIPermissionsHC {
    Param (
        [String]$Namespace = 'CustomCMClasses',
        [String]$Class     = 'Test',
        [String]$Account   = 'DOMAIN\Domain users',
        [String]$Computer  = $env:COMPUTERNAME
    )

    Function Get-Sid {
        Param (
            $Account
        )
        $ID = New-Object System.Security.Principal.NTAccount($Account)
        Return $ID.Translate([System.Security.Principal.SecurityIdentifier]).toString()
    }

    $SID = Get-Sid $Account
    $SDDL = "A;CI;CCSWWP;;;$SID"
    $DCOMSDDL = "A;;CCDCRP;;;$SID"
    $Reg = [WMICLASS]"\\$Computer\root\default:StdRegProv"
    $DCOM = $Reg.GetBinaryValue(2147483650,'software\microsoft\ole','MachineLaunchRestriction').uValue
    $Security = Get-WmiObject -ComputerName $Computer -Namespace "root\$Namespace" -Class __SystemSecurity
    $Converter = New-Object System.Management.ManagementClass Win32_SecurityDescriptorHelper
    $BinarySD = @($null)
    $Result = $Security.PsBase.InvokeMethod('GetSD', $BinarySD)
    $OutSDDL = $Converter.BinarySDToSDDL($BinarySD[0])
    $OutDCOMSDDL = $Converter.BinarySDToSDDL($DCOM)
    $NewSDDL = $OutSDDL.SDDL += '(' + $SDDL + ')'
    $NewDCOMSDDL = $OutDCOMSDDL.SDDL += '(' + $DCOMSDDL + ')'
    $WMIbinarySD = $Converter.SDDLToBinarySD($NewSDDL)
    $WMIconvertedPermissions = ,$WMIbinarySD.BinarySD
    $DCOMbinarySD = $Converter.SDDLToBinarySD($NewDCOMSDDL)
    $DCOMconvertedPermissions = ,$DCOMbinarySD.BinarySD
    $Result = $Security.PsBase.InvokeMethod('SetSD', $WMIconvertedPermissions)
    $Result = $Reg.SetBinaryValue(2147483650,'software\microsoft\ole','MachineLaunchRestriction', $DCOMbinarySD.binarySD)
    Write-Verbose 'WMI Permissions set'
}

Thanks to this blog and the Microsoft blog for explaining the permissions