summaryrefslogtreecommitdiffstats
path: root/contrib/bsnmp/lib
diff options
context:
space:
mode:
authorsyrinx <syrinx@FreeBSD.org>2010-12-08 13:51:38 +0000
committersyrinx <syrinx@FreeBSD.org>2010-12-08 13:51:38 +0000
commited79f703fb1803e10222fccd98100334741138ff (patch)
tree30b62404bfa57932ef78fb729b57635b158c2a4a /contrib/bsnmp/lib
parent6e855a313aac604a57c7b9d8561a9a4e5c2f6666 (diff)
downloadFreeBSD-src-ed79f703fb1803e10222fccd98100334741138ff.zip
FreeBSD-src-ed79f703fb1803e10222fccd98100334741138ff.tar.gz
In bsnmpd(1) add support for SNMPv3 message processing model, including message authentication, packet encryption & view-based access control (RFC 3412, 3414, 3415).
Sponsored by: The FreeBSD Foundation Reviewed by: philip@ (mostly) Approved by: philip@
Diffstat (limited to 'contrib/bsnmp/lib')
-rw-r--r--contrib/bsnmp/lib/asn1.c18
-rw-r--r--contrib/bsnmp/lib/asn1.h3
-rw-r--r--contrib/bsnmp/lib/bsnmpclient.391
-rw-r--r--contrib/bsnmp/lib/bsnmplib.3233
-rw-r--r--contrib/bsnmp/lib/snmp.c551
-rw-r--r--contrib/bsnmp/lib/snmp.h145
-rw-r--r--contrib/bsnmp/lib/snmpagent.c58
-rw-r--r--contrib/bsnmp/lib/snmpclient.c131
-rw-r--r--contrib/bsnmp/lib/snmpclient.h52
-rw-r--r--contrib/bsnmp/lib/snmppriv.h12
10 files changed, 1082 insertions, 212 deletions
diff --git a/contrib/bsnmp/lib/asn1.c b/contrib/bsnmp/lib/asn1.c
index 16b523d..4d9704f 100644
--- a/contrib/bsnmp/lib/asn1.c
+++ b/contrib/bsnmp/lib/asn1.c
@@ -196,7 +196,7 @@ asn_put_temp_header(struct asn_buf *b, u_char type, u_char **ptr)
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 @@ asn_commit_header(struct asn_buf *b, u_char *ptr)
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 @@ asn_skip(struct asn_buf *b, asn_len_t len)
}
/*
+ * 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
diff --git a/contrib/bsnmp/lib/asn1.h b/contrib/bsnmp/lib/asn1.h
index 5748b14..cf9a727 100644
--- a/contrib/bsnmp/lib/asn1.h
+++ b/contrib/bsnmp/lib/asn1.h
@@ -93,7 +93,7 @@ enum asn_err asn_get_header(struct asn_buf *, u_char *, asn_len_t *);
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_get_timeticks(struct asn_buf *, uint32_t *);
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
diff --git a/contrib/bsnmp/lib/bsnmpclient.3 b/contrib/bsnmp/lib/bsnmpclient.3
index 870f546..5fe9a9a 100644
--- a/contrib/bsnmp/lib/bsnmpclient.3
+++ b/contrib/bsnmp/lib/bsnmpclient.3
@@ -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 @@ Begemot SNMP library
.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];
+
+ /* SNMPv3 specific fields */
+ int32_t identifier;
+ int32_t security_model;
+ struct snmp_engine engine;
+ struct snmp_user user;
- char read_community[SNMP_COMMUNITY_MAXLEN + 1];
- char write_community[SNMP_COMMUNITY_MAXLEN + 1];
+ /* SNMPv3 Access control - VACM*/
+ uint32_t clen;
+ uint8_t cengine[SNMP_ENGINE_ID_SIZ];
+ char cname[SNMP_CONTEXT_NAME_SIZ];
- struct timeval timeout;
- u_int retries;
+ struct timeval timeout;
+ u_int retries;
- int dump_pdus;
+ int dump_pdus;
- size_t txbuflen;
- size_t rxbuflen;
+ size_t txbuflen;
+ size_t rxbuflen;
- int fd;
+ int fd;
- int32_t next_reqid;
- int32_t max_reqid;
- int32_t min_reqid;
+ int32_t next_reqid;
+ int32_t max_reqid;
+ int32_t min_reqid;
- char error[SNMP_STRERROR_LEN];
+ char error[SNMP_STRERROR_LEN];
- snmp_timeout_start_f timeout_start;
- snmp_timeout_stop_f timeout_stop;
+ snmp_timeout_start_f timeout_start;
+ snmp_timeout_stop_f timeout_stop;
- /* private */
- char local_path[sizeof(SNMP_LOCAL_PATH)];
+ char local_path[sizeof(SNMP_LOCAL_PATH)];
};
.Ed
.Pp
@@ -194,6 +207,23 @@ The default is
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 @@ returns -1.
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
diff --git a/contrib/bsnmp/lib/bsnmplib.3 b/contrib/bsnmp/lib/bsnmplib.3
index dfbffc4..af36879 100644
--- a/contrib/bsnmp/lib/bsnmplib.3
+++ b/contrib/bsnmp/lib/bsnmplib.3
@@ -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 @@ Begemot SNMP library
.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 @@ Begemot SNMP library
.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 @@ is not zero,
.Fa v.octetstring.octets
points to a string allocated by
.Xr malloc 3 .
+.Pp
+.Bd -literal -offset indent
+#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_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 @@ enum snmp_version {
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 @@ The function
.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 @@ A variable binding value was out of the allowed range.
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 @@ Encoding failed.
.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.
diff --git a/contrib/bsnmp/lib/snmp.c b/contrib/bsnmp/lib/snmp.c
index b2700b1..322ac9f 100644
--- a/contrib/bsnmp/lib/snmp.c
+++ b/contrib/bsnmp/lib/snmp.c
@@ -5,6 +5,12 @@
*
* 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:
@@ -271,47 +277,310 @@ 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;
+
+ 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';
+ }
+
+ 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';
}
- pdu->community[comm_len] = '\0';
- if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) {
+ 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 @@ 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 +605,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");
- return (SNMP_CODE_FAILED);
- }
- if (b->asn_len < len) {
- snmp_error("outer sequence value too short");
+ if ((code = snmp_pdu_calc_digest(b, pdu, digest)) !=
+ SNMP_CODE_OK)
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)) {
- case ASN_ERR_OK:
- return (SNMP_CODE_OK);
+ 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_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(b, pdu)) != SNMP_CODE_OK)
+ return (SNMP_CODE_FAILED);
- return (SNMP_CODE_OK);
+ return (code);
}
/*
@@ -500,6 +734,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 +744,62 @@ 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_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 +833,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 ((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 @@ 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);
@@ -775,6 +1111,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?";
@@ -838,6 +1176,39 @@ snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
}
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;
diff --git a/contrib/bsnmp/lib/snmp.h b/contrib/bsnmp/lib/snmp.h
index 43af22e..3a6cec7 100644
--- a/contrib/bsnmp/lib/snmp.h
+++ b/contrib/bsnmp/lib/snmp.h
@@ -5,6 +5,13 @@
*
* 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:
@@ -35,8 +42,11 @@
#include <sys/types.h>
-#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 @@ struct snmp_value {
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 @@ enum snmp_code {
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 *, ...);
diff --git a/contrib/bsnmp/lib/snmpagent.c b/contrib/bsnmp/lib/snmpagent.c
index ca9ccbd..9cd9676 100644
--- a/contrib/bsnmp/lib/snmpagent.c
+++ b/contrib/bsnmp/lib/snmpagent.c
@@ -165,6 +165,29 @@ find_subnode(const struct snmp_value *value)
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 @@ snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
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 @@ snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
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 @@ snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
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 @@ snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
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 @@ snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
enum snmp_code code;
memset(&resp, 0, sizeof(resp));
-
- /* Message sequence */
- if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
- return (SNMP_RET_IGN);
- if (pdu_b->asn_len < len)
+ if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
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;
diff --git a/contrib/bsnmp/lib/snmpclient.c b/contrib/bsnmp/lib/snmpclient.c
index b77543a..103ea69 100644
--- a/contrib/bsnmp/lib/snmpclient.c
+++ b/contrib/bsnmp/lib/snmpclient.c
@@ -852,6 +852,9 @@ snmp_client_init(struct snmp_client *c)
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 @@ snmp_client_init(struct snmp_client *c)
c->max_reqid = INT32_MAX;
c->min_reqid = 0;
c->next_reqid = 0;
+
+ c->engine.max_msg_size = 1500; /* XXX */
}
@@ -1132,7 +1137,8 @@ snmp_close(void)
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 @@ snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
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 @@ snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv)
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 @@ snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp)
}
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;
diff --git a/contrib/bsnmp/lib/snmpclient.h b/contrib/bsnmp/lib/snmpclient.h
index e137b4c..619f726 100644
--- a/contrib/bsnmp/lib/snmpclient.h
+++ b/contrib/bsnmp/lib/snmpclient.h
@@ -69,36 +69,47 @@ typedef void (*snmp_timeout_stop_f)(void *timeout_id);
* 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 @@ int snmp_table_fetch_async(const struct snmp_table *, void *,
/* 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 *);
diff --git a/contrib/bsnmp/lib/snmppriv.h b/contrib/bsnmp/lib/snmppriv.h
index 535e0e9..98ac7e0 100644
--- a/contrib/bsnmp/lib/snmppriv.h
+++ b/contrib/bsnmp/lib/snmppriv.h
@@ -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"
OpenPOWER on IntegriCloud