diff options
Diffstat (limited to 'rust/vendor/sawp')
-rw-r--r-- | rust/vendor/sawp/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | rust/vendor/sawp/Cargo.toml | 85 | ||||
-rw-r--r-- | rust/vendor/sawp/LICENSE | 13 | ||||
-rw-r--r-- | rust/vendor/sawp/README.md | 65 | ||||
-rw-r--r-- | rust/vendor/sawp/src/error.rs | 153 | ||||
-rw-r--r-- | rust/vendor/sawp/src/ffi.rs | 86 | ||||
-rw-r--r-- | rust/vendor/sawp/src/lib.rs | 44 | ||||
-rw-r--r-- | rust/vendor/sawp/src/parser.rs | 39 | ||||
-rw-r--r-- | rust/vendor/sawp/src/probe.rs | 31 | ||||
-rw-r--r-- | rust/vendor/sawp/src/protocol.rs | 8 |
10 files changed, 525 insertions, 0 deletions
diff --git a/rust/vendor/sawp/.cargo-checksum.json b/rust/vendor/sawp/.cargo-checksum.json new file mode 100644 index 0000000..e5d8f1a --- /dev/null +++ b/rust/vendor/sawp/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"2cdc232981f722846f3d3bae61c4cae0b29c4471c50d29571ed8b92d98bea8da","LICENSE":"7495df8c571413e70cd6363c0c245221e7bf7b7beb640b2224d22bb6f3fa7457","README.md":"65104c252c3509cd6819217da3f3811ab8696dce198e23ec4ec277ff2ce482b3","src/error.rs":"11838323e2d6f73c3b6f5c24f9ce74df4805d6fd8b99757e37b9065af85c6c6f","src/ffi.rs":"95600a8f4d7411202bed3b545fa2e95f7809f5053794ececac967fa6235ce653","src/lib.rs":"cf5ba7e0dcb669196b188fba8dc75cd31e0a8aea367e822756a695896dc7dfea","src/parser.rs":"7405546ba6c587b83b5f7549df63053ac6eeeab8eacfe76795c903c2631b5bec","src/probe.rs":"c8f18daa98417d6ee29dc0272fa1fcd5d48e3ffa87771b22a537423d8ff049bd","src/protocol.rs":"fccd161dc331076b48d09cc54d0031f4372187572a0403770b08f1200c3177bc"},"package":"7e74f84d736420afcba72f689a494d275c97cf4775c3fe248f937e9d3bf83e30"}
\ No newline at end of file diff --git a/rust/vendor/sawp/Cargo.toml b/rust/vendor/sawp/Cargo.toml new file mode 100644 index 0000000..fa2b088 --- /dev/null +++ b/rust/vendor/sawp/Cargo.toml @@ -0,0 +1,85 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.58.1" +name = "sawp" +version = "0.12.1" +authors = ["Canadian Centre for Cyber Security <sa-sawp@cyber.gc.ca>"] +include = [ + "Cargo.toml", + "LICENSE", + "README.md", + "src/**/*.rs", +] +description = "Security Aware Wire Protocol parsing library" +homepage = "https://github.com/CybercentreCanada/sawp" +readme = "README.md" +keywords = [ + "parser", + "streaming", + "protocols", + "network", + "api", +] +categories = [ + "parsing", + "network-programming", +] +license-file = "LICENSE" +repository = "https://github.com/CybercentreCanada/sawp" + +[lib] +crate-type = [ + "staticlib", + "rlib", + "cdylib", +] +bench = false + +[[bench]] +name = "modbus" +path = "benches/modbus.rs" +harness = false + +[dependencies.nom] +version = "7.1.1" + +[dependencies.sawp-ffi] +version = "^0.12.1" +optional = true + +[dev-dependencies.clap] +version = "~2.33" + +[dev-dependencies.criterion] +version = "=0.3.4" + +[dev-dependencies.csv] +version = "=1.1.6" + +[dev-dependencies.rayon] +version = "=1.6.1" + +[dev-dependencies.rayon-core] +version = "=1.10.2" + +[build-dependencies.cbindgen] +version = "0.15" +optional = true + +[features] +ffi = [ + "cbindgen", + "sawp-ffi", +] +verbose = [] diff --git a/rust/vendor/sawp/LICENSE b/rust/vendor/sawp/LICENSE new file mode 100644 index 0000000..24a5028 --- /dev/null +++ b/rust/vendor/sawp/LICENSE @@ -0,0 +1,13 @@ +Copyright 2020 Crown Copyright, Government of Canada (Canadian Centre for Cyber Security / Communications Security Establishment) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rust/vendor/sawp/README.md b/rust/vendor/sawp/README.md new file mode 100644 index 0000000..c8f735b --- /dev/null +++ b/rust/vendor/sawp/README.md @@ -0,0 +1,65 @@ +# [Français](README.fr.md) + +# Security Aware Wire Protocol parsing library. + +This library contains parsers for various wire protocols, +and is intended to be used in network security sensors. + +Each parser exposes a common interface that allows the sensor +engine to feed bytes into the parser and receive parsed +metadata back. The bytes are expected to be at the session layer, +so the engine is responsible for assembling transport layer +data into a session payload, which is then fed into this library. + +This library aims to be resilient and parse as many messages as +possible that are seen in the wild. If a message is invalid or +out-of-spec, it should not be discarded by the parser. Parsers +will set flags on the message when it fails validation instead +of returning an error. + +The interface to each parser is uniform and simple, consisting of +only a few functions to: + +- test that a payload is or is not the protocol in question + (eg. is this modbus?) +- provide more bytes to the parser +- set callbacks to invoke on per-protocol metadata events (todo) +- indicate that some bytes are unavailable (ie. notify of packet + loss) (todo) +- indicate a session has ended (todo) + +The library exposes Rust and C bindings for easy integration into +existing and future network security sensor platforms. (todo) + +# Usage +Start using SAWP by including a parser in your project's `Cargo.toml` +dependencies. The base library will also be required for using common +types. + +**The minimum supported version of `rustc` is `1.58.1`.** + +## Example +``` +[dependencies] +sawp-modbus = "0.12.1" +sawp = "0.12.1" +``` + +## FFI Support +Some parsers have a foreign function interface for use in C/C++ projects. +FFI Support can be enabled by building with the `ffi` feature. + +A [Makefile](Makefile) is also provided to ease the build process. Please refer to this file for more in-depth documentation. + +``` +# Install cbindgen which is required to generate headers +cargo install --force cbindgen + +# Build headers and shared objects +make +``` + +# Contributing + +This project is actively maintained and accepting open source +contributions. See [CONTRIBUTING](CONTRIBUTING.md) for more details. diff --git a/rust/vendor/sawp/src/error.rs b/rust/vendor/sawp/src/error.rs new file mode 100644 index 0000000..d27717b --- /dev/null +++ b/rust/vendor/sawp/src/error.rs @@ -0,0 +1,153 @@ +#[cfg(feature = "ffi")] +use sawp_ffi::GenerateFFI; + +use std::num::NonZeroUsize; + +// Re-export types used for ErrorKind +use nom::error::ErrorKind as NomErrorKind; +use nom::Needed as NomNeeded; + +/// Helper that uses this module's error type +pub type Result<T> = std::result::Result<T, Error>; + +/// Helper for nom's default error type +pub type NomError<I> = nom::error::Error<I>; + +/// Common protocol or parsing error +/// +/// This error type is meant to return the errors that +/// are common across parsers and other sub packages. +/// Sub packages may choose to implement their own error +/// types if they wish to avoid adding extra dependencies +/// to the base crate. +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "ffi", derive(GenerateFFI))] +#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp"))] +pub struct Error { + pub kind: ErrorKind, +} + +impl Error { + pub fn new(kind: ErrorKind) -> Self { + Self { kind } + } + + /// Helper for creating an error with a `ErrorKind::Incomplete` and a needed size. + pub fn incomplete_needed(size: usize) -> Self { + Error::new(ErrorKind::Incomplete( + NonZeroUsize::new(size) + .map(Needed::Size) + .unwrap_or(Needed::Unknown), + )) + } + + /// Helper for creating an error with a `ErrorKind::Incomplete` and an unknown size. + pub fn incomplete() -> Self { + Error::new(ErrorKind::Incomplete(Needed::Unknown)) + } + + /// Helper for creating a parse error. + #[cfg(verbose)] + pub fn parse(msg: Option<String>) -> Self { + Error::new(ErrorKind::ParseError(msg)) + } + + /// Helper for creating a parse error. + #[cfg(not(verbose))] + pub fn parse(_msg: Option<String>) -> Self { + Error::new(ErrorKind::ParseError(None)) + } +} + +impl From<ErrorKind> for Error { + fn from(kind: ErrorKind) -> Self { + Self::new(kind) + } +} + +/// Number of bytes needed for the next parsing attempt. +/// +/// Used in `ErrorKind::Incomplete` to tell the caller how many bytes to wait +/// for before calling the parser with more data. +#[derive(Debug, PartialEq)] +pub enum Needed { + Unknown, + Size(NonZeroUsize), +} + +/// Kinds of common errors used by the parsers +#[derive(Debug, PartialEq)] +#[non_exhaustive] +#[cfg_attr(feature = "ffi", derive(GenerateFFI))] +#[cfg_attr(feature = "ffi", sawp_ffi(type_only, prefix = "sawp"))] +pub enum ErrorKind { + /// Feature is not yet implemented. + Unimplemented, + /// Parser could not advance based on the data provided. + /// + /// Usually indicates the provided input bytes cannot be parsed + /// for the protocol. + // + // Developer note: + // + // This error should only be used as a last resort. Consider + // returning Ok and adding validation error flags to the + // parser's `Message` instead. + InvalidData, + /// Generic parsing error with optional message. + ParseError(Option<String>), + /// Parser did not advance because more data is required to + /// make a decision. + /// + /// The caller should gather more data and try again. + Incomplete(Needed), +} + +impl From<NomErrorKind> for ErrorKind { + #[cfg(verbose)] + fn from(kind: NomErrorKind) -> Self { + Self::ParseError(Some(format!("{:?}", kind))) + } + + #[cfg(not(verbose))] + fn from(_kind: NomErrorKind) -> Self { + Self::ParseError(None) + } +} + +impl<I: std::fmt::Debug> From<nom::Err<NomError<I>>> for Error { + fn from(nom_err: nom::Err<NomError<I>>) -> Self { + match nom_err { + nom::Err::Error(err) | nom::Err::Failure(err) => Error::new(err.code.into()), + nom::Err::Incomplete(needed) => match needed { + NomNeeded::Unknown => Error::incomplete(), + NomNeeded::Size(size) => Error::incomplete_needed(size.into()), + }, + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { + match &self.kind { + ErrorKind::Unimplemented => write!(f, "Unimplemented feature"), + ErrorKind::InvalidData => write!(f, "Encountered invalid data"), + ErrorKind::ParseError(err) if err.is_some() => { + write!(f, "Parsing error: {}", err.clone().unwrap()) + } + ErrorKind::ParseError(_) => write!(f, "Parsing error"), + ErrorKind::Incomplete(Needed::Unknown) => write!(f, "More bytes required to parse"), + ErrorKind::Incomplete(Needed::Size(n)) => { + write!(f, "{} more bytes required to parse", n) + } + } + } +} + +impl std::error::Error for Error {} + +impl<I: std::fmt::Debug> From<NomError<I>> for Error { + fn from(nom_err: NomError<I>) -> Self { + Error::new(nom_err.code.into()) + } +} diff --git a/rust/vendor/sawp/src/ffi.rs b/rust/vendor/sawp/src/ffi.rs new file mode 100644 index 0000000..aeed1cc --- /dev/null +++ b/rust/vendor/sawp/src/ffi.rs @@ -0,0 +1,86 @@ +use sawp_ffi::deref; + +/// Note this function only works for Vec<u8> +/// for other types, use the field_ptr accessor +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_vector_get_data(vec: *const Vec<u8>) -> *const u8 { + deref!(vec).as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_vector_get_size(vec: *const Vec<u8>) -> usize { + deref!(vec).len() +} + +/// Note: Returned string is not null terminated +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_string_get_ptr(s: *const String) -> *const u8 { + deref!(s).as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_string_get_size(s: *const String) -> usize { + deref!(s).len() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipv4addr_get_data(ip: *const std::net::Ipv4Addr) -> u32 { + u32::from_be_bytes(deref!(ip).octets()) +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipv6addr_get_data(ip: *const std::net::Ipv6Addr) -> *const u8 { + deref!(ip).octets().as_slice().as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_is_v4(ip: *const std::net::IpAddr) -> bool { + deref!(ip).is_ipv4() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_is_v6(ip: *const std::net::IpAddr) -> bool { + deref!(ip).is_ipv6() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_as_v4( + ip: *const std::net::IpAddr, +) -> *const std::net::Ipv4Addr { + if let std::net::IpAddr::V4(addr) = deref!(ip) { + addr + } else { + std::ptr::null() + } +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_as_v6( + ip: *const std::net::IpAddr, +) -> *const std::net::Ipv6Addr { + if let std::net::IpAddr::V6(addr) = deref!(ip) { + addr + } else { + std::ptr::null() + } +} diff --git a/rust/vendor/sawp/src/lib.rs b/rust/vendor/sawp/src/lib.rs new file mode 100644 index 0000000..7c5940b --- /dev/null +++ b/rust/vendor/sawp/src/lib.rs @@ -0,0 +1,44 @@ +/*! +# SAWP: Security Aware Wire Protocol parsing library + +This library contains parsers for various wire protocols +and is intended to be used in network security sensors. + +The base library contains all of the common types and traits +used by the parsers. + +Usage documentation can be found in the [README](https://github.com/CybercentreCanada/sawp/blob/main/README.md). + +## Protocols + +Each protocol, along with certain features, are implemented +in a separate package inside this workspace. This reduces the +number dependencies needed for using various protocols or +features. A practical use of this library can be found by +referring to the protocol parser you wish to use: +- [Diameter](/sawp-diameter) +- [Json](/sawp-json) +- [Modbus](/sawp-modbus) + +## Utility + +The following utility packages also exist: +- [File](/sawp-file) Serializes API calls for debugging +*/ + +#![allow(clippy::unneeded_field_pattern)] + +/// Return common errors +pub mod error; + +/// Parse Messages +pub mod parser; + +/// Probe Bytes +pub mod probe; + +/// Describe a Protocol +pub mod protocol; + +#[cfg(feature = "ffi")] +pub mod ffi; diff --git a/rust/vendor/sawp/src/parser.rs b/rust/vendor/sawp/src/parser.rs new file mode 100644 index 0000000..6909305 --- /dev/null +++ b/rust/vendor/sawp/src/parser.rs @@ -0,0 +1,39 @@ +use crate::error::Result; +use crate::protocol::Protocol; + +/// Destination of the input byte stream. +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum Direction { + /// Message is destined to the client + ToClient, + /// Message is destined to the server + ToServer, + /// Direction is not known + Unknown, +} + +/// Trait for parsing message from an input byte stream. +pub trait Parse<'a>: Protocol<'a> { + /// Returns a tuple containing the remaining unparsed data and the parsed `Message`. + /// + /// A return value of `Result::Ok` indicates that the parser has *made progress* + /// and should only be used when the remaining unparsed data is less than the input. + /// + /// A return value of `Result::Err` indicates that *no progress* was made + /// and the user may call the parse function again with the same input in + /// some scenarios: + /// - `ErrorKind::Incomplete`: call `parse` once more input data is available. + /// + /// Consequently, `Result::Ok(None)` is used to indicate the parser made + /// progress but needs more data to return a complete `Message`. Internal + /// buffering may occur depending on the implementation. + /// + /// `Result::Err(ErrorKind::Incomplete(_))` must be used instead of `Result::Ok(None)` + /// when no progress was made parsing the input. + fn parse( + &self, + input: &'a [u8], + direction: Direction, + ) -> Result<(&'a [u8], Option<Self::Message>)>; +} diff --git a/rust/vendor/sawp/src/probe.rs b/rust/vendor/sawp/src/probe.rs new file mode 100644 index 0000000..3aa976d --- /dev/null +++ b/rust/vendor/sawp/src/probe.rs @@ -0,0 +1,31 @@ +use crate::error::{Error, ErrorKind}; +use crate::parser::{Direction, Parse}; +use crate::protocol::Protocol; + +/// Result of probing the underlying bytes. +#[derive(Debug, PartialEq)] +pub enum Status { + /// Data matches this protocol + Recognized, + /// Data does not match this protocol + Unrecognized, + /// More data is needed to make a decision + Incomplete, +} + +pub trait Probe<'a>: Protocol<'a> + Parse<'a> { + /// Probes the input to recognize if the underlying bytes likely match this + /// protocol. + /// + /// Returns a probe status. Probe again once more data is available when the + /// status is `Status::Incomplete`. + fn probe(&self, input: &'a [u8], direction: Direction) -> Status { + match self.parse(input, direction) { + Ok((_, _)) => Status::Recognized, + Err(Error { + kind: ErrorKind::Incomplete(_), + }) => Status::Incomplete, + Err(_) => Status::Unrecognized, + } + } +} diff --git a/rust/vendor/sawp/src/protocol.rs b/rust/vendor/sawp/src/protocol.rs new file mode 100644 index 0000000..a24d7e2 --- /dev/null +++ b/rust/vendor/sawp/src/protocol.rs @@ -0,0 +1,8 @@ +/// Represents the basic elements of a protocol +pub trait Protocol<'a> { + /// Type of message returned when parsing + type Message: 'a; + + /// Protocol name string + fn name() -> &'static str; +} |