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