xref: /openbsd/usr.sbin/snmpd/snmpd.c (revision 73471bf0)
1 /*	$OpenBSD: snmpd.c,v 1.46 2021/08/10 06:52:03 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 <string.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <getopt.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <signal.h>
35 #include <syslog.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 
40 #include "snmpd.h"
41 #include "mib.h"
42 
43 __dead void	 usage(void);
44 
45 void	 snmpd_shutdown(struct snmpd *);
46 void	 snmpd_sig_handler(int, short, void *);
47 int	 snmpd_dispatch_snmpe(int, struct privsep_proc *, struct imsg *);
48 int	 check_child(pid_t, const char *);
49 
50 struct snmpd	*snmpd_env;
51 
52 static struct privsep_proc procs[] = {
53 	{ "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
54 };
55 
56 enum privsep_procid privsep_process;
57 
58 void
59 snmpd_sig_handler(int sig, short event, void *arg)
60 {
61 	struct privsep	*ps = arg;
62 	struct snmpd	*env = ps->ps_env;
63 	int		 die = 0, status, fail, id;
64 	pid_t		pid;
65 	char		*cause;
66 
67 	switch (sig) {
68 	case SIGTERM:
69 	case SIGINT:
70 		die = 1;
71 		/* FALLTHROUGH */
72 	case SIGCHLD:
73 		do {
74 			int len;
75 
76 			pid = waitpid(WAIT_ANY, &status, WNOHANG);
77 			if (pid <= 0)
78 				continue;
79 
80 			fail = 0;
81 			if (WIFSIGNALED(status)) {
82 				fail = 1;
83 				len = asprintf(&cause, "terminated; signal %d",
84 				    WTERMSIG(status));
85 			} else if (WIFEXITED(status)) {
86 				if (WEXITSTATUS(status) != 0) {
87 					fail = 1;
88 					len = asprintf(&cause,
89 					    "exited abnormally");
90 				} else
91 					len = asprintf(&cause, "exited okay");
92 			} else
93 				fatalx("unexpected cause of SIGCHLD");
94 
95 			if (len == -1)
96 				fatal("asprintf");
97 
98 			for (id = 0; id < PROC_MAX; id++) {
99 				if (pid == ps->ps_pid[id] &&
100 				    check_child(ps->ps_pid[id],
101 				    ps->ps_title[id])) {
102 					die  = 1;
103 					if (fail)
104 						log_warnx("lost child: %s %s",
105 						    ps->ps_title[id], cause);
106 					break;
107 				}
108 			}
109 			free(cause);
110 		} while (pid > 0 || (pid == -1 && errno == EINTR));
111 
112 		if (die)
113 			snmpd_shutdown(env);
114 		break;
115 	case SIGHUP:
116 		/* reconfigure */
117 		break;
118 	case SIGUSR1:
119 		/* ignore */
120 		break;
121 	default:
122 		fatalx("unexpected signal");
123 	}
124 }
125 
126 __dead void
127 usage(void)
128 {
129 	extern char	*__progname;
130 
131 	fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
132 	    "[-f file]\n", __progname);
133 	exit(1);
134 }
135 
136 int
137 main(int argc, char *argv[])
138 {
139 	int		 c;
140 	struct snmpd	*env;
141 	int		 debug = 0, verbose = 0;
142 	u_int		 flags = 0;
143 	int		 noaction = 0;
144 	const char	*conffile = CONF_FILE;
145 	struct privsep	*ps;
146 	int		 proc_id = PROC_PARENT, proc_instance = 0;
147 	int		 argc0 = argc;
148 	char		**argv0 = argv;
149 	const char	*errp, *title = NULL;
150 
151 	smi_init();
152 
153 	/* log to stderr until daemonized */
154 	log_init(1, LOG_DAEMON);
155 
156 	while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) {
157 		switch (c) {
158 		case 'd':
159 			debug++;
160 			flags |= SNMPD_F_DEBUG;
161 			break;
162 		case 'D':
163 			if (cmdline_symset(optarg) < 0)
164 				log_warnx("could not parse macro definition %s",
165 				    optarg);
166 			break;
167 		case 'n':
168 			noaction = 1;
169 			break;
170 		case 'N':
171 			flags |= SNMPD_F_NONAMES;
172 			break;
173 		case 'f':
174 			conffile = optarg;
175 			break;
176 		case 'I':
177 			proc_instance = strtonum(optarg, 0,
178 			    PROC_MAX_INSTANCES, &errp);
179 			if (errp)
180 				fatalx("invalid process instance");
181 			break;
182 		case 'P':
183 			title = optarg;
184 			proc_id = proc_getid(procs, nitems(procs), title);
185 			if (proc_id == PROC_MAX)
186 				fatalx("invalid process name");
187 			break;
188 		case 'v':
189 			verbose++;
190 			flags |= SNMPD_F_VERBOSE;
191 			break;
192 		default:
193 			usage();
194 		}
195 	}
196 
197 	argc -= optind;
198 	argv += optind;
199 	if (argc > 0)
200 		usage();
201 
202 	if ((env = parse_config(conffile, flags)) == NULL)
203 		exit(1);
204 
205 	ps = &env->sc_ps;
206 	ps->ps_env = env;
207 	snmpd_env = env;
208 	ps->ps_instance = proc_instance;
209 	if (title)
210 		ps->ps_title[proc_id] = title;
211 
212 	if (noaction) {
213 		fprintf(stderr, "configuration ok\n");
214 		exit(0);
215 	}
216 
217 	if (geteuid())
218 		errx(1, "need root privileges");
219 
220 	if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
221 		errx(1, "unknown user %s", SNMPD_USER);
222 
223 	log_init(debug, LOG_DAEMON);
224 	log_setverbose(verbose);
225 
226 	gettimeofday(&env->sc_starttime, NULL);
227 	env->sc_engine_boots = 0;
228 
229 	pf_init();
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 
256 	if (pledge("stdio dns sendfd proc exec id", NULL) == -1)
257 		fatal("pledge");
258 
259 	event_dispatch();
260 
261 	log_debug("%d parent exiting", getpid());
262 
263 	return (0);
264 }
265 
266 void
267 snmpd_shutdown(struct snmpd *env)
268 {
269 	proc_kill(&env->sc_ps);
270 
271 	free(env);
272 
273 	log_info("terminating");
274 	exit(0);
275 }
276 
277 int
278 check_child(pid_t pid, const char *pname)
279 {
280 	int	status;
281 
282 	if (waitpid(pid, &status, WNOHANG) > 0) {
283 		if (WIFEXITED(status)) {
284 			log_warnx("check_child: lost child: %s exited", pname);
285 			return (1);
286 		}
287 		if (WIFSIGNALED(status)) {
288 			log_warnx("check_child: lost child: %s terminated; "
289 			    "signal %d", pname, WTERMSIG(status));
290 			return (1);
291 		}
292 	}
293 
294 	return (0);
295 }
296 
297 int
298 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
299 {
300 	switch (imsg->hdr.type) {
301 	case IMSG_TRAP_EXEC:
302 		return (traphandler_priv_recvmsg(p, imsg));
303 	case IMSG_CTL_RELOAD:
304 		/* XXX notyet */
305 	default:
306 		break;
307 	}
308 
309 	return (-1);
310 }
311 
312 int
313 snmpd_socket_af(struct sockaddr_storage *ss, int type)
314 {
315 	int fd, serrno;
316 	const int enable = 1;
317 
318 	fd = socket(ss->ss_family, (type == SOCK_STREAM ?
319 	    SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0);
320 	if (fd == -1)
321 		return -1;
322 
323 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
324 	    sizeof(enable)) == -1) {
325 		serrno = errno;
326 		close(fd);
327 		errno = serrno;
328 		return -1;
329 	}
330 	return fd;
331 }
332 
333 u_long
334 snmpd_engine_time(void)
335 {
336 	struct timeval	 now;
337 
338 	/*
339 	 * snmpEngineBoots should be stored in a non-volatile storage.
340 	 * snmpEngineTime is the number of seconds since snmpEngineBoots
341 	 * was last incremented. We don't rely on non-volatile storage.
342 	 * snmpEngineBoots is set to zero and snmpEngineTime to the system
343 	 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is
344 	 * still unique and protects us against replay attacks. It only
345 	 * 'expires' a little bit sooner than the RFC3414 method.
346 	 */
347 	gettimeofday(&now, NULL);
348 	return now.tv_sec;
349 }
350