diff --git a/drivers/android/context.rs b/drivers/android/context.rs index c84e9e21cb1dae..3becf64862a380 100644 --- a/drivers/android/context.rs +++ b/drivers/android/context.rs @@ -5,6 +5,7 @@ extern crate alloc; use kernel::{ bindings, prelude::*, + security, sync::{Mutex, Ref}, Error, }; @@ -51,7 +52,7 @@ impl Context { if manager.node.is_some() { return Err(Error::EBUSY); } - // TODO: Call security_binder_set_context_mgr. + security::binder_set_context_mgr(&node_ref.node.owner.task)?; // TODO: Get the actual caller id. let caller_uid = bindings::kuid_t::default(); diff --git a/drivers/android/process.rs b/drivers/android/process.rs index 539b27d704178c..ce4d73d7ce5ba9 100644 --- a/drivers/android/process.rs +++ b/drivers/android/process.rs @@ -274,6 +274,9 @@ impl ProcessNodeRefs { pub(crate) struct Process { ctx: Ref, + // The task leader (process). + pub(crate) task: Task, + // TODO: For now this a mutex because we have allocations in RangeAllocator while holding the // lock. We may want to split up the process state at some point to use a spin lock for the // other fields. @@ -293,6 +296,7 @@ impl Process { Ok(Ref::pinned(Ref::try_new_and_init( Self { ctx, + task: Task::current().group_leader().clone(), // SAFETY: `inner` is initialised in the call to `mutex_init` below. inner: unsafe { Mutex::new(ProcessInner::new()) }, // SAFETY: `node_refs` is initialised in the call to `mutex_init` below. @@ -917,7 +921,10 @@ impl FileOperations for Process { } fn mmap(this: &Ref, _file: &File, vma: &mut bindings::vm_area_struct) -> Result { - // TODO: Only group leader is allowed to create mappings. + // We don't allow mmap to be used in a different process. + if !Task::current().group_leader().eq(&this.task) { + return Err(Error::EINVAL); + } if vma.vm_start == 0 { return Err(Error::EINVAL); diff --git a/drivers/android/thread.rs b/drivers/android/thread.rs index 1e870f85e16d9b..ab960b06254917 100644 --- a/drivers/android/thread.rs +++ b/drivers/android/thread.rs @@ -9,6 +9,7 @@ use kernel::{ io_buffer::{IoBufferReader, IoBufferWriter}, linked_list::{GetLinks, Links, List}, prelude::*, + security, sync::{CondVar, Ref, SpinLock}, user_ptr::{UserSlicePtr, UserSlicePtrWriter}, Error, @@ -388,9 +389,11 @@ impl Thread { let ptr = unsafe { obj.__bindgen_anon_1.binder } as _; let cookie = obj.cookie as _; let flags = obj.flags as _; - Ok(self + let node = self .process - .get_node(ptr, cookie, flags, strong, Some(self))?) + .get_node(ptr, cookie, flags, strong, Some(self))?; + security::binder_transfer_binder(&self.process.task, &view.alloc.process.task)?; + Ok(node) })?; } BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => { @@ -398,7 +401,9 @@ impl Thread { view.transfer_binder_object(offset, strong, |obj| { // SAFETY: `handle` is a `u32`; any bit pattern is a valid representation. let handle = unsafe { obj.__bindgen_anon_1.handle } as _; - self.process.get_node_from_handle(handle, strong) + let node = self.process.get_node_from_handle(handle, strong)?; + security::binder_transfer_binder(&self.process.task, &view.alloc.process.task)?; + Ok(node) })?; } BINDER_TYPE_FD => { @@ -410,6 +415,11 @@ impl Thread { // SAFETY: `fd` is a `u32`; any bit pattern is a valid representation. let fd = unsafe { obj.__bindgen_anon_1.fd }; let file = File::from_fd(fd)?; + security::binder_transfer_file( + &self.process.task, + &view.alloc.process.task, + &file, + )?; let field_offset = kernel::offset_of!(bindings::binder_fd_object, __bindgen_anon_1.fd) as usize; let file_info = Box::try_new(FileInfo::new(file, offset + field_offset))?; @@ -598,6 +608,7 @@ impl Thread { fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionData) -> BinderResult { let handle = unsafe { tr.target.handle }; let node_ref = self.process.get_transaction_node(handle)?; + security::binder_transaction(&self.process.task, &node_ref.node.owner.task)?; let completion = Arc::try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let transaction = Transaction::new(node_ref, None, self, tr)?; self.inner.lock().push_work(completion); @@ -609,6 +620,7 @@ impl Thread { fn transaction_inner(self: &Arc, tr: &BinderTransactionData) -> BinderResult { let handle = unsafe { tr.target.handle }; let node_ref = self.process.get_transaction_node(handle)?; + security::binder_transaction(&self.process.task, &node_ref.node.owner.task)?; // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; diff --git a/rust/helpers.c b/rust/helpers.c index f57f9340f49f3b..a6e98abb13efdf 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include void rust_helper_BUG(void) { @@ -183,6 +184,34 @@ void rust_helper_put_task_struct(struct task_struct * t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); +int rust_helper_security_binder_set_context_mgr(struct task_struct *mgr) +{ + return security_binder_set_context_mgr(mgr); +} +EXPORT_SYMBOL_GPL(rust_helper_security_binder_set_context_mgr); + +int rust_helper_security_binder_transaction(struct task_struct *from, + struct task_struct *to) +{ + return security_binder_transaction(from, to); +} +EXPORT_SYMBOL_GPL(rust_helper_security_binder_transaction); + +int rust_helper_security_binder_transfer_binder(struct task_struct *from, + struct task_struct *to) +{ + return security_binder_transfer_binder(from, to); +} +EXPORT_SYMBOL_GPL(rust_helper_security_binder_transfer_binder); + +int rust_helper_security_binder_transfer_file(struct task_struct *from, + struct task_struct *to, + struct file *file) +{ + return security_binder_transfer_file(from, to, file); +} +EXPORT_SYMBOL_GPL(rust_helper_security_binder_transfer_file); + /* We use bindgen's --size_t-is-usize option to bind the C size_t type * as the Rust usize type, so we can use it in contexts where Rust * expects a usize like slice (array) indices. usize is defined to be diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 148c75a902bb62..c64a6307da37ec 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -17,6 +17,7 @@ #include #include #include +#include // `bindgen` gets confused at certain things const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 7201cebd7981df..dd7479c35e3e22 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -50,6 +50,7 @@ pub mod file; pub mod file_operations; pub mod miscdev; pub mod pages; +pub mod security; pub mod str; pub mod task; pub mod traits; diff --git a/rust/kernel/security.rs b/rust/kernel/security.rs new file mode 100644 index 00000000000000..c38b0dceb345aa --- /dev/null +++ b/rust/kernel/security.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Linux Security Modules (LSM). +//! +//! C header: [`include/linux/security.h`](../../../../include/linux/security.h). + +use crate::{bindings, c_types, error::Error, file::File, task::Task, Result}; + +extern "C" { + #[allow(improper_ctypes)] + fn rust_helper_security_binder_set_context_mgr( + mgr: *mut bindings::task_struct, + ) -> c_types::c_int; + #[allow(improper_ctypes)] + fn rust_helper_security_binder_transaction( + from: *mut bindings::task_struct, + to: *mut bindings::task_struct, + ) -> c_types::c_int; + #[allow(improper_ctypes)] + fn rust_helper_security_binder_transfer_binder( + from: *mut bindings::task_struct, + to: *mut bindings::task_struct, + ) -> c_types::c_int; + #[allow(improper_ctypes)] + fn rust_helper_security_binder_transfer_file( + from: *mut bindings::task_struct, + to: *mut bindings::task_struct, + file: *mut bindings::file, + ) -> c_types::c_int; +} + +/// Calls the security modules to determine if the given task can become the manager of a binder +/// context. +pub fn binder_set_context_mgr(mgr: &Task) -> Result { + // SAFETY: By the `Task` invariants, `mgr.ptr` is valid. + let ret = unsafe { rust_helper_security_binder_set_context_mgr(mgr.ptr) }; + if ret != 0 { + Err(Error::from_kernel_errno(ret)) + } else { + Ok(()) + } +} + +/// Calls the security modules to determine if binder transactions are allowed from task `from` to +/// task `to`. +pub fn binder_transaction(from: &Task, to: &Task) -> Result { + // SAFETY: By the `Task` invariants, `from.ptr` and `to.ptr` are valid. + let ret = unsafe { rust_helper_security_binder_transaction(from.ptr, to.ptr) }; + if ret != 0 { + Err(Error::from_kernel_errno(ret)) + } else { + Ok(()) + } +} + +/// Calls the security modules to determine if task `from` is allowed to send binder objects +/// (owned by itself or other processes) to task `to` through a binder transaction. +pub fn binder_transfer_binder(from: &Task, to: &Task) -> Result { + // SAFETY: By the `Task` invariants, `from.ptr` and `to.ptr` are valid. + let ret = unsafe { rust_helper_security_binder_transfer_binder(from.ptr, to.ptr) }; + if ret != 0 { + Err(Error::from_kernel_errno(ret)) + } else { + Ok(()) + } +} + +/// Calls the security modules to determine if task `from` is allowed to send the given file to +/// task `to` (which would get its own file descriptor) through a binder transaction. +pub fn binder_transfer_file(from: &Task, to: &Task, file: &File) -> Result { + // SAFETY: By the `Task` invariants, `from.ptr` and `to.ptr` are valid. Similarly, by the + // `File` invariants, `file.ptr` is also valid. + let ret = unsafe { rust_helper_security_binder_transfer_file(from.ptr, to.ptr, file.ptr) }; + if ret != 0 { + Err(Error::from_kernel_errno(ret)) + } else { + Ok(()) + } +}