diff options
Diffstat (limited to 'usr.bin/snmp/smi.c')
-rw-r--r-- | usr.bin/snmp/smi.c | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/usr.bin/snmp/smi.c b/usr.bin/snmp/smi.c new file mode 100644 index 00000000000..8c601df3244 --- /dev/null +++ b/usr.bin/snmp/smi.c @@ -0,0 +1,627 @@ +/* $OpenBSD: smi.c,v 1.1 2019/08/09 06:17:59 martijn Exp $ */ + +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> + * + * 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 <sys/limits.h> +#include <sys/tree.h> +#include <sys/queue.h> + +#include <arpa/inet.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include "ber.h" +#include "mib.h" +#include "snmp.h" +#include "smi.h" + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +int smi_oid_cmp(struct oid *, struct oid *); +int smi_key_cmp(struct oid *, struct oid *); +struct oid * smi_findkey(char *); + +RB_HEAD(oidtree, oid); +RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp) +struct oidtree smi_oidtree; + +RB_HEAD(keytree, oid); +RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp) +struct keytree smi_keytree; + +int +smi_init(void) +{ + /* Initialize the Structure of Managed Information (SMI) */ + RB_INIT(&smi_oidtree); + mib_init(); + return (0); +} + +void +smi_debug_elements(struct ber_element *root) +{ + static int indent = 0; + char *value; + int constructed; + + /* calculate lengths */ + ber_calc_len(root); + + switch (root->be_encoding) { + case BER_TYPE_SEQUENCE: + case BER_TYPE_SET: + constructed = root->be_encoding; + break; + default: + constructed = 0; + break; + } + + fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); + switch (root->be_class) { + case BER_CLASS_UNIVERSAL: + fprintf(stderr, "class: universal(%u) type: ", root->be_class); + switch (root->be_type) { + case BER_TYPE_EOC: + fprintf(stderr, "end-of-content"); + break; + case BER_TYPE_BOOLEAN: + fprintf(stderr, "boolean"); + break; + case BER_TYPE_INTEGER: + fprintf(stderr, "integer"); + break; + case BER_TYPE_BITSTRING: + fprintf(stderr, "bit-string"); + break; + case BER_TYPE_OCTETSTRING: + fprintf(stderr, "octet-string"); + break; + case BER_TYPE_NULL: + fprintf(stderr, "null"); + break; + case BER_TYPE_OBJECT: + fprintf(stderr, "object"); + break; + case BER_TYPE_ENUMERATED: + fprintf(stderr, "enumerated"); + break; + case BER_TYPE_SEQUENCE: + fprintf(stderr, "sequence"); + break; + case BER_TYPE_SET: + fprintf(stderr, "set"); + break; + } + break; + case BER_CLASS_APPLICATION: + fprintf(stderr, "class: application(%u) type: ", + root->be_class); + switch (root->be_type) { + case SNMP_T_IPADDR: + fprintf(stderr, "ipaddr"); + break; + case SNMP_T_COUNTER32: + fprintf(stderr, "counter32"); + break; + case SNMP_T_GAUGE32: + fprintf(stderr, "gauge32"); + break; + case SNMP_T_TIMETICKS: + fprintf(stderr, "timeticks"); + break; + case SNMP_T_OPAQUE: + fprintf(stderr, "opaque"); + break; + case SNMP_T_COUNTER64: + fprintf(stderr, "counter64"); + break; + } + break; + case BER_CLASS_CONTEXT: + fprintf(stderr, "class: context(%u) type: ", + root->be_class); + switch (root->be_type) { + case SNMP_C_GETREQ: + fprintf(stderr, "getreq"); + break; + case SNMP_C_GETNEXTREQ: + fprintf(stderr, "nextreq"); + break; + case SNMP_C_GETRESP: + fprintf(stderr, "getresp"); + break; + case SNMP_C_SETREQ: + fprintf(stderr, "setreq"); + break; + case SNMP_C_TRAP: + fprintf(stderr, "trap"); + break; + case SNMP_C_GETBULKREQ: + fprintf(stderr, "getbulkreq"); + break; + case SNMP_C_INFORMREQ: + fprintf(stderr, "informreq"); + break; + case SNMP_C_TRAPV2: + fprintf(stderr, "trapv2"); + break; + case SNMP_C_REPORT: + fprintf(stderr, "report"); + break; + } + break; + case BER_CLASS_PRIVATE: + fprintf(stderr, "class: private(%u) type: ", root->be_class); + break; + default: + fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class); + break; + } + fprintf(stderr, "(%u) encoding %u ", + root->be_type, root->be_encoding); + + if ((value = smi_print_element(root, 1, smi_os_default, smi_oidl_numeric)) == NULL) + goto invalid; + + switch (root->be_encoding) { + case BER_TYPE_BOOLEAN: + fprintf(stderr, "%s", value); + break; + case BER_TYPE_INTEGER: + case BER_TYPE_ENUMERATED: + fprintf(stderr, "value %s", value); + break; + case BER_TYPE_BITSTRING: + fprintf(stderr, "hexdump %s", value); + break; + case BER_TYPE_OBJECT: + fprintf(stderr, "oid %s", value); + break; + case BER_TYPE_OCTETSTRING: + if (root->be_class == BER_CLASS_APPLICATION && + root->be_type == SNMP_T_IPADDR) { + fprintf(stderr, "addr %s", value); + } else { + fprintf(stderr, "string %s", value); + } + break; + case BER_TYPE_NULL: /* no payload */ + case BER_TYPE_EOC: + case BER_TYPE_SEQUENCE: + case BER_TYPE_SET: + default: + fprintf(stderr, "%s", value); + break; + } + + invalid: + if (value == NULL) + fprintf(stderr, "<INVALID>"); + else + free(value); + fprintf(stderr, "\n"); + + if (constructed) + root->be_encoding = constructed; + + if (constructed && root->be_sub) { + indent += 2; + smi_debug_elements(root->be_sub); + indent -= 2; + } + if (root->be_next) + smi_debug_elements(root->be_next); +} + +char * +smi_print_element(struct ber_element *root, int print_hint, + enum smi_output_string output_string, enum smi_oid_lookup lookup) +{ + char *str = NULL, *buf, *p; + size_t len, i; + long long v, ticks; + int d; + int is_hex = 0; + struct ber_oid o; + char strbuf[BUFSIZ]; + char *hint; + int days, hours, min, sec, csec; + + switch (root->be_encoding) { + case BER_TYPE_BOOLEAN: + if (ber_get_boolean(root, &d) == -1) + goto fail; + if (print_hint) { + if (asprintf(&str, "INTEGER: %s(%d)", + d ? "true" : "false", d) == -1) + goto fail; + } + else + if (asprintf(&str, "%s", d ? "true" : "false") == -1) + goto fail; + break; + case BER_TYPE_INTEGER: + case BER_TYPE_ENUMERATED: + if (ber_get_integer(root, &v) == -1) + goto fail; + if (root->be_class == BER_CLASS_APPLICATION && + root->be_type == SNMP_T_TIMETICKS) { + ticks = v; + days = ticks / (60 * 60 * 24 * 100); + ticks %= (60 * 60 * 24 * 100); + hours = ticks / (60 * 60 * 100); + ticks %= (60 * 60 * 100); + min = ticks / (60 * 100); + ticks %= (60 * 100); + sec = ticks / 100; + ticks %= 100; + csec = ticks; + + if (print_hint) { + if (days == 0) { + if (asprintf(&str, + "Timeticks: (%lld) " + "%d:%02d:%02d.%02d", + v, hours, min, sec, csec) == -1) + goto fail; + } else if (days == 1) { + if (asprintf(&str, + "Timeticks: (%lld) " + "1 day %d:%02d:%02d.%02d", + v, hours, min, sec, csec) == -1) + goto fail; + } else { + if (asprintf(&str, + "Timeticks: (%lld) " + "%d day %d:%02d:%02d.%02d", + v, days, hours, min, sec, csec) == + -1) + goto fail; + } + } else { + if (days == 0) { + if (asprintf(&str, "%d:%02d:%02d.%02d", + hours, min, sec, csec) == -1) + goto fail; + } else if (days == 1) { + if (asprintf(&str, + "1 day %d:%02d:%02d.%02d", + hours, min, sec, csec) == -1) + goto fail; + } else { + if (asprintf(&str, + "%d day %d:%02d:%02d.%02d", + days, hours, min, sec, csec) == -1) + goto fail; + } + } + break; + } + hint = "INTEGER: "; + if (root->be_class == BER_CLASS_APPLICATION) { + if (root->be_type == SNMP_T_COUNTER32) + hint = "Counter32: "; + else if (root->be_type == SNMP_T_GAUGE32) + hint = "Gauge32: "; + else if (root->be_type == SNMP_T_OPAQUE) + hint = "Opaque: "; + else if (root->be_type == SNMP_T_COUNTER64) + hint = "Counter64: "; + } + if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) + == -1) + goto fail; + break; + case BER_TYPE_BITSTRING: + if (ber_get_bitstring(root, (void *)&buf, &len) == -1) + goto fail; + if ((str = calloc(1, len * 2 + 1 + sizeof("BITS: "))) == NULL) + goto fail; + p = str; + if (print_hint) { + strlcpy(str, "BITS: ", sizeof(str)); + p += sizeof("BITS: "); + } + for (i = 0; i < len; i++) { + snprintf(p, 3, "%02x", buf[i]); + p += 2; + } + break; + case BER_TYPE_OBJECT: + if (ber_get_oid(root, &o) == -1) + goto fail; + if (asprintf(&str, "%s%s", + print_hint ? "OID: " : "", + smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1) + goto fail; + break; + case BER_TYPE_OCTETSTRING: + if (ber_get_string(root, &buf) == -1) + goto fail; + if (root->be_class == BER_CLASS_APPLICATION && + root->be_type == SNMP_T_IPADDR) { + if (asprintf(&str, "%s%s", + print_hint ? "IpAddress: " : "", + inet_ntoa(*(struct in_addr *)buf)) == -1) + goto fail; + } else if (root->be_class == BER_CLASS_CONTEXT && + root->be_type == BER_TYPE_EOC) { + str = strdup("No Such Object available on this agent at this OID"); + + }else { + for (i = 0; i < root->be_len; i++) { + if (!isprint(buf[i])) { + if (output_string == smi_os_default) + output_string = smi_os_hex; + else if (output_string == smi_os_ascii) + is_hex = 1; + break; + } + } + /* + * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte) + * ascii can be max (2 * n) + 2 quotes + NUL-byte + */ + if ((p = str = reallocarray(NULL, + output_string == smi_os_hex ? 3 : 2, + root->be_len + 2)) == NULL) + goto fail; + if (is_hex) + *str++ = '"'; + for (i = 0; i < root->be_len; i++) { + switch (output_string) { + case smi_os_default: + /* FALLTHROUGH */ + case smi_os_ascii: + /* + * There's probably more edgecases here, + * not fully investigated + */ + if (is_hex && buf[i] == '\\') + *str++ = '\\'; + *str++ = isprint(buf[i]) ? buf[i] : '.'; + break; + case smi_os_hex: + sprintf(str, "%s%02hhX", + i == 0 ? "" : + i % 16 == 0 ? "\n" : " ", buf[i]); + str += i == 0 ? 2 : 3; + break; + } + } + if (is_hex) + *str++ = '"'; + *str = '\0'; + str = NULL; + if (asprintf(&str, "%s%s", + print_hint ? + output_string == smi_os_hex ? "Hex-STRING: " : + "STRING: " : + "", p) == -1) { + free(p); + goto fail; + } + free(p); + } + break; + case BER_TYPE_NULL: /* no payload */ + case BER_TYPE_EOC: + case BER_TYPE_SEQUENCE: + case BER_TYPE_SET: + default: + str = strdup(""); + break; + } + + return (str); + + fail: + free(str); + return (NULL); +} + +int +smi_string2oid(const char *oidstr, struct ber_oid *o) +{ + char *sp, *p, str[BUFSIZ]; + const char *errstr; + struct oid *oid; + struct ber_oid ko; + + if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) + return (-1); + bzero(o, sizeof(*o)); + + /* + * Parse OID strings in the common form n.n.n or n-n-n. + * Based on ber_string2oid with additional support for symbolic names. + */ + p = sp = str[0] == '.' ? str + 1 : str; + for (; p != NULL; sp = p) { + if ((p = strpbrk(p, ".-")) != NULL) + *p++ = '\0'; + if ((oid = smi_findkey(sp)) != NULL) { + bcopy(&oid->o_id, &ko, sizeof(ko)); + if (o->bo_n && ber_oid_cmp(o, &ko) != 2) + return (-1); + bcopy(&ko, o, sizeof(*o)); + errstr = NULL; + } else { + o->bo_id[o->bo_n++] = + strtonum(sp, 0, UINT_MAX, &errstr); + } + if (errstr || o->bo_n > BER_MAX_OID_LEN) + return (-1); + } + + return (0); +} + +unsigned int +smi_application(struct ber_element *elm) +{ + if (elm->be_class != BER_CLASS_APPLICATION) + return (BER_TYPE_OCTETSTRING); + + switch (elm->be_type) { + case SNMP_T_IPADDR: + return (BER_TYPE_OCTETSTRING); + case SNMP_T_COUNTER32: + case SNMP_T_GAUGE32: + case SNMP_T_TIMETICKS: + case SNMP_T_OPAQUE: + case SNMP_T_COUNTER64: + return (BER_TYPE_INTEGER); + default: + break; + } + return (BER_TYPE_OCTETSTRING); + +} + +char * +smi_oid2string(struct ber_oid *o, char *buf, size_t len, + enum smi_oid_lookup lookup) +{ + char str[256]; + struct oid *value, key; + size_t i; + + bzero(buf, len); + bzero(&key, sizeof(key)); + bcopy(o, &key.o_id, sizeof(struct ber_oid)); + key.o_flags |= OID_KEY; /* do not match wildcards */ + + for (i = 0; i < o->bo_n; i++) { + key.o_oidlen = i + 1; + if (lookup != smi_oidl_numeric && + (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) { + snprintf(str, sizeof(str), "%s", value->o_name); + if (lookup == smi_oidl_short && i + 1 < o->bo_n) { + key.o_oidlen = i + 2; + if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL) + continue; + } + } else + snprintf(str, sizeof(str), "%d", key.o_oid[i]); + if (*buf != '\0' || i == 0) + strlcat(buf, ".", len); + strlcat(buf, str, len); + } + + return (buf); +} + +void +smi_mibtree(struct oid *oids) +{ + struct oid *oid, *decl; + size_t i; + + for (i = 0; oids[i].o_oid[0] != 0; i++) { + oid = &oids[i]; + if (oid->o_name != NULL) { + RB_INSERT(oidtree, &smi_oidtree, oid); + RB_INSERT(keytree, &smi_keytree, oid); + continue; + } + decl = RB_FIND(oidtree, &smi_oidtree, oid); + decl->o_flags = oid->o_flags; + decl->o_get = oid->o_get; + decl->o_set = oid->o_set; + decl->o_table = oid->o_table; + decl->o_val = oid->o_val; + decl->o_data = oid->o_data; + } +} + +struct oid * +smi_findkey(char *name) +{ + struct oid oid; + if (name == NULL) + return (NULL); + oid.o_name = name; + return (RB_FIND(keytree, &smi_keytree, &oid)); +} + +struct oid * +smi_foreach(struct oid *oid, u_int flags) +{ + /* + * Traverse the tree of MIBs with the option to check + * for specific OID flags. + */ + if (oid == NULL) { + oid = RB_MIN(oidtree, &smi_oidtree); + if (oid == NULL) + return (NULL); + if (flags == 0 || (oid->o_flags & flags)) + return (oid); + } + for (;;) { + oid = RB_NEXT(oidtree, &smi_oidtree, oid); + if (oid == NULL) + break; + if (flags == 0 || (oid->o_flags & flags)) + return (oid); + } + + return (oid); +} + +int +smi_oid_cmp(struct oid *a, struct oid *b) +{ + size_t i; + + for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) { + if (a->o_oid[i] != b->o_oid[i]) + return (a->o_oid[i] - b->o_oid[i]); + } + + /* + * Return success if the matched object is a table + * or a MIB registered by a subagent + * (it will match any sub-elements) + */ + if ((b->o_flags & OID_TABLE || + b->o_flags & OID_REGISTERED) && + (a->o_flags & OID_KEY) == 0 && + (a->o_oidlen > b->o_oidlen)) + return (0); + + return (a->o_oidlen - b->o_oidlen); +} + +int +smi_key_cmp(struct oid *a, struct oid *b) +{ + if (a->o_name == NULL || b->o_name == NULL) + return (-1); + return (strcasecmp(a->o_name, b->o_name)); +} + +RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp) +RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp) |