xref: /openbsd-src/usr.sbin/relayd/check_script.c (revision 19a6b45e5e01389b17e47c63c256aec4fbbc7ff9)
1*19a6b45eSjmatthew /*	$OpenBSD: check_script.c,v 1.22 2021/02/22 01:24:59 jmatthew Exp $	*/
24156152fSreyk 
34156152fSreyk /*
4d535e21cSreyk  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
54156152fSreyk  *
64156152fSreyk  * Permission to use, copy, modify, and distribute this software for any
74156152fSreyk  * purpose with or without fee is hereby granted, provided that the above
84156152fSreyk  * copyright notice and this permission notice appear in all copies.
94156152fSreyk  *
104156152fSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114156152fSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124156152fSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134156152fSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144156152fSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154156152fSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164156152fSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174156152fSreyk  */
184156152fSreyk 
194156152fSreyk #include <sys/wait.h>
20f04ff968Sreyk #include <sys/time.h>
214156152fSreyk 
224156152fSreyk #include <errno.h>
234156152fSreyk #include <unistd.h>
244156152fSreyk #include <string.h>
254156152fSreyk #include <stdlib.h>
264156152fSreyk #include <signal.h>
274156152fSreyk #include <pwd.h>
284156152fSreyk 
29748ceb64Sreyk #include "relayd.h"
304156152fSreyk 
314156152fSreyk void	 script_sig_alarm(int);
324156152fSreyk 
334156152fSreyk pid_t			 child = -1;
344156152fSreyk 
354156152fSreyk void
check_script(struct relayd * env,struct host * host)360325c666Sreyk check_script(struct relayd *env, struct host *host)
374156152fSreyk {
384156152fSreyk 	struct ctl_script	 scr;
3982b2e7ebSreyk 	struct table		*table;
4082b2e7ebSreyk 
41*19a6b45eSjmatthew 	if ((host->flags & (F_CHECK_SENT|F_CHECK_DONE)) == F_CHECK_SENT)
42*19a6b45eSjmatthew 		return;
43*19a6b45eSjmatthew 
4482b2e7ebSreyk 	if ((table = table_find(env, host->conf.tableid)) == NULL)
45efc39811Sbenno 		fatalx("%s: invalid table id", __func__);
464156152fSreyk 
474156152fSreyk 	host->last_up = host->up;
484156152fSreyk 	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
494156152fSreyk 
504156152fSreyk 	scr.host = host->conf.id;
519888254dSreyk 	if ((strlcpy(scr.name, host->conf.name,sizeof(scr.name)) >=
529888254dSreyk 	    sizeof(scr.name)) ||
539888254dSreyk 	    (strlcpy(scr.path, table->conf.path, sizeof(scr.path)) >=
549888254dSreyk 	    sizeof(scr.path)))
559888254dSreyk 		fatalx("invalid script path");
5682b2e7ebSreyk 	memcpy(&scr.timeout, &table->conf.timeout, sizeof(scr.timeout));
5782b2e7ebSreyk 
58*19a6b45eSjmatthew 	if (proc_compose(env->sc_ps, PROC_PARENT, IMSG_SCRIPT, &scr,
59*19a6b45eSjmatthew 	    sizeof(scr)) == 0)
60*19a6b45eSjmatthew 		host->flags |= F_CHECK_SENT;
614156152fSreyk }
624156152fSreyk 
634156152fSreyk void
script_done(struct relayd * env,struct ctl_script * scr)64748ceb64Sreyk script_done(struct relayd *env, struct ctl_script *scr)
654156152fSreyk {
664156152fSreyk 	struct host		*host;
674156152fSreyk 
684156152fSreyk 	if ((host = host_find(env, scr->host)) == NULL)
69efc39811Sbenno 		fatalx("%s: invalid host id", __func__);
704156152fSreyk 
714156152fSreyk 	if (scr->retval < 0)
724156152fSreyk 		host->up = HOST_UNKNOWN;
734156152fSreyk 	else if (scr->retval == 0)
744156152fSreyk 		host->up = HOST_DOWN;
754156152fSreyk 	else
764156152fSreyk 		host->up = HOST_UP;
774156152fSreyk 	host->flags |= F_CHECK_DONE;
784156152fSreyk 
794156152fSreyk 	hce_notify_done(host, host->up == HOST_UP ?
80c0dc99f6Sreyk 	    HCE_SCRIPT_OK : HCE_SCRIPT_FAIL);
814156152fSreyk }
824156152fSreyk 
834156152fSreyk void
script_sig_alarm(int sig)844156152fSreyk script_sig_alarm(int sig)
854156152fSreyk {
8615b1562dSderaadt 	int save_errno = errno;
8715b1562dSderaadt 
884156152fSreyk 	if (child != -1)
894156152fSreyk 		kill(child, SIGKILL);
9015b1562dSderaadt 	errno = save_errno;
914156152fSreyk }
924156152fSreyk 
934156152fSreyk int
script_exec(struct relayd * env,struct ctl_script * scr)94748ceb64Sreyk script_exec(struct relayd *env, struct ctl_script *scr)
954156152fSreyk {
964156152fSreyk 	int			 status = 0, ret = 0;
974156152fSreyk 	sig_t			 save_quit, save_int, save_chld;
984156152fSreyk 	struct itimerval	 it;
994156152fSreyk 	struct timeval		*tv;
1004156152fSreyk 	const char		*file, *arg;
1014156152fSreyk 	struct passwd		*pw;
1024156152fSreyk 
103586b5f8aSreyk 	if ((env->sc_conf.flags & F_SCRIPT) == 0) {
1046e8056acSreyk 		log_warnx("%s: script disabled", __func__);
1056e8056acSreyk 		return (-1);
1066e8056acSreyk 	}
1076e8056acSreyk 
10882b2e7ebSreyk 	DPRINTF("%s: running script %s, host %s",
10982b2e7ebSreyk 	    __func__, scr->path, scr->name);
1104156152fSreyk 
11182b2e7ebSreyk 	arg = scr->name;
11282b2e7ebSreyk 	file = scr->path;
11382b2e7ebSreyk 	tv = &scr->timeout;
1144156152fSreyk 
1154156152fSreyk 	save_quit = signal(SIGQUIT, SIG_IGN);
1164156152fSreyk 	save_int = signal(SIGINT, SIG_IGN);
1174156152fSreyk 	save_chld = signal(SIGCHLD, SIG_DFL);
1184156152fSreyk 
1194156152fSreyk 	switch (child = fork()) {
1204156152fSreyk 	case -1:
1214156152fSreyk 		ret = -1;
1224156152fSreyk 		goto done;
1234156152fSreyk 	case 0:
1244156152fSreyk 		signal(SIGQUIT, SIG_DFL);
1254156152fSreyk 		signal(SIGINT, SIG_DFL);
1264156152fSreyk 		signal(SIGCHLD, SIG_DFL);
1274156152fSreyk 
128748ceb64Sreyk 		if ((pw = getpwnam(RELAYD_USER)) == NULL)
129efc39811Sbenno 			fatal("%s: getpwnam", __func__);
1304156152fSreyk 		if (chdir("/") == -1)
131efc39811Sbenno 			fatal("%s: chdir(\"/\")", __func__);
1324156152fSreyk 		if (setgroups(1, &pw->pw_gid) ||
1334156152fSreyk 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1344156152fSreyk 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
135efc39811Sbenno 			fatal("%s: can't drop privileges", __func__);
1364156152fSreyk 
137b5d6a36dSreyk 		/*
138b5d6a36dSreyk 		 * close fds before executing an external program, to
139b5d6a36dSreyk 		 * prevent access to internal fds, eg. IMSG connections
140b5d6a36dSreyk 		 * of internal processes.
141b5d6a36dSreyk 		 */
142b5d6a36dSreyk 		closefrom(STDERR_FILENO + 1);
143b5d6a36dSreyk 
1444156152fSreyk 		execlp(file, file, arg, (char *)NULL);
1454156152fSreyk 		_exit(0);
1464156152fSreyk 		break;
1474156152fSreyk 	default:
1484156152fSreyk 		/* Kill the process after a timeout */
1494156152fSreyk 		signal(SIGALRM, script_sig_alarm);
1504156152fSreyk 		bzero(&it, sizeof(it));
1514156152fSreyk 		bcopy(tv, &it.it_value, sizeof(it.it_value));
1524156152fSreyk 		setitimer(ITIMER_REAL, &it, NULL);
1534156152fSreyk 
1544156152fSreyk 		waitpid(child, &status, 0);
1554156152fSreyk 		break;
1564156152fSreyk 	}
1574156152fSreyk 
1584156152fSreyk 	switch (ret) {
1594156152fSreyk 	case -1:
1604156152fSreyk 		ret = -1;
1614156152fSreyk 		break;
1624156152fSreyk 	default:
1634156152fSreyk 		if (WIFEXITED(status))
1644156152fSreyk 			ret = WEXITSTATUS(status);
1654156152fSreyk 		else
16609c845d4Ssthen 			ret = 0;
1674156152fSreyk 	}
1684156152fSreyk 
1694156152fSreyk  done:
1704156152fSreyk 	/* Disable the process timeout timer */
1714156152fSreyk 	bzero(&it, sizeof(it));
1724156152fSreyk 	setitimer(ITIMER_REAL, &it, NULL);
1734156152fSreyk 	child = -1;
1744156152fSreyk 
1754156152fSreyk 	signal(SIGQUIT, save_quit);
1764156152fSreyk 	signal(SIGINT, save_int);
1774156152fSreyk 	signal(SIGCHLD, save_chld);
1784156152fSreyk 	signal(SIGALRM, SIG_DFL);
1794156152fSreyk 
1804156152fSreyk 	return (ret);
1814156152fSreyk }
182