]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
http2: settings from http1 upgrade
authorPhilippe Antoine <contact@catenacyber.fr>
Thu, 3 Sep 2020 12:22:29 +0000 (14:22 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 9 Sep 2020 14:22:11 +0000 (16:22 +0200)
rules/http2-events.rules
rust/src/http2/detect.rs
rust/src/http2/http2.rs

index 9429edb1395cfafcf6cca7d6ab2aae16c7e883ef..bb2d08ded58393171148cb1fa15ce286c2537ea4 100644 (file)
@@ -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;)
index 8778a44abb95e94c86b156d43eafcce5d475066d..59180263231e2c7cccd07fe85e5346cb0680cc3b 100644 (file)
  */
 
 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<u8, Http2Base64Error> {
+    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<Vec<u8>, 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)
     }
index 3ee0e29973fed83e7f9620a42a0b8045bc65b95f..055fbfe7be45128039589e703ca7676494aaca79 100644 (file)
@@ -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;