From: Philippe Antoine Date: Thu, 3 Sep 2020 12:22:29 +0000 (+0200) Subject: http2: settings from http1 upgrade X-Git-Tag: suricata-6.0.0-rc1~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6694737fcf85f5c8c8205eb83471390a2e6838eb;p=thirdparty%2Fsuricata.git http2: settings from http1 upgrade --- diff --git a/rules/http2-events.rules b/rules/http2-events.rules index 9429edb139..bb2d08ded5 100644 --- a/rules/http2-events.rules +++ b/rules/http2-events.rules @@ -13,3 +13,4 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid frame length"; flow: alert http2 any any -> any any (msg:"SURICATA HTTP2 header frame with extra data"; flow:established; app-layer-event:http2.extra_header_data; classtype:protocol-command-decode; sid:2290005; rev:1;) alert http2 any any -> any any (msg:"SURICATA HTTP2 too long frame data"; flow:established; app-layer-event:http2.long_frame_data; classtype:protocol-command-decode; sid:2290006; rev:1;) alert http2 any any -> any any (msg:"SURICATA HTTP2 stream identifier reuse"; flow:established; app-layer-event:http2.stream_id_reuse; classtype:protocol-command-decode; sid:2290007; rev:1;) +alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid HTTP1 settings during upgrade"; flow:established; app-layer-event:http2.invalid_http1_settings; classtype:protocol-command-decode; sid:2290008; rev:1;) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 8778a44abb..5918026323 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -16,11 +16,10 @@ */ use super::http2::{ - HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState, + HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState, }; use super::parser; use crate::core::STREAM_TOSERVER; -use crate::log::*; use std::ffi::CStr; use std::mem::transmute; use std::str::FromStr; @@ -593,6 +592,141 @@ pub extern "C" fn rs_http2_tx_set_uri(state: &mut HTTP2State, buffer: *const u8, http2_tx_set_header(state, ":path".as_bytes(), slice) } +#[derive(Debug, PartialEq)] +pub enum Http2Base64Error { + InvalidBase64, +} + +impl std::error::Error for Http2Base64Error {} + +impl std::fmt::Display for Http2Base64Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "invalid base64") + } +} + +fn http2_base64_map(input: u8) -> Result { + match input { + 43 => Ok(62), // + + 47 => Ok(63), // / + 48 => Ok(52), // 0 + 49 => Ok(53), // 1 + 50 => Ok(54), // 2 + 51 => Ok(55), // 3 + 52 => Ok(56), // 4 + 53 => Ok(57), // 5 + 54 => Ok(58), // 6 + 55 => Ok(59), // 7 + 56 => Ok(60), // 8 + 57 => Ok(61), // 9 + 65 => Ok(0), // A + 66 => Ok(1), // B + 67 => Ok(2), // C + 68 => Ok(3), // D + 69 => Ok(4), // E + 70 => Ok(5), // F + 71 => Ok(6), // G + 72 => Ok(7), // H + 73 => Ok(8), // I + 74 => Ok(9), // J + 75 => Ok(10), // K + 76 => Ok(11), // L + 77 => Ok(12), // M + 78 => Ok(13), // N + 79 => Ok(14), // O + 80 => Ok(15), // P + 81 => Ok(16), // Q + 82 => Ok(17), // R + 83 => Ok(18), // S + 84 => Ok(19), // T + 85 => Ok(20), // U + 86 => Ok(21), // V + 87 => Ok(22), // W + 88 => Ok(23), // X + 89 => Ok(24), // Y + 90 => Ok(25), // Z + 97 => Ok(26), // a + 98 => Ok(27), // b + 99 => Ok(28), // c + 100 => Ok(29), // d + 101 => Ok(30), // e + 102 => Ok(31), // f + 103 => Ok(32), // g + 104 => Ok(33), // h + 105 => Ok(34), // i + 106 => Ok(35), // j + 107 => Ok(36), // k + 108 => Ok(37), // l + 109 => Ok(38), // m + 110 => Ok(39), // n + 111 => Ok(40), // o + 112 => Ok(41), // p + 113 => Ok(42), // q + 114 => Ok(43), // r + 115 => Ok(44), // s + 116 => Ok(45), // t + 117 => Ok(46), // u + 118 => Ok(47), // v + 119 => Ok(48), // w + 120 => Ok(49), // x + 121 => Ok(50), // y + 122 => Ok(51), // z + _ => Err(Http2Base64Error::InvalidBase64), + } +} + +fn http2_decode_base64(input: &[u8]) -> Result, Http2Base64Error> { + if input.len() % 4 != 0 { + return Err(Http2Base64Error::InvalidBase64); + } + let mut r = vec![0; (input.len() * 3) / 4]; + for i in 0..input.len() / 4 { + let i1 = http2_base64_map(input[4 * i])?; + let i2 = http2_base64_map(input[4 * i + 1])?; + let i3 = http2_base64_map(input[4 * i + 2])?; + let i4 = http2_base64_map(input[4 * i + 3])?; + r[3 * i] = (i1 << 2) | (i2 >> 4); + r[3 * i + 1] = (i2 << 4) | (i3 >> 2); + r[3 * i + 2] = (i3 << 6) | i4; + } + return Ok(r); +} + +fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) { + match http2_decode_base64(input) { + Ok(dec) => { + if dec.len() % 6 != 0 { + state.set_event(HTTP2Event::InvalidHTTP1Settings); + } + + let head = parser::HTTP2FrameHeader { + length: dec.len() as u32, + ftype: parser::HTTP2FrameType::SETTINGS as u8, + flags: 0, + reserved: 0, + stream_id: 0, + }; + + match parser::http2_parse_frame_settings(&dec) { + Ok((_, set)) => { + let txdata = HTTP2FrameTypeData::SETTINGS(set); + let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER); + tx.frames_ts.push(HTTP2Frame { + header: head, + data: txdata, + }); + } + Err(_) => { + state.set_event(HTTP2Event::InvalidHTTP1Settings); + } + } + } + Err(_) => { + state.set_event(HTTP2Event::InvalidHTTP1Settings); + } + } +} + #[no_mangle] pub extern "C" fn rs_http2_tx_add_header( state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32, @@ -600,7 +734,7 @@ pub extern "C" fn rs_http2_tx_add_header( let slice_name = build_slice!(name, name_len as usize); let slice_value = build_slice!(value, value_len as usize); if slice_name == "HTTP2-Settings".as_bytes() { - SCLogNotice!("lol seetings TODO"); + http2_tx_set_settings(state, slice_value) } else { http2_tx_set_header(state, slice_name, slice_value) } diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index 3ee0e29973..055fbfe7be 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -246,6 +246,7 @@ pub enum HTTP2Event { ExtraHeaderData, LongFrameData, StreamIdReuse, + InvalidHTTP1Settings, } impl HTTP2Event { @@ -259,6 +260,7 @@ impl HTTP2Event { 5 => Some(HTTP2Event::ExtraHeaderData), 6 => Some(HTTP2Event::LongFrameData), 7 => Some(HTTP2Event::StreamIdReuse), + 8 => Some(HTTP2Event::InvalidHTTP1Settings), _ => None, } } @@ -297,7 +299,7 @@ impl HTTP2State { self.files.free(); } - fn set_event(&mut self, event: HTTP2Event) { + pub fn set_event(&mut self, event: HTTP2Event) { let len = self.transactions.len(); if len == 0 { return; @@ -1002,6 +1004,7 @@ pub extern "C" fn rs_http2_state_get_event_info( "extra_header_data" => HTTP2Event::ExtraHeaderData as i32, "long_frame_data" => HTTP2Event::LongFrameData as i32, "stream_id_reuse" => HTTP2Event::StreamIdReuse as i32, + "invalid_http1_settings" => HTTP2Event::InvalidHTTP1Settings as i32, _ => -1, // unknown event } } @@ -1029,6 +1032,7 @@ pub extern "C" fn rs_http2_state_get_event_info_by_id( HTTP2Event::ExtraHeaderData => "extra_header_data\0", HTTP2Event::LongFrameData => "long_frame_data\0", HTTP2Event::StreamIdReuse => "stream_id_reuse\0", + HTTP2Event::InvalidHTTP1Settings => "invalid_http1_settings\0", }; unsafe { *event_name = estr.as_ptr() as *const std::os::raw::c_char;