Coverage Report

Created: 2025-07-23 07:29

/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
}