| #!/bin/bash |
| # fixfiles |
| # |
| # Script to restore labels on a SELinux box |
| # |
| # Copyright (C) 2004-2013 Red Hat, Inc. |
| # Authors: Dan Walsh <[email protected]> |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| set -o nounset |
| |
| # |
| # seclabel support was added in 2.6.30. This function will return a positive |
| # number if the current kernel version is greater than 2.6.30, a negative |
| # number if the current is less than 2.6.30 and 0 if they are the same. |
| # |
| function useseclabel { |
| VER=`uname -r` |
| SUP=2.6.30 |
| expr '(' "$VER" : '\([^.]*\)' ')' '-' '(' "$SUP" : '\([^.]*\)' ')' '|' \ |
| '(' "$VER.0" : '[^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0" : '[^.]*[.]\([^.]*\)' ')' '|' \ |
| '(' "$VER.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')' |
| } |
| |
| # |
| # Get all mount points that support labeling. Use the 'seclabel' field if it |
| # is available. Else fall back to known fs types which likely support xattrs |
| # and we know were not context mounted. |
| # |
| get_all_labeled_mounts() { |
| FS="`cat /proc/self/mounts | sort | uniq | awk '{print $2}'`" |
| for i in $FS; do |
| if [ `useseclabel` -ge 0 ] |
| then |
| grep " $i " /proc/self/mounts | awk '{print $4}' | grep -E --silent '(^|,)seclabel(,|$)' && echo $i |
| else |
| grep " $i " /proc/self/mounts | grep -v "context=" | grep -E --silent '(ext[234]| ext4dev | gfs2 | xfs | jfs | btrfs )' && echo $i |
| fi |
| done |
| } |
| |
| get_rw_labeled_mounts() { |
| FS=`get_all_labeled_mounts | sort | uniq` |
| for i in $FS; do |
| grep " $i " /proc/self/mounts | awk '{print $4}' | grep -E --silent '(^|,)rw(,|$)' && echo $i |
| done |
| } |
| |
| get_ro_labeled_mounts() { |
| FS=`get_all_labeled_mounts | sort | uniq` |
| for i in $FS; do |
| grep " $i " /proc/self/mounts | awk '{print $4}' | grep -E --silent '(^|,)ro(,|$)' && echo $i |
| done |
| } |
| |
| # |
| # Get the default label returned from the kernel for a file with a label the |
| # kernel does not understand |
| # |
| get_undefined_type() { |
| SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'` |
| cat ${SELINUXMNT}/initial_contexts/unlabeled | secon -t |
| } |
| |
| # |
| # Get the default label for a file without a label |
| # |
| get_unlabeled_type() { |
| SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'` |
| cat $SELINUXMNT/initial_contexts/file | secon -t |
| } |
| |
| exclude_dirs_from_relabelling() { |
| exclude_from_relabelling= |
| if [ -e /etc/selinux/fixfiles_exclude_dirs ] |
| then |
| while read i |
| do |
| # skip blank line and comment |
| # skip not absolute path |
| # skip not directory |
| [ -z "${i}" ] && continue |
| [[ "${i}" =~ ^[[:blank:]]*# ]] && continue |
| [[ ! "${i}" =~ ^/.* ]] && continue |
| [[ ! -d "${i}" ]] && continue |
| exclude_from_relabelling="$exclude_from_relabelling -e $i" |
| done < /etc/selinux/fixfiles_exclude_dirs |
| fi |
| echo "$exclude_from_relabelling" |
| } |
| |
| # |
| # Set global Variables |
| # |
| fullFlag=0 |
| BOOTTIME="" |
| VERBOSE="-p" |
| FORCEFLAG="" |
| THREADS="" |
| RPMFILES="" |
| PREFC="" |
| RESTORE_MODE="" |
| BIND_MOUNT_FILESYSTEMS="" |
| SETFILES=/sbin/setfiles |
| RESTORECON=/sbin/restorecon |
| FILESYSTEMSRW=`get_rw_labeled_mounts` |
| FILESYSTEMSRO=`get_ro_labeled_mounts` |
| SELINUXTYPE="targeted" |
| if [ -e /etc/selinux/config ]; then |
| . /etc/selinux/config |
| FC=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts |
| else |
| FC=/etc/security/selinux/file_contexts |
| fi |
| |
| # |
| # Log all Read Only file systems |
| # |
| LogReadOnly() { |
| if [ ! -z "$FILESYSTEMSRO" ]; then |
| echo "Warning: Skipping the following R/O filesystems:" |
| echo "$FILESYSTEMSRO" |
| fi |
| } |
| |
| # |
| # Log directories excluded from relabelling by configuration file |
| # |
| LogExcluded() { |
| for i in ${EXCLUDEDIRS//-e / }; do |
| echo "skipping the directory $i" |
| done |
| } |
| |
| # |
| # Find files newer then the passed in date and fix the label |
| # |
| newer() { |
| DATE=$1 |
| shift |
| LogReadOnly |
| for m in `echo $FILESYSTEMSRW`; do |
| find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} ${THREADS} $* -i -0 -f - |
| done; |
| } |
| |
| # |
| # Compare PREVious File Context to currently installed File Context and |
| # run restorecon on all files affected by the differences. |
| # |
| diff_filecontext() { |
| EXCLUDEDIRS="`exclude_dirs_from_relabelling`" |
| for i in /sys /proc /mnt /var/tmp /var/lib/BackupPC /home /root /tmp; do |
| [ -e $i ] && EXCLUDEDIRS="${EXCLUDEDIRS} -e $i"; |
| done |
| LogExcluded |
| |
| if [ -f ${PREFC} -a -x /usr/bin/diff ]; then |
| TEMPFILE=`mktemp ${FC}.XXXXXXXXXX` |
| test -z "$TEMPFILE" && exit |
| PREFCTEMPFILE=`mktemp ${PREFC}.XXXXXXXXXX` |
| sed -r -e 's,:s0, ,g' $PREFC | sort -u > ${PREFCTEMPFILE} |
| sed -r -e 's,:s0, ,g' $FC | sort -u | \ |
| /usr/bin/diff -b ${PREFCTEMPFILE} - | \ |
| grep '^[<>]'|cut -c3-| grep ^/ | \ |
| grep -Ev '(^/home|^/root|^/tmp)' |\ |
| sed -r -e 's,[[:blank:]].*,,g' \ |
| -e 's|\(([/[:alnum:]]+)\)\?|{\1,}|g' \ |
| -e 's|([/[:alnum:]])\?|{\1,}|g' \ |
| -e 's|\?.*|*|g' \ |
| -e 's|\{.*|*|g' \ |
| -e 's|\(.*|*|g' \ |
| -e 's|\[.*|*|g' \ |
| -e 's|\.\*.*|*|g' \ |
| -e 's|\.\+.*|*|g' | \ |
| # These two sorts need to be separate commands \ |
| sort -u | \ |
| sort -d | \ |
| while read pattern ; \ |
| do if ! echo "$pattern" | grep -q -f ${TEMPFILE} 2>/dev/null; then \ |
| echo "$pattern"; \ |
| case "$pattern" in *"*") \ |
| echo "$pattern" | sed -e 's,^,^,' -e 's,\*$,,g' >> ${TEMPFILE};; |
| esac; \ |
| fi; \ |
| done | \ |
| ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f -; \ |
| rm -f ${TEMPFILE} ${PREFCTEMPFILE} |
| fi |
| } |
| |
| rpmlist() { |
| rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' ' |
| [ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr |
| } |
| |
| # unmount tmp bind mount before exit |
| umount_TMP_MOUNT() { |
| if [ -n "$TMP_MOUNT" ]; then |
| umount "${TMP_MOUNT}${m}" || exit 130 |
| rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." |
| fi |
| exit 130 |
| } |
| |
| fix_labels_on_mountpoint() { |
| test -z ${TMP_MOUNT+x} && echo "Unable to find temporary directory!" && exit 1 |
| mkdir -p "${TMP_MOUNT}${m}" || exit 1 |
| mount --bind "${m}" "${TMP_MOUNT}${m}" || exit 1 |
| ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}" |
| umount "${TMP_MOUNT}${m}" || exit 1 |
| rm -rf "${TMP_MOUNT}" || echo "Error cleaning up." |
| } |
| export -f fix_labels_on_mountpoint |
| |
| # |
| # restore |
| # if called with -n will only check file context |
| # |
| restore () { |
| OPTION=$1 |
| shift |
| |
| # [-B | -N time ] |
| if [ -n "$BOOTTIME" ]; then |
| newer $BOOTTIME $* |
| return |
| fi |
| |
| # -C PREVIOUS_FILECONTEXT |
| if [ "$RESTORE_MODE" == PREFC ]; then |
| diff_filecontext $* |
| return |
| fi |
| |
| [ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon |
| |
| EXCLUDEDIRS="`exclude_dirs_from_relabelling`" |
| LogExcluded |
| |
| case "$RESTORE_MODE" in |
| RPMFILES) |
| for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do |
| rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f - |
| done |
| ;; |
| FILEPATH) |
| ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -R -- "$FILEPATH" |
| ;; |
| *) |
| if [ -n "${FILESYSTEMSRW}" ]; then |
| LogReadOnly |
| echo "${OPTION}ing `echo ${FILESYSTEMSRW}`" |
| |
| if [ -z "$BIND_MOUNT_FILESYSTEMS" ]; then |
| ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${THREADS} ${FC} ${FILESYSTEMSRW} |
| else |
| # we bind mount so we can fix the labels of files that have already been |
| # mounted over |
| for m in `echo $FILESYSTEMSRW`; do |
| TMP_MOUNT="$(mktemp -p /run -d fixfiles.XXXXXXXXXX)" |
| export SETFILES VERBOSE EXCLUDEDIRS FORCEFLAG THREADS FC TMP_MOUNT m |
| if type unshare &> /dev/null; then |
| unshare -m bash -c "fix_labels_on_mountpoint $*" || exit $? |
| else |
| trap umount_TMP_MOUNT EXIT |
| fix_labels_on_mountpoint $* |
| trap EXIT |
| fi |
| done; |
| fi |
| else |
| echo >&2 "fixfiles: No suitable file systems found" |
| fi |
| if [ ${OPTION} != "Relabel" ]; then |
| return |
| fi |
| echo "Cleaning up labels on /tmp" |
| rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* |
| |
| UNDEFINED=`get_undefined_type` || exit $? |
| UNLABELED=`get_unlabeled_type` || exit $? |
| find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete |
| find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /tmp {} \; |
| find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/tmp {} \; |
| find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /var/run {} \; |
| [ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --no-dereference --reference /lib {} \; |
| ;; |
| esac |
| } |
| |
| fullrelabel() { |
| echo "Cleaning out /tmp" |
| find /tmp/ -mindepth 1 -delete |
| restore Relabel |
| } |
| |
| |
| relabel() { |
| if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then |
| usage |
| exit 1 |
| fi |
| |
| if [ $fullFlag == 1 ]; then |
| fullrelabel |
| return |
| fi |
| |
| echo -n " |
| Files in the /tmp directory may be labeled incorrectly, this command |
| can remove all files in /tmp. If you choose to remove files from /tmp, |
| a reboot will be required after completion. |
| |
| Do you wish to clean out the /tmp directory [N]? " |
| read answer |
| if [ "$answer" = y -o "$answer" = Y ]; then |
| fullrelabel |
| else |
| restore Relabel |
| fi |
| } |
| |
| process() { |
| # |
| # Make sure they specified one of the three valid commands |
| # |
| case "$1" in |
| restore) restore Relabel;; |
| check) VERBOSE="-v"; restore Check -n;; |
| verify) VERBOSE="-v"; restore Verify -n;; |
| relabel) relabel;; |
| onboot) |
| if [ -n "$RESTORE_MODE" -a "$RESTORE_MODE" != DEFAULT ]; then |
| usage |
| exit 1 |
| fi |
| > /.autorelabel || exit $? |
| [ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel |
| [ -z "$BOOTTIME" ] || echo -n "-N $BOOTTIME " >> /.autorelabel |
| [ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo -n "-M " >> /.autorelabel |
| [ -z "$THREADS" ] || echo -n "$THREADS " >> /.autorelabel |
| # Force full relabel if SELinux is not enabled |
| selinuxenabled || echo -F > /.autorelabel |
| echo "System will relabel on next boot" |
| ;; |
| *) |
| usage |
| exit 1 |
| esac |
| } |
| usage() { |
| echo $""" |
| Usage: $0 [-v] [-F] [-M] [-f] [-T nthreads] relabel |
| or |
| Usage: $0 [-v] [-F] [-B | -N time ] [-T nthreads] { check | restore | verify } |
| or |
| Usage: $0 [-v] [-F] [-T nthreads] { check | restore | verify } dir/file ... |
| or |
| Usage: $0 [-v] [-F] [-T nthreads] -R rpmpackage[,rpmpackage...] { check | restore | verify } |
| or |
| Usage: $0 [-v] [-F] [-T nthreads] -C PREVIOUS_FILECONTEXT { check | restore | verify } |
| or |
| Usage: $0 [-F] [-M] [-B] [-T nthreads] onboot |
| """ |
| } |
| |
| if [ $# -eq 0 ]; then |
| usage |
| exit 1 |
| fi |
| |
| set_restore_mode() { |
| if [ -n "$RESTORE_MODE" ]; then |
| # can't specify two different modes |
| usage |
| exit 1 |
| fi |
| RESTORE_MODE="$1" |
| } |
| |
| # See how we were called. |
| while getopts "N:BC:FfR:l:vMT:" i; do |
| case "$i" in |
| B) |
| BOOTTIME=`/bin/who -b | awk '{print $3}'` |
| set_restore_mode DEFAULT |
| ;; |
| N) |
| BOOTTIME=$OPTARG |
| set_restore_mode BOOTTIME |
| ;; |
| R) |
| RPMFILES=$OPTARG |
| set_restore_mode RPMFILES |
| ;; |
| C) |
| PREFC=$OPTARG |
| set_restore_mode PREFC |
| ;; |
| v) |
| VERBOSE="-v" |
| ;; |
| l) |
| # Old scripts use obsolete option `-l logfile` |
| echo "Redirecting output to $OPTARG" |
| exec >>"$OPTARG" 2>&1 |
| ;; |
| M) |
| BIND_MOUNT_FILESYSTEMS="-M" |
| ;; |
| F) |
| FORCEFLAG="-F" |
| ;; |
| f) |
| fullFlag=1 |
| ;; |
| T) |
| THREADS="-T $OPTARG" |
| ;; |
| *) |
| usage |
| exit 1 |
| esac |
| done |
| # Move out processed options from arguments |
| shift $(( OPTIND - 1 )) |
| |
| # Check for the command |
| if [ $# -eq 0 ]; then |
| usage |
| exit 1 |
| fi |
| command="$1" |
| |
| # Move out command from arguments |
| shift |
| |
| if [ $# -gt 0 ]; then |
| set_restore_mode FILEPATH |
| while [ $# -gt 0 ]; do |
| FILEPATH="$1" |
| process "$command" || exit $? |
| shift |
| done |
| else |
| process "$command" |
| fi |
| |