]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
requires: add option to ignore unknown requirements
authorJason Ish <jason.ish@oisf.net>
Wed, 4 Dec 2024 17:28:17 +0000 (11:28 -0600)
committerVictor Julien <victor@inliniac.net>
Sat, 7 Dec 2024 06:18:15 +0000 (07:18 +0100)
The new behavior in 8, and backported is to treat unknown requirements
as unsatisfied requirements.

For 7.0.8, add a configuration option, "ignore-unknown-requirements"
to completely ignore unknown requirements, effectively treating them
as available.

Ticket: #7434

doc/userguide/upgrade.rst
rust/src/detect/requires.rs
src/detect-requires.c

index e515a769268b1712fadd8613e7f0cd38b208af71..09c18d96b08b9d259d7b3095521048236d0b2325 100644 (file)
@@ -37,8 +37,28 @@ dedicated new configuration.
 Upgrading to 7.0.8
 ------------------
 - Unknown requirements in the ``requires`` keyword will now be treated
-  as unmet requirements, causing the rule to not be loaded. See
-  :ref:`keyword_requires`.
+  as unsatisfied requirements, causing the rule to not be loaded. See
+  :ref:`keyword_requires`. To opt out of this change and to ignore
+  uknown requirements, effectively treating them as satified the
+  ``ignore-unknown-requirements`` configuration option can be used.
+
+  Command line example::
+
+    --set ignore-unknown-requirements=true
+
+  Or as a top-level configuration option in ``suricata.yaml``:
+
+  .. code-block:: yaml
+
+    default-rule-path: /var/lib/suricata/rules
+    rule-files:
+      - suricata.rules
+    ignore-unknown-requirements: true
+
+  .. note:: This option will only exist in Suricata 7.0.8 and future
+            7.0 releases. It will not be provided in
+            Suricata 8. Please fix any rules that depend on this
+            behavior.
 
 Upgrading 6.0 to 7.0
 --------------------
index 2635605d265de76eada3d4665249aa9a11e0585e..bce9031ac0caa014f6873c19fd052d632bf12090 100644 (file)
@@ -297,9 +297,9 @@ fn check_version(
 }
 
 fn check_requires(
-    requires: &Requires, suricata_version: &SuricataVersion,
+    requires: &Requires, suricata_version: &SuricataVersion, ignore_unknown: bool,
 ) -> Result<(), RequiresError> {
-    if !requires.unknown.is_empty() {
+    if !ignore_unknown && !requires.unknown.is_empty() {
         return Err(RequiresError::UnknownRequirement(
             requires.unknown.join(","),
         ));
@@ -453,7 +453,7 @@ pub unsafe extern "C" fn SCDetectRequiresStatusLog(
 #[no_mangle]
 pub unsafe extern "C" fn SCDetectCheckRequires(
     requires: *const c_char, suricata_version_string: *const c_char, errstr: *mut *const c_char,
-    status: &mut SCDetectRequiresStatus,
+    status: &mut SCDetectRequiresStatus, ignore_unknown: c_int,
 ) -> c_int {
     // First parse the running Suricata version.
     let suricata_version = match parse_suricata_version(CStr::from_ptr(suricata_version_string)) {
@@ -476,7 +476,9 @@ pub unsafe extern "C" fn SCDetectCheckRequires(
         }
     };
 
-    match check_requires(&requires, &suricata_version) {
+    let ignore_unknown = ignore_unknown != 0;
+
+    match check_requires(&requires, &suricata_version, ignore_unknown) {
         Ok(()) => 0,
         Err(err) => {
             match &err {
@@ -497,6 +499,7 @@ pub unsafe extern "C" fn SCDetectCheckRequires(
                 RequiresError::VersionGt => {
                     status.gt_count += 1;
                 }
+                RequiresError::UnknownRequirement(_) => {}
                 _ => {}
             }
             *errstr = err.c_errmsg();
@@ -680,7 +683,7 @@ mod test {
         let suricata_version = SuricataVersion::new(7, 0, 4);
         let requires = parse_requires("version >= 8").unwrap();
         assert_eq!(
-            check_requires(&requires, &suricata_version),
+            check_requires(&requires, &suricata_version, false),
             Err(RequiresError::VersionLt(SuricataVersion {
                 major: 8,
                 minor: 0,
@@ -691,79 +694,79 @@ mod test {
         // Have 7.0.4, require 7.0.3.
         let suricata_version = SuricataVersion::new(7, 0, 4);
         let requires = parse_requires("version >= 7.0.3").unwrap();
-        assert_eq!(check_requires(&requires, &suricata_version), Ok(()));
+        assert_eq!(check_requires(&requires, &suricata_version, false), Ok(()));
 
         // Have 8.0.0, require >= 7.0.0 and < 8.0
         let suricata_version = SuricataVersion::new(8, 0, 0);
         let requires = parse_requires("version >= 7.0.0 < 8").unwrap();
         assert_eq!(
-            check_requires(&requires, &suricata_version),
+            check_requires(&requires, &suricata_version, false),
             Err(RequiresError::VersionGt)
         );
 
         // Have 8.0.0, require >= 7.0.0 and < 9.0
         let suricata_version = SuricataVersion::new(8, 0, 0);
         let requires = parse_requires("version >= 7.0.0 < 9").unwrap();
-        assert_eq!(check_requires(&requires, &suricata_version), Ok(()));
+        assert_eq!(check_requires(&requires, &suricata_version, false), Ok(()));
 
         // Require feature foobar.
         let suricata_version = SuricataVersion::new(8, 0, 0);
         let requires = parse_requires("feature foobar").unwrap();
         assert_eq!(
-            check_requires(&requires, &suricata_version),
+            check_requires(&requires, &suricata_version, false),
             Err(RequiresError::MissingFeature("foobar".to_string()))
         );
 
         // Require feature foobar, but this time we have the feature.
         let suricata_version = SuricataVersion::new(8, 0, 0);
         let requires = parse_requires("feature true_foobar").unwrap();
-        assert_eq!(check_requires(&requires, &suricata_version), Ok(()));
+        assert_eq!(check_requires(&requires, &suricata_version, false), Ok(()));
 
         let suricata_version = SuricataVersion::new(8, 0, 1);
         let requires = parse_requires("version >= 7.0.3 < 8").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_err());
+        assert!(check_requires(&requires, &suricata_version, false).is_err());
 
         let suricata_version = SuricataVersion::new(7, 0, 1);
         let requires = parse_requires("version >= 7.0.3 < 8").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_err());
+        assert!(check_requires(&requires, &suricata_version, false).is_err());
 
         let suricata_version = SuricataVersion::new(7, 0, 3);
         let requires = parse_requires("version >= 7.0.3 < 8").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_ok());
+        assert!(check_requires(&requires, &suricata_version, false).is_ok());
 
         let suricata_version = SuricataVersion::new(8, 0, 3);
         let requires = parse_requires("version >= 7.0.3 < 8 | >= 8.0.3").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_ok());
+        assert!(check_requires(&requires, &suricata_version, false).is_ok());
 
         let suricata_version = SuricataVersion::new(8, 0, 2);
         let requires = parse_requires("version >= 7.0.3 < 8 | >= 8.0.3").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_err());
+        assert!(check_requires(&requires, &suricata_version, false).is_err());
 
         let suricata_version = SuricataVersion::new(7, 0, 2);
         let requires = parse_requires("version >= 7.0.3 < 8 | >= 8.0.3").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_err());
+        assert!(check_requires(&requires, &suricata_version, false).is_err());
 
         let suricata_version = SuricataVersion::new(7, 0, 3);
         let requires = parse_requires("version >= 7.0.3 < 8 | >= 8.0.3").unwrap();
-        assert!(check_requires(&requires, &suricata_version).is_ok());
+        assert!(check_requires(&requires, &suricata_version, false).is_ok());
 
         // Example of something that requires a fix/feature that was
         // implemented in 7.0.5, 8.0.4, 9.0.3.
         let requires = parse_requires("version >= 7.0.5 < 8 | >= 8.0.4 < 9 | >= 9.0.3").unwrap();
-        assert!(check_requires(&requires, &SuricataVersion::new(6, 0, 0)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 4)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 5)).is_ok());
-        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 3)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 4)).is_ok());
-        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 2)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 3)).is_ok());
-        assert!(check_requires(&requires, &SuricataVersion::new(10, 0, 0)).is_ok());
+        assert!(check_requires(&requires, &SuricataVersion::new(6, 0, 0), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 4), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 5), false).is_ok());
+        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 3), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 4), false).is_ok());
+        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 2), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 3), false).is_ok());
+        assert!(check_requires(&requires, &SuricataVersion::new(10, 0, 0), false).is_ok());
 
         let requires = parse_requires("version >= 8 < 9").unwrap();
-        assert!(check_requires(&requires, &SuricataVersion::new(6, 0, 0)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 0)).is_err());
-        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0)).is_ok());
-        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 0)).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(6, 0, 0), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(7, 0, 0), false).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0), false).is_ok());
+        assert!(check_requires(&requires, &SuricataVersion::new(9, 0, 0), false).is_err());
 
         // Unknown keyword.
         let requires = parse_requires("feature true_lua, foo bar, version >= 7.0.3").unwrap();
@@ -786,7 +789,7 @@ mod test {
         // This should not pass the requires check as it contains an
         // unknown requires keyword.
         //check_requires(&requires, &SuricataVersion::new(8, 0, 0)).unwrap();
-        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0)).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0), false).is_err());
     }
 
     #[test]
@@ -830,6 +833,6 @@ mod test {
     #[test]
     fn test_requires_keyword() {
         let requires = parse_requires("keyword true_bar").unwrap();
-        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0)).is_err());
+        assert!(check_requires(&requires, &SuricataVersion::new(8, 0, 0), false).is_err());
     }
 }
index 4d7f916b3b82cb65f88e286e020209bfde56e8de..bb478699762cf93c6bdcdade45facef5c13b4353 100644 (file)
 #include "detect-engine.h"
 #include "rust.h"
 
+/* Set to true if unknown requirements should be ingored. In Suricata
+ * 8, unknown requirements are treated as unsatisfied requirements. */
+static int g_ignore_unknown_requirements = 0;
+
 static int DetectRequiresSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
 {
     if (de_ctx->requirements == NULL) {
@@ -28,7 +32,8 @@ static int DetectRequiresSetup(DetectEngineCtx *de_ctx, Signature *s, const char
     }
 
     const char *errmsg = NULL;
-    int res = SCDetectCheckRequires(rawstr, PROG_VER, &errmsg, de_ctx->requirements);
+    int res = SCDetectCheckRequires(
+            rawstr, PROG_VER, &errmsg, de_ctx->requirements, g_ignore_unknown_requirements);
     if (res == -1) {
         // The requires expression is bad, log an error.
         SCLogError("%s: %s", errmsg, rawstr);
@@ -43,6 +48,8 @@ static int DetectRequiresSetup(DetectEngineCtx *de_ctx, Signature *s, const char
 
 void DetectRequiresRegister(void)
 {
+    ConfGetBool("ignore-unknown-requirements", &g_ignore_unknown_requirements);
+
     sigmatch_table[DETECT_REQUIRES].name = "requires";
     sigmatch_table[DETECT_REQUIRES].desc = "require Suricata version or features";
     sigmatch_table[DETECT_REQUIRES].url = "/rules/meta-keywords.html#requires";