svn commit: r216294 - in head: contrib/bsnmp/lib contrib/bsnmp/snmp_usm contrib/bsnmp/snmp_vacm contrib/bsnmp/snmpd lib/libbsnmp/libbsnmp usr.sbin/bsnmpd/bsnmpd usr.sbin/bsnmpd/modules usr.sbin/bsn...

Shteryana Shopova syrinx at FreeBSD.org
Wed Dec 8 13:51:39 UTC 2010


Author: syrinx
Date: Wed Dec  8 13:51:38 2010
New Revision: 216294
URL: http://svn.freebsd.org/changeset/base/216294

Log:
  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@

Added:
  head/contrib/bsnmp/snmp_usm/
  head/contrib/bsnmp/snmp_usm/snmp_usm.3   (contents, props changed)
  head/contrib/bsnmp/snmp_usm/usm_snmp.c   (contents, props changed)
  head/contrib/bsnmp/snmp_usm/usm_tree.def   (contents, props changed)
  head/contrib/bsnmp/snmp_vacm/
  head/contrib/bsnmp/snmp_vacm/snmp_vacm.3   (contents, props changed)
  head/contrib/bsnmp/snmp_vacm/vacm_snmp.c   (contents, props changed)
  head/contrib/bsnmp/snmp_vacm/vacm_tree.def   (contents, props changed)
  head/usr.sbin/bsnmpd/modules/snmp_usm/
  head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile   (contents, props changed)
  head/usr.sbin/bsnmpd/modules/snmp_vacm/
  head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile   (contents, props changed)
Modified:
  head/contrib/bsnmp/lib/asn1.c
  head/contrib/bsnmp/lib/asn1.h
  head/contrib/bsnmp/lib/bsnmpclient.3
  head/contrib/bsnmp/lib/bsnmplib.3
  head/contrib/bsnmp/lib/snmp.c
  head/contrib/bsnmp/lib/snmp.h
  head/contrib/bsnmp/lib/snmpagent.c
  head/contrib/bsnmp/lib/snmpclient.c
  head/contrib/bsnmp/lib/snmpclient.h
  head/contrib/bsnmp/lib/snmppriv.h
  head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
  head/contrib/bsnmp/snmpd/action.c
  head/contrib/bsnmp/snmpd/bsnmpd.1
  head/contrib/bsnmp/snmpd/config.c
  head/contrib/bsnmp/snmpd/export.c
  head/contrib/bsnmp/snmpd/main.c
  head/contrib/bsnmp/snmpd/snmpd.h
  head/contrib/bsnmp/snmpd/snmpmod.3
  head/contrib/bsnmp/snmpd/snmpmod.h
  head/contrib/bsnmp/snmpd/trans_lsock.c
  head/contrib/bsnmp/snmpd/trans_udp.c
  head/contrib/bsnmp/snmpd/trap.c
  head/contrib/bsnmp/snmpd/tree.def
  head/lib/libbsnmp/libbsnmp/Makefile
  head/usr.sbin/bsnmpd/bsnmpd/Makefile
  head/usr.sbin/bsnmpd/modules/Makefile
  head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c

Modified: head/contrib/bsnmp/lib/asn1.c
==============================================================================
--- head/contrib/bsnmp/lib/asn1.c	Wed Dec  8 13:51:25 2010	(r216293)
+++ head/contrib/bsnmp/lib/asn1.c	Wed Dec  8 13:51:38 2010	(r216294)
@@ -196,7 +196,7 @@ asn_put_temp_header(struct asn_buf *b, u
 	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_c
 		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 le
 }
 
 /*
+ * 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

Modified: head/contrib/bsnmp/lib/asn1.h
==============================================================================
--- head/contrib/bsnmp/lib/asn1.h	Wed Dec  8 13:51:25 2010	(r216293)
+++ head/contrib/bsnmp/lib/asn1.h	Wed Dec  8 13:51:38 2010	(r216294)
@@ -93,7 +93,7 @@ enum asn_err asn_get_header(struct asn_b
 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 as
 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

Modified: head/contrib/bsnmp/lib/bsnmpclient.3
==============================================================================
--- head/contrib/bsnmp/lib/bsnmpclient.3	Wed Dec  8 13:51:25 2010	(r216293)
+++ head/contrib/bsnmp/lib/bsnmpclient.3	Wed Dec  8 13:51:38 2010	(r216294)
@@ -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

Modified: head/contrib/bsnmp/lib/bsnmplib.3
==============================================================================
--- head/contrib/bsnmp/lib/bsnmplib.3	Wed Dec  8 13:51:25 2010	(r216293)
+++ head/contrib/bsnmp/lib/bsnmplib.3	Wed Dec  8 13:51:38 2010	(r216294)
@@ -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 
 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 at FreeBSD.org
+.Pp
+.An Shteryana Shopova Aq syrinx at FreeBSD.org
+added support for the SNMPv3 message proccessing and User-Based
+Security model message authentication and privacy.

Modified: head/contrib/bsnmp/lib/snmp.c
==============================================================================
--- head/contrib/bsnmp/lib/snmp.c	Wed Dec  8 13:51:25 2010	(r216293)
+++ head/contrib/bsnmp/lib/snmp.c	Wed Dec  8 13:51:38 2010	(r216294)
@@ -5,6 +5,12 @@
  *
  * Author: Harti Brandt <harti at 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 snm
 	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
 	  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
 	  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(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;
-	}
-
-	switch (parse_message(b, pdu, ip)) {
+	pdu->scoped_ptr = b->asn_ptr;
 
-	  case ASN_ERR_OK:
-		return (SNMP_CODE_OK);
+	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
+	    (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0)
+		return (SNMP_CODE_BADSECLEVEL);
 
-	  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:

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list