Ran Dom
Ran Dom

Reputation: 355

How do I get the length of a PSCustom object?

The following is stored in powershell

#Maintainer Note: The leftmost parameter must match the registry key name exactly e.g. 'DES 56'
#For more information please check https://support.microsoft.com/en-us/kb/245030
$bannedCiphersJSON = @"
   {
    "RC4 128/128":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_WITH_RC4_128_MD5",
                        "SSL_RSA_WITH_RC4_128_SHA",
                        "TLS_RSA_WITH_RC4_128_MD5",
                        "TLS_RSA_WITH_RC4_128_SHA"
        ]
    },

    "Triple DES 168":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                        "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA" ,
                        "TLS_RSA_WITH_3DES_EDE_CBC_SHA" ,
                        "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
        ]
    },

    "RC4 56/128":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"
        ]
    },

    "DES 56":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_WITH_DES_CBC_SHA"
        ]
    },

    "RC4 40/128":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                        "TLS_RSA_EXPORT_WITH_RC4_40_MD5"
        ]
    },

    "RC2 40/128":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
                        "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
        ]
    },

    "MD5":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                        "SSL_RSA_WITH_RC4_128_MD5",
                        "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
                        "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
                        "TLS_RSA_WITH_RC4_128_MD5",
                        "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
        ]
    },

    "SHA":{
        "IsPermitted":false,
        "AffectedCiphers":[
                        "SSL_RSA_WITH_RC4_128_SHA",
                        "SSL_RSA_WITH_DES_CBC_SHA",
                        "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                        "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
                        "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
                        "TLS_RSA_WITH_RC4_128_SHA",
                        "TLS_RSA_WITH_DES_CBC_SHA",
                        "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
                        "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA",
                        "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"
        ]
    }
}
"@

$bannedCiphers =$bannedCiphersJSON  | ConvertFrom-Json

function Get-TLSProtocol{ 

    For ($i=0; $i -lt $bannedCiphers.Count; $i++)
    {

      write-output  $i 
    }
}

Get-TLSProtocol

When I run a Get-Member against the object, each object seems to be a "note property". Because of this I think the array definition isn't correct. (my goal is to get a list of objects that I can use to inspect the registry.

PS C:\users\golden> $bannedCiphers | get-member


   TypeName: System.Management.Automation.PSCustomObject

Name           MemberType   Definition
----           ----------   ----------
Equals         Method       bool Equals(System.Object obj)
GetHashCode    Method       int GetHashCode()
GetType        Method       type GetType()
ToString       Method       string ToString()
DES 56         NoteProperty System.Management.Automation.PSCustomObject DES 56=@{IsPermitted=False; AffectedCiphers=...
MD5            NoteProperty System.Management.Automation.PSCustomObject MD5=@{IsPermitted=False; AffectedCiphers=Sys...
RC2 40/128     NoteProperty System.Management.Automation.PSCustomObject RC2 40/128=@{IsPermitted=False; AffectedCiph...
RC4 128/128    NoteProperty System.Management.Automation.PSCustomObject RC4 128/128=@{IsPermitted=False; AffectedCip...
RC4 40/128     NoteProperty System.Management.Automation.PSCustomObject RC4 40/128=@{IsPermitted=False; AffectedCiph...
RC4 56/128     NoteProperty System.Management.Automation.PSCustomObject RC4 56/128=@{IsPermitted=False; AffectedCiph...
SHA            NoteProperty System.Management.Automation.PSCustomObject SHA=@{IsPermitted=False; AffectedCiphers=Sys...
Triple DES 168 NoteProperty System.Management.Automation.PSCustomObject Triple DES 168=@{IsPermitted=False; Affected...

What is the correct way to define an object within powershell so that I can iterate over the top most layer?

Upvotes: 3

Views: 3994

Answers (5)

Ran Dom
Ran Dom

Reputation: 355

Completed code, for your reference:

#Maintainer Note: The leftmost parameter must match the registry key name exactly e.g. 'DES 56'
#For more information please check https://support.microsoft.com/en-us/kb/245030
$bannedCiphersJSON = @"
[
  {
    "Name": "RC4 128/128",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_WITH_RC4_128_MD5",
      "SSL_RSA_WITH_RC4_128_SHA",
      "TLS_RSA_WITH_RC4_128_MD5",
      "TLS_RSA_WITH_RC4_128_SHA"
    ]
  },
  {
    "Name": "Triple DES 168",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
      "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
      "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
      "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
    ]
  },
  {
    "Name": "RC4 56/128",
    "IsPermitted": false,
    "AffectedCiphers": [
      "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"
    ]
  },
  {
    "Name": "DES 56",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_WITH_DES_CBC_SHA"
    ]
  },
  {
    "Name": "RC4 40/128",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
      "TLS_RSA_EXPORT_WITH_RC4_40_MD5"
    ]
  },
  {
    "Name": "RC2 40/128",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
      "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
    ]
  },
  {
    "Name": "MD5",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
      "SSL_RSA_WITH_RC4_128_MD5",
      "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
      "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
      "TLS_RSA_WITH_RC4_128_MD5",
      "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
    ]
  },
  {
    "Name": "SHA",
    "IsPermitted": false,
    "AffectedCiphers": [
      "SSL_RSA_WITH_RC4_128_SHA",
      "SSL_RSA_WITH_DES_CBC_SHA",
      "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
      "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
      "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
      "TLS_RSA_WITH_RC4_128_SHA",
      "TLS_RSA_WITH_DES_CBC_SHA",
      "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
      "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA",
      "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"
    ]
  }
]
"@

$bannedCiphers = $bannedCiphersJSON  | ConvertFrom-Json

function Get-TLSCipher{ 

    $TLSProtocolCollection = @()
    $regIsEnabledResult
    $regIsEnabledResultFriendly
    $cipherError 

    For ($i=0; $i -lt $bannedCiphers.Name.Count; $i++)
    {
            $TLSProtocolResult = New-Object System.Object
            $cipherError = ""

            $clientPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\" + $bannedCiphers.Name[$i]   
            $canProceedWithKey = Test-Path -Path $clientPath 
            if ($canProceedWithKey)
            {
                $regIsEnabledResult = (Get-ItemProperty -path $clientPath).Enabled
                if ($regIsEnabledResult -eq 0xffffffff -or $regIsEnabledResult -eq 0) {
                   $regIsEnabledResultFriendly = "false"

                   if($bannedCiphers.IsPermitted[$i] -eq "true")
                   {
                       $cipherError = "Should be enabled"
                   }
                }
                else
                {
                   $regIsEnabledResultFriendly = "true"

                   if($bannedCiphers.IsPermitted[$i] -eq "false")
                   {
                       $cipherError = "Should be disabled"
                   }
                }
            }
            else
            {
                $regIsEnabledResultFriendly = "OS Default"
                $cipherError = "Unknown Status" #todo - consider adding a dictionary to determine this outcome
            }

            $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name CipherShortName -Value $bannedCiphers.Name[$i]  
            $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name IsEnabled -Value $regIsEnabledResultFriendly
            $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name Errors -Value $cipherError

            $TLSProtocolCollection += $TLSProtocolResult 
    }

    #Alert if there are cipher configurations on the machine that aren't considered in the script
    $canProceedWithKey2 = Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers"  
    if ($canProceedWithKey2)
    {
        $unexpectedKeys = Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers"
        foreach ($crossCheck in $unexpectedKeys)
        {
            if (!$TLSProtocolCollection.CipherShortName.Contains($crossCheck.PSChildName))
            {
                $TLSProtocolResult = New-Object System.Object

                $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name CipherShortName -Value $crossCheck.PSChildName 
                $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name IsEnabled -Value $crossCheck.Enabled
                $TLSProtocolResult | Add-Member -MemberType NoteProperty -Name Errors -Value "Unexpected registry key. Update the 'bannedCiphersJSON' for this registry value"

                $TLSProtocolCollection += $TLSProtocolResult 
            }
        }
    }

    $TLSProtocolCollection
}

Get-TLSCipher

Upvotes: 0

Maximilian Burszley
Maximilian Burszley

Reputation: 19664

Just to add to the other answers based on your comments.. if you have control of the source of data (such as that JSON blob being hard-coded in your script), then just define an object instead of relying on conversion:

$obj = @{
    'RC4 128/128' = @{
        'IsPermitted'     = $false
        'AffectedCiphers' = @(
            'SSL_RSA_WITH_RC4_128_MD5'
            'SSL_RSA_WITH_RC4_128_SHA'
            'TLS_RSA_WITH_RC4_128_MD5'
            'TLS_RSA_WITH_RC4_128_SHA'
        )
    }

    <# ... #>
}

Now you can access the object (in this case, a hashtable) natively:

$obj.Keys

et cetera.

Upvotes: 0

Lee_Dailey
Lee_Dailey

Reputation: 7479

you have a PSCustomObject, not an array. that object has some arrays buried in the properties, tho. [grin] here's one way to get the list & the count ...

$AffectedCiphers = foreach ($PropName in $Test.PSObject.Properties.Name)
    {
    $Test.$PropName.AffectedCiphers
    }

'There are {0} ciphers in the Banned Ciphers list.' -f $AffectedCiphers.Count

output:

There are 30 ciphers in the Banned Ciphers list.

what the above does:

  • calls the hidden .PSObject property of all powershell objects
  • get the list of normal properties in the parent object
  • iterates thru the list, getting the .AffectedCiphers property values (an array of values, in this case)
  • saves them to $AffectedCiphers
  • gets the count of the items in $AffectedCiphers

hope that helps,
lee

Upvotes: 7

Eldo.Ob
Eldo.Ob

Reputation: 794

I think the only way to iterate over a CustomObject ist to get all the members, and then iterate over the members with a forEach. My solution is nearly the same like gms0ulmans solution, but a bit shorter.

($bannedCiphers | Get-Member -MemberType NoteProperty).ForEach({
    $member = $_.Name
    $value = $bannedCiphers.($_.Name)
    Write-Host "Member: $member"
    Write-Host "Is Permitted: " + $value.IsPermitted
    Write-Host "Affected Ciphers + " ($value.AffectedCiphers -join ",")
})

Upvotes: 1

G42
G42

Reputation: 10019

This may not be the optimal solution but does demonstrate how to iterate over the json contents. You do not need to know the Length/Count to achieve this.

Foreach($member in ($bannedCiphers | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name)){

    Write-Host "Name: $member"
    Write-Host "IsPermitted: $($bannedCiphers.$member.IsPermitted)"

    Write-Host "Ciphers:$($bannedCiphers.$member.AffectedCiphers.Count)"
    Foreach ($cipher in ($bannedCiphers.$member.AffectedCiphers)){
        Write-Host " - $cipher"
    }
    Write-Host "`n"
}

Edit: Added $($bannedCiphers.$member.AffectedCiphers.Count)

Upvotes: 2

Related Questions