# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html # This function specifically validates that the peer we are talking to is signed by the xCAT blessed CA and no other CA Function Import-xCATConfig ($credentialPackage) { $shell = New-Object -com shell.application (Get-Location).Path+"\$credentialPackage" $credpkg = $shell.namespace((Get-Location).Path+"\$credentialPackage") $credpkg $randname = [System.IO.Path]::GetRandomFileName() Push-Location mkdir $env:temp+"\"+$randname Set-Location $env:temp+"\"+$randname $tmpdir = $shell.namespace((Get-Location).Path) $tmpdir $tmpdir.CopyHere($credpkg.items(),0x14) if (!(Test-Path HKCU:\Software\xCAT)) { mkdir HKCU:\Software\xCAT } if (Test-Path xcat.cfg) { $cfgdata=Get-Content xcat.cfg $keyvalue = $cfgdata.Split("=") $servername = $keyvalue[1] Set-ItemProperty HKCU:\Software\xCAT servername $servername } if (Test-Path ca.pem) { ImportxCATCA ca.pem } if (Test-Path user.pfx) { SetxCATClientCertificate user.pfx } Pop-Location } Function VerifyxCATCert ($sender, $cert, $chain, $polerrs) { if ($polerrs -ne "None" -and $polerrs -ne "RemoteCertificateChainErrors") { return $false } #if the overall policy suggests rejection, go with it #why do we tolerate RemoteCertificateChainErrors? Because we are going to check specifically for the CA configured for this xCAT installation #we chose not to add xCAT's CA to the root store, as that implies the OS should trust xCAT's CA for non-xCAT related things. That is madness. #Of course, that's the madness typical with x509, but we need not propogate the badness... #we are measuring something more specific than 'did any old CA sign this', we specifically want to assue the signer CA is xCAT's $mythumb=Get-ItemProperty HKCU:\Software\xCAT foreach ($cert in $chain.chainElements) { if ($mythumb.cacertthumb.Equals($cert.Certificate.thumbprint)) { return $true } } return $false } #we import the xCAT certificate authority into the appropriate scope. #It's not trusted by system policy, but our overidden verify function will find it. Too bad MS doesn't allow us custom store names under the user #repository for whatever reason. We'll just 'import' it every session from file, which is harmless to do multiple times #this isn't quite as innocuous as the openssl mechanisms to do this sort of thing, but it's as close as I could figure to get Function ImportxCATCA ( $certpath ) { $xcatcacert=Import-Certificate -FilePath $certpath -CertStoreLocation Cert:\CurrentUser\My Set-ItemProperty HKCU:\Software\xCAT cacertthumb $xcatcacert.thumbprint } #this removes the xCAT CA from trust store, if user wishes to explicitly remove xCAT key post deploy #A good idea for appliances that want to not show weird stuff. The consequences of not calling it are harmless: a useless extra public cert #in admin's x509 cert store Function RemovexCATCA { $mythumb=Get-ItemProperty HKCU:\Software\xCAT rm cert:\CurrentUser\My\$mythumb.cacertthumb } #specify a client certificate to use in pfx format Function SetxCATClientCertificate ( $pfxPath ) { $xcatclientcert=Import-pfxCertificate $pfxPath -certStoreLocation cert:\currentuser\my Set-ItemProperty HKCU:\Software\xCAT usercertthumb $xcatclientcert.thumbprint } Function RemovexCATClientCertificate { SetxCATClientCertificate($pfxpath) $mythumb=Get-ItemProperty HKCU:\Software\xCAT rm cert:\currentuser\my\$mythumb.usercertthumb } #key here is that we might have two certificates: #-one intended to identify the system that was deployed by xcat #-one intended to identify the user to do things like 'rpower' #however, user will just have to control it by calling Set-xCATClientCertificate on the file for now #TODO: if user wants password protected PFX file, we probably would want to import it once and retain thumb across sessions... Function SelectxCATClientCert ($sender, $targetHost, $localCertificates, $remoteCertificate,$acceptableIssuers) { $mythumb=(Get-ItemProperty HKCU:\Software\xCAT).usercertthumb Get-Item cert:\CurrentUser\My\$mythumb } Function Set-xCATServer { Param( [Parameter(Mandatory=$true)] $xCATServer ) Set-ItemProperty HKCU:\Software\xCAT serveraddress $xCATServer } Function Connect-xCAT { Param( $mgtServer, $mgtServerPort=3001, $mgtServerAltName ) if (! $mgtServer) { $mgtServer=(Get-ItemProperty HKCU:\Software\xCAT).serveraddress if (! $mgtServer) { $mgtServer=(Get-ItemProperty HKCU:\Software\xCAT).servername } } if (! $mgtServerAltName) { $mgtServerAltName=(Get-ItemProperty HKCU:\Software\xCAT).servername } $script:xcatconnection = New-Object Net.Sockets.TcpClient($mgtServer,$mgtServerPort) if (! $script:xcatconnection) { return $false } $verifycallback = Get-Content Function:\VerifyxCATCert $certselect = Get-Content Function:\SelectxCATClientCert $script:xcatstream = $script:xcatconnection.GetStream() $haveclientcert=0 if (Test-Path HKCU:\Software\xCAT) { $xcreg=Get-ItemProperty HKCU:\Software\xCAT if ($xcreg.usercertthumb) { $haveclientcert=1 } } if ($haveclientcert) { $script:securexCATStream = New-Object System.Net.Security.SSLStream($script:xcatstream,$false,$verifycallback,$certselect) } else { $script:securexCATStream = New-Object System.Net.Security.SSLStream($script:xcatstream,$false,$verifycallback) } $script:securexCATStream.AuthenticateAsClient($mgtServerAltName) $script:xcatwriter = New-Object System.IO.StreamWriter($script:securexCATStream) $script:xcatreader = New-Object System.IO.StreamReader($script:securexCATStream) $true } Function Get-NodeInventory { Param( [parameter(ValueFromPipeLine=$true)] $nodeRange, [parameter(ValueFromRemainingArguments=$true)] $inventoryType ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rinv';'noderange'=$nodeRange;'args'=@($inventoryType)} Send-xCATCommand($xcatrequest) } Function Get-NodeBeacon { Param( [parameter(ValueFromPipeLine=$true)] $nodeRange ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rbeacon';'noderange'=$nodeRange;'args'=@('stat')} Send-xCATCommand($xcatrequest) } Function Set-NodeBeacon { Param( [parameter(ValueFromPipeLine=$true)] $nodeRange, $newBeaconState ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rbeacon';'noderange'=$nodeRange;'args'=@($newBeaconState)} Send-xCATCommand($xcatrequest) } Function Get-NodePower { Param( [parameter(Position=0,ValueFromPipeLine=$true)] $nodeRange ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rpower';'noderange'=$nodeRange;'args'=@('stat')} Send-xCATCommand($xcatrequest) } Function Merge-xCATData { #xcoll attempt $groupeddata=$input|Group-Object -Property "node" $hashbyoutput=@{} foreach ($nodedata in $groupeddata) { $gdata= NewMergedxCATData $nodedata.Group if ($hashbyoutput.Contains($gdata.stringcontent)) { $hashbyoutput.Get_Item($gdata.stringcontent).NodeList += $gdata.NodeList } else { $hashbyoutput.Add($gdata.stringcontent,$gdata) } } $distinctoutput=$hashbyoutput.GetEnumerator() foreach ($collateddata in $distinctoutput) { $findata = $collateddata.Value $findata.NodeRange=[string]::Join(",",$findata.NodeList) $findata = $findata |select-object -excludeproperty NodeRangeHint,stringcontent * $mobjname = 'MergedxCATSimpleNodeData' foreach ($do in $findata.dataObjects) { if ($do.ErrorData) { $mobjname='MergedxCATNodeErrorData' break } elseif ($do.description) { $mobjname='MergedxCATNodeData' break } } foreach ($do in $findata.dataObjects) { $do|Add-Member -MemberType NoteProperty -Name NodeRange -Value $findata.NodeRange $do.PSObject.TypeNames.Insert(0,$mobjname) $do } } } Function Set-NodePower { Param( [parameter(ValueFromPipeLine=$true)] $nodeRange, [parameter(HelpMessage="The power action to perform (on/off/boot/reset)")] $powerState="stat" ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rpower';'noderange'=$nodeRange;'args'=@($powerState)} Send-xCATCommand($xcatrequest) } Function Get-Nodes { Param( [parameter(Position=0,ValueFromPipeLine=$true)] $nodeRange, [parameter(ValueFromRemainingArguments=$true)] $tableAndColumn ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='nodels';'noderange'=$nodeRange;'args'=@($tableAndColumn)} Send-xCATCommand($xcatrequest) } Function Get-NodeVitals { Param( [parameter(Position=0,ValueFromPipeLine=$true)] $nodeRange, [parameter(ValueFromRemainingArguments=$true)] $vitalTypes="all" ) $pipednr=@($input) if ($pipednr) { $nodeRange = $pipednr } $xcatrequest=@{'command'='rvitals';'noderange'=$nodeRange;'args'=@($vitalTypes)} Send-xCATCommand($xcatrequest) } Function Send-xCATCommand { Param( $xcatRequest ) if (!(Connect-xCAT)) { return } $requestxml = "`n`t"+$xcatRequest.command+"`n" if ($xcatRequest.noderange) { if ($xcatRequest.noderange.PSObject.TypeNames[0] -eq "xCATNodeData") { $xcatRequest.noderange = $xcatRequest.noderange.Node } if ($xcatRequest.noderange -is [System.Array]) { #powershell wants to arrayify commas because it can't make up its mind #whether it's a scripting language or a shell language, try to undo the #damage $nrparts=@() foreach ($nr in $xcatRequest.noderange) { if ($nr -is [System.String]) { $nrparts += $nr } elseif ($nr.PSObject.TypeNames[0] -like "xCAT*Node*Data") { $nrparts += $nr.Node } elseif ($nr.PSObject.TypeNames[0] -like "Merge*xCAT*Node*Data") { $nrparts += $nr.NodeRange } } $xcatRequest.noderange=[string]::Join(",",$nrparts); } $requestxml = $requestxml + "`t"+$xcatRequest.noderange+"`n" } foreach ($arg in $xcatRequest.args) { if ($arg) { if ($arg -is [System.Array]) { $arg=[string]::join(",",$arg); } $requestxml = $requestxml + "`t"+$arg+"`n" } } $requestxml = $requestxml + "`n" $script:xcatwriter.WriteLine($requestxml) $script:xcatwriter.Flush() $serverdone=0 while (! $serverdone -and $script:xcatreader) { $responsexml="" $lastline="" while ($lastline -ne $null -and ! $lastline.Contains("") -and $script:xcatreader) { $lastline = $script:xcatreader.ReadLine() $responsexml = $responsexml + $lastline } [xml]$response = $responsexml foreach ($elem in $response.xcatresponse.node) { NewxCATDataFromXmlElement $elem -NodeRangeHint $xcatRequest.noderange } foreach ($elem in $response.xcatresponse.error) { Write-Error $elem } #$response.xcatresponse.node.name #$response.xcatresponse.node.data if ($response.xcatresponse.serverdone -ne $null) { $serverdone=1 } } } Function NewMergedxCATData { #takes an arbitrary number of nodeData objects and spits out one Param( $nodeData ) $myobj = @{} $myobj.dataObjects=@() $myobj.NodeList = @($nodeData[0].Node) $myobj.NodeRangeHint = $nodeData[0].NodeRangeHint $myobj.stringcontent = "" $myobj.NodeRange = "" foreach ($data in $nodeData) { $rangedata = $data|select-object -ExcludeProperty Node,NodeRangeHint * foreach ($dataseg in $rangedata) { if ($dataseg.ErrorData) { $myobj.stringcontent += "ERROR: "+$dataseg.ErrorData } $myobj.stringcontent += $dataseg.Description+": "+$dataseg.Data+"`n" } $myobj.dataObjects = $myobj.dataObjects + $rangedata } $newobj = New-Object -TypeName PSObject -Prop $myobj $newobj.PSObject.TypeNames.Insert(0,'TempxCATNodeRangeData') return $newobj } Function NewxCATDataFromXmlElement { Param( $xmlElement, $NodeRangeHint ) $myprops = @{} $objname = 'xCATSimpleNodeData' if ($NodeRangeHint) { #hypothetically, 'xcoll' implementation might find this handy $myprops.NodeRangeHint=$NodeRangeHint } if ($xmlElement.name) { $myprops.Node=$xmlElement.name } if ($xmlElement.data.desc) { $objname = 'xCATNodeData' $myprops.Description=$xmlElement.data.desc } if ($xmlElement.data.contents) { $myprops.Data=$xmlElement.data.contents } else { $myprops.Data="" } if ($xmlElement.error) { $objname = 'xCATNodeErrorData' $errstr= $xmlElement.name + ": " + $xmlElement.error Write-Error $errstr $myprops.ErrorData=$xmlElement.error } $myobj=New-Object -TypeName PSObject -Prop $myprops $myobj.PSObject.TypeNames.Insert(0,$objname) return $myobj } New-Alias -name rpower -value Set-NodePower New-Alias -name rvitals -value Get-Nodevitals New-Alias -name rinv -value Get-NodeInventory New-Alias -name rbeacon -value Set-NodeBeacon New-Alias -name nodels -value Get-Nodes New-Alias -name xcoll -value Merge-xCATData Export-ModuleMember -function *-* -Alias *