GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/snmpd.c Lines: 85 161 52.8 %
Date: 2017-11-07 Branches: 29 86 33.7 %

Line Branch Exec Source
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
80
	struct privsep	*ps = arg;
63
60
	struct snmpd	*env = ps->ps_env;
64
60
	int		 die = 0, status, fail, id;
65
	pid_t		pid;
66
60
	char		*cause;
67
68

60
	switch (sig) {
69
	case SIGTERM:
70
	case SIGINT:
71
		die = 1;
72
		/* FALLTHROUGH */
73
	case SIGCHLD:
74
		do {
75
			int len;
76
77
40
			pid = waitpid(WAIT_ANY, &status, WNOHANG);
78
40
			if (pid <= 0)
79
20
				continue;
80
81
			fail = 0;
82

40
			if (WIFSIGNALED(status)) {
83
				fail = 1;
84
				len = asprintf(&cause, "terminated; signal %d",
85
				    WTERMSIG(status));
86
20
			} else if (WIFEXITED(status)) {
87
20
				if (WEXITSTATUS(status) != 0) {
88
					fail = 1;
89
					len = asprintf(&cause,
90
					    "exited abnormally");
91
				} else
92
20
					len = asprintf(&cause, "exited okay");
93
			} else
94
				fatalx("unexpected cause of SIGCHLD");
95
96
20
			if (len == -1)
97
				fatal("asprintf");
98
99
160
			for (id = 0; id < PROC_MAX; id++) {
100

60
				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
20
			free(cause);
111

80
		} while (pid > 0 || (pid == -1 && errno == EINTR));
112
113
20
		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
20
}
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
32
	const char	*errp, *title = NULL;
151
152
16
	smi_init();
153
154
	/* log to stderr until daemonized */
155
16
	log_init(1, LOG_DAEMON);
156
157
80
	while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) {
158


48
		switch (c) {
159
		case 'd':
160
16
			debug++;
161
16
			flags |= SNMPD_F_DEBUG;
162
16
			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
16
			conffile = optarg;
176
16
			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
16
			verbose++;
191
16
			flags |= SNMPD_F_VERBOSE;
192
16
			break;
193
		default:
194
			usage();
195
		}
196
	}
197
198
16
	argc -= optind;
199
16
	argv += optind;
200
16
	if (argc > 0)
201
		usage();
202
203
16
	if ((env = parse_config(conffile, flags)) == NULL)
204
		exit(1);
205
206
16
	ps = &env->sc_ps;
207
16
	ps->ps_env = env;
208
16
	snmpd_env = env;
209
16
	ps->ps_instance = proc_instance;
210
16
	if (title)
211
		ps->ps_title[proc_id] = title;
212
213
16
	if (noaction) {
214
		fprintf(stderr, "configuration ok\n");
215
		exit(0);
216
	}
217
218
16
	if (geteuid())
219
		errx(1, "need root privileges");
220
221
16
	if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
222
		errx(1, "unknown user %s", SNMPD_USER);
223
224
16
	log_init(debug, LOG_DAEMON);
225
16
	log_setverbose(verbose);
226
227
16
	gettimeofday(&env->sc_starttime, NULL);
228
16
	env->sc_engine_boots = 0;
229
230
16
	pf_init();
231
16
	snmpd_generate_engineid(env);
232
233
16
	proc_init(ps, procs, nitems(procs), argc0, argv0, proc_id);
234

16
	if (!debug && daemon(0, 0) == -1)
235
		err(1, "failed to daemonize");
236
237
16
	log_procinit("parent");
238
16
	log_info("startup");
239
240
16
	event_init();
241
242
16
	signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps);
243
16
	signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps);
244
16
	signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps);
245
16
	signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps);
246
16
	signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps);
247
16
	signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps);
248
249
16
	signal_add(&ps->ps_evsigint, NULL);
250
16
	signal_add(&ps->ps_evsigterm, NULL);
251
16
	signal_add(&ps->ps_evsigchld, NULL);
252
16
	signal_add(&ps->ps_evsighup, NULL);
253
16
	signal_add(&ps->ps_evsigpipe, NULL);
254
16
	signal_add(&ps->ps_evsigusr1, NULL);
255
256
16
	proc_connect(ps);
257
258
16
	if (pledge("stdio rpath cpath dns id proc sendfd exec flock wpath", NULL) == -1)
259
		fatal("pledge");
260
261
16
	event_dispatch();
262
263
16
	log_debug("%d parent exiting", getpid());
264
265
16
	return (0);
266
16
}
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
32
	memset(env->sc_engineid, 0, sizeof(env->sc_engineid));
346
	oid_enterprise = htonl(OIDVAL_openBSD_eid);
347
16
	memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise));
348
16
	env->sc_engineid[0] |= SNMP_ENGINEID_NEW;
349
16
	env->sc_engineid_len = sizeof(oid_enterprise);
350
351
	/* XXX alternatively configure engine id via snmpd.conf */
352
16
	env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID;
353
16
	rnd = arc4random();
354
16
	memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd));
355
16
	env->sc_engineid_len += sizeof(rnd);
356
357
16
	tim = htonl(env->sc_starttime.tv_sec);
358
16
	memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim));
359
16
	env->sc_engineid_len += sizeof(tim);
360
16
}
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
}