1#!/bin/bash
2
3### Exit error codes
4#
5# 0: everything went fine
6# 255: invalid command line-option
7# 254: no root privileges
8# 253: file or directory does not exist
9# 252: policy build failed
10# 251: policy install failed
11# 250: unsupported os/distribution/version
12# 249: conflicting parameters
13# 248: internal error at task selection
14# 246: syslog-ng not installed
15# 244: tty not available
16
17EL_FC=
18EL_TE=
19OS_VERSION=
20INSTALL_PATH="/opt/syslog-ng"
21# RHEL8 note: ports 10514/tcp, 10514/udp, 20514/tcp and 20514/udp have been
22#  allowed by default
23#
24# Post-RHEL6.5 note: ports 514/udp, 6514/udp and 6514/tcp are allowed by default
25#  if you wish to add further ports, just add them to the end of the list
26SYSLOG_NG_TCP_PORTS="601"
27SYSLOG_NG_UDP_PORTS="601"
28TASK_SELECTED="install_default"
29INPUT=
30
31get_console_tty() {
32	if is_available tty; then
33		CONSOLE_TTY=$( tty )
34	else
35		echo "The 'tty' binary is not available!" >&2
36		exit 244
37	fi
38}
39
40
41query_install_path() {
42	echo -n "Please enter your installation path for Syslog-ng PE: [${INSTALL_PATH}] "
43	read INPUT <"${CONSOLE_TTY}"
44}
45
46
47check_dir() {
48	if [ -d "${1}" ]; then
49		return 0
50	else
51		echo "The directory you specified does not exist!" >&2
52		return 1
53	fi
54}
55
56
57verify_input() {
58	INPUT="${INPUT:-${INSTALL_PATH}}"
59	echo -n "You have entered '${INPUT}'. Is this correct? [y/N] "
60	read ACCEPT <"${CONSOLE_TTY}"
61	if [ "x${ACCEPT}x" != "xyx" ]; then return 0; fi
62	check_dir "${INPUT}" && return 1 || return 0
63}
64
65
66is_available () {
67	which "$1" >/dev/null 2>&1;
68}
69
70
71syslog_ng_is_not_installed() {
72	if is_available syslog-ng; then
73		return 1
74	elif [ -x "${INSTALL_PATH}/sbin/syslog-ng" ]; then
75		return 1
76	else
77		return 0
78	fi
79}
80
81
82install_precheck() {
83	if syslog_ng_is_not_installed; then
84		echo "Syslog-ng does not seem to be installed!" >&2
85		exit 246
86	fi
87}
88
89
90extract_version_string() {
91	sed -n 's:^[a-zA-Z ]\+\([0-9]\+\.[0-9]\+\)\(.[0-9]\+\)\?[a-zA-Z ()]\+$:\1:p'
92}
93
94
95detect_os_version() {
96	echo "Detecting RHEL/CentOS/Oracle Linux version..."
97	if [ -x "/usr/bin/lsb_release" ]; then
98		if lsb_release -d | grep -qE "Description:[[:space:]]+(CentOS|CentOS Linux|Red Hat Enterprise Linux Server|Oracle Linux Server|Enterprise Linux Enterprise Linux Server) release"; then
99			OS_VERSION=$( lsb_release -r | cut -f 2 )
100		else
101			echo "You don't seem to be running a supported Linux distribution!" >&2
102			exit 250
103		fi
104	else
105		# The package redhat-lsb-core is most likely not installed...
106		if [ -f "/etc/redhat-release" ]; then
107			OS_VERSION=$( extract_version_string < "/etc/redhat-release" )
108		else
109			echo "You don't seem to be running a supported OS!" >&2
110			exit 250
111		fi
112	fi
113}
114
115
116omit_allowed_tcp_ports() {
117	sed -e 's:^601::g'
118}
119
120
121omit_allowed_udp_ports() {
122	sed -e 's:^601::g'
123}
124
125
126omit_allowed_ports() {
127	SYSLOG_NG_TCP_PORTS=$( omit_allowed_tcp_ports <<<"${SYSLOG_NG_TCP_PORTS}" )
128	SYSLOG_NG_UDP_PORTS=$( omit_allowed_udp_ports <<<"${SYSLOG_NG_UDP_PORTS}" )
129}
130
131
132setup_vars() {
133	echo "Detected RHEL/CentOS/Oracle Linux ${OS_VERSION}."
134	case "${OS_VERSION}" in
135		5.*)
136
137			EL_FC="syslog_ng.el5.fc.in"
138			EL_TE="syslog_ng.el5.te.in"
139			;;
140		6.*)
141			EL_FC="syslog_ng.el6.fc.in"
142
143			local MINORVER
144			MINORVER=$( cut -d. -f 2 <<<"${OS_VERSION}" )
145			if [ "${MINORVER}" -lt 5 ]; then
146				EL_TE="syslog_ng.el6.0to4.te.in"
147			else
148				EL_TE="syslog_ng.el6.5up.te.in"
149
150				# 601/tcp and 601/udp are allowed by default on RHEL6.5+, so there is no need to enable them
151				omit_allowed_ports
152			fi
153			;;
154		7.*)
155			EL_FC="syslog_ng.el78.fc.in"
156			EL_TE="syslog_ng.el7.te.in"
157
158			# 601/tcp and 601/udp are allowed by default on RHEL7, so there is no need to enable them
159			omit_allowed_ports
160			;;
161		8.*)
162			EL_FC="syslog_ng.el78.fc.in"
163			EL_TE="syslog_ng.el8.te.in"
164
165			# 601/tcp and 601/udp are allowed by default on RHEL8, so there is no need to enable them
166			omit_allowed_ports
167			;;
168		*)
169			echo "You don't seem to be running a supported version of RHEL!" >&2
170			exit 250
171			;;
172	esac
173}
174
175
176substitute_install_path() {
177	sed -e "s:^\\\$PATH\\\$:${INSTALL_PATH}:g" "src/root_unsafe/${EL_FC}"
178	sed -e "s:^\\\$PATH\\\$:${INSTALL_PATH}:g" "src/root_safe/${EL_FC}"
179}
180
181
182omit_install_path() {
183	sed -e "s:^\\\$PATH\\\$::g" "src/root_safe/${EL_FC}"
184}
185
186
187prepare_files() {
188	echo "Using '${INSTALL_PATH}'..."
189	if [ "${INSTALL_PATH}" != "/" ]; then
190
191		substitute_install_path > "syslog_ng.fc"
192	else
193		omit_install_path > "syslog_ng.fc"
194	fi
195	cat "src/syslog_ng.module.version" "src/${EL_TE}" > "syslog_ng.te"
196}
197
198
199remove_trainling_slash() {
200	# the trailing slash in the install path (if present) breaks file context rules
201	# thus it needs to be removed (provided that the install path is not "/" itself)
202	sed -e 's:^\(.\+\)/$:\1:'
203}
204
205
206filter_bogus_build_output() {
207	#filter misleading output caused by RHEL bug 1861968
208	fgrep -v /usr/share/selinux/devel/include/services/container.if
209}
210
211
212build_module() {
213	echo "Building and Loading Policy"
214	build_output=$( make -f /usr/share/selinux/devel/Makefile syslog_ng.pp 2>&1 )
215	retval=${?}
216	filter_bogus_build_output <<<"${build_output}"
217	[ ${retval} -eq 0 ] || exit 252
218}
219
220
221add_ports() {
222	for entry in ${@}; do
223		port=${entry%/*}
224		proto=${entry#*/}
225		semanage port -a -t syslogd_port_t -p ${proto} ${port} 2>/dev/null || \
226		semanage port -m -t syslogd_port_t -p ${proto} ${port} 2>/dev/null
227	done
228}
229
230
231install_module() {
232	if /usr/sbin/semodule -l | grep -qw syslog_ng; then
233		echo "The Syslog-ng SELinux policy module is already installed. Nothing to do..."
234		echo "If it belongs to a previous version, then you will have to remove it first."
235	else
236		/usr/sbin/semodule -i syslog_ng.pp -v || exit 251
237
238		# set up syslog-ng specific ports
239		PORTS=
240		for port in ${SYSLOG_NG_TCP_PORTS}; do PORTS="${PORTS} ${port}/tcp"; done
241		for port in ${SYSLOG_NG_UDP_PORTS}; do PORTS="${PORTS} ${port}/udp"; done
242		add_ports "${PORTS}"
243
244		# Fixing the file context
245		/sbin/restorecon -F -Rv "${INSTALL_PATH}"
246		[ -f /etc/init.d/syslog-ng ] && /sbin/restorecon -F -v /etc/init.d/syslog-ng
247		[ -f /etc/rc.d/init.d/syslog-ng ] && /sbin/restorecon -F -v /etc/rc.d/init.d/syslog-ng
248		/sbin/restorecon -F -Rv /dev/log
249
250		echo -e "\nInstallation of the Syslog-ng SELinux policy module finished.\nPlease restart syslog-ng. You can find more information about this in the README file."
251	fi
252}
253
254remove_ports() {
255	for entry in ${@}; do
256		port=${entry%/*}
257		proto=${entry#*/}
258		semanage port -d -t syslogd_port_t -p ${proto} ${port} 2>/dev/null
259	done
260}
261
262remove_module() {
263	if /usr/sbin/semodule -l | grep -q syslog_ng; then
264		echo -n "Removing Syslog-ng SELinux policy module... "
265
266		/usr/sbin/semodule --remove=syslog_ng
267
268		# unconfigure syslog-ng specific ports
269		PORTS=
270		for port in ${SYSLOG_NG_TCP_PORTS}; do PORTS="${PORTS} ${port}/tcp"; done
271		for port in ${SYSLOG_NG_UDP_PORTS}; do PORTS="${PORTS} ${port}/udp"; done
272		remove_ports "${PORTS}"
273
274		[ -f syslog_ng.pp ] && rm -f syslog_ng.pp
275		[ -f syslog_ng.te ] && rm -f syslog_ng.te
276		[ -f syslog_ng.fc ] && rm -f syslog_ng.fc
277		[ -f syslog_ng.if ] && rm -f syslog_ng.if
278		[ -d tmp ] && rm -Rf tmp
279
280		echo "done."
281	else
282		echo "No installed Syslog-ng SELinux policy module was found. No removal is necessary. Skipping..."
283	fi
284}
285
286DIRNAME=$( dirname "${0}" )
287cd "${DIRNAME}"
288USAGE="Usage: $0\t[ --install-dir <DIRECTORY> | --remove | --help ]\n\n$0:\tA tool for building and managing the SELinux policy for the\n\t\tdefault syslog-ng installation."
289
290
291while [ -n "${1}" ]; do
292	case "${1}" in
293		--help)
294			# if --help is supplied, the help message will be printed independently of any other options being specified
295			TASK_SELECTED="showhelp"
296			break
297			;;
298		--install-dir)
299			[ "${TASK_SELECTED}" = "remove" ] && echo -e "ERROR: Conflicting options!\n\n${USAGE}" >&2 && exit 249
300			TASK_SELECTED="install"
301			check_dir "${2}" || exit 253
302			INPUT="${2}"
303			shift 2
304			;;
305		--remove)
306			[ "${TASK_SELECTED}" = "install" ] && echo -e "ERROR: Conflicting options!\n\n${USAGE}" >&2 && exit 249
307			TASK_SELECTED="remove"
308			shift
309			;;
310		*)
311			echo -e "ERROR: Invalid option: '${1}'\n${USAGE}" >&2
312			exit 255
313			;;
314	esac
315
316done
317
318case "${TASK_SELECTED}" in
319	showhelp)
320		echo -e "${USAGE}"
321		exit 0
322		;;
323	remove)
324		detect_os_version
325		setup_vars
326		remove_module
327		exit 0
328		;;
329	install|install_default)
330		if [ $( id -u ) != 0 ]; then
331			echo 'You must be root to run this script!' >&2
332			exit 254
333		fi
334
335		get_console_tty
336
337		if [ -z "${INPUT}" ]; then
338			query_install_path
339			while verify_input; do
340				query_install_path
341			done
342		fi
343
344		INSTALL_PATH=$( remove_trainling_slash <<<"${INPUT}" )
345
346		detect_os_version
347		install_precheck
348		setup_vars
349		prepare_files
350		build_module
351		install_module
352		;;
353	*)
354		echo -e "ERROR: Invalid task: '${TASK_SELECTED}'!" >&2
355		exit 248
356		;;
357esac
358