]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
ssh: add option to select behavior of encrypted parts
authorLukas Sismis <lsismis@oisf.net>
Thu, 19 Sep 2024 07:24:01 +0000 (09:24 +0200)
committerVictor Julien <victor@inliniac.net>
Mon, 7 Apr 2025 04:57:48 +0000 (06:57 +0200)
Ticket: 6788

rust/src/ssh/ssh.rs
src/app-layer-ssh.c
suricata.yaml.in

index 4c8b19be56e13586d5e826e5bb55b3a487e01469..649bea74db70a3e5b14cbc9b636df396b51175b8 100644 (file)
@@ -26,13 +26,28 @@ use suricata_sys::sys::AppProto;
 use std::ffi::CString;
 use std::sync::atomic::{AtomicBool, Ordering};
 
+#[repr(C)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[allow(non_camel_case_types)]
+pub enum SshEncryptionHandling {
+    SSH_HANDLE_ENCRYPTION_TRACK_ONLY = 0, // Disable raw content inspection, continue tracking
+    SSH_HANDLE_ENCRYPTION_BYPASS = 1,  // Skip processing of flow, bypass if possible
+    SSH_HANDLE_ENCRYPTION_FULL = 2,    // Handle fully like any other protocol
+}
+
 static mut ALPROTO_SSH: AppProto = ALPROTO_UNKNOWN;
 static HASSH_ENABLED: AtomicBool = AtomicBool::new(false);
 
+static mut ENCRYPTION_BYPASS_ENABLED: SshEncryptionHandling = SshEncryptionHandling::SSH_HANDLE_ENCRYPTION_TRACK_ONLY;
+
 fn hassh_is_enabled() -> bool {
     HASSH_ENABLED.load(Ordering::Relaxed)
 }
 
+fn encryption_bypass_mode() -> SshEncryptionHandling {
+    unsafe { ENCRYPTION_BYPASS_ENABLED }
+}
+
 #[derive(AppLayerFrameType)]
 pub enum SshFrameType {
     RecordHdr,
@@ -203,13 +218,24 @@ impl SSHState {
                         parser::MessageCode::NewKeys => {
                             hdr.flags = SSHConnectionState::SshStateFinished;
                             if ohdr.flags >= SSHConnectionState::SshStateFinished {
-                                unsafe {
-                                    AppLayerParserStateSetFlag(
-                                        pstate,
-                                        APP_LAYER_PARSER_NO_INSPECTION
+                                let mut flags = 0;
+
+                                match encryption_bypass_mode() {
+                                    SshEncryptionHandling::SSH_HANDLE_ENCRYPTION_BYPASS => {
+                                        flags |= APP_LAYER_PARSER_NO_INSPECTION
                                             | APP_LAYER_PARSER_NO_REASSEMBLY
-                                            | APP_LAYER_PARSER_BYPASS_READY,
-                                    );
+                                            | APP_LAYER_PARSER_BYPASS_READY;
+                                    }
+                                    SshEncryptionHandling::SSH_HANDLE_ENCRYPTION_TRACK_ONLY => {
+                                        flags |= APP_LAYER_PARSER_NO_INSPECTION;
+                                    }
+                                    _ => {}
+                                }
+
+                                if flags != 0 {
+                                    unsafe {
+                                        AppLayerParserStateSetFlag(pstate, flags);
+                                    }
                                 }
                             }
                         }
@@ -553,6 +579,13 @@ pub extern "C" fn SCSshHasshIsEnabled() -> bool {
     hassh_is_enabled()
 }
 
+#[no_mangle]
+pub extern "C" fn SCSshEnableBypass(mode: SshEncryptionHandling) {
+    unsafe {
+        ENCRYPTION_BYPASS_ENABLED = mode;
+    }
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn SCSshTxGetLogCondition(tx: *mut std::os::raw::c_void) -> bool {
     let tx = cast_pointer!(tx, SSHTransaction);
index e82183c0a37e0a42d98164be03d9ccff8a57664c..9223a418b3783d39dd2b4768320c8f9233d00d30 100644 (file)
@@ -55,6 +55,8 @@
 
 /* HASSH fingerprints are disabled by default */
 #define SSH_CONFIG_DEFAULT_HASSH false
+/* Bypassing the encrypted part of the connections */
+#define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS SSH_HANDLE_ENCRYPTION_TRACK_ONLY
 
 static int SSHRegisterPatternsForProtocolDetection(void)
 {
@@ -103,6 +105,25 @@ void RegisterSSHParsers(void)
         if (RunmodeIsUnittests() || enable_hassh) {
             SCSshEnableHassh();
         }
+
+        SshEncryptionHandling encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
+        SCConfNode *encryption_node = SCConfGetNode("app-layer.protocols.ssh.encryption-handling");
+        if (encryption_node != NULL && encryption_node->val != NULL) {
+            if (strcmp(encryption_node->val, "full") == 0) {
+                encryption_bypass = SSH_HANDLE_ENCRYPTION_FULL;
+            } else if (strcmp(encryption_node->val, "track-only") == 0) {
+                encryption_bypass = SSH_HANDLE_ENCRYPTION_TRACK_ONLY;
+            } else if (strcmp(encryption_node->val, "bypass") == 0) {
+                encryption_bypass = SSH_HANDLE_ENCRYPTION_BYPASS;
+            } else {
+                encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
+            }
+        }
+
+        if (encryption_bypass) {
+            SCLogConfig("ssh: bypass on the start of encryption enabled");
+            SCSshEnableBypass(encryption_bypass);
+        }
     }
 
     SCLogDebug("Registering Rust SSH parser.");
index 8623c80bf3a40e7ceaffeadcbc99eba386ede225..4365ce264b755f80b2bc0569574850e87c3b2da9 100644 (file)
@@ -968,6 +968,15 @@ app-layer:
     ssh:
       enabled: yes
       #hassh: yes
+
+      # What to do when the encrypted communications start:
+      # - track-only: keep tracking but stop inspection (default)
+      # - full:    keep tracking and inspect as normal
+      # - bypass:  stop processing this flow as much as possible.
+      #            Offload flow bypass to kernel or hardware if possible.
+      # For the best performance, select 'bypass'.
+      #
+      # encryption-handling: track-only
     doh2:
       enabled: yes
     http2: