1use asn1_rs::{Error, FromBer, Sequence};
12use nom::combinator::{map, map_res};
13use nom::{Err, IResult};
14use std::fmt;
15
16use crate::error::SnmpError;
17use crate::snmp::{parse_snmp_v2c_pdu, SnmpPdu};
18pub use crate::usm::{parse_usm_security_parameters, UsmSecurityParameters};
19
20#[derive(Clone, Copy, Eq, PartialEq)]
21pub struct SecurityModel(pub u32);
22
23#[allow(non_upper_case_globals)]
24impl SecurityModel {
25 pub const SnmpV1: SecurityModel = SecurityModel(1);
26 pub const SnmpV2c: SecurityModel = SecurityModel(2);
27 pub const USM: SecurityModel = SecurityModel(3);
28}
29
30impl fmt::Debug for SecurityModel {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 match self.0 {
33 1 => f.write_str("SnmpV1"),
34 2 => f.write_str("SnmpV2c"),
35 3 => f.write_str("USM"),
36 n => f.debug_tuple("SecurityModel").field(&n).finish(),
37 }
38 }
39}
40
41impl<'a> FromBer<'a> for SecurityModel {
42 fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> {
43 map(u32::from_ber, SecurityModel)(bytes)
44 }
45}
46
47#[derive(Debug, PartialEq)]
48#[allow(clippy::upper_case_acronyms)]
49pub enum SecurityParameters<'a> {
50 Raw(&'a [u8]),
51 USM(UsmSecurityParameters<'a>),
52}
53
54#[derive(Debug, PartialEq)]
79pub struct SnmpV3Message<'a> {
80 pub version: u32,
82 pub header_data: HeaderData,
83 pub security_params: SecurityParameters<'a>,
84 pub data: ScopedPduData<'a>,
85}
86
87impl<'a> FromBer<'a, SnmpError> for SnmpV3Message<'a> {
88 fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self, SnmpError> {
89 Sequence::from_der_and_then(bytes, |i| {
90 let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
91 let (i, header_data) = parse_snmp_v3_headerdata(i)?;
92 let (i, secp) = map_res(<&[u8]>::from_ber, |x| parse_secp(x, &header_data))(i)
93 .map_err(Err::convert)?;
94 let (i, data) = parse_snmp_v3_data(i, &header_data)?;
95 let msg = SnmpV3Message {
96 version,
97 header_data,
98 security_params: secp,
99 data,
100 };
101 Ok((i, msg))
102 })
103 }
104}
105
106#[derive(Clone, Copy, Debug, PartialEq)]
107pub struct HeaderData {
108 pub msg_id: u32,
109 pub msg_max_size: u32,
110 pub msg_flags: u8,
111 pub msg_security_model: SecurityModel,
112}
113
114impl HeaderData {
115 pub fn is_authenticated(&self) -> bool {
116 self.msg_flags & 0b001 != 0
117 }
118
119 pub fn is_encrypted(&self) -> bool {
120 self.msg_flags & 0b010 != 0
121 }
122
123 pub fn is_reportable(&self) -> bool {
124 self.msg_flags & 0b100 != 0
125 }
126}
127
128impl<'a> FromBer<'a> for HeaderData {
129 fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self> {
130 Sequence::from_ber_and_then(bytes, |i| {
131 let (i, msg_id) = u32::from_ber(i)?;
132 let (i, msg_max_size) = u32::from_ber(i)?;
133 let (i, b) = <&[u8]>::from_ber(i)?;
134 let msg_flags = if b.len() == 1 {
135 b[0]
136 } else {
137 return Err(Err::Error(Error::BerValueError));
138 };
139 let (i, msg_security_model) = map(u32::from_ber, SecurityModel)(i)?;
140 let hdr = HeaderData {
141 msg_id,
142 msg_max_size,
143 msg_flags,
144 msg_security_model,
145 };
146 Ok((i, hdr))
147 })
148 }
149}
150
151#[derive(Debug, PartialEq)]
152pub enum ScopedPduData<'a> {
153 Plaintext(ScopedPdu<'a>),
154 Encrypted(&'a [u8]),
155}
156
157#[derive(Debug, PartialEq)]
158pub struct ScopedPdu<'a> {
159 pub ctx_engine_id: &'a [u8],
160 pub ctx_engine_name: &'a [u8],
161 pub data: SnmpPdu<'a>,
163}
164
165pub(crate) fn parse_snmp_v3_data<'a>(
166 i: &'a [u8],
167 hdr: &HeaderData,
168) -> IResult<&'a [u8], ScopedPduData<'a>, SnmpError> {
169 if hdr.is_encrypted() {
170 map(<&[u8]>::from_ber, ScopedPduData::Encrypted)(i).map_err(Err::convert)
171 } else {
172 parse_snmp_v3_plaintext_pdu(i)
173 }
174}
175
176pub(crate) fn parse_secp<'a>(
177 i: &'a [u8],
178 hdr: &HeaderData,
179) -> Result<SecurityParameters<'a>, SnmpError> {
180 match hdr.msg_security_model {
181 SecurityModel::USM => match parse_usm_security_parameters(i) {
182 Ok((_, usm)) => Ok(SecurityParameters::USM(usm)),
183 _ => Err(SnmpError::InvalidSecurityModel),
184 },
185 _ => Ok(SecurityParameters::Raw(i)),
186 }
187}
188
189pub fn parse_snmp_v3(bytes: &[u8]) -> IResult<&[u8], SnmpV3Message, SnmpError> {
213 SnmpV3Message::from_ber(bytes)
214}
215
216#[inline]
217pub(crate) fn parse_snmp_v3_headerdata(i: &[u8]) -> IResult<&[u8], HeaderData, SnmpError> {
218 HeaderData::from_ber(i).map_err(Err::convert)
219}
220
221fn parse_snmp_v3_plaintext_pdu(bytes: &[u8]) -> IResult<&[u8], ScopedPduData, SnmpError> {
222 Sequence::from_der_and_then(bytes, |i| {
223 let (i, ctx_engine_id) = <&[u8]>::from_ber(i).map_err(Err::convert)?;
224 let (i, ctx_engine_name) = <&[u8]>::from_ber(i).map_err(Err::convert)?;
225 let (i, data) = parse_snmp_v2c_pdu(i)?;
226 let pdu = ScopedPdu {
227 ctx_engine_id,
228 ctx_engine_name,
229 data,
230 };
231 Ok((i, ScopedPduData::Plaintext(pdu)))
232 })
233}