]>
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; |
92561837 | 26 | use std::ffi::CString; |
efe11dc3 | 27 | |
13b73997 | 28 | use nom; |
efe11dc3 | 29 | |
92561837 | 30 | #[derive(AppLayerEvent)] |
efe11dc3 | 31 | pub enum NTPEvent { |
92561837 | 32 | UnsolicitedResponse , |
efe11dc3 PC |
33 | MalformedData, |
34 | NotRequest, | |
35 | NotResponse, | |
36 | } | |
37 | ||
efe11dc3 PC |
38 | pub 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)] | |
50 | pub 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 |
60 | impl Transaction for NTPTransaction { |
61 | fn id(&self) -> u64 { | |
62 | self.id | |
63 | } | |
64 | } | |
efe11dc3 PC |
65 | |
66 | impl 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 |
76 | impl State<NTPTransaction> for NTPState { |
77 | fn get_transactions(&self) -> &[NTPTransaction] { | |
78 | &self.transactions | |
79 | } | |
80 | } | |
81 | ||
efe11dc3 | 82 | impl 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 | ||
143 | impl 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 | 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 { |
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 | 164 | pub 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 | 170 | pub 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 | 186 | pub 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 | 202 | pub 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 | 214 | pub 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 | 222 | pub 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 | 230 | pub 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 |
237 | static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN; |
238 | ||
239 | #[no_mangle] | |
422e4892 VJ |
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 | { | |
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 |
264 | export_tx_data_get!(rs_ntp_get_tx_data, NTPTransaction); |
265 | ||
4b655558 PC |
266 | const PARSER_NAME : &'static [u8] = b"ntp\0"; |
267 | ||
83808bbd PC |
268 | #[no_mangle] |
269 | pub 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)] |
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(); | |
3bcf948a | 332 | assert_eq!(0, state.parse(REQ, 0)); |
8a0549c4 PC |
333 | } |
334 | } |