Remove the traphandler process, which was nothing more then a sham.
authormartijn <martijn@openbsd.org>
Fri, 22 Jan 2021 06:33:26 +0000 (06:33 +0000)
committermartijn <martijn@openbsd.org>
Fri, 22 Jan 2021 06:33:26 +0000 (06:33 +0000)
It did nothing more then receive a message over UDP, do some basic ber
and ASN.1 parsing and forward the packet to the parent process. snmpe can
do/does the same thing but with a far more thorough ASN.1 validation.
Because we move trap receiving to snmpe we get trap over tcp for free.

However, to make sure that a normal snmp port doesn't automatically start
handling traps a new set of "listen on" flags are introduced: read, write,
and notify. To enable trap handling either let snmpd listen on port 162
without flags, or add the notify flag. Only a flag without port results in
listening on port 162.

To keep current behaviour copy all UDP-based "listen on" lines without port
and add the notify keyword:
listen on 127.0.0.1 port 666
becomes
listen on 127.0.0.1 port 666
listen on 127.0.0.1 notify

This change also enforces snmpd to honor trap community on receiving a
trap, where previously no community was checked before handling a packet.

OK denis@, rob@

usr.sbin/snmpd/parse.y
usr.sbin/snmpd/snmpd.c
usr.sbin/snmpd/snmpd.conf.5
usr.sbin/snmpd/snmpd.h
usr.sbin/snmpd/snmpe.c
usr.sbin/snmpd/traphandler.c

index 144a4ea..cb9d389 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.62 2020/10/30 07:43:48 martijn Exp $      */
+/*     $OpenBSD: parse.y,v 1.63 2021/01/22 06:33:26 martijn Exp $      */
 
 /*
  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -94,11 +94,10 @@ char                *symget(const char *);
 struct snmpd                   *conf = NULL;
 static int                      errors = 0;
 static struct usmuser          *user = NULL;
-static char                    *snmpd_port = SNMPD_PORT;
 
 int             host(const char *, const char *, int,
                    struct sockaddr_storage *, int);
-int             listen_add(struct sockaddr_storage *, int);
+int             listen_add(struct sockaddr_storage *, int, int);
 
 typedef struct {
        union {
@@ -121,7 +120,7 @@ typedef struct {
 %}
 
 %token INCLUDE
-%token  LISTEN ON
+%token  LISTEN ON READ WRITE NOTIFY
 %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
 %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
 %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED
@@ -130,7 +129,7 @@ typedef struct {
 %token  <v.number>     NUMBER
 %type  <v.string>      hostcmn
 %type  <v.string>      srcaddr port
-%type  <v.number>      optwrite yesno seclevel
+%type  <v.number>      optwrite yesno seclevel listenopt listenopts
 %type  <v.data>        objtype cmd
 %type  <v.oid>         oid hostoid trapoid
 %type  <v.auth>        auth
@@ -281,54 +280,74 @@ listenproto       : UDP listen_udp
                | TCP listen_tcp
                | listen_udp
 
-listen_udp     : STRING port                   {
+listenopts     : /* empty */ { $$ = 0; }
+               | listenopts listenopt { $$ |= $2; }
+               ;
+
+listenopt      : READ { $$ = ADDRESS_FLAG_READ; }
+               | WRITE { $$ = ADDRESS_FLAG_WRITE; }
+               | NOTIFY { $$ = ADDRESS_FLAG_NOTIFY; }
+               ;
+
+listen_udp     : STRING port listenopts        {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
 
-                       nhosts = host($1, $2, SOCK_DGRAM, ss, nitems(ss));
+                       if (port == NULL) {
+                               if ($3 == ADDRESS_FLAG_NOTIFY)
+                                       port = SNMPTRAP_PORT;
+                               else
+                                       port = SNMP_PORT;
+                       }
+
+                       nhosts = host($1, port, SOCK_DGRAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
                                log_warn("%s:%s resolves to more than %zu hosts",
-                                   $1, $2, nitems(ss));
+                                   $1, port, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
-                               free($2);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_DGRAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_DGRAM, $3) == -1) {
                                        yyerror("calloc");
                                        YYERROR;
                                }
                        }
                }
 
-listen_tcp     : STRING port                   {
+listen_tcp     : STRING port listenopts        {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
 
-                       nhosts = host($1, $2, SOCK_STREAM, ss, nitems(ss));
+                       if (port == NULL) {
+                               if ($3 == ADDRESS_FLAG_NOTIFY)
+                                       port = SNMPTRAP_PORT;
+                               else
+                                       port = SNMP_PORT;
+                       }
+                       nhosts = host($1, port, SOCK_STREAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
                                log_warn("%s:%s resolves to more than %zu hosts",
-                                   $1, $2, nitems(ss));
+                                   $1, port, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
-                               free($2);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_STREAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_STREAM, $3) == -1) {
                                        yyerror("calloc");
                                        YYERROR;
                                }
@@ -336,7 +355,7 @@ listen_tcp  : STRING port                   {
                }
 
 port           : /* empty */                   {
-                       $$ = snmpd_port;
+                       $$ = NULL;
                }
                | PORT STRING                   {
                        $$ = $2;
@@ -497,7 +516,7 @@ hostdef             : STRING hostoid hostcmn srcaddr        {
                                YYERROR;
                        }
 
-                       if (host($1, SNMPD_TRAPPORT, SOCK_DGRAM, &ss, 1) <= 0) {
+                       if (host($1, SNMPTRAP_PORT, SOCK_DGRAM, &ss, 1) <= 0) {
                                yyerror("invalid host: %s", $1);
                                free($1);
                                free($2);
@@ -704,9 +723,11 @@ lookup(char *s)
                { "location",                   LOCATION },
                { "name",                       NAME },
                { "none",                       NONE },
+               { "notify",                     NOTIFY },
                { "oid",                        OBJECTID },
                { "on",                         ON },
                { "port",                       PORT },
+               { "read",                       READ },
                { "read-only",                  READONLY },
                { "read-write",                 READWRITE },
                { "receiver",                   RECEIVER },
@@ -718,7 +739,8 @@ lookup(char *s)
                { "tcp",                        TCP },
                { "trap",                       TRAP },
                { "udp",                        UDP },
-               { "user",                       USER }
+               { "user",                       USER },
+               { "write",                      WRITE }
        };
        const struct keywords   *p;
 
@@ -1110,27 +1132,29 @@ parse_config(const char *filename, u_int flags)
 
        /* Setup default listen addresses */
        if (TAILQ_EMPTY(&conf->sc_addresses)) {
-               if (host("0.0.0.0", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
+               if (host("0.0.0.0", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of 0.0.0.0");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
-               if (host("::", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
+               if (host("::", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of ::");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
        }
-       if (conf->sc_traphandler) {
-               found = 0;
-               TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
-                       if (h->type == SOCK_DGRAM)
-                               found = 1;
-               }
-               if (!found) {
-                       log_warnx("trap handler needs at least one "
-                           "udp listen address");
-                       free(conf);
-                       return (NULL);
-               }
+       found = 0;
+       TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
+               if (h->flags & ADDRESS_FLAG_NOTIFY)
+                       found = 1;
+       }
+       if (conf->sc_traphandler && !found) {
+               log_warnx("trap handler needs at least one notify listener");
+               free(conf);
+               return (NULL);
+       }
+       if (!conf->sc_traphandler && found) {
+               log_warnx("notify listener needs at least one trap handler");
+               free(conf);
+               return (NULL);
        }
 
        /* Free macros and check which have not been used. */
@@ -1258,7 +1282,7 @@ host(const char *s, const char *port, int type, struct sockaddr_storage *ss,
 }
 
 int
-listen_add(struct sockaddr_storage *ss, int type)
+listen_add(struct sockaddr_storage *ss, int type, int flags)
 {
        struct sockaddr_in *sin;
        struct sockaddr_in6 *sin6;
@@ -1275,6 +1299,12 @@ listen_add(struct sockaddr_storage *ss, int type)
                h->port = ntohs(sin6->sin6_port);
        }
        h->type = type;
+       if ((h->flags = flags) == 0) {
+               if (h->port == 162)
+                       h->flags = ADDRESS_FLAG_NOTIFY;
+               else
+                       h->flags = ADDRESS_FLAG_READ | ADDRESS_FLAG_WRITE;
+       }
        TAILQ_INSERT_TAIL(&(conf->sc_addresses), h, entry);
 
        return 0;
index b720570..8800537 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: snmpd.c,v 1.42 2020/09/06 15:51:28 martijn Exp $      */
+/*     $OpenBSD: snmpd.c,v 1.43 2021/01/22 06:33:26 martijn Exp $      */
 
 /*
  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -52,8 +52,6 @@ struct snmpd  *snmpd_env;
 
 static struct privsep_proc procs[] = {
        { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
-       { "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler,
-           traphandler_shutdown }
 };
 
 void
@@ -300,6 +298,8 @@ int
 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
        switch (imsg->hdr.type) {
+       case IMSG_TRAP_EXEC:
+               return (traphandler_priv_recvmsg(p, imsg));
        case IMSG_CTL_RELOAD:
                /* XXX notyet */
        default:
index ce1f3c3..1cb865c 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: snmpd.conf.5,v 1.45 2020/10/24 14:52:11 jmc Exp $
+.\" $OpenBSD: snmpd.conf.5,v 1.46 2021/01/22 06:33:26 martijn Exp $
 .\"
 .\" Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: October 24 2020 $
+.Dd $Mdocdate: January 22 2021 $
 .Dt SNMPD.CONF 5
 .Os
 .Sh NAME
@@ -95,13 +95,37 @@ Routing table information will not be available, but CPU use will be
 reduced during bulk updates.
 The default is
 .Ic no .
-.It Ic listen on Oo Ic tcp | udp Oc Ar address Op Ic port Ar port
+.It Ic listen on Oo Ic tcp | udp Oc Ar address Oo Ic port Ar port Oc Op Ic read | Ic write | Ic notify
 Specify the local address
 .Xr snmpd 8
 should listen on for incoming SNMP messages.
+The
+.Ic read
+flag specifies if the listen statement accepts get, getnext and bulkget
+requests.
+The
+.Ic write
+flag specifies if the listen statement accepts set requests
+and
+.Ic notify
+flags specifes if the listen statements accepts trapv1 and trapv2 requests.
 Multiple
 .Ic listen on
-statements are supported, the default is UDP.
+statements are supported.
+The default protocol is
+.Ic udp.
+The default
+.Ar port
+is 161, unless the only listen flags contains
+.Ic notify
+which sets it to 162.
+If no flags are specified it defaults to
+.Dq Ic read Ic write ,
+or
+.Ic notify
+when
+.Ar port
+is 162.
 .It Ic read-only community Ar string
 Specify the name of the read-only community.
 The default value is
index 7d7212e..b08a517 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: snmpd.h,v 1.90 2020/09/06 15:51:28 martijn Exp $      */
+/*     $OpenBSD: snmpd.h,v 1.91 2021/01/22 06:33:26 martijn Exp $      */
 
 /*
  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -48,8 +48,8 @@
 #define CONF_FILE              "/etc/snmpd.conf"
 #define SNMPD_SOCKET           "/var/run/snmpd.sock"
 #define SNMPD_USER             "_snmpd"
-#define SNMPD_PORT             "161"
-#define SNMPD_TRAPPORT         "162"
+#define SNMP_PORT              "161"
+#define SNMPTRAP_PORT          "162"
 
 #define SNMPD_MAXSTRLEN                484
 #define SNMPD_MAXCOMMUNITYLEN  SNMPD_MAXSTRLEN
@@ -88,7 +88,7 @@ enum imsg_type {
        IMSG_CTL_VERBOSE,
        IMSG_CTL_RELOAD,
        IMSG_CTL_PROCFD,
-       IMSG_ALERT
+       IMSG_TRAP_EXEC
 };
 
 struct imsgev {
@@ -110,7 +110,6 @@ struct imsgev {
 enum privsep_procid {
        PROC_PARENT,    /* Parent process and application interface */
        PROC_SNMPE,     /* SNMP engine */
-       PROC_TRAP,      /* SNMP trap receiver */
        PROC_MAX
 };
 
@@ -384,8 +383,11 @@ struct snmp_message {
        struct sockaddr_storage  sm_ss;
        socklen_t                sm_slen;
        int                      sm_sock_tcp;
+       int                      sm_aflags;
+       int                      sm_type;
        struct event             sm_sockev;
        char                     sm_host[HOST_NAME_MAX+1];
+       in_port_t                sm_port;
 
        struct sockaddr_storage  sm_local_ss;
        socklen_t                sm_local_slen;
@@ -482,6 +484,7 @@ struct address {
        struct sockaddr_storage  ss;
        in_port_t                port;
        int                      type;
+       int                      flags;
        int                      fd;
        struct event             ev;
        struct event             evt;
@@ -490,6 +493,10 @@ struct address {
 };
 TAILQ_HEAD(addresslist, address);
 
+#define ADDRESS_FLAG_READ 0x1
+#define ADDRESS_FLAG_WRITE 0x2
+#define ADDRESS_FLAG_NOTIFY 0x4
+
 struct trap_address {
        struct sockaddr_storage  ss;
        struct sockaddr_storage  ss_local;
@@ -761,9 +768,8 @@ struct imsgev *
 int     proc_flush_imsg(struct privsep *, enum privsep_procid, int);
 
 /* traphandler.c */
-void    traphandler(struct privsep *, struct privsep_proc *);
-void    traphandler_shutdown(void);
-int     snmpd_dispatch_traphandler(int, struct privsep_proc *, struct imsg *);
+int     traphandler_parse(struct snmp_message *);
+int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 void    trapcmd_free(struct trapcmd *);
 int     trapcmd_add(struct trapcmd *);
 struct trapcmd *
index dd5ebd3..4aea2d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: snmpe.c,v 1.67 2020/09/06 17:29:35 martijn Exp $      */
+/*     $OpenBSD: snmpe.c,v 1.68 2021/01/22 06:33:27 martijn Exp $      */
 
 /*
  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -108,7 +108,7 @@ snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
                        evtimer_set(&h->evt, snmpe_acceptcb, h);
                } else {
                        event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                           snmpe_recvmsg, env);
+                           snmpe_recvmsg, h);
                }
                event_add(&h->ev, NULL);
        }
@@ -271,6 +271,7 @@ snmpe_parse(struct snmp_message *msg)
        if (class != BER_CLASS_CONTEXT)
                goto parsefail;
 
+       msg->sm_type = type;
        switch (type) {
        case SNMP_C_GETBULKREQ:
                if (msg->sm_version == SNMP_V1) {
@@ -288,6 +289,10 @@ snmpe_parse(struct snmp_message *msg)
        case SNMP_C_GETNEXTREQ:
                if (type == SNMP_C_GETNEXTREQ)
                        stats->snmp_ingetnexts++;
+               if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) {
+                       msg->sm_errstr = "read requests disabled";
+                       goto fail;
+               }
                if (msg->sm_version != SNMP_V3 &&
                    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
                    (env->sc_readonly ||
@@ -301,6 +306,10 @@ snmpe_parse(struct snmp_message *msg)
 
        case SNMP_C_SETREQ:
                stats->snmp_insetrequests++;
+               if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) {
+                       msg->sm_errstr = "write requests disabled";
+                       goto fail;
+               }
                if (msg->sm_version != SNMP_V3 &&
                    (env->sc_readonly ||
                    strcmp(env->sc_rwcommunity, msg->sm_community) != 0)) {
@@ -320,16 +329,40 @@ snmpe_parse(struct snmp_message *msg)
                goto parsefail;
 
        case SNMP_C_TRAP:
+               if (msg->sm_version != SNMP_V1) {
+                       msg->sm_errstr = "trapv1 request on !SNMPv1 message";
+                       goto parsefail;
+               }
        case SNMP_C_TRAPV2:
-               if (msg->sm_version != SNMP_V3 &&
-                   strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
+               if (type == SNMP_C_TRAPV2 &&
+                   !(msg->sm_version == SNMP_V2 ||
+                   msg->sm_version != SNMP_V3)) {
+                       msg->sm_errstr = "trapv2 request on !SNMPv2C or "
+                           "!SNMPv3 message";
+                       goto parsefail;
+               }
+               if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) {
+                       msg->sm_errstr = "notify requests disabled";
+                       goto fail;
+               }
+               if (msg->sm_version == SNMP_V3) {
+                       msg->sm_errstr = "SNMPv3 doesn't support traps yet";
+                       goto fail;
+               }
+               if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
                        stats->snmp_inbadcommunitynames++;
                        msg->sm_errstr = "wrong trap community";
                        goto fail;
                }
                stats->snmp_intraps++;
-               msg->sm_errstr = "received trap";
-               goto fail;
+               /*
+                * This should probably go into parsevarbinds, but that's for a
+                * next refactor
+                */
+               if (traphandler_parse(msg) == -1)
+                       goto fail;
+               /* Shortcircuit */
+               return 0;
 
        default:
                msg->sm_errstr = "invalid context";
@@ -356,15 +389,15 @@ snmpe_parse(struct snmp_message *msg)
 
        print_host(ss, msg->sm_host, sizeof(msg->sm_host));
        if (msg->sm_version == SNMP_V3)
-               log_debug("%s: %s: SNMPv3 context %d, flags %#x, "
+               log_debug("%s: %s:%hd: SNMPv3 context %d, flags %#x, "
                    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
-                   "request %lld", __func__, msg->sm_host, msg->sm_context,
-                   msg->sm_flags, msg->sm_secmodel, msg->sm_username,
-                   tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
-                   msg->sm_ctxname, msg->sm_request);
+                   "request %lld", __func__, msg->sm_host, msg->sm_port,
+                   msg->sm_context, msg->sm_flags, msg->sm_secmodel,
+                   msg->sm_username, tohexstr(msg->sm_ctxengineid,
+                   msg->sm_ctxengineid_len), msg->sm_ctxname, msg->sm_request);
        else
-               log_debug("%s: %s: SNMPv%d '%s' context %d request %lld",
-                   __func__, msg->sm_host, msg->sm_version + 1,
+               log_debug("%s: %s:%hd: SNMPv%d '%s' context %d request %lld",
+                   __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1,
                    msg->sm_community, msg->sm_context, msg->sm_request);
 
        return (0);
@@ -373,7 +406,8 @@ snmpe_parse(struct snmp_message *msg)
        stats->snmp_inasnparseerrs++;
  fail:
        print_host(ss, msg->sm_host, sizeof(msg->sm_host));
-       log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr);
+       log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port,
+           msg->sm_errstr);
        return (-1);
 }
 
@@ -403,8 +437,8 @@ snmpe_parsevarbinds(struct snmp_message *msg)
                if (o.bo_n < BER_MIN_OID_LEN || o.bo_n > BER_MAX_OID_LEN)
                        goto varfail;
 
-               log_debug("%s: %s: oid %s", __func__, msg->sm_host,
-                   smi_oid2string(&o, buf, sizeof(buf), 0));
+               log_debug("%s: %s:%hd: oid %s", __func__, msg->sm_host,
+                   msg->sm_port, smi_oid2string(&o, buf, sizeof(buf), 0));
 
                /*
                 * XXX intotalreqvars should only be incremented after all are
@@ -479,8 +513,8 @@ snmpe_parsevarbinds(struct snmp_message *msg)
 
        return 0;
  varfail:
-       log_debug("%s: %s: %s, error index %d", __func__,
-           msg->sm_host, msg->sm_errstr, i);
+       log_debug("%s: %s:%hd: %s, error index %d", __func__,
+           msg->sm_host, msg->sm_port, msg->sm_errstr, i);
        if (msg->sm_error == 0)
                msg->sm_error = SNMP_ERROR_GENERR;
        msg->sm_errorindex = i;
@@ -515,6 +549,10 @@ snmpe_acceptcb(int fd, short type, void *arg)
        if ((msg = calloc(1, sizeof(*msg))) == NULL)
                goto fail;
 
+       memcpy(&(msg->sm_ss), &ss, len);
+       msg->sm_slen = len;
+       msg->sm_aflags = h->flags;
+       msg->sm_port = h->port;
        snmpe_prepare_read(msg, afd);
        return;
 fail:
@@ -635,6 +673,10 @@ snmpe_writecb(int fd, short type, void *arg)
 
        if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL)
                goto fail;
+       memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen);
+       nmsg->sm_slen = msg->sm_slen;
+       nmsg->sm_aflags = msg->sm_aflags;
+       nmsg->sm_port = msg->sm_port;
 
        /*
         * Reuse the connection.
@@ -662,16 +704,18 @@ snmpe_writecb(int fd, short type, void *arg)
 void
 snmpe_recvmsg(int fd, short sig, void *arg)
 {
-       struct snmpd            *env = arg;
-       struct snmp_stats       *stats = &env->sc_stats;
+       struct address          *h = arg;
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        ssize_t                  len;
        struct snmp_message     *msg;
 
        if ((msg = calloc(1, sizeof(*msg))) == NULL)
                return;
 
+       msg->sm_aflags = h->flags;
        msg->sm_sock = fd;
        msg->sm_slen = sizeof(msg->sm_ss);
+       msg->sm_port = h->port;
        if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
            (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
            (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
@@ -715,6 +759,11 @@ snmpe_recvmsg(int fd, short sig, void *arg)
 void
 snmpe_dispatchmsg(struct snmp_message *msg)
 {
+       if (msg->sm_type == SNMP_C_TRAP ||
+           msg->sm_type == SNMP_C_TRAPV2) {
+               snmp_msgfree(msg);
+               return;
+       }
        /* dispatched to subagent */
        /* XXX Do proper error handling */
        (void) snmpe_parsevarbinds(msg);
index 45e049b..74347b6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: traphandler.c,v 1.19 2021/01/05 18:12:15 martijn Exp $        */
+/*     $OpenBSD: traphandler.c,v 1.20 2021/01/22 06:33:27 martijn Exp $        */
 
 /*
  * Copyright (c) 2014 Bret Stephen Lambert <blambert@openbsd.org>
 #include "snmpd.h"
 #include "mib.h"
 
-char    trap_path[PATH_MAX];
-
-void    traphandler_init(struct privsep *, struct privsep_proc *, void *arg);
-int     traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *);
-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(struct ber_element *, char *, struct sockaddr *);
 struct ber_element *
-        traphandler_v1translate(struct ber_element *, char *, int);
+        traphandler_v1translate(struct snmp_message *, int);
 int     trapcmd_cmp(struct trapcmd *, struct trapcmd *);
 void    trapcmd_exec(struct trapcmd *, struct sockaddr *,
            struct ber_element *);
@@ -65,172 +58,14 @@ RB_GENERATE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp)
 
 struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree);
 
-static struct privsep_proc procs[] = {
-       { "parent",     PROC_PARENT,    traphandler_dispatch_parent }
-};
-
-void
-traphandler(struct privsep *ps, struct privsep_proc *p)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (env->sc_traphandler) {
-               TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-                       if (h->type != SOCK_DGRAM)
-                               continue;
-                       if ((h->fd = traphandler_bind(h)) == -1)
-                               fatal("could not create trap listener socket");
-               }
-       }
-
-       proc_run(ps, p, procs, nitems(procs), traphandler_init, NULL);
-}
-
-void
-traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (pledge("stdio id proc recvfd exec", NULL) == -1)
-               fatal("pledge");
-
-       if (!env->sc_traphandler)
-               return;
-
-       /* listen for SNMP trap messages */
-       TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-               event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                   traphandler_recvmsg, NULL);
-               event_add(&h->ev, NULL);
-       }
-}
-
-int
-traphandler_bind(struct address *addr)
-{
-       int                      s;
-       char                     buf[512];
-       struct sockaddr_in      *sin;
-       struct sockaddr_in6     *sin6;
-
-       if (addr->ss.ss_family == AF_INET) {
-               sin = (struct sockaddr_in *)&(addr->ss);
-               sin->sin_port = htons(162);
-       } else {
-               sin6 = (struct sockaddr_in6 *)&(addr->ss);
-               sin6->sin6_port = htons(162);
-       }
-       if ((s = snmpd_socket_af(&addr->ss, SOCK_DGRAM)) == -1)
-               return (-1);
-
-       if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
-               goto bad;
-
-       if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
-               goto bad;
-
-       if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
-               goto bad;
-
-       log_info("traphandler: listening on %s:%s", buf, SNMPD_TRAPPORT);
-
-       return (s);
- bad:
-       close (s);
-       return (-1);
-}
-
-void
-traphandler_shutdown(void)
-{
-       struct address          *h;
-
-       TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
-               event_del(&h->ev);
-               close(h->fd);
-       }
-}
-
-int
-traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-int
-snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       case IMSG_ALERT:
-               return (traphandler_priv_recvmsg(p, imsg));
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-void
-traphandler_recvmsg(int fd, short events, void *arg)
-{
-       struct ber               ber = {0};
-       struct ber_element      *msg = NULL, *pdu;
-       char                     buf[8196];
-       struct sockaddr_storage  ss;
-       socklen_t                slen;
-       ssize_t                  n;
-       int                      vers;
-       char                    *community;
-
-       slen = sizeof(ss);
-       if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
-           &slen)) == -1)
-               return;
-
-       ober_set_application(&ber, smi_application);
-       ober_set_readbuf(&ber, buf, n);
-
-       if ((msg = ober_read_elements(&ber, NULL)) == NULL)
-               goto parsefail;
-
-       if (ober_scanf_elements(msg, "{dse", &vers, &community, &pdu) == -1)
-               goto parsefail;
-
-       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(struct ber_element *pdu, char *community, struct sockaddr *sa)
+traphandler_parse(struct snmp_message *msg)
 {
        struct privsep          *ps = &snmpd_env->sc_ps;
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        struct ber               ber = {0};
        struct ber_element      *vblist = NULL, *elm, *elm2;
        struct ber_oid           o1, o2, snmpTrapOIDOID;
@@ -241,22 +76,24 @@ traphandler_parse(struct ber_element *pdu, char *community, struct sockaddr *sa)
        ssize_t                  buflen;
        int                      ret = -1;
 
-       switch (pdu->be_type) {
+       switch (msg->sm_pdu->be_type) {
        case SNMP_C_TRAP:
-               vblist = traphandler_v1translate(pdu, community, 0);
-               if (vblist == NULL)
+               if ((vblist = traphandler_v1translate(msg, 0)) == NULL)
                        goto done;
                break;
        case SNMP_C_TRAPV2:
-               if (ober_scanf_elements(pdu, "{SSe}", &elm) == -1)
+               if (ober_scanf_elements(msg->sm_pdu, "{SSe}", &elm) == -1) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
-               if (elm->be_type != BER_TYPE_INTEGER)
+               }
+               if (elm->be_type != BER_TYPE_INTEGER) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
+               }
                vblist = ober_unlink_elements(elm);
                break;
        default:
-               log_warnx("unsupported SNMP trap version '%d'", pdu->be_type);
-               goto done;
+               fatalx("%s called without proper context", __func__);
        }
 
        (void)ober_string2oid("1.3.6.1.2.1.1.3.0", &sysUpTimeOID);
@@ -265,29 +102,36 @@ traphandler_parse(struct ber_element *pdu, char *community, struct sockaddr *sa)
            &snmpTrapOID) == -1 ||
            ober_oid_cmp(&o1, &sysUpTimeOID) != 0 ||
            ober_oid_cmp(&o2, &snmpTrapOIDOID) != 0) {
+               stats->snmp_inasnparseerrs++;
                goto done;
        }
        (void)ober_scanf_elements(vblist, "{Se", &elm);
        for (elm = elm->be_next; elm != NULL; elm = elm->be_next) {
                if (ober_scanf_elements(elm, "{oe}", &o1, &elm2) == -1 ||
-                   elm2->be_next != NULL)
+                   elm2->be_next != NULL) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
+               }
        }
 
        ober_set_application(&ber, smi_application);
 
        if ((buflen = ober_write_elements(&ber, vblist)) == -1 ||
-           ober_get_writebuf(&ber, &buf) == -1)
+           ober_get_writebuf(&ber, &buf) == -1) {
+               msg->sm_errstr = "failed to handle trap";
                goto done;
+       }
 
-       iov[0].iov_base = sa;
-       iov[0].iov_len = sa->sa_len;
+       iov[0].iov_base = &(msg->sm_ss);
+       iov[0].iov_len = msg->sm_slen;
        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)
+       if (proc_composev(ps, PROC_PARENT, IMSG_TRAP_EXEC, iov, 2) == -1) {
+               msg->sm_errstr = "failed to handle trap";
                goto done;
+       }
 
        ret = 0;
 done:
@@ -298,8 +142,9 @@ done:
 }
 
 struct ber_element *
-traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
+traphandler_v1translate(struct snmp_message *msg, int proxy)
 {
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        struct ber_oid trapoid, enterprise, oid, snmpTrapAddressOid;
        struct ber_oid snmpTrapCommunityOid, snmpTrapEnterpriseOid;
        struct ber_element *elm, *last, *vblist, *vb0 = NULL;
@@ -308,12 +153,12 @@ traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
        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 ||
+       if (ober_scanf_elements(msg->sm_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;
+               stats->snmp_inasnparseerrs++;
                return NULL;
        }
        switch (generic_trap) {
@@ -337,15 +182,16 @@ traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
                break;
        case 6:
                trapoid = enterprise;
+               /* Officially this should be 128, but BER_MAX_OID_LEN is 64 */
                if (trapoid.bo_n + 2 > BER_MAX_OID_LEN) {
-                       errno = EINVAL;
+                       stats->snmp_inasnparseerrs++;
                        return NULL;
                }
                trapoid.bo_id[trapoid.bo_n++] = 0;
                trapoid.bo_id[trapoid.bo_n++] = specific_trap;
                break;
        default:
-               errno = EINVAL;
+               stats->snmp_inasnparseerrs++;
                return NULL;
        }
 
@@ -354,12 +200,14 @@ traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
                vb0 = ober_unlink_elements(vblist);
 
        if ((vblist = ober_add_sequence(NULL)) == NULL) {
+               msg->sm_errstr = strerror(errno);
                if (vb0 != 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) {
+               msg->sm_errstr = strerror(errno);
                if (vb0 != 0)
                        ober_free_elements(vb0);
                ober_free_elements(vblist);
@@ -375,6 +223,7 @@ traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
                    &snmpTrapEnterpriseOid);
                for (elm = vblist->be_sub; elm != NULL; elm = elm->be_next) {
                        if (ober_get_oid(elm->be_sub, &oid) == -1) {
+                               msg->sm_errstr = "failed to read oid";
                                ober_free_elements(vblist);
                                return NULL;
                        }
@@ -391,8 +240,9 @@ traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
                        if (ober_printf_elements(last, "{Oxt}{Os}{OO}",
                            &snmpTrapAddressOid, agent_addr, 4,
                            BER_CLASS_APPLICATION, SNMP_T_IPADDR,
-                           &snmpTrapCommunityOid, community,
+                           &snmpTrapCommunityOid, msg->sm_community,
                            &snmpTrapEnterpriseOid, &enterprise) == NULL) {
+                               msg->sm_errstr = strerror(errno);
                                ober_free_elements(vblist);
                                return NULL;
                        }