xref: /aosp_15_r20/external/selinux/policycoreutils/scripts/fixfiles (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
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