xref: /openbmc/openbmc/meta-security/recipes-security/redhat-security/files/rpm-chksec.sh (revision eb8dc40360f0cfef56fb6947cc817a547d6d9bc6)
1#!/bin/sh
2# rpm-chksec
3#
4# Copyright (c) 2011-2013 Steve Grubb. ALL RIGHTS RESERVED.
5# sgrubb@redhat.com
6#
7# This software may be freely redistributed under the terms of the GNU
8# public license.
9#
10# You should have received a copy of the GNU General Public License
11# along with this program; if not, write to the Free Software
12# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
13#
14# Given an rpm, it will look at each file to check that its compiled with
15# the intended flags to make it more secure. Things that are green are OK.
16# Anything in yellow could be better but is passable. Anything in red needs
17# attention.
18#
19# If the --all option is given, it will generate a list of rpms and then
20# summarize the rpm's state. For yes, then all files are in the expected
21# state. Just one file not compiled with the right flags can turn the
22# answer to no. Re-run passing that package (instead of --all) for the details.
23#
24# To save to file: ./rpm-chksec | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee output.txt
25
26VERSION="0.5.2"
27
28usage () {
29	echo "rpm-chksec [--version|--all|<rpmname>...]"
30	if [ ! -x /usr/bin/filecap ] ; then
31		echo "You need to install libcap-ng-utils to test capabilities"
32	fi
33	if [ $EUID != 0 ] ; then
34		echo "You might need to be root to read some files"
35	fi
36	exit 0
37}
38
39if [ "$1" = "--help" -o $# -eq 0 ] ; then
40	usage
41fi
42if [ "$1" = "--version" ] ; then
43	echo "rpm-chksec $VERSION"
44	exit 0
45fi
46if [ "$1" = "--all" ] ; then
47	MODE="all"
48else
49	MODE="single"
50fi
51
52do_one () {
53if ! rpm -q $1 >/dev/null 2>&1 ; then
54	if [ "$MODE" = "single" ] ; then
55		echo "$1 is not installed"
56		exit 1
57	else
58		echo "not installed"
59		return
60	fi
61fi
62files=`rpm -ql $1`
63
64# Look for daemons, need this for later...
65DAEMON=""
66for f in $files
67do
68	if [ ! -f "$f" ] ; then
69		continue
70	fi
71	if [ `echo "$f" | grep '\/etc\/rc.d\/init.d'` ] ; then
72		n=`basename "$f"`
73	        t=`which "$n" 2>/dev/null`
74        	if [ x"$t" != "x" ] ; then
75                	DAEMON="$DAEMON $t"
76	                continue
77        	fi
78	        t=`which "$n"d 2>/dev/null`
79        	if [ x"$t" != "x" ] ; then
80                	DAEMON="$DAEMON $t"
81	                continue
82        	fi
83	        t=`cat "$f" 2>/dev/null | grep 'bin' | grep 'exit 5' | grep -v '\$'`
84        	if [ x"$t" != "x" ] ; then
85                	DAEMON="$DAEMON $t"
86	                continue
87        	fi
88		if [ "$MODE" = "single" ] ; then
89        		echo "Can't find the executable in $f but daemon rules would apply"
90		fi
91	elif [ `echo "$f" | grep '\/lib\/systemd\/'` ] ; then
92		t=`cat "$f" | grep -i '^ExecStart=' | tr '=' ' ' | awk '{ print $2 }'`
93		if [ x"$t" != "x" ] ; then
94                	DAEMON="$DAEMON $t"
95	                continue
96        	fi
97	fi
98done
99
100# Prevent garbled output when doing --all.
101skip_current=0
102
103for f in $files
104do
105	if [ ! -f "$f" ] ; then
106		continue
107	fi
108	# Some packages have files with ~ in them. This avoids it.
109	if ! echo "$f" | grep '^/' >/dev/null ; then
110		continue
111	fi
112	if [ ! -r "$f" ] && [ $EUID != 0 ] ; then
113		if [ $MODE = "single" ] ; then
114			echo "Please re-test $f as the root user"
115		else
116			# Don't print results.
117			skip_current=1
118			echo "Please re-test $1 as the root user"
119		fi
120		continue
121	fi
122	if ! file "$f" | grep -qw 'ELF'; then
123		continue
124	fi
125	RELRO="no"
126	if readelf -l "$f" 2>/dev/null | grep -q 'GNU_RELRO'; then
127		RELRO="partial"
128	fi
129	if readelf -d "$f" 2>/dev/null | grep -q 'BIND_NOW'; then
130		RELRO="full"
131	fi
132	PIE="no"
133	if readelf -h "$f" 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then
134		PIE="DSO"
135		if readelf -d "$f" 2>/dev/null | grep -q '(DEBUG)'; then
136			PIE="yes"
137		fi
138	fi
139	APP=""
140	if [ x"$DAEMON" != "x" ] ; then
141		for d in $DAEMON
142		do
143			if [ "$f" = "$d" ] ; then
144				APP="daemon"
145				break
146			fi
147		done
148	fi
149	if [ x"$APP" = "x" ] ; then
150		# See if this is a library or a setuid app
151		if [ `echo "$f" | grep '\/lib' | grep '\.so'` ] ; then
152			APP="library"
153		elif [ `find "$f" -perm -004000 -type f -print` ] ; then
154			APP="setuid"
155		elif [ `find "$f" -perm -002000 -type f -print` ] ; then
156			APP="setgid"
157		elif [ -x /usr/bin/filecap ] && [ `filecap "$f" 2> /dev/null | wc -w` -gt 0 ] ; then
158			APP="setcap"
159		else
160			syms1=`/usr/bin/readelf -s "$f" 2>/dev/null | egrep ' connect@.*GLIBC| listen@.*GLIBC| accept@.*GLIBC|accept4@.*GLIBC'`
161			syms2=`/usr/bin/readelf -s "$f" 2>/dev/null | egrep ' getaddrinfo@.*GLIBC| getnameinfo@.*GLIBC| getservent@.*GLIBC| getservbyname@.*GLIBC| getservbyport@.*GLIBC|gethostbyname@.*GLIBC| gethostbyname2@.*GLIBC|  gethostbyaddr@.*GLIBC|  gethostbyaddr2@.*GLIBC'`
162			if [ x"$syms1" != "x" ] ; then
163				if [ x"$syms2" != "x" ] ; then
164					APP="network-ip"
165				else
166					APP="network-local"
167				fi
168			fi
169		fi
170	fi
171	if [ x"$APP" = "x" ] ; then
172		APP="exec"
173	fi
174
175	# OK, ready for the output
176	if [ "$MODE" = "single" ] ; then
177		printf "%-56s %-10s  " "$f" $APP
178		if [ "$APP" = "daemon" -o "$APP" = "setuid" -o "$APP" = "setgid" -o "$APP" = "setcap" -o "$APP" = "network-ip" -o "$APP" = "network-local" ] ; then
179			if [ "$RELRO" = "full" ] ; then
180				 printf "\033[32m%-7s\033[m  " $RELRO
181			elif [ "$RELRO" = "partial" ] ; then
182				printf "\033[33m%-7s\033[m  " $RELRO
183			else
184				printf "\033[31m%-7s\033[m  " $RELRO
185			fi
186			if [ "$PIE" = "yes" ] ; then
187				printf "\033[32m%-4s\033[m" $PIE
188			else
189				printf "\033[31m%-4s\033[m" $PIE
190			fi
191		elif [ "$APP" = "library" ] ; then
192			if [ "$RELRO" = "full" -o "$RELRO" = "partial" ] ; then
193				 printf "\033[32m%-7s\033[m  " $RELRO
194			else
195				printf "\033[31m%-7s\033[m  " $RELRO
196			fi
197			printf "\033[32m%-4s\033[m" $PIE
198		else
199			# $APP = exec - we want partial relro
200			if [ "$RELRO" = "no" ] ; then
201				printf "\033[31m%-7s\033[m  " $RELRO
202			else
203				printf "\033[32m%-7s\033[m  " $RELRO
204			fi
205			printf "\033[32m%-4s\033[m" $PIE
206		fi
207		echo
208	else
209		if [ "$APP" = "daemon" -o "$APP" = "setuid" -o "$APP" = "setgid" -o "$APP" = "setcap" -o "$APP" = "network-ip" -o "$APP" = "network-local" ] ; then
210			if [ "$RELRO" = "no" ] ; then
211				RELRO_SUM="no"
212				APP_SUM="$APP"
213			fi
214			if [ "$PIE" = "no" ] ; then
215				PIE_SUM="no"
216				APP_SUM="$APP"
217			fi
218		elif [ "$APP" = "library" ] ; then
219			if [ "$RELRO" = "no" ] ; then
220				RELRO_SUM="no"
221				APP_SUM="$APP"
222			fi
223		# $APP = exec - must have partial or full relro
224		elif [ "$RELRO" = "no" ] ; then
225			RELRO_SUM="no"
226			APP_SUM="$APP"
227		fi
228	fi
229done
230}
231
232if [ "$MODE" = "single" ] ; then
233	printf "%-56s %-10s  %-7s  %-4s" "FILE" "TYPE" "RELRO" "PIE"
234	echo
235	for i; do
236		f=$(basename $1)
237		# Strip the .rpm extension, if present.
238		do_one ${f%%.rpm}
239		shift
240	done
241	exit 0
242fi
243
244# Skip the kernel as its special
245packages=`rpm -qa | egrep -v 'kernel.|debuginfo.|.noarch|gpg-pubkey' | sort`
246printf "%-50s  %-5s  %-4s  %-14s" "PACKAGE" "RELRO" "PIE" "CLASS"
247echo
248for p in $packages
249do
250	RELRO_SUM="yes"
251	PIE_SUM="yes"
252	APP_SUM=""
253	printf "%-50s  " $p
254	do_one $p
255	if [[ $skip_current -eq 1 ]] ; then
256		continue
257	fi
258	if [ "$RELRO_SUM" = "yes" ] ; then
259		printf "\033[32m%-5s\033[m  " "$RELRO_SUM"
260	else
261		printf "\033[31m%-5s\033[m  " "$RELRO_SUM"
262	fi
263	if [ "$PIE_SUM" = "yes" ] ; then
264		printf "\033[32m%-4s\033[m" "$PIE_SUM"
265		if [ "$RELRO_SUM" = "no" ] ; then
266			printf "  %-14s" "$APP_SUM"
267		fi
268	else
269		if [ "$APP_SUM" = "network-local" ] ; then
270			printf "\033[33m%-4s\033[m  %-14s" "$PIE_SUM" "$APP_SUM"
271		else
272			printf "\033[31m%-4s\033[m  %-14s" "$PIE_SUM" "$APP_SUM"
273		fi
274	fi
275	echo
276done
277exit 0
278
279
280