1use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
41
42use rustix::io::Errno;
43
44use crate::last_errno;
45
46const SYS_LANDLOCK_CREATE_RULESET: i64 = 444;
47const SYS_LANDLOCK_ADD_RULE: i64 = 445;
48const SYS_LANDLOCK_RESTRICT_SELF: i64 = 446;
49
50const LANDLOCK_CREATE_RULESET_VERSION: u32 = 1 << 0;
51const LANDLOCK_RULE_PATH_BENEATH: u32 = 1;
52
53pub const LANDLOCK_ACCESS_FS_EXECUTE: u64 = 1 << 0;
55pub const LANDLOCK_ACCESS_FS_WRITE_FILE: u64 = 1 << 1;
56pub const LANDLOCK_ACCESS_FS_READ_FILE: u64 = 1 << 2;
57pub const LANDLOCK_ACCESS_FS_READ_DIR: u64 = 1 << 3;
58pub const LANDLOCK_ACCESS_FS_REMOVE_DIR: u64 = 1 << 4;
59pub const LANDLOCK_ACCESS_FS_REMOVE_FILE: u64 = 1 << 5;
60pub const LANDLOCK_ACCESS_FS_MAKE_CHAR: u64 = 1 << 6;
61pub const LANDLOCK_ACCESS_FS_MAKE_DIR: u64 = 1 << 7;
62pub const LANDLOCK_ACCESS_FS_MAKE_REG: u64 = 1 << 8;
63pub const LANDLOCK_ACCESS_FS_MAKE_SOCK: u64 = 1 << 9;
64pub const LANDLOCK_ACCESS_FS_MAKE_FIFO: u64 = 1 << 10;
65pub const LANDLOCK_ACCESS_FS_MAKE_BLOCK: u64 = 1 << 11;
66pub const LANDLOCK_ACCESS_FS_MAKE_SYM: u64 = 1 << 12;
67
68pub const LANDLOCK_ACCESS_FS_REFER: u64 = 1 << 13;
70
71pub const LANDLOCK_ACCESS_FS_TRUNCATE: u64 = 1 << 14;
73
74pub const LANDLOCK_ACCESS_FS_IOCTL_DEV: u64 = 1 << 15;
76pub const LANDLOCK_ACCESS_NET_BIND_TCP: u64 = 1 << 0;
77pub const LANDLOCK_ACCESS_NET_CONNECT_TCP: u64 = 1 << 1;
78
79pub const LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: u64 = 1 << 0;
82pub const LANDLOCK_SCOPE_SIGNAL: u64 = 1 << 1;
84
85#[repr(C)]
86#[derive(Debug, Default)]
87pub struct LandlockRulesetAttr {
88 pub handled_access_fs: u64,
89 pub handled_access_net: u64,
90 pub scoped: u64,
92}
93
94#[repr(C)]
95#[derive(Debug)]
96pub struct LandlockPathBeneathAttr {
97 pub allowed_access: u64,
98 pub parent_fd: RawFd,
99}
100
101pub fn landlock_abi_version() -> Result<u32, Errno> {
107 let ret = unsafe {
109 libc::syscall(
110 SYS_LANDLOCK_CREATE_RULESET,
111 std::ptr::null::<LandlockRulesetAttr>(),
112 0usize,
113 LANDLOCK_CREATE_RULESET_VERSION,
114 )
115 };
116 if ret < 0 {
117 Err(last_errno())
118 } else {
119 Ok(ret as u32)
120 }
121}
122
123pub fn landlock_create_ruleset(attr: &LandlockRulesetAttr) -> Result<OwnedFd, Errno> {
129 let ret = unsafe {
131 libc::syscall(
132 SYS_LANDLOCK_CREATE_RULESET,
133 attr as *const LandlockRulesetAttr,
134 size_of::<LandlockRulesetAttr>(),
135 0u32,
136 )
137 };
138 if ret < 0 {
139 Err(last_errno())
140 } else {
141 Ok(unsafe { OwnedFd::from_raw_fd(ret as RawFd) })
143 }
144}
145
146pub fn landlock_add_rule_path(
152 ruleset_fd: &OwnedFd,
153 attr: &LandlockPathBeneathAttr,
154) -> Result<(), Errno> {
155 let ret = unsafe {
157 libc::syscall(
158 SYS_LANDLOCK_ADD_RULE,
159 ruleset_fd.as_raw_fd(),
160 LANDLOCK_RULE_PATH_BENEATH,
161 attr as *const LandlockPathBeneathAttr,
162 0u32,
163 )
164 };
165 if ret < 0 { Err(last_errno()) } else { Ok(()) }
166}
167
168pub fn landlock_restrict_self(ruleset_fd: &OwnedFd) -> Result<(), Errno> {
174 let ret = unsafe { libc::syscall(SYS_LANDLOCK_RESTRICT_SELF, ruleset_fd.as_raw_fd(), 0u32) };
176 if ret < 0 { Err(last_errno()) } else { Ok(()) }
177}
178
179pub fn fs_access_for_abi(abi: u32) -> u64 {
181 let mut access = LANDLOCK_ACCESS_FS_EXECUTE
182 | LANDLOCK_ACCESS_FS_WRITE_FILE
183 | LANDLOCK_ACCESS_FS_READ_FILE
184 | LANDLOCK_ACCESS_FS_READ_DIR
185 | LANDLOCK_ACCESS_FS_REMOVE_DIR
186 | LANDLOCK_ACCESS_FS_REMOVE_FILE
187 | LANDLOCK_ACCESS_FS_MAKE_CHAR
188 | LANDLOCK_ACCESS_FS_MAKE_DIR
189 | LANDLOCK_ACCESS_FS_MAKE_REG
190 | LANDLOCK_ACCESS_FS_MAKE_SOCK
191 | LANDLOCK_ACCESS_FS_MAKE_FIFO
192 | LANDLOCK_ACCESS_FS_MAKE_BLOCK
193 | LANDLOCK_ACCESS_FS_MAKE_SYM;
194
195 if abi >= 2 {
196 access |= LANDLOCK_ACCESS_FS_REFER;
197 }
198 if abi >= 3 {
199 access |= LANDLOCK_ACCESS_FS_TRUNCATE;
200 }
201 if abi >= 4 {
202 access |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
203 }
204
205 access
206}
207
208pub fn net_access_for_abi(abi: u32) -> u64 {
210 if abi >= 4 {
211 LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP
212 } else {
213 0
214 }
215}
216
217pub fn scope_for_abi(abi: u32) -> u64 {
222 if abi >= 5 {
223 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL
224 } else {
225 0
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn abi_version() {
235 if let Ok(v) = landlock_abi_version() {
236 assert!(v >= 1);
237 }
238 }
239
240 #[test]
241 fn fs_access_increases_with_abi() {
242 assert!(fs_access_for_abi(2) > fs_access_for_abi(1));
243 assert!(fs_access_for_abi(3) > fs_access_for_abi(2));
244 assert!(fs_access_for_abi(4) > fs_access_for_abi(3));
245 }
246}