Richard Marian Thomaiyar | 14fddef | 2018-07-13 23:55:56 +0530 | [diff] [blame] | 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 | |
| 26 | VERSION="0.5.2" |
| 27 | |
| 28 | usage () { |
| 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 | |
| 39 | if [ "$1" = "--help" -o $# -eq 0 ] ; then |
| 40 | usage |
| 41 | fi |
| 42 | if [ "$1" = "--version" ] ; then |
| 43 | echo "rpm-chksec $VERSION" |
| 44 | exit 0 |
| 45 | fi |
| 46 | if [ "$1" = "--all" ] ; then |
| 47 | MODE="all" |
| 48 | else |
| 49 | MODE="single" |
| 50 | fi |
| 51 | |
| 52 | do_one () { |
| 53 | if ! 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 |
| 61 | fi |
| 62 | files=`rpm -ql $1` |
| 63 | |
| 64 | # Look for daemons, need this for later... |
| 65 | DAEMON="" |
| 66 | for f in $files |
| 67 | do |
| 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 |
| 98 | done |
| 99 | |
| 100 | # Prevent garbled output when doing --all. |
| 101 | skip_current=0 |
| 102 | |
| 103 | for f in $files |
| 104 | do |
| 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 |
| 229 | done |
| 230 | } |
| 231 | |
| 232 | if [ "$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 |
| 242 | fi |
| 243 | |
| 244 | # Skip the kernel as its special |
| 245 | packages=`rpm -qa | egrep -v 'kernel.|debuginfo.|.noarch|gpg-pubkey' | sort` |
| 246 | printf "%-50s %-5s %-4s %-14s" "PACKAGE" "RELRO" "PIE" "CLASS" |
| 247 | echo |
| 248 | for p in $packages |
| 249 | do |
| 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 |
| 276 | done |
| 277 | exit 0 |
| 278 | |
| 279 | |