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