安全模型
OpenOS 的安全模型基于能力(Capability)系统,通过 Handle 和 Rights 实现细粒度的访问控制。
核心安全原则
Section titled “核心安全原则”1. 无环境权限
Section titled “1. 无环境权限”原则: 进程无法访问任何资源,除非它持有具有适当权限的 Handle。
实现:
- 没有”当前目录”
- 没有”环境变量”
- 没有”默认搜索路径”
- 每个资源访问都是显式的
好处:
- 消除了环境权限带来的安全风险
- 权限委托是显式的
- 容易审计权限来源
2. 最小权限原则
Section titled “2. 最小权限原则”原则: 每个进程只拥有完成其任务所需的最小权限。
实现:
- Handle 权限是细粒度的(10 个独立的权限位)
- 权限只能收窄,不能放大
- 创建进程时显式授予权限
3. 单调特权递减
Section titled “3. 单调特权递减”原则: 权限只能沿传递链减少,不能增加。
实现:
原始 Handle: READ | WRITE | TRANSFER | MAP传输 Rights: READ | MAP──────────────────────────────接收方获得: READ | MAP (交集)好处:
- 消除了权限提升攻击
- 权限委托是安全的
- 容易追踪权限来源
4. 故障隔离
Section titled “4. 故障隔离”原则: 一个组件的故障不应影响其他组件。
实现:
- 用户空间驱动隔离
- 独立的地址空间
- 进程间通过 IPC 通信
好处:
- 驱动崩溃不会导致内核崩溃
- 可以独立重启服务
- 更容易进行故障恢复
Handle 作为能力
Section titled “Handle 作为能力”Handle 是 OpenOS 中的唯一访问控制机制:
struct Handle { object_id: u32, // 内核对象 ID rights: Rights, // 权限位掩码 generation: u32, // 防止关闭后使用}bit 0: READ - 从对象读取数据bit 1: WRITE - 向对象写入数据bit 2: EXECUTE - 执行(用于内存对象)bit 3: TRANSFER - 通过 Channel 发送此 Handlebit 4: DUPLICATE - 在同一进程内克隆此 Handlebit 5: SIGNAL - 发送信号bit 6: WAIT - 等待对象bit 7: DESTROY - 关闭/销毁对象bit 8: MAP - 映射到地址空间bit 9: CONFIGURE - 修改对象属性// 权限只能收窄,不能放大fn handle_duplicate(handle: Handle, new_rights: Rights) -> Handle { // new_rights 必须是原始权限的子集 let effective_rights = handle.rights & new_rights; Handle { object_id: handle.object_id, rights: effective_rights, generation: handle.generation + 1, }}
fn handle_transfer(handle: Handle, target: Handle, rights: Rights) -> Result<(), Error> { // rights 是交集 let effective_rights = handle.rights & rights; // 传输给接收方 // ...}没有全局状态
Section titled “没有全局状态”OpenOS 没有全局的访问控制列表或权限表:
- 没有
/etc/passwd - 没有全局文件描述符表
- 没有环境变量
- 每个进程独立的 Handle 表
显式权限委托
Section titled “显式权限委托”// 启动进程(拥有所有初始 Handle)let fs_handle = /* ... */;let net_handle = /* ... */;let console_handle = /* ... */;
// 委托给文件系统服务器(只读)handle_transfer(fs_handle, fs_channel, READ);
// 委托给网络服务器(读写)handle_transfer(net_handle, net_channel, READ | WRITE);
// 委托给用户 Shell(只写)handle_transfer(console_handle, console_channel, WRITE);// 撤回对资源的访问handle_close(fs_handle);
// 如果这是最后一个 Handle,对象被销毁// 所有映射的内存被释放// 所有等待的线程被唤醒(返回错误)无 TOCTOU 竞态
Section titled “无 TOCTOU 竞态”TOCTOU(Time-of-Check to Time-of-Use)是常见的安全漏洞:
// 不安全的 POSIX 代码if (access(file, W_OK) == 0) { // 检查 // 攻击者在这里替换文件 fd = open(file, O_WRONLY); // 使用 write(fd, data, len);}OpenOS 解决方案
Section titled “OpenOS 解决方案”由于 Channel 通信是同步和原子的,不存在 TOCTOU 竞态:
// 安全的 OpenOS 代码let reply = channel_call(fs_handle, ReadMsg { path, offset, len });// 检查和操作是原子的服务器处理请求并在一个事务中发送回复。没有时间窗口让攻击者介入。
Rust 的借用检查器
Section titled “Rust 的借用检查器”OpenOS 使用 Rust 语言,编译期内存安全:
// 编译时捕获内存安全问题fn example() { let mut data = vec![1, 2, 3]; let reference = &data[0]; // 不可变借用 data.push(4); // 错误!不能在借用期间修改 println!("{}", reference);}unsafe 边界
Section titled “unsafe 边界”unsafe 代码明确标记硬件交互点:
// 明确标记 unsafeunsafe fn port_read(port: u16) -> u8 { let mut port = Port::new(port); port.read()}
// 所有 unsafe 调用都是显式的let value = unsafe { port_read(0x60) };中断处理约束
Section titled “中断处理约束”extern "x86-interrupt" fn handler(stack_frame: InterruptStackFrame) { // 1. 不得阻塞 // 2. 必须发送 EOI // 3. 避免死锁(不获取已持有的锁)
// 处理中断 // ...
// 发送 EOI unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); }}// 使用 without_interrupts 保护共享数据fn print_something() { x86_64::instructions::interrupts::without_interrupts(|| { // 在此期间中断被禁用 // 安全地获取 VGA 锁 VGA.lock().write_str("Hello"); });}IPC 安全
Section titled “IPC 安全”fn validate_message(msg: &[u8], handles: &[Handle]) -> Result<(), Error> { // 检查消息大小 if msg.len() > MAX_MESSAGE_SIZE { return Err(Error::TooBig); }
// 检查 Handle 数量 if handles.len() > MAX_HANDLES_PER_MESSAGE { return Err(Error::TooManyHandles); }
// 验证每个 Handle for handle in handles { validate_handle(*handle)?; }
Ok(())}fn check_send_permission(handle: Handle, port_id: u64) -> Result<(), Error> { let entry = handle_table.lookup(handle)?;
// 检查是否有写权限 if !entry.rights.contains(WRITE) { return Err(Error::PermissionDenied); }
// 检查端口是否属于当前进程 if !port_belongs_to_process(port_id) { return Err(Error::PermissionDenied); }
Ok(())}vs POSIX
Section titled “vs POSIX”| 方面 | POSIX | OpenOS |
|---|---|---|
| 访问控制 | UID/GID + 文件权限 | Handle + Rights |
| 权限粒度 | 粗粒度(读/写/执行) | 细粒度(10 个独立位) |
| 环境权限 | 全局(PATH, HOME 等) | 无环境权限 |
| 委托 | 传递 FD 号 | 通过 Channel 传递 Handle |
| 撤回 | 无法撤回 | 关闭 Handle |
| TOCTOU | 存在竞态 | 原子操作 |
vs SELinux
Section titled “vs SELinux”| 方面 | SELinux | OpenOS |
|---|---|---|
| 模型 | 强制访问控制 | 能力系统 |
| 配置 | 复杂的策略文件 | 显式 Handle 委托 |
| 性能 | 开销较大 | 原生性能 |
| 灵活性 | 高 | 中等 |
安全最佳实践
Section titled “安全最佳实践”1. 最小权限
Section titled “1. 最小权限”// 只授予必要的权限handle_transfer(resource, channel, READ); // 而不是 READ | WRITE2. 显式委托
Section titled “2. 显式委托”// 显式传递 Handle,而不是依赖环境let (server, client) = channel_create(0);handle_transfer(resource, server, READ);process_start(child, thread, entry, stack, client);3. 及时撤回
Section titled “3. 及时撤回”// 不再需要时关闭 Handlehandle_close(resource_handle);4. 验证输入
Section titled “4. 验证输入”// 验证所有外部输入fn process_request(msg: &[u8]) -> Result<Response, Error> { if msg.len() < MIN_MESSAGE_SIZE { return Err(Error::InvalidMessage); } // ...}| 功能 | 状态 | 说明 |
|---|---|---|
| Handle 系统 | [WIP] 开发中 | 基本实现 |
| Rights 管理 | [WIP] 开发中 | 基本实现 |
| 权限检查 | [WIP] 开发中 | 基本实现 |
| 进程隔离 | [plan] 计划中 | 需要完整的内存管理 |
| 安全审计 | [plan] 计划中 | 日志和监控 |
- Handle 与 Rights - 能力系统详细设计
- Channel IPC - IPC 安全
- 架构决策记录 - 安全相关决策