1 /* $OpenBSD: traphandler.c,v 1.1 2014/04/25 06:57:11 blambert Exp $ */ 2 /* 3 * Copyright (c) 2014 Bret Stephen Lambert <blambert@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/queue.h> 19 #include <sys/param.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 <unistd.h> 39 #include <pwd.h> 40 41 #include "ber.h" 42 #include "snmpd.h" 43 #include "mib.h" 44 45 int trapsock; 46 struct event trapev; 47 char trap_path[PATH_MAX]; 48 49 void traphandler_init(struct privsep *, struct privsep_proc *, void *arg); 50 int traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *); 51 int traphandler_bind(struct address *); 52 void traphandler_recvmsg(int, short, void *); 53 int traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *); 54 int traphandler_fork_handler(struct privsep_proc *, struct imsg *); 55 int traphandler_parse(char *, size_t, struct ber_element **, 56 struct ber_element **, u_int *, struct ber_oid *); 57 void traphandler_v1translate(struct ber_oid *, u_int, u_int); 58 59 int trapcmd_cmp(struct trapcmd *, struct trapcmd *); 60 void trapcmd_exec(struct trapcmd *, struct sockaddr *, 61 struct ber_element *, char *, u_int); 62 63 char *traphandler_hostname(struct sockaddr *, int); 64 65 RB_PROTOTYPE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp) 66 RB_GENERATE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp) 67 68 struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree); 69 70 static struct privsep_proc procs[] = { 71 { "parent", PROC_PARENT, traphandler_dispatch_parent } 72 }; 73 74 pid_t 75 traphandler(struct privsep *ps, struct privsep_proc *p) 76 { 77 struct snmpd *env = ps->ps_env; 78 79 if (env->sc_traphandler && 80 (trapsock = traphandler_bind(&env->sc_address)) == -1) 81 fatal("could not create trap listener socket"); 82 83 return (proc_run(ps, p, procs, nitems(procs), traphandler_init, 84 NULL)); 85 } 86 87 void 88 traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg) 89 { 90 struct snmpd *env = ps->ps_env; 91 92 if (!env->sc_traphandler) 93 return; 94 95 /* listen for SNMP trap messages */ 96 event_set(&trapev, trapsock, EV_READ|EV_PERSIST, traphandler_recvmsg, 97 ps); 98 event_add(&trapev, NULL); 99 } 100 101 int 102 traphandler_bind(struct address *addr) 103 { 104 int s; 105 106 if ((s = snmpd_socket_af(&addr->ss, htons(SNMPD_TRAPPORT))) == -1) 107 return (-1); 108 109 if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) 110 goto bad; 111 112 if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1) 113 goto bad; 114 115 return (s); 116 bad: 117 close (s); 118 return (-1); 119 } 120 121 void 122 traphandler_shutdown(void) 123 { 124 event_del(&trapev); 125 close(trapsock); 126 } 127 128 int 129 traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 130 { 131 switch (imsg->hdr.type) { 132 default: 133 break; 134 } 135 136 return (-1); 137 } 138 139 int 140 snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg) 141 { 142 switch (imsg->hdr.type) { 143 case IMSG_ALERT: 144 return (traphandler_priv_recvmsg(p, imsg)); 145 default: 146 break; 147 } 148 149 return (-1); 150 } 151 152 void 153 traphandler_recvmsg(int fd, short events, void *arg) 154 { 155 struct privsep *ps = arg; 156 char buf[8196]; 157 struct iovec iov[2]; 158 struct sockaddr_storage ss; 159 socklen_t slen; 160 ssize_t n; 161 struct ber_element *req, *iter; 162 struct ber_oid trapoid; 163 u_int uptime; 164 165 slen = sizeof(ss); 166 if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss, 167 &slen)) == -1) 168 return; 169 170 if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1) 171 goto done; 172 173 iov[0].iov_base = &ss; 174 iov[0].iov_len = ss.ss_len; 175 iov[1].iov_base = buf; 176 iov[1].iov_len = n; 177 178 /* Forward it to the parent process */ 179 if (proc_composev_imsg(ps, PROC_PARENT, -1, IMSG_ALERT, 180 -1, iov, 2) == -1) 181 goto done; 182 183 done: 184 if (req != NULL) 185 ber_free_elements(req); 186 return; 187 } 188 189 /* 190 * Validate received message 191 */ 192 int 193 traphandler_parse(char *buf, size_t n, struct ber_element **req, 194 struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid) 195 { 196 struct ber ber; 197 struct ber_element *elm; 198 u_int vers, gtype, etype; 199 200 bzero(&ber, sizeof(ber)); 201 ber.fd = -1; 202 ber_set_application(&ber, smi_application); 203 ber_set_readbuf(&ber, buf, n); 204 205 if ((*req = ber_read_elements(&ber, NULL)) == NULL) 206 goto done; 207 208 if (ber_scanf_elements(*req, "{dSe", &vers, &elm) == -1) 209 goto done; 210 211 switch (vers) { 212 case SNMP_V1: 213 if (ber_scanf_elements(elm, "{oSddd", 214 trapoid, >ype, &etype, uptime) == -1) 215 goto done; 216 traphandler_v1translate(trapoid, gtype, etype); 217 break; 218 219 case SNMP_V2: 220 if (ber_scanf_elements(elm, "{SSSS{e}}", &elm) == -1 || 221 ber_scanf_elements(elm, "{SdS}{So}e", 222 uptime, trapoid, vbinds) == -1) 223 goto done; 224 break; 225 226 default: 227 log_warnx("unsupported SNMP trap version '%d'", vers); 228 goto done; 229 } 230 231 ber_free(&ber); 232 return (0); 233 234 done: 235 ber_free(&ber); 236 if (*req) 237 ber_free_elements(*req); 238 *req = NULL; 239 return (-1); 240 } 241 242 void 243 traphandler_v1translate(struct ber_oid *oid, u_int gtype, u_int etype) 244 { 245 /* append 'specific trap' number to 'enterprise specific' traps */ 246 if (gtype >= 6) { 247 oid->bo_id[oid->bo_n] = 0; 248 oid->bo_id[oid->bo_n + 1] = etype; 249 oid->bo_n += 2; 250 } 251 } 252 253 int 254 traphandler_priv_recvmsg(struct privsep_proc *p, struct imsg *imsg) 255 { 256 ssize_t n; 257 pid_t pid; 258 259 if ((n = IMSG_DATA_SIZE(imsg)) <= 0) 260 return (-1); /* XXX */ 261 262 switch ((pid = fork())) { 263 case 0: 264 traphandler_fork_handler(p, imsg); 265 /* NOTREACHED */ 266 case -1: 267 log_warn("%s: couldn't fork traphandler", __func__); 268 return (0); 269 default: 270 log_debug("forked process %i to handle trap", pid); 271 return (0); 272 } 273 /* NOTREACHED */ 274 } 275 276 int 277 traphandler_fork_handler(struct privsep_proc *p, struct imsg *imsg) 278 { 279 char oidbuf[SNMP_MAX_OID_STRLEN]; 280 struct sockaddr *sa; 281 char *buf; 282 ssize_t n; 283 struct ber_element *req, *iter; 284 struct trapcmd *cmd; 285 struct ber_oid trapoid; 286 u_int uptime; 287 struct passwd *pw; 288 extern int debug; 289 290 pw = p->p_ps->ps_pw; 291 292 if (setgroups(1, &pw->pw_gid) || 293 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 294 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 295 fatal("traphandler_fork_handler: cannot drop privileges"); 296 297 closefrom(STDERR_FILENO + 1); 298 log_init(debug); 299 300 n = IMSG_DATA_SIZE(imsg); 301 302 sa = imsg->data; 303 n -= sa->sa_len; 304 buf = (char *)imsg->data + sa->sa_len; 305 306 if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1) 307 fatalx("couldn't parse SNMP trap message"); 308 309 smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0); 310 if ((cmd = trapcmd_lookup(&trapoid)) != NULL) 311 trapcmd_exec(cmd, sa, iter, oidbuf, uptime); 312 313 if (req != NULL) 314 ber_free_elements(req); 315 316 exit(0); 317 } 318 319 void 320 trapcmd_exec(struct trapcmd *cmd, struct sockaddr *sa, 321 struct ber_element *iter, char *trapoid, u_int uptime) 322 { 323 char oidbuf[SNMP_MAX_OID_STRLEN]; 324 struct ber_oid oid; 325 struct ber_element *elm; 326 int n, s[2], status = 0; 327 char *value, *host; 328 pid_t child = -1; 329 330 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) { 331 log_warn("could not create pipe for OID '%s'", 332 smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); 333 return; 334 } 335 336 switch (child = fork()) { 337 case 0: 338 dup2(s[1], STDIN_FILENO); 339 340 close(s[0]); 341 close(s[1]); 342 343 closefrom(STDERR_FILENO + 1); 344 345 /* path to command is in argv[0], args follow */ 346 execve(cmd->cmd_argv[0], cmd->cmd_argv, NULL); 347 348 /* this shouldn't happen */ 349 log_warn("could not exec trap command for OID '%s'", 350 smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); 351 _exit(1); 352 /* NOTREACHED */ 353 354 case -1: 355 log_warn("could not fork trap command for OID '%s'", 356 smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); 357 close(s[0]); 358 close(s[1]); 359 return; 360 } 361 362 close(s[1]); 363 364 host = traphandler_hostname(sa, 0); 365 if (dprintf(s[0], "%s\n", host) == -1) 366 goto out; 367 368 host = traphandler_hostname(sa, 1); 369 if (dprintf(s[0], "%s\n", host) == -1) 370 goto out; 371 372 if (dprintf(s[0], 373 "iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0 %u\n", 374 uptime) == -1) 375 goto out; 376 377 if (dprintf(s[0], 378 "iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects." 379 "snmpTrap.snmpTrapOID.0 %s\n", trapoid) == -1) 380 goto out; 381 382 for (; iter != NULL; iter = iter->be_next) { 383 if (ber_scanf_elements(iter, "{oe}", &oid, &elm) == -1) 384 goto out; 385 if ((value = smi_print_element(elm)) == NULL) 386 goto out; 387 smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0); 388 n = dprintf(s[0], "%s %s\n", oidbuf, value); 389 free(value); 390 if (n == -1) 391 goto out; 392 } 393 out: 394 close(s[0]); 395 waitpid(child, &status, 0); 396 397 if (WIFSIGNALED(status)) { 398 log_warnx("child %i exited due to receipt of signal %i", 399 child, WTERMSIG(status)); 400 } else if (WEXITSTATUS(status) != 0) { 401 log_warnx("child %i exited with status %i", 402 child, WEXITSTATUS(status)); 403 } else { 404 log_debug("child %i finished", child); 405 } 406 close(s[1]); 407 408 return; 409 } 410 411 char * 412 traphandler_hostname(struct sockaddr *sa, int numeric) 413 { 414 static char buf[NI_MAXHOST]; 415 int flag = 0; 416 417 if (numeric) 418 flag = NI_NUMERICHOST; 419 420 bzero(buf, sizeof(buf)); 421 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, flag) != 0) 422 return ("Unknown"); 423 424 return (buf); 425 } 426 427 struct trapcmd * 428 trapcmd_lookup(struct ber_oid *oid) 429 { 430 struct trapcmd key, *res; 431 432 bzero(&key, sizeof(key)); 433 key.cmd_oid = oid; 434 435 if ((res = RB_FIND(trapcmd_tree, &trapcmd_tree, &key)) == NULL) 436 res = key.cmd_maybe; 437 return (res); 438 } 439 440 int 441 trapcmd_cmp(struct trapcmd *cmd1, struct trapcmd *cmd2) 442 { 443 int ret; 444 445 ret = ber_oid_cmp(cmd2->cmd_oid, cmd1->cmd_oid); 446 switch (ret) { 447 case 2: 448 /* cmd1 is a child of cmd2 */ 449 cmd1->cmd_maybe = cmd2; 450 return (1); 451 default: 452 return (ret); 453 } 454 /* NOTREACHED */ 455 } 456 457 int 458 trapcmd_add(struct trapcmd *cmd) 459 { 460 return (RB_INSERT(trapcmd_tree, &trapcmd_tree, cmd) != NULL); 461 } 462 463 void 464 trapcmd_free(struct trapcmd *cmd) 465 { 466 RB_REMOVE(trapcmd_tree, &trapcmd_tree, cmd); 467 free(cmd->cmd_argv); 468 free(cmd->cmd_oid); 469 free(cmd); 470 } 471