summaryrefslogtreecommitdiff
path: root/lib/snmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp.c')
-rw-r--r--lib/snmp.c597
1 files changed, 480 insertions, 117 deletions
diff --git a/lib/snmp.c b/lib/snmp.c
index b2700b1c23ce..d1dabc8884a0 100644
--- a/lib/snmp.c
+++ b/lib/snmp.c
@@ -4,7 +4,13 @@
* All rights reserved.
*
* Author: Harti Brandt <harti@freebsd.org>
- *
+ *
+ * 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:
@@ -13,7 +19,7 @@
* 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
@@ -32,19 +38,21 @@
*/
#include <sys/types.h>
#include <sys/socket.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
+#include <string.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#endif
-#include <string.h>
-#include <ctype.h>
-#include <netdb.h>
-#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include "asn1.h"
#include "snmp.h"
@@ -271,47 +279,312 @@ parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
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;
+ u_int len = 256;
+
+ if (asn_get_octetstring(b, buf, &len) != ASN_ERR_OK) {
+ snmp_error("cannot parse usm header");
+ return (ASN_ERR_FAILED);
+ }
+ tb.asn_len = len;
+
+ 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 +599,7 @@ snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
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 +609,64 @@ snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
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);
- }
-
-
- if (*lenp > b->asn_len) {
- snmp_error("pdu length too long");
- return (ASN_ERR_FAILED);
+ return (SNMP_CODE_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(pdu, digest)) !=
+ SNMP_CODE_OK)
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;
- }
- switch (parse_message(b, pdu, ip)) {
+ if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH &&
+ memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0)
+ return (SNMP_CODE_BADDIGEST);
- case ASN_ERR_OK:
- return (SNMP_CODE_OK);
-
- case ASN_ERR_FAILED:
- case ASN_ERR_EOBUF:
- snmp_pdu_free(pdu);
+ 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);
+ }
+ pdu->scoped_ptr = b->asn_ptr;
- case ASN_ERR_BADLEN:
- return (SNMP_CODE_BADLEN);
-
- case ASN_ERR_RANGE:
- return (SNMP_CODE_OORANGE);
+ if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
+ (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0)
+ return (SNMP_CODE_BADSECLEVEL);
- case ASN_ERR_TAG:
- if (pdu->version == SNMP_Verr)
- return (SNMP_CODE_BADVERS);
- else
- return (SNMP_CODE_BADENC);
- }
+ if ((code = snmp_pdu_decrypt(pdu)) != SNMP_CODE_OK)
+ return (SNMP_CODE_FAILED);
- return (SNMP_CODE_OK);
+ return (code);
}
/*
@@ -446,7 +684,7 @@ snmp_pdu_snoop(const struct asn_buf *b0)
struct asn_buf b = *b0;
/* <0x10|0x20> <len> <data...> */
-
+
if (b.asn_len == 0)
return (0);
if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) {
@@ -500,6 +738,7 @@ enum snmp_code
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,14 +748,63 @@ snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
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_TRAP2 &&
+ 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)
@@ -550,13 +838,66 @@ snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
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 (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(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(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 +980,7 @@ snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
return (err);
}
- err = asn_commit_header(b, ptr);
+ err = asn_commit_header(b, ptr, NULL);
if (err != ASN_ERR_OK) {
*b = save;
return (err);
@@ -660,7 +1001,7 @@ snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b)
if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK)
return (err);
for (idx = 0; idx < pdu->nbindings; idx++)
- if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx]))
+ if (snmp_binding_encode(resp_b, &pdu->bindings[idx])
!= ASN_ERR_OK)
return (SNMP_CODE_FAILED);
@@ -775,6 +1116,8 @@ snmp_pdu_dump(const struct snmp_pdu *pdu)
vers = "SNMPv1";
else if (pdu->version == SNMP_V2c)
vers = "SNMPv2c";
+ else if (pdu->version == SNMP_V3)
+ vers = "SNMPv3";
else
vers = "v?";
@@ -811,8 +1154,11 @@ snmp_pdu_dump(const struct snmp_pdu *pdu)
void
snmp_value_free(struct snmp_value *value)
{
- if (value->syntax == SNMP_SYNTAX_OCTETSTRING)
+
+ if (value->syntax == SNMP_SYNTAX_OCTETSTRING) {
free(value->v.octetstring.octets);
+ value->v.octetstring.octets = NULL;
+ }
value->syntax = SNMP_SYNTAX_NULL;
}
@@ -838,12 +1184,42 @@ snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
}
void
+snmp_pdu_init_secparams(struct snmp_pdu *pdu)
+{
+ int32_t rval;
+
+ if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
+ pdu->flags |= SNMP_MSG_AUTH_FLAG;
+
+ switch (pdu->user.priv_proto) {
+ case SNMP_PRIV_DES:
+ memcpy(pdu->msg_salt, &pdu->engine.engine_boots,
+ sizeof(pdu->engine.engine_boots));
+ rval = random();
+ memcpy(pdu->msg_salt + sizeof(pdu->engine.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;
for (i = 0; i < pdu->nbindings; i++)
snmp_value_free(&pdu->bindings[i]);
+ pdu->nbindings = 0;
}
/*
@@ -1014,29 +1390,16 @@ snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v)
case SNMP_SYNTAX_IPADDRESS:
{
struct hostent *he;
- u_long ip[4];
- int n;
-
- if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2],
- &ip[3], &n) == 4 && (size_t)n == strlen(str) &&
- ip[0] <= 0xff && ip[1] <= 0xff &&
- ip[2] <= 0xff && ip[3] <= 0xff) {
- v->ipaddress[0] = (u_char)ip[0];
- v->ipaddress[1] = (u_char)ip[1];
- v->ipaddress[2] = (u_char)ip[2];
- v->ipaddress[3] = (u_char)ip[3];
- return (0);
- }
- if ((he = gethostbyname(str)) == NULL)
+ if (inet_pton(AF_INET, str, &v->ipaddress) == 1)
+ return (0);
+ if ((he = gethostbyname2(str, AF_INET)) == NULL)
return (-1);
if (he->h_addrtype != AF_INET)
return (-1);
- v->ipaddress[0] = he->h_addr[0];
- v->ipaddress[1] = he->h_addr[1];
- v->ipaddress[2] = he->h_addr[2];
- v->ipaddress[3] = he->h_addr[3];
+ memcpy(v->ipaddress, he->h_addr, sizeof(v->ipaddress));
+
return (0);
}