/rust/registry/src/index.crates.io-6f17d22bba15001f/snmp-parser-0.10.0/src/snmpv3.rs
Line | Count | Source (jump to first uncovered line) |
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 asn1_rs::{Error, FromBer, Sequence}; |
12 | | use nom::combinator::{map, map_res}; |
13 | | use nom::{Err, IResult}; |
14 | | use std::fmt; |
15 | | |
16 | | use crate::error::SnmpError; |
17 | | use crate::snmp::{parse_snmp_v2c_pdu, SnmpPdu}; |
18 | | pub use crate::usm::{parse_usm_security_parameters, UsmSecurityParameters}; |
19 | | |
20 | | #[derive(Clone, Copy, Eq, PartialEq)] |
21 | | pub struct SecurityModel(pub u32); |
22 | | |
23 | | #[allow(non_upper_case_globals)] |
24 | | impl SecurityModel { |
25 | | pub const SnmpV1: SecurityModel = SecurityModel(1); |
26 | | pub const SnmpV2c: SecurityModel = SecurityModel(2); |
27 | | pub const USM: SecurityModel = SecurityModel(3); |
28 | | } |
29 | | |
30 | | impl fmt::Debug for SecurityModel { |
31 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
32 | 0 | match self.0 { |
33 | 0 | 1 => f.write_str("SnmpV1"), |
34 | 0 | 2 => f.write_str("SnmpV2c"), |
35 | 0 | 3 => f.write_str("USM"), |
36 | 0 | n => f.debug_tuple("SecurityModel").field(&n).finish(), |
37 | | } |
38 | 0 | } |
39 | | } |
40 | | |
41 | | impl<'a> FromBer<'a> for SecurityModel { |
42 | 0 | fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> { |
43 | 0 | map(u32::from_ber, SecurityModel)(bytes) |
44 | 0 | } |
45 | | } |
46 | | |
47 | | #[derive(Debug, PartialEq)] |
48 | | #[allow(clippy::upper_case_acronyms)] |
49 | | pub enum SecurityParameters<'a> { |
50 | | Raw(&'a [u8]), |
51 | | USM(UsmSecurityParameters<'a>), |
52 | | } |
53 | | |
54 | | /// An SNMPv3 message |
55 | | #[derive(Debug, PartialEq)] |
56 | | pub struct SnmpV3Message<'a> { |
57 | | /// Version, as raw-encoded: 3 for SNMPv3 |
58 | | pub version: u32, |
59 | | pub header_data: HeaderData, |
60 | | pub security_params: SecurityParameters<'a>, |
61 | | pub data: ScopedPduData<'a>, |
62 | | } |
63 | | |
64 | | #[derive(Clone, Copy, Debug, PartialEq)] |
65 | | pub struct HeaderData { |
66 | | pub msg_id: u32, |
67 | | pub msg_max_size: u32, |
68 | | pub msg_flags: u8, |
69 | | pub msg_security_model: SecurityModel, |
70 | | } |
71 | | |
72 | | impl HeaderData { |
73 | 0 | pub fn is_authenticated(&self) -> bool { |
74 | 0 | self.msg_flags & 0b001 != 0 |
75 | 0 | } |
76 | | |
77 | 53.4k | pub fn is_encrypted(&self) -> bool { |
78 | 53.4k | self.msg_flags & 0b010 != 0 |
79 | 53.4k | } |
80 | | |
81 | 0 | pub fn is_reportable(&self) -> bool { |
82 | 0 | self.msg_flags & 0b100 != 0 |
83 | 0 | } |
84 | | } |
85 | | |
86 | | impl<'a> FromBer<'a> for HeaderData { |
87 | 76.0k | fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> { |
88 | 76.0k | Sequence::from_ber_and_then(bytes, |i| { |
89 | 75.5k | let (i, msg_id) = u32::from_ber(i)?; |
90 | 73.7k | let (i, msg_max_size) = u32::from_ber(i)?; |
91 | 72.3k | let (i, b) = <&[u8]>::from_ber(i)?; |
92 | 66.6k | let msg_flags = if b.len() == 1 { |
93 | 66.5k | b[0] |
94 | | } else { |
95 | 106 | return Err(Err::Error(Error::BerValueError)); |
96 | | }; |
97 | 66.5k | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; |
98 | 66.0k | let hdr = HeaderData { |
99 | 66.0k | msg_id, |
100 | 66.0k | msg_max_size, |
101 | 66.0k | msg_flags, |
102 | 66.0k | msg_security_model, |
103 | 66.0k | }; |
104 | 66.0k | Ok((i, hdr)) |
105 | 76.0k | }) |
106 | 76.0k | } |
107 | | } |
108 | | |
109 | | #[derive(Debug, PartialEq)] |
110 | | pub enum ScopedPduData<'a> { |
111 | | Plaintext(ScopedPdu<'a>), |
112 | | Encrypted(&'a [u8]), |
113 | | } |
114 | | |
115 | | #[derive(Debug, PartialEq)] |
116 | | pub struct ScopedPdu<'a> { |
117 | | pub ctx_engine_id: &'a [u8], |
118 | | pub ctx_engine_name: &'a [u8], |
119 | | /// ANY -- e.g., PDUs as defined in [RFC3416](https://tools.ietf.org/html/rfc3416) |
120 | | pub data: SnmpPdu<'a>, |
121 | | } |
122 | | |
123 | 53.4k | pub(crate) fn parse_snmp_v3_data<'a>( |
124 | 53.4k | i: &'a [u8], |
125 | 53.4k | hdr: &HeaderData, |
126 | 53.4k | ) -> IResult<&'a [u8], ScopedPduData<'a>, SnmpError> { |
127 | 53.4k | if hdr.is_encrypted() { |
128 | 2.09k | map(<&[u8]>::from_ber, ScopedPduData::Encrypted)(i).map_err(Err::convert) |
129 | | } else { |
130 | 51.3k | parse_snmp_v3_plaintext_pdu(i) |
131 | | } |
132 | 53.4k | } |
133 | | |
134 | 65.6k | pub(crate) fn parse_secp<'a>( |
135 | 65.6k | i: &'a [u8], |
136 | 65.6k | hdr: &HeaderData, |
137 | 65.6k | ) -> Result<SecurityParameters<'a>, SnmpError> { |
138 | 65.6k | match hdr.msg_security_model { |
139 | 63.2k | SecurityModel::USM => match parse_usm_security_parameters(i) { |
140 | 50.9k | Ok((_, usm)) => Ok(SecurityParameters::USM(usm)), |
141 | 12.2k | _ => Err(SnmpError::InvalidSecurityModel), |
142 | | }, |
143 | 2.41k | _ => Ok(SecurityParameters::Raw(i)), |
144 | | } |
145 | 65.6k | } |
146 | | |
147 | | /// Parse an SNMPv3 top-level message |
148 | | /// |
149 | | /// Example: |
150 | | /// |
151 | | /// ```rust |
152 | | /// use snmp_parser::{parse_snmp_v3,ScopedPduData,SecurityModel}; |
153 | | /// |
154 | | /// static SNMPV3_REQ: &[u8] = include_bytes!("../assets/snmpv3_req.bin"); |
155 | | /// |
156 | | /// # fn main() { |
157 | | /// match parse_snmp_v3(&SNMPV3_REQ) { |
158 | | /// Ok((_, ref r)) => { |
159 | | /// assert!(r.version == 3); |
160 | | /// assert!(r.header_data.msg_security_model == SecurityModel::USM); |
161 | | /// match r.data { |
162 | | /// ScopedPduData::Plaintext(ref _pdu) => { }, |
163 | | /// ScopedPduData::Encrypted(_) => (), |
164 | | /// } |
165 | | /// }, |
166 | | /// Err(e) => panic!("{}", e), |
167 | | /// } |
168 | | /// # } |
169 | | /// ``` |
170 | 0 | pub fn parse_snmp_v3(bytes: &[u8]) -> IResult<&[u8], SnmpV3Message, SnmpError> { |
171 | 0 | Sequence::from_der_and_then(bytes, |i| { |
172 | 0 | let (i, version) = u32::from_ber(i).map_err(Err::convert)?; |
173 | 0 | let (i, header_data) = parse_snmp_v3_headerdata(i)?; |
174 | 0 | let (i, secp) = |
175 | 0 | map_res(<&[u8]>::from_ber, |x| parse_secp(x, &header_data))(i).map_err(Err::convert)?; |
176 | 0 | let (i, data) = parse_snmp_v3_data(i, &header_data)?; |
177 | 0 | let msg = SnmpV3Message { |
178 | 0 | version, |
179 | 0 | header_data, |
180 | 0 | security_params: secp, |
181 | 0 | data, |
182 | 0 | }; |
183 | 0 | Ok((i, msg)) |
184 | 0 | }) |
185 | 0 | } |
186 | | |
187 | | #[inline] |
188 | 76.0k | pub(crate) fn parse_snmp_v3_headerdata(i: &[u8]) -> IResult<&[u8], HeaderData, SnmpError> { |
189 | 76.0k | HeaderData::from_ber(i).map_err(Err::convert) |
190 | 76.0k | } |
191 | | |
192 | 51.3k | fn parse_snmp_v3_plaintext_pdu(bytes: &[u8]) -> IResult<&[u8], ScopedPduData, SnmpError> { |
193 | 51.3k | Sequence::from_der_and_then(bytes, |i| { |
194 | 49.9k | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; |
195 | 47.7k | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; |
196 | 46.7k | let (i, data) = parse_snmp_v2c_pdu(i)?; |
197 | 37.0k | let pdu = ScopedPdu { |
198 | 37.0k | ctx_engine_id, |
199 | 37.0k | ctx_engine_name, |
200 | 37.0k | data, |
201 | 37.0k | }; |
202 | 37.0k | Ok((i, ScopedPduData::Plaintext(pdu))) |
203 | 51.3k | }) |
204 | 51.3k | } |