mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
11 KiB
Rust
274 lines
11 KiB
Rust
/* Copyright (C) 2017-2020 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.
|
|
*/
|
|
|
|
// written by Victor Julien
|
|
|
|
use crate::direction::Direction;
|
|
use crate::nfs::nfs::*;
|
|
use crate::nfs::types::*;
|
|
use crate::nfs::rpc_records::*;
|
|
use crate::nfs::nfs3_records::*;
|
|
|
|
use nom7::IResult;
|
|
use nom7::number::streaming::be_u32;
|
|
|
|
impl NFSState {
|
|
/// complete NFS3 request record
|
|
pub fn process_request_record_v3(&mut self, r: &RpcPacket) {
|
|
SCLogDebug!("REQUEST {} procedure {} ({}) blob size {}",
|
|
r.hdr.xid, r.procedure, self.requestmap.len(), r.prog_data.len());
|
|
|
|
let mut xidmap = NFSRequestXidMap::new(r.progver, r.procedure, 0);
|
|
let mut aux_file_name = Vec::new();
|
|
|
|
if self.nfs_version == 0 {
|
|
self.nfs_version = r.progver as u16;
|
|
}
|
|
|
|
if r.procedure == NFSPROC3_LOOKUP {
|
|
self.process_request_record_lookup(r, &mut xidmap);
|
|
|
|
} else if r.procedure == NFSPROC3_ACCESS {
|
|
if let Ok((_, rd)) = parse_nfs3_request_access(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
self.xidmap_handle2name(&mut xidmap);
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_GETATTR {
|
|
if let Ok((_, rd)) = parse_nfs3_request_getattr(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
self.xidmap_handle2name(&mut xidmap);
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_READDIRPLUS {
|
|
if let Ok((_, rd)) = parse_nfs3_request_readdirplus(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
self.xidmap_handle2name(&mut xidmap);
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_READ {
|
|
if let Ok((_, rd)) = parse_nfs3_request_read(r.prog_data) {
|
|
xidmap.chunk_offset = rd.offset;
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
self.xidmap_handle2name(&mut xidmap);
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_WRITE {
|
|
if let Ok((_, rd)) = parse_nfs3_request_write(r.prog_data, true) {
|
|
self.process_write_record(r, &rd);
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
}
|
|
} else if r.procedure == NFSPROC3_CREATE {
|
|
if let Ok((_, rd)) = parse_nfs3_request_create(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
xidmap.file_name = rd.name_vec;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_REMOVE {
|
|
if let Ok((_, rd)) = parse_nfs3_request_remove(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
xidmap.file_name = rd.name_vec;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_RENAME {
|
|
if let Ok((_, rd)) = parse_nfs3_request_rename(r.prog_data) {
|
|
xidmap.file_handle = rd.from_handle.value.to_vec();
|
|
xidmap.file_name = rd.from_name_vec;
|
|
aux_file_name = rd.to_name_vec;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_MKDIR {
|
|
if let Ok((_, rd)) = parse_nfs3_request_mkdir(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
xidmap.file_name = rd.name_vec;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_RMDIR {
|
|
if let Ok((_, rd)) = parse_nfs3_request_rmdir(r.prog_data) {
|
|
xidmap.file_handle = rd.handle.value.to_vec();
|
|
xidmap.file_name = rd.name_vec;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if r.procedure == NFSPROC3_COMMIT {
|
|
SCLogDebug!("COMMIT, closing shop");
|
|
if let Ok((_, rd)) = parse_nfs3_request_commit(r.prog_data) {
|
|
let file_handle = rd.handle.value.to_vec();
|
|
if let Some(tx) = self.get_file_tx_by_handle(&file_handle, Direction::ToServer) {
|
|
if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
|
|
tdf.chunk_count += 1;
|
|
tdf.file_additional_procs.push(NFSPROC3_COMMIT);
|
|
filetracker_close(&mut tdf.file_tracker);
|
|
tdf.file_last_xid = r.hdr.xid;
|
|
tx.is_last = true;
|
|
tx.request_done = true;
|
|
tx.is_file_closed = true;
|
|
}
|
|
}
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
}
|
|
|
|
if !(r.procedure == NFSPROC3_COMMIT || // commit handled separately
|
|
r.procedure == NFSPROC3_WRITE || // write handled in file tx
|
|
r.procedure == NFSPROC3_READ) // read handled in file tx at reply
|
|
{
|
|
let mut tx = self.new_tx();
|
|
tx.xid = r.hdr.xid;
|
|
tx.procedure = r.procedure;
|
|
tx.request_done = true;
|
|
tx.file_name = xidmap.file_name.to_vec();
|
|
tx.nfs_version = r.progver as u16;
|
|
tx.file_handle = xidmap.file_handle.to_vec();
|
|
|
|
if r.procedure == NFSPROC3_RENAME {
|
|
tx.type_data = Some(NFSTransactionTypeData::RENAME(aux_file_name));
|
|
}
|
|
|
|
tx.auth_type = r.creds_flavor;
|
|
#[allow(clippy::single_match)]
|
|
match r.creds {
|
|
RpcRequestCreds::Unix(ref u) => {
|
|
tx.request_machine_name = u.machine_name_buf.to_vec();
|
|
tx.request_uid = u.uid;
|
|
tx.request_gid = u.gid;
|
|
},
|
|
_ => { },
|
|
}
|
|
SCLogDebug!("TX created: ID {} XID {} PROCEDURE {}",
|
|
tx.id, tx.xid, tx.procedure);
|
|
self.transactions.push(tx);
|
|
|
|
} else if r.procedure == NFSPROC3_READ {
|
|
|
|
let found = self.get_file_tx_by_handle(&xidmap.file_handle, Direction::ToClient).is_some();
|
|
if !found {
|
|
let tx = self.new_file_tx(&xidmap.file_handle, &xidmap.file_name, Direction::ToClient);
|
|
tx.procedure = NFSPROC3_READ;
|
|
tx.xid = r.hdr.xid;
|
|
tx.is_first = true;
|
|
tx.nfs_version = r.progver as u16;
|
|
|
|
tx.auth_type = r.creds_flavor;
|
|
if let RpcRequestCreds::Unix(ref u) = r.creds {
|
|
tx.request_machine_name = u.machine_name_buf.to_vec();
|
|
tx.request_uid = u.uid;
|
|
tx.request_gid = u.gid;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.requestmap.insert(r.hdr.xid, xidmap);
|
|
}
|
|
|
|
pub fn process_reply_record_v3(&mut self, r: &RpcReplyPacket, xidmap: &mut NFSRequestXidMap) {
|
|
let mut nfs_status = 0;
|
|
let mut resp_handle = Vec::new();
|
|
|
|
if xidmap.procedure == NFSPROC3_LOOKUP {
|
|
if let Ok((_, rd)) = parse_nfs3_response_lookup(r.prog_data) {
|
|
SCLogDebug!("LOOKUP: {:?}", rd);
|
|
SCLogDebug!("RESPONSE LOOKUP file_name {:?}", xidmap.file_name);
|
|
|
|
nfs_status = rd.status;
|
|
|
|
SCLogDebug!("LOOKUP handle {:?}", rd.handle);
|
|
self.namemap.insert(rd.handle.value.to_vec(), xidmap.file_name.to_vec());
|
|
resp_handle = rd.handle.value.to_vec();
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if xidmap.procedure == NFSPROC3_CREATE {
|
|
if let Ok((_, rd)) = parse_nfs3_response_create(r.prog_data) {
|
|
SCLogDebug!("nfs3_create_record: {:?}", rd);
|
|
SCLogDebug!("RESPONSE CREATE file_name {:?}", xidmap.file_name);
|
|
nfs_status = rd.status;
|
|
|
|
if let Some(h) = rd.handle {
|
|
SCLogDebug!("handle {:?}", h);
|
|
self.namemap.insert(h.value.to_vec(), xidmap.file_name.to_vec());
|
|
resp_handle = h.value.to_vec();
|
|
}
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
};
|
|
} else if xidmap.procedure == NFSPROC3_READ {
|
|
if let Ok((_, rd)) = parse_nfs3_reply_read(r.prog_data, true) {
|
|
self.process_read_record(r, &rd, Some(xidmap));
|
|
nfs_status = rd.status;
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
}
|
|
} else if xidmap.procedure == NFSPROC3_READDIRPLUS {
|
|
if let Ok((_, rd)) = parse_nfs3_response_readdirplus(r.prog_data) {
|
|
nfs_status = rd.status;
|
|
|
|
// cut off final eof field
|
|
let d = if rd.data.len() >= 4 {
|
|
&rd.data[..rd.data.len()-4_usize]
|
|
} else {
|
|
rd.data
|
|
};
|
|
|
|
// store all handle/filename mappings
|
|
if let Ok((_, ref entries)) = many0_nfs3_response_readdirplus_entries(d) {
|
|
SCLogDebug!("READDIRPLUS ENTRIES reply {:?}", entries);
|
|
for ce in entries {
|
|
SCLogDebug!("ce {:?}", ce);
|
|
if let Some(ref e) = ce.entry {
|
|
SCLogDebug!("e {:?}", e);
|
|
if let Some(ref h) = e.handle {
|
|
SCLogDebug!("h {:?}", h);
|
|
self.namemap.insert(h.value.to_vec(),
|
|
e.name_vec.to_vec());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
}
|
|
} else {
|
|
self.set_event(NFSEvent::MalformedData);
|
|
}
|
|
}
|
|
// for all other record types only parse the status
|
|
else {
|
|
let stat : u32 = match be_u32(r.prog_data) as IResult<&[u8],_> {
|
|
Ok((_, stat)) => stat,
|
|
_ => 0
|
|
};
|
|
nfs_status = stat;
|
|
}
|
|
SCLogDebug!("REPLY {} to procedure {} blob size {}",
|
|
r.hdr.xid, xidmap.procedure, r.prog_data.len());
|
|
|
|
if xidmap.procedure != NFSPROC3_READ {
|
|
self.mark_response_tx_done(r.hdr.xid, r.reply_state, nfs_status, &resp_handle);
|
|
}
|
|
}
|
|
}
|