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 ha...@freebsd.org
+.Pp
+.An Shteryana Shopova Aq syr...@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 <ha...@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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to