#!/bin/sh
# Run tcl from users PATH \
exec tclsh "$0" "$@"

# $Id: xscriptd,v 1.23 2008/03/21 16:13:41 bamm Exp $ #

# Copyright (C) 2002-2008 Robert (Bamm) Visscher <bamm@satx.rr.com>
#
# This program is distributed under the terms of version 1.0 of the
# Q Public License.  See LICENSE.QPL for further details.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


set SERVERPORT 7735
# sguild and xscriptd should be ran on the same system.
# If you run these servers on the sensor then set LOCALSENSOR
# to 1. xscriptd will then look for a local "remote" directory.
# Set LOCALSENSOR to 0 if the sensor is on a remote machine. xscriptd
# will then use ssh to get the data back here. Read the INSTALL for more
# info about setting ssh up correctly.
set LOCALSENSOR 0
# 1=on 0=off DEBUG is very chatty.
set DEBUG 1
# Where you want to archive raw file locally when xscripts are requested.
set LOCAL_LOG_DIR /snort_data/archive
# Where xscriptd can find the remote raw files
set REMOTE_LOG_DIR /snort_data/dailylogs
# Path to different execs here. If you need to send flags then put them here also
# set SSH "/usr/bin/ssh -p 1234"
# NOTE: You only need these if you are using remote sensors (ie LOCALSENSOR=0)
set SSH "/usr/bin/ssh"
set SCP "/usr/bin/scp"
# Username to login as on the remote sensors
set RUSER "root"
# If LOCALSENSOR is 0 then TCPDUMP needs to be the path to TCPDUMP on the sensor.
set TCPDUMP "/usr/sbin/tcpdump"
# http://www.circlemud.org/~jelson/software/tcpflow/
set TCPFLOW "/usr/bin/tcpflow -c"
# p0f - (C) Michal Zalewski <lcamtuf@gis.net>, William Stearns <wstearns@pobox.com>
# If you have p0f (a passive OS fingerprinting system) installed, you can have
# xscriptd attempt to use it by enabling it here. Get p0f at http://www.stearns.org
#
# 1=ON, 0=OFF
set P0F 1
# Path the the p0f binary. Switches -q and -s <filename> are appended on exec,
# add any others you may need here.
set P0F_PATH "/usr/sbin/p0f"


################################################
##### Stop editing here
################################################


set VERSION "SGUIL-0.4.1"

#################### PROCS ######################


proc GetRawDataFromLocal { timestamp srcIP srcPort dstIP dstPort proto sensorDir rawDataFileName {socketID {NONE} } } {
  global LOCAL_LOG_DIR REMOTE_LOG_DIR DEBUG TCPDUMP
  set date [lindex $timestamp 0]
  if {$socketID != "NONE"} { puts $socketID "DEBUG"; puts $socketID "Sensor is LOCAL."}
  if { [file exists $REMOTE_LOG_DIR/$date] && [file isdirectory $REMOTE_LOG_DIR/$date] } { 
    if {$DEBUG} {puts "Making a list of local log files"}
    if {$socketID != "NONE"} {puts "Making a list of local log files."}
    if {$socketID != "NONE"} {puts "Looking in $REMOTE_LOG_DIR/$date."}
    if {$DEBUG} {puts "Looking in $REMOTE_LOG_DIR/$date"}
  } else {
    if {$socketID != "NONE"} {puts $socketID "$REMOTE_LOG_DIR/$date does not exist. Make sure log_packets.sh is configured correctly."}
    if {$DEBUG} {puts "No matching log files."}
    return error
  }
  cd $REMOTE_LOG_DIR/$date
  if {$DEBUG} {puts $REMOTE_LOG_DIR/$date}
  if {$socketID != "NONE"} { puts $socketID "Making a list of local log files in $REMOTE_LOG_DIR/$date."}
  foreach logFile [glob -nocomplain snort.log.*] {
    lappend logFileTimes [lindex [split $logFile .] 2]
  }
  if { ! [info exists logFileTimes] } {
    if {$DEBUG} {puts "No matching log files."}
    if {$socketID != "NONE"} { puts $socketID "No matching log files."}
    return error
  }
  set sLogFileTimes [lsort -decreasing $logFileTimes]
  if {$DEBUG} {puts $sLogFileTimes}
  if {$socketID != "NONE"} { 
    puts $socketID "Available log files:"
    puts $socketID "$sLogFileTimes"
  }
  set eventTime [clock scan $timestamp -gmt true]
  # The first file we find with a time >= to ours should have our packets.
  foreach logFileTime $sLogFileTimes {
    if { $eventTime >= $logFileTime } {
      set logFileName "snort.log.$logFileTime"
      break
    }
  }
  if { ![info exists logFileName] } {
    return error
  }
  if {$DEBUG} { puts "Creating unique data file." }
  if {$socketID != "NONE"} { puts $socketID "Creating unique data file." }
  if {$proto == "1"} {
    set tcpdumpFilter "host $srcIP and host $dstIP and proto $proto"
  } else {
    set tcpdumpFilter "host $srcIP and host $dstIP and port $srcPort and port $dstPort and proto $proto"
  }
  exec $TCPDUMP -r $REMOTE_LOG_DIR/$date/$logFileName -w $sensorDir/$rawDataFileName $tcpdumpFilter
  return $sensorDir/$rawDataFileName
}
proc GetRawDataFromSensor { sensor timestamp srcIP srcPort dstIP dstPort proto sensorDir rawDataFileName {socketID {NONE} } } {
  global LOCAL_LOG_DIR REMOTE_LOG_DIR SSH SCP RUSER TCPDUMP DEBUG
  set date [lindex $timestamp 0]
  if {$DEBUG} {puts "Getting a list of raw data files from $sensor."}
  if {$socketID != "NONE"} { 
    puts $socketID "DEBUG"
    puts $socketID "Sensors are REMOTE, using ssh for accessing system."
    puts $socketID "Getting a list of raw data files from $sensor."
  }
  if {$DEBUG} {puts "Looking on $sensor in $REMOTE_LOG_DIR/$date."}
  if {$socketID != "NONE"} { puts $socketID "Looking on $sensor in $REMOTE_LOG_DIR/$date."}
  # First we need to get a list of raw data files on the sensor so we can
  # determine what file to pull the data from. For now we are using ssh to
  # do this. Logfiles are in the format of snort.log.unixtime.
  set sshID [open "| $SSH -l $RUSER -v $sensor cd $REMOTE_LOG_DIR/$date; ls snort.log.*" r]
  if {$socketID != "NONE"} { puts $socketID "Remote log files:" }
  while { [gets $sshID data] >= 0 } {
    if {$DEBUG} {puts $data}
    if {$socketID != "NONE"} { puts $socketID $data}
    lappend logFileTimes [lindex [split $data .] 2]
  }
  catch {close $sshID} closeError
  if { ! [info exists logFileTimes] } {
    if {$DEBUG} {puts "No matching log files."}
    if {$socketID != "NONE"} { puts $socketID "No matching log files." }
    return error
  }
  set sLogFileTimes [lsort -decreasing $logFileTimes]
  set eventTime [clock scan $timestamp -gmt true]
  # The first file we find with a time >= to ours should have our packets.
  foreach logFileTime $sLogFileTimes {
    if { $eventTime >= $logFileTime } {
      set logFileName "snort.log.$logFileTime"
      break
    }
  }
  if { ![info exists logFileName] } {
    return error
  }
  if {$DEBUG} { puts "Creating unique data file on $sensor." } 
  if {$socketID != "NONE"} { puts $socketID "Creating unique data file on $sensor." }
  if { $proto == "1" } {
      set tcpdumpFilter "host $srcIP and host $dstIP and proto 1"
  } else {
      set tcpdumpFilter "host $srcIP and host $dstIP and port $srcPort and port $dstPort and proto $proto"
  }  
  eval exec $SSH -l $RUSER $sensor $TCPDUMP -r $REMOTE_LOG_DIR/$date/$logFileName -w /tmp/$rawDataFileName $tcpdumpFilter

  if {$DEBUG} { puts "Copying the file from $sensor." }
  if {$socketID != "NONE"} { puts $socketID "Copying the file from $sensor." }
  eval exec $SCP $RUSER@$sensor:/tmp/$rawDataFileName $sensorDir
  if {$DEBUG} { puts "Removing file from $sensor." }
  if {$socketID != "NONE"} { puts $socketID "Removing file from $sensor." }
  eval exec $SSH -l $RUSER $sensor rm -f /tmp/$rawDataFileName
  return $sensorDir/$rawDataFileName
}

proc GetHostbyAddr { ip } {
    if [catch {host_info official_name $ip} hostname] {
    set hostname "Unknown"
  }
  return $hostname
}

proc TcpFlowFormat { srcIP srcPort dstIP dstPort } {
  set tmpSrcIP [split $srcIP .]
  set tmpDstIP [split $dstIP .]
  set tmpData [eval format "%03i.%03i.%03i.%03i.%05i-%03i.%03i.%03i.%03i.%05i" $tmpSrcIP $srcPort $tmpDstIP $dstPort]
  return $tmpData
}
proc EtherealRequest { socketID sensor eventID timestamp srcIP srcPort dstIP dstPort proto force } {
  global DEBUG LOCAL_LOG_DIR REMOTE_LOG_DIR LOCALSENSOR
  set date [lindex $timestamp 0]
  # Check to make sure our dirs exists. We use <rootdir>/date/sensorName/*.raw
  set dateDir "$LOCAL_LOG_DIR/$date"
  if { ! [file exists $dateDir] } {
    file mkdir $dateDir
  }
  set sensorDir "$dateDir/$sensor"
  if { ![file exists $sensorDir] } {
    file mkdir $sensorDir
  }
  # We always make the highest port the apparent source. This way we don't store
  # two copies of the same raw data.
  if { $proto != "1" } {
    if { $srcPort > $dstPort } {
      set rawDataFileName "${srcIP}:${srcPort}_${dstIP}:${dstPort}-${proto}.raw"
    } else {
      set rawDataFileName "${dstIP}:${dstPort}_${srcIP}:${srcPort}-${proto}.raw"
    }
  } else {
    set rawDataFileName "${srcIP}:${dstIP}-${proto}.raw"
  }
  if { ! [file exists $sensorDir/$rawDataFileName] || $force } {
    if {$LOCALSENSOR} {
      set rawDataFile [GetRawDataFromLocal $timestamp $srcIP $srcPort $dstIP $dstPort $proto $sensorDir $rawDataFileName]
    } else {
set rawDataFile [GetRawDataFromSensor $sensor $timestamp $srcIP $srcPort $dstIP $dstPort $proto $sensorDir $rawDataFileName]
    }
  } else {
    set rawDataFile $sensorDir/$rawDataFileName
  }
  if {$rawDataFile == "error" } {
    puts $socketID "ErrorMessage \{Error getting raw data from sensor.\}"
    close $socketID
    return
  }
  # Send client the raw data file to load into ethereal
  set rawFileID [open $rawDataFile r]
  fconfigure $rawFileID -translation binary
  fconfigure $socketID -translation binary
  if { [catch {fcopy $rawFileID $socketID -command [list CopyDone $rawFileID $socketID]} fCopyError] } {
    if {$DEBUG} {puts "Error: $fCopyError"}
  }
  #close $rawFileID
  #catch { close $socketID} closeError
  if {$DEBUG} {puts "$socketID closed."}
}
proc CopyDone { rawFileID socketID bytes {error {}} } {
  global DEBUG
  close $rawFileID
  close $socketID
  if {$DEBUG} {puts "Bytes copied: $bytes"}
}
proc XscriptRequest { socketID sensor eventID timestamp srcIP srcPort dstIP dstPort force } {
  global TCPFLOW DEBUG LOCAL_LOG_DIR REMOTE_LOG_DIR LOCALSENSOR
  global P0F P0F_PATH validSockets
  set date [lindex $timestamp 0]
  # Check to make sure our dirs exists. We use <rootdir>/date/sensorName/*.raw
  set dateDir "$LOCAL_LOG_DIR/$date"
  if { ! [file exists $dateDir] } {
    file mkdir $dateDir
  }
  set sensorDir "$dateDir/$sensor"
  if { ![file exists $sensorDir] } {
    file mkdir $sensorDir
  }
  # We always make the highest port the apparent source. This way we don't store
  # two copies of the same raw data.
  if { $srcPort > $dstPort } {
    set rawDataFileName "${srcIP}:${srcPort}_${dstIP}:${dstPort}-6.raw"
  } else {
    set rawDataFileName "${dstIP}:${dstPort}_${srcIP}:${srcPort}-6.raw"
  }
  if { ! [file exists $sensorDir/$rawDataFileName] || $force } {
    if {$LOCALSENSOR} {
      set rawDataFile [GetRawDataFromLocal $timestamp $srcIP $srcPort $dstIP $dstPort 6 $sensorDir $rawDataFileName $socketID]
    } else {
      set rawDataFile [GetRawDataFromSensor $sensor $timestamp $srcIP $srcPort $dstIP $dstPort 6 $sensorDir $rawDataFileName $socketID]
    }
  } else {
    puts $socketID "DEBUG"
    puts $socketID "Using archived data: $sensorDir/$rawDataFileName"
    set rawDataFile $sensorDir/$rawDataFileName
  }
  if {$rawDataFile == "error" } {
    puts $socketID "ERROR"
    puts $socketID "Error getting raw data from sensor."
    close $socketID
    set validSockets [ldelete $validSockets $socketID]
    return
  }
  # Check for P0F if enabled
  if {$P0F} {
    if { ![file exists $P0F_PATH] || ![file executable $P0F_PATH] } {
      puts $socketID "Cannot find p0f in: $P0F_PATH"
      puts $socketID "OS fingerprint has been disabled"
      set P0F 0
    }
  }
  set NODATAFLAG 1
  # We don't have a really good way for make xscripts yet and are unable
  # to figure out the true src. So we assume the low port was the server
  # port.
  if { $srcPort < $dstPort } {
    set tmpSrcIP $dstIP
    set tmpSrcPort $dstPort
    set dstIP $srcIP
    set dstPort $srcPort
    set srcIP $tmpSrcIP
    set srcPort $tmpSrcPort
  }
  set srcMask [TcpFlowFormat $srcIP $srcPort $dstIP $dstPort]
  set dstMask [TcpFlowFormat $dstIP $dstPort $srcIP $srcPort]

  puts $socketID "HDR"
  puts $socketID "Sensor Name:\t$sensor"
  puts $socketID "Timestamp:\t$timestamp"
  puts $socketID "Connection ID:\t$eventID"
  puts $socketID "Src IP:\t\t$srcIP\t([GetHostbyAddr $srcIP])"
  puts $socketID "Dst IP:\t\t$dstIP\t([GetHostbyAddr $dstIP])"
  puts $socketID "Src Port:\t\t$srcPort"
  puts $socketID "Dst Port:\t\t$dstPort"
  if {$P0F} {
    #set p0fID [open "| $P0F_PATH -s $rawDataFile 2>@ stdout"]
    set p0fID [open "| $P0F_PATH -q -s $rawDataFile"]
    while { [gets $p0fID data] >= 0 } {
      puts $socketID "OS Fingerprint:\t$data"
    }
    catch {close $p0fID} closeError
  }
  puts $socketID "================================================================================="
  set tcpflowID [open "| $TCPFLOW -r $rawDataFile"]
  set state SRC
  while { [gets $tcpflowID data] >= 0 } {
    set NODATAFLAG 0
    if { [regsub ^$srcMask:\  $data {} data] > 0 } {
      set state SRC
    } elseif { [regsub ^$dstMask:\  $data {} data] > 0 } {
      set state DST
    }
    if {$DEBUG} { puts "Sending Client $socketID: $state" }
    puts $socketID "$state"
    if {$DEBUG} { puts "Sending Client $socketID: $data" }
    puts $socketID "$data"
  }
  close $tcpflowID
  if {$NODATAFLAG} {
    puts $socketID "No Data Sent."
  }
  puts $socketID "DONE"
  close $socketID
  set validSockets [ldelete $validSockets $socketID]
}
proc ClientConnect { socketID IPAddr port } {
  global DEBUG VERSION validSockets
  global OPENSSL KEY PEM
  if {$DEBUG} { puts "Client Connect: $IPAddr $port $socketID" }
  fconfigure $socketID -buffering line
  # Do version checks
  if [catch {puts $socketID "$VERSION"} sendError ] {
    catch {close $socketID} closeError
    set validSockets [ldelete $validSockets $socketID]
    if {$DEBUG} { puts "ERROR: Socket $socketID ($IPAddr:$port) closed. $sendError" }
    return
  }
  if [catch {gets $socketID} clientVersion] {
    if {$DEBUG} {puts "$ERROR: $clientVersion : Socket $socketID ($IPAddr:$port) closed."}
    catch {close $socketID} closeError
    set validSockets [ldelete $validSockets $socketID]
    return
  }
  if { $clientVersion != $VERSION } {
    catch {close $socketID} tmpError
    set validSockets [ldelete $validSockets $socketID]
    if {$DEBUG} {puts "ERROR: Client connect denied - mismatched versions" }
    if {$DEBUG} {puts "CLIENT VERSION: $clientVersion" }
    if {$DEBUG} {puts "SERVER VERSION: $VERSION" }
    catch {close $socketID} closeError
    set validSockets [ldelete $validSockets $socketID]
    return
  }
  if {$OPENSSL} {
    tls::import $socketID -server true -keyfile $KEY -certfile $PEM -ssl2 false -ssl3 false -tls1 true
    fileevent $socketID readable [list HandShake $socketID ClientCmdRcvd]
  } else {
    fileevent $socketID readable [list ClientCmdRcvd $socketID]
  }
}
proc GetRandAlphaNumInt {} {
  set x [expr [random 74] + 48]
  while {!($x >= 48 && $x <= 57) && !($x >= 65 && $x <= 90)\
      && !($x >= 97 && $x <= 122)} {
     set x [expr [random 74] + 48]
  }
  return $x
}
proc HandShake { socketID cmd } {
  global validSockets
  puts "HANDSHAKE"
  if {[eof $socketID]} {
    close $socketID
    set validSockets [ldelete $validSockets $socketID]
  } elseif { [catch {tls::handshake $socketID} results] } {
    puts "ERROR: $results"
    close $socketID
    set validSockets [ldelete $validSockets $socketID]
  } elseif {$results == 1} {
    puts "Handshake complete for $socketID"
    fileevent $socketID readable [list $cmd $socketID]
  }
}
proc ClientCmdRcvd { socketID } {
  global DEBUG validSockets
  if { [eof $socketID] || [catch {gets $socketID data}] } {
    close $socketID
    set validSockets [ldelete $validSockets $socketID]
    if {$DEBUG} {puts "Socket $socketID closed."}
  } else {
    if {$DEBUG} {puts "Client Command: $data"}
    set clientCmd [lindex $data 0]
    if { $clientCmd != "ValidateUser" } {
      if { [lsearch -exact $validSockets $socketID] < 0 } {
        catch {puts $socketID\
         "InfoMessage {Client does not appear to be logged in. Please exit and log back in.}"} tmpError
        return
      }
    }
    switch -exact $clientCmd {
      XscriptRequest { eval $clientCmd $socketID [lrange $data 1 end] }
      EtherealRequest { eval $clientCmd $socketID [lrange $data 1 end] }
      ValidateUser { ValidateUser $socketID [lindex $data 1] }
      default { puts "Unrecognized command from $socketID: $data" }
    }
  }
}
proc ValidateUser { socketID username } {
  global USERS_FILE validSockets DEBUG
  fileevent $socketID readable {}
  fconfigure $socketID -buffering line
  if { ![file exists $USERS_FILE] } {
    puts "Fatal Error! Cannot access $USERS_FILE."
    exit 1
  }
  set VALID 0
  set nonce [format "%c%c%c" [GetRandAlphaNumInt] [GetRandAlphaNumInt] [GetRandAlphaNumInt] ]
  for_file line $USERS_FILE {
    if { ![regexp ^# $line] && ![regexp ^$ $line] } {
      # Probably should check for corrupted info here
      set tmpUserName [ctoken line "(.)(.)"]
      set tmpSaltHash [ctoken line "(.)(.)"]
      if { $tmpUserName == $username } {
        set VALID 1
        set tmpSalt [string range $tmpSaltHash 0 1]
        set finalCheck [::sha1::sha1 "${nonce}${tmpSaltHash}"]
        break
      }
    }
  }
  if {$VALID} {
    puts $socketID "$tmpSalt $nonce"
    set finalClient [gets $socketID]
    if { $finalClient == $finalCheck } {
      lappend validSockets $socketID
    } else {
      set validSockets [ldelete $validSockets $socketID]
      catch {puts $socketID "UserID INVALID"} tmpError
      catch {close $socketID} closeError
      if {$DEBUG} {puts "Unable to validate user $username."}
      return
    }
  } else {
    #Not a valid user. Make up info.
    set tmpSalt [format "%c%c" [GetRandAlphaNumInt] [GetRandAlphaNumInt] ]
    set finalCheck [::sha1::sha1 "${nonce}${tmpSalt}"]
    puts $socketID "$tmpSalt $nonce"
    set finalClient [gets $socketID]
    set validSockets [ldelete $validSockets $socketID]
    catch {puts $socketID "UserID INVALID"} tmpError
    catch {close $socketID} closeError
    if {$DEBUG} {puts "Unable to validate user $username."}
    return
  }
  puts $socketID "UserID VALID"
  fileevent $socketID readable [list ClientCmdRcvd $socketID]
}

proc Daemonize {} {
  global PID_FILE DEBUG
  set DEBUG 0
  set childPID [fork]
  # Parent exits.
  if { $childPID == 0 } { exit }
  id process group set
  if {[fork]} {exit 0}
  set PID [id process]
  if { ![info exists PID_FILE] } { set PID_FILE "/var/run/xscriptd.pid" }
  set PID_DIR [file dirname $PID_FILE]
  if { ![file exists $PID_DIR] || ![file isdirectory $PID_DIR] || ![file writable $PID_DIR] } {
    puts "ERROR: Directory $PID_DIR does not exists or is not writable."
    puts "Process ID will not be written to file."
  } else {
    set pidFileID [open $PID_FILE w]
    puts $pidFileID $PID
    close $pidFileID
  } 
  signal trap {QUIT TERM} CleanExit
}
proc DisplayUsage { cmdName } {
  puts "Usage: $cmdName \[-D\] \[-o\] \[-O <filename>\] \[-P <filename>\]"
  puts "  -P <filename>: Name of file to write the PID to."
  puts "                 Default is /var/run/xscriptd.pid"
  puts "  -o Enable OpenSSL"
  puts "  -O <filename>: Enable OpenSSL using PATH to tls (tcl openssl) lib (libtls1.4.so)"
  puts "  -C <directory>: Directory that contains sguild.pem and sguild.key"
  puts "  -D Runs xscriptd in daemon mode."
  exit 1 
}
#
# ldelete: Delete item from a list
#
proc ldelete { list value } {
  set ix [lsearch -exact $list $value]
  if {$ix >= 0} {
    return [lreplace $list $ix $ix]
  } else {
    return $list
  }
}

proc CleanExit {} {
  global PID_FILE 
  if { [info exists PID_FILE] && [file exists $PID_FILE] } {
    if [catch {file delete -force $PID_FILE} delError] {
      puts "ERROR: $delError"
    }
  }
  puts "Exiting..." 
  exit
} 


#################### MAIN #######################

# Load extended tcl
if [catch {package require Tclx} tclxVersion] {
  puts "ERROR: The tclx extension does NOT appear to be installed on this sysem."
  puts "Extended tcl (tclx) is available as a port/package for most linux and BSD systems."
  exit
}
# Load sha1 from tcllib
if [catch {package require sha1} sha1Version] {
  puts "ERROR: The sha1 package does NOT appear to be installed on this sysem."
  puts "The sha1 package is part of the tcllib extension. A port/package is available for most linux and BSD systems."
  exit
}

# GetOpts
set state flag
foreach arg $argv {
  switch -- $state {
    flag {
      switch -glob -- $arg {
        -- { set state flag }
        -D { set DAEMON 1 }
        -P { set state pid_file }
        -O { set state openssl }
        -o { set OPENSSL 1 }
        -C { set state certs }
        -u { set state users_file }
        default { DisplayUsage $argv0 }
      }
    }
    pid_file { set PID_FILE $arg; set state flag }
    openssl { set OPENSSL 1; set TLS_PATH $arg; set state flag }
    certs { set CERTS_PATH $arg; set state flag }
    users_file { set USERS_FILE $arg; set state flag }
    default { DisplayUsage $argv0 }
  }
}
# Check openssl requirements if enabled
if { [info exists OPENSSL] && $OPENSSL } {
  set VERSION "$VERSION OPENSSL ENABLED"
  # Need a path to the tls libs
  if { [info exists TLS_PATH] } {
    if [catch {load $TLS_PATH} tlsError] {
      puts "ERROR: Unable to load tls libs ($TLS_PATH): $tlsError"
      DisplayUsage $argv0
    }
  }
  package require tls
  # Check for certs
  if {![info exists CERTS_PATH]} {
    set CERTS_PATH /etc/sguild/certs
  }
  if {![file exists $CERTS_PATH] || ![file isdirectory $CERTS_PATH]} {
    puts "ERROR: $CERTS_PATH does not exist or is not a directory"
    DisplayUsage $argv0
  }
  # Need sguild.key and sguild.pem
  set PEM [file join $CERTS_PATH sguild.pem]
  set KEY [file join $CERTS_PATH sguild.key]
  if {![file exists $PEM] || ![file readable $PEM] } {
    puts "ERROR: $PEM does not exist or is not readable"
    DisplayUsage $argv0
  }
  if {![file exists $KEY] || ![file readable $KEY] } {
    puts "ERROR: $KEY does not exist or is not readable"
    DisplayUsage $argv0
  }
  # If we get this far we should be good.
} else {
  set VERSION "$VERSION OPENSSL DISABLED"
  set OPENSSL 0
}
# Check for a valid USERS file
if { ![info exists USERS_FILE] } {
  # No users file was specified. Go with the defaults
  if { [file exists /etc/sguild/sguild.users] } {
    set USERS_FILE "/etc/sguild/sguild.users"
  } elseif { [file exists ./sguild.users] } {
    set USERS_FILE "./sguild.users"
  } else {
      puts "ERROR: Could not find a sguild.users file."
      puts "       Checked in ./ and /etc/sguild/"
      DisplayUsage $argv0
  }
} else {
  if { ![file exists $USERS_FILE] } {
      puts "ERROR: $USERS_FILE does not exist"
      DisplayUsage $argv0
  }
}
# zero out validSockets
set validSockets ""
if {[info exists DAEMON] && $DAEMON} { Daemonize }

if [catch {socket -server ClientConnect $SERVERPORT} serverSocket] {
  puts "ERROR: $sensorSocket"
}
puts "Xscriptd Version $VERSION Initialized."
vwait FOREVERBABY

