]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/acl.rs
more clippy fixups
[proxmox-backup.git] / src / tools / acl.rs
CommitLineData
d22096ef
CE
1//! Implementation of the calls to handle POSIX access control lists
2
3// see C header file <sys/acl.h> for reference
4extern crate libc;
5
6use std::ffi::CString;
7use std::marker::PhantomData;
8use std::os::unix::ffi::OsStrExt;
9use std::os::unix::io::RawFd;
10use std::path::Path;
11use std::ptr;
12
13use libc::{c_char, c_int, c_uint, c_void};
14use nix::errno::Errno;
4482f3fe 15use nix::NixPath;
d22096ef 16
0502ce6d
CE
17// from: acl/include/acl.h
18pub const ACL_UNDEFINED_ID: u32 = 0xffffffff;
d22096ef
CE
19// acl_perm_t values
20pub type ACLPerm = c_uint;
dec1ff18
CE
21pub const ACL_READ: ACLPerm = 0x04;
22pub const ACL_WRITE: ACLPerm = 0x02;
d22096ef
CE
23pub const ACL_EXECUTE: ACLPerm = 0x01;
24
25// acl_tag_t values
26pub type ACLTag = c_int;
27pub const ACL_UNDEFINED_TAG: ACLTag = 0x00;
28pub const ACL_USER_OBJ: ACLTag = 0x01;
29pub const ACL_USER: ACLTag = 0x02;
30pub const ACL_GROUP_OBJ: ACLTag = 0x04;
31pub const ACL_GROUP: ACLTag = 0x08;
32pub const ACL_MASK: ACLTag = 0x10;
33pub const ACL_OTHER: ACLTag = 0x20;
34
35// acl_type_t values
36pub type ACLType = c_uint;
37pub const ACL_TYPE_ACCESS: ACLType = 0x8000;
38pub const ACL_TYPE_DEFAULT: ACLType = 0x4000;
39
40// acl entry constants
41pub const ACL_FIRST_ENTRY: c_int = 0;
42pub const ACL_NEXT_ENTRY: c_int = 1;
43
0502ce6d
CE
44// acl to extended attribute names constants
45// from: acl/include/acl_ea.h
8db14689
WB
46pub const ACL_EA_ACCESS: &str = "system.posix_acl_access";
47pub const ACL_EA_DEFAULT: &str = "system.posix_acl_default";
0502ce6d
CE
48pub const ACL_EA_VERSION: u32 = 0x0002;
49
d22096ef
CE
50#[link(name = "acl")]
51extern "C" {
52 fn acl_get_file(path: *const c_char, acl_type: ACLType) -> *mut c_void;
4482f3fe 53 fn acl_set_file(path: *const c_char, acl_type: ACLType, acl: *mut c_void) -> c_int;
d22096ef
CE
54 fn acl_get_fd(fd: RawFd) -> *mut c_void;
55 fn acl_get_entry(acl: *const c_void, entry_id: c_int, entry: *mut *mut c_void) -> c_int;
56 fn acl_create_entry(acl: *mut *mut c_void, entry: *mut *mut c_void) -> c_int;
57 fn acl_get_tag_type(entry: *mut c_void, tag_type: *mut ACLTag) -> c_int;
58 fn acl_set_tag_type(entry: *mut c_void, tag_type: ACLTag) -> c_int;
59 fn acl_get_permset(entry: *mut c_void, permset: *mut *mut c_void) -> c_int;
dec1ff18 60 fn acl_clear_perms(permset: *mut c_void) -> c_int;
d22096ef 61 fn acl_get_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
dec1ff18 62 fn acl_add_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
d22096ef
CE
63 fn acl_get_qualifier(entry: *mut c_void) -> *mut c_void;
64 fn acl_set_qualifier(entry: *mut c_void, qualifier: *const c_void) -> c_int;
65 fn acl_init(count: c_int) -> *mut c_void;
66 fn acl_valid(ptr: *const c_void) -> c_int;
67 fn acl_free(ptr: *mut c_void) -> c_int;
68}
69
70#[derive(Debug)]
71pub struct ACL {
4482f3fe 72 ptr: *mut c_void,
d22096ef
CE
73}
74
75impl Drop for ACL {
76 fn drop(&mut self) {
77 let ret = unsafe { acl_free(self.ptr) };
78 if ret != 0 {
79 panic!("invalid pointer encountered while dropping ACL - {}", Errno::last());
80 }
81 }
82}
83
84impl ACL {
85 pub fn init(count: usize) -> Result<ACL, nix::errno::Errno> {
86 let ptr = unsafe { acl_init(count as i32 as c_int) };
87 if ptr.is_null() {
88 return Err(Errno::last());
89 }
90
834a2f95 91 Ok(ACL { ptr })
d22096ef
CE
92 }
93
94 pub fn get_file<P: AsRef<Path>>(path: P, acl_type: ACLType) -> Result<ACL, nix::errno::Errno> {
95 let path_cstr = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
96 let ptr = unsafe { acl_get_file(path_cstr.as_ptr(), acl_type) };
97 if ptr.is_null() {
98 return Err(Errno::last());
99 }
100
834a2f95 101 Ok(ACL { ptr })
d22096ef
CE
102 }
103
4482f3fe
WB
104 pub fn set_file<P: NixPath + ?Sized>(&self, path: &P, acl_type: ACLType) -> nix::Result<()> {
105 path.with_nix_path(|path| {
106 Errno::result(unsafe { acl_set_file(path.as_ptr(), acl_type, self.ptr) })
107 })?
108 .map(drop)
d22096ef
CE
109 }
110
111 pub fn get_fd(fd: RawFd) -> Result<ACL, nix::errno::Errno> {
112 let ptr = unsafe { acl_get_fd(fd) };
113 if ptr.is_null() {
114 return Err(Errno::last());
115 }
116
834a2f95 117 Ok(ACL { ptr })
d22096ef
CE
118 }
119
62ee2eb4 120 pub fn create_entry(&mut self) -> Result<ACLEntry, nix::errno::Errno> {
d22096ef
CE
121 let mut ptr = ptr::null_mut() as *mut c_void;
122 let res = unsafe { acl_create_entry(&mut self.ptr, &mut ptr) };
123 if res < 0 {
124 return Err(Errno::last());
125 }
126
127 Ok(ACLEntry {
834a2f95 128 ptr,
d22096ef
CE
129 _phantom: PhantomData,
130 })
131 }
132
133 pub fn is_valid(&self) -> bool {
134 let res = unsafe { acl_valid(self.ptr) };
135 if res == 0 {
136 return true;
137 }
138
139 false
140 }
141
142 pub fn entries(self) -> ACLEntriesIterator {
143 ACLEntriesIterator {
144 acl: self,
145 current: ACL_FIRST_ENTRY,
146 }
147 }
dec1ff18
CE
148
149 pub fn add_entry_full(&mut self, tag: ACLTag, qualifier: Option<u64>, permissions: u64)
150 -> Result<(), nix::errno::Errno>
151 {
152 let mut entry = self.create_entry()?;
153 entry.set_tag_type(tag)?;
154 if let Some(qualifier) = qualifier {
155 entry.set_qualifier(qualifier)?;
156 }
157 entry.set_permissions(permissions)?;
158
159 Ok(())
160 }
d22096ef
CE
161}
162
163#[derive(Debug)]
164pub struct ACLEntry<'a> {
165 ptr: *mut c_void,
166 _phantom: PhantomData<&'a mut ()>,
167}
168
169impl<'a> ACLEntry<'a> {
170 pub fn get_tag_type(&self) -> Result<ACLTag, nix::errno::Errno> {
171 let mut tag = ACL_UNDEFINED_TAG;
172 let res = unsafe { acl_get_tag_type(self.ptr, &mut tag as *mut ACLTag) };
173 if res < 0 {
174 return Err(Errno::last());
175 }
176
177 Ok(tag)
178 }
179
180 pub fn set_tag_type(&mut self, tag: ACLTag) -> Result<(), nix::errno::Errno> {
181 let res = unsafe { acl_set_tag_type(self.ptr, tag) };
182 if res < 0 {
183 return Err(Errno::last());
184 }
185
186 Ok(())
187 }
188
189 pub fn get_permissions(&self) -> Result<u64, nix::errno::Errno> {
190 let mut permissions = 0;
191 let mut permset = ptr::null_mut() as *mut c_void;
192 let mut res = unsafe { acl_get_permset(self.ptr, &mut permset) };
193 if res < 0 {
194 return Err(Errno::last());
195 }
196
197 for &perm in &[ACL_READ, ACL_WRITE, ACL_EXECUTE] {
198 res = unsafe { acl_get_perm(permset, perm) };
199 if res < 0 {
200 return Err(Errno::last());
201 } else if res > 0 {
202 permissions |= perm as u64;
203 }
204 }
205
206 Ok(permissions)
207 }
208
dec1ff18
CE
209 pub fn set_permissions(&mut self, permissions: u64) -> Result<u64, nix::errno::Errno> {
210 let mut permset = ptr::null_mut() as *mut c_void;
211 let mut res = unsafe { acl_get_permset(self.ptr, &mut permset) };
212 if res < 0 {
213 return Err(Errno::last());
214 }
215
216 res = unsafe { acl_clear_perms(permset) };
217 if res < 0 {
218 return Err(Errno::last());
219 }
220
221 for &perm in &[ACL_READ, ACL_WRITE, ACL_EXECUTE] {
222 if permissions & perm as u64 == perm as u64 {
223 res = unsafe { acl_add_perm(permset, perm) };
224 if res < 0 {
225 return Err(Errno::last());
226 }
227 }
228 }
229
230 Ok(permissions)
231 }
232
d22096ef
CE
233 pub fn get_qualifier(&self) -> Result<u64, nix::errno::Errno> {
234 let qualifier = unsafe { acl_get_qualifier(self.ptr) };
235 if qualifier.is_null() {
236 return Err(Errno::last());
237 }
238 let result = unsafe { *(qualifier as *const u32) as u64 };
239 let ret = unsafe { acl_free(qualifier) };
240 if ret != 0 {
241 panic!("invalid pointer encountered while dropping ACL qualifier - {}", Errno::last());
242 }
243
244 Ok(result)
245 }
246
247 pub fn set_qualifier(&mut self, qualifier: u64) -> Result<(), nix::errno::Errno> {
248 let val = qualifier as u32;
249 let val_ptr: *const u32 = &val;
250 let res = unsafe { acl_set_qualifier(self.ptr, val_ptr as *const c_void) };
251 if res < 0 {
252 return Err(Errno::last());
253 }
254
255 Ok(())
256 }
257}
258
259#[derive(Debug)]
260pub struct ACLEntriesIterator {
261 acl: ACL,
262 current: c_int,
263}
264
265impl<'a> Iterator for &'a mut ACLEntriesIterator {
266 type Item = ACLEntry<'a>;
267
268 fn next(&mut self) -> Option<Self::Item> {
269 let mut entry_ptr = ptr::null_mut();
270 let res = unsafe { acl_get_entry(self.acl.ptr, self.current, &mut entry_ptr) };
271 self.current = ACL_NEXT_ENTRY;
272 if res == 1 {
273 return Some(ACLEntry { ptr: entry_ptr, _phantom: PhantomData });
274 }
275
276 None
277 }
278}
bcf0d452
CE
279
280/// Helper to transform `PxarEntry`s user mode to acl permissions.
281pub fn mode_user_to_acl_permissions(mode: u64) -> u64 {
282 (mode >> 6) & 7
283}
284
285/// Helper to transform `PxarEntry`s group mode to acl permissions.
286pub fn mode_group_to_acl_permissions(mode: u64) -> u64 {
287 (mode >> 3) & 7
288}
289
290/// Helper to transform `PxarEntry`s other mode to acl permissions.
291pub fn mode_other_to_acl_permissions(mode: u64) -> u64 {
292 mode & 7
293}
0502ce6d
CE
294
295/// Buffer to compose ACLs as extended attribute.
296pub struct ACLXAttrBuffer {
297 buffer: Vec<u8>,
298}
299
300impl ACLXAttrBuffer {
301 /// Create a new buffer to write ACLs as extended attribute.
302 ///
303 /// `version` defines the ACL_EA_VERSION found in acl/include/acl_ea.h
304 pub fn new(version: u32) -> Self {
305 let mut buffer = Vec::new();
306 buffer.extend_from_slice(&version.to_le_bytes());
307 Self { buffer }
308 }
309
310 /// Add ACL entry to buffer.
311 pub fn add_entry(&mut self, tag: ACLTag, qualifier: Option<u64>, permissions: u64) {
312 self.buffer.extend_from_slice(&(tag as u16).to_le_bytes());
313 self.buffer.extend_from_slice(&(permissions as u16).to_le_bytes());
314 match qualifier {
315 Some(qualifier) => self.buffer.extend_from_slice(&(qualifier as u32).to_le_bytes()),
316 None => self.buffer.extend_from_slice(&ACL_UNDEFINED_ID.to_le_bytes()),
317 }
318 }
319
320 /// Length of the buffer in bytes.
321 pub fn len(&self) -> usize {
322 self.buffer.len()
323 }
324
325 /// Borrow raw buffer as mut slice.
326 pub fn as_mut_slice(&mut self) -> &mut [u8] {
327 self.buffer.as_mut_slice()
328 }
329}