Esqueleto inicial: sabia-core + sabia-cli + UI local, OpenWolf
- sabia-core: descoberta de interfaces, controle de modo monitor/canal via iw (ponte até ter nl80211 nativo), captura crua 802.11 via AF_PACKET (sem libpcap), parsing de radiotap e quadros de gerenciamento (beacon/probe/EAPOL), agregação em tabela de APs/ clientes, escrita de .pcap, trilha de auditoria encadeada por hash, auto-atualização via GitHub releases (curl, sem dependências de rede extras) - sabia-cli: `sabia iface`, `sabia scan`, `sabia update`, `sabia audit-verify`, `sabia ui` (servidor local + navegador, protegido por token, mesma filosofia do Guará) - OpenWolf inicializado para memória de projeto entre agentes
This commit is contained in:
commit
e989413018
76 files changed
+8386
No files matched your search
@@ -0,0 +1,122 @@
|
||||
//! Captura crua de quadros 802.11 via `AF_PACKET`, sem depender de libpcap.
|
||||
//!
|
||||
//! A interface já precisa estar em modo monitor (ver [`crate::netlink`]).
|
||||
//! Isso só funciona em Linux — é exatamente o que o libpcap faz por baixo
|
||||
//! quando usado em modo monitor, então reimplementar aqui é pouco código e
|
||||
//! remove uma dependência de sistema.
|
||||
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use crate::error::{Result, SabiaError};
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::time::Duration;
|
||||
|
||||
const ETH_P_ALL: u16 = 0x0003;
|
||||
|
||||
pub struct RawCapture {
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl RawCapture {
|
||||
/// Abre um socket raw ligado à interface `iface` (já em modo monitor).
|
||||
/// Exige `CAP_NET_RAW` (tipicamente root).
|
||||
pub fn open(iface: &str) -> Result<Self> {
|
||||
let fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ALL as i32) };
|
||||
if fd < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
return Err(if err.raw_os_error() == Some(libc::EPERM) {
|
||||
SabiaError::PermissionDenied
|
||||
} else {
|
||||
SabiaError::Io(err)
|
||||
});
|
||||
}
|
||||
|
||||
let ifindex = if_index(iface)?;
|
||||
|
||||
let mut sll: libc::sockaddr_ll = unsafe { mem::zeroed() };
|
||||
sll.sll_family = libc::AF_PACKET as u16;
|
||||
sll.sll_protocol = (ETH_P_ALL as u16).to_be();
|
||||
sll.sll_ifindex = ifindex;
|
||||
|
||||
let ret = unsafe {
|
||||
libc::bind(
|
||||
fd,
|
||||
&sll as *const libc::sockaddr_ll as *const libc::sockaddr,
|
||||
mem::size_of::<libc::sockaddr_ll>() as u32,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
unsafe { libc::close(fd) };
|
||||
return Err(SabiaError::Io(err));
|
||||
}
|
||||
|
||||
Ok(Self { fd })
|
||||
}
|
||||
|
||||
/// Define um timeout de recepção, para o loop de captura poder checar
|
||||
/// periodicamente um sinal de parada sem travar para sempre em `recv`.
|
||||
pub fn set_read_timeout(&self, timeout: Duration) -> Result<()> {
|
||||
let tv = libc::timeval {
|
||||
tv_sec: timeout.as_secs() as libc::time_t,
|
||||
tv_usec: timeout.subsec_micros() as libc::suseconds_t,
|
||||
};
|
||||
let ret = unsafe {
|
||||
libc::setsockopt(
|
||||
self.fd,
|
||||
libc::SOL_SOCKET,
|
||||
libc::SO_RCVTIMEO,
|
||||
&tv as *const _ as *const libc::c_void,
|
||||
mem::size_of::<libc::timeval>() as u32,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(SabiaError::Io(io::Error::last_os_error()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lê um quadro. Retorna `Ok(None)` em timeout (ver `set_read_timeout`).
|
||||
pub fn recv(&self, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let n = unsafe { libc::recv(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) };
|
||||
if n < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
if matches!(err.raw_os_error(), Some(libc::EAGAIN)) {
|
||||
return Ok(None);
|
||||
}
|
||||
return Err(SabiaError::Io(err));
|
||||
}
|
||||
Ok(Some(n as usize))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawCapture {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawCapture {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
||||
|
||||
fn if_index(name: &str) -> Result<i32> {
|
||||
let cname = CString::new(name).map_err(|_| SabiaError::InterfaceNotFound(name.to_string()))?;
|
||||
let idx = unsafe { libc::if_nametoindex(cname.as_ptr()) };
|
||||
if idx == 0 {
|
||||
return Err(SabiaError::InterfaceNotFound(name.to_string()));
|
||||
}
|
||||
Ok(idx as i32)
|
||||
}
|
||||
|
||||
/// Garante, de forma best-effort, que o processo tem privilégio para abrir
|
||||
/// sockets raw — útil para dar um erro claro antes de tentar o resto.
|
||||
pub fn check_capabilities() -> bool {
|
||||
let euid = unsafe { libc::geteuid() };
|
||||
euid == 0
|
||||
}
|
||||
Reference in new issue
Block a user