• 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
23#
24# seclabel support was added in 2.6.30.  This function will return a positive
25# number if the current kernel version is greater than 2.6.30, a negative
26# number if the current is less than 2.6.30 and 0 if they are the same.
27#
28function useseclabel {
29	VER=`uname -r`
30	SUP=2.6.30
31	expr '(' "$VER" : '\([^.]*\)' ')' '-' '(' "$SUP" : '\([^.]*\)' ')' '|' \
32	     '(' "$VER.0" : '[^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0" : '[^.]*[.]\([^.]*\)' ')' '|' \
33	     '(' "$VER.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')' '-' '(' "$SUP.0.0" : '[^.]*[.][^.]*[.]\([^.]*\)' ')'
34}
35
36#
37# Get all mount points that support labeling.  Use the 'seclabel' field if it
38# is available.  Else fall back to known fs types which likely support xattrs
39# and we know were not context mounted.
40#
41get_all_labeled_mounts() {
42FS="`cat /proc/self/mounts | sort | uniq | awk '{print $2}'`"
43for i in $FS; do
44	if [ `useseclabel` -ge 0 ]
45	then
46		grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)seclabel(,|$)' && echo $i
47	else
48		grep " $i " /proc/self/mounts | grep -v "context=" | egrep --silent '(ext[234]| ext4dev | gfs2 | xfs | jfs | btrfs )' && echo $i
49	fi
50done
51}
52
53get_rw_labeled_mounts() {
54FS=`get_all_labeled_mounts | sort | uniq`
55for i in $FS; do
56	grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)rw(,|$)' && echo $i
57done
58}
59
60get_ro_labeled_mounts() {
61FS=`get_all_labeled_mounts | sort | uniq`
62for i in $FS; do
63	grep " $i " /proc/self/mounts | awk '{print $4}' | egrep --silent '(^|,)ro(,|$)' && echo $i
64done
65}
66
67#
68# Get the default label returned from the kernel for a file with a lable the
69# kernel does not understand
70#
71get_undefined_type() {
72	SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'`
73	cat ${SELINUXMNT}/initial_contexts/unlabeled | secon -t
74}
75
76#
77# Get the default label for a file without a label
78#
79get_unlabeled_type() {
80	SELINUXMNT=`grep selinuxfs /proc/self/mountinfo | head -1 | awk '{ print $5 }'`
81	cat $SELINUXMNT/initial_contexts/file | secon -t
82}
83
84exclude_dirs_from_relabelling() {
85    exclude_from_relabelling=
86    if [ -e /etc/selinux/fixfiles_exclude_dirs ]
87    then
88	while read i
89	do
90	  # skip blank line and comment
91	  # skip not absolute path
92	  # skip not directory
93	  [ -z "${i}" ] && continue
94	  [[ "${i}" =~ "^[[:blank:]]*#" ]] && continue
95	  [[ ! "${i}" =~ ^/.* ]] && continue
96	  [[ ! -d "${i}" ]] && continue
97	  exclude_from_relabelling="$exclude_from_relabelling -e $i"
98	  logit "skipping the directory $i"
99	done < /etc/selinux/fixfiles_exclude_dirs
100    fi
101    echo "$exclude_from_relabelling"
102}
103
104exclude_dirs() {
105    exclude=
106    for i in /sys /proc /dev /run /mnt /var/tmp /var/lib/BackupPC /home /tmp /dev; do
107	[ -e $i ]  && exclude="$exclude -e $i";
108    done
109    exclude="$exclude `exclude_dirs_from_relabelling`"
110    echo "$exclude"
111}
112
113#
114# Set global Variables
115#
116fullFlag=0
117BOOTTIME=""
118VERBOSE="-p"
119FORCEFLAG=""
120DIRS=""
121RPMILES=""
122LOGFILE=/proc/self/fd/1
123SETFILES=/sbin/setfiles
124RESTORECON=/sbin/restorecon
125FILESYSTEMSRW=`get_rw_labeled_mounts`
126FILESYSTEMSRO=`get_ro_labeled_mounts`
127FILESYSTEMS="$FILESYSTEMSRW $FILESYSTEMSRO"
128SELINUXTYPE="targeted"
129if [ -e /etc/selinux/config ]; then
130    . /etc/selinux/config
131    FC=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts
132else
133    FC=/etc/security/selinux/file_contexts
134fi
135
136#
137# Write to LOGFILE
138#
139logit () {
140if [ -n $LOGFILE ]; then
141    echo $1 >> "$LOGFILE"
142fi
143}
144#
145# Find files newer then the passed in date and fix the label
146#
147newer() {
148    DATE=$1
149    shift
150    for m in `echo $FILESYSTEMSRW`; do
151	find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} $* -i -0 -f -
152    done;
153
154}
155
156#
157# Compare PREVious File Context to currently installed File Context and
158# run restorecon on all files affected by the differences.
159#
160diff_filecontext() {
161if [ -f ${PREFC} -a -x /usr/bin/diff ]; then
162	TEMPFILE=`mktemp ${FC}.XXXXXXXXXX`
163	test -z "$TEMPFILE" && exit
164	PREFCTEMPFILE=`mktemp ${PREFC}.XXXXXXXXXX`
165	sed -r -e 's,:s0, ,g' $PREFC | sort -u > ${PREFCTEMPFILE}
166	sed -r -e 's,:s0, ,g' $FC | sort -u | \
167	/usr/bin/diff -b ${PREFCTEMPFILE} - | \
168	    grep '^[<>]'|cut -c3-| grep ^/ | \
169	    egrep -v '(^/home|^/root|^/tmp|^/dev)' |\
170	sed -r -e 's,[[:blank:]].*,,g' \
171	       -e 's|\(([/[:alnum:]]+)\)\?|{\1,}|g' \
172	       -e 's|([/[:alnum:]])\?|{\1,}|g' \
173	       -e 's|\?.*|*|g' \
174	       -e 's|\{.*|*|g' \
175	       -e 's|\(.*|*|g' \
176	       -e 's|\[.*|*|g' \
177	       -e 's|\.\*.*|*|g' \
178	       -e 's|\.\+.*|*|g' | \
179	    # These two sorts need to be separate commands \
180	sort -u | \
181	sort -d | \
182	while read pattern ; \
183	    do if ! echo "$pattern" | grep -q -f ${TEMPFILE} 2>/dev/null; then \
184		  echo "$pattern"; \
185		  case "$pattern" in *"*") \
186		       echo "$pattern" | sed -e 's,^,^,' -e 's,\*$,,g' >> ${TEMPFILE};;
187		  esac; \
188	       fi; \
189	    done | \
190	${RESTORECON} ${VERBOSE} -i -f - -R $* `exclude_dirs`; \
191	rm -f ${TEMPFILE} ${PREFCTEMPFILE}
192fi
193}
194#
195# Log all Read Only file systems
196#
197LogReadOnly() {
198if [ ! -z "$FILESYSTEMSRO" ]; then
199    logit "Warning: Skipping the following R/O filesystems:"
200    logit "$FILESYSTEMSRO"
201fi
202}
203
204rpmlist() {
205rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' '
206[ ${PIPESTATUS[0]} != 0 ] && echo "$1 not found" >/dev/stderr
207}
208
209#
210# restore
211# if called with -n will only check file context
212#
213restore () {
214OPTION=$1
215shift
216
217if [ ! -z "$PREFC" ]; then
218    diff_filecontext $*
219    exit $?
220fi
221if [ ! -z "$BOOTTIME" ]; then
222    newer $BOOTTIME $*
223    exit $?
224fi
225[ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon
226LogReadOnly
227#
228exclude_dirs="`exclude_dirs_from_relabelling $OPTION`"
229if [ -n "${exclude_dirs}" ]
230then
231	TEMPFCFILE=`mktemp ${FC}.XXXXXXXXXX`
232	test -z "$TEMPFCFILE" && exit
233	/bin/cp -p ${FC} ${TEMPFCFILE} &>/dev/null || exit
234	tmpdirs=${tempdirs//-e/}
235	for p in ${tmpdirs}
236	do
237		p="${p%/}"
238		p1="${p}(/.*)? -- <<none>>"
239		echo "${p1}" >> $TEMPFCFILE
240		logit "skipping the directory ${p}"
241	done
242FC=$TEMPFCFILE
243fi
244if [ ! -z "$RPMFILES" ]; then
245    for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
246	rpmlist $i | ${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} $* -R -i -f - 2>&1 | cat >> $LOGFILE
247    done
248    exit $?
249fi
250if [ ! -z "$FILEPATH" ]; then
251    ${RESTORECON} $exclude_dirs ${FORCEFLAG} ${VERBOSE} -R $* $FILEPATH 2>&1 | cat >> $LOGFILE
252    return
253fi
254if [  -n "${FILESYSTEMSRW}" ]; then
255    echo "${OPTION}ing `echo ${FILESYSTEMSRW}`"
256    ${SETFILES} ${VERBOSE} $exclude_dirs -q ${FORCEFLAG} $* ${FC} ${FILESYSTEMSRW} 2>&1 | cat >> $LOGFILE
257else
258    echo >&2 "fixfiles: No suitable file systems found"
259fi
260if [ ${OPTION} != "Relabel" ]; then
261    return
262fi
263echo "Cleaning up labels on /tmp"
264rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* $TEMPFCFILE
265
266UNDEFINED=`get_undefined_type` || exit $?
267UNLABELED=`get_unlabeled_type` || exit $?
268find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) \( -type s -o -type p \) -delete
269find /tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /tmp {} \;
270find /var/tmp \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/tmp {} \;
271find /var/run \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /var/run {} \;
272[ ! -e /var/lib/debug ] || find /var/lib/debug \( -context "*:${UNLABELED}*" -o -context "*:${UNDEFINED}*" \) -exec chcon --reference /lib {} \;
273exit 0
274}
275
276fullrelabel() {
277    logit "Cleaning out /tmp"
278    find /tmp/ -mindepth 1 -delete
279    LogReadOnly
280    restore Relabel
281}
282
283relabel() {
284    if [ ! -z "$RPMFILES" ]; then
285	restore Relabel
286    fi
287
288    if [ $fullFlag == 1  ]; then
289	fullrelabel
290    fi
291
292    echo -n "
293    Files in the /tmp directory may be labeled incorrectly, this command
294    can remove all files in /tmp.  If you choose to remove files from /tmp,
295    a reboot will be required after completion.
296
297    Do you wish to clean out the /tmp directory [N]? "
298    read answer
299    if [ "$answer" = y -o  "$answer" = Y ]; then
300	fullrelabel
301    else
302	restore Relabel
303    fi
304}
305
306process() {
307#
308# Make sure they specified one of the three valid commands
309#
310case "$1" in
311    restore) restore Relabel;;
312    check) VERBOSE="-v"; restore Check -n;;
313    verify) restore Verify -n;;
314    relabel) relabel;;
315    onboot)
316	> /.autorelabel
317	[ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel
318	[ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel
319	# Force full relabel if / does not have a label on it
320	getfilecon / > /dev/null 2>&1  || echo -F >/.autorelabel
321	echo "System will relabel on next boot"
322	;;
323    *)
324    usage
325    exit 1
326esac
327}
328usage() {
329	echo $"""
330Usage: $0 [-v] [-F]  [-N time ] [-l logfile ] { check | restore| [-f] relabel | verify } [[dir/file] ... ]
331or
332Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] [-l logfile ] { check | restore | verify }
333or
334Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify }
335or
336Usage: $0 [-F] [-B] onboot
337"""
338}
339
340if [ $# = 0 ]; then
341	usage
342	exit 1
343fi
344
345# See how we were called.
346while getopts "N:BC:FfR:l:v" i; do
347    case "$i" in
348	B)
349		BOOTTIME=`/bin/who -b | awk '{print $3}'`
350		;;
351	f)
352		fullFlag=1
353		;;
354	v)
355		VERBOSE="-v"
356		;;
357	R)
358		RPMFILES=$OPTARG
359		;;
360	l)
361		LOGFILE=$OPTARG
362		;;
363	C)
364		PREFC=$OPTARG
365		;;
366	F)
367		FORCEFLAG="-F"
368		;;
369	N)
370		BOOTTIME=$OPTARG
371		;;
372	*)
373	    usage
374	    exit 1
375esac
376done
377# Move out processed options from arguments
378shift $(( OPTIND - 1 ))
379
380# Check for the command
381command=$1
382if [ -z $command ]; then
383    usage
384fi
385
386# Move out command from arguments
387shift
388
389#
390# check if they specified both DIRS and RPMFILES
391#
392
393if [ ! -z "$RPMFILES" ]; then
394    process $command
395    if [ $# -gt 0 ]; then
396	    usage
397    fi
398else
399    if [ -z "$1" ]; then
400	process $command
401    else
402	while [ -n "$1" ]; do
403	    FILEPATH=$1
404	    process $command
405	    shift
406	done
407    fi
408fi
409exit $?
410