GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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, >ype, &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 |
} |
Generated by: GCOVR (Version 3.3) |