|
|
|
|
@ -17,34 +17,34 @@
|
|
|
|
|
|
|
|
|
|
// written by Clément Galland <clement.galland@epita.fr>
|
|
|
|
|
|
|
|
|
|
use std::str;
|
|
|
|
|
use std;
|
|
|
|
|
use nom8::IResult;
|
|
|
|
|
use nom8::Parser;
|
|
|
|
|
use nom8::combinator::map_res;
|
|
|
|
|
use nom8::bytes::streaming::{tag, take_while};
|
|
|
|
|
use nom8::combinator::map_res;
|
|
|
|
|
use nom8::number::streaming::be_u8;
|
|
|
|
|
use nom8::IResult;
|
|
|
|
|
use nom8::Parser;
|
|
|
|
|
use std;
|
|
|
|
|
use std::str;
|
|
|
|
|
|
|
|
|
|
use crate::applayer::{AppLayerTxData, AppLayerStateData};
|
|
|
|
|
use crate::applayer::{AppLayerStateData, AppLayerTxData};
|
|
|
|
|
|
|
|
|
|
const READREQUEST: u8 = 1;
|
|
|
|
|
const READREQUEST: u8 = 1;
|
|
|
|
|
const WRITEREQUEST: u8 = 2;
|
|
|
|
|
const DATA: u8 = 3;
|
|
|
|
|
const ACK: u8 = 4;
|
|
|
|
|
const ERROR: u8 = 5;
|
|
|
|
|
const DATA: u8 = 3;
|
|
|
|
|
const ACK: u8 = 4;
|
|
|
|
|
const ERROR: u8 = 5;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
|
pub struct TFTPTransaction {
|
|
|
|
|
pub opcode : u8,
|
|
|
|
|
pub filename : String,
|
|
|
|
|
pub mode : String,
|
|
|
|
|
pub opcode: u8,
|
|
|
|
|
pub filename: String,
|
|
|
|
|
pub mode: String,
|
|
|
|
|
id: u64,
|
|
|
|
|
tx_data: AppLayerTxData,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct TFTPState {
|
|
|
|
|
state_data: AppLayerStateData,
|
|
|
|
|
pub transactions : Vec<TFTPTransaction>,
|
|
|
|
|
pub transactions: Vec<TFTPTransaction>,
|
|
|
|
|
/// tx counter for assigning incrementing id's to tx's
|
|
|
|
|
tx_id: u64,
|
|
|
|
|
}
|
|
|
|
|
@ -64,32 +64,36 @@ impl TFTPState {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TFTPTransaction {
|
|
|
|
|
pub fn new(opcode : u8, filename : String, mode : String) -> TFTPTransaction {
|
|
|
|
|
pub fn new(opcode: u8, filename: String, mode: String) -> TFTPTransaction {
|
|
|
|
|
TFTPTransaction {
|
|
|
|
|
opcode,
|
|
|
|
|
filename,
|
|
|
|
|
mode : mode.to_lowercase(),
|
|
|
|
|
id : 0,
|
|
|
|
|
mode: mode.to_lowercase(),
|
|
|
|
|
id: 0,
|
|
|
|
|
tx_data: AppLayerTxData::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn is_mode_ok(&self) -> bool {
|
|
|
|
|
match self.mode.as_str() {
|
|
|
|
|
"netascii" | "mail" | "octet" => true,
|
|
|
|
|
_ => false
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn is_opcode_ok(&self) -> bool {
|
|
|
|
|
match self.opcode {
|
|
|
|
|
READREQUEST | WRITEREQUEST | ACK | DATA | ERROR => true,
|
|
|
|
|
_ => false
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub extern "C" fn SCTftpStateAlloc() -> *mut std::os::raw::c_void {
|
|
|
|
|
let state = TFTPState { state_data: AppLayerStateData::default(), transactions : Vec::new(), tx_id: 0, };
|
|
|
|
|
let state = TFTPState {
|
|
|
|
|
state_data: AppLayerStateData::default(),
|
|
|
|
|
transactions: Vec::new(),
|
|
|
|
|
tx_id: 0,
|
|
|
|
|
};
|
|
|
|
|
let boxed = Box::new(state);
|
|
|
|
|
return Box::into_raw(boxed) as *mut _;
|
|
|
|
|
}
|
|
|
|
|
@ -100,17 +104,15 @@ pub extern "C" fn SCTftpStateFree(state: *mut std::os::raw::c_void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub extern "C" fn SCTftpStateTxFree(state: &mut TFTPState,
|
|
|
|
|
tx_id: u64) {
|
|
|
|
|
pub extern "C" fn SCTftpStateTxFree(state: &mut TFTPState, tx_id: u64) {
|
|
|
|
|
state.free_tx(tx_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub extern "C" fn SCTftpGetTx(state: &mut TFTPState,
|
|
|
|
|
tx_id: u64) -> *mut std::os::raw::c_void {
|
|
|
|
|
pub extern "C" fn SCTftpGetTx(state: &mut TFTPState, tx_id: u64) -> *mut std::os::raw::c_void {
|
|
|
|
|
match state.get_tx_by_id(tx_id) {
|
|
|
|
|
Some(tx) => tx as *const _ as *mut _,
|
|
|
|
|
None => std::ptr::null_mut(),
|
|
|
|
|
None => std::ptr::null_mut(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -120,10 +122,7 @@ pub extern "C" fn SCTftpGetTxCnt(state: &mut TFTPState) -> u64 {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn getstr(i: &[u8]) -> IResult<&[u8], &str> {
|
|
|
|
|
map_res(
|
|
|
|
|
take_while(|c| c != 0),
|
|
|
|
|
str::from_utf8
|
|
|
|
|
).parse(i)
|
|
|
|
|
map_res(take_while(|c| c != 0), str::from_utf8).parse(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tftp_request(slice: &[u8]) -> IResult<&[u8], TFTPTransaction> {
|
|
|
|
|
@ -132,10 +131,10 @@ fn tftp_request(slice: &[u8]) -> IResult<&[u8], TFTPTransaction> {
|
|
|
|
|
let (i, filename) = getstr(i)?;
|
|
|
|
|
let (i, _) = tag(&[0u8][..]).parse(i)?;
|
|
|
|
|
let (i, mode) = getstr(i)?;
|
|
|
|
|
Ok((i,
|
|
|
|
|
TFTPTransaction::new(opcode, String::from(filename), String::from(mode))
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
Ok((
|
|
|
|
|
i,
|
|
|
|
|
TFTPTransaction::new(opcode, String::from(filename), String::from(mode)),
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_tftp_request(input: &[u8]) -> Option<TFTPTransaction> {
|
|
|
|
|
@ -156,9 +155,9 @@ fn parse_tftp_request(input: &[u8]) -> Option<TFTPTransaction> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn SCTftpParseRequest(state: &mut TFTPState,
|
|
|
|
|
input: *const u8,
|
|
|
|
|
len: u32) -> i64 {
|
|
|
|
|
pub unsafe extern "C" fn SCTftpParseRequest(
|
|
|
|
|
state: &mut TFTPState, input: *const u8, len: u32,
|
|
|
|
|
) -> i64 {
|
|
|
|
|
let buf = std::slice::from_raw_parts(input, len as usize);
|
|
|
|
|
match parse_tftp_request(buf) {
|
|
|
|
|
Some(mut tx) => {
|
|
|
|
|
@ -166,27 +165,23 @@ pub unsafe extern "C" fn SCTftpParseRequest(state: &mut TFTPState,
|
|
|
|
|
tx.id = state.tx_id;
|
|
|
|
|
state.transactions.push(tx);
|
|
|
|
|
0
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
-1
|
|
|
|
|
}
|
|
|
|
|
None => -1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn SCTftpGetTxData(
|
|
|
|
|
tx: *mut std::os::raw::c_void)
|
|
|
|
|
-> *mut suricata_sys::sys::AppLayerTxData
|
|
|
|
|
{
|
|
|
|
|
tx: *mut std::os::raw::c_void,
|
|
|
|
|
) -> *mut suricata_sys::sys::AppLayerTxData {
|
|
|
|
|
let tx = cast_pointer!(tx, TFTPTransaction);
|
|
|
|
|
return &mut tx.tx_data.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn SCTftpGetStateData(
|
|
|
|
|
state: *mut std::os::raw::c_void)
|
|
|
|
|
-> *mut AppLayerStateData
|
|
|
|
|
{
|
|
|
|
|
state: *mut std::os::raw::c_void,
|
|
|
|
|
) -> *mut AppLayerStateData {
|
|
|
|
|
let state = cast_pointer!(state, TFTPState);
|
|
|
|
|
return &mut state.state_data;
|
|
|
|
|
}
|
|
|
|
|
@ -195,25 +190,28 @@ pub unsafe extern "C" fn SCTftpGetStateData(
|
|
|
|
|
mod test {
|
|
|
|
|
use super::*;
|
|
|
|
|
static READ_REQUEST: [u8; 20] = [
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x6f,
|
|
|
|
|
0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
];
|
|
|
|
|
/* filename not terminated */
|
|
|
|
|
static READ_REQUEST_INVALID_1: [u8; 20] = [
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x6e, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x6e, 0x6f,
|
|
|
|
|
0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
];
|
|
|
|
|
/* garbage */
|
|
|
|
|
static READ_REQUEST_INVALID_2: [u8; 3] = [
|
|
|
|
|
0xff, 0xff, 0xff,
|
|
|
|
|
];
|
|
|
|
|
static READ_REQUEST_INVALID_2: [u8; 3] = [0xff, 0xff, 0xff];
|
|
|
|
|
static WRITE_REQUEST: [u8; 20] = [
|
|
|
|
|
0x00, 0x02, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
0x00, 0x02, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x6f,
|
|
|
|
|
0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
];
|
|
|
|
|
/* filename not terminated */
|
|
|
|
|
static INVALID_OPCODE: [u8; 20] = [
|
|
|
|
|
0x00, 0x06, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x6e, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
0x00, 0x06, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x6e, 0x6f,
|
|
|
|
|
0x63, 0x74, 0x65, 0x74, 0x00,
|
|
|
|
|
];
|
|
|
|
|
static INVALID_MODE: [u8; 20] = [
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00,
|
|
|
|
|
0x00, 0x01, 0x72, 0x66, 0x63, 0x31, 0x33, 0x35, 0x30, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x63,
|
|
|
|
|
0x63, 0x63, 0x63, 0x63, 0x00,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
@ -263,7 +261,6 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
pub fn test_parse_tftp_invalid_mode() {
|
|
|
|
|
|
|
|
|
|
assert_eq!(None, parse_tftp_request(&INVALID_MODE[..]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|