xref: /openbsd-src/usr.sbin/relayd/check_script.c (revision 5b859c19fe53bbea08f5c342e0a4470e99f883e1)
1 /*	$OpenBSD: check_script.c,v 1.16 2014/06/25 11:05:15 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 
24 #include <net/if.h>
25 
26 #include <limits.h>
27 #include <event.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <pwd.h>
34 #include <err.h>
35 
36 #include <openssl/ssl.h>
37 
38 #include "relayd.h"
39 
40 void	 script_sig_alarm(int);
41 
42 pid_t			 child = -1;
43 
44 void
45 check_script(struct relayd *env, struct host *host)
46 {
47 	struct ctl_script	 scr;
48 	struct table		*table;
49 
50 	if ((table = table_find(env, host->conf.tableid)) == NULL)
51 		fatalx("check_script: invalid table id");
52 
53 	host->last_up = host->up;
54 	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
55 
56 	scr.host = host->conf.id;
57 	if ((strlcpy(scr.name, host->conf.name,sizeof(scr.name)) >=
58 	    sizeof(scr.name)) ||
59 	    (strlcpy(scr.path, table->conf.path, sizeof(scr.path)) >=
60 	    sizeof(scr.path)))
61 		fatalx("invalid script path");
62 	memcpy(&scr.timeout, &table->conf.timeout, sizeof(scr.timeout));
63 
64 	proc_compose_imsg(env->sc_ps, PROC_PARENT, 0, IMSG_SCRIPT,
65 	    -1, &scr, sizeof(scr));
66 }
67 
68 void
69 script_done(struct relayd *env, struct ctl_script *scr)
70 {
71 	struct host		*host;
72 
73 	if ((host = host_find(env, scr->host)) == NULL)
74 		fatalx("hce_dispatch_parent: invalid host id");
75 
76 	if (scr->retval < 0)
77 		host->up = HOST_UNKNOWN;
78 	else if (scr->retval == 0)
79 		host->up = HOST_DOWN;
80 	else
81 		host->up = HOST_UP;
82 	host->flags |= F_CHECK_DONE;
83 
84 	hce_notify_done(host, host->up == HOST_UP ?
85 	    HCE_SCRIPT_OK : HCE_SCRIPT_FAIL);
86 }
87 
88 void
89 script_sig_alarm(int sig)
90 {
91 	int save_errno = errno;
92 
93 	if (child != -1)
94 		kill(child, SIGKILL);
95 	errno = save_errno;
96 }
97 
98 int
99 script_exec(struct relayd *env, struct ctl_script *scr)
100 {
101 	int			 status = 0, ret = 0;
102 	sig_t			 save_quit, save_int, save_chld;
103 	struct itimerval	 it;
104 	struct timeval		*tv;
105 	const char		*file, *arg;
106 	struct passwd		*pw;
107 
108 	if ((env->sc_flags & F_SCRIPT) == 0) {
109 		log_warnx("%s: script disabled", __func__);
110 		return (-1);
111 	}
112 
113 	DPRINTF("%s: running script %s, host %s",
114 	    __func__, scr->path, scr->name);
115 
116 	arg = scr->name;
117 	file = scr->path;
118 	tv = &scr->timeout;
119 
120 	save_quit = signal(SIGQUIT, SIG_IGN);
121 	save_int = signal(SIGINT, SIG_IGN);
122 	save_chld = signal(SIGCHLD, SIG_DFL);
123 
124 	switch (child = fork()) {
125 	case -1:
126 		ret = -1;
127 		goto done;
128 	case 0:
129 		signal(SIGQUIT, SIG_DFL);
130 		signal(SIGINT, SIG_DFL);
131 		signal(SIGCHLD, SIG_DFL);
132 
133 		if ((pw = getpwnam(RELAYD_USER)) == NULL)
134 			fatal("script_exec: getpwnam");
135 		if (chdir("/") == -1)
136 			fatal("script_exec: chdir(\"/\")");
137 		if (setgroups(1, &pw->pw_gid) ||
138 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
139 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
140 			fatal("script_exec: can't drop privileges");
141 
142 		/*
143 		 * close fds before executing an external program, to
144 		 * prevent access to internal fds, eg. IMSG connections
145 		 * of internal processes.
146 		 */
147 		closefrom(STDERR_FILENO + 1);
148 
149 		execlp(file, file, arg, (char *)NULL);
150 		_exit(0);
151 		break;
152 	default:
153 		/* Kill the process after a timeout */
154 		signal(SIGALRM, script_sig_alarm);
155 		bzero(&it, sizeof(it));
156 		bcopy(tv, &it.it_value, sizeof(it.it_value));
157 		setitimer(ITIMER_REAL, &it, NULL);
158 
159 		waitpid(child, &status, 0);
160 		break;
161 	}
162 
163 	switch (ret) {
164 	case -1:
165 		ret = -1;
166 		break;
167 	default:
168 		if (WIFEXITED(status))
169 			ret = WEXITSTATUS(status);
170 		else
171 			ret = 0;
172 	}
173 
174  done:
175 	/* Disable the process timeout timer */
176 	bzero(&it, sizeof(it));
177 	setitimer(ITIMER_REAL, &it, NULL);
178 	child = -1;
179 
180 	signal(SIGQUIT, save_quit);
181 	signal(SIGINT, save_int);
182 	signal(SIGCHLD, save_chld);
183 	signal(SIGALRM, SIG_DFL);
184 
185 	return (ret);
186 }
187