Skip to content

Commit

Permalink
app-layer: websockets protocol support
Browse files Browse the repository at this point in the history
Ticket: 2695
  • Loading branch information
catenacyber committed Dec 6, 2023
1 parent 64d12aa commit 17f6ef0
Show file tree
Hide file tree
Showing 16 changed files with 669 additions and 15 deletions.
22 changes: 22 additions & 0 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3823,6 +3823,9 @@
},
"tls": {
"$ref": "#/$defs/stats_applayer_error"
},
"websockets": {
"$ref": "#/$defs/stats_applayer_error"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -3940,6 +3943,9 @@
},
"tls": {
"type": "integer"
},
"websockets": {
"type": "integer"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -4051,6 +4057,9 @@
},
"tls": {
"type": "integer"
},
"websockets": {
"type": "integer"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -5488,7 +5497,20 @@
}
},
"additionalProperties": false
},
"websockets": {
"type": "object",
"properties": {
"mask": {
"type": "boolean"
},
"opcode": {
"type": "string"
}
},
"additionalProperties": false
}

},
"$defs": {
"stats_applayer_error": {
Expand Down
1 change: 1 addition & 0 deletions rust/src/applayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ pub unsafe fn AppLayerRegisterParser(parser: *const RustParser, alproto: AppProt

// Defined in app-layer-detect-proto.h
extern {
pub fn AppLayerRegisterExpectationProto(ipproto: u8, alproto: AppProto);
pub fn AppLayerProtoDetectPPRegister(ipproto: u8, portstr: *const c_char, alproto: AppProto,
min_depth: u16, max_depth: u16, dir: u8,
pparser1: ProbeFn, pparser2: ProbeFn);
Expand Down
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub mod rfb;
pub mod mqtt;
pub mod pgsql;
pub mod telnet;
pub mod websockets;
pub mod applayertemplate;
pub mod rdp;
pub mod x509;
Expand Down
54 changes: 54 additions & 0 deletions rust/src/websockets/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* Copyright (C) 2023 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.
*/

use super::websockets::WebSocketsTransaction;
use crate::jsonbuilder::{JsonBuilder, JsonError};
use std;

//TODOws detection on opcode and mask, and payload buffer

fn ws_opcode_string(p: u8) -> Option<&'static str> {
match p {
0 => Some("continuation"),
1 => Some("text"),
2 => Some("binary"),
8 => Some("connection_close"),
9 => Some("ping"),
0xa => Some("pong"),
_ => None,
}
}

fn log_websockets(tx: &WebSocketsTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> {
js.open_object("websockets")?;
js.set_bool("mask", tx.pdu.mask)?;
if let Some(val) = ws_opcode_string(tx.pdu.opcode) {
js.set_string("opcode", val)?;
} else {
js.set_string("opcode", &format!("unknown-{}", tx.pdu.opcode))?;
}
js.close()?;
Ok(())
}

#[no_mangle]
pub unsafe extern "C" fn rs_websockets_logger_log(
tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
) -> bool {
let tx = cast_pointer!(tx, WebSocketsTransaction);
log_websockets(tx, js).is_ok()
}
22 changes: 22 additions & 0 deletions rust/src/websockets/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* Copyright (C) 2023 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.
*/

//! Application layer websockets parser and logger module.

pub mod logger;
mod parser;
pub mod websockets;
63 changes: 63 additions & 0 deletions rust/src/websockets/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* Copyright (C) 2023 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.
*/

use nom7::bytes::streaming::take;
use nom7::combinator::cond;
use nom7::number::streaming::{be_u16, be_u64, be_u8};
use nom7::IResult;

#[derive(Clone, Debug, Default)]
pub struct WebSocketsPdu {
pub fin: bool,
pub opcode: u8,
pub mask: bool,
pub payload: Vec<u8>,
}

// cf rfc6455#section-5.2
pub fn parse_message(i: &[u8]) -> IResult<&[u8], WebSocketsPdu> {
let (i, fin_op) = be_u8(i)?;
let fin = (fin_op & 0x80) != 0;
let opcode = fin_op & 0xF;
let (i, mask_plen) = be_u8(i)?;
let mask = (mask_plen & 0x80) != 0;
let (i, payload_len) = match mask_plen & 0x7F {
126 => {
let (i, val) = be_u16(i)?;
Ok((i, val.into()))
}
127 => be_u64(i),
_ => Ok((i, (mask_plen & 0x7F).into())),
}?;
let (i, xormask) = cond(mask, take(4usize))(i)?;
let (i, payload_raw) = take(payload_len)(i)?;
let mut payload = payload_raw.to_vec();
if let Some(xorkey) = xormask {
for i in 0..payload.len() {
payload[i] ^= xorkey[i % 4];
}
}
Ok((
i,
WebSocketsPdu {
fin,
opcode,
mask,
payload,
},
))
}
Loading

0 comments on commit 17f6ef0

Please sign in to comment.