selinux/policycoreutils/scripts/fixfiles

410 lines
10 KiB
Plaintext
Raw Normal View History

#!/bin/bash
# fixfiles
#
# Script to restore labels on a SELinux box
#
# Copyright (C) 2004-2013 Red Hat, Inc.
# Authors: Dan Walsh <dwalsh@redhat.com>
#
# 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
#
# 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}' | egrep --silent '(^|,)seclabel(,|$)' && echo $i
else
grep " $i " /proc/self/mounts | grep -v "context=" | egrep --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}' | egrep --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}' | egrep --silent '(^|,)ro(,|$)' && echo $i
done
}
#
# Get the default label returned from the kernel for a file with a lable 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"
logit "skipping the directory $i"
done < /etc/selinux/fixfiles_exclude_dirs
fi
echo "$exclude_from_relabelling"
}
exclude_dirs() {
exclude=
for i in /sys /proc /dev /run /mnt /var/tmp /var/lib/BackupPC /home /tmp /dev; do
[ -e $i ] && exclude="$exclude -e $i";
done
exclude="$exclude `exclude_dirs_from_relabelling`"
echo "$exclude"
}
#
# Set global Variables
#
fullFlag=0
BOOTTIME=""
VERBOSE="-p"
FORCEFLAG=""
DIRS=""
RPMILES=""
policycoreutils: let output of `fixfiles` be redirected (as normal) fixfiles was redirecting log output to `tty`. This overrides user intent e.g. when shell redirection is used. Redirect it to stdout, using /proc. `tty` equally depended on /proc. We do not depend on /dev/stdout: it might not be present, if a rescue system is booted with devtmpfs (no udev daemon). By default, log messages were redirected into the void when not run from a tty. We consider this a bug, which is now fixed. 1. If calling scripts happen to require the old behaviour, they can easily write the same code themselves. 2. When fixfiles is run from Fedora's selinux-autorelabel.service, the calling script is specifically run from a tty. Also Fedora's calling script chooses to redirect stdout and stderr to /dev/null. This redirection will now suceed, improving the transparency of the code. The previous behaviour may be obtained by choosing not to redirect the progress messages of this long-running process to /dev/null. A patch has been submitted to Fedora to suggest this novel approach: https://bugzilla.redhat.com/show_bug.cgi?id=1415674 Complete disclosure: * Remove unused variable LOGGER. * Fix logfiles containing spaces. Disclaimer: 1. "Log" output may contain escape sequences (backspace?) e.g. in `fixfiles -l log.txt restore`. This is not the usual understanding of a log file. 2. For some reason, not all informative messages are sent to `-l` e.g. the list of filesystems, and "cleaning up labels on /tmp". 3. `function logit` is retained, but the logfile is also written to outside this function. Implementing support for the system log would require another function which accepts piped input. Also see point 1. Signed-off-by: Alan Jenkins <alan.christopher.jenkins@gmail.com>
2017-01-23 12:15:50 +00:00
LOGFILE=/proc/self/fd/1
SETFILES=/sbin/setfiles
RESTORECON=/sbin/restorecon
FILESYSTEMSRW=`get_rw_labeled_mounts`
FILESYSTEMSRO=`get_ro_labeled_mounts`
FILESYSTEMS="$FILESYSTEMSRW $FILESYSTEMSRO"
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
#
policycoreutils: let output of `fixfiles` be redirected (as normal) fixfiles was redirecting log output to `tty`. This overrides user intent e.g. when shell redirection is used. Redirect it to stdout, using /proc. `tty` equally depended on /proc. We do not depend on /dev/stdout: it might not be present, if a rescue system is booted with devtmpfs (no udev daemon). By default, log messages were redirected into the void when not run from a tty. We consider this a bug, which is now fixed. 1. If calling scripts happen to require the old behaviour, they can easily write the same code themselves. 2. When fixfiles is run from Fedora's selinux-autorelabel.service, the calling script is specifically run from a tty. Also Fedora's calling script chooses to redirect stdout and stderr to /dev/null. This redirection will now suceed, improving the transparency of the code. The previous behaviour may be obtained by choosing not to redirect the progress messages of this long-running process to /dev/null. A patch has been submitted to Fedora to suggest this novel approach: https://bugzilla.redhat.com/show_bug.cgi?id=1415674 Complete disclosure: * Remove unused variable LOGGER. * Fix logfiles containing spaces. Disclaimer: 1. "Log" output may contain escape sequences (backspace?) e.g. in `fixfiles -l log.txt restore`. This is not the usual understanding of a log file. 2. For some reason, not all informative messages are sent to `-l` e.g. the list of filesystems, and "cleaning up labels on /tmp". 3. `function logit` is retained, but the logfile is also written to outside this function. Implementing support for the system log would require another function which accepts piped input. Also see point 1. Signed-off-by: Alan Jenkins <alan.christopher.jenkins@gmail.com>
2017-01-23 12:15:50 +00:00
# Write to LOGFILE
#
logit () {
if [ -n $LOGFILE ]; then
policycoreutils: let output of `fixfiles` be redirected (as normal) fixfiles was redirecting log output to `tty`. This overrides user intent e.g. when shell redirection is used. Redirect it to stdout, using /proc. `tty` equally depended on /proc. We do not depend on /dev/stdout: it might not be present, if a rescue system is booted with devtmpfs (no udev daemon). By default, log messages were redirected into the void when not run from a tty. We consider this a bug, which is now fixed. 1. If calling scripts happen to require the old behaviour, they can easily write the same code themselves. 2. When fixfiles is run from Fedora's selinux-autorelabel.service, the calling script is specifically run from a tty. Also Fedora's calling script chooses to redirect stdout and stderr to /dev/null. This redirection will now suceed, improving the transparency of the code. The previous behaviour may be obtained by choosing not to redirect the progress messages of this long-running process to /dev/null. A patch has been submitted to Fedora to suggest this novel approach: https://bugzilla.redhat.com/show_bug.cgi?id=1415674 Complete disclosure: * Remove unused variable LOGGER. * Fix logfiles containing spaces. Disclaimer: 1. "Log" output may contain escape sequences (backspace?) e.g. in `fixfiles -l log.txt restore`. This is not the usual understanding of a log file. 2. For some reason, not all informative messages are sent to `-l` e.g. the list of filesystems, and "cleaning up labels on /tmp". 3. `function logit` is retained, but the logfile is also written to outside this function. Implementing support for the system log would require another function which accepts piped input. Also see point 1. Signed-off-by: Alan Jenkins <alan.christopher.jenkins@gmail.com>
2017-01-23 12:15:50 +00:00
echo $1 >> "$LOGFILE"
fi
}
#
# Find files newer then the passed in date and fix the label
#
newer() {
DATE=$1
shift
for m in `echo $FILESYSTEMSRW`; do
find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} $* -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() {
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 ^/ | \
egrep -v '(^/home|^/root|^/tmp|^/dev)' |\
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} -i -f - -R $* `exclude_dirs`; \
rm -f ${TEMPFILE} ${PREFCTEMPFILE}
fi
}
#
# Log all Read Only file systems
#
LogReadOnly() {
if [ ! -z "$FILESYSTEMSRO" ]; then
logit "Warning: Skipping the following R/O filesystems:"
logit "$FILESYSTEMSRO"
fi
}
rpmlist() {
rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' '
[ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr
}
#
# restore
# if called with -n will only check file context
#
restore () {
OPTION=$1
shift
if [ ! -z "$PREFC" ]; then
diff_filecontext $*
exit $?
fi
if [ ! -z "$BOOTTIME" ]; then
newer $BOOTTIME $*
exit $?
fi
[ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon
LogReadOnly
#
exclude_dirs="`exclude_dirs_from_relabelling $OPTION`"
if [ -n "${exclude_dirs}" ]
then
TEMPFCFILE=`mktemp ${FC}.XXXXXXXXXX`
test -z "$TEMPFCFILE" && exit
/bin/cp -p ${FC} ${TEMPFCFILE} &>/dev/null || exit
tmpdirs=${tempdirs//-e/}
for p in ${tmpdirs}
do
p="${p%/}"
p1="${p}(/.*)? -- <<none>>"
echo "${p1}" >> $TEMPFCFILE
logit "skipping the directory ${p}"
done
FC=$TEMPFCFILE
fi
if [ ! -z "$RPMFILES" ]; then
for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
rpmlist $i | ${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} $* -R -i -f - 2>&1 | cat >> $LOGFILE
done
exit $?
fi
if [ ! -z "$FILEPATH" ]; then
${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} -R $* $FILEPATH 2>&1 | cat >> $LOGFILE
return
fi
if [ -n "${FILESYSTEMSRW}" ]; then
echo "${OPTION}ing `echo ${FILESYSTEMSRW}`"
${SETFILES} ${VERBOSE} $exclude_dirs -q ${FORCEFLAG} $* ${FC} ${FILESYSTEMSRW} 2>&1 | cat >> $LOGFILE
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-* $TEMPFCFILE
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 --reference /tmp {} \;
find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/tmp {} \;
find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/run {} \;
[ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /lib {} \;
exit 0
}
fullrelabel() {
logit "Cleaning out /tmp"
find /tmp/ -mindepth 1 -delete
LogReadOnly
restore Relabel
}
relabel() {
if [ ! -z "$RPMFILES" ]; then
restore Relabel
fi
if [ $fullFlag == 1 ]; then
fullrelabel
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) restore Verify -n -o -;;
relabel) relabel;;
onboot)
> /.autorelabel
[ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel
[ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel
# Force full relabel if / does not have a label on it
getfilecon / > /dev/null 2>&1 || echo -F >/.autorelabel
echo "System will relabel on next boot"
;;
*)
usage
exit 1
esac
}
usage() {
echo $"""
Usage: $0 [-v] [-F] [-N time ] [-l logfile ] { check | restore| [-f] relabel | verify } [[dir/file] ... ]
or
Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] [-l logfile ] { check | restore | verify }
or
Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify }
or
Usage: $0 [-F] [-B] onboot
"""
}
if [ $# = 0 ]; then
usage
exit 1
fi
# See how we were called.
while getopts "N:BC:FfR:l:v" i; do
case "$i" in
B)
BOOTTIME=`/bin/who -b | awk '{print $3}'`
;;
f)
fullFlag=1
;;
v)
VERBOSE="-v"
;;
R)
RPMFILES=$OPTARG
;;
l)
LOGFILE=$OPTARG
;;
C)
PREFC=$OPTARG
;;
F)
FORCEFLAG="-F"
;;
N)
BOOTTIME=$OPTARG
;;
*)
usage
exit 1
esac
done
# Move out processed options from arguments
shift $(( OPTIND - 1 ))
# Check for the command
command=$1
if [ -z $command ]; then
usage
fi
# Move out command from arguments
shift
#
# check if they specified both DIRS and RPMFILES
#
if [ ! -z "$RPMFILES" ]; then
process $command
if [ $# -gt 0 ]; then
usage
fi
else
if [ -z "$1" ]; then
process $command
else
while [ -n "$1" ]; do
FILEPATH=$1
process $command
shift
done
fi
fi
exit $?