1
- use std:: ffi:: CString ;
2
- use std:: { fs, io} ;
1
+ use std:: ffi:: { c_char , c_int , CStr , CString } ;
2
+ use std:: { fs, io, mem } ;
3
3
4
4
use crate :: cutils:: cerr;
5
5
@@ -21,13 +21,43 @@ fn apparmor_is_enabled() -> io::Result<bool> {
21
21
}
22
22
}
23
23
24
- #[ link( name = "apparmor" ) ]
25
- extern "C" {
26
- pub fn aa_change_onexec ( profile : * const libc:: c_char ) -> libc:: c_int ;
27
- }
28
-
29
24
/// Switch the apparmor profile to the given profile on the next exec call
30
25
fn apparmor_prepare_exec ( new_profile : & str ) -> io:: Result < ( ) > {
26
+ // SAFETY: Always safe to call
27
+ unsafe { libc:: dlerror ( ) } ; // Clear any existing error
28
+
29
+ // SAFETY: Loading a known safe dylib. LD_LIBRARY_PATH is ignored because we are setuid.
30
+ let handle = unsafe { libc:: dlopen ( c"libapparmor.so.1" . as_ptr ( ) , libc:: RTLD_NOW ) } ;
31
+ if handle. is_null ( ) {
32
+ // SAFETY: In case of an error, dlerror returns a valid C string.
33
+ return Err ( io:: Error :: new ( io:: ErrorKind :: NotFound , unsafe {
34
+ CStr :: from_ptr ( libc:: dlerror ( ) )
35
+ . to_string_lossy ( )
36
+ . into_owned ( )
37
+ } ) ) ;
38
+ }
39
+
40
+ // SAFETY: dlsym will either return a function pointer of the right signature or NULL.
41
+ // Option<fn()> is guaranteed to represent a NULL value as None and any other value as Some.
42
+ let aa_change_onexec: Option < unsafe extern "C" fn ( * const c_char ) -> c_int > =
43
+ unsafe { mem:: transmute ( libc:: dlsym ( handle, c"aa_change_onexec" . as_ptr ( ) ) ) } ;
44
+ let Some ( aa_change_onexec) = aa_change_onexec else {
45
+ // SAFETY: Always safe to call
46
+ let err = unsafe { libc:: dlerror ( ) } ;
47
+ if err. is_null ( ) {
48
+ // There was no error in dlsym, but the symbol itself was defined as NULL pointer.
49
+ // This is still an error for us, but dlerror will not return any error.
50
+ return Err ( io:: Error :: new (
51
+ io:: ErrorKind :: Other ,
52
+ "aa_change_onexec symbol is a NULL pointer" ,
53
+ ) ) ;
54
+ }
55
+ // SAFETY: In case of an error, dlerror returns a valid C string.
56
+ return Err ( io:: Error :: new ( io:: ErrorKind :: NotFound , unsafe {
57
+ CStr :: from_ptr ( err) . to_string_lossy ( ) . into_owned ( )
58
+ } ) ) ;
59
+ } ;
60
+
31
61
let new_profile_cstr = CString :: new ( new_profile) ?;
32
62
// SAFETY: new_profile_cstr provided by CString ensures a valid ptr
33
63
cerr ( unsafe { aa_change_onexec ( new_profile_cstr. as_ptr ( ) ) } ) ?;
0 commit comments