]>
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; | |
4482f3fe | 15 | use nix::NixPath; |
d22096ef | 16 | |
0502ce6d CE |
17 | // from: acl/include/acl.h |
18 | pub const ACL_UNDEFINED_ID: u32 = 0xffffffff; | |
d22096ef CE |
19 | // acl_perm_t values |
20 | pub type ACLPerm = c_uint; | |
dec1ff18 CE |
21 | pub const ACL_READ: ACLPerm = 0x04; |
22 | pub const ACL_WRITE: ACLPerm = 0x02; | |
d22096ef CE |
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 | ||
0502ce6d CE |
44 | // acl to extended attribute names constants |
45 | // from: acl/include/acl_ea.h | |
8db14689 WB |
46 | pub const ACL_EA_ACCESS: &str = "system.posix_acl_access"; |
47 | pub const ACL_EA_DEFAULT: &str = "system.posix_acl_default"; | |
0502ce6d CE |
48 | pub const ACL_EA_VERSION: u32 = 0x0002; |
49 | ||
d22096ef CE |
50 | #[link(name = "acl")] |
51 | extern "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)] | |
71 | pub struct ACL { | |
4482f3fe | 72 | ptr: *mut c_void, |
d22096ef CE |
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 | ||
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)] | |
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 | } 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)] | |
260 | pub struct ACLEntriesIterator { | |
261 | acl: ACL, | |
262 | current: c_int, | |
263 | } | |
264 | ||
265 | impl<'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. | |
281 | pub 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. | |
286 | pub 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. | |
291 | pub fn mode_other_to_acl_permissions(mode: u64) -> u64 { | |
292 | mode & 7 | |
293 | } | |
0502ce6d CE |
294 | |
295 | /// Buffer to compose ACLs as extended attribute. | |
296 | pub struct ACLXAttrBuffer { | |
297 | buffer: Vec<u8>, | |
298 | } | |
299 | ||
300 | impl 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 | } |