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@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 23set -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# 30function 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# 43get_all_labeled_mounts() { 44FS="`cat /proc/self/mounts | sort | uniq | awk '{print $2}'`" 45for 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 52done 53} 54 55get_rw_labeled_mounts() { 56FS=`get_all_labeled_mounts | sort | uniq` 57for i in $FS; do 58 grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)rw(,|$)' && echo $i 59done 60} 61 62get_ro_labeled_mounts() { 63FS=`get_all_labeled_mounts | sort | uniq` 64for i in $FS; do 65 grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)ro(,|$)' && echo $i 66done 67} 68 69# 70# Get the default label returned from the kernel for a file with a label the 71# kernel does not understand 72# 73get_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# 81get_unlabeled_type() { 82 SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'` 83 cat $SELINUXMNT/initial_contexts/file | secon -t 84} 85 86exclude_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# 108fullFlag=0 109BOOTTIME="" 110VERBOSE="-p" 111FORCEFLAG="" 112THREADS="" 113RPMFILES="" 114PREFC="" 115RESTORE_MODE="" 116BIND_MOUNT_FILESYSTEMS="" 117SETFILES=/sbin/setfiles 118RESTORECON=/sbin/restorecon 119FILESYSTEMSRW=`get_rw_labeled_mounts` 120FILESYSTEMSRO=`get_ro_labeled_mounts` 121SELINUXTYPE="targeted" 122if [ -e /etc/selinux/config ]; then 123 . /etc/selinux/config 124 FC=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts 125else 126 FC=/etc/security/selinux/file_contexts 127fi 128 129# 130# Log all Read Only file systems 131# 132LogReadOnly() { 133if [ ! -z "$FILESYSTEMSRO" ]; then 134 echo "Warning: Skipping the following R/O filesystems:" 135 echo "$FILESYSTEMSRO" 136fi 137} 138 139# 140# Log directories excluded from relabelling by configuration file 141# 142LogExcluded() { 143for i in ${EXCLUDEDIRS//-e / }; do 144 echo "skipping the directory $i" 145done 146} 147 148# 149# Find files newer then the passed in date and fix the label 150# 151newer() { 152 DATE=$1 153 shift 154 LogReadOnly 155 for m in `echo $FILESYSTEMSRW`; do 156 find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} ${THREADS} $* -i -0 -f - 157 done; 158} 159 160# 161# Compare PREVious File Context to currently installed File Context and 162# run restorecon on all files affected by the differences. 163# 164diff_filecontext() { 165EXCLUDEDIRS="`exclude_dirs_from_relabelling`" 166for i in /sys /proc /mnt /var/tmp /var/lib/BackupPC /home /root /tmp; do 167 [ -e $i ] && EXCLUDEDIRS="${EXCLUDEDIRS} -e $i"; 168done 169LogExcluded 170 171if [ -f ${PREFC} -a -x /usr/bin/diff ]; then 172 TEMPFILE=`mktemp ${FC}.XXXXXXXXXX` 173 test -z "$TEMPFILE" && exit 174 PREFCTEMPFILE=`mktemp ${PREFC}.XXXXXXXXXX` 175 sed -r -e 's,:s0, ,g' $PREFC | sort -u > ${PREFCTEMPFILE} 176 sed -r -e 's,:s0, ,g' $FC | sort -u | \ 177 /usr/bin/diff -b ${PREFCTEMPFILE} - | \ 178 grep '^[<>]'|cut -c3-| grep ^/ | \ 179 egrep -v '(^/home|^/root|^/tmp)' |\ 180 sed -r -e 's,[[:blank:]].*,,g' \ 181 -e 's|\(([/[:alnum:]]+)\)\?|{\1,}|g' \ 182 -e 's|([/[:alnum:]])\?|{\1,}|g' \ 183 -e 's|\?.*|*|g' \ 184 -e 's|\{.*|*|g' \ 185 -e 's|\(.*|*|g' \ 186 -e 's|\[.*|*|g' \ 187 -e 's|\.\*.*|*|g' \ 188 -e 's|\.\+.*|*|g' | \ 189 # These two sorts need to be separate commands \ 190 sort -u | \ 191 sort -d | \ 192 while read pattern ; \ 193 do if ! echo "$pattern" | grep -q -f ${TEMPFILE} 2>/dev/null; then \ 194 echo "$pattern"; \ 195 case "$pattern" in *"*") \ 196 echo "$pattern" | sed -e 's,^,^,' -e 's,\*$,,g' >> ${TEMPFILE};; 197 esac; \ 198 fi; \ 199 done | \ 200 ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f -; \ 201 rm -f ${TEMPFILE} ${PREFCTEMPFILE} 202fi 203} 204 205rpmlist() { 206rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' ' 207[ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr 208} 209 210# 211# restore 212# if called with -n will only check file context 213# 214restore () { 215OPTION=$1 216shift 217 218# [-B | -N time ] 219if [ -n "$BOOTTIME" ]; then 220 newer $BOOTTIME $* 221 return 222fi 223 224# -C PREVIOUS_FILECONTEXT 225if [ "$RESTORE_MODE" == PREFC ]; then 226 diff_filecontext $* 227 return 228fi 229 230[ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon 231 232EXCLUDEDIRS="`exclude_dirs_from_relabelling`" 233LogExcluded 234 235case "$RESTORE_MODE" in 236 RPMFILES) 237 for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do 238 rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f - 239 done 240 ;; 241 FILEPATH) 242 ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -R -- "$FILEPATH" 243 ;; 244 *) 245 if [ -n "${FILESYSTEMSRW}" ]; then 246 LogReadOnly 247 echo "${OPTION}ing `echo ${FILESYSTEMSRW}`" 248 249 if [ -z "$BIND_MOUNT_FILESYSTEMS" ]; then 250 ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${THREADS} ${FC} ${FILESYSTEMSRW} 251 else 252 # we bind mount so we can fix the labels of files that have already been 253 # mounted over 254 for m in `echo $FILESYSTEMSRW`; do 255 TMP_MOUNT="$(mktemp -d)" 256 test -z ${TMP_MOUNT+x} && echo "Unable to find temporary directory!" && exit 1 257 258 mkdir -p "${TMP_MOUNT}${m}" || exit 1 259 mount --bind "${m}" "${TMP_MOUNT}${m}" || exit 1 260 ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}" 261 umount "${TMP_MOUNT}${m}" || exit 1 262 rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." 263 done; 264 fi 265 else 266 echo >&2 "fixfiles: No suitable file systems found" 267 fi 268 if [ ${OPTION} != "Relabel" ]; then 269 return 270 fi 271 echo "Cleaning up labels on /tmp" 272 rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* 273 274 UNDEFINED=`get_undefined_type` || exit $? 275 UNLABELED=`get_unlabeled_type` || exit $? 276 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete 277 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /tmp {} \; 278 find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/tmp {} \; 279 find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/run {} \; 280 [ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /lib {} \; 281 ;; 282esac 283} 284 285fullrelabel() { 286 echo "Cleaning out /tmp" 287 find /tmp/ -mindepth 1 -delete 288 restore Relabel 289} 290 291 292relabel() { 293 if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then 294 usage 295 exit 1 296 fi 297 298 if [ $fullFlag == 1 ]; then 299 fullrelabel 300 return 301 fi 302 303 echo -n " 304 Files in the /tmp directory may be labeled incorrectly, this command 305 can remove all files in /tmp. If you choose to remove files from /tmp, 306 a reboot will be required after completion. 307 308 Do you wish to clean out the /tmp directory [N]? " 309 read answer 310 if [ "$answer" = y -o "$answer" = Y ]; then 311 fullrelabel 312 else 313 restore Relabel 314 fi 315} 316 317process() { 318# 319# Make sure they specified one of the three valid commands 320# 321case "$1" in 322 restore) restore Relabel;; 323 check) VERBOSE="-v"; restore Check -n;; 324 verify) VERBOSE="-v"; restore Verify -n;; 325 relabel) relabel;; 326 onboot) 327 if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then 328 usage 329 exit 1 330 fi 331 > /.autorelabel || exit $? 332 [ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel 333 [ -z "$BOOTTIME" ] || echo -n "-N $BOOTTIME " >> /.autorelabel 334 [ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo -n "-M " >> /.autorelabel 335 [ -z "$THREADS" ] || echo -n "$THREADS " >> /.autorelabel 336 # Force full relabel if SELinux is not enabled 337 selinuxenabled || echo -F > /.autorelabel 338 echo "System will relabel on next boot" 339 ;; 340 *) 341 usage 342 exit 1 343esac 344} 345usage() { 346 echo $""" 347Usage: $0 [-v] [-F] [-M] [-f] [-T nthreads] relabel 348or 349Usage: $0 [-v] [-F] [-B | -N time ] [-T nthreads] { check | restore | verify } 350or 351Usage: $0 [-v] [-F] [-T nthreads] { check | restore | verify } dir/file ... 352or 353Usage: $0 [-v] [-F] [-T nthreads] -R rpmpackage[,rpmpackage...] { check | restore | verify } 354or 355Usage: $0 [-v] [-F] [-T nthreads] -C PREVIOUS_FILECONTEXT { check | restore | verify } 356or 357Usage: $0 [-F] [-M] [-B] [-T nthreads] onboot 358""" 359} 360 361if [ $# -eq 0 ]; then 362 usage 363 exit 1 364fi 365 366set_restore_mode() { 367 if [ -n "$RESTORE_MODE" ]; then 368 # can't specify two different modes 369 usage 370 exit 1 371 fi 372 RESTORE_MODE="$1" 373} 374 375# See how we were called. 376while getopts "N:BC:FfR:l:vMT:" i; do 377 case "$i" in 378 B) 379 BOOTTIME=`/bin/who -b | awk '{print $3}'` 380 set_restore_mode DEFAULT 381 ;; 382 N) 383 BOOTTIME=$OPTARG 384 set_restore_mode BOOTTIME 385 ;; 386 R) 387 RPMFILES=$OPTARG 388 set_restore_mode RPMFILES 389 ;; 390 C) 391 PREFC=$OPTARG 392 set_restore_mode PREFC 393 ;; 394 v) 395 VERBOSE="-v" 396 ;; 397 l) 398 # Old scripts use obsolete option `-l logfile` 399 echo "Redirecting output to $OPTARG" 400 exec >>"$OPTARG" 2>&1 401 ;; 402 M) 403 BIND_MOUNT_FILESYSTEMS="-M" 404 ;; 405 F) 406 FORCEFLAG="-F" 407 ;; 408 f) 409 fullFlag=1 410 ;; 411 T) 412 THREADS="-T $OPTARG" 413 ;; 414 *) 415 usage 416 exit 1 417esac 418done 419# Move out processed options from arguments 420shift $(( OPTIND - 1 )) 421 422# Check for the command 423if [ $# -eq 0 ]; then 424 usage 425 exit 1 426fi 427command="$1" 428 429# Move out command from arguments 430shift 431 432if [ $# -gt 0 ]; then 433 set_restore_mode FILEPATH 434 while [ $# -gt 0 ]; do 435 FILEPATH="$1" 436 process "$command" || exit $? 437 shift 438 done 439else 440 process "$command" 441fi 442 443