Right now snmpd's traphandler_parse and traphandler_v1translate are just
weird to me and do very little ASN1 checking. Since I want to remove the
traphandler process anyway and the current code structue is in the way,
this diff is a logical first step towards that goal.

The new traphandler_v1translate should now be properly build around
RFC3584 section 3.1 and I've choosen to disable the proxy addition,
since we don't forward it to another host, but it does work for me and
can be enabled for this scenario if people feel strong about it.
Another major change people should be aware of is when running snmpd
with -N the sysUpTime and snmpTrapOID parameters will now be numerical
(where these were hardcode printed before).
A minor thing people should be aware of is that the new code is a lot
more strict on its ASN1 validation, so if you use it with crappy
SNMP entities that send broken packets, you're out of luck.

Also, traphandler_parse is now called only once, which should help in my
next step in removing the traphandler process altogether.

OK?

martijn@

Index: traphandler.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/traphandler.c,v
retrieving revision 1.18
diff -u -p -r1.18 traphandler.c
--- traphandler.c       6 Sep 2020 15:51:28 -0000       1.18
+++ traphandler.c       3 Jan 2021 14:20:17 -0000
@@ -28,6 +28,7 @@
 #include <arpa/inet.h>
 
 #include <ber.h>
+#include <errno.h>
 #include <event.h>
 #include <fcntl.h>
 #include <imsg.h>
@@ -50,13 +51,12 @@ int  traphandler_bind(struct address *);
 void    traphandler_recvmsg(int, short, void *);
 int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 int     traphandler_fork_handler(struct privsep_proc *, struct imsg *);
-int     traphandler_parse(char *, size_t, struct ber_element **,
-           struct ber_element **, u_int *, struct ber_oid *);
-void    traphandler_v1translate(struct ber_oid *, u_int, u_int);
-
+int     traphandler_parse(struct ber_element *, char *, struct sockaddr *);
+struct ber_element *
+        traphandler_v1translate(struct ber_element *, char *, int);
 int     trapcmd_cmp(struct trapcmd *, struct trapcmd *);
 void    trapcmd_exec(struct trapcmd *, struct sockaddr *,
-           struct ber_element *, char *, u_int);
+           struct ber_element *);
 
 char   *traphandler_hostname(struct sockaddr *, int);
 
@@ -102,7 +102,7 @@ traphandler_init(struct privsep *ps, str
        /* listen for SNMP trap messages */
        TAILQ_FOREACH(h, &env->sc_addresses, entry) {
                event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                   traphandler_recvmsg, ps);
+                   traphandler_recvmsg, NULL);
                event_add(&h->ev, NULL);
        }
 }
@@ -180,104 +180,216 @@ snmpd_dispatch_traphandler(int fd, struc
 void
 traphandler_recvmsg(int fd, short events, void *arg)
 {
-       struct privsep          *ps = arg;
+       struct ber               ber = {0};
+       struct ber_element      *msg = NULL, *pdu;
        char                     buf[8196];
-       struct iovec             iov[2];
        struct sockaddr_storage  ss;
        socklen_t                slen;
        ssize_t                  n;
-       struct ber_element      *req, *iter;
-       struct ber_oid           trapoid;
-       u_int                    uptime;
+       int                      vers;
+       char                    *community;
 
        slen = sizeof(ss);
        if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
            &slen)) == -1)
                return;
 
-       if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
-               goto done;
+       ober_set_application(&ber, smi_application);
+       ober_set_readbuf(&ber, buf, n);
 
-       iov[0].iov_base = &ss;
-       iov[0].iov_len = ss.ss_len;
-       iov[1].iov_base = buf;
-       iov[1].iov_len = n;
+       if ((msg = ober_read_elements(&ber, NULL)) == NULL)
+               goto parsefail;
 
-       /* Forward it to the parent process */
-       if (proc_composev(ps, PROC_PARENT, IMSG_ALERT, iov, 2) == -1)
-               goto done;
+       if (ober_scanf_elements(msg, "{dse", &vers, &community, &pdu) == -1)
+               goto parsefail;
 
- done:
-       if (req != NULL)
-               ober_free_elements(req);
-       return;
+       switch (vers) {
+       case SNMP_V1:
+               if (pdu->be_type != SNMP_C_TRAP)
+                       goto parsefail;
+               break;
+       case SNMP_V2:
+               if (pdu->be_type != SNMP_C_TRAPV2)
+                       goto parsefail;
+               break;
+       default:
+               goto parsefail;
+       }
+
+       (void)traphandler_parse(pdu, community, (struct sockaddr *)&ss);
+
+parsefail:
+       ober_free(&ber);
+       if (msg != NULL)
+               ober_free_elements(msg);
 }
 
 /*
  * Validate received message
  */
 int
-traphandler_parse(char *buf, size_t n, struct ber_element **req,
-    struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid)
+traphandler_parse(struct ber_element *pdu, char *community, struct sockaddr 
*sa)
 {
-       struct ber               ber;
-       struct ber_element      *elm;
-       u_int                    vers, gtype, etype;
+       struct privsep          *ps = &snmpd_env->sc_ps;
+       struct ber               ber = {0};
+       struct ber_element      *vblist = NULL, *elm, *elm2;
+       struct ber_oid           o1, o2, snmpTrapOIDOID;
+       struct ber_oid           snmpTrapOID, sysUpTimeOID;
+       int                      sysUpTime;
+       struct iovec             iov[2];
+       void                    *buf;
+       ssize_t                  buflen;
+       int                      ret = -1;
+
+       switch (pdu->be_type) {
+       case SNMP_C_TRAP:
+               vblist = traphandler_v1translate(pdu, community, 0);
+               if (vblist == NULL)
+                       goto done;
+               break;
+       case SNMP_C_TRAPV2:
+               if (ober_scanf_elements(pdu, "{SSe}", &elm) == -1)
+                       goto done;
+               if (elm->be_type != BER_TYPE_INTEGER)
+                       goto done;
+               vblist = ober_unlink_elements(elm);
+               break;
+       default:
+               log_warnx("unsupported SNMP trap version '%d'", pdu->be_type);
+               goto done;
+       }
+
+       (void)ober_string2oid("1.3.6.1.2.1.1.3.0", &sysUpTimeOID);
+       (void)ober_string2oid("1.3.6.1.6.3.1.1.4.1.0", &snmpTrapOIDOID);
+       if (ober_scanf_elements(vblist, "{{od}{oo}", &o1, &sysUpTime, &o2,
+           &snmpTrapOID) == -1 ||
+           ober_oid_cmp(&o1, &sysUpTimeOID) != 0 ||
+           ober_oid_cmp(&o2, &snmpTrapOIDOID) != 0) {
+               goto done;
+       }
+       for (elm = vblist->be_sub->be_next->be_next; elm != NULL;
+           elm = elm->be_next) {
+               if (ober_scanf_elements(elm, "{oe}", &o1, &elm2) == -1 ||
+                   elm2->be_next != NULL)
+                       goto done;
+       }
 
-       bzero(&ber, sizeof(ber));
        ober_set_application(&ber, smi_application);
-       ober_set_readbuf(&ber, buf, n);
 
-       if ((*req = ober_read_elements(&ber, NULL)) == NULL)
+       if ((buflen = ober_write_elements(&ber, vblist)) == -1 ||
+           ober_get_writebuf(&ber, &buf) == -1)
                goto done;
 
-       if (ober_scanf_elements(*req, "{dSe", &vers, &elm) == -1)
+       iov[0].iov_base = sa;
+       iov[0].iov_len = sa->sa_len;
+       iov[1].iov_base = buf;
+       iov[1].iov_len = buflen;
+
+       /* Forward it to the parent process */
+       if (proc_composev(ps, PROC_PARENT, IMSG_ALERT, iov, 2) == -1)
                goto done;
 
-       switch (vers) {
-       case SNMP_V1:
-               if (ober_scanf_elements(elm, "{oSddde",
-                   trapoid, &gtype, &etype, uptime, &elm) == -1)
-                       goto done;
-               traphandler_v1translate(trapoid, gtype, etype);
-               if (elm->be_type != BER_TYPE_SEQUENCE)
-                       goto done;
-               *vbinds = elm->be_sub;
+       ret = 0;
+done:
+       ober_free(&ber);
+       if (vblist != NULL)
+               ober_free_elements(vblist);
+       return ret;
+}
+
+struct ber_element *
+traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
+{
+       struct ber_oid trapoid, enterprise, oid, snmpTrapAddressOid;
+       struct ber_oid snmpTrapCommunityOid, snmpTrapEnterpriseOid;
+       struct ber_element *elm, *last, *vblist, *vb0;
+       void *agent_addr;
+       size_t agent_addrlen;
+       int generic_trap, specific_trap, time_stamp;
+       int hasaddress = 0, hascommunity = 0, hasenterprise = 0;
+
+       if (ober_scanf_elements(pdu, "{oxddde", &enterprise, &agent_addr,
+           &agent_addrlen, &generic_trap, &specific_trap, &time_stamp,
+           &vblist) == -1 ||
+           agent_addrlen != 4 ||
+           vblist->be_type != BER_TYPE_SEQUENCE) {
+               errno = EINVAL;
+               return NULL;
+       }
+       switch (generic_trap) {
+       case 0:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.1", &trapoid);
                break;
-
-       case SNMP_V2:
-               if (ober_scanf_elements(elm, "{SSS{e}}", &elm) == -1 ||
-                   ober_scanf_elements(elm, "{Sd}{So}",
-                   uptime, trapoid) == -1)
-                       goto done;
-               *vbinds = elm->be_next->be_next;
+       case 1:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.2", &trapoid);
+               break;
+       case 2:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.3", &trapoid);
+               break;
+       case 3:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.4", &trapoid);
+               break;
+       case 4:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.5", &trapoid);
+               break;
+       case 5:
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.5.6", &trapoid);
+               break;
+       case 6:
+               trapoid = enterprise;
+               trapoid.bo_id[trapoid.bo_n++] = 0;
+               trapoid.bo_id[trapoid.bo_n++] = specific_trap;
                break;
-
        default:
-               log_warnx("unsupported SNMP trap version '%d'", vers);
-               goto done;
+               errno = EINVAL;
+               return NULL;
        }
 
-       ober_free(&ber);
-       return (0);
-
- done:
-       ober_free(&ber);
-       if (*req)
-               ober_free_elements(*req);
-       *req = NULL;
-       return (-1);
-}
-
-void
-traphandler_v1translate(struct ber_oid *oid, u_int gtype, u_int etype)
-{
-       /* append 'specific trap' number to 'enterprise specific' traps */
-       if (gtype >= 6) {
-               oid->bo_id[oid->bo_n] = 0;
-               oid->bo_id[oid->bo_n + 1] = etype;
-               oid->bo_n += 2;
+       vb0 = ober_unlink_elements(vblist);
+       if ((vblist = ober_add_sequence(NULL)) == NULL) {
+               ober_free_elements(vb0);
+               return NULL;
+       }
+       if (ober_printf_elements(vblist, "{od}{oO}e", "1.3.6.1.2.1.1.3.0",
+           time_stamp, "1.3.6.1.6.3.1.1.4.1.0", &trapoid, vb0) == NULL) {
+               ober_free_elements(vb0);
+               ober_free_elements(vblist);
+               return NULL;
+       }
+
+       if (proxy) {
+               (void)ober_string2oid("1.3.6.1.6.3.18.1.3.0",
+                   &snmpTrapAddressOid);
+               (void)ober_string2oid("1.3.6.1.6.3.18.1.4.0",
+                   &snmpTrapCommunityOid);
+               (void)ober_string2oid("1.3.6.1.6.3.1.1.4.3.0",
+                   &snmpTrapEnterpriseOid);
+               for (elm = vblist->be_sub; elm != NULL; elm = elm->be_next) {
+                       if (ober_get_oid(elm->be_sub, &oid) == -1) {
+                               ober_free_elements(vblist);
+                               return NULL;
+                       }
+                       if (ober_oid_cmp(&oid, &snmpTrapAddressOid) == 0)
+                               hasaddress = 1;
+                       else if (ober_oid_cmp(&oid, &snmpTrapCommunityOid) == 0)
+                               hascommunity = 1;
+                       else if (ober_oid_cmp(&oid,
+                           &snmpTrapEnterpriseOid) == 0)
+                               hasenterprise = 1;
+                       last = elm;
+               }
+               if (!hasaddress || !hascommunity || !hasenterprise) {
+                       if (ober_printf_elements(last, "{Oxt}{Os}{OO}",
+                           &snmpTrapAddressOid, agent_addr, 4,
+                           BER_CLASS_APPLICATION, SNMP_T_IPADDR,
+                           &snmpTrapCommunityOid, community,
+                           &snmpTrapEnterpriseOid, &enterprise) == NULL) {
+                               ober_free_elements(vblist);
+                               return NULL;
+                       }
+               }
        }
+       return vblist;
 }
 
 int
@@ -308,14 +420,13 @@ traphandler_fork_handler(struct privsep_
 {
        struct privsep          *ps = p->p_ps;
        struct snmpd            *env = ps->ps_env;
-       char                     oidbuf[SNMP_MAX_OID_STRLEN];
+       struct ber               ber = {0};
        struct sockaddr         *sa;
        char                    *buf;
        ssize_t                  n;
-       struct ber_element      *req, *iter;
-       struct trapcmd          *cmd;
+       struct ber_element      *vblist;
        struct ber_oid           trapoid;
-       u_int                    uptime;
+       struct trapcmd          *cmd;
        struct passwd           *pw;
        int                      verbose;
 
@@ -339,22 +450,28 @@ traphandler_fork_handler(struct privsep_
        n -= sa->sa_len;
        buf = (char *)imsg->data + sa->sa_len;
 
-       if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
+       ober_set_application(&ber, smi_application);
+       ober_set_readbuf(&ber, buf, n);
+
+       if ((vblist = ober_read_elements(&ber, NULL)) == NULL)
                fatalx("couldn't parse SNMP trap message");
+       ober_free(&ber);
 
-       smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0);
+       if (ober_get_oid(vblist->be_sub->be_next->be_sub->be_next,
+           &trapoid) == -1)
+               fatalx("couldn't parse SNMP trap message");
+               
        if ((cmd = trapcmd_lookup(&trapoid)) != NULL)
-               trapcmd_exec(cmd, sa, iter, oidbuf, uptime);
+               trapcmd_exec(cmd, sa, vblist->be_sub);
 
-       if (req != NULL)
-               ober_free_elements(req);
+       ober_free_elements(vblist);
 
        exit(0);
 }
 
 void
 trapcmd_exec(struct trapcmd *cmd, struct sockaddr *sa,
-    struct ber_element *iter, char *trapoid, u_int uptime)
+    struct ber_element *vb)
 {
        char                     oidbuf[SNMP_MAX_OID_STRLEN];
        struct ber_oid           oid;
@@ -405,18 +522,8 @@ trapcmd_exec(struct trapcmd *cmd, struct
        if (dprintf(s[0], "%s\n", host) == -1)
                goto out;
 
-       if (dprintf(s[0],
-           "iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0 %u\n",
-           uptime) == -1)
-               goto out;
-
-       if (dprintf(s[0],
-           "iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects."
-           "snmpTrap.snmpTrapOID.0 %s\n", trapoid) == -1)
-               goto out;
-
-       for (; iter != NULL; iter = iter->be_next) {
-               if (ober_scanf_elements(iter, "{oe}", &oid, &elm) == -1)
+       for (; vb != NULL; vb = vb->be_next) {
+               if (ober_scanf_elements(vb, "{oe}", &oid, &elm) == -1)
                        goto out;
                if ((value = smi_print_element(elm)) == NULL)
                        goto out;
@@ -439,7 +546,7 @@ trapcmd_exec(struct trapcmd *cmd, struct
        } else {
                log_debug("child %i finished", child);
        }
-               close(s[1]);
+       close(s[1]);
 
        return;
 }


Reply via email to