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