From: Martijn van Duren Subject: snmpd: testers wanted: varbind syntax validation To: tech@openbsd.org Date: Mon, 2 Nov 2026 13:47:43 +0100 Hello tech@, I'm not sure in what direction I want to take this diff, but below is an early attempt at syntax validation for varbinds in snmpd. With this diff I found the invalid carpGroupDemote INTEGER range for which I just committed a fix. If you run snmpd on a low risk machine I would be interested to see what varbinds break and if we can fix them. Since metrics can change over time, I'm not expecting every failure (assuming there are) to show up on the first fly-over. If you connect external metrics via AgentX, make sure you include their corresponding MIB files via `mib directory` in snmpd.conf, or manually place them (and their dependencies) inside /usr/share/snmp/mibs. How will a failure show up? A GENERR will be returned, and a a "not MIB compliant" line will show up in the log, like: AgentX(2948596552/1757229136): 780061712 OPENBSD-CARP-MIB::carpGroupDemote.1: not MIB compliant After this, the AgentX connection is closed. Meaning that if the data came from snmpd_metrics (spawned by snmpd itself), snmpd_metrics will exit and take snmpd with it. If it's an external daemon it's dependent on the daemon; vmd, and relayd should reconnect automatically, other daemons might behave differently. martijn@ diff refs/heads/master refs/heads/snmpd/mib_syntax commit - 8c9ea0ff3fd3788b066415bd61a787c53e6c77b7 commit + 2e6836dc56c30b72cf51908e715184381a0337ee blob - da9643f9aa602ae007006920d1f162624a0f39f2 blob + 27cd3bda1230b4823b718cdde34de1f91857bbc4 --- usr.sbin/snmpd/application.c +++ usr.sbin/snmpd/application.c @@ -1485,6 +1485,13 @@ appl_varbind_valid(struct appl_varbind *varbind, } } + if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL || + varbind->av_value->be_class == BER_CLASS_APPLICATION || 0) { + if (!mib_varbind_valid(&varbind->av_oid, varbind->av_value)) { + *errstr = "not MIB compliant"; + return 0; + } + } if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) { switch (varbind->av_value->be_type) { case BER_TYPE_NULL: blob - fc2a428828de5e9ca1761e25223fc7eeee165e2f blob + 8c41d11fb48a3821be32387e91bf3b7e21161be6 --- usr.sbin/snmpd/mib.h +++ usr.sbin/snmpd/mib.h @@ -34,6 +34,7 @@ void mib_clear(void); char *mib_oid2string(struct ber_oid *, char *, size_t, enum mib_oidfmt); const char *mib_string2oid(const char *, struct ber_oid *); +int mib_varbind_valid(struct ber_oid *, struct ber_element *); #define MIBDECL(...) { { MIB_##__VA_ARGS__ }, \ (sizeof((uint32_t []) { MIB_##__VA_ARGS__ }) / sizeof(uint32_t))}, #__VA_ARGS__ blob - eb7a1a1272dbfe1ac06fc5b3aebbde9e3fb65b41 blob + a18d5ceebda51285765cc76ced6a803a3e95cf72 --- usr.sbin/snmpd/mib.y +++ usr.sbin/snmpd/mib.y @@ -35,6 +35,7 @@ #include "log.h" #include "mib.h" +#include "snmp.h" /* RFC2578 section 3.1 */ #define DESCRIPTOR_MAX 64 @@ -71,6 +72,45 @@ struct oid_resolved { size_t bo_n; }; +struct range { + long long start; + long long end; +}; + +struct ranges { + struct range range[20]; + size_t nranges; +}; + +enum type { + TYPE_REFERENCE, + TYPE_INTEGER, + TYPE_OID, + TYPE_OSTRING, + TYPE_IPADDRESS, + TYPE_COUNTER32, + TYPE_GAUGE32, + TYPE_UNSIGNED32, + TYPE_TIMETICKS, + TYPE_OPAQUE, + TYPE_COUNTER64, + TYPE_BITS, + TYPE_ENUMERATED, + TYPE_SEQUENCEOF +}; + +struct syntax { + enum type type; + union { + char reference[DESCRIPTOR_MAX + 1]; + struct item *refitem; + }; + union { + struct ranges ranges; + /* TODO support for enumerated */ + }; +}; + enum status { CURRENT, DEPRECATED, @@ -92,7 +132,7 @@ struct objectidentity { }; struct objecttype { - void *syntax; + struct syntax syntax; char *units; enum access maxaccess; enum status status; @@ -114,7 +154,7 @@ struct textualconvention { enum status status; char *description; char *reference; - void *syntax; + struct syntax syntax; }; struct item { @@ -131,7 +171,8 @@ struct item { IT_OBJECT_GROUP, IT_NOTIFICATION_GROUP, IT_MODULE_COMPLIANCE, - IT_AGENT_CAPABITIES + IT_AGENT_CAPABITIES, + IT_SEQUENCE } type; int resolved; struct module *module; @@ -206,19 +247,20 @@ int mib_moduleidentity(const char *, time_t, const c const char *, const char *, const struct oid_unresolved *); int mib_objectidentity(const char *, enum status, const char *, const char *, const struct oid_unresolved *); -int mib_objecttype(const char *, void *, const char *, +int mib_objecttype(const char *, struct syntax *, const char *, enum access, enum status, const char *, const char *, void *, void *, const struct oid_unresolved *); int mib_notificationtype(const char *, void *, enum status, const char *, const char *, const struct oid_unresolved *); int mib_textualconvetion(const char *, const char *, enum status, - const char *, const char *, void *); + const char *, const char *, const struct syntax *); int mib_objectgroup(const char *, void *, enum status, const char *, const char *, const struct oid_unresolved *); int mib_notificationgroup(const char *, void *, enum status, const char *, const char *, const struct oid_unresolved *); int mib_modulecompliance(const char *, enum status, const char *, const char *, void *, const struct oid_unresolved *); +int mib_item_sequence(const char *, void *); struct item *mib_item_find(struct item *, const char *); struct item *mib_item_parent(struct ber_oid *); int mib_resolve_oid(struct oid_resolved *, @@ -272,6 +314,9 @@ typedef union { char symbollist[SYMBOLS_MAX][DESCRIPTOR_MAX + 1]; struct objidcomponent objidcomponent; struct oid_unresolved oid; + struct syntax syntax; + struct range range; + struct ranges ranges; time_t time; enum status status; enum access access; @@ -309,6 +354,7 @@ typedef union { %token NUMBER %token SIGNEDNUMBER %type moduleidentifier smiv2moduleidentifier +%type value %type symbolsfrom %type symbollist %type descriptor symbol @@ -318,6 +364,10 @@ typedef union { %type