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 <[email protected]> 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}' | grep -E --silent '(^|,)seclabel(,|$)' && echo $i 49 else 50 grep " $i " /proc/self/mounts | grep -v "context=" | grep -E --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}' | grep -E --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}' | grep -E --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 grep -Ev '(^/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# unmount tmp bind mount before exit 211umount_TMP_MOUNT() { 212 if [ -n "$TMP_MOUNT" ]; then 213 umount "${TMP_MOUNT}${m}" || exit 130 214 rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." 215 fi 216 exit 130 217} 218 219fix_labels_on_mountpoint() { 220 test -z ${TMP_MOUNT+x} && echo "Unable to find temporary directory!" && exit 1 221 mkdir -p "${TMP_MOUNT}${m}" || exit 1 222 mount --bind "${m}" "${TMP_MOUNT}${m}" || exit 1 223 ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}" 224 umount "${TMP_MOUNT}${m}" || exit 1 225 rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." 226} 227export -f fix_labels_on_mountpoint 228 229# 230# restore 231# if called with -n will only check file context 232# 233restore () { 234OPTION=$1 235shift 236 237# [-B | -N time ] 238if [ -n "$BOOTTIME" ]; then 239 newer $BOOTTIME $* 240 return 241fi 242 243# -C PREVIOUS_FILECONTEXT 244if [ "$RESTORE_MODE" == PREFC ]; then 245 diff_filecontext $* 246 return 247fi 248 249[ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon 250 251EXCLUDEDIRS="`exclude_dirs_from_relabelling`" 252LogExcluded 253 254case "$RESTORE_MODE" in 255 RPMFILES) 256 for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do 257 rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f - 258 done 259 ;; 260 FILEPATH) 261 ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -R -- "$FILEPATH" 262 ;; 263 *) 264 if [ -n "${FILESYSTEMSRW}" ]; then 265 LogReadOnly 266 echo "${OPTION}ing `echo ${FILESYSTEMSRW}`" 267 268 if [ -z "$BIND_MOUNT_FILESYSTEMS" ]; then 269 ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${THREADS} ${FC} ${FILESYSTEMSRW} 270 else 271 # we bind mount so we can fix the labels of files that have already been 272 # mounted over 273 for m in `echo $FILESYSTEMSRW`; do 274 TMP_MOUNT="$(mktemp -p /run -d fixfiles.XXXXXXXXXX)" 275 export SETFILES VERBOSE EXCLUDEDIRS FORCEFLAG THREADS FC TMP_MOUNT m 276 if type unshare &> /dev/null; then 277 unshare -m bash -c "fix_labels_on_mountpoint $*" || exit $? 278 else 279 trap umount_TMP_MOUNT EXIT 280 fix_labels_on_mountpoint $* 281 trap EXIT 282 fi 283 done; 284 fi 285 else 286 echo >&2 "fixfiles: No suitable file systems found" 287 fi 288 if [ ${OPTION} != "Relabel" ]; then 289 return 290 fi 291 echo "Cleaning up labels on /tmp" 292 rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* 293 294 UNDEFINED=`get_undefined_type` || exit $? 295 UNLABELED=`get_unlabeled_type` || exit $? 296 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete 297 find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /tmp {} \; 298 find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/tmp {} \; 299 find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/run {} \; 300 [ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /lib {} \; 301 ;; 302esac 303} 304 305fullrelabel() { 306 echo "Cleaning out /tmp" 307 find /tmp/ -mindepth 1 -delete 308 restore Relabel 309} 310 311 312relabel() { 313 if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then 314 usage 315 exit 1 316 fi 317 318 if [ $fullFlag == 1 ]; then 319 fullrelabel 320 return 321 fi 322 323 echo -n " 324 Files in the /tmp directory may be labeled incorrectly, this command 325 can remove all files in /tmp. If you choose to remove files from /tmp, 326 a reboot will be required after completion. 327 328 Do you wish to clean out the /tmp directory [N]? " 329 read answer 330 if [ "$answer" = y -o "$answer" = Y ]; then 331 fullrelabel 332 else 333 restore Relabel 334 fi 335} 336 337process() { 338# 339# Make sure they specified one of the three valid commands 340# 341case "$1" in 342 restore) restore Relabel;; 343 check) VERBOSE="-v"; restore Check -n;; 344 verify) VERBOSE="-v"; restore Verify -n;; 345 relabel) relabel;; 346 onboot) 347 if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then 348 usage 349 exit 1 350 fi 351 > /.autorelabel || exit $? 352 [ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel 353 [ -z "$BOOTTIME" ] || echo -n "-N $BOOTTIME " >> /.autorelabel 354 [ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo -n "-M " >> /.autorelabel 355 [ -z "$THREADS" ] || echo -n "$THREADS " >> /.autorelabel 356 # Force full relabel if SELinux is not enabled 357 selinuxenabled || echo -F > /.autorelabel 358 echo "System will relabel on next boot" 359 ;; 360 *) 361 usage 362 exit 1 363esac 364} 365usage() { 366 echo $""" 367Usage: $0 [-v] [-F] [-M] [-f] [-T nthreads] relabel 368or 369Usage: $0 [-v] [-F] [-B | -N time ] [-T nthreads] { check | restore | verify } 370or 371Usage: $0 [-v] [-F] [-T nthreads] { check | restore | verify } dir/file ... 372or 373Usage: $0 [-v] [-F] [-T nthreads] -R rpmpackage[,rpmpackage...] { check | restore | verify } 374or 375Usage: $0 [-v] [-F] [-T nthreads] -C PREVIOUS_FILECONTEXT { check | restore | verify } 376or 377Usage: $0 [-F] [-M] [-B] [-T nthreads] onboot 378""" 379} 380 381if [ $# -eq 0 ]; then 382 usage 383 exit 1 384fi 385 386set_restore_mode() { 387 if [ -n "$RESTORE_MODE" ]; then 388 # can't specify two different modes 389 usage 390 exit 1 391 fi 392 RESTORE_MODE="$1" 393} 394 395# See how we were called. 396while getopts "N:BC:FfR:l:vMT:" i; do 397 case "$i" in 398 B) 399 BOOTTIME=`/bin/who -b | awk '{print $3}'` 400 set_restore_mode DEFAULT 401 ;; 402 N) 403 BOOTTIME=$OPTARG 404 set_restore_mode BOOTTIME 405 ;; 406 R) 407 RPMFILES=$OPTARG 408 set_restore_mode RPMFILES 409 ;; 410 C) 411 PREFC=$OPTARG 412 set_restore_mode PREFC 413 ;; 414 v) 415 VERBOSE="-v" 416 ;; 417 l) 418 # Old scripts use obsolete option `-l logfile` 419 echo "Redirecting output to $OPTARG" 420 exec >>"$OPTARG" 2>&1 421 ;; 422 M) 423 BIND_MOUNT_FILESYSTEMS="-M" 424 ;; 425 F) 426 FORCEFLAG="-F" 427 ;; 428 f) 429 fullFlag=1 430 ;; 431 T) 432 THREADS="-T $OPTARG" 433 ;; 434 *) 435 usage 436 exit 1 437esac 438done 439# Move out processed options from arguments 440shift $(( OPTIND - 1 )) 441 442# Check for the command 443if [ $# -eq 0 ]; then 444 usage 445 exit 1 446fi 447command="$1" 448 449# Move out command from arguments 450shift 451 452if [ $# -gt 0 ]; then 453 set_restore_mode FILEPATH 454 while [ $# -gt 0 ]; do 455 FILEPATH="$1" 456 process "$command" || exit $? 457 shift 458 done 459else 460 process "$command" 461fi 462 463