xref: /openbsd-src/usr.sbin/snmpd/snmpd.c (revision f763167468dba5339ed4b14b7ecaca2a397ab0f6)
1 /*	$OpenBSD: snmpd.c,v 1.37 2017/08/12 04:29:57 rob 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 void	 snmpd_generate_engineid(struct snmpd *);
49 int	 check_child(pid_t, const char *);
50 
51 struct snmpd	*snmpd_env;
52 
53 static struct privsep_proc procs[] = {
54 	{ "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
55 	{ "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler,
56 	    traphandler_shutdown }
57 };
58 
59 void
60 snmpd_sig_handler(int sig, short event, void *arg)
61 {
62 	struct privsep	*ps = arg;
63 	struct snmpd	*env = ps->ps_env;
64 	int		 die = 0, status, fail, id;
65 	pid_t		pid;
66 	char		*cause;
67 
68 	switch (sig) {
69 	case SIGTERM:
70 	case SIGINT:
71 		die = 1;
72 		/* FALLTHROUGH */
73 	case SIGCHLD:
74 		do {
75 			int len;
76 
77 			pid = waitpid(WAIT_ANY, &status, WNOHANG);
78 			if (pid <= 0)
79 				continue;
80 
81 			fail = 0;
82 			if (WIFSIGNALED(status)) {
83 				fail = 1;
84 				len = asprintf(&cause, "terminated; signal %d",
85 				    WTERMSIG(status));
86 			} else if (WIFEXITED(status)) {
87 				if (WEXITSTATUS(status) != 0) {
88 					fail = 1;
89 					len = asprintf(&cause,
90 					    "exited abnormally");
91 				} else
92 					len = asprintf(&cause, "exited okay");
93 			} else
94 				fatalx("unexpected cause of SIGCHLD");
95 
96 			if (len == -1)
97 				fatal("asprintf");
98 
99 			for (id = 0; id < PROC_MAX; id++) {
100 				if (pid == ps->ps_pid[id] &&
101 				    check_child(ps->ps_pid[id],
102 				    ps->ps_title[id])) {
103 					die  = 1;
104 					if (fail)
105 						log_warnx("lost child: %s %s",
106 						    ps->ps_title[id], cause);
107 					break;
108 				}
109 			}
110 			free(cause);
111 		} while (pid > 0 || (pid == -1 && errno == EINTR));
112 
113 		if (die)
114 			snmpd_shutdown(env);
115 		break;
116 	case SIGHUP:
117 		/* reconfigure */
118 		break;
119 	case SIGUSR1:
120 		/* ignore */
121 		break;
122 	default:
123 		fatalx("unexpected signal");
124 	}
125 }
126 
127 __dead void
128 usage(void)
129 {
130 	extern char	*__progname;
131 
132 	fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
133 	    "[-f file]\n", __progname);
134 	exit(1);
135 }
136 
137 int
138 main(int argc, char *argv[])
139 {
140 	int		 c;
141 	struct snmpd	*env;
142 	int		 debug = 0, verbose = 0;
143 	u_int		 flags = 0;
144 	int		 noaction = 0;
145 	const char	*conffile = CONF_FILE;
146 	struct privsep	*ps;
147 	int		 proc_id = PROC_PARENT, proc_instance = 0;
148 	int		 argc0 = argc;
149 	char		**argv0 = argv;
150 	const char	*errp, *title = NULL;
151 
152 	smi_init();
153 
154 	/* log to stderr until daemonized */
155 	log_init(1, LOG_DAEMON);
156 
157 	while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) {
158 		switch (c) {
159 		case 'd':
160 			debug++;
161 			flags |= SNMPD_F_DEBUG;
162 			break;
163 		case 'D':
164 			if (cmdline_symset(optarg) < 0)
165 				log_warnx("could not parse macro definition %s",
166 				    optarg);
167 			break;
168 		case 'n':
169 			noaction = 1;
170 			break;
171 		case 'N':
172 			flags |= SNMPD_F_NONAMES;
173 			break;
174 		case 'f':
175 			conffile = optarg;
176 			break;
177 		case 'I':
178 			proc_instance = strtonum(optarg, 0,
179 			    PROC_MAX_INSTANCES, &errp);
180 			if (errp)
181 				fatalx("invalid process instance");
182 			break;
183 		case 'P':
184 			title = optarg;
185 			proc_id = proc_getid(procs, nitems(procs), title);
186 			if (proc_id == PROC_MAX)
187 				fatalx("invalid process name");
188 			break;
189 		case 'v':
190 			verbose++;
191 			flags |= SNMPD_F_VERBOSE;
192 			break;
193 		default:
194 			usage();
195 		}
196 	}
197 
198 	argc -= optind;
199 	argv += optind;
200 	if (argc > 0)
201 		usage();
202 
203 	if ((env = parse_config(conffile, flags)) == NULL)
204 		exit(1);
205 
206 	ps = &env->sc_ps;
207 	ps->ps_env = env;
208 	snmpd_env = env;
209 	ps->ps_instance = proc_instance;
210 	if (title)
211 		ps->ps_title[proc_id] = title;
212 
213 	if (noaction) {
214 		fprintf(stderr, "configuration ok\n");
215 		exit(0);
216 	}
217 
218 	if (geteuid())
219 		errx(1, "need root privileges");
220 
221 	if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
222 		errx(1, "unknown user %s", SNMPD_USER);
223 
224 	log_init(debug, LOG_DAEMON);
225 	log_setverbose(verbose);
226 
227 	gettimeofday(&env->sc_starttime, NULL);
228 	env->sc_engine_boots = 0;
229 
230 	pf_init();
231 	snmpd_generate_engineid(env);
232 
233 	proc_init(ps, procs, nitems(procs), argc0, argv0, proc_id);
234 	if (!debug && daemon(0, 0) == -1)
235 		err(1, "failed to daemonize");
236 
237 	log_procinit("parent");
238 	log_info("startup");
239 
240 	event_init();
241 
242 	signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps);
243 	signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps);
244 	signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps);
245 	signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps);
246 	signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps);
247 	signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps);
248 
249 	signal_add(&ps->ps_evsigint, NULL);
250 	signal_add(&ps->ps_evsigterm, NULL);
251 	signal_add(&ps->ps_evsigchld, NULL);
252 	signal_add(&ps->ps_evsighup, NULL);
253 	signal_add(&ps->ps_evsigpipe, NULL);
254 	signal_add(&ps->ps_evsigusr1, NULL);
255 
256 	proc_connect(ps);
257 
258 	if (pledge("stdio rpath cpath dns id proc sendfd exec", NULL) == -1)
259 		fatal("pledge");
260 
261 	event_dispatch();
262 
263 	log_debug("%d parent exiting", getpid());
264 
265 	return (0);
266 }
267 
268 void
269 snmpd_shutdown(struct snmpd *env)
270 {
271 	proc_kill(&env->sc_ps);
272 
273 	if (env->sc_ps.ps_csock.cs_name != NULL)
274 		(void)unlink(env->sc_ps.ps_csock.cs_name);
275 
276 	free(env);
277 
278 	log_info("terminating");
279 	exit(0);
280 }
281 
282 int
283 check_child(pid_t pid, const char *pname)
284 {
285 	int	status;
286 
287 	if (waitpid(pid, &status, WNOHANG) > 0) {
288 		if (WIFEXITED(status)) {
289 			log_warnx("check_child: lost child: %s exited", pname);
290 			return (1);
291 		}
292 		if (WIFSIGNALED(status)) {
293 			log_warnx("check_child: lost child: %s terminated; "
294 			    "signal %d", pname, WTERMSIG(status));
295 			return (1);
296 		}
297 	}
298 
299 	return (0);
300 }
301 
302 int
303 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
304 {
305 	switch (imsg->hdr.type) {
306 	case IMSG_CTL_RELOAD:
307 		/* XXX notyet */
308 	default:
309 		break;
310 	}
311 
312 	return (-1);
313 }
314 
315 int
316 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port)
317 {
318 	int	 s;
319 
320 	switch (ss->ss_family) {
321 	case AF_INET:
322 		((struct sockaddr_in *)ss)->sin_port = port;
323 		((struct sockaddr_in *)ss)->sin_len =
324 		    sizeof(struct sockaddr_in);
325 		break;
326 	case AF_INET6:
327 		((struct sockaddr_in6 *)ss)->sin6_port = port;
328 		((struct sockaddr_in6 *)ss)->sin6_len =
329 		    sizeof(struct sockaddr_in6);
330 		break;
331 	default:
332 		return (-1);
333 	}
334 
335 	s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP);
336 	return (s);
337 }
338 
339 void
340 snmpd_generate_engineid(struct snmpd *env)
341 {
342 	u_int32_t		 oid_enterprise, rnd, tim;
343 
344 	/* RFC 3411 */
345 	memset(env->sc_engineid, 0, sizeof(env->sc_engineid));
346 	oid_enterprise = htonl(OIDVAL_openBSD_eid);
347 	memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise));
348 	env->sc_engineid[0] |= SNMP_ENGINEID_NEW;
349 	env->sc_engineid_len = sizeof(oid_enterprise);
350 
351 	/* XXX alternatively configure engine id via snmpd.conf */
352 	env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID;
353 	rnd = arc4random();
354 	memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd));
355 	env->sc_engineid_len += sizeof(rnd);
356 
357 	tim = htonl(env->sc_starttime.tv_sec);
358 	memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim));
359 	env->sc_engineid_len += sizeof(tim);
360 }
361 
362 u_long
363 snmpd_engine_time(void)
364 {
365 	struct timeval	 now;
366 
367 	/*
368 	 * snmpEngineBoots should be stored in a non-volatile storage.
369 	 * snmpEngineTime is the number of seconds since snmpEngineBoots
370 	 * was last incremented. We don't rely on non-volatile storage.
371 	 * snmpEngineBoots is set to zero and snmpEngineTime to the system
372 	 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is
373 	 * still unique and protects us against replay attacks. It only
374 	 * 'expires' a little bit sooner than the RFC3414 method.
375 	 */
376 	gettimeofday(&now, NULL);
377 	return now.tv_sec;
378 }
379 
380 char *
381 tohexstr(u_int8_t *bstr, int len)
382 {
383 #define MAXHEXSTRLEN		256
384 	static char hstr[2 * MAXHEXSTRLEN + 1];
385 	static const char hex[] = "0123456789abcdef";
386 	int i;
387 
388 	if (len > MAXHEXSTRLEN)
389 		len = MAXHEXSTRLEN;	/* truncate */
390 	for (i = 0; i < len; i++) {
391 		hstr[i + i] = hex[bstr[i] >> 4];
392 		hstr[i + i + 1] = hex[bstr[i] & 0x0f];
393 	}
394 	hstr[i + i] = '\0';
395 	return hstr;
396 }
397