Home | History | Annotate | Download | only in scripts
      1 #!/bin/bash
      2 # fixfiles
      3 #
      4 # Script to restore labels on a SELinux box
      5 #
      6 # Copyright (C) 2004-2013 Red Hat, Inc.
      7 # Authors: Dan Walsh <dwalsh (at] redhat.com>
      8 #
      9 # This program is free software; you can redistribute it and/or modify
     10 # it under the terms of the GNU General Public License as published by
     11 # the Free Software Foundation; either version 2 of the License, or
     12 # (at your option) any later version.
     13 #
     14 # This program is distributed in the hope that it will be useful,
     15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 # GNU General Public License for more details.
     18 #
     19 # You should have received a copy of the GNU General Public License
     20 # along with this program; if not, write to the Free Software
     21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     22 
     23 set -o nounset
     24 
     25 #
     26 # seclabel support was added in 2.6.30.  This function will return a positive
     27 # number if the current kernel version is greater than 2.6.30, a negative
     28 # number if the current is less than 2.6.30 and 0 if they are the same.
     29 #
     30 function useseclabel {
     31 	VER=`uname -r`
     32 	SUP=2.6.30
     33 	expr '(' "$VER" : '\([^.]*\)' ')' '-' '(' "$SUP" : '\([^.]*\)' ')' '|' \
     34 	     '(' "$VER.0" : '[^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0" : '[^.]*[.]\([^.]*\)' ')' '|' \
     35 	     '(' "$VER.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')'
     36 }
     37 
     38 #
     39 # Get all mount points that support labeling.  Use the 'seclabel' field if it
     40 # is available.  Else fall back to known fs types which likely support xattrs
     41 # and we know were not context mounted.
     42 #
     43 get_all_labeled_mounts() {
     44 FS="`cat /proc/self/mounts | sort | uniq | awk '{print $2}'`"
     45 for i in $FS; do
     46 	if [ `useseclabel` -ge 0 ]
     47 	then
     48 		grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)seclabel(,|$)' && echo $i
     49 	else
     50 		grep " $i " /proc/self/mounts | grep -v "context=" | egrep --silent '(ext[234]| ext4dev | gfs2 | xfs | jfs | btrfs )' && echo $i
     51 	fi
     52 done
     53 }
     54 
     55 get_rw_labeled_mounts() {
     56 FS=`get_all_labeled_mounts | sort | uniq`
     57 for i in $FS; do
     58 	grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)rw(,|$)' && echo $i
     59 done
     60 }
     61 
     62 get_ro_labeled_mounts() {
     63 FS=`get_all_labeled_mounts | sort | uniq`
     64 for i in $FS; do
     65 	grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)ro(,|$)' && echo $i
     66 done
     67 }
     68 
     69 #
     70 # Get the default label returned from the kernel for a file with a lable the
     71 # kernel does not understand
     72 #
     73 get_undefined_type() {
     74 	SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'`
     75 	cat ${SELINUXMNT}/initial_contexts/unlabeled | secon -t
     76 }
     77 
     78 #
     79 # Get the default label for a file without a label
     80 #
     81 get_unlabeled_type() {
     82 	SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'`
     83 	cat $SELINUXMNT/initial_contexts/file | secon -t
     84 }
     85 
     86 exclude_dirs_from_relabelling() {
     87     exclude_from_relabelling=
     88     if [ -e /etc/selinux/fixfiles_exclude_dirs ]
     89     then
     90 	while read i
     91 	do
     92 	  # skip blank line and comment
     93 	  # skip not absolute path
     94 	  # skip not directory
     95 	  [ -z "${i}" ] && continue
     96 	  [[ "${i}" =~ ^[[:blank:]]*# ]] && continue
     97 	  [[ ! "${i}" =~ ^/.* ]] && continue
     98 	  [[ ! -d "${i}" ]] && continue
     99 	  exclude_from_relabelling="$exclude_from_relabelling -e $i"
    100 	done < /etc/selinux/fixfiles_exclude_dirs
    101     fi
    102     echo "$exclude_from_relabelling"
    103 }
    104 
    105 #
    106 # Set global Variables
    107 #
    108 fullFlag=0
    109 BOOTTIME=""
    110 VERBOSE="-p"
    111 FORCEFLAG=""
    112 RPMFILES=""
    113 PREFC=""
    114 RESTORE_MODE="DEFAULT"
    115 SETFILES=/sbin/setfiles
    116 RESTORECON=/sbin/restorecon
    117 FILESYSTEMSRW=`get_rw_labeled_mounts`
    118 FILESYSTEMSRO=`get_ro_labeled_mounts`
    119 SELINUXTYPE="targeted"
    120 if [ -e /etc/selinux/config ]; then
    121     . /etc/selinux/config
    122     FC=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts
    123 else
    124     FC=/etc/security/selinux/file_contexts
    125 fi
    126 
    127 #
    128 # Log all Read Only file systems
    129 #
    130 LogReadOnly() {
    131 if [ ! -z "$FILESYSTEMSRO" ]; then
    132     echo "Warning: Skipping the following R/O filesystems:"
    133     echo "$FILESYSTEMSRO"
    134 fi
    135 }
    136 
    137 #
    138 # Log directories excluded from relabelling by configuration file
    139 #
    140 LogExcluded() {
    141 for i in ${EXCLUDEDIRS//-e / }; do
    142     echo "skipping the directory $i"
    143 done
    144 }
    145 
    146 #
    147 # Find files newer then the passed in date and fix the label
    148 #
    149 newer() {
    150     DATE=$1
    151     shift
    152     LogReadOnly
    153     for m in `echo $FILESYSTEMSRW`; do
    154 	find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} $* -i -0 -f -
    155     done;
    156 }
    157 
    158 #
    159 # Compare PREVious File Context to currently installed File Context and
    160 # run restorecon on all files affected by the differences.
    161 #
    162 diff_filecontext() {
    163 EXCLUDEDIRS="`exclude_dirs_from_relabelling`"
    164 for i in /sys /proc /dev /run /mnt /var/tmp /var/lib/BackupPC /home /tmp /dev; do
    165     [ -e $i ]  && EXCLUDEDIRS="${EXCLUDEDIRS} -e $i";
    166 done
    167 LogExcluded
    168 
    169 if [ -f ${PREFC} -a -x /usr/bin/diff ]; then
    170 	TEMPFILE=`mktemp ${FC}.XXXXXXXXXX`
    171 	test -z "$TEMPFILE" && exit
    172 	PREFCTEMPFILE=`mktemp ${PREFC}.XXXXXXXXXX`
    173 	sed -r -e 's,:s0, ,g' $PREFC | sort -u > ${PREFCTEMPFILE}
    174 	sed -r -e 's,:s0, ,g' $FC | sort -u | \
    175 	/usr/bin/diff -b ${PREFCTEMPFILE} - | \
    176 	    grep '^[<>]'|cut -c3-| grep ^/ | \
    177 	    egrep -v '(^/home|^/root|^/tmp|^/dev)' |\
    178 	sed -r -e 's,[[:blank:]].*,,g' \
    179 	       -e 's|\(([/[:alnum:]]+)\)\?|{\1,}|g' \
    180 	       -e 's|([/[:alnum:]])\?|{\1,}|g' \
    181 	       -e 's|\?.*|*|g' \
    182 	       -e 's|\{.*|*|g' \
    183 	       -e 's|\(.*|*|g' \
    184 	       -e 's|\[.*|*|g' \
    185 	       -e 's|\.\*.*|*|g' \
    186 	       -e 's|\.\+.*|*|g' | \
    187 	    # These two sorts need to be separate commands \
    188 	sort -u | \
    189 	sort -d | \
    190 	while read pattern ; \
    191 	    do if ! echo "$pattern" | grep -q -f ${TEMPFILE} 2>/dev/null; then \
    192 		  echo "$pattern"; \
    193 		  case "$pattern" in *"*") \
    194 		       echo "$pattern" | sed -e 's,^,^,' -e 's,\*$,,g' >> ${TEMPFILE};;
    195 		  esac; \
    196 	       fi; \
    197 	    done | \
    198 	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f -; \
    199 	rm -f ${TEMPFILE} ${PREFCTEMPFILE}
    200 fi
    201 }
    202 
    203 rpmlist() {
    204 rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' '
    205 [ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr
    206 }
    207 
    208 #
    209 # restore
    210 # if called with -n will only check file context
    211 #
    212 restore () {
    213 OPTION=$1
    214 shift
    215 
    216 case "$RESTORE_MODE" in
    217     PREFC)
    218 	diff_filecontext $*
    219 	return
    220     ;;
    221     BOOTTIME)
    222 	newer $BOOTTIME $*
    223 	return
    224     ;;
    225 esac
    226 
    227 [ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon
    228 
    229 EXCLUDEDIRS="`exclude_dirs_from_relabelling`"
    230 LogExcluded
    231 
    232 case "$RESTORE_MODE" in
    233     RPMFILES)
    234 	for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
    235 	    rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f -
    236 	done
    237     ;;
    238     FILEPATH)
    239 	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -R -- "$FILEPATH"
    240     ;;
    241     DEFAULT)
    242 	if [ -n "${FILESYSTEMSRW}" ]; then
    243 	    LogReadOnly
    244 	    echo "${OPTION}ing `echo ${FILESYSTEMSRW}`"
    245 	    ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${FC} ${FILESYSTEMSRW}
    246 	else
    247 	    echo >&2 "fixfiles: No suitable file systems found"
    248 	fi
    249 	if [ ${OPTION} != "Relabel" ]; then
    250 	    return
    251 	fi
    252 	echo "Cleaning up labels on /tmp"
    253 	rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-*
    254 
    255 	UNDEFINED=`get_undefined_type` || exit $?
    256 	UNLABELED=`get_unlabeled_type` || exit $?
    257 	find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete
    258 	find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /tmp {} \;
    259 	find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/tmp {} \;
    260 	find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/run {} \;
    261 	[ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /lib {} \;
    262     ;;
    263 esac
    264 }
    265 
    266 fullrelabel() {
    267     echo "Cleaning out /tmp"
    268     find /tmp/ -mindepth 1 -delete
    269     restore Relabel
    270 }
    271 
    272 
    273 relabel() {
    274     if [ "$RESTORE_MODE" != DEFAULT ]; then
    275 	usage
    276 	exit 1
    277     fi
    278 
    279     if [ $fullFlag == 1  ]; then
    280 	fullrelabel
    281 	return
    282     fi
    283 
    284     echo -n "
    285     Files in the /tmp directory may be labeled incorrectly, this command
    286     can remove all files in /tmp.  If you choose to remove files from /tmp,
    287     a reboot will be required after completion.
    288 
    289     Do you wish to clean out the /tmp directory [N]? "
    290     read answer
    291     if [ "$answer" = y -o  "$answer" = Y ]; then
    292 	fullrelabel
    293     else
    294 	restore Relabel
    295     fi
    296 }
    297 
    298 process() {
    299 #
    300 # Make sure they specified one of the three valid commands
    301 #
    302 case "$1" in
    303     restore) restore Relabel;;
    304     check) VERBOSE="-v"; restore Check -n;;
    305     verify) restore Verify -n;;
    306     relabel) relabel;;
    307     onboot)
    308 	if [ "$RESTORE_MODE" != DEFAULT ]; then
    309 	    usage
    310 	    exit 1
    311 	fi
    312 	> /.autorelabel || exit $?
    313 	[ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel
    314 	[ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel
    315 	# Force full relabel if / does not have a label on it
    316 	getfilecon / > /dev/null 2>&1  || echo -F >/.autorelabel
    317 	echo "System will relabel on next boot"
    318 	;;
    319     *)
    320     usage
    321     exit 1
    322 esac
    323 }
    324 usage() {
    325 	echo $"""
    326 Usage: $0 [-v] [-F] [-f] relabel
    327 or
    328 Usage: $0 [-v] [-F] [-B | -N time ] { check | restore | verify }
    329 or
    330 Usage: $0 [-v] [-F] { check | restore | verify } dir/file ...
    331 or
    332 Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] { check | restore | verify }
    333 or
    334 Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify }
    335 or
    336 Usage: $0 [-F] [-B] onboot
    337 """
    338 }
    339 
    340 if [ $# -eq 0 ]; then
    341 	usage
    342 	exit 1
    343 fi
    344 
    345 set_restore_mode() {
    346 	if [ "$RESTORE_MODE" != DEFAULT ]; then
    347 		# can't specify two different modes
    348 		usage
    349 		exit 1
    350 	fi
    351 	RESTORE_MODE="$1"
    352 }
    353 
    354 # See how we were called.
    355 while getopts "N:BC:FfR:l:v" i; do
    356     case "$i" in
    357 	B)
    358 		BOOTTIME=`/bin/who -b | awk '{print $3}'`
    359 		set_restore_mode BOOTTIME
    360 		;;
    361 	N)
    362 		BOOTTIME=$OPTARG
    363 		set_restore_mode BOOTTIME
    364 		;;
    365 	R)
    366 		RPMFILES=$OPTARG
    367 		set_restore_mode RPMFILES
    368 		;;
    369 	C)
    370 		PREFC=$OPTARG
    371 		set_restore_mode PREFC
    372 		;;
    373 	v)
    374 		VERBOSE="-v"
    375 		;;
    376 	l)
    377 		# Old scripts use obsolete option `-l logfile`
    378 		echo "Redirecting output to $OPTARG"
    379 		exec >>"$OPTARG" 2>&1
    380 		;;
    381 	F)
    382 		FORCEFLAG="-F"
    383 		;;
    384 	f)
    385 		fullFlag=1
    386 		;;
    387 	*)
    388 	    usage
    389 	    exit 1
    390 esac
    391 done
    392 # Move out processed options from arguments
    393 shift $(( OPTIND - 1 ))
    394 
    395 # Check for the command
    396 if [ $# -eq 0 ]; then
    397     usage
    398     exit 1
    399 fi
    400 command="$1"
    401 
    402 # Move out command from arguments
    403 shift
    404 
    405 if [ $# -gt 0 ]; then
    406     set_restore_mode FILEPATH
    407     while [ $# -gt 0 ]; do
    408 	FILEPATH="$1"
    409 	process "$command" || exit $?
    410 	shift
    411     done
    412 else
    413     process "$command"
    414 fi
    415 
    416