rdp/eve: convert to jsonbuilder

pull/5207/head
Zach Kelly 5 years ago committed by Victor Julien
parent ef397daba3
commit 22a2bee614

@ -73,6 +73,7 @@ documentation_style = "doxy"
# default: [] # default: []
include = [ include = [
"AppLayerGetTxIterTuple", "AppLayerGetTxIterTuple",
"RdpState",
"SIPState", "SIPState",
"CMark", "CMark",
] ]

@ -18,115 +18,112 @@
// Author: Zach Kelly <zach.kelly@lmco.com> // Author: Zach Kelly <zach.kelly@lmco.com>
use super::rdp::{RdpTransaction, RdpTransactionItem}; use super::rdp::{RdpTransaction, RdpTransactionItem};
use crate::json::{Json, JsonT}; use crate::jsonbuilder::{JsonBuilder, JsonError};
use crate::rdp::parser::*; use crate::rdp::parser::*;
use crate::rdp::windows; use crate::rdp::windows;
use std;
use x509_parser::parse_x509_der; use x509_parser::parse_x509_der;
#[no_mangle] #[no_mangle]
pub extern "C" fn rs_rdp_to_json(tx: *mut std::os::raw::c_void) -> *mut JsonT { pub extern "C" fn rs_rdp_to_json(tx: &mut RdpTransaction, js: &mut JsonBuilder) -> bool {
let tx = cast_pointer!(tx, RdpTransaction); log(tx, js).is_ok()
match to_json(tx) {
Some(js) => js.unwrap(),
None => std::ptr::null_mut(),
}
} }
/// populate a json object with transactional information, for logging /// populate a json object with transactional information, for logging
fn to_json(tx: &RdpTransaction) -> Option<Json> { fn log(tx: &RdpTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> {
let js = Json::object(); js.open_object("rdp")?;
js.set_uint("tx_id", tx.id)?;
js.set_integer("tx_id", tx.id);
match &tx.item { match &tx.item {
RdpTransactionItem::X224ConnectionRequest(ref x224) => x224_req_to_json(&js, x224), RdpTransactionItem::X224ConnectionRequest(ref x224) => x224_req_to_json(x224, js)?,
RdpTransactionItem::X224ConnectionConfirm(x224) => x224_conf_to_json(&js, x224), RdpTransactionItem::X224ConnectionConfirm(ref x224) => x224_conf_to_json(x224, js)?,
RdpTransactionItem::McsConnectRequest(ref mcs) => { RdpTransactionItem::McsConnectRequest(ref mcs) => {
mcs_req_to_json(&js, mcs); mcs_req_to_json(mcs, js)?;
} }
RdpTransactionItem::McsConnectResponse(_) => { RdpTransactionItem::McsConnectResponse(_) => {
// no additional JSON data beyond `event_type` // no additional JSON data beyond `event_type`
js.set_string("event_type", "connect_response"); js.set_string("event_type", "connect_response")?;
} }
RdpTransactionItem::TlsCertificateChain(chain) => { RdpTransactionItem::TlsCertificateChain(chain) => {
js.set_string("event_type", "tls_handshake"); js.set_string("event_type", "tls_handshake")?;
let js_chain = Json::array(); js.open_array("x509_serials")?;
for blob in chain { for blob in chain {
match parse_x509_der(&blob.data) { match parse_x509_der(&blob.data) {
Ok((_, cert)) => { Ok((_, cert)) => {
js_chain.array_append_string(&cert.tbs_certificate.serial.to_str_radix(16)); js.append_string(&cert.tbs_certificate.serial.to_str_radix(16))?;
} }
_ => {} _ => {}
} }
} }
js.set("x509_serials", js_chain); js.close()?;
} }
} }
return Some(js); js.close()?;
Ok(())
} }
/// json helper for X224ConnectionRequest /// json helper for X224ConnectionRequest
fn x224_req_to_json(js: &Json, x224: &X224ConnectionRequest) { fn x224_req_to_json(x224: &X224ConnectionRequest, js: &mut JsonBuilder) -> Result<(), JsonError> {
use crate::rdp::parser::NegotiationRequestFlags as Flags; use crate::rdp::parser::NegotiationRequestFlags as Flags;
js.set_string("event_type", "initial_request"); js.set_string("event_type", "initial_request")?;
if let Some(ref cookie) = x224.cookie { if let Some(ref cookie) = x224.cookie {
js.set_string("cookie", &cookie.mstshash); js.set_string("cookie", &cookie.mstshash)?;
} }
if let Some(ref req) = x224.negotiation_request { if let Some(ref req) = x224.negotiation_request {
if !req.flags.is_empty() { if !req.flags.is_empty() {
let flags = Json::array(); js.open_array("flags")?;
if req.flags.contains(Flags::RESTRICTED_ADMIN_MODE_REQUIRED) { if req.flags.contains(Flags::RESTRICTED_ADMIN_MODE_REQUIRED) {
flags.array_append_string("restricted_admin_mode_required"); js.append_string("restricted_admin_mode_required")?;
} }
if req if req
.flags .flags
.contains(Flags::REDIRECTED_AUTHENTICATION_MODE_REQUIRED) .contains(Flags::REDIRECTED_AUTHENTICATION_MODE_REQUIRED)
{ {
flags.array_append_string("redirected_authentication_mode_required"); js.append_string("redirected_authentication_mode_required")?;
} }
if req.flags.contains(Flags::CORRELATION_INFO_PRESENT) { if req.flags.contains(Flags::CORRELATION_INFO_PRESENT) {
flags.array_append_string("correlation_info_present"); js.append_string("correlation_info_present")?;
} }
js.set("flags", flags); js.close()?;
} }
} }
Ok(())
} }
/// json helper for X224ConnectionConfirm /// json helper for X224ConnectionConfirm
fn x224_conf_to_json(js: &Json, x224: &X224ConnectionConfirm) { fn x224_conf_to_json(x224: &X224ConnectionConfirm, js: &mut JsonBuilder) -> Result<(), JsonError> {
use crate::rdp::parser::NegotiationResponseFlags as Flags; use crate::rdp::parser::NegotiationResponseFlags as Flags;
js.set_string("event_type", "initial_response"); js.set_string("event_type", "initial_response")?;
if let Some(ref from_server) = x224.negotiation_from_server { if let Some(ref from_server) = x224.negotiation_from_server {
match &from_server { match &from_server {
NegotiationFromServer::Response(ref resp) => { NegotiationFromServer::Response(ref resp) => {
if !resp.flags.is_empty() { if !resp.flags.is_empty() {
let flags = Json::array(); js.open_array("server_supports")?;
if resp.flags.contains(Flags::EXTENDED_CLIENT_DATA_SUPPORTED) { if resp.flags.contains(Flags::EXTENDED_CLIENT_DATA_SUPPORTED) {
flags.array_append_string("extended_client_data"); js.append_string("extended_client_data")?;
} }
if resp.flags.contains(Flags::DYNVC_GFX_PROTOCOL_SUPPORTED) { if resp.flags.contains(Flags::DYNVC_GFX_PROTOCOL_SUPPORTED) {
flags.array_append_string("dynvc_gfx"); js.append_string("dynvc_gfx")?;
} }
// NEGRSP_FLAG_RESERVED not logged // NEGRSP_FLAG_RESERVED not logged
if resp.flags.contains(Flags::RESTRICTED_ADMIN_MODE_SUPPORTED) { if resp.flags.contains(Flags::RESTRICTED_ADMIN_MODE_SUPPORTED) {
flags.array_append_string("restricted_admin"); js.append_string("restricted_admin")?;
} }
if resp if resp
.flags .flags
.contains(Flags::REDIRECTED_AUTHENTICATION_MODE_SUPPORTED) .contains(Flags::REDIRECTED_AUTHENTICATION_MODE_SUPPORTED)
{ {
flags.array_append_string("redirected_authentication"); js.append_string("redirected_authentication")?;
} }
js.set("server_supports", flags); js.close()?;
} }
let protocol = match resp.protocol { let protocol = match resp.protocol {
@ -136,111 +133,117 @@ fn x224_conf_to_json(js: &Json, x224: &X224ConnectionConfirm) {
Protocol::ProtocolRdsTls => "rds_tls", Protocol::ProtocolRdsTls => "rds_tls",
Protocol::ProtocolHybridEx => "hybrid_ex", Protocol::ProtocolHybridEx => "hybrid_ex",
}; };
js.set_string("protocol", protocol); js.set_string("protocol", protocol)?;
} }
NegotiationFromServer::Failure(ref fail) => match fail.code { NegotiationFromServer::Failure(ref fail) => match fail.code {
NegotiationFailureCode::SslRequiredByServer => { NegotiationFailureCode::SslRequiredByServer => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::SslRequiredByServer as u64, NegotiationFailureCode::SslRequiredByServer as u64,
); )?;
js.set_string("reason", "ssl required by server") js.set_string("reason", "ssl required by server")?;
} }
NegotiationFailureCode::SslNotAllowedByServer => { NegotiationFailureCode::SslNotAllowedByServer => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::SslNotAllowedByServer as u64, NegotiationFailureCode::SslNotAllowedByServer as u64,
); )?;
js.set_string("reason", "ssl not allowed by server") js.set_string("reason", "ssl not allowed by server")?;
} }
NegotiationFailureCode::SslCertNotOnServer => { NegotiationFailureCode::SslCertNotOnServer => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::SslCertNotOnServer as u64, NegotiationFailureCode::SslCertNotOnServer as u64,
); )?;
js.set_string("reason", "ssl cert not on server") js.set_string("reason", "ssl cert not on server")?;
} }
NegotiationFailureCode::InconsistentFlags => { NegotiationFailureCode::InconsistentFlags => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::InconsistentFlags as u64, NegotiationFailureCode::InconsistentFlags as u64,
); )?;
js.set_string("reason", "inconsistent flags") js.set_string("reason", "inconsistent flags")?;
} }
NegotiationFailureCode::HybridRequiredByServer => { NegotiationFailureCode::HybridRequiredByServer => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::HybridRequiredByServer as u64, NegotiationFailureCode::HybridRequiredByServer as u64,
); )?;
js.set_string("reason", "hybrid required by server") js.set_string("reason", "hybrid required by server")?;
} }
NegotiationFailureCode::SslWithUserAuthRequiredByServer => { NegotiationFailureCode::SslWithUserAuthRequiredByServer => {
js.set_integer( js.set_uint(
"error_code", "error_code",
NegotiationFailureCode::SslWithUserAuthRequiredByServer as u64, NegotiationFailureCode::SslWithUserAuthRequiredByServer as u64,
); )?;
js.set_string("reason", "ssl with user auth required by server") js.set_string("reason", "ssl with user auth required by server")?;
} }
}, },
} }
} }
Ok(())
} }
/// json helper for McsConnectRequest /// json helper for McsConnectRequest
fn mcs_req_to_json(js: &Json, mcs: &McsConnectRequest) { fn mcs_req_to_json(mcs: &McsConnectRequest, js: &mut JsonBuilder) -> Result<(), JsonError> {
// placeholder string value. We do not simply omit "unknown" values so that they can // placeholder string value. We do not simply omit "unknown" values so that they can
// help indicate that a given enum may be out of date (new Windows version, etc.) // help indicate that a given enum may be out of date (new Windows version, etc.)
let unknown = String::from("unknown"); let unknown = String::from("unknown");
js.set_string("event_type", "connect_request"); js.set_string("event_type", "connect_request")?;
for child in &mcs.children { for child in &mcs.children {
match child { match child {
McsConnectRequestChild::CsClientCore(ref client) => { McsConnectRequestChild::CsClientCore(ref client) => {
let js_client = Json::object(); js.open_object("client")?;
match client.version { match client.version {
Some(ref ver) => js_client.set_string("version", &version_to_string(ver, "v")), Some(ref ver) => {
None => js_client.set_string("version", &unknown), js.set_string("version", &version_to_string(ver, "v"))?;
}
None => {
js.set_string("version", &unknown)?;
}
} }
js_client.set_integer("desktop_width", client.desktop_width as u64); js.set_uint("desktop_width", client.desktop_width as u64)?;
js_client.set_integer("desktop_height", client.desktop_height as u64); js.set_uint("desktop_height", client.desktop_height as u64)?;
if let Some(depth) = get_color_depth(client) { if let Some(depth) = get_color_depth(client) {
js_client.set_integer("color_depth", depth); js.set_uint("color_depth", depth)?;
} }
// sas_sequence not logged // sas_sequence not logged
js_client.set_string( js.set_string(
"keyboard_layout", "keyboard_layout",
&windows::lcid_to_string(client.keyboard_layout, &unknown), &windows::lcid_to_string(client.keyboard_layout, &unknown),
); )?;
js_client.set_string( js.set_string(
"build", "build",
&windows::os_to_string(&client.client_build, &unknown), &windows::os_to_string(&client.client_build, &unknown),
); )?;
if client.client_name.len() > 0 { if client.client_name.len() > 0 {
js_client.set_string("client_name", &client.client_name); js.set_string("client_name", &client.client_name)?;
} }
if let Some(ref kb) = client.keyboard_type { if let Some(ref kb) = client.keyboard_type {
js_client.set_string("keyboard_type", &keyboard_to_string(kb)); js.set_string("keyboard_type", &keyboard_to_string(kb))?;
} }
if client.keyboard_subtype != 0 { if client.keyboard_subtype != 0 {
js_client.set_integer("keyboard_subtype", client.keyboard_subtype as u64); js.set_uint("keyboard_subtype", client.keyboard_subtype as u64)?;
} }
if client.keyboard_function_key != 0 { if client.keyboard_function_key != 0 {
js_client.set_integer("function_keys", client.keyboard_function_key as u64); js.set_uint("function_keys", client.keyboard_function_key as u64)?;
} }
if client.ime_file_name.len() > 0 { if client.ime_file_name.len() > 0 {
js_client.set_string("ime", &client.ime_file_name); js.set_string("ime", &client.ime_file_name)?;
} }
// //
@ -248,12 +251,12 @@ fn mcs_req_to_json(js: &Json, mcs: &McsConnectRequest) {
// //
if let Some(id) = client.client_product_id { if let Some(id) = client.client_product_id {
js_client.set_integer("product_id", id as u64); js.set_uint("product_id", id as u64)?;
} }
if let Some(serial) = client.serial_number { if let Some(serial) = client.serial_number {
if serial != 0 { if serial != 0 {
js_client.set_integer("serial_number", serial as u64); js.set_uint("serial_number", serial as u64)?;
} }
} }
@ -263,57 +266,57 @@ fn mcs_req_to_json(js: &Json, mcs: &McsConnectRequest) {
use crate::rdp::parser::EarlyCapabilityFlags as Flags; use crate::rdp::parser::EarlyCapabilityFlags as Flags;
if !early_capability_flags.is_empty() { if !early_capability_flags.is_empty() {
let flags = Json::array(); js.open_array("capabilities")?;
if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_ERRINFO_PDF) { if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_ERRINFO_PDF) {
flags.array_append_string("support_errinfo_pdf"); js.append_string("support_errinfo_pdf")?;
} }
if early_capability_flags.contains(Flags::RNS_UD_CS_WANT_32BPP_SESSION) { if early_capability_flags.contains(Flags::RNS_UD_CS_WANT_32BPP_SESSION) {
flags.array_append_string("want_32bpp_session"); js.append_string("want_32bpp_session")?;
} }
if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_STATUSINFO_PDU) if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_STATUSINFO_PDU)
{ {
flags.array_append_string("support_statusinfo_pdu"); js.append_string("support_statusinfo_pdu")?;
} }
if early_capability_flags.contains(Flags::RNS_UD_CS_STRONG_ASYMMETRIC_KEYS) if early_capability_flags.contains(Flags::RNS_UD_CS_STRONG_ASYMMETRIC_KEYS)
{ {
flags.array_append_string("strong_asymmetric_keys"); js.append_string("strong_asymmetric_keys")?;
} }
// RNS_UD_CS_UNUSED not logged // RNS_UD_CS_UNUSED not logged
if early_capability_flags.contains(Flags::RNS_UD_CS_VALID_CONNECTION_TYPE) { if early_capability_flags.contains(Flags::RNS_UD_CS_VALID_CONNECTION_TYPE) {
flags.array_append_string("valid_connection_type"); js.append_string("valid_connection_type")?;
} }
if early_capability_flags if early_capability_flags
.contains(Flags::RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU) .contains(Flags::RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU)
{ {
flags.array_append_string("support_monitor_layout_pdu"); js.append_string("support_monitor_layout_pdu")?;
} }
if early_capability_flags if early_capability_flags
.contains(Flags::RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT) .contains(Flags::RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT)
{ {
flags.array_append_string("support_netchar_autodetect"); js.append_string("support_netchar_autodetect")?;
} }
if early_capability_flags if early_capability_flags
.contains(Flags::RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL) .contains(Flags::RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL)
{ {
flags.array_append_string("support_dynvc_gfx_protocol"); js.append_string("support_dynvc_gfx_protocol")?;
} }
if early_capability_flags if early_capability_flags
.contains(Flags::RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE) .contains(Flags::RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE)
{ {
flags.array_append_string("support_dynamic_time_zone"); js.append_string("support_dynamic_time_zone")?;
} }
if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_HEARTBEAT_PDU) { if early_capability_flags.contains(Flags::RNS_UD_CS_SUPPORT_HEARTBEAT_PDU) {
flags.array_append_string("support_heartbeat_pdu"); js.append_string("support_heartbeat_pdu")?;
} }
js_client.set("capabilities", flags); js.close()?;
} }
} }
if let Some(ref id) = client.client_dig_product_id { if let Some(ref id) = client.client_dig_product_id {
if id.len() > 0 { if id.len() > 0 {
js_client.set_string("id", id); js.set_string("id", id)?;
} }
} }
@ -329,47 +332,49 @@ fn mcs_req_to_json(js: &Json, mcs: &McsConnectRequest) {
ConnectionHint::ConnectionHintNotProvided => "", ConnectionHint::ConnectionHintNotProvided => "",
}; };
if *hint != ConnectionHint::ConnectionHintNotProvided { if *hint != ConnectionHint::ConnectionHintNotProvided {
js_client.set_string("connection_hint", s); js.set_string("connection_hint", s)?;
} }
} }
// server_selected_procotol not logged // server_selected_procotol not logged
if let Some(width) = client.desktop_physical_width { if let Some(width) = client.desktop_physical_width {
js_client.set_integer("physical_width", width as u64); js.set_uint("physical_width", width as u64)?;
} }
if let Some(height) = client.desktop_physical_height { if let Some(height) = client.desktop_physical_height {
js_client.set_integer("physical_height", height as u64); js.set_uint("physical_height", height as u64)?;
} }
if let Some(orientation) = client.desktop_orientation { if let Some(orientation) = client.desktop_orientation {
js_client.set_integer("desktop_orientation", orientation as u64); js.set_uint("desktop_orientation", orientation as u64)?;
} }
if let Some(scale) = client.desktop_scale_factor { if let Some(scale) = client.desktop_scale_factor {
js_client.set_integer("scale_factor", scale as u64); js.set_uint("scale_factor", scale as u64)?;
} }
if let Some(scale) = client.device_scale_factor { if let Some(scale) = client.device_scale_factor {
js_client.set_integer("device_scale_factor", scale as u64); js.set_uint("device_scale_factor", scale as u64)?;
} }
js.set("client", js_client); js.close()?;
} }
McsConnectRequestChild::CsNet(ref net) => { McsConnectRequestChild::CsNet(ref net) => {
if net.channels.len() > 0 { if net.channels.len() > 0 {
let channels = Json::array(); js.open_array("channels")?;
for channel in &net.channels { for channel in &net.channels {
channels.array_append_string(&channel); js.append_string(&channel)?;
} }
js.set("channels", channels); js.close()?;
} }
} }
McsConnectRequestChild::CsUnknown(_) => {} McsConnectRequestChild::CsUnknown(_) => {}
} }
} }
Ok(())
} }
/// converts RdpClientVersion to a string, using the provided prefix /// converts RdpClientVersion to a string, using the provided prefix
@ -437,7 +442,7 @@ fn keyboard_to_string(kb: &KeyboardType) -> String {
mod tests { mod tests {
use super::*; use super::*;
// for now, unsure how to effectively test Json/JsonT // for now, testing of JsonBuilder output is done by suricata-verify
#[test] #[test]
fn test_version_string() { fn test_version_string() {

@ -60,27 +60,20 @@ static int JsonRdpLogger(ThreadVars *tv, void *thread_data,
{ {
LogRdpLogThread *thread = thread_data; LogRdpLogThread *thread = thread_data;
json_t *js = CreateJSONHeader(p, LOG_DIR_PACKET, "rdp", NULL); JsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, "rdp", NULL);
if (unlikely(js == NULL)) { if (unlikely(js == NULL)) {
return TM_ECODE_FAILED; return TM_ECODE_OK;
} }
EveAddCommonOptions(&thread->rdplog_ctx->cfg, p, f, js);
json_t *rdp_js = rs_rdp_to_json(tx); if (!rs_rdp_to_json(tx, js)) {
if (unlikely(rdp_js == NULL)) { jb_free(js);
goto error; return TM_ECODE_FAILED;
} }
json_object_set_new(js, "rdp", rdp_js);
JsonAddCommonOptions(&thread->rdplog_ctx->cfg, p, f, js);
MemBufferReset(thread->buffer); MemBufferReset(thread->buffer);
OutputJSONBuffer(js, thread->rdplog_ctx->file_ctx, &thread->buffer); OutputJsonBuilderBuffer(js, thread->rdplog_ctx->file_ctx, &thread->buffer);
json_decref(js);
jb_free(js);
return TM_ECODE_OK; return TM_ECODE_OK;
error:
json_decref(js);
return TM_ECODE_FAILED;
} }
static void OutputRdpLogDeInitCtxSub(OutputCtx *output_ctx) static void OutputRdpLogDeInitCtxSub(OutputCtx *output_ctx)

Loading…
Cancel
Save