ntp: add ntp.version keyword

SNMP was used as a template.

Ticket: #8430
pull/15220/head
Jason Ish 5 months ago
parent 74bb525401
commit ec344fe68d

@ -28,6 +28,7 @@ Suricata Rules
kerberos-keywords
smb-keywords
snmp-keywords
ntp-keywords
base64-keywords
sip-keywords
sdp-keywords

@ -0,0 +1,27 @@
NTP Keywords
############
.. role:: example-rule-options
ntp.version
***********
NTP protocol version (integer). Expected values are 3 and 4.
``ntp.version`` uses an :ref:`unsigned 8-bit integer <rules-integer-keywords>`.
Syntax::
ntp.version:[op]<number>
The version can be matched exactly, or compared using the ``op`` setting::
ntp.version:4 # exactly 4
ntp.version:<4 # smaller than 4
ntp.version:>=3 # greater or equal than 3
Signature Example:
.. container:: example-rule
alert ntp any any -> any any (msg:"NTP version 4"; :example-rule-options:`ntp.version:4;` sid:1; rev:1;)

@ -0,0 +1,88 @@
/* Copyright (C) 2026 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use super::ntp::{NTPTransaction, ALPROTO_NTP};
use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
use crate::detect::uint::{DetectUintData, SCDetectU8Free, SCDetectU8Match, SCDetectU8Parse};
use crate::detect::SIGMATCH_INFO_UINT8;
use std::os::raw::{c_int, c_void};
use suricata_sys::sys::{
DetectEngineCtx, DetectEngineThreadCtx, Flow, SCDetectHelperBufferProgressRegister,
SCDetectHelperKeywordRegister, SCDetectSignatureSetAppProto, SCSigMatchAppendSMToList,
SCSigTableAppLiteElmt, SigMatchCtx, Signature,
};
static mut G_NTP_VERSION_KW_ID: u16 = 0;
static mut G_NTP_GENERIC_BUFFER_ID: c_int = 0;
unsafe extern "C" fn ntp_detect_version_setup(
de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char,
) -> c_int {
if SCDetectSignatureSetAppProto(s, ALPROTO_NTP) != 0 {
return -1;
}
let ctx = SCDetectU8Parse(raw) as *mut c_void;
if ctx.is_null() {
return -1;
}
if SCSigMatchAppendSMToList(
de,
s,
G_NTP_VERSION_KW_ID,
ctx as *mut SigMatchCtx,
G_NTP_GENERIC_BUFFER_ID,
)
.is_null()
{
ntp_detect_version_free(std::ptr::null_mut(), ctx);
return -1;
}
return 0;
}
unsafe extern "C" fn ntp_detect_version_match(
_de: *mut DetectEngineThreadCtx, _f: *mut Flow, _flags: u8, _state: *mut c_void,
tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx,
) -> c_int {
let tx = cast_pointer!(tx, NTPTransaction);
let ctx = cast_pointer!(ctx, DetectUintData<u8>);
return SCDetectU8Match(tx.version, ctx);
}
unsafe extern "C" fn ntp_detect_version_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) {
let ctx = cast_pointer!(ctx, DetectUintData<u8>);
SCDetectU8Free(ctx);
}
pub(super) unsafe extern "C" fn detect_ntp_register() {
let kw = SCSigTableAppLiteElmt {
name: b"ntp.version\0".as_ptr() as *const libc::c_char,
desc: b"match NTP version\0".as_ptr() as *const libc::c_char,
url: b"/rules/ntp-keywords.html#ntp-version\0".as_ptr() as *const libc::c_char,
AppLayerTxMatch: Some(ntp_detect_version_match),
Setup: Some(ntp_detect_version_setup),
Free: Some(ntp_detect_version_free),
flags: SIGMATCH_INFO_UINT8,
};
G_NTP_VERSION_KW_ID = SCDetectHelperKeywordRegister(&kw);
G_NTP_GENERIC_BUFFER_ID = SCDetectHelperBufferProgressRegister(
b"ntp.generic\0".as_ptr() as *const libc::c_char,
ALPROTO_NTP,
STREAM_TOSERVER | STREAM_TOCLIENT,
1,
);
}

@ -19,5 +19,6 @@
// written by Pierre Chifflier <chifflier@wzdftpd.net>
pub mod detect;
pub mod log;
pub mod ntp;

@ -19,6 +19,7 @@
extern crate ntp_parser;
use self::ntp_parser::*;
use super::detect::detect_ntp_register;
use super::log::ntp_log_json;
use crate::applayer::{self, *};
use crate::core;
@ -33,7 +34,7 @@ use suricata_sys::sys::{
AppLayerParserState, AppProto, EveJsonTxLoggerRegistrationData,
SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger,
SCAppLayerProtoDetectConfProtoDetectionEnabled, SCOutputEvePreRegisterLogger,
SCOutputJsonLogDirection,
SCOutputJsonLogDirection, SCSigTablePreRegister,
};
#[derive(AppLayerEvent)]
@ -240,7 +241,7 @@ extern "C" fn ntp_tx_get_alstate_progress(
1
}
static mut ALPROTO_NTP: AppProto = ALPROTO_UNKNOWN;
pub(super) static mut ALPROTO_NTP: AppProto = ALPROTO_UNKNOWN;
extern "C" fn ntp_probing_parser(
_flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
@ -317,6 +318,7 @@ pub unsafe extern "C" fn SCRegisterNtpParser() {
LogTx: Some(ntp_log_json),
};
SCOutputEvePreRegisterLogger(reg_data);
SCSigTablePreRegister(Some(detect_ntp_register));
if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, ALPROTO_NTP);
}

Loading…
Cancel
Save