use crate::keeper;
use crate::keeper::OidErr;
use crate::notifier;
use crate::oidmap::{ContextMap, OidMap};
use crate::perms::FlagPerm;
use crate::privacy;
use crate::usm;
use log::{debug, error, warn};
use rasn;
use rasn::types::{Integer, ObjectIdentifier, OctetString};
use rasn_smi::v2::{ObjectSyntax, SimpleSyntax};
use rasn_snmp::v2::{Pdu, Report, VarBind};
use rasn_snmp::v3::VarBindValue;
use rasn_snmp::v3::{GetBulkRequest, GetNextRequest, GetRequest, SetRequest};
use rasn_snmp::v3::{HeaderData, Message, Pdus, ScopedPdu, USMSecurityParameters};
use rasn_snmp::v3::{Response, ScopedPduData};
use std::collections::HashSet;
use std::fmt::Display;
use std::fs::{read_to_string, write};
use std::net::{SocketAddr, UdpSocket};
use std::str::FromStr;
use std::sync::mpsc::Sender;
use std::thread::sleep;
use std::time::{Duration, Instant};
const BOOT_CNT_FILENAME: &str = "boot-cnt.txt";
const Z12: OctetString = OctetString::from_static(&[0u8; 12]);
const Z16: OctetString = OctetString::from_static(&[0u8; 16]);
const Z24: OctetString = OctetString::from_static(&[0u8; 24]);
const Z32: OctetString = OctetString::from_static(&[0u8; 32]);
const Z48: OctetString = OctetString::from_static(&[0u8; 48]);
const ZB: OctetString = OctetString::from_static(b"");
const ARC_COLD_START: [u32; 10] = [1, 3, 6, 1, 6, 3, 1, 1, 5, 1];
const ARC_AUTHENTICATION_FAILURE: [u32; 10] = [1, 3, 6, 1, 6, 3, 1, 1, 5, 5];
fn get_increment_boot_cnt() -> isize {
let mut boots: isize = 0;
let mut retry_counter = 0;
loop {
let cnt_res: Result<String, std::io::Error> = read_to_string(BOOT_CNT_FILENAME);
if let Ok(string) = cnt_res {
if let Ok(parse) = isize::from_str(string.trim()) {
boots = parse;
break;
}
} else {
break;
}
retry_counter += 1;
sleep(Duration::new(0, 10000)); if retry_counter > 10 {
panic!("Boot count not parsed after 10 retries - probably corrupted file or hardware failure.");
}
}
boots += 1;
write(BOOT_CNT_FILENAME, boots.to_string().as_bytes()).unwrap(); boots
}
struct PduArg {
error_index: u32,
error_status: u32,
vb_cnt: u32,
}
pub struct Agent {
socket: UdpSocket,
engine_id: OctetString,
pub start_time: Instant,
boots: isize,
pub in_pkts: u64,
pub unknown_users: u32,
pub wrong_digests: u32,
pub not_in_time_window: u32,
pub unknown_engine_ids: u32,
pub decode_error_cnt: u32,
pub decryption_errors: u32,
pub unknown_contexts: u32,
notifier: Option<Sender<notifier::Notification>>,
send_auth_fails: bool,
}
impl Agent {
pub fn build(eid: OctetString, addr_str: &str, send_auth_fails: bool) -> Self {
let sock = UdpSocket::bind(addr_str).expect("Couldn't bind to address");
Agent {
socket: sock,
engine_id: eid,
start_time: Instant::now(),
boots: get_increment_boot_cnt(),
in_pkts: 0u64,
unknown_users: 0u32,
wrong_digests: 0u32,
not_in_time_window: 0u32,
unknown_engine_ids: 0u32,
decode_error_cnt: 0u32,
decryption_errors: 0u32,
unknown_contexts: 0u32,
notifier: None,
send_auth_fails,
}
}
pub fn start_notifier(&mut self, sink: &str, community: &str) {
let not = notifier::Notification {
name: ObjectIdentifier::new(&ARC_COLD_START).unwrap(), vb: vec![],
};
let notifier =
notifier::Notifier::start(sink, community, self.engine_id.clone(), self.start_time);
notifier.send(not).expect("Send failure"); self.notifier = Some(notifier);
}
fn id_response(&self, request_id: i32, message_id: Integer) -> Message {
let vb: Vec<VarBind> = vec![VarBind {
name: ObjectIdentifier::new_unchecked(vec![1, 3, 6, 1, 6, 3, 15, 1, 1, 4].into()),
value: VarBindValue::Unspecified,
}];
self.report(request_id, message_id, vb)
}
fn unknown_user(&self, request_id: i32, message_id: Integer) -> Message {
let vb: Vec<VarBind> = vec![VarBind {
name: ObjectIdentifier::new_unchecked(vec![1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0].into()),
value: VarBindValue::Value(ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
1,
)))),
}];
self.report(request_id, message_id, vb)
}
fn auth_failure(&self, request_id: i32, message_id: Integer) -> Message {
let vb: Vec<VarBind> = vec![VarBind {
name: ObjectIdentifier::new_unchecked(vec![1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0].into()),
value: VarBindValue::Value(ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
1,
)))),
}];
self.report(request_id, message_id, vb)
}
fn report(&self, request_id: i32, message_id: Integer, vb: Vec<VarBind>) -> Message {
let pdu = Pdu {
request_id,
error_index: 0,
error_status: 0,
variable_bindings: vb,
};
let report: Report = Report(pdu);
let head = HeaderData {
flags: OctetString::from_static(b"\x00"),
message_id,
max_size: Integer::from(65000),
security_model: Integer::from(3),
};
let scpd: ScopedPdu = ScopedPdu {
engine_id: self.engine_id.clone(),
name: OctetString::from_static(b""),
data: Pdus::Report(report),
};
let spd: ScopedPduData = ScopedPduData::CleartextPdu(scpd);
let run_time: isize = self
.start_time
.elapsed()
.as_secs()
.try_into()
.unwrap_or(isize::MAX);
let usm: USMSecurityParameters = USMSecurityParameters {
authoritative_engine_boots: Integer::from(self.boots),
authoritative_engine_id: self.engine_id.clone(),
authoritative_engine_time: Integer::from(run_time),
user_name: ZB,
authentication_parameters: ZB,
privacy_parameters: ZB,
};
let mut message: Message = Message {
version: Integer::from(3),
global_data: head,
scoped_data: spd,
security_parameters: ZB,
};
_ = message.encode_security_parameters(rasn::Codec::Ber, &usm);
message
}
fn prepare_back(
&self,
message_id: Integer,
resp: Response,
user: &usm::User,
usp: USMSecurityParameters,
encrypted: bool,
) -> Message {
let head = HeaderData {
flags: OctetString::from_static(b"\x00"),
message_id,
max_size: Integer::from(65000),
security_model: Integer::from(3),
};
let scpd: ScopedPdu = ScopedPdu {
engine_id: self.engine_id.clone(),
name: ZB,
data: Pdus::Response(resp),
};
let user_name = OctetString::from_slice(&user.name);
let mut spd: ScopedPduData = ScopedPduData::CleartextPdu(scpd);
let run_time: isize = self
.start_time
.elapsed()
.as_secs()
.try_into()
.unwrap_or(isize::MAX);
let mut usm: USMSecurityParameters = USMSecurityParameters {
authoritative_engine_boots: Integer::from(self.boots),
authoritative_engine_id: self.engine_id.clone(),
authoritative_engine_time: Integer::from(run_time),
user_name,
authentication_parameters: ZB,
privacy_parameters: ZB,
};
if encrypted {
usm.privacy_parameters = usp.privacy_parameters.clone();
let key = &user.priv_key.borrow();
let enc_octs = rasn::ber::encode(&spd).unwrap(); let value: Vec<u8> = privacy::encrypt(&mut enc_octs.to_vec(), usp, key);
spd = ScopedPduData::EncryptedPdu(OctetString::from(value));
}
let mut output: Message = Message {
version: Integer::from(3),
global_data: head,
scoped_data: spd,
security_parameters: ZB,
};
_ = output.encode_security_parameters(rasn::Codec::Ber, &usm); output
}
fn get(
&self,
oid_map: &mut OidMap,
r: GetRequest,
vb: &mut Vec<VarBind>,
perm: &FlagPerm,
) -> (u32, u32, i32) {
let mut error_status = Pdu::ERROR_STATUS_NO_ERROR;
let mut error_index = 0;
let request_id = r.0.request_id;
let mut vb_cnt = 0;
for vbind in r.0.variable_bindings {
let roid = vbind.name.clone();
if !perm.check(false, &roid) {
error_status = Pdu::ERROR_STATUS_NO_ACCESS;
error_index = vb_cnt;
return (error_status, error_index, request_id);
}
let opt_get: Result<usize, usize> = oid_map.search(&roid);
match opt_get {
Err(insert_point) => {
debug!("Get miss case {insert_point}");
error_status = Pdu::ERROR_STATUS_NO_SUCH_NAME;
vb.push(VarBind {
name: roid,
value: VarBindValue::NoSuchObject,
});
error_index = vb_cnt;
break;
}
Ok(which) => {
vb_cnt += 1;
let okeep = &mut oid_map.idx(which);
let value_res = okeep.get(roid.clone());
if let Ok(value) = value_res {
vb.push(VarBind {
name: roid.clone(),
value,
});
} else {
error_status = Pdu::ERROR_STATUS_NO_SUCH_NAME;
vb.push(VarBind {
name: roid,
value: VarBindValue::NoSuchInstance,
});
error_index = vb_cnt;
break;
}
}
}
}
(error_status, error_index, request_id)
}
fn do_next(
&self,
roid: ObjectIdentifier,
oid_map: &mut OidMap,
vb: &mut Vec<VarBind>,
parg: &mut PduArg,
perm: &FlagPerm,
) {
let opt_get: Result<usize, usize> = oid_map.search(&roid);
let mut oid1 = roid.clone();
debug!(
"Perm group is {}",
String::from_utf8(perm.perm.group_name.clone()).expect("nasty")
);
let mut cnt;
match opt_get {
Err(insert_point) => {
if insert_point >= oid_map.len() {
debug!("miss case {insert_point} >= oid_map.len()");
vb.push(VarBind {
name: roid.clone(),
value: VarBindValue::EndOfMibView,
});
return;
}
cnt = insert_point;
}
Ok(which) => {
if oid_map.idx(which).is_scalar(roid.clone()) {
cnt = which + 1;
oid1 = oid_map.oid(cnt).clone();
} else {
cnt = which;
}
}
}
debug!("Entering loop oid is {oid1:?}");
loop {
if perm.check(false, &oid1) {
let keep = oid_map.idx(cnt);
let acc = if keep.is_scalar(oid1.clone()) {
keep.access(oid1.clone())
} else {
keeper::Access::ReadOnly
};
if (acc == keeper::Access::ReadOnly
|| acc == keeper::Access::ReadWrite
|| acc == keeper::Access::ReadCreate)
&& !keep.is_empty()
{
if keep.is_scalar(oid1.clone()) {
let value_res = keep.get(oid1.clone());
match value_res {
Ok(value) => {
vb.push(VarBind {
name: oid_map.oid(cnt).clone(),
value,
});
return;
}
Err(_err) => {
parg.error_index = parg.vb_cnt;
parg.error_status = Pdu::ERROR_STATUS_GEN_ERR;
vb.push(VarBind {
name: oid1.clone(),
value: VarBindValue::Unspecified,
});
return;
}
};
} else {
match keep.get_next(oid1.clone()) {
Ok(bind) => {
vb.push(bind);
return;
}
Err(_) => {
}
}
}
}
}
cnt += 1;
if cnt >= oid_map.len() {
vb.push(VarBind {
name: oid1.clone(),
value: VarBindValue::EndOfMibView,
});
return;
}
oid1 = oid_map.oid(cnt).clone();
debug!("After increment {} {:?}", cnt, oid1);
}
}
fn getnext(
&self,
oid_map: &mut OidMap,
r: GetNextRequest,
vb: &mut Vec<VarBind>,
perm: &FlagPerm,
) -> (u32, u32, i32) {
let error_status = Pdu::ERROR_STATUS_NO_ERROR;
let error_index = 0u32;
let request_id = r.0.request_id;
for (vb_cnt, vbind) in r.0.variable_bindings.iter().enumerate() {
let roid = vbind.name.clone();
let mut parg = PduArg {
error_index,
error_status,
vb_cnt: vb_cnt.try_into().unwrap(), };
self.do_next(roid, oid_map, vb, &mut parg, perm);
if parg.error_status != Pdu::ERROR_STATUS_NO_ERROR {
return (parg.error_status, parg.error_index, request_id);
}
}
(error_status, error_index, request_id)
}
fn set(
&self,
oid_map: &mut OidMap,
r: SetRequest,
vb: &mut Vec<VarBind>,
perm: &FlagPerm,
user: &usm::User,
) -> (u32, u32, i32) {
let mut keeps = HashSet::<usize>::new();
let mut error_status = Pdu::ERROR_STATUS_NO_ERROR;
let mut error_index = 0;
let request_id = r.0.request_id;
let mut vb_cnt = 0;
for vbind in &r.0.variable_bindings {
let roid = vbind.name.clone();
let opt_set: Result<usize, usize> = oid_map.search(&roid);
match opt_set {
Err(_) => debug!("Miss gathering handlers"),
Ok(indx) => {
keeps.insert(indx);
}
}
}
for indx in &keeps {
let okeep = oid_map.idx(*indx);
let _ = okeep.begin_transaction();
}
for vbind in r.0.variable_bindings {
let roid = vbind.name.clone();
if !perm.check(true, &roid) {
error_status = Pdu::ERROR_STATUS_NO_ACCESS;
error_index = vb_cnt;
break;
}
let opt_set: Result<usize, usize> = oid_map.search(&roid);
match opt_set {
Err(insert_point) => {
debug!("Set miss case {insert_point}");
let okeep = &mut oid_map.idx(insert_point - 1);
if okeep.is_scalar(roid.clone()) {
debug!("Scalar set miss"); error_status = Pdu::ERROR_STATUS_NO_SUCH_NAME;
error_index = vb_cnt;
vb.push(VarBind {
name: roid,
value: VarBindValue::NoSuchObject,
});
break;
} else {
debug!("Table set ");
let set_res = okeep.set(roid.clone(), vbind.value, user);
debug!("Table set {set_res:?}");
match set_res {
Ok(res) => vb.push(VarBind {
name: roid.clone(),
value: res,
}),
Err(_) => {
error_status = Pdu::ERROR_STATUS_NO_SUCH_NAME;
vb.push(VarBind {
name: roid,
value: VarBindValue::NoSuchObject,
});
}
}
}
}
Ok(which) => {
vb_cnt += 1;
let okeep = &mut oid_map.idx(which);
let set_result = (**okeep).set(roid.clone(), vbind.value.clone(), user);
if let Err(OidErr::WrongType) = set_result {
error_status = Pdu::ERROR_STATUS_WRONG_TYPE;
vb.push(VarBind {
name: roid.clone(),
value: vbind.value,
});
} else {
let svalue = set_result.unwrap();
vb.push(VarBind {
name: roid.clone(),
value: svalue,
});
}
}
}
}
for indx in &keeps {
let keep = oid_map.idx(*indx);
if error_status == Pdu::ERROR_STATUS_NO_ERROR {
let cres = keep.commit(user);
if cres.is_err() {
error_status = Pdu::ERROR_STATUS_COMMIT_FAILED;
break;
}
} else {
let rres = keep.rollback();
if rres.is_err() {
error_status = Pdu::ERROR_STATUS_UNDO_FAILED;
}
}
}
(error_status, error_index, request_id)
}
fn bulk(
&self,
oid_map: &mut OidMap,
r: GetBulkRequest,
vb: &mut Vec<VarBind>,
perm: &FlagPerm,
) -> (u32, u32, i32) {
let error_status = Pdu::ERROR_STATUS_NO_ERROR;
let error_index = 0;
let mut vb_cnt = 0u32;
let request_id = r.0.request_id;
let non_repeaters: usize = r.0.non_repeaters.try_into().unwrap(); let max_repeats = r.0.max_repetitions;
let mut rep_oids: Vec<ObjectIdentifier> = vec![];
for (n, vbind) in r.0.variable_bindings.iter().enumerate() {
if n < non_repeaters {
let roid = vbind.name.clone();
let mut parg = PduArg {
error_index,
error_status,
vb_cnt,
}; self.do_next(roid, oid_map, vb, &mut parg, perm);
if parg.error_status != Pdu::ERROR_STATUS_NO_ERROR {
return (parg.error_status, parg.error_index, request_id);
}
vb_cnt += 1;
} else {
debug!("Repeat {vbind:?}");
rep_oids.push(vbind.name.clone());
}
}
for i in 0..max_repeats {
let mut new_oids: Vec<ObjectIdentifier> = vec![];
let mut parg = PduArg {
error_index,
error_status,
vb_cnt,
}; for roid in &rep_oids {
self.do_next(roid.clone(), oid_map, vb, &mut parg, perm);
if parg.error_status != Pdu::ERROR_STATUS_NO_ERROR {
return (parg.error_status, parg.error_index, request_id);
}
let last = vb.last().unwrap(); new_oids.push(last.name.clone());
vb_cnt += 1;
}
debug!("{i}th the repetition");
for (n, oid) in new_oids.iter().enumerate() {
rep_oids[n] = oid.clone();
}
}
(error_status, error_index, request_id)
}
fn do_scoped_pdu(
&self,
flags: u8,
user: &usm::User,
scoped_pdu: &ScopedPdu,
oid_map: &mut OidMap,
) -> Option<Response> {
let mut skip_pdu = false;
let mut vb: Vec<VarBind> = Vec::new();
let mut error_status = Pdu::ERROR_STATUS_NO_ERROR;
let mut error_index = 0;
let mut request_id = 0;
let context_name = &scoped_pdu.name;
let fperm = FlagPerm::new(flags, context_name, &user.perm);
match &scoped_pdu.data {
Pdus::GetRequest(r) => {
(error_status, error_index, request_id) =
self.get(oid_map, r.clone(), &mut vb, &fperm);
}
Pdus::GetNextRequest(r) => {
(error_status, error_index, request_id) =
self.getnext(oid_map, r.clone(), &mut vb, &fperm);
}
Pdus::SetRequest(r) => {
(error_status, error_index, request_id) =
self.set(oid_map, r.clone(), &mut vb, &fperm, user);
}
Pdus::GetBulkRequest(r) => {
(error_status, error_index, request_id) =
self.bulk(oid_map, r.clone(), &mut vb, &fperm);
}
_ => skip_pdu = true,
}
if skip_pdu {
warn!["skip_pdu is true"];
None
} else {
let pdu = Pdu {
request_id,
error_index,
error_status,
variable_bindings: vb,
};
Some(Response(pdu))
}
}
fn send(&self, addr: SocketAddr, message: Message) {
let buf_res = rasn::ber::encode(&message);
match buf_res {
Ok(buf) => {
let _ = self.socket.send_to(&buf, addr);
}
Err(err) => error!("encodeError on returned Message{err:?}, dropping packet"),
}
}
fn process_message<'a>(
&mut self,
context_map: &'a mut ContextMap,
users: &'a usm::Users,
message: &mut Message,
) -> (Option<Message>, Option<&'a usm::User>) {
let resp_opt: Option<Response>;
let mut out_message: Message;
let opt_user: Option<&usm::User>;
let message_id = message.global_data.message_id.to_owned();
if message.global_data.flags.is_empty() {
return (None, None);
}
let flags: u8 = *message.global_data.flags.first().unwrap();
let r_sp: Result<USMSecurityParameters, Box<dyn Display>> =
message.decode_security_parameters(rasn::Codec::Ber);
if r_sp.is_err() {
self.decode_error_cnt += 1;
return (None, None);
}
let usp: USMSecurityParameters = r_sp.ok().expect("Errors caught above");
if !usp.user_name.is_empty() {
opt_user = users.lookup_user(usp.user_name.to_vec());
if opt_user.is_none() {
self.unknown_users += 1;
if self.send_auth_fails {
if let Some(sender) = &self.notifier {
let auth_fail = notifier::Notification {
name: ObjectIdentifier::new(&ARC_AUTHENTICATION_FAILURE).unwrap(), vb: vec![],
};
let _ = sender.send(auth_fail);
}
}
if let ScopedPduData::CleartextPdu(scp) = &message.scoped_data {
let request_id = match &scp.data {
Pdus::GetRequest(r) => r.0.request_id,
Pdus::GetNextRequest(r) => r.0.request_id,
Pdus::SetRequest(r) => r.0.request_id,
Pdus::GetBulkRequest(r) => r.0.request_id,
_ => 1,
};
return (Some(self.unknown_user(request_id, message_id)), None);
}
return (None, None);
}
} else {
if let ScopedPduData::CleartextPdu(ref scoped_pdu) = message.scoped_data {
if scoped_pdu.engine_id.to_vec() == b"" {
if let Pdus::GetRequest(r) = &scoped_pdu.data {
let request_id = r.0.request_id;
self.unknown_engine_ids += 1;
return (Some(self.id_response(request_id, message_id)), None);
}
}
}
return (None, None);
}
let user = opt_user.unwrap(); if flags & 1 == 1 && self.wrong_auth(message, user, usp.clone()) {
warn!("Wrong auth, dropping");
if self.send_auth_fails {
if let Some(sender) = &self.notifier {
let auth_fail = notifier::Notification {
name: ObjectIdentifier::new(&ARC_AUTHENTICATION_FAILURE).unwrap(), vb: vec![],
};
let _ = sender.send(auth_fail);
}
}
if let ScopedPduData::CleartextPdu(scp) = &message.scoped_data {
let request_id = match &scp.data {
Pdus::GetRequest(r) => r.0.request_id,
Pdus::GetNextRequest(r) => r.0.request_id,
Pdus::SetRequest(r) => r.0.request_id,
Pdus::GetBulkRequest(r) => r.0.request_id,
_ => 1,
};
return (Some(self.auth_failure(request_id, message_id)), None);
}
return (None, None);
}
match &message.scoped_data {
ScopedPduData::CleartextPdu(scoped_pdu) => {
let map_opt = context_map.lookup(&scoped_pdu.name);
if let Some(oid_map) = map_opt {
resp_opt = self.do_scoped_pdu(flags, user, scoped_pdu, oid_map);
} else {
resp_opt = None }
}
ScopedPduData::EncryptedPdu(enc_octs) => {
let key = &user.priv_key.borrow();
let buf2: Vec<u8> = privacy::decrypt(&mut enc_octs.to_vec(), usp.clone(), key);
let pdu_decode_res: Result<ScopedPdu, rasn::error::DecodeError> =
rasn::ber::decode(&buf2);
if pdu_decode_res.is_err() {
self.decryption_errors += 1;
warn!("Decode error {pdu_decode_res:?}");
return (None, None);
}
let scoped_pdu: ScopedPdu = pdu_decode_res.unwrap(); let map_opt = context_map.lookup(&scoped_pdu.name);
if let Some(oid_map) = map_opt {
resp_opt = self.do_scoped_pdu(flags, user, &scoped_pdu, oid_map);
} else {
resp_opt = None; }
}
}
if resp_opt.is_none() {
warn!("No response, discarding");
self.unknown_contexts += 1;
return (None, None);
}
let resp = resp_opt.unwrap(); out_message = self.prepare_back(message_id, resp, user, usp, flags & 2 == 2);
out_message.global_data.flags = message.global_data.flags.clone();
if flags & 1 == 1 {
self.set_auth(&mut out_message, user);
}
(Some(out_message), Some(user))
}
pub fn loop_forever<'a>(&mut self, context_map: &'a mut ContextMap, users: &'a usm::Users) {
let mut buf = [0; 65100];
context_map.sort();
loop {
let recv_res = self.socket.recv_from(&mut buf);
if recv_res.is_err() {
continue;
}
self.in_pkts += 1;
let (amt, src) = recv_res.unwrap();
let buf = &mut buf[..amt];
let decode_res: Result<Message, rasn::error::DecodeError> = rasn::ber::decode(buf);
if decode_res.is_err() {
self.decode_error_cnt += 1;
continue;
}
let mut message: Message = decode_res.unwrap(); let (out_opt, user_opt) = self.process_message(context_map, users, &mut message);
if let Some(out_message) = out_opt {
self.send(src, out_message);
}
if let Some(user) = user_opt {
if !*user.clean.borrow() && users.save_to_file().is_err() {
error!("Error saving passwpord file, changes may not be persistent and file might be corrupted");
}
}
}
}
fn set_auth(&self, message: &mut Message, usr: &usm::User) -> Vec<u8> {
let r_sp: Result<USMSecurityParameters, Box<dyn Display>> =
message.decode_security_parameters(rasn::Codec::Ber);
if r_sp.is_err() {
warn!("Decode security parameters failed");
return vec![];
}
let mut usp: USMSecurityParameters = r_sp.ok().expect("Errors caught above");
usp.authentication_parameters = match usr.auth_length {
12 => Z12,
16 => Z16,
24 => Z24,
32 => Z32,
_ => Z48,
};
let enc_res = message.encode_security_parameters(rasn::Codec::Ber, &usp);
if enc_res.is_err() {
warn!("Encode security parameters failed");
return vec![];
}
let buf_res = rasn::ber::encode(message);
if buf_res.is_err() {
warn!("Encode message failed");
return vec![];
}
let buf = buf_res.unwrap(); let auth = usr.auth_from_bytes(&buf);
usp.authentication_parameters = OctetString::from_slice(&auth);
let _ = message.encode_security_parameters(rasn::Codec::Ber, &usp);
auth
}
fn wrong_auth(
&mut self,
message: &mut Message,
user: &usm::User,
usp: USMSecurityParameters,
) -> bool {
if usp.authentication_parameters.len() != user.auth_length {
warn!(
"Authentication parameters must be {} bytes",
user.auth_length
);
return true;
}
let boots: isize = usp
.authoritative_engine_boots
.try_into()
.unwrap_or(isize::MAX);
if boots != self.boots {
self.not_in_time_window += 1;
return true;
}
let run_time: i32 = self
.start_time
.elapsed()
.as_secs()
.try_into()
.unwrap_or(i32::MAX);
let man_time: i32 = usp.authoritative_engine_time.try_into().unwrap_or(i32::MAX);
let delta_t: i32 = man_time - run_time;
if !(-150..=150).contains(&delta_t) {
self.not_in_time_window += 1;
return true;
}
let hmac = usp.authentication_parameters.clone().to_vec();
let our_hmac = self.set_auth(message, user);
if hmac != our_hmac {
warn!("Message hmac {hmac:?} ours {our_hmac:?} ");
self.wrong_digests += 1;
return true;
}
false
}
}
#[cfg(test)]
mod tests {
use rasn_snmp::v2::BulkPdu;
use super::*;
use crate::engine_id::static_engine_id;
use crate::keeper::{Access, OType, OidKeeper};
use crate::oidmap;
use crate::perms::{Perm, Rule};
use crate::table::TableMemOid;
use crate::usm::User;
use crate::utils::*;
use std::env;
fn make_agent(port: &str) -> Agent {
env::set_var("RUST_LOG", "info");
let eid = OctetString::from_static(b"test");
let addr = "127.0.0.1:".to_owned() + port;
Agent::build(eid, &addr, false)
}
fn get_pdu(arg: &'static [u32]) -> GetRequest {
let vb = vec![VarBind {
name: ObjectIdentifier::new(arg).unwrap(), value: VarBindValue::Unspecified,
}];
let pdu = Pdu {
request_id: 1,
error_status: 0,
error_index: 0,
variable_bindings: vb,
};
GetRequest(pdu)
}
fn get_next_pdu(arg: &'static [u32]) -> GetNextRequest {
let vb = vec![VarBind {
name: ObjectIdentifier::new(arg).unwrap(), value: VarBindValue::Unspecified,
}];
let pdu = Pdu {
request_id: 1,
error_status: 0,
error_index: 0,
variable_bindings: vb,
};
GetNextRequest(pdu)
}
fn set_pdu(arg: &'static [u32], val: ObjectSyntax) -> SetRequest {
let vb = vec![VarBind {
name: ObjectIdentifier::new(arg).unwrap(), value: VarBindValue::Value(val),
}];
let pdu = Pdu {
request_id: 1,
error_status: 0,
error_index: 0,
variable_bindings: vb,
};
SetRequest(pdu)
}
fn get_bulk_pdu(arg: &'static [u32]) -> GetBulkRequest {
let oid = ObjectIdentifier::new(arg).unwrap(); let vb = vec![
VarBind {
name: oid.clone(),
value: VarBindValue::Unspecified,
},
VarBind {
name: oid.clone(),
value: VarBindValue::Unspecified,
},
];
let pdu = BulkPdu {
non_repeaters: 1,
max_repetitions: 1,
request_id: 1,
variable_bindings: vb,
};
GetBulkRequest(pdu)
}
const ARC2: [u32; 2] = [1, 6];
fn tab_fixture() -> Box<dyn OidKeeper> {
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let first = simple_from_str(b"abc");
let last = simple_from_str(b"xyz");
let blank = simple_from_str(b"");
let s0 = simple_from_int(0);
let s42 = simple_from_int(42);
let s41 = simple_from_int(41);
let s4 = simple_from_int(4);
let s5 = simple_from_int(5);
let mut tab = Box::new(TableMemOid::new(
vec![blank.clone(), s0.clone(), s0.clone()],
3,
&oid2,
vec![OType::String, OType::Integer, OType::Integer],
vec![Access::ReadOnly, Access::ReadOnly, Access::ReadWrite],
vec![1usize, 2usize],
false,
));
tab.set_data(vec![
vec![first.clone(), s4.clone(), s41.clone()],
vec![last.clone(), s5.clone(), s42.clone()],
]);
tab
}
fn make_oid_map() -> OidMap {
let mut om = oidmap::OidMap::new();
let tab = tab_fixture();
let oid = ObjectIdentifier::new(&ARC2).unwrap(); om.push(oid, tab);
om
}
fn perms() -> Vec<Perm> {
let rules = vec![Rule {
read: true,
write: true,
context: None,
include: vec![vec![1u32]],
exclude: vec![],
}];
vec![Perm {
rules,
security_level: 1u8, group_name: "test".as_bytes().to_vec(),
}]
}
fn user_fixture<'a>(pv: &'a Vec<Perm>) -> User {
let s ="test test sha1 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b aes 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c";
let u = User::from_str(s, pv).unwrap(); u
}
fn users_fixture<'a>(pv: &'a Vec<Perm>) -> usm::Users {
let mut u = usm::Users::new();
let s ="test test sha1 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b aes 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c";
u.load_from_str(pv, s);
u
}
#[test]
fn test_get() {
let agent = make_agent("3161");
let gp = get_pdu(&ARC2);
let mut vb: Vec<VarBind> = vec![];
let mut oid_map = make_oid_map();
let perm = &perms()[0];
let context = [];
let fperm = FlagPerm::new(3, &context, perm);
let (status, idx, r_id) = agent.get(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 1);
assert_eq!(status, Pdu::ERROR_STATUS_NO_SUCH_NAME);
assert_eq!(vb.len(), 1);
vb.clear();
let gp = get_pdu(&[1, 7, 1, 3, 4]);
let (status, idx, r_id) = agent.get(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_SUCH_NAME);
assert_eq!(vb.len(), 1);
vb.clear();
let gp = get_pdu(&[1, 6, 1, 2, 3, 120, 121, 122, 5]);
let (status, idx, r_id) = agent.get(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
assert_eq!(vb[0].value, VarBindValue::Value(simple_from_int(5)));
}
#[test]
fn test_get_next() {
let agent = make_agent("3162");
let gp = get_next_pdu(&ARC2);
let mut vb: Vec<VarBind> = vec![];
let mut oid_map = make_oid_map();
let perm = &perms()[0];
let context = [];
let fperm = FlagPerm::new(3, &context, perm);
let (status, idx, r_id) = agent.getnext(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
vb.clear();
let gp = get_next_pdu(&[1, 6, 1, 2, 3, 120, 121, 122, 5]);
let (status, idx, r_id) = agent.getnext(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
assert_eq!(vb[0].value, VarBindValue::Value(simple_from_int(41)));
vb.clear();
let gp = get_next_pdu(&[1, 5, 1]);
let (status, idx, r_id) = agent.getnext(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
assert_eq!(vb[0].value, VarBindValue::Value(simple_from_str(b"abc")));
}
#[test]
fn test_set() {
let agent = make_agent("3163");
let sp = set_pdu(&[1, 6, 1, 3, 3, 120, 121, 122, 5], simple_from_int(4));
let mut vb: Vec<VarBind> = vec![];
let mut oid_map = make_oid_map();
let perm = &perms()[0];
let context = [];
let fperm = FlagPerm::new(3, &context, perm);
let pv = perms();
let user = user_fixture(&pv);
let (status, idx, r_id) = agent.set(&mut oid_map, sp, &mut vb, &fperm, &user);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
vb.clear();
let gp = get_pdu(&[1, 6, 1, 2, 3, 120, 121, 122, 5]);
let (status, idx, r_id) = agent.get(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 1);
}
#[test]
fn test_bulk() {
let agent = make_agent("3164");
let gp = get_bulk_pdu(&ARC2);
let mut vb: Vec<VarBind> = vec![];
let mut oid_map = make_oid_map();
let perm = &perms()[0];
let context = [];
let fperm = FlagPerm::new(3, &context, perm);
let (status, idx, r_id) = agent.bulk(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 2);
vb.clear();
let gp = get_bulk_pdu(&[1, 6, 1, 2, 3, 120, 121, 122, 5]);
let (status, idx, r_id) = agent.bulk(&mut oid_map, gp, &mut vb, &fperm);
assert_eq!(r_id, 1);
assert_eq!(idx, 0);
assert_eq!(status, Pdu::ERROR_STATUS_NO_ERROR);
assert_eq!(vb.len(), 2);
assert_eq!(vb[0].value, VarBindValue::Value(simple_from_int(41)));
}
#[test]
fn test_do_scoped_pdu_get() {
let pv = perms();
let user = user_fixture(&pv);
let agent = make_agent("3165");
let message_id = Integer::from(213);
let zb = OctetString::from_static(b"");
let gp = get_pdu(&ARC2);
let eid = static_engine_id(1, b"besttest");
let scoped_pdu = ScopedPdu {
engine_id: eid.clone(),
name: zb.clone(),
data: Pdus::GetRequest(gp),
};
let mut oid_map = make_oid_map();
let opt_resp = agent.do_scoped_pdu(2u8, &user, &scoped_pdu, &mut oid_map);
assert!(opt_resp.is_some());
if let Some(resp) = opt_resp {
let usp: USMSecurityParameters = USMSecurityParameters {
authoritative_engine_id: eid,
authoritative_engine_boots: Integer::from(7),
authoritative_engine_time: Integer::from(8),
user_name: zb.clone(),
authentication_parameters: zb.clone(),
privacy_parameters: zb,
};
let message = agent.prepare_back(message_id, resp, &user, usp, false);
assert_eq!(message.version, Integer::from(3));
}
}
#[test]
fn test_reports() {
let agent = make_agent("3166");
let id_message = agent.id_response(7, Integer::from(8));
assert_eq!(id_message.version, Integer::from(3));
let auth_message = agent.auth_failure(8, Integer::from(9));
assert_eq!(auth_message.version, Integer::from(3));
let use_message = agent.unknown_user(8, Integer::from(9));
assert_eq!(use_message.version, Integer::from(3));
}
#[test]
fn test_wrong_and_good_auth() {
let mut agent = make_agent("3167");
let pv = perms();
let user = user_fixture(&pv);
let zb = OctetString::from_static(b"");
let name = OctetString::from_static(b"test");
let gp = get_pdu(&ARC2);
let eid = static_engine_id(1, b"besttest");
let usp: USMSecurityParameters = USMSecurityParameters {
authoritative_engine_id: eid.clone(),
authoritative_engine_boots: Integer::from(agent.boots),
authoritative_engine_time: Integer::from(8),
user_name: name.clone(),
authentication_parameters: OctetString::from_static(&[0u8; 12]),
privacy_parameters: zb.clone(),
};
let scpl = ScopedPdu {
engine_id: eid.clone(),
name: zb.clone(),
data: Pdus::GetRequest(gp),
};
let hd = HeaderData {
message_id: Integer::from(7),
max_size: Integer::from(7650),
flags: OctetString::from_static(&[1]),
security_model: Integer::from(3),
};
let mut message = Message {
version: Integer::from(3),
global_data: hd,
security_parameters: OctetString::from_static(&[0u8; 12]),
scoped_data: ScopedPduData::CleartextPdu(scpl),
};
let auth = agent.wrong_auth(&mut message, &user, usp.clone());
assert!(auth);
let _ = message.encode_security_parameters(rasn::Codec::Ber, &usp);
let sauth = agent.set_auth(&mut message, &user);
println!("Set auth returned {sauth:?}");
let r_sp: Result<USMSecurityParameters, Box<dyn Display>> =
message.decode_security_parameters(rasn::Codec::Ber);
let usp: USMSecurityParameters = r_sp.ok().expect("Errors caught above");
let auth = agent.wrong_auth(&mut message, &user, usp); assert!(!auth);
}
#[test]
fn test_process_message() {
let mut agent = make_agent("3168");
let pv = perms();
let users = users_fixture(&pv);
let zb = OctetString::from_static(b"");
let gp = get_pdu(&ARC2);
let eid = static_engine_id(1, b"besttest");
let usp: USMSecurityParameters = USMSecurityParameters {
authoritative_engine_id: eid.clone(),
authoritative_engine_boots: Integer::from(0),
authoritative_engine_time: Integer::from(8),
user_name: OctetString::from_static(b"test"),
authentication_parameters: OctetString::from_static(&[0u8; 16]),
privacy_parameters: zb.clone(),
};
let scpl = ScopedPdu {
engine_id: eid.clone(),
name: zb.clone(),
data: Pdus::GetRequest(gp),
};
let hd = HeaderData {
message_id: Integer::from(7),
max_size: Integer::from(7650),
flags: OctetString::from_static(&[3]),
security_model: Integer::from(3),
};
let mut message = Message {
version: Integer::from(3),
global_data: hd,
security_parameters: OctetString::from_static(&[0u8; 16]),
scoped_data: ScopedPduData::CleartextPdu(scpl),
};
let _ = message.encode_security_parameters(rasn::Codec::Ber, &usp);
let mut oid_map = make_oid_map();
let mut context_map = ContextMap::new();
context_map.insert(b"", &mut oid_map);
let (msg, usr) = agent.process_message(&mut context_map, &users, &mut message);
assert!(usr.is_none());
assert!(msg.is_some());
}
}