/rust/registry/src/index.crates.io-1949cf8c6b5b557f/snmp-parser-0.10.0/src/snmpv3.rs
Line | Count | Source |
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 | } Unexecuted instantiation: <snmp_parser::snmpv3::SecurityModel as core::fmt::Debug>::fmt Unexecuted instantiation: <snmp_parser::snmpv3::SecurityModel as core::fmt::Debug>::fmt |
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 | } Unexecuted instantiation: <snmp_parser::snmpv3::SecurityModel as asn1_rs::traits::FromBer>::from_ber Unexecuted instantiation: <snmp_parser::snmpv3::SecurityModel as asn1_rs::traits::FromBer>::from_ber |
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 | } Unexecuted instantiation: <snmp_parser::snmpv3::HeaderData>::is_authenticated Unexecuted instantiation: <snmp_parser::snmpv3::HeaderData>::is_authenticated |
76 | | |
77 | 91.8k | pub fn is_encrypted(&self) -> bool { |
78 | 91.8k | self.msg_flags & 0b010 != 0 |
79 | 91.8k | } <snmp_parser::snmpv3::HeaderData>::is_encrypted Line | Count | Source | 77 | 9.62k | pub fn is_encrypted(&self) -> bool { | 78 | 9.62k | self.msg_flags & 0b010 != 0 | 79 | 9.62k | } |
<snmp_parser::snmpv3::HeaderData>::is_encrypted Line | Count | Source | 77 | 82.2k | pub fn is_encrypted(&self) -> bool { | 78 | 82.2k | self.msg_flags & 0b010 != 0 | 79 | 82.2k | } |
|
80 | | |
81 | 0 | pub fn is_reportable(&self) -> bool { |
82 | 0 | self.msg_flags & 0b100 != 0 |
83 | 0 | } Unexecuted instantiation: <snmp_parser::snmpv3::HeaderData>::is_reportable Unexecuted instantiation: <snmp_parser::snmpv3::HeaderData>::is_reportable |
84 | | } |
85 | | |
86 | | impl<'a> FromBer<'a> for HeaderData { |
87 | 113k | fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> { |
88 | 113k | Sequence::from_ber_and_then(bytes, |i| { |
89 | 112k | let (i, msg_id) = u32::from_ber(i)?; |
90 | 110k | let (i, msg_max_size) = u32::from_ber(i)?; |
91 | 109k | let (i, b) = <&[u8]>::from_ber(i)?; |
92 | 104k | let msg_flags = if b.len() == 1 { |
93 | 104k | b[0] |
94 | | } else { |
95 | 82 | return Err(Err::Error(Error::BerValueError)); |
96 | | }; |
97 | 104k | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; |
98 | 103k | let hdr = HeaderData { |
99 | 103k | msg_id, |
100 | 103k | msg_max_size, |
101 | 103k | msg_flags, |
102 | 103k | msg_security_model, |
103 | 103k | }; |
104 | 103k | Ok((i, hdr)) |
105 | 112k | }) <snmp_parser::snmpv3::HeaderData as asn1_rs::traits::FromBer>::from_ber::{closure#0}Line | Count | Source | 88 | 9.73k | Sequence::from_ber_and_then(bytes, |i| { | 89 | 9.73k | let (i, msg_id) = u32::from_ber(i)?; | 90 | 9.72k | let (i, msg_max_size) = u32::from_ber(i)?; | 91 | 9.71k | let (i, b) = <&[u8]>::from_ber(i)?; | 92 | 9.70k | let msg_flags = if b.len() == 1 { | 93 | 9.70k | b[0] | 94 | | } else { | 95 | 1 | return Err(Err::Error(Error::BerValueError)); | 96 | | }; | 97 | 9.70k | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; | 98 | 9.69k | let hdr = HeaderData { | 99 | 9.69k | msg_id, | 100 | 9.69k | msg_max_size, | 101 | 9.69k | msg_flags, | 102 | 9.69k | msg_security_model, | 103 | 9.69k | }; | 104 | 9.69k | Ok((i, hdr)) | 105 | 9.73k | }) |
<snmp_parser::snmpv3::HeaderData as asn1_rs::traits::FromBer>::from_ber::{closure#0}Line | Count | Source | 88 | 102k | Sequence::from_ber_and_then(bytes, |i| { | 89 | 102k | let (i, msg_id) = u32::from_ber(i)?; | 90 | 101k | let (i, msg_max_size) = u32::from_ber(i)?; | 91 | 99.6k | let (i, b) = <&[u8]>::from_ber(i)?; | 92 | 94.5k | let msg_flags = if b.len() == 1 { | 93 | 94.4k | b[0] | 94 | | } else { | 95 | 81 | return Err(Err::Error(Error::BerValueError)); | 96 | | }; | 97 | 94.4k | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; | 98 | 93.9k | let hdr = HeaderData { | 99 | 93.9k | msg_id, | 100 | 93.9k | msg_max_size, | 101 | 93.9k | msg_flags, | 102 | 93.9k | msg_security_model, | 103 | 93.9k | }; | 104 | 93.9k | Ok((i, hdr)) | 105 | 102k | }) |
|
106 | 113k | } <snmp_parser::snmpv3::HeaderData as asn1_rs::traits::FromBer>::from_ber Line | Count | Source | 87 | 9.76k | fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> { | 88 | 9.76k | Sequence::from_ber_and_then(bytes, |i| { | 89 | | let (i, msg_id) = u32::from_ber(i)?; | 90 | | let (i, msg_max_size) = u32::from_ber(i)?; | 91 | | let (i, b) = <&[u8]>::from_ber(i)?; | 92 | | let msg_flags = if b.len() == 1 { | 93 | | b[0] | 94 | | } else { | 95 | | return Err(Err::Error(Error::BerValueError)); | 96 | | }; | 97 | | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; | 98 | | let hdr = HeaderData { | 99 | | msg_id, | 100 | | msg_max_size, | 101 | | msg_flags, | 102 | | msg_security_model, | 103 | | }; | 104 | | Ok((i, hdr)) | 105 | | }) | 106 | 9.76k | } |
<snmp_parser::snmpv3::HeaderData as asn1_rs::traits::FromBer>::from_ber Line | Count | Source | 87 | 103k | fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> { | 88 | 103k | Sequence::from_ber_and_then(bytes, |i| { | 89 | | let (i, msg_id) = u32::from_ber(i)?; | 90 | | let (i, msg_max_size) = u32::from_ber(i)?; | 91 | | let (i, b) = <&[u8]>::from_ber(i)?; | 92 | | let msg_flags = if b.len() == 1 { | 93 | | b[0] | 94 | | } else { | 95 | | return Err(Err::Error(Error::BerValueError)); | 96 | | }; | 97 | | let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?; | 98 | | let hdr = HeaderData { | 99 | | msg_id, | 100 | | msg_max_size, | 101 | | msg_flags, | 102 | | msg_security_model, | 103 | | }; | 104 | | Ok((i, hdr)) | 105 | | }) | 106 | 103k | } |
|
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 | 91.8k | pub(crate) fn parse_snmp_v3_data<'a>( |
124 | 91.8k | i: &'a [u8], |
125 | 91.8k | hdr: &HeaderData, |
126 | 91.8k | ) -> IResult<&'a [u8], ScopedPduData<'a>, SnmpError> { |
127 | 91.8k | if hdr.is_encrypted() { |
128 | 43.7k | map(<&[u8]>::from_ber, ScopedPduData::Encrypted)(i).map_err(Err::convert) |
129 | | } else { |
130 | 48.0k | parse_snmp_v3_plaintext_pdu(i) |
131 | | } |
132 | 91.8k | } snmp_parser::snmpv3::parse_snmp_v3_data Line | Count | Source | 123 | 9.62k | pub(crate) fn parse_snmp_v3_data<'a>( | 124 | 9.62k | i: &'a [u8], | 125 | 9.62k | hdr: &HeaderData, | 126 | 9.62k | ) -> IResult<&'a [u8], ScopedPduData<'a>, SnmpError> { | 127 | 9.62k | if hdr.is_encrypted() { | 128 | 9.50k | map(<&[u8]>::from_ber, ScopedPduData::Encrypted)(i).map_err(Err::convert) | 129 | | } else { | 130 | 120 | parse_snmp_v3_plaintext_pdu(i) | 131 | | } | 132 | 9.62k | } |
snmp_parser::snmpv3::parse_snmp_v3_data Line | Count | Source | 123 | 82.2k | pub(crate) fn parse_snmp_v3_data<'a>( | 124 | 82.2k | i: &'a [u8], | 125 | 82.2k | hdr: &HeaderData, | 126 | 82.2k | ) -> IResult<&'a [u8], ScopedPduData<'a>, SnmpError> { | 127 | 82.2k | if hdr.is_encrypted() { | 128 | 34.2k | map(<&[u8]>::from_ber, ScopedPduData::Encrypted)(i).map_err(Err::convert) | 129 | | } else { | 130 | 47.9k | parse_snmp_v3_plaintext_pdu(i) | 131 | | } | 132 | 82.2k | } |
|
133 | | |
134 | 103k | pub(crate) fn parse_secp<'a>( |
135 | 103k | i: &'a [u8], |
136 | 103k | hdr: &HeaderData, |
137 | 103k | ) -> Result<SecurityParameters<'a>, SnmpError> { |
138 | 103k | match hdr.msg_security_model { |
139 | 59.0k | SecurityModel::USM => match parse_usm_security_parameters(i) { |
140 | 47.5k | Ok((_, usm)) => Ok(SecurityParameters::USM(usm)), |
141 | 11.4k | _ => Err(SnmpError::InvalidSecurityModel), |
142 | | }, |
143 | 44.2k | _ => Ok(SecurityParameters::Raw(i)), |
144 | | } |
145 | 103k | } snmp_parser::snmpv3::parse_secp Line | Count | Source | 134 | 9.64k | pub(crate) fn parse_secp<'a>( | 135 | 9.64k | i: &'a [u8], | 136 | 9.64k | hdr: &HeaderData, | 137 | 9.64k | ) -> Result<SecurityParameters<'a>, SnmpError> { | 138 | 9.64k | match hdr.msg_security_model { | 139 | 32 | SecurityModel::USM => match parse_usm_security_parameters(i) { | 140 | 3 | Ok((_, usm)) => Ok(SecurityParameters::USM(usm)), | 141 | 29 | _ => Err(SnmpError::InvalidSecurityModel), | 142 | | }, | 143 | 9.61k | _ => Ok(SecurityParameters::Raw(i)), | 144 | | } | 145 | 9.64k | } |
snmp_parser::snmpv3::parse_secp Line | Count | Source | 134 | 93.6k | pub(crate) fn parse_secp<'a>( | 135 | 93.6k | i: &'a [u8], | 136 | 93.6k | hdr: &HeaderData, | 137 | 93.6k | ) -> Result<SecurityParameters<'a>, SnmpError> { | 138 | 93.6k | match hdr.msg_security_model { | 139 | 58.9k | SecurityModel::USM => match parse_usm_security_parameters(i) { | 140 | 47.5k | Ok((_, usm)) => Ok(SecurityParameters::USM(usm)), | 141 | 11.3k | _ => Err(SnmpError::InvalidSecurityModel), | 142 | | }, | 143 | 34.6k | _ => Ok(SecurityParameters::Raw(i)), | 144 | | } | 145 | 93.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)?; Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3::{closure#0}::{closure#0}Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3::{closure#0}::{closure#0} |
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 | }) Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3::{closure#0}Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3::{closure#0} |
185 | 0 | } Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3 Unexecuted instantiation: snmp_parser::snmpv3::parse_snmp_v3 |
186 | | |
187 | | #[inline] |
188 | 113k | pub(crate) fn parse_snmp_v3_headerdata(i: &[u8]) -> IResult<&[u8], HeaderData, SnmpError> { |
189 | 113k | HeaderData::from_ber(i).map_err(Err::convert) |
190 | 113k | } snmp_parser::snmpv3::parse_snmp_v3_headerdata Line | Count | Source | 188 | 9.76k | pub(crate) fn parse_snmp_v3_headerdata(i: &[u8]) -> IResult<&[u8], HeaderData, SnmpError> { | 189 | 9.76k | HeaderData::from_ber(i).map_err(Err::convert) | 190 | 9.76k | } |
snmp_parser::snmpv3::parse_snmp_v3_headerdata Line | Count | Source | 188 | 103k | pub(crate) fn parse_snmp_v3_headerdata(i: &[u8]) -> IResult<&[u8], HeaderData, SnmpError> { | 189 | 103k | HeaderData::from_ber(i).map_err(Err::convert) | 190 | 103k | } |
|
191 | | |
192 | 48.0k | fn parse_snmp_v3_plaintext_pdu(bytes: &[u8]) -> IResult<&[u8], ScopedPduData, SnmpError> { |
193 | 48.0k | Sequence::from_der_and_then(bytes, |i| { |
194 | 46.9k | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; |
195 | 44.6k | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; |
196 | 43.8k | let (i, data) = parse_snmp_v2c_pdu(i)?; |
197 | 34.5k | let pdu = ScopedPdu { |
198 | 34.5k | ctx_engine_id, |
199 | 34.5k | ctx_engine_name, |
200 | 34.5k | data, |
201 | 34.5k | }; |
202 | 34.5k | Ok((i, ScopedPduData::Plaintext(pdu))) |
203 | 46.9k | }) snmp_parser::snmpv3::parse_snmp_v3_plaintext_pdu::{closure#0}Line | Count | Source | 193 | 12 | Sequence::from_der_and_then(bytes, |i| { | 194 | 12 | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 195 | 7 | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 196 | 1 | let (i, data) = parse_snmp_v2c_pdu(i)?; | 197 | 0 | let pdu = ScopedPdu { | 198 | 0 | ctx_engine_id, | 199 | 0 | ctx_engine_name, | 200 | 0 | data, | 201 | 0 | }; | 202 | 0 | Ok((i, ScopedPduData::Plaintext(pdu))) | 203 | 12 | }) |
snmp_parser::snmpv3::parse_snmp_v3_plaintext_pdu::{closure#0}Line | Count | Source | 193 | 46.9k | Sequence::from_der_and_then(bytes, |i| { | 194 | 46.9k | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 195 | 44.6k | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 196 | 43.8k | let (i, data) = parse_snmp_v2c_pdu(i)?; | 197 | 34.5k | let pdu = ScopedPdu { | 198 | 34.5k | ctx_engine_id, | 199 | 34.5k | ctx_engine_name, | 200 | 34.5k | data, | 201 | 34.5k | }; | 202 | 34.5k | Ok((i, ScopedPduData::Plaintext(pdu))) | 203 | 46.9k | }) |
|
204 | 48.0k | } snmp_parser::snmpv3::parse_snmp_v3_plaintext_pdu Line | Count | Source | 192 | 120 | fn parse_snmp_v3_plaintext_pdu(bytes: &[u8]) -> IResult<&[u8], ScopedPduData, SnmpError> { | 193 | 120 | Sequence::from_der_and_then(bytes, |i| { | 194 | | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 195 | | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 196 | | let (i, data) = parse_snmp_v2c_pdu(i)?; | 197 | | let pdu = ScopedPdu { | 198 | | ctx_engine_id, | 199 | | ctx_engine_name, | 200 | | data, | 201 | | }; | 202 | | Ok((i, ScopedPduData::Plaintext(pdu))) | 203 | | }) | 204 | 120 | } |
snmp_parser::snmpv3::parse_snmp_v3_plaintext_pdu Line | Count | Source | 192 | 47.9k | fn parse_snmp_v3_plaintext_pdu(bytes: &[u8]) -> IResult<&[u8], ScopedPduData, SnmpError> { | 193 | 47.9k | Sequence::from_der_and_then(bytes, |i| { | 194 | | let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 195 | | let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?; | 196 | | let (i, data) = parse_snmp_v2c_pdu(i)?; | 197 | | let pdu = ScopedPdu { | 198 | | ctx_engine_id, | 199 | | ctx_engine_name, | 200 | | data, | 201 | | }; | 202 | | Ok((i, ScopedPduData::Plaintext(pdu))) | 203 | | }) | 204 | 47.9k | } |
|