From: martijn Date: Fri, 22 Jan 2021 06:33:26 +0000 (+0000) Subject: Remove the traphandler process, which was nothing more then a sham. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=20f2292fce5684f178bef363384e1ce048632f8f;p=openbsd Remove the traphandler process, which was nothing more then a sham. 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@ --- diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y index 144a4ea8fbf..cb9d389ea90 100644 --- a/usr.sbin/snmpd/parse.y +++ b/usr.sbin/snmpd/parse.y @@ -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 @@ -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 NUMBER %type hostcmn %type srcaddr port -%type optwrite yesno seclevel +%type optwrite yesno seclevel listenopt listenopts %type objtype cmd %type oid hostoid trapoid %type 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; diff --git a/usr.sbin/snmpd/snmpd.c b/usr.sbin/snmpd/snmpd.c index b7205701b5a..88005374dac 100644 --- a/usr.sbin/snmpd/snmpd.c +++ b/usr.sbin/snmpd/snmpd.c @@ -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 @@ -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: diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5 index ce1f3c349d0..1cb865cf15c 100644 --- a/usr.sbin/snmpd/snmpd.conf.5 +++ b/usr.sbin/snmpd/snmpd.conf.5 @@ -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 .\" @@ -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 diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h index 7d7212e9ffc..b08a5173bab 100644 --- a/usr.sbin/snmpd/snmpd.h +++ b/usr.sbin/snmpd/snmpd.h @@ -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 @@ -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 * diff --git a/usr.sbin/snmpd/snmpe.c b/usr.sbin/snmpd/snmpe.c index dd5ebd38261..4aea2d5c2b1 100644 --- a/usr.sbin/snmpd/snmpe.c +++ b/usr.sbin/snmpd/snmpe.c @@ -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 @@ -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); diff --git a/usr.sbin/snmpd/traphandler.c b/usr.sbin/snmpd/traphandler.c index 45e049b4654..74347b684f0 100644 --- a/usr.sbin/snmpd/traphandler.c +++ b/usr.sbin/snmpd/traphandler.c @@ -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 @@ -43,17 +43,10 @@ #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; }