#!/bin/sh
# vim: tabstop=4
#
# author:    chris friedhoff - chris@friedhoff.org
# version:   pcaps4server  5  Tue Mar 11 2008
#
#
# changelog:
# 1 - initial release pcaps4convenience
# 1 - 2007.02.15 - initial release
# 2 - 2007.11.02 - changed to new setfcaps api; each app is now callable; supressed error of id
# 3 - 2007.12.28 - changed to libcap2 package setcap/getcap
# 4 - renamed to pcaps4server
#      removed suid0 and convenience files,
#      they are now in pcaps4suid0 resp. pcaps4convenience
# 5 - changed 'attr -S -r' to 'setcap -r' and removed attr code
#
#
###########################################################################
# change the installation of different server to be able not to run as root
# and have their own unpriviledged user. The binary has the needed POSIX
# Capabilities.
# to ensure that the server is really started as his respective user, we set
# the suid bit (BUT NOT 0)!
# paths are hard coded and derive from a slackware system
# change it to your needs !!
###########################################################################



VERBOSE="-v"
#VERBOSE=""
APPS=""

message(){
	printRedMessage "$1"
}

printRedMessage(){
	# print message red and turn back to white
	echo -e "\n\033[00;31m $1 ...\033[00;00m\n"
}

printGreenMessage(){
	# print message red and turn back to white
	echo -e "\033[00;32m $1 ...\033[00;00m\n"
	sleep 0.5
}

checkReturnCode(){
    if [ "$?" != "0" ]; then
        printRedMessage "!! I'M HAVING A PROBLEM !! THE RETURNCODE IS NOT 0 !! I STOP HERE !!"
        exit 1
    else
        printGreenMessage ":-)"
		sleep 0.5
    fi
}



p4r_test(){
	#for now, we work with root
	if [ "$( id -u )" != "0" ]; then
		echo "Sorry, you must be root !"
		exit
	fi
}




# apache 1.3
########
#APPS="$APPS apache1"
apache1_convert(){
	message "converting apache1"
	if [ "$( id -g apache 2>/dev/null )" == "" ]; then
		groupadd -g 60 apache
	fi
	if [ "$( id -u apache 2>/dev/null )" == "" ]; then
		useradd -g apache -d / -u 600 apache
	fi
	sed -i -e "{s|^\(User\).*|\1 apache|; s|^\(Group\) .*|\1 apache|}" /etc/apache/httpd.conf
	chown $VERBOSE -R apache:apache /var/run/apache/
	chown $VERBOSE -R apache:apache /etc/apache/
	chown $VERBOSE -R apache:apache /var/log/apache/
	chown $VERBOSE apache:apache /usr/sbin/httpd
	chmod $VERBOSE u+s /usr/sbin/httpd
	setcap cap_net_bind_service=ep /usr/sbin/httpd
	checkReturnCode
}
apache1_revert(){
	message "reverting apache1"
	chown $VERBOSE -R root:root /var/run/apache/
	chown $VERBOSE -R root:root /etc/apache/
	chown $VERBOSE -R root:root /var/log/apache/
	chown $VERBOSE root:root /usr/sbin/httpd
	chmod $VERBOSE u-s /usr/sbin/httpd
	setcap -r /usr/sbin/httpd
	checkReturnCode
	sed -i -e "{s|^\(User\).*|\1 nobody|; s|^\(Group\).*|\1 nogroup|}" /etc/apache/httpd.conf
	userdel apache
	groupdel apache
}


# apache 2.x
########
APPS="$APPS apache2"
apache2_convert(){
	message "converting apache2"
	if [ "$( id -g apache 2>/dev/null )" == "" ]; then
		groupadd -g 60 apache
	fi
	if [ "$( id -u apache 2>/dev/null )" == "" ]; then
		useradd -g apache -d / -u 600 apache
	fi
	sed -i -e "{s|^\(User\).*|\1 apache|; s|^\(Group\) .*|\1 apache|}" /etc/httpd/httpd.conf
	chown $VERBOSE -R apache:apache /var/run/httpd/
	chown $VERBOSE -R apache:apache /etc/httpd/
	chown $VERBOSE -R apache:apache /var/log/httpd/
	chown $VERBOSE apache:apache /usr/sbin/httpd
	chmod $VERBOSE u+s /usr/sbin/httpd
	#setfcaps -c cap_net_bind_service=p -e /usr/sbin/httpd
	setcap cap_net_bind_service=ep /usr/sbin/httpd
	checkReturnCode
}
apache2_revert(){
	message "reverting apache2"
	chown $VERBOSE -R root:root /var/run/httpd/
	chown $VERBOSE -R root:root /etc/httpd/
	chown $VERBOSE -R root:root /var/log/httpd/
	chown $VERBOSE root:root /usr/sbin/httpd
	chmod $VERBOSE u-s /usr/sbin/httpd
	setcap -r /usr/sbin/httpd
	checkReturnCode
	sed -i -e "{s|^\(User\).*|\1 nobody|; s|^\(Group\).*|\1 nogroup|}" /etc/httpd/httpd.conf
	userdel apache
	groupdel apache
}


# samba
#######
APPS="$APPS samba"
samba_convert(){
	message "converting samba"
	if [ "$( id -g samba 2>/dev/null )" == "" ]; then
		groupadd -g 61 samba
	fi
	if [ "$( id -u samba 2>/dev/null )" == "" ]; then
		useradd -g samba -d / -u 610 samba
	fi
	chown $VERBOSE -R samba:samba /var/log/samba
	chown $VERBOSE -R samba:samba /etc/samba
	chown $VERBOSE -R samba:samba /var/run/samba
	chown $VERBOSE -R samba:samba /var/cache/samba
	chown $VERBOSE samba:samba /usr/sbin/smbd /usr/sbin/nmbd
	chmod $VERBOSE u+s /usr/sbin/smbd /usr/sbin/nmbd
	setcap cap_net_bind_service,cap_sys_resource,cap_dac_override=ep /usr/sbin/smbd
	checkReturnCode
	setcap cap_net_bind_service=ep /usr/sbin/nmbd
	checkReturnCode
}

samba_revert(){
	message "reverting samba"
	chown $VERBOSE -R root:root /var/log/samba
	chown $VERBOSE -R root:root /etc/samba
	chown $VERBOSE -R root:root /var/run/samba
	chown $VERBOSE -R root:root /var/cache/samba
	chown $VERBOSE root:root /usr/sbin/smbd /usr/sbin/nmbd
	chmod $VERBOSE u-s /usr/sbin/smbd /usr/sbin/nmbd
	setcap -r /usr/sbin/smbd
	checkReturnCode
	setcap -r /usr/sbin/nmbd
	checkReturnCode
	userdel samba
	groupdel samba
}


# bind
######
APPS="$APPS bind"
bind_convert(){
	message "converting bind"
	if [ "$( id -g bind 2>/dev/null )" == "" ]; then
		groupadd -g 62 bind
	fi
	if [ "$( id -u bind 2>/dev/null )" == "" ]; then
		useradd -g bind -d / -u 620 bind
	fi
	chown $VERBOSE -R bind:bind /var/run/named
	chown $VERBOSE -R bind:bind /var/named
	chown $VERBOSE bind:bind /etc/rndc.key
	chown $VERBOSE bind:bind /usr/sbin/named
	chmod $VERBOSE u+s /usr/sbin/named
	setcap cap_net_bind_service=ep /usr/sbin/named
	checkReturnCode
}
bind_revert(){
	message "reverting bind"
	chown $VERBOSE -R root:root /var/run/named
	chown $VERBOSE -R root:root /var/named
	chown $VERBOSE root:root /etc/rndc.key
	chown $VERBOSE root:root /usr/sbin/named
	chmod $VERBOSE u-s /usr/sbin/named
	setcap -r /usr/sbin/named
	checkReturnCode
	userdel bind
	groupdel bind
}


# dhcpd
#######
APPS="$APPS dhcpd"
dhcpd_convert(){
	message "converting dhcpd"
	if [ "$( id -g dhcpd 2>/dev/null )" == "" ]; then
		groupadd -g 63 dhcpd
	fi
	if [ "$( id -u dhcpd 2>/dev/null )" == "" ]; then
		useradd -g dhcpd -d / -u 630 dhcpd
	fi
	chown $VERBOSE dhcpd:dhcpd /var/run/dhcpd
	chown $VERBOSE dhcpd:dhcpd /etc/dhcpd.conf
	chown $VERBOSE -R dhcpd:dhcpd /var/state/dhcp/
	chown $VERBOSE dhcpd:dhcpd /usr/sbin/dhcpd
	chmod $VERBOSE u+s /usr/sbin/dhcpd
	setcap cap_net_bind_service,cap_net_raw=ep /usr/sbin/dhcpd
	checkReturnCode
}
dhcpd_revert(){
	message "reverting dhcpd"
	chown $VERBOSE root:root /var/run/dhcpd
	chown $VERBOSE root:root /etc/dhcpd.conf
	chown $VERBOSE -R root:root /var/state/dhcp/
	chown $VERBOSE root:root /usr/sbin/dhcpd
	chmod $VERBOSE u-s /usr/sbin/dhcpd
	setcap -r /usr/sbin/dhcpd
	checkReturnCode
	userdel dhcpd
	groupdel dhcpd
}


# cupsd
#######
APPS="$APPS cupsd"
cupsd_convert(){
	message "converting cupsd"
	if [ "$( id -g cupsd 2>/dev/null )" == "" ]; then
		groupadd -g 64 cupsd
	fi
	if [ "$( id -u cupsd 2>/dev/null )" == "" ]; then
		useradd -g cupsd -d / -u 640 cupsd
	fi
	sed -i -e "{s|^\(User\).*|\1 cupsd|; s|^\(Group\) .*|\1 cupsd|}" /etc/cups/cupsd.conf
	chown $VERBOSE -R cupsd:cupsd /etc/cups
	chown $VERBOSE -R cupsd:cupsd /var/cache/cups
	chown $VERBOSE -R cupsd:cupsd /var/log/cups
	chown $VERBOSE -R cupsd:cupsd /var/spool/cups
	chown $VERBOSE -R cupsd:cupsd /var/run/cups
	chown $VERBOSE cupsd:cupsd /usr/sbin/cupsd
	chmod $VERBOSE u+s /usr/sbin/cupsd
	setcap cap_net_bind_service,cap_dac_read_search=ep /usr/sbin/cupsd
	checkReturnCode
}
cupsd_revert(){
	message "reverting cupsd"
	chown $VERBOSE -R root:root /etc/cups
	chown $VERBOSE -R root:lp /var/cache/cups
	chown $VERBOSE -R root:root /var/log/cups
	chown $VERBOSE -R root:root /var/spool/cups
	chown $VERBOSE root:lp /var/run/cups
	chown $VERBOSE lp:sys /var/run/cups/certs
	chmod $VERBOSE 750 /var/run/cups/certs
	chown $VERBOSE root:root /usr/sbin/cupsd
	chmod $VERBOSE u-s /usr/sbin/cupsd
	setcap -r /usr/sbin/cupsd
	checkReturnCode
	sed -i -e "{s|^\(User\).*|\1 lp|; s|^\(Group\) .*|\1 sys|}" /etc/cups/cupsd.conf
	userdel cupsd
	groupdel cupsd
}


usage_message(){
	echo "Try 'pcaps4server help' for more information"
}


p4r_usage(){
    echo
    echo "pcaps4server"
    echo
    echo "pcaps4server stores the needed POSIX Capabilities for server binaries to"
    echo "run successful into their Permitted and Effective Set."
    echo "The server are now able to run as an unpriviledged user."
	echo "For each server software an unpriviledged user is added the system."
    echo "The ownership of all the respective paths are	changed to this user."
	echo "To ensure that the server is starting as this unpriviledgesd user, the"
    echo "suid bit (NOT 0) is set."
	echo "Effectively this means every user can start this server daemons (for now)."
	echo "All paths are hard coded!"
	echo "You have been warned. Enjoy!"
    echo
    echo "Your Filesystem has to support extended attributes and your kernel must have"
    echo "support for POSIX File Capabilities (CONFIG_SECURITY_FILE_CAPABILITIES)."
    echo
    echo "Usage:  pcaps4server [PROG] [con(vert)|rev(ert)|help]"
    echo
    echo "         con|convert - from setuid0 to POSIX Capabilities"
    echo "         rev|revert  - from POSIX Capabilities back to setui0"
    echo "         help        - this help message"
	echo
	echo "  PROG: $APPS"
    echo
}




case "$1" in
	con|convert)
		p4r_test
		for j in $APPS; do
			${j}_convert
		done
		exit
		;;
	rev|renvert)
		p4r_test
		for j in $APPS; do
			${j}_revert
		done
		exit
		;;
	help)
		p4r_usage
		exit
		;;
esac

for i in ${APPS}; do
	if [ "$1" == "$i" ]; then
		case "$2" in
			con|convert)
				p4r_test
				${i}_convert
				exit
				;;
			rev|revert)
				p4r_test
				${i}_revert
				exit
				;;
			*)
				usage_message
				exit 1
				;;
			esac
	fi
done

usage_message