GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/traphandler.c Lines: 10 195 5.1 %
Date: 2017-11-13 Branches: 6 279 2.2 %

Line Branch Exec Source
1
/*	$OpenBSD: traphandler.c,v 1.9 2017/08/12 04:29:57 rob Exp $	*/
2
3
/*
4
 * Copyright (c) 2014 Bret Stephen Lambert <blambert@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/queue.h>
20
#include <sys/socket.h>
21
#include <sys/socketvar.h>
22
#include <sys/stat.h>
23
#include <sys/types.h>
24
#include <sys/uio.h>
25
#include <sys/wait.h>
26
27
#include <net/if.h>
28
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
31
#include <event.h>
32
#include <fcntl.h>
33
#include <imsg.h>
34
#include <netdb.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <syslog.h>
39
#include <unistd.h>
40
#include <pwd.h>
41
42
#include "ber.h"
43
#include "snmpd.h"
44
#include "mib.h"
45
46
char	 trap_path[PATH_MAX];
47
48
void	 traphandler_init(struct privsep *, struct privsep_proc *, void *arg);
49
int	 traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *);
50
int	 traphandler_bind(struct address *);
51
void	 traphandler_recvmsg(int, short, void *);
52
int	 traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
53
int	 traphandler_fork_handler(struct privsep_proc *, struct imsg *);
54
int	 traphandler_parse(char *, size_t, struct ber_element **,
55
	    struct ber_element **, u_int *, struct ber_oid *);
56
void	 traphandler_v1translate(struct ber_oid *, u_int, u_int);
57
58
int	 trapcmd_cmp(struct trapcmd *, struct trapcmd *);
59
void	 trapcmd_exec(struct trapcmd *, struct sockaddr *,
60
	    struct ber_element *, char *, u_int);
61
62
char	*traphandler_hostname(struct sockaddr *, int);
63
64
RB_PROTOTYPE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp)
65













































44
RB_GENERATE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp)
66
67
struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree);
68
69
static struct privsep_proc procs[] = {
70
	{ "parent",	PROC_PARENT,	traphandler_dispatch_parent }
71
};
72
73
void
74
traphandler(struct privsep *ps, struct privsep_proc *p)
75
{
76
	struct snmpd		*env = ps->ps_env;
77
	struct address		*h;
78
	struct listen_sock	*so;
79
80
	if (env->sc_traphandler) {
81
		TAILQ_FOREACH(h, &env->sc_addresses, entry) {
82
			if ((so = calloc(1, sizeof(*so))) == NULL)
83
				fatal("%s", __func__);
84
			if ((so->s_fd = traphandler_bind(h)) == -1)
85
				fatal("could not create trap listener socket");
86
			TAILQ_INSERT_TAIL(&env->sc_sockets, so, entry);
87
		}
88
	}
89
90
	proc_run(ps, p, procs, nitems(procs), traphandler_init, NULL);
91
}
92
93
void
94
traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg)
95
{
96
	struct snmpd		*env = ps->ps_env;
97
	struct listen_sock	*so;
98
99
	if (pledge("stdio id proc recvfd exec flock rpath cpath wpath", NULL) == -1)
100
		fatal("pledge");
101
102
	if (!env->sc_traphandler)
103
		return;
104
105
	/* listen for SNMP trap messages */
106
	TAILQ_FOREACH(so, &env->sc_sockets, entry) {
107
		event_set(&so->s_ev, so->s_fd, EV_READ|EV_PERSIST,
108
		    traphandler_recvmsg, ps);
109
		event_add(&so->s_ev, NULL);
110
	}
111
}
112
113
int
114
traphandler_bind(struct address *addr)
115
{
116
	int			 s;
117
	char			 buf[512];
118
119
	if ((s = snmpd_socket_af(&addr->ss, htons(SNMPD_TRAPPORT))) == -1)
120
		return (-1);
121
122
	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
123
		goto bad;
124
125
	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
126
		goto bad;
127
128
	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
129
		goto bad;
130
131
	log_info("traphandler: listening on %s:%d", buf, SNMPD_TRAPPORT);
132
133
	return (s);
134
 bad:
135
	close (s);
136
	return (-1);
137
}
138
139
void
140
traphandler_shutdown(void)
141
{
142
	struct listen_sock	*so;
143
144
	TAILQ_FOREACH(so, &snmpd_env->sc_sockets, entry) {
145
		event_del(&so->s_ev);
146
		close(so->s_fd);
147
	}
148
}
149
150
int
151
traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
152
{
153
	switch (imsg->hdr.type) {
154
	default:
155
		break;
156
	}
157
158
	return (-1);
159
}
160
161
int
162
snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg)
163
{
164
8
	switch (imsg->hdr.type) {
165
	case IMSG_ALERT:
166
4
		return (traphandler_priv_recvmsg(p, imsg));
167
	default:
168
		break;
169
	}
170
171
	return (-1);
172
4
}
173
174
void
175
traphandler_recvmsg(int fd, short events, void *arg)
176
{
177
	struct privsep		*ps = arg;
178
	char			 buf[8196];
179
	struct iovec		 iov[2];
180
	struct sockaddr_storage	 ss;
181
	socklen_t		 slen;
182
	ssize_t			 n;
183
	struct ber_element	*req, *iter;
184
	struct ber_oid		 trapoid;
185
	u_int			 uptime;
186
187
	slen = sizeof(ss);
188
	if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
189
	    &slen)) == -1)
190
		return;
191
192
	if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
193
		goto done;
194
195
	iov[0].iov_base = &ss;
196
	iov[0].iov_len = ss.ss_len;
197
	iov[1].iov_base = buf;
198
	iov[1].iov_len = n;
199
200
	/* Forward it to the parent process */
201
	if (proc_composev(ps, PROC_PARENT, IMSG_ALERT, iov, 2) == -1)
202
		goto done;
203
204
 done:
205
	if (req != NULL)
206
		ber_free_elements(req);
207
	return;
208
}
209
210
/*
211
 * Validate received message
212
 */
213
int
214
traphandler_parse(char *buf, size_t n, struct ber_element **req,
215
    struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid)
216
{
217
	struct ber		 ber;
218
	struct ber_element	*elm;
219
	u_int			 vers, gtype, etype;
220
221
	bzero(&ber, sizeof(ber));
222
	ber.fd = -1;
223
	ber_set_application(&ber, smi_application);
224
	ber_set_readbuf(&ber, buf, n);
225
226
	if ((*req = ber_read_elements(&ber, NULL)) == NULL)
227
		goto done;
228
229
	if (ber_scanf_elements(*req, "{dSe", &vers, &elm) == -1)
230
		goto done;
231
232
	switch (vers) {
233
	case SNMP_V1:
234
		if (ber_scanf_elements(elm, "{oSddd",
235
		    trapoid, &gtype, &etype, uptime) == -1)
236
			goto done;
237
		traphandler_v1translate(trapoid, gtype, etype);
238
		break;
239
240
	case SNMP_V2:
241
		if (ber_scanf_elements(elm, "{SSSS{e}}", &elm) == -1 ||
242
		    ber_scanf_elements(elm, "{SdS}{So}e",
243
		    uptime, trapoid, vbinds) == -1)
244
			goto done;
245
		break;
246
247
	default:
248
		log_warnx("unsupported SNMP trap version '%d'", vers);
249
		goto done;
250
	}
251
252
	ber_free(&ber);
253
	return (0);
254
255
 done:
256
	ber_free(&ber);
257
	if (*req)
258
		ber_free_elements(*req);
259
	*req = NULL;
260
	return (-1);
261
}
262
263
void
264
traphandler_v1translate(struct ber_oid *oid, u_int gtype, u_int etype)
265
{
266
	/* append 'specific trap' number to 'enterprise specific' traps */
267
	if (gtype >= 6) {
268
		oid->bo_id[oid->bo_n] = 0;
269
		oid->bo_id[oid->bo_n + 1] = etype;
270
		oid->bo_n += 2;
271
	}
272
}
273
274
int
275
traphandler_priv_recvmsg(struct privsep_proc *p, struct imsg *imsg)
276
{
277
	ssize_t			 n;
278
	pid_t			 pid;
279
280
8
	if ((n = IMSG_DATA_SIZE(imsg)) <= 0)
281
		return (-1);			/* XXX */
282
283
4
	switch ((pid = fork())) {
284
	case 0:
285
		traphandler_fork_handler(p, imsg);
286
		/* NOTREACHED */
287
	case -1:
288
		log_warn("%s: couldn't fork traphandler", __func__);
289
		return (0);
290
	default:
291
4
		log_debug("forked process %i to handle trap", pid);
292
4
		return (0);
293
	}
294
	/* NOTREACHED */
295
4
}
296
297
int
298
traphandler_fork_handler(struct privsep_proc *p, struct imsg *imsg)
299
{
300
	struct privsep		*ps = p->p_ps;
301
	struct snmpd		*env = ps->ps_env;
302
	char			 oidbuf[SNMP_MAX_OID_STRLEN];
303
	struct sockaddr		*sa;
304
	char			*buf;
305
	ssize_t			 n;
306
	struct ber_element	*req, *iter;
307
	struct trapcmd		*cmd;
308
	struct ber_oid		 trapoid;
309
	u_int			 uptime;
310
	struct passwd		*pw;
311
	int			 verbose;
312
313
	pw = ps->ps_pw;
314
	verbose = log_getverbose();
315
316
	if (setgroups(1, &pw->pw_gid) ||
317
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
318
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
319
		fatal("traphandler_fork_handler: cannot drop privileges");
320
321
	closefrom(STDERR_FILENO + 1);
322
323
	log_init((env->sc_flags & SNMPD_F_DEBUG) ? 1 : 0, LOG_DAEMON);
324
	log_setverbose(verbose);
325
	log_procinit(p->p_title);
326
327
	n = IMSG_DATA_SIZE(imsg);
328
329
	sa = imsg->data;
330
	n -= sa->sa_len;
331
	buf = (char *)imsg->data + sa->sa_len;
332
333
	if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
334
		fatalx("couldn't parse SNMP trap message");
335
336
	smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0);
337
	if ((cmd = trapcmd_lookup(&trapoid)) != NULL)
338
		trapcmd_exec(cmd, sa, iter, oidbuf, uptime);
339
340
	if (req != NULL)
341
		ber_free_elements(req);
342
343
	exit(0);
344
}
345
346
void
347
trapcmd_exec(struct trapcmd *cmd, struct sockaddr *sa,
348
    struct ber_element *iter, char *trapoid, u_int uptime)
349
{
350
	char			 oidbuf[SNMP_MAX_OID_STRLEN];
351
	struct ber_oid		 oid;
352
	struct ber_element	*elm;
353
	int			 n, s[2], status = 0;
354
	char			*value, *host;
355
	pid_t			 child = -1;
356
357
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) {
358
		log_warn("could not create pipe for OID '%s'",
359
		    smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
360
		return;
361
	}
362
363
	switch (child = fork()) {
364
	case 0:
365
		dup2(s[1], STDIN_FILENO);
366
367
		close(s[0]);
368
		close(s[1]);
369
370
		closefrom(STDERR_FILENO + 1);
371
372
		/* path to command is in argv[0], args follow */
373
		execve(cmd->cmd_argv[0], cmd->cmd_argv, NULL);
374
375
		/* this shouldn't happen */
376
		log_warn("could not exec trap command for OID '%s'",
377
		    smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
378
		_exit(1);
379
		/* NOTREACHED */
380
381
	case -1:
382
		log_warn("could not fork trap command for OID '%s'",
383
		    smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
384
		close(s[0]);
385
		close(s[1]);
386
		return;
387
	}
388
389
	close(s[1]);
390
391
	host = traphandler_hostname(sa, 0);
392
	if (dprintf(s[0], "%s\n", host) == -1)
393
		goto out;
394
395
	host = traphandler_hostname(sa, 1);
396
	if (dprintf(s[0], "%s\n", host) == -1)
397
		goto out;
398
399
	if (dprintf(s[0],
400
	    "iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0 %u\n",
401
	    uptime) == -1)
402
		goto out;
403
404
	if (dprintf(s[0],
405
	    "iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects."
406
	    "snmpTrap.snmpTrapOID.0 %s\n", trapoid) == -1)
407
		goto out;
408
409
	for (; iter != NULL; iter = iter->be_next) {
410
		if (ber_scanf_elements(iter, "{oe}", &oid, &elm) == -1)
411
			goto out;
412
		if ((value = smi_print_element(elm)) == NULL)
413
			goto out;
414
		smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0);
415
		n = dprintf(s[0], "%s %s\n", oidbuf, value);
416
		free(value);
417
		if (n == -1)
418
			goto out;
419
	}
420
 out:
421
	close(s[0]);
422
	waitpid(child, &status, 0);
423
424
	if (WIFSIGNALED(status)) {
425
		log_warnx("child %i exited due to receipt of signal %i",
426
		    child, WTERMSIG(status));
427
	} else if (WEXITSTATUS(status) != 0) {
428
		log_warnx("child %i exited with status %i",
429
		    child, WEXITSTATUS(status));
430
	} else {
431
		log_debug("child %i finished", child);
432
	}
433
		close(s[1]);
434
435
	return;
436
}
437
438
char *
439
traphandler_hostname(struct sockaddr *sa, int numeric)
440
{
441
	static char	 buf[NI_MAXHOST];
442
	int		 flag = 0;
443
444
	if (numeric)
445
		flag = NI_NUMERICHOST;
446
447
	bzero(buf, sizeof(buf));
448
	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, flag) != 0)
449
		return ("Unknown");
450
451
	return (buf);
452
}
453
454
struct trapcmd *
455
trapcmd_lookup(struct ber_oid *oid)
456
{
457
	struct trapcmd	key, *res;
458
459
	bzero(&key, sizeof(key));
460
	key.cmd_oid = oid;
461
462
	if ((res = RB_FIND(trapcmd_tree, &trapcmd_tree, &key)) == NULL)
463
		res = key.cmd_maybe;
464
	return (res);
465
}
466
467
int
468
trapcmd_cmp(struct trapcmd *cmd1, struct trapcmd *cmd2)
469
{
470
	int ret;
471
472
	ret = ber_oid_cmp(cmd2->cmd_oid, cmd1->cmd_oid);
473
	switch (ret) {
474
	case 2:
475
		/* cmd1 is a child of cmd2 */
476
		cmd1->cmd_maybe = cmd2;
477
		return (1);
478
	default:
479
		return (ret);
480
	}
481
	/* NOTREACHED */
482
}
483
484
int
485
trapcmd_add(struct trapcmd *cmd)
486
{
487
8
	return (RB_INSERT(trapcmd_tree, &trapcmd_tree, cmd) != NULL);
488
}
489
490
void
491
trapcmd_free(struct trapcmd *cmd)
492
{
493
	RB_REMOVE(trapcmd_tree, &trapcmd_tree, cmd);
494
	free(cmd->cmd_argv);
495
	free(cmd->cmd_oid);
496
	free(cmd);
497
}