The only real way to audit Linux servers remotely is via SSH connections. Unfortunately this is not built into Windows (although apparently a .NET library can be called through PowerShell). The solution may be the Chilkat SSH ActiveX control. Unfortunately it does cost - $250US for a site license at last check.
Here is a script that uses that SSH ActiveX control to connect to Linux servers and extract HPA (Highly Privileged Access) accounts - i.e. all accounts that have root access. The script can be modified to run almost any command however. It then exports the information to CSV files. The two CSVs exported are the user - group memberships, and the SUDoers file which says what each user and group can do.
Use Excel to create formulas to determine which users and groups are HPA.
Here is the script that has been tested against RHEL 4 and 5 as well as Solaris 8 and 10. Not guaranteed to work against all servers however as it depends considerably on the setup of each server:
'************************************************************
'************************************************************
'******* Script to connect to UNIX and LINUX type ***********
'******* servers to run remote commands ***********
'******* ***********
'******* Includes function to elevate privileges ***********
'******* ***********
'******* Requires Chilkat SSH ActiveX component ***********
'************************************************************
'************************************************************
'************************************************************
'DEFINE TEXT FILES AND CREATE OBJECTS
'************************************************************
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Dim fso, logFile, dataFile, serverFile, UIDFile, groupFile, sudoersFile, x, y
' verboseLogging = 0 ' Minimal logging
' verboseLogging = 1 ' Verbose logging
verboseLogging = 2 ' Full logging
Set fso = CreateObject("Scripting.FileSystemObject")
Set dataFile = fso.CreateTextFile("RawData.txt", True) 'Output information from commands
Set logFile = fso.CreateTextFile("SessionLog.txt", True) 'Verbose Logging
Set groupFile = fso.CreateTextFile("GroupMemberships.txt", True) 'User group memberships
Set sudoersFile = fso.CreateTextFile("SUDOERS.txt", True) 'Sudoers output
Set serverFile = fso.OpenTextFile("Servers.txt", ForReading) 'Input information about servers
If Err.Number <> 0 Then
wscript.echo "CRASH: Cannot open file for reading: Servers.txt. " & Err.Number & " " & Err.Description
wscript.quit
End If
' Important: It is helpful to send the contents of the
' ssh.LastErrorText property when requesting support.
set ssh = CreateObject("Chilkat.Ssh") 'Create connection to Chilkat ActiveX Control
success = ssh.UnlockComponent("30-day trial") 'Unlock 30 day trial of ActiveX Control
If (success <> 1) Then
MsgBox ssh.LastErrorText
WScript.Quit
End If
if verboseLogging > 0 Then logFile.WriteLine("The result of creating an object should be 1. The result is: " & success)
if verboseLogging > 0 Then logFile.WriteLine("The last error message is: " & ssh.LastErrorText)
'************************************************************
'THESE ARE THE COMMANDS WE WILL RUN AGAINST EACH SERVER
'************************************************************
' Grab the user with UID of 0 as this is the root user
command2 = "cut -d: -f1,3 /etc/passwd |grep :0$"
' Grab all users (listed in passwd) and return their group memberships. If Solaris (SunOS) then it also prints the username. RHEL does this automatically.
command3 = "for i in `cat /etc/passwd |cut -d: -f1`; do if [ ""`uname -s`"" = ""SunOS"" ]; then printf ""$i : ""; fi; groups $i; done"
' Find sudo and parse its binary for the location of sudoers. We then grep out lines starting with # (^#), and print to screen
' ^# is escaped with backslash as it can be a special character on Solaris.
command5a = "which sudo | xargs strings |grep sudoers |grep \/"
command5 = "which sudo | xargs strings |grep sudoers |grep \/ |xargs grep -v \^# |grep ALL |awk '{print$1, $2, $3}'"
' If unable to find sudoers location in the binary of sudo then fall back to searching for sudoers in one of the following locations.
command6 = "find /opt/sfw/etc /usr/local/etc /etc -name sudoers -print 2>/dev/null |xargs grep -v \^# |grep ALL |awk '{print$1,$2,$3}'"
'************************************************************
'MAIN ROUTINE (CONNECT TO SERVERS IN SERVERS.TXT)
'************************************************************
' Servers.txt MUST contain 5 comma separated items on each line:
' Hostname or IP Address,SSH Port,Username,Password,SU Password
' myhost,22,username,password,supassword
Do Until serverFile.AtEndOfStream
blFoundErr = "False"
' Parse servers.txt in CSV format and read the arguments
arrFileLine = CSVParser(serverFile.ReadLine)
On Error Resume Next
hostname = trim(arrFileLine(0))
port = trim(arrFileLine(1))
authuser = trim(arrFileLine(2))
authpwd = trim(arrFileLine(3))
supwd = trim(arrFileLine(4))
If Err.Number <> 0 Then
logFile.WriteLine("Error reported: "&Err.Number)
logFile.WriteLine("ERROR: A line in the server.txt was an error and was not processed.")
blFoundErr = True
End If
On Error Goto 0
' If no errors then connect to server and collect information
If blFoundErr = "False" Then
lineToWrite = "INFO: Working with hostname """ & hostname &_
""" on port: """ & port & """ with username """ & authuser & """." & vbCRLF &_
vbCRLF & "This script will connect SSH, elevate SU and run the commands: "&vbCRLF&_
command2&vbCRLF&command3&vbCRLF&command5&vbCRLF&command6
wscript.echo lineToWrite
logFile.WriteLine(lineToWrite)
logFile.WriteLine("Servername: "&hostname)
dataFile.WriteLine("Servername: "&hostname)
groupFile.WriteLine("Servername: "&hostname)
sudoersFile.WriteLine("Servername: "&hostname)
Result=""
'************************************************************
'CONNECT TO THE SERVER USING SSH
'************************************************************
' Keep a session log, which is available via the SessionLog property:
ssh.KeepSessionLog = 1
' Connect to the server using SSH
success = ssh.Connect(hostname,port)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Connecting to host "&hostname&" on port "&port&".")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
'wscript.echo "Success code when connecting to host: " & success
' When reading, if no additional data arrives for more than 20 seconds, then abort:
ssh.IdleTimeoutMs = 20000
' SSH Server Authentication
' If there is no login/password required, you must still call AuthenticatePw and use any values for login/password.
If Result <> "ERROR" Then
success = ssh.AuthenticatePw(authuser,authpwd)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Was able to connect to "&hostname&" but unable to authorize with the provided credentials.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If
'wscript.echo "Success code when authenticating to server: " & success
' Open a session channel.
If Result <> "ERROR" Then
channelNum = ssh.OpenSessionChannel()
If (channelNum < 0) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Opening Session on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If
' Request a pseudo-terminal
termType = "dumb"
widthInChars = 120
heightInChars = 40
pixWidth = 0
pixHeight = 0
If Result <> "ERROR" Then
success = ssh.SendReqPty(channelNum,termType,widthInChars,heightInChars,pixWidth,pixHeight)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Requesting Pseudo-Terminal on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If
'wscript.echo "Success code when requesting pseudo-terminal on host: " & success
' Start a shell on the channel:
If Result <> "ERROR" Then
success = ssh.SendReqShell(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Requesting Pseudo-Terminal on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If
'wscript.echo "Success code trying to start shell on the channel: " & success
If Result <> "ERROR" Then logFile.WriteLine("INFO: This server appears to have been connected to successfully.")
logFile.WriteLine("INFO: Server "&hostname&" was connected if this is 1: " & ssh.IsConnected & vbCRLF)
'wscript.sleep(5000)
if verboseLogging > 0 Then logFile.WriteLine("INFO: VERBOSE: Completed a sleep interval and starting to run commands.")
'************************************************************
'RUN OUR COMMANDS HERE
'************************************************************
if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Begin function to elevete SU.")
If Result <> "ERROR" Then Result=ElevateSU(supwd)
if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Exited function to elevete SU.")
If Result <> "ERROR" Then
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Result is not set to ERROR.")
'Get users with root ID
CommandResult=ExecuteCommand(command2)
If CommandResult <> "ERROR" Then parseRootUID(CommandResult)
'Get users and group memberships
CommandResult=ExecuteCommand(command3)
If CommandResult <> "ERROR" Then parseGroupMemberships(CommandResult)
'Get Sudoers contents using either 'strings' or 'find' commands
commandForSudoers = command6
CommandResult=ExecuteCommand(command5a)
If CommandResult <> "ERROR" Then
If (TestStringContainsSudoers(CommandResult)) = "TRUE" Then
commandForSudoers = command5
End If
End if
logFile.WriteLine("INFO: Sudoers Command to execute: & """ & commandForSudoers & """.")
CommandResult=ExecuteCommand(commandForSudoers)
If CommandResult <> "ERROR" Then parseSUDoers(CommandResult)
End If
'************************************************************
'CLOSE THE SSH SESSION AND DISCONNECT
'************************************************************
Result=""
If verboseLogging > 0 Then logFile.WriteLine("INFO: VERBOSE: Now attempting to close down the SSH session and disconnect.")
success = ssh.ChannelSendEof(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to successfully shutdown the session.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
success = ssh.ChannelSendClose(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to successfully shutdown the session.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
' Disconnect
ssh.Disconnect
If Result <> "ERROR" Then logFile.WriteLine("INFO: The session appears to be shut down successfully.")
lineToWrite = "Server " & hostname & " was disconnected successfully if response is 0: " & ssh.IsConnected
logFile.WriteLine (lineToWrite)
wscript.echo lineToWrite
wscript.sleep(2000)
logFile.WriteLine ("INFO: " & hostname & ": Completed sleep interval.")
logFile.WriteLine ("INFO: " & hostname & ": End of server processing." & vbCRLF)
End If
Loop
'************************************************************
'FINALLY CLOSE OFF OUR LOG FILES
'************************************************************
logFile.WriteLine("INFO: Finished processing all servers in file and closing off files.")
logFile.Close
dataFile.Close
groupFile.Close
sudoersFile.close
'************************************************************
'************************************************************
'Here come the functions!!!
'************************************************************
'************************************************************
'************************************************************
'PARSE OUTPUT OF COMMAND: ROOT UID (ROOT UID IS ALWAYS 0)
'EXPORT TO CSV
'************************************************************
Function parseRootUID(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Root UID.")
splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(command2,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 0 Then
if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
'Lets Trim the line
x=Trim(x)
'Lets replace : with ,
x=replace(x,":",",")
'Lets write it to a file
groupFile.WriteLine(hostname&","&x)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
Next
logFile.WriteLine("INFO: " & hostname & ": Finished parsing Root UID." & vbCRLF)
End Function
'************************************************************
'PARSE OUTPUT OF COMMAND: GROUP MEMBERSHIPS
'EXPORT TO CSV
'************************************************************
Function parseGroupMemberships(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Group Memberships.")
splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(command3,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 1 Then
' Delimiter : was found so parsing line - this is common for Red Hat so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
userComponent = trim(left(x,inStr(x,":")-1))
groupComponent = trim(right(x,len(x)-inStr(x,":")))
splitGroupComponent=Split(groupComponent," ")
groupFile.WriteLine(hostname&","&userComponent) ' Addition 7/12/2011
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
ElseIf instr(x," ") > 1 Then
'Space Delimiter was found with no colon - this is common for Solaris so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [SPACE] found: " & x)
userComponent = trim(left(x,inStr(x," ")-1))
groupComponent = trim(right(x,len(x)-inStr(x," ")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
Next
logFile.WriteLine("INFO: " & hostname & ": Finished parsing Group Memberships." & vbCRLF)
End Function
'************************************************************
'PARSE OUTPUT OF COMMAND: SU DOERS (PERMISSIONS ASSIGNMENT)
'EXPORT TO CSV
'************************************************************
Function parseSUDoers(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing SUDOERS file.")
specialDelimiter = " "
splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(commandForSudoers,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,specialDelimiter) > 0 Then
' Special delimiter has been found - parsing with special delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter """ & specialDelimiter & """ found: " & x)
rewriteLine = Replace(Trim(x),specialDelimiter,",",1,1)
sudoersFile.WriteLine(hostname&","&rewriteLine)
ElseIf instr(x,vbtab) > 0 Then
' Tab delimiter has been found - parsing with tab delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [TAB] found: " & x)
rewriteLine = Replace(Trim(x),vbtab,",",1,1)
' userComponent = trim(left(x,inStr(x,vbtab)-1))
' roleComponent = trim(right(x,len(x)-inStr(x,vbtab)))
sudoersFile.WriteLine(hostname&","&rewriteLine)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
Next
logFile.WriteLine("INFO: " & hostname & ": Finished parsing SUDOERS file." & vbCRLF)
End Function
'************************************************************
'ELEVATE TO SUPER USER (SU)
'************************************************************
Function ElevateSU(supwd)
'wscript.echo "Now running within the elevate SU function."
' Send the 'sudo su -' command.
' (With Linux Ubuntu system running OpenSSH, it is important to send a bare-LF and not a CRLF.)
cmd = "sudo su -" & vbLf
success = ssh.ChannelSendString(channelNum,cmd,"ansi")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while sending the string 'su')")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
End If
' Write out the received text to the log file (this empties the received text buffer)
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
'wscript.echo "Success code for running the sudo su command is: " & success
'wscript.echo " If ElevateSU is set to ERROR then there has been an error: " & ElevateSU
'wscript.echo "We will now check the channel for a line in the received text containing *password*"
' Read until we get the prompt for the password
success = ssh.ChannelReceiveUntilMatch(channelNum,"*password*","ansi",0) 'Set to 0 for case-insensitive
'wscript.echo "Success code for finding *password* was: " & success
' Handle error if a password prompt was not received and check whether it has automatically elevated the prompt w/o asking for password
If (success <> 1) Then
sshReturnedText = ssh.GetReceivedText(channelNum,"ansi")
' If # was returned then the prompt has elevated automatically
if instr(sshReturnedText,"#") > 0 Then
logFile.WriteLine("INFO: Prompt appears to have automatically elevated. Received #. No need to send the password")
logFile.WriteLine("INFO: Following text will provide information regarding an attempt to elevate the prompt. However it does not necessarily mean there is an issue.")
logFile.WriteLine(ssh.LastErrorText)
exit function
' For all other returned prompts, it was probably not elevated so we should not continue processing this server.
else
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while waiting for prompt for password) - this server will not be processed")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(sshReturnedText) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
end if
End If
' Display what we've received so far:
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
' Send the password. Again, make sure it uses a bare-LF and not a CRLF.
password = supwd & vbLf
success = ssh.ChannelSendString(channelNum,password,"ansi")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while trying to send the password)")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
End If
' Read the response until we get the shell prompt (assuming it's successful)
' In my case, the shell prompt is: "root@ubuntu:/home/chilkat# "
' It will be different in your case.
success = ssh.ChannelReceiveUntilMatch(channelNum,"*#*","ansi",0) 'Set to 0 for case-insensitive
If (success <> 1) Then
' Check the last-error information and the session log...
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while waiting for the shell prompt following running 'su') - this can indicate an incorrect password")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
' Check to see what was received.
ElevateSU = "ERROR"
exit function
End If
' Display what we've received so far. This clears the internal receive buffer, which is important.
' After we send the command, we'll be reading until the next command prompt. If the command prompt
' is already in the internal receive buffer, we'll think we're already finished...
logFile.WriteLine("INFO: No errors appear to have been encountered running SU")
logFile.WriteLine("INFO: Now writing ssh connection info...")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi") & vbCRLF)
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
logFile.WriteLine("INFO: Completed writing ssh connection info...")
End Function
'************************************************************
'EXECUTE COMMAND
'************************************************************
Function ExecuteCommand(mycommand)
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Now entering the command function.")
' Send the command.
cmd = mycommand & vbLf
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Now sending command: " & cmd)
success = ssh.ChannelSendString(channelNum,cmd,"ansi")
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Finished running command.")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to run the command: "&mycommand)
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ExecuteCommand = "ERROR"
exit function
End If
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: About to read channel for output.")
' Start reading the data.
' If it pauses for more than 6000ms then we can assume there is no more output.
' Initial timeout is still controlled by IdleTimeoutMs
timeoutOnceReceivingData = 6000
success = ssh.ChannelReadAndPoll(channelNum,timeoutOnceReceivingData)
If (success < 0) Then
if (success = -1) Then logFile.WriteLine("ERROR: " & hostname & ": was unable to process the command: "&mycommand)
if (success = -1) Then logFile.WriteLine("INFO: This may have been caused by a server disconnect or other reason.")
if (success = -2) Then logFile.WriteLine("ERROR: " & hostname & ": The following command was processed and no data was returned within the configured timeout: "&mycommand)
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ExecuteCommand = "ERROR"
exit function
end if
logFile.WriteLine("INFO: we appear to have run this command successfully: "&mycommand)
' Write the data to log files:
storeCmdOutput=(ssh.GetReceivedText(channelNum,"ansi"))
ExecuteCommand = storeCmdOutput
dataFile.WriteLine(storeCmdOutput)
logFile.WriteLine(storeCmdOutput)
End Function
'************************************************************
'Check if a string contains a valid sudoers file
'************************************************************
Function TestStringContainsSudoers(teststring)
TestStringContainsSudoers = "TRUE"
splitstring=split(teststring,vbLF)
teststring=trim(splitstring(1))
'wscript.echo "This is the test string: " & teststring
'Does trimmed string begin with '/'
If (Left(teststring,1) <> "/") Then TestStringContainsSudoers = "FALSE"
If (instr(teststring,"/sudoers")) <1 Then TestStringContainsSudoers = "FALSE"
If TestStringContainsSudoers = "TRUE" Then logFile.WriteLine("INFO: SUDOERS file has been detected in " & teststring & ".")
If TestStringContainsSudoers = "FALSE" Then logFile.WriteLine("INFO: SUDoers file has not been detected in " & teststring & ".")
End Function
'************************************************************
'PARSE A GIVEN CSV FILE INTO ARGUMENTS
'************************************************************
Function CSVParser (CSVDataToProcess)
'Declaring variables for text delimiter and text qualifyer
Dim TextDelimiter, TextQualifyer
'Declaring the variables used in determining action to be taken
Dim ProcessQualifyer, NewRecordCreate
'Declaring variables dealing with input string
Dim CharMaxNumber, CharLocation, CharCurrentVal, CharCounter, CharStorage
'Declaring variables that handle array duties
Dim CSVArray(), CSVArrayCount
'-------------------
'Setting default values for various variables
'<- Text delimiter is a comma
TextDelimiter = ","
'<- Chr(34) is the ascii code for "
TextQualifyer = Chr(34)
'<- Determining how record should be processed
ProcessQualifyer = False
'<- Calculating no. of characters in variable
CharMaxNumber = Len(CSVDataToProcess)
'<- Determining how to handle record at different
' stages of operation
' 0 = Don't create new record
' 1 = Write data to existing record
' 2 = Close record and open new one
NewRecordCreate = 0
'<- Priming the array counter
CSVArrayCount = 0
'<- Initializing the array
Redim Preserve CSVArray(CSVArrayCount)
'<- Record character counter
CharCounter = 0
'-------------------
'Starting the main loop
For CharLocation = 1 to CharMaxNumber
'Retrieving the next character in sequence from CSVDataToProcess
CharCurrentVal = Mid(CSVDataToProcess, CharLocation, 1)
'This will figure out if the record uses a text qualifyer or not
If CharCurrentVal = TextQualifyer And CharCounter = 0 Then
ProcessQualifyer = True
CharCurrentVal = ""
End If
'Advancing the record 'letter count' counter
CharCounter = CharCounter + 1
'Choosing data extraction method (text qualifyer or no text qualifyer)
If ProcessQualifyer = True Then
'This section handles records with a text qualifyer and text delimiter
'It is also handles the special case scenario, where the qualifyer is
'part of the data. In the CSV file, a double quote represents a single
'one ie. "" = "
If Len(CharStorage) <> 0 Then
If CharCurrentVal = TextDelimiter Then
CharStorage = ""
ProcessQualifyer = False
NewRecordCreate = 2
Else
CharStorage = ""
NewRecordCreate = 1
End If
Else
If CharCurrentVal = TextQualifyer Then
CharStorage = CharStorage & CharCurrentVal
NewRecordCreate = 0
Else
NewRecordCreate = 1
End If
End If
'This section handles a regular CSV record.. without the text qualifyer
Else
If CharCurrentVal = TextDelimiter Then
NewRecordCreate = 2
Else
NewRecordCreate = 1
End If
End If
'Writing the data to the array
Select Case NewRecordCreate
'This section just writes the info to the array
Case 1
CSVArray(CSVArrayCount) = CSVArray(CSVArrayCount) & CharCurrentVal
'This section closes the current record and creates a new one
Case 2
CharCounter = 0
CSVArrayCount = CSVArrayCount + 1
Redim Preserve CSVArray(CSVArrayCount)
End Select
Next
'------------------
'Finishing Up
CSVParser = CSVArray
End Function
==========================
And here is a script that can take a manual cut and paste of the commands in the above code, and convert to a CSV format:
Dim xmlDoc, objNodeList, plot, nodeBook
' =======================================================
' Set up files to be written and read
' =======================================================
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Dim fso, logFile, dataFile, x, y
Set fso = CreateObject("Scripting.FileSystemObject")
Set dataFile = fso.CreateTextFile("Windows_Groups.txt", True) 'Output information from commands
Set logFile = fso.CreateTextFile("SessionLog.txt", True) 'Verbose Logging
Set groupFile = fso.CreateTextFile("GroupMemberships.txt", True) 'User group memberships
Set sudoersFile = fso.CreateTextFile("SUDOERS.txt", True) 'Sudoers output
If Err.Number <> 0 Then
wscript.echo "CRASH: Cannot open file for reading. " & Err.Number & " " & Err.Description
wscript.quit
End If
wscript.echo "Please have text files in the specified directory that are named [servername].TXT. Put a line at the beginning of the SUDOERS section" &_
" containing 'SUDOERSSECTION' which will be used to determine when to start processing as SUDOERS"
' =======================================================
' Browse for folder containing XML files
' =======================================================
Const WINDOW_HANDLE = 0
Const BIF_EDITBOX = &H10
Const BIF_NONEWFOLDER = &H0200
Const BIF_RETURNONLYFSDIRS = &H1
Set objShell = CreateObject("Shell.Application")
Set wshShell = CreateObject("WScript.Shell")
'**Browse For Folder To Be Processed
strPrompt = "Please select the folder to process."
intOptions = BIF_RETURNONLYFSDIRS + BIF_NONEWFOLDER + BIF_EDITBOX
strTargetPath = "C:\"
strFolderPath = Browse4Folder(strPrompt, intOptions, strTargetPath)
Set objFolder = fso.GetFolder(strFolderPath)
Set objColFiles = objFolder.Files
For Each file In objColFiles
' Check if we are looking at an XML file
If UCASE(RIGHT(file.Name,3)) <> "TXT" Then
logFile.WriteLine("WRN: Filename " & file.Name & " does not appear to be a TXT file.")
Else
'perform the work
Set serverFile = fso.OpenTextFile(file, ForReading) 'Input information about servers
' =======================================================
' Browse each TXT file for groups and memberships
' =======================================================
serverSource=file.Name
fullServerSource=objFolder&"\"&serverSource
msgbox "Processing " & file.Name
' msgbox fullServerSource
hostname=LEFT(serverSource,INSTR(serverSource,".")-1)
logFile.WriteLine("INFO: Now processing server: " & serverSource)
reachedSudoers = 0
Do Until serverFile.AtEndOfStream
' Parse servers.txt in CSV format and read the arguments
arrFileLine = serverFile.ReadLine
If INSTR(arrFileLine,"SUDOERSSECTION") Then reachedSudoers=1
if reachedSudoers = 0 Then
parseGroupMemberships(arrFileLine)
else
parseSudoers(arrFileLine)
End If
Loop
serverFile.close
End If
Next
'************************************************************
'PARSE OUTPUT OF COMMAND: GROUP MEMBERSHIPS
'EXPORT TO CSV
'************************************************************
Function parseGroupMemberships(x)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Group Membership")
If RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 1 Then
' Delimiter : was found so parsing line - this is common for Red Hat so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
userComponent = trim(left(x,inStr(x,":")-1))
groupComponent = trim(right(x,len(x)-inStr(x,":")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
ElseIf instr(x," ") > 1 Then
'Space Delimiter was found with no colon - this is common for Solaris so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [SPACE] found: " & x)
userComponent = trim(left(x,inStr(x," ")-1))
groupComponent = trim(right(x,len(x)-inStr(x," ")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
logFile.WriteLine("INFO: " & hostname & ": Finished parsing Group Memberships." & vbCRLF)
End Function
'************************************************************
'PARSE OUTPUT OF COMMAND: SU DOERS (PERMISSIONS ASSIGNMENT)
'EXPORT TO CSV
'************************************************************
Function parseSUDoers(x)
logFile.WriteLine("INFO: " & hostname & ": Now parsing SUDOERS file.")
specialDelimiter = " "
If RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,specialDelimiter) > 0 Then
' Special delimiter has been found - parsing with special delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter """ & specialDelimiter & """ found: " & x)
rewriteLine = Replace(Trim(x),specialDelimiter,",",1,1)
sudoersFile.WriteLine(hostname&","&rewriteLine)
ElseIf instr(x,vbtab) > 0 Then
' Tab delimiter has been found - parsing with tab delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [TAB] found: " & x)
rewriteLine = Replace(Trim(x),vbtab,",",1,1)
' userComponent = trim(left(x,inStr(x,vbtab)-1))
' roleComponent = trim(right(x,len(x)-inStr(x,vbtab)))
sudoersFile.WriteLine(hostname&","&rewriteLine)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
logFile.WriteLine("INFO: " & hostname & ": Finished parsing SUDOERS file." & vbCRLF)
End Function
' =======================================================
' Function for browsing for folders
' =======================================================
'**Browse4Folder Function
Function Browse4Folder(strPrompt, intOptions, strRoot)
Dim objFolder, objFolderItem
On Error Resume Next
Set objFolder = objShell.BrowseForFolder(0, strPrompt, intOptions, strRoot)
If (objFolder Is Nothing) Then
Wscript.Quit
End If
Set objFolderItem = objFolder.Self
Browse4Folder = objFolderItem.Path
Set objFolderItem = Nothing
Set objFolder = Nothing
End Function