xref: /openbsd-src/usr.sbin/snmpd/snmpd.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: snmpd.c,v 1.48 2022/10/06 14:41:08 martijn Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2012 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/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 #include <sys/tree.h>
24 
25 #include <net/if.h>
26 
27 #include <dirent.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <signal.h>
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40 
41 #include "snmpd.h"
42 #include "mib.h"
43 
44 __dead void	 usage(void);
45 
46 void	 snmpd_shutdown(struct snmpd *);
47 void	 snmpd_sig_handler(int, short, void *);
48 int	 snmpd_dispatch_snmpe(int, struct privsep_proc *, struct imsg *);
49 int	 check_child(pid_t, const char *);
50 void	 snmpd_backend(struct snmpd *);
51 
52 struct snmpd	*snmpd_env;
53 
54 static struct privsep_proc procs[] = {
55 	{ "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
56 };
57 
58 enum privsep_procid privsep_process;
59 
60 void
61 snmpd_sig_handler(int sig, short event, void *arg)
62 {
63 	struct privsep	*ps = arg;
64 	struct snmpd	*env = ps->ps_env;
65 	int		 die = 0, status, fail, id;
66 	pid_t		pid;
67 	char		*cause;
68 
69 	switch (sig) {
70 	case SIGTERM:
71 	case SIGINT:
72 		die = 1;
73 		/* FALLTHROUGH */
74 	case SIGCHLD:
75 		do {
76 			int len;
77 
78 			pid = waitpid(WAIT_ANY, &status, WNOHANG);
79 			if (pid <= 0)
80 				continue;
81 
82 			fail = 0;
83 			if (WIFSIGNALED(status)) {
84 				fail = 1;
85 				len = asprintf(&cause, "terminated; signal %d",
86 				    WTERMSIG(status));
87 			} else if (WIFEXITED(status)) {
88 				if (WEXITSTATUS(status) != 0) {
89 					fail = 1;
90 					len = asprintf(&cause,
91 					    "exited abnormally");
92 				} else
93 					len = asprintf(&cause, "exited okay");
94 			} else
95 				fatalx("unexpected cause of SIGCHLD");
96 
97 			if (len == -1)
98 				fatal("asprintf");
99 
100 			for (id = 0; id < PROC_MAX; id++) {
101 				if (pid == ps->ps_pid[id] &&
102 				    check_child(ps->ps_pid[id],
103 				    ps->ps_title[id])) {
104 					die  = 1;
105 					if (fail)
106 						log_warnx("lost child: %s %s",
107 						    ps->ps_title[id], cause);
108 					break;
109 				}
110 			}
111 			free(cause);
112 		} while (pid > 0 || (pid == -1 && errno == EINTR));
113 
114 		if (die)
115 			snmpd_shutdown(env);
116 		break;
117 	case SIGHUP:
118 		/* reconfigure */
119 		break;
120 	case SIGUSR1:
121 		/* ignore */
122 		break;
123 	default:
124 		fatalx("unexpected signal");
125 	}
126 }
127 
128 __dead void
129 usage(void)
130 {
131 	extern char	*__progname;
132 
133 	fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
134 	    "[-f file]\n", __progname);
135 	exit(1);
136 }
137 
138 int
139 main(int argc, char *argv[])
140 {
141 	int		 c;
142 	struct snmpd	*env;
143 	int		 debug = 0, verbose = 0;
144 	u_int		 flags = 0;
145 	int		 noaction = 0;
146 	const char	*conffile = CONF_FILE;
147 	struct privsep	*ps;
148 	int		 proc_id = PROC_PARENT, proc_instance = 0;
149 	int		 argc0 = argc;
150 	char		**argv0 = argv;
151 	const char	*errp, *title = NULL;
152 
153 	smi_init();
154 
155 	/* log to stderr until daemonized */
156 	log_init(1, LOG_DAEMON);
157 
158 	while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) {
159 		switch (c) {
160 		case 'd':
161 			debug++;
162 			flags |= SNMPD_F_DEBUG;
163 			break;
164 		case 'D':
165 			if (cmdline_symset(optarg) < 0)
166 				log_warnx("could not parse macro definition %s",
167 				    optarg);
168 			break;
169 		case 'n':
170 			noaction = 1;
171 			break;
172 		case 'N':
173 			flags |= SNMPD_F_NONAMES;
174 			break;
175 		case 'f':
176 			conffile = optarg;
177 			break;
178 		case 'I':
179 			proc_instance = strtonum(optarg, 0,
180 			    PROC_MAX_INSTANCES, &errp);
181 			if (errp)
182 				fatalx("invalid process instance");
183 			break;
184 		case 'P':
185 			title = optarg;
186 			proc_id = proc_getid(procs, nitems(procs), title);
187 			if (proc_id == PROC_MAX)
188 				fatalx("invalid process name");
189 			break;
190 		case 'v':
191 			verbose++;
192 			flags |= SNMPD_F_VERBOSE;
193 			break;
194 		default:
195 			usage();
196 		}
197 	}
198 
199 	argc -= optind;
200 	argv += optind;
201 	if (argc > 0)
202 		usage();
203 
204 	if ((env = parse_config(conffile, flags)) == NULL)
205 		exit(1);
206 
207 	ps = &env->sc_ps;
208 	ps->ps_env = env;
209 	snmpd_env = env;
210 	ps->ps_instance = proc_instance;
211 	if (title)
212 		ps->ps_title[proc_id] = title;
213 
214 	if (noaction) {
215 		fprintf(stderr, "configuration ok\n");
216 		exit(0);
217 	}
218 
219 	if (geteuid())
220 		errx(1, "need root privileges");
221 
222 	if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
223 		errx(1, "unknown user %s", SNMPD_USER);
224 
225 	log_init(debug, LOG_DAEMON);
226 	log_setverbose(verbose);
227 
228 	gettimeofday(&env->sc_starttime, NULL);
229 	env->sc_engine_boots = 0;
230 
231 	proc_init(ps, procs, nitems(procs), debug, argc0, argv0, proc_id);
232 	if (!debug && daemon(0, 0) == -1)
233 		err(1, "failed to daemonize");
234 
235 	log_procinit("parent");
236 	log_info("startup");
237 
238 	event_init();
239 
240 	signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps);
241 	signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps);
242 	signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps);
243 	signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps);
244 	signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps);
245 	signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps);
246 
247 	signal_add(&ps->ps_evsigint, NULL);
248 	signal_add(&ps->ps_evsigterm, NULL);
249 	signal_add(&ps->ps_evsigchld, NULL);
250 	signal_add(&ps->ps_evsighup, NULL);
251 	signal_add(&ps->ps_evsigpipe, NULL);
252 	signal_add(&ps->ps_evsigusr1, NULL);
253 
254 	proc_connect(ps);
255 	snmpd_backend(env);
256 
257 	if (pledge("stdio dns sendfd proc exec id", NULL) == -1)
258 		fatal("pledge");
259 
260 	event_dispatch();
261 
262 	log_debug("%d parent exiting", getpid());
263 
264 	return (0);
265 }
266 
267 void
268 snmpd_shutdown(struct snmpd *env)
269 {
270 	proc_kill(&env->sc_ps);
271 
272 	free(env);
273 
274 	log_info("terminating");
275 	exit(0);
276 }
277 
278 int
279 check_child(pid_t pid, const char *pname)
280 {
281 	int	status;
282 
283 	if (waitpid(pid, &status, WNOHANG) > 0) {
284 		if (WIFEXITED(status)) {
285 			log_warnx("check_child: lost child: %s exited", pname);
286 			return (1);
287 		}
288 		if (WIFSIGNALED(status)) {
289 			log_warnx("check_child: lost child: %s terminated; "
290 			    "signal %d", pname, WTERMSIG(status));
291 			return (1);
292 		}
293 	}
294 
295 	return (0);
296 }
297 
298 int
299 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
300 {
301 	switch (imsg->hdr.type) {
302 	case IMSG_TRAP_EXEC:
303 		return (traphandler_priv_recvmsg(p, imsg));
304 	default:
305 		break;
306 	}
307 
308 	return (-1);
309 }
310 
311 int
312 snmpd_socket_af(struct sockaddr_storage *ss, int type)
313 {
314 	int fd, serrno;
315 	const int enable = 1;
316 
317 	fd = socket(ss->ss_family, (type == SOCK_STREAM ?
318 	    SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0);
319 	if (fd == -1)
320 		return -1;
321 
322 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
323 	    sizeof(enable)) == -1) {
324 		serrno = errno;
325 		close(fd);
326 		errno = serrno;
327 		return -1;
328 	}
329 	return fd;
330 }
331 
332 u_long
333 snmpd_engine_time(void)
334 {
335 	struct timeval	 now;
336 
337 	/*
338 	 * snmpEngineBoots should be stored in a non-volatile storage.
339 	 * snmpEngineTime is the number of seconds since snmpEngineBoots
340 	 * was last incremented. We don't rely on non-volatile storage.
341 	 * snmpEngineBoots is set to zero and snmpEngineTime to the system
342 	 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is
343 	 * still unique and protects us against replay attacks. It only
344 	 * 'expires' a little bit sooner than the RFC3414 method.
345 	 */
346 	gettimeofday(&now, NULL);
347 	return now.tv_sec;
348 }
349 
350 void
351 snmpd_backend(struct snmpd *env)
352 {
353 	DIR *dir;
354 	struct dirent *file;
355 	int pair[2];
356 	char *argv[8];
357 	char execpath[PATH_MAX];
358 	size_t i = 0;
359 
360 	if ((dir = opendir(SNMPD_BACKEND)) == NULL)
361 		fatal("opendir \"%s\"", SNMPD_BACKEND);
362 
363 	argv[i++] = execpath;
364 	if (env->sc_rtfilter) {
365 		argv[i++] = "-C";
366 		argv[i++] = "filter-routes";
367 	}
368 	if (env->sc_flags & SNMPD_F_VERBOSE)
369 		argv[i++] = "-vv";
370 	if (env->sc_flags & SNMPD_F_DEBUG) {
371 		argv[i++] = "-d";
372 		argv[i++] = "-x";
373 		argv[i++] = "3";
374 	} else {
375 		argv[i++] = "-x";
376 		argv[i++] = "0";
377 	}
378 	argv[i] = NULL;
379 	while ((file = readdir(dir)) != NULL) {
380 		if (file->d_name[0] == '.')
381 			continue;
382 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
383 			fatal("socketpair");
384 		switch (fork()) {
385 		case -1:
386 			fatal("fork");
387 		case 0:
388 			close(pair[1]);
389 			if (dup2(pair[0],
390 			    env->sc_flags & SNMPD_F_DEBUG ? 3 : 0) == -1)
391 				fatal("dup2");
392 			if (closefrom(env->sc_flags & SNMPD_F_DEBUG ? 4 : 1) == -1)
393 				fatal("closefrom");
394 			(void)snprintf(execpath, sizeof(execpath), "%s/%s",
395 			    SNMPD_BACKEND, file->d_name);
396 			execv(argv[0], argv);
397 			fatal("execv");
398 		default:
399 			close(pair[0]);
400 			if (proc_compose_imsg(&env->sc_ps, PROC_SNMPE, -1,
401 			    IMSG_AX_FD, -1, pair[1], NULL, 0) == -1)
402 				fatal("proc_compose_imsg");
403 			continue;
404 		}
405 	}
406 }
407