# version 0.55 - Diana Version # Modified to account for fact that the HTML Status command no longer works on Analysis Templates. # Modified to allow analyses to be queued using the Mu::Run command. Previously, the analysis woudl Queue, but the script would not return until # the status became runnin. package require base64 package require tls package require http package require vfs::zip namespace eval Mu { variable ipAddress variable cUserName variable cPassword variable lAuth variable cData "" variable nCode variable lError variable aTestBed variable nMinFileSize 0 variable nMaxConnectTime 30000 variable nMaxStartTime 30 variable nMaxStopTime 3600 variable lStatusValues [list "NEW" "FINISHED" "ABORTED" "FAILED" "SUSPENDED" "RUNNING" "QUEUED"] variable cListCmd "/api/v3/analysis/list" variable cImportCmd "/api/v3/templates/import" variable cRunCmd "/api/v3/analysis/run?uuid=" variable cPauseCmd "/api/v3/analysis/pause?uuid=" variable cResumeCmd "/api/v3/analysis/resume?uuid=" variable cStopCmd "/api/v3/analysis/stop?uuid=" variable cStatusCmd "/api/v3/analysis/status?uuid=" variable cExportCmd "/api/v3/templates/export?uuid=" variable cGetFaultsCmd "/api/v3/analysis/getFaultList?uuid=" variable cTemplateDelete "/api/v3/templates/delete?uuid=" variable cAnalysisDelete "/api/v3/analysis/delete?uuid=" variable cArchiveCmd "/api/v3/archive/run?uuid=" variable cArchiveStatusCmd "/api/v3/archive/status?jobId=" variable cJobStatusCmd "/api/v3/archive/status?jobId=" variable cJobList "/api/v3/jobs/list" variable cArchiveGet "/api/v3/archive/get?jobId=" variable cTemplateGet "/api/v3/templates/list?type=analysis" variable cArchiveDirectory "./" variable aAttributes proc Connect {ipAddressTmp cUserNameTmp cPasswordTmp} { variable ipAddress variable cUserName variable cPassword variable nMaxStopTime variable lAuth variable cImportCmd variable nCode variable lError [list] set ipAddress $ipAddressTmp set cUserName $cUserNameTmp set cPassword $cPasswordTmp http::register https 443 ::tls::socket set lAuth [list Authorization "Basic [base64::encode $cUserName:$cPassword]"] set cDummy "Test" if {[catch {set hToken [http::geturl https://$ipAddress$cImportCmd -headers $lAuth -query $cDummy -timeout $nMaxStopTime]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process url \"https://$ipAddress$cImportCmd -headers $lAuth -query $cDummy -timeout $nMaxStopTime\" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cImportCmd -headers $lAuth -query $cDummy -timeout $nMaxStopTime" http::cleanup $hToken if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } return $nReturn } proc List {} { variable ipAddress variable lAuth variable cListCmd variable lAllAnalyses variable cData "" variable lError [list] set nTest [catch {set hToken [http::geturl https://$ipAddress$cListCmd -headers $lAuth]} cError] if {$nTest == 0} { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cListCmd -headers $lAuth" if {$nCode ==200} { set nReturn 0 set cList [http::data $hToken] set cData [BldAnalysisList $cList] } else { set nReturn 1 } http::cleanup $hToken } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process url \"https://$ipAddress$cListCmd -headers $lAuth\" - $cError" } return $nReturn } proc GetUUID {cName {nType 3}} { # Type 1 - Template # Type 2 - Analysis # Type 3 - Any variable cData variable lError set bFound false set cName [string trim $cName] if {$nType > 3 || $nType < 1} { set nReturn 3 lappend lError -1 lappend lError "Type parameter must be between 1 and 3 inclusive." } else { if {[List] == 0} { set lAllAnalyses $cData foreach lAnalysis $lAllAnalyses { set cStatus [lindex $lAnalysis 2] set cAnalysisName [lindex $lAnalysis 0] if {[IsType $nType $cStatus] && [string match $cAnalysisName [string trim $cName]]} { set bFound true set nReturn 0 set cData [lindex $lAnalysis 1] break } } if {! $bFound} { set nReturn 3 lappend lError -1 switch $nType { 1 {set cErrorTag "Template"} 2 {set cErrorTag "Analysis"} 3 {set cErrorTag "Analysis/Template"} } lappend lError "$cErrorTag name $cName not found." } } else { # Return code 1 indicates that the List command failed details available in Mu::lError set nReturn 3 lappend lError -1 lappend lError "Mu::List command failed to run. Details should be in previous list pair." } } return $nReturn } proc ImportTemplate {cFileName} { variable ipAddress variable lAuth variable cImportCmd variable nCode variable nMinFileSize variable lError [list] set nReturn [ValidateImportFile $cFileName] if {$nReturn == 0} { if {[catch {set hFile [open $cFileName r]} cError]} { set nReturn 3 lappend lError -1 lappend lError "Unable to open file $cFileName. - $cError" } else { if {[catch {set hToken [http::geturl https://$ipAddress$cImportCmd -headers $lAuth -querychannel $hFile]} $cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process url \"https://$ipAddress$cImportCmd -headers $lAuth -querychannel $hFile\" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cImportCmd -headers $lAuth -querychannel $hFile" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } close $hFile } } return $nReturn } proc ValidateImportFile {cFileName} { variable lError variable nMinFileSize # Validate that the reference file exists, is readable and contains data. if {![file exists $cFileName]} { set nReturn 3 lappend lError -1 lappend lError "File name $cFileName does not exist." } else { if {![file readable $cFileName]} { set nReturn 3 lappend lError -1 lappend lError "File name $cFileName is not readable." } else { if {[file size $cFileName] <= $nMinFileSize} { set nReturn 3 lappend lError -1 lappend lError "Template file is too small to be valid." } else { set nReturn 0 } } } return $nReturn } proc Run {cUUID {cNewName ""}} { variable ipAddress variable lAuth variable cRunCmd variable cStatusCmd variable nMaxStartTime variable nCode variable cData "" variable lError [list] set lDone false set nLoop 1 set cNewUUID "" if {[string length $cNewName] > 0} { set cReNameCmd "&rename=$cNewName" } else { set cReNameCmd "" } if {[catch {set hToken [http::geturl https://$ipAddress$cRunCmd$cUUID$cReNameCmd -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cRunCmd$cUUID -headers $lAuth\" - $cError" } else { set nCode [http::ncode $hToken] if {$nCode == 200} { # The data returned by the http command "/api/v3/analysis/run?uuid=" is XML which contains a new UUID, this IF statment verifies that the UUID actually exists. set cOutput [http::data $hToken] if {[FindUUID $cOutput]} { set nReturn 3 lappend lError -1 lappend lError "Run failed, $cOutput" } else { set cNewUUID $cData while {!$lDone} { after 10000 if {[Status $cNewUUID] == 0} { if {[string match "RUNNING" $cData] || [string match "QUEUED" $cData]} { set lDone true set cData $cNewUUID set nReturn 0 } } incr nLoop if {$nLoop > $nMaxStartTime} { set lDone true set nReturn 3 lappend lError -1 lappend lError "Analysis failed to start after $nMaxStartTime seconds." } } } } else { set nReturn 1 lappend lError $nCode lappend lError "https://$ipAddress$cRunCmd$cUUID -headers $lAuth" } } return $nReturn } proc Pause {cUUID} { variable ipAddress variable lAuth variable cPauseCmd variable nMaxStopTime variable nCode variable cData "" variable lError [list] set lDone false set nLoop 1 if {[Status $cUUID] == 0} { set cStatus $cData if {[string match "RUNNING" $cStatus]} { if {[catch {set hToken [http::geturl https://$ipAddress$cPauseCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cPauseCmd$cUUID -headers $lAuth\" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError " https://$ipAddress$cPauseCmd$cUUID -headers $lAuth" if {$nCode == 200} { while {!$lDone} { after 1000 if {[Status $cUUID] == 0} { if {[string match "SUSPENDED" $cData]} { set lDone true set nReturn 0 } } incr nLoop if {$nLoop > $nMaxStopTime} { set lDone true set nReturn 3 lappend lError -1 lappend lError "Analysis failed to pause after $nMaxStopTime seconds." } } } else { set nReturn 1 } } } else { set nReturn 3 lappend lError -1 lappend lError "Anlaysis is in state $cStatus and therefore cannot be paused." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to obtain status of the analysis with UUID: $cUUID" } return $nReturn } proc Resume {cUUID} { variable ipAddress variable lAuth variable cResumeCmd variable nMaxStartTime variable nCode variable cData "" variable lError [list] set lDone false set nLoop 1 if {[Status $cUUID] == 0} { set cStatus $cData if {[string match "SUSPENDED" $cStatus]} { if {[catch {set hToken [http::geturl https://$ipAddress$cResumeCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cResumeCmd$cUUID -headers $lAuth\" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cResumeCmd$cUUID -headers $lAuth" if {$nCode == 200} { while {!$lDone} { after 1000 if {[Status $cUUID] == 0} { if {[string match "RUNNING" $cData]} { set lDone true set nReturn 0 } } incr nLoop if {$nLoop > $nMaxStartTime} { set lDone true set nReturn 3 lappend lError -1 lappend lError "Analysis failed to resume after $nMaxStartTime seconds." } } } else { set nReturn 1 lappend lError $nCode lappend lError " https://$ipAddress$cResumeCmd$cUUID -headers $lAuth" } } } else { set nReturn 3 lappend lError -1 lappend lError "Anlaysis is in state $cStatus and therefore cannot be resumed." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to obtain status of the analysis with UUID: $cUUID" } return $nReturn } proc Stop {cUUID} { variable ipAddress variable lAuth variable cStopCmd variable nMaxStopTime variable nCode variable cData variable lError [list] set lDone false set nLoop 1 if {[Status $cUUID] == 0} { set cStatus $cData if {[string match "RUNNING" $cStatus] || [string match "SUSPENDED" $cStatus] } { if {[catch {set hToken [http::geturl https://$ipAddress$cStopCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cStopCmd$cUUID -headers $lAuth" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cStopCmd$cUUID -headers $lAuth" if {$nCode == 200} { while {!$lDone} { after 1000 if {[Status $cUUID] == 0} { if {[string match "ABORTED" $cData]} { set lDone true set nReturn 0 } } incr nLoop if {$nLoop > $nMaxStopTime} { set lDone true set nReturn 3 lappend lError -1 lappend lError "Analysis failed to stop after $nMaxStopTime seconds." } } } else { set nReturn 1 } } } else { set nReturn 3 lappend lError -1 lappend lError "Anlaysis is in state $cStatus and therefore cannot be stoped." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to obtain status of the analysis with UUID: $cUUID" } return $nReturn } proc Status {cUUID} { variable ipAddress variable lAuth variable cStatusCmd variable cData "" variable nCode variable lError [list] if {[IsType $cUUID] == 1} { set nReturn 0 set cData "NEW" } else { if {[catch {set hToken [http::geturl https://$ipAddress$cStatusCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cStatusCmd$cUUID -headers $lAuth" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cStatusCmd$cUUID -headers $lAuth" if {$nCode == 200} { set cOutput [http::data $hToken] set cData [GetXmlData status $cOutput] if {[IsStatus $cData]} { set nReturn 0 } else { set nReturn 3 lappend lError -1 lappend lError "Analysis/Template identified by UUID = $cUUID did not return a valid status." } } else { set nReturn 1 } } } return $nReturn } proc IsStatus {cTest} { variable lStatusValues set nReturn 0 foreach cStatus $lStatusValues { if {[string match $cTest $cStatus]} { set nReturn 1 break } } return $nReturn } proc ExportTemplate {cUUID cName} { variable ipAddress variable lAuth variable cExportCmd variable nMinFileSize variable lError [list] if {[catch {set hFile [open $cName w]} cError]} { set nReturn 3 lappend lError -1 lappend lError "Unable to open file $cName. - $cError" } else { if {[catch {set hToken [http::geturl https://$ipAddress$cExportCmd$cUUID -headers $lAuth -channel $hFile]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cExportCmd$cUUID -headers $lAuth -channel $hFile" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cExportCmd$cUUID -headers $lAuth -channel $hFile" if {$nCode == 200} { if {[http::size $hToken] > $nMinFileSize} { set nReturn 0 } else { set nReturn 3 lappend lError -1 lappend lError "The exported template is to small to be valid." } } else { set nReturn 1 } } close $hFile } return $nReturn } proc WriteFaultList {cUUID cName} { variable ipAddress variable lAuth variable cGetFaultsCmd variable lError [list] if {[catch {set hFile [open $cName w]} cError]} { set nReturn 3 lappend lError -1 lappend lError "Unable to open file $cName. - $cError" } else { if {[catch {set hToken [http::geturl https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth -channel $hFile]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth -channel $hFile" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth -channel $hFile" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } close $hFile } return $nReturn } proc GetXmlData {cTag cInput} { set nOffset [expr [string length $cTag] + 2] set nStart [expr [string first <$cTag> $cInput] + $nOffset] if {$nStart >= $nOffset} { set nEnd [expr [string first $cInput] - 1] set cData [string range $cInput $nStart $nEnd] } else { set cData "" } return $cData } proc BldAnalysisList {cList} { variable cData set AnalysisList [list] set cSubstring $cList set lDone false while {! $lDone} { if {[FindUUID $cSubstring]} { set lDone true } else { set cUUID $cData set cName [GetXmlData name $cSubstring] set cStatus [GetXmlData status $cSubstring] lappend AnalysisList [list $cName $cUUID $cStatus] set nNewStart [string first $cSubstring] set cSubstring [string range $cSubstring [expr $nNewStart + 11] end] } } return $AnalysisList } proc FindUUID {cAnalyses} { variable cData set nStart [expr [string first " 15} { set nEnd [expr $nStart + 35] set cData [string range $cAnalyses $nStart $nEnd] set nReturn 0 } else { set nReturn 1 } return $nReturn } proc GetFaultList {cUUID} { variable ipAddress variable lAuth variable cGetFaultsCmd variable cData [list] variable lError [list] if {[catch {set hToken [http::geturl https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth" - $cError" } else { set nCode [http::ncode $hToken] set cFaults [http::data $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cGetFaultsCmd$cUUID -headers $lAuth" if {$nCode == 200} { set nReturn 0 set bDone false while {! $bDone} { set cNextFault [GetXmlData tba_fault $cFaults] if {[string length $cNextFault] == 0} { set bDone true } else { set nConfidence [GetXmlData confidence_level $cNextFault] set cFaultName [GetXmlData title $cNextFault] set cDetection [GetXmlData fault_detection $cNextFault] set cAttackType [GetXmlData attack_type $cNextFault] lappend cData [list $nConfidence $cFaultName $cDetection $cAttackType] set nIndex [expr [string first $cFaults] + 11] set cFaults [string range $cFaults $nIndex end] } } } else { set nReturn 1 } } return $nReturn } proc ReadTemplate {cUUID} { variable ipAddress variable lAuth variable cExportCmd variable nMinFileSize variable cData variable lError [list] if {[catch {set hToken [http::geturl https://$ipAddress$cExportCmd$cUUID -headers $lAuth]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cExportCmd$cUUID -headers $lAuth" - $cError" } else { set cData [http::data $hToken] set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cExportCmd$cUUID -headers $lAuth" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } return $nReturn } proc CopyAsTemplate {cUUID {cNewName ""}} { # This procedure is used to copy an existing Template or Analysis to a new Template with a unique # name and UUID. The original Analysis/Template is unchanged as a result of this copy. If the # cNewName parameter is provided and is valid (contains only alphanumeric characters and the # underscore, the new template will have this name. The program does not check to see if the # new name is unique. If an existing name is provided a second analysis with the same name will # be created. However, because the UUID is unique this will not cause # the original Analysis/Template to be overwritten. # # If the cNewName is not provided, or contains only whitespace, the name will be made unique by # parenthetically appending the time expressed as a 12 digit hexidecimal number representing the # number of seconds since the epoch on the source computer. # # In all cases the UUID is made unique by replacing the last 12 digits of the original UUID with # he time expressed as a 12 digit hexidecimal number representing the # number of seconds since the epoch on the source computer. variable lError [list] variable cData variable aAttributes if {[ValidName $cNewName]} { if {![ReadTemplate $cUUID] > 0} { set xmlAnalysis $cData if {![GetValue $xmlAnalysis analysis]} { if {[info exists aAttributes(name)] && [info exists aAttributes(uuid)]} { set cSuffix [format "%012x" [clock seconds]] set cStubUUID [string range $aAttributes(uuid) 0 23] set cNewUUID $cStubUUID$cSuffix if {[string length [string trim $cNewName]] == 0} { set cName [string range $cTemplate $nStartName $nEndName] set cNewName "$cName ($cSuffix)" } if {![ModifyAttribute $xmlAnalysis analysis name $cNewName]} { set xmlAnalysis $cData if {![ModifyAttribute $xmlAnalysis analysis uuid $cNewUUID]} { set xmlAnalysis $cData if {![WriteTemplate $xmlAnalysis]} { set nReturn 0 set cData [list $cNewUUID $cNewName] } else { set nReturn 3 lappend lError -1 lappend lError "Failed to write new Analysis Template to Mu." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to modify UUID." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to modify Name." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to obtain current values for UUID or Name." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to retreive XML information for tag \"analysis\"" } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to read template with UUID = $cUUID." } } else { set nReturn 3 lappend lError -1 lappend lError "Provided name \"$cNewName\" is invalid." } return $nReturn } proc ValidName {cName} { if {[string is print $cName]} { set nReturn 1 } else { set nReturn 0 } return $nReturn } proc WriteTemplate {cTemplate} { variable ipAddress variable lAuth variable cImportCmd variable nCode variable nMinFileSize variable lError [list] if {[catch {set hToken [http::geturl https://$ipAddress$cImportCmd -headers $lAuth -query $cTemplate]} cError]} { set nReturn 2 lappend lError -1 lappend lError "Failed to process url \"https://$ipAddress$cImportCmd -headers $lAuth -query $cTemplate\" - $cError" } else { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cImportCmd -headers $lAuth -query $cTemplate" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } return $nReturn } proc IsType {nType cStatus} { # 1 - Template # 2 - Analysis # 3 - Either set nReturn 0 switch $nType { 1 {if {[string match NEW $cStatus]} {set nReturn 1}} 2 {if {[string match ABORTED $cStatus] || [string match RUNNING $cStatus] || [string match SUSPENDED $cStatus] || [string match FINISHED $cStatus] || [string match FAILED $cStatus]} {set nReturn 1}} 3 {set nReturn 1} } return $nReturn } proc DeleteAnalysis {cUUID} { variable cAnalysisDelete variable lError variable ipAddress variable lAuth set nAnalysisType [AnalysisType $cUUID] if {$nAnalysisType == 2} { if {! [catch {set hToken [http::geturl https://$ipAddress$cAnalysisDelete$cUUID -headers $lAuth]} cError]} { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cAnalysisDelete$cUUID -headers $lAuth" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cAnalysisDelete$cUUID -headers $lAuth\" - $cError" } } else { set nReturn 3 lappend lError -1 if {$nAnalysisType == 1} { lappend lError "UUID = $cUUID refers to a Template, use DeleteTemplate to delete." } else { lappend lError "UUID = $cUUID was not found." } } return $nReturn } proc DeleteTemplate {cUUID} { variable cTemplateDelete variable lError variable ipAddress variable lAuth set nAnalysisType [AnalysisType $cUUID] if {$nAnalysisType == 1} { if {! [catch {set hToken [http::geturl https://$ipAddress$cTemplateDelete$cUUID -headers $lAuth]} cError]} { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cTemplateDelete$cUUID -headers $lAuth" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cAnalysisDelete$cUUID -headers $lAuth\" - $cError" } } else { set nReturn 3 lappend lError -1 if {$nAnalysisType == 1} { lappend lError "UUID = $cUUID refers to an Analysis, use DeleteAnalysis to delete." } else { lappend lError "UUID = $cUUID was not found." } } return $nReturn } proc GetValue {xmlData cTag {nOffset 0}} { variable lError variable cData variable aAttributes set lError [list] set nReturn 0 if {[regexp -indices "(<$cTag)(\[^/>\]*)(\[/>\]*)" $xmlData lOpenTagComplete lTagBegin lTagAttributes lTagEnd]} { set cEndChar [string range $xmlData [lindex $lTagEnd 0] [lindex $lTagEnd 1]] if {[string match $cEndChar "/>"]} { set xmlBody "" } else { if {[regexp -indices "()" $xmlData lCloseTagComplete]} { set nBodyStart [expr [lindex $lOpenTagComplete 1] + 1] set nBodyEnd [expr [lindex $lCloseTagComplete 0] - 1] set xmlBody [string range $xmlData $nBodyStart $nBodyEnd] } else { set nReturn 3 lappend lError -1 lappend lError "XML Data appears malformed. There is a begin Tag, but no end." } } if {[lindex $lTagAttributes 0] > 0} { set cAttributes [string range $xmlData [lindex $lTagAttributes 0] [lindex $lTagAttributes 1]] GetAttributes $cAttributes } set nAbsoluteBodyStart [expr $nBodyStart + $nOffset] set nAbsoluteBodyEnd [expr $nBodyEnd + $nOffset] set lBodyLocation [list $nAbsoluteBodyStart $nAbsoluteBodyEnd] set cData [list $xmlBody $lBodyLocation] } else { set nReturn 3 lappend lError -1 lappend lError "Specified tag \"$cTag\" not found in provided data." } return $nReturn } proc GetAttributes {cAttributes} { variable aAttributes set bDone false while {! $bDone} { if {[regexp -indices {([^=]*)(=)([ \t]*)(\")([^"]*)(\")} $cAttributes lWhole lName lEqual lWhite lOpenQuote lValue lCloseQuote]} { set cName [string trim [string range $cAttributes [lindex $lName 0] [lindex $lName 1]]] set cValue [string trim [string range $cAttributes [lindex $lValue 0] [lindex $lValue 1]]] set aAttributes($cName) $cValue set nNewStart [expr [lindex $lCloseQuote 0] + 1] set cAttributes [string range $cAttributes $nNewStart end] } else { set bDone true } } } proc InitTestBed {} { variable aTestBed variable lHostParams [list macAddress cTargetName ipV4TargetAddress ipV4Gateway ipV6TargetAddress ipV6Gateway] variable lMuParams [list cPort ipV4MuAddress nV4Mask ipV6MuAddress nV6Mask] set aTestBed(Tag,cPort) port set aTestBed(Tag,ipV4MuAddress) v4_addr set aTestBed(Tag,nV4Mask) v4_mask set aTestBed(Tag,ipV6MuAddress) v6_global_addr set aTestBed(Tag,nV6Mask) v6_global_mask set aTestBed(Tag,macAddress) mac set aTestBed(Tag,cTargetName) name set aTestBed(Tag,ipV4TargetAddress) v4_addr set aTestBed(Tag,ipV4Gateway) v4_gateway set aTestBed(Tag,ipV6TargetAdress) v6_addr set aTestBed(Tag,ipV6Gateway) v6_gateway set aTestBed(Tag,nVlan) vlan_id set aTestBed(Input,cPort) "" set aTestBed(Input,ipV4MuAddress) "" set aTestBed(Input,nV4Mask) "" set aTestBed(Input,ipV6MuAddress) "" set aTestBed(Input,nV6Mask) "" set aTestBed(Input,macAddress) "" set aTestBed(Input,cTargetName) "" set aTestBed(Input,ipV4TargetAddress) "" set aTestBed(Input,ipV4Gateway) "" set aTestBed(Input,ipV6TargetAddress) "" set aTestBed(Input,ipV6Gateway) "" set aTestBed(Input,nVlan) "" set aTestBed(Output,cPort) "" set aTestBed(Output,ipV4MuAddress) "" set aTestBed(Output,nV4Mask) "" set aTestBed(Output,ipV6MuAddress) "" set aTestBed(Output,nV6Mask) "" set aTestBed(Output,macAddress) "" set aTestBed(Output,cTargetName) "" set aTestBed(Output,ipV4TargetAddress) "" set aTestBed(Output,ipV4Gateway) "" set aTestBed(Output,ipV6TargetAddress) "" set aTestBed(Output,ipV6Gateway) "" set aTestBed(Output,nVlan) "" } # This procedure reads an existing template and replaces the existing TestBed template with a new # template built using the values that have been stored in the array Mu::aTestBed. This version # does not check to make sure that the values supplied in aTestBed are valid. proc NewTestBed {cUUID} { variable aTestBed variable lError [list] variable cData if {![ReadTemplate $cUUID]} { set xmlData $cData if {![GetValue $xmlData testbed]} { #If GetValue returns 0, Mu::cData returns a list. The 0 element is the XML value # contained within the tag. The second is the index of where this data appears in # xmlData. This will later be used to overwrite the new data. set lTestBedIndex [lindex $Mu::cData 1] if {![GetValue $xmlData analyzer_mode]} { set bClientServer false set lIndices [list Input] set cMode [string trim [lindex $Mu::cData 0]] if {[string match ClientServer $cMode]} { lappend lIndices Output set bClientServer true } set xmlTestBed [BldIF $lIndices] if {![WriteTestBed $xmlData $xmlTestBed $lTestBedIndex $bClientServer]} { set nReturn 0 set lNewAnalysis $cData set cAnalysisUUID [lindex $lNewAnalysis 0] set cAnalysisName [lindex $lNewAnalysis 1] } else { set nReturn 3 lappend lError -1 lappend lError "Failed to update attributes or import template to Mu." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to determine analysis mode of original template." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to retrieve TestBed section of original analysis template." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to retrieve analysis template for UUID = $cUUID." } return $nReturn } proc BldXML {cTag cBody {nLevel 0} {lAllAttributes [list]} {xmlData ""}} { variable cTab set xmlNew "" set cSpacing "" for {set i 0} {$i < $nLevel} {incr i} { set cSpacing $cTab$cSpacing } set cAttributeString "" if {[llength $lAllAttributes] > 0} { foreach lAttribute $lAllAttributes { set cNewAttribute [lindex $lAttribute 0]=\"[lindex $lAttribute 1]\" set cAttributeString "$cAttributeString $cNewAttribute" } } if {[string first \n $cBody] >= 0} { set cBody [TabBody $cBody $cSpacing] set xmlNew $cSpacing<$cTag$cAttributeString>\n$cBody\n$cSpacing } else { if {[string length $cBody] > 0} { set xmlNew $cSpacing<$cTag$cAttributeString>$cBody } else { set xmlNew $cSpacing<$cTag$cAttributeString/> } } if {[string length [string trim $xmlData]] > 0} { set xmlData $xmlData\n$xmlNew } else { set xmlData $xmlNew } return $xmlData } proc TabBody {cBody cSpacing} { variable cTab if {[string first \n $cBody] > 0} { set cBody $cSpacing$cTab$cBody } regsub -all \n $cBody \n$cSpacing$cTab cBody return $cBody } proc BldIF {lIndices} { variable aTestBed variable lHostParams variable lMuParams set xmlData "" foreach cIndex $lIndices { set xmlHalf "" foreach cParameter $lHostParams { if {[string length $aTestBed($cIndex,$cParameter)] > 0} { set xmlHalf [BldXML $aTestBed(Tag,$cParameter) $aTestBed($cIndex,$cParameter) 7 $xmlHalf] } } set xmlHalf [BldXML host $xmlHalf 6] set xmlHalf [BldXML hosts $xmlHalf 5] foreach cParameter $lMuParams { if {[string length $aTestBed($cIndex,$cParameter)] > 0} { set xmlHalf [BldXML $aTestBed(Tag,$cParameter) $aTestBed($cIndex,$cParameter) 5 $xmlHalf] } } set xmlHalf [BldXML attack_if $xmlHalf 4] if {[string length $xmlData] > 0} { set xmlData $xmlData\n$xmlHalf } else { set xmlData $xmlHalf } } set xmlData [BldXML mu_ifs $xmlData 3] return $xmlData } proc WriteTestBed {xmlAnalysis xmlTestBed lTestBedIndices bClientServer} { variable cData variable lError variable aTestBed variable aAttributes set xmlTestBed \n$xmlTestBed\n set xmlAnalysis [string replace $xmlAnalysis [lindex $lTestBedIndices 0] [lindex $lTestBedIndices 1] $xmlTestBed] if {![GetValue $xmlAnalysis analysis]} { if {[info exists aAttributes(name)]} { set cSuffix "_NewIP" set cNewName $aAttributes(name)$cSuffix if {![ModifyAttribute $xmlAnalysis analysis name $cNewName]} { set xmlAnalysis $cData if {[info exists aAttributes(uuid)]} { set cStubUUID [string range $aAttributes(uuid) 0 23] set cSuffix [format "%012x" [clock seconds]] set cNewUUID $cStubUUID$cSuffix if {![ModifyAttribute $xmlAnalysis analysis uuid $cNewUUID]} { set xmlAnalysis $cData if {[info exists aTestBed(Input,cTargetName)]} { if {![ModifyAttribute $xmlAnalysis target_in ref $aTestBed(Input,cTargetName)]} { set xmlAnalysis $cData if {$bClientServer} { if {[info exists aTestBed(Output,cTargetName)]} { if {![ModifyAttribute $xmlAnalysis target_out ref $aTestBed(Output,cTargetName)]} { set xmlAnalysis $cData if {![WriteTemplate $xmlAnalysis]} { set nReturn 0 set cData [list $cNewUUID $cNewName] } else { set nReturn 3 lappend lError -1 lappend lError "Failed to import modified template to Mu." } } else { set nReturn 3 lappend lError -1 lappend lError "Failed to modify ref attribute of target_out." } } else { set nReturn 3 lappend lError -1 lappend lError "Value for Output-Target Name is required and was not set." } } } else { set nReturn 3 lappend lError -1 lappend lError "Failed to modify ref attribute of target_in." } } else { set nReturn 3 lappend lError -1 lappend lError "Value for Input-Target Name is required and was not set." } } else { set nReturn 3 lappend lError -1 lappend lError "Failed to modify UUID attribute of Analysis." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to determine value of uuid attribute in source config." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to modify Name attribute of analysis." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to determine value of name attribute in source config." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to retrieve Analysis XML data from source config." } return $nReturn } proc RunNewIP {cUUID} { if {![NewTestBed $cUUID]} { set cNewUUID [lindex $Mu::cData 0] if {![Run $cNewUUID]} { if {![DeleteTemplate $cNewUUID]} { set nReturn 0 } else { set nReturn 3 lappend lError -1 lappend lError "Failed to delete temporary template" } } else { set nReturn 3 lappend lError -1 lappend lError "Failed to run modified template." } } else { set nReturn 0 lappend lError -1 lappend lError "Failed to create temporary template with new TestBed." } return $nReturn } proc ModifyAttribute {xmlData cTag cAttributeName cNewValue} { # This code takes searches the XML data provided for the indicated Tag, looks for an Attribute # named cAttributeName and replaces whatever the current value is with cNewValue. # At present this procedure does not handle instances where there may be multiple copies of the # same Tag or where Tag names are not unique. In these cases it will only look at the fist #matching tag. variable lError [list] variable cData "" # Append =" to end of attribute name to facilitate finding the attribute value location. set cSuffix =\" set cAttributeName $cAttributeName$cSuffix # Obtain complete matching XML Tag, then copy this tag to a new variable # "\" are required before all TCL significant characters because the "regexp" body is # contained in Double-Quotes rather than Braces to allow variable substitution. if {[regexp -indices "(<$cTag)(\[^/>\]*)(\[/>\]*)" $xmlData lBeginTag]} { set xmlBeginTag [string range $xmlData [lindex $lBeginTag 0] [lindex $lBeginTag 1]] # Search the Begin Tag for an attribute matching cAttributeName and obtain the location of the current attribute value. # The indexes within the string will be contained in lValue. if {[regexp -indices "($cAttributeName)(\[^\"\]*)" $xmlBeginTag lWhole lName lValue]} { set xmlBeginTag [string replace $xmlBeginTag [lindex $lValue 0] [lindex $lValue 1] $cNewValue] set xmlData [string replace $xmlData [lindex $lBeginTag 0] [lindex $lBeginTag 1] $xmlBeginTag] set nReturn 0 set cData $xmlData } else { set nReturn 3 lappend lError -1 lappend lError "Attribute named \"$cAttributeName\" not found in tag \"$cTag\"." } } else { set nReturn 3 lappend lError -1 lappend lError "Tag named \"$cTag\" not found." } return $nReturn } proc Archive {cUUID} { variable ipAddress variable lAuth variable cArchiveCmd variable nCode variable cData "" variable lError [list] variable aAttributes if {[Status $cUUID] == 0} { set cStatus $cData if {[string match "FINISHED" $cData] || [string match "ABORTED" $cData]} { if {![catch {set hToken [http::geturl https://$ipAddress$cArchiveCmd$cUUID -headers $lAuth]} cError]} { set nCode [http::ncode $hToken] if {$nCode == 200} { set cOutput [http::data $hToken] if {![GetValue $cOutput job]} { if {[info exists aAttributes(id)]} { set cData $aAttributes(id) lappend lError 0 lappend lError $cOutput set nReturn 0 } else { set nReturn 3 lappend lError $nCode lappend lError "Unable to obtain Job ID for archive creation." } } else { set nReturn 3 lappend lError $nCode lappend lError "Unable to identify XML tag \"job\"." } } else { set nReturn 1 lappend lError $nCode lappend lError $cOutput } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cArchiveCmd$cUUID -headers $lAuth\" - $cError" } } else { set nReturn 3 lappend lError -1 lappend lError "Analysis identified by UUID: $cUUID is in the $cStatus state and cannot be archived." } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to obtain status of analysis with UUID: $cUUID." } return $nReturn } proc ArchiveStatus {cJobID} { variable ipAddress variable lAuth variable cArchiveStatusCmd variable nMaxStartTime variable nCode variable cData "" variable lError if {![catch {set hToken [http::geturl https://$ipAddress$cArchiveStatusCmd$cJobID -headers $lAuth]} cError]} { set nCode [http::ncode $hToken] set cOutput [http::data $hToken] if {$nCode == 200} { # If GetValue returns 0, the value of cData will be set to the Status of the Job if {![GetValue $cOutput status]} { set nReturn 0 set lStatus $cData set cData [lindex $lStatus 0] } else { set nReturn 3 lappend lError -1 lappend lError "Unalbe to find tag \"status\" in returned XML code" } } else { set nReturn 1 lappend lError $nCode lappend lError "https://$ipAddress$cArchiveStatusCmd$cJobID -headers $lAuth" } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cArchiveStatusCmd$cJobID -headers $lAuth\" - $cError" } return $nReturn } proc JobStatus {cJobID} { variable ipAddress variable lAuth variable cJobStatusCmd variable nCode variable cData "" variable lError [list] if {![catch {set hToken [http::geturl https://$ipAddress$cJobStatusCmd$cJobID -headers $lAuth]} cError]} { set nCode [http::ncode $hToken] set cOutput [http::data $hToken] if {$nCode == 200} { # If GetValue returns 0, the value of cData will be set to the Status of the Job if {![GetValue $cOutput status]} { set nReturn 0 set lStatus $cData set cData [lindex $lStatus 0] } else { set nReturn 3 lappend lError -1 lappend lError "Unalbe to find tag \"status\" in returned XML code" } } else { set nReturn 1 lappend lError $nCode lappend lError "https://$ipAddress$cJobStatusCmd$cJobID -headers $lAuth" } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cJobStatusCmd$cJobID -headers $lAuth\" - $cError" } return $nReturn } proc ArchiveGet {cJobID {cName ""}} { variable ipAddress variable lAuth variable cArchiveGet variable nCode variable cData "" variable lError set cExtension ".zip" if {[string length $cName] == 0} { set cName $cJobID$cExtension } if {[ValidName $cName]} { if {![catch {set hFile [open $cName w]} cError]} { fconfigure $hFile -translation binary -encoding binary if {![catch {set hToken [http::geturl https://$ipAddress$cArchiveGet$cJobID -binary 1 -headers $lAuth -channel $hFile]} cError]} { set nCode [http::ncode $hToken] lappend lError $nCode lappend lError "https://$ipAddress$cArchiveGet$cJobID -binary true -headers $lAuth -channel $hFile" if {$nCode == 200} { set nReturn 0 } else { set nReturn 1 } } else { set nReturn 2 lappend lError -1 lappend lError "Failed to process URL, \"https://$ipAddress$cArchiveGet$cJobID -binary true -headers $lAuth -channel $hFile\" - $cError" } } else { set nReturn 3 lappend lError -1 lappend lError "Unable to open file $cName. - $cError" } } else { set nReturn 3 lappend lError -1 lappend lError "Name \"$cName\" is invalid" } # after 30000 close $hFile return $nReturn } proc DecompressReport {cZipReportName {cNewDirName ""} {cNewRootDir ""}} { variable cArchiveDirectory variable lError [list] if {[string length $cNewDirName] == 0} { set cNewDirName [file rootname [file tail $cZipReportName]] } if {[string length $cNewRootDir] == 0} { set cNewRootDir $cArchiveDirectory } set cSeparator [file separator] set cDir $cNewRootDir$cSeparator$cNewDirName if {[ValidName $cNewDirName]} { if {! [catch {set hZip [vfs::zip::Mount $cZipReportName $cZipReportName]} cZipError]} { if {! [catch {file copy $cZipReportName $cDir} cCopyError]} { set nReturn 0 } else { set nReturn 3 lappend lError -1 lappend lError "Unable to copy decompressed file - $cCopyError" } vfs::zip::Unmount $hZip $cZipReportName } else { set nReturn 3 lappend lError -1 lappend lError "Unable to open zip file $cZipReportName - $cZipError" } } else { set nReturn 3 lappend lError -1 lappend lError "Directory name \"$cNewDirName\" is invalid." } return $nReturn } proc AnalysisType {cUUID} { variable cData if {![List]} { set lAnalyses $cData set nReturn 0 set bFound 0 foreach lCase $lAnalyses { if {[string match $cUUID [lindex $lCase 1]]} { set bFound 1 if {[string match NEW [lindex $lCase 2]]} { set nReturn 1 } else { set nReturn 2 } } if {$bFound} {break} } } return $nReturn } }