]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rdp/eve: convert to jsonbuilder
authorZach Kelly <zach.kelly@lmco.com>
Sat, 18 Jul 2020 00:58:48 +0000 (20:58 -0400)
committerVictor Julien <victor@inliniac.net>
Tue, 21 Jul 2020 14:50:26 +0000 (16:50 +0200)
rust/cbindgen.toml
rust/src/rdp/log.rs
src/output-json-rdp.c

index 8c41789e5817a47c32994b99ef0bc715da90236c..cd5cf5c9e305e7f71e379af5a3d81f0d9bbc447c 100644 (file)
@@ -73,6 +73,7 @@ documentation_style = "doxy"
 # default: []
 include = [
     "AppLayerGetTxIterTuple",
+    "RdpState",
     "SIPState",
     "CMark",
 ]
index b5383debd63ef4a35ccdf0f35cf03910599e73ad..a2e806c0e287f7ec6c0fe393286e7379e0e988fc 100644 (file)
 // Author: Zach Kelly <zach.kelly@lmco.com>
 
 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<Json> {
-    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() {
index d9d5c33e1baa029b8766342ef58e58723b2c9add..4c4971b22fbc41b41a1b37f847727d0bf35176ad 100644 (file)
@@ -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)