]>
Commit | Line | Data |
---|---|---|
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 | ||
20 | extern crate ntp_parser; | |
21 | use self::ntp_parser::*; | |
42e5065a JI |
22 | use crate::core; |
23 | use crate::core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED}; | |
2f5834cd | 24 | use crate::applayer::{self, *}; |
efe11dc3 | 25 | use std; |
83808bbd | 26 | use std::ffi::{CStr,CString}; |
efe11dc3 | 27 | |
13b73997 | 28 | use nom; |
efe11dc3 PC |
29 | |
30 | #[repr(u32)] | |
31 | pub enum NTPEvent { | |
32 | UnsolicitedResponse = 0, | |
33 | MalformedData, | |
34 | NotRequest, | |
35 | NotResponse, | |
36 | } | |
37 | ||
7560b755 JL |
38 | impl NTPEvent { |
39 | fn from_i32(value: i32) -> Option<NTPEvent> { | |
40 | match value { | |
41 | 0 => Some(NTPEvent::UnsolicitedResponse), | |
42 | 1 => Some(NTPEvent::MalformedData), | |
43 | 2 => Some(NTPEvent::NotRequest), | |
44 | 3 => Some(NTPEvent::NotResponse), | |
45 | _ => None, | |
46 | } | |
47 | } | |
48 | } | |
efe11dc3 PC |
49 | |
50 | pub struct NTPState { | |
51 | /// List of transactions for this session | |
52 | transactions: Vec<NTPTransaction>, | |
53 | ||
efe11dc3 PC |
54 | /// Events counter |
55 | events: u16, | |
56 | ||
57 | /// tx counter for assigning incrementing id's to tx's | |
58 | tx_id: u64, | |
59 | } | |
60 | ||
61 | #[derive(Debug)] | |
62 | pub struct NTPTransaction { | |
63 | /// The NTP reference ID | |
64 | pub xid: u32, | |
65 | ||
66 | /// The internal transaction id | |
67 | id: u64, | |
68 | ||
69 | /// The detection engine state, if present | |
70 | de_state: Option<*mut core::DetectEngineState>, | |
71 | ||
72 | /// The events associated with this transaction | |
73 | events: *mut core::AppLayerDecoderEvents, | |
74 | ||
cc1210c9 | 75 | tx_data: applayer::AppLayerTxData, |
efe11dc3 PC |
76 | } |
77 | ||
78 | ||
79 | ||
80 | impl NTPState { | |
81 | pub fn new() -> NTPState { | |
82 | NTPState{ | |
83 | transactions: Vec::new(), | |
efe11dc3 PC |
84 | events: 0, |
85 | tx_id: 0, | |
86 | } | |
87 | } | |
88 | } | |
89 | ||
90 | impl NTPState { | |
8a0549c4 PC |
91 | /// Parse an NTP request message |
92 | /// | |
44d3f264 | 93 | /// Returns 0 if successful, or -1 on error |
ae10a92b | 94 | fn parse(&mut self, i: &[u8], _direction: u8) -> i32 { |
efe11dc3 | 95 | match parse_ntp(i) { |
13b73997 | 96 | Ok((_,ref msg)) => { |
efe11dc3 | 97 | // SCLogDebug!("parse_ntp: {:?}",msg); |
2d1c4420 | 98 | if msg.mode == NtpMode::SymmetricActive || msg.mode == NtpMode::Client { |
efe11dc3 PC |
99 | let mut tx = self.new_tx(); |
100 | // use the reference id as identifier | |
101 | tx.xid = msg.ref_id; | |
102 | self.transactions.push(tx); | |
103 | } | |
3bcf948a | 104 | 0 |
efe11dc3 | 105 | }, |
13b73997 | 106 | Err(nom::Err::Incomplete(_)) => { |
efe11dc3 PC |
107 | SCLogDebug!("Insufficient data while parsing NTP data"); |
108 | self.set_event(NTPEvent::MalformedData); | |
109 | -1 | |
110 | }, | |
13b73997 | 111 | Err(_) => { |
efe11dc3 PC |
112 | SCLogDebug!("Error while parsing NTP data"); |
113 | self.set_event(NTPEvent::MalformedData); | |
114 | -1 | |
115 | }, | |
116 | } | |
117 | } | |
118 | ||
119 | fn free(&mut self) { | |
120 | // All transactions are freed when the `transactions` object is freed. | |
121 | // But let's be explicit | |
122 | self.transactions.clear(); | |
123 | } | |
124 | ||
125 | fn new_tx(&mut self) -> NTPTransaction { | |
126 | self.tx_id += 1; | |
127 | NTPTransaction::new(self.tx_id) | |
128 | } | |
129 | ||
130 | pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NTPTransaction> { | |
131 | self.transactions.iter().find(|&tx| tx.id == tx_id + 1) | |
132 | } | |
133 | ||
134 | fn free_tx(&mut self, tx_id: u64) { | |
135 | let tx = self.transactions.iter().position(|ref tx| tx.id == tx_id + 1); | |
136 | debug_assert!(tx != None); | |
137 | if let Some(idx) = tx { | |
138 | let _ = self.transactions.remove(idx); | |
139 | } | |
140 | } | |
141 | ||
142 | /// Set an event. The event is set on the most recent transaction. | |
143 | pub fn set_event(&mut self, event: NTPEvent) { | |
144 | if let Some(tx) = self.transactions.last_mut() { | |
145 | let ev = event as u8; | |
146 | core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev); | |
147 | self.events += 1; | |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | impl NTPTransaction { | |
153 | pub fn new(id: u64) -> NTPTransaction { | |
154 | NTPTransaction { | |
155 | xid: 0, | |
156 | id: id, | |
157 | de_state: None, | |
158 | events: std::ptr::null_mut(), | |
cc1210c9 | 159 | tx_data: applayer::AppLayerTxData::new(), |
efe11dc3 PC |
160 | } |
161 | } | |
162 | ||
163 | fn free(&mut self) { | |
164 | if self.events != std::ptr::null_mut() { | |
165 | core::sc_app_layer_decoder_events_free_events(&mut self.events); | |
166 | } | |
167 | } | |
168 | } | |
169 | ||
170 | impl Drop for NTPTransaction { | |
171 | fn drop(&mut self) { | |
172 | self.free(); | |
173 | } | |
174 | } | |
175 | ||
efe11dc3 PC |
176 | /// Returns *mut NTPState |
177 | #[no_mangle] | |
547d6c2d | 178 | 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 { |
efe11dc3 PC |
179 | let state = NTPState::new(); |
180 | let boxed = Box::new(state); | |
181 | return unsafe{std::mem::transmute(boxed)}; | |
182 | } | |
183 | ||
184 | /// Params: | |
185 | /// - state: *mut NTPState as void pointer | |
186 | #[no_mangle] | |
3f6624bf | 187 | pub extern "C" fn rs_ntp_state_free(state: *mut std::os::raw::c_void) { |
efe11dc3 PC |
188 | // Just unbox... |
189 | let mut ntp_state: Box<NTPState> = unsafe{std::mem::transmute(state)}; | |
190 | ntp_state.free(); | |
191 | } | |
192 | ||
193 | #[no_mangle] | |
194 | pub extern "C" fn rs_ntp_parse_request(_flow: *const core::Flow, | |
3f6624bf VJ |
195 | state: *mut std::os::raw::c_void, |
196 | _pstate: *mut std::os::raw::c_void, | |
bf1bd407 | 197 | input: *const u8, |
efe11dc3 | 198 | input_len: u32, |
3f6624bf | 199 | _data: *const std::os::raw::c_void, |
44d3f264 | 200 | _flags: u8) -> AppLayerResult { |
83808bbd PC |
201 | let buf = build_slice!(input,input_len as usize); |
202 | let state = cast_pointer!(state,NTPState); | |
44d3f264 VJ |
203 | if state.parse(buf, 0) < 0 { |
204 | return AppLayerResult::err(); | |
205 | } | |
206 | AppLayerResult::ok() | |
efe11dc3 PC |
207 | } |
208 | ||
209 | #[no_mangle] | |
210 | pub extern "C" fn rs_ntp_parse_response(_flow: *const core::Flow, | |
3f6624bf VJ |
211 | state: *mut std::os::raw::c_void, |
212 | _pstate: *mut std::os::raw::c_void, | |
bf1bd407 | 213 | input: *const u8, |
efe11dc3 | 214 | input_len: u32, |
3f6624bf | 215 | _data: *const std::os::raw::c_void, |
44d3f264 | 216 | _flags: u8) -> AppLayerResult { |
83808bbd PC |
217 | let buf = build_slice!(input,input_len as usize); |
218 | let state = cast_pointer!(state,NTPState); | |
44d3f264 VJ |
219 | if state.parse(buf, 1) < 0 { |
220 | return AppLayerResult::err(); | |
221 | } | |
222 | AppLayerResult::ok() | |
efe11dc3 PC |
223 | } |
224 | ||
225 | #[no_mangle] | |
3f6624bf | 226 | pub extern "C" fn rs_ntp_state_get_tx(state: *mut std::os::raw::c_void, |
bf1bd407 | 227 | tx_id: u64) |
3f6624bf | 228 | -> *mut std::os::raw::c_void |
efe11dc3 | 229 | { |
83808bbd | 230 | let state = cast_pointer!(state,NTPState); |
efe11dc3 PC |
231 | match state.get_tx_by_id(tx_id) { |
232 | Some(tx) => unsafe{std::mem::transmute(tx)}, | |
233 | None => std::ptr::null_mut(), | |
234 | } | |
235 | } | |
236 | ||
237 | #[no_mangle] | |
3f6624bf | 238 | pub extern "C" fn rs_ntp_state_get_tx_count(state: *mut std::os::raw::c_void) |
bf1bd407 | 239 | -> u64 |
efe11dc3 | 240 | { |
83808bbd | 241 | let state = cast_pointer!(state,NTPState); |
efe11dc3 PC |
242 | state.tx_id |
243 | } | |
244 | ||
245 | #[no_mangle] | |
3f6624bf | 246 | pub extern "C" fn rs_ntp_state_tx_free(state: *mut std::os::raw::c_void, |
bf1bd407 | 247 | tx_id: u64) |
efe11dc3 | 248 | { |
83808bbd | 249 | let state = cast_pointer!(state,NTPState); |
efe11dc3 PC |
250 | state.free_tx(tx_id); |
251 | } | |
252 | ||
efe11dc3 | 253 | #[no_mangle] |
3f6624bf | 254 | pub extern "C" fn rs_ntp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void, |
bf1bd407 | 255 | _direction: u8) |
3f6624bf | 256 | -> std::os::raw::c_int |
efe11dc3 PC |
257 | { |
258 | 1 | |
259 | } | |
260 | ||
efe11dc3 PC |
261 | #[no_mangle] |
262 | pub extern "C" fn rs_ntp_state_set_tx_detect_state( | |
3f6624bf VJ |
263 | tx: *mut std::os::raw::c_void, |
264 | de_state: &mut core::DetectEngineState) -> std::os::raw::c_int | |
efe11dc3 | 265 | { |
83808bbd | 266 | let tx = cast_pointer!(tx,NTPTransaction); |
efe11dc3 | 267 | tx.de_state = Some(de_state); |
83808bbd | 268 | 0 |
efe11dc3 PC |
269 | } |
270 | ||
271 | #[no_mangle] | |
272 | pub extern "C" fn rs_ntp_state_get_tx_detect_state( | |
3f6624bf | 273 | tx: *mut std::os::raw::c_void) |
efe11dc3 PC |
274 | -> *mut core::DetectEngineState |
275 | { | |
83808bbd | 276 | let tx = cast_pointer!(tx,NTPTransaction); |
efe11dc3 PC |
277 | match tx.de_state { |
278 | Some(ds) => ds, | |
279 | None => std::ptr::null_mut(), | |
280 | } | |
281 | } | |
282 | ||
7560b755 JL |
283 | #[no_mangle] |
284 | pub extern "C" fn rs_ntp_state_get_event_info_by_id(event_id: std::os::raw::c_int, | |
285 | event_name: *mut *const std::os::raw::c_char, | |
286 | event_type: *mut core::AppLayerEventType) | |
287 | -> i8 | |
288 | { | |
289 | if let Some(e) = NTPEvent::from_i32(event_id as i32) { | |
290 | let estr = match e { | |
291 | NTPEvent::UnsolicitedResponse => { "unsolicited_response\0" }, | |
292 | NTPEvent::MalformedData => { "malformed_data\0" }, | |
293 | NTPEvent::NotRequest => { "not_request\0" }, | |
294 | NTPEvent::NotResponse => { "not_response\0" }, | |
295 | }; | |
296 | unsafe{ | |
297 | *event_name = estr.as_ptr() as *const std::os::raw::c_char; | |
298 | *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; | |
299 | }; | |
300 | 0 | |
301 | } else { | |
302 | -1 | |
303 | } | |
304 | } | |
efe11dc3 | 305 | |
efe11dc3 | 306 | #[no_mangle] |
3f6624bf | 307 | pub extern "C" fn rs_ntp_state_get_events(tx: *mut std::os::raw::c_void) |
efe11dc3 PC |
308 | -> *mut core::AppLayerDecoderEvents |
309 | { | |
d568e7fa JL |
310 | let tx = cast_pointer!(tx, NTPTransaction); |
311 | return tx.events; | |
efe11dc3 PC |
312 | } |
313 | ||
314 | #[no_mangle] | |
3f6624bf VJ |
315 | pub extern "C" fn rs_ntp_state_get_event_info(event_name: *const std::os::raw::c_char, |
316 | event_id: *mut std::os::raw::c_int, | |
efe11dc3 | 317 | event_type: *mut core::AppLayerEventType) |
3f6624bf | 318 | -> std::os::raw::c_int |
efe11dc3 PC |
319 | { |
320 | if event_name == std::ptr::null() { return -1; } | |
321 | let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) }; | |
322 | let event = match c_event_name.to_str() { | |
323 | Ok(s) => { | |
324 | match s { | |
325 | "malformed_data" => NTPEvent::MalformedData as i32, | |
326 | _ => -1, // unknown event | |
327 | } | |
328 | }, | |
329 | Err(_) => -1, // UTF-8 conversion failed | |
330 | }; | |
331 | unsafe{ | |
332 | *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; | |
3f6624bf | 333 | *event_id = event as std::os::raw::c_int; |
efe11dc3 PC |
334 | }; |
335 | 0 | |
336 | } | |
8a0549c4 | 337 | |
83808bbd PC |
338 | |
339 | static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN; | |
340 | ||
341 | #[no_mangle] | |
422e4892 VJ |
342 | pub extern "C" fn ntp_probing_parser(_flow: *const Flow, |
343 | _direction: u8, | |
344 | input:*const u8, input_len: u32, | |
345 | _rdir: *mut u8) -> AppProto | |
346 | { | |
83808bbd PC |
347 | let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, input_len as usize) }; |
348 | let alproto = unsafe{ ALPROTO_NTP }; | |
349 | match parse_ntp(slice) { | |
13b73997 | 350 | Ok((_, ref msg)) => { |
83808bbd PC |
351 | if msg.version == 3 || msg.version == 4 { |
352 | return alproto; | |
353 | } else { | |
354 | return unsafe{ALPROTO_FAILED}; | |
355 | } | |
356 | }, | |
13b73997 | 357 | Err(nom::Err::Incomplete(_)) => { |
83808bbd PC |
358 | return ALPROTO_UNKNOWN; |
359 | }, | |
13b73997 | 360 | Err(_) => { |
83808bbd PC |
361 | return unsafe{ALPROTO_FAILED}; |
362 | }, | |
363 | } | |
364 | } | |
365 | ||
cc1210c9 VJ |
366 | export_tx_data_get!(rs_ntp_get_tx_data, NTPTransaction); |
367 | ||
4b655558 PC |
368 | const PARSER_NAME : &'static [u8] = b"ntp\0"; |
369 | ||
83808bbd PC |
370 | #[no_mangle] |
371 | pub unsafe extern "C" fn rs_register_ntp_parser() { | |
83808bbd PC |
372 | let default_port = CString::new("123").unwrap(); |
373 | let parser = RustParser { | |
7560b755 JL |
374 | name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
375 | default_port : default_port.as_ptr(), | |
376 | ipproto : core::IPPROTO_UDP, | |
66632465 PA |
377 | probe_ts : Some(ntp_probing_parser), |
378 | probe_tc : Some(ntp_probing_parser), | |
7560b755 JL |
379 | min_depth : 0, |
380 | max_depth : 16, | |
381 | state_new : rs_ntp_state_new, | |
382 | state_free : rs_ntp_state_free, | |
383 | tx_free : rs_ntp_state_tx_free, | |
384 | parse_ts : rs_ntp_parse_request, | |
385 | parse_tc : rs_ntp_parse_response, | |
386 | get_tx_count : rs_ntp_state_get_tx_count, | |
387 | get_tx : rs_ntp_state_get_tx, | |
efc9a7a3 VJ |
388 | tx_comp_st_ts : 1, |
389 | tx_comp_st_tc : 1, | |
7560b755 | 390 | tx_get_progress : rs_ntp_tx_get_alstate_progress, |
7560b755 JL |
391 | get_de_state : rs_ntp_state_get_tx_detect_state, |
392 | set_de_state : rs_ntp_state_set_tx_detect_state, | |
393 | get_events : Some(rs_ntp_state_get_events), | |
394 | get_eventinfo : Some(rs_ntp_state_get_event_info), | |
395 | get_eventinfo_byid : Some(rs_ntp_state_get_event_info_by_id), | |
396 | localstorage_new : None, | |
397 | localstorage_free : None, | |
7560b755 JL |
398 | get_files : None, |
399 | get_tx_iterator : None, | |
c94a5e63 | 400 | get_tx_data : rs_ntp_get_tx_data, |
5665fc83 | 401 | apply_tx_config : None, |
984d3c7f | 402 | flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS, |
4da0d9bd | 403 | truncate : None, |
83808bbd PC |
404 | }; |
405 | ||
406 | let ip_proto_str = CString::new("udp").unwrap(); | |
407 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
408 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); | |
409 | // store the allocated ID for the probe function | |
410 | ALPROTO_NTP = alproto; | |
411 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
412 | let _ = AppLayerRegisterParser(&parser, alproto); | |
413 | } | |
414 | } else { | |
7560b755 | 415 | SCLogDebug!("Protocol detector and parser disabled for NTP."); |
83808bbd PC |
416 | } |
417 | } | |
418 | ||
419 | ||
8a0549c4 PC |
420 | #[cfg(test)] |
421 | mod tests { | |
422 | use super::NTPState; | |
423 | ||
424 | #[test] | |
425 | fn test_ntp_parse_request_valid() { | |
426 | // A UDP NTP v4 request, in client mode | |
427 | const REQ : &[u8] = &[ | |
428 | 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
429 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
430 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
431 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
432 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
433 | 0x18, 0x57, 0xab, 0xc3, 0x4a, 0x5f, 0x2c, 0xfe | |
434 | ]; | |
435 | ||
436 | let mut state = NTPState::new(); | |
3bcf948a | 437 | assert_eq!(0, state.parse(REQ, 0)); |
8a0549c4 PC |
438 | } |
439 | } |