]> git.proxmox.com Git - pve-lxc-syscalld.git/blame - src/lxcseccomp.rs
working on receiving data for the syscalls
[pve-lxc-syscalld.git] / src / lxcseccomp.rs
CommitLineData
9cffeac4
WB
1//! Module for LXC specific related seccomp handling.
2
3use std::convert::TryFrom;
c95be5f6
WB
4use std::ffi::CString;
5use std::os::unix::fs::FileExt;
6use std::os::unix::io::{FromRawFd, IntoRawFd};
7use std::{io, mem};
9cffeac4
WB
8
9use failure::{bail, Error};
41ff6d28 10use lazy_static::lazy_static;
9cffeac4
WB
11use libc::pid_t;
12
c95be5f6 13use crate::pidfd::PidFd;
e420f6f9 14use crate::seccomp::{SeccompNotif, SeccompNotifResp, SeccompNotifSizes};
0e2d0fa2 15use crate::socket::AsyncSeqPacketSocket;
c95be5f6 16use crate::tools::{IoVec, IoVecMut};
9cffeac4
WB
17
18/// Seccomp notification proxy message sent by the lxc monitor.
19///
20/// Whenever a process in a container triggers a seccomp notification, and lxc has a seccomp
21/// notification proxy configured, this is sent over to the proxy, together with a `SeccompNotif`,
22/// `SeccompNotifResp` and a cookie.
23///
24/// Using this struct may be inconvenient. See the [`ProxyMessageBuffer`] for a convenient helper
25/// for communcation.
26#[repr(C)]
27pub struct SeccompNotifyProxyMsg {
28 /// Reserved data must be zero.
29 reserved0: u64,
30
31 /// The lxc monitor pid.
32 ///
33 /// Unless some other proxy forwards proxy messages, this should be the same pid as the peer
34 /// we receive this message from.
35 monitor_pid: pid_t,
36
37 /// The container's init pid.
38 ///
39 /// If supported by the kernel, the lxc monitor should keep a pidfd open to this process, so
40 /// this pid should be valid as long as `monitor_pid` is valid.
41 init_pid: pid_t,
42
43 /// Information about the seccomp structure sizes.
44 ///
45 /// This must be equal to `SeccompNotifSizes::get()`, otherwise the proxy and lxc monitor have
46 /// inconsistent views of the kernel's seccomp API.
47 sizes: SeccompNotifSizes,
48
49 /// The length of the container's configured `lxc.seccomp.notify.cookie` value.
50 cookie_len: u64,
51}
52
53/// Helper to receive and verify proxy notification messages.
9cffeac4 54pub struct ProxyMessageBuffer {
571dbe03
WB
55 proxy_msg: SeccompNotifyProxyMsg,
56 seccomp_notif: SeccompNotif,
57 seccomp_resp: SeccompNotifResp,
58 cookie_buf: Vec<u8>,
59
9cffeac4
WB
60 sizes: SeccompNotifSizes,
61 seccomp_packet_size: usize,
41214ae2 62
c95be5f6
WB
63 pid_fd: Option<PidFd>,
64 mem_fd: Option<std::fs::File>,
9cffeac4
WB
65}
66
571dbe03
WB
67unsafe fn io_vec_mut<T>(value: &mut T) -> IoVecMut {
68 IoVecMut::new(std::slice::from_raw_parts_mut(
69 value as *mut T as *mut u8,
70 mem::size_of::<T>(),
71 ))
72}
73
74unsafe fn io_vec<T>(value: &T) -> IoVec {
75 IoVec::new(std::slice::from_raw_parts(
76 value as *const T as *const u8,
77 mem::size_of::<T>(),
78 ))
79}
80
41ff6d28 81lazy_static! {
e420f6f9
WB
82 static ref SECCOMP_SIZES: SeccompNotifSizes = SeccompNotifSizes::get_checked()
83 .map_err(|e| panic!("{}\nrefusing to run", e))
84 .unwrap();
41ff6d28
WB
85}
86
9cffeac4
WB
87impl ProxyMessageBuffer {
88 /// Allocate a new proxy message buffer with a specific maximum cookie size.
e420f6f9 89 pub fn new(max_cookie: usize) -> Self {
41ff6d28 90 let sizes = SECCOMP_SIZES.clone();
571dbe03 91
9cffeac4
WB
92 let seccomp_packet_size = mem::size_of::<SeccompNotifyProxyMsg>()
93 + sizes.notif as usize
94 + sizes.notif_resp as usize;
571dbe03 95
e420f6f9 96 Self {
571dbe03
WB
97 proxy_msg: unsafe { mem::zeroed() },
98 seccomp_notif: unsafe { mem::zeroed() },
99 seccomp_resp: unsafe { mem::zeroed() },
100 cookie_buf: unsafe { super::tools::vec::uninitialized(max_cookie) },
9cffeac4
WB
101 sizes,
102 seccomp_packet_size,
41214ae2
WB
103 pid_fd: None,
104 mem_fd: None,
e420f6f9 105 }
9cffeac4
WB
106 }
107
0e2d0fa2 108 /// Returns None on EOF.
c95be5f6 109 pub async fn recv(&mut self, socket: &AsyncSeqPacketSocket) -> Result<bool, Error> {
571dbe03
WB
110 self.proxy_msg.cookie_len = 0;
111
112 unsafe {
113 self.cookie_buf.set_len(self.cookie_buf.capacity());
114 }
115
0e2d0fa2 116 let mut iovec = [
571dbe03
WB
117 unsafe { io_vec_mut(&mut self.proxy_msg) },
118 unsafe { io_vec_mut(&mut self.seccomp_notif) },
119 unsafe { io_vec_mut(&mut self.seccomp_resp) },
120 IoVecMut::new(self.cookie_buf.as_mut_slice()),
121 ];
9cffeac4 122
9cffeac4 123 unsafe {
571dbe03 124 self.cookie_buf.set_len(0);
9cffeac4 125 }
571dbe03 126
41214ae2
WB
127 let (size, fds) = socket.recv_fds_vectored(&mut iovec, 2).await?;
128 if size == 0 {
129 return Ok(false);
130 }
131
0e2d0fa2
WB
132 self.set_len(size)?;
133
41214ae2 134 let mut fds = fds.into_iter();
c95be5f6
WB
135 self.pid_fd = fds
136 .next()
137 .map(|fd| unsafe { PidFd::from_raw_fd(fd.into_raw_fd()) });
138 self.mem_fd = fds
139 .next()
140 .map(|fd| unsafe { std::fs::File::from_raw_fd(fd.into_raw_fd()) });
41214ae2
WB
141 if self.mem_fd.is_none() {
142 self.drop_fds();
143 bail!("missing file descriptors with proxied seccomp message");
144 }
145
146 Ok(true)
147 }
148
c95be5f6
WB
149 /// Get the process' pidfd.
150 ///
151 /// Note that the message must be valid, otherwise this panics!
152 pub fn pid_fd(&self) -> &PidFd {
153 self.pid_fd.as_ref().unwrap()
154 }
155
156 /// Get the process' mem fd.
157 ///
158 /// Note that this returns a non-mut trait object. This is because positional I/O does not need
159 /// mutable self and the standard library correctly represents this in its `FileExt` trait!
160 ///
161 /// Note that the message must be valid, otherwise this panics!
162 pub fn mem_fd(&self) -> &dyn FileExt {
163 self.mem_fd.as_ref().unwrap()
164 }
165
41214ae2
WB
166 pub fn drop_fds(&mut self) {
167 self.pid_fd = None;
168 self.mem_fd = None;
9cffeac4
WB
169 }
170
0e2d0fa2 171 /// Send the current data as response.
c95be5f6 172 pub async fn respond(&mut self, socket: &AsyncSeqPacketSocket) -> io::Result<()> {
0e2d0fa2 173 let iov = [
571dbe03
WB
174 unsafe { io_vec(&self.proxy_msg) },
175 unsafe { io_vec(&self.seccomp_notif) },
176 unsafe { io_vec(&self.seccomp_resp) },
0e2d0fa2
WB
177 ];
178 socket.sendmsg_vectored(&iov).await
9cffeac4
WB
179 }
180
181 #[inline]
182 fn prepare_response(&mut self) {
183 let id = self.request().id;
184 let resp = self.response_mut();
185 resp.id = id;
186 resp.val = -1;
187 resp.error = -libc::ENOSYS;
188 resp.flags = 0;
189 }
190
571dbe03
WB
191 /// Called by with_io_slice after the callback returned the new size. This verifies that
192 /// there's enough data available.
9cffeac4 193 pub fn set_len(&mut self, len: usize) -> Result<(), Error> {
571dbe03
WB
194 if len < self.seccomp_packet_size {
195 bail!("seccomp proxy message too short");
9cffeac4
WB
196 }
197
571dbe03
WB
198 if self.proxy_msg.reserved0 != 0 {
199 bail!("reserved data wasn't 0, liblxc secocmp notify protocol mismatch");
200 }
201
202 if !self.check_sizes() {
9cffeac4
WB
203 bail!("seccomp proxy message content size validation failed");
204 }
205
571dbe03
WB
206 if len - self.seccomp_packet_size > self.cookie_buf.capacity() {
207 bail!("seccomp proxy message too long");
208 }
209
210 let cookie_len = match usize::try_from(self.proxy_msg.cookie_len) {
211 Ok(cl) => cl,
212 Err(_) => {
213 self.proxy_msg.cookie_len = 0;
214 bail!("cookie length exceeds our size type!");
215 }
216 };
217
218 if len != self.seccomp_packet_size + cookie_len {
52f50bd4
WB
219 bail!(
220 "seccomp proxy packet contains unexpected cookie length {} + {} != {}",
221 self.seccomp_packet_size,
571dbe03 222 cookie_len,
52f50bd4
WB
223 len
224 );
9cffeac4
WB
225 }
226
227 unsafe {
571dbe03 228 self.cookie_buf.set_len(cookie_len);
9cffeac4
WB
229 }
230
231 self.prepare_response();
232
233 Ok(())
234 }
235
571dbe03
WB
236 fn check_sizes(&self) -> bool {
237 let got = self.proxy_msg.sizes.clone();
9cffeac4
WB
238 got.notif == self.sizes.notif
239 && got.notif_resp == self.sizes.notif_resp
240 && got.data == self.sizes.data
241 }
242
9cffeac4
WB
243 /// Get the monitor pid from the current message.
244 ///
245 /// There's no guarantee that the pid is valid.
246 pub fn monitor_pid(&self) -> pid_t {
571dbe03 247 self.proxy_msg.monitor_pid
9cffeac4
WB
248 }
249
250 /// Get the container's init pid from the current message.
251 ///
252 /// There's no guarantee that the pid is valid.
253 pub fn init_pid(&self) -> pid_t {
571dbe03 254 self.proxy_msg.init_pid
9cffeac4
WB
255 }
256
257 /// Get the syscall request structure of this message.
258 pub fn request(&self) -> &SeccompNotif {
571dbe03 259 &self.seccomp_notif
9cffeac4
WB
260 }
261
262 /// Access the response buffer of this message.
263 pub fn response_mut(&mut self) -> &mut SeccompNotifResp {
571dbe03 264 &mut self.seccomp_resp
9cffeac4
WB
265 }
266
267 /// Get the cookie's length.
268 pub fn cookie_len(&self) -> usize {
571dbe03 269 usize::try_from(self.proxy_msg.cookie_len).expect("cookie size should fit in an usize")
9cffeac4
WB
270 }
271
272 /// Get the cookie sent along with this message.
273 pub fn cookie(&self) -> &[u8] {
571dbe03 274 &self.cookie_buf
9cffeac4 275 }
c95be5f6
WB
276
277 #[inline]
278 fn checked_arg(&self, arg: u32) -> nix::Result<u64> {
279 self.request()
280 .data
281 .args
282 .get(arg as usize)
283 .map(|x| *x)
284 .ok_or_else(|| nix::errno::Errno::ERANGE.into())
285 }
286
287 /// Get a parameter as C String.
288 ///
289 /// Strings are limited to 4k bytes currently.
290 pub fn arg_c_string(&self, arg: u32) -> Result<CString, Error> {
291 crate::syscall::get_c_string(self, self.checked_arg(arg)?)
292 }
9cffeac4 293}