Index: contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt =================================================================== --- contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 214335) +++ contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (working copy) @@ -139,7 +139,8 @@ bits are defined: 0x00000001 - SNMPv1 - 0x00000002 - SNMPv2c" + 0x00000002 - SNMPv2c + 0x00000004 - SNMPv3" DEFVAL { 3 } ::= { begemotSnmpdConfig 5 } Index: contrib/bsnmp/snmpd/trans_udp.c =================================================================== --- contrib/bsnmp/snmpd/trans_udp.c (revision 214335) +++ contrib/bsnmp/snmpd/trans_udp.c (working copy) @@ -31,6 +31,7 @@ * UDP transport */ #include +#include #include #include Index: contrib/bsnmp/snmpd/bsnmpd.1 =================================================================== --- contrib/bsnmp/snmpd/bsnmpd.1 (revision 214335) +++ contrib/bsnmp/snmpd/bsnmpd.1 (working copy) @@ -31,7 +31,7 @@ .\" .\" $Begemot: bsnmp/snmpd/bsnmpd.1,v 1.12 2006/02/27 09:50:03 brandt_h Exp $ .\" -.Dd October 23, 2010 +.Dd September 9, 2010 .Dt BSNMPD 1 .Os .Sh NAME @@ -42,6 +42,7 @@ .Op Fl dh .Op Fl c Ar file .Op Fl D Ar options +.Op Fl e Ar file .Op Fl I Ar paths .Op Fl l Ar prefix .Op Fl m Ar variable Ns Op = Ns Ar value @@ -68,9 +69,11 @@ .Ar file as configuration file instead of the standard one. .It Fl D Ar options -Debugging options are specified as a comma separated string. +Debugging options are specified with a +.Fl o +flag followed by a comma separated string of options. The following options are available. -.Bl -tag -width "trace=level" +.Bl -tag -width ".It Cm trace Ns Cm = Ns Cm level" .It Cm dump Dump all sent and received PDUs to the terminal. .It Cm events @@ -80,8 +83,11 @@ .It Cm trace Ns Cm = Ns Cm level Set the snmp library trace flag to the specified value. +.El The value can be specified in the usual C-syntax for numbers. -.El +.It Fl e Ar file +Specify an alternate file where the agent's engine id and number of boots +are saved. .It Fl I Ar paths Specify a colon separated list of directories to search for configuration include files. @@ -246,6 +252,8 @@ .Aq prefix is .Dq snmpd . +.It Pa /var/ Ns Ao Ar prefix Ac Ns \&.engine +Default engine id file. .It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid Default pid file. .It Pa /etc:/usr/etc/:/usr/local/etc Index: contrib/bsnmp/snmpd/config.c =================================================================== --- contrib/bsnmp/snmpd/config.c (revision 214335) +++ contrib/bsnmp/snmpd/config.c (working copy) @@ -31,6 +31,7 @@ * Parse configuration file. */ #include +#include #include #include #include @@ -810,6 +811,7 @@ struct snmp_node *node; u_int i; u_char ip[4]; + struct asn_oid str_oid; for (node = tree; node < &tree[tree_size]; node++) if (strcmp(varname, node->name) == 0) @@ -824,7 +826,19 @@ report("subid too large %#"QUADXFMT, numval); if (oid->len == ASN_MAXOIDLEN) report("index too long"); - oid->subs[oid->len++] = numval; + if (gettoken() != ':') + oid->subs[oid->len++] = numval; + else { + str_oid.len = 0; + str_oid.subs[str_oid.len++] = numval; + while (gettoken() == TOK_NUM) { + str_oid.subs[str_oid.len++] = numval; + if (gettoken() != ':') + break; + } + oid->subs[oid->len++] = str_oid.len; + asn_append_oid(oid, &str_oid); + } } else if (token == TOK_STR) { if (strvallen + oid->len + 1 > ASN_MAXOIDLEN) @@ -832,6 +846,7 @@ oid->subs[oid->len++] = strvallen; for (i = 0; i < strvallen; i++) oid->subs[oid->len++] = strval[i]; + gettoken(); } else if (token == TOK_HOST) { gethost(strval, ip); @@ -839,10 +854,9 @@ report("index too long"); for (i = 0; i < 4; i++) oid->subs[oid->len++] = ip[i]; - + gettoken(); } else report("bad token in index"); - gettoken(); } return (node); @@ -1006,7 +1020,7 @@ node = parse_oid(varname, &vindex); if (token != '=') - report("'=' expected"); + report("'=' expected, got '%c'", token); gettoken(); if (ignore) { Index: contrib/bsnmp/snmpd/trap.c =================================================================== --- contrib/bsnmp/snmpd/trap.c (revision 214335) +++ contrib/bsnmp/snmpd/trap.c (working copy) @@ -31,6 +31,7 @@ * TrapSinkTable */ #include +#include #include #include #include Index: contrib/bsnmp/snmpd/tree.def =================================================================== --- contrib/bsnmp/snmpd/tree.def (revision 214335) +++ contrib/bsnmp/snmpd/tree.def (working copy) @@ -196,5 +196,15 @@ ) ) ) + (10 snmpFrameworkMIB + (2 snmpFrameworkMIBObjects + (1 snmpEngine + (1 snmpEngineID OCTETSTRING | SnmpEngineID op_snmp_engine GET) + (2 snmpEngineBoots INTEGER op_snmp_engine GET) + (3 snmpEngineTime INTEGER op_snmp_engine GET) + (4 snmpEngineMaxMessageSize INTEGER op_snmp_engine GET) + ) + ) + ) )) ) Index: contrib/bsnmp/snmpd/action.c =================================================================== --- contrib/bsnmp/snmpd/action.c (revision 214335) +++ contrib/bsnmp/snmpd/action.c (working copy) @@ -34,6 +34,7 @@ * Variable access for SNMPd */ #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include "snmpmod.h" @@ -162,8 +164,84 @@ return (0); } +/* + * Initialize global variables of the snmpEngine group. + */ +int +init_snmpd_engine(void) +{ + char *hostid; + snmpd_engine.engine_boots = 1; + snmpd_engine.engine_time = 1; + snmpd_engine.max_msg_size = 1500; /* XXX */ + snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80; + snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16; + snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8; + snmpd_engine.engine_id[3] = OID_freeBSD & 0xff; + snmpd_engine.engine_id[4] = 128; + snmpd_engine.engine_len = 5; + + if ((hostid = act_getkernint(KERN_HOSTID)) == NULL) + return (-1); + + if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) { + memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, + hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len); + snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ; + } else { + memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, + hostid, strlen(hostid)); + snmpd_engine.engine_len += strlen(hostid); + } + + free(hostid); + + return (0); +} + +int +set_snmpd_engine(void) +{ + FILE *fp; + uint32_t i; + uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; + uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2]; + + if (engine_file[0] == '\0') + return (-1); + + cptr = myengine; + for (i = 0; i < snmpd_engine.engine_len; i++) + cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]); + *cptr++ = '\n'; + *cptr++ = '\0'; + + if ((fp = fopen(engine_file, "r+")) != NULL) { + if (fgets(engine, sizeof(engine) - 1, fp) == NULL || + fscanf(fp, "%u", &snmpd_engine.engine_boots) <= 0) { + fclose(fp); + goto save_boots; + } + + fclose(fp); + if (strcmp(myengine, engine) != 0) + snmpd_engine.engine_boots = 1; + else + snmpd_engine.engine_boots++; + } else if (errno != ENOENT) + return (-1); + +save_boots: + if ((fp = fopen(engine_file, "w+")) == NULL) + return (-1); + fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots); + fclose(fp); + + return (0); +} + /************************************************************* * * System group @@ -980,6 +1058,103 @@ } /* + * SNMP Engine + */ +int +op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + asn_subid_t which = value->var.subs[sub - 1]; + + switch (op) { + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_GET: + break; + + case SNMP_OP_SET: + if (community != COMM_INITIALIZE) + return (SNMP_ERR_NOT_WRITEABLE); + switch (which) { + case LEAF_snmpEngineID: + if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ) + return (SNMP_ERR_WRONG_VALUE); + ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id, + snmpd_engine.engine_len); + ctx->scratch->int1 = snmpd_engine.engine_len; + snmpd_engine.engine_len = value->v.octetstring.len; + memcpy(snmpd_engine.engine_id, + value->v.octetstring.octets, + value->v.octetstring.len); + break; + + case LEAF_snmpEngineMaxMessageSize: + ctx->scratch->int1 = snmpd_engine.max_msg_size; + snmpd_engine.max_msg_size = value->v.integer; + break; + + default: + return (SNMP_ERR_NOT_WRITEABLE); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + switch (which) { + case LEAF_snmpEngineID: + snmpd_engine.engine_len = ctx->scratch->int1; + memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1, + snmpd_engine.engine_len); + free(ctx->scratch->ptr1); + break; + + case LEAF_snmpEngineMaxMessageSize: + snmpd_engine.max_msg_size = ctx->scratch->int1; + break; + + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if (which == LEAF_snmpEngineID) { + if (set_snmpd_engine() < 0) { + snmpd_engine.engine_len = ctx->scratch->int1; + memcpy(snmpd_engine.engine_id, + ctx->scratch->ptr1, ctx->scratch->int1); + } + free(ctx->scratch->ptr1); + } + return (SNMP_ERR_NOERROR); + } + + + switch (which) { + case LEAF_snmpEngineID: + return (string_get(value, snmpd_engine.engine_id, + snmpd_engine.engine_len)); + case LEAF_snmpEngineBoots: + value->v.integer = snmpd_engine.engine_boots; + break; + case LEAF_snmpEngineTime: + snmpd_engine.engine_time = (get_ticks() - start_tick) / 100ULL; + value->v.integer = snmpd_engine.engine_time; + break; + case LEAF_snmpEngineMaxMessageSize: + value->v.integer = snmpd_engine.max_msg_size; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +/* * Transport table */ int Index: contrib/bsnmp/snmpd/export.c =================================================================== --- contrib/bsnmp/snmpd/export.c (revision 214335) +++ contrib/bsnmp/snmpd/export.c (working copy) @@ -31,6 +31,7 @@ * Support functions for modules. */ #include +#include #include #include #include Index: contrib/bsnmp/snmpd/snmpmod.h =================================================================== --- contrib/bsnmp/snmpd/snmpmod.h (revision 214335) +++ contrib/bsnmp/snmpd/snmpmod.h (working copy) @@ -332,11 +332,137 @@ /* community for current packet */ extern u_int community; -/* +/* + * SNMP User-based Security Model data. Modified via the snmp_usm(3) module. + */ +struct snmpd_usmstat { + uint32_t unsupported_seclevels; + uint32_t not_in_time_windows; + uint32_t unknown_users; + uint32_t unknown_engine_ids; + uint32_t wrong_digests; + uint32_t decrypt_errors; +}; + +extern struct snmpd_usmstat snmpd_usmstats; +struct snmpd_usmstat *bsnmpd_get_usm_stats(void); +void bsnmpd_reset_usm_stats(void); + +struct usm_user { + struct snmp_user suser; + uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ]; + uint32_t user_engine_len; + char user_public[SNMP_ADM_STR32_SIZ]; + uint32_t user_public_len; + int32_t status; + int32_t type; + SLIST_ENTRY(usm_user) up; +}; + +SLIST_HEAD(usm_userlist, usm_user); +struct usm_user *usm_first_user(void); +struct usm_user *usm_next_user(struct usm_user *); +struct usm_user *usm_find_user(uint8_t *, uint32_t, char *); +struct usm_user *usm_new_user(uint8_t *, uint32_t, char *); +void usm_delete_user(struct usm_user *); +void usm_flush_users(void); + +/* USM user for current packet */ +extern struct usm_user *usm_user; + +/* + * SNMP View-based Access Control Model data. Modified via the snmp_vacm(3) module. + */ +struct vacm_group; + +struct vacm_user { + /* Security user name from USM */ + char secname[SNMP_ADM_STR32_SIZ]; + int32_t sec_model; + /* Back pointer to user assigned group name */ + struct vacm_group *group; + int32_t type; + int32_t status; + SLIST_ENTRY(vacm_user) vvu; + SLIST_ENTRY(vacm_user) vvg; +}; + +SLIST_HEAD(vacm_userlist, vacm_user); + +struct vacm_group { + char groupname[SNMP_ADM_STR32_SIZ]; + struct vacm_userlist group_users; + SLIST_ENTRY(vacm_group) vge; +}; + +SLIST_HEAD(vacm_grouplist, vacm_group); + +struct vacm_access { + /* The group name is index, not a column in the table */ + struct vacm_group *group; + char ctx_prefix[SNMP_ADM_STR32_SIZ]; + int32_t sec_model; + int32_t sec_level; + int32_t ctx_match; + struct vacm_view *read_view; + struct vacm_view *write_view; + struct vacm_view *notify_view; + int32_t type; + int32_t status; + TAILQ_ENTRY(vacm_access) vva; +}; + +TAILQ_HEAD(vacm_accesslist, vacm_access); + +struct vacm_view { + char viewname[SNMP_ADM_STR32_SIZ]; /* key */ + struct asn_oid subtree; /* key */ + uint8_t mask[16]; + uint8_t exclude; + int32_t type; + int32_t status; + SLIST_ENTRY(vacm_view) vvl; +}; + +SLIST_HEAD(vacm_viewlist, vacm_view); + +struct vacm_context { + /* The ID of the module that registered this context */ + int32_t regid; + char ctxname[SNMP_ADM_STR32_SIZ]; + SLIST_ENTRY(vacm_context) vcl; +}; + +SLIST_HEAD(vacm_contextlist, vacm_context); + +void vacm_groups_init(void); +struct vacm_user *vacm_first_user(void); +struct vacm_user *vacm_next_user(struct vacm_user *); +struct vacm_user *vacm_new_user(int32_t, char *); +int vacm_delete_user(struct vacm_user *); +int vacm_user_set_group(struct vacm_user *, u_char *, u_int); +struct vacm_access *vacm_first_access_rule(void); +struct vacm_access *vacm_next_access_rule(struct vacm_access *); +struct vacm_access *vacm_new_access_rule(char *, char *, int32_t, int32_t); +int vacm_delete_access_rule(struct vacm_access *); +struct vacm_view *vacm_first_view(void); +struct vacm_view *vacm_next_view(struct vacm_view *); +struct vacm_view *vacm_new_view(char *, struct asn_oid *); +int vacm_delete_view(struct vacm_view *); +struct vacm_context *vacm_first_context(void); +struct vacm_context *vacm_next_context(struct vacm_context *); +struct vacm_context *vacm_add_context(char *, int32_t); +void vacm_flush_contexts(int32_t); + +/* * Well known OIDs */ extern const struct asn_oid oid_zeroDotZero; +/* SNMPv3 Engine Discovery */ +extern const struct asn_oid oid_usmUnknownEngineIDs; +extern const struct asn_oid oid_usmNotInTimeWindows; + /* * Request ID ranges. * Index: contrib/bsnmp/snmpd/trans_lsock.c =================================================================== --- contrib/bsnmp/snmpd/trans_lsock.c (revision 214335) +++ contrib/bsnmp/snmpd/trans_lsock.c (working copy) @@ -31,6 +31,7 @@ * Local domain socket transport */ #include +#include #include #include Index: contrib/bsnmp/snmpd/main.c =================================================================== --- contrib/bsnmp/snmpd/main.c (revision 214335) +++ contrib/bsnmp/snmpd/main.c (working copy) @@ -30,6 +30,8 @@ * * SNMPd main stuff. */ + +#include #include #include #include @@ -60,6 +62,7 @@ #define PATH_PID "/var/run/%s.pid" #define PATH_CONFIG "/etc/%s.config" +#define PATH_ENGINE "/var/%s.engine" uint64_t this_tick; /* start of processing of current packet (absolute) */ uint64_t start_tick; /* start of processing */ @@ -87,6 +90,11 @@ }; struct snmpd_stats snmpd_stats; +struct snmpd_usmstat snmpd_usmstats; + +/* snmpEngine */ +struct snmp_engine snmpd_engine; + /* snmpSerialNo */ int32_t snmp_serial_no; @@ -102,6 +110,29 @@ /* list of all known communities */ struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list); +/* list of all known USM users */ +struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist); + +/* A list of all VACM users configured, including v1, v2c and v3 */ +struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist); + +/* A list of all VACM groups */ +struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist); + +static struct vacm_group vacm_default_group = { + .groupname = "", +}; + +/* The list of configured access entries */ +struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist); + +/* The list of configured views */ +struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist); + +/* The list of configured contexts */ +struct vacm_contextlist vacm_contextlist = + SLIST_HEAD_INITIALIZER(vacm_contextlist); + /* list of all installed object resources */ struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list); @@ -128,9 +159,13 @@ u_int community; static struct community *comm; +/* current USM user */ +struct usm_user *usm_user; + /* file names */ static char config_file[MAXPATHLEN + 1]; static char pid_file[MAXPATHLEN + 1]; +char engine_file[MAXPATHLEN + 1]; #ifndef USE_LIBBEGEMOT /* event context */ @@ -154,6 +189,12 @@ const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }}; +const struct asn_oid oid_usmUnknownEngineIDs = + { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}}; + +const struct asn_oid oid_usmNotInTimeWindows = + { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}}; + /* request id generator for traps */ u_int trap_reqid; @@ -161,13 +202,15 @@ static const char usgtxt[] = "\ Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\ Open Communication Systems (FhG Fokus). All rights reserved.\n\ -usage: snmpd [-dh] [-c file] [-D options] [-I path] [-l prefix]\n\ - [-m variable=value] [-p file]\n\ +Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\ +usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\ + [-l prefix] [-m variable=value] [-p file]\n\ options:\n\ -d don't daemonize\n\ -h print this info\n\ -c file specify configuration file\n\ -D options debugging options\n\ + -e file specify engine id file\n\ -I path system include path\n\ -l prefix default basename for pid and config file\n\ -m var=val define variable\n\ @@ -243,8 +286,192 @@ } /* - * SNMP input. Start: decode the PDU, find the community. + * Check USM PDU header credentials against local SNMP Engine & users. */ +static enum snmp_code +snmp_pdu_auth_user(struct snmp_pdu *pdu) +{ + uint64_t etime; + usm_user = NULL; + + /* un-authenticated snmpEngineId discovery */ + if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) { + pdu->engine.engine_len = snmpd_engine.engine_len; + memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, + snmpd_engine.engine_len); + pdu->engine.engine_boots = snmpd_engine.engine_boots; + pdu->engine.engine_time = snmpd_engine.engine_time; + pdu->flags |= SNMP_MSG_AUTODISCOVER; + return (SNMP_CODE_OK); + } + + if ((usm_user = usm_find_user(pdu->engine.engine_id, + pdu->engine.engine_len, pdu->user.sec_name)) == NULL || + usm_user->status != 1 /* active */) + return (SNMP_CODE_BADUSER); + + if (usm_user->user_engine_len != snmpd_engine.engine_len || + memcmp(usm_user->user_engine_id, snmpd_engine.engine_id, + snmpd_engine.engine_len) != 0) + return (SNMP_CODE_BADENGINE); + + pdu->user.priv_proto = usm_user->suser.priv_proto; + memcpy(pdu->user.priv_key, usm_user->suser.priv_key, + sizeof(pdu->user.priv_key)); + + /* authenticated snmpEngineId discovery */ + if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { + etime = (get_ticks() - start_tick) / 100ULL; + if (etime < INT32_MAX) + snmpd_engine.engine_time = etime; + else { + start_tick = get_ticks(); + set_snmpd_engine(); + snmpd_engine.engine_time = start_tick; + } + + pdu->user.auth_proto = usm_user->suser.auth_proto; + memcpy(pdu->user.auth_key, usm_user->suser.auth_key, + sizeof(pdu->user.auth_key)); + + if (pdu->engine.engine_boots == 0 && + pdu->engine.engine_time == 0) { + pdu->flags |= SNMP_MSG_AUTODISCOVER; + return (SNMP_CODE_OK); + } + + if (pdu->engine.engine_boots != snmpd_engine.engine_boots || + abs(pdu->engine.engine_time - snmpd_engine.engine_time) > + SNMP_TIME_WINDOW) + return (SNMP_CODE_NOTINTIME); + } + + if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && + (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) || + ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 && + usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) || + ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 && + usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV)) + return (SNMP_CODE_BADSECLEVEL); + + return (SNMP_CODE_OK); +} + +/* + * Check whether access to each of var bindings in the PDU is allowed based + * on the user credentials against the configured User groups & VACM views. + */ +static enum snmp_code +snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip) +{ + const char *uname; + int32_t suboid, smodel; + uint32_t i; + struct vacm_user *vuser; + struct vacm_access *acl; + struct vacm_context *vacmctx; + struct vacm_view *view; + + /* + * At least a default context exists if the snmpd_vacm(3) module is + * running. + */ + if (SLIST_EMPTY(&vacm_contextlist) || + (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0) + return (SNMP_CODE_OK); + + switch (pdu->version) { + case SNMP_V1: + if ((uname = comm_string(community)) == NULL) + return (SNMP_CODE_FAILED); + smodel = SNMP_SECMODEL_SNMPv1; + break; + + case SNMP_V2c: + if ((uname = comm_string(community)) == NULL) + return (SNMP_CODE_FAILED); + smodel = SNMP_SECMODEL_SNMPv2c; + break; + + case SNMP_V3: + uname = pdu->user.sec_name; + if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM) + return (SNMP_CODE_FAILED); + /* Compare the PDU context engine id against the agent's */ + if (pdu->context_engine_len != snmpd_engine.engine_len || + memcmp(pdu->context_engine, snmpd_engine.engine_id, + snmpd_engine.engine_len) != 0) + return (SNMP_CODE_FAILED); + break; + + default: + abort(); + } + + SLIST_FOREACH(vuser, &vacm_userlist, vvu) + if (strcmp(uname, vuser->secname) == 0 && + vuser->sec_model == smodel) + break; + + if (vuser == NULL || vuser->group == NULL) + return (SNMP_CODE_FAILED); + + /* XXX: shteryana - recheck */ + TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) { + if (acl->group != vuser->group) + continue; + SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl) + if (memcmp(vacmctx->ctxname, acl->ctx_prefix, + acl->ctx_match) == 0) + goto match; + } + + return (SNMP_CODE_FAILED); + +match: + + switch (pdu->type) { + case SNMP_PDU_GET: + case SNMP_PDU_GETNEXT: + case SNMP_PDU_GETBULK: + if ((view = acl->read_view) == NULL) + return (SNMP_CODE_FAILED); + break; + + case SNMP_PDU_SET: + if ((view = acl->write_view) == NULL) + return (SNMP_CODE_FAILED); + break; + + case SNMP_PDU_TRAP: + case SNMP_PDU_INFORM: + case SNMP_PDU_TRAP2: + case SNMP_PDU_REPORT: + if ((view = acl->notify_view) == NULL) + return (SNMP_CODE_FAILED); + break; + case SNMP_PDU_RESPONSE: + /* NOTREACHED */ + return (SNMP_CODE_FAILED); + default: + abort(); + } + + for (i = 0; i < pdu->nbindings; i++) { + /* XXX - view->mask*/ + suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var); + if ((!suboid && !view->exclude) || (suboid && view->exclude)) { + *ip = i + 1; + return (SNMP_CODE_FAILED); + } + } + + return (SNMP_CODE_OK); +} + +/* + * SNMP input. Start: decode the PDU, find the user or community. + */ enum snmpd_input_err snmp_input_start(const u_char *buf, size_t len, const char *source, struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen) @@ -254,6 +481,9 @@ enum snmpd_input_err ret; int sret; + /* update uptime */ + this_tick = get_ticks(); + b.asn_cptr = buf; b.asn_len = len; @@ -269,11 +499,27 @@ } b.asn_len = *pdulen = (size_t)sret; - code = snmp_pdu_decode(&b, pdu, ip); + memset(pdu, 0, sizeof(*pdu)); + if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK) + goto decoded; + if (pdu->version == SNMP_V3) { + if (pdu->security_model != SNMP_SECMODEL_USM) { + code = SNMP_CODE_FAILED; + goto decoded; + } + if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK) + goto decoded; + if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK) + goto decoded; + } + code = snmp_pdu_decode_scoped(&b, pdu, ip); + + ret = SNMPD_INPUT_OK; + +decoded: snmpd_stats.inPkts++; - ret = SNMPD_INPUT_OK; switch (code) { case SNMP_CODE_FAILED: @@ -300,6 +546,30 @@ ret = SNMPD_INPUT_VALBADENC; break; + case SNMP_CODE_BADSECLEVEL: + snmpd_usmstats.unsupported_seclevels++; + return (SNMPD_INPUT_FAILED); + + case SNMP_CODE_NOTINTIME: + snmpd_usmstats.not_in_time_windows++; + return (SNMPD_INPUT_FAILED); + + case SNMP_CODE_BADUSER: + snmpd_usmstats.unknown_users++; + return (SNMPD_INPUT_FAILED); + + case SNMP_CODE_BADENGINE: + snmpd_usmstats.unknown_engine_ids++; + return (SNMPD_INPUT_FAILED); + + case SNMP_CODE_BADDIGEST: + snmpd_usmstats.wrong_digests++; + return (SNMPD_INPUT_FAILED); + + case SNMP_CODE_EDECRYPT: + snmpd_usmstats.decrypt_errors++; + return (SNMPD_INPUT_FAILED); + case SNMP_CODE_OK: switch (pdu->version) { @@ -313,6 +583,11 @@ goto bad_vers; break; + case SNMP_V3: + if (!(snmpd.version_enable & VERS_ENABLE_V3)) + goto bad_vers; + break; + case SNMP_Verr: goto bad_vers; } @@ -325,26 +600,48 @@ } /* - * Look, whether we know the community + * Look, whether we know the community or user */ - TAILQ_FOREACH(comm, &community_list, link) - if (comm->string != NULL && - strcmp(comm->string, pdu->community) == 0) - break; - if (comm == NULL) { - snmpd_stats.inBadCommunityNames++; - snmp_pdu_free(pdu); - if (snmpd.auth_traps) - snmp_send_trap(&oid_authenticationFailure, - (struct snmp_value *)NULL); - ret = SNMPD_INPUT_BAD_COMM; - } else - community = comm->value; + if (pdu->version != SNMP_V3) { + TAILQ_FOREACH(comm, &community_list, link) + if (comm->string != NULL && + strcmp(comm->string, pdu->community) == 0) + break; - /* update uptime */ - this_tick = get_ticks(); + if (comm == NULL) { + snmpd_stats.inBadCommunityNames++; + snmp_pdu_free(pdu); + if (snmpd.auth_traps) + snmp_send_trap(&oid_authenticationFailure, + (struct snmp_value *)NULL); + ret = SNMPD_INPUT_BAD_COMM; + } else + community = comm->value; + } else if (pdu->nbindings == 0) { + /* RFC 3414 - snmpEngineID Discovery */ + if (strlen(pdu->user.sec_name) == 0) { + asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), + &oid_usmUnknownEngineIDs); + pdu->context_engine_len = snmpd_engine.engine_len; + memcpy(pdu->context_engine, snmpd_engine.engine_id, + snmpd_engine.engine_len); + } else if (pdu->engine.engine_boots == 0 && + pdu->engine.engine_time == 0) { + asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), + &oid_usmNotInTimeWindows); + pdu->engine.engine_boots = snmpd_engine.engine_boots; + pdu->engine.engine_time = snmpd_engine.engine_time; + } + } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH && + (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) { + snmpd_usmstats.not_in_time_windows++; + ret = SNMP_CODE_FAILED; + } + if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK) + ret = SNMP_CODE_FAILED; + return (ret); } @@ -960,7 +1257,8 @@ * If that is a module community and the module has a proxy function, * the hand it over to the module. */ - if (comm->owner != NULL && comm->owner->config->proxy != NULL) { + if (comm != NULL && comm->owner != NULL && + comm->owner->config->proxy != NULL) { perr = (*comm->owner->config->proxy)(&pdu, tport->transport, &tport->index, pi->peer, pi->peerlen, ierr, vi, !pi->cred || pi->priv); @@ -1016,9 +1314,10 @@ /* * Check community */ - if ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) || + if (pdu.version < SNMP_V3 && + ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) || (community != COMM_WRITE && - (pdu.type == SNMP_PDU_SET || community != COMM_READ))) { + (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) { snmpd_stats.inBadCommunityUses++; snmp_pdu_free(&pdu); snmp_input_consume(pi); @@ -1337,7 +1636,7 @@ struct tport *p; const char *prefix = "snmpd"; struct lmodule *m; - char *value, *option; + char *value = NULL, *option; /* XXX */ struct transport *t; #define DBG_DUMP 0 @@ -1355,7 +1654,7 @@ snmp_debug = snmp_debug_func; asn_error = asn_error_func; - while ((opt = getopt(argc, argv, "c:dD:hI:l:m:p:")) != EOF) + while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF) switch (opt) { case 'c': @@ -1401,6 +1700,9 @@ } break; + case 'e': + strlcpy(engine_file, optarg, sizeof(engine_file)); + break; case 'h': fprintf(stderr, "%s", usgtxt); exit(0); @@ -1471,6 +1773,7 @@ snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix); init_actvals(); + init_snmpd_engine(); this_tick = get_ticks(); start_tick = this_tick; @@ -1497,6 +1800,9 @@ evSetDebug(evctx, 10, stderr); #endif + if (engine_file[0] == '\0') + snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix); + if (read_config(config_file, NULL)) { syslog(LOG_ERR, "error in config file"); exit(1); @@ -1601,7 +1907,7 @@ } uint64_t -get_ticks() +get_ticks(void) { struct timeval tv; uint64_t ret; @@ -2374,3 +2680,569 @@ return; } } + +/* + * RFC 3414 User-based Security Model support + */ + +struct snmpd_usmstat * +bsnmpd_get_usm_stats(void) +{ + return (&snmpd_usmstats); +} + +void +bsnmpd_reset_usm_stats(void) +{ + memset(&snmpd_usmstats, 0, sizeof(&snmpd_usmstats)); +} + +struct usm_user * +usm_first_user(void) +{ + return (SLIST_FIRST(&usm_userlist)); +} + +struct usm_user * +usm_next_user(struct usm_user *uuser) +{ + if (uuser == NULL) + return (NULL); + + return (SLIST_NEXT(uuser, up)); +} + +struct usm_user * +usm_find_user(uint8_t *engine, uint32_t elen, char *uname) +{ + struct usm_user *uuser; + + SLIST_FOREACH(uuser, &usm_userlist, up) + if (uuser->user_engine_len == elen && + memcmp(uuser->user_engine_id, engine, elen) == 0 && + strlen(uuser->suser.sec_name) == strlen(uname) && + strcmp(uuser->suser.sec_name, uname) == 0) + break; + + return (uuser); +} + +static int +usm_compare_user(struct usm_user *u1, struct usm_user *u2) +{ + uint32_t i; + + if (u1->user_engine_len < u2->user_engine_len) + return (-1); + if (u1->user_engine_len > u2->user_engine_len) + return (1); + + for (i = 0; i < u1->user_engine_len; i++) { + if (u1->user_engine_id[i] < u2->user_engine_id[i]) + return (-1); + if (u1->user_engine_id[i] > u2->user_engine_id[i]) + return (1); + } + + if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name)) + return (-1); + if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name)) + return (1); + + for (i = 0; i < strlen(u1->suser.sec_name); i++) { + if (u1->suser.sec_name[i] < u2->suser.sec_name[i]) + return (-1); + if (u1->suser.sec_name[i] > u2->suser.sec_name[i]) + return (1); + } + + return (0); +} + +struct usm_user * +usm_new_user(uint8_t *eid, uint32_t elen, char *uname) +{ + int cmp; + struct usm_user *uuser, *temp, *prev; + + for (uuser = usm_first_user(); uuser != NULL; + (uuser = usm_next_user(uuser))) { + if (uuser->user_engine_len == elen && + strlen(uname) == strlen(uuser->suser.sec_name) && + strcmp(uname, uuser->suser.sec_name) == 0 && + memcmp(eid, uuser->user_engine_id, elen) == 0) + return (NULL); + } + + if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL) + return (NULL); + + memset(uuser, 0, sizeof(struct usm_user)); + strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ); + memcpy(uuser->user_engine_id, eid, elen); + uuser->user_engine_len = elen; + + if ((prev = SLIST_FIRST(&usm_userlist)) == NULL || + usm_compare_user(uuser, prev) < 0) { + SLIST_INSERT_HEAD(&usm_userlist, uuser, up); + return (uuser); + } + + SLIST_FOREACH(temp, &usm_userlist, up) { + if ((cmp = usm_compare_user(uuser, temp)) <= 0) + break; + prev = temp; + } + + if (temp == NULL || cmp < 0) + SLIST_INSERT_AFTER(prev, uuser, up); + else if (cmp > 0) + SLIST_INSERT_AFTER(temp, uuser, up); + else { + syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name); + free(uuser); + return (NULL); + } + + return (uuser); +} + +void +usm_delete_user(struct usm_user *uuser) +{ + SLIST_REMOVE(&usm_userlist, uuser, usm_user, up); + free(uuser); +} + +void +usm_flush_users(void) +{ + struct usm_user *uuser; + + while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) { + SLIST_REMOVE_HEAD(&usm_userlist, up); + free(uuser); + } + + SLIST_INIT(&usm_userlist); +} + +/* + * RFC 3415 View-based Access Control Model support + */ +struct vacm_user * +vacm_first_user(void) +{ + return (SLIST_FIRST(&vacm_userlist)); +} + +struct vacm_user * +vacm_next_user(struct vacm_user *vuser) +{ + if (vuser == NULL) + return (NULL); + + return (SLIST_NEXT(vuser, vvu)); +} + +static int +vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2) +{ + uint32_t i; + + if (v1->sec_model < v2->sec_model) + return (-1); + if (v1->sec_model > v2->sec_model) + return (1); + + if (strlen(v1->secname) < strlen(v2->secname)) + return (-1); + if (strlen(v1->secname) > strlen(v2->secname)) + return (1); + + for (i = 0; i < strlen(v1->secname); i++) { + if (v1->secname[i] < v2->secname[i]) + return (-1); + if (v1->secname[i] > v2->secname[i]) + return (1); + } + + return (0); +} + +struct vacm_user * +vacm_new_user(int32_t smodel, char *uname) +{ + int cmp; + struct vacm_user *user, *temp, *prev; + + SLIST_FOREACH(user, &vacm_userlist, vvu) + if (strcmp(uname, user->secname) == 0 && + smodel == user->sec_model) + return (NULL); + + if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL) + return (NULL); + + memset(user, 0, sizeof(*user)); + /* XXX: - shteryana insert sorted ? */ + user->group = &vacm_default_group; + SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg); + user->sec_model = smodel; + strlcpy(user->secname, uname, sizeof(user->secname)); + + if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL || + vacm_compare_user(user, prev) < 0) { + SLIST_INSERT_HEAD(&vacm_userlist, user, vvu); + return (user); + } + + SLIST_FOREACH(temp, &vacm_userlist, vvu) { + if ((cmp = vacm_compare_user(user, temp)) <= 0) + break; + prev = temp; + } + + if (temp == NULL || cmp < 0) + SLIST_INSERT_AFTER(prev, user, vvu); + else if (cmp > 0) + SLIST_INSERT_AFTER(temp, user, vvu); + else { + syslog(LOG_ERR, "User %s exists", user->secname); + free(user); + return (NULL); + } + + return (user); +} + +int +vacm_delete_user(struct vacm_user *user) +{ + if (user->group != NULL && user->group != &vacm_default_group) { + SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); + if (SLIST_EMPTY(&user->group->group_users)) { + SLIST_REMOVE(&vacm_grouplist, user->group, + vacm_group, vge); + free(user->group); + } + } + + SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu); + free(user); + + return (0); +} + +int +vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len) +{ + struct vacm_group *group; + + if (len >= SNMP_ADM_STR32_SIZ) + return (-1); + + SLIST_FOREACH(group, &vacm_grouplist, vge) + if (strlen(group->groupname) == len && + memcmp(octets, group->groupname, len) == 0) + break; + + if (group == NULL) { + if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL) + return (-1); + memset(group, 0, sizeof(*group)); + memcpy(group->groupname, octets, len); + group->groupname[len] = '\0'; + SLIST_INSERT_HEAD(&vacm_grouplist, group, vge); + } + + /* XXX: - shteryana insert sorted ? */ + SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); + SLIST_INSERT_HEAD(&group->group_users, user, vvg); + user->group = group; + + return (0); +} + +void +vacm_groups_init(void) +{ + SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge); +} + +struct vacm_access * +vacm_first_access_rule(void) +{ + return (TAILQ_FIRST(&vacm_accesslist)); +} + +struct vacm_access * +vacm_next_access_rule(struct vacm_access *acl) +{ + if (acl == NULL) + return (NULL); + + return (TAILQ_NEXT(acl, vva)); +} + +static int +vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2) +{ + uint32_t i; + + if (strlen(v1->group->groupname) < strlen(v2->group->groupname)) + return (-1); + if (strlen(v1->group->groupname) > strlen(v2->group->groupname)) + return (1); + + for (i = 0; i < strlen(v1->group->groupname); i++) { + if (v1->group->groupname[i] < v2->group->groupname[i]) + return (-1); + if (v1->group->groupname[i] > v2->group->groupname[i]) + return (1); + } + + if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix)) + return (-1); + if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix)) + return (1); + + for (i = 0; i < strlen(v1->ctx_prefix); i++) { + if (v1->ctx_prefix[i] < v2->ctx_prefix[i]) + return (-1); + if (v1->ctx_prefix[i] > v2->ctx_prefix[i]) + return (1); + } + + if (v1->sec_model < v2->sec_model) + return (-1); + if (v1->sec_model > v2->sec_model) + return (1); + + if (v1->sec_level < v2->sec_level) + return (-1); + if (v1->sec_level > v2->sec_level) + return (1); + + return (0); +} + +struct vacm_access * +vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel) +{ + struct vacm_group *group; + struct vacm_access *acl, *temp; + + TAILQ_FOREACH(acl, &vacm_accesslist, vva) { + if (acl->group == NULL) + continue; /* XXX: shteryana ?*/ + if (strcmp(gname, acl->group->groupname) == 0 && + strcmp(cprefix, acl->ctx_prefix) == 0 && + acl->sec_model == smodel && acl->sec_level == slevel) + return (NULL); + } + + /* Make sure the group exists */ + SLIST_FOREACH(group, &vacm_grouplist, vge) + if (strcmp(gname, group->groupname) == 0) + break; + + if (group == NULL) + return (NULL); + + if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL) + return (NULL); + + memset(acl, 0, sizeof(*acl)); + acl->group = group; + strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix)); + acl->sec_model = smodel; + acl->sec_level = slevel; + + if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL || + vacm_compare_access_rule(acl, temp) < 0) { + TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva); + return (acl); + } + + TAILQ_FOREACH(temp, &vacm_accesslist, vva) + if (vacm_compare_access_rule(acl, temp) < 0) { + TAILQ_INSERT_BEFORE(temp, acl, vva); + return (acl); + } + + TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva); + + return (acl); +} + +int +vacm_delete_access_rule(struct vacm_access *acl) +{ + TAILQ_REMOVE(&vacm_accesslist, acl, vva); + free(acl); + + return (0); +} + +struct vacm_view * +vacm_first_view(void) +{ + return (SLIST_FIRST(&vacm_viewlist)); +} + +struct vacm_view * +vacm_next_view(struct vacm_view *view) +{ + if (view == NULL) + return (NULL); + + return (SLIST_NEXT(view, vvl)); +} + +static int +vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2) +{ + uint32_t i; + + if (strlen(v1->viewname) < strlen(v2->viewname)) + return (-1); + if (strlen(v1->viewname) > strlen(v2->viewname)) + return (1); + + for (i = 0; i < strlen(v1->viewname); i++) { + if (v1->viewname[i] < v2->viewname[i]) + return (-1); + if (v1->viewname[i] > v2->viewname[i]) + return (1); + } + + return (asn_compare_oid(&v1->subtree, &v2->subtree)); +} + +struct vacm_view * +vacm_new_view(char *vname, struct asn_oid *oid) +{ + int cmp; + struct vacm_view *view, *temp, *prev; + + SLIST_FOREACH(view, &vacm_viewlist, vvl) + if (strcmp(vname, view->viewname) == 0) + return (NULL); + + if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL) + return (NULL); + + memset(view, 0, sizeof(*view)); + strlcpy(view->viewname, vname, sizeof(view->viewname)); + asn_append_oid(&view->subtree, oid); + + if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL || + vacm_compare_view(view, prev) < 0) { + SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl); + return (view); + } + + SLIST_FOREACH(temp, &vacm_viewlist, vvl) { + if ((cmp = vacm_compare_view(view, temp)) <= 0) + break; + prev = temp; + } + + if (temp == NULL || cmp < 0) + SLIST_INSERT_AFTER(prev, view, vvl); + else if (cmp > 0) + SLIST_INSERT_AFTER(temp, view, vvl); + else { + syslog(LOG_ERR, "View %s exists", view->viewname); + free(view); + return (NULL); + } + + return (view); +} + +int +vacm_delete_view(struct vacm_view *view) +{ + /* XXX: shteryana - sanity checks - ref count ?*/ + SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl); + free(view); + + return (0); +} + +struct vacm_context * +vacm_first_context(void) +{ + return (SLIST_FIRST(&vacm_contextlist)); +} + +struct vacm_context * +vacm_next_context(struct vacm_context *vacmctx) +{ + if (vacmctx == NULL) + return (NULL); + + return (SLIST_NEXT(vacmctx, vcl)); +} + +struct vacm_context * +vacm_add_context(char *ctxname, int regid) +{ + int cmp; + struct vacm_context *ctx, *temp, *prev; + + SLIST_FOREACH(ctx, &vacm_contextlist, vcl) + if (strcmp(ctxname, ctx->ctxname) == 0) { + syslog(LOG_ERR, "Context %s exists", ctx->ctxname); + return (NULL); + } + + if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL) + return (NULL); + + memset(ctx, 0, sizeof(*ctx)); + strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname)); + ctx->regid = regid; + + if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL || + strlen(ctx->ctxname) < strlen(prev->ctxname) || + strcmp(ctx->ctxname, prev->ctxname) < 0) { + SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl); + return (ctx); + } + + SLIST_FOREACH(temp, &vacm_contextlist, vcl) { + if (strlen(ctx->ctxname) < strlen(temp->ctxname) || + strcmp(ctx->ctxname, temp->ctxname) < 0) { + cmp = -1; + break; + } + prev = temp; + } + + if (temp == NULL || cmp < 0) + SLIST_INSERT_AFTER(prev, ctx, vcl); + else if (cmp > 0) + SLIST_INSERT_AFTER(temp, ctx, vcl); + else { + syslog(LOG_ERR, "Context %s exists", ctx->ctxname); + free(ctx); + return (NULL); + } + + return (ctx); +} + +void +vacm_flush_contexts(int regid) +{ + struct vacm_context *ctx, *temp; + + SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp) + if (ctx->regid == regid) { + SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl); + free(ctx); + } +} Index: contrib/bsnmp/snmpd/snmpd.h =================================================================== --- contrib/bsnmp/snmpd/snmpd.h (revision 214335) +++ contrib/bsnmp/snmpd/snmpd.h (working copy) @@ -30,7 +30,7 @@ * * Private SNMPd data and functions. */ -#include + #ifdef USE_LIBBEGEMOT #include #else @@ -247,7 +247,8 @@ #define VERS_ENABLE_V1 0x00000001 #define VERS_ENABLE_V2C 0x00000002 -#define VERS_ENABLE_ALL 0x00000003 +#define VERS_ENABLE_V3 0x00000004 +#define VERS_ENABLE_ALL (VERS_ENABLE_V1 | VERS_ENABLE_V2C | VERS_ENABLE_V3) /* * The debug group @@ -280,6 +281,11 @@ extern struct snmpd_stats snmpd_stats; /* + * SNMPd Engine + */ +extern struct snmp_engine snmpd_engine; + +/* * OR Table */ struct objres { @@ -322,6 +328,11 @@ extern int32_t snmp_serial_no; int init_actvals(void); + +extern char engine_file[]; +int init_snmpd_engine(void); +int set_snmpd_engine(void); + int read_config(const char *, struct lmodule *); int define_macro(const char *name, const char *value); Index: contrib/bsnmp/snmpd/snmpmod.3 =================================================================== --- contrib/bsnmp/snmpd/snmpmod.3 (revision 214335) +++ contrib/bsnmp/snmpd/snmpmod.3 (working copy) @@ -31,7 +31,7 @@ .\" .\" $Begemot: bsnmp/snmpd/snmpmod.3,v 1.14 2005/10/04 13:30:35 brandt_h Exp $ .\" -.Dd February 27, 2006 +.Dd September 9, 2010 .Dt SNMPMOD 3 .Os .Sh NAME @@ -60,6 +60,8 @@ .Nm comm_define , .Nm community , .Nm oid_zeroDotZero , +.Nm oid_usmUnknownEngineIDs , +.Nm oid_usmNotInTimeWindows , .Nm reqid_allocate , .Nm reqid_next , .Nm reqid_base , @@ -99,7 +101,16 @@ .Nm index_compare , .Nm index_compare_off , .Nm index_append , -.Nm index_append_off +.Nm index_append_off, +.Nm bsnmpd_get_usm_stats, +.Nm bsnmpd_reset_usm_stats, +.Nm usm_first_user, +.Nm usm_next_user, +.Nm usm_find_user, +.Nm usm_new_user, +.Nm usm_delete_user, +.Nm usm_flush_users, +.Nm usm_user .Nd "SNMP daemon loadable module interface" .Sh LIBRARY Begemot SNMP library @@ -228,6 +239,25 @@ .Fn index_append "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src" .Ft void .Fn index_append_off "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src" "u_int off" +.Ft struct snmpd_usmstat * +.Fn bsnmpd_get_usm_stats "void" +.Ft void +.Fn bsnmpd_reset_usm_stats "void" +.Ft struct usm_user * +.Fn usm_first_user "void" +.Ft struct usm_user * +.Fn usm_next_user "struct usm_user *uuser" +.Ft struct usm_user * +.Fn usm_find_user "uint8_t *engine" "uint32_t elen" "char *uname" +.Ft struct usm_user * +.Fn usm_new_user "uint8_t *engine" "uint32_t elen" "char *uname" +.Ft void +.Fn usm_delete_user "struct usm_user *" +.Ft void +.Fn usm_flush_users "void" +.Vt extern struct usm_user *usm_user; +.Vt extern const struct asn_oid oid_usmUnknownEngineIDs; +.Vt extern const struct asn_oid oid_usmNotInTimeWindows; .Sh DESCRIPTION The .Xr bsnmpd 1 @@ -539,7 +569,7 @@ .El .Pp The function returns a globally unique community identifier. -If a PDU is +If a SNMPv1 or SNMPv2 PDU is received who's community string matches, this identifier is set into the global .Va community . .Pp @@ -549,10 +579,76 @@ .Pp All communities defined by a module are automatically released when the module is unloaded. +.Ss THE USER-BASED SECURITY GROUP +The scalar statistics of the USM group are held in the global variable +.Va snmpd_usmstats : +.Bd -literal -offset indent +struct snmpd_usmstat { + uint32_t unsupported_seclevels; + uint32_t not_in_time_windows; + uint32_t unknown_users; + uint32_t unknown_engine_ids; + uint32_t wrong_digests; + uint32_t decrypt_errors; +}; +.Ed +.Fn bsnmpd_get_usm_stats +returns a pointer to the global structure containing the statistics. +.Fn bsnmpd_reset_usm_stats +clears the statistics of the USM group. +.Pp +A global list of configured USM users is maintained by the daemon. +.Bd -literal -offset indent +struct usm_user { + struct snmp_user suser; + uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ]; + uint32_t user_engine_len; + char user_public[SNMP_USM_NAME_SIZ]; + uint32_t user_public_len; + int32_t status; + int32_t type; + SLIST_ENTRY(usm_user) up; +}; +.Ed +This structure represents an USM user. The daemon only responds to SNMPv3 PDUs +with user credentials matching an USM user entry in its global list. +If a SNMPv3 PDU is received, whose security model is USM, the global +.Va usm_user +is set to point at the user entry that matches the credentials contained in +the PDU. +However, the daemon does not create or remove USM users, it gives an interface +to external loadable module(s) to manage the list. +.Fn usm_new_user +adds an user entry in the list, and +.Fn usm_delete_user +deletes an existing entry from the list. +.Fn usm_flush_users +is used to remove all configured USM users. +.Fn usm_first_user +will return the first user in the list, or +.Li NULL +if the list is empty. +.Fn usm_next_user +will return the next user of a given entry if one exists, or +.Li NULL . +The list is sorted according to the USM user name and Engine ID. +.Fn usm_find_user +returns the USM user entry matching the given +.Fa engine +and +.Fa uname +or +.Li NULL +if an user with the specified name and engine id is not present in the list. .Ss WELL KNOWN OIDS The global variable .Va oid_zeroDotZero contains the OID 0.0. +The global variables +.Va oid_usmUnknownEngineIDs +.Va oid_usmNotInTimeWindows +contains the OIDs 1.3.6.1.6.3.15.1.1.4.0 and 1.3.6.1.6.3.15.1.1.2.0 used +in the SNMPv3 USM Engine Discovery. .Ss REQUEST ID RANGES For modules that implement SNMP client functions besides SNMP agent functions it may be necessary to identify SNMP requests by their identifier to allow Index: contrib/bsnmp/snmp_vacm/vacm_tree.def =================================================================== --- contrib/bsnmp/snmp_vacm/vacm_tree.def (revision 0) +++ contrib/bsnmp/snmp_vacm/vacm_tree.def (revision 0) @@ -0,0 +1,104 @@ +#- +# Copyright (C) 2010 The FreeBSD Foundation +# All rights reserved. +# +# This software was developed by Shteryana Sotirova Shopova under +# sponsorship from the FreeBSD Foundation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +typedef StorageType ENUM ( + 1 other + 2 volatile + 3 nonVolatile + 4 permanent + 5 readOnly +) + +typedef RowStatus ENUM ( + 1 active + 2 notInService + 3 notReady + 4 createAndGo + 5 createAndWait + 6 destroy +) + +(1 internet + (6 snmpV2 + (3 snmpModules + (16 snmpVacmMIB + (1 vacmMIBObjects + (1 vacmContextTable + (1 vacmContextEntry : OCTETSTRING op_vacm_context + (1 vacmContextName OCTETSTRING GET) + ) + ) + (2 vacmSecurityToGroupTable + (1 vacmSecurityToGroupEntry : INTEGER OCTETSTRING op_vacm_security_to_group + (1 vacmSecurityModel INTEGER) + (2 vacmSecurityName OCTETSTRING) + (3 vacmGroupName OCTETSTRING GET SET) + (4 vacmSecurityToGroupStorageType StorageType GET SET) + (5 vacmSecurityToGroupStatus RowStatus GET SET) + ) + ) + (4 vacmAccessTable + (1 vacmAccessEntry : OCTETSTRING OCTETSTRING INTEGER ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv ) op_vacm_access + (1 vacmAccessContextPrefix OCTETSTRING) + (2 vacmAccessSecurityModel INTEGER) + (3 vacmAccessSecurityLevel ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv )) + (4 vacmAccessContextMatch ENUM ( 1 exact 2 prefix ) GET SET) + (5 vacmAccessReadViewName OCTETSTRING GET SET) + (6 vacmAccessWriteViewName OCTETSTRING GET SET) + (7 vacmAccessNotifyViewName OCTETSTRING GET SET) + (8 vacmAccessStorageType StorageType GET SET) + (9 vacmAccessStatus RowStatus GET SET) + ) + ) + (5 vacmMIBViews + (1 vacmViewSpinLock INTEGER op_vacm_view_lock GET SET) + (2 vacmViewTreeFamilyTable + (1 vacmViewTreeFamilyEntry : OCTETSTRING OID op_vacm_view + (1 vacmViewTreeFamilyViewName OCTETSTRING) + (2 vacmViewTreeFamilySubtree OID) + (3 vacmViewTreeFamilyMask OCTETSTRING GET SET) + (4 vacmViewTreeFamilyType ENUM ( 1 included 2 excluded ) GET SET) + (5 vacmViewTreeFamilyStorageType StorageType GET SET) + (6 vacmViewTreeFamilyStatus RowStatus GET SET) + ) + ) + ) + ) + (2 vacmMIBConformance + (1 vacmMIBCompliances + ) + (2 vacmMIBGroups + ) + ) + ) + ) + ) +) Index: contrib/bsnmp/snmp_vacm/snmp_vacm.3 =================================================================== --- contrib/bsnmp/snmp_vacm/snmp_vacm.3 (revision 0) +++ contrib/bsnmp/snmp_vacm/snmp_vacm.3 (revision 0) @@ -0,0 +1,94 @@ +.\"- +.\" Copyright (C) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Shteryana Sotirova Shopova under +.\" sponsorship from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 7, 2010 +.Dt SNMP_VACM 3 +.Os +.Sh NAME +.Nm snmp_vacm +.Nd "View-based Access Control module for +.Xr bsnmpd 1 +.Sh LIBRARY +.Pq begemotSnmpdModulePath."vacm" = "/usr/lib/snmp_vacm.so" +.Sh DESCRIPTION +The +.Nm snmp_vacm +module implements SNMPv3 View-based Access Control Model MIB as defined in +RFC 3415. The module is used to manage the internal lists of SNMPv1, v2c and +v3 user names and groups and their access rights to fetch or modify the values +of the MIB objects maintained by +.Nm bsnmpd +and the modules loaded in the daemon. +The module must be loaded for +.Nm bsnmpd +to implement proper view-based access control. If the module is not loaded, +access is granted to all configured SNMPv1 & SNMPv2c communities and SNMPv3 +USM users. +.Sh IMPLEMENTATION NOTES +An entry in any table implemented by this MIB may be created by setting the +relevant RowStatus column to createAndGo (4) - in fact, any other value for +those columns in a SET operation will cause an error. When an entry is created, +any of its columns that is not used as index, is set to the default value as +specified in the SNMP-VIEW-BASED-ACM-MIB. All entries maintained by the module +are persistent after reboot if created via +.Nm bsnmpd 's +config file, otherwise entries created via SNMP are lost after reboot. +A short description of the objects in the MIB follows. +.Bl -tag -width "XXXXXXXXX" +.It Va vacmContextTable +A read-only table that consists of a list of SNMP contexts available in +.Nm bsnmpd . +.It Va vacmSecurityToGroupTable +The table contains a list of SNMPv1, v2c and v3 user names and the groups +they belong to. +.It Va vacmAccessTable +The table contains a list of SNMP contexts to groups mappings and respectively +the names of the SNMP views under those contexts, to which users in the group +are granted read-only, read-write access or receive notifications for the +objects under the subtree in the relevant view. +.It Va vacmViewTreeFamilyTable +The table contains a list of SNMP views, i.e. entries specifying the OID of a +MIB subtree and whether access to the objects under this subtree is to be +allowed or forbiden. +.El +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/snmp/defs/vacm_tree.def +The description of the MIB tree implemented by +.Nm . +.El +.Sh SEE ALSO +.Xr bsnmpd 1 , +.Xr gensnmptree 1 , +.Xr snmpmod 3 +.Sh STANDARDS +IETF RFC 3415 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org Index: contrib/bsnmp/snmp_vacm/vacm_snmp.c =================================================================== --- contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 0) +++ contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 0) @@ -0,0 +1,1026 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "asn1.h" +#include "snmp.h" +#include "snmpmod.h" + +#include "vacm_tree.h" +#include "vacm_oid.h" + +static struct lmodule *vacm_module; +/* For the registration. */ +static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB; + +static uint reg_vacm; + +static int32_t vacm_lock; + +/* + * Internal datastructures and forward declarations. + */ +static void vacm_append_userindex(struct asn_oid *, + uint, const struct vacm_user *); +static int vacm_user_index_decode(const struct asn_oid *, + uint, int32_t *, char *); +static struct vacm_user *vacm_get_user(const struct asn_oid *, + uint); +static struct vacm_user *vacm_get_next_user(const struct asn_oid *, + uint); +static void vacm_append_access_rule_index(struct asn_oid *, + uint, const struct vacm_access *); +static int vacm_access_rule_index_decode(const struct asn_oid *, + uint, char *, char *, int32_t *, int32_t *); +static struct vacm_access * vacm_get_access_rule(const struct asn_oid *, + uint); +static struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *, + uint); +static int vacm_view_index_decode(const struct asn_oid *, uint, + char *, struct asn_oid *); +static void vacm_append_viewindex(struct asn_oid *, uint, + const struct vacm_view *); +static struct vacm_view *vacm_get_view(const struct asn_oid *, uint); +static struct vacm_view *vacm_get_next_view(const struct asn_oid *, uint); +static struct vacm_view *vacm_get_view_by_name(u_char *, u_int); +static struct vacm_context *vacm_get_context(const struct asn_oid *, uint); +static struct vacm_context *vacm_get_next_context(const struct asn_oid *, + uint); +static void vacm_append_ctxindex(struct asn_oid *, uint, + const struct vacm_context *); + +int +op_vacm_context(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + char cname[SNMP_ADM_STR32_SIZ]; + size_t cnamelen; + struct vacm_context *vacm_ctx; + + if (val->var.subs[sub - 1] != LEAF_vacmContextName) + abort(); + + switch (op) { + case SNMP_OP_GET: + if ((vacm_ctx = vacm_get_context(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((vacm_ctx = vacm_get_next_context(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + vacm_append_ctxindex(&val->var, sub, vacm_ctx); + break; + + case SNMP_OP_SET: + if ((vacm_ctx = vacm_get_context(&val->var, sub)) != NULL) + return (SNMP_ERR_WRONG_VALUE); + if (community != COMM_INITIALIZE) + return (SNMP_ERR_NOT_WRITEABLE); + if (val->var.subs[sub] >= SNMP_ADM_STR32_SIZ) + return (SNMP_ERR_WRONG_VALUE); + if (index_decode(&val->var, sub, iidx, &cname, &cnamelen)) + return (SNMP_ERR_GENERR); + cname[cnamelen] = '\0'; + if ((vacm_ctx = vacm_add_context(cname, reg_vacm)) == NULL) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + /* FALLTHROUGH*/ + case SNMP_OP_ROLLBACK: + return (SNMP_ERR_NOERROR); + default: + abort(); + } + + return (string_get(val, vacm_ctx->ctxname, -1)); +} + +int +op_vacm_security_to_group(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int32_t smodel; + char uname[SNMP_ADM_STR32_SIZ]; + struct vacm_user *user; + + switch (op) { + case SNMP_OP_GET: + if ((user = vacm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((user = vacm_get_next_user(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + vacm_append_userindex(&val->var, sub, user); + break; + + case SNMP_OP_SET: + if ((user = vacm_get_user(&val->var, sub)) == NULL && + val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) + return (SNMP_ERR_NOSUCHNAME); + + if (user != NULL) { + if (community != COMM_INITIALIZE && + user->type == StorageType_readOnly) + return (SNMP_ERR_NOT_WRITEABLE); + if (user->status == RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmGroupName: + ctx->scratch->ptr1 = user->group->groupname; + ctx->scratch->int1 = strlen(user->group->groupname); + return (vacm_user_set_group(user, + val->v.octetstring.octets,val->v.octetstring.len)); + + case LEAF_vacmSecurityToGroupStorageType: + return (SNMP_ERR_INCONS_VALUE); + + case LEAF_vacmSecurityToGroupStatus: + if (user == NULL) { + if (val->v.integer != RowStatus_createAndGo || + vacm_user_index_decode(&val->var, sub, + &smodel, uname) < 0) + return (SNMP_ERR_INCONS_VALUE); + user = vacm_new_user(smodel, uname); + if (user == NULL) + return (SNMP_ERR_GENERR); + user->status = RowStatus_destroy; + if (community != COMM_INITIALIZE) + user->type = StorageType_volatile; + else + user->type = StorageType_readOnly; + } else if (val->v.integer != RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = user->status; + user->status = val->v.integer; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if (val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) + return (SNMP_ERR_NOERROR); + if ((user = vacm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->v.integer) { + case RowStatus_destroy: + return (vacm_delete_user(user)); + + case RowStatus_createAndGo: + user->status = RowStatus_active; + break; + + default: + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((user = vacm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->var.subs[sub - 1]) { + case LEAF_vacmGroupName: + return (vacm_user_set_group(user, ctx->scratch->ptr1, + ctx->scratch->int1)); + + case LEAF_vacmSecurityToGroupStatus: + if (ctx->scratch->int1 == RowStatus_destroy) + return (vacm_delete_user(user)); + user->status = ctx->scratch->int1; + break; + + default: + break; + } + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmGroupName: + return (string_get(val, user->group->groupname, -1)); + case LEAF_vacmSecurityToGroupStorageType: + val->v.integer = user->type; + break; + case LEAF_vacmSecurityToGroupStatus: + val->v.integer = user->status; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_vacm_access(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, + uint32_t iidx __unused, enum snmp_op op) +{ + int32_t smodel, slevel; + char gname[SNMP_ADM_STR32_SIZ], cprefix[SNMP_ADM_STR32_SIZ]; + struct vacm_access *acl; + + switch (op) { + case SNMP_OP_GET: + if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((acl = vacm_get_next_access_rule(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + vacm_append_access_rule_index(&val->var, sub, acl); + break; + + case SNMP_OP_SET: + if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL && + val->var.subs[sub - 1] != LEAF_vacmAccessStatus) + return (SNMP_ERR_NOSUCHNAME); + if (acl != NULL && community != COMM_INITIALIZE && + acl->type == StorageType_readOnly) + return (SNMP_ERR_NOT_WRITEABLE); + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmAccessContextMatch: + ctx->scratch->int1 = acl->ctx_match; + if (val->v.integer == vacmAccessContextMatch_exact) + acl->ctx_match = 1; + else if (val->v.integer == vacmAccessContextMatch_prefix) + acl->ctx_match = 0; + else + return (SNMP_ERR_WRONG_VALUE); + break; + + case LEAF_vacmAccessReadViewName: + ctx->scratch->ptr1 = acl->read_view; + acl->read_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len); + if (acl->read_view == NULL) { + acl->read_view = ctx->scratch->ptr1; + return (SNMP_ERR_INCONS_VALUE); + } + return (SNMP_ERR_NOERROR); + + case LEAF_vacmAccessWriteViewName: + ctx->scratch->ptr1 = acl->write_view; + if ((acl->write_view = + vacm_get_view_by_name(val->v.octetstring.octets, + val->v.octetstring.len)) == NULL) { + acl->write_view = ctx->scratch->ptr1; + return (SNMP_ERR_INCONS_VALUE); + } + break; + + case LEAF_vacmAccessNotifyViewName: + ctx->scratch->ptr1 = acl->notify_view; + if ((acl->notify_view = + vacm_get_view_by_name(val->v.octetstring.octets, + val->v.octetstring.len)) == NULL) { + acl->notify_view = ctx->scratch->ptr1; + return (SNMP_ERR_INCONS_VALUE); + } + break; + + case LEAF_vacmAccessStorageType: + return (SNMP_ERR_INCONS_VALUE); + + case LEAF_vacmAccessStatus: + if (acl == NULL) { + if (val->v.integer != RowStatus_createAndGo || + vacm_access_rule_index_decode(&val->var, + sub, gname, cprefix, &smodel, &slevel) < 0) + return (SNMP_ERR_INCONS_VALUE); + if ((acl = vacm_new_access_rule(gname, cprefix, + smodel, slevel)) == NULL) + return (SNMP_ERR_GENERR); + acl->status = RowStatus_destroy; + if (community != COMM_INITIALIZE) + acl->type = StorageType_volatile; + else + acl->type = StorageType_readOnly; + } else if (val->v.integer != RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = acl->status; + acl->status = val->v.integer; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if (val->var.subs[sub - 1] != LEAF_vacmAccessStatus) + return (SNMP_ERR_NOERROR); + if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + if (val->v.integer == RowStatus_destroy) + return (vacm_delete_access_rule(acl)); + else + acl->status = RowStatus_active; + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->var.subs[sub - 1]) { + case LEAF_vacmAccessContextMatch: + acl->ctx_match = ctx->scratch->int1; + break; + case LEAF_vacmAccessReadViewName: + acl->read_view = ctx->scratch->ptr1; + break; + case LEAF_vacmAccessWriteViewName: + acl->write_view = ctx->scratch->ptr1; + break; + case LEAF_vacmAccessNotifyViewName: + acl->notify_view = ctx->scratch->ptr1; + break; + case LEAF_vacmAccessStatus: + if (ctx->scratch->int1 == RowStatus_destroy) + return (vacm_delete_access_rule(acl)); + default: + break; + } + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmAccessContextMatch: + return (string_get(val, acl->ctx_prefix, -1)); + case LEAF_vacmAccessReadViewName: + if (acl->read_view != NULL) + return (string_get(val, acl->read_view->viewname, -1)); + else + return (string_get(val, NULL, 0)); + case LEAF_vacmAccessWriteViewName: + if (acl->write_view != NULL) + return (string_get(val, acl->write_view->viewname, -1)); + else + return (string_get(val, NULL, 0)); + case LEAF_vacmAccessNotifyViewName: + if (acl->notify_view != NULL) + return (string_get(val, acl->notify_view->viewname, -1)); + else + return (string_get(val, NULL, 0)); + case LEAF_vacmAccessStorageType: + val->v.integer = acl->type; + break; + case LEAF_vacmAccessStatus: + val->v.integer = acl->status; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_vacm_view_lock(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + if (val->var.subs[sub - 1] != LEAF_vacmViewSpinLock) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (++vacm_lock == INT32_MAX) + vacm_lock = 0; + val->v.integer = vacm_lock; + break; + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + if (val->v.integer != vacm_lock) + return (SNMP_ERR_INCONS_VALUE); + break; + + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + case SNMP_OP_COMMIT: + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_vacm_view(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, + uint32_t iidx __unused, enum snmp_op op) +{ + char vname[SNMP_ADM_STR32_SIZ]; + struct asn_oid oid; + struct vacm_view *view; + + switch (op) { + case SNMP_OP_GET: + if ((view = vacm_get_view(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((view = vacm_get_next_view(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + vacm_append_viewindex(&val->var, sub, view); + break; + + case SNMP_OP_SET: + if ((view = vacm_get_view(&val->var, sub)) == NULL && + val->var.subs[sub - 1] != LEAF_vacmViewTreeFamilyStatus) + return (SNMP_ERR_NOSUCHNAME); + + if (view != NULL) { + if (community != COMM_INITIALIZE && + view->type == StorageType_readOnly) + return (SNMP_ERR_NOT_WRITEABLE); + if (view->status == RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmViewTreeFamilyMask: + if (val->v.octetstring.len > sizeof(view->mask)) + ctx->scratch->ptr1 = malloc(sizeof(view->mask)); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memset(ctx->scratch->ptr1, 0, sizeof(view->mask)); + memcpy(ctx->scratch->ptr1, view->mask, + sizeof(view->mask)); + memset(view->mask, 0, sizeof(view->mask)); + memcpy(view->mask, val->v.octetstring.octets, + val->v.octetstring.len); + break; + + case LEAF_vacmViewTreeFamilyType: + ctx->scratch->int1 = view->exclude; + if (val->v.integer == vacmViewTreeFamilyType_included) + view->exclude = 0; + else if (val->v.integer == vacmViewTreeFamilyType_excluded) + view->exclude = 1; + else + return (SNMP_ERR_WRONG_VALUE); + break; + + case LEAF_vacmViewTreeFamilyStorageType: + return (SNMP_ERR_INCONS_VALUE); + + case LEAF_vacmViewTreeFamilyStatus: + if (view == NULL) { + if (val->v.integer != RowStatus_createAndGo || + vacm_view_index_decode(&val->var, sub, vname, + &oid) < 0) + return (SNMP_ERR_INCONS_VALUE); + if ((view = vacm_new_view(vname, &oid)) == NULL) + return (SNMP_ERR_GENERR); + view->status = RowStatus_destroy; + if (community != COMM_INITIALIZE) + view->type = StorageType_volatile; + else + view->type = StorageType_readOnly; + } else if (val->v.integer != RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = view->status; + view->status = val->v.integer; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + switch (val->var.subs[sub - 1]) { + case LEAF_vacmViewTreeFamilyMask: + free(ctx->scratch->ptr1); + break; + case LEAF_vacmViewTreeFamilyStatus: + if ((view = vacm_get_view(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->v.integer) { + case RowStatus_destroy: + return (vacm_delete_view(view)); + + case RowStatus_createAndGo: + view->status = RowStatus_active; + break; + + default: + /* NOTREACHED*/ + return (SNMP_ERR_GENERR); + } + default: + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((view = vacm_get_view(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->var.subs[sub - 1]) { + case LEAF_vacmViewTreeFamilyMask: + memcpy(view->mask, ctx->scratch->ptr1, + sizeof(view->mask)); + free(ctx->scratch->ptr1); + break; + case LEAF_vacmViewTreeFamilyType: + view->exclude = ctx->scratch->int1; + break; + case LEAF_vacmViewTreeFamilyStatus: + if (ctx->scratch->int1 == RowStatus_destroy) + return (vacm_delete_view(view)); + break; + default: + break; + } + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_vacmViewTreeFamilyMask: + return (string_get(val, view->mask, sizeof(view->mask))); + case LEAF_vacmViewTreeFamilyType: + if (view->exclude) + val->v.integer = vacmViewTreeFamilyType_excluded; + else + val->v.integer = vacmViewTreeFamilyType_included; + break; + case LEAF_vacmViewTreeFamilyStorageType: + val->v.integer = view->type; + break; + case LEAF_vacmViewTreeFamilyStatus: + val->v.integer = view->status; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +static void +vacm_append_userindex(struct asn_oid *oid, uint sub, + const struct vacm_user *user) +{ + uint32_t i; + + oid->len = sub + strlen(user->secname) + 2; + oid->subs[sub++] = user->sec_model; + oid->subs[sub] = strlen(user->secname); + for (i = 1; i <= strlen(user->secname); i++) + oid->subs[sub + i] = user->secname[i - 1]; +} + +static int +vacm_user_index_decode(const struct asn_oid *oid, uint sub, + int32_t *smodel, char *uname) +{ + uint32_t i; + + *smodel = oid->subs[sub++]; + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + uname[i] = oid->subs[sub + i + 1]; + uname[i] = '\0'; + + return (0); +} + +static struct vacm_user * +vacm_get_user(const struct asn_oid *oid, uint sub) +{ + int32_t smodel; + char uname[SNMP_ADM_STR32_SIZ]; + struct vacm_user *user; + + if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) + return (NULL); + + for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) + if (strcmp(uname, user->secname) == 0 && + user->sec_model == smodel) + return (user); + + return (NULL); +} + +static struct vacm_user * +vacm_get_next_user(const struct asn_oid *oid, uint sub) +{ + int32_t smodel; + char uname[SNMP_ADM_STR32_SIZ]; + struct vacm_user *user; + + if (oid->len - sub == 0) + return (vacm_first_user()); + + if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) + return (NULL); + + for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) + if (strcmp(uname, user->secname) == 0 && + user->sec_model == smodel) + return (vacm_next_user(user)); + + return (NULL); +} + +static void +vacm_append_access_rule_index(struct asn_oid *oid, uint sub, + const struct vacm_access *acl) +{ + uint32_t i; + + oid->len = sub + strlen(acl->group->groupname) + + strlen(acl->ctx_prefix) + 4; + + oid->subs[sub] = strlen(acl->group->groupname); + for (i = 1; i <= strlen(acl->group->groupname); i++) + oid->subs[sub + i] = acl->group->groupname[i - 1]; + sub += strlen(acl->group->groupname) + 1; + + oid->subs[sub] = strlen(acl->ctx_prefix); + for (i = 1; i <= strlen(acl->ctx_prefix); i++) + oid->subs[sub + i] = acl->ctx_prefix[i - 1]; + sub += strlen(acl->ctx_prefix) + 1; + oid->subs[sub++] = acl->sec_model; + oid->subs[sub] = acl->sec_level; +} + +static int +vacm_access_rule_index_decode(const struct asn_oid *oid, uint sub, char *gname, + char *cprefix, int32_t *smodel, int32_t *slevel) +{ + uint32_t i; + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + gname[i] = oid->subs[sub + i + 1]; + gname[i] = '\0'; + sub += strlen(gname) + 1; + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + cprefix[i] = oid->subs[sub + i + 1]; + cprefix[i] = '\0'; + sub += strlen(cprefix) + 1; + + *smodel = oid->subs[sub++]; + *slevel = oid->subs[sub]; + + return (0); +} + +struct vacm_access * +vacm_get_access_rule(const struct asn_oid *oid, uint sub) +{ + int32_t smodel, slevel; + char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; + struct vacm_access *acl; + + if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, + &slevel) < 0) + return (NULL); + + for (acl = vacm_first_access_rule(); acl != NULL; + acl = vacm_next_access_rule(acl)) + if (strcmp(gname, acl->group->groupname) == 0 && + strcmp(prefix, acl->ctx_prefix) == 0 && + smodel == acl->sec_model && slevel == acl->sec_level) + return (acl); + + return (NULL); +} + +struct vacm_access * +vacm_get_next_access_rule(const struct asn_oid *oid __unused, uint sub __unused) +{ + int32_t smodel, slevel; + char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; + struct vacm_access *acl; + + if (oid->len - sub == 0) + return (vacm_first_access_rule()); + + if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, + &slevel) < 0) + return (NULL); + + for (acl = vacm_first_access_rule(); acl != NULL; + acl = vacm_next_access_rule(acl)) + if (strcmp(gname, acl->group->groupname) == 0 && + strcmp(prefix, acl->ctx_prefix) == 0 && + smodel == acl->sec_model && slevel == acl->sec_model) + return (vacm_next_access_rule(acl)); + + return (NULL); +} + +static int +vacm_view_index_decode(const struct asn_oid *oid, uint sub, char *vname, + struct asn_oid *view_oid) +{ + uint32_t i; + int viod_off; + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + vname[i] = oid->subs[sub + i + 1]; + vname[i] = '\0'; + + viod_off = sub + oid->subs[sub] + 1; + if ((view_oid->len = oid->subs[viod_off]) > ASN_MAXOIDLEN) + return (-1); + + memcpy(&view_oid->subs[0], &oid->subs[viod_off + 1], + view_oid->len * sizeof(view_oid->subs[0])); + + return (0); +} + +static void +vacm_append_viewindex(struct asn_oid *oid, uint sub, const struct vacm_view *view) +{ + uint32_t i; + + oid->len = sub + strlen(view->viewname) + 1; + oid->subs[sub] = strlen(view->viewname); + for (i = 1; i <= strlen(view->viewname); i++) + oid->subs[sub + i] = view->viewname[i - 1]; + + sub += strlen(view->viewname) + 1; + oid->subs[sub] = view->subtree.len; + oid->len++; + asn_append_oid(oid, &view->subtree); +} + +struct vacm_view * +vacm_get_view(const struct asn_oid *oid, uint sub) +{ + char vname[SNMP_ADM_STR32_SIZ]; + struct asn_oid subtree; + struct vacm_view *view; + + if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) + return (NULL); + + for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) + if (strcmp(vname, view->viewname) == 0 && + asn_compare_oid(&subtree, &view->subtree)== 0) + return (view); + + return (NULL); +} + +struct vacm_view * +vacm_get_next_view(const struct asn_oid *oid, uint sub) +{ + char vname[SNMP_ADM_STR32_SIZ]; + struct asn_oid subtree; + struct vacm_view *view; + + if (oid->len - sub == 0) + return (vacm_first_view()); + + if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) + return (NULL); + + for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) + if (strcmp(vname, view->viewname) == 0 && + asn_compare_oid(&subtree, &view->subtree)== 0) + return (vacm_next_view(view)); + + return (NULL); +} + +static struct vacm_view * +vacm_get_view_by_name(u_char *octets, u_int len) +{ + struct vacm_view *view; + + for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) + if (strlen(view->viewname) == len && + memcmp(octets, view->viewname, len) == 0) + return (view); + + return (NULL); +} + +static struct vacm_context * +vacm_get_context(const struct asn_oid *oid, uint sub) +{ + char cname[SNMP_ADM_STR32_SIZ]; + size_t cnamelen; + u_int index_count; + struct vacm_context *vacm_ctx; + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (NULL); + + index_count = 0; + index_count = SNMP_INDEX(index_count, 1); + if (index_decode(oid, sub, index_count, &cname, &cnamelen)) + return (NULL); + + for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; + vacm_ctx = vacm_next_context(vacm_ctx)) + if (strcmp(cname, vacm_ctx->ctxname) == 0) + return (vacm_ctx); + + return (NULL); +} + +static struct vacm_context * +vacm_get_next_context(const struct asn_oid *oid, uint sub) +{ + char cname[SNMP_ADM_STR32_SIZ]; + size_t cnamelen; + u_int index_count; + struct vacm_context *vacm_ctx; + + if (oid->len - sub == 0) + return (vacm_first_context()); + + if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) + return (NULL); + + index_count = 0; + index_count = SNMP_INDEX(index_count, 1); + if (index_decode(oid, sub, index_count, &cname, &cnamelen)) + return (NULL); + + for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; + vacm_ctx = vacm_next_context(vacm_ctx)) + if (strcmp(cname, vacm_ctx->ctxname) == 0) + return (vacm_next_context(vacm_ctx)); + + return (NULL); +} + +static void +vacm_append_ctxindex(struct asn_oid *oid, uint sub, + const struct vacm_context *ctx) +{ + uint32_t i; + + oid->len = sub + strlen(ctx->ctxname) + 1; + oid->subs[sub] = strlen(ctx->ctxname); + for (i = 1; i <= strlen(ctx->ctxname); i++) + oid->subs[sub + i] = ctx->ctxname[i - 1]; +} + +/* + * VACM snmp module initialization hook. + * Returns 0 on success, < 0 on error. + */ +static int +vacm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) +{ + vacm_module = mod; + vacm_lock = random(); + vacm_groups_init(); + + /* XXX: TODO - initialize structures */ + return (0); +} + +/* + * VACM snmp module finalization hook. + */ +static int +vacm_fini(void) +{ + /* XXX: TODO - cleanup */ + vacm_flush_contexts(reg_vacm); + or_unregister(reg_vacm); + + return (0); +} + +/* + * VACM snmp module start operation. + */ +static void +vacm_start(void) +{ + static char dflt_ctx[] = ""; + + reg_vacm = or_register(&oid_vacm, + "The MIB module for managing SNMP View-based Access Control Model.", + vacm_module); + + (void)vacm_add_context(dflt_ctx, reg_vacm); +} + +static void +vacm_dump(void) +{ + struct vacm_context *vacmctx; + struct vacm_user *vuser; + struct vacm_access *vacl; + struct vacm_view *view; + static char oidbuf[ASN_OIDSTRLEN]; + + syslog(LOG_ERR, "\n"); + syslog(LOG_ERR, "Context list:"); + for (vacmctx = vacm_first_context(); vacmctx != NULL; + vacmctx = vacm_next_context(vacmctx)) + syslog(LOG_ERR, "Context \"%s\", module id %d", + vacmctx->ctxname, vacmctx->regid); + + syslog(LOG_ERR, "VACM users:"); + for (vuser = vacm_first_user(); vuser != NULL; + vuser = vacm_next_user(vuser)) + syslog(LOG_ERR, "Uname %s, Group %s, model %d", vuser->secname, + vuser->group!= NULL?vuser->group->groupname:"Unknown", + vuser->sec_model); + + syslog(LOG_ERR, "VACM Access rules:"); + for (vacl = vacm_first_access_rule(); vacl != NULL; + vacl = vacm_next_access_rule(vacl)) + syslog(LOG_ERR, "Group %s, CtxPrefix %s, Model %d, Level %d, " + "RV %s, WR %s, NV %s", vacl->group!=NULL? + vacl->group->groupname:"Unknown", vacl->ctx_prefix, + vacl->sec_model, vacl->sec_level, vacl->read_view!=NULL? + vacl->read_view->viewname:"None", vacl->write_view!=NULL? + vacl->write_view->viewname:"None", vacl->notify_view!=NULL? + vacl->notify_view->viewname:"None"); + + syslog(LOG_ERR, "VACM Views:"); + for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) + syslog(LOG_ERR, "View %s, Tree %s - %s", view->viewname, + asn_oid2str_r(&view->subtree, oidbuf), view->exclude? + "excluded":"included"); +} + +const char vacm_comment[] = \ +"This module implements SNMP View-based Access Control Model defined in RFC 3415."; + +const struct snmp_module config = { + .comment = vacm_comment, + .init = vacm_init, + .fini = vacm_fini, + .start = vacm_start, + .tree = vacm_ctree, + .dump = vacm_dump, + .tree_size = vacm_CTREE_SIZE, +}; Index: contrib/bsnmp/snmp_usm/usm_tree.def =================================================================== --- contrib/bsnmp/snmp_usm/usm_tree.def (revision 0) +++ contrib/bsnmp/snmp_usm/usm_tree.def (revision 0) @@ -0,0 +1,109 @@ +#- +# Copyright (C) 2010 The FreeBSD Foundation +# All rights reserved. +# +# This software was developed by Shteryana Sotirova Shopova under +# sponsorship from the FreeBSD Foundation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +typedef StorageType ENUM ( + 1 other + 2 volatile + 3 nonVolatile + 4 permanent + 5 readOnly +) + +typedef RowStatus ENUM ( + 1 active + 2 notInService + 3 notReady + 4 createAndGo + 5 createAndWait + 6 destroy +) + +(1 internet + (6 snmpV2 + (3 snmpModules + (10 snmpFrameworkMIB + (1 snmpFrameworkAdmin + (1 snmpAuthProtocols + (1 usmNoAuthProtocol + ) + (2 usmHMACMD5AuthProtocol + ) + (3 usmHMACSHAAuthProtocol + ) + ) + (2 snmpPrivProtocols + (1 usmNoPrivProtocol + ) + (2 usmDESPrivProtocol + ) + (4 usmAesCfb128Protocol + ) + ) + ) + ) + (15 snmpUsmMIB + (1 usmMIBObjects + (1 usmStats + (1 usmStatsUnsupportedSecLevels COUNTER op_usm_stats GET) + (2 usmStatsNotInTimeWindows COUNTER op_usm_stats GET) + (3 usmStatsUnknownUserNames COUNTER op_usm_stats GET) + (4 usmStatsUnknownEngineIDs COUNTER op_usm_stats GET) + (5 usmStatsWrongDigests COUNTER op_usm_stats GET) + (6 usmStatsDecryptionErrors COUNTER op_usm_stats GET) + ) + (2 usmUser + (1 usmUserSpinLock INTEGER op_usm_lock GET SET) + (2 usmUserTable + (1 usmUserEntry : OCTETSTRING | SnmpEngineID OCTETSTRING op_usm_users + (1 usmUserEngineID OCTETSTRING | SnmpEngineID) + (2 usmUserName OCTETSTRING) + (3 usmUserSecurityName OCTETSTRING | SnmpAdminString GET) + (4 usmUserCloneFrom OID GET SET) + (5 usmUserAuthProtocol OID GET SET) + (6 usmUserAuthKeyChange OCTETSTRING | KeyChange GET SET) + (7 usmUserOwnAuthKeyChange OCTETSTRING | KeyChange GET SET) + (8 usmUserPrivProtocol OID GET SET) + (9 usmUserPrivKeyChange OCTETSTRING | KeyChange GET SET) + (10 usmUserOwnPrivKeyChange OCTETSTRING | KeyChange GET SET) + (11 usmUserPublic OCTETSTRING GET SET) + (12 usmUserStorageType StorageType GET SET) + (13 usmUserStatus RowStatus GET SET) + ) + ) + ) + ) + ) + (20 snmpUsmAesMIB + ) + ) + ) +) + Index: contrib/bsnmp/snmp_usm/usm_snmp.c =================================================================== --- contrib/bsnmp/snmp_usm/usm_snmp.c (revision 0) +++ contrib/bsnmp/snmp_usm/usm_snmp.c (revision 0) @@ -0,0 +1,615 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "asn1.h" +#include "snmp.h" +#include "snmpmod.h" + +#include "usm_tree.h" +#include "usm_oid.h" + +static struct lmodule *usm_module; +/* For the registration. */ +static const struct asn_oid oid_usm = OIDX_snmpUsmMIB; + +static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol; +static const struct asn_oid oid_usmHMACMD5AuthProtocol = \ + OIDX_usmHMACMD5AuthProtocol; +static const struct asn_oid oid_usmHMACSHAAuthProtocol = \ + OIDX_usmHMACSHAAuthProtocol; + +static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol; +static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol; +static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol; + +static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName; + +/* The registration. */ +static uint reg_usm; + +static int32_t usm_lock; + +static struct usm_user * usm_get_user(const struct asn_oid *, uint); +static struct usm_user * usm_get_next_user(const struct asn_oid *, uint); +static void usm_append_userindex(struct asn_oid *, uint, + const struct usm_user *); +static int usm_user_index_decode(const struct asn_oid *, uint, uint8_t *, + uint32_t *, char *); + +int +op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op) +{ + struct snmpd_usmstat *usmstats; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if ((usmstats = bsnmpd_get_usm_stats()) == NULL) + return (SNMP_ERR_GENERR); + + if (op == SNMP_OP_GET) { + switch (val->var.subs[sub - 1]) { + case LEAF_usmStatsUnsupportedSecLevels: + val->v.uint32 = usmstats->unsupported_seclevels; + break; + case LEAF_usmStatsNotInTimeWindows: + val->v.uint32 = usmstats->not_in_time_windows; + break; + case LEAF_usmStatsUnknownUserNames: + val->v.uint32 = usmstats->unknown_users; + break; + case LEAF_usmStatsUnknownEngineIDs: + val->v.uint32 = usmstats->unknown_engine_ids; + break; + case LEAF_usmStatsWrongDigests: + val->v.uint32 = usmstats->wrong_digests; + break; + case LEAF_usmStatsDecryptionErrors: + val->v.uint32 = usmstats->decrypt_errors; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + return (SNMP_ERR_NOERROR); + } + abort(); +} + +int +op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (++usm_lock == INT32_MAX) + usm_lock = 0; + val->v.integer = usm_lock; + break; + case SNMP_OP_GETNEXT: + abort(); + case SNMP_OP_SET: + if (val->v.integer != usm_lock) + return (SNMP_ERR_INCONS_VALUE); + break; + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + case SNMP_OP_COMMIT: + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_usm_users(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + uint32_t elen; + struct usm_user *uuser, *clone; + char uname[SNMP_ADM_STR32_SIZ]; + uint8_t eid[SNMP_ENGINE_ID_SIZ]; + + switch (op) { + case SNMP_OP_GET: + if ((uuser = usm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((uuser = usm_get_next_user(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + usm_append_userindex(&val->var, sub, uuser); + break; + + case SNMP_OP_SET: + if ((uuser = usm_get_user(&val->var, sub)) == NULL && + val->var.subs[sub - 1] != LEAF_usmUserStatus && + val->var.subs[sub - 1] != LEAF_usmUserCloneFrom) + return (SNMP_ERR_NOSUCHNAME); + + if (community != COMM_INITIALIZE && + uuser->type == StorageType_readOnly) + return (SNMP_ERR_NOT_WRITEABLE); + + switch (val->var.subs[sub - 1]) { + case LEAF_usmUserSecurityName: + return (SNMP_ERR_NOT_WRITEABLE); + + case LEAF_usmUserCloneFrom: + if (uuser != NULL || usm_user_index_decode(&val->var, + sub, eid, &elen, uname) < 0 || + !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid))) + return (SNMP_ERR_WRONG_VALUE); + if ((clone = usm_get_user(&val->v.oid, sub)) == NULL) + return (SNMP_ERR_INCONS_VALUE); + if ((uuser = usm_new_user(eid, elen, uname)) == NULL) + return (SNMP_ERR_GENERR); + uuser->status = RowStatus_notReady; + if (community != COMM_INITIALIZE) + uuser->type = StorageType_volatile; + else + uuser->type = StorageType_readOnly; + + uuser->suser.auth_proto = clone->suser.auth_proto; + uuser->suser.priv_proto = clone->suser.priv_proto; + memcpy(uuser->suser.auth_key, clone->suser.auth_key, + sizeof(uuser->suser.auth_key)); + memcpy(uuser->suser.priv_key, clone->suser.priv_key, + sizeof(uuser->suser.priv_key)); + ctx->scratch->int1 = RowStatus_createAndWait; + break; + + case LEAF_usmUserAuthProtocol: + ctx->scratch->int1 = uuser->suser.auth_proto; + if (asn_compare_oid(&oid_usmNoAuthProtocol, + &val->v.oid) == 0) + uuser->suser.auth_proto = SNMP_AUTH_NOAUTH; + else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol, + &val->v.oid) == 0) + uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5; + else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol, + &val->v.oid) == 0) + uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA; + else + return (SNMP_ERR_WRONG_VALUE); + break; + + case LEAF_usmUserAuthKeyChange: + case LEAF_usmUserOwnAuthKeyChange: + if (val->var.subs[sub - 1] == + LEAF_usmUserOwnAuthKeyChange && + (usm_user == NULL || strcmp(uuser->suser.sec_name, + usm_user->suser.sec_name) != 0)) + return (SNMP_ERR_NO_ACCESS); + if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, uuser->suser.auth_key, + SNMP_AUTH_KEY_SIZ); + memcpy(uuser->suser.auth_key, val->v.octetstring.octets, + val->v.octetstring.len); + break; + + case LEAF_usmUserPrivProtocol: + ctx->scratch->int1 = uuser->suser.priv_proto; + if (asn_compare_oid(&oid_usmNoPrivProtocol, + &val->v.oid) == 0) + uuser->suser.priv_proto = SNMP_PRIV_NOPRIV; + else if (asn_compare_oid(&oid_usmDESPrivProtocol, + &val->v.oid) == 0) + uuser->suser.priv_proto = SNMP_PRIV_DES; + else if (asn_compare_oid(&oid_usmAesCfb128Protocol, + &val->v.oid) == 0) + uuser->suser.priv_proto = SNMP_PRIV_AES; + else + return (SNMP_ERR_WRONG_VALUE); + break; + + case LEAF_usmUserPrivKeyChange: + case LEAF_usmUserOwnPrivKeyChange: + if (val->var.subs[sub - 1] == + LEAF_usmUserOwnPrivKeyChange && + (usm_user == NULL || strcmp(uuser->suser.sec_name, + usm_user->suser.sec_name) != 0)) + return (SNMP_ERR_NO_ACCESS); + if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, uuser->suser.priv_key, + SNMP_PRIV_KEY_SIZ); + memcpy(uuser->suser.priv_key, val->v.octetstring.octets, + val->v.octetstring.len); + break; + + case LEAF_usmUserPublic: + if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ) + return (SNMP_ERR_INCONS_VALUE); + if (uuser->user_public_len > 0) { + ctx->scratch->ptr2 = + malloc(uuser->user_public_len); + if (ctx->scratch->ptr2 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr2, uuser->user_public, + uuser->user_public_len); + ctx->scratch->int2 = uuser->user_public_len; + } + if (val->v.octetstring.len > 0) { + memcpy(uuser->user_public, + val->v.octetstring.octets, + val->v.octetstring.len); + uuser->user_public_len = val->v.octetstring.len; + } else { + memset(uuser->user_public, 0, + SNMP_ADM_STR32_SIZ); + uuser->user_public_len = 0; + } + break; + + case LEAF_usmUserStorageType: + return (SNMP_ERR_INCONS_VALUE); + + case LEAF_usmUserStatus: + /* XXX: shteryana - save old status in ctx->scratch->int1 */ + if (uuser == NULL) { + if (val->v.integer != RowStatus_createAndWait || + usm_user_index_decode(&val->var, sub, eid, + &elen, uname) < 0) + return (SNMP_ERR_INCONS_VALUE); + uuser = usm_new_user(eid, elen, uname); + if (uuser == NULL) + return (SNMP_ERR_GENERR); + uuser->status = RowStatus_notReady; + if (community != COMM_INITIALIZE) + uuser->type = StorageType_volatile; + else + uuser->type = StorageType_readOnly; + } else if (val->v.integer != RowStatus_active && + val->v.integer != RowStatus_destroy) + return (SNMP_ERR_INCONS_VALUE); + + uuser->status = val->v.integer; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + switch (val->var.subs[sub - 1]) { + case LEAF_usmUserAuthKeyChange: + case LEAF_usmUserOwnAuthKeyChange: + case LEAF_usmUserPrivKeyChange: + case LEAF_usmUserOwnPrivKeyChange: + free(ctx->scratch->ptr1); + break; + case LEAF_usmUserPublic: + if (ctx->scratch->ptr2 != NULL) + free(ctx->scratch->ptr2); + break; + case LEAF_usmUserStatus: + if (val->v.integer != RowStatus_destroy) + break; + if ((uuser = usm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + usm_delete_user(uuser); + break; + default: + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((uuser = usm_get_user(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->var.subs[sub - 1]) { + case LEAF_usmUserAuthProtocol: + uuser->suser.auth_proto = ctx->scratch->int1; + break; + case LEAF_usmUserAuthKeyChange: + case LEAF_usmUserOwnAuthKeyChange: + memcpy(uuser->suser.auth_key, ctx->scratch->ptr1, + SNMP_AUTH_KEY_SIZ); + free(ctx->scratch->ptr1); + break; + case LEAF_usmUserPrivProtocol: + uuser->suser.priv_proto = ctx->scratch->int1; + break; + case LEAF_usmUserPrivKeyChange: + case LEAF_usmUserOwnPrivKeyChange: + memcpy(uuser->suser.priv_key, ctx->scratch->ptr1, + SNMP_AUTH_KEY_SIZ); + free(ctx->scratch->ptr1); + break; + case LEAF_usmUserPublic: + if (ctx->scratch->ptr2 != NULL) { + memcpy(uuser->user_public, ctx->scratch->ptr2, + ctx->scratch->int2); + uuser->user_public_len = ctx->scratch->int2; + free(ctx->scratch->ptr2); + } else { + memset(uuser->user_public, 0, + SNMP_ADM_STR32_SIZ); + uuser->user_public_len = 0; + } + break; + case LEAF_usmUserCloneFrom: + case LEAF_usmUserStatus: + if (ctx->scratch->int1 == RowStatus_createAndWait) + usm_delete_user(uuser); + break; + default: + break; + } + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_usmUserSecurityName: + return (string_get(val, uuser->suser.sec_name, -1)); + case LEAF_usmUserCloneFrom: + memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero)); + break; + case LEAF_usmUserAuthProtocol: + switch (uuser->suser.auth_proto) { + case SNMP_AUTH_HMAC_MD5: + memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol, + sizeof(oid_usmHMACMD5AuthProtocol)); + break; + case SNMP_AUTH_HMAC_SHA: + memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol, + sizeof(oid_usmHMACSHAAuthProtocol)); + break; + default: + memcpy(&val->v.oid, &oid_usmNoAuthProtocol, + sizeof(oid_usmNoAuthProtocol)); + break; + } + break; + case LEAF_usmUserAuthKeyChange: + case LEAF_usmUserOwnAuthKeyChange: + return (string_get(val, (char *)uuser->suser.auth_key, 0)); + case LEAF_usmUserPrivProtocol: + switch (uuser->suser.priv_proto) { + case SNMP_PRIV_DES: + memcpy(&val->v.oid, &oid_usmDESPrivProtocol, + sizeof(oid_usmDESPrivProtocol)); + break; + case SNMP_PRIV_AES: + memcpy(&val->v.oid, &oid_usmAesCfb128Protocol, + sizeof(oid_usmAesCfb128Protocol)); + break; + default: + memcpy(&val->v.oid, &oid_usmNoPrivProtocol, + sizeof(oid_usmNoPrivProtocol)); + break; + } + break; + case LEAF_usmUserPrivKeyChange: + case LEAF_usmUserOwnPrivKeyChange: + return (string_get(val, (char *)uuser->suser.priv_key, 0)); + case LEAF_usmUserPublic: + return (string_get(val, uuser->user_public, + uuser->user_public_len)); + case LEAF_usmUserStorageType: + val->v.integer = uuser->type; + break; + case LEAF_usmUserStatus: + val->v.integer = uuser->status; + break; + } + + return (SNMP_ERR_NOERROR); +} + +static int +usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine, + uint32_t *elen, char *uname) +{ + uint32_t i, nlen; + int uname_off; + + if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + engine[i] = oid->subs[sub + i + 1]; + *elen = i; + + uname_off = sub + oid->subs[sub] + 1; + if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ) + return (-1); + + for (i = 0; i < nlen; i++) + uname[i] = oid->subs[uname_off + i + 1]; + uname[nlen] = '\0'; + + return (0); +} + +static void +usm_append_userindex(struct asn_oid *oid, uint sub, + const struct usm_user *uuser) +{ + uint32_t i; + + oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name); + oid->len += 2; + oid->subs[sub] = uuser->user_engine_len; + for (i = 1; i < uuser->user_engine_len + 1; i++) + oid->subs[sub + i] = uuser->user_engine_id[i - 1]; + + sub += uuser->user_engine_len + 1; + oid->subs[sub] = strlen(uuser->suser.sec_name); + for (i = 1; i <= oid->subs[sub]; i++) + oid->subs[sub + i] = uuser->suser.sec_name[i - 1]; +} + +static struct usm_user * +usm_get_user(const struct asn_oid *oid, uint sub) +{ + uint32_t enginelen; + char username[SNMP_ADM_STR32_SIZ]; + uint8_t engineid[SNMP_ENGINE_ID_SIZ]; + + if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) + return (NULL); + + return (usm_find_user(engineid, enginelen, username)); +} + +static struct usm_user * +usm_get_next_user(const struct asn_oid *oid, uint sub) +{ + uint32_t enginelen; + char username[SNMP_ADM_STR32_SIZ]; + uint8_t engineid[SNMP_ENGINE_ID_SIZ]; + struct usm_user *uuser; + + if (oid->len - sub == 0) + return (usm_first_user()); + + if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) + return (NULL); + + if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL) + return (usm_next_user(uuser)); + + return (NULL); +} + +/* + * USM snmp module initialization hook. + * Returns 0 on success, < 0 on error. + */ +static int +usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) +{ + usm_module = mod; + usm_lock = random(); + bsnmpd_reset_usm_stats(); + return (0); +} + +/* + * USM snmp module finalization hook. + */ +static int +usm_fini(void) +{ + usm_flush_users(); + or_unregister(reg_usm); + + return (0); +} + +/* + * USM snmp module start operation. + */ +static void +usm_start(void) +{ + reg_usm = or_register(&oid_usm, + "The MIB module for managing SNMP User-Based Security Model.", + usm_module); +} + +static void +usm_dump(void) +{ + struct usm_user *uuser; + struct snmpd_usmstat *usmstats; + const char *const authstr[] = { + "noauth", + "md5", + "sha", + NULL + }; + const char *const privstr[] = { + "nopriv", + "des", + "aes", + NULL + }; + + if ((usmstats = bsnmpd_get_usm_stats()) != NULL) { + syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u", + usmstats->unsupported_seclevels); + syslog(LOG_ERR, "NotInTimeWindows\t\t%u", + usmstats->not_in_time_windows); + syslog(LOG_ERR, "UnknownUserNames\t\t%u", + usmstats->unknown_users); + syslog(LOG_ERR, "UnknownEngineIDs\t\t%u", + usmstats->unknown_engine_ids); + syslog(LOG_ERR, "WrongDigests\t\t%u", + usmstats->wrong_digests); + syslog(LOG_ERR, "DecryptionErrors\t\t%u", + usmstats->decrypt_errors); + } + + syslog(LOG_ERR, "USM users"); + for (uuser = usm_first_user(); uuser != NULL; + (uuser = usm_next_user(uuser))) + syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name, + authstr[uuser->suser.auth_proto], + privstr[uuser->suser.priv_proto]); +} + +const char usm_comment[] = \ +"This module implements SNMP User-based Security Model defined in RFC 3414."; + +const struct snmp_module config = { + .comment = usm_comment, + .init = usm_init, + .fini = usm_fini, + .start = usm_start, + .tree = usm_ctree, + .dump = usm_dump, + .tree_size = usm_CTREE_SIZE, +}; Index: contrib/bsnmp/snmp_usm/snmp_usm.3 =================================================================== --- contrib/bsnmp/snmp_usm/snmp_usm.3 (revision 0) +++ contrib/bsnmp/snmp_usm/snmp_usm.3 (revision 0) @@ -0,0 +1,132 @@ +.\"- +.\" Copyright (C) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Shteryana Sotirova Shopova under +.\" sponsorship from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 9, 2010 +.Dt SNMP_USM 3 +.Os +.Sh NAME +.Nm snmp_usm +.Nd "user-based security module for +.Xr bsnmpd 1 +.Sh LIBRARY +.Pq begemotSnmpdModulePath."usm" = "/usr/lib/snmp_usm.so" +.Sh DESCRIPTION +The +.Nm snmp_usm +module implements SNMPv3 User-Based Security Model MIB as defined in RFC 3414. +The module is used to manage the internal list of SNMPv3 USM active users in +.Nm bsnmpd . +The module must be loaded for +.Nm bsnmpd +to receive and process SNMPv3 USM PDUs correctly. +.Sh IMPLEMENTATION NOTES +A short description of the objects in the MIB follows. +.Bl -tag -width "XXXXXXXXX" +.It Va usmStats +The subtree contains statistics for the User-based Security Model PDU processing. +The statistics are reset each time the module is loaded. +.It Va usmUserSpinLock +An advisory lock used to coordinate several Command Generator Applications when +altering the SNMP USM users. +.It Va usmUserTable +The table contains all SNMP USM users configured in +.Nm bsnmpd. +The table contains the following objects +.Bl -tag -width ".It Va usmUserEngineID" +.It Va usmUserEngineID +An SNMP engine's administratively-unique identifier. Must be set to the same +Engine ID as +.Nm bsnmpd +so that the user will actually be allowed to communicate with the daemon. +The column is used as entry key and is not accessible for GET or SET operations. +.It Va usmUserName +The USM user name. The second entry key, again not accessible for GET or SET +operations. +.It Va usmUserSecurityName +The column has the exact same value as the +.Va usmUserName +column, however is accessible for GET operations. +.It Va usmUserCloneFrom +A GET on this column will return an empty OID. SET operations are currently not +supported. +.It Va usmUserAuthProtocol +The value of this column contains the OID corresponding to the authentication +protocol used by the USM user. The following protocols and their OIDs are known to +.Nm +module +.Bl -tag -width ".It Va NoAuthProtocol" +.It NoAuthProtocol 1.3.6.1.6.3.10.1.1.1 +.It HMACMD5AuthProtocol 1.3.6.1.6.3.10.1.1.2 +.It HMACSHAAuthProtocol 1.3.6.1.6.3.10.1.1.3 +.El +.It Va usmUserAuthKeyChange , Va usmUserOwnAuthKeyChange +These columns may be used to change the user's authentication key. +.It Va usmUserPrivProtocol +The value of this column contains the OID corresponding to the privacy +protocol used by the USM user. The following protocols and their OIDs are known to +.Nm +module +.Bl -tag -width ".It Va NoPrivProtocol" +.It NoPrivProtocol 1.3.6.1.6.3.10.1.2.1 +.It DESPrivProtoco 1.3.6.1.6.3.10.1.2.2 +.It AesCfb128Protocol 1.3.6.1.6.3.10.1.2.4 +.El +.It Va usmUserPrivKeyChange , Va usmUserOwnPrivKeyChange +These columns may be used to change the user's privacy key. +.It Va usmUserPublic +An arbitrary octet string that may be modified to confirm a SET operation on any +of the columns was successfull. +.It Va usmUserStorageType +This column always has either of two values. Entries created via +.Nm bsnmpd's +configuration file always have this column set to readOnly (5) and +it is not possible to modify those entries. Entries created by Command Generator +Applications always have this column set to volatile(2) and such entries are +lost when the module is restarted. A SET operation on this column is not +allowed. +.It Va usmUserStatus +This column is used to create new USM user entries or delete exsiting ones from +the table. +.El +.EL +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/snmp/defs/usm_tree.def +The description of the MIB tree implemented by +.Nm . +.El +.Sh SEE ALSO +.Xr bsnmpd 1 , +.Xr gensnmptree 1 , +.Xr snmpmod 3 +.Sh STANDARDS +IETF RFC 3414 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org Index: contrib/bsnmp/lib/bsnmpclient.3 =================================================================== --- contrib/bsnmp/lib/bsnmpclient.3 (revision 214335) +++ contrib/bsnmp/lib/bsnmpclient.3 (working copy) @@ -31,7 +31,7 @@ .\" .\" $Begemot: bsnmp/lib/bsnmpclient.3,v 1.12 2005/10/04 08:46:50 brandt_h Exp $ .\" -.Dd October 4, 2005 +.Dd September 9, 2010 .Dt BSNMPCLIENT 3 .Os .Sh NAME @@ -52,7 +52,8 @@ .Nm snmp_table_cb_f , .Nm snmp_table_fetch , .Nm snmp_table_fetch_async , -.Nm snmp_dialog +.Nm snmp_dialog , +.Nm snmp_discover_engine .Nd "SNMP client library" .Sh LIBRARY Begemot SNMP library @@ -102,44 +103,56 @@ .Fn snmp_table_fetch_async "const struct snmp_table *descr" "void *list" "snmp_table_cb_f callback" "void *uarg" .Ft int .Fn snmp_dialog "struct snmp_pdu *req" "struct snmp_pdu *resp" +.Ft int +.Fn snmp_discover_engine "void" .Sh DESCRIPTION The SNMP library contains routines to easily build SNMP client applications -that use SNMP versions 1 or 2. +that use SNMP versions 1, 2 or 3. Most of the routines use a .Vt struct snmp_client : .Bd -literal -offset indent struct snmp_client { - enum snmp_version version; - int trans; /* transport type to use */ + enum snmp_version version; + int trans; /* which transport to use */ /* these two are read-only for the application */ - char *cport; /* port number as string */ - char *chost; /* host name or IP address as string */ + char *cport; /* port number as string */ + char *chost; /* host name or IP address as string */ - char read_community[SNMP_COMMUNITY_MAXLEN + 1]; - char write_community[SNMP_COMMUNITY_MAXLEN + 1]; + char read_community[SNMP_COMMUNITY_MAXLEN + 1]; + char write_community[SNMP_COMMUNITY_MAXLEN + 1]; - struct timeval timeout; - u_int retries; + /* SNMPv3 specific fields */ + int32_t identifier; + int32_t security_model; + struct snmp_engine engine; + struct snmp_user user; - int dump_pdus; + /* SNMPv3 Access control - VACM*/ + uint32_t clen; + uint8_t cengine[SNMP_ENGINE_ID_SIZ]; + char cname[SNMP_CONTEXT_NAME_SIZ]; - size_t txbuflen; - size_t rxbuflen; + struct timeval timeout; + u_int retries; - int fd; + int dump_pdus; - int32_t next_reqid; - int32_t max_reqid; - int32_t min_reqid; + size_t txbuflen; + size_t rxbuflen; - char error[SNMP_STRERROR_LEN]; + int fd; - snmp_timeout_start_f timeout_start; - snmp_timeout_stop_f timeout_stop; + int32_t next_reqid; + int32_t max_reqid; + int32_t min_reqid; - /* private */ - char local_path[sizeof(SNMP_LOCAL_PATH)]; + char error[SNMP_STRERROR_LEN]; + + snmp_timeout_start_f timeout_start; + snmp_timeout_stop_f timeout_stop; + + char local_path[sizeof(SNMP_LOCAL_PATH)]; }; .Ed .Pp @@ -194,6 +207,23 @@ The community name to be used for SET requests. The default is .Sq private . +.It Va identifier +The message indentifier value to be used with SNMPv3 PDUs. Incremented with +each transmitted PDU. +.It Va security_model +The security model to be used with SNMPv3 PDUs. Currently only User-Based +Security model specified by RFC 3414 (value 3) is supported. +.It Va engine +The authorative SNMP engine parameters to be used with SNMPv3 PDUs. +.It Va user +The USM SNMP user credentials to be used with SNMPv3 PDUs. +.It Va clen +The length of the context engine id to be used with SNMPv3 PDUs. +.It Va cengine +The context engine id to be used with SNMPv3 PDUs. Default is empty. +.It Va cname +The context name to be used with SNMPv3 PDUs. Default is +.Sq "" . .It Va timeout The maximum time to wait for responses to requests. If the time elapses, the request is resent up to @@ -617,6 +647,21 @@ If a response was received 0 is returned. .Pp The function +.Fn snmp_discover_engine +is used to discover the authorative snmpEngineId of a remote SNMPv3 agent. +A request PDU with empty USM user name is sent and the client's engine +parameters are set according to the snmpEngine parameters received in the +response PDU. +If the client is configured to use authentication and/or privacy and the +snmpEngineBoots and/or snmpEngineTime in the response had zero values, an +additional request (possibly encrypted) with the appropriate user credentials +is sent to fetch the missing values. +Note, that the function blocks until the discovery proccess is completed. +If no response could be received after all timeouts and retries, or the +response contained errors the function returns -1. +If the discovery proccess was completed 0 is returned. +.Pp +The function .Fn snmp_parse_server is used to parse an SNMP server specification string and fill in the fields of a Index: contrib/bsnmp/lib/asn1.c =================================================================== --- contrib/bsnmp/lib/asn1.c (revision 214335) +++ contrib/bsnmp/lib/asn1.c (working copy) @@ -196,7 +196,7 @@ return (ret); } enum asn_err -asn_commit_header(struct asn_buf *b, u_char *ptr) +asn_commit_header(struct asn_buf *b, u_char *ptr, size_t *moved) { asn_len_t len; u_int lenlen, shift; @@ -215,6 +215,8 @@ memmove(ptr + 1 + lenlen, ptr + TEMP_LEN, len); b->asn_ptr -= shift; b->asn_len += shift; + if (moved != NULL) + *moved = shift; } return (ASN_ERR_OK); } @@ -913,6 +915,20 @@ } /* + * Add a padding + */ +enum asn_err +asn_pad(struct asn_buf *b, asn_len_t len) +{ + if (b->asn_len < len) + return (ASN_ERR_EOBUF); + b->asn_ptr += len; + b->asn_len -= len; + + return (ASN_ERR_OK); +} + +/* * Compare two OIDs. * * o1 < o2 : -1 Index: contrib/bsnmp/lib/asn1.h =================================================================== --- contrib/bsnmp/lib/asn1.h (revision 214335) +++ contrib/bsnmp/lib/asn1.h (working copy) @@ -93,7 +93,7 @@ enum asn_err asn_put_header(struct asn_buf *, u_char, asn_len_t); enum asn_err asn_put_temp_header(struct asn_buf *, u_char, u_char **); -enum asn_err asn_commit_header(struct asn_buf *, u_char *); +enum asn_err asn_commit_header(struct asn_buf *, u_char *, size_t *); enum asn_err asn_get_integer_raw(struct asn_buf *, asn_len_t, int32_t *); enum asn_err asn_get_integer(struct asn_buf *, int32_t *); @@ -129,6 +129,7 @@ enum asn_err asn_put_timeticks(struct asn_buf *, uint32_t); enum asn_err asn_skip(struct asn_buf *, asn_len_t); +enum asn_err asn_pad(struct asn_buf *, asn_len_t); /* * Utility functions for OIDs Index: contrib/bsnmp/lib/snmpclient.c =================================================================== --- contrib/bsnmp/lib/snmpclient.c (revision 214335) +++ contrib/bsnmp/lib/snmpclient.c (working copy) @@ -852,6 +852,9 @@ strcpy(c->read_community, "public"); strcpy(c->write_community, "private"); + + c->security_model = SNMP_SECMODEL_USM; + strcpy(c->cname, ""); c->timeout.tv_sec = 3; c->timeout.tv_usec = 0; @@ -864,6 +867,8 @@ c->max_reqid = INT32_MAX; c->min_reqid = 0; c->next_reqid = 0; + + c->engine.max_msg_size = 1500; /* XXX */ } @@ -1132,7 +1137,8 @@ void snmp_pdu_create(struct snmp_pdu *pdu, u_int op) { - memset(pdu,0,sizeof(struct snmp_pdu)); + memset(pdu, 0, sizeof(struct snmp_pdu)); + if (op == SNMP_PDU_SET) strlcpy(pdu->community, snmp_client.write_community, sizeof(pdu->community)); @@ -1145,6 +1151,33 @@ pdu->error_status = 0; pdu->error_index = 0; pdu->nbindings = 0; + + if (snmp_client.version != SNMP_V3) + return; + + pdu->identifier = ++snmp_client.identifier; + pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; + pdu->flags = 0; + pdu->security_model = snmp_client.security_model; + + if (snmp_client.security_model == SNMP_SECMODEL_USM) + snmp_pdu_init_secparams(pdu, &snmp_client.engine, + &snmp_client.user); + else + seterr(&snmp_client, "unknown security model"); + + if (snmp_client.clen > 0) { + memcpy(pdu->context_engine, snmp_client.cengine, + snmp_client.clen); + pdu->context_engine_len = snmp_client.clen; + } else { + memcpy(pdu->context_engine, snmp_client.engine.engine_id, + snmp_client.engine.engine_len); + pdu->context_engine_len = snmp_client.engine.engine_len; + } + + strlcpy(pdu->context_name, snmp_client.cname, + sizeof(pdu->context_name)); } /* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ @@ -1406,15 +1439,24 @@ abuf.asn_ptr = buf; abuf.asn_len = ret; + memset(pdu, 0, sizeof(*pdu)); + if (snmp_client.security_model == SNMP_SECMODEL_USM) + snmp_pdu_init_secparams(pdu, &snmp_client.engine, + &snmp_client.user); + if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); free(buf); return (-1); } + free(buf); if (snmp_client.dump_pdus) snmp_pdu_dump(pdu); + snmp_client.engine.engine_time = pdu->engine.engine_time; + snmp_client.engine.engine_boots = pdu->engine.engine_boots; + return (+1); } @@ -1685,6 +1727,93 @@ } int +snmp_discover_engine(char *passwd) +{ + char cname[SNMP_ADM_STR32_SIZ]; + enum snmp_authentication cap; + enum snmp_privacy cpp; + struct snmp_pdu req, resp; + + if (snmp_client.version != SNMP_V3) + seterr(&snmp_client, "wrong version"); + + strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); + cap = snmp_client.user.auth_proto; + cpp = snmp_client.user.priv_proto; + + snmp_client.engine.engine_len = 0; + snmp_client.engine.engine_boots = 0; + snmp_client.engine.engine_time = 0; + snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; + snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; + memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); + + snmp_pdu_create(&req, SNMP_PDU_GET); + + if (snmp_dialog(&req, &resp) == -1) + return (-1); + + if (resp.version != req.version) { + seterr(&snmp_client, "wrong version"); + return (-1); + } + + if (resp.error_status != SNMP_ERR_NOERROR) { + seterr(&snmp_client, "Error %d in responce", resp.error_status); + return (-1); + } + + snmp_client.engine.engine_len = resp.engine.engine_len; + snmp_client.engine.max_msg_size = resp.engine.max_msg_size; + memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, + resp.engine.engine_len); + + strlcpy(snmp_client.user.sec_name, cname, + sizeof(snmp_client.user.sec_name)); + snmp_client.user.auth_proto = cap; + snmp_client.user.priv_proto = cpp; + + if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) + return (0); + + if (passwd == NULL || + snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || + snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, + snmp_client.engine.engine_len) != SNMP_CODE_OK) + return (-1); + + if (resp.engine.engine_boots != 0) + snmp_client.engine.engine_boots = resp.engine.engine_boots; + + if (resp.engine.engine_time != 0) { + snmp_client.engine.engine_time = resp.engine.engine_time; + return (0); + } + + snmp_pdu_create(&req, SNMP_PDU_GET); + req.engine.engine_boots = 0; + req.engine.engine_time = 0; + + if (snmp_dialog(&req, &resp) == -1) + return (-1); + + if (resp.version != req.version) { + seterr(&snmp_client, "wrong version"); + return (-1); + } + + if (resp.error_status != SNMP_ERR_NOERROR) { + seterr(&snmp_client, "Error %d in responce", resp.error_status); + return (-1); + } + + snmp_client.engine.engine_boots = resp.engine.engine_boots; + snmp_client.engine.engine_time = resp.engine.engine_time; + + return (0); +} + +int snmp_client_set_host(struct snmp_client *cl, const char *h) { char *np; Index: contrib/bsnmp/lib/snmp.c =================================================================== --- contrib/bsnmp/lib/snmp.c (revision 214335) +++ contrib/bsnmp/lib/snmp.c (working copy) @@ -5,6 +5,12 @@ * * Author: Harti Brandt * + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Shteryana Sotirova Shopova + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -271,47 +277,310 @@ return (err); } + +static enum asn_err +parse_secparams(struct asn_buf *b, struct snmp_pdu *pdu) +{ + asn_len_t octs_len; + u_char buf[256]; /* XXX: calc max possible size here */ + struct asn_buf tb; + + memset(buf, 0, 256); + tb.asn_ptr = buf; + tb.asn_len = 256; + + if (asn_get_octetstring(b, buf, &tb.asn_len) != ASN_ERR_OK) { + snmp_error("cannot parse usm header"); + return (ASN_ERR_FAILED); + } + + if (asn_get_sequence(&tb, &octs_len) != ASN_ERR_OK) { + snmp_error("cannot decode usm header"); + return (ASN_ERR_FAILED); + } + + octs_len = SNMP_ENGINE_ID_SIZ; + if (asn_get_octetstring(&tb, (u_char *)&pdu->engine.engine_id, + &octs_len) != ASN_ERR_OK) { + snmp_error("cannot decode msg engine id"); + return (ASN_ERR_FAILED); + } + pdu->engine.engine_len = octs_len; + + if (asn_get_integer(&tb, &pdu->engine.engine_boots) != ASN_ERR_OK) { + snmp_error("cannot decode msg engine boots"); + return (ASN_ERR_FAILED); + } + + if (asn_get_integer(&tb, &pdu->engine.engine_time) != ASN_ERR_OK) { + snmp_error("cannot decode msg engine time"); + return (ASN_ERR_FAILED); + } + + octs_len = SNMP_ADM_STR32_SIZ - 1; + if (asn_get_octetstring(&tb, (u_char *)&pdu->user.sec_name, &octs_len) + != ASN_ERR_OK) { + snmp_error("cannot decode msg user name"); + return (ASN_ERR_FAILED); + } + pdu->user.sec_name[octs_len] = '\0'; + + octs_len = sizeof(pdu->msg_digest); + if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_digest, &octs_len) != + ASN_ERR_OK || ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0 && + octs_len != sizeof(pdu->msg_digest))) { + snmp_error("cannot decode msg authentication param"); + return (ASN_ERR_FAILED); + } + + octs_len = sizeof(pdu->msg_salt); + if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_salt, &octs_len) != + ASN_ERR_OK ||((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && + octs_len != sizeof(pdu->msg_salt))) { + snmp_error("cannot decode msg authentication param"); + return (ASN_ERR_FAILED); + } + + if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { + pdu->digest_ptr = b->asn_ptr - SNMP_USM_AUTH_SIZE; + pdu->digest_ptr -= octs_len + ASN_MAXLENLEN; + } + + return (ASN_ERR_OK); +} + +static enum snmp_code +pdu_encode_secparams(struct asn_buf *b, struct snmp_pdu *pdu) +{ + u_char buf[256], *sptr; + struct asn_buf tb; + size_t auth_off, moved = 0; + + auth_off = 0; + memset(buf, 0, 256); + tb.asn_ptr = buf; + tb.asn_len = 256; + + if (asn_put_temp_header(&tb, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), + &sptr) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_octetstring(&tb, (u_char *)pdu->engine.engine_id, + pdu->engine.engine_len) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_integer(&tb, pdu->engine.engine_boots) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_integer(&tb, pdu->engine.engine_time) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_octetstring(&tb, (u_char *)pdu->user.sec_name, + strlen(pdu->user.sec_name)) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { + auth_off = sizeof(buf) - tb.asn_len + ASN_MAXLENLEN; + if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, + sizeof(pdu->msg_digest)) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } else { + if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, 0) + != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } + + if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0) { + if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, + sizeof(pdu->msg_salt)) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } else { + if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, 0) + != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } + + if (asn_commit_header(&tb, sptr, &moved) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) + pdu->digest_ptr = b->asn_ptr + auth_off - moved; + + if (asn_put_octetstring(b, buf, sizeof(buf) - tb.asn_len) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + pdu->digest_ptr += ASN_MAXLENLEN; + + if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && asn_put_temp_header(b, + ASN_TYPE_OCTETSTRING, &pdu->encrypted_ptr) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + return (SNMP_CODE_OK); +} + /* - * Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'. + * Decode the PDU except for the variable bindings itself. + * If decoding fails because of a bad binding, but the rest can be + * decoded, ip points to the index of the failed variable (errors + * OORANGE, BADLEN or BADVERS). */ -enum asn_err -snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) +enum snmp_code +snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { + enum snmp_code code; + + if ((code = snmp_pdu_decode_header(b, pdu)) != SNMP_CODE_OK) + return (code); + + if (pdu->version == SNMP_V3) { + if (pdu->security_model != SNMP_SECMODEL_USM) + return (SNMP_CODE_FAILED); + if ((code = snmp_pdu_decode_secmode(b, pdu)) != SNMP_CODE_OK) + return (code); + } + + code = snmp_pdu_decode_scoped(b, pdu, ip); + + switch (code) { + case SNMP_CODE_FAILED: + snmp_pdu_free(pdu); + break; + + case SNMP_CODE_BADENC: + if (pdu->version == SNMP_Verr) + return (SNMP_CODE_BADVERS); + + default: + break; + } + + return (code); +} + +enum snmp_code +snmp_pdu_decode_header(struct asn_buf *b, struct snmp_pdu *pdu) +{ int32_t version; - u_char type; - u_int comm_len; + u_int octs_len; + asn_len_t len; + pdu->outer_ptr = b->asn_ptr; + pdu->outer_len = b->asn_len; + + if (asn_get_sequence(b, &len) != ASN_ERR_OK) { + snmp_error("cannot decode pdu header"); + return (SNMP_CODE_FAILED); + } + if (b->asn_len < len) { + snmp_error("outer sequence value too short"); + return (SNMP_CODE_FAILED); + } + if (b->asn_len != len) { + snmp_error("ignoring trailing junk in message"); + b->asn_len = len; + } + if (asn_get_integer(b, &version) != ASN_ERR_OK) { snmp_error("cannot decode version"); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } - if (version == 0) { + if (version == 0) pdu->version = SNMP_V1; - } else if (version == 1) { + else if (version == 1) pdu->version = SNMP_V2c; - } else { + else if (version == 3) + pdu->version = SNMP_V3; + else { pdu->version = SNMP_Verr; snmp_error("unsupported SNMP version"); - return (ASN_ERR_TAG); + return (SNMP_CODE_BADENC); } - comm_len = SNMP_COMMUNITY_MAXLEN; - if (asn_get_octetstring(b, (u_char *)pdu->community, - &comm_len) != ASN_ERR_OK) { - snmp_error("cannot decode community"); - return (ASN_ERR_FAILED); + if (pdu->version == SNMP_V3) { + if (asn_get_sequence(b, &len) != ASN_ERR_OK) { + snmp_error("cannot decode pdu global data header"); + return (SNMP_CODE_FAILED); + } + + if (asn_get_integer(b, &pdu->identifier) != ASN_ERR_OK) { + snmp_error("cannot decode msg indetifier"); + return (SNMP_CODE_FAILED); + } + + if (asn_get_integer(b, &pdu->engine.max_msg_size) + != ASN_ERR_OK) { + snmp_error("cannot decode msg size"); + return (SNMP_CODE_FAILED); + } + + octs_len = 1; + if (asn_get_octetstring(b, (u_char *)&pdu->flags, + &octs_len) != ASN_ERR_OK) { + snmp_error("cannot decode msg flags"); + return (SNMP_CODE_FAILED); + } + + if (asn_get_integer(b, &pdu->security_model) != ASN_ERR_OK) { + snmp_error("cannot decode msg size"); + return (SNMP_CODE_FAILED); + } + + if (pdu->security_model != SNMP_SECMODEL_USM) + return (SNMP_CODE_FAILED); + + if (parse_secparams(b, pdu) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } else { + octs_len = SNMP_COMMUNITY_MAXLEN; + if (asn_get_octetstring(b, (u_char *)pdu->community, + &octs_len) != ASN_ERR_OK) { + snmp_error("cannot decode community"); + return (SNMP_CODE_FAILED); + } + pdu->community[octs_len] = '\0'; } - pdu->community[comm_len] = '\0'; - if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) { + return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_pdu_decode_scoped(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) +{ + u_char type; + asn_len_t len, trailer; + enum asn_err err; + + if (pdu->version == SNMP_V3) { + if (asn_get_sequence(b, &len) != ASN_ERR_OK) { + snmp_error("cannot decode scoped pdu header"); + return (SNMP_CODE_FAILED); + } + + len = SNMP_ENGINE_ID_SIZ; + if (asn_get_octetstring(b, (u_char *)&pdu->context_engine, + &len) != ASN_ERR_OK) { + snmp_error("cannot decode msg context engine"); + return (SNMP_CODE_FAILED); + } + pdu->context_engine_len = len; + + len = SNMP_CONTEXT_NAME_SIZ; + if (asn_get_octetstring(b, (u_char *)&pdu->context_name, + &len) != ASN_ERR_OK) { + snmp_error("cannot decode msg context name"); + return (SNMP_CODE_FAILED); + } + pdu->context_name[len] = '\0'; + } + + if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot get pdu header"); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } if ((type & ~ASN_TYPE_MASK) != (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { snmp_error("bad pdu header tag"); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } pdu->type = type & ASN_TYPE_MASK; @@ -326,7 +595,7 @@ case SNMP_PDU_TRAP: if (pdu->version != SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } break; @@ -336,99 +605,64 @@ case SNMP_PDU_REPORT: if (pdu->version == SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } break; default: snmp_error("bad pdu type %u", pdu->type); - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); } - - if (*lenp > b->asn_len) { - snmp_error("pdu length too long"); - return (ASN_ERR_FAILED); - } - - return (ASN_ERR_OK); -} - -static enum asn_err -parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) -{ - enum asn_err err; - asn_len_t len, trailer; - - err = snmp_parse_message_hdr(b, pdu, &len); - if (ASN_ERR_STOPPED(err)) - return (err); - trailer = b->asn_len - len; b->asn_len = len; err = parse_pdus(b, pdu, ip); if (ASN_ERR_STOPPED(err)) - return (ASN_ERR_FAILED); + return (SNMP_CODE_FAILED); if (b->asn_len != 0) snmp_error("ignoring trailing junk after pdu"); b->asn_len = trailer; - return (err); + return (SNMP_CODE_OK); } -/* - * Decode the PDU except for the variable bindings itself. - * If decoding fails because of a bad binding, but the rest can be - * decoded, ip points to the index of the failed variable (errors - * OORANGE, BADLEN or BADVERS). - */ enum snmp_code -snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) +snmp_pdu_decode_secmode(struct asn_buf *b, struct snmp_pdu *pdu) { - asn_len_t len; + u_char type; + enum snmp_code code; + uint8_t digest[SNMP_USM_AUTH_SIZE]; - memset(pdu, 0, sizeof(*pdu)); + if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && + (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) + return (SNMP_CODE_BADSECLEVEL); - if (asn_get_sequence(b, &len) != ASN_ERR_OK) { - snmp_error("cannot decode pdu header"); + if ((code = snmp_pdu_calc_digest(b, pdu, digest)) != + SNMP_CODE_OK) return (SNMP_CODE_FAILED); - } - if (b->asn_len < len) { - snmp_error("outer sequence value too short"); + + if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && + memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0) + return (SNMP_CODE_BADDIGEST); + + if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(b, &type, + &pdu->scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) { + snmp_error("cannot decode encrypted pdu"); return (SNMP_CODE_FAILED); } - if (b->asn_len != len) { - snmp_error("ignoring trailing junk in message"); - b->asn_len = len; - } + pdu->scoped_ptr = b->asn_ptr; - switch (parse_message(b, pdu, ip)) { + if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && + (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0) + return (SNMP_CODE_BADSECLEVEL); - case ASN_ERR_OK: - return (SNMP_CODE_OK); - - case ASN_ERR_FAILED: - case ASN_ERR_EOBUF: - snmp_pdu_free(pdu); + if ((code = snmp_pdu_decrypt(b, pdu)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); - case ASN_ERR_BADLEN: - return (SNMP_CODE_BADLEN); - - case ASN_ERR_RANGE: - return (SNMP_CODE_OORANGE); - - case ASN_ERR_TAG: - if (pdu->version == SNMP_Verr) - return (SNMP_CODE_BADVERS); - else - return (SNMP_CODE_BADENC); - } - - return (SNMP_CODE_OK); + return (code); } /* @@ -500,6 +734,7 @@ snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu) { enum asn_err err; + u_char *v3_hdr_ptr; if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &pdu->outer_ptr) != ASN_ERR_OK) @@ -509,15 +744,63 @@ err = asn_put_integer(b, 0); else if (pdu->version == SNMP_V2c) err = asn_put_integer(b, 1); + else if (pdu->version == SNMP_V3) + err = asn_put_integer(b, 3); else return (SNMP_CODE_BADVERS); if (err != ASN_ERR_OK) return (SNMP_CODE_FAILED); - if (asn_put_octetstring(b, (u_char *)pdu->community, - strlen(pdu->community)) != ASN_ERR_OK) - return (SNMP_CODE_FAILED); + if (pdu->version == SNMP_V3) { + if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | + ASN_TYPE_CONSTRUCTED), &v3_hdr_ptr) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_integer(b, pdu->identifier) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + if (asn_put_integer(b, pdu->engine.max_msg_size) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (pdu->type != SNMP_PDU_RESPONSE && + pdu->type != SNMP_PDU_TRAP && + pdu->type != SNMP_PDU_REPORT) + pdu->flags |= SNMP_MSG_REPORT_FLAG; + + if (asn_put_octetstring(b, (u_char *)&pdu->flags, 1) + != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_integer(b, pdu->security_model) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_commit_header(b, v3_hdr_ptr, NULL) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (pdu->security_model != SNMP_SECMODEL_USM) + return (SNMP_CODE_FAILED); + + if (pdu_encode_secparams(b, pdu) != SNMP_CODE_OK) + return (SNMP_CODE_FAILED); + + /* View-based Access Conntrol information */ + if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | + ASN_TYPE_CONSTRUCTED), &pdu->scoped_ptr) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_octetstring(b, (u_char *)pdu->context_engine, + pdu->context_engine_len) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (asn_put_octetstring(b, (u_char *)pdu->context_name, + strlen(pdu->context_name)) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } else { + if (asn_put_octetstring(b, (u_char *)pdu->community, + strlen(pdu->community)) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } + if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT | pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); @@ -550,13 +833,66 @@ return (SNMP_CODE_OK); } +static enum asn_err +snmp_pdu_fix_padd(struct asn_buf *b, struct snmp_pdu *pdu) +{ + asn_len_t padlen; + + if (pdu->user.priv_proto == SNMP_PRIV_DES && pdu->scoped_len % 8 != 0) { + padlen = 8 - (pdu->scoped_len % 8); + if (asn_pad(b, padlen) != ASN_ERR_OK) + return (ASN_ERR_FAILED); + pdu->scoped_len += padlen; + } + + return (ASN_ERR_OK); +} + enum snmp_code -snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu) +snmp_fix_encoding(struct asn_buf *b, struct snmp_pdu *pdu) { - if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK || - asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK || - asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK) + size_t moved = 0; + enum snmp_code code; + + if (asn_commit_header(b, pdu->vars_ptr, NULL) != ASN_ERR_OK || + asn_commit_header(b, pdu->pdu_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); + + if (pdu->version == SNMP_V3) { + if (asn_commit_header(b, pdu->scoped_ptr, NULL) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + pdu->scoped_len = b->asn_ptr - pdu->scoped_ptr; + if ((code = snmp_pdu_fix_padd(b, pdu))!= ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + if (pdu->security_model != SNMP_SECMODEL_USM) + return (SNMP_CODE_FAILED); + + if (snmp_pdu_encrypt(b, pdu) != SNMP_CODE_OK) + return (SNMP_CODE_FAILED); + + if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && + asn_commit_header(b, pdu->encrypted_ptr, NULL) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + } + + if (asn_commit_header(b, pdu->outer_ptr, &moved) != ASN_ERR_OK) + return (SNMP_CODE_FAILED); + + pdu->outer_len = b->asn_ptr - pdu->outer_ptr; + pdu->digest_ptr -= moved; + + if (pdu->version == SNMP_V3) { + if ((code = snmp_pdu_calc_digest(b, pdu, pdu->msg_digest)) != + SNMP_CODE_OK) + return (SNMP_CODE_FAILED); + + if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) + memcpy(pdu->digest_ptr, pdu->msg_digest, + sizeof(pdu->msg_digest)); + } + return (SNMP_CODE_OK); } @@ -639,7 +975,7 @@ return (err); } - err = asn_commit_header(b, ptr); + err = asn_commit_header(b, ptr, NULL); if (err != ASN_ERR_OK) { *b = save; return (err); @@ -775,6 +1111,8 @@ vers = "SNMPv1"; else if (pdu->version == SNMP_V2c) vers = "SNMPv2c"; + else if (pdu->version == SNMP_V3) + vers = "SNMPv3"; else vers = "v?"; @@ -838,6 +1176,39 @@ } void +snmp_pdu_init_secparams(struct snmp_pdu *pdu, struct snmp_engine *eng, + struct snmp_user *user) +{ + int32_t rval; + + memcpy(&pdu->engine, eng, sizeof(pdu->engine)); + memcpy(&pdu->user, user, sizeof(pdu->user)); + + if (user->auth_proto != SNMP_AUTH_NOAUTH) + pdu->flags |= SNMP_MSG_AUTH_FLAG; + + switch (user->priv_proto) { + case SNMP_PRIV_DES: + memcpy(pdu->msg_salt, &eng->engine_boots, + sizeof(eng->engine_boots)); + rval = random(); + memcpy(pdu->msg_salt + sizeof(eng->engine_boots), &rval, + sizeof(int32_t)); + pdu->flags |= SNMP_MSG_PRIV_FLAG; + break; + case SNMP_PRIV_AES: + rval = random(); + memcpy(pdu->msg_salt, &rval, sizeof(int32_t)); + rval = random(); + memcpy(pdu->msg_salt + sizeof(int32_t), &rval, sizeof(int32_t)); + pdu->flags |= SNMP_MSG_PRIV_FLAG; + break; + default: + break; + } +} + +void snmp_pdu_free(struct snmp_pdu *pdu) { u_int i; Index: contrib/bsnmp/lib/snmpcrypto.c =================================================================== --- contrib/bsnmp/lib/snmpcrypto.c (revision 0) +++ contrib/bsnmp/lib/snmpcrypto.c (revision 0) @@ -0,0 +1,404 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#elif defined(HAVE_INTTYPES_H) +#include +#endif +#include +#include +#include +#include + +#ifdef HAVE_LIBCRYPTO +#include +#endif + +#include "asn1.h" +#include "snmp.h" +#include "snmppriv.h" + +#define SNMP_PRIV_AES_IV_SIZ 16 +#define SNMP_EXTENDED_KEY_SIZ 64 +#define SNMP_AUTH_KEY_LOOPCNT 1048576 +#define SNMP_AUTH_BUF_SIZE 72 + +static const uint8_t ipad = 0x36; +static const uint8_t opad = 0x5c; + +#ifdef HAVE_LIBCRYPTO + +static int32_t +snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx, + const EVP_MD **dtype, uint32_t *keylen) +{ + if (user->auth_proto == SNMP_AUTH_HMAC_MD5) { + *dtype = EVP_md5(); + *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; + } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) { + *dtype = EVP_sha1(); + *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; + } else if (user->auth_proto == SNMP_AUTH_NOAUTH) + return (0); + else { + snmp_error("unknown authentication option - %d", + user->auth_proto); + return (-1); + } + + if (EVP_DigestInit(ctx, *dtype) != 1) + return (-1); + + return (1); +} + +enum snmp_code +snmp_pdu_calc_digest(struct asn_buf *b, const struct snmp_pdu *pdu, + uint8_t *digest) +{ + uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ]; + uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ]; + uint32_t i, keylen, olen; + int32_t err; + const EVP_MD *dtype; + EVP_MD_CTX ctx; + + err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen); + if (err < 0) + return (SNMP_CODE_BADDIGEST); + else if (err == 0) + return (SNMP_CODE_OK); + + memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest)); + memcpy(extkey, pdu->user.auth_key, keylen); + memset(extkey + keylen, 0, sizeof(extkey) - keylen); + + for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) { + key1[i] = extkey[i] ^ ipad; + key2[i] = extkey[i] ^ opad; + } + + if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 || + EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 || + EVP_DigestFinal(&ctx, md, &olen) != 1) + goto failed; + + if (EVP_DigestInit(&ctx, dtype) != 1 || + EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 || + EVP_DigestUpdate(&ctx, md, olen) != 1 || + EVP_DigestFinal(&ctx, md, &olen) != 1) + goto failed; + + if (olen < SNMP_USM_AUTH_SIZE) { + snmp_error("bad digest size - %d", olen); + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_BADDIGEST); + } + + memcpy(digest, md, SNMP_USM_AUTH_SIZE); + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_OK); + +failed: + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_BADDIGEST); +} + +static int32_t +snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len, + EVP_CIPHER_CTX *ctx, const EVP_CIPHER **ctype, uint8_t *piv) +{ + int i; + uint32_t netint; + + if (pdu->user.priv_proto == SNMP_PRIV_DES) { + if (len % 8 != 0) + return (-1); + *ctype = EVP_des_cbc(); + memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); + for (i = 0; i < 8; i++) + piv[i] = piv[i] ^ pdu->user.priv_key[8 + i]; + } else if (pdu->user.priv_proto == SNMP_PRIV_AES) { + *ctype = EVP_aes_128_cfb128(); + netint = htonl(pdu->engine.engine_boots); + memcpy(piv, &netint, sizeof(netint)); + piv += sizeof(netint); + netint = htonl(pdu->engine.engine_time); + memcpy(piv, &netint, sizeof(netint)); + piv += sizeof(netint); + memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); + } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV) + return (0); + else { + snmp_error("unknown privacy option - %d", pdu->user.priv_proto); + return (-1); + } + + return (1); +} + +enum snmp_code +snmp_pdu_encrypt(struct asn_buf *b, const struct snmp_pdu *pdu) +{ + int32_t err, olen; + uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; + const EVP_CIPHER *ctype; + EVP_CIPHER_CTX ctx; + + err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctx, &ctype, iv); + if (err < 0) + return (SNMP_CODE_EDECRYPT); + else if (err == 0) + return (SNMP_CODE_OK); + + if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1) + return (SNMP_CODE_FAILED); + + if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, + pdu->scoped_len) != 1 || + EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { + EVP_CIPHER_CTX_cleanup(&ctx); + return (SNMP_CODE_FAILED); + } + + EVP_CIPHER_CTX_cleanup(&ctx); + return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_pdu_decrypt(struct asn_buf *b, const struct snmp_pdu *pdu) +{ + int32_t err, olen; + uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; + const EVP_CIPHER *ctype; + EVP_CIPHER_CTX ctx; + + err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctx, &ctype, iv); + if (err < 0) + return (SNMP_CODE_EDECRYPT); + else if (err == 0) + return (SNMP_CODE_OK); + + if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 || + EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1) + return (SNMP_CODE_EDECRYPT); + + if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, + pdu->scoped_len) != 1 || + EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { + EVP_CIPHER_CTX_cleanup(&ctx); + return (SNMP_CODE_EDECRYPT); + } + + EVP_CIPHER_CTX_cleanup(&ctx); + return (SNMP_CODE_OK); +} + +/* [RFC 3414] - A.2. Password to Key Algorithm */ +enum snmp_code +snmp_passwd_to_keys(struct snmp_user *user, char *passwd) +{ + int err, loop, i, pwdlen; + uint32_t keylen, olen; + const EVP_MD *dtype; + EVP_MD_CTX ctx; + uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; + + if (passwd == NULL || user == NULL) + return (SNMP_CODE_FAILED); + + err = snmp_digest_init(user, &ctx, &dtype, &keylen); + if (err < 0) + return (SNMP_CODE_BADDIGEST); + else if (err == 0) + return (SNMP_CODE_OK); + + memset(user->auth_key, 0, sizeof(user->auth_key)); + pwdlen = strlen(passwd); + + for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) { + for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) + authbuf[i] = passwd[(loop + i) % pwdlen]; + if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1) + goto failed; + } + + if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) + goto failed; + + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_OK); + +failed: + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_BADDIGEST); +} + +/* [RFC 3414] - 2.6. Key Localization Algorithm */ +enum snmp_code +snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen) +{ + int err; + uint32_t keylen, olen; + const EVP_MD *dtype; + EVP_MD_CTX ctx; + uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; + + if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ) + return (SNMP_CODE_FAILED); + + memset(user->priv_key, 0, sizeof(user->priv_key)); + memset(authbuf, 0, sizeof(authbuf)); + + err = snmp_digest_init(user, &ctx, &dtype, &keylen); + if (err < 0) + return (SNMP_CODE_BADDIGEST); + else if (err == 0) + return (SNMP_CODE_OK); + + memcpy(authbuf, user->auth_key, keylen); + memcpy(authbuf + keylen, eid, elen); + memcpy(authbuf + keylen + elen, user->auth_key, keylen); + + if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 || + EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) { + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_BADDIGEST); + } + EVP_MD_CTX_cleanup(&ctx); + + if (user->priv_proto != SNMP_PRIV_NOPRIV) + memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key)); + + return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange) +{ + int32_t i, err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4]; + uint32_t keylen, olen; + const EVP_MD *dtype; + EVP_MD_CTX ctx; + + err = snmp_digest_init(user, &ctx, &dtype, &keylen); + if (err < 0) + return (SNMP_CODE_BADDIGEST); + else if (err == 0) + return (SNMP_CODE_OK); + + for (i = 0; i < keylen / 4; i++) + rvalue[i] = random(); + + memcpy(keychange, user->auth_key, keylen); + memcpy(keychange + keylen, rvalue, keylen); + + if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 || + EVP_DigestFinal(&ctx, keychange, &olen) != 1) { + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_BADDIGEST); + } + + EVP_MD_CTX_cleanup(&ctx); + return (SNMP_CODE_OK); +} + +#else /* !HAVE_LIBCRYPTO */ + +enum snmp_code +snmp_pdu_calc_digest(struct asn_buf *b __unused, const struct snmp_pdu *pdu, + uint8_t *digest __unused) +{ + if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) + return (SNMP_CODE_BADSECLEVEL); + + + return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_pdu_encrypt(struct asn_buf *b __unused, const struct snmp_pdu *pdu) +{ + if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) + return (SNMP_CODE_BADSECLEVEL); + + return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_pdu_decrypt(struct asn_buf *b __unused, const struct snmp_pdu *pdu) +{ + if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) + return (SNMP_CODE_BADSECLEVEL); + + return (SNMP_CODE_OK); +} + +int +snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused) +{ + if (user->auth_proto == SNMP_AUTH_NOAUTH && + user->priv_proto == SNMP_PRIV_NOPRIV) + return (SNMP_CODE_OK); + + errno = EPROTONOSUPPORT; + + return (SNMP_CODE_FAILED); +} + +int +snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused, + uint32_t elen __unused) +{ + if (user->auth_proto == SNMP_AUTH_NOAUTH && + user->priv_proto == SNMP_PRIV_NOPRIV) + return (SNMP_CODE_OK); + + errno = EPROTONOSUPPORT; + + return (SNMP_CODE_FAILED); +} + +enum snmp_code +snmp_calc_keychange(struct snmp_user *user __unused, + uint8_t *keychange __unused) +{ + errno = EPROTONOSUPPORT; + return (SNMP_CODE_FAILED); +} + +#endif /* HAVE_LIBCRYPTO */ Index: contrib/bsnmp/lib/snmpclient.h =================================================================== --- contrib/bsnmp/lib/snmpclient.h (revision 214335) +++ contrib/bsnmp/lib/snmpclient.h (working copy) @@ -69,36 +69,47 @@ * Client context. */ struct snmp_client { - enum snmp_version version; - int trans; /* which transport to use */ + enum snmp_version version; + int trans; /* which transport to use */ /* these two are read-only for the application */ - char *cport; /* port number as string */ - char *chost; /* host name or IP address as string */ + char *cport; /* port number as string */ + char *chost; /* host name or IP address as string */ - char read_community[SNMP_COMMUNITY_MAXLEN + 1]; - char write_community[SNMP_COMMUNITY_MAXLEN + 1]; + char read_community[SNMP_COMMUNITY_MAXLEN + 1]; + char write_community[SNMP_COMMUNITY_MAXLEN + 1]; - struct timeval timeout; - u_int retries; + /* SNMPv3 specific fields */ + int32_t identifier; + int32_t security_model; + struct snmp_engine engine; + struct snmp_user user; - int dump_pdus; + /* SNMPv3 Access control - VACM*/ + uint32_t clen; + uint8_t cengine[SNMP_ENGINE_ID_SIZ]; + char cname[SNMP_CONTEXT_NAME_SIZ]; - size_t txbuflen; - size_t rxbuflen; + struct timeval timeout; + u_int retries; - int fd; + int dump_pdus; - int32_t next_reqid; - int32_t max_reqid; - int32_t min_reqid; + size_t txbuflen; + size_t rxbuflen; - char error[SNMP_STRERROR_LEN]; + int fd; - snmp_timeout_start_f timeout_start; - snmp_timeout_stop_f timeout_stop; + int32_t next_reqid; + int32_t max_reqid; + int32_t min_reqid; - char local_path[sizeof(SNMP_LOCAL_PATH)]; + char error[SNMP_STRERROR_LEN]; + + snmp_timeout_start_f timeout_start; + snmp_timeout_stop_f timeout_stop; + + char local_path[sizeof(SNMP_LOCAL_PATH)]; }; /* the global context */ @@ -181,6 +192,9 @@ /* send a request and wait for the response */ int snmp_dialog(struct snmp_pdu *_req, struct snmp_pdu *_resp); +/* discover an authorative snmpEngineId */ +int snmp_discover_engine(char *); + /* parse a server specification */ int snmp_parse_server(struct snmp_client *, const char *); Index: contrib/bsnmp/lib/snmp.h =================================================================== --- contrib/bsnmp/lib/snmp.h (revision 214335) +++ contrib/bsnmp/lib/snmp.h (working copy) @@ -5,6 +5,13 @@ * * Author: Harti Brandt * + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Shteryana Sotirova Shopova + * under sponsorship from the FreeBSD Foundation. + * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,8 +42,11 @@ #include -#define SNMP_COMMUNITY_MAXLEN 128 -#define SNMP_MAX_BINDINGS 100 +#define SNMP_COMMUNITY_MAXLEN 128 +#define SNMP_MAX_BINDINGS 100 +#define SNMP_CONTEXT_NAME_SIZ (32 + 1) +#define SNMP_ENGINE_ID_SIZ 32 +#define SNMP_TIME_WINDOW 150 enum snmp_syntax { SNMP_SYNTAX_NULL = 0, @@ -75,33 +85,110 @@ enum snmp_version { SNMP_Verr = 0, SNMP_V1 = 1, - SNMP_V2c, + SNMP_V2c = 2, + SNMP_V3, }; +#define SNMP_ADM_STR32_SIZ (32 + 1) +#define SNMP_AUTH_KEY_SIZ 40 +#define SNMP_PRIV_KEY_SIZ 32 +#define SNMP_USM_AUTH_SIZE 12 +#define SNMP_USM_PRIV_SIZE 8 +#define SNMP_AUTH_HMACMD5_KEY_SIZ 16 +#define SNMP_AUTH_HMACSHA_KEY_SIZ 20 +#define SNMP_PRIV_AES_KEY_SIZ 16 +#define SNMP_PRIV_DES_KEY_SIZ 8 + + +enum snmp_secmodel { + SNMP_SECMODEL_ANY = 0, + SNMP_SECMODEL_SNMPv1 = 1, + SNMP_SECMODEL_SNMPv2c = 2, + SNMP_SECMODEL_USM = 3, + SNMP_SECMODEL_UNKNOWN +}; + +enum snmp_usm_level { + SNMP_noAuthNoPriv = 1, + SNMP_authNoPriv = 2, + SNMP_authPriv = 3 +}; + +enum snmp_authentication { + SNMP_AUTH_NOAUTH = 0, + SNMP_AUTH_HMAC_MD5, + SNMP_AUTH_HMAC_SHA +}; + +enum snmp_privacy { + SNMP_PRIV_NOPRIV = 0, + SNMP_PRIV_DES = 1, + SNMP_PRIV_AES +}; + +struct snmp_engine { + uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; + uint32_t engine_len; + int32_t engine_boots; + int32_t engine_time; + int32_t max_msg_size; +}; + +struct snmp_user { + char sec_name[SNMP_ADM_STR32_SIZ]; + enum snmp_authentication auth_proto; + enum snmp_privacy priv_proto; + uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; + uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; +}; + struct snmp_pdu { - char community[SNMP_COMMUNITY_MAXLEN + 1]; - enum snmp_version version; - u_int type; + char community[SNMP_COMMUNITY_MAXLEN + 1]; + enum snmp_version version; + u_int type; + /* SNMPv3 PDU header fields */ + int32_t identifier; + uint8_t flags; + int32_t security_model; + struct snmp_engine engine; + + /* Associated USM user parameters */ + struct snmp_user user; + uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; + uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; + + /* View-based Access Model */ + /* XXX: put in separate structure - conflicts with struct snmp_context */ + uint32_t context_engine_len; + uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; + char context_name[SNMP_CONTEXT_NAME_SIZ]; + /* trap only */ - struct asn_oid enterprise; - u_char agent_addr[4]; - int32_t generic_trap; - int32_t specific_trap; - uint32_t time_stamp; + struct asn_oid enterprise; + u_char agent_addr[4]; + int32_t generic_trap; + int32_t specific_trap; + uint32_t time_stamp; /* others */ - int32_t request_id; - int32_t error_status; - int32_t error_index; + int32_t request_id; + int32_t error_status; + int32_t error_index; /* fixes for encoding */ - u_char *outer_ptr; - u_char *pdu_ptr; - u_char *vars_ptr; + size_t outer_len; + size_t scoped_len; + u_char *outer_ptr; + u_char *digest_ptr; + u_char *encrypted_ptr; + u_char *scoped_ptr; + u_char *pdu_ptr; + u_char *vars_ptr; - struct snmp_value bindings[SNMP_MAX_BINDINGS]; - u_int nbindings; + + struct snmp_value bindings[SNMP_MAX_BINDINGS]; + u_int nbindings; }; #define snmp_v1_pdu snmp_pdu @@ -150,20 +237,38 @@ SNMP_CODE_BADLEN, SNMP_CODE_BADENC, SNMP_CODE_OORANGE, + SNMP_CODE_BADSECLEVEL, + SNMP_CODE_NOTINTIME, + SNMP_CODE_BADUSER, + SNMP_CODE_BADENGINE, + SNMP_CODE_BADDIGEST, + SNMP_CODE_EDECRYPT }; +#define SNMP_MSG_AUTH_FLAG 0x1 +#define SNMP_MSG_PRIV_FLAG 0x2 +#define SNMP_MSG_REPORT_FLAG 0x4 +#define SNMP_MSG_AUTODISCOVER 0x80 + void snmp_value_free(struct snmp_value *); int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *); int snmp_value_copy(struct snmp_value *, const struct snmp_value *); void snmp_pdu_free(struct snmp_pdu *); enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *); -enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b); +enum snmp_code snmp_pdu_decode_header(struct asn_buf *, struct snmp_pdu *); +enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *, struct snmp_pdu *, int32_t *); +enum snmp_code snmp_pdu_encode(struct snmp_pdu *, struct asn_buf *); +enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *, struct snmp_pdu *); int snmp_pdu_snoop(const struct asn_buf *); void snmp_pdu_dump(const struct snmp_pdu *pdu); +enum snmp_code snmp_passwd_to_keys(struct snmp_user *, char *); +enum snmp_code snmp_get_local_keys(struct snmp_user *, uint8_t *, uint32_t); +enum snmp_code snmp_calc_keychange(struct snmp_user *, uint8_t *); + extern void (*snmp_error)(const char *, ...); extern void (*snmp_printf)(const char *, ...); Index: contrib/bsnmp/lib/snmppriv.h =================================================================== --- contrib/bsnmp/lib/snmppriv.h (revision 214335) +++ contrib/bsnmp/lib/snmppriv.h (working copy) @@ -34,12 +34,18 @@ enum asn_err snmp_binding_encode(struct asn_buf *, const struct snmp_value *); enum snmp_code snmp_pdu_encode_header(struct asn_buf *, struct snmp_pdu *); -enum snmp_code snmp_fix_encoding(struct asn_buf *, const struct snmp_pdu *); -enum asn_err snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, - asn_len_t *lenp); +enum snmp_code snmp_fix_encoding(struct asn_buf *, struct snmp_pdu *); enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp); +void snmp_pdu_init_secparams(struct snmp_pdu *, struct snmp_engine *, + struct snmp_user *); + +enum snmp_code snmp_pdu_calc_digest(struct asn_buf *, const struct snmp_pdu *, + uint8_t *); +enum snmp_code snmp_pdu_encrypt(struct asn_buf *, const struct snmp_pdu *); +enum snmp_code snmp_pdu_decrypt(struct asn_buf *, const struct snmp_pdu *); + #define DEFAULT_HOST "localhost" #define DEFAULT_PORT "snmp" #define DEFAULT_LOCAL "/var/run/snmp.sock" Index: contrib/bsnmp/lib/bsnmplib.3 =================================================================== --- contrib/bsnmp/lib/bsnmplib.3 (revision 214335) +++ contrib/bsnmp/lib/bsnmplib.3 (working copy) @@ -1,4 +1,10 @@ .\" +.\" Copyright (c) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" Portions of this documentation were written by Shteryana Sotirova Shopova +.\" under sponsorship from the FreeBSD Foundation. +.\" .\" Copyright (c) 2004-2005 .\" Hartmut Brandt. .\" All rights reserved. @@ -31,7 +37,7 @@ .\" .\" $Begemot: bsnmp/lib/bsnmplib.3,v 1.9 2005/10/04 08:46:51 brandt_h Exp $ .\" -.Dd October 4, 2005 +.Dd September 9, 2010 .Dt BSNMPLIB 3 .Os .Sh NAME @@ -39,9 +45,15 @@ .Nm snmp_value_parse , .Nm snmp_value_copy , .Nm snmp_pdu_free , -.Nm snmp_code snmp_pdu_decode , -.Nm snmp_code snmp_pdu_encode , +.Nm snmp_pdu_decode , +.Nm snmp_pdu_encode , +.Nm snmp_pdu_decode_header , +.Nm snmp_pdu_decode_scoped , +.Nm snmp_pdu_decode_secmode , .Nm snmp_pdu_dump , +.Nm snmp_passwd_to_keys , +.Nm snmp_get_local_keys , +.Nm snmp_calc_keychange , .Nm TRUTH_MK , .Nm TRUTH_GET , .Nm TRUTH_OK @@ -64,8 +76,20 @@ .Fn snmp_pdu_decode "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip" .Ft enum snmp_code .Fn snmp_pdu_encode "struct snmp_pdu *pdu" "struct asn_buf *buf" +.Ft enum snmp_code +.Fn snmp_pdu_decode_header "struct snmp_pdu *pdu" "struct asn_buf *buf" +.Ft enum snmp_code +.Fn snmp_pdu_decode_scoped "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip" +.Ft enum snmp_code +.Fn snmp_pdu_decode_secmode "struct asn_buf *buf" "struct snmp_pdu *pdu" .Ft void .Fn snmp_pdu_dump "const struct snmp_pdu *pdu" +.Ft enum snmp_code +.Fn snmp_passwd_to_keys "struct snmp_user *user" "char *passwd" +.Ft enum snmp_code +.Fn snmp_get_local_keys "struct snmp_user *user" "uint8_t *eid" "uint32_t elen" +.Ft enum snmp_code +.Fn snmp_calc_keychange "struct snmp_user *user" "uint8_t *keychange" .Ft int .Fn TRUTH_MK "F" .Ft int @@ -73,8 +97,8 @@ .Ft int .Fn TRUTH_OK "T" .Sh DESCRIPTION -The SNMP library contains routines to handle SNMP version 1 and 2 PDUs. -There are two basic structures used throughout the library: +The SNMP library contains routines to handle SNMP version 1, 2 and 3 PDUs. +There are several basic structures used throughout the library: .Bd -literal -offset indent struct snmp_value { struct asn_oid var; @@ -134,34 +158,126 @@ .Fa v.octetstring.octets points to a string allocated by .Xr malloc 3 . +.Pp .Bd -literal -offset indent -#define SNMP_COMMUNITY_MAXLEN 128 -#define SNMP_MAX_BINDINGS 100 +#define SNMP_ENGINE_ID_SIZ 32 +struct snmp_engine { + uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; + uint32_t engine_len; + int32_t engine_boots; + int32_t engine_time; + int32_t max_msg_size; +}; +.Ed +.Pp +This structure represents an SNMP engine as specified by the SNMP Management +Architecture described in RFC 3411. +.Pp +.Bd -literal -offset indent +#define SNMP_USM_NAME_SIZ (32 + 1) +#define SNMP_AUTH_KEY_SIZ 40 +#define SNMP_PRIV_KEY_SIZ 32 + +struct snmp_user { + char sec_name[SNMP_USM_NAME_SIZ]; + enum snmp_authentication auth_proto; + enum snmp_privacy priv_proto; + uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; + uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; +}; +.Ed +.Pp +This structure represents an SNMPv3 user as specified by the User-based +Security Model (USM) described in RFC 3414. The field +.Fa sec_name +is a human readable string containing the security user name. +.Fa auth_proto +contains the id of the authentication protocol in use by the user and may be one +of: +.Bd -literal -offset indent +enum snmp_authentication { + SNMP_AUTH_NOAUTH = 0, + SNMP_AUTH_HMAC_MD5, + SNMP_AUTH_HMAC_SHA +}; +.Ed +.Fa priv_proto +contains the id of the privacy protocol in use by the user and may be one +of: +.Bd -literal -offset indent +enum snmp_privacy { + SNMP_PRIV_NOPRIV = 0, + SNMP_PRIV_DES = 1, + SNMP_PRIV_AES +}; +.Ed +.Fa auth_key +and +.Fa priv_key +contain the authentication and privacy keys for the user. +.Pp +.Bd -literal -offset indent +#define SNMP_COMMUNITY_MAXLEN 128 +#define SNMP_MAX_BINDINGS 100 +#define SNMP_CONTEXT_NAME_SIZ (32 + 1) +#define SNMP_TIME_WINDOW 150 + +#define SNMP_USM_AUTH_SIZE 12 +#define SNMP_USM_PRIV_SIZE 8 + +#define SNMP_MSG_AUTH_FLAG 0x1 +#define SNMP_MSG_PRIV_FLAG 0x2 +#define SNMP_MSG_REPORT_FLAG 0x4 + +#define SNMP_SECMODEL_USM 3 + struct snmp_pdu { - char community[SNMP_COMMUNITY_MAXLEN + 1]; - enum snmp_version version; - u_int type; + char community[SNMP_COMMUNITY_MAXLEN + 1]; + enum snmp_version version; + u_int type; + /* SNMPv3 PDU header fields */ + int32_t identifier; + uint8_t flags; + int32_t security_model; + struct snmp_engine engine; + + /* Associated USM user parameters */ + struct snmp_user user; + uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; + uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; + + /* View-based Access Model */ + uint32_t context_engine_len; + uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; + char context_name[SNMP_CONTEXT_NAME_SIZ]; + /* trap only */ - struct asn_oid enterprise; - u_char agent_addr[4]; - int32_t generic_trap; - int32_t specific_trap; - u_int32_t time_stamp; + struct asn_oid enterprise; + u_char agent_addr[4]; + int32_t generic_trap; + int32_t specific_trap; + uint32_t time_stamp; /* others */ - int32_t request_id; - int32_t error_status; - int32_t error_index; + int32_t request_id; + int32_t error_status; + int32_t error_index; /* fixes for encoding */ - u_char *outer_ptr; - u_char *pdu_ptr; - u_char *vars_ptr; + size_t outer_len; + size_t scoped_len; + u_char *outer_ptr; + u_char *digest_ptr; + u_char *encrypted_ptr; + u_char *scoped_ptr; + u_char *pdu_ptr; + u_char *vars_ptr; - struct snmp_value bindings[SNMP_MAX_BINDINGS]; - u_int nbindings; + + struct snmp_value bindings[SNMP_MAX_BINDINGS]; + u_int nbindings; }; .Ed This structure contains a decoded SNMP PDU. @@ -172,11 +288,15 @@ SNMP_Verr = 0, SNMP_V1 = 1, SNMP_V2c, + SNMP_V3 }; .Ed and .Fa type is the type of the PDU. +.Fa security_model +is the security model used for SNMPv3 PDUs. The only supported +value currently is 3 (User-based Security Model). .Pp The function .Fn snmp_value_free @@ -223,15 +343,60 @@ .Fn snmp_pdu_encode encodes the PDU .Fa pdu -into the an octetstring in buffer +into the an octetstring in buffer, and if authentication and privacy are used, +calculates a message digest and encrypts the PDU data in the buffer .Fa buf . .Pp The function +.Fn snmp_pdu_decode_header +decodes the header of the PDU pointed to by +.Fa buf . +The uncoded PDU contents remain in the buffer. +.Pp +The function +.Fn snmp_pdu_decode_scoped +decodes the scoped PDU pointed to by +.Fa buf . +.Pp +The function +.Fn snmp_pdu_decode_secmode +verifies the authentication parameter contained in the PDU (if present) and +if the PDU is encrypted, decrypts the PDU contents pointed to by +.Fa buf . +If successfull, a plain text scoped PDU is stored in the buffer. +.Pp +The function .Fn snmp_pdu_dump dumps the PDU in a human readable form by calling .Fn snmp_printf . .Pp The function +.Fn snmp_passwd_to_keys +calculates a binary private authentication key corresponding to a plain text human +readable password string. The calculated key is placed in the +.Fa auth_key +field of the +.Fa user . +.Pp +The function +.Fn snmp_get_local_keys +calculates a localazied authentication and privacy keys for a specified SNMPv3 +engine. The calculateds keys are placed in the +.Fa auth_key +and +.Fa priv_key +fields of the +.Fa user . +.Pp +The function +.Fn snmp_calc_keychange +calculates a binary key change octet string based on the contents of an old and +a new binary localized key. The rezult is placed in the buffer pointer to by +.Fa keychange +and may be used by an SNMPv3 user who wishes to change his/her password +or localized key. +.Pp +The function .Fn TRUTH_MK takes a C truth value (zero or non-zero) and makes an SNMP truth value (2 or 1). The function @@ -281,6 +446,13 @@ The PDU is of an unsupported version. .It Bq Er SNMP_CODE_BADENQ There was an ASN.1 value with an unsupported tag. +.It Bq Er SNMP_CODE_BADSECLEVEL +The requested securityLevel contained in the PDU is not supported. +.It Bq Er SNMP_CODE_BADDIGEST +The PDU authentication parameter received in the PDU did not match the +calculated message digest. +.It Bq Er SNMP_CODE_EDECRYPT +Error occured while trying to decrypt the PDU. .El .Pp .Fn snmp_pdu_encode @@ -297,8 +469,21 @@ .Xr bsnmpagent 3 , .Xr bsnmpclient 3 , .Xr bsnmplib 3 +.Sh CAVEAT +The SNMPv3 message digests, encryption and decryption, and key routines use +the cryptographic functions from +.Xr crypto 3 . +The library may optionally be built without references to the +.Xr crypto 3 +library. In such case only plain text SNMPv3 PDUs without message digests +may be proccessed correctly. .Sh STANDARDS This implementation conforms to the applicable IETF RFCs and ITU-T recommendations. .Sh AUTHORS +The Begemot SNMP library was originally written by .An Hartmut Brandt Aq harti@FreeBSD.org +.Pp +.An Shteryana Shopova Aq syrinx@FreeBSD.org +added support for the SNMPv3 message proccessing and User-Based +Security model message authentication and privacy. Index: contrib/bsnmp/lib/snmpagent.c =================================================================== --- contrib/bsnmp/lib/snmpagent.c (revision 214335) +++ contrib/bsnmp/lib/snmpagent.c (working copy) @@ -165,6 +165,29 @@ return (NULL); } +static void +snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp) +{ + memset(resp, 0, sizeof(*resp)); + strcpy(resp->community, pdu->community); + resp->version = pdu->version; + resp->type = SNMP_PDU_RESPONSE; + resp->request_id = pdu->request_id; + resp->version = pdu->version; + + if (resp->version != SNMP_V3) + return; + + snmp_pdu_init_secparams(resp, &pdu->engine, &pdu->user); + resp->identifier = pdu->identifier; + resp->security_model = pdu->security_model; + resp->context_engine_len = pdu->context_engine_len; + memcpy(resp->context_engine, pdu->context_engine, + resp->context_engine_len); + strlcpy(resp->context_name, pdu->context_name, + sizeof(resp->context_name)); +} + /* * Execute a GET operation. The tree is rooted at the global 'root'. * Build the response PDU on the fly. If the return code is SNMP_RET_ERR @@ -184,12 +207,7 @@ memset(&context, 0, sizeof(context)); context.ctx.data = data; - memset(resp, 0, sizeof(*resp)); - strcpy(resp->community, pdu->community); - resp->version = pdu->version; - resp->type = SNMP_PDU_RESPONSE; - resp->request_id = pdu->request_id; - resp->version = pdu->version; + snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ @@ -384,11 +402,7 @@ memset(&context, 0, sizeof(context)); context.ctx.data = data; - memset(resp, 0, sizeof(*resp)); - strcpy(resp->community, pdu->community); - resp->type = SNMP_PDU_RESPONSE; - resp->request_id = pdu->request_id; - resp->version = pdu->version; + snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); @@ -440,12 +454,7 @@ memset(&context, 0, sizeof(context)); context.ctx.data = data; - memset(resp, 0, sizeof(*resp)); - strcpy(resp->community, pdu->community); - resp->version = pdu->version; - resp->type = SNMP_PDU_RESPONSE; - resp->request_id = pdu->request_id; - resp->version = pdu->version; + snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ @@ -652,11 +661,7 @@ TAILQ_INIT(&context.dlist); context.ctx.data = data; - memset(resp, 0, sizeof(*resp)); - strcpy(resp->community, pdu->community); - resp->type = SNMP_PDU_RESPONSE; - resp->request_id = pdu->request_id; - resp->version = pdu->version; + snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); @@ -951,16 +956,9 @@ enum snmp_code code; memset(&resp, 0, sizeof(resp)); - - /* Message sequence */ - if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) + if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) return (SNMP_RET_IGN); - if (pdu_b->asn_len < len) - return (SNMP_RET_IGN); - err = snmp_parse_message_hdr(pdu_b, &resp, &len); - if (ASN_ERR_STOPPED(err)) - return (SNMP_RET_IGN); if (pdu_b->asn_len < len) return (SNMP_RET_IGN); pdu_b->asn_len = len; Index: usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c =================================================================== --- usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c (revision 0) +++ usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c (revision 0) @@ -0,0 +1,1273 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents, + * bsnmpset can be used to set MIB objects in an agent. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "bsnmptc.h" +#include "bsnmptools.h" + +static const char *program_name = NULL; +static enum program_e { + BSNMPGET, + BSNMPWALK, + BSNMPSET +} program; + +/* ***************************************************************************** + * Common bsnmptools functions. + */ +static void +usage(void) +{ + fprintf(stderr, +"Usage:\n" +"%s %s [-A options] [-b buffersize] [-C options] [-I options]\n" +"\t[-i filelist] [-l filename]%s [-o output] [-P options]\n" +"\t%s[-r retries] [-s [trans::][community@][server][:port]]\n" +"\t[-t timeout] [-U options] [-v version]%s\n", + program_name, + (program == BSNMPGET) ? "[-aDdehnK]" : + (program == BSNMPWALK) ? "[-dhnK]" : + (program == BSNMPSET) ? "[-adehnK]" : + "", + (program == BSNMPGET) ? " [-M max-repetitions] [-N non-repeaters]" : "", + (program == BSNMPGET) ? "[-p pdu] " : "", + (program == BSNMPGET) ? " OID [OID ...]" : + (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" : + "" + ); +} + +static int32_t +parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + uint32_t v; + + assert(opt_arg != NULL); + + v = strtoul(opt_arg, (void *) NULL, 10); + + if (v > SNMP_MAX_BINDINGS) { + warnx("Max repetitions value greater than %d maximum allowed.", + SNMP_MAX_BINDINGS); + return (-1); + } + + SET_MAXREP(snmptoolctx, v); + return (2); +} + +static int32_t +parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + uint32_t v; + + assert(opt_arg != NULL); + + v = strtoul(opt_arg, (void *) NULL, 10); + + if (v > SNMP_MAX_BINDINGS) { + warnx("Non repeaters value greater than %d maximum allowed.", + SNMP_MAX_BINDINGS); + return (-1); + } + + SET_NONREP(snmptoolctx, v); + return (2); +} + +static int32_t +parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (strcasecmp(opt_arg, "getbulk") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK); + else if (strcasecmp(opt_arg, "getnext") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT); + else if (strcasecmp(opt_arg, "get") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET); + else { + warnx("PDU type '%s' not supported.", opt_arg); + return (-1); + } + + return (2); +} + +static int32_t +snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv) +{ + int32_t count, optnum = 0; + int ch; + const char *opts; + + switch (program) { + case BSNMPWALK: + opts = "dhnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; + break; + case BSNMPGET: + opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; + break; + case BSNMPSET: + opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; + break; + default: + return (-1); + } + + while ((ch = getopt(argc, argv, opts)) != EOF) { + switch (ch) { + case 'A': + count = parse_authentication(snmptoolctx, optarg); + break; + case 'a': + count = parse_skip_access(snmptoolctx); + break; + case 'b': + count = parse_buflen(optarg); + break; + case 'D': + count = parse_discovery(snmptoolctx); + break; + case 'd': + count = parse_debug(); + break; + case 'e': + count = parse_errors(snmptoolctx); + break; + case 'h': + usage(); + return (-2); + case 'C': + count = parse_context(snmptoolctx, optarg); + break; + case 'I': + count = parse_include(snmptoolctx, optarg); + break; + case 'i': + count = parse_file(snmptoolctx, optarg); + break; + case 'K': + count = parse_local_key(snmptoolctx); + break; + case 'l': + count = parse_local_path(optarg); + break; + case 'M': + count = parse_max_repetitions(snmptoolctx, optarg); + break; + case 'N': + count = parse_non_repeaters(snmptoolctx, optarg); + break; + case 'n': + count = parse_num_oids(snmptoolctx); + break; + case 'o': + count = parse_output(snmptoolctx, optarg); + break; + case 'P': + count = parse_privacy(snmptoolctx, optarg); + break; + case 'p': + count = parse_pdu_type(snmptoolctx, optarg); + break; + case 'r': + count = parse_retry(optarg); + break; + case 's': + count = parse_server(optarg); + break; + case 't': + count = parse_timeout(optarg); + break; + case 'U': + count = parse_user_security(snmptoolctx, optarg); + break; + case 'v': + count = parse_version(optarg); + break; + case '?': + default: + usage(); + return (-1); + } + if (count < 0) + return (-1); + optnum += count; + } + + return (optnum); +} + +/* + * Read user input OID - one of following formats: + * 1) 1.2.1.1.2.1.0 - that is if option numeric was giveni; + * 2) string - in such case append .0 to the asn_oid subs; + * 3) string.1 - no additional proccessing required in such case. + */ +static char * +snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + char string[MAXSTR], *str; + int32_t i = 0; + struct asn_oid in_oid; + + str = argv; + + if (*str == '.') + str++; + + while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) { + str++; + i++; + } + + if (i <= 0 || i >= MAXSTR) + return (NULL); + + memset(&in_oid, 0, sizeof(struct asn_oid)); + if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) { + warnx("Invalid OID - %s", argv); + return (NULL); + } + + strlcpy(string, argv, i + 1); + if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) { + warnx("No entry for %s in mapping lists", string); + return (NULL); + } + + /* If OID given on command line append it. */ + if (in_oid.len > 0) + asn_append_oid(&(obj->val.var), &in_oid); + else if (*str == '[') { + if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL) + return (NULL); + } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) == + SNMP_PDU_GET) { + if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0) + return (NULL); + } + + return (str); +} + +static int32_t +snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + if (argv == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) { + if (snmp_parse_numoid(argv, &(obj->val.var)) < 0) + return (-1); + } else { + if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL && + snmp_parse_numoid(argv, &(obj->val.var)) < 0) + return (-1); + } + + return (1); +} + +static int32_t +snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) +{ + if (obj->error > 0) + return (0); + + asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); + pdu->nbindings++; + + return (pdu->nbindings); +} + +/* ***************************************************************************** + * bsnmpget private functions. + */ +static int32_t +snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, + struct snmp_object *obj) +{ + if (pdu->version == SNMP_V1 && obj->val.syntax == + SNMP_SYNTAX_COUNTER64) { + warnx("64-bit counters are not supported in SNMPv1 PDU"); + return (-1); + } + + if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT || + pdu->type == SNMP_PDU_GETBULK) + return (1); + + if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) { + warnx("Only leaf object values can be added to GET PDU"); + return (-1); + } + + return (1); +} + +/* + * In case of a getbulk PDU, the error_status and error_index fields are used by + * libbsnmp to hold the values of the non-repeaters and max-repetitions fields + * that are present only in the getbulk - so before sending the PDU make sure + * these have correct values as well. + */ +static void +snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep) +{ + assert(pdu != NULL); + + if (pdu->nbindings < non_rep) + pdu->error_status = pdu->nbindings; + else + pdu->error_status = non_rep; + + if (max_rep > 0) + pdu->error_index = max_rep; + else + pdu->error_index = 1; +} + +static int +snmptool_get(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + + snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); + + while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind, + snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { + + if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) + snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), + GET_NONREP(snmptoolctx)); + + if (snmp_dialog(&req, &resp) == -1) { + warnx("Snmp dialog - %s", strerror(errno)); + break; + } + + if (snmp_parse_resp(&resp, &req) >= 0) { + snmp_output_resp(snmptoolctx, &resp); + break; + } + + snmp_output_err_resp(snmptoolctx, &resp); + if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || + !ISSET_RETRY(snmptoolctx)) + break; + + /* + * Loop through the object list and set object->error to the + * varbinding that caused the error. + */ + if (snmp_object_seterror(snmptoolctx, + &(resp.bindings[resp.error_index - 1]), + resp.error_status) <= 0) + break; + + fprintf(stderr, "Retrying...\n"); + snmp_pdu_free(&resp); + snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); + } + + snmp_pdu_free(&resp); + + return (0); +} + + +/* ***************************************************************************** + * bsnmpwalk private functions. + */ +/* The default tree to walk. */ +static const struct asn_oid snmp_mibII_OID = { + 6 , { 1, 3, 6, 1, 2, 1 } +}; + +static int32_t +snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, + struct snmp_object *obj, char *string __unused) +{ + asn_append_oid(&(obj->val.var), &snmp_mibII_OID); + return (1); +} + +/* + * Prepare the next GetNext/Get PDU to send. + */ +static void +snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) +{ + snmp_pdu_create(pdu, op); + asn_append_oid(&(pdu->bindings[0].var), var); + pdu->nbindings = 1; +} + +static int +snmptool_walk(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + struct asn_oid root; /* Keep the inital oid. */ + int32_t outputs, rc; + + snmp_pdu_create(&req, SNMP_PDU_GETNEXT); + + while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, + snmptool_add_vbind, &req, 1)) > 0) { + + /* Remember the root where the walk started from. */ + memset(&root, 0, sizeof(struct asn_oid)); + asn_append_oid(&root, &(req.bindings[0].var)); + + outputs = 0; + while (snmp_dialog(&req, &resp) >= 0) { + if ((snmp_parse_resp(&resp, &req)) < 0) { + snmp_output_err_resp(snmptoolctx, &resp); + snmp_pdu_free(&resp); + outputs = -1; + break; + } + + if (!(asn_is_suboid(&root, &(resp.bindings[0].var)))) { + snmp_pdu_free(&resp); + break; + } + + if (snmp_output_resp(snmptoolctx, &resp)!= 0) { + snmp_pdu_free(&resp); + outputs = -1; + break; + } + outputs++; + snmp_pdu_free(&resp); + + snmpwalk_nextpdu_create(SNMP_PDU_GETNEXT, + &(resp.bindings[0].var), &req); + } + + /* Just in case our root was a leaf. */ + if (outputs == 0) { + snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); + if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { + if (snmp_parse_resp(&resp,&req) < 0) + snmp_output_err_resp(snmptoolctx, &resp); + else + snmp_output_resp(snmptoolctx, &(resp)); + + snmp_pdu_free(&resp); + } else + warnx("Snmp dialog - %s", strerror(errno)); + } + + if (snmp_object_remove(snmptoolctx, &root) < 0) { + warnx("snmp_object_remove"); + break; + } + + snmp_pdu_create(&req, SNMP_PDU_GETNEXT); + } + + if (rc == 0) + return (0); + else + return (1); +} + +/* ***************************************************************************** + * bsnmpset private functions. + */ + +static int32_t +parse_oid_numeric(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t saved_errno; + asn_subid_t suboid; + + do { + saved_errno = errno; + errno = 0; + suboid = strtoul(val, &endptr, 10); + if (errno != 0) { + warnx("Value %s not supported - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + if ((asn_subid_t) suboid > ASN_MAXID) { + warnx("Suboid %u > ASN_MAXID", suboid); + return (-1); + } + if (snmp_suboid_append(&(value->v.oid), suboid) < 0) + return (-1); + val = endptr + 1; + } while (*endptr == '.'); + + if (*endptr != '\0') + warnx("OID value %s not supported", val); + + value->syntax = SNMP_SYNTAX_OID; + return (0); +} + +/* + * Allow OID leaf in both forms: + * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; + * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. + */ +static int32_t +parse_oid_string(struct snmp_toolinfo *snmptoolctx, + struct snmp_value *value, char *string) +{ + struct snmp_object obj; + + if (isdigit(string[0])) + return (parse_oid_numeric(value, string)); + + memset(&obj, 0, sizeof(struct snmp_object)); + if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { + warnx("Unknown OID enum string - %s", string); + return (-1); + } + + asn_append_oid(&(value->v.oid), &(obj.val.var)); + return (1); +} + +static int32_t +parse_ip(struct snmp_value * value, char * val) +{ + uint32_t v; + int32_t i; + char *endptr, *str; + + str = val; + for (i = 0; i < 4; i++) { + v = strtoul(str, &endptr, 10); + if (v > 0xff) + return (-1); + if (*endptr != '.' && *endptr != '\0' && i != 3) + break; + str = endptr + 1; + value->v.ipaddress[i] = (uint8_t) v; + } + + value->syntax = SNMP_SYNTAX_IPADDRESS; + return (0); +} + +static int32_t +parse_int(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t v, saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtol(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->syntax = SNMP_SYNTAX_INTEGER; + value->v.integer = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_int_string(struct snmp_object *object, char *val) +{ + int32_t v; + + if (isdigit(val[0])) + return ((parse_int(&(object->val), val))); + + if (object->info == NULL) { + warnx("Unknown enumerated integer type - %s", val); + return (-1); + } + if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) + warnx("Unknown enumerated integer type - %s", val); + + object->val.v.integer = v; + return (1); +} + +/* + * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, + * SNMP_SYNTAX_TIMETICKS. + */ +static int32_t +parse_uint(struct snmp_value *value, char *val) +{ + char *endptr; + uint32_t v = 0; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoul(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->v.uint32 = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_ticks(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_TIMETICKS; + return (0); +} + +static int32_t +parse_gauge(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_GAUGE; + return (0); +} + +static int32_t +parse_counter(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_COUNTER; + return (0); +} + +static int32_t +parse_uint64(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t saved_errno; + uint64_t v; + + saved_errno = errno; + errno = 0; + + v = strtoull(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->syntax = SNMP_SYNTAX_COUNTER64; + value->v.counter64 = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) +{ + switch (syntax) { + case SNMP_SYNTAX_INTEGER: + return (parse_int(value, val)); + case SNMP_SYNTAX_IPADDRESS: + return (parse_ip(value, val)); + case SNMP_SYNTAX_COUNTER: + return (parse_counter(value, val)); + case SNMP_SYNTAX_GAUGE: + return (parse_gauge(value, val)); + case SNMP_SYNTAX_TIMETICKS: + return (parse_ticks(value, val)); + case SNMP_SYNTAX_COUNTER64: + return (parse_uint64(value, val)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oct(SNMP_STRING, value, val)); + case SNMP_SYNTAX_OID: + return (parse_oid_numeric(value, val)); + default: + /* NOTREACHED */ + break; + } + + return (-1); +} + +/* + * Parse a command line argument of type OID=syntax:value and fill in whatever + * fields can be derived from the input into snmp_value structure. Reads numeric + * OIDs. + */ +static int32_t +parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) +{ + int32_t cnt; + char *ptr; + enum snmp_syntax syntax; + char oid_str[ASN_OIDSTRLEN]; + + ptr = str; + for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) + if (ptr[cnt] == '=') + break; + + if (cnt >= ASN_OIDSTRLEN) { + warnx("OID too long - %s", str); + return (-1); + } + strlcpy(oid_str, ptr, (size_t) (cnt + 1)); + + ptr = str + cnt + 1; + for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) + if(ptr[cnt] == ':') + break; + + if (cnt >= MAX_CMD_SYNTAX_LEN) { + warnx("Unknown syntax in OID - %s", str); + return (-1); + } + + if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { + warnx("Unknown syntax in OID - %s", ptr); + return (-1); + } + + ptr = ptr + cnt + 1; + for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) + if (ptr[cnt] == '\0') + break; + + if (ptr[cnt] != '\0') { + warnx("Value string too long - %s",ptr); + return (-1); + } + + /* + * Here try parsing the OIDs and syntaxes and then check values - have + * to know syntax to check value boundaries. + */ + if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { + warnx("Error parsing OID %s",oid_str); + return (-1); + } + + if (parse_syntax_val(snmp_val, syntax, ptr) < 0) + return (-1); + + return (1); +} + +/* XXX-BZ aruments should be swapped. */ +static int32_t +parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str, + struct snmp_object *object) +{ + uint32_t len; + enum snmp_syntax syn; + + /* + * Syntax string here not required - still may be present. + */ + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { + for (len = 0 ; *(str + len) != ':'; len++) { + if (*(str + len) == '\0') { + warnx("Syntax missing in value - %s", str); + return (-1); + } + } + if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { + warnx("Unknown syntax in - %s", str); + return (-1); + } + if (syn != object->val.syntax) { + if (!ISSET_ERRIGNORE(snmptoolctx)) { + warnx("Bad syntax in - %s", str); + return (-1); + } else + object->val.syntax = syn; + } + len++; + } else + len = 0; + + switch (object->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (parse_int_string(object, str + len)); + case SNMP_SYNTAX_IPADDRESS: + return (parse_ip(&(object->val), str + len)); + case SNMP_SYNTAX_COUNTER: + return (parse_counter(&(object->val), str + len)); + case SNMP_SYNTAX_GAUGE: + return (parse_gauge(&(object->val), str + len)); + case SNMP_SYNTAX_TIMETICKS: + return (parse_ticks(&(object->val), str + len)); + case SNMP_SYNTAX_COUNTER64: + return (parse_uint64(&(object->val), str + len)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oct(object->info->tc, &(object->val), + str + len)); + case SNMP_SYNTAX_OID: + return (parse_oid_string(snmptoolctx, &(object->val), + str + len)); + default: + /* NOTREACHED */ + break; + } + + return (-1); +} + +static int32_t +parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + char *ptr; + + if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) + return (-1); + + if (*ptr != '=') { + warnx("Value to set expected after OID"); + return (-1); + } + + if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0) + return (-1); + + return (1); +} + + +static int32_t +snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + if (argv == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) { + if (parse_pair_numoid_val(argv, &(obj->val)) < 0) + return (-1); + } else { + if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) + return (-1); + } + + return (1); +} + +static int32_t +add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + int8_t i; + + dst->syntax = SNMP_SYNTAX_IPADDRESS; + for (i = 0; i < 4; i++) + dst->v.ipaddress[i] = src->v.ipaddress[i]; + + return (1); +} + +static int32_t +add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { + warnx("OctetString len too big - %u",src->v.octetstring.len); + return (-1); + } + + if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == + NULL) { + syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); + return (-1); + } + + memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, + src->v.octetstring.len); + dst->syntax = SNMP_SYNTAX_OCTETSTRING; + dst->v.octetstring.len = src->v.octetstring.len; + + return(0); +} + +static int32_t +add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + asn_append_oid(&(dst->v.oid), &(src->v.oid)); + dst->syntax = SNMP_SYNTAX_OID; + return (0); +} + +/* + * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, + * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - + * return error. + */ +static int32_t +snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) +{ + if (dst == NULL || src == NULL) + return (-1); + + switch (src->syntax) { + case SNMP_SYNTAX_INTEGER: + dst->v.integer = src->v.integer; + dst->syntax = SNMP_SYNTAX_INTEGER; + break; + case SNMP_SYNTAX_TIMETICKS: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_TIMETICKS; + break; + case SNMP_SYNTAX_GAUGE: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_GAUGE; + break; + case SNMP_SYNTAX_COUNTER: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_COUNTER; + break; + case SNMP_SYNTAX_COUNTER64: + dst->v.counter64 = src->v.counter64; + dst->syntax = SNMP_SYNTAX_COUNTER64; + break; + case SNMP_SYNTAX_IPADDRESS: + add_ip_syntax(dst, src); + break; + case SNMP_SYNTAX_OCTETSTRING: + add_octstring_syntax(dst, src); + break; + case SNMP_SYNTAX_OID: + add_oid_syntax(dst, src); + break; + default: + warnx("Unknown syntax %d", src->syntax); + return (-1); + } + + return (0); +} + +static int32_t +snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, + struct snmp_object *obj) +{ + if (pdu->version == SNMP_V1 && obj->val.syntax == + SNMP_SYNTAX_COUNTER64) { + warnx("64-bit counters are not supported in SNMPv1 PDU"); + return (-1); + } + + if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) + return (1); + + if (obj->info->access < SNMP_ACCESS_SET) { + warnx("Object %s not accessible for set - try 'bsnmpset -a'", + obj->info->string); + return (-1); + } + + return (1); +} + +static int32_t +snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) +{ + if (pdu->nbindings > SNMP_MAX_BINDINGS) { + warnx("Too many OIDs for one PDU"); + return (-1); + } + + if (obj->error > 0) + return (0); + + if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) + < 0) + return (-1); + + asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); + pdu->nbindings++; + + return (pdu->nbindings); +} + +static int +snmptool_set(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + + snmp_pdu_create(&req, SNMP_PDU_SET); + + while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, + snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { + if (snmp_dialog(&req, &resp)) { + warnx("Snmp dialog - %s", strerror(errno)); + break; + } + + if (snmp_pdu_check(&req, &resp) > 0) { + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) + snmp_output_resp(snmptoolctx, &resp); + break; + } + + snmp_output_err_resp(snmptoolctx, &resp); + if (!ISSET_RETRY(snmptoolctx)) + break; + + if (snmp_object_seterror(snmptoolctx, + &(resp.bindings[resp.error_index - 1]), + resp.error_status) <= 0) + break; + + fprintf(stderr, "Retrying...\n"); + snmp_pdu_free(&req); + snmp_pdu_free(&resp); + snmp_pdu_create(&req, SNMP_PDU_SET); + } + + snmp_pdu_free(&resp); + + return (0); +} + +/* ***************************************************************************** + * main + */ +/* + * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. + * Wait for a responce and print it. + */ +/* + * Do a 'snmp walk' - according to command line options request for values + * lexicographically subsequent and subrooted at a common node. Send a GetNext + * PDU requesting the value for each next variable and print the responce. Stop + * when a Responce PDU is received that contains the value of a variable not + * subrooted at the variable the walk started. + */ +int +main(int argc, char ** argv) +{ + struct snmp_toolinfo snmptoolctx; + int32_t oid_cnt, last_oid, opt_num; + int rc = 0; + + /* Make sure program_name is set and valid. */ + if (*argv == NULL) + program_name = "snmptool"; + else { + program_name = strrchr(*argv, '/'); + if (program_name != NULL) + program_name++; + else + program_name = *argv; + } + + if (program_name == NULL) { + fprintf(stderr, "Error: No program name?\n"); + exit (1); + } else if (strcmp(program_name, "bsnmpget") == 0) + program = BSNMPGET; + else if (strcmp(program_name, "bsnmpwalk") == 0) + program = BSNMPWALK; + else if (strcmp(program_name, "bsnmpset") == 0) + program = BSNMPSET; + else { + fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); + exit (1); + } + + /* Initialize. */ + if (snmptool_init(&snmptoolctx) < 0) + exit (1); + + if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { + snmp_tool_freeall(&snmptoolctx); + /* On -h (help) exit without error. */ + if (opt_num == -2) + exit(0); + else + exit(1); + } + + oid_cnt = argc - opt_num - 1; + if (oid_cnt == 0) { + switch (program) { + case BSNMPGET: + if (!ISSET_EDISCOVER(&snmptoolctx) && + !ISSET_LOCALKEY(&snmptoolctx)) { + fprintf(stderr, "No OID given.\n"); + usage(); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + break; + + case BSNMPWALK: + if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, + NULL) < 0) { + fprintf(stderr, + "Error setting default subtree.\n"); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + break; + + case BSNMPSET: + fprintf(stderr, "No OID given.\n"); + usage(); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + } + + if (snmp_import_all(&snmptoolctx) < 0) { + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + /* A simple sanity check - can not send GETBULK when using SNMPv1. */ + if (program == BSNMPGET && snmp_client.version == SNMP_V1 && + GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { + fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { + if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? + snmpset_parse_oid : snmptools_parse_oid, + argv[last_oid])) < 0) { + fprintf(stderr, "Error parsing OID string '%s'.\n", + argv[last_oid]); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + } + + if (snmp_open(NULL, NULL, NULL, NULL)) { + warnx("Failed to open snmp session: %s.", strerror(errno)); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) + SET_EDISCOVER(&snmptoolctx); + + if (ISSET_EDISCOVER(&snmptoolctx) && + snmp_discover_engine(snmptoolctx.passwd) < 0) { + warnx("Unknown SNMP Engine ID: %s.", strerror(errno)); + rc = 1; + goto cleanup; + } + + if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || + ISSET_EDISCOVER(&snmptoolctx)) + snmp_output_engine(); + + if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && + !ISSET_EDISCOVER(&snmptoolctx)) { + if (snmp_passwd_to_keys(&snmp_client.user, + snmptoolctx.passwd) != SNMP_CODE_OK || + snmp_get_local_keys(&snmp_client.user, + snmp_client.engine.engine_id, + snmp_client.engine.engine_len) != SNMP_CODE_OK) { + warnx("Failed to get keys: %s.", strerror(errno)); + rc = 1; + goto cleanup; + } + } + + if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || + ISSET_EDISCOVER(&snmptoolctx)) + snmp_output_keys(); + + if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) + goto cleanup; + + switch (program) { + case BSNMPGET: + rc = snmptool_get(&snmptoolctx); + break; + case BSNMPWALK: + rc = snmptool_walk(&snmptoolctx); + break; + case BSNMPSET: + rc = snmptool_set(&snmptoolctx); + break; + } + + +cleanup: + snmp_tool_freeall(&snmptoolctx); + snmp_close(); + + exit(rc); +} Index: usr.sbin/bsnmpd/tools/bsnmptools/Makefile =================================================================== --- usr.sbin/bsnmpd/tools/bsnmptools/Makefile (revision 0) +++ usr.sbin/bsnmpd/tools/bsnmptools/Makefile (revision 0) @@ -0,0 +1,28 @@ +# $FreeBSD$ +# Author: Shteryana Shopova + +.include + +.PATH: ${.CURDIR} + +PROG= bsnmpget + +DPADD+= ${LIBBSNMP} ${LIBBSNMPTOOLS} +LDADD+= -lbsnmp -lbsnmptools +CFLAGS+= -I${.CURDIR}/../libbsnmptools +LDFLAGS+= -L${LIBBSNMPTOOLSDIR} + +.if ${MK_OPENSSL} != "no" +DPADD+= ${LIBCRYPTO} +LDADD+= -lcrypto +.endif + +LINKS= ${DESTDIR}/usr/bin/bsnmpget ${DESTDIR}/usr/bin/bsnmpwalk +LINKS+= ${DESTDIR}/usr/bin/bsnmpget ${DESTDIR}/usr/bin/bsnmpset + +MAN= bsnmpget.1 + +MLINKS= bsnmpget.1 bsnmpwalk.1 +MLINKS+= bsnmpget.1 bsnmpset.1 + +.include Index: usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 =================================================================== --- usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 (revision 0) +++ usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 (revision 0) @@ -0,0 +1,401 @@ +.\" +.\" Copyright (c) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" Portions of this documentation were written by Shteryana Sotirova Shopova +.\" under sponsorship from the FreeBSD Foundation. +.\" +.\" Copyright (c) 2005-2007 The FreeBSD Project. +.\" All rights reserved. +.\" +.\" Author: Shteryana Shopova +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 17, 2007 +.Dt BSNMPGET 1 +.Os +.Sh NAME +.Nm bsnmpget , +.Nm bsnmpwalk , +.Nm bsnmpset +.Nd "simple tools for querying SNMP agents" +.Sh SYNOPSIS +.Nm +.Op Fl aDdehnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl M Ar max-repetitions +.Op Fl N Ar non-repeaters +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl p Ar pdu +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Op Ar OID ... +.Pp +.Nm bsnmpwalk +.Op Fl dhnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Op Ar OID ... +.Pp +.Nm bsnmpset +.Op Fl adehnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Ar OID Ns = Ar syntax Ns : Ns Ar value +.Op Ar OID Ns = Ar syntax Ns : Ns Ar value ... +.Sh DESCRIPTION +.Nm , +.Nm bsnmpwalk +and +.Nm bsnmpset +are simple tools for retrieving management information from and setting +management information to a Simple Network Managment Protocol (SNMP) agent. +.Pp +Depending on the options +.Nm bsnmpget +constructs either a SMNP GetRequest, GetNextRequest +or a GetBulkRequest packet, fills in the object identifiers (OIDs) of the +objects whose values will be retrived, waits for a response and prints it if +received successfully. +.Pp +.Nm Bsnmpwalk +queries an agent with SMNP GetNextRequest packets, +asking for values of OID instances that are a part of the object subtree +rooted at the provided OIDs. +.Pp +.Nm Bsnmpset +constructs a SMNP SetRequest packet, fills in the OIDs (object identifiers), +syntaxes and values of the objects whose values are to be set and waits for a +responce from server. +.Sh OPTIONS +.Pp +The options are as follows (not all apply to all three programs): +.Bl -tag -width ".It Fl D Ar options" +.It Fl A Ar options +Authentication options to use with SNMPv3 PDUs +.Bl -tag -width +.It Cm proto=[md5|sha] +The protocol to use when calculating the PDU message digest. +.It Cm key=authkey +A binary localized authentication key to use when calculating the PDU message +digest. +.El +.Pp +By default SNMPv3 PDUs are sent unauthenticated. +.It Fl a +Skip any sanity checks when adding OIDs to a Protocol Data Unit (PDU): +ingore syntax/access type, allow adding of non-leaf objects for GetPdu and +read-only objects to a SetPDU. +.It Fl b Ar buffersize +Tune the size of buffers used to send and receive packets. +The default size is 10000 bytes which should be enough unless an agent sends +a really large octetstring. +The maximum allowed length is 65535 according to the Structure of Management +Information (SMIv2). +.It Fl C Ar options +The context to query with SNMPv3 PDUs. +.Bl -tag -width +.It Cm context=name +The context name. Default is "" (empty). +.It Cm context-engine=engine-id +The SNMP Engine ID of the context to query with SNMPv3 PDUs, represented as +binary octet string. By default, this is set to the Engine ID of the SNMP agent. +.El +.It Fl D +Perform SNMP USM Engine Discovery, rather than sending a request for the value +of a specific object. +.It Fl d +Turn on debugging. +This option will cause the packets sent and received to be dumped to the +terminal. +.It Fl e +Retry on error. +If an error is returned in the response PDU, resend the request removing the +variable that caused the error until a valid response is received. +This is only usefull for a GetRequest- and a GetNextRequest-PDU. +.It Fl h +Print a short help text with default values for various options. +.It Fl I Ar options +Load each MIB description file from the given list to translate symbolic +object names to their numerical representation and vice versa. +Use the other options to obtain a non-default behaviour: +.Bl -tag -width +.It Cm cut=OID +Specifies the initial OID that was cut by +.Xr gensnmpdef 1 +when producing the MIB description file. +The default value is .iso(1).org(3).dod(6) which is what should have been +used for all the files installed under /usr/share/snmp/defs/ . +Use this only if you generated your own files, providing a '-c' option to +.Xr gensnmpdef 1 . +.It Cm path=filedir +The directory where files in the list will be searched. +The default is +.Pa /usr/share/snmp/defs/ . +.It Cm file=filelist +A comma separated list of files to which the two options above will apply. +.El +.Pp +The file suboption has to come after the other suboptions so that their +non-default values will be applied to the list of files. +The order of the other suboptions before each file suboption can be random. +Suboptions may be separated either by commas or by spaces. +If using spaces make sure the entire option string is one argument, for +example using quotes. +.It Fl i Ar filelist +List of MIB description files produced by +.Xr gensnmpdef 1 which +.Nm bsnmpget , +.Nm bsnmpwalk +or +.Nm bsnmpset +will search to translate numerical OIDs to their symbolic object names. +Multiple files can be provided either giving this option multiple times +or a comma separated list of file names. +If a filename begins with a letter the default directory, +/usr/share/snmp/defs/ , +will be searched. +.It Fl K +Calculate and display the localized authentication and privacy keys +corresponding to a plain text password. The password is obtain via the +environment. Additionally, if one or more OIDs are specified, the calculated +keys are used when processing the SNMPv3 requests. +.It Fl l Ar filename +The path of the posix local (unix domain) socket if local +transport is used. +.It Fl M Ar max-repetitions +The value for the max-repetitions field in a GetBulk PDU. +Default is 1. +.It Fl N Ar non-repeaters +The value for the non-repeaters field in a GetBulk PDU. +Default is 0. +.It Fl n +Only use numerical representations for input and output OIDs and do not +try to resolve symbolic object names. +Note that +.Nm bsnmpget , +.Nm bsnmpwalk +and +.Nm bsnmpset +will print numerical OIDs anyway if the corresponding string representation +is not found in the MIB description files. +.It Fl o Ar [quiet|short|verbose] +The format used to print the received response. +Quiet only prints values, short (default) prints an abbreviated OID +representation and the value. +In addition to the short output verbose prints the type before the value. +.It Fl P Ar options +Privacy options to use with SNMPv3 PDUs +.Bl -tag -width +.It Cm proto=[aes|des] +The protocol to use when encypting/decrypting SNMPv3 PDU data. +.It Cm key=privkey +A binary localized privacy key to use when encypting/decrypting SNMPv3 PDU data. +.El +.Pp +By default plain text SNMPv3 PDUs are sent. +.It Fl p Ar [get|getnext|getbulk] +The PDU type to send by +.Nm bsmpget . +Default is get. +.It Fl r Ar retries +Number of resends of request packets before giving up if the agent does +not respond after the first try. +Default is 3. +.It Fl s Ar [trans::] Ns Ar [community@] Ns Ar [server] Ns Ar [:port] +Each of the server specification components is optional but at least one +has to be provided if '-s' option is used. +The server specification is constructed in the following manner: +.Bl -tag -width +.It Cm trans:: +Transport type may be one of udp, stream or dgram. +If this option is not provided an udp inet/inet6 socket will be used, which +is the most common. +Stream stands for a posix local stream socket and a posix local datagram +socket will be used if dgram is specified. +.It Cm community@ +Specify an SNMP community string to be used when sending packets. +If the option is skipped the default "public" will be used for +.Nm +and +.Nm bsnmpwalk +and the default "private" community string will be used for +.Nm bsnmpset . +.It Cm server +This might be either the IP address or the hostname where the agent is +listening. +The default is 'localhost'. +.It Cm port +The destination port to send the requests to. +This is useful if the SNMP agent listens on a non-default port. +Default is given by the 'snmp' entry in /etc/services, port 161. +.El +.It Fl t Ar timeout +Number of seconds before resending a request packet if the agent does +not respond. +The default value is 3 seconds. +.It Fl U Ar options +User credentials when sending SNMPv3 PDUs. +.Bl -tag -width +.It Cm engine=id +The Engine ID of the SNMP agent represented as a binary octet string. +.It Cm engine-boots=value +The value of the snmpEngineBoots of the SNMP agent. +.It Cm engine-time=value +The value of the snmpEngineTime of the SNMP agent. +.Pp +If any of the above is not specified, SNMP USM Engine Discovery is attempted. +This is also the default behavior. +.It Cm name=username +The USM user name to include in the SNMPv3 PDUs. By default, the user name is +obtain via the environment +.El +.It Fl v Ar version +The SNMP protocol version to use when sending requests. SNMP versions 1, 2 and +3 are supported. +If no version option is provided +.Nm bsnmpget , +.Nm bsnmpwalk +and +.Nm bsnmpset +will use version 2. +Note that GetBulkRequest-PDUs were introduced in SNMPv2 thus setting the +version to 1 is incompatiable with sending a GetBulk PDU. +.It OID +The object identifier whose value to retrive. +At least one OID should be provided for +.Nm bsnmpget +to be able to send a request. +.Pp +For +.Nm bsnmpwalk +this is the root object identifier of the subtree whose values are to be +retrived. +If no OID is provided +.Nm bsnmpwalk +will walk the mib2 subtree rooted +at .iso(1).org(3).dod(6).internet(1).mgmt(2).mib2(1) . +.Pp +Any of the formats used to print a single variable +is valid as input OID: +.Bl -tag -width +.It 1.3.6.1.2.1.25.1.1.0 +.It sysDescr +.It ifPhysAddress.1 +.It ifRcvAddressStatus.2.6.255.255.255.255.255.255 +.It ifRcvAddressType[2,ff:ff:ff:ff:ff:ff] +.It ifRcvAddressStatus[Integer:1,OctetString:ff:ff:ff:ff:ff:ff] +(requires '-o verbose' option) +.El +.Pp +Square brackets are used to denote an entry's indexes. +When used in an input OID, the square brackets may have to be +escaped or the OID has to be quoted to protect it from the shell. +Note there is no difference between ifName.1 and "ifName[1]". +.It OID Ns = Ns Ar [syntax Ns :] Ns Ar value +The object identifier with its syntax type and value that is to be set. +At least one such string OID=[syntax:]value should be provided to +.Nm bsnmpset +to be able to send a request. +.Bl -tag -width +.It Cm OID +OID may be input as a string, a string followed by a random number of integers +(suboids) separated by dots, a sequence of integers separated by dots - that is +if '-n' options is used - and in such case a syntax is required for every value, +or a string followed by square brackets (used to denote an entry's indexes) and +corresponding indexes. +Any of formats used to print a single variable by +.Nm bsnmpset is +valid for inpit OID as well: +.Bl -tag -width +.It 1.3.6.1.2.1.25.1.1.0=TimeTicks:537615486 +.It sysLocation=OctetString:"@ Home" (with '-o verbose' option) +.It sysLocation.0="@ Home" +.It 1.3.6.1.2.1.2.2.1.6.1=OctetString:ffffffffffff +.It ifPhysAddress.1="00:02:b3:1d:1c:a3" +.It ifRcvAddressStatus.1.6.255.255.255.255.255.255=1 +.It "ifRcvAddressStatus[Integer:1,OctetString:ff:ff:ff:ff:ff:ff]=Integer:1" +(with '-o verbose' option) +.El +.It Cm syntax +where syntax string is one of : +Integer, OctetString, OID, IpAddress, Counter32, Gauge, TimeTicks, Counter64. +.It Cm value +The value to be set - IP address in form of u.u.u.u - for example +1.3.1.6.1.2.0=IpAddress:192.168.0.1, strings require inverted-commas if they +contain any special characters or spaces, all other numeric types don't. +.El +.Sh ENVIRONMENT +.Nm , +.Nm bsnmpwalk +and +.Nm bsnmpset +use the following environment variables: +.Bl -tag -width SNMPAUTH +.It Ev SNMPAUTH +Specifies a default SNMP USM authentication protocol. +.It Ev SNMPPRIV +Specifies a default SNMP USM privacy protocol. +.It Ev SNMPUSER +Specifies a default SNMP USM user name. +.It Ev SNMPPASSWD +Specifies the SNMP USM plain text password to use when calculating localized +authentication and privacy keys. If this variable exists in the environment, +SMNPv3 is the default version to use for outgoing requests. +.Sh SEE ALSO +.Xr gensnmpdef 1 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org Index: usr.sbin/bsnmpd/tools/Makefile.inc =================================================================== --- usr.sbin/bsnmpd/tools/Makefile.inc (revision 0) +++ usr.sbin/bsnmpd/tools/Makefile.inc (revision 0) @@ -0,0 +1,13 @@ +# $FreeBSD$ +# Author: Shteryana Shopova + +BINDIR?= /usr/bin + +CFLAGS+= -I. -I${.CURDIR} + +.if exists(${.OBJDIR}/../libbsnmptools) +LIBBSNMPTOOLSDIR= ${.OBJDIR}/../libbsnmptools +.else +LIBBSNMPTOOLSDIR= ${.CURDIR}/../libbsnmptools +.endif +LIBBSNMPTOOLS= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 0) @@ -0,0 +1,2119 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Helper functions for snmp client tools + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "bsnmptc.h" +#include "bsnmptools.h" + +/* Internal varibale to turn on library debugging for testing and to + * find bugs. It is not exported via the header file. + * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */ +int _bsnmptools_debug = 0; + +/* Default files to import mapping from if none explicitly provided. */ +#define bsnmpd_defs "/usr/share/snmp/defs/tree.def" +#define mibII_defs "/usr/share/snmp/defs/mibII_tree.def" + +/* + * The .iso.org.dod oid that has to be prepended to every OID when requesting + * a value. + */ +const struct asn_oid IsoOrgDod_OID = { + 3, { 1, 3, 6 } +}; + + +#define SNMP_ERR_UNKNOWN 0 + +/* + * An array of error strings corresponding to error definitions from libbsnmp. + */ +static const struct { + const char *str; + int32_t error; +} error_strings[] = { + { "Unknown", SNMP_ERR_UNKNOWN }, + { "Too big ", SNMP_ERR_TOOBIG }, + { "No such Name", SNMP_ERR_NOSUCHNAME }, + { "Bad Value", SNMP_ERR_BADVALUE }, + { "Readonly", SNMP_ERR_READONLY }, + { "General error", SNMP_ERR_GENERR }, + { "No access", SNMP_ERR_NO_ACCESS }, + { "Wrong type", SNMP_ERR_WRONG_TYPE }, + { "Wrong lenght", SNMP_ERR_WRONG_LENGTH }, + { "Wrong encoding", SNMP_ERR_WRONG_ENCODING }, + { "Wrong value", SNMP_ERR_WRONG_VALUE }, + { "No creation", SNMP_ERR_NO_CREATION }, + { "Inconsistent value", SNMP_ERR_INCONS_VALUE }, + { "Resource unavailable", SNMP_ERR_RES_UNAVAIL }, + { "Commit failed", SNMP_ERR_COMMIT_FAILED }, + { "Undo failed", SNMP_ERR_UNDO_FAILED }, + { "Authorization error", SNMP_ERR_AUTH_ERR }, + { "Not writable", SNMP_ERR_NOT_WRITEABLE }, + { "Inconsistent name", SNMP_ERR_INCONS_NAME }, + { NULL, 0 } +}; + +/* This one and any following are exceptions. */ +#define SNMP_SYNTAX_UNKNOWN SNMP_SYNTAX_NOSUCHOBJECT + +static const struct { + const char *str; + enum snmp_syntax stx; +} syntax_strings[] = { + { "Null", SNMP_SYNTAX_NULL }, + { "Integer", SNMP_SYNTAX_INTEGER }, + { "OctetString", SNMP_SYNTAX_OCTETSTRING }, + { "OID", SNMP_SYNTAX_OID }, + { "IpAddress", SNMP_SYNTAX_IPADDRESS }, + { "Counter32", SNMP_SYNTAX_COUNTER }, + { "Gauge", SNMP_SYNTAX_GAUGE }, + { "TimeTicks", SNMP_SYNTAX_TIMETICKS }, + { "Counter64", SNMP_SYNTAX_COUNTER64 }, + { "Unknown", SNMP_SYNTAX_UNKNOWN }, +}; + +int +snmptool_init(struct snmp_toolinfo *snmptoolctx) +{ + char *str; + size_t slen; + + memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo)); + snmptoolctx->objects = 0; + snmptoolctx->mappings = NULL; + snmptoolctx->flags = SNMP_PDU_GET; /* XXX */ + SLIST_INIT(&snmptoolctx->filelist); + snmp_client_init(&snmp_client); + + if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0) + warnx("Error adding file %s to list", bsnmpd_defs); + + if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0) + warnx("Error adding file %s to list", mibII_defs); + + /* Read the environment */ + if ((str = getenv("SNMPAUTH")) != NULL) { + slen = strlen(str); + if (slen == strlen("md5") && strcasecmp(str, "md5") == 0) + snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; + else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0) + snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; + else if (slen != 0) + warnx("Bad authentication type - %s in SNMPAUTH", str); + } + + if ((str = getenv("SNMPPRIV")) != NULL) { + slen = strlen(str); + if (slen == strlen("des") && strcasecmp(str, "des") == 0) + snmp_client.user.priv_proto = SNMP_PRIV_DES; + else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0) + snmp_client.user.priv_proto = SNMP_PRIV_AES; + else if (slen != 0) + warnx("Bad privacy type - %s in SNMPPRIV", str); + } + + if ((str = getenv("SNMPUSER")) != NULL) { + if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) { + warnx("Username too long - %s in SNMPUSER", str); + return (-1); + } + if (slen > 0) { + strlcpy(snmp_client.user.sec_name, str, + sizeof(snmp_client.user.sec_name)); + snmp_client.version = SNMP_V3; + } + } + + if ((str = getenv("SNMPPASSWD")) != NULL) { + if ((slen = strlen(str)) > MAXSTR) + slen = MAXSTR - 1; + if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + if (slen > 0) + strlcpy(snmptoolctx->passwd, str, slen + 1); + } + + return (0); +} + +#define OBJECT_IDX_LIST(o) o->info->table_idx->index_list + +/* + * Walk through the file list and import string<->oid mappings from each file. + */ +int32_t +snmp_import_all(struct snmp_toolinfo *snmptoolctx) +{ + int32_t fc; + struct fname *tmp; + + if (snmptoolctx == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) + return (0); + + if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL) + return (-1); + + fc = 0; + if (SLIST_EMPTY(&snmptoolctx->filelist)) { + warnx("No files to read OID <-> string conversions from"); + return (-1); + } else { + SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) { + if (tmp->done) + continue; + if (snmp_import_file(snmptoolctx, tmp) < 0) { + fc = -1; + break; + } + fc++; + } + } + + snmp_mapping_dump(snmptoolctx); + return (fc); +} + +/* + * Add a filename to the file list - the initail idea of keeping a list with all + * files to read OIDs from was that an application might want to have loaded in + * memory the OIDs from a single file only and when done with them read the OIDs + * from another file. This is not used yet but might be a good idea at some + * point. Size argument is number of bytes in string including trailing '\0', + * not string lenght. + */ +int32_t +add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename, + const struct asn_oid *cut, int32_t done) +{ + char *fstring; + struct fname *entry; + + if (snmptoolctx == NULL) + return (-1); + + /* Make sure file was not in list. */ + SLIST_FOREACH(entry, &snmptoolctx->filelist, link) { + if (strncmp(entry->name, filename, strlen(entry->name)) == 0) + return (0); + } + + if ((fstring = malloc(strlen(filename) + 1)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + + if ((entry = malloc(sizeof(struct fname))) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + free(fstring); + return (-1); + } + + memset(entry, 0, sizeof(struct fname)); + + if (cut != NULL) + asn_append_oid(&(entry->cut), cut); + strlcpy(fstring, filename, strlen(filename) + 1); + entry->name = fstring; + entry->done = done; + SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link); + + return (1); +} + +void +free_filelist(struct snmp_toolinfo *snmptoolctx) +{ + struct fname *f; + + if (snmptoolctx == NULL) + return; /* XXX error handling */ + + while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) { + SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link); + if (f->name) + free(f->name); + free(f); + } +} + +static char +isvalid_fchar(char c, int pos) +{ + if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' || + (pos != 0 && isdigit(c))){ + return (c); + } + + if (c == '\0') + return (0); + + if (!isascii(c) || !isprint(c)) + warnx("Unexpected character %#2x", (u_int) c); + else + warnx("Illegal character '%c'", c); + + return (-1); +} + +/* + * Re-implement getsubopt from scratch, because the second argument is broken + * and will not compile with WARNS=5. + * Copied from src/contrib/bsnmp/snmpd/main.c. + */ +static int +getsubopt1(char **arg, const char *const *options, char **valp, char **optp) +{ + static const char *const delim = ",\t "; + u_int i; + char *ptr; + + *optp = NULL; + + /* Skip leading junk. */ + for (ptr = *arg; *ptr != '\0'; ptr++) + if (strchr(delim, *ptr) == NULL) + break; + if (*ptr == '\0') { + *arg = ptr; + return (-1); + } + *optp = ptr; + + /* Find the end of the option. */ + while (*++ptr != '\0') + if (strchr(delim, *ptr) != NULL || *ptr == '=') + break; + + if (*ptr != '\0') { + if (*ptr == '=') { + *ptr++ = '\0'; + *valp = ptr; + while (*ptr != '\0' && strchr(delim, *ptr) == NULL) + ptr++; + if (*ptr != '\0') + *ptr++ = '\0'; + } else + *ptr++ = '\0'; + } + + *arg = ptr; + + for (i = 0; *options != NULL; options++, i++) + if (strcmp(*optp, *options) == 0) + return (i); + return (-1); +} + +static int32_t +parse_path(char *value) +{ + int32_t i, len; + + if (value == NULL) + return (-1); + + for (len = 0; len < MAXPATHLEN; len++) { + i = isvalid_fchar(*(value + len), len) ; + + if (i == 0) + break; + else if (i < 0) + return (-1); + } + + if (len >= MAXPATHLEN || value[len] != '\0') { + warnx("Bad pathname - '%s'", value); + return (-1); + } + + return (len); +} + +static int32_t +parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path, + const struct asn_oid *cut) +{ + int32_t namelen; + char filename[MAXPATHLEN + 1]; + + if (value == NULL) + return (-1); + + do { + memset(filename, 0, MAXPATHLEN + 1); + + if (isalpha(*value) && (path == NULL || path[0] == '\0')) { + strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1); + namelen = strlen(SNMP_DEFS_DIR); + } else if (path != NULL){ + strlcpy(filename, path, MAXPATHLEN + 1); + namelen = strlen(path); + } else + namelen = 0; + + for ( ; namelen < MAXPATHLEN; value++) { + if (isvalid_fchar(*value, namelen) > 0) { + filename[namelen++] = *value; + continue; + } + + if (*value == ',' ) + value++; + else if (*value == '\0') + ; + else { + if (!isascii(*value) || !isprint(*value)) + warnx("Unexpected character %#2x in" + " filename", (u_int) *value); + else + warnx("Illegal character '%c' in" + " filename", *value); + return (-1); + } + + filename[namelen]='\0'; + break; + } + + if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) { + warnx("Filename %s too long", filename); + return (-1); + } + + if (add_filename(snmptoolctx, filename, cut, 0) < 0) { + warnx("Error adding file %s to list", filename); + return (-1); + } + } while (*value != '\0'); + + return(1); +} + +static int32_t +parse_ascii(char *ascii, uint8_t *binstr, size_t binlen) +{ + int32_t alen, count, saved_errno, i; + uint32_t val; + char dptr[3]; + + /* Filter 0x at the beggining */ + if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x') + i = 2; + else + i = 0; + + saved_errno = errno; + errno = 0; + for (count = 0; i < alen; i += 2) { + /* XXX: consider strlen(ascii) % 2 != 0 */ + dptr[0] = ascii[i]; + dptr[1] = ascii[i + 1]; + dptr[2] = '\0'; + if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) { + errno = saved_errno; + return (-1); + } + binstr[count] = (uint8_t) val; + if (++count >= binlen) { + warnx("Key %s too long - truncating to %lu octest", + ascii, binlen); + break; + } + } + + return (count); +} + +/* + * Functions to parse common input options for client tools and fill in the + * snmp_client structure. + */ +int32_t +parse_authentication(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "proto", + "key", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'proto' requires an argument"); + return (-1); + } + if (strlen(val) != 3) { + warnx("Unknown auth protocol - %s", val); + return (-1); + } + if (strncasecmp("md5", val, strlen("md5")) == 0) + snmp_client.user.auth_proto = + SNMP_AUTH_HMAC_MD5; + else if (strncasecmp("sha", val, strlen("sha")) == 0) + snmp_client.user.auth_proto = + SNMP_AUTH_HMAC_SHA; + else { + warnx("Unknown auth protocol - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'key' requires an argument"); + return (-1); + } + if (parse_ascii(val, snmp_client.user.auth_key, + SNMP_AUTH_KEY_SIZ) < 0) { + warnx("Bad authentication key- %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_privacy(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "proto", + "key", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'proto' requires an argument"); + return (-1); + } + if (strlen(val) != 3) { + warnx("Unknown privacy protocol - %s", val); + return (-1); + } + if (strncasecmp("aes", val, strlen("aes")) == 0) + snmp_client.user.priv_proto = SNMP_PRIV_AES; + else if (strncasecmp("des", val, strlen("des")) == 0) + snmp_client.user.priv_proto = SNMP_PRIV_DES; + else { + warnx("Unknown privacy protocol - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'key' requires an argument"); + return (-1); + } + if (parse_ascii(val, snmp_client.user.priv_key, + SNMP_PRIV_KEY_SIZ) < 0) { + warnx("Bad privacy key- %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_context(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "context", + "context-engine", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'context' - no argument"); + return (-1); + } + strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ); + break; + case 1: + if (val == NULL) { + warnx("Suboption 'context-engine' - no argument"); + return (-1); + } + if ((snmp_client.clen = parse_ascii(val, + snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) < 0) { + warnx("Bad EngineID - %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_user_security(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt, saved_errno; + char *val, *option; + const char *const subopts[] = { + "engine", + "engine-boots", + "engine-time", + "name", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'engine' - no argument"); + return (-1); + } + snmp_client.engine.engine_len = parse_ascii(val, + snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ); + if (snmp_client.engine.engine_len < 0) { + warnx("Bad EngineID - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'engine-boots' - no argument"); + return (-1); + } + saved_errno = errno; + errno = 0; + snmp_client.engine.engine_boots = strtoul(val, NULL, 10); + if (errno != 0) { + warnx("Bad 'engine-boots' value %s - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + break; + case 2: + if (val == NULL) { + warnx("Suboption 'engine-time' - no argument"); + return (-1); + } + saved_errno = errno; + errno = 0; + snmp_client.engine.engine_time = strtoul(val, NULL, 10); + if (errno != 0) { + warnx("Bad 'engine-time' value %s - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + break; + case 3: + strlcpy(snmp_client.user.sec_name, val, + SNMP_ADM_STR32_SIZ); + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0) + return (-1); + + return (2); +} + +int32_t +parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + char path[MAXPATHLEN + 1]; + int32_t cut_dflt, len, subopt; + struct asn_oid cut; + char *val, *option; + const char *const subopts[] = { + "cut", + "path", + "file", + NULL + }; + +#define INC_CUT 0 +#define INC_PATH 1 +#define INC_LIST 2 + + assert(opt_arg != NULL); + + /* if (opt == 'i') + free_filelist(snmptoolctx, ); */ + /* + * This function should be called only after getopt(3) - otherwise if + * no previous validation of opt_arg strlen() may not return what is + * expected. + */ + + path[0] = '\0'; + memset(&cut, 0, sizeof(struct asn_oid)); + cut_dflt = -1; + + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case INC_CUT: + if (val == NULL) { + warnx("Suboption 'cut' requires an argument"); + return (-1); + } else { + if (snmp_parse_numoid(val, &cut) < 0) + return (-1); + } + cut_dflt = 1; + break; + + case INC_PATH: + if ((len = parse_path(val)) < 0) + return (-1); + strlcpy(path, val, len + 1); + break; + + case INC_LIST: + if (val == NULL) + return (-1); + if (cut_dflt == -1) + len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID); + else + len = parse_flist(snmptoolctx, val, path, &cut); + if (len < 0) + return (-1); + break; + + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + } + + /* XXX: Fix me - returning two is wrong here */ + return (2); +} + +int32_t +parse_server(char *opt_arg) +{ + assert(opt_arg != NULL); + + if (snmp_parse_server(&snmp_client, opt_arg) < 0) + return (-1); + + if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) { + if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL + 1))) + == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL); + } + + return (2); +} + +int32_t +parse_timeout(char *opt_arg) +{ + int32_t v, saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtol(opt_arg, NULL, 10); + if (errno != 0) { + warnx( "Error parsing timeout value - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + snmp_client.timeout.tv_sec = v; + errno = saved_errno; + return (2); +} + +int32_t +parse_retry(char *opt_arg) +{ + uint32_t v; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing retries count - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + snmp_client.retries = v; + errno = saved_errno; + return (2); +} + +int32_t +parse_version(char *opt_arg) +{ + uint32_t v; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing version - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + switch (v) { + case 1: + snmp_client.version = SNMP_V1; + break; + case 2: + snmp_client.version = SNMP_V2c; + break; + case 3: + snmp_client.version = SNMP_V3; + break; + default: + warnx("Unsupported SNMP version - %u", v); + errno = saved_errno; + return (-1); + } + + errno = saved_errno; + return (2); +} + +int32_t +parse_local_path(char *opt_arg) +{ + assert(opt_arg != NULL); + + if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) { + warnx("Filename too long - %s", opt_arg); + return (-1); + } + + strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH)); + return (2); +} + +int32_t +parse_buflen(char *opt_arg) +{ + uint32_t size; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + size = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing buffer size - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + if (size > MAX_BUFF_SIZE) { + warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE); + errno = saved_errno; + return (-1); + } + + snmp_client.txbuflen = snmp_client.rxbuflen = size; + errno = saved_errno; + return (2); +} + +int32_t +parse_debug(void) +{ + snmp_client.dump_pdus = 1; + return (1); +} + +int32_t +parse_discovery(struct snmp_toolinfo *snmptoolctx) +{ + SET_EDISCOVER(snmptoolctx); + snmp_client.version = SNMP_V3; + return (1); +} + +int32_t +parse_local_key(struct snmp_toolinfo *snmptoolctx) +{ + SET_LOCALKEY(snmptoolctx); + snmp_client.version = SNMP_V3; + return (1); +} + +int32_t +parse_num_oids(struct snmp_toolinfo *snmptoolctx) +{ + SET_NUMERIC(snmptoolctx); + return (1); +} + +int32_t +parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (strlen(opt_arg) > strlen("verbose")) { + warnx( "Invalid output option - %s",opt_arg); + return (-1); + } + + if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_SHORT); + else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE); + else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR); + else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_QUIET); + else { + warnx( "Invalid output option - %s", opt_arg); + return (-1); + } + + return (2); +} + +int32_t +parse_errors(struct snmp_toolinfo *snmptoolctx) +{ + SET_RETRY(snmptoolctx); + return (1); +} + +int32_t +parse_skip_access(struct snmp_toolinfo *snmptoolctx) +{ + SET_ERRIGNORE(snmptoolctx); + return (1); +} + +char * +snmp_parse_suboid(char *str, struct asn_oid *oid) +{ + char *endptr; + asn_subid_t suboid; + + if (*str == '.') + str++; + + if (*str < '0' || *str > '9') + return (str); + + do { + suboid = strtoul(str, &endptr, 10); + if ((asn_subid_t) suboid > ASN_MAXID) { + warnx("Suboid %u > ASN_MAXID", suboid); + return (NULL); + } + if (snmp_suboid_append(oid, suboid) < 0) + return (NULL); + str = endptr + 1; + } while (*endptr == '.'); + + return (endptr); +} + +static char * +snmp_int2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + int32_t v, saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtol(str, &endptr, 10); + if (errno != 0) { + warnx("Integer value %s not supported - %s", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +/* It is a bit weird to have a table indexed by OID but still... */ +static char * +snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str, + struct asn_oid *oid) +{ + int32_t i; + char string[MAXSTR], *endptr; + struct snmp_object obj; + + for (i = 0; i < MAXSTR; i++) + if (isalpha (*(str + i)) == 0) + break; + + endptr = str + i; + memset(&obj, 0, sizeof(struct snmp_object)); + if (i == 0) { + if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0) + return (NULL); + } else { + strlcpy(string, str, i + 1); + string[i] = '\0'; + if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { + warnx("Unknown string - %s",string); + return (NULL); + } + free(string); + } + + asn_append_oid(oid, &(obj.val.var)); + return (endptr); +} + +static char * +snmp_ip2asn_oid(char *str, struct asn_oid *oid) +{ + uint32_t v; + int32_t i; + char *endptr, *ptr; + + ptr = str; + for (i = 0; i < 4; i++) { + v = strtoul(ptr, &endptr, 10); + if (v > 0xff) + return (NULL); + if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + return (endptr); +} + +/* 32-bit counter, gauge, timeticks. */ +static char * +snmp_uint2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + uint32_t v; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoul(str, &endptr, 10); + if (errno != 0) { + warnx("Integer value %s not supported - %s\n", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static char * +snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + uint64_t v; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoull(str, &endptr, 10); + + if (errno != 0) { + warnx("Integer value %s not supported - %s", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0) + return (NULL); + + return (endptr); +} + +enum snmp_syntax +parse_syntax(char *str) +{ + int32_t i; + + for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) { + if (strncmp(syntax_strings[i].str, str, + strlen(syntax_strings[i].str)) == 0) + return (syntax_strings[i].stx); + } + + return (SNMP_SYNTAX_NULL); +} + +static char * +snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str, + struct index *idx, struct snmp_object *object) +{ + char *ptr; + int32_t i; + enum snmp_syntax stx; + char syntax[MAX_CMD_SYNTAX_LEN]; + + ptr = str; + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { + for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) { + if (*(ptr + i) == ':') + break; + } + + if (i >= MAX_CMD_SYNTAX_LEN) { + warnx("Unknown syntax in OID - %s", str); + return (NULL); + } + /* Expect a syntax string here. */ + if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { + warnx("Invalid syntax - %s",syntax); + return (NULL); + } + + if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) { + warnx("Syntax mismatch - %d expected, %d given", + idx->syntax, stx); + return (NULL); + } + /* + * That is where the suboid started + the syntax length + one + * character for ':'. + */ + ptr = str + i + 1; + } else + stx = idx->syntax; + + switch (stx) { + case SNMP_SYNTAX_INTEGER: + return (snmp_int2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_OID: + return (snmp_oid2asn_oid(snmptoolctx, ptr, + &(object->val.var))); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_ip2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_COUNTER: + /* FALLTHROUGH */ + case SNMP_SYNTAX_GAUGE: + /* FALLTHROUGH */ + case SNMP_SYNTAX_TIMETICKS: + return (snmp_uint2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_COUNTER64: + return (snmp_cnt64_2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oid(idx->tc, ptr, &(object->val.var))); + default: + /* NOTREACHED */ + break; + } + + return (NULL); +} + +char * +snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str, + struct snmp_object *object) +{ + char *ptr; + struct index *temp; + + if (object->info->table_idx == NULL) + return (NULL); + + ptr = NULL; + STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) { + if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object)) + == NULL) + return (NULL); + + if (*ptr != ',' && *ptr != ']') + return (NULL); + str = ptr + 1; + } + + if (ptr == NULL || *ptr != ']') { + warnx("Mismatching index - %s", str); + return (NULL); + } + + return (ptr + 1); +} + +/* + * Fill in the struct asn_oid member of snmp_value with suboids from input. + * If an error occurs - print message on stderr and return (-1). + * If all is ok - return the length of the oid. + */ +int32_t +snmp_parse_numoid(char *argv, struct asn_oid *var) +{ + char *endptr, *str; + asn_subid_t suboid; + + str = argv; + + if (*str == '.') + str++; + + do { + if (var->len == ASN_MAXOIDLEN) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + suboid = strtoul(str, &endptr, 10); + if (suboid > ASN_MAXID) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + var->subs[var->len++] = suboid; + str = endptr + 1; + } while ( *endptr == '.'); + + if (*endptr != '\0') { + warnx("Invalid oid string - %s", argv); + return (-1); + } + + return (var->len); +} + +/* Append a length 1 suboid to an asn_oid structure. */ +int32_t +snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid) +{ + if (var == NULL) + return (-1); + + if (var->len >= ASN_MAXOIDLEN) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + var->subs[var->len++] = suboid; + + return (1); +} + +/* Pop the last suboid from an asn_oid structure. */ +int32_t +snmp_suboid_pop(struct asn_oid *var) +{ + asn_subid_t suboid; + + if (var == NULL) + return (-1); + + if (var->len < 1) + return (-1); + + suboid = var->subs[--(var->len)]; + var->subs[var->len] = 0; + + return (suboid); +} + +/* + * Parse the command-line provided string into an OID - alocate memory for a new + * snmp object, fill in its fields and insert it in the object list. A + * (snmp_verify_inoid_f) function must be provided to validate the input string. + */ +int32_t +snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func, + char *string) +{ + struct snmp_object *obj; + + if (snmptoolctx == NULL) + return (-1); + + /* XXX-BZ does that chack make sense? */ + if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) { + warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1); + return (-1); + } + + if ((obj = malloc(sizeof(struct snmp_object))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(obj, 0, sizeof(struct snmp_object)); + if (func(snmptoolctx, obj, string) < 0) { + warnx("Invalid OID - %s", string); + free(obj); + return (-1); + } + + snmptoolctx->objects++; + SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link); + + return (1); +} + +/* Given an OID, find it in the object list and remove it. */ +int32_t +snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) +{ + struct snmp_object *temp; + + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) { + warnx("Object list already empty"); + return (-1); + } + + + SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link) + if (asn_compare_oid(&(temp->val.var), oid) == 0) + break; + + if (temp == NULL) { + warnx("No such object in list"); + return (-1); + } + + SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link); + if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING && + temp->val.v.octetstring.octets != NULL) + free(temp->val.v.octetstring.octets); + free(temp); + + return (1); +} + +static void +snmp_object_freeall(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_object *o; + + while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) { + SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link); + + if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING && + o->val.v.octetstring.octets != NULL) + free(o->val.v.octetstring.octets); + free(o); + } +} + +/* Do all possible memory release before exit. */ +void +snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx) +{ + if (snmp_client.chost != NULL) { + free(snmp_client.chost); + snmp_client.chost = NULL; + } + + if (snmp_client.cport != NULL) { + free(snmp_client.cport); + snmp_client.cport = NULL; + } + + snmp_mapping_free(snmptoolctx); + free_filelist(snmptoolctx); + snmp_object_freeall(snmptoolctx); + + if (snmptoolctx->passwd != NULL) { + free(snmptoolctx->passwd); + snmptoolctx->passwd = NULL; + } +} + +/* + * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f) + * function should check whether the variable is consistent in this PDU + * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to + * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the + * function actually adds the variable to the PDU and must not be NULL. + */ +int32_t +snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx, + snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc, + struct snmp_pdu *pdu, int32_t maxcount) +{ + int32_t nbindings, abind; + struct snmp_object *obj; + + if (pdu == NULL || afunc == NULL) + return (-1); + + /* Return 0 in case of no more work todo. */ + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) + return (0); + + if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) { + warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS"); + return (-1); + } + + nbindings = 0; + SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) { + if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) { + nbindings = -1; + break; + } + if ((abind = afunc(pdu, obj)) < 0) { + nbindings = -1; + break; + } + + if (abind > 0) { + /* Do not put more varbindings than requested. */ + if (++nbindings >= maxcount) + break; + } + } + + return (nbindings); +} + +/* + * Locate an object in the object list and set a corresponding error status. + */ +int32_t +snmp_object_seterror(struct snmp_toolinfo *snmptoolctx, + struct snmp_value *err_value, int32_t error_status) +{ + struct snmp_object *obj; + + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL) + return (-1); + + SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) + if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) { + obj->error = error_status; + return (1); + } + + return (0); +} + +/* + * Check a PDU received in responce to a SNMP_PDU_GET/SNMP_PDU_GETBULK request + * but don't compare syntaxes - when sending a request PDU they must be null. + * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes + * checks and some other checks skiped. + */ +int32_t +snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + uint32_t i; + + for (i = 0; i < req->nbindings; i++) { + if (asn_compare_oid(&req->bindings[i].var, + &resp->bindings[i].var) != 0) { + warnx("Bad OID in response"); + return (-1); + } + + if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax + == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax == + SNMP_SYNTAX_NOSUCHINSTANCE)) + return (0); + } + + return (1); +} + +int32_t +snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + int32_t N, R, M, r; + + if (req->error_status > (int32_t) resp->nbindings) { + warnx("Bad number of bindings in response"); + return (-1); + } + + for (N = 0; N < req->error_status; N++) { + if (asn_is_suboid(&req->bindings[N].var, + &resp->bindings[N].var) == 0) + return (0); + if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) + return (0); + } + + for (R = N , r = N; R < (int32_t) req->nbindings; R++) { + for (M = 0; M < req->error_index && (r + M) < + (int32_t) resp->nbindings; M++) { + if (asn_is_suboid(&req->bindings[R].var, + &resp->bindings[r + M].var) == 0) + return (0); + + if (resp->bindings[r + M].syntax == + SNMP_SYNTAX_ENDOFMIBVIEW) { + M++; + break; + } + } + r += M; + } + + return (0); +} + +int32_t +snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + uint32_t i; + + for (i = 0; i < req->nbindings; i++) { + if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var) + == 0) + return (0); + + if (resp->version != SNMP_V1 && resp->bindings[i].syntax == + SNMP_SYNTAX_ENDOFMIBVIEW) + return (0); + } + + return (1); +} + +/* + * Should be called to check a responce to get/getnext/getbulk. + */ +int32_t +snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + if (resp == NULL || req == NULL) + return (-2); + + if (resp->version != req->version) { + warnx("Response has wrong version"); + return (-1); + } + + if (resp->error_status == SNMP_ERR_NOSUCHNAME) { + warnx("Error - No Such Name"); + return (0); + } + + if (resp->error_status != SNMP_ERR_NOERROR) { + warnx("Error %d in responce", resp->error_status); + return (-1); + } + + if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){ + warnx("Bad number of bindings in response"); + return (-1); + } + + switch (req->type) { + case SNMP_PDU_GET: + return (snmp_parse_get_resp(resp,req)); + case SNMP_PDU_GETBULK: + return (snmp_parse_getbulk_resp(resp,req)); + case SNMP_PDU_GETNEXT: + return (snmp_parse_getnext_resp(resp,req)); + default: + /* NOTREACHED */ + break; + } + + return (-2); +} + +static void +snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, + uint32_t len, uint8_t *octets) +{ + char *buf; + + if (len == 0 || octets == NULL) + return; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_OCTETSTRING].str); + + if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) { + fprintf(stdout, "%s", buf); + free(buf); + } +} + +static void +snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, + struct asn_oid *oid) +{ + uint32_t i; + uint8_t *s; + + if ((s = malloc(oid->subs[0] + 1)) == NULL) + syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); + else { + for (i = 0; i < oid->subs[0]; i++) + s[i] = (u_char) (oid->subs[i + 1]); + + snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s); + free(s); + } +} + +/* + * Check and output syntax type and value. + */ +static void +snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) +{ + char oid_string[ASN_OIDSTRLEN]; + struct snmp_object obj; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str); + + if(!ISSET_NUMERIC(snmptoolctx)) { + memset(&obj, 0, sizeof(struct snmp_object)); + asn_append_oid(&(obj.val.var), oid); + + if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else { + (void) asn_oid2str_r(oid, oid_string); + fprintf(stdout, "%s", oid_string); + } + } else { + (void) asn_oid2str_r(oid, oid_string); + fprintf(stdout, "%s", oid_string); + } +} + +static void +snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums, + int32_t int_val) +{ + char *string; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_INTEGER].str); + + if (enums != NULL && (string = enum_string_lookup(enums, int_val)) + != NULL) + fprintf(stdout, "%s", string); + else + fprintf(stdout, "%d", int_val); +} + +static void +snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_IPADDRESS].str); + + fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); +} + +static void +snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_COUNTER].str); + + fprintf(stdout, "%u", counter); +} + +static void +snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str); + + fprintf(stdout, "%u", gauge); +} + +static void +snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_TIMETICKS].str); + + fprintf(stdout, "%u", ticks); +} + +static void +snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_COUNTER64].str); + + fprintf(stdout,"%ju", counter64); +} + +int32_t +snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val, + struct snmp_oid2str *entry) +{ + if (val == NULL) + return (-1); + + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) + fprintf(stdout, " = "); + + switch (val->syntax) { + case SNMP_SYNTAX_INTEGER: + if (entry != NULL) + snmp_output_int(snmptoolctx, entry->snmp_enum, + val->v.integer); + else + snmp_output_int(snmptoolctx, NULL, val->v.integer); + break; + + case SNMP_SYNTAX_OCTETSTRING: + if (entry != NULL) + snmp_output_octetstring(snmptoolctx, entry->tc, + val->v.octetstring.len, val->v.octetstring.octets); + else + snmp_output_octetstring(snmptoolctx, SNMP_STRING, + val->v.octetstring.len, val->v.octetstring.octets); + break; + + case SNMP_SYNTAX_OID: + snmp_output_oid_value(snmptoolctx, &(val->v.oid)); + break; + + case SNMP_SYNTAX_IPADDRESS: + snmp_output_ipaddress(snmptoolctx, val->v.ipaddress); + break; + + case SNMP_SYNTAX_COUNTER: + snmp_output_counter(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_GAUGE: + snmp_output_gauge(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_TIMETICKS: + snmp_output_ticks(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_COUNTER64: + snmp_output_counter64(snmptoolctx, val->v.counter64); + break; + + case SNMP_SYNTAX_NOSUCHOBJECT: + fprintf(stdout, "No Such Object\n"); + return (val->syntax); + + case SNMP_SYNTAX_NOSUCHINSTANCE: + fprintf(stdout, "No Such Instance\n"); + return (val->syntax); + + case SNMP_SYNTAX_ENDOFMIBVIEW: + fprintf(stdout, "End of Mib View\n"); + return (val->syntax); + + case SNMP_SYNTAX_NULL: + /* NOTREACHED */ + fprintf(stdout, "agent returned NULL Syntax\n"); + return (val->syntax); + + default: + /* NOTREACHED - If here - then all went completely wrong. */ + fprintf(stdout, "agent returned unknown syntax\n"); + return (-1); + } + + fprintf(stdout, "\n"); + + return (0); +} + +static int32_t +snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, + struct snmp_value *val) +{ + int32_t rc; + asn_subid_t suboid; + + if (obj == NULL || val == NULL) + return (-1); + + if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID) + return (-1); + + memset(obj, 0, sizeof(struct snmp_object)); + asn_append_oid(&(obj->val.var), &(val->var)); + obj->val.syntax = val->syntax; + + if (obj->val.syntax > 0) + rc = snmp_lookup_leafstring(snmptoolctx, obj); + else + rc = snmp_lookup_nonleaf_string(snmptoolctx, obj); + + (void) snmp_suboid_append(&(val->var), suboid); + (void) snmp_suboid_append(&(obj->val.var), suboid); + + return (rc); +} + +static int32_t +snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx, + struct asn_oid *oid) +{ + uint8_t ip[4]; + uint32_t bytes = 1; + uint64_t cnt64; + struct asn_oid temp, out; + + if (oid->len < bytes) + return (-1); + + memset(&temp, 0, sizeof(struct asn_oid)); + asn_append_oid(&temp, oid); + + switch (stx->syntax) { + case SNMP_SYNTAX_INTEGER: + snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]); + break; + + case SNMP_SYNTAX_OCTETSTRING: + if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] > + ASN_MAXOCTETSTRING)) + return (-1); + snmp_output_octetindex(snmptoolctx, stx->tc, &temp); + bytes += temp.subs[0]; + break; + + case SNMP_SYNTAX_OID: + if ((temp.subs[0] > temp.len -1) || (temp.subs[0] > + ASN_MAXOIDLEN)) + return (-1); + + bytes += temp.subs[0]; + memset(&out, 0, sizeof(struct asn_oid)); + asn_slice_oid(&out, &temp, 1, bytes); + snmp_output_oid_value(snmptoolctx, &out); + break; + + case SNMP_SYNTAX_IPADDRESS: + if (temp.len < 4) + return (-1); + for (bytes = 0; bytes < 4; bytes++) + ip[bytes] = temp.subs[bytes]; + + snmp_output_ipaddress(snmptoolctx, ip); + bytes = 4; + break; + + case SNMP_SYNTAX_COUNTER: + snmp_output_counter(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_GAUGE: + snmp_output_gauge(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_TIMETICKS: + snmp_output_ticks(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_COUNTER64: + if (oid->len < 2) + return (-1); + bytes = 2; + memcpy(&cnt64, temp.subs, bytes); + snmp_output_counter64(snmptoolctx, cnt64); + break; + + default: + return (-1); + } + + return (bytes); +} + +static int32_t +snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o) +{ + int32_t i, first, len; + struct asn_oid oid; + struct index *temp; + + if (ISSET_NUMERIC(snmptoolctx)) + return (-1); + + if (o->info->table_idx == NULL) { + fprintf(stdout,"%s.%d", o->info->string, + o->val.var.subs[o->val.var.len - 1]); + return (1); + } + + fprintf(stdout,"%s[", o->info->string); + memset(&oid, 0, sizeof(struct asn_oid)); + + len = 1; + asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), + o->val.var.len); + + first = 1; + STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) { + if(first) + first = 0; + else + fprintf(stdout, ", "); + if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0) + break; + len += i; + memset(&oid, 0, sizeof(struct asn_oid)); + asn_slice_oid(&oid, &(o->val.var), + (o->info->table_idx->var.len + len), o->val.var.len + 1); + } + + fprintf(stdout,"]"); + return (1); +} + +void +snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_object object; + + if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) { + fprintf(stdout,"Invalid error index in PDU\n"); + return; + } + + fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost, + snmp_client.cport); + + if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, + &(pdu->bindings[pdu->error_index - 1])) > 0)) + snmp_output_object(snmptoolctx, &object); + else { + asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf); + fprintf(stdout,"%s", buf); + } + + fprintf(stdout," caused error - "); + if ((pdu->error_status > 0) && (pdu->error_status <= + SNMP_ERR_INCONS_NAME)) + fprintf(stdout, "%s\n", error_strings[pdu->error_status].str); + else + fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str); +} + +int32_t +snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) +{ + int32_t error; + char p[ASN_OIDSTRLEN]; + uint32_t i; + struct snmp_object object; + + for (i = 0, error = 0; i < pdu->nbindings; i++) { + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) { + if (!ISSET_NUMERIC(snmptoolctx) && + (snmp_fill_object(snmptoolctx, &object, + &(pdu->bindings[i])) > 0)) + snmp_output_object(snmptoolctx, &object); + else { + asn_oid2str_r(&(pdu->bindings[i].var), p); + fprintf(stdout, "%s", p); + } + } + error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info); + } + + return (error); +} + +void +snmp_output_engine(void) +{ + uint32_t i; + char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; + + cptr = engine; + for (i = 0; i < snmp_client.engine.engine_len; i++) + cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]); + *cptr++ = '\0'; + + fprintf(stdout, "Engine ID 0x%s\n", engine); + fprintf(stdout, "Boots : %u\t\tTime : %d\n", + snmp_client.engine.engine_boots, + snmp_client.engine.engine_time); +} + +void +snmp_output_keys(void) +{ + uint32_t i, keylen = 0; + char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2]; + + fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name); + if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) { + fprintf(stdout, "MD5 : 0x"); + keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; + } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) { + fprintf(stdout, "SHA : 0x"); + keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; + } + if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) { + cptr = extkey; + for (i = 0; i < keylen; i++) + cptr += sprintf(cptr, "%.2x", + snmp_client.user.auth_key[i]); + *cptr++ = '\0'; + fprintf(stdout, "%s\n", extkey); + } + + if (snmp_client.user.priv_proto == SNMP_PRIV_DES) { + fprintf(stdout, "DES : 0x"); + keylen = SNMP_PRIV_DES_KEY_SIZ; + } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) { + fprintf(stdout, "AES : 0x"); + keylen = SNMP_PRIV_AES_KEY_SIZ; + } + if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) { + cptr = extkey; + for (i = 0; i < keylen; i++) + cptr += sprintf(cptr, "%.2x", + snmp_client.user.priv_key[i]); + *cptr++ = '\0'; + fprintf(stdout, "%s\n", extkey); + } +} Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h (revision 0) @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Helper functions common for all tools. + */ +#ifndef _BSNMP_TOOLS_H_ +#define _BSNMP_TOOLS_H_ + +/* From asn1.h + 1 byte for trailing zero. */ +#define MAX_OCTSTRING_LEN ASN_MAXOCTETSTRING + 1 +#define MAX_CMD_SYNTAX_LEN 12 + +/* Arbitrary upper limit on node names and function names - gensnmptree.c. */ +#define MAXSTR 1000 + +/* Should be enough to fetch the biggest allowed octet string. */ +#define MAX_BUFF_SIZE (ASN_MAXOCTETSTRING + 50) + +#define SNMP_DEFS_DIR "/usr/share/snmp/defs/" +#define SNMP_DEFAULT_LOCAL "/var/run/snmpd.sock" + +enum snmp_access { + SNMP_ACCESS_NONE = 0, + SNMP_ACCESS_GET, + SNMP_ACCESS_SET, + SNMP_ACCESS_GETSET, +}; + +/* A structure for integer-string enumerations. */ +struct enum_pair { + int32_t enum_val; + char *enum_str; + STAILQ_ENTRY(enum_pair) link; +}; + +STAILQ_HEAD(enum_pairs, enum_pair); + +struct enum_type { + char *name; + uint32_t syntax; + int32_t is_enum; + int32_t is_bits; + struct enum_pairs *snmp_enum; + SLIST_ENTRY(enum_type) link; +}; + +SLIST_HEAD(snmp_enum_tc, enum_type); + +struct index { + enum snmp_tc tc; + enum snmp_syntax syntax; + struct enum_pairs *snmp_enum; + STAILQ_ENTRY(index) link; +}; + +STAILQ_HEAD(snmp_idxlist, index); + +struct snmp_index_entry { + char *string; + uint32_t strlen; + struct asn_oid var; + struct snmp_idxlist index_list; + SLIST_ENTRY(snmp_index_entry) link; +}; + +/* Information needed for oid to string conversion. */ +struct snmp_oid2str { + char *string; + uint32_t strlen; + enum snmp_tc tc; + enum snmp_syntax syntax; + enum snmp_access access; + struct asn_oid var; + /* A pointer to a entry from the table list - OK if NULL. */ + struct snmp_index_entry *table_idx; + /* + * A singly-linked tail queue of all (int, string) pairs - + * for INTEGER syntax only. + */ + struct enum_pairs *snmp_enum; + SLIST_ENTRY(snmp_oid2str) link; +}; + +/* A structure to hold each oid input by user. */ +struct snmp_object { + /* Flag - if set, the variable caused error in a previous request. */ + int32_t error; + /* + * A pointer in the mapping lists - not used if OIDs are input as + * numericals. + */ + struct snmp_oid2str *info; + /* A snmp value to hold the actual oid, syntax and value. */ + struct snmp_value val; + SLIST_ENTRY(snmp_object) link; +}; + +struct fname { + char *name; + int32_t done; + struct asn_oid cut; + SLIST_ENTRY(fname) link; +}; + +SLIST_HEAD(snmp_mapping, snmp_oid2str); +SLIST_HEAD(fname_list, fname); +SLIST_HEAD(snmp_table_index, snmp_index_entry); + +/* + * Keep a list for every syntax type. + */ +struct snmp_mappings { + /* The list containing all non-leaf nodes. */ + struct snmp_mapping nodelist; + /* INTEGER/INTEGER32 types. */ + struct snmp_mapping intlist; + /* OCTETSTRING types. */ + struct snmp_mapping octlist; + /* OID types. */ + struct snmp_mapping oidlist; + /* IPADDRESS types. */ + struct snmp_mapping iplist; + /* TIMETICKS types. */ + struct snmp_mapping ticklist; + /* COUNTER types. */ + struct snmp_mapping cntlist; + /* GAUGE types. */ + struct snmp_mapping gaugelist; + /* COUNTER64 types. */ + struct snmp_mapping cnt64list; + /* ENUM values for oid types. */ + struct snmp_mapping enumlist; + /* Description of all table entry types. */ + struct snmp_table_index tablelist; + /* Defined enumerated textual conventions. */ + struct snmp_enum_tc tclist; +}; + +struct snmp_toolinfo { + uint32_t flags; + /* Number of initially input OIDs. */ + int32_t objects; + /* List of all input OIDs. */ + SLIST_HEAD(snmp_objectlist, snmp_object) snmp_objectlist; + /* All known OID to string mapping data. */ + struct snmp_mappings *mappings; + /* A list of .defs filenames to search oid<->string mapping. */ + struct fname_list filelist; + /* SNMPv3 USM user credentials */ + char *passwd; +}; + +/* XXX we might want to get away with this and will need to touch + * XXX the MACROS then too */ +extern struct snmp_toolinfo snmptool; + +/* Definitions for some flags' bits. */ +#define OUTPUT_BITS 0x00000003 /* bits 0-1 for output type */ +#define NUMERIC_BIT 0x00000004 /* bit 2 for numeric oids */ +#define RETRY_BIT 0x00000008 /* bit 3 for retry on error responce */ +#define ERRIGNORE_BIT 0x00000010 /* bit 4 for skip sanity checking */ +#define ERRIGNORE_BIT 0x00000010 /* bit 4 for skip sanity checking */ +#define EDISCOVER_BIT 0x00000020 /* bit 5 for SNMP Engine Discovery */ +#define LOCALKEY_BIT 0x00000040 /* bit 6 for using localized key */ + /* 0x00000080 */ /* bit 7 reserverd */ +#define PDUTYPE_BITS 0x00000f00 /* bits 8-11 for pdu type */ + /* 0x0000f000 */ /* bit 12-15 reserverd */ +#define MAXREP_BITS 0x00ff0000 /* bits 16-23 for max-repetit. value */ +#define NONREP_BITS 0xff000000 /* bits 24-31 for non-repeaters value */ + +#define OUTPUT_SHORT 0x0 +#define OUTPUT_VERBOSE 0x1 +#define OUTPUT_TABULAR 0x2 +#define OUTPUT_QUIET 0x3 + +/* Macros for playing with flags' bits. */ +#define SET_OUTPUT(ctx, type) ((ctx)->flags |= ((type) & OUTPUT_BITS)) +#define GET_OUTPUT(ctx) ((ctx)->flags & OUTPUT_BITS) + +#define SET_NUMERIC(ctx) ((ctx)->flags |= NUMERIC_BIT) +#define ISSET_NUMERIC(ctx) ((ctx)->flags & NUMERIC_BIT) + +#define SET_RETRY(ctx) ((ctx)->flags |= RETRY_BIT) +#define ISSET_RETRY(ctx) ((ctx)->flags & RETRY_BIT) + +#define SET_ERRIGNORE(ctx) ((ctx)->flags |= ERRIGNORE_BIT) +#define ISSET_ERRIGNORE(ctx) ((ctx)->flags & ERRIGNORE_BIT) + +#define SET_EDISCOVER(ctx) ((ctx)->flags |= EDISCOVER_BIT) +#define ISSET_EDISCOVER(ctx) ((ctx)->flags & EDISCOVER_BIT) + +#define SET_LOCALKEY(ctx) ((ctx)->flags |= LOCALKEY_BIT) +#define ISSET_LOCALKEY(ctx) ((ctx)->flags & LOCALKEY_BIT) + +#define SET_PDUTYPE(ctx, type) (((ctx)->flags |= (((type) & 0xf) << 8))) +#define GET_PDUTYPE(ctx) (((ctx)->flags & PDUTYPE_BITS) >> 8) + +#define SET_MAXREP(ctx, i) (((ctx)->flags |= (((i) & 0xff) << 16))) +#define GET_MAXREP(ctx) (((ctx)->flags & MAXREP_BITS) >> 16) + +#define SET_NONREP(ctx, i) (((ctx)->flags |= (((i) & 0xff) << 24))) +#define GET_NONREP(ctx) (((ctx)->flags & NONREP_BITS) >> 24) + + +extern const struct asn_oid IsoOrgDod_OID; + +int snmptool_init(struct snmp_toolinfo *); +int32_t snmp_import_file(struct snmp_toolinfo *, struct fname *); +int32_t snmp_import_all(struct snmp_toolinfo *); +int32_t add_filename(struct snmp_toolinfo *, const char *, + const struct asn_oid *, int32_t); +void free_filelist(struct snmp_toolinfo *); +void snmp_tool_freeall(struct snmp_toolinfo *); +void snmp_import_dump(int); + +/* bsnmpmap.c */ +struct snmp_mappings *snmp_mapping_init(void); +int32_t snmp_mapping_free(struct snmp_toolinfo *); +void snmp_index_listfree(struct snmp_idxlist *); +void snmp_dump_oid2str(struct snmp_oid2str *); +int32_t snmp_node_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +int32_t snmp_leaf_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +int32_t snmp_enum_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +struct enum_pairs *enum_pairs_init(void); +void enum_pairs_free(struct enum_pairs *); +void snmp_mapping_entryfree(struct snmp_oid2str *); +int32_t enum_pair_insert(struct enum_pairs *, int32_t, char *); +char *enum_string_lookup(struct enum_pairs *, int32_t); +int32_t enum_number_lookup(struct enum_pairs *, char *); +int32_t snmp_syntax_insert(struct snmp_idxlist *, struct enum_pairs *, + enum snmp_syntax, enum snmp_tc); +int32_t snmp_table_insert(struct snmp_toolinfo *, struct snmp_index_entry *); + +struct enum_type *snmp_enumtc_init(char *); +void snmp_enumtc_free(struct enum_type *); +void snmp_enumtc_insert(struct snmp_toolinfo *, struct enum_type *); +struct enum_type *snmp_enumtc_lookup(struct snmp_toolinfo *, char *); + +void snmp_mapping_dump(struct snmp_toolinfo *); +int32_t snmp_lookup_leafstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_enumstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_oidstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_nonleaf_string(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_allstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_nodestring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_oidall(struct snmp_toolinfo *, struct snmp_object *, char *); +int32_t snmp_lookup_enumoid(struct snmp_toolinfo *, struct snmp_object *, char *); +int32_t snmp_lookup_oid(struct snmp_toolinfo *, struct snmp_object *, char *); + +/* Functions parsing common options for all tools. */ +int32_t parse_server(char *); +int32_t parse_timeout(char *); +int32_t parse_retry(char *); +int32_t parse_version(char *); +int32_t parse_local_path(char *); +int32_t parse_buflen(char *); +int32_t parse_debug(void); +int32_t parse_discovery(struct snmp_toolinfo *); +int32_t parse_local_key(struct snmp_toolinfo *); +int32_t parse_num_oids(struct snmp_toolinfo *); +int32_t parse_file(struct snmp_toolinfo *, char *); +int32_t parse_include(struct snmp_toolinfo *, char *); +int32_t parse_output(struct snmp_toolinfo *, char *); +int32_t parse_errors(struct snmp_toolinfo *); +int32_t parse_skip_access(struct snmp_toolinfo *); +int32_t parse_authentication(struct snmp_toolinfo *, char *); +int32_t parse_privacy(struct snmp_toolinfo *, char *); +int32_t parse_context(struct snmp_toolinfo *, char *); +int32_t parse_user_security(struct snmp_toolinfo *, char *); + +typedef int32_t (*snmp_verify_inoid_f) (struct snmp_toolinfo *, + struct snmp_object *, char *); +int32_t snmp_object_add(struct snmp_toolinfo *, snmp_verify_inoid_f, char *); +int32_t snmp_object_remove(struct snmp_toolinfo *, struct asn_oid *oid); +int32_t snmp_object_seterror(struct snmp_toolinfo *, struct snmp_value *, + int32_t); + +enum snmp_syntax parse_syntax(char *); +char *snmp_parse_suboid(char *, struct asn_oid *); +char *snmp_parse_index(struct snmp_toolinfo *, char *, struct snmp_object *); +int32_t snmp_parse_numoid(char *, struct asn_oid *); +int32_t snmp_suboid_append(struct asn_oid *, asn_subid_t); +int32_t snmp_suboid_pop(struct asn_oid *); + +typedef int32_t (*snmp_verify_vbind_f) (struct snmp_toolinfo *, + struct snmp_pdu *, struct snmp_object *); +typedef int32_t (*snmp_add_vbind_f) (struct snmp_pdu *, struct snmp_object *); +int32_t snmp_pdu_add_bindings(struct snmp_toolinfo *, snmp_verify_vbind_f, + snmp_add_vbind_f, struct snmp_pdu *, int32_t); + +int32_t snmp_parse_get_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_getbulk_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_getnext_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_output_numval(struct snmp_toolinfo *, struct snmp_value *, + struct snmp_oid2str *); +void snmp_output_val(struct snmp_value *); +int32_t snmp_output_resp(struct snmp_toolinfo *, struct snmp_pdu *); +void snmp_output_err_resp(struct snmp_toolinfo *, struct snmp_pdu *); +void snmp_output_engine(void); +void snmp_output_keys(void); + +#endif /* _BSNMP_TOOLS_H_ */ Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 0) @@ -0,0 +1,1285 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Textual conventions for OctetStrings + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "bsnmptc.h" +#include "bsnmptools.h" + +/* OctetString, DisplayString */ +static char *snmp_oct2str(uint32_t, char *, char *); +static char *snmp_str2asn_oid(char *, struct asn_oid *); +static int parse_octetstring(struct snmp_value *, char *); + +/* DateAndTime */ +static char *snmp_octstr2date(uint32_t, char *, char *); +static char *snmp_date2asn_oid(char * , struct asn_oid *); +static int parse_dateandtime(struct snmp_value *, char *); + +/* PhysAddress */ +static char *snmp_oct2physAddr(uint32_t, char *, char *); +static char *snmp_addr2asn_oid(char *, struct asn_oid *); +static int parse_physaddress(struct snmp_value *, char *); + +/* NTPTimeStamp */ +static char *snmp_oct2ntp_ts(uint32_t, char *, char *); +static char *snmp_ntp_ts2asn_oid(char *, struct asn_oid *); +static int parse_ntp_ts(struct snmp_value *, char *); + +/* BridgeId */ +static char *snmp_oct2bridgeid(uint32_t, char *, char *); +static char *snmp_bridgeid2oct(char *, struct asn_oid *); +static int parse_bridge_id(struct snmp_value *, char *); + +/* BridgePortId */ +static char *snmp_oct2bport_id(uint32_t, char *, char *); +static char *snmp_bport_id2oct(char *, struct asn_oid *); +static int parse_bport_id(struct snmp_value *, char *); + +/* InetAddress */ +static char *snmp_oct2inetaddr(uint32_t len, char *octets, char *buf); +static char *snmp_inetaddr2oct(char *str, struct asn_oid *oid); +static int32_t parse_inetaddr(struct snmp_value *value, char *string); + +static char *snmp_oct2bits(uint32_t len, char *octets, char *buf); +static char *snmp_bits2oct(char *str, struct asn_oid *oid); +static int32_t parse_bits(struct snmp_value *value, char *string); + +struct snmp_text_conv { + enum snmp_tc tc; + const char *tc_str; + int32_t len; + snmp_oct2tc_f oct2tc; + snmp_tc2oid_f tc2oid; + snmp_tc2oct_f tc2oct; +} text_convs[] = { + { SNMP_STRING, "OctetString", SNMP_VAR_STRSZ, + snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, + + { SNMP_DISPLAYSTRING, "DisplayString" , SNMP_VAR_STRSZ, + snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, + + { SNMP_DATEANDTIME, "DateAndTime", SNMP_DATETIME_STRSZ, + snmp_octstr2date, snmp_date2asn_oid, parse_dateandtime }, + + { SNMP_PHYSADDR, "PhysAddress", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_ATMESI, "AtmESI", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_NTP_TIMESTAMP, "NTPTimeStamp", SNMP_NTP_TS_STRSZ, + snmp_oct2ntp_ts, snmp_ntp_ts2asn_oid, parse_ntp_ts }, + + { SNMP_MACADDRESS, "MacAddress", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_BRIDGE_ID, "BridgeId", SNMP_BRIDGEID_STRSZ, + snmp_oct2bridgeid, snmp_bridgeid2oct, parse_bridge_id }, + + { SNMP_BPORT_ID, "BridgePortId", SNMP_BPORT_STRSZ, + snmp_oct2bport_id, snmp_bport_id2oct, parse_bport_id }, + + { SNMP_INETADDRESS, "InetAddress", SNMP_INADDRS_STRSZ, + snmp_oct2inetaddr, snmp_inetaddr2oct, parse_inetaddr }, + + { SNMP_TC_OWN, "BITS", SNMP_VAR_STRSZ, + snmp_oct2bits, snmp_bits2oct, parse_bits }, + + { SNMP_UNKNOWN, "Unknown", SNMP_VAR_STRSZ, snmp_oct2str, + snmp_str2asn_oid, parse_octetstring } /* keep last */ +}; + +/* Common API */ +enum snmp_tc +snmp_get_tc(char *str) +{ + int i; + for (i = 0; i < SNMP_UNKNOWN; i++) { + if (!strncmp(text_convs[i].tc_str, str, + strlen(text_convs[i].tc_str))) + return (text_convs[i].tc); + } + + return (SNMP_STRING); +} + +char * +snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets) +{ + uint32_t tc_len; + char * buf; + + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + if (text_convs[tc].len > 0) + tc_len = text_convs[tc].len; + else + tc_len = 2 * len + 3; + + if ((buf = malloc(tc_len)) == NULL ) { + syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); + return (NULL); + } + + memset(buf, 0, tc_len); + if (text_convs[tc].oct2tc(len, octets, buf) == NULL) { + free(buf); + return (NULL); + } + + return (buf); +} + +char * +snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid) +{ + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + return (text_convs[tc].tc2oid(str, oid)); +} + +int32_t +snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string) +{ + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + return (text_convs[tc].tc2oct(value, string)); +} + +/***************************************************** +* Basic OctetString type. +*/ +static char * +snmp_oct2str(uint32_t len, char *octets, char *buf) +{ + uint8_t binary = 0; + uint32_t i; + char *ptr; + + if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) + return (NULL); + + for (ptr = buf, i = 0; i < len; i++) + if (!isprint(octets[i])) { + binary = 1; + buf += sprintf(buf, "0x"); + break; + } + + for (ptr = buf, i = 0; i < len; i++) + if (!binary) + ptr += sprintf(ptr, "%c", octets[i]); + else + ptr += sprintf(ptr, "%2.2x", (u_char)octets[i]); + + return (buf); +} + +static char * +snmp_str2asn_oid(char *str, struct asn_oid *oid) +{ + uint32_t i, len = 0; + + /* + * OctetStrings are allowed max length of ASN_MAXOCTETSTRING, + * but trying to index an entry with such a long OctetString + * will fail anyway. + */ + for (len = 0; len < ASN_MAXOIDLEN; len++) { + if (strchr(",]", *(str + len)) != NULL) + break; + } + + if (len >= ASN_MAXOIDLEN) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) len) < 0) + return (NULL); + + for (i = 0; i < len; i++) + if (snmp_suboid_append(oid, (asn_subid_t) *(str + i)) < 0) + return (NULL); + + return (str + len); +} + +static int32_t +parse_octetstring(struct snmp_value *value, char *val) +{ + size_t len; + + if ((len = strlen(val)) >= MAX_OCTSTRING_LEN) { + warnx("Octetstring too long - %d is max allowed", + MAX_OCTSTRING_LEN - 1); + return (-1); + } + + value->v.octetstring.len = len; + + if((value->v.octetstring.octets = malloc(len)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + memcpy(value->v.octetstring.octets, val, len); + value->syntax = SNMP_SYNTAX_OCTETSTRING; + + return (0); +} + +/************************************************************* + * DateAndTime + ************************************************************* + * rfc 2579 specification: + * DateAndTime ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d" + * STATUS current + * DESCRIPTION + * "A date-time specification. + * + * field octets contents range + * ----- ------ -------- ----- + * 1 1-2 year* 0..65536 + * 2 3 month 1..12 + * 3 4 day 1..31 + * 4 5 hour 0..23 + * 5 6 minutes 0..59 + * 6 7 seconds 0..60 + * (use 60 for leap-second) + * 7 8 deci-seconds 0..9 + * 8 9 direction from UTC '+' / '-' + * 9 10 hours from UTC* 0..13 + * 10 11 minutes from UTC 0..59 + * + * * Notes: + * - the value of year is in network-byte order + * - daylight saving time in New Zealand is +13 + * + * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be + * displayed as: + * + * 1992-5-26,13:30:15.0,-4:0 + */ +static char * +snmp_octstr2date(uint32_t len, char *octets, char *buf) +{ + int year; + char *ptr; + + if (len != SNMP_DATETIME_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + year = (octets[0] << 8); + year += (octets[1]); + + ptr = buf; + ptr += sprintf(ptr, "%4.4d-%.2d-%.2d, ", year, octets[2],octets[3]); + ptr += sprintf(ptr, "%2.2d:%2.2d:%2.2d.%.2d, ", octets[4],octets[5], + octets[6],octets[7]); + ptr += sprintf(ptr, "%c%.2d:%.2d", octets[8],octets[9],octets[10]); + + return (buf); +} + +static char * +snmp_date2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v; + int32_t saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_DATETIME_OCTETS) < 0) + return (NULL); + + /* Read 'YYYY-' and write it in two subs. */ + ptr = str; + saved_errno = errno; + errno = 0; + v = strtoul(ptr, &endptr, 10); + if (v > 0xffff) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) ((v & 0xff00) >> 8)) < 0) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) + return (NULL); + + /* 'MM-' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'DD,' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'HH:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'MM:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'SS.' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '.') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'M(mseconds),' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ',') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'UTC' - optional */ + ptr = endptr + 1; + if (*ptr == 'U' && *(ptr + 1) == 'T' && *(ptr + 1) == 'C') + ptr += 3; + + /* '+/-' */ + if (*ptr == '-' || *ptr == '+') { + if (snmp_suboid_append(oid, (asn_subid_t) (*ptr)) < 0) + return (NULL); + } else + goto error1; + + /* 'HH:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'MM' - last one - ignore endptr here. */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); + + error: + errno = saved_errno; + error1: + warnx("Date value %s not supported", str); + return (NULL); +} + +/* Read a DateAndTime string eg. 1992-5-26,13:30:15.0,-4:0. */ +static int32_t +parse_dateandtime(struct snmp_value *sv, char *val) +{ + char *endptr; + uint32_t v; + uint8_t date[SNMP_DATETIME_OCTETS]; + + /* 'YYYY-' */ + v = strtoul(val, &endptr, 10); + if (v > 0xffff || *endptr != '-') + goto error; + date[0] = ((v & 0xff00) >> 8); + date[1] = (v & 0xff); + val = endptr + 1; + + /* 'MM-' */ + v = strtoul(val, &endptr, 10); + if (v == 0 || v > 12 || *endptr != '-') + goto error; + date[2] = v; + val = endptr + 1; + + /* 'DD,' */ + v = strtoul(val, &endptr, 10); + if (v == 0 || v > 31 || *endptr != ',') + goto error; + date[3] = v; + val = endptr + 1; + + /* 'HH:' */ + v = strtoul(val, &endptr, 10); + if (v > 23 || *endptr != ':') + goto error; + date[4] = v; + val = endptr + 1; + + /* 'MM:' */ + v = strtoul(val, &endptr, 10); + if (v > 59 || *endptr != ':') + goto error; + date[5] = v; + val = endptr + 1; + + /* 'SS.' */ + v = strtoul(val, &endptr, 10); + if (v > 60 || *endptr != '.') + goto error; + date[6] = v; + val = endptr + 1; + + /* '(deci-)s,' */ + v = strtoul(val, &endptr, 10); + if (v > 9 || *endptr != ',') + goto error; + date[7] = v; + val = endptr + 1; + + /* offset - '+/-' */ + if (*val != '-' && *val != '+') + goto error; + date[8] = (uint8_t) *val; + val = endptr + 1; + + /* 'HH:' - offset from UTC */ + v = strtoul(val, &endptr, 10); + if (v > 13 || *endptr != ':') + goto error; + date[9] = v; + val = endptr + 1; + + /* 'MM'\0'' offset from UTC */ + v = strtoul(val, &endptr, 10); + if (v > 59 || *endptr != '\0') + goto error; + date[10] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_DATETIME_OCTETS)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_DATETIME_OCTETS; + memcpy(sv->v.octetstring.octets, date, SNMP_DATETIME_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); + + error: + warnx("Date value %s not supported", val); + return (-1); +} + +/************************************************************** + * PhysAddress + */ +static char * +snmp_oct2physAddr(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i; + + if (len != SNMP_PHYSADDR_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + + ptr = buf; + ptr += sprintf(ptr, "%2.2x", octets[0]); + for (i = 1; i < 6; i++) + ptr += sprintf(ptr, ":%2.2x", octets[i]); + + return (buf); +} + +static char * +snmp_addr2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_PHYSADDR_OCTETS) < 0) + return (NULL); + + ptr = str; + for (i = 0; i < 5; i++) { + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (*endptr != ':') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_physaddress(struct snmp_value *sv, char *val) +{ + char *endptr; + int32_t i; + uint32_t v; + uint8_t phys_addr[SNMP_PHYSADDR_OCTETS]; + + for (i = 0; i < 5; i++) { + v = strtoul(val, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", val); + return (-1); + } + if(*endptr != ':') { + warnx("Failed reading octet - %s", val); + return (-1); + } + phys_addr[i] = v; + val = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + v = strtoul(val, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", val); + return (-1); + } + phys_addr[5] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_PHYSADDR_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_PHYSADDR_OCTETS; + memcpy(sv->v.octetstring.octets, phys_addr, SNMP_PHYSADDR_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * NTPTimeStamp + ************************************************************** + * NTP MIB, Revision 0.2, 7/25/97: + * NTPTimeStamp ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "4x.4x" + * STATUS current + * DESCRIPTION + * "" + * SYNTAX OCTET STRING (SIZE(8)) + */ +static char * +snmp_oct2ntp_ts(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i; + + if (len != SNMP_NTP_TS_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + + ptr = buf; + i = octets[0] * 1000 + octets[1] * 100 + octets[2] * 10 + octets[3]; + ptr += sprintf(ptr, "%4.4d", i); + i = octets[4] * 1000 + octets[5] * 100 + octets[6] * 10 + octets[7]; + ptr += sprintf(ptr, ".%4.4d", i); + + return (buf); +} + +static char * +snmp_ntp_ts2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i, d; + struct asn_oid suboid; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_NTP_TS_OCTETS) < 0) + return (NULL); + + ptr = str; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + warnx("Integer value %s not supported", str); + errno = saved_errno; + return (NULL); + } else + errno = saved_errno; + + if (*endptr != '.') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + + memset(&suboid, 0, sizeof(struct asn_oid)); + suboid.len = SNMP_NTP_TS_OCTETS; + + for (i = 0, d = 1000; i < 4; i++) { + suboid.subs[i] = v / d; + v = v % d; + d = d / 10; + } + + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + warnx("Integer value %s not supported", str); + errno = saved_errno; + return (NULL); + } else + errno = saved_errno; + + for (i = 0, d = 1000; i < 4; i++) { + suboid.subs[i + 4] = v / d; + v = v % d; + d = d / 10; + } + + asn_append_oid(oid, &suboid); + return (endptr); +} + +static int32_t +parse_ntp_ts(struct snmp_value *sv, char *val) +{ + char *endptr; + int32_t i, d, saved_errno; + uint32_t v; + uint8_t ntp_ts[SNMP_NTP_TS_OCTETS]; + + saved_errno = errno; + v = strtoul(val, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + saved_errno = errno; + warnx("Integer value %s not supported", val); + return (-1); + } else + saved_errno = errno; + + if (*endptr != '.') { + warnx("Failed reading octet - %s", val); + return (-1); + } + + for (i = 0, d = 1000; i < 4; i++) { + ntp_ts[i] = v / d; + v = v % d; + d = d / 10; + } + val = endptr + 1; + + saved_errno = errno; + v = strtoul(val, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + saved_errno = errno; + warnx("Integer value %s not supported", val); + return (-1); + } else + saved_errno = errno; + + for (i = 0, d = 1000; i < 4; i++) { + ntp_ts[i + 4] = v / d; + v = v % d; + d = d / 10; + } + + if ((sv->v.octetstring.octets = malloc(SNMP_NTP_TS_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_NTP_TS_OCTETS; + memcpy(sv->v.octetstring.octets, ntp_ts, SNMP_NTP_TS_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * BridgeId + ************************************************************** + * BRIDGE-MIB, REVISION "200509190000Z" + * BridgeId ::= TEXTUAL-CONVENTION + * STATUS current + * DESCRIPTION + * "The Bridge-Identifier, as used in the Spanning Tree + * Protocol, to uniquely identify a bridge. Its first two + * octets (in network byte order) contain a priority value, + * and its last 6 octets contain the MAC address used to + * refer to a bridge in a unique fashion (typically, the + * numerically smallest MAC address of all ports on the + * bridge)." + * SYNTAX OCTET STRING (SIZE (8)) + */ +static char * +snmp_oct2bridgeid(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i, priority; + + if (len != SNMP_BRIDGEID_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + ptr = buf; + + priority = octets[0] << 8; + priority += octets[1]; + if (priority > SNMP_MAX_BRIDGE_PRIORITY) { + warnx("Invalid bridge priority %d", priority); + return (NULL); + } else + ptr += sprintf(ptr, "%d.", octets[0]); + + ptr += sprintf(ptr, "%2.2x", octets[2]); + + for (i = 1; i < 6; i++) + ptr += sprintf(ptr, ":%2.2x", octets[i + 2]); + + return (buf); +} + +static char * +snmp_bridgeid2oct(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i; + int32_t saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BRIDGEID_OCTETS) < 0) + return (NULL); + + ptr = str; + /* Read the priority. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + errno = 0; + + if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge priority value %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff00)) < 0) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) + return (NULL); + + ptr = endptr + 1; + for (i = 0; i < 5; i++) { + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (*endptr != ':') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bridge_id(struct snmp_value *sv, char *string) +{ + char *ptr, *endptr; + int32_t i, saved_errno; + uint32_t v; + uint8_t bridge_id[SNMP_BRIDGEID_OCTETS]; + + ptr = string; + /* Read the priority. */ + saved_errno = errno; + errno = 0; + v = strtoul(string, &endptr, 10); + errno = saved_errno; + + if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge priority value %d", v); + return (-1); + } + + bridge_id[0] = (v & 0xff00); + bridge_id[1] = (v & 0xff); + + string = endptr + 1; + + for (i = 0; i < 5; i++) { + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", string); + return (-1); + } + if(*endptr != ':') { + warnx("Failed reading octet - %s", string); + return (-1); + } + bridge_id[i + 2] = v; + string = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", string); + return (-1); + } + bridge_id[7] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_BRIDGEID_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_BRIDGEID_OCTETS; + memcpy(sv->v.octetstring.octets, bridge_id, SNMP_BRIDGEID_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * BridgePortId + ************************************************************** + * BEGEMOT-BRIDGE-MIB, LAST-UPDATED "200608100000Z" + * BridgePortId ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "1x.1x" + * STATUS current + * DESCRIPTION + * "A port identifier that contains a bridge port's STP priority + * in the first octet and the port number in the second octet." + * SYNTAX OCTET STRING (SIZE(2)) + */ +static char * +snmp_oct2bport_id(uint32_t len, char *octets, char *buf) +{ + char *ptr; + + if (len != SNMP_BPORT_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + ptr = buf; + + ptr += sprintf(ptr, "%d.", octets[0]); + ptr += sprintf(ptr, "%d", octets[1]); + + return (buf); +} + +static char * +snmp_bport_id2oct(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BPORT_OCTETS) < 0) + return (NULL); + + ptr = str; + /* Read the priority. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + errno = 0; + + if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge port priority value %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + + if (v > 0xff) { + warnx("Bad port number - %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bport_id(struct snmp_value *value, char *string) +{ + char *ptr, *endptr; + int saved_errno; + uint32_t v; + uint8_t bport_id[SNMP_BPORT_OCTETS]; + + ptr = string; + /* Read the priority. */ + saved_errno = errno; + errno = 0; + v = strtoul(string, &endptr, 10); + errno = saved_errno; + + if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge port priority value %d", v); + return (-1); + } + + bport_id[0] = v; + + string = endptr + 1; + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Bad port number - %d", v); + return (-1); + } + + bport_id[1] = v; + + if ((value->v.octetstring.octets = malloc(SNMP_BPORT_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + value->v.octetstring.len = SNMP_BPORT_OCTETS; + memcpy(value->v.octetstring.octets, bport_id, SNMP_BPORT_OCTETS); + value->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} +/************************************************************** + * InetAddress + ************************************************************** + * INET-ADDRESS-MIB, REVISION "200502040000Z" + * InetAddress ::= TEXTUAL-CONVENTION + * STATUS current + * DESCRIPTION + * "Denotes a generic Internet address. + * + * An InetAddress value is always interpreted within the context + * of an InetAddressType value. Every usage of the InetAddress + * textual convention is required to specify the InetAddressType + * object that provides the context. It is suggested that the + * InetAddressType object be logically registered before the + * object(s) that use the InetAddress textual convention, if + * they appear in the same logical row. + * + * The value of an InetAddress object must always be + * consistent with the value of the associated InetAddressType + * object. Attempts to set an InetAddress object to a value + * inconsistent with the associated InetAddressType + * must fail with an inconsistentValue error. + * + * When this textual convention is used as the syntax of an + * index object, there may be issues with the limit of 128 + * sub-identifiers specified in SMIv2, STD 58. In this case, + * the object definition MUST include a 'SIZE' clause to + * limit the number of potential instance sub-identifiers; + * otherwise the applicable constraints MUST be stated in + * the appropriate conceptual row DESCRIPTION clauses, or + * in the surrounding documentation if there is no single + * DESCRIPTION clause that is appropriate." + * SYNTAX OCTET STRING (SIZE (0..255)) + ************************************************************** + * TODO: FIXME!!! syrinx: Since we do not support checking the + * consistency of a varbinding based on the value of a previous + * one, try to guess the type of address based on the + * OctetString SIZE - 4 for IPv4, 16 for IPv6, others currently + * not supported. + */ +static char * +snmp_oct2inetaddr(uint32_t len, char *octets, char *buf) +{ + int af; + void *ip; + struct in_addr ipv4; + struct in6_addr ipv6; + + if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) + return (NULL); + + switch (len) { + /* XXX: FIXME - IPv4*/ + case 4: + memcpy(&ipv4.s_addr, octets, sizeof(ipv4.s_addr)); + af = AF_INET; + ip = &ipv4; + break; + + /* XXX: FIXME - IPv4*/ + case 16: + memcpy(ipv6.s6_addr, octets, sizeof(ipv6.s6_addr)); + af = AF_INET6; + ip = &ipv6; + break; + + default: + return (NULL); + } + + if (inet_ntop(af, ip, buf, SNMP_INADDRS_STRSZ) == NULL) { + warnx("inet_ntop failed - %s", strerror(errno)); + return (NULL); + } + + return (buf); +} + +static char * +snmp_inetaddr2oct(char *str, struct asn_oid *oid) +{ + return (NULL); +} + +static int32_t +parse_inetaddr(struct snmp_value *value, char *string) +{ + return (-1); +} + +/************************************************************** + * SNMP BITS type - XXX: FIXME + **************************************************************/ +static char * +snmp_oct2bits(uint32_t len, char *octets, char *buf) +{ + int i, bits; + uint64_t value; + + if (len > sizeof(value) || octets == NULL || buf == NULL) + return (NULL); + + for (i = len, value = 0, bits = 0; i > 0; i--, bits += 8) + value += octets[i] << bits; + + buf[0]= '\0'; + sprintf(buf, "0x%llx.",(long long unsigned) value); + + return (buf); +} + +static char * +snmp_bits2oct(char *str, struct asn_oid *oid) +{ + char *endptr; + int i, size, bits, saved_errno; + uint64_t v, mask = 0xFF00000000000000; + + saved_errno = errno; + errno = 0; + + v = strtoull(str, &endptr, 16); + if (errno != 0) { + warnx("Bad BITS value %s - %s", str, strerror(errno)); + errno = saved_errno; + return (NULL); + } + + bits = 8; + /* Determine length - up to 8 octets supported so far. */ + for (size = sizeof(v); size > 0; size--) { + if ((v & mask) != 0) + break; + mask = mask >> bits; + } + + if (size == 0) + size = 1; + + if (snmp_suboid_append(oid, (asn_subid_t) size) < 0) + return (NULL); + + for (i = 0, bits = 0; i < size; i++, bits += 8) + if (snmp_suboid_append(oid, + (asn_subid_t)((v & mask) >> bits)) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bits(struct snmp_value *value, char *string) +{ + char *endptr; + int i, size, bits, saved_errno; + uint64_t v, mask = 0xFF00000000000000; + + saved_errno = errno; + errno = 0; + + v = strtoull(string, &endptr, 16); + + if (errno != 0) { + warnx("Bad BITS value %s - %s", string, strerror(errno)); + errno = saved_errno; + return (-1); + } + + bits = 8; + /* Determine length - up to 8 octets supported so far. */ + for (size = sizeof(v); size > 0; size--) { + if ((v & mask) != 0) + break; + mask = mask >> bits; + } + + if (size == 0) + size = 1; + + if ((value->v.octetstring.octets = malloc(size)) == NULL) { + syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); + return (-1); + } + + value->v.octetstring.len = size; + for (i = 0, bits = 0; i < size; i++, bits += 8) + value->v.octetstring.octets[i] = (v & mask) >> bits; + value->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 0) @@ -0,0 +1,968 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Read file containing table description - reuse magic from gensnmptree.c. + * Hopefully one day most of the code here will be part of libbsnmp and + * this duplication won't be necessary. + * + * Syntax is: + * --------- + * file := top | top file + * + * top := tree | typedef | include + * + * tree := head elements ')' + * + * entry := head ':' index STRING elements ')' + * + * leaf := head type STRING ACCESS ')' + * + * column := head type ACCESS ')' + * + * type := BASETYPE | BASETYPE '|' subtype | enum | bits + * + * subtype := STRING + * + * enum := ENUM '(' value ')' + * + * bits := BITS '(' value ')' + * + * value := INT STRING | INT STRING value + * + * head := '(' INT STRING + * + * elements := EMPTY | elements element + * + * element := tree | leaf | column + * + * index := type | index type + * + * typedef := 'typedef' STRING type + * + * include := 'include' filespec + * + * filespec := '"' STRING '"' | '<' STRING '>' + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* SNMP_INDEXES_MAX */ +#include "bsnmptc.h" +#include "bsnmptools.h" + +enum snmp_tbl_entry { + ENTRY_NONE = 0, + ENTRY_INDEX, + ENTRY_DATA +}; + +enum { + FL_GET = 0x01, + FL_SET = 0x02, +}; + +/************************************************************ + * + * Allocate memory and panic just in the case... + */ +static void * +xalloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + err(1, "allocing %zu bytes", size); + + return (ptr); +} + +static char * +savestr(const char *s) +{ + if (s == NULL) + return (NULL); + + return (strcpy(xalloc(strlen(s) + 1), s)); +} + +/************************************************************ + * + * Input stack + */ +struct input { + FILE *fp; + uint32_t lno; + char *fname; + char *path; + LIST_ENTRY(input) link; +}; + +LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); +struct input *input = NULL; +int32_t pbchar = -1; + +#define MAX_PATHS 100 + +static const char *paths[MAX_PATHS + 1] = { + "/usr/share/snmp/defs", + "/usr/local/share/snmp/defs", + NULL +}; + +static void +input_new(FILE *fp, const char *path, const char *fname) +{ + struct input *ip; + + ip = xalloc(sizeof(*ip)); + ip->fp = fp; + ip->lno = 1; + ip->fname = savestr(fname); + ip->path = savestr(path); + LIST_INSERT_HEAD(&inputs, ip, link); + + input = ip; +} + +static void +input_close(void) +{ + if (input == NULL) + return; + + fclose(input->fp); + free(input->fname); + free(input->path); + LIST_REMOVE(input, link); + free(input); + + input = LIST_FIRST(&inputs); +} + +static FILE * +tryopen(const char *path, const char *fname) +{ + char *fn; + FILE *fp; + + if (path == NULL) + fn = savestr(fname); + else { + fn = xalloc(strlen(path) + strlen(fname) + 2); + sprintf(fn, "%s/%s", path, fname); + } + fp = fopen(fn, "r"); + free(fn); + return (fp); +} + +static int32_t +input_fopen(const char *fname) +{ + FILE *fp; + u_int p; + + if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') { + if ((fp = tryopen(NULL, fname)) != NULL) { + input_new(fp, NULL, fname); + return (0); + } + + } else { + + for (p = 0; paths[p] != NULL; p++) + if ((fp = tryopen(paths[p], fname)) != NULL) { + input_new(fp, paths[p], fname); + return (0); + } + } + + warnx("cannot open '%s'", fname); + return (-1); +} + +static int32_t +tgetc(void) +{ + int c; + + if (pbchar != -1) { + c = pbchar; + pbchar = -1; + return (c); + } + + for (;;) { + if (input == NULL) + return (EOF); + + if ((c = getc(input->fp)) != EOF) + return (c); + + input_close(); + } +} + +static int32_t +tungetc(int c) +{ + + if (pbchar != -1) + return (-1); + + pbchar = c; + return (1); +} + +/************************************************************ + * + * Parsing input + */ +enum tok { + TOK_EOF = 0200, /* end-of-file seen */ + TOK_NUM, /* number */ + TOK_STR, /* string */ + TOK_ACCESS, /* access operator */ + TOK_TYPE, /* type operator */ + TOK_ENUM, /* enum token (kind of a type) */ + TOK_TYPEDEF, /* typedef directive */ + TOK_DEFTYPE, /* defined type */ + TOK_INCLUDE, /* include directive */ + TOK_FILENAME, /* filename ("foo.bar" or ) */ + TOK_BITS, /* bits token (kind of a type) */ + TOK_ERR /* unexpected char - exit */ +}; + +static const struct { + const char *str; + enum tok tok; + uint32_t val; +} keywords[] = { + { "GET", TOK_ACCESS, FL_GET }, + { "SET", TOK_ACCESS, FL_SET }, + { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, + { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, + { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, + { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, + { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, + { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, + { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, + { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, + { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, + { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, + { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, + { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, + { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, + { "typedef", TOK_TYPEDEF, 0 }, + { "include", TOK_INCLUDE, 0 }, + { NULL, 0, 0 } +}; + +struct { + /* Current OID type, regarding table membership. */ + enum snmp_tbl_entry tbl_type; + /* A pointer to a structure in table list to add to its members. */ + struct snmp_index_entry *table_idx; +} table_data; + +struct asn_oid current_oid; +char nexttok[MAXSTR]; +u_long val; /* integer values */ +int32_t all_cond; /* all conditions are true */ +int32_t saved_token = -1; + +/* Prepare the global data before parsing a new file. */ +static void +snmp_import_init(struct asn_oid *append) +{ + memset(&table_data, 0, sizeof(table_data)); + memset(¤t_oid, 0, sizeof(struct asn_oid)); + memset(nexttok, 0, MAXSTR); + + if (append != NULL) + asn_append_oid(¤t_oid, append); + + all_cond = 0; + val = 0; + saved_token = -1; +} + +static int32_t +gettoken(struct snmp_toolinfo *snmptoolctx) +{ + int c; + struct enum_type *t; + + if (saved_token != -1) { + c = saved_token; + saved_token = -1; + return (c); + } + + again: + /* + * Skip any whitespace before the next token. + */ + while ((c = tgetc()) != EOF) { + if (c == '\n') + input->lno++; + if (!isspace(c)) + break; + } + if (c == EOF) + return (TOK_EOF); + + if (!isascii(c)) { + warnx("unexpected character %#2x", (u_int) c); + return (TOK_ERR); + } + + /* + * Skip comments. + */ + if (c == '#') { + while ((c = tgetc()) != EOF) { + if (c == '\n') { + input->lno++; + goto again; + } + } + warnx("unexpected EOF in comment"); + return (TOK_ERR); + } + + /* + * Single character tokens. + */ + if (strchr("():|", c) != NULL) + return (c); + + if (c == '"' || c == '<') { + int32_t end = c; + size_t n = 0; + + val = 1; + if (c == '<') { + val = 0; + end = '>'; + } + + while ((c = tgetc()) != EOF) { + if (c == end) + break; + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("filename too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + return (TOK_FILENAME); + } + + /* + * Sort out numbers. + */ + if (isdigit(c)) { + size_t n = 0; + nexttok[n++] = c; + while ((c = tgetc()) != EOF) { + if (!isdigit(c)) { + if (tungetc(c) < 0) + return (TOK_ERR); + break; + } + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("number too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + sscanf(nexttok, "%lu", &val); + return (TOK_NUM); + } + + /* + * So that has to be a string. + */ + if (isalpha(c) || c == '_' || c == '-') { + size_t n = 0; + nexttok[n++] = c; + while ((c = tgetc()) != EOF) { + if (!isalnum(c) && c != '_' && c != '-') { + if (tungetc (c) < 0) + return (TOK_ERR); + break; + } + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("string too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + + /* + * Keywords. + */ + for (c = 0; keywords[c].str != NULL; c++) + if (strcmp(keywords[c].str, nexttok) == 0) { + val = keywords[c].val; + return (keywords[c].tok); + } + + if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) { + val = t->syntax; + return (TOK_DEFTYPE); + } + + return (TOK_STR); + } + + if (isprint(c)) + warnx("%u: unexpected character '%c'", input->lno, c); + else + warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c); + + return (TOK_ERR); +} + +/* + * Update table information. + */ +static struct snmp_index_entry * +snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl) +{ + switch (te) { + case ENTRY_NONE: + if (table_data.tbl_type == ENTRY_NONE) + return (NULL); + if (table_data.tbl_type == ENTRY_INDEX) + table_data.table_idx = NULL; + table_data.tbl_type--; + return (NULL); + + case ENTRY_INDEX: + if (tbl == NULL) + warnx("No table_index to add!!!"); + table_data.table_idx = tbl; + table_data.tbl_type = ENTRY_INDEX; + return (tbl); + + case ENTRY_DATA: + if (table_data.tbl_type == ENTRY_INDEX) { + table_data.tbl_type = ENTRY_DATA; + return (table_data.table_idx); + } + return (NULL); + + default: + /* NOTREACHED */ + warnx("Unknown table entry type!!!"); + break; + } + + return (NULL); +} + +static int32_t +parse_enum(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + struct enum_pairs *enums) +{ + while ((*tok = gettoken(snmptoolctx)) == TOK_STR) { + if (enum_pair_insert(enums, val, nexttok) < 0) + return (-1); + if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) + break; + } + + if (*tok != ')') { + warnx("')' at end of enums"); + return (-1); + } + + return (1); +} + +static int32_t +parse_subtype(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + enum snmp_tc *tc) +{ + if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { + warnx("subtype expected after '|'"); + return (-1); + } + + *tc = snmp_get_tc(nexttok); + *tok = gettoken(snmptoolctx); + + return (1); +} + +static int32_t +parse_type(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + enum snmp_tc *tc, struct enum_pairs **snmp_enum) +{ + int32_t syntax, mem; + + syntax = val; + *tc = 0; + + if (*tok == TOK_ENUM || *tok == TOK_BITS) { + if (*snmp_enum == NULL) { + if ((*snmp_enum = enum_pairs_init()) == NULL) + return (-1); + mem = 1; + *tc = SNMP_TC_OWN; + } else + mem = 0; + + if (gettoken(snmptoolctx) != '(') { + warnx("'(' expected after ENUM/BITS"); + return (-1); + } + + if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) { + warnx("need value for ENUM//BITS"); + if (mem == 1) { + free(*snmp_enum); + *snmp_enum = NULL; + } + return (-1); + } + + if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) { + enum_pairs_free(*snmp_enum); + *snmp_enum = NULL; + return (-1); + } + + *tok = gettoken(snmptoolctx); + + } else if (*tok == TOK_DEFTYPE) { + struct enum_type *t; + + *tc = 0; + t = snmp_enumtc_lookup(snmptoolctx, nexttok); + if (t != NULL) + *snmp_enum = t->snmp_enum; + + *tok = gettoken(snmptoolctx); + + } else { + if ((*tok = gettoken(snmptoolctx)) == '|') { + if (parse_subtype(snmptoolctx, tok, tc) < 0) + return (-1); + } + } + + return (syntax); +} + +static int32_t +snmp_import_head(struct snmp_toolinfo *snmptoolctx) +{ + enum tok tok; + + if ((tok = gettoken(snmptoolctx)) == '(') + tok = gettoken(snmptoolctx); + + if (tok != TOK_NUM || val > ASN_MAXID ) { + warnx("Suboid expected - line %d", input->lno); + return (-1); + } + + if (gettoken(snmptoolctx) != TOK_STR) { + warnx("Node name expected at line %d", input->lno); + return (-1); + } + + return (1); +} + +static int32_t +snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj) +{ + int32_t i; + enum snmp_tc tc; + enum tok tok; + struct snmp_index_entry *entry; + + if ((entry = malloc(sizeof(struct snmp_index_entry))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(entry, 0, sizeof(struct snmp_index_entry)); + STAILQ_INIT(&(entry->index_list)); + + for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) { + int32_t syntax; + struct enum_pairs *enums = NULL; + + if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM && + tok != TOK_BITS) + break; + + if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) { + enum_pairs_free(enums); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + if (snmp_syntax_insert(&(entry->index_list), enums, syntax, + tc) < 0) { + snmp_index_listfree(&(entry->index_list)); + enum_pairs_free(enums); + free(entry); + return (-1); + } + } + + if (i == 0 || i > SNMP_INDEXES_MAX) { + warnx("Bad number of indexes at line %d", input->lno); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + if (tok != TOK_STR) { + warnx("String expected after indexes at line %d", input->lno); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + entry->string = obj->string; + entry->strlen = obj->strlen; + asn_append_oid(&(entry->var), &(obj->var)); + + if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) { + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } else if (i == 0) { + /* Same entry already present in lists. */ + free(entry->string); + free(entry); + } + + (void) snmp_import_update_table(ENTRY_INDEX, entry); + + return (1); +} + +/* + * Read everything after the syntax type that is certainly a leaf OID info. + */ +static int32_t +snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + struct snmp_oid2str *oid2str) +{ + int32_t i, syntax; + + if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum))) + < 0) + return(-1); + + oid2str->syntax = syntax; + /* + * That is the name of the function, corresponding to the entry. + * It is used by bsnmpd, but is not interesting for us. + */ + if (*tok == TOK_STR) + *tok = gettoken(snmptoolctx); + + for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) { + oid2str->access |= (uint32_t) val; + *tok = gettoken(snmptoolctx); + } + + if (*tok != ')') { + warnx("')' expected at end of line %d", input->lno); + return (-1); + } + + oid2str->table_idx = snmp_import_update_table(ENTRY_DATA, NULL); + + if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) { + warnx("Error adding leaf %s to list", oid2str->string); + return (-1); + } + + /* + * Same entry is already present in the mapping lists and + * the new one was not inserted. + */ + if (i == 0) { + free(oid2str->string); + free(oid2str); + } + + (void) snmp_import_update_table(ENTRY_NONE, NULL); + + return (1); +} + +static int32_t +snmp_import_object(struct snmp_toolinfo *snmptoolctx) +{ + char *string; + int i; + enum tok tok; + struct snmp_oid2str *oid2str; + + if (snmp_import_head(snmptoolctx) < 0) + return (-1); + + if ((oid2str = malloc(sizeof(struct snmp_oid2str))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + if ((string = malloc(strlen(nexttok) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(oid2str); + return (-1); + } + + memset(oid2str, 0, sizeof(struct snmp_oid2str)); + strlcpy(string, nexttok, strlen(nexttok) + 1); + oid2str->string = string; + oid2str->strlen = strlen(nexttok); + + asn_append_oid(&(oid2str->var), &(current_oid)); + if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0) + goto error; + + /* + * Prepared the entry - now figure out where to insert it. + * After the object we have following options: + * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist; + * 2) new line , ( - nonleaf oid -> snmp_nodelist; + * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more) + * may follow and second string must end line -> snmp_tablelist; + * 3) OID , string ) - this is a trap entry or a leaf -> snmp_oidlist; + * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last + * and )- this is definitely a leaf. + */ + + switch (tok = gettoken(snmptoolctx)) { + case ')': + if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0) + goto error; + if (i == 0) { + free(oid2str->string); + free(oid2str); + } + return (1); + + case '(': + if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) + goto error; + + /* + * Ignore the error for nodes since the .def files currently + * contain different strings for 1.3.6.1.2.1 - mibII. Only make + * sure the memory is freed and don't complain. + */ + if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) { + free(string); + free(oid2str); + } + return (snmp_import_object(snmptoolctx)); + + case ':': + if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) + goto error; + if (snmp_import_table(snmptoolctx, oid2str) < 0) + goto error; + /* + * A different table entry type was malloced and the data is + * contained there. + */ + free(oid2str); + return (1); + + case TOK_TYPE: + /* FALLTHROUGH */ + case TOK_DEFTYPE: + /* FALLTHROUGH */ + case TOK_ENUM: + /* FALLTHROUGH */ + case TOK_BITS: + if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0) + goto error; + return (1); + + default: + warnx("Unexpected token at line %d - %s", input->lno, + input->fname); + break; + } + +error: + snmp_mapping_entryfree(oid2str); + + return (-1); +} + +static int32_t +snmp_import_tree(struct snmp_toolinfo *snmptoolctx, enum tok *tok) +{ + while (*tok != TOK_EOF) { + switch (*tok) { + case TOK_ERR: + return (-1); + case '(': + if (snmp_import_object(snmptoolctx) < 0) + return (-1); + break; + case ')': + if (snmp_suboid_pop(¤t_oid) < 0) + return (-1); + (void) snmp_import_update_table(ENTRY_NONE, NULL); + break; + default: + /* Anything else here would be illegal. */ + return (-1); + } + *tok = gettoken(snmptoolctx); + } + + return (0); +} + +static int32_t +snmp_import_top(struct snmp_toolinfo *snmptoolctx, enum tok *tok) +{ + enum snmp_tc tc; + struct enum_type *t; + + if (*tok == '(') + return (snmp_import_tree(snmptoolctx, tok)); + + if (*tok == TOK_TYPEDEF) { + if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { + warnx("type name expected after typedef - %s", + input->fname); + return (-1); + } + + t = snmp_enumtc_init(nexttok); + + *tok = gettoken(snmptoolctx); + t->is_enum = (*tok == TOK_ENUM); + t->is_bits = (*tok == TOK_BITS); + t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum)); + snmp_enumtc_insert(snmptoolctx, t); + + return (1); + } + + if (*tok == TOK_INCLUDE) { + int i; + + *tok = gettoken(snmptoolctx); + if (*tok != TOK_FILENAME) { + warnx("filename expected in include directive - %s", + nexttok); + return (-1); + } + + if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) { + *tok = gettoken(snmptoolctx); + return (1); + } + + if (i == -1) + return (-1); + + input_fopen(nexttok); + *tok = gettoken(snmptoolctx); + return (1); + } + + warnx("'(' or 'typedef' expected - %s", nexttok); + return (-1); +} + +static int32_t +snmp_import(struct snmp_toolinfo *snmptoolctx) +{ + int i; + enum tok tok; + + tok = gettoken(snmptoolctx); + + do + i = snmp_import_top(snmptoolctx, &tok); + while (i > 0); + + return (i); +} + +/* + * Read a .def file and import oid<->string mapping. + * Mappings are inserted into a global structure containing list for each OID + * syntax type. + */ +int32_t +snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file) +{ + int idx; + + snmp_import_init(&(file->cut)); + input_fopen(file->name); + if ((idx = snmp_import(snmptoolctx)) < 0) + warnx("Failed to read mappings from file %s", file->name); + + input_close(); + + return (idx); +} Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h (revision 0) @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Textual conventions for snmp + */ +#ifndef _BSNMP_TEXT_CONV_H_ +#define _BSNMP_TEXT_CONV_H_ + +/* Variable display length string. */ +#define SNMP_VAR_STRSZ -1 + +/* + * 11 bytes - octets that represent DateAndTime Textual convention + * and the size of string used to diplay that. + */ +#define SNMP_DATETIME_OCTETS 11 +#define SNMP_DATETIME_STRSZ 32 + +/* + * 6 bytes - octets that represent PhysAddress Textual convention + * and the size of string used to diplay that. + */ +#define SNMP_PHYSADDR_OCTETS 6 +#define SNMP_PHYSADDR_STRSZ 19 + +/* NTPTimeStamp. */ +#define SNMP_NTP_TS_OCTETS 8 +#define SNMP_NTP_TS_STRSZ 10 + +/* BridgeId. */ +#define SNMP_BRIDGEID_OCTETS 8 +#define SNMP_BRIDGEID_STRSZ 25 +#define SNMP_MAX_BRIDGE_PRIORITY 65535 + +/* BridgePortId. */ +#define SNMP_BPORT_OCTETS 2 +#define SNMP_BPORT_STRSZ 7 +#define SNMP_MAX_BPORT_PRIORITY 255 + +/* InetAddress. */ +#define SNMP_INADDRS_STRSZ INET6_ADDRSTRLEN + +enum snmp_tc { + SNMP_STRING = 0, + SNMP_DISPLAYSTRING = 1, + SNMP_DATEANDTIME = 2, + SNMP_PHYSADDR = 3, + SNMP_ATMESI = 4, + SNMP_NTP_TIMESTAMP = 5, + SNMP_MACADDRESS = 6, + SNMP_BRIDGE_ID = 7, + SNMP_BPORT_ID = 8, + SNMP_INETADDRESS = 9, + SNMP_TC_OWN = 10, + SNMP_UNKNOWN, /* keep last */ +}; + +typedef char * (*snmp_oct2tc_f) (uint32_t len, char *octs, char *buf); +typedef char * (*snmp_tc2oid_f) (char *str, struct asn_oid *oid); +typedef int32_t (*snmp_tc2oct_f) (struct snmp_value *value, char *string); + +enum snmp_tc snmp_get_tc(char *str); +char *snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets); +char *snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid); +int32_t snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string); + +#endif /* _BSNMP_TEXT_CONV_H_ */ Index: usr.sbin/bsnmpd/tools/libbsnmptools/Makefile =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/Makefile (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/Makefile (revision 0) @@ -0,0 +1,14 @@ +# +# $FreeBSD$ +# + +.PATH: ${.CURDIR} + +LIB= bsnmptools +#INTERNALLIB= +SRCS= bsnmpimport.c bsnmpmap.c bsnmptools.c bsnmptc.c +CFLAGS+= -g -Wall -Werror + +SHLIB_MAJOR= 0 + +.include Index: usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c =================================================================== --- usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c (revision 0) +++ usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c (revision 0) @@ -0,0 +1,1016 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "bsnmptc.h" +#include "bsnmptools.h" + +extern int _bsnmptools_debug; +#define DEBUG if (_bsnmptools_debug) fprintf + +/* Allocate memory and initialize list. */ +struct snmp_mappings * +snmp_mapping_init(void) +{ + struct snmp_mappings *m; + + if ((m = malloc(sizeof(struct snmp_mappings))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + memset(m, 0, sizeof(struct snmp_mappings)); + return (m); +} + +#define snmp_nodelist mappings->nodelist +#define snmp_intlist mappings->intlist +#define snmp_octlist mappings->octlist +#define snmp_oidlist mappings->oidlist +#define snmp_iplist mappings->iplist +#define snmp_ticklist mappings->ticklist +#define snmp_cntlist mappings->cntlist +#define snmp_gaugelist mappings->gaugelist +#define snmp_cnt64list mappings->cnt64list +#define snmp_enumlist mappings->enumlist +#define snmp_tablelist mappings->tablelist +#define snmp_tclist mappings->tclist + +void +enum_pairs_free(struct enum_pairs *headp) +{ + struct enum_pair *e; + + if (headp == NULL) + return; + + while ((e = STAILQ_FIRST(headp)) != NULL) { + STAILQ_REMOVE_HEAD(headp, link); + + if (e->enum_str) + free(e->enum_str); + free(e); + } + + free(headp); +} + +void +snmp_mapping_entryfree(struct snmp_oid2str *entry) +{ + if (entry->string) + free(entry->string); + + if (entry->tc == SNMP_TC_OWN) + enum_pairs_free(entry->snmp_enum); + + free(entry); +} + +static void +snmp_mapping_listfree(struct snmp_mapping *headp) +{ + struct snmp_oid2str *p; + + while ((p = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (p->string) + free(p->string); + + if (p->tc == SNMP_TC_OWN) + enum_pairs_free(p->snmp_enum); + free(p); + } + + SLIST_INIT(headp); +} + +void +snmp_index_listfree(struct snmp_idxlist *headp) +{ + struct index *i; + + while ((i = STAILQ_FIRST(headp)) != NULL) { + STAILQ_REMOVE_HEAD(headp, link); + if (i->tc == SNMP_TC_OWN) + enum_pairs_free(i->snmp_enum); + free(i); + } + + STAILQ_INIT(headp); +} + +static void +snmp_mapping_table_listfree(struct snmp_table_index *headp) +{ + struct snmp_index_entry *t; + + while ((t = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (t->string) + free(t->string); + + snmp_index_listfree(&(t->index_list)); + free(t); + } +} + +static void +snmp_enumtc_listfree(struct snmp_enum_tc *headp) +{ + struct enum_type *t; + + while ((t = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (t->name) + free(t->name); + enum_pairs_free(t->snmp_enum); + free(t); + } +} + +int +snmp_mapping_free(struct snmp_toolinfo *snmptoolctx) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (-1); + + snmp_mapping_listfree(&snmptoolctx->snmp_nodelist); + snmp_mapping_listfree(&snmptoolctx->snmp_intlist); + snmp_mapping_listfree(&snmptoolctx->snmp_octlist); + snmp_mapping_listfree(&snmptoolctx->snmp_oidlist); + snmp_mapping_listfree(&snmptoolctx->snmp_iplist); + snmp_mapping_listfree(&snmptoolctx->snmp_ticklist); + snmp_mapping_listfree(&snmptoolctx->snmp_cntlist); + snmp_mapping_listfree(&snmptoolctx->snmp_gaugelist); + snmp_mapping_listfree(&snmptoolctx->snmp_cnt64list); + snmp_mapping_listfree(&snmptoolctx->snmp_enumlist); + snmp_mapping_table_listfree(&snmptoolctx->snmp_tablelist); + snmp_enumtc_listfree(&snmptoolctx->snmp_tclist); + free(snmptoolctx->mappings); + + return (0); +} + +static void +snmp_dump_enumpairs(struct enum_pairs *headp) +{ + struct enum_pair *entry; + + if (headp == NULL) + return; + + fprintf(stderr,"enums: "); + STAILQ_FOREACH(entry, headp, link) + fprintf(stderr,"%d - %s, ", entry->enum_val, + (entry->enum_str == NULL)?"NULL":entry->enum_str); + + fprintf(stderr,"; "); +} + +void +snmp_dump_oid2str(struct snmp_oid2str *entry) +{ + char buf[ASN_OIDSTRLEN]; + + if (entry != NULL) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + DEBUG(stderr, "%s - %s - %d - %d - %d", buf, entry->string, + entry->syntax, entry->access, entry->strlen); + snmp_dump_enumpairs(entry->snmp_enum); + DEBUG(stderr,"%s \n", (entry->table_idx == NULL)?"No table": + entry->table_idx->string); + } +} + +static void +snmp_dump_indexlist(struct snmp_idxlist *headp) +{ + struct index *entry; + + if (headp == NULL) + return; + + STAILQ_FOREACH(entry, headp, link) { + fprintf(stderr,"%d, ", entry->syntax); + snmp_dump_enumpairs(entry->snmp_enum); + } + + fprintf(stderr,"\n"); +} + +/* Initialize the enum pairs list of a oid2str entry. */ +struct enum_pairs * +enum_pairs_init(void) +{ + struct enum_pairs *snmp_enum; + + if ((snmp_enum = malloc(sizeof(struct enum_pairs))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + STAILQ_INIT(snmp_enum); + return (snmp_enum); +} + +/* + * Given a number and string, allocate memory for a (int, string) pair and add + * it to the given oid2str mapping entry's enum pairs list. + */ +int32_t +enum_pair_insert(struct enum_pairs *headp, int32_t enum_val, char *enum_str) +{ + struct enum_pair *e_new; + + if ((e_new = malloc(sizeof(struct enum_pair))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(e_new, 0, sizeof(struct enum_pair)); + + if ((e_new->enum_str = malloc(strlen(enum_str) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(e_new); + return (-1); + } + + e_new->enum_val = enum_val; + strlcpy(e_new->enum_str, enum_str, strlen(enum_str) + 1); + STAILQ_INSERT_TAIL(headp, e_new, link); + + return (1); + +} + +/* + * Insert an entry in a list - entries are lexicographicaly order by asn_oid. + * Returns 1 on success, -1 if list is not initialized, 0 if a matching oid already + * exists. Error cheking is left to calling function. + */ +static int +snmp_mapping_insert(struct snmp_mapping *headp, struct snmp_oid2str *entry) +{ + int32_t rc; + struct snmp_oid2str *temp, *prev; + + if (entry == NULL) + return(-1); + + if ((prev = SLIST_FIRST(headp)) == NULL || + asn_compare_oid(&(entry->var), &(prev->var)) < 0) { + SLIST_INSERT_HEAD(headp, entry, link); + return (1); + } else + rc = -1; /* Make the compiler happy. */ + + SLIST_FOREACH(temp, headp, link) { + if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) + break; + prev = temp; + rc = -1; + } + + switch (rc) { + case 0: + /* Ops, matching OIDs - hope the rest info also matches. */ + if (strncmp(temp->string, entry->string, entry->strlen)) { + syslog(LOG_INFO, "Matching OIDs with different string " + "mappings: old - %s, new - %s", temp->string, + entry->string); + return (-1); + } + /* + * Ok, we have that already. + * As long as the strings match - don't complain. + */ + return (0); + + case 1: + SLIST_INSERT_AFTER(temp, entry, link); + break; + + case -1: + SLIST_INSERT_AFTER(prev, entry, link); + break; + + default: + /* NOTREACHED */ + return (-1); + } + + return (1); +} + +int32_t +snmp_node_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_nodelist,entry)); + + return (-1); +} + +static int32_t +snmp_int_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_intlist,entry)); + + return (-1); +} + +static int32_t +snmp_oct_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_octlist,entry)); + + return (-1); +} + +static int32_t +snmp_oid_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_oidlist,entry)); + + return (-1); +} + +static int32_t +snmp_ip_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_iplist,entry)); + + return (-1); +} + +static int32_t +snmp_tick_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_ticklist,entry)); + + return (-1); +} + +static int32_t +snmp_cnt_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_cntlist,entry)); + + return (-1); +} + +static int32_t +snmp_gauge_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_gaugelist,entry)); + + return (-1); +} + +static int32_t +snmp_cnt64_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_cnt64list,entry)); + + return (-1); +} + +int32_t +snmp_enum_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_enumlist,entry)); + + return (-1); +} + +int32_t +snmp_leaf_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + switch (entry->syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_int_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_oct_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_OID: + return (snmp_oid_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_ip_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_COUNTER: + return (snmp_cnt_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_GAUGE: + return (snmp_gauge_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_tick_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_cnt64_insert(snmptoolctx, entry)); + default: + break; + } + + return (-1); +} + +static int32_t +snmp_index_insert(struct snmp_idxlist *headp, struct index *idx) +{ + if (headp == NULL || index == NULL) + return (-1); + + STAILQ_INSERT_TAIL(headp, idx, link); + return (1); +} + +int32_t +snmp_syntax_insert(struct snmp_idxlist *headp, struct enum_pairs *enums, + enum snmp_syntax syntax, enum snmp_tc tc) +{ + struct index *idx; + + if ((idx = malloc(sizeof(struct index))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(idx, 0, sizeof(struct index)); + + if (snmp_index_insert(headp, idx) < 0) { + free(idx); + return (-1); + } + + idx->syntax = syntax; + idx->snmp_enum = enums; + idx->tc = tc; + + return (1); +} + +int32_t +snmp_table_insert(struct snmp_toolinfo *snmptoolctx, + struct snmp_index_entry *entry) +{ + int32_t rc; + struct snmp_index_entry *temp, *prev; + + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || + entry == NULL) + return(-1); + + if ((prev = SLIST_FIRST(&snmptoolctx->snmp_tablelist)) == NULL || + asn_compare_oid(&(entry->var), &(prev->var)) < 0) { + SLIST_INSERT_HEAD(&snmptoolctx->snmp_tablelist, entry, link); + return (1); + } else + rc = -1; /* Make the compiler happy. */ + + SLIST_FOREACH(temp, &snmptoolctx->snmp_tablelist, link) { + if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) + break; + prev = temp; + rc = -1; + } + + switch (rc) { + case 0: + /* Ops, matching OIDs - hope the rest info also matches. */ + if (strncmp(temp->string, entry->string, entry->strlen)) { + syslog(LOG_INFO, "Matching OIDs with different string " + "mapping - old - %s, new - %s", temp->string, + entry->string); + return (-1); + } + return(0); + + case 1: + SLIST_INSERT_AFTER(temp, entry, link); + break; + + case -1: + SLIST_INSERT_AFTER(prev, entry, link); + break; + + default: + /* NOTREACHED */ + return (-1); + } + + return (1); +} + +struct enum_type * +snmp_enumtc_init(char *name) +{ + struct enum_type *enum_tc; + + if ((enum_tc = malloc(sizeof(struct enum_type))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + memset(enum_tc, 0, sizeof(struct enum_type)); + if ((enum_tc->name = malloc(strlen(name) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(enum_tc); + return (NULL); + } + strlcpy(enum_tc->name, name, strlen(name) + 1); + + return (enum_tc); +} + +void +snmp_enumtc_free(struct enum_type *tc) +{ + if (tc->name) + free(tc->name); + if (tc->snmp_enum) + enum_pairs_free(tc->snmp_enum); + free(tc); +} + +void +snmp_enumtc_insert(struct snmp_toolinfo *snmptoolctx, struct enum_type *entry) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return; /* XXX no error handling? */ + + SLIST_INSERT_HEAD(&snmptoolctx->snmp_tclist, entry, link); +} + +struct enum_type * +snmp_enumtc_lookup(struct snmp_toolinfo *snmptoolctx, char *name) +{ + struct enum_type *temp; + + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (NULL); + + SLIST_FOREACH(temp, &snmptoolctx->snmp_tclist, link) { + if (strcmp(temp->name, name) == 0) + return (temp); + } + return (NULL); +} + +static void +snmp_mapping_dumplist(struct snmp_mapping *headp) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_oid2str *entry; + + if (headp == NULL) + return; + + SLIST_FOREACH(entry,headp,link) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + fprintf(stderr, "%s - %s - %d - %d - %d", buf, entry->string, + entry->syntax, entry->access ,entry->strlen); + fprintf(stderr," - %s \n", (entry->table_idx == NULL)? + "No table":entry->table_idx->string); + } +} + +static void +snmp_mapping_dumptable(struct snmp_table_index *headp) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_index_entry *entry; + + if (headp == NULL) + return; + + SLIST_FOREACH(entry, headp, link) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + fprintf(stderr,"%s - %s - %d - ", buf, entry->string, + entry->strlen); + snmp_dump_indexlist(&(entry->index_list)); + } +} + +void +snmp_mapping_dump(struct snmp_toolinfo *snmptoolctx /* int bits */) +{ + if (!_bsnmptools_debug) + return; + + if (snmptoolctx == NULL) { + fprintf(stderr,"No snmptool context!\n"); + return; + } + + if (snmptoolctx->mappings == NULL) { + fprintf(stderr,"No mappings!\n"); + return; + } + + fprintf(stderr,"snmp_nodelist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_nodelist); + + fprintf(stderr,"snmp_intlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_intlist); + + fprintf(stderr,"snmp_octlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_octlist); + + fprintf(stderr,"snmp_oidlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_oidlist); + + fprintf(stderr,"snmp_iplist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_iplist); + + fprintf(stderr,"snmp_ticklist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_ticklist); + + fprintf(stderr,"snmp_cntlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_cntlist); + + fprintf(stderr,"snmp_gaugelist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_gaugelist); + + fprintf(stderr,"snmp_cnt64list:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_cnt64list); + + fprintf(stderr,"snmp_enumlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_enumlist); + + fprintf(stderr,"snmp_tablelist:\n"); + snmp_mapping_dumptable(&snmptoolctx->snmp_tablelist); +} + +char * +enum_string_lookup(struct enum_pairs *headp, int32_t enum_val) +{ + struct enum_pair *temp; + + if (headp == NULL) + return (NULL); + + STAILQ_FOREACH(temp, headp, link) { + if (temp->enum_val == enum_val) + return (temp->enum_str); + } + + return (NULL); +} + +int32_t +enum_number_lookup(struct enum_pairs *headp, char *e_str) +{ + struct enum_pair *tmp; + + if (headp == NULL) + return (-1); + + STAILQ_FOREACH(tmp, headp, link) + if (strncmp(tmp->enum_str, e_str, strlen(tmp->enum_str)) == 0) + return (tmp->enum_val); + + return (-1); +} + +static int32_t +snmp_lookuplist_string(struct snmp_mapping *headp, struct snmp_object *s) +{ + struct snmp_oid2str *temp; + + if (headp == NULL) + return (-1); + + SLIST_FOREACH(temp, headp, link) + if (asn_compare_oid(&(temp->var), &(s->val.var)) == 0) + break; + + if ((s->info = temp) == NULL) + return (-1); + + return (1); +} + +/* provided an asn_oid find the corresponding string for it */ +static int32_t +snmp_lookup_leaf(struct snmp_mapping *headp, struct snmp_object *s) +{ + struct snmp_oid2str *temp; + + if (headp == NULL) + return (-1); + + SLIST_FOREACH(temp,headp,link) { + if ((asn_compare_oid(&(temp->var), &(s->val.var)) == 0) || + (asn_is_suboid(&(temp->var), &(s->val.var)))) { + s->info = temp; + return (1); + } + } + + return (-1); +} + +int32_t +snmp_lookup_leafstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + switch (s->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s)); + case SNMP_SYNTAX_OID: + return (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s)); + case SNMP_SYNTAX_COUNTER: + return (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s)); + case SNMP_SYNTAX_GAUGE: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_gaugelist, s)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_ticklist, s)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_cnt64list, s)); + case SNMP_SYNTAX_NOSUCHOBJECT: + /* FALLTHROUGH */ + case SNMP_SYNTAX_NOSUCHINSTANCE: + /* FALLTHROUGH */ + case SNMP_SYNTAX_ENDOFMIBVIEW: + return (snmp_lookup_allstring(snmptoolctx, s)); + default: + warnx("Unknown syntax - %d", s->val.syntax); + break; + } + + return (-1); +} + +int32_t +snmp_lookup_enumstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s)); +} + +int32_t +snmp_lookup_oidstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_oidlist, s)); +} + +int32_t +snmp_lookup_nodestring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s)); +} + +int32_t +snmp_lookup_allstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (-1); + + if (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_gaugelist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_ticklist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_cnt64list, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + + return (-1); +} + +int32_t +snmp_lookup_nonleaf_string(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *s) +{ + if (snmptoolctx == NULL) + return (-1); + + if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) + return (1); + + return (-1); +} + +static int32_t +snmp_lookup_oidlist(struct snmp_mapping *hp, struct snmp_object *s, char *oid) +{ + struct snmp_oid2str *temp; + + if (hp == NULL) + return (-1); + + SLIST_FOREACH(temp, hp, link) { + if (temp->strlen != strlen(oid)) + continue; + + if (strncmp(temp->string, oid, temp->strlen)) + continue; + + s->val.syntax = temp->syntax; + s->info = temp; + asn_append_oid(&(s->val.var), &(temp->var)); + return (1); + } + + return (-1); +} + +static int32_t +snmp_lookup_tablelist(struct snmp_toolinfo *snmptoolctx, + struct snmp_table_index *headp, struct snmp_object *s, char *oid) +{ + struct snmp_index_entry *temp; + + if (snmptoolctx == NULL || headp == NULL) + return (-1); + + SLIST_FOREACH(temp, headp, link) { + if (temp->strlen != strlen(oid)) + continue; + + if (strncmp(temp->string, oid, temp->strlen)) + continue; + + /* + * Another hack here - if we were given a table name + * return the corresponding pointer to it's entry. + * That should not change the reponce we'll get. + */ + s->val.syntax = SNMP_SYNTAX_NULL; + asn_append_oid(&(s->val.var), &(temp->var)); + if (snmp_lookup_leaf(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + else + return (-1); + } + + return (-1); +} + +int32_t +snmp_lookup_oidall(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL || oid == NULL) + return (-1); + + if (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, s, oid) > 0) + return (1); + if (snmp_lookup_tablelist(snmptoolctx, &snmptoolctx->snmp_tablelist, + s, oid) > 0) + return (1); + + return (-1); +} + +int32_t +snmp_lookup_enumoid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL) + return (-1); + + return (snmp_lookup_oidlist(&snmptoolctx->snmp_enumlist, s, oid)); +} + +int32_t +snmp_lookup_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL) + return (-1); + + switch (s->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, + s, oid)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, + s, oid)); + case SNMP_SYNTAX_OID: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, + s, oid)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, + s, oid)); + case SNMP_SYNTAX_COUNTER: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, + s, oid)); + case SNMP_SYNTAX_GAUGE: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, + s, oid)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, + s, oid)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, + s, oid)); + case SNMP_SYNTAX_NULL: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, + s, oid)); + default: + warnx("Unknown syntax - %d", s->val.syntax); + break; + } + + return (-1); +} Index: usr.sbin/bsnmpd/tools/Makefile =================================================================== --- usr.sbin/bsnmpd/tools/Makefile (revision 0) +++ usr.sbin/bsnmpd/tools/Makefile (revision 0) @@ -0,0 +1,7 @@ +# $FreeBSD$ +# Author: Shteryana Shopova + +SUBDIR= libbsnmptools \ + bsnmptools + +.include Index: usr.sbin/bsnmpd/bsnmpd/Makefile =================================================================== --- usr.sbin/bsnmpd/bsnmpd/Makefile (revision 214335) +++ usr.sbin/bsnmpd/bsnmpd/Makefile (working copy) @@ -2,6 +2,8 @@ # # Author: Harti Brandt +.include + CONTRIB=${.CURDIR}/../../../contrib/bsnmp .PATH: ${CONTRIB}/snmpd @@ -11,7 +13,7 @@ XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ sysUpTime snmpTrapOID coldStart authenticationFailure \ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \ - freeBSDVersion + freeBSD freeBSDVersion CLEANFILES= oid.h tree.c tree.h MAN= bsnmpd.1 snmpmod.3 NO_WERROR= @@ -31,6 +33,10 @@ LDFLAGS= -export-dynamic +.if ${MK_OPENSSL} != "no" +CFLAGS+= -DHAVE_LIBCRYPTO +.endif + oid.h: tree.def Makefile gensnmptree -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET} Index: usr.sbin/bsnmpd/modules/snmp_usm/Makefile =================================================================== --- usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 0) +++ usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 0) @@ -0,0 +1,28 @@ +# $FreeBSD$ +# +# Author: Shteryana Shopova + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_usm + +MOD= usm +SRCS= usm_snmp.c +XSYM= snmpUsmMIB usmNoAuthProtocol usmHMACMD5AuthProtocol \ + usmHMACSHAAuthProtocol usmNoPrivProtocol usmDESPrivProtocol \ + usmAesCfb128Protocol usmUserSecurityName + +MAN= snmp_usm.3 + +# XXX +CFLAGS+= -Wall -Werror -g +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +DEFS= ${MOD}_tree.def +BMIBS= + +.include + +#smilint: +# env SMIPATH=/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \ +# smilint -c /dev/null -l6 -i group-membership ${BMIBS:C/^/${CONTRIB}\/snmp_mibII\//} Index: usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c =================================================================== --- usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 214335) +++ usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (working copy) @@ -26,6 +26,7 @@ * $FreeBSD$ */ +#include #include #include Index: usr.sbin/bsnmpd/modules/snmp_vacm/Makefile =================================================================== --- usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 0) +++ usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 0) @@ -0,0 +1,26 @@ +# $FreeBSD$ +# +# Author: Shteryana Shopova + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_vacm + +MOD= vacm +SRCS= vacm_snmp.c +XSYM= snmpVacmMIB + +MAN= snmp_vacm.3 + +# XXX +CFLAGS+= -Wall -Werror -g +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +DEFS= ${MOD}_tree.def +BMIBS= + +.include + +#smilint: +# env SMIPATH=/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \ +# smilint -c /dev/null -l6 -i group-membership ${BMIBS:C/^/${CONTRIB}\/snmp_mibII\//} Index: usr.sbin/bsnmpd/modules/Makefile =================================================================== --- usr.sbin/bsnmpd/modules/Makefile (revision 214335) +++ usr.sbin/bsnmpd/modules/Makefile (working copy) @@ -13,6 +13,8 @@ snmp_hostres \ snmp_mibII \ snmp_pf \ + snmp_usm \ + snmp_vacm \ snmp_wlan .if ${MK_NETGRAPH_SUPPORT} != "no" Index: usr.sbin/bsnmpd/Makefile =================================================================== --- usr.sbin/bsnmpd/Makefile (revision 214335) +++ usr.sbin/bsnmpd/Makefile (working copy) @@ -2,6 +2,7 @@ SUBDIR= gensnmptree \ bsnmpd \ - modules + modules \ + tools .include Index: lib/libbsnmp/libbsnmp/Makefile =================================================================== --- lib/libbsnmp/libbsnmp/Makefile (revision 214335) +++ lib/libbsnmp/libbsnmp/Makefile (working copy) @@ -2,6 +2,8 @@ # # Author: Harti Brandt +.include + CONTRIB= ${.CURDIR}/../../../contrib/bsnmp/lib .PATH: ${CONTRIB} @@ -11,8 +13,14 @@ CFLAGS+= -I${CONTRIB} -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DQUADFMT='"llu"' -DQUADXFMT='"llx"' -SRCS= asn1.c snmp.c snmpagent.c snmpclient.c support.c -INCS= asn1.h snmp.h snmpagent.h snmpclient.h +.if ${MK_OPENSSL} != "no" +CFLAGS+= -DHAVE_LIBCRYPTO +#DPADD+= ${LIBCRYPTO} +LDADD+= -lcrypto +.endif + +SRCS= asn1.c snmp.c snmpagent.c snmpclient.c snmpcrypto.c support.c +INCS= asn1.h snmp.h snmpagent.h snmpclient.h MAN= asn1.3 bsnmplib.3 bsnmpclient.3 bsnmpagent.3 .include