]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/ntp/ntp.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / ntp / ntp.rs
CommitLineData
2f5834cd 1/* Copyright (C) 2017-2020 Open Information Security Foundation
efe11dc3
PC
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18// written by Pierre Chifflier <chifflier@wzdftpd.net>
19
20extern crate ntp_parser;
21use self::ntp_parser::*;
42e5065a
JI
22use crate::core;
23use crate::core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED};
2f5834cd 24use crate::applayer::{self, *};
efe11dc3 25use std;
92561837 26use std::ffi::CString;
efe11dc3 27
13b73997 28use nom;
efe11dc3 29
92561837 30#[derive(AppLayerEvent)]
efe11dc3 31pub enum NTPEvent {
92561837 32 UnsolicitedResponse ,
efe11dc3
PC
33 MalformedData,
34 NotRequest,
35 NotResponse,
36}
37
efe11dc3
PC
38pub struct NTPState {
39 /// List of transactions for this session
40 transactions: Vec<NTPTransaction>,
41
efe11dc3
PC
42 /// Events counter
43 events: u16,
44
45 /// tx counter for assigning incrementing id's to tx's
46 tx_id: u64,
47}
48
49#[derive(Debug)]
50pub struct NTPTransaction {
51 /// The NTP reference ID
52 pub xid: u32,
53
54 /// The internal transaction id
55 id: u64,
56
cc1210c9 57 tx_data: applayer::AppLayerTxData,
efe11dc3
PC
58}
59
ef0c3519
JI
60impl Transaction for NTPTransaction {
61 fn id(&self) -> u64 {
62 self.id
63 }
64}
efe11dc3
PC
65
66impl NTPState {
67 pub fn new() -> NTPState {
68 NTPState{
69 transactions: Vec::new(),
efe11dc3
PC
70 events: 0,
71 tx_id: 0,
72 }
73 }
74}
75
ef0c3519
JI
76impl State<NTPTransaction> for NTPState {
77 fn get_transactions(&self) -> &[NTPTransaction] {
78 &self.transactions
79 }
80}
81
efe11dc3 82impl NTPState {
8a0549c4
PC
83 /// Parse an NTP request message
84 ///
44d3f264 85 /// Returns 0 if successful, or -1 on error
ae10a92b 86 fn parse(&mut self, i: &[u8], _direction: u8) -> i32 {
efe11dc3 87 match parse_ntp(i) {
13b73997 88 Ok((_,ref msg)) => {
efe11dc3 89 // SCLogDebug!("parse_ntp: {:?}",msg);
2d1c4420 90 if msg.mode == NtpMode::SymmetricActive || msg.mode == NtpMode::Client {
efe11dc3
PC
91 let mut tx = self.new_tx();
92 // use the reference id as identifier
93 tx.xid = msg.ref_id;
94 self.transactions.push(tx);
95 }
3bcf948a 96 0
efe11dc3 97 },
13b73997 98 Err(nom::Err::Incomplete(_)) => {
efe11dc3
PC
99 SCLogDebug!("Insufficient data while parsing NTP data");
100 self.set_event(NTPEvent::MalformedData);
101 -1
102 },
13b73997 103 Err(_) => {
efe11dc3
PC
104 SCLogDebug!("Error while parsing NTP data");
105 self.set_event(NTPEvent::MalformedData);
106 -1
107 },
108 }
109 }
110
111 fn free(&mut self) {
112 // All transactions are freed when the `transactions` object is freed.
113 // But let's be explicit
114 self.transactions.clear();
115 }
116
117 fn new_tx(&mut self) -> NTPTransaction {
118 self.tx_id += 1;
119 NTPTransaction::new(self.tx_id)
120 }
121
122 pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NTPTransaction> {
123 self.transactions.iter().find(|&tx| tx.id == tx_id + 1)
124 }
125
126 fn free_tx(&mut self, tx_id: u64) {
69cf5c9e 127 let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1);
efe11dc3
PC
128 debug_assert!(tx != None);
129 if let Some(idx) = tx {
130 let _ = self.transactions.remove(idx);
131 }
132 }
133
134 /// Set an event. The event is set on the most recent transaction.
135 pub fn set_event(&mut self, event: NTPEvent) {
136 if let Some(tx) = self.transactions.last_mut() {
7732efbe 137 tx.tx_data.set_event(event as u8);
efe11dc3
PC
138 self.events += 1;
139 }
140 }
141}
142
143impl NTPTransaction {
144 pub fn new(id: u64) -> NTPTransaction {
145 NTPTransaction {
146 xid: 0,
147 id: id,
cc1210c9 148 tx_data: applayer::AppLayerTxData::new(),
efe11dc3
PC
149 }
150 }
efe11dc3
PC
151}
152
efe11dc3
PC
153/// Returns *mut NTPState
154#[no_mangle]
547d6c2d 155pub extern "C" fn rs_ntp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
efe11dc3
PC
156 let state = NTPState::new();
157 let boxed = Box::new(state);
53413f2d 158 return Box::into_raw(boxed) as *mut _;
efe11dc3
PC
159}
160
161/// Params:
162/// - state: *mut NTPState as void pointer
163#[no_mangle]
3f6624bf 164pub extern "C" fn rs_ntp_state_free(state: *mut std::os::raw::c_void) {
53413f2d 165 let mut ntp_state = unsafe{ Box::from_raw(state as *mut NTPState) };
efe11dc3
PC
166 ntp_state.free();
167}
168
169#[no_mangle]
363b5f99 170pub unsafe extern "C" fn rs_ntp_parse_request(_flow: *const core::Flow,
3f6624bf
VJ
171 state: *mut std::os::raw::c_void,
172 _pstate: *mut std::os::raw::c_void,
bf1bd407 173 input: *const u8,
efe11dc3 174 input_len: u32,
3f6624bf 175 _data: *const std::os::raw::c_void,
44d3f264 176 _flags: u8) -> AppLayerResult {
83808bbd
PC
177 let buf = build_slice!(input,input_len as usize);
178 let state = cast_pointer!(state,NTPState);
44d3f264
VJ
179 if state.parse(buf, 0) < 0 {
180 return AppLayerResult::err();
181 }
182 AppLayerResult::ok()
efe11dc3
PC
183}
184
185#[no_mangle]
363b5f99 186pub unsafe extern "C" fn rs_ntp_parse_response(_flow: *const core::Flow,
3f6624bf
VJ
187 state: *mut std::os::raw::c_void,
188 _pstate: *mut std::os::raw::c_void,
bf1bd407 189 input: *const u8,
efe11dc3 190 input_len: u32,
3f6624bf 191 _data: *const std::os::raw::c_void,
44d3f264 192 _flags: u8) -> AppLayerResult {
83808bbd
PC
193 let buf = build_slice!(input,input_len as usize);
194 let state = cast_pointer!(state,NTPState);
44d3f264
VJ
195 if state.parse(buf, 1) < 0 {
196 return AppLayerResult::err();
197 }
198 AppLayerResult::ok()
efe11dc3
PC
199}
200
201#[no_mangle]
363b5f99 202pub unsafe extern "C" fn rs_ntp_state_get_tx(state: *mut std::os::raw::c_void,
bf1bd407 203 tx_id: u64)
3f6624bf 204 -> *mut std::os::raw::c_void
efe11dc3 205{
83808bbd 206 let state = cast_pointer!(state,NTPState);
efe11dc3 207 match state.get_tx_by_id(tx_id) {
53413f2d 208 Some(tx) => tx as *const _ as *mut _,
efe11dc3
PC
209 None => std::ptr::null_mut(),
210 }
211}
212
213#[no_mangle]
363b5f99 214pub unsafe extern "C" fn rs_ntp_state_get_tx_count(state: *mut std::os::raw::c_void)
bf1bd407 215 -> u64
efe11dc3 216{
83808bbd 217 let state = cast_pointer!(state,NTPState);
efe11dc3
PC
218 state.tx_id
219}
220
221#[no_mangle]
363b5f99 222pub unsafe extern "C" fn rs_ntp_state_tx_free(state: *mut std::os::raw::c_void,
bf1bd407 223 tx_id: u64)
efe11dc3 224{
83808bbd 225 let state = cast_pointer!(state,NTPState);
efe11dc3
PC
226 state.free_tx(tx_id);
227}
228
efe11dc3 229#[no_mangle]
3f6624bf 230pub extern "C" fn rs_ntp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
bf1bd407 231 _direction: u8)
3f6624bf 232 -> std::os::raw::c_int
efe11dc3
PC
233{
234 1
235}
236
83808bbd
PC
237static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN;
238
239#[no_mangle]
422e4892
VJ
240pub extern "C" fn ntp_probing_parser(_flow: *const Flow,
241 _direction: u8,
242 input:*const u8, input_len: u32,
243 _rdir: *mut u8) -> AppProto
244{
83808bbd
PC
245 let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, input_len as usize) };
246 let alproto = unsafe{ ALPROTO_NTP };
247 match parse_ntp(slice) {
13b73997 248 Ok((_, ref msg)) => {
83808bbd
PC
249 if msg.version == 3 || msg.version == 4 {
250 return alproto;
251 } else {
252 return unsafe{ALPROTO_FAILED};
253 }
254 },
13b73997 255 Err(nom::Err::Incomplete(_)) => {
83808bbd
PC
256 return ALPROTO_UNKNOWN;
257 },
13b73997 258 Err(_) => {
83808bbd
PC
259 return unsafe{ALPROTO_FAILED};
260 },
261 }
262}
263
cc1210c9
VJ
264export_tx_data_get!(rs_ntp_get_tx_data, NTPTransaction);
265
4b655558
PC
266const PARSER_NAME : &'static [u8] = b"ntp\0";
267
83808bbd
PC
268#[no_mangle]
269pub unsafe extern "C" fn rs_register_ntp_parser() {
83808bbd
PC
270 let default_port = CString::new("123").unwrap();
271 let parser = RustParser {
7560b755
JL
272 name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
273 default_port : default_port.as_ptr(),
274 ipproto : core::IPPROTO_UDP,
66632465
PA
275 probe_ts : Some(ntp_probing_parser),
276 probe_tc : Some(ntp_probing_parser),
7560b755
JL
277 min_depth : 0,
278 max_depth : 16,
279 state_new : rs_ntp_state_new,
280 state_free : rs_ntp_state_free,
281 tx_free : rs_ntp_state_tx_free,
282 parse_ts : rs_ntp_parse_request,
283 parse_tc : rs_ntp_parse_response,
284 get_tx_count : rs_ntp_state_get_tx_count,
285 get_tx : rs_ntp_state_get_tx,
efc9a7a3
VJ
286 tx_comp_st_ts : 1,
287 tx_comp_st_tc : 1,
7560b755 288 tx_get_progress : rs_ntp_tx_get_alstate_progress,
92561837
JI
289 get_eventinfo : Some(NTPEvent::get_event_info),
290 get_eventinfo_byid : Some(NTPEvent::get_event_info_by_id),
7560b755
JL
291 localstorage_new : None,
292 localstorage_free : None,
7560b755 293 get_files : None,
ef0c3519 294 get_tx_iterator : Some(applayer::state_get_tx_iterator::<NTPState, NTPTransaction>),
c94a5e63 295 get_tx_data : rs_ntp_get_tx_data,
5665fc83 296 apply_tx_config : None,
984d3c7f 297 flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS,
4da0d9bd 298 truncate : None,
83808bbd
PC
299 };
300
301 let ip_proto_str = CString::new("udp").unwrap();
302 if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
303 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
304 // store the allocated ID for the probe function
305 ALPROTO_NTP = alproto;
306 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
307 let _ = AppLayerRegisterParser(&parser, alproto);
308 }
309 } else {
7560b755 310 SCLogDebug!("Protocol detector and parser disabled for NTP.");
83808bbd
PC
311 }
312}
313
314
8a0549c4
PC
315#[cfg(test)]
316mod tests {
317 use super::NTPState;
318
319 #[test]
320 fn test_ntp_parse_request_valid() {
321 // A UDP NTP v4 request, in client mode
322 const REQ : &[u8] = &[
323 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x18, 0x57, 0xab, 0xc3, 0x4a, 0x5f, 0x2c, 0xfe
329 ];
330
331 let mut state = NTPState::new();
3bcf948a 332 assert_eq!(0, state.parse(REQ, 0));
8a0549c4
PC
333 }
334}