From owner-svn-src-all@FreeBSD.ORG Wed Dec 8 13:51:39 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 073EF1065696; Wed, 8 Dec 2010 13:51:39 +0000 (UTC) (envelope-from syrinx@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id E6A118FC19; Wed, 8 Dec 2010 13:51:38 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id oB8DpcCg005257; Wed, 8 Dec 2010 13:51:38 GMT (envelope-from syrinx@svn.freebsd.org) Received: (from syrinx@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id oB8DpcYw005250; Wed, 8 Dec 2010 13:51:38 GMT (envelope-from syrinx@svn.freebsd.org) Message-Id: <201012081351.oB8DpcYw005250@svn.freebsd.org> From: Shteryana Shopova Date: Wed, 8 Dec 2010 13:51:38 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: 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... X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Dec 2010 13:51:39 -0000 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@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. 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 * + * 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 ***