6e27d51989986c3cdac5fa86235b4875146cf12a
[pkgusr] / usr / bin / list_suspicious_files
1 #!/bin/bash
2 ## Originally...
3 # Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
4 # You may do everything with this code except misrepresent its origin.
5 # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
6
7 # Copyright (C) 2014 Steve Youngs <steve@steveyoungs.com>
8 # many updates/tweaks --SY.
9
10 # The following list should contain the mount points of all filesystems
11 # that are to be scanned as a space-separated list within parentheses. 
12 # / will usually be in this list and if you have /usr
13 # on a separate partition, it will also be in this list. Other non-special
14 # filesystems where suspicious files could be located should also be put in 
15 # this list.
16 # Mount points whose filesystems are special, such as procfs or sysfs should
17 # not be in this list. 
18
19 fs_to_scan=(/)
20
21 ## Bastard settings
22 #fs_to_scan=(\
23 #       / \
24 #       /opt \
25 #       /usr \
26 #       /usr/src \
27 #       /var)
28
29 # Files with a path prefix found in the following list are ignored.
30 # DO !!!!NOT!!! PUT /usr/src OR WHATEVER THE HOME DIRECTORY prefix is for your
31 # package users into this list!!! You DO want to scan those directories in
32 # order to spot e.g. world-writable tarballs and other abominations that
33 # may have crept in.
34 # Ideally, this list should be empty.
35
36 prune_prefixes=(/root /{,*/{,*/}}lost+found) #NO TRAILING SLASHES!!!
37
38 ## Bastard settings
39 #prune_prefixes=(\
40 #       /{,*/{,*/}}lost+found \
41 #       /root \
42 #       /opt/pgsql/data \
43 #       /opt/sql-ledger/{spool,templates,users,css} \
44 #       /etc/apache/ssl.key \
45 #       /etc/audisp/plugins.d \
46 #       /etc/cups/ssl \
47 #       /etc/firewall \
48 #       /etc/mail/spamassassin \
49 #       /etc/pam.d \
50 #       /etc/polkit-1/rules.d \
51 #       /etc/skel \
52 #       /etc/ssl/private \
53 #       /etc/sudoers.d \
54 #       /var/lib/colord/.cache \
55 #       /var/lib/{sasl,sudo,net-snmp,udisks{,2},NetworkManager} \
56 #       /var/log \
57 #       /usr/lib/pkgusr \
58 #       /usr/share/polkit-1/rules.d \
59 #       /var/tmp \
60 #       /var/{cache,chroot,db,run,snmp,spool} \
61 #       /var/lib/{sshd,nfs,spamassassin,pulse,machines}) #NO TRAILING SLASHES!!!!
62
63 # Set the following to `-noleaf' if you are scanning non-UNIX filesystems
64 # like MS-DOS, CD-ROM etc.  But only do so if you really need it as it
65 # will slow the search significantly.
66 # NOLEAF='-noleaf'
67 NOLEAF=
68
69 # If the following variable is set to "yes", then files that contain
70 # control characters or other non-printable characters (except for space)
71 # will be reported as suspicious. 
72 # This test slows down the search considerably!
73 #enable_illchars=yes
74 enable_illchars=no
75
76
77 # suppress ugly debug output from shell
78 trap ':' SIGPIPE
79
80 # "-false" as 1st argument is used when called by list_suspicious_files_from
81 if [ $# -ge 1 -a "$1" != "-false" ]; then
82     echo 1>&2 
83     echo 1>&2 "USAGE: ${0##*/}"
84     echo 1>&2 
85     echo 1>&2 '  Outputs a categorized list of files and directories with properties'
86     echo 1>&2 '  that could mean trouble and should be investigated.'
87     echo 1>&2
88     exit 1
89 fi
90
91
92 usergroupmatch=(-true)
93 if [ "$1" = "-false" ]; then
94     usergroupmatch=(\( "$@" \))
95 fi
96
97 # construct find commands that match the prune_prefixes. Each prefix will be
98 # matched as -path <prefix> -or -path <prefix>/*
99 # so that the directory itself and all subdirectories are matched.
100 y=(\( -false)
101 for ((i=0; $i<${#prune_prefixes[@]}; i=$i+1)) 
102 do
103     y[${#y[@]}]='-or'
104     y[${#y[@]}]=-path
105     y[${#y[@]}]="${prune_prefixes[$i]}"
106     y[${#y[@]}]='-or'
107     y[${#y[@]}]=-path
108     y[${#y[@]}]="${prune_prefixes[$i]}/*"
109 done
110 y[${#y[@]}]=')'
111
112 illchars=( $'\x1'  $'\x2'  $'\x3'  $'\x4'  $'\x5'  $'\x6'  $'\x7'  $'\x8'  
113   $'\x9'  $'\xA'  $'\xB'  $'\xC'  $'\xD'  $'\xE'  $'\xF'  $'\x10'  $'\x11' 
114   $'\x12'  $'\x13'  $'\x14'  $'\x15'  $'\x16'  $'\x17'  $'\x18'  $'\x19'  
115   $'\x1A'  $'\x1B'  $'\x1C'  $'\x1D'  $'\x1E'  $'\x1F'  $'\x7f' $'\x80' 
116   $'\x81'  $'\x82'  $'\x83'  $'\x84'  $'\x85'  $'\x86'  $'\x87'  $'\x88'  
117   $'\x89'  $'\x8A'  $'\x8B'  $'\x8C'  $'\x8D'  $'\x8E'  $'\x8F'  $'\x90'  
118   $'\x91'  $'\x92'  $'\x93'  $'\x94'  $'\x95'  $'\x96'  $'\x97'  $'\x98'  
119   $'\x99'  $'\x9A'  $'\x9B'  $'\x9C'  $'\x9D'  $'\x9E'  $'\x9F' ) 
120
121
122 if [ "$enable_illchars" = yes ]; then
123
124     illname=(\( -false)
125     for ((i=0; $i<${#illchars[@]}; i=$i+1)) 
126     do
127         #handle bash \x7f error
128         if [ "*${illchars[$i]}*" = "**" ]; then
129             illchars[$i]=$'\x80'  #'
130         fi
131         illname[${#illname[@]}]='-or'
132         illname[${#illname[@]}]=-name
133         illname[${#illname[@]}]="*${illchars[$i]}*"
134     done
135     illname[${#illname[@]}]=')'
136
137     illlink=(\( -false)
138     for ((i=0; $i<${#illchars[@]}; i=$i+1)) 
139     do
140         illlink[${#illlink[@]}]='-or'
141         illlink[${#illlink[@]}]=-lname
142         illlink[${#illlink[@]}]="*${illchars[$i]}*"
143     done
144     illlink[${#illlink[@]}]=')'
145 else #if [ "$enable_illchars" = no ]
146     illlink=(-false)
147     illname=(-false)
148 fi
149
150 # $1=section heading
151 # $2=inode message
152 report()
153 {
154     echo -printf "increment_code_here"
155     echo -printf 
156     echo "1 ${1}\\n" | sed 's/ /\\040/g'
157     echo -printf "insert_code_here"
158   
159     if [ -n "$2" ]; then
160         echo -printf 
161         echo "2 %i 1 ${2}\\n" | sed 's/ /\\040/g'
162         echo -printf "insert_code_here"
163         echo -printf 
164         echo "2 %i 2 " | sed 's/ /\\040/g'
165     else
166         echo -printf "2\\040" 
167     fi
168   
169     echo -exec ls -T 0 -ladQ {} \;
170 }
171
172
173 filegoodperm=(\( -perm 644 -or -perm 755 -or -perm 555 -or -perm 444 -or -perm 600 -or -perm 700 -or -perm 640 \))
174 dirgoodperm=(\( -perm 755 -or -perm 555 -or -perm 700 -or -perm 750 \))
175
176 good=( \( 
177           -not \( -not -type d -links +1 \)
178           -not -nouser -not -nogroup 
179           -not \( "${illname[@]}" \) 
180           -not \( "${illlink[@]}" \) 
181        \) 
182        -and
183 \(
184       \( -type f -not -group install "${filegoodperm[@]}" \) 
185   -or \( -type d -not -group install "${dirgoodperm[@]}" \)
186   -or \( -type d -group install \( -perm 1775 \) \)
187   -or \( -type d -group root -user root -path "/tmp" \( -perm 1777 \) \)
188   -or \( -type d -group root -user root -path "/var/tmp" \( -perm 1777 \) \)
189   -or \( -not -type d -not -type f -not -type l -path "/dev/*" \)
190   -or \( -type l \( -xtype b -or -xtype c -or -xtype d -or -xtype p -or -xtype f \) \)
191 \)
192 )
193
194 bad=(
195     \( "${illname[@]}" $(report  "NON-PRINTABLE CHAR IN NAME")  \)
196  OP \( "${illlink[@]}" $(report  "NON-PRINTABLE-CHAR IN LINK-TARGET")  \)
197  OP \( -type f -perm -4000 $(report  "SETUID FILES")  \)
198  OP \( -type f -perm -2000 $(report  "SETGID FILES")  \)
199  OP \( -type f -perm -1000 $(report  "STICKY FILES")  \)
200  OP \( -type d -perm -2000 $(report  "GROUP-KEEPING DIRECTORIES")  \)
201  OP \( -type d -not -group install -perm -1000  $(report  "STICKY DIRECTORIES")  \)
202  OP \( -type f -perm -g+w  $(report  "GROUP-WRITABLE FILES")  \)
203  OP \( -type f -perm -o+w  $(report  "WORLD-WRITABLE FILES")  \)
204  OP \( -type d -perm -g+w  $(report  "GROUP-WRITABLE DIRECTORIES")  \)
205  OP \( -type d -perm -o+w  $(report  "WORLD-WRITABLE DIRECTORIES")  \)
206  OP \( -not \( -type f -or -type l -or -type d \) -not -path "/dev/*"  $(report  "SPECIAL FILES OUTSIDE /dev")  \)
207  OP \( -type d -group install -not -perm 1755  $(report  "INSTALL DIRECTORIES WITH UNUSUAL PERMISSIONS")  \)
208  OP \( -type f -group install $(report  "FILES ASSIGNED TO GROUP INSTALL")  \)
209  OP \( -type l -not \( -xtype b -or -xtype c -or -xtype d -or -xtype p -or -xtype f \) $(report  "SYMLINKS POSSIBLY BROKEN OR LOOP")  \)
210  OP \( -not -type d -links +1 $(report "HARDLINKED FILES" "Inode %i is shared by %n files, including") \)
211  OP \( -nouser  $(report  "THINGS HAVING UID WITH NO ASSIGNED USER NAME")  \)
212  OP \( -nogroup  $(report  "THINGS HAVING GID WITH NO ASSIGNED GROUP NAME")  \)
213  OP \( -type f -not -group install -not "${filegoodperm[@]}"  $(report  "FILES WITH UNUSUAL PERMISSIONS")  \)
214  OP \( -type d -not -group install -not "${dirgoodperm[@]}"  $(report  "DIRECTORIES WITH UNUSUAL PERMISSIONS")  \)
215 )
216
217 # insert unique codes for the messages
218 code=100
219 for ((i=0; $i<${#bad[@]}; i=$i+1)) 
220 do
221     if [ "${bad[$i]}" = "increment_code_here" ]; then
222         code=$(($code + 1))
223         bad[$i]=$code
224     elif [ "${bad[$i]}" = "insert_code_here" ]; then
225         bad[$i]=$code
226     fi
227 done
228
229 allbad=()  #all bad matches are reported
230 onebad=()  #only the first bad match is reported
231 for ((i=0; $i<${#bad[@]}; i=$i+1)) 
232 do
233     if [ "${bad[$i]}" = "OP" ]; then
234         allbad[$i]=","
235         onebad[$i]="-or"
236     else
237         allbad[$i]="${bad[$i]}"
238         onebad[$i]="${bad[$i]}"
239     fi
240 done
241
242 # Add a default case to onebad.
243 # This should never be hit, because the explicit cases should catch all
244 # files, but just in case I've missed something, this will catch it.
245 onebad=("${onebad[@]}" -or  $(report  "WEIRD SHIT") )
246
247 # make allbad always return false
248 allbad=("${allbad[@]}" , -false)
249
250 cmd=( "${usergroupmatch[@]}" -and
251      \( \( "${good[@]}" \) -or \( "${allbad[@]}" \) -or \( "${onebad[@]}" \) \)
252     )
253
254 # In the following find command, the part
255 # -not ( ( "${y[@]}" -prune ) -or "${y[@]}" )
256 # is responsible for preventing the files that match prune_prefixes from
257 # being processed. The 2nd "${y[@]}" may seem redundant, but it isn't, because
258 # -prune has no effect and is always false when -depth is used.
259 find "${fs_to_scan[@]}" -xdev $NOLEAF \
260     -not \( \( "${y[@]}" -prune \) -or "${y[@]}" \) \
261     -and \( "${cmd[@]}" \) 2>/dev/null | 
262 sed 's/^\(...2\) \([0-9]\+ 2 \)\?\([^ ]\+\) \+[^ ]\+ \+\([^ ]\+\) \+\([^ ]\+\) \+[^"]\+\(".\+\)/\1 \2\3 \6  \4:\5/' |
263 sort -u | 
264 sed 's/^...1 /\'$'\n''/;s/^...2 [0-9]\+ 1 /\'$'\n''  /;s/^...2 [0-9]\+ 2 /    /;s/^...2 /  /'