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