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