rust/nfs: NFSv3 parser, logger and detection

pull/2747/head
Victor Julien 8 years ago
parent 69bf219b39
commit d6592211d0

@ -36,3 +36,4 @@ pub mod filetracker;
pub mod lua;
pub mod dns;
pub mod nfs;

@ -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()
}

@ -39,6 +39,7 @@ app-layer-protos.c app-layer-protos.h \
app-layer-smb2.c app-layer-smb2.h \
app-layer-smb.c app-layer-smb.h \
app-layer-smtp.c app-layer-smtp.h \
app-layer-nfs3.c app-layer-nfs3.h \
app-layer-template.c app-layer-template.h \
app-layer-ssh.c app-layer-ssh.h \
app-layer-ssl.c app-layer-ssl.h \
@ -207,6 +208,7 @@ detect-lua-extensions.c detect-lua-extensions.h \
detect-mark.c detect-mark.h \
detect-metadata.c detect-metadata.h \
detect-msg.c detect-msg.h \
detect-nfs3-procedure.c detect-nfs3-procedure.h \
detect-noalert.c detect-noalert.h \
detect-nocase.c detect-nocase.h \
detect-offset.c detect-offset.h \
@ -296,6 +298,7 @@ output-json-smtp.c output-json-smtp.h \
output-json-ssh.c output-json-ssh.h \
output-json-stats.c output-json-stats.h \
output-json-tls.c output-json-tls.h \
output-json-nfs3.c output-json-nfs3.h \
output-json-template.c output-json-template.h \
output-json-vars.c output-json-vars.h \
output-lua.c output-lua.h \

@ -696,6 +696,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
printf(" alproto: ALPROTO_MODBUS\n");
else if (pp_pe->alproto == ALPROTO_ENIP)
printf(" alproto: ALPROTO_ENIP\n");
else if (pp_pe->alproto == ALPROTO_NFS3)
printf(" alproto: ALPROTO_NFS3\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)
@ -753,6 +755,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
printf(" alproto: ALPROTO_MODBUS\n");
else if (pp_pe->alproto == ALPROTO_ENIP)
printf(" alproto: ALPROTO_ENIP\n");
else if (pp_pe->alproto == ALPROTO_NFS3)
printf(" alproto: ALPROTO_NFS3\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)

@ -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__ */

@ -60,6 +60,7 @@
#include "app-layer-modbus.h"
#include "app-layer-enip.h"
#include "app-layer-dnp3.h"
#include "app-layer-nfs3.h"
#include "app-layer-template.h"
#include "conf.h"
@ -1282,6 +1283,7 @@ void AppLayerParserRegisterProtocolParsers(void)
RegisterENIPUDPParsers();
RegisterENIPTCPParsers();
RegisterDNP3Parsers();
RegisterNFS3Parsers();
RegisterTemplateParsers();
/** IMAP */

@ -81,6 +81,9 @@ const char *AppProtoToString(AppProto alproto)
case ALPROTO_DNP3:
proto_name = "dnp3";
break;
case ALPROTO_NFS3:
proto_name = "nfs3";
break;
case ALPROTO_TEMPLATE:
proto_name = "template";
break;

@ -44,6 +44,7 @@ enum AppProtoEnum {
ALPROTO_MODBUS,
ALPROTO_ENIP,
ALPROTO_DNP3,
ALPROTO_NFS3,
ALPROTO_TEMPLATE,
/* used by the probing parser when alproto detection fails

@ -84,6 +84,13 @@ void DetectFilenameRegister(void)
ALPROTO_SMTP, SIG_FLAG_TOSERVER, 0,
DetectFileInspectGeneric);
DetectAppLayerInspectEngineRegister("files",
ALPROTO_NFS3, SIG_FLAG_TOSERVER, 0,
DetectFileInspectGeneric);
DetectAppLayerInspectEngineRegister("files",
ALPROTO_NFS3, SIG_FLAG_TOCLIENT, 0,
DetectFileInspectGeneric);
g_file_match_list_id = DetectBufferTypeGetByName("files");
SCLogDebug("registering filename rule option");

@ -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__ */

@ -63,6 +63,8 @@
#include "detect-http-hh.h"
#include "detect-http-hrh.h"
#include "detect-nfs3-procedure.h"
#include "detect-engine-event.h"
#include "decode.h"
@ -3909,6 +3911,7 @@ void SigTableSetup(void)
DetectTlsRegister();
DetectTlsValidityRegister();
DetectTlsVersionRegister();
DetectNfs3ProcedureRegister();
DetectUrilenRegister();
DetectDetectionFilterRegister();
DetectAsn1Register();

@ -1289,6 +1289,7 @@ enum {
DETECT_AL_HTTP_RAW_HOST,
DETECT_AL_HTTP_REQUEST_LINE,
DETECT_AL_HTTP_RESPONSE_LINE,
DETECT_AL_NFS3_PROCEDURE,
DETECT_AL_SSH_PROTOCOL,
DETECT_AL_SSH_PROTOVERSION,
DETECT_AL_SSH_SOFTWARE,

@ -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__ */

@ -68,6 +68,7 @@
#include "log-tcp-data.h"
#include "log-stats.h"
#include "output-json.h"
#include "output-json-nfs3.h"
#include "output-json-template.h"
#include "output-lua.h"
#include "output-json-dnp3.h"
@ -1081,6 +1082,8 @@ void OutputRegisterLoggers(void)
JsonDNP3LogRegister();
JsonVarsLogRegister();
/* NFS3 JSON logger. */
JsonNFS3LogRegister();
/* Template JSON logger. */
JsonTemplateLogRegister();
}

@ -39,4 +39,13 @@ typedef struct SuricataContext_ {
} SuricataContext;
typedef struct SuricataFileContext_ {
const StreamingBufferConfig *sbcfg;
} SuricataFileContext;
struct _Store;
typedef struct _Store Store;
#endif /* !__RUST_H__ */

@ -396,6 +396,7 @@ typedef enum {
LOGGER_JSON_HTTP,
LOGGER_JSON_SMTP,
LOGGER_JSON_TLS,
LOGGER_JSON_NFS3,
LOGGER_JSON_TEMPLATE,
LOGGER_TLS_STORE,
LOGGER_TLS,

@ -235,6 +235,8 @@ outputs:
# to yes
#md5: [body, subject]
#- dnp3
#- nfs3
- ssh
- stats:
totals: yes # stats for all threads merged together
@ -244,7 +246,6 @@ outputs:
- flow
# uni-directional flows
#- netflow
#- dnp3
# Vars log flowbits and other packet and flow vars
#- vars
@ -675,6 +676,8 @@ pcap-file:
# "detection-only" enables protocol detection only (parser disabled).
app-layer:
protocols:
nfs3:
enabled: yes
tls:
enabled: yes
detection-ports:

Loading…
Cancel
Save