#!/bin/sh
#
# Update Security Enhanced Linux Policy
# Copyright (C) 2008 Tresys Technology
#
#    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
#
# Authors:
# 	Caleb Case <ccase@tresys.com>
#
#
# Policy modules which are intended to be loaded are stored in /etc/selinux.d.
# Typically this would be done via a symlink to the module (e.g.
# /etc/selinux.d/refpolicy/init.pp -> /usr/share/selinux/refpolicy/init.pp).
# 
# /etc/selinux.d (and its sub directories) may contain any number of policy
# modules (*.pp files). However, only _ONE_ base.pp is allowed to exist.
#
# The list of modules found will be compared against the list of loaded modules
# and changes (installed/removed) commited via /usr/sbin/semodule

set -e

# load the selinux configuration
if [ -e /etc/selinux/config ]
then
	. /etc/selinux/config
else
	echo >&2 "Error: /etc/selinux/config does not exist! SELinux policy cannot be updated."
	exit 1
fi

SEMODULE=/usr/sbin/semodule

# module configuration dir
MODS_D=/etc/selinux.d

# get list of module paths that should be loaded
ON_P=`/usr/bin/find $MODS_D -iregex '^.*\.pp$'`

# trim to just base module
BASE_P=`/bin/echo "$ON_P" | /bin/grep -ie '^.*base\.pp$'`

# get the base module name
BASE_N=`/bin/echo "$BASE_P" | /usr/bin/xargs -I{} /usr/bin/basename {} | /usr/bin/cut -d \. -f 1`

# determine how many base modules we have
BASE_LINES=`/bin/echo "$BASE_N" | /usr/bin/wc -w`
if [ $BASE_LINES = 0 ]
then
	echo >&2 "Error: No base module enabled! A base module 'base.pp' must be present in /etc/selinux.d"
	exit 1
fi

if [ $BASE_LINES != 1 ]
then
	echo >&2 "Error: Too many base modules! Only one base module is allowed."
	echo >&2 $BASE_P
	exit 1
fi

# trim to list of modules (without base)
ON_MODS_P=`/bin/echo "$ON_P" | /bin/grep -v "$BASE_P"`
#echo ON_MODS_P
#echo $ON_MODS_P

# get list of module names that should be loaded
ON_MODS_N=`/bin/echo "$ON_MODS_P" | /usr/bin/xargs -I{} /usr/bin/basename {} | /usr/bin/cut -d \. -f 1`
#echo ON_MODS_N
#echo $ON_MODS_N

# check that there aren't any duplicate modules
ON_MODS_C=`/bin/echo "$ON_MODS_N" | /usr/bin/wc -w`
ON_MODS_CU=`/bin/echo "$ON_MODS_N" | /usr/bin/sort -u | /usr/bin/wc -w`

#echo $ON_MODS_C
#echo $ON_MODS_CU

if [ $ON_MODS_C != $ON_MODS_CU ]
then
	echo >&2 "Error: There are duplicate modules in /etc/selinux.d! Module names must be unique. Please examine the modules in /etc/selinux.d."
	exit 1
fi

# get list of loaded modules
MODS_L=`$SEMODULE -l || /bin/echo ""`
LOAD_MODS=""

if [ "$MODS_L" != "No modules." ]
then
	LOAD_MODS=`/bin/echo "$MODS_L" | /usr/bin/cut -f 1`
fi

#echo LOAD_MODS
#echo $LOAD_MODS

# list of modules that need to be -i
# It is important that all the modules in /etc/selinux.d are listed here
# if they are not, then it is possible to remove modules which are
# required for modules that are currently installed.

IN_MODS=""
for i in $ON_MODS_P
do
	IN_MODS="$IN_MODS -i$i"
done

#echo $IN_MODS

# list of modules that need to be -r
RM_MODS=""

FOUND=0
for i in $LOAD_MODS
do
	FOUND=0
	for j in $ON_MODS
	do
		if [ $i = $j ]
		then
			#echo "$i:	ON	LOADED"
			FOUND=1
		fi
	done

	if [ $FOUND -eq 0 ]
	then
		#echo "$i:	OFF	LOADED"
		RM_MODS="$RM_MODS -r$i"
	fi
done

#echo $RM_MODS

# determine if selinux is enabled
# if it is disabled, then we should not 
# reload the policy after committing
if ! selinuxenabled
then
	RELOAD="-n"
fi

#echo "$SEMODULE -b $BASE_P $RM_MODS $IN_MODS"
$SEMODULE $RELOAD -s $SELINUXTYPE -b $BASE_P $RM_MODS $IN_MODS

# schedule a relabel
/etc/init.d/selinux relabel

exit 0
