From: Richard Lewis <richard.lewis.debian@googlemail.com>
Date: Sun, 20 Oct 2024 21:27:00 +0100
Subject: chkrootkit: top-level

Improvements for chkrootkit: top-level only:
a) Reindent, remove trialing spaces, use $(...) not `...`, quote variables, ensure global variables like $QUIET are defined
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Sat Feb 18 20:13:12 2023 +0000  and subsequently

b) Define egrep using ${grep}
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Sat Feb 18 20:30:05 2023 +0000

c) Simplify loc()
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date: Sun, 7 Jul 2024 18:17:18 +0100

d) getCMD - do not look at running processes if -r is given
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>

e) Simplify exclude_fstype()
     Originally from: Francois Marier <francois@debian.org>
     Date: Sun, 9 Jul 2017 18:42:55 +0200
     Rewritten by: Richard Lewis <richard.lewis.debian@googlemail.com>

f) Add option -e, to exclude files from results
     Author: francois@debian.org <francois@debian.org>, Roger Leigh <Roger Leigh rleigh@debian.org>
     Date:   Sun Jul 9 18:42:55 2017 +0200
   (From: richard.lewis.debian@googlemail.com:
     Also use the $findargs variable to make -e apply to tests that use $find.
	 By using set -f, we can allow globbing in the argument to -e


g) Add option -s, to exclude results from ifpromisc results
     Author: Stefano Torricella <stetor@y2k.it>
     Date:   Thu May 6 12:34:31 2010 +0200

h) If -q is given, prepare to pass -q to helpers
     Author: lantz moore <lmoore@debian.org>
     Date:   Thu Oct 3 01:02:10 2002 -0400

i) Improve -h output
     Author: Marcos Fouces <marcos@debian.org>
     Date:   Mon Apr 13 10:59:08 2020 +0000
     Bug-Debian: https://bugs.debian.org/586897

j) add xargs to $cmdlist - we will use it in future
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>

k) define $path_for_tools to that external helpers can use the same path set with -p
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>

l) fix relative dirs in PATH
     when setting $newpth, need a / after ROOTDIR in the 'else' branch to support
	 relative dirs in $PATH (eg: .)
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Mon Jul 8 00:14:02 2024 +0100

m) use check_if_debian
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Wed Aug 14 18:24:14 2024 +0100

n) define _filter __filter, find_and_check  etc
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Mon Aug 26 17:02:57 2024 +0100

    NB that Move checking of $EXCLUDE inside _filter, so that every
	test now makes use of the excludes passed with -e

    This means that the debian-specific part moves to __filter()
    (which is a no-op on non-debian or if using -r)

    (There is a slight incompatibility in that if you use -r, you
    should now include the ROOTDIR in the argument to -e, and you
    should include a leading /)

    NB: we use a 'case' rather than 'if' so that '-e /usr/*' works:
    '$EXCLUDE' will contain '/usr/*' and we need to prevent this from
    expanding too soon. So we use 'set -f' before the case statement
    (which is POSIX-compliant): this ensures that $exclude is set to
    '/usr/*' rather than having one exclude for eveyr top-level
    directory under /usr. But then inside the case statement, we do
    want the "*" to be a pattern, so $exclude should be unquoted.


o) improve chk_netstat_or_ss
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Tue Feb 21 18:36:54 2023 +0000
   - prefer ss to netstat (if it is installed)
   - ensure $netstat can be influenced by -p like the other commands

p) do not hard-code return values from chk_* functions
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>

q) allow running as non-root -- give a warning, but no need to exit as
   most tests can work
     Author: Richard Lewis <richard.lewis.debian@googlemail.com>
     Date:   Wed Sep 4 10:22:32 2024 +0100

r) exit 0 at the end
     Author: Arjan Opmeer, Giuseppe Iuculano <giuseppe@iuculano.it>
     Date:   Tue Aug 11 12:48:02 2009 +0000

Forwarded: yes
 (Forwarded by email: 21 Dec 2024)
---
 chkrootkit | 262 +++++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 159 insertions(+), 103 deletions(-)

diff --git a/chkrootkit b/chkrootkit
index cc3459e..02462d0 100755
--- a/chkrootkit
+++ b/chkrootkit
@@ -1,8 +1,8 @@
 #! /bin/sh
 # -*- Shell-script -*-
 
-# $Id: chkrootkit, v 0.58b 2023/07/05 
-CHKROOTKIT_VERSION='0.58b' 
+# $Id: chkrootkit, v 0.58b 2023/07/05
+CHKROOTKIT_VERSION='0.58b'
 
 # Authors: Nelson Murilo <nmurilo@gmail.com> (main author) and
 #          Klaus Steding-Jessen <jessen@cert.br>
@@ -18,19 +18,18 @@ unalias ss > /dev/null 2>&1
 unalias ps > /dev/null 2>&1
 unalias dirname > /dev/null 2>&1
 
-egrep='grep -E' 
 
 # Workaround for recent GNU coreutils
 _POSIX2_VERSION=199209
 export _POSIX2_VERSION
 
-KALLSYMS="/proc/kallsyms" 
-[ -f /proc/ksysm ] && KALLSYMS="/proc/$KALLSYMS" 
+KALLSYMS="/proc/kallsyms"
+[ -f /proc/ksysm ] && KALLSYMS="/proc/$KALLSYMS"
 
 # Native commands
 TROJAN="amd basename biff chfn chsh cron crontab date du dirname echo egrep \
 env find fingerd gpm grep hdparm su ifconfig inetd inetdconf identd init \
-killall  ldsopreload login ls lsof mail mingetty netstat named passwd pidof \
+killall ldsopreload login ls lsof mail mingetty netstat named passwd pidof \
 pop2 pop3 ps pstree rpcinfo rlogind rshd slogin sendmail sshd syslogd tar tcpd \
 tcpdump top telnetd timed traceroute vdir w write"
 
@@ -1470,59 +1469,42 @@ fi
 
 # our which(1)
 loc () {
-    ### usage: loc filename filename_to_return_if_nothing_was_found path
+    ### usage: loc filename filename_to_return_if_nothing_was_found "path"
     thing=$1
-    shift
-    dflt=$1
-    shift
-    for dir in $*; do
-            case "$thing" in
-            .)
-            if test -d $dir/$thing; then
-                    echo $dir
-                    exit 0
-            fi
-            ;;
-            *)
-            for thisthing in $dir/$thing; do
-                    :
-            done
-            if test -f $thisthing; then
-                    echo $thisthing
-                    exit 0
-            fi
-            ;;
-            esac
+    dflt=$2
+    for dir in $3; do
+        if test -f "$dir/$thing"; then
+            echo "$dir/$thing"
+            exit 0
+        fi
     done
-    if [ "${ROOTDIR}" = "/" ]; then
-      echo ${dflt}
-    else
-      echo "${ROOTDIR}${dflt}"
-    fi
+    echo "${dflt}"
     exit 1
 }
 
+# find $1 either from ps(1) or using loc. (it only makes sense to use
+# ps(1) if we are not using '-r')
 getCMD() {
-   RUNNING=`${ps} ${ps_cmd} | ${egrep} "${L_REGEXP}${1}${R_REGEXP}" | \
-            ${egrep} -v grep | ${egrep} -v chkrootkit | _head -1 | \
-            ${awk} '{ print $5 }'`
-
-   for i in ${ROOTDIR}${RUNNING} ${ROOTDIR}usr/sbin/${1} `loc ${1} ${1} $pth`
-   do
-      CMD="${i}"
-      if [ -r "${i}" ]
-        then
-        return 0
-      fi
-   done
-   return 1
+    if [ "$mode" = "rt" ]; then
+        # prefer to test the running $1 if it appears in ps - this fails for sshd which as 'sshd: ..'
+        # and not as '/usr/sbin/sshd'
+        RUNNING=$("${ps}" "${ps_cmd}" | ${egrep} "${L_REGEXP}${1}${R_REGEXP}" | \
+            ${egrep} -v grep | ${egrep} -v chkrootkit | _head -1 | "${awk}" '{ print $5 }')
+        if [ -n "${RUNNING}" ] && [ -r "$RUNNING" ]; then
+            CMD=${RUNNING}
+            return 0
+        fi
+    fi
+    # either using -r or $1 is not running: find in $pth
+    CMD="$(loc "${1}" "${1}" "$pth")"
+    return $?
 }
 
 expertmode_output() {
     echo "###"
     echo "### Output of: $1"
     echo "###"
-    eval $1 2>&1
+    eval "$1" 2>&1
 #    cat <<EOF
 #`$1 2>&1`
 #EOF
@@ -1538,12 +1520,8 @@ exclude_fstype ()
   fi
 
    ## Check if -fstype $type works
-   if find /etc -maxdepth 0 >/dev/null 2>&1; then
-        find /etc ! -fstype "$1" -maxdepth 0 >/dev/null 2>&1 && \
-           findargs="${findargs} ! -fstype $1 "
-   elif find /etc -prune > /dev/null 2>&1; then
-        find /etc ! -fstype "$1" -prune > /dev/null 2>&1 && \
-           findargs="${findargs} ! -fstype $1 "
+   if "${find}" /etc -maxdepth 0 -fstype "$1" -prune >/dev/null 2>&1; then
+       findargs="${findargs} -fstype $1 -prune -o "
    fi
 }
 
@@ -2847,18 +2825,24 @@ R_REGEXP='([^A-Za-z0-9_]|$)'
 
 ### default ROOTDIR is "/"
 ROOTDIR='/'
-mode="rt" 
+mode="rt"
 
 findargs=""
 
+EXPERT=""
+QUIET=""
+QUIET_ARG=""
+EXCLUDES=""
+EXCLUDES_SNIF=""
+
 while :
 do
-        case $1 in
-        -r)    [ -z "$2" ] && exit 1; 
+        case "${1-}" in
+        -r)    [ -z "$2" ] && exit 1;
                shift
-               mode="pm" 
+               mode="pm"
                ROOTDIR=$1;;
-        -p)    [ -z "$2" ] && exit 1; 
+        -p)    [ -z "$2" ] && exit 1;
                 shift
                 CHKRKPATH=$1;;
 
@@ -2866,7 +2850,16 @@ do
 
         -x)     EXPERT=t;;
 
-        -q)     QUIET=t;;
+        -e)     shift
+                findargs="${findargs} -path $1 -prune -o"
+                EXCLUDES="$EXCLUDES $1";;
+
+        -s)     shift
+                EXCLUDES_SNIF="$1";;
+
+        -q)     QUIET=t
+                QUIET_ARG="-q"
+                ;;
 
         -V)     echo >&2 "chkrootkit version ${CHKROOTKIT_VERSION}"
                 exit 1;;
@@ -2887,10 +2880,14 @@ Options:
         -d                debug
         -q                quiet mode
         -x                expert mode
-        -r dir            use dir as the root directory
-        -p dir1:dir2:dirN path for the external commands used by chkrootkit
+        -e 'FILE1 FILE2'  exclude files/dirs from results. Must be followed by a space-separated list of files/dirs.
+                          Read /usr/share/doc/chkrootkit/README.FALSE-POSITIVES first.
+        -s REGEXP         filter results of sniffer test through 'grep -Ev REGEXP' to exclude expected
+                          PACKET_SNIFFERs. Read /usr/share/doc/chkrootkit/README.FALSE-POSITIVES first.
+        -r DIR            use DIR as the root directory
+        -p DIR1:DIR2:DIRN path for the external commands used by chkrootkit
         -n                skip NFS mount points
-        -T fstype         skip mount points of the supplied file system type"
+        -T FSTYPE         skip mount points of the specified file system type"
                 exit 1;;
         *)      break
         esac
@@ -2913,22 +2910,26 @@ ps
 sed
 strings
 uname
+xargs
 "
 
 ### PATH used by loc
-pth=`echo $PATH | sed -e "s/:/ /g"`
+pth=$(echo "$PATH" | sed -e "s/:/ /g")
 pth="$pth /sbin /usr/sbin /lib /usr/lib /usr/libexec ."
 
-### external command's PATH
+### external (trusted) command's PATH
 if [ "${CHKRKPATH}" = "" ]; then
-  chkrkpth=${pth}
+		chkrkpth=${pth}
+		path_for_tools="$PATH"
 else
-  ### use the path provided with the -p option
-  chkrkpth=`echo ${CHKRKPATH} | sed -e "s/:/ /g"`
+		### use the path provided with the -p option
+		chkrkpth=$(echo "${CHKRKPATH}" | sed -e "s/:/ /g")
+		# chkutmp and chkproc assume 'ps' from $PATH is safe to run
+		path_for_tools="$CHKRKPATH:$PATH"
 fi
 for file in $cmdlist; do
-        xxx=`loc $file $file $chkrkpth`
-        eval $file=$xxx
+        xxx=$(loc "$file" "$file" "$chkrkpth")
+        eval "$file=$xxx"
         case "$xxx" in
         /* | ./* | ../*)
 
@@ -2944,22 +2945,76 @@ for file in $cmdlist; do
                 ;;
         esac
 done
+egrep="${grep} -E"
+if [ "${mode}" = "rt" ]; then
+    dpkg_query=$(loc dpkg-query "" "$chkrkpth")
+else
+    dpkg_query=""
+fi
+if [ -n "${dpkg_query}" ] && [ ! -x "${dpkg_query}" ]; then
+    # unlikely
+    echo >&2 "chkrootkit: can't exec dpkg-query: \`${dpkg_query}'."
+    exit 1
+fi
 
-SYSTEM=`${uname} -s`
-VERSION=`${uname} -r`
-if [ "${SYSTEM}" != "FreeBSD" -a ${SYSTEM} != "OpenBSD" ] ; then
+# check if $1 is excluded by $EXCLUDES. $2 is the previous results. Use as
+#  results=$(_filter "$f" "$results")
+# which appends f to $results unless f is excluded.
+_filter(){
+    file_to_report="$1"
+    prev_results="$2"
+    if [ -n "$prev_results" ]; then
+        echo "$prev_results"
+    fi
+    # We need $EXCLUDES to not be a glob, but its componants
+    # (eg /usr/*) to be globbed, so $exclude is unquoted below.
+    # And we need to reset 'set +f' in both return paths in case
+    # we are not run in a subshell
+    set -f
+    for exclude in $EXCLUDES; do
+        case "$file_to_report" in
+            $exclude) set +f; return 0 ;;
+        esac
+    done
+    set +f
+    __filter "$file_to_report"
+}
+
+if [ -n "${dpkg_query}" ]; then
+    find_and_check(){
+        "${find}" "$@" -print0 2>/dev/null | PATH="$path_for_tools" "${xargs}" -0 -I@ ./check_if_debian @ "${dpkg_query}"
+    }
+
+    __filter(){
+        PATH="$path_for_tools" ./check_if_debian "$1" "${dpkg_query}"
+    }
+else
+    # non-Debian or using -r
+    find_and_check(){
+        "${find}" "$@" -print 2>/dev/null
+    }
+
+    __filter(){
+        echo "$1"
+    }
+fi
+
+
+SYSTEM=$("${uname}" -s)
+VERSION=$("${uname}" -r)
+if [ "${SYSTEM}" != "FreeBSD" -a "${SYSTEM}" != "OpenBSD" ] ; then
    V=4.4
 else
-   V=`echo $VERSION| ${sed} -e 's/[-_@].*//'| ${awk} -F . '{ print $1 "." $2 $3 }'`
+   V=$(echo "$VERSION"| "${sed}" -e 's/[-_@].*//'| "${awk}" -F . '{ print $1 "." $2 $3 }')
 fi
 
 # head command
 _head()
 {
-   if `$echo a | $head -n 1 >/dev/null 2>&1` ; then
-      $head -n `echo $1 | tr -d "-"`
+   if "${echo}" a | "${head}" -n 1 >/dev/null 2>&1; then
+      "${head}" -n "$(echo "$1" | tr -d "-")"
    else
-      $head $1
+      "${head}" "$1"
    fi
 }
 # ps command
@@ -2977,21 +3032,20 @@ if [ "$SYSTEM" = "SunOS" ]; then
   fi
 fi
 # Check if ps command is ok
-if ${ps} ax >/dev/null 2>&1 ; then
+if "${ps}" ax >/dev/null 2>&1 ; then
    ps_cmd="ax"
 else
    ps_cmd="-fe"
 fi
 
-if [ `${id} | ${cut} -d= -f2 | ${cut} -d\( -f1` -ne 0 ]; then
-   echo "$0 needs root privileges"
-   exit 1
+if [ "$("${id}" | "${cut}" -d= -f2 | "${cut}" -d\( -f1)" -ne 0 ]; then
+   echo "$0 needs root privileges: some checks may not work"
 fi
 
 if [ $# -gt 0 ]
 then
     ### perform only tests supplied as arguments
-    for arg in $*
+    for arg in "$@"
     do
         ### check if is a valid test name
         if echo "${TROJAN} ${TOOLS}"| \
@@ -3014,11 +3068,12 @@ fi
 if [ "${ROOTDIR}" != "/" ]; then
 
     ### remove trailing `/'
-    ROOTDIR=`echo ${ROOTDIR} | ${sed} -e 's/\/*$//g'`
+    ROOTDIR=$(echo "${ROOTDIR}" | "${sed}" -e 's/\/*$//g')
 
+    newpth=""
     for dir in ${pth}
     do
-      if echo ${dir} | ${egrep} '^/' > /dev/null 2>&1
+      if echo "${dir}" | ${egrep} '^/' > /dev/null 2>&1
       then
         newpth="${newpth} ${ROOTDIR}${dir}"
       else
@@ -3026,7 +3081,7 @@ if [ "${ROOTDIR}" != "/" ]; then
       fi
     done
     pth=${newpth}
-   ROOTDIR="${ROOTDIR}/"
+    ROOTDIR="${ROOTDIR}/"
 fi
 if [ "${QUIET}" != "t" ]; then
     echo "ROOTDIR is \`${ROOTDIR}'"
@@ -3034,11 +3089,10 @@ fi
 #
 # NETSTAT OR SS
 #
-_chk_netstat_or_ss() 
+_chk_netstat_or_ss()
 {
-    netstat="netstat"  
-    CMD=`loc ss ss $pth`
-    [ ${?} -eq 0 ] && netstat="ss"  
+    netstat=$(loc ss ss "$chkrkpth")
+    [ ${?} -eq 0 ] || netstat=$(loc netstat netstat "$chkrkpth")
 }
 
 for cmd in ${LIST}
@@ -3047,33 +3101,35 @@ do
     ${egrep} "${L_REGEXP}$cmd${R_REGEXP}" > /dev/null 2>&1
     then
         if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
-           printn "Checking \`${cmd}'... "
+            printn "Checking \`${cmd}'... "
         fi
-        chk_${cmd}
+        # each chk_xxx function should not produce any output (unless in EXPERT mode)
+        "chk_${cmd}"
         STATUS=$?
         ### quiet mode
         if [ "${QUIET}" = "t" ]; then
-            ### show only INFECTED status
-            if [ ${STATUS} -eq 0 ]; then
+            ### show only if INFECTED status
+            if [ "${STATUS}" -eq "${INFECTED}" ]; then
                 echo "Checking \`${cmd}'... INFECTED"
             fi
-            continue
+        else
+            case $STATUS in
+                "$INFECTED") echo "INFECTED";;
+                "$NOT_INFECTED") echo "not infected";;
+                "$NOT_TESTED") echo "not tested";;
+                "$NOT_FOUND") echo "not found";;
+                "$INFECTED_BUT_DISABLED") echo "INFECTED but disabled";;
+                5) ;;   ### expert mode
+            esac
         fi
-        case $STATUS in
-        0) echo "INFECTED";;
-        1) echo "not infected";;
-        2) echo "not tested";;
-        3) echo "not found";;
-        4) echo "infected but disabled";;
-        5) ;;   ### expert mode
-        esac
     else
         ### external tool
         if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
             printn "Checking \`$cmd'... "
         fi
-        ${cmd}
+        # unlike the chk_* functions, the functions in $TOOLS are expected to handle $QUIET
+        "${cmd}"
     fi
 done
-
+exit 0
 ### chkrootkit ends here.
