1 //! SNMPv3 Parser
2 //!
3 //! SNMPv3 is defined in the following RFCs:
4 //!   - [RFC2570](https://tools.ietf.org/html/rfc2570): Introduction to SNMP v3
5 //!   - [RFC3412](https://tools.ietf.org/html/rfc3412): Message Processing and Dispatching for the
6 //!     Simple Network Management Protocol (SNMP)
7 //!
8 //! See also:
9 //!   - [RFC2578](https://tools.ietf.org/html/rfc2578): Structure of Management Information Version 2 (SMIv2)
10 
11 use std::fmt;
12 
13 use der_parser::ber::*;
14 use der_parser::error::*;
15 use nom::IResult;
16 
17 use crate::snmp::{SnmpPdu, parse_ber_octetstring_as_slice, parse_snmp_v2c_pdu};
18 pub use crate::usm::{UsmSecurityParameters,parse_usm_security_parameters};
19 use crate::error::SnmpError;
20 
21 #[derive(Clone, Copy, Eq, PartialEq)]
22 pub struct SecurityModel(pub u32);
23 
24 #[allow(non_upper_case_globals)]
25 impl SecurityModel {
26     pub const SnmpV1    : SecurityModel = SecurityModel(1);
27     pub const SnmpV2c   : SecurityModel = SecurityModel(2);
28     pub const USM       : SecurityModel = SecurityModel(3);
29 }
30 
31 impl fmt::Debug for SecurityModel {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result32     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33         match self.0 {
34            1 => f.write_str("SnmpV1"),
35            2 => f.write_str("SnmpV2c"),
36            3 => f.write_str("USM"),
37            n => f.debug_tuple("SecurityModel").field(&n).finish(),
38         }
39     }
40 }
41 
42 #[derive(Debug,PartialEq)]
43 pub enum SecurityParameters<'a> {
44     Raw(&'a[u8]),
45     USM(UsmSecurityParameters<'a>)
46 }
47 
48 /// An SNMPv3 message
49 #[derive(Debug,PartialEq)]
50 pub struct SnmpV3Message<'a> {
51     /// Version, as raw-encoded: 3 for SNMPv3
52     pub version: u32,
53     pub header_data: HeaderData,
54     pub security_params: SecurityParameters<'a>,
55     pub data: ScopedPduData<'a>,
56 }
57 
58 #[derive(Clone, Copy, Debug, PartialEq)]
59 pub struct HeaderData {
60     pub msg_id: u32,
61     pub msg_max_size: u32,
62     pub msg_flags: u8,
63     pub msg_security_model: SecurityModel,
64 }
65 
66 impl HeaderData {
is_authenticated(&self) -> bool67     pub fn is_authenticated(&self) -> bool { self.msg_flags & 0b001 != 0 }
68 
is_encrypted(&self) -> bool69     pub fn is_encrypted(&self) -> bool { self.msg_flags & 0b010 != 0 }
70 
is_reportable(&self) -> bool71     pub fn is_reportable(&self) -> bool { self.msg_flags & 0b100 != 0 }
72 }
73 
74 #[derive(Debug,PartialEq)]
75 pub enum ScopedPduData<'a> {
76     Plaintext(ScopedPdu<'a>),
77     Encrypted(&'a[u8]),
78 }
79 
80 #[derive(Debug,PartialEq)]
81 pub struct ScopedPdu<'a> {
82     pub ctx_engine_id: &'a[u8],
83     pub ctx_engine_name: &'a[u8],
84     /// ANY -- e.g., PDUs as defined in [RFC3416](https://tools.ietf.org/html/rfc3416)
85     pub data: SnmpPdu<'a>,
86 }
87 
88 
89 
90 
parse_snmp_v3_data<'a>(i:&'a[u8], hdr: &HeaderData) -> IResult<&'a[u8], ScopedPduData<'a>, BerError>91 pub(crate) fn parse_snmp_v3_data<'a>(i:&'a[u8], hdr: &HeaderData) -> IResult<&'a[u8], ScopedPduData<'a>, BerError> {
92     if hdr.is_encrypted()
93     {
94         map_res!(i,
95                  parse_ber_octetstring,
96                  |x: BerObject<'a>| x.as_slice().map(|x| ScopedPduData::Encrypted(x))
97         )
98     } else {
99         parse_snmp_v3_plaintext_pdu(i)
100     }
101 }
102 
parse_secp<'a>(x:&BerObject<'a>, hdr:&HeaderData) -> Result<SecurityParameters<'a>, BerError>103 pub(crate) fn parse_secp<'a>(x:&BerObject<'a>, hdr:&HeaderData) -> Result<SecurityParameters<'a>, BerError> {
104     match x.as_slice() {
105         Ok(i) => {
106             match hdr.msg_security_model {
107                 SecurityModel::USM => {
108                     match parse_usm_security_parameters(i) {
109                         Ok((_,usm)) => Ok(SecurityParameters::USM(usm)),
110                         _           => Err(BerError::BerValueError)
111                     }
112                 },
113                 _                  => Ok(SecurityParameters::Raw(i))
114             }
115         },
116         _     => Err(BerError::BerValueError)
117     }
118 }
119 
120 /// Parse an SNMPv3 top-level message
121 ///
122 /// Example:
123 ///
124 /// ```rust
125 /// # extern crate nom;
126 /// # #[macro_use] extern crate snmp_parser;
127 /// use snmp_parser::{parse_snmp_v3,ScopedPduData,SecurityModel};
128 ///
129 /// static SNMPV3_REQ: &'static [u8] = include_bytes!("../assets/snmpv3_req.bin");
130 ///
131 /// # fn main() {
132 /// match parse_snmp_v3(&SNMPV3_REQ) {
133 ///   Ok((_, ref r)) => {
134 ///     assert!(r.version == 3);
135 ///     assert!(r.header_data.msg_security_model == SecurityModel::USM);
136 ///     match r.data {
137 ///       ScopedPduData::Plaintext(ref _pdu) => { },
138 ///       ScopedPduData::Encrypted(_) => (),
139 ///     }
140 ///   },
141 ///   Err(e) => panic!(e),
142 /// }
143 /// # }
144 /// ```
parse_snmp_v3<'a>(i:&'a[u8]) -> IResult<&'a[u8], SnmpV3Message<'a>, SnmpError>145 pub fn parse_snmp_v3<'a>(i:&'a[u8]) -> IResult<&'a[u8], SnmpV3Message<'a>, SnmpError> {
146     upgrade_error! {
147         parse_der_struct!(
148             i,
149             TAG BerTag::Sequence,
150             vers: parse_ber_u32 >>
151             hdr:  parse_snmp_v3_headerdata >>
152             secp: map_res!(parse_ber_octetstring, |x: BerObject<'a>| parse_secp(&x,&hdr)) >>
153             data: call!(parse_snmp_v3_data,&hdr) >>
154             ({
155                 SnmpV3Message{
156                     version: vers,
157                     header_data: hdr,
158                     security_params: secp,
159                     data
160                 }
161             })
162         )
163         .map(|(rem,x)| (rem,x.1))
164     }
165 }
166 
parse_snmp_v3_headerdata(i:&[u8]) -> IResult<&[u8], HeaderData, BerError>167 pub(crate) fn parse_snmp_v3_headerdata(i:&[u8]) -> IResult<&[u8], HeaderData, BerError> {
168     parse_der_struct!(
169         i,
170         TAG BerTag::Sequence,
171         id: parse_ber_u32 >>
172         sz: parse_ber_u32 >>
173         fl: map_res!(parse_ber_octetstring, |x: BerObject| x.as_slice().and_then(|s|
174             if s.len() == 1 { Ok(s[0]) } else { Err(BerError::BerValueError) })) >>
175         sm: parse_ber_u32 >>
176         (
177             HeaderData{
178                 msg_id: id,
179                 msg_max_size: sz,
180                 msg_flags: fl,
181                 msg_security_model: SecurityModel(sm),
182             }
183         )
184     ).map(|(rem,x)| (rem,x.1))
185 }
186 
parse_snmp_v3_plaintext_pdu(i:&[u8]) -> IResult<&[u8], ScopedPduData, BerError>187 fn parse_snmp_v3_plaintext_pdu(i:&[u8]) -> IResult<&[u8], ScopedPduData, BerError> {
188     parse_der_struct!(
189         i,
190         ctx_eng_id: parse_ber_octetstring_as_slice >>
191         ctx_name:   parse_ber_octetstring_as_slice >>
192         data:       parse_snmp_v2c_pdu >>
193         (
194             ScopedPduData::Plaintext(ScopedPdu{
195                 ctx_engine_id: ctx_eng_id,
196                 ctx_engine_name: ctx_name,
197                 data
198             })
199         )
200     ).map(|(rem,x)| (rem,x.1))
201 }
202