]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-tools/src/acl.rs
move client to pbs-client subcrate
[proxmox-backup.git] / pbs-tools / src / acl.rs
1 //! Implementation of the calls to handle POSIX access control lists
2
3 // see C header file <sys/acl.h> for reference
4 extern crate libc;
5
6 use std::ffi::CString;
7 use std::marker::PhantomData;
8 use std::os::unix::ffi::OsStrExt;
9 use std::os::unix::io::RawFd;
10 use std::path::Path;
11 use std::ptr;
12
13 use libc::{c_char, c_int, c_uint, c_void};
14 use nix::errno::Errno;
15 use nix::NixPath;
16
17 // from: acl/include/acl.h
18 pub const ACL_UNDEFINED_ID: u32 = 0xffffffff;
19 // acl_perm_t values
20 pub type ACLPerm = c_uint;
21 pub const ACL_READ: ACLPerm = 0x04;
22 pub const ACL_WRITE: ACLPerm = 0x02;
23 pub const ACL_EXECUTE: ACLPerm = 0x01;
24
25 // acl_tag_t values
26 pub type ACLTag = c_int;
27 pub const ACL_UNDEFINED_TAG: ACLTag = 0x00;
28 pub const ACL_USER_OBJ: ACLTag = 0x01;
29 pub const ACL_USER: ACLTag = 0x02;
30 pub const ACL_GROUP_OBJ: ACLTag = 0x04;
31 pub const ACL_GROUP: ACLTag = 0x08;
32 pub const ACL_MASK: ACLTag = 0x10;
33 pub const ACL_OTHER: ACLTag = 0x20;
34
35 // acl_type_t values
36 pub type ACLType = c_uint;
37 pub const ACL_TYPE_ACCESS: ACLType = 0x8000;
38 pub const ACL_TYPE_DEFAULT: ACLType = 0x4000;
39
40 // acl entry constants
41 pub const ACL_FIRST_ENTRY: c_int = 0;
42 pub const ACL_NEXT_ENTRY: c_int = 1;
43
44 // acl to extended attribute names constants
45 // from: acl/include/acl_ea.h
46 pub const ACL_EA_ACCESS: &str = "system.posix_acl_access";
47 pub const ACL_EA_DEFAULT: &str = "system.posix_acl_default";
48 pub const ACL_EA_VERSION: u32 = 0x0002;
49
50 #[link(name = "acl")]
51 extern "C" {
52 fn acl_get_file(path: *const c_char, acl_type: ACLType) -> *mut c_void;
53 fn acl_set_file(path: *const c_char, acl_type: ACLType, acl: *mut c_void) -> c_int;
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;
60 fn acl_clear_perms(permset: *mut c_void) -> c_int;
61 fn acl_get_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
62 fn acl_add_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
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)]
71 pub struct ACL {
72 ptr: *mut c_void,
73 }
74
75 impl 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
84 impl 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
91 Ok(ACL { ptr })
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
101 Ok(ACL { ptr })
102 }
103
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)
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
117 Ok(ACL { ptr })
118 }
119
120 pub fn create_entry(&mut self) -> Result<ACLEntry, nix::errno::Errno> {
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 {
128 ptr,
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 }
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 }
161 }
162
163 #[derive(Debug)]
164 pub struct ACLEntry<'a> {
165 ptr: *mut c_void,
166 _phantom: PhantomData<&'a mut ()>,
167 }
168
169 impl<'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 }
202
203 if res == 1 {
204 permissions |= perm as u64;
205 }
206 }
207
208 Ok(permissions)
209 }
210
211 pub fn set_permissions(&mut self, permissions: u64) -> Result<u64, nix::errno::Errno> {
212 let mut permset = ptr::null_mut() as *mut c_void;
213 let mut res = unsafe { acl_get_permset(self.ptr, &mut permset) };
214 if res < 0 {
215 return Err(Errno::last());
216 }
217
218 res = unsafe { acl_clear_perms(permset) };
219 if res < 0 {
220 return Err(Errno::last());
221 }
222
223 for &perm in &[ACL_READ, ACL_WRITE, ACL_EXECUTE] {
224 if permissions & perm as u64 == perm as u64 {
225 res = unsafe { acl_add_perm(permset, perm) };
226 if res < 0 {
227 return Err(Errno::last());
228 }
229 }
230 }
231
232 Ok(permissions)
233 }
234
235 pub fn get_qualifier(&self) -> Result<u64, nix::errno::Errno> {
236 let qualifier = unsafe { acl_get_qualifier(self.ptr) };
237 if qualifier.is_null() {
238 return Err(Errno::last());
239 }
240 let result = unsafe { *(qualifier as *const u32) as u64 };
241 let ret = unsafe { acl_free(qualifier) };
242 if ret != 0 {
243 panic!("invalid pointer encountered while dropping ACL qualifier - {}", Errno::last());
244 }
245
246 Ok(result)
247 }
248
249 pub fn set_qualifier(&mut self, qualifier: u64) -> Result<(), nix::errno::Errno> {
250 let val = qualifier as u32;
251 let val_ptr: *const u32 = &val;
252 let res = unsafe { acl_set_qualifier(self.ptr, val_ptr as *const c_void) };
253 if res < 0 {
254 return Err(Errno::last());
255 }
256
257 Ok(())
258 }
259 }
260
261 #[derive(Debug)]
262 pub struct ACLEntriesIterator {
263 acl: ACL,
264 current: c_int,
265 }
266
267 impl<'a> Iterator for &'a mut ACLEntriesIterator {
268 type Item = ACLEntry<'a>;
269
270 fn next(&mut self) -> Option<Self::Item> {
271 let mut entry_ptr = ptr::null_mut();
272 let res = unsafe { acl_get_entry(self.acl.ptr, self.current, &mut entry_ptr) };
273 self.current = ACL_NEXT_ENTRY;
274 if res == 1 {
275 return Some(ACLEntry { ptr: entry_ptr, _phantom: PhantomData });
276 }
277
278 None
279 }
280 }
281
282 /// Helper to transform `PxarEntry`s user mode to acl permissions.
283 pub fn mode_user_to_acl_permissions(mode: u64) -> u64 {
284 (mode >> 6) & 7
285 }
286
287 /// Helper to transform `PxarEntry`s group mode to acl permissions.
288 pub fn mode_group_to_acl_permissions(mode: u64) -> u64 {
289 (mode >> 3) & 7
290 }
291
292 /// Helper to transform `PxarEntry`s other mode to acl permissions.
293 pub fn mode_other_to_acl_permissions(mode: u64) -> u64 {
294 mode & 7
295 }
296
297 /// Buffer to compose ACLs as extended attribute.
298 pub struct ACLXAttrBuffer {
299 buffer: Vec<u8>,
300 }
301
302 impl ACLXAttrBuffer {
303 /// Create a new buffer to write ACLs as extended attribute.
304 ///
305 /// `version` defines the ACL_EA_VERSION found in acl/include/acl_ea.h
306 pub fn new(version: u32) -> Self {
307 let mut buffer = Vec::new();
308 buffer.extend_from_slice(&version.to_le_bytes());
309 Self { buffer }
310 }
311
312 /// Add ACL entry to buffer.
313 pub fn add_entry(&mut self, tag: ACLTag, qualifier: Option<u64>, permissions: u64) {
314 self.buffer.extend_from_slice(&(tag as u16).to_le_bytes());
315 self.buffer.extend_from_slice(&(permissions as u16).to_le_bytes());
316 match qualifier {
317 Some(qualifier) => self.buffer.extend_from_slice(&(qualifier as u32).to_le_bytes()),
318 None => self.buffer.extend_from_slice(&ACL_UNDEFINED_ID.to_le_bytes()),
319 }
320 }
321
322 /// Length of the buffer in bytes.
323 pub fn len(&self) -> usize {
324 self.buffer.len()
325 }
326
327 /// The buffer always contains at least the version, it is never empty
328 pub const fn is_empty(&self) -> bool { false }
329
330 /// Borrow raw buffer as mut slice.
331 pub fn as_mut_slice(&mut self) -> &mut [u8] {
332 self.buffer.as_mut_slice()
333 }
334 }