]> git.proxmox.com Git - pve-lxc-syscalld.git/blob - src/process/pid_fd.rs
fix id mapping code
[pve-lxc-syscalld.git] / src / process / pid_fd.rs
1 //! pidfd helper functionality
2
3 use std::ffi::{CStr, CString, OsString};
4 use std::io::{self, BufRead, BufReader};
5 use std::os::raw::c_int;
6 use std::os::unix::ffi::OsStringExt;
7 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
8
9 use anyhow::{bail, Error};
10 use libc::pid_t;
11
12 use crate::capability::Capabilities;
13 use crate::error::io_err_other;
14 use crate::nsfd::{ns_type, NsFd};
15
16 use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps};
17
18 pub struct PidFd(OwnedFd, pid_t);
19 file_descriptor_impl!(PidFd);
20
21 impl PidFd {
22 pub fn current() -> io::Result<Self> {
23 Self::open(unsafe { libc::getpid() })
24 }
25
26 pub fn open(pid: pid_t) -> io::Result<Self> {
27 let path = CString::new(format!("/proc/{pid}")).unwrap();
28
29 let fd = c_try!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) });
30 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
31
32 Ok(Self(fd, pid))
33 }
34
35 /// Turn a valid pid file descriptor into a PidFd.
36 ///
37 /// # Safety
38 ///
39 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
40 /// fails if reading the pid from the pidfd's proc entry fails.
41 pub unsafe fn try_from_fd(fd: OwnedFd) -> io::Result<Self> {
42 #[allow(clippy::unnecessary_cast)] // pid_t is a type alias
43 let mut this = Self(fd, -1 as pid_t);
44 let pid = this.read_pid()?;
45 this.1 = pid;
46 Ok(this)
47 }
48
49 pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> {
50 NsFd::openat(self.0.as_raw_fd(), c_str!("ns/mnt"))
51 }
52
53 pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> {
54 NsFd::openat(self.0.as_raw_fd(), c_str!("ns/cgroup"))
55 }
56
57 pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> {
58 NsFd::openat(self.0.as_raw_fd(), c_str!("ns/user"))
59 }
60
61 fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<OwnedFd> {
62 Ok(unsafe {
63 OwnedFd::from_raw_fd(c_try!(libc::openat(
64 self.as_raw_fd(),
65 path.as_ptr() as *const _,
66 flags | libc::O_CLOEXEC,
67 mode,
68 )))
69 })
70 }
71
72 pub fn fd_cwd(&self) -> io::Result<OwnedFd> {
73 self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0)
74 }
75
76 pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<OwnedFd> {
77 let path = format!("fd/{num}\0");
78 self.fd(
79 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) },
80 flags,
81 0,
82 )
83 }
84
85 pub fn enter_cwd(&self) -> io::Result<()> {
86 c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) });
87 Ok(())
88 }
89
90 pub fn enter_chroot(&self) -> io::Result<()> {
91 c_try!(unsafe { libc::fchdir(self.as_raw_fd()) });
92 c_try!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) });
93 c_try!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) });
94 Ok(())
95 }
96
97 // procfs files cannot be async, we cannot add them to epoll...
98 pub fn open_file(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<std::fs::File> {
99 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) })
100 }
101
102 #[inline]
103 fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> {
104 Ok(BufReader::new(self.open_file(
105 path,
106 libc::O_RDONLY | libc::O_CLOEXEC,
107 0,
108 )?))
109 }
110
111 #[inline]
112 pub fn get_pid(&self) -> pid_t {
113 self.1
114 }
115
116 fn read_pid(&self) -> io::Result<pid_t> {
117 let reader = self.open_buffered(c_str!("status"))?;
118
119 for line in reader.lines() {
120 let line = line?;
121 let mut parts = line.split_ascii_whitespace();
122 if parts.next() == Some("Pid:") {
123 let pid = parts
124 .next()
125 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Pid:' line in proc"))?
126 .parse::<pid_t>()
127 .map_err(|_| {
128 io::Error::new(io::ErrorKind::Other, "failed to parse pid from proc")
129 })?;
130 return Ok(pid);
131 }
132 }
133
134 Err(io::ErrorKind::NotFound.into())
135 }
136
137 #[inline]
138 fn __check_uid_gid(value: Option<&str>) -> io::Result<libc::uid_t> {
139 value
140 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Uid/Gid:' line in proc"))?
141 .parse::<libc::uid_t>()
142 .map_err(|_| io::Error::new(io::ErrorKind::Other, "failed to parse uid from proc"))
143 }
144
145 pub fn get_status(&self) -> io::Result<ProcStatus> {
146 let reader = self.open_buffered(c_str!("status"))?;
147
148 #[inline]
149 fn check_u64_hex(value: Option<&str>) -> io::Result<u64> {
150 u64::from_str_radix(
151 value.ok_or_else(|| {
152 io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc")
153 })?,
154 16,
155 )
156 .map_err(io_err_other)
157 }
158
159 #[inline]
160 fn check_u32_oct(value: Option<&str>) -> io::Result<u32> {
161 u32::from_str_radix(
162 value.ok_or_else(|| {
163 io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc")
164 })?,
165 8,
166 )
167 .map_err(io_err_other)
168 }
169
170 let mut ids = Uids::default();
171 let mut caps = Capabilities::default();
172 let mut umask = 0o022;
173 for line in reader.lines() {
174 let line = line?;
175 let mut parts = line.split_ascii_whitespace();
176 match parts.next() {
177 Some("Uid:") => {
178 ids.ruid = Self::__check_uid_gid(parts.next())?;
179 ids.euid = Self::__check_uid_gid(parts.next())?;
180 ids.suid = Self::__check_uid_gid(parts.next())?;
181 ids.fsuid = Self::__check_uid_gid(parts.next())?;
182 }
183 Some("Gid:") => {
184 ids.rgid = Self::__check_uid_gid(parts.next())?;
185 ids.egid = Self::__check_uid_gid(parts.next())?;
186 ids.sgid = Self::__check_uid_gid(parts.next())?;
187 ids.fsgid = Self::__check_uid_gid(parts.next())?;
188 }
189 Some("CapInh:") => caps.inheritable = check_u64_hex(parts.next())?,
190 Some("CapPrm:") => caps.permitted = check_u64_hex(parts.next())?,
191 Some("CapEff:") => caps.effective = check_u64_hex(parts.next())?,
192 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
193 Some("Umask:") => umask = check_u32_oct(parts.next())?,
194 _ => continue,
195 }
196 }
197
198 Ok(ProcStatus {
199 uids: ids,
200 capabilities: caps,
201 umask,
202 })
203 }
204
205 pub fn get_cgroups(&self) -> Result<CGroups, Error> {
206 let reader = self.open_buffered(c_str!("cgroup"))?;
207
208 let mut cgroups = CGroups::new();
209
210 for line in reader.split(b'\n') {
211 let line = line?;
212 let mut parts = line.splitn(3, |b| *b == b':');
213 let num = parts.next();
214 let name = parts.next();
215 let path = parts.next();
216 if num.is_none() || name.is_none() || path.is_none() || parts.next().is_some() {
217 bail!("failed to parse cgroup line: {:?}", line);
218 }
219
220 let name = String::from_utf8(name.unwrap().to_vec())?;
221 let path = OsString::from_vec(path.unwrap().to_vec());
222
223 if name.is_empty() {
224 cgroups.v2 = Some(path);
225 } else {
226 for entry in name.split(',') {
227 cgroups
228 .v1
229 .get_or_insert_with(Default::default)
230 .insert(entry.to_string(), path.clone());
231 }
232 }
233 }
234
235 Ok(cgroups)
236 }
237
238 pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> {
239 let reader = self.open_buffered(file)?;
240
241 let mut entries = Vec::new();
242 for line in reader.lines() {
243 let line = line?;
244 let mut parts = line.split_ascii_whitespace();
245 let ns = u64::from(Self::__check_uid_gid(parts.next())?);
246 let host = u64::from(Self::__check_uid_gid(parts.next())?);
247 let range = u64::from(Self::__check_uid_gid(parts.next())?);
248 entries.push(IdMapEntry { ns, host, range });
249 }
250
251 Ok(IdMap::new(entries))
252 }
253
254 pub fn get_uid_map(&self) -> Result<IdMap, Error> {
255 self.get_uid_gid_map(c_str!("uid_map"))
256 }
257
258 pub fn get_gid_map(&self) -> Result<IdMap, Error> {
259 self.get_uid_gid_map(c_str!("gid_map"))
260 }
261
262 pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> {
263 use io::Read;
264
265 let mut reader = self.open_file(file, libc::O_RDONLY | libc::O_CLOEXEC, 0)?;
266 let mut out = Vec::new();
267 reader.read_to_end(&mut out)?;
268 Ok(out)
269 }
270
271 pub fn user_caps(&self) -> Result<UserCaps, Error> {
272 UserCaps::new(self)
273 }
274 }