use crate::applayer::*;
use crate::core::STREAM_TOCLIENT;
use crate::ike::ipsec_parser::*;
use super::ipsec_parser::IkeV2Transform;
use crate::ike::ike::{IKEState, IkeEvent};
use crate::ike::parser::IsakmpHeader;
use ipsec_parser::{IkeExchangeType, IkePayloadType, IkeV2Header};
#[derive(Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum IKEV2ConnectionState {
Init,
InitSASent,
InitKESent,
InitNonceSent,
RespSASent,
RespKESent,
RespNonceSent,
RespCertReqSent,
ParsingDone,
Invalid,
}
impl IKEV2ConnectionState {
pub fn advance(&self, payload: &IkeV2Payload) -> IKEV2ConnectionState {
use self::IKEV2ConnectionState::*;
match (self, &payload.content) {
(&Init, &IkeV2PayloadContent::SA(_)) => InitSASent,
(&InitSASent, &IkeV2PayloadContent::KE(_)) => InitKESent,
(&InitKESent, &IkeV2PayloadContent::Nonce(_)) => InitNonceSent,
(&InitNonceSent, &IkeV2PayloadContent::SA(_)) => RespSASent,
(&RespSASent, &IkeV2PayloadContent::KE(_)) => RespKESent,
(&RespKESent, &IkeV2PayloadContent::Nonce(_)) => ParsingDone, (&RespNonceSent, &IkeV2PayloadContent::CertificateRequest(_)) => ParsingDone, (&ParsingDone, _) => self.clone(),
(_, &IkeV2PayloadContent::Notify(_)) => self.clone(),
(_, &IkeV2PayloadContent::Dummy) => self.clone(),
(_, _) => Invalid,
}
}
}
pub struct Ikev2Container {
pub connection_state: IKEV2ConnectionState,
pub client_transforms: Vec<Vec<IkeV2Transform>>,
pub server_transforms: Vec<Vec<IkeV2Transform>>,
pub alg_enc: IkeTransformEncType,
pub alg_auth: IkeTransformAuthType,
pub alg_prf: IkeTransformPRFType,
pub alg_dh: IkeTransformDHType,
pub alg_esn: IkeTransformESNType,
pub dh_group: IkeTransformDHType,
}
impl Default for Ikev2Container {
fn default() -> Ikev2Container {
Ikev2Container {
connection_state: IKEV2ConnectionState::Init,
dh_group: IkeTransformDHType::None,
client_transforms: Vec::new(),
server_transforms: Vec::new(),
alg_enc: IkeTransformEncType::ENCR_NULL,
alg_auth: IkeTransformAuthType::NONE,
alg_prf: IkeTransformPRFType::PRF_NULL,
alg_dh: IkeTransformDHType::None,
alg_esn: IkeTransformESNType::NoESN,
}
}
}
pub fn handle_ikev2(
mut state: &mut IKEState, current: &[u8], isakmp_header: IsakmpHeader, direction: u8,
) -> AppLayerResult {
let hdr = IkeV2Header {
init_spi: isakmp_header.init_spi,
resp_spi: isakmp_header.resp_spi,
next_payload: IkePayloadType(isakmp_header.next_payload),
maj_ver: isakmp_header.maj_ver,
min_ver: isakmp_header.min_ver,
exch_type: IkeExchangeType(isakmp_header.exch_type),
flags: isakmp_header.flags,
msg_id: isakmp_header.msg_id,
length: isakmp_header.length,
};
let mut tx = state.new_tx();
tx.ike_version = 2;
tx.hdr.ikev2_header = hdr.clone();
tx.hdr.spi_initiator = format!("{:016x}", isakmp_header.init_spi);
tx.hdr.spi_responder = format!("{:016x}", isakmp_header.resp_spi);
tx.hdr.maj_ver = isakmp_header.maj_ver;
tx.hdr.min_ver = isakmp_header.min_ver;
tx.hdr.msg_id = isakmp_header.msg_id;
tx.hdr.flags = isakmp_header.flags;
state.transactions.push(tx);
let mut payload_types = Vec::new();
let mut errors = 0;
let mut notify_types = Vec::new();
match parse_ikev2_payload_list(current, hdr.next_payload) {
Ok((_, Ok(ref p))) => {
for payload in p {
payload_types.push(payload.hdr.next_payload_type);
match payload.content {
IkeV2PayloadContent::Dummy => (),
IkeV2PayloadContent::SA(ref prop) => {
add_proposals(state, prop, direction);
}
IkeV2PayloadContent::KE(ref kex) => {
SCLogDebug!("KEX {:?}", kex.dh_group);
if direction == STREAM_TOCLIENT {
state.ikev2_container.dh_group = kex.dh_group;
}
}
IkeV2PayloadContent::Nonce(ref _n) => {
SCLogDebug!("Nonce: {:?}", _n);
}
IkeV2PayloadContent::Notify(ref n) => {
SCLogDebug!("Notify: {:?}", n);
if n.notify_type.is_error() {
errors += 1;
}
notify_types.push(n.notify_type);
}
_ => {
SCLogDebug!("Unknown payload content {:?}", payload.content);
}
}
state.ikev2_container.connection_state =
state.ikev2_container.connection_state.advance(payload);
if let Some(tx) = state.transactions.last_mut() {
tx.payload_types
.ikev2_payload_types
.append(&mut payload_types);
tx.errors = errors;
tx.notify_types.append(&mut notify_types);
if direction == STREAM_TOCLIENT
&& state.ikev2_container.server_transforms.len() > 0
{
tx.hdr.ikev2_transforms.clear();
for transforms in &state.ikev2_container.server_transforms {
let mut proposal = Vec::new();
transforms.iter().for_each(|t| match *t {
IkeV2Transform::Encryption(ref e) => {
proposal.push(IkeV2Transform::Encryption(*e))
}
IkeV2Transform::Auth(ref e) => {
proposal.push(IkeV2Transform::Auth(*e))
}
IkeV2Transform::PRF(ref e) => {
proposal.push(IkeV2Transform::PRF(*e))
}
IkeV2Transform::DH(ref e) => proposal.push(IkeV2Transform::DH(*e)),
IkeV2Transform::ESN(ref e) => {
proposal.push(IkeV2Transform::ESN(*e))
}
_ => (),
});
tx.hdr.ikev2_transforms.push(proposal);
}
}
}
}
}
_e => {
SCLogDebug!("parse_ikev2_payload_with_type: {:?}", _e);
}
}
return AppLayerResult::ok();
}
fn add_proposals(state: &mut IKEState, prop: &Vec<IkeV2Proposal>, direction: u8) {
for p in prop {
let transforms: Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect();
for xform in &transforms {
match *xform {
IkeV2Transform::Encryption(ref enc) => {
match *enc {
IkeTransformEncType::ENCR_DES_IV64
| IkeTransformEncType::ENCR_DES
| IkeTransformEncType::ENCR_3DES
| IkeTransformEncType::ENCR_RC5
| IkeTransformEncType::ENCR_IDEA
| IkeTransformEncType::ENCR_CAST
| IkeTransformEncType::ENCR_BLOWFISH
| IkeTransformEncType::ENCR_3IDEA
| IkeTransformEncType::ENCR_DES_IV32
| IkeTransformEncType::ENCR_NULL => {
SCLogDebug!("Weak Encryption: {:?}", enc);
state.set_event(IkeEvent::WeakCryptoEnc);
}
_ => (),
}
}
IkeV2Transform::PRF(ref prf) => match *prf {
IkeTransformPRFType::PRF_NULL => {
SCLogDebug!("'Null' PRF transform proposed");
state.set_event(IkeEvent::InvalidProposal);
}
IkeTransformPRFType::PRF_HMAC_MD5 | IkeTransformPRFType::PRF_HMAC_SHA1 => {
SCLogDebug!("Weak PRF: {:?}", prf);
state.set_event(IkeEvent::WeakCryptoPrf);
}
_ => (),
},
IkeV2Transform::Auth(ref auth) => {
match *auth {
IkeTransformAuthType::NONE => {
}
IkeTransformAuthType::AUTH_HMAC_MD5_96
| IkeTransformAuthType::AUTH_HMAC_SHA1_96
| IkeTransformAuthType::AUTH_DES_MAC
| IkeTransformAuthType::AUTH_KPDK_MD5
| IkeTransformAuthType::AUTH_AES_XCBC_96
| IkeTransformAuthType::AUTH_HMAC_MD5_128
| IkeTransformAuthType::AUTH_HMAC_SHA1_160 => {
SCLogDebug!("Weak auth: {:?}", auth);
state.set_event(IkeEvent::WeakCryptoAuth);
}
_ => (),
}
}
IkeV2Transform::DH(ref dh) => match *dh {
IkeTransformDHType::None => {
SCLogDebug!("'None' DH transform proposed");
state.set_event(IkeEvent::InvalidProposal);
}
IkeTransformDHType::Modp768
| IkeTransformDHType::Modp1024
| IkeTransformDHType::Modp1024s160
| IkeTransformDHType::Modp1536 => {
SCLogDebug!("Weak DH: {:?}", dh);
state.set_event(IkeEvent::WeakCryptoDh);
}
_ => (),
},
IkeV2Transform::Unknown(_tx_type, _tx_id) => {
SCLogDebug!("Unknown proposal: type={:?}, id={}", _tx_type, _tx_id);
state.set_event(IkeEvent::UnknownProposal);
}
_ => (),
}
}
if !transforms.iter().any(|x| match *x {
IkeV2Transform::DH(_) => true,
_ => false,
}) {
SCLogDebug!("No DH transform found");
state.set_event(IkeEvent::WeakCryptoNoDh);
}
if p.protocol_id == ProtocolID::AH {
SCLogDebug!("Proposal uses protocol AH - no confidentiality");
state.set_event(IkeEvent::NoEncryption);
}
if !transforms.iter().any(|x| match *x {
IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false,
IkeV2Transform::Auth(_) => true,
_ => false,
}) {
if !transforms.iter().any(|x| match *x {
IkeV2Transform::Encryption(ref enc) => enc.is_aead(),
_ => false,
}) {
SCLogDebug!("No integrity transform found");
state.set_event(IkeEvent::WeakCryptoNoAuth);
}
}
if direction == STREAM_TOCLIENT {
transforms.iter().for_each(|t| match *t {
IkeV2Transform::Encryption(ref e) => state.ikev2_container.alg_enc = *e,
IkeV2Transform::Auth(ref a) => state.ikev2_container.alg_auth = *a,
IkeV2Transform::PRF(ref p) => state.ikev2_container.alg_prf = *p,
IkeV2Transform::DH(ref dh) => state.ikev2_container.alg_dh = *dh,
IkeV2Transform::ESN(ref e) => state.ikev2_container.alg_esn = *e,
_ => (),
});
SCLogDebug!("Selected transforms: {:?}", transforms);
state.ikev2_container.server_transforms.push(transforms);
} else {
SCLogDebug!("Proposed transforms: {:?}", transforms);
state.ikev2_container.client_transforms.push(transforms);
}
}
}