mirror of https://github.com/OISF/suricata
add RFB parser
This commit adds support for the Remote Framebuffer Protocol (RFB) as used, for example, by various VNC implementations. It targets the official versions 3.3, 3.7 and 3.8 of the protocol and provides logging for the RFB handshake communication for now. Logged events include endpoint versions, details of the security (i.e. authentication) exchange as well as metadata about the image transfer parameters. Detection is enabled using keywords for: - rfb.name: Session name as sticky buffer - rfb.sectype: Security type, e.g. VNC-style challenge-response - rfb.secresult: Result of the security exchange, e.g. OK, FAIL, ... The latter could be used, for example, to detect brute-force attempts on open VNC servers, while the name could be used to map unwanted VNC sessions to the desktop owners or machines. We also ship example EVE-JSON output and keyword docs as part of the Sphinx source for Suricata's RTD documentation.pull/4710/head
parent
b4d75b7448
commit
1c8943dedd
@ -0,0 +1,56 @@
|
||||
RFB Keywords
|
||||
============
|
||||
|
||||
The ``rfb.name`` and ``rfb.sectype`` keywords can be used for matching on various properties of
|
||||
RFB (Remote Framebuffer, i.e. VNC) handshakes.
|
||||
|
||||
|
||||
rfb.name
|
||||
--------
|
||||
|
||||
Match on the value of the RFB desktop name field.
|
||||
|
||||
Examples::
|
||||
|
||||
rfb.name; content:"Alice's desktop";
|
||||
rfb.name; pcre:"/.* \(screen [0-9]\)$/";
|
||||
|
||||
``rfb.name`` is a 'sticky buffer'.
|
||||
|
||||
``rfb.name`` can be used as ``fast_pattern``.
|
||||
|
||||
|
||||
rfb.secresult
|
||||
-------------
|
||||
|
||||
Match on the value of the RFB security result, e.g. ``ok``, ``fail``, ``toomany`` or ``unknown``.
|
||||
|
||||
Examples::
|
||||
|
||||
rfb.secresult: ok;
|
||||
rfb.secresult: unknown;
|
||||
|
||||
|
||||
rfb.sectype
|
||||
-----------
|
||||
|
||||
Match on the value of the RFB security type field, e.g. ``2`` for VNC challenge-response authentication, ``0`` for no authentication, and ``30`` for Apple's custom Remote Desktop authentication.
|
||||
|
||||
This keyword takes a numeric argument after a colon and supports additional qualifiers, such as:
|
||||
|
||||
* ``>`` (greater than)
|
||||
* ``<`` (less than)
|
||||
* ``>=`` (greater than or equal)
|
||||
* ``<=`` (less than or equal)
|
||||
|
||||
Examples::
|
||||
|
||||
rfb.sectype:2;
|
||||
rfb.sectype:>=3;
|
||||
|
||||
|
||||
Additional information
|
||||
----------------------
|
||||
|
||||
More information on the protocol can be found here:
|
||||
`<https://tools.ietf.org/html/rfc6143>`_
|
@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
// Author: Frank Honza <frank.honza@dcso.de>
|
||||
|
||||
use crate::rfb::rfb::*;
|
||||
use std::ptr;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rs_rfb_tx_get_name(
|
||||
tx: &mut RFBTransaction,
|
||||
buffer: *mut *const u8,
|
||||
buffer_len: *mut u32,
|
||||
) -> u8 {
|
||||
if let Some(ref r) = tx.tc_server_init {
|
||||
let p = &r.name;
|
||||
if p.len() > 0 {
|
||||
*buffer = p.as_ptr();
|
||||
*buffer_len = p.len() as u32;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = ptr::null();
|
||||
*buffer_len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rs_rfb_tx_get_sectype(
|
||||
tx: &mut RFBTransaction,
|
||||
sectype: *mut u32,
|
||||
) -> u8 {
|
||||
if let Some(ref r) = tx.chosen_security_type {
|
||||
*sectype = *r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
*sectype = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rs_rfb_tx_get_secresult(
|
||||
tx: &mut RFBTransaction,
|
||||
secresult: *mut u32,
|
||||
) -> u8 {
|
||||
if let Some(ref r) = tx.tc_security_result {
|
||||
*secresult = r.status;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
// Author: Frank Honza <frank.honza@dcso.de>
|
||||
|
||||
use std;
|
||||
use std::fmt::Write;
|
||||
use crate::json::*;
|
||||
use super::rfb::{RFBState, RFBTransaction};
|
||||
|
||||
fn log_rfb(tx: &RFBTransaction) -> Option<Json> {
|
||||
let js = Json::object();
|
||||
|
||||
// Protocol version
|
||||
if let Some(tx_spv) = &tx.tc_server_protocol_version {
|
||||
let spv = Json::object();
|
||||
spv.set_string("major", &tx_spv.major);
|
||||
spv.set_string("minor", &tx_spv.minor);
|
||||
js.set("server_protocol_version", spv);
|
||||
}
|
||||
if let Some(tx_cpv) = &tx.ts_client_protocol_version {
|
||||
let cpv = Json::object();
|
||||
cpv.set_string("major", &tx_cpv.major);
|
||||
cpv.set_string("minor", &tx_cpv.minor);
|
||||
js.set("client_protocol_version", cpv);
|
||||
}
|
||||
|
||||
// Authentication
|
||||
let auth = Json::object();
|
||||
if let Some(chosen_security_type) = tx.chosen_security_type {
|
||||
auth.set_integer("security_type", chosen_security_type as u64);
|
||||
}
|
||||
match tx.chosen_security_type {
|
||||
Some(2) => {
|
||||
let vncauth = Json::object();
|
||||
if let Some(ref sc) = tx.tc_vnc_challenge {
|
||||
let mut s = String::new();
|
||||
for &byte in &sc.secret[..] {
|
||||
write!(&mut s, "{:02x}", byte).expect("Unable to write");
|
||||
}
|
||||
vncauth.set_string("challenge", &s);
|
||||
}
|
||||
if let Some(ref sr) = tx.ts_vnc_response {
|
||||
let mut s = String::new();
|
||||
for &byte in &sr.secret[..] {
|
||||
write!(&mut s, "{:02x}", byte).expect("Unable to write");
|
||||
}
|
||||
vncauth.set_string("response", &s);
|
||||
}
|
||||
auth.set("vnc", vncauth);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
if let Some(security_result) = &tx.tc_security_result {
|
||||
match security_result.status {
|
||||
0 => auth.set_string("security_result", "OK"),
|
||||
1 => auth.set_string("security-result", "FAIL"),
|
||||
2 => auth.set_string("security_result", "TOOMANY"),
|
||||
_ => auth.set_string("security_result",
|
||||
&format!("UNKNOWN ({})", security_result.status)),
|
||||
}
|
||||
}
|
||||
js.set("authentication", auth);
|
||||
|
||||
if let Some(ref reason) = tx.tc_failure_reason {
|
||||
js.set_string("server_security_failure_reason", &reason.reason_string);
|
||||
}
|
||||
|
||||
// Client/Server init
|
||||
if let Some(s) = &tx.ts_client_init {
|
||||
js.set_boolean("screen_shared", s.shared != 0);
|
||||
}
|
||||
if let Some(tc_server_init) = &tx.tc_server_init {
|
||||
let fb = Json::object();
|
||||
fb.set_integer("width", tc_server_init.width as u64);
|
||||
fb.set_integer("height", tc_server_init.height as u64);
|
||||
fb.set_string_from_bytes("name", &tc_server_init.name);
|
||||
|
||||
let pfj = Json::object();
|
||||
pfj.set_integer("bits_per_pixel", tc_server_init.pixel_format.bits_per_pixel as u64);
|
||||
pfj.set_integer("depth", tc_server_init.pixel_format.depth as u64);
|
||||
pfj.set_boolean("big_endian", tc_server_init.pixel_format.big_endian_flag != 0);
|
||||
pfj.set_boolean("true_color", tc_server_init.pixel_format.true_colour_flag != 0);
|
||||
pfj.set_integer("red_max", tc_server_init.pixel_format.red_max as u64);
|
||||
pfj.set_integer("green_max", tc_server_init.pixel_format.green_max as u64);
|
||||
pfj.set_integer("blue_max", tc_server_init.pixel_format.blue_max as u64);
|
||||
pfj.set_integer("red_shift", tc_server_init.pixel_format.red_shift as u64);
|
||||
pfj.set_integer("green_shift", tc_server_init.pixel_format.green_shift as u64);
|
||||
pfj.set_integer("blue_shift", tc_server_init.pixel_format.blue_shift as u64);
|
||||
pfj.set_integer("depth", tc_server_init.pixel_format.depth as u64);
|
||||
fb.set("pixel_format", pfj);
|
||||
|
||||
js.set("framebuffer", fb);
|
||||
}
|
||||
|
||||
return Some(js);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_logger_log(_state: &mut RFBState, tx: *mut std::os::raw::c_void) -> *mut JsonT {
|
||||
let tx = cast_pointer!(tx, RFBTransaction);
|
||||
match log_rfb(tx) {
|
||||
Some(js) => js.unwrap(),
|
||||
None => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
// Author: Frank Honza <frank.honza@dcso.de>
|
||||
|
||||
pub mod detect;
|
||||
pub mod logger;
|
||||
pub mod parser;
|
||||
pub mod rfb;
|
@ -0,0 +1,321 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
// Author: Frank Honza <frank.honza@dcso.de>
|
||||
|
||||
use std::fmt;
|
||||
use nom::*;
|
||||
use nom::number::streaming::*;
|
||||
|
||||
pub enum RFBGlobalState {
|
||||
TCServerProtocolVersion,
|
||||
TCSupportedSecurityTypes,
|
||||
TCVncChallenge,
|
||||
TCServerInit,
|
||||
TCFailureReason,
|
||||
TSClientProtocolVersion,
|
||||
TCServerSecurityType,
|
||||
TSSecurityTypeSelection,
|
||||
TSVncResponse,
|
||||
TCSecurityResult,
|
||||
TSClientInit,
|
||||
Message
|
||||
}
|
||||
|
||||
impl fmt::Display for RFBGlobalState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RFBGlobalState::TCServerProtocolVersion => write!(f, "TCServerProtocolVersion"),
|
||||
RFBGlobalState::TCSupportedSecurityTypes => write!(f, "TCSupportedSecurityTypes"),
|
||||
RFBGlobalState::TCVncChallenge => write!(f, "TCVncChallenge"),
|
||||
RFBGlobalState::TCServerInit => write!(f, "TCServerInit"),
|
||||
RFBGlobalState::TCFailureReason => write!(f, "TCFailureReason"),
|
||||
RFBGlobalState::TSClientProtocolVersion => write!(f, "TSClientProtocolVersion"),
|
||||
RFBGlobalState::TSSecurityTypeSelection => write!(f, "TSSecurityTypeSelection"),
|
||||
RFBGlobalState::TSVncResponse => write!(f, "TSVncResponse"),
|
||||
RFBGlobalState::TCSecurityResult => write!(f, "TCSecurityResult"),
|
||||
RFBGlobalState::TCServerSecurityType => write!(f, "TCServerSecurityType"),
|
||||
RFBGlobalState::TSClientInit => write!(f, "TSClientInit"),
|
||||
RFBGlobalState::Message => write!(f, "Message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProtocolVersion {
|
||||
pub major: String,
|
||||
pub minor: String
|
||||
}
|
||||
|
||||
pub struct SupportedSecurityTypes {
|
||||
pub number_of_types: u8,
|
||||
pub types: Vec<u8>
|
||||
}
|
||||
|
||||
pub struct SecurityTypeSelection {
|
||||
pub security_type: u8
|
||||
}
|
||||
|
||||
pub struct ServerSecurityType {
|
||||
pub security_type: u32
|
||||
}
|
||||
|
||||
pub struct SecurityResult {
|
||||
pub status: u32
|
||||
}
|
||||
|
||||
pub struct FailureReason {
|
||||
pub reason_string: String
|
||||
}
|
||||
|
||||
pub struct VncAuth {
|
||||
pub secret: Vec<u8>
|
||||
}
|
||||
|
||||
pub struct ClientInit {
|
||||
pub shared: u8
|
||||
}
|
||||
|
||||
pub struct PixelFormat {
|
||||
pub bits_per_pixel: u8,
|
||||
pub depth: u8,
|
||||
pub big_endian_flag: u8,
|
||||
pub true_colour_flag: u8,
|
||||
pub red_max: u16,
|
||||
pub green_max: u16,
|
||||
pub blue_max: u16,
|
||||
pub red_shift: u8,
|
||||
pub green_shift: u8,
|
||||
pub blue_shift: u8,
|
||||
}
|
||||
|
||||
pub struct ServerInit {
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub pixel_format: PixelFormat,
|
||||
pub name_length: u32,
|
||||
pub name: Vec<u8>
|
||||
}
|
||||
|
||||
named!(pub parse_protocol_version<ProtocolVersion>,
|
||||
do_parse!(
|
||||
_rfb_string: take_str!(3)
|
||||
>> be_u8
|
||||
>> major: take_str!(3)
|
||||
>> be_u8
|
||||
>> minor: take_str!(3)
|
||||
>> be_u8
|
||||
>> (
|
||||
ProtocolVersion{
|
||||
major: major.to_string(),
|
||||
minor: minor.to_string(),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_supported_security_types<SupportedSecurityTypes>,
|
||||
do_parse!(
|
||||
number_of_types: be_u8
|
||||
>> types: take!(number_of_types)
|
||||
>> (
|
||||
SupportedSecurityTypes{
|
||||
number_of_types: number_of_types,
|
||||
types: types.to_vec()
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_server_security_type<ServerSecurityType>,
|
||||
do_parse!(
|
||||
security_type: be_u32
|
||||
>> (
|
||||
ServerSecurityType{
|
||||
security_type: security_type,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_vnc_auth<VncAuth>,
|
||||
do_parse!(
|
||||
secret: take!(16)
|
||||
>> (
|
||||
VncAuth {
|
||||
secret: secret.to_vec()
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_security_type_selection<SecurityTypeSelection>,
|
||||
do_parse!(
|
||||
security_type: be_u8
|
||||
>> (
|
||||
SecurityTypeSelection {
|
||||
security_type: security_type
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_security_result<SecurityResult>,
|
||||
do_parse!(
|
||||
status: be_u32
|
||||
>> (
|
||||
SecurityResult {
|
||||
status: status
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_failure_reason<FailureReason>,
|
||||
do_parse!(
|
||||
reason_length: be_u32
|
||||
>> reason_string: take_str!(reason_length)
|
||||
>> (
|
||||
FailureReason {
|
||||
reason_string: reason_string.to_string()
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_client_init<ClientInit>,
|
||||
do_parse!(
|
||||
shared: be_u8
|
||||
>> (
|
||||
ClientInit {
|
||||
shared: shared
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_pixel_format<PixelFormat>,
|
||||
do_parse!(
|
||||
bits_per_pixel: be_u8
|
||||
>> depth: be_u8
|
||||
>> big_endian_flag: be_u8
|
||||
>> true_colour_flag: be_u8
|
||||
>> red_max: be_u16
|
||||
>> green_max: be_u16
|
||||
>> blue_max: be_u16
|
||||
>> red_shift: be_u8
|
||||
>> green_shift: be_u8
|
||||
>> blue_shift: be_u8
|
||||
>> take!(3)
|
||||
>> (
|
||||
PixelFormat {
|
||||
bits_per_pixel: bits_per_pixel,
|
||||
depth: depth,
|
||||
big_endian_flag: big_endian_flag,
|
||||
true_colour_flag: true_colour_flag,
|
||||
red_max: red_max,
|
||||
green_max: green_max,
|
||||
blue_max: blue_max,
|
||||
red_shift: red_shift,
|
||||
green_shift: green_shift,
|
||||
blue_shift: blue_shift,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_server_init<ServerInit>,
|
||||
do_parse!(
|
||||
width: be_u16
|
||||
>> height: be_u16
|
||||
>> pixel_format: parse_pixel_format
|
||||
>> name_length: be_u32
|
||||
>> name: take!(name_length)
|
||||
>> (
|
||||
ServerInit {
|
||||
width: width,
|
||||
height: height,
|
||||
pixel_format: pixel_format,
|
||||
name_length: name_length,
|
||||
name: name.to_vec()
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nom::*;
|
||||
use super::*;
|
||||
|
||||
/// Simple test of some valid data.
|
||||
#[test]
|
||||
fn test_parse_version() {
|
||||
let buf = b"RFB 003.008\n";
|
||||
|
||||
let result = parse_protocol_version(buf);
|
||||
match result {
|
||||
Ok((remainder, message)) => {
|
||||
// Check the first message.
|
||||
assert_eq!(message.major, "003");
|
||||
|
||||
// And we should have 6 bytes left.
|
||||
assert_eq!(remainder.len(), 0);
|
||||
}
|
||||
Err(Err::Incomplete(_)) => {
|
||||
panic!("Result should not have been incomplete.");
|
||||
}
|
||||
Err(Err::Error(err)) |
|
||||
Err(Err::Failure(err)) => {
|
||||
panic!("Result should not be an error: {:?}.", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_server_init() {
|
||||
let buf = [
|
||||
0x05, 0x00, 0x03, 0x20, 0x20, 0x18, 0x00, 0x01,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x10, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
|
||||
0x61, 0x6e, 0x65, 0x61, 0x67, 0x6c, 0x65, 0x73,
|
||||
0x40, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e
|
||||
];
|
||||
|
||||
let result = parse_server_init(&buf);
|
||||
match result {
|
||||
Ok((remainder, message)) => {
|
||||
// Check the first message.
|
||||
assert_eq!(message.width, 1280);
|
||||
assert_eq!(message.height, 800);
|
||||
assert_eq!(message.pixel_format.bits_per_pixel, 32);
|
||||
|
||||
// And we should have 6 bytes left.
|
||||
assert_eq!(remainder.len(), 0);
|
||||
}
|
||||
Err(Err::Incomplete(_)) => {
|
||||
panic!("Result should not have been incomplete.");
|
||||
}
|
||||
Err(Err::Error(err)) |
|
||||
Err(Err::Failure(err)) => {
|
||||
panic!("Result should not be an error: {:?}.", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,810 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
// Author: Frank Honza <frank.honza@dcso.de>
|
||||
|
||||
use std;
|
||||
use std::ffi::CString;
|
||||
use std::mem::transmute;
|
||||
use crate::core::{self, ALPROTO_UNKNOWN, AppProto, Flow, IPPROTO_TCP};
|
||||
use crate::log::*;
|
||||
use crate::applayer;
|
||||
use crate::applayer::*;
|
||||
use nom;
|
||||
use super::parser;
|
||||
|
||||
static mut ALPROTO_RFB: AppProto = ALPROTO_UNKNOWN;
|
||||
|
||||
pub struct RFBTransaction {
|
||||
tx_id: u64,
|
||||
pub complete: bool,
|
||||
pub chosen_security_type: Option<u32>,
|
||||
|
||||
pub tc_server_protocol_version: Option<parser::ProtocolVersion>,
|
||||
pub ts_client_protocol_version: Option<parser::ProtocolVersion>,
|
||||
pub tc_supported_security_types: Option<parser::SupportedSecurityTypes>,
|
||||
pub ts_security_type_selection: Option<parser::SecurityTypeSelection>,
|
||||
pub tc_server_security_type: Option<parser::ServerSecurityType>,
|
||||
pub tc_vnc_challenge: Option<parser::VncAuth>,
|
||||
pub ts_vnc_response: Option<parser::VncAuth>,
|
||||
pub ts_client_init: Option<parser::ClientInit>,
|
||||
pub tc_security_result: Option<parser::SecurityResult>,
|
||||
pub tc_failure_reason: Option<parser::FailureReason>,
|
||||
pub tc_server_init: Option<parser::ServerInit>,
|
||||
|
||||
logged: LoggerFlags,
|
||||
de_state: Option<*mut core::DetectEngineState>,
|
||||
events: *mut core::AppLayerDecoderEvents,
|
||||
detect_flags: applayer::TxDetectFlags,
|
||||
}
|
||||
|
||||
impl RFBTransaction {
|
||||
pub fn new() -> RFBTransaction {
|
||||
RFBTransaction {
|
||||
tx_id: 0,
|
||||
complete: false,
|
||||
chosen_security_type: None,
|
||||
|
||||
tc_server_protocol_version: None,
|
||||
ts_client_protocol_version: None,
|
||||
tc_supported_security_types: None,
|
||||
ts_security_type_selection: None,
|
||||
tc_server_security_type: None,
|
||||
tc_vnc_challenge: None,
|
||||
ts_vnc_response: None,
|
||||
ts_client_init: None,
|
||||
tc_security_result: None,
|
||||
tc_failure_reason: None,
|
||||
tc_server_init: None,
|
||||
|
||||
logged: LoggerFlags::new(),
|
||||
de_state: None,
|
||||
events: std::ptr::null_mut(),
|
||||
detect_flags: applayer::TxDetectFlags::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free(&mut self) {
|
||||
if self.events != std::ptr::null_mut() {
|
||||
core::sc_app_layer_decoder_events_free_events(&mut self.events);
|
||||
}
|
||||
if let Some(state) = self.de_state {
|
||||
core::sc_detect_engine_state_free(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RFBTransaction {
|
||||
fn drop(&mut self) {
|
||||
self.free();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RFBState {
|
||||
tx_id: u64,
|
||||
transactions: Vec<RFBTransaction>,
|
||||
state: parser::RFBGlobalState
|
||||
}
|
||||
|
||||
impl RFBState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tx_id: 0,
|
||||
transactions: Vec::new(),
|
||||
state: parser::RFBGlobalState::TCServerProtocolVersion
|
||||
}
|
||||
}
|
||||
|
||||
// Free a transaction by ID.
|
||||
fn free_tx(&mut self, tx_id: u64) {
|
||||
let len = self.transactions.len();
|
||||
let mut found = false;
|
||||
let mut index = 0;
|
||||
for i in 0..len {
|
||||
let tx = &self.transactions[i];
|
||||
if tx.tx_id == tx_id + 1 {
|
||||
found = true;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
self.transactions.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tx(&mut self, tx_id: u64) -> Option<&RFBTransaction> {
|
||||
for tx in &mut self.transactions {
|
||||
if tx.tx_id == tx_id + 1 {
|
||||
return Some(tx);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn new_tx(&mut self) -> RFBTransaction {
|
||||
let mut tx = RFBTransaction::new();
|
||||
self.tx_id += 1;
|
||||
tx.tx_id = self.tx_id;
|
||||
return tx;
|
||||
}
|
||||
|
||||
fn get_current_tx(&mut self) -> Option<&mut RFBTransaction> {
|
||||
for tx in &mut self.transactions {
|
||||
if tx.tx_id == self.tx_id {
|
||||
return Some(tx);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn parse_request(&mut self, input: &[u8]) -> AppLayerResult {
|
||||
// We're not interested in empty requests.
|
||||
if input.len() == 0 {
|
||||
return AppLayerResult::ok();
|
||||
}
|
||||
|
||||
let mut current = input;
|
||||
SCLogDebug!("request_state {}, input_len {}", self.state, input.len());
|
||||
loop {
|
||||
if current.len() == 0 {
|
||||
return AppLayerResult::ok();
|
||||
}
|
||||
match self.state {
|
||||
parser::RFBGlobalState::TSClientProtocolVersion => {
|
||||
match parser::parse_protocol_version(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
if request.major == "003" && request.minor == "003" {
|
||||
// in version 3.3 the server decided security type
|
||||
self.state = parser::RFBGlobalState::TCServerSecurityType;
|
||||
} else {
|
||||
self.state = parser::RFBGlobalState::TCSupportedSecurityTypes;
|
||||
}
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.ts_client_protocol_version = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TSSecurityTypeSelection => {
|
||||
match parser::parse_security_type_selection(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
|
||||
let chosen_security_type = request.security_type;
|
||||
match chosen_security_type {
|
||||
2 => self.state = parser::RFBGlobalState::TCVncChallenge,
|
||||
1 => self.state = parser::RFBGlobalState::TSClientInit,
|
||||
_ => return AppLayerResult::err(),
|
||||
}
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.ts_security_type_selection = Some(request);
|
||||
current_transaction.chosen_security_type = Some(chosen_security_type as u32);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TSVncResponse => {
|
||||
match parser::parse_vnc_auth(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
|
||||
self.state = parser::RFBGlobalState::TCSecurityResult;
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.ts_vnc_response = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TSClientInit => {
|
||||
match parser::parse_client_init(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
self.state = parser::RFBGlobalState::TCServerInit;
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.ts_client_init = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::Message => {
|
||||
//todo implement RFB messages, for now we stop here
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
parser::RFBGlobalState::TCServerProtocolVersion => {
|
||||
SCLogDebug!("Reversed traffic, expected response.");
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
_ => {
|
||||
SCLogDebug!("Invalid state for request {}", self.state);
|
||||
current = b"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&mut self, input: &[u8]) -> AppLayerResult {
|
||||
// We're not interested in empty responses.
|
||||
if input.len() == 0 {
|
||||
return AppLayerResult::ok();
|
||||
}
|
||||
|
||||
let mut current = input;
|
||||
SCLogDebug!("response_state {}, response_len {}", self.state, input.len());
|
||||
loop {
|
||||
if current.len() == 0 {
|
||||
return AppLayerResult::ok();
|
||||
}
|
||||
match self.state {
|
||||
parser::RFBGlobalState::TCServerProtocolVersion => {
|
||||
match parser::parse_protocol_version(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
self.state = parser::RFBGlobalState::TSClientProtocolVersion;
|
||||
let tx = self.new_tx();
|
||||
self.transactions.push(tx);
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_server_protocol_version = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCSupportedSecurityTypes => {
|
||||
match parser::parse_supported_security_types(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
SCLogDebug!(
|
||||
"supported_security_types: {}, types: {}", request.number_of_types,
|
||||
request.types.iter().map(ToString::to_string).map(|v| v + " ").collect::<String>()
|
||||
);
|
||||
|
||||
self.state = parser::RFBGlobalState::TSSecurityTypeSelection;
|
||||
if request.number_of_types == 0 {
|
||||
self.state = parser::RFBGlobalState::TCFailureReason;
|
||||
}
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_supported_security_types = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCServerSecurityType => {
|
||||
// In RFB 3.3, the server decides the authentication type
|
||||
match parser::parse_server_security_type(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
let chosen_security_type = request.security_type;
|
||||
SCLogDebug!("chosen_security_type: {}", chosen_security_type);
|
||||
match chosen_security_type {
|
||||
0 => self.state = parser::RFBGlobalState::TCFailureReason,
|
||||
1 => self.state = parser::RFBGlobalState::TSClientInit,
|
||||
2 => self.state = parser::RFBGlobalState::TCVncChallenge,
|
||||
_ => {
|
||||
// TODO Event unknown security type
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_server_security_type = Some(request);
|
||||
current_transaction.chosen_security_type = Some(chosen_security_type);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCVncChallenge => {
|
||||
match parser::parse_vnc_auth(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
|
||||
self.state = parser::RFBGlobalState::TSVncResponse;
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_vnc_challenge = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCSecurityResult => {
|
||||
match parser::parse_security_result(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
|
||||
if request.status == 0 {
|
||||
self.state = parser::RFBGlobalState::TSClientInit;
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_security_result = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
} else if request.status == 1 {
|
||||
self.state = parser::RFBGlobalState::TCFailureReason;
|
||||
} else {
|
||||
// TODO: Event: unknown security result value
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCFailureReason => {
|
||||
match parser::parse_failure_reason(current) {
|
||||
Ok((_rem, request)) => {
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_failure_reason = Some(request);
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::TCServerInit => {
|
||||
match parser::parse_server_init(current) {
|
||||
Ok((rem, request)) => {
|
||||
current = rem;
|
||||
self.state = parser::RFBGlobalState::Message;
|
||||
|
||||
match self.get_current_tx() {
|
||||
Some(current_transaction) => {
|
||||
current_transaction.tc_server_init = Some(request);
|
||||
// connection initialization is complete and parsed
|
||||
current_transaction.complete = true;
|
||||
}
|
||||
_ => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Incomplete(v)) => {
|
||||
if let nom::Needed::Size(n) = v {
|
||||
return AppLayerResult::incomplete((input.len() - current.len()) as u32,
|
||||
(current.len() + n) as u32);
|
||||
}
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
Err(_) => {
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
parser::RFBGlobalState::Message => {
|
||||
//todo implement RFB messages, for now we stop here
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
_ => {
|
||||
SCLogDebug!("Invalid state for response");
|
||||
return AppLayerResult::err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_iterator(
|
||||
&mut self,
|
||||
min_tx_id: u64,
|
||||
state: &mut u64,
|
||||
) -> Option<(&RFBTransaction, u64, bool)> {
|
||||
let mut index = *state as usize;
|
||||
let len = self.transactions.len();
|
||||
|
||||
while index < len {
|
||||
let tx = &self.transactions[index];
|
||||
if tx.tx_id < min_tx_id + 1 {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
*state = index as u64;
|
||||
return Some((tx, tx.tx_id - 1, (len - index) > 1));
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// C exports.
|
||||
|
||||
export_tx_get_detect_state!(
|
||||
rs_rfb_tx_get_detect_state,
|
||||
RFBTransaction
|
||||
);
|
||||
export_tx_set_detect_state!(
|
||||
rs_rfb_tx_set_detect_state,
|
||||
RFBTransaction
|
||||
);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_new() -> *mut std::os::raw::c_void {
|
||||
let state = RFBState::new();
|
||||
let boxed = Box::new(state);
|
||||
return unsafe { transmute(boxed) };
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_free(state: *mut std::os::raw::c_void) {
|
||||
// Just unbox...
|
||||
let _drop: Box<RFBState> = unsafe { transmute(state) };
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_tx_free(
|
||||
state: *mut std::os::raw::c_void,
|
||||
tx_id: u64,
|
||||
) {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
state.free_tx(tx_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_parse_request(
|
||||
_flow: *const Flow,
|
||||
state: *mut std::os::raw::c_void,
|
||||
_pstate: *mut std::os::raw::c_void,
|
||||
input: *const u8,
|
||||
input_len: u32,
|
||||
_data: *const std::os::raw::c_void,
|
||||
_flags: u8,
|
||||
) -> AppLayerResult {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
let buf = build_slice!(input, input_len as usize);
|
||||
return state.parse_request(buf);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_parse_response(
|
||||
_flow: *const Flow,
|
||||
state: *mut std::os::raw::c_void,
|
||||
_pstate: *mut std::os::raw::c_void,
|
||||
input: *const u8,
|
||||
input_len: u32,
|
||||
_data: *const std::os::raw::c_void,
|
||||
_flags: u8,
|
||||
) -> AppLayerResult {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
let buf = build_slice!(input, input_len as usize);
|
||||
return state.parse_response(buf);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_tx(
|
||||
state: *mut std::os::raw::c_void,
|
||||
tx_id: u64,
|
||||
) -> *mut std::os::raw::c_void {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
match state.get_tx(tx_id) {
|
||||
Some(tx) => {
|
||||
return unsafe { transmute(tx) };
|
||||
}
|
||||
None => {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_tx_count(
|
||||
state: *mut std::os::raw::c_void,
|
||||
) -> u64 {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
return state.tx_id;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_progress_completion_status(
|
||||
_direction: u8,
|
||||
) -> std::os::raw::c_int {
|
||||
// This parser uses 1 to signal transaction completion status.
|
||||
return 1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_tx_get_alstate_progress(
|
||||
tx: *mut std::os::raw::c_void,
|
||||
_direction: u8,
|
||||
) -> std::os::raw::c_int {
|
||||
let tx = cast_pointer!(tx, RFBTransaction);
|
||||
if tx.complete {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_tx_get_logged(
|
||||
_state: *mut std::os::raw::c_void,
|
||||
tx: *mut std::os::raw::c_void,
|
||||
) -> u32 {
|
||||
let tx = cast_pointer!(tx, RFBTransaction);
|
||||
return tx.logged.get();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_tx_set_logged(
|
||||
_state: *mut std::os::raw::c_void,
|
||||
tx: *mut std::os::raw::c_void,
|
||||
logged: u32,
|
||||
) {
|
||||
let tx = cast_pointer!(tx, RFBTransaction);
|
||||
tx.logged.set(logged);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_events(
|
||||
tx: *mut std::os::raw::c_void
|
||||
) -> *mut core::AppLayerDecoderEvents {
|
||||
let tx = cast_pointer!(tx, RFBTransaction);
|
||||
return tx.events;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_event_info(
|
||||
_event_name: *const std::os::raw::c_char,
|
||||
_event_id: *mut std::os::raw::c_int,
|
||||
_event_type: *mut core::AppLayerEventType,
|
||||
) -> std::os::raw::c_int {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_event_info_by_id(_event_id: std::os::raw::c_int,
|
||||
_event_name: *mut *const std::os::raw::c_char,
|
||||
_event_type: *mut core::AppLayerEventType
|
||||
) -> i8 {
|
||||
return -1;
|
||||
}
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_rfb_state_get_tx_iterator(
|
||||
_ipproto: u8,
|
||||
_alproto: AppProto,
|
||||
state: *mut std::os::raw::c_void,
|
||||
min_tx_id: u64,
|
||||
_max_tx_id: u64,
|
||||
istate: &mut u64,
|
||||
) -> applayer::AppLayerGetTxIterTuple {
|
||||
let state = cast_pointer!(state, RFBState);
|
||||
match state.tx_iterator(min_tx_id, istate) {
|
||||
Some((tx, out_tx_id, has_next)) => {
|
||||
let c_tx = unsafe { transmute(tx) };
|
||||
let ires = applayer::AppLayerGetTxIterTuple::with_values(
|
||||
c_tx,
|
||||
out_tx_id,
|
||||
has_next,
|
||||
);
|
||||
return ires;
|
||||
}
|
||||
None => {
|
||||
return applayer::AppLayerGetTxIterTuple::not_found();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parser name as a C style string.
|
||||
const PARSER_NAME: &'static [u8] = b"rfb\0";
|
||||
|
||||
export_tx_detect_flags_set!(rs_rfb_set_tx_detect_flags, RFBTransaction);
|
||||
export_tx_detect_flags_get!(rs_rfb_get_tx_detect_flags, RFBTransaction);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rs_rfb_register_parser() {
|
||||
let default_port = CString::new("[5900]").unwrap();
|
||||
let parser = RustParser {
|
||||
name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
|
||||
default_port: default_port.as_ptr(),
|
||||
ipproto: IPPROTO_TCP,
|
||||
probe_ts: None,
|
||||
probe_tc: None,
|
||||
min_depth: 0,
|
||||
max_depth: 16,
|
||||
state_new: rs_rfb_state_new,
|
||||
state_free: rs_rfb_state_free,
|
||||
tx_free: rs_rfb_state_tx_free,
|
||||
parse_ts: rs_rfb_parse_request,
|
||||
parse_tc: rs_rfb_parse_response,
|
||||
get_tx_count: rs_rfb_state_get_tx_count,
|
||||
get_tx: rs_rfb_state_get_tx,
|
||||
tx_get_comp_st: rs_rfb_state_progress_completion_status,
|
||||
tx_get_progress: rs_rfb_tx_get_alstate_progress,
|
||||
get_tx_logged: Some(rs_rfb_tx_get_logged),
|
||||
set_tx_logged: Some(rs_rfb_tx_set_logged),
|
||||
get_de_state: rs_rfb_tx_get_detect_state,
|
||||
set_de_state: rs_rfb_tx_set_detect_state,
|
||||
get_events: Some(rs_rfb_state_get_events),
|
||||
get_eventinfo: Some(rs_rfb_state_get_event_info),
|
||||
get_eventinfo_byid : Some(rs_rfb_state_get_event_info_by_id),
|
||||
localstorage_new: None,
|
||||
localstorage_free: None,
|
||||
get_tx_mpm_id: None,
|
||||
set_tx_mpm_id: None,
|
||||
get_files: None,
|
||||
get_tx_iterator: Some(rs_rfb_state_get_tx_iterator),
|
||||
get_tx_detect_flags: Some(rs_rfb_get_tx_detect_flags),
|
||||
set_tx_detect_flags: Some(rs_rfb_set_tx_detect_flags),
|
||||
};
|
||||
|
||||
let ip_proto_str = CString::new("tcp").unwrap();
|
||||
|
||||
if AppLayerProtoDetectConfProtoDetectionEnabled(
|
||||
ip_proto_str.as_ptr(),
|
||||
parser.name,
|
||||
) != 0
|
||||
{
|
||||
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
|
||||
ALPROTO_RFB = alproto;
|
||||
if AppLayerParserConfParserEnabled(
|
||||
ip_proto_str.as_ptr(),
|
||||
parser.name,
|
||||
) != 0
|
||||
{
|
||||
let _ = AppLayerRegisterParser(&parser, alproto);
|
||||
}
|
||||
SCLogDebug!("Rust rfb parser registered.");
|
||||
} else {
|
||||
SCLogDebug!("Protocol detector and parser disabled for RFB.");
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*
|
||||
* RFB (VNC) application layer detector and parser.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
|
||||
#include "app-layer-detect-proto.h"
|
||||
#include "app-layer-parser.h"
|
||||
#include "app-layer-rfb.h"
|
||||
|
||||
#include "rust-bindings.h"
|
||||
|
||||
static int RFBRegisterPatternsForProtocolDetection(void)
|
||||
{
|
||||
if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_RFB,
|
||||
"RFB ", 4, 0, STREAM_TOCLIENT) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_RFB,
|
||||
"RFB ", 4, 0, STREAM_TOSERVER) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RFBParserRegisterTests(void);
|
||||
|
||||
void RegisterRFBParsers(void)
|
||||
{
|
||||
rs_rfb_register_parser();
|
||||
if (RFBRegisterPatternsForProtocolDetection() < 0 )
|
||||
return;
|
||||
#ifdef UNITTESTS
|
||||
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_RFB,
|
||||
RFBParserRegisterTests);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
#include "stream-tcp.h"
|
||||
#include "util-unittest-helper.h"
|
||||
|
||||
static int RFBParserTest(void)
|
||||
{
|
||||
uint64_t ret[4];
|
||||
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
||||
FAIL_IF_NULL(alp_tctx);
|
||||
|
||||
StreamTcpInitConfig(TRUE);
|
||||
TcpSession ssn;
|
||||
memset(&ssn, 0, sizeof(ssn));
|
||||
|
||||
Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 59001, 5900);
|
||||
FAIL_IF_NULL(f);
|
||||
f->protoctx = &ssn;
|
||||
f->proto = IPPROTO_TCP;
|
||||
f->alproto = ALPROTO_RFB;
|
||||
|
||||
static const unsigned char rfb_version_str[12] = {
|
||||
0x52, 0x46, 0x42, 0x20, 0x30, 0x30, 0x33, 0x2e, 0x30, 0x30, 0x37, 0x0a
|
||||
};
|
||||
|
||||
// the RFB server sending the first handshake message
|
||||
int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOCLIENT | STREAM_START,
|
||||
(uint8_t *)rfb_version_str, sizeof(rfb_version_str));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
r = AppLayerParserParse(
|
||||
NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOSERVER, (uint8_t *)rfb_version_str, sizeof(rfb_version_str));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
static const unsigned char security_types[3] = {
|
||||
0x02, 0x01, 0x02
|
||||
};
|
||||
r = AppLayerParserParse(
|
||||
NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOCLIENT, (uint8_t *)security_types, sizeof(security_types));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
static const unsigned char type_selection[1] = {
|
||||
0x01
|
||||
};
|
||||
r = AppLayerParserParse(
|
||||
NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOSERVER, (uint8_t *)type_selection, sizeof(type_selection));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
static const unsigned char client_init[1] = {
|
||||
0x01
|
||||
};
|
||||
r = AppLayerParserParse(
|
||||
NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOSERVER, (uint8_t *)client_init, sizeof(client_init));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
static const unsigned char server_init[] = {
|
||||
0x05, 0x00, 0x03, 0x20, 0x20, 0x18, 0x00, 0x01,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x10, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
|
||||
0x61, 0x6e, 0x65, 0x61, 0x67, 0x6c, 0x65, 0x73,
|
||||
0x40, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e
|
||||
};
|
||||
|
||||
r = AppLayerParserParse(
|
||||
NULL, alp_tctx, f, ALPROTO_RFB, STREAM_TOCLIENT, (uint8_t *)server_init, sizeof(server_init));
|
||||
FAIL_IF_NOT(r == 0);
|
||||
|
||||
AppLayerParserTransactionsCleanup(f);
|
||||
UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
|
||||
FAIL_IF_NOT(ret[0] == 1); // inspect_id[0]
|
||||
FAIL_IF_NOT(ret[1] == 1); // inspect_id[1]
|
||||
FAIL_IF_NOT(ret[2] == 1); // log_id
|
||||
FAIL_IF_NOT(ret[3] == 1); // min_id
|
||||
|
||||
AppLayerParserTransactionsCleanup(f);
|
||||
AppLayerParserThreadCtxFree(alp_tctx);
|
||||
StreamTcpFreeConfig(TRUE);
|
||||
UTHFreeFlow(f);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
void RFBParserRegisterTests(void)
|
||||
{
|
||||
UtRegisterTest("RFBParserTest", RFBParserTest);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#ifndef __APP_LAYER_RFB_H__
|
||||
#define __APP_LAYER_RFB_H__
|
||||
|
||||
void RegisterRFBParsers(void);
|
||||
|
||||
#endif /* __APP_LAYER_RFB_H__ */
|
@ -0,0 +1,113 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* \author Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#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-engine-prefilter.h"
|
||||
#include "detect-urilen.h"
|
||||
|
||||
#include "flow.h"
|
||||
#include "flow-var.h"
|
||||
#include "flow-util.h"
|
||||
|
||||
#include "util-debug.h"
|
||||
#include "util-unittest.h"
|
||||
#include "util-unittest-helper.h"
|
||||
#include "util-spm.h"
|
||||
|
||||
#include "app-layer.h"
|
||||
#include "app-layer-parser.h"
|
||||
|
||||
#include "detect-rfb-name.h"
|
||||
#include "stream-tcp.h"
|
||||
|
||||
#include "rust.h"
|
||||
#include "app-layer-rfb.h"
|
||||
#include "rust-bindings.h"
|
||||
|
||||
#define KEYWORD_NAME "rfb.name"
|
||||
#define KEYWORD_DOC "rfb-keywords.html#rfb-name";
|
||||
#define BUFFER_NAME "rfb.name"
|
||||
#define BUFFER_DESC "rfb name"
|
||||
static int g_buffer_id = 0;
|
||||
|
||||
static int DetectRfbNameSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
|
||||
{
|
||||
if (DetectBufferSetActiveList(s, g_buffer_id) < 0)
|
||||
return -1;
|
||||
|
||||
if (DetectSignatureSetAppProto(s, ALPROTO_RFB) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
|
||||
const DetectEngineTransforms *transforms, Flow *_f,
|
||||
const uint8_t _flow_flags, void *txv, const int list_id)
|
||||
{
|
||||
InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
|
||||
if (buffer->inspect == NULL) {
|
||||
const uint8_t *b = NULL;
|
||||
uint32_t b_len = 0;
|
||||
|
||||
if (rs_rfb_tx_get_name(txv, &b, &b_len) != 1)
|
||||
return NULL;
|
||||
if (b == NULL || b_len == 0)
|
||||
return NULL;
|
||||
|
||||
InspectionBufferSetup(buffer, b, b_len);
|
||||
InspectionBufferApplyTransforms(buffer, transforms);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void DetectRfbNameRegister(void)
|
||||
{
|
||||
sigmatch_table[DETECT_AL_RFB_NAME].name = KEYWORD_NAME;
|
||||
sigmatch_table[DETECT_AL_RFB_NAME].url = DOC_URL DOC_VERSION "/rules/" KEYWORD_DOC
|
||||
sigmatch_table[DETECT_AL_RFB_NAME].desc = "sticky buffer to match on the RFB desktop name";
|
||||
sigmatch_table[DETECT_AL_RFB_NAME].Setup = DetectRfbNameSetup;
|
||||
sigmatch_table[DETECT_AL_RFB_NAME].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER;
|
||||
|
||||
DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_RFB,
|
||||
SIG_FLAG_TOCLIENT, 1,
|
||||
DetectEngineInspectBufferGeneric, GetData);
|
||||
|
||||
DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 1,
|
||||
PrefilterGenericMpmRegister, GetData, ALPROTO_RFB,
|
||||
1);
|
||||
|
||||
DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
|
||||
|
||||
g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME);
|
||||
|
||||
SCLogDebug("registering " BUFFER_NAME " rule option");
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2020 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 Frank Honza
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_RFB_NAME_H__
|
||||
#define __DETECT_RFB_NAME_H__
|
||||
|
||||
void DetectRfbNameRegister(void);
|
||||
|
||||
#endif /* __DETECT_RFB_NAME_H__ */
|
@ -0,0 +1,311 @@
|
||||
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "conf.h"
|
||||
#include "detect.h"
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-content-inspection.h"
|
||||
#include "detect-rfb-secresult.h"
|
||||
#include "util-unittest.h"
|
||||
|
||||
#include "rust-bindings.h"
|
||||
|
||||
#define PARSE_REGEX "\\S[A-z]"
|
||||
static DetectParseRegex parse_regex;
|
||||
|
||||
static int rfb_secresult_id = 0;
|
||||
|
||||
static int DetectRfbSecresultMatch(DetectEngineThreadCtx *det_ctx,
|
||||
Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s,
|
||||
const SigMatchCtx *ctx);
|
||||
static int DetectRfbSecresultSetup (DetectEngineCtx *, Signature *, const char *);
|
||||
void RfbSecresultRegisterTests(void);
|
||||
void DetectRfbSecresultFree(void *);
|
||||
|
||||
static int DetectEngineInspectRfbSecresultGeneric(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);
|
||||
|
||||
typedef struct DetectRfbSecresultData_ {
|
||||
uint32_t result; /** result code */
|
||||
} DetectRfbSecresultData;
|
||||
|
||||
/**
|
||||
* \brief Registration function for rfb.secresult: keyword
|
||||
*/
|
||||
void DetectRfbSecresultRegister (void)
|
||||
{
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].name = "rfb.secresult";
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].desc = "match RFB security result";
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].url = DOC_URL DOC_VERSION "/rules/rfb-keywords.html#rfb-secresult";
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].AppLayerTxMatch = DetectRfbSecresultMatch;
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].Setup = DetectRfbSecresultSetup;
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].Free = DetectRfbSecresultFree;
|
||||
sigmatch_table[DETECT_AL_RFB_SECRESULT].RegisterTests = RfbSecresultRegisterTests;
|
||||
|
||||
DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
|
||||
|
||||
DetectAppLayerInspectEngineRegister("rfb.secresult",
|
||||
ALPROTO_RFB, SIG_FLAG_TOCLIENT, 1,
|
||||
DetectEngineInspectRfbSecresultGeneric);
|
||||
|
||||
rfb_secresult_id = DetectBufferTypeGetByName("rfb.secresult");
|
||||
}
|
||||
|
||||
static int DetectEngineInspectRfbSecresultGeneric(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);
|
||||
}
|
||||
|
||||
enum {
|
||||
RFB_SECRESULT_OK = 0,
|
||||
RFB_SECRESULT_FAIL,
|
||||
RFB_SECRESULT_TOOMANY,
|
||||
RFB_SECRESULT_UNKNOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct DetectRfbSecresult_
|
||||
* DetectRfbSecresult_ is used to store values
|
||||
*/
|
||||
|
||||
struct DetectRfbSecresult_ {
|
||||
const char *result;
|
||||
uint16_t code;
|
||||
} results[] = {
|
||||
{ "ok", RFB_SECRESULT_OK, },
|
||||
{ "fail", RFB_SECRESULT_FAIL, },
|
||||
{ "toomany", RFB_SECRESULT_TOOMANY, },
|
||||
{ "unknown", RFB_SECRESULT_UNKNOWN, },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to match security result of a RFB TX
|
||||
*
|
||||
* \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 txv Pointer to the RFBTransaction.
|
||||
* \param s Pointer to the Signature.
|
||||
* \param ctx Pointer to the sigmatch that we will cast into DetectRfbSecresultData.
|
||||
*
|
||||
* \retval 0 no match.
|
||||
* \retval 1 match.
|
||||
*/
|
||||
static int DetectRfbSecresultMatch(DetectEngineThreadCtx *det_ctx,
|
||||
Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s,
|
||||
const SigMatchCtx *ctx)
|
||||
{
|
||||
const DetectRfbSecresultData *de = (const DetectRfbSecresultData *)ctx;
|
||||
uint32_t resultcode;
|
||||
int ret = 0;
|
||||
|
||||
if (!de)
|
||||
return 0;
|
||||
|
||||
ret = rs_rfb_tx_get_secresult(txv, &resultcode);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (de->result < 3) {
|
||||
/* we are asking for a defined code... */
|
||||
if (resultcode == de->result) {
|
||||
/* ... which needs to match */
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* we are asking for an unknown code */
|
||||
if (resultcode > 2) {
|
||||
/* match any unknown code */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief This function is used to parse options passed via rfb.secresults: keyword
|
||||
*
|
||||
* \param rawstr Pointer to the user provided secresult options
|
||||
*
|
||||
* \retval de pointer to DetectRfbSecresultData on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
static DetectRfbSecresultData *DetectRfbSecresultParse (const char *rawstr)
|
||||
{
|
||||
int i;
|
||||
DetectRfbSecresultData *de = NULL;
|
||||
#define MAX_SUBSTRINGS 30
|
||||
int ret = 0, found = 0;
|
||||
int ov[MAX_SUBSTRINGS];
|
||||
|
||||
ret = DetectParsePcreExec(&parse_regex, rawstr, 0, 0, ov, MAX_SUBSTRINGS);
|
||||
if (ret < 1) {
|
||||
SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for(i = 0; results[i].result != NULL; i++) {
|
||||
if((strcasecmp(results[i].result,rawstr)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found == 0) {
|
||||
SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown secresult value %s", rawstr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
de = SCMalloc(sizeof(DetectRfbSecresultData));
|
||||
if (unlikely(de == NULL))
|
||||
goto error;
|
||||
|
||||
de->result = results[i].code;
|
||||
|
||||
return de;
|
||||
|
||||
error:
|
||||
if (de) SCFree(de);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief this function is used to add the parsed secresult 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 secresult options
|
||||
*
|
||||
* \retval 0 on Success
|
||||
* \retval -1 on Failure
|
||||
*/
|
||||
static int DetectRfbSecresultSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
|
||||
{
|
||||
DetectRfbSecresultData *de = NULL;
|
||||
SigMatch *sm = NULL;
|
||||
|
||||
de = DetectRfbSecresultParse(rawstr);
|
||||
if (de == NULL)
|
||||
goto error;
|
||||
|
||||
sm = SigMatchAlloc();
|
||||
if (sm == NULL)
|
||||
goto error;
|
||||
|
||||
sm->type = DETECT_AL_RFB_SECRESULT;
|
||||
sm->ctx = (SigMatchCtx *)de;
|
||||
|
||||
SigMatchAppendSMToList(s, sm, rfb_secresult_id);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (de) SCFree(de);
|
||||
if (sm) SCFree(sm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief this function will free memory associated with DetectRfbSecresultData
|
||||
*
|
||||
* \param de pointer to DetectRfbSecresultData
|
||||
*/
|
||||
void DetectRfbSecresultFree(void *de_ptr)
|
||||
{
|
||||
DetectRfbSecresultData *de = (DetectRfbSecresultData *)de_ptr;
|
||||
if(de) SCFree(de);
|
||||
}
|
||||
|
||||
/*
|
||||
* ONLY TESTS BELOW THIS COMMENT
|
||||
*/
|
||||
|
||||
#ifdef UNITTESTS
|
||||
/**
|
||||
* \test RfbSecresultTestParse01 is a test for a valid secresult value
|
||||
*
|
||||
* \retval 1 on succces
|
||||
* \retval 0 on failure
|
||||
*/
|
||||
static int RfbSecresultTestParse01 (void)
|
||||
{
|
||||
DetectRfbSecresultData *de = NULL;
|
||||
de = DetectRfbSecresultParse("fail");
|
||||
if (de) {
|
||||
DetectRfbSecresultFree(de);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test RfbSecresultTestParse02 is a test for an invalid secresult value
|
||||
*
|
||||
* \retval 1 on succces
|
||||
* \retval 0 on failure
|
||||
*/
|
||||
static int RfbSecresultTestParse02 (void)
|
||||
{
|
||||
DetectRfbSecresultData *de = NULL;
|
||||
de = DetectRfbSecresultParse("invalidopt");
|
||||
if (de) {
|
||||
DetectRfbSecresultFree(de);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
/**
|
||||
* \brief this function registers unit tests for RfbSecresult
|
||||
*/
|
||||
void RfbSecresultRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
UtRegisterTest("RfbSecresultTestParse01", RfbSecresultTestParse01);
|
||||
UtRegisterTest("RfbSecresultTestParse02", RfbSecresultTestParse02);
|
||||
#endif /* UNITTESTS */
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_RFB_SECRESULT_H__
|
||||
#define __DETECT_RFB_SECRESULT_H__
|
||||
|
||||
void DetectRfbSecresultRegister(void);
|
||||
|
||||
#endif /*__DETECT_RFB_SECRESULT_H__ */
|
@ -0,0 +1,283 @@
|
||||
/* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* \author Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "conf.h"
|
||||
#include "detect.h"
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-content-inspection.h"
|
||||
#include "detect-rfb-sectype.h"
|
||||
#include "app-layer-parser.h"
|
||||
#include "util-byte.h"
|
||||
|
||||
#include "rust-bindings.h"
|
||||
|
||||
/**
|
||||
* [rfb.sectype]:[<|>|<=|>=]<type>;
|
||||
*/
|
||||
#define PARSE_REGEX "^\\s*(<=|>=|<|>)?\\s*([0-9]+)\\s*$"
|
||||
static DetectParseRegex parse_regex;
|
||||
|
||||
enum DetectRfbSectypeCompareMode {
|
||||
PROCEDURE_EQ = 1, /* equal */
|
||||
PROCEDURE_LT, /* less than */
|
||||
PROCEDURE_LE, /* less than or equal */
|
||||
PROCEDURE_GT, /* greater than */
|
||||
PROCEDURE_GE, /* greater than or equal */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
enum DetectRfbSectypeCompareMode mode;
|
||||
} DetectRfbSectypeData;
|
||||
|
||||
static DetectRfbSectypeData *DetectRfbSectypeParse (const char *);
|
||||
static int DetectRfbSectypeSetup (DetectEngineCtx *, Signature *s, const char *str);
|
||||
static void DetectRfbSectypeFree(void *);
|
||||
static int g_rfb_sectype_buffer_id = 0;
|
||||
|
||||
static int DetectEngineInspectRfbSectypeGeneric(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 DetectRfbSectypeMatch (DetectEngineThreadCtx *, Flow *,
|
||||
uint8_t, void *, void *, const Signature *,
|
||||
const SigMatchCtx *);
|
||||
|
||||
/**
|
||||
* \brief Registration function for rfb.sectype keyword.
|
||||
*/
|
||||
void DetectRfbSectypeRegister (void)
|
||||
{
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].name = "rfb.sectype";
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].desc = "match RFB security type";
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].url = DOC_URL DOC_VERSION "/rules/rfb-keywords.html#rfb-sectype";
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].AppLayerTxMatch = DetectRfbSectypeMatch;
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].Setup = DetectRfbSectypeSetup;
|
||||
sigmatch_table[DETECT_AL_RFB_SECTYPE].Free = DetectRfbSectypeFree;
|
||||
|
||||
DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
|
||||
|
||||
DetectAppLayerInspectEngineRegister("rfb.sectype",
|
||||
ALPROTO_RFB, SIG_FLAG_TOSERVER, 1,
|
||||
DetectEngineInspectRfbSectypeGeneric);
|
||||
|
||||
g_rfb_sectype_buffer_id = DetectBufferTypeGetByName("rfb.sectype");
|
||||
}
|
||||
|
||||
static int DetectEngineInspectRfbSectypeGeneric(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 SectypeMatch(const uint32_t version,
|
||||
enum DetectRfbSectypeCompareMode mode, uint32_t ref_version)
|
||||
{
|
||||
switch (mode) {
|
||||
case PROCEDURE_EQ:
|
||||
if (version == ref_version)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_LT:
|
||||
if (version < ref_version)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_LE:
|
||||
if (version <= ref_version)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_GT:
|
||||
if (version > ref_version)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
case PROCEDURE_GE:
|
||||
if (version >= ref_version)
|
||||
SCReturnInt(1);
|
||||
break;
|
||||
}
|
||||
SCReturnInt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to match security type of a RFB TX
|
||||
*
|
||||
* \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 txv Pointer to the RFBTransaction.
|
||||
* \param s Pointer to the Signature.
|
||||
* \param ctx Pointer to the sigmatch that we will cast into DetectRfbSectypeData.
|
||||
*
|
||||
* \retval 0 no match.
|
||||
* \retval 1 match.
|
||||
*/
|
||||
static int DetectRfbSectypeMatch (DetectEngineThreadCtx *det_ctx,
|
||||
Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s,
|
||||
const SigMatchCtx *ctx)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
const DetectRfbSectypeData *dd = (const DetectRfbSectypeData *)ctx;
|
||||
uint32_t version;
|
||||
rs_rfb_tx_get_sectype(txv, &version);
|
||||
if (SectypeMatch(version, dd->mode, dd->version))
|
||||
SCReturnInt(1);
|
||||
SCReturnInt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to parse options passed via rfb.sectype keywords.
|
||||
*
|
||||
* \param rawstr Pointer to the user provided options.
|
||||
*
|
||||
* \retval dd pointer to DetectRfbSectypeData on success.
|
||||
* \retval NULL on failure.
|
||||
*/
|
||||
static DetectRfbSectypeData *DetectRfbSectypeParse (const char *rawstr)
|
||||
{
|
||||
DetectRfbSectypeData *dd = NULL;
|
||||
#define MAX_SUBSTRINGS 30
|
||||
int ret = 0, res = 0;
|
||||
int ov[MAX_SUBSTRINGS];
|
||||
char mode[2] = "";
|
||||
char value1[20] = "";
|
||||
|
||||
ret = DetectParsePcreExec(&parse_regex, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dd = SCCalloc(1, sizeof(DetectRfbSectypeData));
|
||||
if (unlikely(dd == NULL))
|
||||
goto error;
|
||||
|
||||
if (strlen(mode) == 0) {
|
||||
dd->mode = PROCEDURE_EQ;
|
||||
} else 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;
|
||||
} else {
|
||||
SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid mode for rfb.sectype keyword");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dd->mode == 0) {
|
||||
dd->mode = PROCEDURE_EQ;
|
||||
}
|
||||
|
||||
/* set the first value */
|
||||
if (ByteExtractStringUint32(&dd->version, 10, strlen(value1), value1) <= 0) {
|
||||
SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
|
||||
"to rfb.sectype keyword");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return dd;
|
||||
|
||||
error:
|
||||
if (dd)
|
||||
SCFree(dd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Function to add the parsed RFB security type 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.
|
||||
*
|
||||
* \retval 0 on Success.
|
||||
* \retval -1 on Failure.
|
||||
*/
|
||||
static int DetectRfbSectypeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
|
||||
{
|
||||
if (DetectSignatureSetAppProto(s, ALPROTO_RFB) != 0)
|
||||
return -1;
|
||||
|
||||
DetectRfbSectypeData *dd = DetectRfbSectypeParse(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. */
|
||||
SigMatch *sm = SigMatchAlloc();
|
||||
if (sm == NULL)
|
||||
goto error;
|
||||
|
||||
sm->type = DETECT_AL_RFB_SECTYPE;
|
||||
sm->ctx = (void *)dd;
|
||||
|
||||
SigMatchAppendSMToList(s, sm, g_rfb_sectype_buffer_id);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
DetectRfbSectypeFree(dd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief Function to free memory associated with DetectRfbSectypeData.
|
||||
*
|
||||
* \param de_ptr Pointer to DetectRfbSectypeData.
|
||||
*/
|
||||
static void DetectRfbSectypeFree(void *ptr)
|
||||
{
|
||||
SCFree(ptr);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_RFB_SECTYPE_H__
|
||||
#define __DETECT_RFB_SECTYPE_H__
|
||||
|
||||
void DetectRfbSectypeRegister(void);
|
||||
|
||||
#endif /* __DETECT_RFB_SECTYPE_H__ */
|
@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2020 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 Frank Honza <frank.honza@dcso.de>
|
||||
*
|
||||
* Implement JSON/eve logging app-layer RFB.
|
||||
*/
|
||||
|
||||
#include "suricata-common.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-rfb.h"
|
||||
#include "output-json-rfb.h"
|
||||
|
||||
#include "rust-bindings.h"
|
||||
|
||||
typedef struct LogRFBFileCtx_ {
|
||||
LogFileCtx *file_ctx;
|
||||
uint32_t flags;
|
||||
} LogRFBFileCtx;
|
||||
|
||||
typedef struct LogRFBLogThread_ {
|
||||
LogRFBFileCtx *rfblog_ctx;
|
||||
uint32_t count;
|
||||
MemBuffer *buffer;
|
||||
} LogRFBLogThread;
|
||||
|
||||
json_t *JsonRFBAddMetadata(const Flow *f, uint64_t tx_id)
|
||||
{
|
||||
RFBState *state = FlowGetAppState(f);
|
||||
if (state) {
|
||||
RFBTransaction *tx = AppLayerParserGetTx(f->proto, ALPROTO_RFB, state, tx_id);
|
||||
if (tx) {
|
||||
return rs_rfb_logger_log(state, tx);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int JsonRFBLogger(ThreadVars *tv, void *thread_data,
|
||||
const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
|
||||
{
|
||||
LogRFBLogThread *thread = thread_data;
|
||||
|
||||
json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "rfb");
|
||||
if (unlikely(js == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
json_t *rfb_js = rs_rfb_logger_log(NULL, tx);
|
||||
if (unlikely(rfb_js == NULL)) {
|
||||
goto error;
|
||||
}
|
||||
json_object_set_new(js, "rfb", rfb_js);
|
||||
|
||||
MemBufferReset(thread->buffer);
|
||||
OutputJSONBuffer(js, thread->rfblog_ctx->file_ctx, &thread->buffer);
|
||||
json_decref(js);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
|
||||
error:
|
||||
json_decref(js);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
static void OutputRFBLogDeInitCtxSub(OutputCtx *output_ctx)
|
||||
{
|
||||
LogRFBFileCtx *rfblog_ctx = (LogRFBFileCtx *)output_ctx->data;
|
||||
SCFree(rfblog_ctx);
|
||||
SCFree(output_ctx);
|
||||
}
|
||||
|
||||
static OutputInitResult OutputRFBLogInitSub(ConfNode *conf,
|
||||
OutputCtx *parent_ctx)
|
||||
{
|
||||
OutputInitResult result = { NULL, false };
|
||||
OutputJsonCtx *ajt = parent_ctx->data;
|
||||
|
||||
LogRFBFileCtx *rfblog_ctx = SCCalloc(1, sizeof(*rfblog_ctx));
|
||||
if (unlikely(rfblog_ctx == NULL)) {
|
||||
return result;
|
||||
}
|
||||
rfblog_ctx->file_ctx = ajt->file_ctx;
|
||||
|
||||
OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
|
||||
if (unlikely(output_ctx == NULL)) {
|
||||
SCFree(rfblog_ctx);
|
||||
return result;
|
||||
}
|
||||
output_ctx->data = rfblog_ctx;
|
||||
output_ctx->DeInit = OutputRFBLogDeInitCtxSub;
|
||||
|
||||
AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_RFB);
|
||||
|
||||
result.ctx = output_ctx;
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TmEcode JsonRFBLogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
||||
{
|
||||
if (initdata == NULL) {
|
||||
SCLogDebug("Error getting context for EveLogRFB. \"initdata\" is NULL.");
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
LogRFBLogThread *thread = SCCalloc(1, sizeof(*thread));
|
||||
if (unlikely(thread == NULL)) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->buffer = MemBufferCreateNew(JSON_OUTPUT_BUFFER_SIZE);
|
||||
if (unlikely(thread->buffer == NULL)) {
|
||||
SCFree(thread);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
thread->rfblog_ctx = ((OutputCtx *)initdata)->data;
|
||||
*data = (void *)thread;
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static TmEcode JsonRFBLogThreadDeinit(ThreadVars *t, void *data)
|
||||
{
|
||||
LogRFBLogThread *thread = (LogRFBLogThread *)data;
|
||||
if (thread == NULL) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
if (thread->buffer != NULL) {
|
||||
MemBufferFree(thread->buffer);
|
||||
}
|
||||
SCFree(thread);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
void JsonRFBLogRegister(void)
|
||||
{
|
||||
/* Register as an eve sub-module. */
|
||||
OutputRegisterTxSubModule(LOGGER_JSON_RFB, "eve-log",
|
||||
"JsonRFBLog", "eve-log.rfb",
|
||||
OutputRFBLogInitSub, ALPROTO_RFB, JsonRFBLogger,
|
||||
JsonRFBLogThreadInit, JsonRFBLogThreadDeinit, NULL);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2020 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 Frank Honza <frank.honza@dcso.de>
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_JSON_RFB_H__
|
||||
#define __OUTPUT_JSON_RFB_H__
|
||||
|
||||
void JsonRFBLogRegister(void);
|
||||
|
||||
json_t *JsonRFBAddMetadata(const Flow *f, uint64_t tx_id);
|
||||
|
||||
#endif /* __OUTPUT_JSON_RFB_H__ */
|
Loading…
Reference in New Issue