Spaces:
Running
Running
File size: 5,325 Bytes
6d57fe0 4b8758b 6d57fe0 4b8758b 6d57fe0 4b8758b 6d57fe0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | use super::perms::{check_permission, Access};
use super::{Gid, Uid, ROOT_UID, WHEEL_GID};
use crate::fs::inode::Inode;
/// Context for the user an agent is acting on behalf of.
#[derive(Debug, Clone)]
pub struct DelegateContext {
pub uid: Uid,
pub gid: Gid,
pub groups: Vec<Gid>,
pub username: String,
}
#[derive(Debug, Clone)]
pub struct Session {
pub uid: Uid,
pub gid: Gid,
pub groups: Vec<Gid>,
pub username: String,
/// When set, the session acts on behalf of this user.
/// All permission checks require BOTH the principal AND
/// the delegate to have access (intersection / least-privilege).
pub delegate: Option<DelegateContext>,
}
impl Session {
pub fn new(uid: Uid, gid: Gid, groups: Vec<Gid>, username: String) -> Self {
Session {
uid,
gid,
groups,
username,
delegate: None,
}
}
pub fn root() -> Self {
Session {
uid: ROOT_UID,
gid: 0,
groups: vec![0, 1],
username: "root".to_string(),
delegate: None,
}
}
pub fn is_root(&self) -> bool {
self.uid == ROOT_UID
}
/// Check permission with delegation intersection.
/// Returns true only if both the principal AND the delegate (if any) have access.
pub fn has_permission(&self, inode: &Inode, access: Access) -> bool {
if !check_permission(inode, self.uid, &self.groups, access) {
return false;
}
if let Some(ref delegate) = self.delegate {
if !check_permission(inode, delegate.uid, &delegate.groups, access) {
return false;
}
}
true
}
/// Check permission using raw mode/uid/gid bits (for LsEntry filtering).
/// Respects delegation intersection.
pub fn has_permission_bits(
&self,
mode: u16,
file_uid: Uid,
file_gid: Gid,
access: Access,
) -> bool {
if !check_bits(self.uid, &self.groups, mode, file_uid, file_gid, access) {
return false;
}
if let Some(ref delegate) = self.delegate {
if !check_bits(
delegate.uid,
&delegate.groups,
mode,
file_uid,
file_gid,
access,
) {
return false;
}
}
true
}
/// The effective uid for file ownership — if delegating, use the delegate's uid.
pub fn effective_uid(&self) -> Uid {
match &self.delegate {
Some(d) => d.uid,
None => self.uid,
}
}
/// The effective gid for file ownership — if delegating, use the delegate's gid.
pub fn effective_gid(&self) -> Gid {
match &self.delegate {
Some(d) => d.gid,
None => self.gid,
}
}
/// Whether the principal is a wheel-group member (admin-equivalent).
pub fn is_wheel(&self) -> bool {
self.groups.contains(&WHEEL_GID)
}
/// Whether the session may bypass per-file Read checks (i.e., see every
/// directory entry and read every file). True for literal uid=0 or any
/// wheel-group member. Privileged write ops (chmod-across-owner,
/// chown-uid) still go through the stricter `is_effectively_root` check.
/// When delegating, the delegate must also be admin-equivalent.
pub fn can_read_anywhere(&self) -> bool {
let principal_admin = self.uid == ROOT_UID || self.is_wheel();
if !principal_admin {
return false;
}
match &self.delegate {
Some(d) => d.uid == ROOT_UID || d.groups.contains(&WHEEL_GID),
None => true,
}
}
/// Whether either the principal or (if delegating) the delegate is root.
/// Strict: only literal uid=0 qualifies. Used by privileged write ops.
pub fn is_effectively_root(&self) -> bool {
if self.uid != ROOT_UID {
return false;
}
match &self.delegate {
Some(d) => d.uid == ROOT_UID,
None => true,
}
}
/// Whether the principal (ignoring delegation) is the owner of a file.
/// When delegating, checks if the delegate is the owner.
pub fn is_effective_owner(&self, file_uid: Uid) -> bool {
match &self.delegate {
Some(d) => {
// Both must be owner or root
let principal_ok = self.uid == ROOT_UID || self.uid == file_uid;
let delegate_ok = d.uid == ROOT_UID || d.uid == file_uid;
principal_ok && delegate_ok
}
None => self.uid == ROOT_UID || self.uid == file_uid,
}
}
}
/// Raw bit-level permission check for a single principal.
fn check_bits(
uid: Uid,
groups: &[Gid],
mode: u16,
file_uid: Uid,
file_gid: Gid,
access: Access,
) -> bool {
if uid == ROOT_UID {
return true;
}
let bit = match access {
Access::Read => 4,
Access::Write => 2,
Access::Execute => 1,
};
if uid == file_uid {
return (mode >> 6) & bit != 0;
}
if groups.contains(&file_gid) {
return (mode >> 3) & bit != 0;
}
mode & bit != 0
}
|