From 320015f8fce66eba06563affcf536ab2c58be3a5 Mon Sep 17 00:00:00 2001 From: martijn Date: Sat, 27 Jan 2024 09:53:59 +0000 Subject: [PATCH] Implement an initial SMIv2 parser based around RFC257[89]. RFC2580 isn't supported yet. SMIv1 is not supported. Parsing is done in a strict manner, but except for the (deprecated) IPV6-TC MIB everything from IETF/IANA that I found parses. For now this code will be used OID<->name translations, but other functionality could be added in the future. This commit just includes the parser, usage and including the MIB files will be done in separate commits. Go ahead from tb@ --- usr.sbin/snmpd/Makefile | 8 +- usr.sbin/snmpd/mib.h | 18 +- usr.sbin/snmpd/mib.y | 2260 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 2283 insertions(+), 3 deletions(-) create mode 100644 usr.sbin/snmpd/mib.y diff --git a/usr.sbin/snmpd/Makefile b/usr.sbin/snmpd/Makefile index dfb72716902..9eb74e5dd58 100644 --- a/usr.sbin/snmpd/Makefile +++ b/usr.sbin/snmpd/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.25 2023/11/12 16:07:34 martijn Exp $ +# $OpenBSD: Makefile,v 1.26 2024/01/27 09:53:59 martijn Exp $ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 -SRCS= parse.y log.c snmpe.c application.c application_blocklist.c \ +SRCS= mib.y parse.y log.c snmpe.c application.c application_blocklist.c \ application_internal.c application_agentx.c ax.c \ trap.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c @@ -16,4 +16,8 @@ CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare YFLAGS= +mib.c: mib.y + ${YACC.y} -pmib -o ${.TARGET} ${.IMPSRC} + + .include diff --git a/usr.sbin/snmpd/mib.h b/usr.sbin/snmpd/mib.h index 06774a9080c..fc2a428828d 100644 --- a/usr.sbin/snmpd/mib.h +++ b/usr.sbin/snmpd/mib.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mib.h,v 1.43 2023/12/21 13:54:05 martijn Exp $ */ +/* $OpenBSD: mib.h,v 1.44 2024/01/27 09:53:59 martijn Exp $ */ /* * Copyright (c) 2007, 2008 Reyk Floeter @@ -19,6 +19,22 @@ #ifndef SNMPD_MIB_H #define SNMPD_MIB_H +#include + +struct ber_oid; +enum mib_oidfmt { + MIB_OIDNUMERIC, + MIB_OIDSYMBOLIC +}; + +void mib_parsefile(const char *); +void mib_parsedir(const char *); +void mib_resolve(void); +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 *); + #define MIBDECL(...) { { MIB_##__VA_ARGS__ }, \ (sizeof((uint32_t []) { MIB_##__VA_ARGS__ }) / sizeof(uint32_t))}, #__VA_ARGS__ #define MIBEND { { 0 } }, NULL diff --git a/usr.sbin/snmpd/mib.y b/usr.sbin/snmpd/mib.y new file mode 100644 index 00000000000..5e18dbecc3b --- /dev/null +++ b/usr.sbin/snmpd/mib.y @@ -0,0 +1,2260 @@ +/* $OpenBSD: mib.y,v 1.1 2024/01/27 09:53:59 martijn Exp $ */ + +/* + * Copyright (c) 2023 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "mib.h" + +/* RFC2578 section 3.1 */ +#define DESCRIPTOR_MAX 64 + +/* Values from real life testing, could be adjusted */ +#define ITEM_MAX DESCRIPTOR_MAX +#define MODULENAME_MAX 64 +#define SYMBOLS_MAX 256 +#define IMPORTS_MAX 16 +#define TEXT_MAX 16384 + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +struct objidcomponent { + enum { + OCT_DESCRIPTOR, + OCT_NUMBER, + OCT_NAMEANDNUMBER + } type; + uint32_t number; + char name[DESCRIPTOR_MAX + 1]; +}; + +struct oid_unresolved { + /* Unusual to have long lists of unresolved components */ + struct objidcomponent bo_id[16]; + size_t bo_n; +}; + +struct oid_resolved { + uint32_t *bo_id; + size_t bo_n; +}; + +enum status { + CURRENT, + DEPRECATED, + OBSOLETE +}; + +enum access { + NOTACCESSIBLE, + ACCESSIBLEFORNOTIFY, + READONLY, + READWRITE, + READCREATE +}; + +struct objectidentity { + enum status status; + char *description; + char *reference; +}; + +struct objecttype { + void *syntax; + char *units; + enum access maxaccess; + enum status status; + char *description; + char *reference; + void *index; + void *defval; +}; + +struct notificationtype { + void *objects; + enum status status; + char *description; + char *reference; +}; + +struct textualconvention { + char *displayhint; + enum status status; + char *description; + char *reference; + void *syntax; +}; + +struct item { + char name[DESCRIPTOR_MAX + 1]; + enum item_type { + IT_OID, + IT_MACRO, + IT_MODULE_IDENTITY, + IT_OBJECT_IDENTITY, + IT_APPLICATIONSYNTAX, + IT_OBJECT_TYPE, + IT_NOTIFICATION_TYPE, + IT_TEXTUAL_CONVENTION, + IT_OBJECT_GROUP, + IT_NOTIFICATION_GROUP, + IT_MODULE_COMPLIANCE, + IT_AGENT_CAPABITIES + } type; + int resolved; + struct module *module; + + union { + struct oid_unresolved *oid_unresolved; + struct oid_resolved oid; + }; + + union { + struct objectidentity objectidentity; + struct objecttype objecttype; + struct notificationtype notificationtype; + struct textualconvention textualconvention; + }; + + /* Global case insensitive */ + RB_ENTRY(item) entrygci; + /* Module case insensitive */ + RB_ENTRY(item) entryci; + /* Module case sensitive */ + RB_ENTRY(item) entrycs; + /* Global oid */ + RB_ENTRY(item) entry; +}; + +struct import_symbol { + char name[DESCRIPTOR_MAX + 1]; + struct item *item; +}; + +struct import { + char name[MODULENAME_MAX + 1]; + struct module *module; + struct import_symbol symbols[SYMBOLS_MAX]; + size_t nsymbols; +}; + +static struct module { + char name[MODULENAME_MAX + 1]; + int8_t resolved; + + time_t lastupdated; + + struct import *imports; + + RB_HEAD(itemscs, item) itemscs; + RB_HEAD(itemsci, item) itemsci; + + RB_ENTRY(module) entryci; + RB_ENTRY(module) entrycs; +} *module; + +int yylex(void); +static void yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void mib_defaults(void); +void mib_modulefree(struct module *); +int mib_imports_add(char *, char **); +int mib_oid_append(struct oid_unresolved *, + const struct objidcomponent *); +int mib_macro(const char *); +int mib_oid_concat(struct oid_unresolved *, + const struct oid_unresolved *); +struct item *mib_item(const char *, enum item_type); +int mib_item_oid(struct item *, + const struct oid_unresolved *); +int mib_macro(const char *); +int mib_applicationsyntax(const char *); +struct item *mib_oid(const char *, const struct oid_unresolved *); +int mib_moduleidentity(const char *, time_t, const char *, + 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 *, + 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 *); +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 *); +struct item *mib_item_find(struct item *, const char *); +struct item *mib_item_parent(struct ber_oid *); +int mib_resolve_oid(struct oid_resolved *, + struct oid_unresolved *, struct item *); +int mib_resolve_item(struct item *); +int mib_resolve_module(struct module *); +int module_cmp_cs(struct module *, struct module *); +int module_cmp_ci(struct module *, struct module *); +int item_cmp_cs(struct item *, struct item *); +int item_cmp_ci(struct item *, struct item *); +int item_cmp_oid(struct item *, struct item *); + +RB_HEAD(modulesci, module) modulesci = RB_INITIALIZER(&modulesci); +RB_HEAD(modulescs, module) modulescs = RB_INITIALIZER(&modulescs); +RB_HEAD(items, item) items = RB_INITIALIZER(&items); +RB_HEAD(itemsgci, item) itemsci = RB_INITIALIZER(&itemsci); +/* + * Use case sensitive matching internally (for resolving IMPORTS) and + * case sensitive matching, followed by case insensitive matching + * for end-user resolving (e.g. mib_string2oid()). + * It shouldn't happen there's case-based overlap in module/item names, + * but allow all to be resolved in case there is. + */ +RB_PROTOTYPE_STATIC(modulesci, module, entryci, module_cmp_ci); +RB_PROTOTYPE_STATIC(modulescs, module, entrycs, module_cmp_cs); +/* + * mib_string2oid() should match case insensitive on: + * :: + * + */ +RB_PROTOTYPE_STATIC(itemsgci, item, entrygci, item_cmp_ci); +RB_PROTOTYPE_STATIC(itemsci, item, entryci, item_cmp_ci); +RB_PROTOTYPE_STATIC(itemscs, item, entrycs, item_cmp_cs); +RB_PROTOTYPE_STATIC(items, item, entry, item_cmp_oid); + +struct file { + FILE *stream; + const char *name; + size_t lineno; + enum { + FILE_UNDEFINED, + FILE_ASN1, + FILE_SMI2 + } state; +} file; + +typedef union { + char string[TEXT_MAX]; + unsigned long long number; + long long signednumber; + char symbollist[SYMBOLS_MAX][DESCRIPTOR_MAX + 1]; + struct objidcomponent objidcomponent; + struct oid_unresolved oid; + time_t time; + enum status status; + enum access access; +} YYSTYPE; + +%} + +%token ERROR +%token HSTRING BSTRING + +/* RFC2578 section 3.7 */ +%token ABSENT ACCESS AGENTCAPABILITIES ANY APPLICATION AUGMENTS BEGIN +%token BIT BITS BOOLEAN BY CHOICE COMPONENT COMPONENTS CONTACTINFO +%token CREATIONREQUIRES Counter32 Counter64 DEFAULT DEFINED +%token DEFINITIONS DEFVAL DESCRIPTION DISPLAYHINT END ENUMERATED +%token ENTERPRISE EXPLICIT EXPORTS EXTERNAL FALSE FROM GROUP Gauge32 +%token IDENTIFIER IMPLICIT IMPLIED IMPORTS INCLUDES INDEX INTEGER +%token Integer32 IpAddress LASTUPDATED MANDATORYGROUPS MAX MAXACCESS +%token MIN MINACCESS MINUSINFINITY MODULE MODULECOMPLIANCE MODULEIDENTITY +%token NOTIFICATIONGROUP NOTIFICATIONTYPE NOTIFICATIONS ASNNULL +%token OBJECT OBJECTGROUP OBJECTIDENTITY OBJECTTYPE OBJECTS OCTET OF +%token OPTIONAL ORGANIZATION Opaque PLUSINFINITY PRESENT PRIVATE +%token PRODUCTRELEASE REAL REFERENCE REVISION SEQUENCE SET SIZE STATUS +%token STRING SUPPORTS SYNTAX TAGS TEXTUALCONVENTION TRAPTYPE TRUE +%token TimeTicks UNITS UNIVERSAL Unsigned32 VARIABLES VARIATION WITH +%token WRITESYNTAX + +/* SMIv2 */ +%token SNMPv2SMI SNMPv2CONF SNMPv2TC + +/* X.208 */ +%token PRODUCTION RANGESEPARATOR + +%token typereference identifier TEXT HSTRING BSTRING +%token NUMBER +%token SIGNEDNUMBER +%type moduleidentifier smiv2moduleidentifier +%type symbolsfrom +%type symbollist +%type descriptor symbol +%type objidcomponentfirst objidcomponent +%type objidcomponentlist objectidentifiervalue +%type displaypart referpart unitspart +%type