#!/bin/sh # # rkhunter -- Scan the system for rootkits and other known security issues. # # Copyright (c) 2003-2007, Michael Boelen ( michael AT rootkit DOT nl ) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # 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. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. # # # Unfortunately we must do some O/S checks at the very beginning. # Otherwise SunOS will complain about some of the ksh/bash syntax. # OPERATING_SYSTEM=`uname 2>/dev/null` if [ "${OPERATING_SYSTEM}" = "SunOS" ]; then if [ -z "$RANDOM" ]; then if [ -n "`which bash 2>/dev/null | grep '^/'`" ]; then exec bash $0 $* else exec ksh $0 $* fi exit 0 fi fi if [ "$1" = "--debug" ]; then DEBUG_FILE=`mktemp -t rkhunter-debug.XXXXXXXXXX` if [ -e "$DEBUG_FILE" ]; then if [ -f "$DEBUG_FILE" -a ! -h "$DEBUG_FILE" ]; then rm -f $DEBUG_FILE >/dev/null 2>&1 else echo "Cannot use '--debug' option. $DEBUG_FILE already exists, but it is not a file." exit 1 fi fi DEBUG_OPT=1 exec 1>$DEBUG_FILE 2>&1 set -x else DEBUG_OPT=0 fi case "${OPERATING_SYSTEM}" in AIX|OpenBSD|SunOS|IRIX*) # What is the default shell? if print >/dev/null 2>&1; then alias echo='print' ECHOOPT="--" MYSHELL=ksh elif [ "${OPERATING_SYSTEM}" = "IRIX" -o "${OPERATING_SYSTEM}" = "IRIX64" ]; then ECHOOPT="" MYSHELL=sh else ECHOOPT="-e" MYSHELL=bash fi ;; *) ECHOOPT="-e" # # We want to get the actual shell used by this program, and # so we need to test /bin/sh. # MYSHELL=/bin/sh test -h ${MYSHELL} && MYSHELL=`readlink ${MYSHELL} 2>/dev/null` MYSHELL=`basename ${MYSHELL} 2>/dev/null` if [ -z "${MYSHELL}" ]; then MYSHELL=bash elif [ "${MYSHELL}" = "dash" -o "${MYSHELL}" = "ash" ]; then ECHOOPT="" fi ;; esac ###################################################################### # # Global function definitions # ###################################################################### display() { # # This function is used to display text messages on to the # users screen, as well as in to the log file. The same # message is written to both. However, the screen may have # a coloured result (green for good, red for bad, etc), and # the log file will have the time prefixed to the message and, # optionally, additional information messages after the main # message. All the messages are indexed in the language file. # # Syntax: display --to --type # [--screen-indent ] [--log-indent ] # [--nl []] [--nl-after] [--log-nl] # [--result --color ] # [optional message arguments] # # where the destination can be one of SCREEN, LOG or SCREEN+LOG. # The type can be one of PLAIN, INFO or WARNING. # The language file will have all the current values. # # The --screen-indent and --log-indent options are used to # forcibly indent a message. # The --nl option causes a blank-line to be output before the # message both on the screen and in the log file. A following # number can be used to indicate how many blank lines should # be displayed on the screen. # The --log-nl option outputs a blank line only in the log file. # The --nl-after option outputs a blank line on the screen after # the message. # # # We first initialize some variables and then # process the switches used. # WARN_MSG=0; NL=0; NLAFTER=0; LOGINDENT=0; SCREENINDENT=0 LOGNL=0 WRITETO=''; TYPE=''; RESULT=''; COLOR=''; MSG='' LINE1=''; LOGLINE1=''; SPACES='' DISPLAY_LINE="display $*" if [ $# -le 0 ]; then echo "Error: Invalid display call - no arguments given" return fi while [ $# -ge 1 ]; do case "$1" in --to) case "$2" in SCREEN|LOG|SCREEN+LOG) WRITETO=$2 ;; *) echo "Error: Invalid display destination: $2 Display line: ${DISPLAY_LINE}" return ;; esac shift ;; --type) TYPE=`eval echo "\\$MSG_TYPE_$2"` if [ -z "${TYPE}" -a "$2" != "PLAIN" ]; then if [ $RKHLANGUPDT -eq 0 ]; then echo "Error: Invalid display type: $2 Display line: ${DISPLAY_LINE}" return fi fi test "$2" = "WARNING" && WARN_MSG=1 shift ;; --result) RESULT=`eval echo "\\$MSG_RESULT_$2"` if [ -z "${RESULT}" ]; then if [ $RKHLANGUPDT -eq 0 ]; then echo "Error: Invalid display result: $2 Display line: ${DISPLAY_LINE}" return fi fi shift ;; --color) if [ $COLORS -eq 1 ]; then test -n "$2" && COLOR=`eval "echo \\${$2}"` if [ -z "${COLOR}" ]; then echo "Error: Invalid display color: $2 Display line: ${DISPLAY_LINE}" return fi fi shift ;; --log-indent) LOGINDENT=$2 if [ -z "${LOGINDENT}" ]; then echo "Error: No --log-indent value given. Display line: ${DISPLAY_LINE}" return elif [ -z "`echo ${LOGINDENT} | grep '^[0-9]*$'`" ]; then echo "Error: Invalid --log-indent value given: $2 Display line: ${DISPLAY_LINE}" return fi shift ;; --screen-indent) SCREENINDENT=$2 if [ -z "${SCREENINDENT}" ]; then echo "Error: No --screen-indent value given. Display line: ${DISPLAY_LINE}" return elif [ -z "`echo ${SCREENINDENT} | grep '^[0-9]*$'`" ]; then echo "Error: Invalid --screen-indent value given: $2 Display line: ${DISPLAY_LINE}" return fi shift ;; --nl) NL=1 case "$2" in [0-9]) NL=$2 shift ;; esac ;; --log-nl) LOGNL=1 ;; --nl-after) NLAFTER=1 ;; -*) echo "Error: Invalid display option given: $1 Display line: ${DISPLAY_LINE}" return ;; *) MSG=$1 shift break ;; esac shift done # # Before anything we must record if this is a warning message. # test $WARN_MSG -eq 1 && WARNING_COUNT=`expr ${WARNING_COUNT} + 1` # # For simplicity we now set variables as to whether the output # goes to the screen and/or the log file. In some cases we do # not need to output anything, and so can just return. # if [ $NOLOG -eq 1 ]; then test "${WRITETO}" = "LOG" && return test "${WRITETO}" = "SCREEN+LOG" && WRITETO="SCREEN" fi if [ $NOTTY -eq 1 ]; then test "${WRITETO}" = "SCREEN" && return test "${WRITETO}" = "SCREEN+LOG" && WRITETO="LOG" fi test "${WRITETO}" = "SCREEN" -o "${WRITETO}" = "SCREEN+LOG" && WRITETOTTY=1 || WRITETOTTY=0 test "${WRITETO}" = "LOG" -o "${WRITETO}" = "SCREEN+LOG" && WRITETOLOG=1 || WRITETOLOG=0 # # Now check that the options we have been given make sense. # if [ $WRITETOTTY -eq 0 -a $WRITETOLOG -eq 0 ]; then echo "Error: Invalid display destination: Display line: ${DISPLAY_LINE}" return elif [ $WRITETOTTY -eq 1 -a $COLORS -eq 1 -a -n "${RESULT}" -a -z "${COLOR}" ]; then echo "Error: Invalid display - no color given: Display line: ${DISPLAY_LINE}" return fi # # We set the variable LINE1 to contain the first line of the message. # For the log file we use the variable LOGLINE1. We also set # where the language file is located. If a message cannot be found # in the file, then we look in the English file. This will allow RKH # to still work even when the language files change. # LANG_FILE="${DB_PATH}/i18n/${LANGUAGE}" if [ -n "${MSG}" ]; then LINE1=`grep "^${MSG}:" ${LANG_FILE} 2>/dev/null | head -n 1 | cut -d: -f2-` if [ -z "${LINE1}" ]; then LANG_FILE="${DB_PATH}/i18n/en" LINE1=`grep "^${MSG}:" ${LANG_FILE} 2>/dev/null | head -n 1 | cut -d: -f2-` if [ -z "${LINE1}" ]; then echo "Error: Invalid display - language keyword cannot be found: Display line: ${DISPLAY_LINE}" return fi else LINE1=`echo "${LINE1}" | sed -e 's/\`/\\\\\`/g'` fi test -n "${LINE1}" && LINE1=`eval "echo \"${LINE1}\" | sed -e 's/;/\\;/g'"` fi # # At this point LINE1 is the text of the message. We have to # see if the message is to be indented, and must prefix the # time to log file messages. We must do the log file first # because it uses LINE1. # if [ $WRITETOLOG -eq 1 ]; then LOGLINE1=`date '+[%H:%M:%S]'` test $NL -gt 0 -o $LOGNL -eq 1 && echo "${LOGLINE1}" >>${RKHLOGFILE} if [ -n "${TYPE}" ]; then LOGLINE1="${LOGLINE1} ${TYPE}: ${LINE1}" else test $LOGINDENT -gt 0 && SPACES=`echo "${BLANK_LINE}" | cut -c1-$LOGINDENT` LOGLINE1="${LOGLINE1} ${SPACES}${LINE1}" fi fi if [ $WRITETOTTY -eq 1 -a $SCREENINDENT -gt 0 ]; then SPACES=`echo "${BLANK_LINE}" | cut -c1-$SCREENINDENT` LINE1="${SPACES}${LINE1}" fi # # We now check to see if a result is to be output. If it is, # then we need to space-out the line and color the result. # if [ -n "${RESULT}" ]; then if [ $WRITETOTTY -eq 1 ]; then LINE1_NUM=`echo "${LINE1}" | wc -c | tr -d ' '` NUM_SPACES=`expr 62 - ${LINE1_NUM}` test $NUM_SPACES -lt 1 && NUM_SPACES=1 if [ $COLORS -eq 0 ]; then SPACES=`echo "${BLANK_LINE}" | cut -c1-$NUM_SPACES` LINE1="${LINE1}${SPACES}[ ${RESULT} ]" else LINE1="${LINE1}\033[${NUM_SPACES}C[ ${COLOR}${RESULT}${NORMAL} ]" fi fi if [ $WRITETOLOG -eq 1 ]; then LOGLINE1_NUM=`echo "${LOGLINE1}" | wc -c | tr -d ' '` NUM_SPACES=`expr 62 - ${LOGLINE1_NUM}` test $NUM_SPACES -lt 1 && NUM_SPACES=1 SPACES=`echo "${BLANK_LINE}" | cut -c1-$NUM_SPACES` LOGLINE1="${LOGLINE1}${SPACES}[ ${RESULT} ]" fi elif [ $WRITETOTTY -eq 1 -a -n "${COLOR}" ]; then LINE1="${COLOR}${LINE1}${NORMAL}" fi # # We can now output the message. We start with any required blank # lines, and then the first line. If this is a warning message we # write to the log file any additional lines. # if [ $WRITETOTTY -eq 1 ]; then NLLOOP=$NL while test $NLLOOP -gt 0; do echo "" NLLOOP=`expr ${NLLOOP} - 1` done echo $ECHOOPT "${LINE1}" fi if [ $WRITETOLOG -eq 1 ]; then echo $ECHOOPT "${LOGLINE1}" >>${RKHLOGFILE} if [ $WARN_MSG -eq 1 ]; then test $SHOWWARNINGSONLY -eq 1 && echo $ECHOOPT "${LOGLINE1}" | cut -d' ' -f2- LINE1=1 OLDIFS="${IFS}" IFS=$IFSNL for LOGLINE1 in `grep "^${MSG}:" ${LANG_FILE} 2>/dev/null | cut -d: -f2-`; do if [ $LINE1 -eq 1 ]; then LINE1=0 continue else test $SHOWWARNINGSONLY -eq 1 && echo $ECHOOPT " ${LOGLINE1}" echo $ECHOOPT " ${LOGLINE1}" >>${RKHLOGFILE} fi done IFS="${OLDIFS}" elif [ $SHOWWARNINGSONLY -eq 1 -a -n "`echo \"${LOGLINE1}\" | grep '^\[[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\] '`" ]; then echo $ECHOOPT "${LOGLINE1}" | cut -d' ' -f2- fi fi # # Output a final blank line if requested to do so. # test $WRITETOTTY -eq 1 -a $NLAFTER -eq 1 && echo "" return } keypresspause() { # # This function will display a prompt message to the user. # if [ $SKIP_KEY_PRESS -eq 0 -a $QUIET -eq 0 ]; then display --to SCREEN --type PLAIN --nl PRESSENTER read RKHTMPVAR test "${RKHTMPVAR}" = "s" -o "${RKHTMPVAR}" = "S" && SKIP_KEY_PRESS=1 fi return } get_option() { # # This function is used to process configuration file options. # # Syntax: get_option