mirror of https://github.com/OISF/suricata
rust/nfs: NFSv3 parser, logger and detection
parent
69bf219b39
commit
d6592211d0
@ -0,0 +1,110 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::string::String;
|
||||
use json::*;
|
||||
//use log::*;
|
||||
use nfs::types::*;
|
||||
use nfs::nfs3::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_nfs3_tx_logging_is_filtered(tx: &mut NFS3Transaction)
|
||||
-> libc::uint8_t
|
||||
{
|
||||
// TODO probably best to make this configurable
|
||||
|
||||
if tx.procedure == NFSPROC3_GETATTR {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn nfs3_creds_object(tx: &NFS3Transaction) -> Json
|
||||
{
|
||||
let js = Json::object();
|
||||
let mach_name = String::from_utf8_lossy(&tx.request_machine_name);
|
||||
js.set_string("machine_name", &mach_name);
|
||||
js.set_integer("uid", tx.request_uid as u64);
|
||||
js.set_integer("gid", tx.request_gid as u64);
|
||||
return js;
|
||||
}
|
||||
|
||||
fn nfs3_write_object(tx: &NFS3Transaction) -> Json
|
||||
{
|
||||
let js = Json::object();
|
||||
js.set_boolean("first", tx.is_first);
|
||||
js.set_boolean("last", tx.is_last);
|
||||
js.set_integer("last_xid", tx.file_last_xid as u64);
|
||||
return js;
|
||||
}
|
||||
|
||||
fn nfs3_read_object(tx: &NFS3Transaction) -> Json
|
||||
{
|
||||
let js = Json::object();
|
||||
js.set_boolean("first", tx.is_first);
|
||||
js.set_boolean("last", tx.is_last);
|
||||
js.set_integer("last_xid", tx.file_last_xid as u64);
|
||||
return js;
|
||||
}
|
||||
|
||||
fn nfs3_common_header(tx: &NFS3Transaction) -> Json
|
||||
{
|
||||
let js = Json::object();
|
||||
js.set_integer("xid", tx.xid as u64);
|
||||
js.set_string("procedure", &nfs3_procedure_string(tx.procedure));
|
||||
let file_name = String::from_utf8_lossy(&tx.file_name);
|
||||
js.set_string("filename", &file_name);
|
||||
js.set_integer("id", tx.id as u64);
|
||||
js.set_boolean("file_tx", tx.is_file_tx);
|
||||
return js;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_nfs3_log_json_request(tx: &mut NFS3Transaction) -> *mut JsonT
|
||||
{
|
||||
let js = nfs3_common_header(tx);
|
||||
js.set_string("type", "request");
|
||||
return js.unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_nfs3_log_json_response(tx: &mut NFS3Transaction) -> *mut JsonT
|
||||
{
|
||||
let js = nfs3_common_header(tx);
|
||||
js.set_string("type", "response");
|
||||
|
||||
js.set_string("status", &nfs3_status_string(tx.response_status));
|
||||
|
||||
if tx.has_creds {
|
||||
let creds_js = nfs3_creds_object(tx);
|
||||
js.set("creds", creds_js);
|
||||
}
|
||||
|
||||
if tx.procedure == NFSPROC3_READ {
|
||||
let read_js = nfs3_read_object(tx);
|
||||
js.set("read", read_js);
|
||||
}
|
||||
if tx.procedure == NFSPROC3_WRITE {
|
||||
let write_js = nfs3_write_object(tx);
|
||||
js.set("write", write_js);
|
||||
}
|
||||
|
||||
return js.unwrap();
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
pub mod types;
|
||||
#[macro_use]
|
||||
pub mod parser;
|
||||
pub mod nfs3;
|
||||
pub mod log;
|
||||
|
||||
//#[cfg(feature = "lua")]
|
||||
//pub mod lua;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,531 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
//! Nom parsers for RPC & NFSv3
|
||||
|
||||
use nom::{be_u32, be_u64, rest};
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct RpcRequestCredsUnix<'a> {
|
||||
pub stamp: u32,
|
||||
pub machine_name_len: u32,
|
||||
pub machine_name_buf: &'a[u8],
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub aux_gids: Option<Vec<u32>>,
|
||||
// list of gids
|
||||
}
|
||||
|
||||
//named!(parse_rpc_creds_unix_aux_gids<Vec<u32>>,
|
||||
// many0!(be_u32)
|
||||
//);
|
||||
|
||||
named!(pub parse_rfc_request_creds_unix<RpcRequestCredsUnix>,
|
||||
do_parse!(
|
||||
stamp: be_u32
|
||||
>> machine_name_len: be_u32
|
||||
>> machine_name_buf: take!(machine_name_len)
|
||||
>> uid: be_u32
|
||||
>> gid: be_u32
|
||||
//>> aux_gids: parse_rpc_creds_unix_aux_gids
|
||||
|
||||
>> (
|
||||
RpcRequestCredsUnix {
|
||||
stamp:stamp,
|
||||
machine_name_len:machine_name_len,
|
||||
machine_name_buf:machine_name_buf,
|
||||
uid:uid,
|
||||
gid:gid,
|
||||
aux_gids:None,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3Handle<'a> {
|
||||
pub len: u32,
|
||||
pub value: &'a[u8],
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_handle<Nfs3Handle>,
|
||||
do_parse!(
|
||||
obj_len: be_u32
|
||||
>> obj: take!(obj_len)
|
||||
>> (
|
||||
Nfs3Handle {
|
||||
len:obj_len,
|
||||
value:obj,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ReplyCreate<'a> {
|
||||
pub status: u32,
|
||||
pub handle: Option<Nfs3Handle<'a>>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_response_create<Nfs3ReplyCreate>,
|
||||
do_parse!(
|
||||
status: be_u32
|
||||
>> handle_has_value: be_u32
|
||||
>> handle: cond!(handle_has_value == 1, parse_nfs3_handle)
|
||||
>> (
|
||||
Nfs3ReplyCreate {
|
||||
status:status,
|
||||
handle:handle,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ReplyLookup<'a> {
|
||||
pub status: u32,
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_response_lookup<Nfs3ReplyLookup>,
|
||||
do_parse!(
|
||||
status: be_u32
|
||||
>> handle: parse_nfs3_handle
|
||||
>> (
|
||||
Nfs3ReplyLookup {
|
||||
status:status,
|
||||
handle:handle,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestCreate<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
pub name_len: u32,
|
||||
pub create_mode: u32,
|
||||
pub verifier: &'a[u8],
|
||||
pub name_vec: Vec<u8>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_create<Nfs3RequestCreate>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> name_len: be_u32
|
||||
>> name: take!(name_len)
|
||||
>> create_mode: be_u32
|
||||
>> verifier: rest
|
||||
>> (
|
||||
Nfs3RequestCreate {
|
||||
handle:handle,
|
||||
name_len:name_len,
|
||||
create_mode:create_mode,
|
||||
verifier:verifier,
|
||||
name_vec:name.to_vec(),
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestAccess<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
pub check_access: u32,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_access<Nfs3RequestAccess>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> check_access: be_u32
|
||||
>> (
|
||||
Nfs3RequestAccess {
|
||||
handle:handle,
|
||||
check_access:check_access,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestCommit<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_commit<Nfs3RequestCommit>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> offset: be_u64
|
||||
>> count: be_u32
|
||||
>> (
|
||||
Nfs3RequestCommit {
|
||||
handle:handle,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestRead<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_read<Nfs3RequestRead>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> offset: be_u64
|
||||
>> count: be_u32
|
||||
>> (
|
||||
Nfs3RequestRead {
|
||||
handle:handle,
|
||||
offset:offset,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestLookup<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
|
||||
pub name_vec: Vec<u8>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_lookup<Nfs3RequestLookup>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> name_len: be_u32
|
||||
>> name_contents: take!(name_len)
|
||||
>> name_padding: rest
|
||||
>> (
|
||||
Nfs3RequestLookup {
|
||||
handle:handle,
|
||||
name_vec:name_contents.to_vec(),
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ResponseReaddirplusEntryC<'a> {
|
||||
pub name_vec: Vec<u8>,
|
||||
pub handle: Option<Nfs3Handle<'a>>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_response_readdirplus_entry<Nfs3ResponseReaddirplusEntryC>,
|
||||
do_parse!(
|
||||
file_id: be_u64
|
||||
>> name_len: be_u32
|
||||
>> name_content: take!(name_len)
|
||||
>> fill_bytes: cond!(name_len % 4 != 0, take!(4 - name_len % 4))
|
||||
>> cookie: take!(8)
|
||||
>> attr_value_follows: be_u32
|
||||
>> attr: cond!(attr_value_follows==1, take!(84))
|
||||
>> handle_value_follows: be_u32
|
||||
>> handle: cond!(handle_value_follows==1, parse_nfs3_handle)
|
||||
>> (
|
||||
Nfs3ResponseReaddirplusEntryC {
|
||||
name_vec:name_content.to_vec(),
|
||||
handle:handle,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ResponseReaddirplusEntry<'a> {
|
||||
pub entry: Option<Nfs3ResponseReaddirplusEntryC<'a>>,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_response_readdirplus_entry_cond<Nfs3ResponseReaddirplusEntry>,
|
||||
do_parse!(
|
||||
value_follows: be_u32
|
||||
>> entry: cond!(value_follows==1, parse_nfs3_response_readdirplus_entry)
|
||||
>> (
|
||||
Nfs3ResponseReaddirplusEntry {
|
||||
entry:entry,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ResponseReaddirplus<'a> {
|
||||
pub status: u32,
|
||||
pub data: &'a[u8],
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_response_readdirplus<Nfs3ResponseReaddirplus>,
|
||||
do_parse!(
|
||||
status: be_u32
|
||||
>> dir_attr_follows: be_u32
|
||||
>> dir_attr: cond!(dir_attr_follows == 1, take!(84))
|
||||
>> verifier: take!(8)
|
||||
>> data: rest
|
||||
|
||||
>> ( Nfs3ResponseReaddirplus {
|
||||
status:status,
|
||||
data:data,
|
||||
} ))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestReaddirplus<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
|
||||
pub cookie: u32,
|
||||
pub verifier: &'a[u8],
|
||||
pub dircount: u32,
|
||||
pub maxcount: u32,
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_readdirplus<Nfs3RequestReaddirplus>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> cookie: be_u32
|
||||
>> verifier: take!(8)
|
||||
>> dircount: be_u32
|
||||
>> maxcount: be_u32
|
||||
>> (
|
||||
Nfs3RequestReaddirplus {
|
||||
handle:handle,
|
||||
cookie:cookie,
|
||||
verifier:verifier,
|
||||
dircount:dircount,
|
||||
maxcount:maxcount,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3RequestWrite<'a> {
|
||||
pub handle: Nfs3Handle<'a>,
|
||||
|
||||
pub offset: u64,
|
||||
pub count: u32,
|
||||
pub stable: u32,
|
||||
pub file_len: u32,
|
||||
pub file_data: &'a[u8],
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_request_write<Nfs3RequestWrite>,
|
||||
do_parse!(
|
||||
handle: parse_nfs3_handle
|
||||
>> offset: be_u64
|
||||
>> count: be_u32
|
||||
>> stable: be_u32
|
||||
>> file_len: be_u32
|
||||
>> file_data: rest // likely partial
|
||||
>> (
|
||||
Nfs3RequestWrite {
|
||||
handle:handle,
|
||||
offset:offset,
|
||||
count:count,
|
||||
stable:stable,
|
||||
file_len:file_len,
|
||||
file_data:file_data,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Nfs3ReplyRead<'a> {
|
||||
pub status: u32,
|
||||
pub attr_follows: u32,
|
||||
pub attr_blob: &'a[u8],
|
||||
pub count: u32,
|
||||
pub eof: bool,
|
||||
pub data_len: u32,
|
||||
pub data: &'a[u8], // likely partial
|
||||
}
|
||||
|
||||
named!(pub parse_nfs3_reply_read<Nfs3ReplyRead>,
|
||||
do_parse!(
|
||||
status: be_u32
|
||||
>> attr_follows: be_u32
|
||||
>> attr_blob: take!(84) // fixed size?
|
||||
>> count: be_u32
|
||||
>> eof: be_u32
|
||||
>> data_len: be_u32
|
||||
>> data_contents: rest
|
||||
>> (
|
||||
Nfs3ReplyRead {
|
||||
status:status,
|
||||
attr_follows:attr_follows,
|
||||
attr_blob:attr_blob,
|
||||
count:count,
|
||||
eof:eof != 0,
|
||||
data_len:data_len,
|
||||
data:data_contents,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct RpcPacketHeader<> {
|
||||
pub frag_is_last: bool,
|
||||
pub frag_len: u32,
|
||||
pub xid: u32,
|
||||
pub msgtype: u32,
|
||||
}
|
||||
|
||||
named!(pub parse_rpc_packet_header<RpcPacketHeader>,
|
||||
do_parse!(
|
||||
fraghdr: bits!(tuple!(
|
||||
take_bits!(u8, 1), // is_last
|
||||
take_bits!(u32, 31))) // len
|
||||
|
||||
>> xid: be_u32
|
||||
>> msgtype: be_u32
|
||||
>> (
|
||||
RpcPacketHeader {
|
||||
frag_is_last:fraghdr.0 == 1,
|
||||
frag_len:fraghdr.1,
|
||||
xid:xid,
|
||||
msgtype:msgtype,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct RpcReplyPacket<'a> {
|
||||
pub hdr: RpcPacketHeader<>,
|
||||
|
||||
pub verifier_flavor: u32,
|
||||
pub verifier_len: u32,
|
||||
pub verifier: Option<&'a[u8]>,
|
||||
|
||||
pub reply_state: u32,
|
||||
pub accept_state: u32,
|
||||
|
||||
pub prog_data: &'a[u8],
|
||||
}
|
||||
|
||||
// top of request packet, just to get to procedure
|
||||
#[derive(Debug)]
|
||||
pub struct RpcRequestPacketPartial {
|
||||
pub hdr: RpcPacketHeader,
|
||||
|
||||
pub rpcver: u32,
|
||||
pub program: u32,
|
||||
pub progver: u32,
|
||||
pub procedure: u32,
|
||||
}
|
||||
|
||||
named!(pub parse_rpc_request_partial<RpcRequestPacketPartial>,
|
||||
do_parse!(
|
||||
hdr: parse_rpc_packet_header
|
||||
>> rpcver: be_u32
|
||||
>> program: be_u32
|
||||
>> progver: be_u32
|
||||
>> procedure: be_u32
|
||||
>> (
|
||||
RpcRequestPacketPartial {
|
||||
hdr:hdr,
|
||||
rpcver:rpcver,
|
||||
program:program,
|
||||
progver:progver,
|
||||
procedure:procedure,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct RpcPacket<'a> {
|
||||
pub hdr: RpcPacketHeader<>,
|
||||
|
||||
pub rpcver: u32,
|
||||
pub program: u32,
|
||||
pub progver: u32,
|
||||
pub procedure: u32,
|
||||
|
||||
pub creds_flavor: u32,
|
||||
pub creds_len: u32,
|
||||
pub creds: Option<&'a[u8]>,
|
||||
pub creds_unix:Option<RpcRequestCredsUnix<'a>>,
|
||||
|
||||
pub verifier_flavor: u32,
|
||||
pub verifier_len: u32,
|
||||
pub verifier: Option<&'a[u8]>,
|
||||
|
||||
pub prog_data: &'a[u8],
|
||||
}
|
||||
|
||||
named!(pub parse_rpc<RpcPacket>,
|
||||
do_parse!(
|
||||
hdr: parse_rpc_packet_header
|
||||
|
||||
>> rpcver: be_u32
|
||||
>> program: be_u32
|
||||
>> progver: be_u32
|
||||
>> procedure: be_u32
|
||||
|
||||
>> creds_flavor: be_u32
|
||||
>> creds_len: be_u32
|
||||
>> creds: cond!(creds_flavor != 1 && creds_len > 0, take!(creds_len as usize))
|
||||
>> creds_unix: cond!(creds_len > 0 && creds_flavor == 1, flat_map!(take!((creds_len) as usize),parse_rfc_request_creds_unix))
|
||||
|
||||
>> verifier_flavor: be_u32
|
||||
>> verifier_len: be_u32
|
||||
>> verifier: cond!(verifier_len > 0, take!(verifier_len as usize))
|
||||
|
||||
>> pl: rest
|
||||
|
||||
>> (
|
||||
RpcPacket {
|
||||
hdr:hdr,
|
||||
|
||||
rpcver:rpcver,
|
||||
program:program,
|
||||
progver:progver,
|
||||
procedure:procedure,
|
||||
|
||||
creds_flavor:creds_flavor,
|
||||
creds_len:creds_len,
|
||||
creds:creds,
|
||||
creds_unix:creds_unix,
|
||||
|
||||
verifier_flavor:verifier_flavor,
|
||||
verifier_len:verifier_len,
|
||||
verifier:verifier,
|
||||
|
||||
prog_data:pl,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
// to be called with data <= hdr.frag_len + 4. Sending more data is undefined.
|
||||
named!(pub parse_rpc_reply<RpcReplyPacket>,
|
||||
do_parse!(
|
||||
hdr: parse_rpc_packet_header
|
||||
|
||||
>> verifier_flavor: be_u32
|
||||
>> verifier_len: be_u32
|
||||
>> verifier: cond!(verifier_len > 0, take!(verifier_len as usize))
|
||||
|
||||
>> reply_state: be_u32
|
||||
>> accept_state: be_u32
|
||||
|
||||
>> pl: rest
|
||||
|
||||
>> (
|
||||
RpcReplyPacket {
|
||||
hdr:hdr,
|
||||
|
||||
verifier_flavor:verifier_flavor,
|
||||
verifier_len:verifier_len,
|
||||
verifier:verifier,
|
||||
|
||||
reply_state:reply_state,
|
||||
accept_state:accept_state,
|
||||
|
||||
prog_data:pl,
|
||||
}
|
||||
))
|
||||
);
|
@ -0,0 +1,138 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/* RFC 1813, section '3. Server Procedures' */
|
||||
pub const NFSPROC3_NULL: u32 = 0;
|
||||
pub const NFSPROC3_GETATTR: u32 = 1;
|
||||
pub const NFSPROC3_SETATTR: u32 = 2;
|
||||
pub const NFSPROC3_LOOKUP: u32 = 3;
|
||||
pub const NFSPROC3_ACCESS: u32 = 4;
|
||||
pub const NFSPROC3_READLINK: u32 = 5;
|
||||
pub const NFSPROC3_READ: u32 = 6;
|
||||
pub const NFSPROC3_WRITE: u32 = 7;
|
||||
pub const NFSPROC3_CREATE: u32 = 8;
|
||||
pub const NFSPROC3_MKDIR: u32 = 9;
|
||||
pub const NFSPROC3_SYMLINK: u32 = 10;
|
||||
pub const NFSPROC3_MKNOD: u32 = 11;
|
||||
pub const NFSPROC3_REMOVE: u32 = 12;
|
||||
pub const NFSPROC3_RMDIR: u32 = 13;
|
||||
pub const NFSPROC3_RENAME: u32 = 14;
|
||||
pub const NFSPROC3_LINK: u32 = 15;
|
||||
pub const NFSPROC3_READDIR: u32 = 16;
|
||||
pub const NFSPROC3_READDIRPLUS: u32 = 17;
|
||||
pub const NFSPROC3_FSSTAT: u32 = 18;
|
||||
pub const NFSPROC3_FSINFO: u32 = 19;
|
||||
pub const NFSPROC3_PATHCONF: u32 = 20;
|
||||
pub const NFSPROC3_COMMIT: u32 = 21;
|
||||
|
||||
pub fn nfs3_procedure_string(procedure: u32) -> String {
|
||||
match procedure {
|
||||
NFSPROC3_NULL => "NULL",
|
||||
NFSPROC3_GETATTR => "GETATTR",
|
||||
NFSPROC3_SETATTR => "SETATTR",
|
||||
NFSPROC3_LOOKUP => "LOOKUP",
|
||||
NFSPROC3_ACCESS => "ACCESS",
|
||||
NFSPROC3_READLINK => "READLINK",
|
||||
NFSPROC3_READ => "READ",
|
||||
NFSPROC3_WRITE => "WRITE",
|
||||
NFSPROC3_CREATE => "CREATE",
|
||||
NFSPROC3_MKDIR => "MKDIR",
|
||||
NFSPROC3_SYMLINK => "SYMLINK",
|
||||
NFSPROC3_MKNOD => "MKNOD",
|
||||
NFSPROC3_REMOVE => "REMOVE",
|
||||
NFSPROC3_RMDIR => "RMDIR",
|
||||
NFSPROC3_RENAME => "RENAME",
|
||||
NFSPROC3_LINK => "LINK",
|
||||
NFSPROC3_READDIR => "READDIR",
|
||||
NFSPROC3_READDIRPLUS => "READDIRPLUS",
|
||||
NFSPROC3_FSSTAT => "FSSTAT",
|
||||
NFSPROC3_FSINFO => "FSINFO",
|
||||
NFSPROC3_PATHCONF => "PATHCONF",
|
||||
NFSPROC3_COMMIT => "COMMIT",
|
||||
_ => {
|
||||
return (procedure).to_string();
|
||||
}
|
||||
}.to_string()
|
||||
}
|
||||
|
||||
/* RFC 1813, section '2.6 Defined Error Numbers' */
|
||||
pub const NFS3_OK: u32 = 0;
|
||||
pub const NFS3ERR_PERM: u32 = 1;
|
||||
pub const NFS3ERR_NOENT: u32 = 2;
|
||||
pub const NFS3ERR_IO: u32 = 5;
|
||||
pub const NFS3ERR_NXIO: u32 = 6;
|
||||
pub const NFS3ERR_ACCES: u32 = 13;
|
||||
pub const NFS3ERR_EXIST: u32 = 17;
|
||||
pub const NFS3ERR_XDEV: u32 = 18;
|
||||
pub const NFS3ERR_NODEV: u32 = 19;
|
||||
pub const NFS3ERR_NOTDIR: u32 = 20;
|
||||
pub const NFS3ERR_ISDIR: u32 = 21;
|
||||
pub const NFS3ERR_INVAL: u32 = 22;
|
||||
pub const NFS3ERR_FBIG: u32 = 27;
|
||||
pub const NFS3ERR_NOSPC: u32 = 28;
|
||||
pub const NFS3ERR_ROFS: u32 = 30;
|
||||
pub const NFS3ERR_MLINK: u32 = 31;
|
||||
pub const NFS3ERR_NAMETOOLONG: u32 = 63;
|
||||
pub const NFS3ERR_NOTEMPTY: u32 = 66;
|
||||
pub const NFS3ERR_DQUOT: u32 = 69;
|
||||
pub const NFS3ERR_STALE: u32 = 70;
|
||||
pub const NFS3ERR_REMOTE: u32 = 71;
|
||||
pub const NFS3ERR_BADHANDLE: u32 = 10001;
|
||||
pub const NFS3ERR_NOT_SYNC: u32 = 10002;
|
||||
pub const NFS3ERR_BAD_COOKIE: u32 = 10003;
|
||||
pub const NFS3ERR_NOTSUPP: u32 = 10004;
|
||||
pub const NFS3ERR_TOOSMALL: u32 = 10005;
|
||||
pub const NFS3ERR_SERVERFAULT: u32 = 10006;
|
||||
pub const NFS3ERR_BADTYPE: u32 = 10007;
|
||||
pub const NFS3ERR_JUKEBOX: u32 = 10008;
|
||||
|
||||
pub fn nfs3_status_string(status: u32) -> String {
|
||||
match status {
|
||||
NFS3_OK => "OK",
|
||||
NFS3ERR_PERM => "ERR_PERM",
|
||||
NFS3ERR_NOENT => "ERR_NOENT",
|
||||
NFS3ERR_IO => "ERR_IO",
|
||||
NFS3ERR_NXIO => "ERR_NXIO",
|
||||
NFS3ERR_ACCES => "ERR_ACCES",
|
||||
NFS3ERR_EXIST => "ERR_EXIST",
|
||||
NFS3ERR_XDEV => "ERR_XDEV",
|
||||
NFS3ERR_NODEV => "ERR_NODEV",
|
||||
NFS3ERR_NOTDIR => "ERR_NOTDIR",
|
||||
NFS3ERR_ISDIR => "ERR_ISDIR",
|
||||
NFS3ERR_INVAL => "ERR_INVAL",
|
||||
NFS3ERR_FBIG => "ERR_FBIG",
|
||||
NFS3ERR_NOSPC => "ERR_NOSPC",
|
||||
NFS3ERR_ROFS => "ERR_ROFS",
|
||||
NFS3ERR_MLINK => "ERR_MLINK",
|
||||
NFS3ERR_NAMETOOLONG => "ERR_NAMETOOLONG",
|
||||
NFS3ERR_NOTEMPTY => "ERR_NOTEMPTY",
|
||||
NFS3ERR_DQUOT => "ERR_DQUOT",
|
||||
NFS3ERR_STALE => "ERR_STALE",
|
||||
NFS3ERR_REMOTE => "ERR_REMOTE",
|
||||
NFS3ERR_BADHANDLE => "ERR_BADHANDLE",
|
||||
NFS3ERR_NOT_SYNC => "ERR_NOT_SYNC",
|
||||
NFS3ERR_BAD_COOKIE => "ERR_BAD_COOKIE",
|
||||
NFS3ERR_NOTSUPP => "ERR_NOTSUPP",
|
||||
NFS3ERR_TOOSMALL => "ERR_TOOSMALL",
|
||||
NFS3ERR_SERVERFAULT => "ERR_SERVERFAULT",
|
||||
NFS3ERR_BADTYPE => "ERR_BADTYPE",
|
||||
NFS3ERR_JUKEBOX => "ERR_JUKEBOX",
|
||||
_ => {
|
||||
return (status).to_string();
|
||||
},
|
||||
}.to_string()
|
||||
}
|
@ -0,0 +1,369 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*
|
||||
* NFS3 application layer detector and parser for learning and
|
||||
* nfs3 pruposes.
|
||||
*
|
||||
* This nfs3 implements a simple application layer for something
|
||||
* like the NFS3 protocol running on port 2049.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "stream.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
|
||||
#include "app-layer-detect-proto.h"
|
||||
#include "app-layer-parser.h"
|
||||
|
||||
#include "app-layer-nfs3.h"
|
||||
|
||||
#ifndef HAVE_RUST
|
||||
void RegisterNFS3Parsers(void)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "rust.h"
|
||||
#include "rust-nfs-nfs3-gen.h"
|
||||
|
||||
/* The default port to probe for echo traffic if not provided in the
|
||||
* configuration file. */
|
||||
#define NFS3_DEFAULT_PORT "2049"
|
||||
|
||||
/* The minimum size for a RFC message. For some protocols this might
|
||||
* be the size of a header. TODO actual min size is likely larger */
|
||||
#define NFS3_MIN_FRAME_LEN 32
|
||||
|
||||
/* Enum of app-layer events for an echo protocol. Normally you might
|
||||
* have events for errors in parsing data, like unexpected data being
|
||||
* received. For echo we'll make something up, and log an app-layer
|
||||
* level alert if an empty message is received.
|
||||
*
|
||||
* Example rule:
|
||||
*
|
||||
* alert nfs3 any any -> any any (msg:"SURICATA NFS3 empty message"; \
|
||||
* app-layer-event:nfs3.empty_message; sid:X; rev:Y;)
|
||||
*/
|
||||
enum {
|
||||
NFS3_DECODER_EVENT_EMPTY_MESSAGE,
|
||||
};
|
||||
|
||||
SCEnumCharMap nfs3_decoder_event_table[] = {
|
||||
{"EMPTY_MESSAGE", NFS3_DECODER_EVENT_EMPTY_MESSAGE},
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static void *NFS3StateAlloc(void)
|
||||
{
|
||||
return rs_nfs3_state_new();
|
||||
}
|
||||
|
||||
static void NFS3StateFree(void *state)
|
||||
{
|
||||
rs_nfs3_state_free(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Callback from the application layer to have a transaction freed.
|
||||
*
|
||||
* \param state a void pointer to the NFS3State object.
|
||||
* \param tx_id the transaction ID to free.
|
||||
*/
|
||||
static void NFS3StateTxFree(void *state, uint64_t tx_id)
|
||||
{
|
||||
rs_nfs3_state_tx_free(state, tx_id);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int NFS3StateGetEventInfo(const char *event_name, int *event_id,
|
||||
AppLayerEventType *event_type)
|
||||
{
|
||||
*event_id = SCMapEnumNameToValue(event_name, nfs3_decoder_event_table);
|
||||
if (*event_id == -1) {
|
||||
SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
|
||||
"nfs3 enum map table.", event_name);
|
||||
/* This should be treated as fatal. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AppLayerDecoderEvents *NFS3GetEvents(void *state, uint64_t tx_id)
|
||||
{
|
||||
NFS3State *nfs3_state = state;
|
||||
NFS3Transaction *tx;
|
||||
|
||||
TAILQ_FOREACH(tx, &nfs3_state->tx_list, next) {
|
||||
if (tx->tx_id == tx_id) {
|
||||
return tx->decoder_events;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int NFS3HasEvents(void *state)
|
||||
{
|
||||
NFS3State *echo = state;
|
||||
return echo->events;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Probe the input to see if it looks like echo.
|
||||
*
|
||||
* \retval ALPROTO_NFS3 if it looks like echo, otherwise
|
||||
* ALPROTO_UNKNOWN.
|
||||
*/
|
||||
static AppProto NFS3ProbingParser(uint8_t *input, uint32_t input_len,
|
||||
uint32_t *offset)
|
||||
{
|
||||
if (input_len < NFS3_MIN_FRAME_LEN) {
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
|
||||
int8_t r = rs_nfs_probe(input, input_len);
|
||||
if (r == 1) {
|
||||
return ALPROTO_NFS3;
|
||||
} else if (r == -1) {
|
||||
return ALPROTO_FAILED;
|
||||
}
|
||||
|
||||
SCLogDebug("Protocol not detected as ALPROTO_NFS3.");
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
|
||||
static int NFS3ParseRequest(Flow *f, void *state,
|
||||
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
|
||||
void *local_data)
|
||||
{
|
||||
uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
|
||||
rs_nfs3_setfileflags(0, state, file_flags);
|
||||
|
||||
return rs_nfs3_parse_request(f, state, pstate, input, input_len, local_data);
|
||||
}
|
||||
|
||||
static int NFS3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
|
||||
uint8_t *input, uint32_t input_len, void *local_data)
|
||||
{
|
||||
uint16_t file_flags = FileFlowToFlags(f, STREAM_TOCLIENT);
|
||||
rs_nfs3_setfileflags(1, state, file_flags);
|
||||
|
||||
return rs_nfs3_parse_response(f, state, pstate, input, input_len, local_data);
|
||||
}
|
||||
|
||||
static uint64_t NFS3GetTxCnt(void *state)
|
||||
{
|
||||
return rs_nfs3_state_get_tx_count(state);
|
||||
}
|
||||
|
||||
static void *NFS3GetTx(void *state, uint64_t tx_id)
|
||||
{
|
||||
return rs_nfs3_state_get_tx(state, tx_id);
|
||||
}
|
||||
|
||||
static void NFS3SetTxLogged(void *state, void *vtx, uint32_t logger)
|
||||
{
|
||||
rs_nfs3_tx_set_logged(state, vtx, logger);
|
||||
}
|
||||
|
||||
static int NFS3GetTxLogged(void *state, void *vtx, uint32_t logger)
|
||||
{
|
||||
return rs_nfs3_tx_get_logged(state, vtx, logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Called by the application layer.
|
||||
*
|
||||
* In most cases 1 can be returned here.
|
||||
*/
|
||||
static int NFS3GetAlstateProgressCompletionStatus(uint8_t direction) {
|
||||
return rs_nfs3_state_progress_completion_status(direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the state of a transaction in a given direction.
|
||||
*
|
||||
* In the case of the echo protocol, the existence of a transaction
|
||||
* means that the request is done. However, some protocols that may
|
||||
* need multiple chunks of data to complete the request may need more
|
||||
* than just the existence of a transaction for the request to be
|
||||
* considered complete.
|
||||
*
|
||||
* For the response to be considered done, the response for a request
|
||||
* needs to be seen. The response_done flag is set on response for
|
||||
* checking here.
|
||||
*/
|
||||
static int NFS3GetStateProgress(void *tx, uint8_t direction)
|
||||
{
|
||||
return rs_nfs3_tx_get_alstate_progress(tx, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief get stored tx detect state
|
||||
*/
|
||||
static DetectEngineState *NFS3GetTxDetectState(void *vtx)
|
||||
{
|
||||
return rs_nfs3_state_get_tx_detect_state(vtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief set store tx detect state
|
||||
*/
|
||||
static int NFS3SetTxDetectState(void *state, void *vtx,
|
||||
DetectEngineState *s)
|
||||
{
|
||||
rs_nfs3_state_set_tx_detect_state(state, vtx, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FileContainer *NFS3GetFiles(void *state, uint8_t direction)
|
||||
{
|
||||
return rs_nfs3_getfiles(direction, state);
|
||||
}
|
||||
|
||||
static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
|
||||
static SuricataFileContext sfc = { &sbcfg };
|
||||
|
||||
void RegisterNFS3Parsers(void)
|
||||
{
|
||||
const char *proto_name = "nfs3";
|
||||
|
||||
/* Check if NFS3 TCP detection is enabled. If it does not exist in
|
||||
* the configuration file then it will be enabled by default. */
|
||||
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
|
||||
|
||||
rs_nfs3_init(&sfc);
|
||||
|
||||
SCLogDebug("NFS3 TCP protocol detection enabled.");
|
||||
|
||||
AppLayerProtoDetectRegisterProtocol(ALPROTO_NFS3, proto_name);
|
||||
|
||||
if (RunmodeIsUnittests()) {
|
||||
|
||||
SCLogDebug("Unittest mode, registering default configuration.");
|
||||
AppLayerProtoDetectPPRegister(IPPROTO_TCP, NFS3_DEFAULT_PORT,
|
||||
ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||
NFS3ProbingParser, NULL);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
|
||||
proto_name, ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN,
|
||||
NFS3ProbingParser, NULL)) {
|
||||
SCLogDebug("No NFS3 app-layer configuration, enabling NFS3"
|
||||
" detection TCP detection on port %s.",
|
||||
NFS3_DEFAULT_PORT);
|
||||
AppLayerProtoDetectPPRegister(IPPROTO_TCP,
|
||||
NFS3_DEFAULT_PORT, ALPROTO_NFS3, 0,
|
||||
NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||
NFS3ProbingParser, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
SCLogDebug("Protocol detecter and parser disabled for NFS3.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppLayerParserConfParserEnabled("tcp", proto_name))
|
||||
{
|
||||
SCLogDebug("Registering NFS3 protocol parser.");
|
||||
|
||||
/* Register functions for state allocation and freeing. A
|
||||
* state is allocated for every new NFS3 flow. */
|
||||
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3StateAlloc, NFS3StateFree);
|
||||
|
||||
/* Register request parser for parsing frame from server to client. */
|
||||
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
STREAM_TOSERVER, NFS3ParseRequest);
|
||||
|
||||
/* Register response parser for parsing frames from server to client. */
|
||||
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
STREAM_TOCLIENT, NFS3ParseResponse);
|
||||
|
||||
/* Register a function to be called by the application layer
|
||||
* when a transaction is to be freed. */
|
||||
AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3StateTxFree);
|
||||
|
||||
AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3GetTxLogged, NFS3SetTxLogged);
|
||||
|
||||
/* Register a function to return the current transaction count. */
|
||||
AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3GetTxCnt);
|
||||
|
||||
/* Transaction handling. */
|
||||
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_NFS3,
|
||||
NFS3GetAlstateProgressCompletionStatus);
|
||||
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP,
|
||||
ALPROTO_NFS3, NFS3GetStateProgress);
|
||||
AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3GetTx);
|
||||
|
||||
AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_NFS3, NFS3GetFiles);
|
||||
|
||||
/* Application layer event handling. */
|
||||
// AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
// NFS3HasEvents);
|
||||
|
||||
/* What is this being registered for? */
|
||||
AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NULL, NFS3GetTxDetectState, NFS3SetTxDetectState);
|
||||
|
||||
// AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
// NFS3StateGetEventInfo);
|
||||
// AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
// NFS3GetEvents);
|
||||
}
|
||||
else {
|
||||
SCLogDebug("NFS3 protocol parsing disabled.");
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_NFS3,
|
||||
NFS3ParserRegisterTests);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
|
||||
void NFS3ParserRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* HAVE_RUST */
|
@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*/
|
||||
|
||||
#ifndef __APP_LAYER_NFS3_H__
|
||||
#define __APP_LAYER_NFS3_H__
|
||||
|
||||
void RegisterNFS3Parsers(void);
|
||||
void NFS3ParserRegisterTests(void);
|
||||
|
||||
/** Opaque Rust types. */
|
||||
typedef struct NFS3tate_ NFS3State;
|
||||
typedef struct NFS3Transaction_ NFS3Transaction;
|
||||
|
||||
#endif /* __APP_LAYER_NFS3_H__ */
|
@ -0,0 +1,641 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "threads.h"
|
||||
#include "debug.h"
|
||||
#include "decode.h"
|
||||
#include "detect.h"
|
||||
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-mpm.h"
|
||||
#include "detect-content.h"
|
||||
#include "detect-pcre.h"
|
||||
#include "detect-nfs3-procedure.h"
|
||||
|
||||
#include "flow.h"
|
||||
#include "flow-util.h"
|
||||
#include "flow-var.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
#include "util-unittest-helper.h"
|
||||
|
||||
#ifndef HAVE_RUST
|
||||
void DetectNfs3ProcedureRegister(void)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "app-layer-nfs3.h"
|
||||
#include "rust.h"
|
||||
#include "rust-nfs-nfs3-gen.h"
|
||||
|
||||
/**
|
||||
* [nfs3_procedure]:[<|>]<proc>[<><proc>];
|
||||
*/
|
||||
#define PARSE_REGEX "^\\s*(<=|>=|<|>)?\\s*([0-9]+)\\s*(?:(<>)\\s*([0-9]+))?\\s*$"
|
||||
static pcre *parse_regex;
|
||||
static pcre_extra *parse_regex_study;
|
||||
|
||||
enum DetectNfs3ProcedureMode {
|
||||
PROCEDURE_EQ = 1, /* equal */
|
||||
PROCEDURE_LT, /* less than */
|
||||
PROCEDURE_LE, /* less than */
|
||||
PROCEDURE_GT, /* greater than */
|
||||
PROCEDURE_GE, /* greater than */
|
||||
PROCEDURE_RA, /* range */
|
||||
};
|
||||
|
||||
typedef struct DetectNfs3ProcedureData_ {
|
||||
uint32_t lo;
|
||||
uint32_t hi;
|
||||
enum DetectNfs3ProcedureMode mode;
|
||||
} DetectNfs3ProcedureData;
|
||||
|
||||
static DetectNfs3ProcedureData *DetectNfs3ProcedureParse (const char *);
|
||||
static int DetectNfs3ProcedureSetup (DetectEngineCtx *, Signature *s, const char *str);
|
||||
static void DetectNfs3ProcedureFree(void *);
|
||||
static void DetectNfs3ProcedureRegisterTests(void);
|
||||
static int g_nfs3_request_buffer_id = 0;
|
||||
|
||||
static int DetectEngineInspectNfs3RequestGeneric(ThreadVars *tv,
|
||||
DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
||||
const Signature *s, const SigMatchData *smd,
|
||||
Flow *f, uint8_t flags, void *alstate,
|
||||
void *txv, uint64_t tx_id);
|
||||
|
||||
static int DetectNfs3ProcedureMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
|
||||
uint8_t, void *, void *, const Signature *,
|
||||
const SigMatchCtx *);
|
||||
|
||||
/**
|
||||
* \brief Registration function for nfs3_procedure keyword.
|
||||
*/
|
||||
void DetectNfs3ProcedureRegister (void)
|
||||
{
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].name = "nfs3_procedure";
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].desc = "match NFSv3 procedure";
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].url = DOC_URL DOC_VERSION "/rules/nfs3-keywords.html#procedure";
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].Match = NULL;
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].AppLayerTxMatch = DetectNfs3ProcedureMatch;
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].Setup = DetectNfs3ProcedureSetup;
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].Free = DetectNfs3ProcedureFree;
|
||||
sigmatch_table[DETECT_AL_NFS3_PROCEDURE].RegisterTests = DetectNfs3ProcedureRegisterTests;
|
||||
|
||||
|
||||
DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
|
||||
|
||||
DetectAppLayerInspectEngineRegister("nfs3_request",
|
||||
ALPROTO_NFS3, SIG_FLAG_TOSERVER, 0,
|
||||
DetectEngineInspectNfs3RequestGeneric);
|
||||
|
||||
g_nfs3_request_buffer_id = DetectBufferTypeGetByName("nfs3_request");
|
||||
|
||||
SCLogDebug("g_nfs3_request_buffer_id %d", g_nfs3_request_buffer_id);
|
||||
}
|
||||
|
||||
static int DetectEngineInspectNfs3RequestGeneric(ThreadVars *tv,
|
||||
DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
||||
const Signature *s, const SigMatchData *smd,
|
||||
Flow *f, uint8_t flags, void *alstate,
|
||||
void *txv, uint64_t tx_id)
|
||||
{
|
||||
return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
|
||||
f, flags, alstate, txv, tx_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ProcedureMatch(const uint32_t procedure,
|
||||
enum DetectNfs3ProcedureMode mode, uint32_t lo, uint32_t hi)
|
||||
{
|
||||
switch (mode) {
|
||||
case PROCEDURE_EQ:
|
||||
if (procedure == lo)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_LT:
|
||||
if (procedure < lo)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_LE:
|
||||
if (procedure <= lo)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_GT:
|
||||
if (procedure > lo)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_GE:
|
||||
if (procedure >= lo)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_RA:
|
||||
if (procedure >= lo && procedure <= hi)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
}
|
||||
SCReturnInt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to match procedure of a TX
|
||||
*
|
||||
* For 'file txs'
|
||||
*
|
||||
* \param t Pointer to thread vars.
|
||||
* \param det_ctx Pointer to the pattern matcher thread.
|
||||
* \param f Pointer to the current flow.
|
||||
* \param flags Flags.
|
||||
* \param state App layer state.
|
||||
* \param s Pointer to the Signature.
|
||||
* \param m Pointer to the sigmatch that we will cast into
|
||||
* DetectNfs3ProcedureData.
|
||||
*
|
||||
* \retval 0 no match.
|
||||
* \retval 1 match.
|
||||
*/
|
||||
static int DetectNfs3ProcedureMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
|
||||
Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s,
|
||||
const SigMatchCtx *ctx)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
const DetectNfs3ProcedureData *dd = (const DetectNfs3ProcedureData *)ctx;
|
||||
uint16_t i;
|
||||
for (i = 0; i < 256; i++) {
|
||||
uint32_t procedure;
|
||||
if (rs_nfs3_tx_get_procedures(txv, i, &procedure) == 1) {
|
||||
SCLogDebug("proc %u mode %u lo %u hi %u",
|
||||
procedure, dd->mode, dd->lo, dd->hi);
|
||||
if (ProcedureMatch(procedure, dd->mode, dd->lo, dd->hi))
|
||||
SCReturnInt(1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
SCReturnInt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to parse options passed via tls validity keywords.
|
||||
*
|
||||
* \param rawstr Pointer to the user provided options.
|
||||
*
|
||||
* \retval dd pointer to DetectNfs3ProcedureData on success.
|
||||
* \retval NULL on failure.
|
||||
*/
|
||||
static DetectNfs3ProcedureData *DetectNfs3ProcedureParse (const char *rawstr)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
#define MAX_SUBSTRINGS 30
|
||||
int ret = 0, res = 0;
|
||||
int ov[MAX_SUBSTRINGS];
|
||||
char mode[2] = "";
|
||||
char value1[20] = "";
|
||||
char value2[20] = "";
|
||||
char range[3] = "";
|
||||
|
||||
ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0,
|
||||
0, ov, MAX_SUBSTRINGS);
|
||||
if (ret < 3 || ret > 5) {
|
||||
SCLogError(SC_ERR_PCRE_MATCH, "Parse error %s", rawstr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, mode,
|
||||
sizeof(mode));
|
||||
if (res < 0) {
|
||||
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
||||
goto error;
|
||||
}
|
||||
SCLogDebug("mode \"%s\"", mode);
|
||||
|
||||
res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, value1,
|
||||
sizeof(value1));
|
||||
if (res < 0) {
|
||||
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
||||
goto error;
|
||||
}
|
||||
SCLogDebug("value1 \"%s\"", value1);
|
||||
|
||||
if (ret > 3) {
|
||||
res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 3,
|
||||
range, sizeof(range));
|
||||
if (res < 0) {
|
||||
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
||||
goto error;
|
||||
}
|
||||
SCLogDebug("range \"%s\"", range);
|
||||
|
||||
if (ret > 4) {
|
||||
res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 4,
|
||||
value2, sizeof(value2));
|
||||
if (res < 0) {
|
||||
SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
|
||||
"pcre_copy_substring failed");
|
||||
goto error;
|
||||
}
|
||||
SCLogDebug("value2 \"%s\"", value2);
|
||||
}
|
||||
}
|
||||
|
||||
dd = SCCalloc(1, sizeof(DetectNfs3ProcedureData));
|
||||
if (unlikely(dd == NULL))
|
||||
goto error;
|
||||
|
||||
if (strlen(mode) == 1) {
|
||||
if (mode[0] == '<')
|
||||
dd->mode = PROCEDURE_LT;
|
||||
else if (mode[0] == '>')
|
||||
dd->mode = PROCEDURE_GT;
|
||||
} else if (strlen(mode) == 2) {
|
||||
if (strcmp(mode, "<=") == 0)
|
||||
dd->mode = PROCEDURE_LE;
|
||||
if (strcmp(mode, ">=") == 0)
|
||||
dd->mode = PROCEDURE_GE;
|
||||
}
|
||||
|
||||
if (strlen(range) > 0) {
|
||||
if (strcmp("<>", range) == 0)
|
||||
dd->mode = PROCEDURE_RA;
|
||||
}
|
||||
|
||||
if (strlen(range) != 0 && strlen(mode) != 0) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"Range specified but mode also set");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dd->mode == 0) {
|
||||
dd->mode = PROCEDURE_EQ;
|
||||
}
|
||||
|
||||
/* set the first value */
|
||||
dd->lo = atoi(value1); //TODO
|
||||
|
||||
/* set the second value if specified */
|
||||
if (strlen(value2) > 0) {
|
||||
if (!(dd->mode == PROCEDURE_RA)) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"Multiple tls validity values specified but mode is not range");
|
||||
goto error;
|
||||
}
|
||||
|
||||
//
|
||||
dd->hi = atoi(value2); // TODO
|
||||
|
||||
if (dd->hi <= dd->lo) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
||||
"Second value in range must not be smaller than the first");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return dd;
|
||||
|
||||
error:
|
||||
if (dd)
|
||||
SCFree(dd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Function to add the parsed tls validity field into the current signature.
|
||||
*
|
||||
* \param de_ctx Pointer to the Detection Engine Context.
|
||||
* \param s Pointer to the Current Signature.
|
||||
* \param rawstr Pointer to the user provided flags options.
|
||||
* \param type Defines if this is notBefore or notAfter.
|
||||
*
|
||||
* \retval 0 on Success.
|
||||
* \retval -1 on Failure.
|
||||
*/
|
||||
static int DetectNfs3ProcedureSetup (DetectEngineCtx *de_ctx, Signature *s,
|
||||
const char *rawstr)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
SigMatch *sm = NULL;
|
||||
|
||||
SCLogDebug("\'%s\'", rawstr);
|
||||
|
||||
if (DetectSignatureSetAppProto(s, ALPROTO_NFS3) != 0)
|
||||
return -1;
|
||||
|
||||
dd = DetectNfs3ProcedureParse(rawstr);
|
||||
if (dd == NULL) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT,"Parsing \'%s\' failed", rawstr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* okay so far so good, lets get this into a SigMatch
|
||||
* and put it in the Signature. */
|
||||
sm = SigMatchAlloc();
|
||||
if (sm == NULL)
|
||||
goto error;
|
||||
|
||||
sm->type = DETECT_AL_NFS3_PROCEDURE;
|
||||
sm->ctx = (void *)dd;
|
||||
|
||||
s->flags |= SIG_FLAG_STATE_MATCH;
|
||||
SCLogDebug("low %u hi %u", dd->lo, dd->hi);
|
||||
SigMatchAppendSMToList(s, sm, g_nfs3_request_buffer_id);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to free memory associated with DetectNfs3ProcedureData.
|
||||
*
|
||||
* \param de_ptr Pointer to DetectNfs3ProcedureData.
|
||||
*/
|
||||
void DetectNfs3ProcedureFree(void *ptr)
|
||||
{
|
||||
SCFree(ptr);
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value 1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse01 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("1430000000");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->mode == PROCEDURE_EQ);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value >1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse02 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse(">1430000000");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->mode == PROCEDURE_GT);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value <1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse03 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("<1430000000");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->mode == PROCEDURE_LT);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value 1430000000<>1470000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse04 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("1430000000<>1470000000");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->hi == 1470000000 &&
|
||||
dd->mode == PROCEDURE_RA);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value A.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse05 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("A");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value >1430000000<>1470000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse06 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse(">1430000000<>1470000000");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value 1430000000<>.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse07 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("1430000000<>");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value <>1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse08 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("<>1430000000");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value "".
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse09 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value " ".
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse10 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse(" ");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a invalid value 1490000000<>1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse11 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("1490000000<>1430000000");
|
||||
FAIL_IF_NOT_NULL(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value 1430000000 <> 1490000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse12 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("1430000000 <> 1490000000");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->hi == 1490000000 &&
|
||||
dd->mode == PROCEDURE_RA);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value > 1430000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse13 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("> 1430000000 ");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1430000000 && dd->mode == PROCEDURE_GT);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value < 1490000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse14 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse("< 1490000000 ");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1490000000 && dd->mode == PROCEDURE_LT);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test This is a test for a valid value 1490000000.
|
||||
*
|
||||
* \retval 1 on success.
|
||||
* \retval 0 on failure.
|
||||
*/
|
||||
static int ValidityTestParse15 (void)
|
||||
{
|
||||
DetectNfs3ProcedureData *dd = NULL;
|
||||
dd = DetectNfs3ProcedureParse(" 1490000000 ");
|
||||
FAIL_IF_NULL(dd);
|
||||
FAIL_IF_NOT(dd->lo == 1490000000 && dd->mode == PROCEDURE_EQ);
|
||||
DetectNfs3ProcedureFree(dd);
|
||||
PASS;
|
||||
}
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
/**
|
||||
* \brief Register unit tests for nfs3_procedure.
|
||||
*/
|
||||
void DetectNfs3ProcedureRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS /* UNITTESTS */
|
||||
UtRegisterTest("ValidityTestParse01", ValidityTestParse01);
|
||||
UtRegisterTest("ValidityTestParse02", ValidityTestParse02);
|
||||
UtRegisterTest("ValidityTestParse03", ValidityTestParse03);
|
||||
UtRegisterTest("ValidityTestParse04", ValidityTestParse04);
|
||||
UtRegisterTest("ValidityTestParse05", ValidityTestParse05);
|
||||
UtRegisterTest("ValidityTestParse06", ValidityTestParse06);
|
||||
UtRegisterTest("ValidityTestParse07", ValidityTestParse07);
|
||||
UtRegisterTest("ValidityTestParse08", ValidityTestParse08);
|
||||
UtRegisterTest("ValidityTestParse09", ValidityTestParse09);
|
||||
UtRegisterTest("ValidityTestParse10", ValidityTestParse10);
|
||||
UtRegisterTest("ValidityTestParse11", ValidityTestParse11);
|
||||
UtRegisterTest("ValidityTestParse12", ValidityTestParse12);
|
||||
UtRegisterTest("ValidityTestParse13", ValidityTestParse13);
|
||||
UtRegisterTest("ValidityTestParse14", ValidityTestParse14);
|
||||
UtRegisterTest("ValidityTestParse15", ValidityTestParse15);
|
||||
#endif /* UNITTESTS */
|
||||
}
|
||||
#endif /* HAVE_RUST */
|
@ -0,0 +1,30 @@
|
||||
/* Copyright (C) 2017 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_NFS3_PROCEDURE_H__
|
||||
#define __DETECT_NFS3_PROCEDURE_H__
|
||||
|
||||
/* prototypes */
|
||||
void DetectNfs3ProcedureRegister (void);
|
||||
|
||||
#endif /* __DETECT_NFS3_PROCEDURE_H__ */
|
@ -0,0 +1,202 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Update \author in this file and in output-json-nfs3.h.
|
||||
* TODO: Remove SCLogNotice statements, or convert to debug.
|
||||
* TODO: Implement your app-layers logging.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author FirstName LastName <yourname@domain>
|
||||
*
|
||||
* Implement JSON/eve logging app-layer NFS3.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "debug.h"
|
||||
#include "detect.h"
|
||||
#include "pkt-var.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include "threads.h"
|
||||
#include "threadvars.h"
|
||||
#include "tm-threads.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
#include "util-buffer.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-byte.h"
|
||||
|
||||
#include "output.h"
|
||||
#include "output-json.h"
|
||||
|
||||
#include "app-layer.h"
|
||||
#include "app-layer-parser.h"
|
||||
|
||||
#include "app-layer-nfs3.h"
|
||||
#include "output-json-nfs3.h"
|
||||
|
||||
#ifdef HAVE_RUST
|
||||
#ifdef HAVE_LIBJANSSON
|
||||
#include "rust-nfs-log-gen.h"
|
||||
|
||||
typedef struct LogNFS3FileCtx_ {
|
||||
LogFileCtx *file_ctx;
|
||||
uint32_t flags;
|
||||
} LogNFS3FileCtx;
|
||||
|
||||
typedef struct LogNFS3LogThread_ {
|
||||
LogNFS3FileCtx *nfs3log_ctx;
|
||||
uint32_t count;
|
||||
MemBuffer *buffer;
|
||||
} LogNFS3LogThread;
|
||||
|
||||
static int JsonNFS3Logger(ThreadVars *tv, void *thread_data,
|
||||
const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
|
||||
{
|
||||
NFS3Transaction *nfs3tx = tx;
|
||||
LogNFS3LogThread *thread = thread_data;
|
||||
json_t *js, *nfs3js;
|
||||
|
||||
if (rs_nfs3_tx_logging_is_filtered(nfs3tx))
|
||||
return TM_ECODE_OK;
|
||||
|
||||
js = CreateJSONHeader((Packet *)p, 0, "nfs3");
|
||||
if (unlikely(js == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
nfs3js = rs_nfs3_log_json_response(tx);
|
||||
if (unlikely(nfs3js == NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
json_object_set_new(js, "nfs3", nfs3js);
|
||||
|
||||
MemBufferReset(thread->buffer);
|
||||
OutputJSONBuffer(js, thread->nfs3log_ctx->file_ctx, &thread->buffer);
|
||||
|
||||
json_decref(js);
|
||||
return TM_ECODE_OK;
|
||||
|
||||
error:
|
||||
json_decref(js);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
static void OutputNFS3LogDeInitCtxSub(OutputCtx *output_ctx)
|
||||
{
|
||||
LogNFS3FileCtx *nfs3log_ctx = (LogNFS3FileCtx *)output_ctx->data;
|
||||
SCFree(nfs3log_ctx);
|
||||
SCFree(output_ctx);
|
||||
}
|
||||
|
||||
static OutputCtx *OutputNFS3LogInitSub(ConfNode *conf,
|
||||
OutputCtx *parent_ctx)
|
||||
{
|
||||
AlertJsonThread *ajt = parent_ctx->data;
|
||||
|
||||
LogNFS3FileCtx *nfs3log_ctx = SCCalloc(1, sizeof(*nfs3log_ctx));
|
||||
if (unlikely(nfs3log_ctx == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
nfs3log_ctx->file_ctx = ajt->file_ctx;
|
||||
|
||||
OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
|
||||
if (unlikely(output_ctx == NULL)) {
|
||||
SCFree(nfs3log_ctx);
|
||||
return NULL;
|
||||
}
|
||||
output_ctx->data = nfs3log_ctx;
|
||||
output_ctx->DeInit = OutputNFS3LogDeInitCtxSub;
|
||||
|
||||
SCLogDebug("NFS3 log sub-module initialized.");
|
||||
|
||||
AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_NFS3);
|
||||
|
||||
return output_ctx;
|
||||
}
|
||||
|
||||
#define OUTPUT_BUFFER_SIZE 65535
|
||||
|
||||
static TmEcode JsonNFS3LogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
||||
{
|
||||
LogNFS3LogThread *thread = SCCalloc(1, sizeof(*thread));
|
||||
if (unlikely(thread == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
if (initdata == NULL) {
|
||||
SCLogDebug("Error getting context for EveLogNFS3. \"initdata\" is NULL.");
|
||||
SCFree(thread);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
|
||||
if (unlikely(thread->buffer == NULL)) {
|
||||
SCFree(thread);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->nfs3log_ctx = ((OutputCtx *)initdata)->data;
|
||||
*data = (void *)thread;
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static TmEcode JsonNFS3LogThreadDeinit(ThreadVars *t, void *data)
|
||||
{
|
||||
LogNFS3LogThread *thread = (LogNFS3LogThread *)data;
|
||||
if (thread == NULL) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
if (thread->buffer != NULL) {
|
||||
MemBufferFree(thread->buffer);
|
||||
}
|
||||
SCFree(thread);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
void JsonNFS3LogRegister(void)
|
||||
{
|
||||
/* Register as an eve sub-module. */
|
||||
OutputRegisterTxSubModule(LOGGER_JSON_NFS3, "eve-log", "JsonNFS3Log",
|
||||
"eve-log.nfs3", OutputNFS3LogInitSub, ALPROTO_NFS3,
|
||||
JsonNFS3Logger, JsonNFS3LogThreadInit,
|
||||
JsonNFS3LogThreadDeinit, NULL);
|
||||
|
||||
SCLogDebug("NFS3 JSON logger registered.");
|
||||
}
|
||||
|
||||
#else /* No JSON support. */
|
||||
|
||||
void JsonNFS3LogRegister(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBJANSSON */
|
||||
|
||||
#else /* no rust */
|
||||
|
||||
void JsonNFS3LogRegister(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* HAVE_RUST */
|
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2015 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author FirstName LastName <name@domain>
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_JSON_NFS3_H__
|
||||
#define __OUTPUT_JSON_NFS3_H__
|
||||
|
||||
void JsonNFS3LogRegister(void);
|
||||
|
||||
#endif /* __OUTPUT_JSON_NFS3_H__ */
|
Loading…
Reference in New Issue