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=`tty`
    123 if [ $? != 0 ]; then
    124     LOGFILE="/dev/null"
    125 fi
    126 LOGGER=/usr/sbin/logger
    127 SETFILES=/sbin/setfiles
    128 RESTORECON=/sbin/restorecon
    129 FILESYSTEMSRW=`get_rw_labeled_mounts`
    130 FILESYSTEMSRO=`get_ro_labeled_mounts`
    131 FILESYSTEMS="$FILESYSTEMSRW $FILESYSTEMSRO"
    132 SELINUXTYPE="targeted"
    133 if [ -e /etc/selinux/config ]; then
    134     . /etc/selinux/config
    135     FC=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts
    136 else
    137     FC=/etc/security/selinux/file_contexts
    138 fi
    139 
    140 #
    141 # Log to either syslog or a LOGFILE
    142 #
    143 logit () {
    144 if [ -n $LOGFILE ]; then
    145     echo $1 >> $LOGFILE
    146 fi
    147 }
    148 #
    149 # Find files newer then the passed in date and fix the label
    150 #
    151 newer() {
    152     DATE=$1
    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 #
    160 # Compare PREVious File Context to currently installed File Context and
    161 # run restorecon on all files affected by the differences.
    162 #
    163 diff_filecontext() {
    164 if [ -f ${PREFC} -a -x /usr/bin/diff ]; then
    165 	TEMPFILE=`mktemp ${FC}.XXXXXXXXXX`
    166 	test -z "$TEMPFILE" && exit
    167 	PREFCTEMPFILE=`mktemp ${PREFC}.XXXXXXXXXX`
    168 	sed -r -e 's,:s0, ,g' $PREFC | sort -u > ${PREFCTEMPFILE}
    169 	sed -r -e 's,:s0, ,g' $FC | sort -u | \
    170 	/usr/bin/diff -b ${PREFCTEMPFILE} - | \
    171 	    grep '^[<>]'|cut -c3-| grep ^/ | \
    172 	    egrep -v '(^/home|^/root|^/tmp|^/dev)' |\
    173 	sed -r -e 's,[[:blank:]].*,,g' \
    174 	       -e 's|\(([/[:alnum:]]+)\)\?|{\1,}|g' \
    175 	       -e 's|([/[:alnum:]])\?|{\1,}|g' \
    176 	       -e 's|\?.*|*|g' \
    177 	       -e 's|\{.*|*|g' \
    178 	       -e 's|\(.*|*|g' \
    179 	       -e 's|\[.*|*|g' \
    180 	       -e 's|\.\*.*|*|g' \
    181 	       -e 's|\.\+.*|*|g' | \
    182 	    # These two sorts need to be separate commands \
    183 	sort -u | \
    184 	sort -d | \
    185 	while read pattern ; \
    186 	    do if ! echo "$pattern" | grep -q -f ${TEMPFILE} 2>/dev/null; then \
    187 		  echo "$pattern"; \
    188 		  case "$pattern" in *"*") \
    189 		       echo "$pattern" | sed -e 's,^,^,' -e 's,\*$,,g' >> ${TEMPFILE};;
    190 		  esac; \
    191 	       fi; \
    192 	    done | \
    193 	${RESTORECON} ${VERBOSE} -i -f - -R `exclude_dirs`; \
    194 	rm -f ${TEMPFILE} ${PREFCTEMPFILE}
    195 fi
    196 }
    197 #
    198 # Log all Read Only file systems
    199 #
    200 LogReadOnly() {
    201 if [ ! -z "$FILESYSTEMSRO" ]; then
    202     logit "Warning: Skipping the following R/O filesystems:"
    203     logit "$FILESYSTEMSRO"
    204 fi
    205 }
    206 
    207 rpmlist() {
    208 rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' '
    209 [ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr
    210 }
    211 
    212 #
    213 # restore
    214 # if called with -n will only check file context
    215 #
    216 restore () {
    217 OPTION=$1
    218 shift
    219 
    220 if [ ! -z "$PREFC" ]; then
    221     diff_filecontext $*
    222     exit $?
    223 fi
    224 if [ ! -z "$BOOTTIME" ]; then
    225     newer $BOOTTIME
    226     exit $?
    227 fi
    228 [ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon
    229 LogReadOnly
    230 #
    231 exclude_dirs="`exclude_dirs_from_relabelling $OPTION`"
    232 if [ -n "${exclude_dirs}" ]
    233 then
    234 	TEMPFCFILE=`mktemp ${FC}.XXXXXXXXXX`
    235 	test -z "$TEMPFCFILE" && exit
    236 	/bin/cp -p ${FC} ${TEMPFCFILE} &>/dev/null || exit
    237 	tmpdirs=${tempdirs//-e/}
    238 	for p in ${tmpdirs}
    239 	do
    240 		p="${p%/}"
    241 		p1="${p}(/.*)? -- <<none>>"
    242 		echo "${p1}" >> $TEMPFCFILE
    243 		logit "skipping the directory ${p}"
    244 	done
    245 FC=$TEMPFCFILE
    246 fi
    247 if [ ! -z "$RPMFILES" ]; then
    248     for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
    249 	rpmlist $i | ${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} $* -R -i -f - 2>&1 | cat >> $LOGFILE
    250     done
    251     exit $?
    252 fi
    253 if [ ! -z "$FILEPATH" ]; then
    254     ${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} -R $* $FILEPATH 2>&1 | cat >> $LOGFILE
    255     return
    256 fi
    257 if [  -n "${FILESYSTEMSRW}" ]; then
    258     echo "${OPTION}ing `echo ${FILESYSTEMSRW}`"
    259     ${SETFILES} ${VERBOSE} $exclude_dirs -q ${FORCEFLAG} $* ${FC} ${FILESYSTEMSRW} 2>&1 | cat >> $LOGFILE
    260 else
    261     echo >&2 "fixfiles: No suitable file systems found"
    262 fi
    263 if [ ${OPTION} != "Relabel" ]; then
    264     return
    265 fi
    266 echo "Cleaning up labels on /tmp"
    267 rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* $TEMPFCFILE
    268 
    269 UNDEFINED=`get_undefined_type` || exit $?
    270 UNLABELED=`get_unlabeled_type` || exit $?
    271 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete
    272 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /tmp {} \;
    273 find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/tmp {} \;
    274 find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/run {} \;
    275 [ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /lib {} \;
    276 exit 0
    277 }
    278 
    279 fullrelabel() {
    280     logit "Cleaning out /tmp"
    281     find /tmp/ -mindepth 1 -delete
    282     LogReadOnly
    283     restore Relabel
    284 }
    285 
    286 relabel() {
    287     if [ ! -z "$RPMFILES" ]; then
    288 	restore Relabel
    289     fi
    290 
    291     if [ $fullFlag == 1  ]; then
    292 	fullrelabel
    293     fi
    294 
    295     echo -n "
    296     Files in the /tmp directory may be labeled incorrectly, this command
    297     can remove all files in /tmp.  If you choose to remove files from /tmp,
    298     a reboot will be required after completion.
    299 
    300     Do you wish to clean out the /tmp directory [N]? "
    301     read answer
    302     if [ "$answer" = y -o  "$answer" = Y ]; then
    303 	fullrelabel
    304     else
    305 	restore Relabel
    306     fi
    307 }
    308 
    309 process() {
    310 #
    311 # Make sure they specified one of the three valid commands
    312 #
    313 case "$1" in
    314     restore) restore Relabel;;
    315     check) VERBOSE="-v"; restore Check -n;;
    316     verify) restore Verify -n -o -;;
    317     relabel) relabel;;
    318     onboot)
    319 	> /.autorelabel
    320 	[ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel
    321 	[ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel
    322 	# Force full relabel if / does not have a label on it
    323 	getfilecon / > /dev/null 2>&1  || echo -F >/.autorelabel
    324 	echo "System will relabel on next boot"
    325 	;;
    326     *)
    327     usage
    328     exit 1
    329 esac
    330 }
    331 usage() {
    332 	echo $"""
    333 Usage: $0 [-v] [-F]  [-N time ] [-l logfile ] { check | restore| [-f] relabel | verify } [[dir/file] ... ]
    334 or
    335 Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] [-l logfile ] { check | restore | verify }
    336 or
    337 Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify }
    338 or
    339 Usage: $0 [-F] [-B] onboot
    340 """
    341 }
    342 
    343 if [ $# = 0 ]; then
    344 	usage
    345 	exit 1
    346 fi
    347 
    348 # See how we were called.
    349 while getopts "N:BC:FfR:l:v" i; do
    350     case "$i" in
    351 	B)
    352 		BOOTTIME=`/bin/who -b | awk '{print $3}'`
    353 		;;
    354 	f)
    355 		fullFlag=1
    356 		;;
    357 	v)
    358 		VERBOSE="-v"
    359 		;;
    360 	R)
    361 		RPMFILES=$OPTARG
    362 		;;
    363 	l)
    364 		LOGFILE=$OPTARG
    365 		;;
    366 	C)
    367 		PREFC=$OPTARG
    368 		;;
    369 	F)
    370 		FORCEFLAG="-F"
    371 		;;
    372 	N)
    373 		BOOTTIME=$OPTARG
    374 		;;
    375 	*)
    376 	    usage
    377 	    exit 1
    378 esac
    379 done
    380 # Move out processed options from arguments
    381 shift $(( OPTIND - 1 ))
    382 
    383 # Check for the command
    384 command=$1
    385 if [ -z $command ]; then
    386     usage
    387 fi
    388 
    389 # Move out command from arguments
    390 shift
    391 
    392 #
    393 # check if they specified both DIRS and RPMFILES
    394 #
    395 
    396 if [ ! -z "$RPMFILES" ]; then
    397     process $command
    398     if [ $# -gt 0 ]; then
    399 	    usage
    400     fi
    401 else
    402     if [ -z "$1" ]; then
    403 	process $command
    404     else
    405 	while [ -n "$1" ]; do
    406 	    FILEPATH=$1
    407 	    process $command
    408 	    shift
    409 	done
    410     fi
    411 fi
    412 exit $?
    413