Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ members = [
"client",
"crypto-auditing",
"event-broker",
"log-parser"
"log-parser",
"monitor"
]
resolver = "2"

Expand All @@ -32,7 +33,7 @@ openssl = "0.10"
page_size = "0.6"
probe = "0.5"
plain = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_cbor = "0.11"
serde_json = "1.0"
serde_with = "3"
Expand Down
7 changes: 5 additions & 2 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ programs = \
${TARGETDIR}/${PROFILE}/crau-agent \
${TARGETDIR}/${PROFILE}/crau-client \
${TARGETDIR}/${PROFILE}/crau-event-broker \
${TARGETDIR}/${PROFILE}/crau-log-parser
${TARGETDIR}/${PROFILE}/crau-query \
${TARGETDIR}/${PROFILE}/crau-monitor

conffiles = \
dist/conf/agent.conf \
dist/conf/client.conf \
dist/conf/event-broker.conf
dist/conf/event-broker.conf \
dist/conf/query.conf \
dist/conf/monitor.conf

.PHONY: all
all: $(programs)
Expand Down
20 changes: 4 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The design documents can be found from the following links:
```console
$ git clone --depth=1 -b wip/usdt https://gitlab.com/gnutls/gnutls.git
$ ./bootstrap
$ ./configure --prefix=/path/to/installation
$ ./configure --prefix=/path/to/installation --enable-crypto-auditing
$ make -j$(nproc)
$ sudo make install
```
Expand All @@ -40,16 +40,6 @@ $ make
$ sudo make install
```

The first step requires `agent/src/bpf/vmlinux.h` to be populated. By
default it is done through BTF dump from the running kernel with
`bpftool`, but if it is not supported in your system, it is possible
to use `vmlinux.h` included in the `kernel-devel` package:

```console
$ sudo dnf install kernel-devel
$ cp $(rpm -ql kernel-devel | grep '/vmlinux.h$' | tail -1) agent/src/bpf
```

## Running

1. Create dedicated user and group (e.g., crypto-auditing:crypto-auditing)
Expand All @@ -73,16 +63,14 @@ SocketMode=0660
library = ["/path/to/installation/lib64/libgnutls.so.30"]
user = "crypto-auditing:crypto-auditing"
```
5. Enable agent and event-broker
5. Enable agent
```console
$ sudo systemctl daemon-reload
$ sudo systemctl start crau-agent.service
$ sudo systemctl start crau-event-broker.socket
```
6. Connect to event-broker with client
6. Run monitor
```console
$ crau-client --scope tls --format json
$ crau-client --scope tls --format cbor --output audit.cborseq
$ crau-monitor
```
7. On another terminal, run any commands using the instrumented library
```console
Expand Down
2 changes: 1 addition & 1 deletion crypto-auditing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ futures.workspace = true
libc.workspace = true
serde.workspace = true
serde_cbor.workspace = true
serde_with.workspace = true
serde_with = { workspace = true, features = ["hex"] }
thiserror.workspace = true
tokio = { workspace = true, features = ["net", "rt"] }
tokio-serde.workspace = true
Expand Down
45 changes: 40 additions & 5 deletions crypto-auditing/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2022-2023 The crypto-auditing developers.

use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde::{
Deserialize, Serialize,
ser::{SerializeSeq, Serializer},
};
use serde_with::{hex::Hex, serde_as};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ffi::CStr;
use std::rc::Rc;
use std::time::Duration;

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

pub type ContextID = [u8; 16];

fn only_values<K, V, S>(source: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
V: Serialize,
{
let mut seq = serializer.serialize_seq(Some(source.len()))?;
for value in source.values() {
seq.serialize_element(value)?;
}
seq.end()
}

#[serde_as]
#[derive(Debug, Default, Serialize)]
pub struct Context {
#[serde_as(as = "Hex")]
pub context: ContextID,
#[serde_as(as = "Hex")]
pub origin: Vec<u8>,
#[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
pub start: Duration,
#[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
pub end: Duration,
pub events: BTreeMap<String, EventData>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
#[serde(serialize_with = "only_values")]
pub spans: BTreeMap<ContextID, Rc<RefCell<Context>>>,
}

#[serde_as]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
Expand Down Expand Up @@ -48,7 +83,7 @@ pub struct EventGroup {
events: Vec<Event>,
}

fn format_context(pid_tgid: u64, context: i64) -> ContextID {
fn format_context_id(pid_tgid: u64, context: i64) -> ContextID {
let mut result: ContextID = Default::default();
result[..8].copy_from_slice(&u64::to_le_bytes(pid_tgid));
result[8..].copy_from_slice(&i64::to_le_bytes(context));
Expand Down Expand Up @@ -115,13 +150,13 @@ impl EventGroup {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
let header = bytes.as_ptr() as *mut audit_event_header_st;
let context =
unsafe { format_context((*header).pid_tgid.into(), (*header).context.into()) };
unsafe { format_context_id((*header).pid_tgid.into(), (*header).context.into()) };
let ktime = unsafe { Duration::from_nanos((*header).ktime.into()) };
let event = match unsafe { (*header).type_ } {
audit_event_type_t::AUDIT_EVENT_NEW_CONTEXT => {
let raw_new_context = bytes.as_ptr() as *mut audit_new_context_event_st;
let parent = unsafe {
format_context((*header).pid_tgid.into(), (*raw_new_context).parent.into())
format_context_id((*header).pid_tgid.into(), (*raw_new_context).parent.into())
};
let origin = unsafe {
(&(*raw_new_context).origin)[..(*raw_new_context).origin_size as usize].to_vec()
Expand Down
2 changes: 2 additions & 0 deletions dist/conf/monitor.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# log_file = "/var/log/crypto-auditing/audit.cborseq"
# event_window = 3000
1 change: 1 addition & 0 deletions dist/conf/query.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# log_file = "/var/log/crypto-auditing/audit.cborseq"
8 changes: 5 additions & 3 deletions log-parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ anyhow.workspace = true
clap = { workspace = true, features=["derive"] }
crypto-auditing.workspace = true
hex.workspace = true
serde = { workspace = true, features = ["rc"] }
serde.workspace = true
serde_cbor.workspace = true
serde_json.workspace = true
serde_with = { workspace = true, features = ["hex"] }
toml.workspace = true
pager = "0.16"

[[bin]]
name = "crau-log-parser"
path = "src/log_parser.rs"
name = "crau-query"
path = "src/query.rs"
89 changes: 89 additions & 0 deletions log-parser/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2022-2023 The crypto-auditing developers.

use anyhow::{Context as _, Result, anyhow};
use clap::{ArgMatches, arg, command, parser::ValueSource, value_parser};
use std::fs;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use toml::{Table, Value};

const CONFIG: &str = "/etc/crypto-auditing/query.conf";
const LOG: &str = "/var/log/crypto-auditing/audit.cborseq";

#[derive(Debug)]
pub struct Config {
/// Path to output log file
pub log_file: PathBuf,
}

impl Default for Config {
fn default() -> Self {
Self {
log_file: PathBuf::from(LOG),
}
}
}

impl Config {
pub fn new() -> Result<Self> {
let mut config = Config::default();

let matches = command!()
.arg(
arg!(
-c --config <FILE> "Path to configuration file"
)
.required(false)
.value_parser(value_parser!(PathBuf)),
)
.arg(
arg!(
--"log-file" <FILE> "Path to output log file"
)
.required(false)
.value_parser(value_parser!(PathBuf))
.default_value("audit.cborseq"),
)
.get_matches();

if let Some(config_file) = matches.get_one::<PathBuf>("config") {
config.merge_config_file(config_file)?;
} else if Path::new(CONFIG).exists() {
config.merge_config_file(CONFIG)?;
}

config.merge_arg_matches(&matches)?;

Ok(config)
}

fn merge_config_file(&mut self, file: impl AsRef<Path>) -> Result<()> {
let s = fs::read_to_string(file.as_ref())
.with_context(|| format!("unable to read config file `{}`", file.as_ref().display()))?;
let config = Table::from_str(&s).with_context(|| {
format!("unable to parse config file `{}`", file.as_ref().display())
})?;

if let Some(value) = config.get("log_file") {
self.log_file = pathbuf_from_value(value)?;
}

Ok(())
}

fn merge_arg_matches(&mut self, matches: &ArgMatches) -> Result<()> {
if let Some(ValueSource::CommandLine) = matches.value_source("log-file") {
self.log_file = matches.try_get_one::<PathBuf>("log-file")?.unwrap().clone();
}

Ok(())
}
}

fn pathbuf_from_value(value: &Value) -> Result<PathBuf> {
value
.as_str()
.ok_or_else(|| anyhow!("value must be string"))
.map(PathBuf::from)
}
Loading