From 22a2bee6149ee6896594b330bad28fc27fe1b0af Mon Sep 17 00:00:00 2001 From: Zach Kelly Date: Fri, 17 Jul 2020 20:58:48 -0400 Subject: [PATCH] rdp/eve: convert to jsonbuilder --- rust/cbindgen.toml | 1 + rust/src/rdp/log.rs | 203 ++++++++++++++++++++++-------------------- src/output-json-rdp.c | 23 ++--- 3 files changed, 113 insertions(+), 114 deletions(-) diff --git a/rust/cbindgen.toml b/rust/cbindgen.toml index 8c41789e58..cd5cf5c9e3 100644 --- a/rust/cbindgen.toml +++ b/rust/cbindgen.toml @@ -73,6 +73,7 @@ documentation_style = "doxy" # default: [] include = [ "AppLayerGetTxIterTuple", + "RdpState", "SIPState", "CMark", ] diff --git a/rust/src/rdp/log.rs b/rust/src/rdp/log.rs index b5383debd6..a2e806c0e2 100644 --- a/rust/src/rdp/log.rs +++ b/rust/src/rdp/log.rs @@ -18,115 +18,112 @@ // Author: Zach Kelly use super::rdp::{RdpTransaction, RdpTransactionItem}; -use crate::json::{Json, JsonT}; +use crate::jsonbuilder::{JsonBuilder, JsonError}; use crate::rdp::parser::*; use crate::rdp::windows; -use std; use x509_parser::parse_x509_der; #[no_mangle] -pub extern "C" fn rs_rdp_to_json(tx: *mut std::os::raw::c_void) -> *mut JsonT { - let tx = cast_pointer!(tx, RdpTransaction); - match to_json(tx) { - Some(js) => js.unwrap(), - None => std::ptr::null_mut(), - } +pub extern "C" fn rs_rdp_to_json(tx: &mut RdpTransaction, js: &mut JsonBuilder) -> bool { + log(tx, js).is_ok() } /// populate a json object with transactional information, for logging -fn to_json(tx: &RdpTransaction) -> Option { - let js = Json::object(); - - js.set_integer("tx_id", tx.id); +fn log(tx: &RdpTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { + js.open_object("rdp")?; + js.set_uint("tx_id", tx.id)?; match &tx.item { - RdpTransactionItem::X224ConnectionRequest(ref x224) => x224_req_to_json(&js, x224), - RdpTransactionItem::X224ConnectionConfirm(x224) => x224_conf_to_json(&js, x224), + RdpTransactionItem::X224ConnectionRequest(ref x224) => x224_req_to_json(x224, js)?, + RdpTransactionItem::X224ConnectionConfirm(ref x224) => x224_conf_to_json(x224, js)?, RdpTransactionItem::McsConnectRequest(ref mcs) => { - mcs_req_to_json(&js, mcs); + mcs_req_to_json(mcs, js)?; } RdpTransactionItem::McsConnectResponse(_) => { // no additional JSON data beyond `event_type` - js.set_string("event_type", "connect_response"); + js.set_string("event_type", "connect_response")?; } RdpTransactionItem::TlsCertificateChain(chain) => { - js.set_string("event_type", "tls_handshake"); - let js_chain = Json::array(); + js.set_string("event_type", "tls_handshake")?; + js.open_array("x509_serials")?; for blob in chain { match parse_x509_der(&blob.data) { 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 -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; - js.set_string("event_type", "initial_request"); + js.set_string("event_type", "initial_request")?; 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 !req.flags.is_empty() { - let flags = Json::array(); + js.open_array("flags")?; 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 .flags .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) { - flags.array_append_string("correlation_info_present"); + js.append_string("correlation_info_present")?; } - js.set("flags", flags); + js.close()?; } } + + Ok(()) } /// 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; - js.set_string("event_type", "initial_response"); + js.set_string("event_type", "initial_response")?; if let Some(ref from_server) = x224.negotiation_from_server { match &from_server { NegotiationFromServer::Response(ref resp) => { if !resp.flags.is_empty() { - let flags = Json::array(); + js.open_array("server_supports")?; 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) { - flags.array_append_string("dynvc_gfx"); + js.append_string("dynvc_gfx")?; } // NEGRSP_FLAG_RESERVED not logged if resp.flags.contains(Flags::RESTRICTED_ADMIN_MODE_SUPPORTED) { - flags.array_append_string("restricted_admin"); + js.append_string("restricted_admin")?; } if resp .flags .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 { @@ -136,111 +133,117 @@ fn x224_conf_to_json(js: &Json, x224: &X224ConnectionConfirm) { Protocol::ProtocolRdsTls => "rds_tls", Protocol::ProtocolHybridEx => "hybrid_ex", }; - js.set_string("protocol", protocol); + js.set_string("protocol", protocol)?; } NegotiationFromServer::Failure(ref fail) => match fail.code { NegotiationFailureCode::SslRequiredByServer => { - js.set_integer( + js.set_uint( "error_code", NegotiationFailureCode::SslRequiredByServer as u64, - ); - js.set_string("reason", "ssl required by server") + )?; + js.set_string("reason", "ssl required by server")?; } NegotiationFailureCode::SslNotAllowedByServer => { - js.set_integer( + js.set_uint( "error_code", NegotiationFailureCode::SslNotAllowedByServer as u64, - ); - js.set_string("reason", "ssl not allowed by server") + )?; + js.set_string("reason", "ssl not allowed by server")?; } NegotiationFailureCode::SslCertNotOnServer => { - js.set_integer( + js.set_uint( "error_code", NegotiationFailureCode::SslCertNotOnServer as u64, - ); - js.set_string("reason", "ssl cert not on server") + )?; + js.set_string("reason", "ssl cert not on server")?; } NegotiationFailureCode::InconsistentFlags => { - js.set_integer( + js.set_uint( "error_code", NegotiationFailureCode::InconsistentFlags as u64, - ); - js.set_string("reason", "inconsistent flags") + )?; + js.set_string("reason", "inconsistent flags")?; } NegotiationFailureCode::HybridRequiredByServer => { - js.set_integer( + js.set_uint( "error_code", NegotiationFailureCode::HybridRequiredByServer as u64, - ); - js.set_string("reason", "hybrid required by server") + )?; + js.set_string("reason", "hybrid required by server")?; } NegotiationFailureCode::SslWithUserAuthRequiredByServer => { - js.set_integer( + js.set_uint( "error_code", 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 -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 // help indicate that a given enum may be out of date (new Windows version, etc.) let unknown = String::from("unknown"); - js.set_string("event_type", "connect_request"); + js.set_string("event_type", "connect_request")?; for child in &mcs.children { match child { McsConnectRequestChild::CsClientCore(ref client) => { - let js_client = Json::object(); + js.open_object("client")?; match client.version { - Some(ref ver) => js_client.set_string("version", &version_to_string(ver, "v")), - None => js_client.set_string("version", &unknown), + Some(ref ver) => { + 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_client.set_integer("desktop_height", client.desktop_height as u64); + js.set_uint("desktop_width", client.desktop_width as u64)?; + js.set_uint("desktop_height", client.desktop_height as u64)?; 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 - js_client.set_string( + js.set_string( "keyboard_layout", &windows::lcid_to_string(client.keyboard_layout, &unknown), - ); + )?; - js_client.set_string( + js.set_string( "build", &windows::os_to_string(&client.client_build, &unknown), - ); + )?; 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 { - js_client.set_string("keyboard_type", &keyboard_to_string(kb)); + js.set_string("keyboard_type", &keyboard_to_string(kb))?; } 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 { - 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 { - 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 { - 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 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; 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) { - 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) { - 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) { - 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) { - flags.array_append_string("strong_asymmetric_keys"); + js.append_string("strong_asymmetric_keys")?; } // RNS_UD_CS_UNUSED not logged 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 .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 .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 .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 .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) { - 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 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 => "", }; if *hint != ConnectionHint::ConnectionHintNotProvided { - js_client.set_string("connection_hint", s); + js.set_string("connection_hint", s)?; } } // server_selected_procotol not logged 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 { - js_client.set_integer("physical_height", height as u64); + js.set_uint("physical_height", height as u64)?; } 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 { - 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 { - 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) => { if net.channels.len() > 0 { - let channels = Json::array(); + js.open_array("channels")?; for channel in &net.channels { - channels.array_append_string(&channel); + js.append_string(&channel)?; } - js.set("channels", channels); + js.close()?; } } McsConnectRequestChild::CsUnknown(_) => {} } } + + Ok(()) } /// converts RdpClientVersion to a string, using the provided prefix @@ -437,7 +442,7 @@ fn keyboard_to_string(kb: &KeyboardType) -> String { mod tests { use super::*; - // for now, unsure how to effectively test Json/JsonT + // for now, testing of JsonBuilder output is done by suricata-verify #[test] fn test_version_string() { diff --git a/src/output-json-rdp.c b/src/output-json-rdp.c index d9d5c33e1b..4c4971b22f 100644 --- a/src/output-json-rdp.c +++ b/src/output-json-rdp.c @@ -60,27 +60,20 @@ static int JsonRdpLogger(ThreadVars *tv, void *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)) { - return TM_ECODE_FAILED; + return TM_ECODE_OK; } - - json_t *rdp_js = rs_rdp_to_json(tx); - if (unlikely(rdp_js == NULL)) { - goto error; + EveAddCommonOptions(&thread->rdplog_ctx->cfg, p, f, js); + if (!rs_rdp_to_json(tx, js)) { + jb_free(js); + return TM_ECODE_FAILED; } - json_object_set_new(js, "rdp", rdp_js); - - JsonAddCommonOptions(&thread->rdplog_ctx->cfg, p, f, js); MemBufferReset(thread->buffer); - OutputJSONBuffer(js, thread->rdplog_ctx->file_ctx, &thread->buffer); - json_decref(js); + OutputJsonBuilderBuffer(js, thread->rdplog_ctx->file_ctx, &thread->buffer); + jb_free(js); return TM_ECODE_OK; - -error: - json_decref(js); - return TM_ECODE_FAILED; } static void OutputRdpLogDeInitCtxSub(OutputCtx *output_ctx)