xref: /openbsd-src/usr.sbin/snmpd/snmpd.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: snmpd.c,v 1.23 2014/05/23 18:37:20 benno 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/param.h>
23 #include <sys/wait.h>
24 #include <sys/tree.h>
25 
26 #include <net/if.h>
27 
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 <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 			pid = waitpid(WAIT_ANY, &status, WNOHANG);
76 			if (pid <= 0)
77 				continue;
78 
79 			fail = 0;
80 			if (WIFSIGNALED(status)) {
81 				fail = 1;
82 				asprintf(&cause, "terminated; signal %d",
83 				    WTERMSIG(status));
84 			} else if (WIFEXITED(status)) {
85 				if (WEXITSTATUS(status) != 0) {
86 					fail = 1;
87 					asprintf(&cause, "exited abnormally");
88 				} else
89 					asprintf(&cause, "exited okay");
90 			} else
91 				fatalx("unexpected cause of SIGCHLD");
92 
93 			for (id = 0; id < PROC_MAX; id++) {
94 				if (pid == ps->ps_pid[id] &&
95 				    check_child(ps->ps_pid[id],
96 				    ps->ps_title[id])) {
97 					die  = 1;
98 					if (fail)
99 						log_warnx("lost child: %s %s",
100 						    ps->ps_title[id], cause);
101 					break;
102 				}
103 			}
104 			free(cause);
105 		} while (pid > 0 || (pid == -1 && errno == EINTR));
106 
107 		if (die)
108 			snmpd_shutdown(env);
109 		break;
110 	case SIGHUP:
111 		/* reconfigure */
112 		break;
113 	default:
114 		fatalx("unexpected signal");
115 	}
116 }
117 
118 __dead void
119 usage(void)
120 {
121 	extern char	*__progname;
122 
123 	fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
124 	    "[-f file]\n", __progname);
125 	exit(1);
126 }
127 
128 int
129 main(int argc, char *argv[])
130 {
131 	int		 c;
132 	struct snmpd	*env;
133 	int		 debug = 0, verbose = 0;
134 	u_int		 flags = 0;
135 	int		 noaction = 0;
136 	const char	*conffile = CONF_FILE;
137 	struct privsep	*ps;
138 
139 	smi_init();
140 	log_init(1);	/* log to stderr until daemonized */
141 
142 	while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) {
143 		switch (c) {
144 		case 'd':
145 			debug++;
146 			break;
147 		case 'D':
148 			if (cmdline_symset(optarg) < 0)
149 				log_warnx("could not parse macro definition %s",
150 				    optarg);
151 			break;
152 		case 'n':
153 			noaction++;
154 			break;
155 		case 'N':
156 			flags |= SNMPD_F_NONAMES;
157 			break;
158 		case 'f':
159 			conffile = optarg;
160 			break;
161 		case 'v':
162 			verbose++;
163 			flags |= SNMPD_F_VERBOSE;
164 			break;
165 		default:
166 			usage();
167 		}
168 	}
169 
170 	argc -= optind;
171 	argv += optind;
172 	if (argc > 0)
173 		usage();
174 
175 	if ((env = parse_config(conffile, flags)) == NULL)
176 		exit(1);
177 
178 	ps = &env->sc_ps;
179 	ps->ps_env = env;
180 	snmpd_env = env;
181 
182 	if (noaction) {
183 		fprintf(stderr, "configuration ok\n");
184 		exit(0);
185 	}
186 
187 	if (geteuid())
188 		errx(1, "need root privileges");
189 
190 	if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
191 		errx(1, "unknown user %s", SNMPD_USER);
192 
193 	log_init(debug);
194 	log_verbose(verbose);
195 
196 	if (!debug && daemon(0, 0) == -1)
197 		err(1, "failed to daemonize");
198 
199 	gettimeofday(&env->sc_starttime, NULL);
200 	env->sc_engine_boots = 0;
201 
202 	pf_init();
203 	snmpd_generate_engineid(env);
204 
205 	ps->ps_ninstances = 1;
206 	proc_init(ps, procs, nitems(procs));
207 
208 	setproctitle("parent");
209 	log_info("startup");
210 
211 	event_init();
212 
213 	signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps);
214 	signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps);
215 	signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps);
216 	signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps);
217 	signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps);
218 
219 	signal_add(&ps->ps_evsigint, NULL);
220 	signal_add(&ps->ps_evsigterm, NULL);
221 	signal_add(&ps->ps_evsigchld, NULL);
222 	signal_add(&ps->ps_evsighup, NULL);
223 	signal_add(&ps->ps_evsigpipe, NULL);
224 
225 	proc_listen(ps, procs, nitems(procs));
226 
227 	event_dispatch();
228 
229 	log_debug("%d parent exiting", getpid());
230 
231 	return (0);
232 }
233 
234 void
235 snmpd_shutdown(struct snmpd *env)
236 {
237 	proc_kill(&env->sc_ps);
238 
239 	free(env);
240 
241 	log_info("terminating");
242 	exit(0);
243 }
244 
245 int
246 check_child(pid_t pid, const char *pname)
247 {
248 	int	status;
249 
250 	if (waitpid(pid, &status, WNOHANG) > 0) {
251 		if (WIFEXITED(status)) {
252 			log_warnx("check_child: lost child: %s exited", pname);
253 			return (1);
254 		}
255 		if (WIFSIGNALED(status)) {
256 			log_warnx("check_child: lost child: %s terminated; "
257 			    "signal %d", pname, WTERMSIG(status));
258 			return (1);
259 		}
260 	}
261 
262 	return (0);
263 }
264 
265 int
266 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
267 {
268 	switch (imsg->hdr.type) {
269 	case IMSG_CTL_RELOAD:
270 		/* XXX notyet */
271 	default:
272 		break;
273 	}
274 
275 	return (-1);
276 }
277 
278 int
279 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port)
280 {
281 	int	 s;
282 
283 	switch (ss->ss_family) {
284 	case AF_INET:
285 		((struct sockaddr_in *)ss)->sin_port = port;
286 		((struct sockaddr_in *)ss)->sin_len =
287 		    sizeof(struct sockaddr_in);
288 		break;
289 	case AF_INET6:
290 		((struct sockaddr_in6 *)ss)->sin6_port = port;
291 		((struct sockaddr_in6 *)ss)->sin6_len =
292 		    sizeof(struct sockaddr_in6);
293 		break;
294 	default:
295 		return (-1);
296 	}
297 
298 	s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP);
299 	return (s);
300 }
301 
302 void
303 snmpd_generate_engineid(struct snmpd *env)
304 {
305 	u_int32_t		 oid_enterprise, rnd, tim;
306 
307 	/* RFC 3411 */
308 	memset(env->sc_engineid, 0, sizeof(env->sc_engineid));
309 	oid_enterprise = htonl(OIDVAL_openBSD_eid);
310 	memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise));
311 	env->sc_engineid[0] |= SNMP_ENGINEID_NEW;
312 	env->sc_engineid_len = sizeof(oid_enterprise);
313 
314 	/* XXX alternatively configure engine id via snmpd.conf */
315 	env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID;
316 	rnd = arc4random();
317 	memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd));
318 	env->sc_engineid_len += sizeof(rnd);
319 
320 	tim = htonl(env->sc_starttime.tv_sec);
321 	memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim));
322 	env->sc_engineid_len += sizeof(tim);
323 }
324 
325 u_long
326 snmpd_engine_time(void)
327 {
328 	struct timeval	 now;
329 
330 	/*
331 	 * snmpEngineBoots should be stored in a non-volatile storage.
332 	 * snmpEngineTime is the number of seconds since snmpEngineBoots
333 	 * was last incremented. We don't rely on non-volatile storage.
334 	 * snmpEngineBoots is set to zero and snmpEngineTime to the system
335 	 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is
336 	 * still unique and protects us against replay attacks. It only
337 	 * 'expires' a little bit sooner than the RFC3414 method.
338 	 */
339 	gettimeofday(&now, NULL);
340 	return now.tv_sec;
341 }
342 
343 char *
344 tohexstr(u_int8_t *str, int len)
345 {
346 #define MAXHEXSTRLEN		256
347 	static char hstr[2 * MAXHEXSTRLEN + 1];
348 	char *r = hstr;
349 
350 	if (len > MAXHEXSTRLEN)
351 		len = MAXHEXSTRLEN;	/* truncate */
352 	while (len-- > 0)
353 		r += snprintf(r, len * 2, "%0*x", 2, *str++);
354 	*r = '\0';
355 	return hstr;
356 }
357 
358 void
359 socket_set_blockmode(int fd, enum blockmodes bm)
360 {
361 	int	flags;
362 
363 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
364 		fatal("fcntl F_GETFL");
365 
366 	if (bm == BM_NONBLOCK)
367 		flags |= O_NONBLOCK;
368 	else
369 		flags &= ~O_NONBLOCK;
370 
371 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
372 		fatal("fcntl F_SETFL");
373 }
374