]> git.ipfire.org Git - people/ms/suricata.git/blob - rust/src/ntp/ntp.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / ntp / ntp.rs
1 /* Copyright (C) 2017-2020 Open Information Security Foundation
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
20 extern crate ntp_parser;
21 use self::ntp_parser::*;
22 use crate::core;
23 use crate::core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED};
24 use crate::applayer::{self, *};
25 use std;
26 use std::ffi::CString;
27
28 use nom;
29
30 #[derive(AppLayerEvent)]
31 pub enum NTPEvent {
32 UnsolicitedResponse ,
33 MalformedData,
34 NotRequest,
35 NotResponse,
36 }
37
38 pub struct NTPState {
39 /// List of transactions for this session
40 transactions: Vec<NTPTransaction>,
41
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)]
50 pub struct NTPTransaction {
51 /// The NTP reference ID
52 pub xid: u32,
53
54 /// The internal transaction id
55 id: u64,
56
57 tx_data: applayer::AppLayerTxData,
58 }
59
60 impl Transaction for NTPTransaction {
61 fn id(&self) -> u64 {
62 self.id
63 }
64 }
65
66 impl NTPState {
67 pub fn new() -> NTPState {
68 NTPState{
69 transactions: Vec::new(),
70 events: 0,
71 tx_id: 0,
72 }
73 }
74 }
75
76 impl State<NTPTransaction> for NTPState {
77 fn get_transactions(&self) -> &[NTPTransaction] {
78 &self.transactions
79 }
80 }
81
82 impl NTPState {
83 /// Parse an NTP request message
84 ///
85 /// Returns 0 if successful, or -1 on error
86 fn parse(&mut self, i: &[u8], _direction: u8) -> i32 {
87 match parse_ntp(i) {
88 Ok((_,ref msg)) => {
89 // SCLogDebug!("parse_ntp: {:?}",msg);
90 if msg.mode == NtpMode::SymmetricActive || msg.mode == NtpMode::Client {
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 }
96 0
97 },
98 Err(nom::Err::Incomplete(_)) => {
99 SCLogDebug!("Insufficient data while parsing NTP data");
100 self.set_event(NTPEvent::MalformedData);
101 -1
102 },
103 Err(_) => {
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) {
127 let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1);
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() {
137 tx.tx_data.set_event(event as u8);
138 self.events += 1;
139 }
140 }
141 }
142
143 impl NTPTransaction {
144 pub fn new(id: u64) -> NTPTransaction {
145 NTPTransaction {
146 xid: 0,
147 id: id,
148 tx_data: applayer::AppLayerTxData::new(),
149 }
150 }
151 }
152
153 /// Returns *mut NTPState
154 #[no_mangle]
155 pub extern "C" fn rs_ntp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
156 let state = NTPState::new();
157 let boxed = Box::new(state);
158 return Box::into_raw(boxed) as *mut _;
159 }
160
161 /// Params:
162 /// - state: *mut NTPState as void pointer
163 #[no_mangle]
164 pub extern "C" fn rs_ntp_state_free(state: *mut std::os::raw::c_void) {
165 let mut ntp_state = unsafe{ Box::from_raw(state as *mut NTPState) };
166 ntp_state.free();
167 }
168
169 #[no_mangle]
170 pub unsafe extern "C" fn rs_ntp_parse_request(_flow: *const core::Flow,
171 state: *mut std::os::raw::c_void,
172 _pstate: *mut std::os::raw::c_void,
173 input: *const u8,
174 input_len: u32,
175 _data: *const std::os::raw::c_void,
176 _flags: u8) -> AppLayerResult {
177 let buf = build_slice!(input,input_len as usize);
178 let state = cast_pointer!(state,NTPState);
179 if state.parse(buf, 0) < 0 {
180 return AppLayerResult::err();
181 }
182 AppLayerResult::ok()
183 }
184
185 #[no_mangle]
186 pub unsafe extern "C" fn rs_ntp_parse_response(_flow: *const core::Flow,
187 state: *mut std::os::raw::c_void,
188 _pstate: *mut std::os::raw::c_void,
189 input: *const u8,
190 input_len: u32,
191 _data: *const std::os::raw::c_void,
192 _flags: u8) -> AppLayerResult {
193 let buf = build_slice!(input,input_len as usize);
194 let state = cast_pointer!(state,NTPState);
195 if state.parse(buf, 1) < 0 {
196 return AppLayerResult::err();
197 }
198 AppLayerResult::ok()
199 }
200
201 #[no_mangle]
202 pub unsafe extern "C" fn rs_ntp_state_get_tx(state: *mut std::os::raw::c_void,
203 tx_id: u64)
204 -> *mut std::os::raw::c_void
205 {
206 let state = cast_pointer!(state,NTPState);
207 match state.get_tx_by_id(tx_id) {
208 Some(tx) => tx as *const _ as *mut _,
209 None => std::ptr::null_mut(),
210 }
211 }
212
213 #[no_mangle]
214 pub unsafe extern "C" fn rs_ntp_state_get_tx_count(state: *mut std::os::raw::c_void)
215 -> u64
216 {
217 let state = cast_pointer!(state,NTPState);
218 state.tx_id
219 }
220
221 #[no_mangle]
222 pub unsafe extern "C" fn rs_ntp_state_tx_free(state: *mut std::os::raw::c_void,
223 tx_id: u64)
224 {
225 let state = cast_pointer!(state,NTPState);
226 state.free_tx(tx_id);
227 }
228
229 #[no_mangle]
230 pub extern "C" fn rs_ntp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
231 _direction: u8)
232 -> std::os::raw::c_int
233 {
234 1
235 }
236
237 static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN;
238
239 #[no_mangle]
240 pub 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 {
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) {
248 Ok((_, ref msg)) => {
249 if msg.version == 3 || msg.version == 4 {
250 return alproto;
251 } else {
252 return unsafe{ALPROTO_FAILED};
253 }
254 },
255 Err(nom::Err::Incomplete(_)) => {
256 return ALPROTO_UNKNOWN;
257 },
258 Err(_) => {
259 return unsafe{ALPROTO_FAILED};
260 },
261 }
262 }
263
264 export_tx_data_get!(rs_ntp_get_tx_data, NTPTransaction);
265
266 const PARSER_NAME : &'static [u8] = b"ntp\0";
267
268 #[no_mangle]
269 pub unsafe extern "C" fn rs_register_ntp_parser() {
270 let default_port = CString::new("123").unwrap();
271 let parser = RustParser {
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,
275 probe_ts : Some(ntp_probing_parser),
276 probe_tc : Some(ntp_probing_parser),
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,
286 tx_comp_st_ts : 1,
287 tx_comp_st_tc : 1,
288 tx_get_progress : rs_ntp_tx_get_alstate_progress,
289 get_eventinfo : Some(NTPEvent::get_event_info),
290 get_eventinfo_byid : Some(NTPEvent::get_event_info_by_id),
291 localstorage_new : None,
292 localstorage_free : None,
293 get_files : None,
294 get_tx_iterator : Some(applayer::state_get_tx_iterator::<NTPState, NTPTransaction>),
295 get_tx_data : rs_ntp_get_tx_data,
296 apply_tx_config : None,
297 flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS,
298 truncate : None,
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 {
310 SCLogDebug!("Protocol detector and parser disabled for NTP.");
311 }
312 }
313
314
315 #[cfg(test)]
316 mod 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();
332 assert_eq!(0, state.parse(REQ, 0));
333 }
334 }