Skip to content

Commit 4bf4eca

Browse files
committed
ospf: Check LLS data block authentication.
For OSPFv{2,3}, if the authentication is disabled, the LLS data block checksum is verified. For OSPFv2, when the authentication is enabled, the CA-TLV digest is verified. For OSPFv3, when the authentication is enabled, there is nothing to do as the LLS data block is included in the authentication digest. Signed-off-by: Nicolas Rybowski <[email protected]>
1 parent 44f5bcd commit 4bf4eca

File tree

6 files changed

+138
-40
lines changed

6 files changed

+138
-40
lines changed

holo-ospf/src/ospfv2/packet/lls.rs

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use derive_new::new;
55
use num_traits::FromPrimitive;
66
use serde::{Deserialize, Serialize};
77

8+
use super::PacketHdrAuth;
89
use crate::ospfv2::packet::packet_options;
910
use crate::packet::OptionsVersion;
10-
use crate::packet::auth::{self, AuthEncodeCtx};
11+
use crate::packet::auth::{self, AuthDecodeCtx, AuthEncodeCtx, AuthMethod};
1112
use crate::packet::error::{DecodeError, DecodeResult};
1213
use crate::packet::lls::{
1314
ExtendedOptionsFlagsTlv, LLS_HDR_SIZE, LlsDbDescData, LlsHelloData,
@@ -76,7 +77,7 @@ impl From<LlsDataBlock> for LlsDbDescData {
7677
pub struct CryptoAuthTlv {
7778
pub seqno: u32,
7879
#[new(default)]
79-
pub auth_data: Vec<u32>,
80+
pub auth_data: Bytes,
8081
}
8182

8283
impl CryptoAuthTlv {
@@ -104,12 +105,9 @@ impl CryptoAuthTlv {
104105
return Err(DecodeError::InvalidTlvLength(tlv_len));
105106
}
106107
let seqno = buf.try_get_u32()?;
107-
let mut auth_data_len = tlv_len - 4;
108-
let mut auth_data = Vec::new();
109-
while auth_data_len > 0 {
110-
auth_data.push(buf.try_get_u32()?);
111-
auth_data_len -= 4;
112-
}
108+
let auth_data_len = tlv_len - 4;
109+
let auth_data = buf.slice(..auth_data_len as usize);
110+
113111
Ok(CryptoAuthTlv { seqno, auth_data })
114112
}
115113
}
@@ -140,27 +138,36 @@ impl LlsVersion<Self> for Ospfv2 {
140138
}
141139

142140
fn decode_lls_block(
143-
buf: &[u8],
141+
data: &[u8],
144142
pkt_len: u16,
143+
hdr_auth: PacketHdrAuth,
144+
auth: Option<AuthDecodeCtx<'_>>,
145145
) -> DecodeResult<Option<LlsDataBlock>> {
146146
// Test the presence of the L-bit indicating a LLS data block.
147-
if packet_options(buf).is_none_or(|options| !options.l_bit()) {
147+
if packet_options(data).is_none_or(|options| !options.l_bit()) {
148148
return Ok(None);
149149
}
150150

151-
let mut buf = Bytes::copy_from_slice(&buf[pkt_len as usize..]);
151+
let mut buf = Bytes::copy_from_slice(&data[pkt_len as usize..]);
152152

153153
// Sanity check on buffer length.
154154
if buf.remaining() < LLS_HDR_SIZE as usize {
155155
return Err(DecodeError::InvalidLength(buf.len() as u16));
156156
}
157157

158158
// If authentication trailer is embedded, skip it.
159+
// The authentication digest has already been verified earlier, so no
160+
// need for a double check here.
161+
if let PacketHdrAuth::Cryptographic { auth_len, .. } = hdr_auth {
162+
buf.advance(auth_len as usize);
163+
} else {
164+
// Validate LLS block checksum when authentication is disabled.
165+
Self::verify_cksum(&buf)?;
166+
};
159167

160-
let mut lls_block = LlsDataBlock::new();
168+
let mut data = buf.clone();
161169

162-
// TODO: Validate LLS block checksum
163-
// Self::verify_cksum(buf)?;
170+
let mut lls_block = LlsDataBlock::new();
164171

165172
let _cksum = buf.try_get_u16()?;
166173
let lls_len = buf.try_get_u16()?;
@@ -175,6 +182,7 @@ impl LlsVersion<Self> for Ospfv2 {
175182
return Err(DecodeError::InvalidLength(block_len as u16));
176183
}
177184
buf = buf.slice(0..block_len);
185+
data = data.slice(0..block_len + LLS_HDR_SIZE as usize);
178186

179187
while buf.remaining() >= LLS_HDR_SIZE as usize {
180188
// Parse TLV type.
@@ -198,7 +206,80 @@ impl LlsVersion<Self> for Ospfv2 {
198206
}
199207
Some(LlsTlvType::CryptoAuth) => {
200208
let ca = CryptoAuthTlv::decode(tlv_len, &mut buf_tlv)?;
201-
lls_block.ca = Some(ca);
209+
210+
// RFC 5613 Section 2.5 : "The Sequence Number field
211+
// contains the cryptographic sequence number
212+
// that is used to prevent simple replay attacks. For the
213+
// LLS block to be considered authentic, the Sequence Number
214+
// in the CA-TLV MUST match the Sequence Number in the
215+
// OSPFv2 packet header Authentication field (which MUST be
216+
// present)."
217+
// TODO : check that seqno match
218+
219+
if let PacketHdrAuth::Cryptographic {
220+
key_id,
221+
auth_len,
222+
seqno,
223+
} = hdr_auth
224+
{
225+
// Get authentication key.
226+
let auth = auth.as_ref().unwrap();
227+
let auth_key = match auth.method {
228+
AuthMethod::ManualKey(key) => {
229+
// Check if the Key ID matches.
230+
if key.id != key_id as u64 {
231+
return Err(
232+
DecodeError::AuthKeyIdNotFound(
233+
key_id as u32,
234+
),
235+
);
236+
}
237+
key
238+
}
239+
AuthMethod::Keychain(keychain) => keychain
240+
.key_lookup_accept(key_id as u64)
241+
.ok_or(DecodeError::AuthKeyIdNotFound(
242+
key_id as u32,
243+
))?,
244+
};
245+
246+
// Sanity check.
247+
if auth_key.algo.digest_size() != auth_len {
248+
return Err(DecodeError::AuthLenError(
249+
auth_len as u16,
250+
));
251+
}
252+
253+
// Skip the CA TLV digest
254+
let mut digest_data = BytesMut::from(
255+
&data[..data.len() - ca.auth_data.len()],
256+
);
257+
258+
// Remove LLS block length at its unknown upon digest
259+
// encoding.
260+
digest_data[3] = 0;
261+
262+
// Remove LLS CA TLV length at its unknown upon digest
263+
// encoding. Per RFC 5613, CA TLV MUST be the last in
264+
// the LLS data block.
265+
let offset = digest_data.len() - 5;
266+
digest_data[offset] = 0;
267+
268+
let digest = auth::message_digest(
269+
&digest_data,
270+
auth_key.algo,
271+
&auth_key.string,
272+
None,
273+
None,
274+
);
275+
276+
// Check if the received message digest is valid.
277+
if ca.auth_data != digest {
278+
return Err(DecodeError::AuthError);
279+
}
280+
281+
lls_block.ca = Some(ca);
282+
}
202283
}
203284
_ => {
204285
// Save unknown TLV.

holo-ospf/src/ospfv2/packet/mod.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ impl PacketVersion<Self> for Ospfv2 {
839839
fn decode_auth_validate(
840840
data: &[u8],
841841
pkt_len: u16,
842-
hdr_auth: PacketHdrAuth,
842+
hdr_auth: &PacketHdrAuth,
843843
auth: Option<AuthDecodeCtx<'_>>,
844844
) -> DecodeResult<Option<u64>> {
845845
// Discard the packet if its authentication type doesn't match the
@@ -864,26 +864,28 @@ impl PacketVersion<Self> for Ospfv2 {
864864
let auth_key = match auth.method {
865865
AuthMethod::ManualKey(key) => {
866866
// Check if the Key ID matches.
867-
if key.id != key_id as u64 {
867+
if key.id != *key_id as u64 {
868868
return Err(DecodeError::AuthKeyIdNotFound(
869-
key_id as u32,
869+
*key_id as u32,
870870
));
871871
}
872872
key
873873
}
874-
AuthMethod::Keychain(keychain) => keychain
875-
.key_lookup_accept(key_id as u64)
876-
.ok_or(DecodeError::AuthKeyIdNotFound(key_id as u32))?,
874+
AuthMethod::Keychain(keychain) => {
875+
keychain.key_lookup_accept(*key_id as u64).ok_or(
876+
DecodeError::AuthKeyIdNotFound(*key_id as u32),
877+
)?
878+
}
877879
};
878880

879881
// Sanity check.
880-
if auth_key.algo.digest_size() != auth_len {
881-
return Err(DecodeError::AuthLenError(auth_len as u16));
882+
if auth_key.algo.digest_size() != *auth_len {
883+
return Err(DecodeError::AuthLenError(*auth_len as u16));
882884
}
883885

884886
// Get the authentication trailer.
885887
let auth_trailer = &data
886-
[pkt_len as usize..pkt_len as usize + auth_len as usize];
888+
[pkt_len as usize..pkt_len as usize + *auth_len as usize];
887889

888890
// Compute message digest.
889891
let data = &data[..pkt_len as usize];
@@ -901,7 +903,7 @@ impl PacketVersion<Self> for Ospfv2 {
901903
}
902904

903905
// Authentication succeeded.
904-
Ok(Some(seqno.into()))
906+
Ok(Some((*seqno).into()))
905907
}
906908
}
907909
}
@@ -939,11 +941,11 @@ pub(crate) fn packet_options(data: &[u8]) -> Option<Options> {
939941
let pkt_type = PacketType::from_u8(data[1]).unwrap();
940942
match pkt_type {
941943
PacketType::Hello => {
942-
let options = &data[PacketHdr::LENGTH as usize + 7..];
944+
let options = &data[PacketHdr::LENGTH as usize + 6..];
943945
Some(Options::from_bits_truncate(options[0]))
944946
}
945947
PacketType::DbDesc => {
946-
let options = &data[PacketHdr::LENGTH as usize + 3..];
948+
let options = &data[PacketHdr::LENGTH as usize + 2..];
947949
Some(Options::from_bits_truncate(options[0]))
948950
}
949951
PacketType::LsRequest | PacketType::LsUpdate | PacketType::LsAck => {

holo-ospf/src/ospfv3/packet/lls.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use bytes::{Buf, Bytes, BytesMut};
22
use derive_new::new;
33
use num_traits::FromPrimitive;
44

5-
use super::packet_options;
5+
use super::{PacketHdrAuth, packet_options};
6+
use crate::ospfv3::packet::Options;
67
use crate::packet::OptionsVersion;
7-
// use serde::{Deserialize, Serialize};
8-
use crate::packet::auth::AuthEncodeCtx;
8+
use crate::packet::auth::{AuthDecodeCtx, AuthEncodeCtx};
99
use crate::packet::error::{DecodeError, DecodeResult};
1010
use crate::packet::lls::{
1111
ExtendedOptionsFlagsTlv, LLS_HDR_SIZE, LlsDbDescData, LlsHelloData,
@@ -72,9 +72,12 @@ impl LlsVersion<Self> for Ospfv3 {
7272
fn decode_lls_block(
7373
buf: &[u8],
7474
pkt_len: u16,
75+
_hdr_auth: PacketHdrAuth,
76+
_auth: Option<AuthDecodeCtx<'_>>,
7577
) -> DecodeResult<Option<LlsDataBlock>> {
7678
// Test the presence of the L-bit indicating a LLS data block.
77-
if packet_options(buf).is_none_or(|options| !options.l_bit()) {
79+
let options = packet_options(buf);
80+
if options.is_none_or(|options| !options.l_bit()) {
7881
return Ok(None);
7982
}
8083

@@ -85,10 +88,14 @@ impl LlsVersion<Self> for Ospfv3 {
8588
return Err(DecodeError::InvalidLength(buf.len() as u16));
8689
}
8790

88-
let mut lls_block = LlsDataBlock::new();
91+
// Validate LLS block checksum when authentication is disabled.
92+
if let Some(options) = options
93+
&& !options.contains(Options::AT)
94+
{
95+
Self::verify_cksum(&buf)?;
96+
}
8997

90-
// TODO: Validate LLS block checksum
91-
// Self::verify_cksum(buf)?;
98+
let mut lls_block = LlsDataBlock::new();
9299

93100
let _cksum = buf.try_get_u16()?;
94101
let lls_len = buf.try_get_u16()?;

holo-ospf/src/ospfv3/packet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ impl PacketVersion<Self> for Ospfv3 {
811811
fn decode_auth_validate(
812812
data: &[u8],
813813
pkt_len: u16,
814-
_hdr_auth: PacketHdrAuth,
814+
_hdr_auth: &PacketHdrAuth,
815815
auth: Option<AuthDecodeCtx<'_>>,
816816
) -> DecodeResult<Option<u64>> {
817817
let options = packet_options(data);

holo-ospf/src/packet/lls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use serde::{self, Deserialize, Serialize};
77
use super::auth::AuthEncodeCtx;
88
use super::error::{DecodeError, DecodeResult};
99
use super::tlv::{tlv_encode_end, tlv_encode_start};
10+
use crate::ospfv2::packet::PacketHdr;
11+
use crate::packet::AuthDecodeCtx;
1012
use crate::version::Version;
1113

1214
// LLS header size.
@@ -26,6 +28,8 @@ pub trait LlsVersion<V: Version> {
2628
fn decode_lls_block(
2729
buf: &[u8],
2830
pkt_len: u16,
31+
hdr_auth: V::PacketHdrAuth,
32+
auth: Option<AuthDecodeCtx<'_>>,
2933
) -> DecodeResult<Option<V::LlsDataBlock>>;
3034

3135
const CKSUM_RANGE: std::ops::Range<usize> = 0..2;

holo-ospf/src/packet/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub trait PacketVersion<V: Version> {
8585
fn decode_auth_validate(
8686
data: &[u8],
8787
pkt_len: u16,
88-
hdr_auth: V::PacketHdrAuth,
88+
hdr_auth: &V::PacketHdrAuth,
8989
auth: Option<AuthDecodeCtx<'_>>,
9090
) -> DecodeResult<Option<u64>>;
9191

@@ -324,13 +324,17 @@ impl<V: Version> Packet<V> {
324324
let _span_guard = span.enter();
325325

326326
// Validate the packet authentication.
327-
if let Some(auth_seqno) =
328-
V::decode_auth_validate(&buf_orig, pkt_len, hdr_auth, auth)?
329-
{
327+
if let Some(auth_seqno) = V::decode_auth_validate(
328+
&buf_orig,
329+
pkt_len,
330+
&hdr_auth,
331+
// TODO : Pass a reference rather than a clone
332+
auth.clone(),
333+
)? {
330334
hdr.set_auth_seqno(auth_seqno);
331335
}
332336

333-
let lls = V::decode_lls_block(&buf_orig, pkt_len)?;
337+
let lls = V::decode_lls_block(&buf_orig, pkt_len, hdr_auth, auth)?;
334338

335339
// Decode the packet body.
336340
let packet = match hdr.pkt_type() {

0 commit comments

Comments
 (0)